summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--StubLibraries.bp12
-rw-r--r--apct-tests/perftests/core/AndroidTest.xml5
-rw-r--r--apct-tests/perftests/windowmanager/Android.bp26
-rw-r--r--apct-tests/perftests/windowmanager/AndroidManifest.xml32
-rw-r--r--apct-tests/perftests/windowmanager/AndroidTest.xml33
-rw-r--r--apct-tests/perftests/windowmanager/src/android/wm/InternalWindowOperationPerfTest.java (renamed from apct-tests/perftests/core/src/android/wm/InternalWindowOperationPerfTest.java)0
-rw-r--r--apct-tests/perftests/windowmanager/src/android/wm/RecentsAnimationPerfTest.java (renamed from apct-tests/perftests/core/src/android/wm/RecentsAnimationPerfTest.java)15
-rw-r--r--apct-tests/perftests/windowmanager/src/android/wm/RelayoutPerfTest.java (renamed from apct-tests/perftests/core/src/android/wm/RelayoutPerfTest.java)0
-rw-r--r--apct-tests/perftests/windowmanager/src/android/wm/WindowAddRemovePerfTest.java (renamed from apct-tests/perftests/core/src/android/wm/WindowAddRemovePerfTest.java)0
-rw-r--r--apct-tests/perftests/windowmanager/src/android/wm/WindowManagerPerfTestBase.java (renamed from apct-tests/perftests/core/src/android/wm/WindowManagerPerfTestBase.java)0
-rw-r--r--apex/Android.bp12
-rw-r--r--apex/extservices/Android.bp2
-rw-r--r--apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java13
-rw-r--r--apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java41
-rw-r--r--apex/media/framework/Android.bp27
-rw-r--r--apex/permission/framework/Android.bp27
-rw-r--r--apex/permission/service/Android.bp9
-rw-r--r--apex/sdkextensions/framework/Android.bp27
-rw-r--r--apex/statsd/framework/Android.bp27
-rw-r--r--apex/statsd/framework/java/android/app/StatsManager.java4
-rw-r--r--apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java159
-rw-r--r--apex/statsd/service/java/com/android/server/stats/StatsManagerService.java4
-rw-r--r--api/test-current.txt1
-rw-r--r--cmds/statsd/src/StatsService.cpp2
-rw-r--r--cmds/statsd/src/atoms.proto45
-rw-r--r--cmds/statsd/src/external/StatsCallbackPuller.cpp1
-rw-r--r--cmds/statsd/src/external/StatsPuller.cpp5
-rw-r--r--cmds/statsd/src/external/StatsPullerManager.cpp13
-rw-r--r--cmds/statsd/src/guardrail/StatsdStats.cpp24
-rw-r--r--cmds/statsd/src/guardrail/StatsdStats.h28
-rw-r--r--cmds/statsd/src/metrics/GaugeMetricProducer.cpp10
-rw-r--r--cmds/statsd/src/metrics/ValueMetricProducer.cpp5
-rw-r--r--cmds/statsd/src/stats_log.proto7
-rw-r--r--cmds/statsd/src/stats_log_util.cpp15
-rw-r--r--cmds/statsd/tests/guardrail/StatsdStats_test.cpp8
-rw-r--r--core/java/android/app/DreamManager.java20
-rw-r--r--core/java/android/app/IActivityManager.aidl6
-rw-r--r--core/java/android/content/ContentResolver.java39
-rw-r--r--core/java/android/content/pm/PackageManager.java39
-rw-r--r--core/java/android/content/pm/PackageParser.java4
-rw-r--r--core/java/android/content/pm/parsing/ApkLiteParseUtils.java222
-rw-r--r--core/java/android/content/pm/parsing/ParsingPackageUtils.java159
-rw-r--r--core/java/android/content/pm/parsing/result/ParseTypeImpl.java26
-rw-r--r--core/java/android/os/UserManager.java5
-rw-r--r--core/java/android/permission/PermissionControllerManager.java37
-rw-r--r--core/java/android/permission/PermissionControllerService.java7
-rw-r--r--core/java/android/provider/DocumentsProvider.java2
-rwxr-xr-xcore/java/android/provider/Settings.java8
-rw-r--r--core/java/android/service/autofill/IInlineSuggestionUi.aidl29
-rw-r--r--core/java/android/service/autofill/IInlineSuggestionUiCallback.aidl6
-rw-r--r--core/java/android/service/autofill/ISurfacePackageResultCallback.aidl28
-rw-r--r--core/java/android/service/autofill/InlineSuggestionRenderService.java121
-rw-r--r--core/java/android/service/dreams/DreamActivity.java14
-rw-r--r--core/java/android/service/dreams/DreamService.java9
-rw-r--r--core/java/android/view/InsetsController.java2
-rw-r--r--core/java/android/view/InsetsSourceConsumer.java15
-rw-r--r--core/java/android/view/ViewRootImpl.java2
-rw-r--r--core/java/android/view/ViewRootInsetsControllerHost.java6
-rw-r--r--core/java/android/view/WindowlessWindowManager.java23
-rw-r--r--core/java/android/view/inputmethod/InlineSuggestion.java270
-rw-r--r--core/java/android/view/inputmethod/InputMethodManager.java5
-rw-r--r--core/java/android/widget/inline/InlineContentView.java137
-rw-r--r--core/java/android/window/TaskEmbedder.java69
-rw-r--r--core/java/android/window/TaskOrganizerTaskEmbedder.java6
-rw-r--r--core/java/android/window/VirtualDisplayTaskEmbedder.java58
-rw-r--r--core/java/com/android/internal/app/ChooserActivity.java75
-rw-r--r--core/java/com/android/internal/app/ChooserGridLayoutManager.java70
-rw-r--r--core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java69
-rw-r--r--core/java/com/android/internal/app/ResolverActivity.java12
-rw-r--r--core/java/com/android/internal/app/ResolverListAdapter.java16
-rw-r--r--core/java/com/android/internal/app/chooser/SelectableTargetInfo.java5
-rw-r--r--core/java/com/android/internal/infra/AbstractRemoteService.java14
-rw-r--r--core/java/com/android/internal/policy/TaskResizingAlgorithm.java8
-rw-r--r--core/java/com/android/internal/view/inline/IInlineContentProvider.aidl2
-rw-r--r--core/java/com/android/internal/widget/GridLayoutManager.java6
-rw-r--r--core/java/com/android/internal/widget/ResolverDrawerLayout.java13
-rw-r--r--core/jni/com_android_internal_os_Zygote.cpp29
-rw-r--r--core/proto/android/providers/settings/global.proto3
-rw-r--r--core/res/res/anim/dream_activity_close_exit.xml23
-rw-r--r--core/res/res/anim/dream_activity_open_enter.xml26
-rw-r--r--core/res/res/anim/dream_activity_open_exit.xml25
-rw-r--r--core/res/res/layout/chooser_list_per_profile.xml4
-rw-r--r--core/res/res/layout/resolver_list.xml1
-rw-r--r--core/res/res/values-mcc334-mnc020/config.xml3
-rw-r--r--core/res/res/values-mcc334-mnc020/strings.xml24
-rw-r--r--core/res/res/values/config.xml4
-rw-r--r--core/res/res/values/strings.xml9
-rw-r--r--core/res/res/values/styles.xml6
-rw-r--r--core/res/res/values/symbols.xml11
-rw-r--r--core/res/res/values/themes.xml1
-rw-r--r--core/tests/coretests/src/android/content/pm/PackageManagerTests.java157
-rw-r--r--core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java4
-rw-r--r--core/tests/coretests/src/android/view/InsetsControllerTest.java40
-rw-r--r--core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java71
-rw-r--r--errorprone/Android.bp2
-rw-r--r--errorprone/java/com/google/errorprone/bugpatterns/android/RethrowFromSystemChecker.java80
-rw-r--r--errorprone/java/com/google/errorprone/bugpatterns/android/TargetSdkChecker.java21
-rw-r--r--location/java/android/location/LocationManager.java40
-rw-r--r--media/java/android/media/AudioMetadata.java34
-rw-r--r--media/java/android/media/MediaRouter2Manager.java252
-rw-r--r--media/java/android/media/RoutingSessionInfo.java28
-rw-r--r--media/java/android/media/tv/TvInputManager.java8
-rw-r--r--media/java/android/media/tv/tunerresourcemanager/ResourceClientProfile.java5
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBarController.java4
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/car/notification/CarHeadsUpNotificationSystemContainer.java23
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java15
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewMediator.java12
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/car/window/OverlayPanelViewController.java4
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewController.java17
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewGlobalStateController.java165
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/car/window/OverlayWindowModule.java8
-rw-r--r--packages/CarSystemUI/tests/res/layout/overlay_view_global_state_controller_test.xml43
-rw-r--r--packages/CarSystemUI/tests/src/com/android/systemui/car/keyguard/CarKeyguardViewControllerTest.java45
-rw-r--r--packages/CarSystemUI/tests/src/com/android/systemui/car/notification/CarHeadsUpNotificationSystemContainerTest.java76
-rw-r--r--packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayPanelViewControllerTest.java15
-rw-r--r--packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayViewGlobalStateControllerTest.java424
-rw-r--r--packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java10
-rw-r--r--packages/PackageInstaller/src/com/android/packageinstaller/PackageUtil.java13
-rw-r--r--packages/PackageInstaller/src/com/android/packageinstaller/wear/WearPackageInstallerService.java56
-rw-r--r--packages/SettingsLib/res/values/arrays.xml14
-rw-r--r--packages/SettingsLib/res/values/strings.xml8
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/applications/AppUtils.java11
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java16
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java15
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java30
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java25
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java8
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java11
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java47
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/PhoneMediaDeviceTest.java22
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java3
-rw-r--r--packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java3
-rw-r--r--packages/SystemUI/res/drawable/qs_media_background.xml16
-rw-r--r--packages/SystemUI/res/drawable/screenshot_actions_background_protection.xml2
-rw-r--r--packages/SystemUI/res/layout/bubble_overflow_view.xml2
-rw-r--r--packages/SystemUI/res/layout/global_screenshot.xml2
-rw-r--r--packages/SystemUI/res/layout/keyguard_media_header.xml6
-rw-r--r--packages/SystemUI/res/layout/qqs_media_panel.xml6
-rw-r--r--packages/SystemUI/res/layout/qs_media_panel.xml10
-rw-r--r--packages/SystemUI/res/values-night/colors.xml1
-rw-r--r--packages/SystemUI/res/values-night/dimens.xml4
-rw-r--r--packages/SystemUI/res/values/attrs.xml7
-rw-r--r--packages/SystemUI/res/values/colors.xml1
-rw-r--r--packages/SystemUI/res/values/config.xml4
-rw-r--r--packages/SystemUI/res/values/dimens.xml3
-rw-r--r--packages/SystemUI/res/values/strings.xml7
-rw-r--r--packages/SystemUI/res/values/styles.xml4
-rw-r--r--packages/SystemUI/src/com/android/systemui/CameraAvailabilityListener.kt18
-rw-r--r--packages/SystemUI/src/com/android/systemui/broadcast/UserBroadcastDispatcher.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleDataRepository.kt49
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java36
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java23
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/storage/BubblePersistentRepository.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/storage/BubbleVolatileRepository.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/storage/BubbleXmlHelper.kt35
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java23
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/IlluminationDrawable.kt264
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QuickQSMediaPlayer.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt52
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/TargetSdkResolver.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/BindStage.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipeline.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java39
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowController.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java43
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/wm/SystemWindows.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java12
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java26
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java3
-rw-r--r--packages/Tethering/Android.bp3
-rw-r--r--packages/Tethering/apex/Android.bp9
-rw-r--r--packages/Tethering/common/TetheringLib/Android.bp27
-rw-r--r--packages/Tethering/res/values/config.xml7
-rw-r--r--packages/Tethering/res/values/overlayable.xml1
-rw-r--r--packages/Tethering/src/com/android/networkstack/tethering/OffloadController.java19
-rw-r--r--packages/Tethering/src/com/android/networkstack/tethering/Tethering.java14
-rw-r--r--packages/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java20
-rw-r--r--packages/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java14
-rw-r--r--packages/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadControllerTest.java114
-rw-r--r--packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java19
-rw-r--r--packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java40
-rw-r--r--services/Android.bp4
-rw-r--r--services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java27
-rw-r--r--services/appprediction/java/com/android/server/appprediction/RemoteAppPredictionService.java7
-rw-r--r--services/autofill/java/com/android/server/autofill/AutofillInlineSuggestionsRequestSession.java4
-rw-r--r--services/autofill/java/com/android/server/autofill/RemoteInlineSuggestionRenderService.java2
-rw-r--r--services/autofill/java/com/android/server/autofill/ui/InlineContentProviderImpl.java140
-rw-r--r--services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java102
-rw-r--r--services/autofill/java/com/android/server/autofill/ui/RemoteInlineSuggestionUi.java291
-rw-r--r--services/autofill/java/com/android/server/autofill/ui/RemoteInlineSuggestionViewConnector.java125
-rw-r--r--services/core/java/com/android/server/RescueParty.java27
-rw-r--r--services/core/java/com/android/server/ServiceWatcher.java6
-rw-r--r--services/core/java/com/android/server/StorageManagerService.java36
-rw-r--r--services/core/java/com/android/server/UiModeManagerService.java46
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java32
-rw-r--r--services/core/java/com/android/server/am/CachedAppOptimizer.java11
-rw-r--r--services/core/java/com/android/server/appop/TEST_MAPPING7
-rwxr-xr-xservices/core/java/com/android/server/audio/AudioService.java4
-rw-r--r--services/core/java/com/android/server/display/DisplayPowerController.java22
-rw-r--r--services/core/java/com/android/server/display/DisplayPowerState.java2
-rw-r--r--services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java2
-rw-r--r--services/core/java/com/android/server/location/LocationManagerService.java12
-rw-r--r--services/core/java/com/android/server/location/LocationRequestStatistics.java22
-rw-r--r--services/core/java/com/android/server/location/LocationShellCommand.java92
-rw-r--r--services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java19
-rw-r--r--services/core/java/com/android/server/net/NetworkPolicyManagerService.java59
-rw-r--r--services/core/java/com/android/server/notification/NotificationRecord.java12
-rw-r--r--services/core/java/com/android/server/pm/ApexManager.java2
-rw-r--r--services/core/java/com/android/server/pm/AppsFilter.java32
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerService.java9
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerSession.java89
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java28
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerShellCommand.java19
-rw-r--r--services/core/java/com/android/server/pm/StagingManager.java176
-rw-r--r--services/core/java/com/android/server/pm/TEST_MAPPING3
-rw-r--r--services/core/java/com/android/server/pm/UserManagerService.java36
-rw-r--r--services/core/java/com/android/server/pm/UserRestrictionsUtils.java1
-rw-r--r--services/core/java/com/android/server/pm/UserSystemPackageInstaller.java171
-rw-r--r--services/core/java/com/android/server/pm/parsing/PackageParser2.java16
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerService.java7
-rw-r--r--services/core/java/com/android/server/policy/PhoneWindowManager.java10
-rw-r--r--services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java15
-rw-r--r--services/core/java/com/android/server/stats/pull/StatsPullAtomService.java8
-rwxr-xr-xservices/core/java/com/android/server/tv/TvInputHardwareManager.java51
-rw-r--r--services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java74
-rw-r--r--services/core/java/com/android/server/uri/UriGrantsManagerService.java7
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java8
-rw-r--r--services/core/java/com/android/server/wm/ActivityStack.java72
-rw-r--r--services/core/java/com/android/server/wm/ActivityStackSupervisor.java62
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java11
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerService.java14
-rw-r--r--services/core/java/com/android/server/wm/AppTransitionController.java20
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java3
-rw-r--r--services/core/java/com/android/server/wm/DisplayPolicy.java10
-rw-r--r--services/core/java/com/android/server/wm/RootWindowContainer.java21
-rw-r--r--services/core/java/com/android/server/wm/Task.java27
-rw-r--r--services/core/java/com/android/server/wm/TaskDisplayArea.java40
-rw-r--r--services/core/java/com/android/server/wm/TaskTapPointerEventListener.java22
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java14
-rw-r--r--services/core/java/com/android/server/wm/WindowStateAnimator.java22
-rw-r--r--services/core/jni/com_android_server_input_InputManagerService.cpp62
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java10
-rw-r--r--services/java/com/android/server/SystemServer.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java36
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/PackageInstallerSessionTest.java1
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java13
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/dex/DexMetadataHelperTest.java14
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingTestBase.kt24
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/parsing/PackageParserLegacyCoreTest.java17
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/parsing/PackageParsingDeferErrorTest.kt17
-rw-r--r--services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java26
-rw-r--r--services/tests/servicestests/src/com/android/server/uri/UriGrantsManagerServiceTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java64
-rw-r--r--services/tests/uiservicestests/src/com/android/server/UiServiceTestCase.java3
-rwxr-xr-xservices/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java49
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java24
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java2
-rwxr-xr-xtelecomm/java/android/telecom/ConnectionService.java58
-rw-r--r--telecomm/java/android/telecom/Log.java44
-rw-r--r--telecomm/java/android/telecom/Logging/Session.java40
-rw-r--r--telecomm/java/android/telecom/Logging/SessionManager.java16
-rw-r--r--telecomm/java/android/telecom/RemoteConnection.java83
-rw-r--r--telephony/common/com/android/internal/telephony/CellBroadcastUtils.java65
-rw-r--r--wifi/Android.bp29
-rw-r--r--wifi/api/lint-baseline.txt13
-rw-r--r--wifi/api/system-lint-baseline.txt6
281 files changed, 6197 insertions, 2236 deletions
diff --git a/StubLibraries.bp b/StubLibraries.bp
index 170cac43764a..50524998d416 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -48,7 +48,6 @@ stubs_defaults {
":opt-telephony-srcs",
":opt-net-voip-srcs",
":art-module-public-api-stubs-source",
- ":conscrypt.module.public.api.stubs.source",
":android_icu4j_public_api_files",
],
// TODO(b/147699819): remove below aidl includes.
@@ -69,7 +68,10 @@ stubs_defaults {
stubs_defaults {
name: "metalava-full-api-stubs-default",
defaults: ["metalava-base-api-stubs-default"],
- srcs: [":framework-updatable-sources"],
+ srcs: [
+ ":conscrypt.module.public.api.stubs.source",
+ ":framework-updatable-sources",
+ ],
sdk_version: "core_platform",
}
@@ -110,7 +112,7 @@ droidstubs {
},
last_released: {
api_file: ":android.api.public.latest",
- removed_api_file: "api/removed.txt",
+ removed_api_file: ":removed.api.public.latest",
baseline_file: ":public-api-incompatibilities-with-last-released",
},
api_lint: {
@@ -152,7 +154,7 @@ droidstubs {
},
last_released: {
api_file: ":android.api.system.latest",
- removed_api_file: "api/system-removed.txt",
+ removed_api_file: ":removed.api.system.latest",
baseline_file: ":system-api-incompatibilities-with-last-released"
},
api_lint: {
@@ -216,7 +218,7 @@ droidstubs {
},
last_released: {
api_file: ":android.api.module-lib.latest",
- removed_api_file: "api/module-lib-removed.txt",
+ removed_api_file: ":removed.api.module-lib.latest",
baseline_file: ":module-lib-api-incompatibilities-with-last-released"
},
api_lint: {
diff --git a/apct-tests/perftests/core/AndroidTest.xml b/apct-tests/perftests/core/AndroidTest.xml
index 478cfc1fe811..1b289130124f 100644
--- a/apct-tests/perftests/core/AndroidTest.xml
+++ b/apct-tests/perftests/core/AndroidTest.xml
@@ -25,9 +25,4 @@
<option name="package" value="com.android.perftests.core" />
<option name="hidden-api-checks" value="false"/>
</test>
-
- <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
- <option name="directory-keys" value="/data/local/CorePerfTests" />
- <option name="collect-on-run-ended-only" value="true" />
- </metrics_collector>
</configuration>
diff --git a/apct-tests/perftests/windowmanager/Android.bp b/apct-tests/perftests/windowmanager/Android.bp
new file mode 100644
index 000000000000..f02cbcfc4daf
--- /dev/null
+++ b/apct-tests/perftests/windowmanager/Android.bp
@@ -0,0 +1,26 @@
+// 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 {
+ name: "WmPerfTests",
+ srcs: ["src/**/*.java"],
+ static_libs: [
+ "androidx.test.rules",
+ "androidx.annotation_annotation",
+ "apct-perftests-utils",
+ ],
+ test_suites: ["device-tests"],
+ platform_apis: true,
+ certificate: "platform",
+}
diff --git a/apct-tests/perftests/windowmanager/AndroidManifest.xml b/apct-tests/perftests/windowmanager/AndroidManifest.xml
new file mode 100644
index 000000000000..7198176f62a0
--- /dev/null
+++ b/apct-tests/perftests/windowmanager/AndroidManifest.xml
@@ -0,0 +1,32 @@
+<?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.perftests.wm">
+
+ <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
+
+ <application>
+ <uses-library android:name="android.test.runner" />
+ <activity android:name="android.perftests.utils.PerfTestActivity">
+ <intent-filter>
+ <action android:name="com.android.perftests.core.PERFTEST" />
+ </intent-filter>
+ </activity>
+ </application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.perftests.wm"/>
+</manifest>
diff --git a/apct-tests/perftests/windowmanager/AndroidTest.xml b/apct-tests/perftests/windowmanager/AndroidTest.xml
new file mode 100644
index 000000000000..69d187f9419a
--- /dev/null
+++ b/apct-tests/perftests/windowmanager/AndroidTest.xml
@@ -0,0 +1,33 @@
+<?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="Runs WmPerfTests metric instrumentation.">
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="apct-metric-instrumentation" />
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="WmPerfTests.apk" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="com.android.perftests.wm" />
+ <option name="hidden-api-checks" value="false"/>
+ </test>
+
+ <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
+ <option name="directory-keys" value="/data/local/WmPerfTests" />
+ <option name="collect-on-run-ended-only" value="true" />
+ </metrics_collector>
+</configuration>
diff --git a/apct-tests/perftests/core/src/android/wm/InternalWindowOperationPerfTest.java b/apct-tests/perftests/windowmanager/src/android/wm/InternalWindowOperationPerfTest.java
index 4ed3b4e09d11..4ed3b4e09d11 100644
--- a/apct-tests/perftests/core/src/android/wm/InternalWindowOperationPerfTest.java
+++ b/apct-tests/perftests/windowmanager/src/android/wm/InternalWindowOperationPerfTest.java
diff --git a/apct-tests/perftests/core/src/android/wm/RecentsAnimationPerfTest.java b/apct-tests/perftests/windowmanager/src/android/wm/RecentsAnimationPerfTest.java
index 836e6b617395..1667c1658a07 100644
--- a/apct-tests/perftests/core/src/android/wm/RecentsAnimationPerfTest.java
+++ b/apct-tests/perftests/windowmanager/src/android/wm/RecentsAnimationPerfTest.java
@@ -46,6 +46,7 @@ import androidx.test.runner.lifecycle.Stage;
import org.junit.AfterClass;
import org.junit.Assume;
+import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
@@ -72,6 +73,12 @@ public class RecentsAnimationPerfTest extends WindowManagerPerfTestBase {
private long mMeasuredTimeNs;
+ /**
+ * Used to skip each test method if there is error. It cannot be raised in static setup because
+ * that will break the amount of target method count.
+ */
+ private static Exception sSetUpClassException;
+
@Parameterized.Parameter(0)
public int intervalBetweenOperations;
@@ -107,15 +114,21 @@ public class RecentsAnimationPerfTest extends WindowManagerPerfTestBase {
sRecentsIntent =
new Intent().setComponent(homeIsRecents ? defaultHome : recentsComponent);
} catch (Exception e) {
- Assume.assumeNoException(e);
+ sSetUpClassException = e;
}
}
@AfterClass
public static void tearDownClass() {
+ sSetUpClassException = null;
sUiAutomation.dropShellPermissionIdentity();
}
+ @Before
+ public void setUp() {
+ Assume.assumeNoException(sSetUpClassException);
+ }
+
/** Simulate the timing of touch. */
private void makeInterval() {
SystemClock.sleep(intervalBetweenOperations);
diff --git a/apct-tests/perftests/core/src/android/wm/RelayoutPerfTest.java b/apct-tests/perftests/windowmanager/src/android/wm/RelayoutPerfTest.java
index 8139a2e963c5..8139a2e963c5 100644
--- a/apct-tests/perftests/core/src/android/wm/RelayoutPerfTest.java
+++ b/apct-tests/perftests/windowmanager/src/android/wm/RelayoutPerfTest.java
diff --git a/apct-tests/perftests/core/src/android/wm/WindowAddRemovePerfTest.java b/apct-tests/perftests/windowmanager/src/android/wm/WindowAddRemovePerfTest.java
index c72cc9d635e0..c72cc9d635e0 100644
--- a/apct-tests/perftests/core/src/android/wm/WindowAddRemovePerfTest.java
+++ b/apct-tests/perftests/windowmanager/src/android/wm/WindowAddRemovePerfTest.java
diff --git a/apct-tests/perftests/core/src/android/wm/WindowManagerPerfTestBase.java b/apct-tests/perftests/windowmanager/src/android/wm/WindowManagerPerfTestBase.java
index 9e17e940a06b..9e17e940a06b 100644
--- a/apct-tests/perftests/core/src/android/wm/WindowManagerPerfTestBase.java
+++ b/apct-tests/perftests/windowmanager/src/android/wm/WindowManagerPerfTestBase.java
diff --git a/apex/Android.bp b/apex/Android.bp
index 67cd0d7fcd1e..51e030bd174d 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -74,6 +74,9 @@ stubs_defaults {
api_file: "api/current.txt",
removed_api_file: "api/removed.txt",
},
+ api_lint: {
+ enabled: true,
+ },
},
dist: {
targets: ["sdk", "win_sdk"],
@@ -93,6 +96,9 @@ stubs_defaults {
api_file: "api/system-current.txt",
removed_api_file: "api/system-removed.txt",
},
+ api_lint: {
+ enabled: true,
+ },
},
dist: {
targets: ["sdk", "win_sdk"],
@@ -147,6 +153,9 @@ stubs_defaults {
api_file: "api/module-lib-current.txt",
removed_api_file: "api/module-lib-removed.txt",
},
+ api_lint: {
+ enabled: true,
+ },
},
dist: {
targets: ["sdk", "win_sdk"],
@@ -173,6 +182,9 @@ stubs_defaults {
api_file: "api/current.txt",
removed_api_file: "api/removed.txt",
},
+ api_lint: {
+ enabled: true,
+ },
},
dist: {
targets: ["sdk", "win_sdk"],
diff --git a/apex/extservices/Android.bp b/apex/extservices/Android.bp
index 68350afdac85..0c6c4c23dce1 100644
--- a/apex/extservices/Android.bp
+++ b/apex/extservices/Android.bp
@@ -21,7 +21,7 @@ apex {
apex_defaults {
name: "com.android.extservices-defaults",
updatable: true,
- min_sdk_version: "R",
+ min_sdk_version: "current",
key: "com.android.extservices.key",
certificate: ":com.android.extservices.certificate",
apps: ["ExtServices"],
diff --git a/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java b/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java
index 6d9e3eddf616..887d82c6413f 100644
--- a/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java
+++ b/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java
@@ -45,6 +45,14 @@ public interface AppStandbyInternal {
boolean idle, int bucket, int reason);
/**
+ * Callback to inform listeners that the parole state has changed. This means apps are
+ * allowed to do work even if they're idle or in a low bucket.
+ */
+ public void onParoleStateChanged(boolean isParoleOn) {
+ // No-op by default
+ }
+
+ /**
* Optional callback to inform the listener that the app has transitioned into
* an active state due to user interaction.
*/
@@ -92,6 +100,11 @@ public interface AppStandbyInternal {
boolean isAppIdleFiltered(String packageName, int appId, int userId,
long elapsedRealtime);
+ /**
+ * @return true if currently app idle parole mode is on.
+ */
+ boolean isInParole();
+
int[] getIdleUidsForUser(int userId);
void setAppIdleAsync(String packageName, boolean idle, int userId);
diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
index 24728dd8edca..cb5cb175ff24 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
+++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
@@ -214,8 +214,7 @@ public class AppStandbyController implements AppStandbyInternal {
private AppIdleHistory mAppIdleHistory;
@GuardedBy("mPackageAccessListeners")
- private ArrayList<AppIdleStateChangeListener>
- mPackageAccessListeners = new ArrayList<>();
+ private final ArrayList<AppIdleStateChangeListener> mPackageAccessListeners = new ArrayList<>();
/** Whether we've queried the list of carrier privileged apps. */
@GuardedBy("mAppIdleLock")
@@ -235,6 +234,7 @@ public class AppStandbyController implements AppStandbyInternal {
static final int MSG_FORCE_IDLE_STATE = 4;
static final int MSG_CHECK_IDLE_STATES = 5;
static final int MSG_REPORT_CONTENT_PROVIDER_USAGE = 8;
+ static final int MSG_PAROLE_STATE_CHANGED = 9;
static final int MSG_ONE_TIME_CHECK_IDLE_STATES = 10;
/** Check the state of one app: arg1 = userId, arg2 = uid, obj = (String) packageName */
static final int MSG_CHECK_PACKAGE_IDLE_STATE = 11;
@@ -390,7 +390,16 @@ public class AppStandbyController implements AppStandbyInternal {
@VisibleForTesting
void setAppIdleEnabled(boolean enabled) {
- mAppIdleEnabled = enabled;
+ synchronized (mAppIdleLock) {
+ if (mAppIdleEnabled != enabled) {
+ final boolean oldParoleState = isInParole();
+ mAppIdleEnabled = enabled;
+ if (isInParole() != oldParoleState) {
+ postParoleStateChanged();
+ }
+ }
+ }
+
}
@Override
@@ -563,11 +572,23 @@ public class AppStandbyController implements AppStandbyInternal {
if (mIsCharging != isCharging) {
if (DEBUG) Slog.d(TAG, "Setting mIsCharging to " + isCharging);
mIsCharging = isCharging;
+ postParoleStateChanged();
}
}
}
@Override
+ public boolean isInParole() {
+ return !mAppIdleEnabled || mIsCharging;
+ }
+
+ private void postParoleStateChanged() {
+ if (DEBUG) Slog.d(TAG, "Posting MSG_PAROLE_STATE_CHANGED");
+ mHandler.removeMessages(MSG_PAROLE_STATE_CHANGED);
+ mHandler.sendEmptyMessage(MSG_PAROLE_STATE_CHANGED);
+ }
+
+ @Override
public void postCheckIdleStates(int userId) {
mHandler.sendMessage(mHandler.obtainMessage(MSG_CHECK_IDLE_STATES, userId, 0));
}
@@ -1502,6 +1523,15 @@ public class AppStandbyController implements AppStandbyInternal {
}
}
+ private void informParoleStateChanged() {
+ final boolean paroled = isInParole();
+ synchronized (mPackageAccessListeners) {
+ for (AppIdleStateChangeListener listener : mPackageAccessListeners) {
+ listener.onParoleStateChanged(paroled);
+ }
+ }
+ }
+
@Override
public void flushToDisk(int userId) {
synchronized (mAppIdleLock) {
@@ -1920,6 +1950,11 @@ public class AppStandbyController implements AppStandbyInternal {
args.recycle();
break;
+ case MSG_PAROLE_STATE_CHANGED:
+ if (DEBUG) Slog.d(TAG, "Parole state: " + isInParole());
+ informParoleStateChanged();
+ break;
+
case MSG_CHECK_PACKAGE_IDLE_STATE:
checkAndUpdateStandbyState((String) msg.obj, msg.arg1, msg.arg2,
mInjector.elapsedRealtime());
diff --git a/apex/media/framework/Android.bp b/apex/media/framework/Android.bp
index a1c886a26562..3bc4f7b0ab72 100644
--- a/apex/media/framework/Android.bp
+++ b/apex/media/framework/Android.bp
@@ -102,6 +102,15 @@ droidstubs {
"framework-media-stubs-srcs-defaults",
"framework-module-stubs-defaults-publicapi",
],
+ check_api: {
+ last_released: {
+ api_file: ":framework-media.api.public.latest",
+ removed_api_file: ":framework-media-removed.api.public.latest",
+ },
+ api_lint: {
+ new_since: ":framework-media.api.public.latest",
+ },
+ },
}
droidstubs {
@@ -110,6 +119,15 @@ droidstubs {
"framework-media-stubs-srcs-defaults",
"framework-module-stubs-defaults-systemapi",
],
+ check_api: {
+ last_released: {
+ api_file: ":framework-media.api.system.latest",
+ removed_api_file: ":framework-media-removed.api.system.latest",
+ },
+ api_lint: {
+ new_since: ":framework-media.api.system.latest",
+ },
+ },
}
droidstubs {
@@ -118,6 +136,15 @@ droidstubs {
"framework-media-stubs-srcs-defaults",
"framework-module-api-defaults-module_libs_api",
],
+ check_api: {
+ last_released: {
+ api_file: ":framework-media.api.module-lib.latest",
+ removed_api_file: ":framework-media-removed.api.module-lib.latest",
+ },
+ api_lint: {
+ new_since: ":framework-media.api.module-lib.latest",
+ },
+ },
}
droidstubs {
diff --git a/apex/permission/framework/Android.bp b/apex/permission/framework/Android.bp
index 3119b7d29b36..68c27a8327cb 100644
--- a/apex/permission/framework/Android.bp
+++ b/apex/permission/framework/Android.bp
@@ -55,6 +55,15 @@ droidstubs {
"framework-module-stubs-defaults-publicapi",
"framework-permission-stubs-defaults",
],
+ check_api: {
+ last_released: {
+ api_file: ":framework-permission.api.public.latest",
+ removed_api_file: ":framework-permission-removed.api.public.latest",
+ },
+ api_lint: {
+ new_since: ":framework-permission.api.public.latest",
+ },
+ },
}
droidstubs {
@@ -63,6 +72,15 @@ droidstubs {
"framework-module-stubs-defaults-systemapi",
"framework-permission-stubs-defaults",
],
+ check_api: {
+ last_released: {
+ api_file: ":framework-permission.api.system.latest",
+ removed_api_file: ":framework-permission-removed.api.system.latest",
+ },
+ api_lint: {
+ new_since: ":framework-permission.api.system.latest",
+ },
+ },
}
droidstubs {
@@ -71,6 +89,15 @@ droidstubs {
"framework-module-api-defaults-module_libs_api",
"framework-permission-stubs-defaults",
],
+ check_api: {
+ last_released: {
+ api_file: ":framework-permission.api.module-lib.latest",
+ removed_api_file: ":framework-permission-removed.api.module-lib.latest",
+ },
+ api_lint: {
+ new_since: ":framework-permission.api.module-lib.latest",
+ },
+ },
}
droidstubs {
diff --git a/apex/permission/service/Android.bp b/apex/permission/service/Android.bp
index 2d92d00b6309..61449763540b 100644
--- a/apex/permission/service/Android.bp
+++ b/apex/permission/service/Android.bp
@@ -41,6 +41,15 @@ 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" },
}
diff --git a/apex/sdkextensions/framework/Android.bp b/apex/sdkextensions/framework/Android.bp
index 6a787116c005..14e23ed9a8a1 100644
--- a/apex/sdkextensions/framework/Android.bp
+++ b/apex/sdkextensions/framework/Android.bp
@@ -57,6 +57,15 @@ droidstubs {
"framework-module-stubs-defaults-publicapi",
"framework-sdkextensions-stubs-defaults",
],
+ check_api: {
+ last_released: {
+ api_file: ":framework-sdkextensions.api.public.latest",
+ removed_api_file: ":framework-sdkextensions-removed.api.public.latest",
+ },
+ api_lint: {
+ new_since: ":framework-sdkextensions.api.public.latest",
+ },
+ },
}
droidstubs {
@@ -65,6 +74,15 @@ droidstubs {
"framework-module-stubs-defaults-systemapi",
"framework-sdkextensions-stubs-defaults",
],
+ check_api: {
+ last_released: {
+ api_file: ":framework-sdkextensions.api.system.latest",
+ removed_api_file: ":framework-sdkextensions-removed.api.system.latest",
+ },
+ api_lint: {
+ new_since: ":framework-sdkextensions.api.system.latest",
+ },
+ },
}
droidstubs {
@@ -73,6 +91,15 @@ droidstubs {
"framework-module-api-defaults-module_libs_api",
"framework-sdkextensions-stubs-defaults",
],
+ check_api: {
+ last_released: {
+ api_file: ":framework-sdkextensions.api.module-lib.latest",
+ removed_api_file: ":framework-sdkextensions-removed.api.module-lib.latest",
+ },
+ api_lint: {
+ new_since: ":framework-sdkextensions.api.module-lib.latest",
+ },
+ },
}
droidstubs {
diff --git a/apex/statsd/framework/Android.bp b/apex/statsd/framework/Android.bp
index 7d0f2ee274cd..9f5d933bb48a 100644
--- a/apex/statsd/framework/Android.bp
+++ b/apex/statsd/framework/Android.bp
@@ -93,6 +93,15 @@ droidstubs {
"framework-module-stubs-defaults-publicapi",
"framework-statsd-stubs-srcs-defaults",
],
+ check_api: {
+ last_released: {
+ api_file: ":framework-statsd.api.public.latest",
+ removed_api_file: ":framework-statsd-removed.api.public.latest",
+ },
+ api_lint: {
+ new_since: ":framework-statsd.api.public.latest",
+ },
+ },
}
droidstubs {
@@ -101,6 +110,15 @@ droidstubs {
"framework-module-stubs-defaults-systemapi",
"framework-statsd-stubs-srcs-defaults",
],
+ check_api: {
+ last_released: {
+ api_file: ":framework-statsd.api.system.latest",
+ removed_api_file: ":framework-statsd-removed.api.system.latest",
+ },
+ api_lint: {
+ new_since: ":framework-statsd.api.system.latest",
+ },
+ },
}
droidstubs {
@@ -109,6 +127,15 @@ droidstubs {
"framework-module-api-defaults-module_libs_api",
"framework-statsd-stubs-srcs-defaults",
],
+ check_api: {
+ last_released: {
+ api_file: ":framework-statsd.api.module-lib.latest",
+ removed_api_file: ":framework-statsd-removed.api.module-lib.latest",
+ },
+ api_lint: {
+ new_since: ":framework-statsd.api.module-lib.latest",
+ },
+ },
}
droidstubs {
diff --git a/apex/statsd/framework/java/android/app/StatsManager.java b/apex/statsd/framework/java/android/app/StatsManager.java
index 7fbfc4318949..d1b7d8dc2c7a 100644
--- a/apex/statsd/framework/java/android/app/StatsManager.java
+++ b/apex/statsd/framework/java/android/app/StatsManager.java
@@ -28,7 +28,6 @@ import android.os.Binder;
import android.os.IPullAtomCallback;
import android.os.IPullAtomResultReceiver;
import android.os.IStatsManagerService;
-import android.os.IStatsd;
import android.os.RemoteException;
import android.os.StatsFrameworkInitializer;
import android.util.AndroidException;
@@ -57,9 +56,6 @@ public final class StatsManager {
private final Context mContext;
@GuardedBy("sLock")
- private IStatsd mService;
-
- @GuardedBy("sLock")
private IStatsManagerService mStatsManagerService;
/**
diff --git a/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java b/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java
index 93e6c108a289..5cf5e0b1d182 100644
--- a/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java
+++ b/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java
@@ -54,11 +54,11 @@ import java.io.FileDescriptor;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
-import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
/**
* Helper service for statsd (the native stats management service in cmds/statsd/).
@@ -112,17 +112,8 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
private final HashMap<Long, String> mDeletedFiles = new HashMap<>();
private final CompanionHandler mHandler;
- // Flag that is set when PHASE_BOOT_COMPLETED is triggered in the StatsCompanion lifecycle. This
- // and the flag mSentBootComplete below is used for synchronization to ensure that the boot
- // complete signal is only ever sent once to statsd. Two signals are needed because
- // #sayHiToStatsd can be called from both statsd and #onBootPhase
- // PHASE_THIRD_PARTY_APPS_CAN_START.
- @GuardedBy("sStatsdLock")
- private boolean mBootCompleted = false;
- // Flag that is set when IStatsd#bootCompleted is called. This flag ensures that boot complete
- // signal is only ever sent once.
- @GuardedBy("sStatsdLock")
- private boolean mSentBootComplete = false;
+ // Flag that is set when PHASE_BOOT_COMPLETED is triggered in the StatsCompanion lifecycle.
+ private AtomicBoolean mBootCompleted = new AtomicBoolean(false);
public StatsCompanionService(Context context) {
super();
@@ -607,27 +598,35 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
// Statsd related code
/**
- * Fetches the statsd IBinder service. This is a blocking call.
+ * Fetches the statsd IBinder service. This is a blocking call that always refetches statsd
+ * instead of returning the cached sStatsd.
* Note: This should only be called from {@link #sayHiToStatsd()}. All other clients should use
* the cached sStatsd via {@link #getStatsdNonblocking()}.
*/
- private IStatsd fetchStatsdService(StatsdDeathRecipient deathRecipient) {
- synchronized (sStatsdLock) {
- if (sStatsd == null) {
- sStatsd = IStatsd.Stub.asInterface(StatsFrameworkInitializer
- .getStatsServiceManager()
- .getStatsdServiceRegisterer()
- .get());
- if (sStatsd != null) {
- try {
- sStatsd.asBinder().linkToDeath(deathRecipient, /* flags */ 0);
- } catch (RemoteException e) {
- Log.e(TAG, "linkToDeath(StatsdDeathRecipient) failed");
- statsdNotReadyLocked();
- }
+ private IStatsd fetchStatsdServiceLocked() {
+ sStatsd = IStatsd.Stub.asInterface(StatsFrameworkInitializer
+ .getStatsServiceManager()
+ .getStatsdServiceRegisterer()
+ .get());
+ return sStatsd;
+ }
+
+ private void registerStatsdDeathRecipient(IStatsd statsd, List<BroadcastReceiver> receivers) {
+ StatsdDeathRecipient deathRecipient = new StatsdDeathRecipient(statsd, receivers);
+
+ try {
+ statsd.asBinder().linkToDeath(deathRecipient, /*flags=*/0);
+ } catch (RemoteException e) {
+ Log.e(TAG, "linkToDeath (StatsdDeathRecipient) failed");
+ // Statsd has already died. Unregister receivers ourselves.
+ for (BroadcastReceiver receiver : receivers) {
+ mContext.unregisterReceiver(receiver);
+ }
+ synchronized (sStatsdLock) {
+ if (statsd == sStatsd) {
+ statsdNotReadyLocked();
}
}
- return sStatsd;
}
}
@@ -648,22 +647,23 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
* statsd.
*/
private void sayHiToStatsd() {
- if (getStatsdNonblocking() != null) {
- Log.e(TAG, "Trying to fetch statsd, but it was already fetched",
- new IllegalStateException(
- "sStatsd is not null when being fetched"));
- return;
+ IStatsd statsd;
+ synchronized (sStatsdLock) {
+ if (sStatsd != null && sStatsd.asBinder().isBinderAlive()) {
+ Log.e(TAG, "statsd has already been fetched before",
+ new IllegalStateException("IStatsd object should be null or dead"));
+ return;
+ }
+ statsd = fetchStatsdServiceLocked();
}
- StatsdDeathRecipient deathRecipient = new StatsdDeathRecipient();
- IStatsd statsd = fetchStatsdService(deathRecipient);
+
if (statsd == null) {
- Log.i(TAG,
- "Could not yet find statsd to tell it that StatsCompanion is "
- + "alive.");
+ Log.i(TAG, "Could not yet find statsd to tell it that StatsCompanion is alive.");
return;
}
- mStatsManagerService.statsdReady(statsd);
+
if (DEBUG) Log.d(TAG, "Saying hi to statsd");
+ mStatsManagerService.statsdReady(statsd);
try {
statsd.statsCompanionReady();
@@ -682,8 +682,7 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
mContext.registerReceiverForAllUsers(appUpdateReceiver, filter, null, null);
// Setup receiver for user initialize (which happens once for a new user)
- // and
- // if a user is removed.
+ // and if a user is removed.
filter = new IntentFilter(Intent.ACTION_USER_INITIALIZE);
filter.addAction(Intent.ACTION_USER_REMOVED);
mContext.registerReceiverForAllUsers(userUpdateReceiver, filter, null, null);
@@ -691,27 +690,20 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
// Setup receiver for device reboots or shutdowns.
filter = new IntentFilter(Intent.ACTION_REBOOT);
filter.addAction(Intent.ACTION_SHUTDOWN);
- mContext.registerReceiverForAllUsers(
- shutdownEventReceiver, filter, null, null);
+ mContext.registerReceiverForAllUsers(shutdownEventReceiver, filter, null, null);
- // Only add the receivers if the registration is successful.
- deathRecipient.addRegisteredBroadcastReceivers(
- List.of(appUpdateReceiver, userUpdateReceiver, shutdownEventReceiver));
+ // Register death recipient.
+ List<BroadcastReceiver> broadcastReceivers =
+ List.of(appUpdateReceiver, userUpdateReceiver, shutdownEventReceiver);
+ registerStatsdDeathRecipient(statsd, broadcastReceivers);
- // Used so we can call statsd.bootComplete() outside of the lock.
- boolean shouldSendBootComplete = false;
- synchronized (sStatsdLock) {
- if (mBootCompleted && !mSentBootComplete) {
- mSentBootComplete = true;
- shouldSendBootComplete = true;
- }
- }
- if (shouldSendBootComplete) {
+ // Tell statsd that boot has completed. The signal may have already been sent, but since
+ // the signal-receiving function is idempotent, that's ok.
+ if (mBootCompleted.get()) {
statsd.bootCompleted();
}
- // Pull the latest state of UID->app name, version mapping when
- // statsd starts.
+ // Pull the latest state of UID->app name, version mapping when statsd starts.
informAllUids(mContext);
Log.i(TAG, "Told statsd that StatsCompanionService is alive.");
@@ -722,18 +714,16 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
private class StatsdDeathRecipient implements IBinder.DeathRecipient {
- private List<BroadcastReceiver> mReceiversToUnregister;
-
- StatsdDeathRecipient() {
- mReceiversToUnregister = new ArrayList<>();
- }
+ private final IStatsd mStatsd;
+ private final List<BroadcastReceiver> mReceiversToUnregister;
- public void addRegisteredBroadcastReceivers(List<BroadcastReceiver> receivers) {
- synchronized (sStatsdLock) {
- mReceiversToUnregister.addAll(receivers);
- }
+ StatsdDeathRecipient(IStatsd statsd, List<BroadcastReceiver> receivers) {
+ mStatsd = statsd;
+ mReceiversToUnregister = receivers;
}
+ // It is possible for binderDied to be called after a restarted statsd calls statsdReady,
+ // but that's alright because the code does not assume an ordering of the two calls.
@Override
public void binderDied() {
Log.i(TAG, "Statsd is dead - erase all my knowledge, except pullers");
@@ -762,13 +752,19 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
}
}
}
- // We only unregister in binder death becaseu receivers can only be unregistered
- // once, or an IllegalArgumentException is thrown.
+
+ // Unregister receivers on death because receivers can only be unregistered once.
+ // Otherwise, an IllegalArgumentException is thrown.
for (BroadcastReceiver receiver: mReceiversToUnregister) {
mContext.unregisterReceiver(receiver);
}
- statsdNotReadyLocked();
- mSentBootComplete = false;
+
+ // It's possible for statsd to have restarted and called statsdReady, causing a new
+ // sStatsd binder object to be fetched, before the binderDied callback runs. Only
+ // call #statsdNotReadyLocked if that hasn't happened yet.
+ if (mStatsd == sStatsd) {
+ statsdNotReadyLocked();
+ }
}
}
}
@@ -779,19 +775,12 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
}
void bootCompleted() {
+ mBootCompleted.set(true);
IStatsd statsd = getStatsdNonblocking();
- synchronized (sStatsdLock) {
- mBootCompleted = true;
- if (mSentBootComplete) {
- // do not send a boot complete a second time.
- return;
- }
- if (statsd == null) {
- // Statsd is not yet ready.
- // Delay the boot completed ping to {@link #sayHiToStatsd()}
- return;
- }
- mSentBootComplete = true;
+ if (statsd == null) {
+ // Statsd is not yet ready.
+ // Delay the boot completed ping to {@link #sayHiToStatsd()}
+ return;
}
try {
statsd.bootCompleted();
@@ -808,8 +797,7 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
}
synchronized (sStatsdLock) {
- writer.println(
- "Number of configuration files deleted: " + mDeletedFiles.size());
+ writer.println("Number of configuration files deleted: " + mDeletedFiles.size());
if (mDeletedFiles.size() > 0) {
writer.println(" timestamp, deleted file name");
}
@@ -817,8 +805,7 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
SystemClock.currentThreadTimeMillis() - SystemClock.elapsedRealtime();
for (Long elapsedMillis : mDeletedFiles.keySet()) {
long deletionMillis = lastBootMillis + elapsedMillis;
- writer.println(
- " " + deletionMillis + ", " + mDeletedFiles.get(elapsedMillis));
+ writer.println(" " + deletionMillis + ", " + mDeletedFiles.get(elapsedMillis));
}
}
}
diff --git a/apex/statsd/service/java/com/android/server/stats/StatsManagerService.java b/apex/statsd/service/java/com/android/server/stats/StatsManagerService.java
index 90764b0bd426..97846f2397a5 100644
--- a/apex/statsd/service/java/com/android/server/stats/StatsManagerService.java
+++ b/apex/statsd/service/java/com/android/server/stats/StatsManagerService.java
@@ -172,6 +172,10 @@ public class StatsManagerService extends IStatsManagerService.Stub {
public void registerPullAtomCallback(int atomTag, long coolDownMillis, long timeoutMillis,
int[] additiveFields, IPullAtomCallback pullerCallback) {
enforceRegisterStatsPullAtomPermission();
+ if (pullerCallback == null) {
+ Log.w(TAG, "Puller callback is null for atom " + atomTag);
+ return;
+ }
int callingUid = Binder.getCallingUid();
PullerKey key = new PullerKey(callingUid, atomTag);
PullerValue val =
diff --git a/api/test-current.txt b/api/test-current.txt
index cc3604ce728c..46049bd949c5 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -431,6 +431,7 @@ package android.app {
}
public class DreamManager {
+ method @RequiresPermission("android.permission.READ_DREAM_STATE") public boolean isDreaming();
method @RequiresPermission("android.permission.WRITE_DREAM_STATE") public void setActiveDream(@NonNull android.content.ComponentName);
method @RequiresPermission("android.permission.WRITE_DREAM_STATE") public void startDream(@NonNull android.content.ComponentName);
method @RequiresPermission("android.permission.WRITE_DREAM_STATE") public void stopDream();
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index bd9f7a59fcbd..a65f5f792daa 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -343,9 +343,11 @@ status_t StatsService::handleShellCommand(int in, int out, int err, const char**
if (!utf8Args[0].compare(String8("print-logs"))) {
return cmd_print_logs(out, utf8Args);
}
+
if (!utf8Args[0].compare(String8("send-active-configs"))) {
return cmd_trigger_active_config_broadcast(out, utf8Args);
}
+
if (!utf8Args[0].compare(String8("data-subscribe"))) {
{
std::lock_guard<std::mutex> lock(mShellSubscriberMutex);
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 650545f939cc..504890f6cf52 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -3021,14 +3021,14 @@ message LauncherUIChanged {
optional string component_name = 11;
// (x, y) coordinate and the index information of the target on the container
- optional int32 grid_x = 12;
- optional int32 grid_y = 13;
- optional int32 page_id = 14;
+ optional int32 grid_x = 12 [default = -1];
+ optional int32 grid_y = 13 [default = -1];
+ optional int32 page_id = 14 [default = -2];
// e.g., folder icon's (x, y) location and index information on the workspace
- optional int32 grid_x_parent = 15;
- optional int32 grid_y_parent = 16;
- optional int32 page_id_parent = 17;
+ optional int32 grid_x_parent = 15 [default = -1];
+ optional int32 grid_y_parent = 16 [default = -1];
+ optional int32 page_id_parent = 17 [default = -2];
// e.g., SEARCHBOX_ALLAPPS, FOLDER_WORKSPACE
optional int32 hierarchy = 18;
@@ -3036,7 +3036,7 @@ message LauncherUIChanged {
optional bool is_work_profile = 19;
// Used to store the predicted rank of the target
- optional int32 rank = 20;
+ optional int32 rank = 20 [default = -1];
// e.g., folderLabelState can be captured in the following two fields
optional int32 from_state = 21;
@@ -3044,6 +3044,9 @@ message LauncherUIChanged {
// e.g., autofilled or suggested texts that are not user entered
optional string edittext = 23;
+
+ // e.g., number of contents inside a container (e.g., icons inside a folder)
+ optional int32 cardinality = 24;
}
/**
@@ -3064,22 +3067,34 @@ message LauncherStaticLayout {
optional string component_name = 6;
// (x, y) coordinate and the index information of the target on the container
- optional int32 grid_x = 7;
- optional int32 grid_y = 8;
- optional int32 page_id = 9;
+ optional int32 grid_x = 7 [default = -1];
+ optional int32 grid_y = 8 [default = -1];
+ optional int32 page_id = 9 [default = -2];
// e.g., folder icon's (x, y) location and index information on the workspace
- optional int32 grid_x_parent = 10;
- optional int32 grid_y_parent = 11;
- optional int32 page_id_parent = 12;
-
- // e.g., WORKSPACE, HOTSEAT, FOLDER_WORKSPACE, FOLDER_HOTSEAT
+ // e.g., when used with widgets target, use these values for (span_x, span_y)
+ optional int32 grid_x_parent = 10 [default = -1];
+ optional int32 grid_y_parent = 11 [default = -1];
+ optional int32 page_id_parent = 12 [default = -2];
+
+ // UNKNOWN = 0
+ // HOTSEAT = 1
+ // WORKSPACE = 2
+ // FOLDER_HOTSEAT = 3
+ // FOLDER_WORKSPACE = 4
optional int32 hierarchy = 13;
optional bool is_work_profile = 14;
// e.g., PIN, WIDGET TRAY, APPS TRAY, PREDICTION
optional int32 origin = 15;
+
+ // e.g., number of icons inside a folder
+ optional int32 cardinality = 16;
+
+ // e.g., (x, y) span of the widget inside homescreen grid system
+ optional int32 span_x = 17 [default = 1];
+ optional int32 span_y = 18 [default = 1];
}
/**
diff --git a/cmds/statsd/src/external/StatsCallbackPuller.cpp b/cmds/statsd/src/external/StatsCallbackPuller.cpp
index 3618bb0dd08b..78e6f094db7e 100644
--- a/cmds/statsd/src/external/StatsCallbackPuller.cpp
+++ b/cmds/statsd/src/external/StatsCallbackPuller.cpp
@@ -86,6 +86,7 @@ bool StatsCallbackPuller::PullInternal(vector<shared_ptr<LogEvent>>* data) {
// in unit tests. In process calls are not oneway.
Status status = mCallback->onPullAtom(mTagId, resultReceiver);
if (!status.isOk()) {
+ StatsdStats::getInstance().notePullBinderCallFailed(mTagId);
return false;
}
diff --git a/cmds/statsd/src/external/StatsPuller.cpp b/cmds/statsd/src/external/StatsPuller.cpp
index 5192ddf301e1..829a60345ba7 100644
--- a/cmds/statsd/src/external/StatsPuller.cpp
+++ b/cmds/statsd/src/external/StatsPuller.cpp
@@ -82,6 +82,11 @@ bool StatsPuller::Pull(std::vector<std::shared_ptr<LogEvent>>* data) {
mapAndMergeIsolatedUidsToHostUid(mCachedData, mUidMap, mTagId, mAdditiveFields);
}
+ if (mCachedData.empty()) {
+ VLOG("Data pulled is empty");
+ StatsdStats::getInstance().noteEmptyData(mTagId);
+ }
+
(*data) = mCachedData;
return mHasGoodData;
}
diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp
index cfd5d14b0d3b..1a52eb928777 100644
--- a/cmds/statsd/src/external/StatsPullerManager.cpp
+++ b/cmds/statsd/src/external/StatsPullerManager.cpp
@@ -111,12 +111,14 @@ bool StatsPullerManager::PullLocked(int tagId, const ConfigKey& configKey,
if (uidProviderIt == mPullUidProviders.end()) {
ALOGE("Error pulling tag %d. No pull uid provider for config key %s", tagId,
configKey.ToString().c_str());
+ StatsdStats::getInstance().notePullUidProviderNotFound(tagId);
return false;
}
sp<PullUidProvider> pullUidProvider = uidProviderIt->second.promote();
if (pullUidProvider == nullptr) {
ALOGE("Error pulling tag %d, pull uid provider for config %s is gone.", tagId,
configKey.ToString().c_str());
+ StatsdStats::getInstance().notePullUidProviderNotFound(tagId);
return false;
}
uids = pullUidProvider->getPullAtomUids(tagId);
@@ -140,6 +142,7 @@ bool StatsPullerManager::PullLocked(int tagId, const vector<int32_t>& uids,
return ret;
}
}
+ StatsdStats::getInstance().notePullerNotFound(tagId);
ALOGW("StatsPullerManager: Unknown tagId %d", tagId);
return false; // Return early since we don't know what to pull.
} else {
@@ -288,10 +291,7 @@ void StatsPullerManager::OnAlarmFired(int64_t elapsedTimeNs) {
for (const auto& pullInfo : needToPull) {
vector<shared_ptr<LogEvent>> data;
bool pullSuccess = PullLocked(pullInfo.first->atomTag, pullInfo.first->configKey, &data);
- if (pullSuccess) {
- StatsdStats::getInstance().notePullDelay(pullInfo.first->atomTag,
- getElapsedRealtimeNs() - elapsedTimeNs);
- } else {
+ if (!pullSuccess) {
VLOG("pull failed at %lld, will try again later", (long long)elapsedTimeNs);
}
@@ -354,6 +354,11 @@ void StatsPullerManager::RegisterPullAtomCallback(const int uid, const int32_t a
std::lock_guard<std::mutex> _l(mLock);
VLOG("RegisterPullerCallback: adding puller for tag %d", atomTag);
+ if (callback == nullptr) {
+ ALOGW("SetPullAtomCallback called with null callback for atom %d.", atomTag);
+ return;
+ }
+
StatsdStats::getInstance().notePullerCallbackRegistrationChanged(atomTag, /*registered=*/true);
int64_t actualCoolDownNs = coolDownNs < kMinCoolDownNs ? kMinCoolDownNs : coolDownNs;
int64_t actualTimeoutNs = timeoutNs > kMaxTimeoutNs ? kMaxTimeoutNs : timeoutNs;
diff --git a/cmds/statsd/src/guardrail/StatsdStats.cpp b/cmds/statsd/src/guardrail/StatsdStats.cpp
index 46f5dbda5521..c027fffd20a0 100644
--- a/cmds/statsd/src/guardrail/StatsdStats.cpp
+++ b/cmds/statsd/src/guardrail/StatsdStats.cpp
@@ -472,14 +472,19 @@ void StatsdStats::notePullFailed(int atomId) {
mPulledAtomStats[atomId].pullFailed++;
}
-void StatsdStats::noteStatsCompanionPullFailed(int atomId) {
+void StatsdStats::notePullUidProviderNotFound(int atomId) {
lock_guard<std::mutex> lock(mLock);
- mPulledAtomStats[atomId].statsCompanionPullFailed++;
+ mPulledAtomStats[atomId].pullUidProviderNotFound++;
}
-void StatsdStats::noteStatsCompanionPullBinderTransactionFailed(int atomId) {
+void StatsdStats::notePullerNotFound(int atomId) {
lock_guard<std::mutex> lock(mLock);
- mPulledAtomStats[atomId].statsCompanionPullBinderTransactionFailed++;
+ mPulledAtomStats[atomId].pullerNotFound++;
+}
+
+void StatsdStats::notePullBinderCallFailed(int atomId) {
+ lock_guard<std::mutex> lock(mLock);
+ mPulledAtomStats[atomId].binderCallFailCount++;
}
void StatsdStats::noteEmptyData(int atomId) {
@@ -608,6 +613,7 @@ void StatsdStats::resetInternalLocked() {
for (auto& pullStats : mPulledAtomStats) {
pullStats.second.totalPull = 0;
pullStats.second.totalPullFromCache = 0;
+ pullStats.second.minPullIntervalSec = LONG_MAX;
pullStats.second.avgPullTimeNs = 0;
pullStats.second.maxPullTimeNs = 0;
pullStats.second.numPullTime = 0;
@@ -617,9 +623,13 @@ void StatsdStats::resetInternalLocked() {
pullStats.second.dataError = 0;
pullStats.second.pullTimeout = 0;
pullStats.second.pullExceedMaxDelay = 0;
+ pullStats.second.pullFailed = 0;
+ pullStats.second.pullUidProviderNotFound = 0;
+ pullStats.second.pullerNotFound = 0;
pullStats.second.registeredCount = 0;
pullStats.second.unregisteredCount = 0;
pullStats.second.atomErrorCount = 0;
+ pullStats.second.binderCallFailCount = 0;
}
mAtomMetricStats.clear();
mActivationBroadcastGuardrailStats.clear();
@@ -764,14 +774,16 @@ void StatsdStats::dumpStats(int out) const {
" (average pull time nanos)%lld, (max pull time nanos)%lld, (average pull delay "
"nanos)%lld, "
" (max pull delay nanos)%lld, (data error)%ld\n"
- " (pull timeout)%ld, (pull exceed max delay)%ld\n"
- " (registered count) %ld, (unregistered count) %ld\n"
+ " (pull timeout)%ld, (pull exceed max delay)%ld"
+ " (no uid provider count)%ld, (no puller found count)%ld\n"
+ " (registered count) %ld, (unregistered count) %ld"
" (atom error count) %d\n",
(int)pair.first, (long)pair.second.totalPull, (long)pair.second.totalPullFromCache,
(long)pair.second.pullFailed, (long)pair.second.minPullIntervalSec,
(long long)pair.second.avgPullTimeNs, (long long)pair.second.maxPullTimeNs,
(long long)pair.second.avgPullDelayNs, (long long)pair.second.maxPullDelayNs,
pair.second.dataError, pair.second.pullTimeout, pair.second.pullExceedMaxDelay,
+ pair.second.pullUidProviderNotFound, pair.second.pullerNotFound,
pair.second.registeredCount, pair.second.unregisteredCount,
pair.second.atomErrorCount);
}
diff --git a/cmds/statsd/src/guardrail/StatsdStats.h b/cmds/statsd/src/guardrail/StatsdStats.h
index 805281ccd2d2..3d0eeb840064 100644
--- a/cmds/statsd/src/guardrail/StatsdStats.h
+++ b/cmds/statsd/src/guardrail/StatsdStats.h
@@ -371,21 +371,30 @@ public:
int32_t lastAtomTag, int32_t uid, int32_t pid);
/**
- * Records that the pull of an atom has failed
+ * Records that the pull of an atom has failed. Eg, if the client indicated the pull failed, if
+ * the pull timed out, or if the outgoing binder call failed.
+ * This count will only increment if the puller was actually invoked.
+ *
+ * It does not include a pull not occurring due to not finding the appropriate
+ * puller. These cases are covered in other counts.
*/
void notePullFailed(int atomId);
/**
- * Records that the pull of StatsCompanionService atom has failed
+ * Records that the pull of an atom has failed due to not having a uid provider.
+ */
+ void notePullUidProviderNotFound(int atomId);
+
+ /**
+ * Records that the pull of an atom has failed due not finding a puller registered by a
+ * trusted uid.
*/
- void noteStatsCompanionPullFailed(int atomId);
+ void notePullerNotFound(int atomId);
/**
- * Records that the pull of a StatsCompanionService atom has failed due to a failed binder
- * transaction. This can happen when StatsCompanionService returns too
- * much data (the max Binder parcel size is 1MB)
+ * Records that the pull has failed due to the outgoing binder call failing.
*/
- void noteStatsCompanionPullBinderTransactionFailed(int atomId);
+ void notePullBinderCallFailed(int atomId);
/**
* A pull with no data occurred
@@ -503,12 +512,13 @@ public:
long pullTimeout = 0;
long pullExceedMaxDelay = 0;
long pullFailed = 0;
- long statsCompanionPullFailed = 0;
- long statsCompanionPullBinderTransactionFailed = 0;
+ long pullUidProviderNotFound = 0;
+ long pullerNotFound = 0;
long emptyData = 0;
long registeredCount = 0;
long unregisteredCount = 0;
int32_t atomErrorCount = 0;
+ long binderCallFailCount = 0;
} PulledAtomStats;
typedef struct {
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
index c4bd0549465a..cc4c56537c4c 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
@@ -326,13 +326,12 @@ void GaugeMetricProducer::pullAndMatchEventsLocked(const int64_t timestampNs) {
return;
}
const int64_t pullDelayNs = getElapsedRealtimeNs() - timestampNs;
+ StatsdStats::getInstance().notePullDelay(mPullTagId, pullDelayNs);
if (pullDelayNs > mMaxPullDelayNs) {
ALOGE("Pull finish too late for atom %d", mPullTagId);
StatsdStats::getInstance().notePullExceedMaxDelay(mPullTagId);
- StatsdStats::getInstance().notePullDelay(mPullTagId, pullDelayNs);
return;
}
- StatsdStats::getInstance().notePullDelay(mPullTagId, pullDelayNs);
for (const auto& data : allData) {
LogEvent localCopy = data->makeCopy();
localCopy.setElapsedTimestampNs(timestampNs);
@@ -415,6 +414,13 @@ void GaugeMetricProducer::onDataPulled(const std::vector<std::shared_ptr<LogEven
if (!pullSuccess || allData.size() == 0) {
return;
}
+ const int64_t pullDelayNs = getElapsedRealtimeNs() - originalPullTimeNs;
+ StatsdStats::getInstance().notePullDelay(mPullTagId, pullDelayNs);
+ if (pullDelayNs > mMaxPullDelayNs) {
+ ALOGE("Pull finish too late for atom %d", mPullTagId);
+ StatsdStats::getInstance().notePullExceedMaxDelay(mPullTagId);
+ return;
+ }
for (const auto& data : allData) {
if (mEventMatcherWizard->matchLogEvent(
*data, mWhatMatcherIndex) == MatchingState::kMatched) {
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
index f34423a27aff..e5ec72e3d0f5 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
@@ -584,11 +584,6 @@ void ValueMetricProducer::accumulateEvents(const std::vector<std::shared_ptr<Log
return;
}
- if (allData.size() == 0) {
- VLOG("Data pulled is empty");
- StatsdStats::getInstance().noteEmptyData(mPullTagId);
- }
-
mMatchedMetricDimensionKeys.clear();
for (const auto& data : allData) {
LogEvent localCopy = data->makeCopy();
diff --git a/cmds/statsd/src/stats_log.proto b/cmds/statsd/src/stats_log.proto
index 868247bc9d64..1121392f1db0 100644
--- a/cmds/statsd/src/stats_log.proto
+++ b/cmds/statsd/src/stats_log.proto
@@ -456,12 +456,15 @@ message StatsdStatsReport {
optional int64 pull_timeout = 10;
optional int64 pull_exceed_max_delay = 11;
optional int64 pull_failed = 12;
- optional int64 stats_companion_pull_failed = 13;
- optional int64 stats_companion_pull_binder_transaction_failed = 14;
+ optional int64 stats_companion_pull_failed = 13 [deprecated = true];
+ optional int64 stats_companion_pull_binder_transaction_failed = 14 [deprecated = true];
optional int64 empty_data = 15;
optional int64 registered_count = 16;
optional int64 unregistered_count = 17;
optional int32 atom_error_count = 18;
+ optional int64 binder_call_failed = 19;
+ optional int64 failed_uid_provider_not_found = 20;
+ optional int64 puller_not_found = 21;
}
repeated PulledAtomStats pulled_atom_stats = 10;
diff --git a/cmds/statsd/src/stats_log_util.cpp b/cmds/statsd/src/stats_log_util.cpp
index 2acffee0f443..bafdfcba59b2 100644
--- a/cmds/statsd/src/stats_log_util.cpp
+++ b/cmds/statsd/src/stats_log_util.cpp
@@ -74,12 +74,13 @@ const int FIELD_ID_DATA_ERROR = 9;
const int FIELD_ID_PULL_TIMEOUT = 10;
const int FIELD_ID_PULL_EXCEED_MAX_DELAY = 11;
const int FIELD_ID_PULL_FAILED = 12;
-const int FIELD_ID_STATS_COMPANION_FAILED = 13;
-const int FIELD_ID_STATS_COMPANION_BINDER_TRANSACTION_FAILED = 14;
const int FIELD_ID_EMPTY_DATA = 15;
const int FIELD_ID_PULL_REGISTERED_COUNT = 16;
const int FIELD_ID_PULL_UNREGISTERED_COUNT = 17;
const int FIELD_ID_ATOM_ERROR_COUNT = 18;
+const int FIELD_ID_BINDER_CALL_FAIL_COUNT = 19;
+const int FIELD_ID_PULL_UID_PROVIDER_NOT_FOUND = 20;
+const int FIELD_ID_PULLER_NOT_FOUND = 21;
// for AtomMetricStats proto
const int FIELD_ID_ATOM_METRIC_STATS = 17;
@@ -483,10 +484,6 @@ void writePullerStatsToStream(const std::pair<int, StatsdStats::PulledAtomStats>
(long long)pair.second.pullExceedMaxDelay);
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_PULL_FAILED,
(long long)pair.second.pullFailed);
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_STATS_COMPANION_FAILED,
- (long long)pair.second.statsCompanionPullFailed);
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_STATS_COMPANION_BINDER_TRANSACTION_FAILED,
- (long long)pair.second.statsCompanionPullBinderTransactionFailed);
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_EMPTY_DATA,
(long long)pair.second.emptyData);
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_PULL_REGISTERED_COUNT,
@@ -494,6 +491,12 @@ void writePullerStatsToStream(const std::pair<int, StatsdStats::PulledAtomStats>
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_PULL_UNREGISTERED_COUNT,
(long long) pair.second.unregisteredCount);
protoOutput->write(FIELD_TYPE_INT32 | FIELD_ID_ATOM_ERROR_COUNT, pair.second.atomErrorCount);
+ protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BINDER_CALL_FAIL_COUNT,
+ (long long)pair.second.binderCallFailCount);
+ protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_PULL_UID_PROVIDER_NOT_FOUND,
+ (long long)pair.second.pullUidProviderNotFound);
+ protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_PULLER_NOT_FOUND,
+ (long long)pair.second.pullerNotFound);
protoOutput->end(token);
}
diff --git a/cmds/statsd/tests/guardrail/StatsdStats_test.cpp b/cmds/statsd/tests/guardrail/StatsdStats_test.cpp
index cdde603f4c0b..948d58706971 100644
--- a/cmds/statsd/tests/guardrail/StatsdStats_test.cpp
+++ b/cmds/statsd/tests/guardrail/StatsdStats_test.cpp
@@ -302,7 +302,10 @@ TEST(StatsdStatsTest, TestPullAtomStats) {
stats.notePullerCallbackRegistrationChanged(util::DISK_SPACE, true);
stats.notePullerCallbackRegistrationChanged(util::DISK_SPACE, false);
stats.notePullerCallbackRegistrationChanged(util::DISK_SPACE, true);
-
+ stats.notePullBinderCallFailed(util::DISK_SPACE);
+ stats.notePullUidProviderNotFound(util::DISK_SPACE);
+ stats.notePullerNotFound(util::DISK_SPACE);
+ stats.notePullerNotFound(util::DISK_SPACE);
vector<uint8_t> output;
stats.dumpStats(&output, false);
@@ -322,6 +325,9 @@ TEST(StatsdStatsTest, TestPullAtomStats) {
EXPECT_EQ(3335L, report.pulled_atom_stats(0).max_pull_delay_nanos());
EXPECT_EQ(2L, report.pulled_atom_stats(0).registered_count());
EXPECT_EQ(1L, report.pulled_atom_stats(0).unregistered_count());
+ EXPECT_EQ(1L, report.pulled_atom_stats(0).binder_call_failed());
+ EXPECT_EQ(1L, report.pulled_atom_stats(0).failed_uid_provider_not_found());
+ EXPECT_EQ(2L, report.pulled_atom_stats(0).puller_not_found());
}
TEST(StatsdStatsTest, TestAtomMetricsStats) {
diff --git a/core/java/android/app/DreamManager.java b/core/java/android/app/DreamManager.java
index fe13b8f26d78..f23681373f53 100644
--- a/core/java/android/app/DreamManager.java
+++ b/core/java/android/app/DreamManager.java
@@ -58,7 +58,7 @@ public class DreamManager {
@RequiresPermission(android.Manifest.permission.WRITE_DREAM_STATE)
public void startDream(@NonNull ComponentName name) {
try {
- mService.testDream(mContext.getUserId(), name);
+ mService.dream();
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
@@ -99,4 +99,22 @@ public class DreamManager {
e.rethrowFromSystemServer();
}
}
+
+ /**
+ * Returns whether the device is Dreaming.
+ *
+ * <p> This is only used for testing the dream service APIs.
+ *
+ * @hide
+ */
+ @TestApi
+ @RequiresPermission(android.Manifest.permission.READ_DREAM_STATE)
+ public boolean isDreaming() {
+ try {
+ return mService.isDreaming();
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ return false;
+ }
}
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 833bfed573b2..e84c5e574713 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -99,7 +99,6 @@ interface IActivityManager {
void unregisterUidObserver(in IUidObserver observer);
boolean isUidActive(int uid, String callingPackage);
int getUidProcessState(int uid, in String callingPackage);
- boolean isUidActiveOrForeground(int uid, String callingPackage);
// =============== End of transactions used on native side as well ============================
// Special low-level communication with activity manager.
@@ -673,4 +672,9 @@ interface IActivityManager {
* @param state The customized state data
*/
void setProcessStateSummary(in byte[] state);
+
+ /**
+ * Return whether the app freezer is supported (true) or not (false) by this system.
+ */
+ boolean isAppFreezerSupported();
}
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 0a4627da223a..c409613589b0 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -1449,7 +1449,7 @@ public abstract class ContentResolver implements ContentInterface {
* on these schemes.
*
* @param uri The desired URI.
- * @return InputStream
+ * @return InputStream or {@code null} if the provider recently crashed.
* @throws FileNotFoundException if the provided URI could not be opened.
* @see #openAssetFileDescriptor(Uri, String)
*/
@@ -1484,6 +1484,9 @@ public abstract class ContentResolver implements ContentInterface {
/**
* Synonym for {@link #openOutputStream(Uri, String)
* openOutputStream(uri, "w")}.
+ *
+ * @param uri The desired URI.
+ * @return an OutputStream or {@code null} if the provider recently crashed.
* @throws FileNotFoundException if the provided URI could not be opened.
*/
public final @Nullable OutputStream openOutputStream(@NonNull Uri uri)
@@ -1506,7 +1509,7 @@ public abstract class ContentResolver implements ContentInterface {
*
* @param uri The desired URI.
* @param mode May be "w", "wa", "rw", or "rwt".
- * @return OutputStream
+ * @return an OutputStream or {@code null} if the provider recently crashed.
* @throws FileNotFoundException if the provided URI could not be opened.
* @see #openAssetFileDescriptor(Uri, String)
*/
@@ -1563,8 +1566,9 @@ public abstract class ContentResolver implements ContentInterface {
* @param uri The desired URI to open.
* @param mode The file mode to use, as per {@link ContentProvider#openFile
* ContentProvider.openFile}.
- * @return Returns a new ParcelFileDescriptor pointing to the file. You
- * own this descriptor and are responsible for closing it when done.
+ * @return Returns a new ParcelFileDescriptor pointing to the file or {@code null} if the
+ * provider recently crashed. You own this descriptor and are responsible for closing it
+ * when done.
* @throws FileNotFoundException Throws FileNotFoundException if no
* file exists under the URI or the mode is invalid.
* @see #openAssetFileDescriptor(Uri, String)
@@ -1608,8 +1612,9 @@ public abstract class ContentResolver implements ContentInterface {
* @param cancellationSignal A signal to cancel the operation in progress,
* or null if none. If the operation is canceled, then
* {@link OperationCanceledException} will be thrown.
- * @return Returns a new ParcelFileDescriptor pointing to the file. You
- * own this descriptor and are responsible for closing it when done.
+ * @return Returns a new ParcelFileDescriptor pointing to the file or {@code null} if the
+ * provider recently crashed. You own this descriptor and are responsible for closing it
+ * when done.
* @throws FileNotFoundException Throws FileNotFoundException if no
* file exists under the URI or the mode is invalid.
* @see #openAssetFileDescriptor(Uri, String)
@@ -1698,8 +1703,9 @@ public abstract class ContentResolver implements ContentInterface {
* @param uri The desired URI to open.
* @param mode The file mode to use, as per {@link ContentProvider#openAssetFile
* ContentProvider.openAssetFile}.
- * @return Returns a new ParcelFileDescriptor pointing to the file. You
- * own this descriptor and are responsible for closing it when done.
+ * @return Returns a new ParcelFileDescriptor pointing to the file or {@code null} if the
+ * provider recently crashed. You own this descriptor and are responsible for closing it
+ * when done.
* @throws FileNotFoundException Throws FileNotFoundException of no
* file exists under the URI or the mode is invalid.
*/
@@ -1754,8 +1760,9 @@ public abstract class ContentResolver implements ContentInterface {
* @param cancellationSignal A signal to cancel the operation in progress, or null if
* none. If the operation is canceled, then
* {@link OperationCanceledException} will be thrown.
- * @return Returns a new ParcelFileDescriptor pointing to the file. You
- * own this descriptor and are responsible for closing it when done.
+ * @return Returns a new ParcelFileDescriptor pointing to the file or {@code null} if the
+ * provider recently crashed. You own this descriptor and are responsible for closing it
+ * when done.
* @throws FileNotFoundException Throws FileNotFoundException of no
* file exists under the URI or the mode is invalid.
*/
@@ -1902,9 +1909,9 @@ public abstract class ContentResolver implements ContentInterface {
* it is returning.
* @param opts Additional provider-dependent options.
* @return Returns a new ParcelFileDescriptor from which you can read the
- * data stream from the provider. Note that this may be a pipe, meaning
- * you can't seek in it. The only seek you should do is if the
- * AssetFileDescriptor contains an offset, to move to that offset before
+ * data stream from the provider or {@code null} if the provider recently crashed.
+ * Note that this may be a pipe, meaning you can't seek in it. The only seek you
+ * should do is if the AssetFileDescriptor contains an offset, to move to that offset before
* reading. You own this descriptor and are responsible for closing it when done.
* @throws FileNotFoundException Throws FileNotFoundException of no
* data of the desired type exists under the URI.
@@ -1938,9 +1945,9 @@ public abstract class ContentResolver implements ContentInterface {
* or null if none. If the operation is canceled, then
* {@link OperationCanceledException} will be thrown.
* @return Returns a new ParcelFileDescriptor from which you can read the
- * data stream from the provider. Note that this may be a pipe, meaning
- * you can't seek in it. The only seek you should do is if the
- * AssetFileDescriptor contains an offset, to move to that offset before
+ * data stream from the provider or {@code null} if the provider recently crashed.
+ * Note that this may be a pipe, meaning you can't seek in it. The only seek you
+ * should do is if the AssetFileDescriptor contains an offset, to move to that offset before
* reading. You own this descriptor and are responsible for closing it when done.
* @throws FileNotFoundException Throws FileNotFoundException of no
* data of the desired type exists under the URI.
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index d06a69caf837..4d718ef6bebe 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -47,8 +47,11 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.IntentSender;
-import android.content.pm.PackageParser.PackageParserException;
import android.content.pm.dex.ArtManager;
+import android.content.pm.parsing.PackageInfoWithoutStateUtils;
+import android.content.pm.parsing.ParsingPackage;
+import android.content.pm.parsing.ParsingPackageUtils;
+import android.content.pm.parsing.result.ParseResult;
import android.content.res.Resources;
import android.content.res.XmlResourceParser;
import android.graphics.Rect;
@@ -6037,28 +6040,24 @@ public abstract class PackageManager {
@Nullable
public PackageInfo getPackageArchiveInfo(@NonNull String archiveFilePath,
@PackageInfoFlags int flags) {
- final PackageParser parser = new PackageParser();
- parser.setCallback(new PackageParser.CallbackImpl(this));
- final File apkFile = new File(archiveFilePath);
- try {
- if ((flags & (MATCH_DIRECT_BOOT_UNAWARE | MATCH_DIRECT_BOOT_AWARE)) != 0) {
- // Caller expressed an explicit opinion about what encryption
- // aware/unaware components they want to see, so fall through and
- // give them what they want
- } else {
- // Caller expressed no opinion, so match everything
- flags |= MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE;
- }
+ if ((flags & (PackageManager.MATCH_DIRECT_BOOT_UNAWARE
+ | PackageManager.MATCH_DIRECT_BOOT_AWARE)) == 0) {
+ // Caller expressed no opinion about what encryption
+ // aware/unaware components they want to see, so match both
+ flags |= PackageManager.MATCH_DIRECT_BOOT_AWARE
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
+ }
- PackageParser.Package pkg = parser.parseMonolithicPackage(apkFile, 0);
- if ((flags & GET_SIGNATURES) != 0) {
- PackageParser.collectCertificates(pkg, false /* skipVerify */);
- }
- PackageUserState state = new PackageUserState();
- return PackageParser.generatePackageInfo(pkg, null, flags, 0, 0, null, state);
- } catch (PackageParserException e) {
+ boolean collectCertificates = (flags & PackageManager.GET_SIGNATURES) != 0
+ || (flags & PackageManager.GET_SIGNING_CERTIFICATES) != 0;
+
+ ParseResult<ParsingPackage> result = ParsingPackageUtils.parseDefaultOneTime(
+ new File(archiveFilePath), 0, collectCertificates);
+ if (result.isError()) {
return null;
}
+ return PackageInfoWithoutStateUtils.generate(result.getResult(), null, flags, 0, 0, null,
+ new PackageUserState(), UserHandle.getCallingUserId());
}
/**
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 3b3521f834aa..312e98e77636 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -1517,6 +1517,10 @@ public class PackageParser {
? null : "must have at least one '.' separator";
}
+ /**
+ * @deprecated Use {@link android.content.pm.parsing.ApkLiteParseUtils#parsePackageSplitNames}
+ */
+ @Deprecated
public static Pair<String, String> parsePackageSplitNames(XmlPullParser parser,
AttributeSet attrs) throws IOException, XmlPullParserException,
PackageParserException {
diff --git a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
index 2f416a2538ba..d2172d3741d1 100644
--- a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
+++ b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
@@ -16,6 +16,8 @@
package android.content.pm.parsing;
+import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME;
+import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
import android.compat.annotation.UnsupportedAppUsage;
@@ -23,6 +25,8 @@ import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageParser;
import android.content.pm.VerifierInfo;
+import android.content.pm.parsing.result.ParseInput;
+import android.content.pm.parsing.result.ParseResult;
import android.content.res.ApkAssets;
import android.content.res.XmlResourceParser;
import android.os.Trace;
@@ -70,82 +74,93 @@ public class ApkLiteParseUtils {
*
* @see PackageParser#parsePackage(File, int)
*/
- @UnsupportedAppUsage
- public static PackageParser.PackageLite parsePackageLite(File packageFile, int flags)
- throws PackageParser.PackageParserException {
+ public static ParseResult<PackageParser.PackageLite> parsePackageLite(ParseInput input,
+ File packageFile, int flags) {
if (packageFile.isDirectory()) {
- return parseClusterPackageLite(packageFile, flags);
+ return parseClusterPackageLite(input, packageFile, flags);
} else {
- return parseMonolithicPackageLite(packageFile, flags);
+ return parseMonolithicPackageLite(input, packageFile, flags);
}
}
- public static PackageParser.PackageLite parseMonolithicPackageLite(File packageFile, int flags)
- throws PackageParser.PackageParserException {
+ public static ParseResult<PackageParser.PackageLite> parseMonolithicPackageLite(
+ ParseInput input, File packageFile, int flags) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parseApkLite");
- final PackageParser.ApkLite baseApk = parseApkLite(packageFile, flags);
- final String packagePath = packageFile.getAbsolutePath();
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
- return new PackageParser.PackageLite(packagePath, baseApk, null, null, null, null,
- null, null);
+ try {
+ ParseResult<PackageParser.ApkLite> result = parseApkLite(input, packageFile, flags);
+ if (result.isError()) {
+ return input.error(result);
+ }
+
+ final PackageParser.ApkLite baseApk = result.getResult();
+ final String packagePath = packageFile.getAbsolutePath();
+ return input.success(
+ new PackageParser.PackageLite(packagePath, baseApk, null, null, null, null,
+ null, null));
+ } finally {
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ }
}
- public static PackageParser.PackageLite parseClusterPackageLite(File packageDir, int flags)
- throws PackageParser.PackageParserException {
+ public static ParseResult<PackageParser.PackageLite> parseClusterPackageLite(ParseInput input,
+ File packageDir, int flags) {
final File[] files = packageDir.listFiles();
if (ArrayUtils.isEmpty(files)) {
- throw new PackageParser.PackageParserException(
- PackageManager.INSTALL_PARSE_FAILED_NOT_APK, "No packages found in split");
+ return input.error(PackageManager.INSTALL_PARSE_FAILED_NOT_APK,
+ "No packages found in split");
}
// Apk directory is directly nested under the current directory
if (files.length == 1 && files[0].isDirectory()) {
- return parseClusterPackageLite(files[0], flags);
+ return parseClusterPackageLite(input, files[0], flags);
}
String packageName = null;
int versionCode = 0;
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parseApkLite");
final ArrayMap<String, PackageParser.ApkLite> apks = new ArrayMap<>();
- for (File file : files) {
- if (PackageParser.isApkFile(file)) {
- final PackageParser.ApkLite lite = parseApkLite(file, flags);
-
- // Assert that all package names and version codes are
- // consistent with the first one we encounter.
- if (packageName == null) {
- packageName = lite.packageName;
- versionCode = lite.versionCode;
- } else {
- if (!packageName.equals(lite.packageName)) {
- throw new PackageParser.PackageParserException(
- PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST,
- "Inconsistent package " + lite.packageName + " in " + file
- + "; expected " + packageName);
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parseApkLite");
+ try {
+ for (File file : files) {
+ if (PackageParser.isApkFile(file)) {
+ ParseResult<PackageParser.ApkLite> result = parseApkLite(input, file, flags);
+ if (result.isError()) {
+ return input.error(result);
}
- if (versionCode != lite.versionCode) {
- throw new PackageParser.PackageParserException(
- PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST,
- "Inconsistent version " + lite.versionCode + " in " + file
- + "; expected " + versionCode);
+
+ final PackageParser.ApkLite lite = result.getResult();
+ // Assert that all package names and version codes are
+ // consistent with the first one we encounter.
+ if (packageName == null) {
+ packageName = lite.packageName;
+ versionCode = lite.versionCode;
+ } else {
+ if (!packageName.equals(lite.packageName)) {
+ return input.error(PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST,
+ "Inconsistent package " + lite.packageName + " in " + file
+ + "; expected " + packageName);
+ }
+ if (versionCode != lite.versionCode) {
+ return input.error(PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST,
+ "Inconsistent version " + lite.versionCode + " in " + file
+ + "; expected " + versionCode);
+ }
}
- }
- // Assert that each split is defined only oncuses-static-libe
- if (apks.put(lite.splitName, lite) != null) {
- throw new PackageParser.PackageParserException(
- PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST,
- "Split name " + lite.splitName
- + " defined more than once; most recent was " + file);
+ // Assert that each split is defined only oncuses-static-libe
+ if (apks.put(lite.splitName, lite) != null) {
+ return input.error(PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST,
+ "Split name " + lite.splitName
+ + " defined more than once; most recent was " + file);
+ }
}
}
+ } finally {
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
final PackageParser.ApkLite baseApk = apks.remove(null);
if (baseApk == null) {
- throw new PackageParser.PackageParserException(
- PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST,
+ return input.error(PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST,
"Missing base APK in " + packageDir);
}
@@ -180,8 +195,9 @@ public class ApkLiteParseUtils {
}
final String codePath = packageDir.getAbsolutePath();
- return new PackageParser.PackageLite(codePath, baseApk, splitNames, isFeatureSplits,
- usesSplitNames, configForSplits, splitCodePaths, splitRevisionCodes);
+ return input.success(new PackageParser.PackageLite(codePath, baseApk, splitNames,
+ isFeatureSplits, usesSplitNames, configForSplits, splitCodePaths,
+ splitRevisionCodes));
}
/**
@@ -192,9 +208,9 @@ public class ApkLiteParseUtils {
* @param flags optional parse flags, such as
* {@link PackageParser#PARSE_COLLECT_CERTIFICATES}
*/
- public static PackageParser.ApkLite parseApkLite(File apkFile, int flags)
- throws PackageParser.PackageParserException {
- return parseApkLiteInner(apkFile, null, null, flags);
+ public static ParseResult<PackageParser.ApkLite> parseApkLite(ParseInput input, File apkFile,
+ int flags) {
+ return parseApkLiteInner(input, apkFile, null, null, flags);
}
/**
@@ -206,13 +222,13 @@ public class ApkLiteParseUtils {
* @param flags optional parse flags, such as
* {@link PackageParser#PARSE_COLLECT_CERTIFICATES}
*/
- public static PackageParser.ApkLite parseApkLite(FileDescriptor fd, String debugPathName,
- int flags) throws PackageParser.PackageParserException {
- return parseApkLiteInner(null, fd, debugPathName, flags);
+ public static ParseResult<PackageParser.ApkLite> parseApkLite(ParseInput input,
+ FileDescriptor fd, String debugPathName, int flags) {
+ return parseApkLiteInner(input, null, fd, debugPathName, flags);
}
- private static PackageParser.ApkLite parseApkLiteInner(File apkFile, FileDescriptor fd,
- String debugPathName, int flags) throws PackageParser.PackageParserException {
+ private static ParseResult<PackageParser.ApkLite> parseApkLiteInner(ParseInput input,
+ File apkFile, FileDescriptor fd, String debugPathName, int flags) {
final String apkPath = fd != null ? debugPathName : apkFile.getAbsolutePath();
XmlResourceParser parser = null;
@@ -223,8 +239,7 @@ public class ApkLiteParseUtils {
? ApkAssets.loadFromFd(fd, debugPathName, 0 /* flags */, null /* assets */)
: ApkAssets.loadFromPath(apkPath);
} catch (IOException e) {
- throw new PackageParser.PackageParserException(
- PackageManager.INSTALL_PARSE_FAILED_NOT_APK,
+ return input.error(PackageManager.INSTALL_PARSE_FAILED_NOT_APK,
"Failed to parse " + apkPath, e);
}
@@ -235,9 +250,15 @@ public class ApkLiteParseUtils {
final boolean skipVerify = (flags & PackageParser.PARSE_IS_SYSTEM_DIR) != 0;
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectCertificates");
try {
- signingDetails = ParsingPackageUtils.collectCertificates(apkFile.getAbsolutePath(),
- skipVerify, false, PackageParser.SigningDetails.UNKNOWN,
- DEFAULT_TARGET_SDK_VERSION);
+ ParseResult<PackageParser.SigningDetails> result =
+ ParsingPackageUtils.getSigningDetails(input,
+ apkFile.getAbsolutePath(), skipVerify, false,
+ PackageParser.SigningDetails.UNKNOWN,
+ DEFAULT_TARGET_SDK_VERSION);
+ if (result.isError()) {
+ return input.error(result);
+ }
+ signingDetails = result.getResult();
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
@@ -246,12 +267,10 @@ public class ApkLiteParseUtils {
}
final AttributeSet attrs = parser;
- return parseApkLite(apkPath, parser, attrs, signingDetails);
-
+ return parseApkLite(input, apkPath, parser, attrs, signingDetails);
} catch (XmlPullParserException | IOException | RuntimeException e) {
Slog.w(TAG, "Failed to parse " + apkPath, e);
- throw new PackageParser.PackageParserException(
- PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
+ return input.error(PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
"Failed to parse " + apkPath, e);
} finally {
IoUtils.closeQuietly(parser);
@@ -265,12 +284,16 @@ public class ApkLiteParseUtils {
}
}
- private static PackageParser.ApkLite parseApkLite(
+ private static ParseResult<PackageParser.ApkLite> parseApkLite(ParseInput input,
String codePath, XmlPullParser parser, AttributeSet attrs,
PackageParser.SigningDetails signingDetails)
- throws IOException, XmlPullParserException, PackageParser.PackageParserException {
- final Pair<String, String> packageSplit = PackageParser.parsePackageSplitNames(
- parser, attrs);
+ throws IOException, XmlPullParserException {
+ ParseResult<Pair<String, String>> result = parsePackageSplitNames(input, parser, attrs);
+ if (result.isError()) {
+ return input.error(result);
+ }
+
+ Pair<String, String> packageSplit = result.getResult();
int installLocation = PARSE_DEFAULT_INSTALL_LOCATION;
int versionCode = 0;
@@ -394,8 +417,7 @@ public class ApkLiteParseUtils {
usesSplitName = attrs.getAttributeValue(PackageParser.ANDROID_RESOURCES, "name");
if (usesSplitName == null) {
- throw new PackageParser.PackageParserException(
- PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+ return input.error(PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
"<uses-split> tag requires 'android:name' attribute");
}
} else if (PackageParser.TAG_USES_SDK.equals(parser.getName())) {
@@ -423,12 +445,54 @@ public class ApkLiteParseUtils {
overlayPriority = 0;
}
- return new PackageParser.ApkLite(codePath, packageSplit.first, packageSplit.second,
- isFeatureSplit, configForSplit, usesSplitName, isSplitRequired, versionCode,
- versionCodeMajor, revisionCode, installLocation, verifiers, signingDetails,
- coreApp, debuggable, multiArch, use32bitAbi, useEmbeddedDex, extractNativeLibs,
- isolatedSplits, targetPackage, overlayIsStatic, overlayPriority, minSdkVersion,
- targetSdkVersion);
+ return input.success(new PackageParser.ApkLite(codePath, packageSplit.first,
+ packageSplit.second, isFeatureSplit, configForSplit, usesSplitName, isSplitRequired,
+ versionCode, versionCodeMajor, revisionCode, installLocation, verifiers,
+ signingDetails, coreApp, debuggable, multiArch, use32bitAbi, useEmbeddedDex,
+ extractNativeLibs, isolatedSplits, targetPackage, overlayIsStatic, overlayPriority,
+ minSdkVersion, targetSdkVersion));
+ }
+
+ public static ParseResult<Pair<String, String>> parsePackageSplitNames(ParseInput input,
+ XmlPullParser parser, AttributeSet attrs) throws IOException, XmlPullParserException {
+ int type;
+ while ((type = parser.next()) != XmlPullParser.START_TAG
+ && type != XmlPullParser.END_DOCUMENT) {
+ }
+
+ if (type != XmlPullParser.START_TAG) {
+ return input.error(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+ "No start tag found");
+ }
+ if (!parser.getName().equals(PackageParser.TAG_MANIFEST)) {
+ return input.error(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+ "No <manifest> tag");
+ }
+
+ final String packageName = attrs.getAttributeValue(null, "package");
+ if (!"android".equals(packageName)) {
+ final String error = PackageParser.validateName(packageName, true, true);
+ if (error != null) {
+ return input.error(INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME,
+ "Invalid manifest package: " + error);
+ }
+ }
+
+ String splitName = attrs.getAttributeValue(null, "split");
+ if (splitName != null) {
+ if (splitName.length() == 0) {
+ splitName = null;
+ } else {
+ final String error = PackageParser.validateName(splitName, false, false);
+ if (error != null) {
+ return input.error(INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME,
+ "Invalid manifest split: " + error);
+ }
+ }
+ }
+
+ return input.success(Pair.create(packageName.intern(),
+ (splitName != null) ? splitName.intern() : splitName));
}
public static VerifierInfo parseVerifier(AttributeSet attrs) {
diff --git a/core/java/android/content/pm/parsing/ParsingPackageUtils.java b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
index d3d15c82dd1b..9372c957af34 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageUtils.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
@@ -29,6 +29,7 @@ import static android.os.Build.VERSION_CODES.O;
import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
import android.annotation.AnyRes;
+import android.annotation.CheckResult;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -135,23 +136,32 @@ public class ParsingPackageUtils {
* for feature support.
*/
@NonNull
- public static ParseResult<ParsingPackage> parseDefaultOneTime(File file, int flags,
- @NonNull ParseInput.Callback inputCallback, @NonNull Callback callback) {
- if ((flags & (PackageManager.MATCH_DIRECT_BOOT_UNAWARE
- | PackageManager.MATCH_DIRECT_BOOT_AWARE)) == 0) {
- // Caller expressed no opinion about what encryption
- // aware/unaware components they want to see, so match both
- flags |= PackageManager.MATCH_DIRECT_BOOT_AWARE
- | PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
- }
-
- ParseInput input = new ParseTypeImpl(inputCallback).reset();
+ public static ParseResult<ParsingPackage> parseDefaultOneTime(File file,
+ @PackageParser.ParseFlags int parseFlags, boolean collectCertificates) {
+ ParseInput input = ParseTypeImpl.forDefaultParsing().reset();
ParseResult<ParsingPackage> result;
-
- ParsingPackageUtils parser = new ParsingPackageUtils(false, null, null, callback);
+ ParsingPackageUtils parser = new ParsingPackageUtils(false, null, null, new Callback() {
+ @Override
+ public boolean hasFeature(String feature) {
+ // Assume the device doesn't support anything. This will affect permission parsing
+ // and will force <uses-permission/> declarations to include all requiredNotFeature
+ // permissions and exclude all requiredFeature permissions. This mirrors the old
+ // behavior.
+ return false;
+ }
+
+ @Override
+ public ParsingPackage startParsingPackage(
+ @NonNull String packageName,
+ @NonNull String baseCodePath,
+ @NonNull String codePath,
+ @NonNull TypedArray manifestArray, boolean isCoreApp) {
+ return new ParsingPackageImpl(packageName, baseCodePath, codePath, manifestArray);
+ }
+ });
try {
- result = parser.parsePackage(input, file, flags);
+ result = parser.parsePackage(input, file, parseFlags);
if (result.isError()) {
return result;
}
@@ -162,9 +172,9 @@ public class ParsingPackageUtils {
try {
ParsingPackage pkg = result.getResult();
- if ((flags & PackageManager.GET_SIGNATURES) != 0
- || (flags & PackageManager.GET_SIGNING_CERTIFICATES) != 0) {
- ParsingPackageUtils.collectCertificates(pkg, false /* skipVerify */);
+ if (collectCertificates) {
+ pkg.setSigningDetails(
+ ParsingPackageUtils.getSigningDetails(pkg, false /* skipVerify */));
}
return input.success(pkg);
@@ -197,7 +207,7 @@ public class ParsingPackageUtils {
* and unique split names.
* <p>
* Note that this <em>does not</em> perform signature verification; that
- * must be done separately in {@link #collectCertificates(ParsingPackageRead, boolean)}.
+ * must be done separately in {@link #getSigningDetails(ParsingPackageRead, boolean)}.
*
* If {@code useCaches} is true, the package parser might return a cached
* result from a previous parse of the same {@code packageFile} with the same
@@ -223,12 +233,17 @@ public class ParsingPackageUtils {
* split names.
* <p>
* Note that this <em>does not</em> perform signature verification; that
- * must be done separately in {@link #collectCertificates(ParsingPackageRead, boolean)}.
+ * must be done separately in {@link #getSigningDetails(ParsingPackageRead, boolean)}.
*/
private ParseResult<ParsingPackage> parseClusterPackage(ParseInput input, File packageDir,
- int flags) throws PackageParserException {
- final PackageParser.PackageLite lite = ApkLiteParseUtils.parseClusterPackageLite(packageDir,
- 0);
+ int flags) {
+ ParseResult<PackageParser.PackageLite> liteResult =
+ ApkLiteParseUtils.parseClusterPackageLite(input, packageDir, 0);
+ if (liteResult.isError()) {
+ return input.error(liteResult);
+ }
+
+ final PackageParser.PackageLite lite = liteResult.getResult();
if (mOnlyCoreApps && !lite.coreApp) {
return input.error(INSTALL_PARSE_FAILED_ONLY_COREAPP_ALLOWED,
"Not a coreApp: " + packageDir);
@@ -275,6 +290,9 @@ public class ParsingPackageUtils {
pkg.setUse32BitAbi(lite.use32bitAbi);
return input.success(pkg);
+ } catch (PackageParserException e) {
+ return input.error(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
+ "Failed to load assets: " + lite.baseCodePath, e);
} finally {
IoUtils.closeQuietly(assetLoader);
}
@@ -284,12 +302,17 @@ public class ParsingPackageUtils {
* Parse the given APK file, treating it as as a single monolithic package.
* <p>
* Note that this <em>does not</em> perform signature verification; that
- * must be done separately in {@link #collectCertificates(ParsingPackageRead, boolean)}.
+ * must be done separately in {@link #getSigningDetails(ParsingPackageRead, boolean)}.
*/
private ParseResult<ParsingPackage> parseMonolithicPackage(ParseInput input, File apkFile,
int flags) throws PackageParserException {
- final PackageParser.PackageLite lite = ApkLiteParseUtils.parseMonolithicPackageLite(apkFile,
- flags);
+ ParseResult<PackageParser.PackageLite> liteResult =
+ ApkLiteParseUtils.parseMonolithicPackageLite(input, apkFile, flags);
+ if (liteResult.isError()) {
+ return input.error(liteResult);
+ }
+
+ final PackageParser.PackageLite lite = liteResult.getResult();
if (mOnlyCoreApps && !lite.coreApp) {
return input.error(INSTALL_PARSE_FAILED_ONLY_COREAPP_ALLOWED,
"Not a coreApp: " + apkFile);
@@ -430,20 +453,21 @@ public class ParsingPackageUtils {
final String splitName;
final String pkgName;
- try {
- Pair<String, String> packageSplit = PackageParser.parsePackageSplitNames(parser,
- parser);
- pkgName = packageSplit.first;
- splitName = packageSplit.second;
+ ParseResult<Pair<String, String>> packageSplitResult =
+ ApkLiteParseUtils.parsePackageSplitNames(input, parser, parser);
+ if (packageSplitResult.isError()) {
+ return input.error(packageSplitResult);
+ }
- if (!TextUtils.isEmpty(splitName)) {
- return input.error(
- PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME,
- "Expected base APK, but found split " + splitName
- );
- }
- } catch (PackageParserException e) {
- return input.error(PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME);
+ Pair<String, String> packageSplit = packageSplitResult.getResult();
+ pkgName = packageSplit.first;
+ splitName = packageSplit.second;
+
+ if (!TextUtils.isEmpty(splitName)) {
+ return input.error(
+ PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME,
+ "Expected base APK, but found split " + splitName
+ );
}
final TypedArray manifestArray = res.obtainAttributes(parser, R.styleable.AndroidManifest);
@@ -2624,31 +2648,53 @@ public class ParsingPackageUtils {
/**
* Collect certificates from all the APKs described in the given package. Also asserts that
* all APK contents are signed correctly and consistently.
+ *
+ * TODO(b/155513789): Remove this in favor of collecting certificates during the original parse
+ * call if requested. Leaving this as an optional method for the caller means we have to
+ * construct a dummy ParseInput.
*/
- public static SigningDetails collectCertificates(ParsingPackageRead pkg, boolean skipVerify)
+ @CheckResult
+ public static SigningDetails getSigningDetails(ParsingPackageRead pkg, boolean skipVerify)
throws PackageParserException {
SigningDetails signingDetails = SigningDetails.UNKNOWN;
+ ParseInput input = ParseTypeImpl.forDefaultParsing().reset();
+
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectCertificates");
try {
- signingDetails = collectCertificates(
+ ParseResult<SigningDetails> result = getSigningDetails(
+ input,
pkg.getBaseCodePath(),
skipVerify,
pkg.isStaticSharedLibrary(),
signingDetails,
pkg.getTargetSdkVersion()
);
+ if (result.isError()) {
+ throw new PackageParser.PackageParserException(result.getErrorCode(),
+ result.getErrorMessage(), result.getException());
+ }
+
+ signingDetails = result.getResult();
String[] splitCodePaths = pkg.getSplitCodePaths();
if (!ArrayUtils.isEmpty(splitCodePaths)) {
for (int i = 0; i < splitCodePaths.length; i++) {
- signingDetails = collectCertificates(
+ result = getSigningDetails(
+ input,
splitCodePaths[i],
skipVerify,
pkg.isStaticSharedLibrary(),
signingDetails,
pkg.getTargetSdkVersion()
);
+ if (result.isError()) {
+ throw new PackageParser.PackageParserException(result.getErrorCode(),
+ result.getErrorMessage(), result.getException());
+ }
+
+
+ signingDetails = result.getResult();
}
}
return signingDetails;
@@ -2657,9 +2703,10 @@ public class ParsingPackageUtils {
}
}
- public static SigningDetails collectCertificates(String baseCodePath, boolean skipVerify,
- boolean isStaticSharedLibrary, @NonNull SigningDetails existingSigningDetails,
- int targetSdk) throws PackageParserException {
+ @CheckResult
+ public static ParseResult<SigningDetails> getSigningDetails(ParseInput input,
+ String baseCodePath, boolean skipVerify, boolean isStaticSharedLibrary,
+ @NonNull SigningDetails existingSigningDetails, int targetSdk) {
int minSignatureScheme = ApkSignatureVerifier.getMinimumSignatureSchemeVersionForTargetSdk(
targetSdk);
if (isStaticSharedLibrary) {
@@ -2667,27 +2714,31 @@ public class ParsingPackageUtils {
minSignatureScheme = SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V2;
}
SigningDetails verified;
- if (skipVerify) {
- // systemDir APKs are already trusted, save time by not verifying
- verified = ApkSignatureVerifier.unsafeGetCertsWithoutVerification(
- baseCodePath, minSignatureScheme);
- } else {
- verified = ApkSignatureVerifier.verify(baseCodePath, minSignatureScheme);
+ try {
+ if (skipVerify) {
+ // systemDir APKs are already trusted, save time by not verifying
+ verified = ApkSignatureVerifier.unsafeGetCertsWithoutVerification(
+ baseCodePath, minSignatureScheme);
+ } else {
+ verified = ApkSignatureVerifier.verify(baseCodePath, minSignatureScheme);
+ }
+ } catch (PackageParserException e) {
+ return input.error(PackageManager.INSTALL_PARSE_FAILED_NO_CERTIFICATES,
+ "Failed collecting certificates for " + baseCodePath, e);
}
// Verify that entries are signed consistently with the first pkg
// we encountered. Note that for splits, certificates may have
// already been populated during an earlier parse of a base APK.
if (existingSigningDetails == SigningDetails.UNKNOWN) {
- return verified;
+ return input.success(verified);
} else {
if (!Signature.areExactMatch(existingSigningDetails.signatures, verified.signatures)) {
- throw new PackageParserException(
- INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES,
+ return input.error(INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES,
baseCodePath + " has mismatched certificates");
}
- return existingSigningDetails;
+ return input.success(existingSigningDetails);
}
}
diff --git a/core/java/android/content/pm/parsing/result/ParseTypeImpl.java b/core/java/android/content/pm/parsing/result/ParseTypeImpl.java
index 61152061ae10..91e571be3d89 100644
--- a/core/java/android/content/pm/parsing/result/ParseTypeImpl.java
+++ b/core/java/android/content/pm/parsing/result/ParseTypeImpl.java
@@ -18,12 +18,16 @@ package android.content.pm.parsing.result;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.parsing.ParsingUtils;
+import android.os.ServiceManager;
import android.util.ArrayMap;
import android.util.Log;
import android.util.Slog;
+import com.android.internal.compat.IPlatformCompat;
import com.android.internal.util.CollectionUtils;
/** @hide */
@@ -61,6 +65,28 @@ public class ParseTypeImpl implements ParseInput, ParseResult<Object> {
private Integer mTargetSdkVersion;
/**
+ * Assumes {@link Context#PLATFORM_COMPAT_SERVICE} is available to the caller. For use
+ * with {@link android.content.pm.parsing.ApkLiteParseUtils} or similar where parsing is
+ * done outside of {@link com.android.server.pm.PackageManagerService}.
+ */
+ public static ParseTypeImpl forDefaultParsing() {
+ IPlatformCompat platformCompat = IPlatformCompat.Stub.asInterface(
+ ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE));
+ return new ParseTypeImpl((changeId, packageName, targetSdkVersion) -> {
+ ApplicationInfo appInfo = new ApplicationInfo();
+ appInfo.packageName = packageName;
+ appInfo.targetSdkVersion = targetSdkVersion;
+ try {
+ return platformCompat.isChangeEnabled(changeId, appInfo);
+ } catch (Exception e) {
+ // This shouldn't happen, but assume enforcement if it does
+ Slog.wtf(ParsingUtils.TAG, "IPlatformCompat query failed", e);
+ return true;
+ }
+ });
+ }
+
+ /**
* @param callback if nullable, fallback to manual targetSdk > Q check
*/
public ParseTypeImpl(@NonNull Callback callback) {
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 187274a837a0..7912dacac377 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -635,10 +635,11 @@ public class UserManager {
/**
* Specifies if a user is disallowed from adding new users. This can only be set by device
- * owners, profile owners on the primary user or profile owners of organization-owned managed
- * profiles on the parent profile. The default value is <code>false</code>.
+ * owners or profile owners on the primary user. The default value is <code>false</code>.
* <p>This restriction has no effect on secondary users and managed profiles since only the
* primary user can add other users.
+ * <p> When the device is an organization-owned device provisioned with a managed profile,
+ * this restriction will be set as a base restriction which cannot be removed by any admin.
*
* <p>Key for user restrictions.
* <p>Type: Boolean
diff --git a/core/java/android/permission/PermissionControllerManager.java b/core/java/android/permission/PermissionControllerManager.java
index ed429dd835c3..06caa03e3cb4 100644
--- a/core/java/android/permission/PermissionControllerManager.java
+++ b/core/java/android/permission/PermissionControllerManager.java
@@ -56,10 +56,15 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.infra.AndroidFuture;
import com.android.internal.infra.RemoteStream;
import com.android.internal.infra.ServiceConnector;
+import com.android.internal.os.TransferPipe;
import com.android.internal.util.CollectionUtils;
import libcore.util.EmptyArray;
+import java.io.FileDescriptor;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
@@ -67,7 +72,9 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
+import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
+import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
/**
@@ -476,6 +483,36 @@ public final class PermissionControllerManager {
}
/**
+ * Dump permission controller state.
+ *
+ * @hide
+ */
+ public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @Nullable String[] args) {
+ CompletableFuture<Throwable> dumpResult = new CompletableFuture<>();
+ mRemoteService.postForResult(
+ service -> TransferPipe.dumpAsync(service.asBinder(), args))
+ .whenComplete(
+ (dump, err) -> {
+ try (FileOutputStream out = new FileOutputStream(fd)) {
+ out.write(dump);
+ } catch (IOException | NullPointerException e) {
+ Log.e(TAG, "Could for forwards permission controller dump", e);
+ }
+
+ dumpResult.complete(err);
+ });
+
+ try {
+ Throwable err = dumpResult.get(UNBIND_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
+ if (err != null) {
+ throw err;
+ }
+ } catch (Throwable e) {
+ Log.e(TAG, "Could not dump permission controller state", e);
+ }
+ }
+
+ /**
* Gets the runtime permissions for an app.
*
* @param packageName The package for which to query.
diff --git a/core/java/android/permission/PermissionControllerService.java b/core/java/android/permission/PermissionControllerService.java
index 82a7d788100d..c6ede32d0864 100644
--- a/core/java/android/permission/PermissionControllerService.java
+++ b/core/java/android/permission/PermissionControllerService.java
@@ -50,9 +50,11 @@ import com.android.internal.infra.AndroidFuture;
import com.android.internal.util.CollectionUtils;
import com.android.internal.util.Preconditions;
+import java.io.FileDescriptor;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -494,6 +496,11 @@ public abstract class PermissionControllerService extends Service {
"packageName cannot be null");
onOneTimePermissionSessionTimeout(packageName);
}
+
+ @Override
+ protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
+ PermissionControllerService.this.dump(fd, writer, args);
+ }
};
}
}
diff --git a/core/java/android/provider/DocumentsProvider.java b/core/java/android/provider/DocumentsProvider.java
index 2e00c0c9d2a4..327bca268a7b 100644
--- a/core/java/android/provider/DocumentsProvider.java
+++ b/core/java/android/provider/DocumentsProvider.java
@@ -1274,8 +1274,6 @@ public abstract class DocumentsProvider extends ContentProvider {
out.putParcelable(DocumentsContract.EXTRA_RESULT, path);
} else if (METHOD_GET_DOCUMENT_METADATA.equals(method)) {
- enforceReadPermissionInner(documentUri, getCallingPackage(),
- getCallingAttributionTag(), null);
return getDocumentMetadata(documentId);
} else {
throw new UnsupportedOperationException("Method not supported " + method);
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 6c8bd7f352d4..c0d0c21af1ab 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -14033,6 +14033,14 @@ public final class Settings {
"zram_enabled";
/**
+ * Whether the app freezer is enabled on this device.
+ * The value of "enabled" enables the app freezer, "disabled" disables it and
+ * "device_default" will let the system decide whether to enable the freezer or not
+ * @hide
+ */
+ public static final String CACHED_APPS_FREEZER_ENABLED = "cached_apps_freezer";
+
+ /**
* Configuration flags for smart replies in notifications.
* This is encoded as a key=value list, separated by commas. Ex:
*
diff --git a/core/java/android/service/autofill/IInlineSuggestionUi.aidl b/core/java/android/service/autofill/IInlineSuggestionUi.aidl
new file mode 100644
index 000000000000..7289853064f8
--- /dev/null
+++ b/core/java/android/service/autofill/IInlineSuggestionUi.aidl
@@ -0,0 +1,29 @@
+/*
+ * 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.service.autofill;
+
+import android.service.autofill.ISurfacePackageResultCallback;
+
+/**
+ * Interface to interact with a remote inline suggestion UI.
+ *
+ * @hide
+ */
+oneway interface IInlineSuggestionUi {
+ void getSurfacePackage(ISurfacePackageResultCallback callback);
+ void releaseSurfaceControlViewHost();
+}
diff --git a/core/java/android/service/autofill/IInlineSuggestionUiCallback.aidl b/core/java/android/service/autofill/IInlineSuggestionUiCallback.aidl
index 172cfef9fee2..97eb790b9acc 100644
--- a/core/java/android/service/autofill/IInlineSuggestionUiCallback.aidl
+++ b/core/java/android/service/autofill/IInlineSuggestionUiCallback.aidl
@@ -18,17 +18,19 @@ package android.service.autofill;
import android.content.IntentSender;
import android.os.IBinder;
+import android.service.autofill.IInlineSuggestionUi;
import android.view.SurfaceControlViewHost;
/**
- * Interface to receive events from inline suggestions.
+ * Interface to receive events from a remote inline suggestion UI.
*
* @hide
*/
oneway interface IInlineSuggestionUiCallback {
void onClick();
void onLongClick();
- void onContent(in SurfaceControlViewHost.SurfacePackage surface, int width, int height);
+ void onContent(in IInlineSuggestionUi content, in SurfaceControlViewHost.SurfacePackage surface,
+ int width, int height);
void onError();
void onTransferTouchFocusToImeWindow(in IBinder sourceInputToken, int displayId);
void onStartIntentSender(in IntentSender intentSender);
diff --git a/core/java/android/service/autofill/ISurfacePackageResultCallback.aidl b/core/java/android/service/autofill/ISurfacePackageResultCallback.aidl
new file mode 100644
index 000000000000..0c2c624952eb
--- /dev/null
+++ b/core/java/android/service/autofill/ISurfacePackageResultCallback.aidl
@@ -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.
+ */
+
+package android.service.autofill;
+
+import android.view.SurfaceControlViewHost;
+
+/**
+ * Interface to receive a SurfaceControlViewHost.SurfacePackage.
+ *
+ * @hide
+ */
+oneway interface ISurfacePackageResultCallback {
+ void onResult(in SurfaceControlViewHost.SurfacePackage result);
+}
diff --git a/core/java/android/service/autofill/InlineSuggestionRenderService.java b/core/java/android/service/autofill/InlineSuggestionRenderService.java
index 6c22b1936d74..3ea443bab3f8 100644
--- a/core/java/android/service/autofill/InlineSuggestionRenderService.java
+++ b/core/java/android/service/autofill/InlineSuggestionRenderService.java
@@ -33,6 +33,7 @@ import android.os.Looper;
import android.os.RemoteCallback;
import android.os.RemoteException;
import android.util.Log;
+import android.util.LruCache;
import android.util.Size;
import android.view.Display;
import android.view.SurfaceControlViewHost;
@@ -40,6 +41,8 @@ import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
+import java.lang.ref.WeakReference;
+
/**
* A service that renders an inline presentation view given the {@link InlinePresentation}.
*
@@ -65,6 +68,27 @@ public abstract class InlineSuggestionRenderService extends Service {
private IInlineSuggestionUiCallback mCallback;
+
+ /**
+ * A local LRU cache keeping references to the inflated {@link SurfaceControlViewHost}s, so
+ * they can be released properly when no longer used. Each view needs to be tracked separately,
+ * therefore for simplicity we use the hash code of the value object as key in the cache.
+ */
+ private final LruCache<InlineSuggestionUiImpl, Boolean> mActiveInlineSuggestions =
+ new LruCache<InlineSuggestionUiImpl, Boolean>(30) {
+ @Override
+ public void entryRemoved(boolean evicted, InlineSuggestionUiImpl key,
+ Boolean oldValue,
+ Boolean newValue) {
+ if (evicted) {
+ Log.w(TAG,
+ "Hit max=100 entries in the cache. Releasing oldest one to make "
+ + "space.");
+ key.releaseSurfaceControlViewHost();
+ }
+ }
+ };
+
/**
* If the specified {@code width}/{@code height} is an exact value, then it will be returned as
* is, otherwise the method tries to measure a size that is just large enough to fit the view
@@ -169,8 +193,14 @@ public abstract class InlineSuggestionRenderService extends Service {
return true;
});
- sendResult(callback, host.getSurfacePackage(), measuredSize.getWidth(),
- measuredSize.getHeight());
+ try {
+ InlineSuggestionUiImpl uiImpl = new InlineSuggestionUiImpl(host, mHandler);
+ mActiveInlineSuggestions.put(uiImpl, true);
+ callback.onContent(new InlineSuggestionUiWrapper(uiImpl), host.getSurfacePackage(),
+ measuredSize.getWidth(), measuredSize.getHeight());
+ } catch (RemoteException e) {
+ Log.w(TAG, "RemoteException calling onContent()");
+ }
} finally {
updateDisplay(Display.DEFAULT_DISPLAY);
}
@@ -181,12 +211,87 @@ public abstract class InlineSuggestionRenderService extends Service {
callback.sendResult(rendererInfo);
}
- private void sendResult(@NonNull IInlineSuggestionUiCallback callback,
- @Nullable SurfaceControlViewHost.SurfacePackage surface, int width, int height) {
- try {
- callback.onContent(surface, width, height);
- } catch (RemoteException e) {
- Log.w(TAG, "RemoteException calling onContent(" + surface + ")");
+ /**
+ * A wrapper class around the {@link InlineSuggestionUiImpl} to ensure it's not strongly
+ * reference by the remote system server process.
+ */
+ private static final class InlineSuggestionUiWrapper extends
+ android.service.autofill.IInlineSuggestionUi.Stub {
+
+ private final WeakReference<InlineSuggestionUiImpl> mUiImpl;
+
+ InlineSuggestionUiWrapper(InlineSuggestionUiImpl uiImpl) {
+ mUiImpl = new WeakReference<>(uiImpl);
+ }
+
+ @Override
+ public void releaseSurfaceControlViewHost() {
+ final InlineSuggestionUiImpl uiImpl = mUiImpl.get();
+ if (uiImpl != null) {
+ uiImpl.releaseSurfaceControlViewHost();
+ }
+ }
+
+ @Override
+ public void getSurfacePackage(ISurfacePackageResultCallback callback) {
+ final InlineSuggestionUiImpl uiImpl = mUiImpl.get();
+ if (uiImpl != null) {
+ uiImpl.getSurfacePackage(callback);
+ }
+ }
+ }
+
+ /**
+ * Keeps track of a SurfaceControlViewHost to ensure it's released when its lifecycle ends.
+ *
+ * <p>This class is thread safe, because all the outside calls are piped into a single
+ * handler thread to be processed.
+ */
+ private final class InlineSuggestionUiImpl {
+
+ @Nullable
+ private SurfaceControlViewHost mViewHost;
+ @NonNull
+ private final Handler mHandler;
+
+ InlineSuggestionUiImpl(SurfaceControlViewHost viewHost, Handler handler) {
+ this.mViewHost = viewHost;
+ this.mHandler = handler;
+ }
+
+ /**
+ * Call {@link SurfaceControlViewHost#release()} to release it. After this, this view is
+ * not usable, and any further calls to the
+ * {@link #getSurfacePackage(ISurfacePackageResultCallback)} will get {@code null} result.
+ */
+ public void releaseSurfaceControlViewHost() {
+ mHandler.post(() -> {
+ if (mViewHost == null) {
+ return;
+ }
+ Log.v(TAG, "Releasing inline suggestion view host");
+ mViewHost.release();
+ mViewHost = null;
+ InlineSuggestionRenderService.this.mActiveInlineSuggestions.remove(
+ InlineSuggestionUiImpl.this);
+ Log.v(TAG, "Removed the inline suggestion from the cache, current size="
+ + InlineSuggestionRenderService.this.mActiveInlineSuggestions.size());
+ });
+ }
+
+ /**
+ * Sends back a new {@link android.view.SurfaceControlViewHost.SurfacePackage} if the view
+ * is not released, {@code null} otherwise.
+ */
+ public void getSurfacePackage(ISurfacePackageResultCallback callback) {
+ Log.d(TAG, "getSurfacePackage");
+ mHandler.post(() -> {
+ try {
+ callback.onResult(mViewHost == null ? null : mViewHost.getSurfacePackage());
+ } catch (RemoteException e) {
+ Log.w(TAG, "RemoteException calling onSurfacePackage");
+ }
+ });
}
}
diff --git a/core/java/android/service/dreams/DreamActivity.java b/core/java/android/service/dreams/DreamActivity.java
index 0a29edc81f2f..09d1bb9d2b12 100644
--- a/core/java/android/service/dreams/DreamActivity.java
+++ b/core/java/android/service/dreams/DreamActivity.java
@@ -21,6 +21,8 @@ import android.app.Activity;
import android.os.Bundle;
import android.view.WindowInsets;
+import com.android.internal.R;
+
/**
* The Activity used by the {@link DreamService} to draw screensaver content
* on the screen. This activity runs in dream application's process, but is started by a
@@ -56,8 +58,20 @@ public class DreamActivity extends Activity {
if (callback != null) {
callback.onActivityCreated(this);
}
+ }
+ @Override
+ public void onResume() {
+ super.onResume();
// Hide all insets (nav bar, status bar, etc) when the dream is showing
getWindow().getInsetsController().hide(WindowInsets.Type.systemBars());
+ overridePendingTransition(R.anim.dream_activity_open_enter,
+ R.anim.dream_activity_open_exit);
+ }
+
+ @Override
+ public void finishAndRemoveTask() {
+ super.finishAndRemoveTask();
+ overridePendingTransition(0, R.anim.dream_activity_close_exit);
}
}
diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java
index 337027ef5bc9..d2dfb29ba25c 100644
--- a/core/java/android/service/dreams/DreamService.java
+++ b/core/java/android/service/dreams/DreamService.java
@@ -1052,7 +1052,6 @@ public class DreamService extends Service implements Window.Callback {
mWindow.requestFeature(Window.FEATURE_NO_TITLE);
WindowManager.LayoutParams lp = mWindow.getAttributes();
- lp.windowAnimations = com.android.internal.R.style.Animation_Dream;
lp.flags |= (WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
| WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR
| WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
@@ -1076,8 +1075,12 @@ public class DreamService extends Service implements Window.Callback {
@Override
public void onViewDetachedFromWindow(View v) {
- mActivity = null;
- finish();
+ if (mActivity == null || !mActivity.isChangingConfigurations()) {
+ // Only stop the dream if the view is not detached by relaunching
+ // activity for configuration changes.
+ mActivity = null;
+ finish();
+ }
}
});
}
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index a135b0ca148b..fcc4a6ec4d92 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -1117,7 +1117,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
* Cancel on-going animation to show/hide {@link InsetsType}.
*/
@VisibleForTesting
- public void cancelExistingAnimation() {
+ public void cancelExistingAnimations() {
cancelExistingControllers(all());
}
diff --git a/core/java/android/view/InsetsSourceConsumer.java b/core/java/android/view/InsetsSourceConsumer.java
index 58ec9ec11e56..2dcfd899adf4 100644
--- a/core/java/android/view/InsetsSourceConsumer.java
+++ b/core/java/android/view/InsetsSourceConsumer.java
@@ -200,15 +200,6 @@ public class InsetsSourceConsumer {
}
boolean applyLocalVisibilityOverride() {
- return applyLocalVisibilityOverride(false /* notifyWithoutControl */);
- }
-
- /**
- * @param notifyWithoutControl set it true when the caller wants to notify the visibility
- * changes even if the consumer doesn't have the control.
- * @return true if it needs to notify the visibility changes to the controller
- */
- private boolean applyLocalVisibilityOverride(boolean notifyWithoutControl) {
InsetsSource source = mState.peekSource(mType);
final boolean isVisible = source != null && source.isVisible();
final boolean hasControl = mSourceControl != null;
@@ -220,7 +211,7 @@ public class InsetsSourceConsumer {
// If we don't have control, we are not able to change the visibility.
if (!hasControl) {
- return notifyWithoutControl;
+ return false;
}
if (isVisible == mRequestedVisible) {
return false;
@@ -302,9 +293,7 @@ public class InsetsSourceConsumer {
mRequestedVisible = requestedVisible;
mIsAnimationPending = false;
}
- // We need to notify the visibility changed even if we don't have mSourceControl in order
- // to update color views.
- if (applyLocalVisibilityOverride(true /* notifyWithoutControl */)) {
+ if (applyLocalVisibilityOverride()) {
mController.notifyVisibilityChanged();
}
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 42f11c162473..a17af6c90617 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -4624,6 +4624,8 @@ public final class ViewRootImpl implements ViewParent,
setAccessibilityFocus(null, null);
+ mInsetsController.cancelExistingAnimations();
+
mView.assignParent(null);
mView = null;
mAttachInfo.mRootView = null;
diff --git a/core/java/android/view/ViewRootInsetsControllerHost.java b/core/java/android/view/ViewRootInsetsControllerHost.java
index d8bf58f78339..9674a80c8159 100644
--- a/core/java/android/view/ViewRootInsetsControllerHost.java
+++ b/core/java/android/view/ViewRootInsetsControllerHost.java
@@ -104,10 +104,10 @@ public class ViewRootInsetsControllerHost implements InsetsController.Host {
@Override
public void applySurfaceParams(SyncRtSurfaceTransactionApplier.SurfaceParams... params) {
+ if (mViewRoot.mView == null) {
+ throw new IllegalStateException("View of the ViewRootImpl is not initiated.");
+ }
if (mApplier == null) {
- if (mViewRoot.mView == null) {
- throw new IllegalStateException("View of the ViewRootImpl is not initiated.");
- }
mApplier = new SyncRtSurfaceTransactionApplier(mViewRoot.mView);
}
if (mViewRoot.mView.isHardwareAccelerated()) {
diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java
index 95d6d651cc79..15604a2ae2c1 100644
--- a/core/java/android/view/WindowlessWindowManager.java
+++ b/core/java/android/view/WindowlessWindowManager.java
@@ -41,6 +41,7 @@ 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;
@@ -239,21 +240,19 @@ public class WindowlessWindowManager implements IWindowSession {
}
WindowManager.LayoutParams attrs = state.mParams;
- final Rect surfaceInsets = attrs.surfaceInsets;
- int width = surfaceInsets != null ?
- attrs.width + surfaceInsets.left + surfaceInsets.right : attrs.width;
- int height = surfaceInsets != null ?
- attrs.height + surfaceInsets.top + surfaceInsets.bottom : attrs.height;
-
- t.setBufferSize(sc, width, height)
- .setOpaque(sc, isOpaque(attrs));
if (viewFlags == View.VISIBLE) {
- t.show(sc);
+ final Rect surfaceInsets = attrs.surfaceInsets;
+ int width = surfaceInsets != null
+ ? attrs.width + surfaceInsets.left + surfaceInsets.right : attrs.width;
+ int height = surfaceInsets != null
+ ? attrs.height + surfaceInsets.top + surfaceInsets.bottom : attrs.height;
+
+ t.setBufferSize(sc, width, height).setOpaque(sc, isOpaque(attrs)).show(sc).apply();
+ outSurfaceControl.copyFrom(sc);
} else {
- t.hide(sc);
+ t.hide(sc).apply();
+ outSurfaceControl.release();
}
- t.apply();
- outSurfaceControl.copyFrom(sc);
outFrame.set(0, 0, attrs.width, attrs.height);
mergedConfiguration.setConfiguration(mConfiguration, mConfiguration);
diff --git a/core/java/android/view/inputmethod/InlineSuggestion.java b/core/java/android/view/inputmethod/InlineSuggestion.java
index 6b1a480986c8..4c72474435a4 100644
--- a/core/java/android/view/inputmethod/InlineSuggestion.java
+++ b/core/java/android/view/inputmethod/InlineSuggestion.java
@@ -18,11 +18,13 @@ package android.view.inputmethod;
import android.annotation.BinderThread;
import android.annotation.CallbackExecutor;
+import android.annotation.MainThread;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.TestApi;
import android.content.Context;
-import android.os.AsyncTask;
+import android.os.Handler;
+import android.os.Looper;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.RemoteException;
@@ -42,26 +44,26 @@ import java.util.concurrent.Executor;
import java.util.function.Consumer;
/**
- * This class represents an inline suggestion which is made by one app
- * and can be embedded into the UI of another. Suggestions may contain
- * sensitive information not known to the host app which needs to be
- * protected from spoofing. To address that the suggestion view inflated
- * on demand for embedding is created in such a way that the hosting app
- * cannot introspect its content and cannot interact with it.
+ * This class represents an inline suggestion which is made by one app and can be embedded into the
+ * UI of another. Suggestions may contain sensitive information not known to the host app which
+ * needs to be protected from spoofing. To address that the suggestion view inflated on demand for
+ * embedding is created in such a way that the hosting app cannot introspect its content and cannot
+ * interact with it.
*/
-@DataClass(
- genEqualsHashCode = true,
- genToString = true,
- genHiddenConstDefs = true,
+@DataClass(genEqualsHashCode = true, genToString = true, genHiddenConstDefs = true,
genHiddenConstructor = true)
-@DataClass.Suppress({"getContentProvider"})
public final class InlineSuggestion implements Parcelable {
private static final String TAG = "InlineSuggestion";
- private final @NonNull InlineSuggestionInfo mInfo;
+ @NonNull
+ private final InlineSuggestionInfo mInfo;
- private final @Nullable IInlineContentProvider mContentProvider;
+ /**
+ * @hide
+ */
+ @Nullable
+ private final IInlineContentProvider mContentProvider;
/**
* Used to keep a strong reference to the callback so it doesn't get garbage collected.
@@ -69,7 +71,8 @@ public final class InlineSuggestion implements Parcelable {
* @hide
*/
@DataClass.ParcelWith(InlineContentCallbackImplParceling.class)
- private @Nullable InlineContentCallbackImpl mInlineContentCallback;
+ @Nullable
+ private InlineContentCallbackImpl mInlineContentCallback;
/**
* Creates a new {@link InlineSuggestion}, for testing purpose.
@@ -87,8 +90,7 @@ public final class InlineSuggestion implements Parcelable {
*
* @hide
*/
- public InlineSuggestion(
- @NonNull InlineSuggestionInfo info,
+ public InlineSuggestion(@NonNull InlineSuggestionInfo info,
@Nullable IInlineContentProvider contentProvider) {
this(info, contentProvider, /* inlineContentCallback */ null);
}
@@ -96,25 +98,30 @@ public final class InlineSuggestion implements Parcelable {
/**
* Inflates a view with the content of this suggestion at a specific size.
*
- * <p> The size must be either 1) between the
- * {@link android.widget.inline.InlinePresentationSpec#getMinSize() min size} and the
- * {@link android.widget.inline.InlinePresentationSpec#getMaxSize() max size} of the
- * presentation spec returned by {@link InlineSuggestionInfo#getInlinePresentationSpec()},
- * or 2) {@link ViewGroup.LayoutParams#WRAP_CONTENT}. If the size is set to
- * {@link ViewGroup.LayoutParams#WRAP_CONTENT}, then the size of the inflated view will be just
- * large enough to fit the content, while still conforming to the min / max size specified by
- * the {@link android.widget.inline.InlinePresentationSpec}.
+ * <p> Each dimension of the size must satisfy one of the following conditions:
+ *
+ * <ol>
+ * <li>between {@link android.widget.inline.InlinePresentationSpec#getMinSize()} and
+ * {@link android.widget.inline.InlinePresentationSpec#getMaxSize()} of the presentation spec
+ * from {@code mInfo}
+ * <li>{@link ViewGroup.LayoutParams#WRAP_CONTENT}
+ * </ol>
+ *
+ * If the size is set to {@link
+ * ViewGroup.LayoutParams#WRAP_CONTENT}, then the size of the inflated view will be just large
+ * enough to fit the content, while still conforming to the min / max size specified by the
+ * {@link android.widget.inline.InlinePresentationSpec}.
*
* <p> The caller can attach an {@link android.view.View.OnClickListener} and/or an
- * {@link android.view.View.OnLongClickListener} to the view in the
- * {@code callback} to receive click and long click events on the view.
+ * {@link android.view.View.OnLongClickListener} to the view in the {@code callback} to receive
+ * click and long click events on the view.
*
* @param context Context in which to inflate the view.
- * @param size The size at which to inflate the suggestion. For each dimension, it maybe
- * an exact value or {@link ViewGroup.LayoutParams#WRAP_CONTENT}.
- * @param callback Callback for receiving the inflated view, where the
- * {@link ViewGroup.LayoutParams} of the view is set as the actual size of
- * the underlying remote view.
+ * @param size The size at which to inflate the suggestion. For each dimension, it maybe an
+ * exact value or {@link ViewGroup.LayoutParams#WRAP_CONTENT}.
+ * @param callback Callback for receiving the inflated view, where the {@link
+ * ViewGroup.LayoutParams} of the view is set as the actual size of the
+ * underlying remote view.
* @throws IllegalArgumentException If an invalid argument is passed.
* @throws IllegalStateException If this method is already called.
*/
@@ -130,19 +137,17 @@ public final class InlineSuggestion implements Parcelable {
+ ", nor wrap_content");
}
mInlineContentCallback = getInlineContentCallback(context, callbackExecutor, callback);
- AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> {
- if (mContentProvider == null) {
- callback.accept(/* view */ null);
- return;
- }
- try {
- mContentProvider.provideContent(size.getWidth(), size.getHeight(),
- new InlineContentCallbackWrapper(mInlineContentCallback));
- } catch (RemoteException e) {
- Slog.w(TAG, "Error creating suggestion content surface: " + e);
- callback.accept(/* view */ null);
- }
- });
+ if (mContentProvider == null) {
+ callbackExecutor.execute(() -> callback.accept(/* view */ null));
+ return;
+ }
+ try {
+ mContentProvider.provideContent(size.getWidth(), size.getHeight(),
+ new InlineContentCallbackWrapper(mInlineContentCallback));
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Error creating suggestion content surface: " + e);
+ callbackExecutor.execute(() -> callback.accept(/* view */ null));
+ }
}
/**
@@ -161,9 +166,14 @@ public final class InlineSuggestion implements Parcelable {
if (mInlineContentCallback != null) {
throw new IllegalStateException("Already called #inflate()");
}
- return new InlineContentCallbackImpl(context, callbackExecutor, callback);
+ return new InlineContentCallbackImpl(context, mContentProvider, callbackExecutor,
+ callback);
}
+ /**
+ * A wrapper class around the {@link InlineContentCallbackImpl} to ensure it's not strongly
+ * reference by the remote system server process.
+ */
private static final class InlineContentCallbackWrapper extends IInlineContentCallback.Stub {
private final WeakReference<InlineContentCallbackImpl> mCallbackImpl;
@@ -201,17 +211,68 @@ public final class InlineSuggestion implements Parcelable {
}
}
+ /**
+ * Handles the communication between the inline suggestion view in current (IME) process and
+ * the remote view provided from the system server.
+ *
+ * <p>This class is thread safe, because all the outside calls are piped into a single
+ * handler thread to be processed.
+ */
private static final class InlineContentCallbackImpl {
- private final @NonNull Context mContext;
- private final @NonNull Executor mCallbackExecutor;
- private final @NonNull Consumer<InlineContentView> mCallback;
- private @Nullable InlineContentView mView;
+ @NonNull
+ private final Handler mMainHandler = new Handler(Looper.getMainLooper());
+
+ @NonNull
+ private final Context mContext;
+ @Nullable
+ private final IInlineContentProvider mInlineContentProvider;
+ @NonNull
+ private final Executor mCallbackExecutor;
+
+ /**
+ * Callback from the client (IME) that will receive the inflated suggestion view. It'll
+ * only be called once when the view SurfacePackage is first sent back to the client. Any
+ * updates to the view due to attach to window and detach from window events will be
+ * handled under the hood, transparent from the client.
+ */
+ @NonNull
+ private final Consumer<InlineContentView> mCallback;
+
+ /**
+ * Indicates whether the first content has been received or not.
+ */
+ private boolean mFirstContentReceived = false;
+
+ /**
+ * The client (IME) side view which internally wraps a remote view. It'll be set when
+ * {@link #onContent(SurfaceControlViewHost.SurfacePackage, int, int)} is called, which
+ * should only happen once in the lifecycle of this inline suggestion instance.
+ */
+ @Nullable
+ private InlineContentView mView;
+
+ /**
+ * The SurfacePackage pointing to the remote view. It's cached here to be sent to the next
+ * available consumer.
+ */
+ @Nullable
+ private SurfaceControlViewHost.SurfacePackage mSurfacePackage;
+
+ /**
+ * The callback (from the {@link InlineContentView}) which consumes the surface package.
+ * It's cached here to be called when the SurfacePackage is returned from the remote
+ * view owning process.
+ */
+ @Nullable
+ private Consumer<SurfaceControlViewHost.SurfacePackage> mSurfacePackageConsumer;
InlineContentCallbackImpl(@NonNull Context context,
+ @Nullable IInlineContentProvider inlineContentProvider,
@NonNull @CallbackExecutor Executor callbackExecutor,
@NonNull Consumer<InlineContentView> callback) {
mContext = context;
+ mInlineContentProvider = inlineContentProvider;
mCallbackExecutor = callbackExecutor;
mCallback = callback;
}
@@ -219,28 +280,110 @@ public final class InlineSuggestion implements Parcelable {
@BinderThread
public void onContent(SurfaceControlViewHost.SurfacePackage content, int width,
int height) {
- if (content == null) {
+ mMainHandler.post(() -> handleOnContent(content, width, height));
+ }
+
+ @MainThread
+ private void handleOnContent(SurfaceControlViewHost.SurfacePackage content, int width,
+ int height) {
+ if (!mFirstContentReceived) {
+ handleOnFirstContentReceived(content, width, height);
+ mFirstContentReceived = true;
+ } else {
+ handleOnSurfacePackage(content);
+ }
+ }
+
+ /**
+ * Called when the view content is returned for the first time.
+ */
+ @MainThread
+ private void handleOnFirstContentReceived(SurfaceControlViewHost.SurfacePackage content,
+ int width, int height) {
+ mSurfacePackage = content;
+ if (mSurfacePackage == null) {
mCallbackExecutor.execute(() -> mCallback.accept(/* view */null));
} else {
mView = new InlineContentView(mContext);
mView.setLayoutParams(new ViewGroup.LayoutParams(width, height));
- mView.setChildSurfacePackage(content);
+ mView.setChildSurfacePackageUpdater(getSurfacePackageUpdater());
mCallbackExecutor.execute(() -> mCallback.accept(mView));
}
}
+ /**
+ * Called when any subsequent SurfacePackage is returned from the remote view owning
+ * process.
+ */
+ @MainThread
+ private void handleOnSurfacePackage(SurfaceControlViewHost.SurfacePackage surfacePackage) {
+ mSurfacePackage = surfacePackage;
+ if (mSurfacePackage != null && mSurfacePackageConsumer != null) {
+ mSurfacePackageConsumer.accept(mSurfacePackage);
+ mSurfacePackageConsumer = null;
+ }
+ }
+
+ @MainThread
+ private void handleOnSurfacePackageReleased() {
+ mSurfacePackage = null;
+ try {
+ mInlineContentProvider.onSurfacePackageReleased();
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Error calling onSurfacePackageReleased(): " + e);
+ }
+ }
+
+ @MainThread
+ private void handleGetSurfacePackage(
+ Consumer<SurfaceControlViewHost.SurfacePackage> consumer) {
+ if (mSurfacePackage != null) {
+ consumer.accept(mSurfacePackage);
+ } else {
+ mSurfacePackageConsumer = consumer;
+ try {
+ mInlineContentProvider.requestSurfacePackage();
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Error calling getSurfacePackage(): " + e);
+ consumer.accept(null);
+ mSurfacePackageConsumer = null;
+ }
+ }
+ }
+
+ private InlineContentView.SurfacePackageUpdater getSurfacePackageUpdater() {
+ return new InlineContentView.SurfacePackageUpdater() {
+ @Override
+ public void onSurfacePackageReleased() {
+ mMainHandler.post(
+ () -> InlineContentCallbackImpl.this.handleOnSurfacePackageReleased());
+ }
+
+ @Override
+ public void getSurfacePackage(
+ Consumer<SurfaceControlViewHost.SurfacePackage> consumer) {
+ mMainHandler.post(
+ () -> InlineContentCallbackImpl.this.handleGetSurfacePackage(consumer));
+ }
+ };
+ }
+
@BinderThread
public void onClick() {
- if (mView != null && mView.hasOnClickListeners()) {
- mView.callOnClick();
- }
+ mMainHandler.post(() -> {
+ if (mView != null && mView.hasOnClickListeners()) {
+ mView.callOnClick();
+ }
+ });
}
@BinderThread
public void onLongClick() {
- if (mView != null && mView.hasOnLongClickListeners()) {
- mView.performLongClick();
- }
+ mMainHandler.post(() -> {
+ if (mView != null && mView.hasOnLongClickListeners()) {
+ mView.performLongClick();
+ }
+ });
}
}
@@ -262,6 +405,7 @@ public final class InlineSuggestion implements Parcelable {
+
// Code below generated by codegen v1.0.15.
//
// DO NOT MODIFY!
@@ -302,6 +446,14 @@ public final class InlineSuggestion implements Parcelable {
}
/**
+ * @hide
+ */
+ @DataClass.Generated.Member
+ public @Nullable IInlineContentProvider getContentProvider() {
+ return mContentProvider;
+ }
+
+ /**
* Used to keep a strong reference to the callback so it doesn't get garbage collected.
*
* @hide
@@ -421,7 +573,7 @@ public final class InlineSuggestion implements Parcelable {
};
@DataClass.Generated(
- time = 1587771173367L,
+ time = 1588308946517L,
codegenVersion = "1.0.15",
sourceFile = "frameworks/base/core/java/android/view/inputmethod/InlineSuggestion.java",
inputSignatures = "private static final java.lang.String TAG\nprivate final @android.annotation.NonNull android.view.inputmethod.InlineSuggestionInfo mInfo\nprivate final @android.annotation.Nullable com.android.internal.view.inline.IInlineContentProvider mContentProvider\nprivate @com.android.internal.util.DataClass.ParcelWith(android.view.inputmethod.InlineSuggestion.InlineContentCallbackImplParceling.class) @android.annotation.Nullable android.view.inputmethod.InlineSuggestion.InlineContentCallbackImpl mInlineContentCallback\npublic static @android.annotation.TestApi @android.annotation.NonNull android.view.inputmethod.InlineSuggestion newInlineSuggestion(android.view.inputmethod.InlineSuggestionInfo)\npublic void inflate(android.content.Context,android.util.Size,java.util.concurrent.Executor,java.util.function.Consumer<android.widget.inline.InlineContentView>)\nprivate static boolean isValid(int,int,int)\nprivate synchronized android.view.inputmethod.InlineSuggestion.InlineContentCallbackImpl getInlineContentCallback(android.content.Context,java.util.concurrent.Executor,java.util.function.Consumer<android.widget.inline.InlineContentView>)\nclass InlineSuggestion extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genHiddenConstDefs=true, genHiddenConstructor=true)")
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 3cf61098f11c..71dd6653f6a6 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -645,6 +645,11 @@ public final class InputMethodManager {
@Override
public void setCurrentRootView(ViewRootImpl rootView) {
synchronized (mH) {
+ if (mCurRootView != null) {
+ // Reset the last served view and restart window focus state of the root view.
+ mCurRootView.getImeFocusController().setServedView(null);
+ mRestartOnNextWindowFocus = true;
+ }
mCurRootView = rootView;
}
}
diff --git a/core/java/android/widget/inline/InlineContentView.java b/core/java/android/widget/inline/InlineContentView.java
index 4f2af63626cf..8657e828a3f6 100644
--- a/core/java/android/widget/inline/InlineContentView.java
+++ b/core/java/android/widget/inline/InlineContentView.java
@@ -21,40 +21,45 @@ import android.annotation.Nullable;
import android.content.Context;
import android.graphics.PixelFormat;
import android.util.AttributeSet;
+import android.util.Log;
import android.view.SurfaceControl;
import android.view.SurfaceControlViewHost;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.ViewGroup;
+import java.util.function.Consumer;
+
/**
- * This class represents a view that holds opaque content from another app that
- * you can inline in your UI.
+ * This class represents a view that holds opaque content from another app that you can inline in
+ * your UI.
*
* <p>Since the content presented by this view is from another security domain,it is
- * shown on a remote surface preventing the host application from accessing that content.
- * Also the host application cannot interact with the inlined content by injecting touch
- * events or clicking programmatically.
+ * shown on a remote surface preventing the host application from accessing that content. Also the
+ * host application cannot interact with the inlined content by injecting touch events or clicking
+ * programmatically.
*
* <p>This view can be overlaid by other windows, i.e. redressed, but if this is the case
- * the inined UI would not be interactive. Sometimes this is desirable, e.g. animating
- * transitions.
+ * the inlined UI would not be interactive. Sometimes this is desirable, e.g. animating transitions.
*
* <p>By default the surface backing this view is shown on top of the hosting window such
- * that the inlined content is interactive. However, you can temporarily move the surface
- * under the hosting window which could be useful in some cases, e.g. animating transitions.
- * At this point the inlined content will not be interactive and the touch events would
- * be delivered to your app.
- * <p>
- * Instances of this class are created by the platform and can be programmatically attached
- * to your UI. Once you attach and detach this view it can not longer be reused and you
- * should obtain a new view from the platform via the dedicated APIs.
+ * that the inlined content is interactive. However, you can temporarily move the surface under the
+ * hosting window which could be useful in some cases, e.g. animating transitions. At this point the
+ * inlined content will not be interactive and the touch events would be delivered to your app.
+ *
+ * <p> Instances of this class are created by the platform and can be programmatically attached to
+ * your UI. Once the view is attached to the window, you may detach and reattach it to the window.
+ * It should work seamlessly from the hosting process's point of view.
*/
public class InlineContentView extends ViewGroup {
+ private static final String TAG = "InlineContentView";
+
+ private static final boolean DEBUG = false;
+
/**
- * Callback for observing the lifecycle of the surface control
- * that manipulates the backing secure embedded UI surface.
+ * Callback for observing the lifecycle of the surface control that manipulates the backing
+ * secure embedded UI surface.
*/
public interface SurfaceControlCallback {
/**
@@ -72,15 +77,41 @@ public class InlineContentView extends ViewGroup {
void onDestroyed(@NonNull SurfaceControl surfaceControl);
}
- private final @NonNull SurfaceHolder.Callback mSurfaceCallback = new SurfaceHolder.Callback() {
+ /**
+ * Callback for sending an updated surface package in case the previous one is released
+ * from the detached from window event, and for getting notified of such event.
+ *
+ * This is expected to be provided to the {@link InlineContentView} so it can get updates
+ * from and send updates to the remote content (i.e. surface package) provider.
+ *
+ * @hide
+ */
+ public interface SurfacePackageUpdater {
+
+ /**
+ * Called when the previous surface package is released due to view being detached
+ * from the window.
+ */
+ void onSurfacePackageReleased();
+
+ /**
+ * Called to request an updated surface package.
+ *
+ * @param consumer consumes the updated surface package.
+ */
+ void getSurfacePackage(Consumer<SurfaceControlViewHost.SurfacePackage> consumer);
+ }
+
+ @NonNull
+ private final SurfaceHolder.Callback mSurfaceCallback = new SurfaceHolder.Callback() {
@Override
public void surfaceCreated(@NonNull SurfaceHolder holder) {
mSurfaceControlCallback.onCreated(mSurfaceView.getSurfaceControl());
}
@Override
- public void surfaceChanged(@NonNull SurfaceHolder holder,
- int format, int width, int height) {
+ public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width,
+ int height) {
/* do nothing */
}
@@ -90,13 +121,17 @@ public class InlineContentView extends ViewGroup {
}
};
- private final @NonNull SurfaceView mSurfaceView;
+ @NonNull
+ private final SurfaceView mSurfaceView;
+
+ @Nullable
+ private SurfaceControlCallback mSurfaceControlCallback;
- private @Nullable SurfaceControlCallback mSurfaceControlCallback;
+ @Nullable
+ private SurfacePackageUpdater mSurfacePackageUpdater;
/**
* @inheritDoc
- *
* @hide
*/
public InlineContentView(@NonNull Context context) {
@@ -105,7 +140,6 @@ public class InlineContentView extends ViewGroup {
/**
* @inheritDoc
- *
* @hide
*/
public InlineContentView(@NonNull Context context, @Nullable AttributeSet attrs) {
@@ -114,7 +148,6 @@ public class InlineContentView extends ViewGroup {
/**
* @inheritDoc
- *
* @hide
*/
public InlineContentView(@NonNull Context context, @Nullable AttributeSet attrs,
@@ -123,20 +156,18 @@ public class InlineContentView extends ViewGroup {
}
/**
- * Gets the surface control. If the surface is not created this method
- * returns {@code null}.
+ * Gets the surface control. If the surface is not created this method returns {@code null}.
*
* @return The surface control.
- *
* @see #setSurfaceControlCallback(SurfaceControlCallback)
*/
- public @Nullable SurfaceControl getSurfaceControl() {
+ @Nullable
+ public SurfaceControl getSurfaceControl() {
return mSurfaceView.getSurfaceControl();
}
/**
* @inheritDoc
- *
* @hide
*/
public InlineContentView(@NonNull Context context, @Nullable AttributeSet attrs,
@@ -149,14 +180,35 @@ public class InlineContentView extends ViewGroup {
}
/**
- * Sets the embedded UI.
- * @param surfacePackage The embedded UI.
+ * Sets the embedded UI provider.
*
* @hide
*/
- public void setChildSurfacePackage(
- @Nullable SurfaceControlViewHost.SurfacePackage surfacePackage) {
- mSurfaceView.setChildSurfacePackage(surfacePackage);
+ public void setChildSurfacePackageUpdater(
+ @Nullable SurfacePackageUpdater surfacePackageUpdater) {
+ mSurfacePackageUpdater = surfacePackageUpdater;
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ if (DEBUG) Log.v(TAG, "onAttachedToWindow");
+ super.onAttachedToWindow();
+ if (mSurfacePackageUpdater != null) {
+ mSurfacePackageUpdater.getSurfacePackage(
+ sp -> {
+ if (DEBUG) Log.v(TAG, "Received new SurfacePackage");
+ mSurfaceView.setChildSurfacePackage(sp);
+ });
+ }
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ if (DEBUG) Log.v(TAG, "onDetachedFromWindow");
+ super.onDetachedFromWindow();
+ if (mSurfacePackageUpdater != null) {
+ mSurfacePackageUpdater.onSurfacePackageReleased();
+ }
}
@Override
@@ -165,8 +217,8 @@ public class InlineContentView extends ViewGroup {
}
/**
- * Sets a callback to observe the lifecycle of the surface control for
- * managing the backing surface.
+ * Sets a callback to observe the lifecycle of the surface control for managing the backing
+ * surface.
*
* @param callback The callback to set or {@code null} to clear.
*/
@@ -182,7 +234,6 @@ public class InlineContentView extends ViewGroup {
/**
* @return Whether the surface backing this view appears on top of its parent.
- *
* @see #setZOrderedOnTop(boolean)
*/
public boolean isZOrderedOnTop() {
@@ -190,17 +241,15 @@ public class InlineContentView extends ViewGroup {
}
/**
- * Controls whether the backing surface is placed on top of this view's window.
- * Normally, it is placed on top of the window, to allow interaction
- * with the inlined UI. Via this method, you can place the surface below the
- * window. This means that all of the contents of the window this view is in
- * will be visible on top of its surface.
+ * Controls whether the backing surface is placed on top of this view's window. Normally, it is
+ * placed on top of the window, to allow interaction with the inlined UI. Via this method, you
+ * can place the surface below the window. This means that all of the contents of the window
+ * this view is in will be visible on top of its surface.
*
* <p> The Z ordering can be changed dynamically if the backing surface is
* created, otherwise the ordering would be applied at surface construction time.
*
* @param onTop Whether to show the surface on top of this view's window.
- *
* @see #isZOrderedOnTop()
*/
public boolean setZOrderedOnTop(boolean onTop) {
diff --git a/core/java/android/window/TaskEmbedder.java b/core/java/android/window/TaskEmbedder.java
index 4257ce084829..ca6c568c2668 100644
--- a/core/java/android/window/TaskEmbedder.java
+++ b/core/java/android/window/TaskEmbedder.java
@@ -36,11 +36,14 @@ import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.Region;
import android.hardware.display.VirtualDisplay;
+import android.os.RemoteException;
import android.os.UserHandle;
import android.view.IWindow;
import android.view.IWindowManager;
+import android.view.IWindowSession;
import android.view.KeyEvent;
import android.view.SurfaceControl;
+import android.view.WindowManagerGlobal;
import dalvik.system.CloseGuard;
@@ -184,31 +187,45 @@ public abstract class TaskEmbedder {
/**
* Called when the task embedder should be initialized.
+ * NOTE: all overriding methods should call this one after they finish their initialization.
* @return whether to report whether the embedder was initialized.
*/
- public abstract boolean onInitialize();
+ public boolean onInitialize() {
+ updateLocationAndTapExcludeRegion();
+ return true;
+ }
/**
* Called when the task embedder should be released.
* @return whether to report whether the embedder was released.
*/
- protected abstract boolean onRelease();
+ protected boolean onRelease() {
+ // Clear tap-exclude region (if any) for this window.
+ clearTapExcludeRegion();
+ return true;
+ }
/**
* Starts presentation of tasks in this container.
*/
- public abstract void start();
+ public void start() {
+ updateLocationAndTapExcludeRegion();
+ }
/**
* Stops presentation of tasks in this container.
*/
- public abstract void stop();
+ public void stop() {
+ clearTapExcludeRegion();
+ }
/**
* This should be called whenever the position or size of the surface changes
* or if touchable areas above the surface are added or removed.
*/
- public abstract void notifyBoundsChanged();
+ public void notifyBoundsChanged() {
+ updateLocationAndTapExcludeRegion();
+ }
/**
* Called to update the dimensions whenever the host size changes.
@@ -268,6 +285,48 @@ public abstract class TaskEmbedder {
}
/**
+ * Updates position and bounds information needed by WM and IME to manage window
+ * focus and touch events properly.
+ * <p>
+ * This should be called whenever the position or size of the surface changes
+ * or if touchable areas above the surface are added or removed.
+ */
+ protected void updateLocationAndTapExcludeRegion() {
+ if (!isInitialized() || mHost.getWindow() == null) {
+ return;
+ }
+ applyTapExcludeRegion(mHost.getWindow(), mHost.getTapExcludeRegion());
+ }
+
+ /**
+ * Call to update the tap exclude region for the window.
+ * <p>
+ * This should not normally be called directly, but through
+ * {@link #updateLocationAndTapExcludeRegion()}. This method
+ * is provided as an optimization when managing multiple TaskSurfaces within a view.
+ *
+ * @see IWindowSession#updateTapExcludeRegion(IWindow, Region)
+ */
+ private void applyTapExcludeRegion(IWindow window, @Nullable Region tapExcludeRegion) {
+ try {
+ IWindowSession session = WindowManagerGlobal.getWindowSession();
+ session.updateTapExcludeRegion(window, tapExcludeRegion);
+ } catch (RemoteException e) {
+ e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * Removes the tap exclude region set by {@link #updateLocationAndTapExcludeRegion()}.
+ */
+ private void clearTapExcludeRegion() {
+ if (!isInitialized() || mHost.getWindow() == null) {
+ return;
+ }
+ applyTapExcludeRegion(mHost.getWindow(), null);
+ }
+
+ /**
* Set the callback to be notified about state changes.
* <p>This class must finish initializing before {@link #startActivity(Intent)} can be called.
* <p>Note: If the instance was ready prior to this call being made, then
diff --git a/core/java/android/window/TaskOrganizerTaskEmbedder.java b/core/java/android/window/TaskOrganizerTaskEmbedder.java
index 2fb46509f337..1b87521f3a96 100644
--- a/core/java/android/window/TaskOrganizerTaskEmbedder.java
+++ b/core/java/android/window/TaskOrganizerTaskEmbedder.java
@@ -75,7 +75,8 @@ public class TaskOrganizerTaskEmbedder extends TaskEmbedder {
// infrastructure is ready.
mTaskOrganizer.registerOrganizer(WINDOWING_MODE_MULTI_WINDOW);
mTaskOrganizer.setInterceptBackPressedOnTaskRoot(true);
- return true;
+
+ return super.onInitialize();
}
@Override
@@ -96,6 +97,7 @@ public class TaskOrganizerTaskEmbedder extends TaskEmbedder {
*/
@Override
public void start() {
+ super.start();
if (DEBUG) {
log("start");
}
@@ -119,6 +121,7 @@ public class TaskOrganizerTaskEmbedder extends TaskEmbedder {
*/
@Override
public void stop() {
+ super.stop();
if (DEBUG) {
log("stop");
}
@@ -143,6 +146,7 @@ public class TaskOrganizerTaskEmbedder extends TaskEmbedder {
*/
@Override
public void notifyBoundsChanged() {
+ super.notifyBoundsChanged();
if (DEBUG) {
log("notifyBoundsChanged: screenBounds=" + mHost.getScreenBounds());
}
diff --git a/core/java/android/window/VirtualDisplayTaskEmbedder.java b/core/java/android/window/VirtualDisplayTaskEmbedder.java
index 6f85dc263a4d..2e6cbeee7d22 100644
--- a/core/java/android/window/VirtualDisplayTaskEmbedder.java
+++ b/core/java/android/window/VirtualDisplayTaskEmbedder.java
@@ -21,7 +21,6 @@ import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_C
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC;
import static android.view.Display.INVALID_DISPLAY;
-import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityOptions;
import android.app.ActivityTaskManager;
@@ -40,7 +39,6 @@ import android.os.RemoteException;
import android.os.SystemClock;
import android.util.DisplayMetrics;
import android.util.Log;
-import android.view.IWindow;
import android.view.IWindowManager;
import android.view.IWindowSession;
import android.view.InputDevice;
@@ -134,20 +132,15 @@ public class VirtualDisplayTaskEmbedder extends TaskEmbedder {
e.rethrowAsRuntimeException();
}
- if (mHost.getWindow() != null) {
- updateLocationAndTapExcludeRegion();
- }
- return true;
+ return super.onInitialize();
}
@Override
protected boolean onRelease() {
+ super.onRelease();
// Clear activity view geometry for IME on this display
clearActivityViewGeometryForIme();
- // Clear tap-exclude region (if any) for this window.
- clearTapExcludeRegion();
-
if (mTaskStackListener != null) {
try {
mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener);
@@ -170,9 +163,9 @@ public class VirtualDisplayTaskEmbedder extends TaskEmbedder {
*/
@Override
public void start() {
+ super.start();
if (isInitialized()) {
mVirtualDisplay.setDisplayState(true);
- updateLocationAndTapExcludeRegion();
}
}
@@ -181,23 +174,14 @@ public class VirtualDisplayTaskEmbedder extends TaskEmbedder {
*/
@Override
public void stop() {
+ super.stop();
if (isInitialized()) {
mVirtualDisplay.setDisplayState(false);
clearActivityViewGeometryForIme();
- clearTapExcludeRegion();
}
}
/**
- * This should be called whenever the position or size of the surface changes
- * or if touchable areas above the surface are added or removed.
- */
- @Override
- public void notifyBoundsChanged() {
- updateLocationAndTapExcludeRegion();
- }
-
- /**
* Called to update the dimensions whenever the host size changes.
*
* @param width the new width of the surface
@@ -298,12 +282,13 @@ public class VirtualDisplayTaskEmbedder extends TaskEmbedder {
* This should be called whenever the position or size of the surface changes
* or if touchable areas above the surface are added or removed.
*/
- private void updateLocationAndTapExcludeRegion() {
+ @Override
+ protected void updateLocationAndTapExcludeRegion() {
+ super.updateLocationAndTapExcludeRegion();
if (!isInitialized() || mHost.getWindow() == null) {
return;
}
reportLocation(mHost.getScreenToTaskMatrix(), mHost.getPositionInWindow());
- applyTapExcludeRegion(mHost.getWindow(), mHost.getTapExcludeRegion());
}
/**
@@ -332,24 +317,6 @@ public class VirtualDisplayTaskEmbedder extends TaskEmbedder {
}
/**
- * Call to update the tap exclude region for the window.
- * <p>
- * This should not normally be called directly, but through
- * {@link #updateLocationAndTapExcludeRegion()}. This method
- * is provided as an optimization when managing multiple TaskSurfaces within a view.
- *
- * @see IWindowSession#updateTapExcludeRegion(IWindow, Region)
- */
- private void applyTapExcludeRegion(IWindow window, @Nullable Region tapExcludeRegion) {
- try {
- IWindowSession session = WindowManagerGlobal.getWindowSession();
- session.updateTapExcludeRegion(window, tapExcludeRegion);
- } catch (RemoteException e) {
- e.rethrowAsRuntimeException();
- }
- }
-
- /**
* @see InputMethodManager#reportActivityView(int, Matrix)
*/
private void clearActivityViewGeometryForIme() {
@@ -357,17 +324,6 @@ public class VirtualDisplayTaskEmbedder extends TaskEmbedder {
mContext.getSystemService(InputMethodManager.class).reportActivityView(displayId, null);
}
- /**
- * Removes the tap exclude region set by {@link #updateLocationAndTapExcludeRegion()}.
- */
- private void clearTapExcludeRegion() {
- if (mHost.getWindow() == null) {
- Log.w(TAG, "clearTapExcludeRegion: not attached to window!");
- return;
- }
- applyTapExcludeRegion(mHost.getWindow(), null);
- }
-
private static KeyEvent createKeyEvent(int action, int code, int displayId) {
long when = SystemClock.uptimeMillis();
final KeyEvent ev = new KeyEvent(when, when, action, code, 0 /* repeat */,
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index be06913c78e5..3fc3f3e65d37 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -158,6 +158,7 @@ public class ChooserActivity extends ResolverActivity implements
private static final String TAG = "ChooserActivity";
private AppPredictor mPersonalAppPredictor;
private AppPredictor mWorkAppPredictor;
+ private boolean mShouldDisplayLandscape;
@UnsupportedAppUsage
public ChooserActivity() {
@@ -253,7 +254,7 @@ public class ChooserActivity extends ResolverActivity implements
private boolean mChooserTargetRankingEnabled = DeviceConfig.getBoolean(
DeviceConfig.NAMESPACE_SYSTEMUI,
SystemUiDeviceConfigFlags.CHOOSER_TARGET_RANKING_ENABLED,
- false);
+ true);
private Bundle mReplacementExtras;
private IntentSender mChosenComponentSender;
@@ -716,6 +717,8 @@ public class ChooserActivity extends ResolverActivity implements
mCallerChooserTargets = targets;
}
+ mShouldDisplayLandscape = shouldDisplayLandscape(
+ getResources().getConfiguration().orientation);
setRetainInOnStop(intent.getBooleanExtra(EXTRA_PRIVATE_RETAIN_IN_ON_STOP, false));
super.onCreate(savedInstanceState, target, title, defaultTitleRes, initialIntents,
null, false);
@@ -884,7 +887,8 @@ public class ChooserActivity extends ResolverActivity implements
/* context */ this,
adapter,
getPersonalProfileUserHandle(),
- /* workProfileUserHandle= */ null);
+ /* workProfileUserHandle= */ null,
+ isSendAction(getTargetIntent()));
}
private ChooserMultiProfilePagerAdapter createChooserMultiProfilePagerAdapterForTwoProfiles(
@@ -914,7 +918,8 @@ public class ChooserActivity extends ResolverActivity implements
workAdapter,
selectedProfile,
getPersonalProfileUserHandle(),
- getWorkProfileUserHandle());
+ getWorkProfileUserHandle(),
+ isSendAction(getTargetIntent()));
}
private int findSelectedProfile() {
@@ -1071,6 +1076,7 @@ public class ChooserActivity extends ResolverActivity implements
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
+ mShouldDisplayLandscape = shouldDisplayLandscape(newConfig.orientation);
adjustPreviewWidth(newConfig.orientation, null);
updateStickyContentPreview();
}
@@ -1084,7 +1090,7 @@ public class ChooserActivity extends ResolverActivity implements
private void adjustPreviewWidth(int orientation, View parent) {
int width = -1;
- if (shouldDisplayLandscape(orientation)) {
+ if (mShouldDisplayLandscape) {
width = getResources().getDimensionPixelSize(R.dimen.chooser_preview_width);
}
@@ -2938,6 +2944,19 @@ public class ChooserActivity extends ResolverActivity implements
.setSubtype(previewType));
}
+ class ViewHolderBase extends RecyclerView.ViewHolder {
+ private int mViewType;
+
+ ViewHolderBase(View itemView, int viewType) {
+ super(itemView);
+ this.mViewType = viewType;
+ }
+
+ int getViewType() {
+ return mViewType;
+ }
+ }
+
/**
* Used to bind types of individual item including
* {@link ChooserGridAdapter#VIEW_TYPE_NORMAL},
@@ -2945,12 +2964,12 @@ public class ChooserActivity extends ResolverActivity implements
* {@link ChooserGridAdapter#VIEW_TYPE_PROFILE},
* and {@link ChooserGridAdapter#VIEW_TYPE_AZ_LABEL}.
*/
- final class ItemViewHolder extends RecyclerView.ViewHolder {
+ final class ItemViewHolder extends ViewHolderBase {
ResolverListAdapter.ViewHolder mWrappedViewHolder;
int mListPosition = ChooserListAdapter.NO_POSITION;
- ItemViewHolder(View itemView, boolean isClickable) {
- super(itemView);
+ ItemViewHolder(View itemView, boolean isClickable, int viewType) {
+ super(itemView, viewType);
mWrappedViewHolder = new ResolverListAdapter.ViewHolder(itemView);
if (isClickable) {
itemView.setOnClickListener(v -> startSelected(mListPosition,
@@ -2968,9 +2987,9 @@ public class ChooserActivity extends ResolverActivity implements
/**
* Add a footer to the list, to support scrolling behavior below the navbar.
*/
- final class FooterViewHolder extends RecyclerView.ViewHolder {
- FooterViewHolder(View itemView) {
- super(itemView);
+ final class FooterViewHolder extends ViewHolderBase {
+ FooterViewHolder(View itemView, int viewType) {
+ super(itemView, viewType);
}
}
@@ -3081,7 +3100,7 @@ public class ChooserActivity extends ResolverActivity implements
int getMaxTargetsPerRow() {
int maxTargets = MAX_TARGETS_PER_ROW_PORTRAIT;
- if (shouldDisplayLandscape(getResources().getConfiguration().orientation)) {
+ if (mShouldDisplayLandscape) {
maxTargets = MAX_TARGETS_PER_ROW_LANDSCAPE;
}
return maxTargets;
@@ -3189,13 +3208,14 @@ public class ChooserActivity extends ResolverActivity implements
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
switch (viewType) {
case VIEW_TYPE_CONTENT_PREVIEW:
- return new ItemViewHolder(createContentPreviewView(parent), false);
+ return new ItemViewHolder(createContentPreviewView(parent), false, viewType);
case VIEW_TYPE_PROFILE:
- return new ItemViewHolder(createProfileView(parent), false);
+ return new ItemViewHolder(createProfileView(parent), false, viewType);
case VIEW_TYPE_AZ_LABEL:
- return new ItemViewHolder(createAzLabelView(parent), false);
+ return new ItemViewHolder(createAzLabelView(parent), false, viewType);
case VIEW_TYPE_NORMAL:
- return new ItemViewHolder(mChooserListAdapter.createView(parent), true);
+ return new ItemViewHolder(
+ mChooserListAdapter.createView(parent), true, viewType);
case VIEW_TYPE_DIRECT_SHARE:
case VIEW_TYPE_CALLER_AND_RANK:
return createItemGroupViewHolder(viewType, parent);
@@ -3203,7 +3223,7 @@ public class ChooserActivity extends ResolverActivity implements
Space sp = new Space(parent.getContext());
sp.setLayoutParams(new RecyclerView.LayoutParams(
LayoutParams.MATCH_PARENT, mFooterHeight));
- return new FooterViewHolder(sp);
+ return new FooterViewHolder(sp, viewType);
default:
// Since we catch all possible viewTypes above, no chance this is being called.
return null;
@@ -3212,7 +3232,7 @@ public class ChooserActivity extends ResolverActivity implements
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
- int viewType = getItemViewType(position);
+ int viewType = ((ViewHolderBase) holder).getViewType();
switch (viewType) {
case VIEW_TYPE_DIRECT_SHARE:
case VIEW_TYPE_CALLER_AND_RANK:
@@ -3323,7 +3343,6 @@ public class ChooserActivity extends ResolverActivity implements
}
viewGroup.setTag(holder);
-
return holder;
}
@@ -3350,14 +3369,15 @@ public class ChooserActivity extends ResolverActivity implements
parentGroup.addView(row2);
mDirectShareViewHolder = new DirectShareViewHolder(parentGroup,
- Lists.newArrayList(row1, row2), getMaxTargetsPerRow());
+ Lists.newArrayList(row1, row2), getMaxTargetsPerRow(), viewType);
loadViewsIntoGroup(mDirectShareViewHolder);
return mDirectShareViewHolder;
} else {
ViewGroup row = (ViewGroup) mLayoutInflater.inflate(R.layout.chooser_row, parent,
false);
- ItemGroupViewHolder holder = new SingleRowViewHolder(row, getMaxTargetsPerRow());
+ ItemGroupViewHolder holder =
+ new SingleRowViewHolder(row, getMaxTargetsPerRow(), viewType);
loadViewsIntoGroup(holder);
return holder;
@@ -3519,14 +3539,14 @@ public class ChooserActivity extends ResolverActivity implements
* {@link ChooserGridAdapter#VIEW_TYPE_DIRECT_SHARE},
* and {@link ChooserGridAdapter#VIEW_TYPE_CALLER_AND_RANK}.
*/
- abstract class ItemGroupViewHolder extends RecyclerView.ViewHolder {
+ abstract class ItemGroupViewHolder extends ViewHolderBase {
protected int mMeasuredRowHeight;
private int[] mItemIndices;
protected final View[] mCells;
private final int mColumnCount;
- ItemGroupViewHolder(int cellCount, View itemView) {
- super(itemView);
+ ItemGroupViewHolder(int cellCount, View itemView, int viewType) {
+ super(itemView, viewType);
this.mCells = new View[cellCount];
this.mItemIndices = new int[cellCount];
this.mColumnCount = cellCount;
@@ -3572,8 +3592,8 @@ public class ChooserActivity extends ResolverActivity implements
class SingleRowViewHolder extends ItemGroupViewHolder {
private final ViewGroup mRow;
- SingleRowViewHolder(ViewGroup row, int cellCount) {
- super(cellCount, row);
+ SingleRowViewHolder(ViewGroup row, int cellCount, int viewType) {
+ super(cellCount, row, viewType);
this.mRow = row;
}
@@ -3615,8 +3635,9 @@ public class ChooserActivity extends ResolverActivity implements
private final boolean[] mCellVisibility;
- DirectShareViewHolder(ViewGroup parent, List<ViewGroup> rows, int cellCountPerRow) {
- super(rows.size() * cellCountPerRow, parent);
+ DirectShareViewHolder(ViewGroup parent, List<ViewGroup> rows, int cellCountPerRow,
+ int viewType) {
+ super(rows.size() * cellCountPerRow, parent, viewType);
this.mParent = parent;
this.mRows = rows;
diff --git a/core/java/com/android/internal/app/ChooserGridLayoutManager.java b/core/java/com/android/internal/app/ChooserGridLayoutManager.java
new file mode 100644
index 000000000000..317a987cf359
--- /dev/null
+++ b/core/java/com/android/internal/app/ChooserGridLayoutManager.java
@@ -0,0 +1,70 @@
+/*
+ * 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.app;
+
+import android.content.Context;
+import android.util.AttributeSet;
+
+import com.android.internal.widget.GridLayoutManager;
+import com.android.internal.widget.RecyclerView;
+
+/**
+ * For a11y and per {@link RecyclerView#onInitializeAccessibilityNodeInfo}, override
+ * methods to ensure proper row counts.
+ */
+public class ChooserGridLayoutManager extends GridLayoutManager {
+
+ /**
+ * Constructor used when layout manager is set in XML by RecyclerView attribute
+ * "layoutManager". If spanCount is not specified in the XML, it defaults to a
+ * single column.
+ *
+ */
+ public ChooserGridLayoutManager(Context context, AttributeSet attrs, int defStyleAttr,
+ int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ }
+
+ /**
+ * Creates a vertical GridLayoutManager
+ *
+ * @param context Current context, will be used to access resources.
+ * @param spanCount The number of columns in the grid
+ */
+ public ChooserGridLayoutManager(Context context, int spanCount) {
+ super(context, spanCount);
+ }
+
+ /**
+ * @param context Current context, will be used to access resources.
+ * @param spanCount The number of columns or rows in the grid
+ * @param orientation Layout orientation. Should be {@link #HORIZONTAL} or {@link
+ * #VERTICAL}.
+ * @param reverseLayout When set to true, layouts from end to start.
+ */
+ public ChooserGridLayoutManager(Context context, int spanCount, int orientation,
+ boolean reverseLayout) {
+ super(context, spanCount, orientation, reverseLayout);
+ }
+
+ @Override
+ public int getRowCountForAccessibility(RecyclerView.Recycler recycler,
+ RecyclerView.State state) {
+ // Do not count the footer view in the official count
+ return super.getRowCountForAccessibility(recycler, state) - 1;
+ }
+}
diff --git a/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java b/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java
index 57157f7b1f5b..774be3c9c4b8 100644
--- a/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java
+++ b/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java
@@ -37,15 +37,18 @@ public class ChooserMultiProfilePagerAdapter extends AbstractMultiProfilePagerAd
private static final int SINGLE_CELL_SPAN_SIZE = 1;
private final ChooserProfileDescriptor[] mItems;
+ private final boolean mIsSendAction;
ChooserMultiProfilePagerAdapter(Context context,
ChooserActivity.ChooserGridAdapter adapter,
UserHandle personalProfileUserHandle,
- UserHandle workProfileUserHandle) {
+ UserHandle workProfileUserHandle,
+ boolean isSendAction) {
super(context, /* currentPage */ 0, personalProfileUserHandle, workProfileUserHandle);
mItems = new ChooserProfileDescriptor[] {
createProfileDescriptor(adapter)
};
+ mIsSendAction = isSendAction;
}
ChooserMultiProfilePagerAdapter(Context context,
@@ -53,13 +56,15 @@ public class ChooserMultiProfilePagerAdapter extends AbstractMultiProfilePagerAd
ChooserActivity.ChooserGridAdapter workAdapter,
@Profile int defaultProfile,
UserHandle personalProfileUserHandle,
- UserHandle workProfileUserHandle) {
+ UserHandle workProfileUserHandle,
+ boolean isSendAction) {
super(context, /* currentPage */ defaultProfile, personalProfileUserHandle,
workProfileUserHandle);
mItems = new ChooserProfileDescriptor[] {
createProfileDescriptor(personalAdapter),
createProfileDescriptor(workAdapter)
};
+ mIsSendAction = isSendAction;
}
private ChooserProfileDescriptor createProfileDescriptor(
@@ -182,34 +187,62 @@ public class ChooserMultiProfilePagerAdapter extends AbstractMultiProfilePagerAd
@Override
protected void showNoPersonalToWorkIntentsEmptyState(ResolverListAdapter activeListAdapter) {
- showEmptyState(activeListAdapter,
- R.drawable.ic_sharing_disabled,
- R.string.resolver_cant_share_with_work_apps,
- R.string.resolver_cant_share_with_work_apps_explanation);
+ if (mIsSendAction) {
+ showEmptyState(activeListAdapter,
+ R.drawable.ic_sharing_disabled,
+ R.string.resolver_cant_share_with_work_apps,
+ R.string.resolver_cant_share_with_work_apps_explanation);
+ } else {
+ showEmptyState(activeListAdapter,
+ R.drawable.ic_sharing_disabled,
+ R.string.resolver_cant_access_work_apps,
+ R.string.resolver_cant_access_work_apps_explanation);
+ }
}
@Override
protected void showNoWorkToPersonalIntentsEmptyState(ResolverListAdapter activeListAdapter) {
- showEmptyState(activeListAdapter,
- R.drawable.ic_sharing_disabled,
- R.string.resolver_cant_share_with_personal_apps,
- R.string.resolver_cant_share_with_personal_apps_explanation);
+ if (mIsSendAction) {
+ showEmptyState(activeListAdapter,
+ R.drawable.ic_sharing_disabled,
+ R.string.resolver_cant_share_with_personal_apps,
+ R.string.resolver_cant_share_with_personal_apps_explanation);
+ } else {
+ showEmptyState(activeListAdapter,
+ R.drawable.ic_sharing_disabled,
+ R.string.resolver_cant_access_personal_apps,
+ R.string.resolver_cant_access_personal_apps_explanation);
+ }
}
@Override
protected void showNoPersonalAppsAvailableEmptyState(ResolverListAdapter listAdapter) {
- showEmptyState(listAdapter,
- R.drawable.ic_no_apps,
- R.string.resolver_no_personal_apps_available_share,
- /* subtitleRes */ 0);
+ if (mIsSendAction) {
+ showEmptyState(listAdapter,
+ R.drawable.ic_no_apps,
+ R.string.resolver_no_personal_apps_available_share,
+ /* subtitleRes */ 0);
+ } else {
+ showEmptyState(listAdapter,
+ R.drawable.ic_no_apps,
+ R.string.resolver_no_personal_apps_available_resolve,
+ /* subtitleRes */ 0);
+ }
}
@Override
protected void showNoWorkAppsAvailableEmptyState(ResolverListAdapter listAdapter) {
- showEmptyState(listAdapter,
- R.drawable.ic_no_apps,
- R.string.resolver_no_work_apps_available_share,
- /* subtitleRes */ 0);
+ if (mIsSendAction) {
+ showEmptyState(listAdapter,
+ R.drawable.ic_no_apps,
+ R.string.resolver_no_work_apps_available_share,
+ /* subtitleRes */ 0);
+ } else {
+ showEmptyState(listAdapter,
+ R.drawable.ic_no_apps,
+ R.string.resolver_no_work_apps_available_resolve,
+ /* subtitleRes */ 0);
+ }
}
class ChooserProfileDescriptor extends ProfileDescriptor {
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 2f62f8e7a0c9..83dabe8d0525 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -182,6 +182,8 @@ public class ResolverActivity extends Activity implements
private BroadcastReceiver mWorkProfileStateReceiver;
private UserHandle mHeaderCreatorUser;
+ private UserHandle mWorkProfileUserHandle;
+
/**
* Get the string resource to be used as a label for the link to the resolver activity for an
* action.
@@ -363,6 +365,7 @@ public class ResolverActivity extends Activity implements
// a more complicated UI that the current voice interaction flow is not able
// to handle.
boolean filterLastUsed = mSupportsAlwaysUseOption && !isVoiceInteraction();
+ mWorkProfileUserHandle = fetchWorkProfileUserProfile();
mMultiProfilePagerAdapter = createMultiProfilePagerAdapter(initialIntents, rList, filterLastUsed);
if (configureContentView()) {
return;
@@ -527,13 +530,18 @@ public class ResolverActivity extends Activity implements
return UserHandle.of(ActivityManager.getCurrentUser());
}
protected @Nullable UserHandle getWorkProfileUserHandle() {
+ return mWorkProfileUserHandle;
+ }
+
+ protected @Nullable UserHandle fetchWorkProfileUserProfile() {
+ mWorkProfileUserHandle = null;
UserManager userManager = getSystemService(UserManager.class);
for (final UserInfo userInfo : userManager.getProfiles(ActivityManager.getCurrentUser())) {
if (userInfo.isManagedProfile()) {
- return userInfo.getUserHandle();
+ mWorkProfileUserHandle = userInfo.getUserHandle();
}
}
- return null;
+ return mWorkProfileUserHandle;
}
private boolean hasWorkProfile() {
diff --git a/core/java/com/android/internal/app/ResolverListAdapter.java b/core/java/com/android/internal/app/ResolverListAdapter.java
index 2fd938f45291..24bf98b6502c 100644
--- a/core/java/com/android/internal/app/ResolverListAdapter.java
+++ b/core/java/com/android/internal/app/ResolverListAdapter.java
@@ -54,6 +54,7 @@ import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.ResolverActivity.ResolvedComponentInfo;
import com.android.internal.app.chooser.DisplayResolveInfo;
+import com.android.internal.app.chooser.SelectableTargetInfo;
import com.android.internal.app.chooser.TargetInfo;
import java.util.ArrayList;
@@ -549,6 +550,15 @@ public class ResolverListAdapter extends BaseAdapter {
getLoadLabelTask((DisplayResolveInfo) info, holder).execute();
} else {
holder.bindLabel(info.getDisplayLabel(), info.getExtendedInfo());
+ if (info instanceof SelectableTargetInfo) {
+ // direct share targets should append the application name for a better readout
+ DisplayResolveInfo rInfo = ((SelectableTargetInfo) info).getDisplayResolveInfo();
+ CharSequence appName = rInfo != null ? rInfo.getDisplayLabel() : "";
+ CharSequence extendedInfo = info.getExtendedInfo();
+ String contentDescription = String.join(" ", info.getDisplayLabel(),
+ extendedInfo != null ? extendedInfo : "", appName);
+ holder.updateContentDescription(contentDescription);
+ }
}
if (info.isSuspended()) {
@@ -697,6 +707,12 @@ public class ResolverListAdapter extends BaseAdapter {
text2.setVisibility(View.VISIBLE);
text2.setText(subLabel);
}
+
+ itemView.setContentDescription(null);
+ }
+
+ public void updateContentDescription(String description) {
+ itemView.setContentDescription(description);
}
}
diff --git a/core/java/com/android/internal/app/chooser/SelectableTargetInfo.java b/core/java/com/android/internal/app/chooser/SelectableTargetInfo.java
index 246a07d3d0fe..900e18d468bb 100644
--- a/core/java/com/android/internal/app/chooser/SelectableTargetInfo.java
+++ b/core/java/com/android/internal/app/chooser/SelectableTargetInfo.java
@@ -44,7 +44,6 @@ import com.android.internal.app.ResolverListAdapter.ActivityInfoPresentationGett
import com.android.internal.app.SimpleIconFactory;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.List;
/**
@@ -136,6 +135,10 @@ public final class SelectableTargetInfo implements ChooserTargetInfo {
return mIsSuspended;
}
+ public DisplayResolveInfo getDisplayResolveInfo() {
+ return mSourceInfo;
+ }
+
private Drawable getChooserTargetIconDrawable(ChooserTarget target,
@Nullable ShortcutInfo shortcutInfo) {
Drawable directShareIcon = null;
diff --git a/core/java/com/android/internal/infra/AbstractRemoteService.java b/core/java/com/android/internal/infra/AbstractRemoteService.java
index 3900f1674c13..7195b45a4055 100644
--- a/core/java/com/android/internal/infra/AbstractRemoteService.java
+++ b/core/java/com/android/internal/infra/AbstractRemoteService.java
@@ -321,6 +321,20 @@ public abstract class AbstractRemoteService<S extends AbstractRemoteService<S, I
obtainMessage(AbstractRemoteService::handlePendingRequest, this, asyncRequest));
}
+ /**
+ * Executes an async request immediately instead of sending it to Handler queue as what
+ * {@link scheduleAsyncRequest} does.
+ *
+ * <p>This request is not expecting a callback from the service, hence it's represented by
+ * a simple {@link Runnable}.
+ */
+ protected void executeAsyncRequest(@NonNull AsyncRequest<I> request) {
+ // TODO(b/117779333): fix generics below
+ @SuppressWarnings({"unchecked", "rawtypes"})
+ final MyAsyncPendingRequest<S, I> asyncRequest = new MyAsyncPendingRequest(this, request);
+ handlePendingRequest(asyncRequest);
+ }
+
private void cancelScheduledUnbind() {
mHandler.removeMessages(MSG_UNBIND);
}
diff --git a/core/java/com/android/internal/policy/TaskResizingAlgorithm.java b/core/java/com/android/internal/policy/TaskResizingAlgorithm.java
index 1fce098fb93e..1ec020696cf3 100644
--- a/core/java/com/android/internal/policy/TaskResizingAlgorithm.java
+++ b/core/java/com/android/internal/policy/TaskResizingAlgorithm.java
@@ -91,14 +91,14 @@ public class TaskResizingAlgorithm {
int width = right - left;
int height = bottom - top;
if ((ctrlType & CTRL_LEFT) != 0) {
- width = Math.max(minVisibleWidth, width - deltaX);
+ width = Math.max(minVisibleWidth, Math.min(width - deltaX, maxVisibleSize.x));
} else if ((ctrlType & CTRL_RIGHT) != 0) {
- width = Math.max(minVisibleWidth, width + deltaX);
+ width = Math.max(minVisibleWidth, Math.min(width + deltaX, maxVisibleSize.x));
}
if ((ctrlType & CTRL_TOP) != 0) {
- height = Math.max(minVisibleHeight, height - deltaY);
+ height = Math.max(minVisibleHeight, Math.min(height - deltaY, maxVisibleSize.y));
} else if ((ctrlType & CTRL_BOTTOM) != 0) {
- height = Math.max(minVisibleHeight, height + deltaY);
+ height = Math.max(minVisibleHeight, Math.min(height + deltaY, maxVisibleSize.y));
}
// If we have to preserve the orientation - check that we are doing so.
diff --git a/core/java/com/android/internal/view/inline/IInlineContentProvider.aidl b/core/java/com/android/internal/view/inline/IInlineContentProvider.aidl
index 08a349c21c8b..78df3eb660a5 100644
--- a/core/java/com/android/internal/view/inline/IInlineContentProvider.aidl
+++ b/core/java/com/android/internal/view/inline/IInlineContentProvider.aidl
@@ -24,4 +24,6 @@ import com.android.internal.view.inline.IInlineContentCallback;
*/
oneway interface IInlineContentProvider {
void provideContent(int width, int height, in IInlineContentCallback callback);
+ void requestSurfacePackage();
+ void onSurfacePackageReleased();
}
diff --git a/core/java/com/android/internal/widget/GridLayoutManager.java b/core/java/com/android/internal/widget/GridLayoutManager.java
index e0502f129f7f..09e6a991b1ac 100644
--- a/core/java/com/android/internal/widget/GridLayoutManager.java
+++ b/core/java/com/android/internal/widget/GridLayoutManager.java
@@ -153,13 +153,11 @@ public class GridLayoutManager extends LinearLayoutManager {
if (mOrientation == HORIZONTAL) {
info.setCollectionItemInfo(AccessibilityNodeInfo.CollectionItemInfo.obtain(
glp.getSpanIndex(), glp.getSpanSize(),
- spanGroupIndex, 1,
- mSpanCount > 1 && glp.getSpanSize() == mSpanCount, false));
+ spanGroupIndex, 1, false, false));
} else { // VERTICAL
info.setCollectionItemInfo(AccessibilityNodeInfo.CollectionItemInfo.obtain(
spanGroupIndex, 1,
- glp.getSpanIndex(), glp.getSpanSize(),
- mSpanCount > 1 && glp.getSpanSize() == mSpanCount, false));
+ glp.getSpanIndex(), glp.getSpanSize(), false, false));
}
}
diff --git a/core/java/com/android/internal/widget/ResolverDrawerLayout.java b/core/java/com/android/internal/widget/ResolverDrawerLayout.java
index fb2ecf3a478f..3f708f84750c 100644
--- a/core/java/com/android/internal/widget/ResolverDrawerLayout.java
+++ b/core/java/com/android/internal/widget/ResolverDrawerLayout.java
@@ -825,18 +825,6 @@ public class ResolverDrawerLayout extends ViewGroup {
return true;
}
break;
- case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD:
- case R.id.accessibilityActionScrollUp:
- if (mCollapseOffset < mCollapsibleHeight) {
- smoothScrollTo(mCollapsibleHeight, 0);
- return true;
- } else if ((mCollapseOffset < mCollapsibleHeight + mUncollapsibleHeight)
- && isDismissable()) {
- smoothScrollTo(mCollapsibleHeight + mUncollapsibleHeight, 0);
- mDismissOnScrollerFinished = true;
- return true;
- }
- break;
case AccessibilityNodeInfo.ACTION_COLLAPSE:
if (mCollapseOffset < mCollapsibleHeight) {
smoothScrollTo(mCollapsibleHeight, 0);
@@ -886,7 +874,6 @@ public class ResolverDrawerLayout extends ViewGroup {
}
if ((mCollapseOffset < mCollapsibleHeight + mUncollapsibleHeight)
&& ((mCollapseOffset < mCollapsibleHeight) || isDismissable())) {
- info.addAction(AccessibilityAction.ACTION_SCROLL_BACKWARD);
info.addAction(AccessibilityAction.ACTION_SCROLL_UP);
info.setScrollable(true);
}
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 3a5720fd8c4c..c5bc083dfabf 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -1552,8 +1552,8 @@ static void isolateJitProfile(JNIEnv* env, jobjectArray pkg_data_info_list,
}
}
-static void BindMountStorageToLowerFs(const userid_t user_id, const char* dir_name,
- const char* package, fail_fn_t fail_fn) {
+static void BindMountStorageToLowerFs(const userid_t user_id, const uid_t uid,
+ const char* dir_name, const char* package, fail_fn_t fail_fn) {
bool hasSdcardFs = IsFilesystemSupported("sdcardfs");
std::string source;
@@ -1565,6 +1565,9 @@ static void BindMountStorageToLowerFs(const userid_t user_id, const char* dir_na
}
std::string target = StringPrintf("/storage/emulated/%d/%s/%s", user_id, dir_name, package);
+ // As the parent is mounted as tmpfs, we need to create the target dir here.
+ PrepareDirIfNotPresent(target, 0700, uid, uid, fail_fn);
+
if (access(source.c_str(), F_OK) != 0) {
fail_fn(CREATE_ERROR("Error accessing %s: %s", source.c_str(), strerror(errno)));
}
@@ -1574,9 +1577,8 @@ static void BindMountStorageToLowerFs(const userid_t user_id, const char* dir_na
BindMount(source, target, fail_fn);
}
-// Bind mount all obb & data directories that are visible to this app.
-// If app data isolation is not enabled for this process, bind mount the whole obb
-// and data directory instead.
+// Mount tmpfs on Android/data and Android/obb, then bind mount all app visible package
+// directories in data and obb directories.
static void BindMountStorageDirs(JNIEnv* env, jobjectArray pkg_data_info_list,
uid_t uid, const char* process_name, jstring managed_nice_name, fail_fn_t fail_fn) {
@@ -1590,12 +1592,18 @@ static void BindMountStorageDirs(JNIEnv* env, jobjectArray pkg_data_info_list,
fail_fn(CREATE_ERROR("Data package list cannot be empty"));
}
+ // Create tmpfs on Android/obb and Android/data so these 2 dirs won't enter fuse anymore.
+ std::string androidObbDir = StringPrintf("/storage/emulated/%d/Android/obb", user_id);
+ MountAppDataTmpFs(androidObbDir, fail_fn);
+ std::string androidDataDir = StringPrintf("/storage/emulated/%d/Android/data", user_id);
+ MountAppDataTmpFs(androidDataDir, fail_fn);
+
// Bind mount each package obb directory
for (int i = 0; i < size; i += 3) {
jstring package_str = (jstring) (env->GetObjectArrayElement(pkg_data_info_list, i));
std::string packageName = extract_fn(package_str).value();
- BindMountStorageToLowerFs(user_id, "Android/obb", packageName.c_str(), fail_fn);
- BindMountStorageToLowerFs(user_id, "Android/data", packageName.c_str(), fail_fn);
+ BindMountStorageToLowerFs(user_id, uid, "Android/obb", packageName.c_str(), fail_fn);
+ BindMountStorageToLowerFs(user_id, uid, "Android/data", packageName.c_str(), fail_fn);
}
}
@@ -1648,9 +1656,10 @@ static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray gids,
uid, process_name, managed_nice_name, fail_fn);
isolateJitProfile(env, pkg_data_info_list, uid, process_name, managed_nice_name, fail_fn);
}
- if (mount_external != MOUNT_EXTERNAL_INSTALLER &&
- mount_external != MOUNT_EXTERNAL_PASS_THROUGH &&
- mount_storage_dirs) {
+ // MOUNT_EXTERNAL_INSTALLER, MOUNT_EXTERNAL_PASS_THROUGH, MOUNT_EXTERNAL_ANDROID_WRITABLE apps
+ // will have mount_storage_dirs == false here (set by ProcessList.needsStorageDataIsolation()),
+ // and hence they won't bind mount storage dirs.
+ if (mount_storage_dirs) {
BindMountStorageDirs(env, pkg_data_info_list, uid, process_name, managed_nice_name, fail_fn);
}
diff --git a/core/proto/android/providers/settings/global.proto b/core/proto/android/providers/settings/global.proto
index d5384a1c2fdd..762895b6320f 100644
--- a/core/proto/android/providers/settings/global.proto
+++ b/core/proto/android/providers/settings/global.proto
@@ -169,6 +169,7 @@ message GlobalSettingsProto {
optional SettingProto boot_count = 22 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto bugreport_in_power_menu = 23 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ optional SettingProto cached_apps_freezer_enabled = 152 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto call_auto_retry = 24 [ (android.privacy).dest = DEST_AUTOMATIC ];
message CaptivePortal {
@@ -1059,5 +1060,5 @@ message GlobalSettingsProto {
// Please insert fields in alphabetical order and group them into messages
// if possible (to avoid reaching the method limit).
- // Next tag = 152;
+ // Next tag = 153;
}
diff --git a/core/res/res/anim/dream_activity_close_exit.xml b/core/res/res/anim/dream_activity_close_exit.xml
new file mode 100644
index 000000000000..c4599dad31a0
--- /dev/null
+++ b/core/res/res/anim/dream_activity_close_exit.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* Copyright 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.
+*/
+-->
+
+<alpha xmlns:android="http://schemas.android.com/apk/res/android"
+ android:fromAlpha="1.0"
+ android:toAlpha="0.0"
+ android:duration="100" />
+
diff --git a/core/res/res/anim/dream_activity_open_enter.xml b/core/res/res/anim/dream_activity_open_enter.xml
new file mode 100644
index 000000000000..9e1c6e2ee0d7
--- /dev/null
+++ b/core/res/res/anim/dream_activity_open_enter.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* Copyright 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.
+*/
+-->
+
+<!-- During this animation we keep the previous activity on the screen
+using a noop animation for it (dream_activity_open_exit). The duration of
+those two has to be the same. -->
+<alpha xmlns:android="http://schemas.android.com/apk/res/android"
+ android:fromAlpha="0.0"
+ android:toAlpha="1.0"
+ android:duration="1000" />
+
diff --git a/core/res/res/anim/dream_activity_open_exit.xml b/core/res/res/anim/dream_activity_open_exit.xml
new file mode 100644
index 000000000000..740f52856b7f
--- /dev/null
+++ b/core/res/res/anim/dream_activity_open_exit.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* Copyright 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.
+*/
+-->
+
+<!-- A noop animation to keep the previous activity alive during the dream
+enter animation. The duration should match the duration of the
+dream_activity_open_enter animation. -->
+<alpha xmlns:android="http://schemas.android.com/apk/res/android"
+ android:fromAlpha="1.0"
+ android:toAlpha="1.0"
+ android:duration="1000" />
diff --git a/core/res/res/layout/chooser_list_per_profile.xml b/core/res/res/layout/chooser_list_per_profile.xml
index 6b1b002267cb..86dc71cbbfb8 100644
--- a/core/res/res/layout/chooser_list_per_profile.xml
+++ b/core/res/res/layout/chooser_list_per_profile.xml
@@ -20,7 +20,7 @@
<com.android.internal.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:layoutManager="com.android.internal.widget.GridLayoutManager"
+ android:layoutManager="com.android.internal.app.ChooserGridLayoutManager"
android:id="@+id/resolver_list"
android:clipToPadding="false"
android:background="?attr/colorBackgroundFloating"
@@ -29,4 +29,4 @@
android:nestedScrollingEnabled="true" />
<include layout="@layout/resolver_empty_states" />
-</RelativeLayout> \ No newline at end of file
+</RelativeLayout>
diff --git a/core/res/res/layout/resolver_list.xml b/core/res/res/layout/resolver_list.xml
index 4d0837f495df..446ce3fbaf4b 100644
--- a/core/res/res/layout/resolver_list.xml
+++ b/core/res/res/layout/resolver_list.xml
@@ -83,6 +83,7 @@
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
+ android:accessibilityTraversalAfter="@id/title"
android:background="?attr/colorBackgroundFloating">
<LinearLayout
android:orientation="vertical"
diff --git a/core/res/res/values-mcc334-mnc020/config.xml b/core/res/res/values-mcc334-mnc020/config.xml
index 0970517835b6..c64acc7c29db 100644
--- a/core/res/res/values-mcc334-mnc020/config.xml
+++ b/core/res/res/values-mcc334-mnc020/config.xml
@@ -18,4 +18,7 @@
-->
<resources>
<bool name="config_use_sim_language_file">false</bool>
+
+ <bool name="config_pdp_rejeect_enable_retry">true</bool>
+ <integer name="config_pdp_reject_retry_delay_ms">45000</integer>
</resources> \ No newline at end of file
diff --git a/core/res/res/values-mcc334-mnc020/strings.xml b/core/res/res/values-mcc334-mnc020/strings.xml
new file mode 100644
index 000000000000..a8a78d5ef3fc
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title"></string>
+ <string name="config_pdp_reject_user_authentication_failed">AUTHENTICATION FAILURE -29-</string>
+ <string name="config_pdp_reject_service_not_subscribed">NOT SUBSCRIBED TO SERVICE -33-</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed">Multiple PDN connections for a given APN not allowed -55-</string>
+</resources> \ No newline at end of file
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index fa4c25ad460a..65fa3fa1ee1d 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -4421,4 +4421,8 @@
<!-- Set to true to make assistant show in front of the dream/screensaver. -->
<bool name="config_assistantOnTopOfDream">false</bool>
+ <!-- pdp data retry for cause 29, 33 and 55 -->
+ <bool name="config_pdp_reject_enable_retry">false</bool>
+ <!-- pdp data reject retry delay in ms -->
+ <integer name="config_pdp_reject_retry_delay_ms">-1</integer>
</resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 51b23dbfb59b..35a7857b839f 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -5747,4 +5747,13 @@ ul.</string>
<string name="PERSOSUBSTATE_SIM_IMPI_SUCCESS">IMPI unlock successful.</string>
<!-- Success message displayed on SIM NS_SP Depersonalization panel [CHAR LIMIT=none] -->
<string name="PERSOSUBSTATE_SIM_NS_SP_SUCCESS">Network subset service provider unlock successful.</string>
+
+ <!-- pdp data reject dialog string for cause 29, 33 and 55 [CHAR LIMIT=100] -->
+ <string name="config_pdp_reject_dialog_title"></string>
+ <!-- pdp data reject dialog string for cause 29 (USER_AUTHENTICATION) [CHAR LIMIT=100] -->
+ <string name="config_pdp_reject_user_authentication_failed"></string>
+ <!-- pdp data reject dialog string for cause 33 (SERVICE_OPTION_NOT_SUBSCRIBED) [CHAR LIMIT=100] -->
+ <string name="config_pdp_reject_service_not_subscribed"></string>
+ <!-- pdp data reject dialog string for cause 55 (MULTI_CONN_TO_SAME_PDN_NOT_ALLOWED) [CHAR LIMIT=100] -->
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed"></string>
</resources>
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index bcce1f05f0dd..f920083f5cb3 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -248,12 +248,6 @@ please see styles_device_defaults.xml.
<item name="windowExitAnimation">@anim/fast_fade_out</item>
</style>
- <!-- Window animations for screen savers. {@hide} -->
- <style name="Animation.Dream">
- <item name="windowEnterAnimation">@anim/slow_fade_in</item>
- <item name="windowExitAnimation">@anim/fast_fade_out</item>
- </style>
-
<!-- Status Bar Styles -->
<style name="TextAppearance.StatusBar">
<item name="textAppearance">?attr/textAppearanceSmall</item>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index a82e7784b167..3ac2dc54c00a 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1580,7 +1580,6 @@
<java-symbol type="style" name="Animation.Tooltip" />
<java-symbol type="style" name="Animation.TypingFilter" />
<java-symbol type="style" name="Animation.TypingFilterRestore" />
- <java-symbol type="style" name="Animation.Dream" />
<java-symbol type="style" name="Theme.DeviceDefault.Dialog.Alert" />
<java-symbol type="style" name="Theme.DeviceDefault.Light.Dialog.Alert" />
<java-symbol type="style" name="Theme.Dialog.Alert" />
@@ -1825,6 +1824,9 @@
<java-symbol type="anim" name="rotation_animation_jump_exit" />
<java-symbol type="anim" name="rotation_animation_xfade_exit" />
<java-symbol type="anim" name="rotation_animation_enter" />
+ <java-symbol type="anim" name="dream_activity_open_exit" />
+ <java-symbol type="anim" name="dream_activity_open_enter" />
+ <java-symbol type="anim" name="dream_activity_close_exit" />
<java-symbol type="array" name="config_autoBrightnessButtonBacklightValues" />
<java-symbol type="array" name="config_autoBrightnessKeyboardBacklightValues" />
<java-symbol type="array" name="config_autoBrightnessLcdBacklightValues" />
@@ -3999,4 +4001,11 @@
<java-symbol type="string" name="notification_channel_network_alerts" />
<java-symbol type="string" name="notification_channel_network_available" />
+ <!-- For Pdn throttle feature -->
+ <java-symbol type="bool" name="config_pdp_reject_enable_retry" />
+ <java-symbol type="integer" name="config_pdp_reject_retry_delay_ms" />
+ <java-symbol type="string" name="config_pdp_reject_dialog_title" />
+ <java-symbol type="string" name="config_pdp_reject_user_authentication_failed" />
+ <java-symbol type="string" name="config_pdp_reject_service_not_subscribed" />
+ <java-symbol type="string" name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" />
</resources>
diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml
index 88f9fc2199e5..6e2995de0fe1 100644
--- a/core/res/res/values/themes.xml
+++ b/core/res/res/values/themes.xml
@@ -704,6 +704,7 @@ please see themes_device_defaults.xml.
<style name="Theme.Dream">
<item name="windowBackground">@color/black</item>
<item name="windowDisablePreview">true</item>
+ <item name="windowActivityTransitions">true</item>
</style>
<!-- Default theme for dialog windows and activities (on API level 10 and lower),
diff --git a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
index 567552f66b35..6720ed6b7bf9 100644
--- a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
+++ b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
@@ -35,6 +35,9 @@ import android.content.IntentSender;
import android.content.pm.PackageInstaller.SessionParams;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PackageParser.PackageParserException;
+import android.content.pm.parsing.ParsingPackage;
+import android.content.pm.parsing.ParsingPackageUtils;
+import android.content.pm.parsing.result.ParseResult;
import android.content.res.Resources;
import android.content.res.Resources.NotFoundException;
import android.net.Uri;
@@ -300,7 +303,8 @@ public class PackageManagerTests extends AndroidTestCase {
final Intent result = localReceiver.getResult();
final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
PackageInstaller.STATUS_SUCCESS);
- assertEquals(expectedResult, status);
+ String statusMessage = result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE);
+ assertEquals(statusMessage, expectedResult, status);
} catch (IllegalArgumentException | IOException | RemoteException e) {
Log.w(TAG, "Failed to install package; path=" + inPath, e);
fail("Failed to install package; path=" + inPath + ", e=" + e);
@@ -327,13 +331,14 @@ public class PackageManagerTests extends AndroidTestCase {
return Uri.fromFile(outFile);
}
- private PackageParser.Package parsePackage(Uri packageURI) throws PackageParserException {
+ private ParsingPackage parsePackage(Uri packageURI) {
final String archiveFilePath = packageURI.getPath();
- PackageParser packageParser = new PackageParser();
- File sourceFile = new File(archiveFilePath);
- PackageParser.Package pkg = packageParser.parseMonolithicPackage(sourceFile, 0);
- packageParser = null;
- return pkg;
+ ParseResult<ParsingPackage> result = ParsingPackageUtils.parseDefaultOneTime(
+ new File(archiveFilePath), 0 /*flags*/, false /*collectCertificates*/);
+ if (result.isError()) {
+ throw new IllegalStateException(result.getErrorMessage(), result.getException());
+ }
+ return result.getResult();
}
private boolean checkSd(long pkgLen) {
@@ -417,9 +422,9 @@ public class PackageManagerTests extends AndroidTestCase {
return INSTALL_LOC_ERR;
}
- private void assertInstall(PackageParser.Package pkg, int flags, int expInstallLocation) {
+ private void assertInstall(ParsingPackage pkg, int flags, int expInstallLocation) {
try {
- String pkgName = pkg.packageName;
+ String pkgName = pkg.getPackageName();
ApplicationInfo info = getPm().getApplicationInfo(pkgName, 0);
assertNotNull(info);
assertEquals(pkgName, info.packageName);
@@ -565,20 +570,20 @@ public class PackageManagerTests extends AndroidTestCase {
class InstallParams {
Uri packageURI;
- PackageParser.Package pkg;
+ ParsingPackage pkg;
InstallParams(String outFileName, int rawResId) throws PackageParserException {
this.pkg = getParsedPackage(outFileName, rawResId);
- this.packageURI = Uri.fromFile(new File(pkg.codePath));
+ this.packageURI = Uri.fromFile(new File(pkg.getCodePath()));
}
- InstallParams(PackageParser.Package pkg) {
- this.packageURI = Uri.fromFile(new File(pkg.codePath));
+ InstallParams(ParsingPackage pkg) {
+ this.packageURI = Uri.fromFile(new File(pkg.getCodePath()));
this.pkg = pkg;
}
long getApkSize() {
- File file = new File(pkg.codePath);
+ File file = new File(pkg.getCodePath());
return file.length();
}
}
@@ -680,14 +685,12 @@ public class PackageManagerTests extends AndroidTestCase {
}
}
- private PackageParser.Package getParsedPackage(String outFileName, int rawResId)
- throws PackageParserException {
+ private ParsingPackage getParsedPackage(String outFileName, int rawResId) {
PackageManager pm = mContext.getPackageManager();
File filesDir = mContext.getFilesDir();
File outFile = new File(filesDir, outFileName);
Uri packageURI = getInstallablePackage(rawResId, outFile);
- PackageParser.Package pkg = parsePackage(packageURI);
- return pkg;
+ return parsePackage(packageURI);
}
/*
@@ -698,15 +701,15 @@ public class PackageManagerTests extends AndroidTestCase {
private void installFromRawResource(InstallParams ip, int flags, boolean cleanUp, boolean fail,
int result, int expInstallLocation) throws Exception {
PackageManager pm = mContext.getPackageManager();
- PackageParser.Package pkg = ip.pkg;
+ ParsingPackage pkg = ip.pkg;
Uri packageURI = ip.packageURI;
if ((flags & PackageManager.INSTALL_REPLACE_EXISTING) == 0) {
// Make sure the package doesn't exist
try {
- ApplicationInfo appInfo = pm.getApplicationInfo(pkg.packageName,
+ ApplicationInfo appInfo = pm.getApplicationInfo(pkg.getPackageName(),
PackageManager.MATCH_UNINSTALLED_PACKAGES);
- GenericReceiver receiver = new DeleteReceiver(pkg.packageName);
- invokeDeletePackage(pkg.packageName, 0, receiver);
+ GenericReceiver receiver = new DeleteReceiver(pkg.getPackageName());
+ invokeDeletePackage(pkg.getPackageName(), 0, receiver);
} catch (IllegalArgumentException | NameNotFoundException e) {
}
}
@@ -714,10 +717,10 @@ public class PackageManagerTests extends AndroidTestCase {
if (fail) {
invokeInstallPackageFail(packageURI, flags, result);
if ((flags & PackageManager.INSTALL_REPLACE_EXISTING) == 0) {
- assertNotInstalled(pkg.packageName);
+ assertNotInstalled(pkg.getPackageName());
}
} else {
- InstallReceiver receiver = new InstallReceiver(pkg.packageName);
+ InstallReceiver receiver = new InstallReceiver(pkg.getPackageName());
invokeInstallPackage(packageURI, flags, receiver, true);
// Verify installed information
assertInstall(pkg, flags, expInstallLocation);
@@ -818,15 +821,15 @@ public class PackageManagerTests extends AndroidTestCase {
Log.i(TAG, "replace=" + replace);
GenericReceiver receiver;
if (replace) {
- receiver = new ReplaceReceiver(ip.pkg.packageName);
+ receiver = new ReplaceReceiver(ip.pkg.getPackageName());
Log.i(TAG, "Creating replaceReceiver");
} else {
- receiver = new InstallReceiver(ip.pkg.packageName);
+ receiver = new InstallReceiver(ip.pkg.getPackageName());
}
try {
invokeInstallPackage(ip.packageURI, flags, receiver, true);
if (replace) {
- assertInstall(ip.pkg, flags, ip.pkg.installLocation);
+ assertInstall(ip.pkg, flags, ip.pkg.getInstallLocation());
}
} finally {
cleanUpInstall(ip);
@@ -957,20 +960,20 @@ public class PackageManagerTests extends AndroidTestCase {
public void deleteFromRawResource(int iFlags, int dFlags) throws Exception {
InstallParams ip = sampleInstallFromRawResource(iFlags, false);
boolean retainData = ((dFlags & PackageManager.DELETE_KEEP_DATA) != 0);
- GenericReceiver receiver = new DeleteReceiver(ip.pkg.packageName);
+ GenericReceiver receiver = new DeleteReceiver(ip.pkg.getPackageName());
try {
- assertTrue(invokeDeletePackage(ip.pkg.packageName, dFlags, receiver));
+ assertTrue(invokeDeletePackage(ip.pkg.getPackageName(), dFlags, receiver));
ApplicationInfo info = null;
Log.i(TAG, "okay4");
try {
- info = getPm().getApplicationInfo(ip.pkg.packageName,
+ info = getPm().getApplicationInfo(ip.pkg.getPackageName(),
PackageManager.MATCH_UNINSTALLED_PACKAGES);
} catch (NameNotFoundException e) {
info = null;
}
if (retainData) {
assertNotNull(info);
- assertEquals(info.packageName, ip.pkg.packageName);
+ assertEquals(info.packageName, ip.pkg.getPackageName());
} else {
assertNull(info);
}
@@ -998,9 +1001,9 @@ public class PackageManagerTests extends AndroidTestCase {
}
Runtime.getRuntime().gc();
try {
- cleanUpInstall(ip.pkg.packageName);
+ cleanUpInstall(ip.pkg.getPackageName());
} finally {
- File outFile = new File(ip.pkg.codePath);
+ File outFile = new File(ip.pkg.getCodePath());
if (outFile != null && outFile.exists()) {
outFile.delete();
}
@@ -1070,13 +1073,13 @@ public class PackageManagerTests extends AndroidTestCase {
InstallParams ip = installFromRawResource("install.apk", iApk,
iFlags, false,
false, -1, PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
- GenericReceiver receiver = new ReplaceReceiver(ip.pkg.packageName);
+ GenericReceiver receiver = new ReplaceReceiver(ip.pkg.getPackageName());
int replaceFlags = rFlags | PackageManager.INSTALL_REPLACE_EXISTING;
try {
InstallParams rp = installFromRawResource("install.apk", rApk,
replaceFlags, false,
false, -1, PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL);
- assertInstall(rp.pkg, replaceFlags, rp.pkg.installLocation);
+ assertInstall(rp.pkg, replaceFlags, rp.pkg.getInstallLocation());
} catch (Exception e) {
failStr("Failed with exception : " + e);
} finally {
@@ -1103,7 +1106,7 @@ public class PackageManagerTests extends AndroidTestCase {
InstallParams rp = installFromRawResource("install.apk", rApk,
replaceFlags, false,
false, -1, PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL);
- assertInstall(rp.pkg, replaceFlags, ip.pkg.installLocation);
+ assertInstall(rp.pkg, replaceFlags, ip.pkg.getInstallLocation());
} catch (Exception e) {
failStr("Failed with exception : " + e);
} finally {
@@ -1204,18 +1207,18 @@ public class PackageManagerTests extends AndroidTestCase {
// Install first
ip = installFromRawResource("install.apk", rawResId, installFlags, false,
false, -1, PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
- ApplicationInfo oldAppInfo = getPm().getApplicationInfo(ip.pkg.packageName, 0);
+ ApplicationInfo oldAppInfo = getPm().getApplicationInfo(ip.pkg.getPackageName(), 0);
if (fail) {
- assertTrue(invokeMovePackageFail(ip.pkg.packageName, moveFlags, result));
- ApplicationInfo info = getPm().getApplicationInfo(ip.pkg.packageName, 0);
+ assertTrue(invokeMovePackageFail(ip.pkg.getPackageName(), moveFlags, result));
+ ApplicationInfo info = getPm().getApplicationInfo(ip.pkg.getPackageName(), 0);
assertNotNull(info);
assertEquals(oldAppInfo.flags, info.flags);
} else {
// Create receiver based on expRetCode
- MoveReceiver receiver = new MoveReceiver(ip.pkg.packageName);
- boolean retCode = invokeMovePackage(ip.pkg.packageName, moveFlags, receiver);
+ MoveReceiver receiver = new MoveReceiver(ip.pkg.getPackageName());
+ boolean retCode = invokeMovePackage(ip.pkg.getPackageName(), moveFlags, receiver);
assertTrue(retCode);
- ApplicationInfo info = getPm().getApplicationInfo(ip.pkg.packageName, 0);
+ ApplicationInfo info = getPm().getApplicationInfo(ip.pkg.getPackageName(), 0);
assertNotNull("ApplicationInfo for recently installed application should exist",
info);
if ((moveFlags & PackageManager.MOVE_INTERNAL) != 0) {
@@ -1294,9 +1297,9 @@ public class PackageManagerTests extends AndroidTestCase {
ip = installFromRawResource("install.apk", R.raw.install, installFlags, false,
false, -1, PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
// Delete the package now retaining data.
- GenericReceiver receiver = new DeleteReceiver(ip.pkg.packageName);
- invokeDeletePackage(ip.pkg.packageName, PackageManager.DELETE_KEEP_DATA, receiver);
- assertTrue(invokeMovePackageFail(ip.pkg.packageName, moveFlags, result));
+ GenericReceiver receiver = new DeleteReceiver(ip.pkg.getPackageName());
+ invokeDeletePackage(ip.pkg.getPackageName(), PackageManager.DELETE_KEEP_DATA, receiver);
+ assertTrue(invokeMovePackageFail(ip.pkg.getPackageName(), moveFlags, result));
} catch (Exception e) {
failStr(e);
} finally {
@@ -1712,7 +1715,7 @@ public class PackageManagerTests extends AndroidTestCase {
ip = installFromRawResource("install.apk", iApk,
iFlags, false,
false, -1, PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
- assertInstall(ip.pkg, iFlags, ip.pkg.installLocation);
+ assertInstall(ip.pkg, iFlags, ip.pkg.getInstallLocation());
assertPermissions(BASE_PERMISSIONS_DEFINED);
// **: Upon installing package, are its permissions granted?
@@ -1722,15 +1725,15 @@ public class PackageManagerTests extends AndroidTestCase {
ip2 = installFromRawResource("install2.apk", i2Apk,
i2Flags, false,
false, -1, PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
- assertInstall(ip2.pkg, i2Flags, ip2.pkg.installLocation);
+ assertInstall(ip2.pkg, i2Flags, ip2.pkg.getInstallLocation());
assertPermissions(BASE_PERMISSIONS_USED);
// **: Upon removing but not deleting, are permissions retained?
- GenericReceiver receiver = new DeleteReceiver(ip.pkg.packageName);
+ GenericReceiver receiver = new DeleteReceiver(ip.pkg.getPackageName());
try {
- invokeDeletePackage(ip.pkg.packageName, PackageManager.DELETE_KEEP_DATA, receiver);
+ invokeDeletePackage(ip.pkg.getPackageName(), PackageManager.DELETE_KEEP_DATA, receiver);
} catch (Exception e) {
failStr(e);
}
@@ -1742,14 +1745,14 @@ public class PackageManagerTests extends AndroidTestCase {
ip = installFromRawResource("install.apk", iApk,
iFlags | PackageManager.INSTALL_REPLACE_EXISTING, false,
false, -1, PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
- assertInstall(ip.pkg, iFlags, ip.pkg.installLocation);
+ assertInstall(ip.pkg, iFlags, ip.pkg.getInstallLocation());
assertPermissions(BASE_PERMISSIONS_DEFINED);
assertPermissions(BASE_PERMISSIONS_USED);
// **: Upon deleting package, are all permissions removed?
try {
- invokeDeletePackage(ip.pkg.packageName, 0, receiver);
+ invokeDeletePackage(ip.pkg.getPackageName(), 0, receiver);
ip = null;
} catch (Exception e) {
failStr(e);
@@ -1759,9 +1762,9 @@ public class PackageManagerTests extends AndroidTestCase {
// **: Delete package using permissions; nothing to check here.
- GenericReceiver receiver2 = new DeleteReceiver(ip2.pkg.packageName);
+ GenericReceiver receiver2 = new DeleteReceiver(ip2.pkg.getPackageName());
try {
- invokeDeletePackage(ip2.pkg.packageName, 0, receiver);
+ invokeDeletePackage(ip2.pkg.getPackageName(), 0, receiver);
ip2 = null;
} catch (Exception e) {
failStr(e);
@@ -1772,7 +1775,7 @@ public class PackageManagerTests extends AndroidTestCase {
ip2 = installFromRawResource("install2.apk", i2Apk,
i2Flags, false,
false, -1, PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
- assertInstall(ip2.pkg, i2Flags, ip2.pkg.installLocation);
+ assertInstall(ip2.pkg, i2Flags, ip2.pkg.getInstallLocation());
assertPermissions(BASE_PERMISSIONS_NOTUSED);
// **: Upon installing declaring package, are sig permissions granted
@@ -1781,7 +1784,7 @@ public class PackageManagerTests extends AndroidTestCase {
ip = installFromRawResource("install.apk", iApk,
iFlags, false,
false, -1, PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
- assertInstall(ip.pkg, iFlags, ip.pkg.installLocation);
+ assertInstall(ip.pkg, iFlags, ip.pkg.getInstallLocation());
assertPermissions(BASE_PERMISSIONS_DEFINED);
assertPermissions(BASE_PERMISSIONS_SIGUSED);
@@ -1790,13 +1793,13 @@ public class PackageManagerTests extends AndroidTestCase {
ip2 = installFromRawResource("install2.apk", i2Apk,
i2Flags | PackageManager.INSTALL_REPLACE_EXISTING, false,
false, -1, PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
- assertInstall(ip2.pkg, i2Flags, ip2.pkg.installLocation);
+ assertInstall(ip2.pkg, i2Flags, ip2.pkg.getInstallLocation());
assertPermissions(BASE_PERMISSIONS_NOTUSED);
// **: Upon deleting package, are all permissions removed?
try {
- invokeDeletePackage(ip.pkg.packageName, 0, receiver);
+ invokeDeletePackage(ip.pkg.getPackageName(), 0, receiver);
ip = null;
} catch (Exception e) {
failStr(e);
@@ -1807,7 +1810,7 @@ public class PackageManagerTests extends AndroidTestCase {
// **: Delete package using permissions; nothing to check here.
try {
- invokeDeletePackage(ip2.pkg.packageName, 0, receiver);
+ invokeDeletePackage(ip2.pkg.getPackageName(), 0, receiver);
ip2 = null;
} catch (Exception e) {
failStr(e);
@@ -1862,7 +1865,7 @@ public class PackageManagerTests extends AndroidTestCase {
int rFlags = PackageManager.INSTALL_REPLACE_EXISTING;
String apk1Name = "install1.apk";
String apk2Name = "install2.apk";
- PackageParser.Package pkg1 = getParsedPackage(apk1Name, apk1);
+ ParsingPackage pkg1 = getParsedPackage(apk1Name, apk1);
try {
InstallParams ip = installFromRawResource(apk1Name, apk1, 0, false,
false, -1, PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
@@ -1873,7 +1876,7 @@ public class PackageManagerTests extends AndroidTestCase {
failStr(e.getMessage());
} finally {
if (cleanUp) {
- cleanUpInstall(pkg1.packageName);
+ cleanUpInstall(pkg1.getPackageName());
}
}
return null;
@@ -2460,16 +2463,16 @@ public class PackageManagerTests extends AndroidTestCase {
File outFile = new File(filesDir, apk2Name);
int rawResId = apk2;
Uri packageURI = getInstallablePackage(rawResId, outFile);
- PackageParser.Package pkg = parsePackage(packageURI);
+ ParsingPackage pkg = parsePackage(packageURI);
try {
- getPi().uninstall(pkg.packageName,
+ getPi().uninstall(pkg.getPackageName(),
PackageManager.DELETE_ALL_USERS,
null /*statusReceiver*/);
} catch (IllegalArgumentException ignore) {
}
// Check signatures now
int match = mContext.getPackageManager().checkSignatures(
- ip.pkg.packageName, pkg.packageName);
+ ip.pkg.getPackageName(), pkg.getPackageName());
assertEquals(PackageManager.SIGNATURE_UNKNOWN_PACKAGE, match);
} finally {
cleanUpInstall(ip);
@@ -2530,13 +2533,13 @@ public class PackageManagerTests extends AndroidTestCase {
int retCode, int expMatchResult) throws Exception {
String apk1Name = "install1.apk";
String apk2Name = "install2.apk";
- PackageParser.Package pkg1 = getParsedPackage(apk1Name, apk1);
- PackageParser.Package pkg2 = getParsedPackage(apk2Name, apk2);
+ ParsingPackage pkg1 = getParsedPackage(apk1Name, apk1);
+ ParsingPackage pkg2 = getParsedPackage(apk2Name, apk2);
try {
// Clean up before testing first.
- cleanUpInstall(pkg1.packageName);
- cleanUpInstall(pkg2.packageName);
+ cleanUpInstall(pkg1.getPackageName());
+ cleanUpInstall(pkg2.getPackageName());
installFromRawResource(apk1Name, apk1, 0, false, false, -1,
PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
if (fail) {
@@ -2545,14 +2548,14 @@ public class PackageManagerTests extends AndroidTestCase {
} else {
installFromRawResource(apk2Name, apk2, 0, false, false, -1,
PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
- int match = mContext.getPackageManager().checkSignatures(pkg1.packageName,
- pkg2.packageName);
+ int match = mContext.getPackageManager().checkSignatures(pkg1.getPackageName(),
+ pkg2.getPackageName());
assertEquals(expMatchResult, match);
}
} finally {
if (cleanUp) {
- cleanUpInstall(pkg1.packageName);
- cleanUpInstall(pkg2.packageName);
+ cleanUpInstall(pkg1.getPackageName());
+ cleanUpInstall(pkg2.getPackageName());
}
}
}
@@ -2618,15 +2621,15 @@ public class PackageManagerTests extends AndroidTestCase {
false, -1, PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
PackageManager pm = mContext.getPackageManager();
// Delete app2
- PackageParser.Package pkg = getParsedPackage(apk2Name, apk2);
+ ParsingPackage pkg = getParsedPackage(apk2Name, apk2);
try {
- getPi().uninstall(
- pkg.packageName, PackageManager.DELETE_ALL_USERS, null /*statusReceiver*/);
+ getPi().uninstall(pkg.getPackageName(), PackageManager.DELETE_ALL_USERS,
+ null /*statusReceiver*/);
} catch (IllegalArgumentException ignore) {
}
// Check signatures now
int match = mContext.getPackageManager().checkSignatures(
- ip1.pkg.packageName, pkg.packageName);
+ ip1.pkg.getPackageName(), pkg.getPackageName());
assertEquals(PackageManager.SIGNATURE_UNKNOWN_PACKAGE, match);
} finally {
if (ip1 != null) {
@@ -2831,8 +2834,8 @@ public class PackageManagerTests extends AndroidTestCase {
PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
try {
// then, remove it, keeping it's data around
- final GenericReceiver receiver = new DeleteReceiver(ip.pkg.packageName);
- invokeDeletePackage(ip.pkg.packageName, PackageManager.DELETE_KEEP_DATA, receiver);
+ final GenericReceiver receiver = new DeleteReceiver(ip.pkg.getPackageName());
+ invokeDeletePackage(ip.pkg.getPackageName(), PackageManager.DELETE_KEEP_DATA, receiver);
final List<PackageInfo> packages = getPm().getInstalledPackages(flags);
assertNotNull("installed packages cannot be null", packages);
diff --git a/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java b/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java
index 164c372768c0..bfcf52af80bf 100644
--- a/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java
+++ b/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java
@@ -100,12 +100,12 @@ public class ImeInsetsSourceConsumerTest {
// test if setVisibility can show IME
mImeConsumer.onWindowFocusGained();
mImeConsumer.applyImeVisibility(true);
- mController.cancelExistingAnimation();
+ mController.cancelExistingAnimations();
assertTrue(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
// test if setVisibility can hide IME
mImeConsumer.applyImeVisibility(false);
- mController.cancelExistingAnimation();
+ mController.cancelExistingAnimations();
assertFalse(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
});
}
diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java
index cc85332590ba..d4c256972b28 100644
--- a/core/tests/coretests/src/android/view/InsetsControllerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java
@@ -245,14 +245,14 @@ public class InsetsControllerTest {
mController.applyImeVisibility(true /* setVisible */);
mController.show(Type.all());
// quickly jump to final state by cancelling it.
- mController.cancelExistingAnimation();
+ mController.cancelExistingAnimations();
assertTrue(mController.getSourceConsumer(navBar.getType()).isRequestedVisible());
assertTrue(mController.getSourceConsumer(statusBar.getType()).isRequestedVisible());
assertTrue(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
mController.applyImeVisibility(false /* setVisible */);
mController.hide(Type.all());
- mController.cancelExistingAnimation();
+ mController.cancelExistingAnimations();
assertFalse(mController.getSourceConsumer(navBar.getType()).isRequestedVisible());
assertFalse(mController.getSourceConsumer(statusBar.getType()).isRequestedVisible());
assertFalse(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
@@ -268,10 +268,10 @@ public class InsetsControllerTest {
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
mController.getSourceConsumer(ITYPE_IME).onWindowFocusGained();
mController.applyImeVisibility(true);
- mController.cancelExistingAnimation();
+ mController.cancelExistingAnimations();
assertTrue(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
mController.applyImeVisibility(false);
- mController.cancelExistingAnimation();
+ mController.cancelExistingAnimations();
assertFalse(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
mController.getSourceConsumer(ITYPE_IME).onWindowFocusLost();
});
@@ -291,7 +291,7 @@ public class InsetsControllerTest {
mController.hide(types);
assertEquals(ANIMATION_TYPE_HIDE, mController.getAnimationType(ITYPE_NAVIGATION_BAR));
assertEquals(ANIMATION_TYPE_HIDE, mController.getAnimationType(ITYPE_STATUS_BAR));
- mController.cancelExistingAnimation();
+ mController.cancelExistingAnimations();
assertEquals(ANIMATION_TYPE_NONE, mController.getAnimationType(ITYPE_NAVIGATION_BAR));
assertEquals(ANIMATION_TYPE_NONE, mController.getAnimationType(ITYPE_STATUS_BAR));
assertFalse(mController.getSourceConsumer(navBar.getType()).isRequestedVisible());
@@ -302,7 +302,7 @@ public class InsetsControllerTest {
mController.show(types);
assertEquals(ANIMATION_TYPE_SHOW, mController.getAnimationType(ITYPE_NAVIGATION_BAR));
assertEquals(ANIMATION_TYPE_SHOW, mController.getAnimationType(ITYPE_STATUS_BAR));
- mController.cancelExistingAnimation();
+ mController.cancelExistingAnimations();
assertTrue(mController.getSourceConsumer(navBar.getType()).isRequestedVisible());
assertTrue(mController.getSourceConsumer(statusBar.getType()).isRequestedVisible());
assertFalse(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
@@ -321,21 +321,21 @@ public class InsetsControllerTest {
int types = Type.navigationBars() | Type.systemBars();
// test show select types.
mController.show(types);
- mController.cancelExistingAnimation();
+ mController.cancelExistingAnimations();
assertTrue(mController.getSourceConsumer(navBar.getType()).isRequestedVisible());
assertTrue(mController.getSourceConsumer(statusBar.getType()).isRequestedVisible());
assertFalse(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
// test hide all
mController.hide(Type.all());
- mController.cancelExistingAnimation();
+ mController.cancelExistingAnimations();
assertFalse(mController.getSourceConsumer(navBar.getType()).isRequestedVisible());
assertFalse(mController.getSourceConsumer(statusBar.getType()).isRequestedVisible());
assertFalse(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
// test single show
mController.show(Type.navigationBars());
- mController.cancelExistingAnimation();
+ mController.cancelExistingAnimations();
assertTrue(mController.getSourceConsumer(navBar.getType()).isRequestedVisible());
assertFalse(mController.getSourceConsumer(statusBar.getType()).isRequestedVisible());
assertFalse(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
@@ -363,7 +363,7 @@ public class InsetsControllerTest {
mController.hide(Type.systemBars());
assertEquals(ANIMATION_TYPE_HIDE, mController.getAnimationType(ITYPE_NAVIGATION_BAR));
assertEquals(ANIMATION_TYPE_HIDE, mController.getAnimationType(ITYPE_STATUS_BAR));
- mController.cancelExistingAnimation();
+ mController.cancelExistingAnimations();
assertFalse(mController.getSourceConsumer(navBar.getType()).isRequestedVisible());
assertFalse(mController.getSourceConsumer(statusBar.getType()).isRequestedVisible());
assertFalse(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
@@ -372,7 +372,7 @@ public class InsetsControllerTest {
mController.show(Type.systemBars());
assertEquals(ANIMATION_TYPE_SHOW, mController.getAnimationType(ITYPE_NAVIGATION_BAR));
assertEquals(ANIMATION_TYPE_SHOW, mController.getAnimationType(ITYPE_STATUS_BAR));
- mController.cancelExistingAnimation();
+ mController.cancelExistingAnimations();
assertTrue(mController.getSourceConsumer(navBar.getType()).isRequestedVisible());
assertTrue(mController.getSourceConsumer(statusBar.getType()).isRequestedVisible());
assertFalse(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
@@ -383,7 +383,7 @@ public class InsetsControllerTest {
mController.hide(Type.navigationBars());
assertEquals(ANIMATION_TYPE_HIDE, mController.getAnimationType(ITYPE_NAVIGATION_BAR));
assertEquals(ANIMATION_TYPE_NONE, mController.getAnimationType(ITYPE_STATUS_BAR));
- mController.cancelExistingAnimation();
+ mController.cancelExistingAnimations();
assertFalse(mController.getSourceConsumer(navBar.getType()).isRequestedVisible());
assertTrue(mController.getSourceConsumer(statusBar.getType()).isRequestedVisible());
assertFalse(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
@@ -391,7 +391,7 @@ public class InsetsControllerTest {
mController.hide(Type.systemBars());
assertEquals(ANIMATION_TYPE_NONE, mController.getAnimationType(ITYPE_NAVIGATION_BAR));
assertEquals(ANIMATION_TYPE_HIDE, mController.getAnimationType(ITYPE_STATUS_BAR));
- mController.cancelExistingAnimation();
+ mController.cancelExistingAnimations();
assertFalse(mController.getSourceConsumer(navBar.getType()).isRequestedVisible());
assertFalse(mController.getSourceConsumer(statusBar.getType()).isRequestedVisible());
assertFalse(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
@@ -411,13 +411,13 @@ public class InsetsControllerTest {
// show two at a time and hide one by one.
mController.show(types);
mController.hide(Type.navigationBars());
- mController.cancelExistingAnimation();
+ mController.cancelExistingAnimations();
assertFalse(mController.getSourceConsumer(navBar.getType()).isRequestedVisible());
assertTrue(mController.getSourceConsumer(statusBar.getType()).isRequestedVisible());
assertFalse(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
mController.hide(Type.systemBars());
- mController.cancelExistingAnimation();
+ mController.cancelExistingAnimations();
assertFalse(mController.getSourceConsumer(navBar.getType()).isRequestedVisible());
assertFalse(mController.getSourceConsumer(statusBar.getType()).isRequestedVisible());
assertFalse(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
@@ -431,7 +431,7 @@ public class InsetsControllerTest {
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
mController.hide(Type.statusBars());
- mController.cancelExistingAnimation();
+ mController.cancelExistingAnimations();
assertFalse(mController.getSourceConsumer(ITYPE_STATUS_BAR).isRequestedVisible());
assertFalse(mController.getState().getSource(ITYPE_STATUS_BAR).isVisible());
@@ -446,7 +446,7 @@ public class InsetsControllerTest {
// Gaining control
mController.onControlsChanged(createSingletonControl(ITYPE_STATUS_BAR));
assertEquals(ANIMATION_TYPE_HIDE, mController.getAnimationType(ITYPE_STATUS_BAR));
- mController.cancelExistingAnimation();
+ mController.cancelExistingAnimations();
assertFalse(mController.getSourceConsumer(ITYPE_STATUS_BAR).isRequestedVisible());
assertFalse(mController.getState().getSource(ITYPE_STATUS_BAR).isVisible());
});
@@ -468,7 +468,7 @@ public class InsetsControllerTest {
mController.onControlsChanged(createSingletonControl(ITYPE_IME));
assertEquals(ANIMATION_TYPE_SHOW, mController.getAnimationType(ITYPE_IME));
- mController.cancelExistingAnimation();
+ mController.cancelExistingAnimations();
assertTrue(mController.getSourceConsumer(ITYPE_IME).isRequestedVisible());
assertTrue(mController.getState().getSource(ITYPE_IME).isVisible());
});
@@ -489,7 +489,7 @@ public class InsetsControllerTest {
mController.show(ime(), true /* fromIme */);
assertEquals(ANIMATION_TYPE_SHOW, mController.getAnimationType(ITYPE_IME));
- mController.cancelExistingAnimation();
+ mController.cancelExistingAnimations();
assertTrue(mController.getSourceConsumer(ITYPE_IME).isRequestedVisible());
assertTrue(mController.getState().getSource(ITYPE_IME).isVisible());
});
@@ -658,7 +658,7 @@ public class InsetsControllerTest {
mController.getState().getSource(ITYPE_IME).getFrame());
assertNotEquals(new Rect(4, 5, 6, 7),
mController.getState().getSource(ITYPE_IME).getVisibleFrame());
- mController.cancelExistingAnimation();
+ mController.cancelExistingAnimations();
assertEquals(new Rect(0, 1, 2, 3),
mController.getState().getSource(ITYPE_IME).getFrame());
assertEquals(new Rect(4, 5, 6, 7),
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 80fb35813009..e23a3cad914b 100644
--- a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
@@ -1763,7 +1763,8 @@ public class ChooserActivityTest {
Mockito.anyBoolean(),
Mockito.isA(List.class)))
.thenReturn(new ArrayList<>(personalResolvedComponentInfos));
- Intent chooserIntent = createChooserIntent(new Intent[] {new Intent("action.fake")});
+ Intent chooserIntent = createChooserIntent(createSendTextIntent(),
+ new Intent[] {new Intent("action.fake")});
ResolveInfo[] chosen = new ResolveInfo[1];
sOverrides.onSafelyStartCallback = targetInfo -> {
chosen[0] = targetInfo.getResolveInfo();
@@ -1796,7 +1797,7 @@ public class ChooserActivityTest {
new Intent("action.fake1"),
new Intent("action.fake2")
};
- Intent chooserIntent = createChooserIntent(initialIntents);
+ Intent chooserIntent = createChooserIntent(createSendTextIntent(), initialIntents);
sOverrides.packageManager = mock(PackageManager.class);
when(sOverrides.packageManager.resolveActivity(any(Intent.class), anyInt()))
.thenReturn(createFakeResolveInfo());
@@ -1809,12 +1810,74 @@ public class ChooserActivityTest {
assertThat(activity.getWorkListAdapter().getCallerTargetCount(), is(0));
}
- private Intent createChooserIntent(Intent[] initialIntents) {
+ @Test
+ public void testWorkTab_xProfileIntentsDisabled_personalToWork_nonSendIntent_emptyStateShown() {
+ // enable the work tab feature flag
+ ResolverActivity.ENABLE_TABBED_VIEW = true;
+ markWorkProfileUserAvailable();
+ int workProfileTargets = 4;
+ List<ResolvedComponentInfo> personalResolvedComponentInfos =
+ createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10);
+ List<ResolvedComponentInfo> workResolvedComponentInfos =
+ createResolvedComponentsForTest(workProfileTargets);
+ sOverrides.hasCrossProfileIntents = false;
+ setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
+ Intent[] initialIntents = {
+ new Intent("action.fake1"),
+ new Intent("action.fake2")
+ };
+ Intent chooserIntent = createChooserIntent(new Intent(), initialIntents);
+ sOverrides.packageManager = mock(PackageManager.class);
+ when(sOverrides.packageManager.resolveActivity(any(Intent.class), anyInt()))
+ .thenReturn(createFakeResolveInfo());
+
+ final ChooserWrapperActivity activity = mActivityRule.launchActivity(chooserIntent);
+ waitForIdle();
+ onView(withText(R.string.resolver_work_tab)).perform(click());
+ waitForIdle();
+ onView(withId(R.id.contentPanel))
+ .perform(swipeUp());
+
+ onView(withText(R.string.resolver_cant_access_work_apps))
+ .check(matches(isDisplayed()));
+ }
+
+ @Test
+ public void testWorkTab_noWorkAppsAvailable_nonSendIntent_emptyStateShown() {
+ // enable the work tab feature flag
+ ResolverActivity.ENABLE_TABBED_VIEW = true;
+ markWorkProfileUserAvailable();
+ List<ResolvedComponentInfo> personalResolvedComponentInfos =
+ createResolvedComponentsForTest(3);
+ List<ResolvedComponentInfo> workResolvedComponentInfos =
+ createResolvedComponentsForTest(0);
+ setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
+ Intent[] initialIntents = {
+ new Intent("action.fake1"),
+ new Intent("action.fake2")
+ };
+ Intent chooserIntent = createChooserIntent(new Intent(), initialIntents);
+ sOverrides.packageManager = mock(PackageManager.class);
+ when(sOverrides.packageManager.resolveActivity(any(Intent.class), anyInt()))
+ .thenReturn(createFakeResolveInfo());
+
+ mActivityRule.launchActivity(chooserIntent);
+ waitForIdle();
+ onView(withId(R.id.contentPanel))
+ .perform(swipeUp());
+ onView(withText(R.string.resolver_work_tab)).perform(click());
+ waitForIdle();
+
+ onView(withText(R.string.resolver_no_work_apps_available_resolve))
+ .check(matches(isDisplayed()));
+ }
+
+ private Intent createChooserIntent(Intent intent, Intent[] initialIntents) {
Intent chooserIntent = new Intent();
chooserIntent.setAction(Intent.ACTION_CHOOSER);
chooserIntent.putExtra(Intent.EXTRA_TEXT, "testing intent sending");
chooserIntent.putExtra(Intent.EXTRA_TITLE, "some title");
- chooserIntent.putExtra(Intent.EXTRA_INTENT, createSendTextIntent());
+ chooserIntent.putExtra(Intent.EXTRA_INTENT, intent);
chooserIntent.setType("text/plain");
if (initialIntents != null) {
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, initialIntents);
diff --git a/errorprone/Android.bp b/errorprone/Android.bp
index 016b85510a94..098f4bfa74ac 100644
--- a/errorprone/Android.bp
+++ b/errorprone/Android.bp
@@ -20,6 +20,4 @@ java_library_host {
plugins: [
"//external/dagger2:dagger2-auto-service",
],
-
- javacflags: ["-verbose"],
}
diff --git a/errorprone/java/com/google/errorprone/bugpatterns/android/RethrowFromSystemChecker.java b/errorprone/java/com/google/errorprone/bugpatterns/android/RethrowFromSystemChecker.java
new file mode 100644
index 000000000000..48123abd26cb
--- /dev/null
+++ b/errorprone/java/com/google/errorprone/bugpatterns/android/RethrowFromSystemChecker.java
@@ -0,0 +1,80 @@
+/*
+ * 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.google.errorprone.bugpatterns.android;
+
+import static com.google.errorprone.BugPattern.SeverityLevel.WARNING;
+import static com.google.errorprone.matchers.Matchers.enclosingClass;
+import static com.google.errorprone.matchers.Matchers.hasAnnotation;
+import static com.google.errorprone.matchers.Matchers.instanceMethod;
+import static com.google.errorprone.matchers.Matchers.isSameType;
+import static com.google.errorprone.matchers.Matchers.methodInvocation;
+import static com.google.errorprone.matchers.Matchers.throwStatement;
+import static com.google.errorprone.matchers.Matchers.variableType;
+
+import com.google.auto.service.AutoService;
+import com.google.errorprone.BugPattern;
+import com.google.errorprone.VisitorState;
+import com.google.errorprone.bugpatterns.BugChecker;
+import com.google.errorprone.bugpatterns.BugChecker.CatchTreeMatcher;
+import com.google.errorprone.matchers.Description;
+import com.google.errorprone.matchers.Matcher;
+import com.sun.source.tree.CatchTree;
+import com.sun.source.tree.StatementTree;
+import com.sun.source.tree.Tree;
+import com.sun.source.tree.VariableTree;
+
+import java.util.List;
+
+/**
+ * Apps making calls into the system server may end up persisting internal state
+ * or making security decisions based on the perceived success or failure of a
+ * call, or any default values returned. For this reason, we want to strongly
+ * throw when there was trouble with the transaction.
+ * <p>
+ * The rethrowFromSystemServer() method is the best-practice way of doing this
+ * correctly, so that we don't clutter logs with misleading stack traces, and
+ * this checker verifies that best-practice is used.
+ */
+@AutoService(BugChecker.class)
+@BugPattern(
+ name = "AndroidFrameworkRethrowFromSystem",
+ summary = "Verifies that system_server calls use rethrowFromSystemServer()",
+ severity = WARNING)
+public final class RethrowFromSystemChecker extends BugChecker implements CatchTreeMatcher {
+ private static final Matcher<Tree> INSIDE_MANAGER =
+ enclosingClass(hasAnnotation("android.annotation.SystemService"));
+ private static final Matcher<VariableTree> REMOTE_EXCEPTION = variableType(
+ isSameType("android.os.RemoteException"));
+ private static final Matcher<StatementTree> RETHROW_FROM_SYSTEM = throwStatement(
+ methodInvocation(instanceMethod().onExactClass("android.os.RemoteException")
+ .named("rethrowFromSystemServer")));
+
+ @Override
+ public Description matchCatch(CatchTree tree, VisitorState state) {
+ if (INSIDE_MANAGER.matches(tree, state)
+ && REMOTE_EXCEPTION.matches(tree.getParameter(), state)) {
+ final List<? extends StatementTree> statements = tree.getBlock().getStatements();
+ if (statements.size() != 1 || !RETHROW_FROM_SYSTEM.matches(statements.get(0), state)) {
+ return buildDescription(tree)
+ .setMessage("Must contain single "
+ + "'throw e.rethrowFromSystemServer()' statement")
+ .build();
+ }
+ }
+ return Description.NO_MATCH;
+ }
+}
diff --git a/errorprone/java/com/google/errorprone/bugpatterns/android/TargetSdkChecker.java b/errorprone/java/com/google/errorprone/bugpatterns/android/TargetSdkChecker.java
index 1ce816c34990..232cf3f0d677 100644
--- a/errorprone/java/com/google/errorprone/bugpatterns/android/TargetSdkChecker.java
+++ b/errorprone/java/com/google/errorprone/bugpatterns/android/TargetSdkChecker.java
@@ -34,6 +34,27 @@ import com.sun.source.tree.BinaryTree;
import com.sun.source.tree.ExpressionTree;
import com.sun.source.tree.Tree.Kind;
+/**
+ * Over the years we've had several obscure bugs related to how SDK level
+ * comparisons are performed, specifically during the window of time where we've
+ * started distributing the "frankenbuild" to developers.
+ * <p>
+ * Consider the case where a framework developer shipping release "R" wants to
+ * only grant a specific behavior to modern apps; they could write this in two
+ * different ways:
+ * <ol>
+ * <li>if (targetSdkVersion > Build.VERSION_CODES.Q) {
+ * <li>if (targetSdkVersion >= Build.VERSION_CODES.R) {
+ * </ol>
+ * The safer of these two options is (2), which will ensure that developers only
+ * get the behavior when <em>both</em> the app and the platform agree on the
+ * specific SDK level having shipped.
+ * <p>
+ * Consider the breakage that would happen with option (1) if we started
+ * shipping APKs that are based on the final R SDK, but are then installed on
+ * earlier preview releases which still consider R to be CUR_DEVELOPMENT; they'd
+ * risk crashing due to behaviors that were never part of the official R SDK.
+ */
@AutoService(BugChecker.class)
@BugPattern(
name = "AndroidFrameworkTargetSdk",
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index 412b43e6a582..a112bdd0ce03 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -2954,10 +2954,10 @@ public class LocationManager {
@Override
protected void unregisterService() throws RemoteException {
- Preconditions.checkState(mListenerTransport != null);
-
- mService.unregisterGnssStatusCallback(mListenerTransport);
- mListenerTransport = null;
+ if (mListenerTransport != null) {
+ mService.unregisterGnssStatusCallback(mListenerTransport);
+ mListenerTransport = null;
+ }
}
private class GnssStatusListener extends IGnssStatusListener.Stub {
@@ -3020,10 +3020,10 @@ public class LocationManager {
@Override
protected void unregisterService() throws RemoteException {
- Preconditions.checkState(mListenerTransport != null);
-
- mService.removeGnssMeasurementsListener(mListenerTransport);
- mListenerTransport = null;
+ if (mListenerTransport != null) {
+ mService.removeGnssMeasurementsListener(mListenerTransport);
+ mListenerTransport = null;
+ }
}
@Override
@@ -3073,10 +3073,10 @@ public class LocationManager {
@Override
protected void unregisterService() throws RemoteException {
- Preconditions.checkState(mListenerTransport != null);
-
- mService.removeGnssNavigationMessageListener(mListenerTransport);
- mListenerTransport = null;
+ if (mListenerTransport != null) {
+ mService.removeGnssNavigationMessageListener(mListenerTransport);
+ mListenerTransport = null;
+ }
}
private class GnssNavigationMessageListener extends IGnssNavigationMessageListener.Stub {
@@ -3114,10 +3114,10 @@ public class LocationManager {
@Override
protected void unregisterService() throws RemoteException {
- Preconditions.checkState(mListenerTransport != null);
-
- mService.removeGnssAntennaInfoListener(mListenerTransport);
- mListenerTransport = null;
+ if (mListenerTransport != null) {
+ mService.removeGnssAntennaInfoListener(mListenerTransport);
+ mListenerTransport = null;
+ }
}
private class GnssAntennaInfoListener extends IGnssAntennaInfoListener.Stub {
@@ -3151,10 +3151,10 @@ public class LocationManager {
@Override
protected void unregisterService() throws RemoteException {
- Preconditions.checkState(mListenerTransport != null);
-
- mService.removeGnssBatchingCallback();
- mListenerTransport = null;
+ if (mListenerTransport != null) {
+ mService.removeGnssBatchingCallback();
+ mListenerTransport = null;
+ }
}
private class BatchedLocationCallback extends IBatchedLocationCallback.Stub {
diff --git a/media/java/android/media/AudioMetadata.java b/media/java/android/media/AudioMetadata.java
index c91ff0d099cf..ff9fd4187272 100644
--- a/media/java/android/media/AudioMetadata.java
+++ b/media/java/android/media/AudioMetadata.java
@@ -166,10 +166,25 @@ public final class AudioMetadata {
*
* A Boolean value which is true if Atmos is present in an E-AC3 stream.
*/
+
+ // Since Boolean isn't handled by Parceling, we translate
+ // internally to KEY_HAS_ATMOS when sending through JNI.
+ // Consider deprecating this key for KEY_HAS_ATMOS in the future.
+ //
@NonNull public static final Key<Boolean> KEY_ATMOS_PRESENT =
createKey("atmos-present", Boolean.class);
/**
+ * A key representing the presence of Atmos in an E-AC3 stream.
+ *
+ * An Integer value which is nonzero if Atmos is present in an E-AC3 stream.
+ * The integer representation is used for communication to the native side.
+ * @hide
+ */
+ @NonNull public static final Key<Integer> KEY_HAS_ATMOS =
+ createKey("has-atmos", Integer.class);
+
+ /**
* A key representing the audio encoding used for the stream.
* This is the same encoding used in {@link AudioFormat#getEncoding()}.
*
@@ -731,6 +746,15 @@ public final class AudioMetadata {
Log.e(TAG, "Failed to unpack value for map");
return null;
}
+
+ // Special handling of KEY_ATMOS_PRESENT.
+ if (key.equals(Format.KEY_HAS_ATMOS.getName())
+ && value.first == Format.KEY_HAS_ATMOS.getValueClass()) {
+ ret.set(Format.KEY_ATMOS_PRESENT,
+ (Boolean) ((int) value.second != 0)); // Translate Integer to Boolean
+ continue; // Should we store both keys in the java table?
+ }
+
ret.set(createKey(key, value.first), value.first.cast(value.second));
}
return ret;
@@ -746,11 +770,19 @@ public final class AudioMetadata {
return false;
}
for (Key<?> key : obj.keySet()) {
+ Object value = obj.get(key);
+
+ // Special handling of KEY_ATMOS_PRESENT.
+ if (key == Format.KEY_ATMOS_PRESENT) {
+ key = Format.KEY_HAS_ATMOS;
+ value = (Integer) ((boolean) value ? 1 : 0); // Translate Boolean to Integer
+ }
+
if (!strDataPackage.pack(output, key.getName())) {
Log.i(TAG, "Failed to pack key: " + key.getName());
return false;
}
- if (!OBJECT_PACKAGE.pack(output, new Pair<>(key.getValueClass(), obj.get(key)))) {
+ if (!OBJECT_PACKAGE.pack(output, new Pair<>(key.getValueClass(), value))) {
Log.i(TAG, "Failed to pack value: " + obj.get(key));
return false;
}
diff --git a/media/java/android/media/MediaRouter2Manager.java b/media/java/android/media/MediaRouter2Manager.java
index 5d61dd06c792..e3c7336905d1 100644
--- a/media/java/android/media/MediaRouter2Manager.java
+++ b/media/java/android/media/MediaRouter2Manager.java
@@ -24,7 +24,6 @@ import android.annotation.Nullable;
import android.content.Context;
import android.media.session.MediaController;
import android.media.session.MediaSessionManager;
-import android.os.Bundle;
import android.os.Handler;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -171,8 +170,7 @@ public final class MediaRouter2Manager {
public MediaController getMediaControllerForRoutingSession(
@NonNull RoutingSessionInfo sessionInfo) {
for (MediaController controller : mMediaSessionManager.getActiveSessions(null)) {
- String volumeControlId = controller.getPlaybackInfo().getVolumeControlId();
- if (TextUtils.equals(sessionInfo.getId(), volumeControlId)) {
+ if (areSessionsMatched(controller, sessionInfo)) {
return controller;
}
}
@@ -207,6 +205,37 @@ public final class MediaRouter2Manager {
}
/**
+ * Gets available routes for the given routing session.
+ * The returned routes can be passed to
+ * {@link #transfer(RoutingSessionInfo, MediaRoute2Info)} for transferring the routing session.
+ *
+ * @param sessionInfo the routing session that would be transferred
+ */
+ @NonNull
+ public List<MediaRoute2Info> getAvailableRoutesForRoutingSession(
+ @NonNull RoutingSessionInfo sessionInfo) {
+ Objects.requireNonNull(sessionInfo, "sessionInfo must not be null");
+
+ List<MediaRoute2Info> routes = new ArrayList<>();
+
+ String packageName = sessionInfo.getClientPackageName();
+ List<String> preferredFeatures = mPreferredFeaturesMap.get(packageName);
+ if (preferredFeatures == null) {
+ preferredFeatures = Collections.emptyList();
+ }
+ synchronized (mRoutesLock) {
+ for (MediaRoute2Info route : mRoutes.values()) {
+ if (route.isSystemRoute() || route.hasAnyFeatures(preferredFeatures)
+ || sessionInfo.getSelectedRoutes().contains(route.getId())
+ || sessionInfo.getTransferableRoutes().contains(route.getId())) {
+ routes.add(route);
+ }
+ }
+ }
+ return routes;
+ }
+
+ /**
* Gets the system routing session associated with no specific application.
*/
@NonNull
@@ -220,6 +249,33 @@ public final class MediaRouter2Manager {
}
/**
+ * Gets the routing session of a media session.
+ * If the session is using {#link PlaybackInfo#PLAYBACK_TYPE_LOCAL local playback},
+ * the system routing session is returned.
+ * If the session is using {#link PlaybackInfo#PLAYBACK_TYPE_REMOTE remote playback},
+ * it returns the corresponding routing session or {@code null} if it's unavailable.
+ */
+ @Nullable
+ public RoutingSessionInfo getRoutingSessionForMediaController(MediaController mediaController) {
+ MediaController.PlaybackInfo playbackInfo = mediaController.getPlaybackInfo();
+ if (playbackInfo == null) {
+ return null;
+ }
+ if (playbackInfo.getPlaybackType() == MediaController.PlaybackInfo.PLAYBACK_TYPE_LOCAL) {
+ return new RoutingSessionInfo.Builder(getSystemRoutingSession())
+ .setClientPackageName(mediaController.getPackageName())
+ .build();
+ }
+ for (RoutingSessionInfo sessionInfo : getActiveSessions()) {
+ if (!sessionInfo.isSystemSession()
+ && areSessionsMatched(mediaController, sessionInfo)) {
+ return sessionInfo;
+ }
+ }
+ return null;
+ }
+
+ /**
* Gets routing sessions of an application with the given package name.
* The first element of the returned list is the system routing session.
*
@@ -344,14 +400,6 @@ public final class MediaRouter2Manager {
/**
* Requests a volume change for a route asynchronously.
- */
- //TODO: remove this.
- public void requestSetVolume(MediaRoute2Info route, int volume) {
- setRouteVolume(route, volume);
- }
-
- /**
- * Requests a volume change for a route asynchronously.
* <p>
* It may have no effect if the route is currently not selected.
* </p>
@@ -576,22 +624,11 @@ public final class MediaRouter2Manager {
}
for (CallbackRecord record : mCallbackRecords) {
record.mExecutor.execute(() -> record.mCallback
- .onControlCategoriesChanged(packageName, preferredFeatures));
- }
- for (CallbackRecord record : mCallbackRecords) {
- record.mExecutor.execute(() -> record.mCallback
.onPreferredFeaturesChanged(packageName, preferredFeatures));
}
}
/**
- * @hide
- */
- public RoutingController getControllerForSession(@NonNull RoutingSessionInfo sessionInfo) {
- return new RoutingController(sessionInfo);
- }
-
- /**
* Gets the unmodifiable list of selected routes for the session.
*/
@NonNull
@@ -782,153 +819,32 @@ public final class MediaRouter2Manager {
}
}
- private List<MediaRoute2Info> getRoutesWithIds(List<String> routeIds) {
- synchronized (sLock) {
- return routeIds.stream().map(mRoutes::get)
- .filter(Objects::nonNull)
- .collect(Collectors.toList());
+ private boolean areSessionsMatched(MediaController mediaController,
+ RoutingSessionInfo sessionInfo) {
+ MediaController.PlaybackInfo playbackInfo = mediaController.getPlaybackInfo();
+ if (playbackInfo == null) {
+ return false;
}
- }
- //TODO: Remove this.
- /**
- * A class to control media routing session in media route provider.
- * With routing controller, an application can select a route into the session or deselect
- * a route in the session.
- */
- public final class RoutingController {
- private final Object mControllerLock = new Object();
- @GuardedBy("mControllerLock")
- private RoutingSessionInfo mSessionInfo;
-
- RoutingController(@NonNull RoutingSessionInfo sessionInfo) {
- mSessionInfo = sessionInfo;
+ String volumeControlId = playbackInfo.getVolumeControlId();
+ if (volumeControlId == null) {
+ return false;
}
- /**
- * Releases the session
- */
- public void release() {
- synchronized (mControllerLock) {
- releaseSession(mSessionInfo);
- }
- }
-
- /**
- * Gets the ID of the session
- */
- @NonNull
- public String getSessionId() {
- synchronized (mControllerLock) {
- return mSessionInfo.getId();
- }
- }
-
- /**
- * Gets the client package name of the session
- */
- @NonNull
- public String getClientPackageName() {
- synchronized (mControllerLock) {
- return mSessionInfo.getClientPackageName();
- }
- }
-
- /**
- * @return the control hints used to control route session if available.
- */
- @Nullable
- public Bundle getControlHints() {
- synchronized (mControllerLock) {
- return mSessionInfo.getControlHints();
- }
- }
-
- /**
- * @return the unmodifiable list of currently selected routes
- */
- @NonNull
- public List<MediaRoute2Info> getSelectedRoutes() {
- return MediaRouter2Manager.this.getSelectedRoutes(mSessionInfo);
- }
-
- /**
- * @return the unmodifiable list of selectable routes for the session.
- */
- @NonNull
- public List<MediaRoute2Info> getSelectableRoutes() {
- return MediaRouter2Manager.this.getSelectableRoutes(mSessionInfo);
- }
-
- /**
- * @return the unmodifiable list of deselectable routes for the session.
- */
- @NonNull
- public List<MediaRoute2Info> getDeselectableRoutes() {
- return MediaRouter2Manager.this.getDeselectableRoutes(mSessionInfo);
- }
-
- /**
- * @return the unmodifiable list of transferable routes for the session.
- */
- @NonNull
- public List<MediaRoute2Info> getTransferableRoutes() {
- List<String> routeIds;
- synchronized (mControllerLock) {
- routeIds = mSessionInfo.getTransferableRoutes();
- }
- return getRoutesWithIds(routeIds);
- }
-
- /**
- * Selects a route for the remote session. The given route must satisfy all of the
- * following conditions:
- * <ul>
- * <li>ID should not be included in {@link #getSelectedRoutes()}</li>
- * <li>ID should be included in {@link #getSelectableRoutes()}</li>
- * </ul>
- * If the route doesn't meet any of above conditions, it will be ignored.
- *
- * @see #getSelectedRoutes()
- * @see #getSelectableRoutes()
- */
- public void selectRoute(@NonNull MediaRoute2Info route) {
- MediaRouter2Manager.this.selectRoute(mSessionInfo, route);
- }
-
- /**
- * Deselects a route from the remote session. The given route must satisfy all of the
- * following conditions:
- * <ul>
- * <li>ID should be included in {@link #getSelectedRoutes()}</li>
- * <li>ID should be included in {@link #getDeselectableRoutes()}</li>
- * </ul>
- * If the route doesn't meet any of above conditions, it will be ignored.
- *
- * @see #getSelectedRoutes()
- * @see #getDeselectableRoutes()
- */
- public void deselectRoute(@NonNull MediaRoute2Info route) {
- MediaRouter2Manager.this.deselectRoute(mSessionInfo, route);
- }
-
- /**
- * Transfers session to the given rotue.
- */
- public void transferToRoute(@NonNull MediaRoute2Info route) {
- MediaRouter2Manager.this.transferToRoute(mSessionInfo, route);
+ if (TextUtils.equals(volumeControlId, sessionInfo.getId())) {
+ return true;
}
+ // Workaround for provider not being able to know the unique session ID.
+ return TextUtils.equals(volumeControlId, sessionInfo.getOriginalId())
+ && TextUtils.equals(mediaController.getPackageName(),
+ sessionInfo.getOwnerPackageName());
+ }
- /**
- * Gets the session info of the session
- *
- * @hide
- */
- @NonNull
- public RoutingSessionInfo getSessionInfo() {
- synchronized (mControllerLock) {
- return mSessionInfo;
- }
+ private List<MediaRoute2Info> getRoutesWithIds(List<String> routeIds) {
+ synchronized (sLock) {
+ return routeIds.stream().map(mRoutes::get)
+ .filter(Objects::nonNull)
+ .collect(Collectors.toList());
}
}
@@ -976,16 +892,6 @@ public final class MediaRouter2Manager {
public void onTransferFailed(@NonNull RoutingSessionInfo session,
@NonNull MediaRoute2Info route) { }
- //TODO: Remove this.
- /**
- * Called when the preferred route features of an app is changed.
- *
- * @param packageName the package name of the application
- * @param preferredFeatures the list of preferred route features set by an application.
- */
- public void onControlCategoriesChanged(@NonNull String packageName,
- @NonNull List<String> preferredFeatures) {}
-
/**
* Called when the preferred route features of an app is changed.
*
diff --git a/media/java/android/media/RoutingSessionInfo.java b/media/java/android/media/RoutingSessionInfo.java
index 608e29a7a6ca..edf1fc58ecf5 100644
--- a/media/java/android/media/RoutingSessionInfo.java
+++ b/media/java/android/media/RoutingSessionInfo.java
@@ -50,6 +50,7 @@ public final class RoutingSessionInfo implements Parcelable {
final String mId;
final CharSequence mName;
+ final String mOwnerPackageName;
final String mClientPackageName;
@Nullable
final String mProviderId;
@@ -71,6 +72,7 @@ public final class RoutingSessionInfo implements Parcelable {
mId = builder.mId;
mName = builder.mName;
+ mOwnerPackageName = builder.mOwnerPackageName;
mClientPackageName = builder.mClientPackageName;
mProviderId = builder.mProviderId;
@@ -96,6 +98,7 @@ public final class RoutingSessionInfo implements Parcelable {
mId = ensureString(src.readString());
mName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(src);
+ mOwnerPackageName = src.readString();
mClientPackageName = ensureString(src.readString());
mProviderId = src.readString();
@@ -159,6 +162,15 @@ public final class RoutingSessionInfo implements Parcelable {
}
/**
+ * Gets the package name of the session owner.
+ * @hide
+ */
+ @Nullable
+ public String getOwnerPackageName() {
+ return mOwnerPackageName;
+ }
+
+ /**
* Gets the client package name of the session
*/
@NonNull
@@ -263,6 +275,7 @@ public final class RoutingSessionInfo implements Parcelable {
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeString(mId);
dest.writeCharSequence(mName);
+ dest.writeString(mOwnerPackageName);
dest.writeString(mClientPackageName);
dest.writeString(mProviderId);
dest.writeStringList(mSelectedRoutes);
@@ -288,6 +301,7 @@ public final class RoutingSessionInfo implements Parcelable {
RoutingSessionInfo other = (RoutingSessionInfo) obj;
return Objects.equals(mId, other.mId)
&& Objects.equals(mName, other.mName)
+ && Objects.equals(mOwnerPackageName, other.mOwnerPackageName)
&& Objects.equals(mClientPackageName, other.mClientPackageName)
&& Objects.equals(mProviderId, other.mProviderId)
&& Objects.equals(mSelectedRoutes, other.mSelectedRoutes)
@@ -301,7 +315,7 @@ public final class RoutingSessionInfo implements Parcelable {
@Override
public int hashCode() {
- return Objects.hash(mId, mName, mClientPackageName, mProviderId,
+ return Objects.hash(mId, mName, mOwnerPackageName, mClientPackageName, mProviderId,
mSelectedRoutes, mSelectableRoutes, mDeselectableRoutes, mTransferableRoutes,
mVolumeMax, mVolumeHandling, mVolume);
}
@@ -356,6 +370,7 @@ public final class RoutingSessionInfo implements Parcelable {
// TODO: Reorder these (important ones first)
final String mId;
CharSequence mName;
+ String mOwnerPackageName;
String mClientPackageName;
String mProviderId;
final List<String> mSelectedRoutes;
@@ -440,6 +455,17 @@ public final class RoutingSessionInfo implements Parcelable {
}
/**
+ * Sets the package name of the session owner. It is expected to be called by the system.
+ *
+ * @hide
+ */
+ @NonNull
+ public Builder setOwnerPackageName(@Nullable String packageName) {
+ mOwnerPackageName = packageName;
+ return this;
+ }
+
+ /**
* Sets the client package name of the session.
*
* @hide
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index f058a02ceb1e..e701055c2894 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -1805,7 +1805,7 @@ public final class TvInputManager {
String tvInputSessionId, int priorityHint,
Executor executor, final HardwareCallback callback) {
try {
- return new Hardware(
+ ITvInputHardware hardware =
mService.acquireTvInputHardware(deviceId, new ITvInputHardwareCallback.Stub() {
@Override
public void onReleased() {
@@ -1826,7 +1826,11 @@ public final class TvInputManager {
Binder.restoreCallingIdentity(identity);
}
}
- }, info, mUserId, tvInputSessionId, priorityHint));
+ }, info, mUserId, tvInputSessionId, priorityHint);
+ if (hardware == null) {
+ return null;
+ }
+ return new Hardware(hardware);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/media/java/android/media/tv/tunerresourcemanager/ResourceClientProfile.java b/media/java/android/media/tv/tunerresourcemanager/ResourceClientProfile.java
index 598ff8f3f075..28f1ac916690 100644
--- a/media/java/android/media/tv/tunerresourcemanager/ResourceClientProfile.java
+++ b/media/java/android/media/tv/tunerresourcemanager/ResourceClientProfile.java
@@ -17,6 +17,7 @@
package android.media.tv.tunerresourcemanager;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Log;
@@ -81,7 +82,7 @@ public final class ResourceClientProfile implements Parcelable {
* OEM. The id of the useCaseVendor should be passed through this parameter. Any
* undefined use case would cause IllegalArgumentException.
*/
- public ResourceClientProfile(@NonNull String tvInputSessionId,
+ public ResourceClientProfile(@Nullable String tvInputSessionId,
int useCase) {
mTvInputSessionId = tvInputSessionId;
mUseCase = useCase;
@@ -92,7 +93,7 @@ public final class ResourceClientProfile implements Parcelable {
*
* @return the value of the tv input session id.
*/
- @NonNull
+ @Nullable
public String getTvInputSessionId() {
return mTvInputSessionId;
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBarController.java b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBarController.java
index 9e194fb49d3a..288e5cf13c2e 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBarController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBarController.java
@@ -73,7 +73,7 @@ public class CarNavigationBarController {
}
/**
- * Hides all navigation bars.
+ * Hides all system bars.
*/
public void hideBars() {
if (mTopView != null) {
@@ -85,7 +85,7 @@ public class CarNavigationBarController {
}
/**
- * Shows all navigation bars.
+ * Shows all system bars.
*/
public void showBars() {
if (mTopView != null) {
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/notification/CarHeadsUpNotificationSystemContainer.java b/packages/CarSystemUI/src/com/android/systemui/car/notification/CarHeadsUpNotificationSystemContainer.java
index 20fcca0d0220..aeb1d39599db 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/notification/CarHeadsUpNotificationSystemContainer.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/notification/CarHeadsUpNotificationSystemContainer.java
@@ -29,41 +29,40 @@ import android.widget.FrameLayout;
import com.android.car.notification.R;
import com.android.car.notification.headsup.CarHeadsUpNotificationContainer;
import com.android.systemui.car.CarDeviceProvisionedController;
+import com.android.systemui.car.window.OverlayViewGlobalStateController;
import com.android.systemui.dagger.qualifiers.Main;
import javax.inject.Inject;
import javax.inject.Singleton;
-import dagger.Lazy;
-
/**
* A controller for SysUI's HUN display.
*/
@Singleton
public class CarHeadsUpNotificationSystemContainer implements CarHeadsUpNotificationContainer {
private final CarDeviceProvisionedController mCarDeviceProvisionedController;
- private final Lazy<NotificationPanelViewController> mNotificationPanelViewControllerLazy;
+ private final OverlayViewGlobalStateController mOverlayViewGlobalStateController;
private final ViewGroup mWindow;
private final FrameLayout mHeadsUpContentFrame;
- private final boolean mEnableHeadsUpNotificationWhenNotificationShadeOpen;
-
@Inject
CarHeadsUpNotificationSystemContainer(Context context,
@Main Resources resources,
CarDeviceProvisionedController deviceProvisionedController,
WindowManager windowManager,
- Lazy<NotificationPanelViewController> notificationPanelViewControllerLazy) {
+ OverlayViewGlobalStateController overlayViewGlobalStateController) {
mCarDeviceProvisionedController = deviceProvisionedController;
- mNotificationPanelViewControllerLazy = notificationPanelViewControllerLazy;
+ mOverlayViewGlobalStateController = overlayViewGlobalStateController;
boolean showOnBottom = resources.getBoolean(R.bool.config_showHeadsUpNotificationOnBottom);
+ // Use TYPE_STATUS_BAR_SUB_PANEL window type since we need to find a window that is above
+ // status bar but below navigation bar.
WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.WRAP_CONTENT,
- WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG,
+ WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN,
PixelFormat.TRANSLUCENT);
@@ -78,15 +77,11 @@ public class CarHeadsUpNotificationSystemContainer implements CarHeadsUpNotifica
windowManager.addView(mWindow, lp);
mWindow.setVisibility(View.INVISIBLE);
mHeadsUpContentFrame = mWindow.findViewById(R.id.headsup_content);
-
- mEnableHeadsUpNotificationWhenNotificationShadeOpen = resources.getBoolean(
- R.bool.config_enableHeadsUpNotificationWhenNotificationShadeOpen);
}
private void animateShow() {
- if ((mEnableHeadsUpNotificationWhenNotificationShadeOpen
- || !mNotificationPanelViewControllerLazy.get().isPanelExpanded())
- && mCarDeviceProvisionedController.isCurrentUserFullySetup()) {
+ if (mCarDeviceProvisionedController.isCurrentUserFullySetup()
+ && mOverlayViewGlobalStateController.shouldShowHUN()) {
mWindow.setVisibility(View.VISIBLE);
}
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java
index cb9539ad5b1d..1738091d14c9 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java
@@ -73,6 +73,7 @@ public class NotificationPanelViewController extends OverlayPanelViewController
private final CarNotificationListener mCarNotificationListener;
private final NotificationClickHandlerFactory mNotificationClickHandlerFactory;
private final StatusBarStateController mStatusBarStateController;
+ private final boolean mEnableHeadsUpNotificationWhenNotificationShadeOpen;
private float mInitialBackgroundAlpha;
private float mBackgroundAlphaDiff;
@@ -144,6 +145,10 @@ public class NotificationPanelViewController extends OverlayPanelViewController
+ " percentage");
}
mBackgroundAlphaDiff = finalBackgroundAlpha - mInitialBackgroundAlpha;
+
+ mEnableHeadsUpNotificationWhenNotificationShadeOpen = mResources.getBoolean(
+ com.android.car.notification.R.bool
+ .config_enableHeadsUpNotificationWhenNotificationShadeOpen);
}
@Override
@@ -151,6 +156,16 @@ public class NotificationPanelViewController extends OverlayPanelViewController
reinflate();
}
+ @Override
+ protected boolean shouldShowNavigationBar() {
+ return true;
+ }
+
+ @Override
+ protected boolean shouldShowHUN() {
+ return mEnableHeadsUpNotificationWhenNotificationShadeOpen;
+ }
+
/** Reinflates the view. */
public void reinflate() {
ViewGroup container = (ViewGroup) getLayout();
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 8f52638afdf1..41349b284147 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewMediator.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewMediator.java
@@ -26,8 +26,15 @@ import com.android.systemui.car.navigationbar.CarNavigationBarController;
import com.android.systemui.car.window.OverlayViewMediator;
import com.android.systemui.statusbar.policy.ConfigurationController;
-/** The view mediator which attaches the view controller to other elements of the system ui. */
-public abstract class NotificationPanelViewMediator implements OverlayViewMediator,
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ * The view mediator which attaches the view controller to other elements of the system ui. Disables
+ * drag open behavior of the notification panel from any navigation bar.
+ */
+@Singleton
+public class NotificationPanelViewMediator implements OverlayViewMediator,
ConfigurationController.ConfigurationListener {
private final CarNavigationBarController mCarNavigationBarController;
@@ -36,6 +43,7 @@ public abstract class NotificationPanelViewMediator implements OverlayViewMediat
private final CarDeviceProvisionedController mCarDeviceProvisionedController;
private final ConfigurationController mConfigurationController;
+ @Inject
public NotificationPanelViewMediator(
CarNavigationBarController carNavigationBarController,
NotificationPanelViewController notificationPanelViewController,
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayPanelViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayPanelViewController.java
index 0fe985684543..45808a8a0b3e 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayPanelViewController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayPanelViewController.java
@@ -375,10 +375,10 @@ public abstract class OverlayPanelViewController extends OverlayViewController {
}
if (visible && !getOverlayViewGlobalStateController().isWindowVisible()) {
- getOverlayViewGlobalStateController().setWindowVisible(true);
+ getOverlayViewGlobalStateController().showView(/* panelViewController= */ this);
}
if (!visible && getOverlayViewGlobalStateController().isWindowVisible()) {
- getOverlayViewGlobalStateController().setWindowVisible(false);
+ getOverlayViewGlobalStateController().hideView(/* panelViewController= */ this);
}
getLayout().setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
getOverlayViewGlobalStateController().setWindowFocusable(visible);
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewController.java
index 87f20208476b..30e26578bd73 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewController.java
@@ -54,7 +54,6 @@ public class OverlayViewController {
mOverlayViewGlobalStateController.hideView(/* viewController= */ this, this::hide);
}
-
/**
* Inflate layout owned by controller.
*/
@@ -72,7 +71,7 @@ public class OverlayViewController {
}
/**
- * Returns [@code true} if layout owned by controller has been inflated.
+ * Returns {@code true} if layout owned by controller has been inflated.
*/
public final boolean isInflated() {
return mLayout != null;
@@ -125,4 +124,18 @@ public class OverlayViewController {
protected final OverlayViewGlobalStateController getOverlayViewGlobalStateController() {
return mOverlayViewGlobalStateController;
}
+
+ /**
+ * Returns {@code true} if heads up notifications should be displayed over this view.
+ */
+ protected boolean shouldShowHUN() {
+ return true;
+ }
+
+ /**
+ * Returns {@code true} if navigation bar should be displayed over this view.
+ */
+ protected boolean shouldShowNavigationBar() {
+ return false;
+ }
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewGlobalStateController.java b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewGlobalStateController.java
index 290505f5042a..70260b0d4cef 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewGlobalStateController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewGlobalStateController.java
@@ -16,14 +16,17 @@
package com.android.systemui.car.window;
+import android.annotation.Nullable;
import android.util.Log;
import androidx.annotation.VisibleForTesting;
import com.android.systemui.car.navigationbar.CarNavigationBarController;
-import java.util.HashSet;
-import java.util.Set;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.SortedMap;
+import java.util.TreeMap;
import javax.inject.Inject;
import javax.inject.Singleton;
@@ -39,11 +42,17 @@ import javax.inject.Singleton;
*/
@Singleton
public class OverlayViewGlobalStateController {
+ private static final boolean DEBUG = false;
private static final String TAG = OverlayViewGlobalStateController.class.getSimpleName();
+ private static final int UNKNOWN_Z_ORDER = -1;
private final SystemUIOverlayWindowController mSystemUIOverlayWindowController;
private final CarNavigationBarController mCarNavigationBarController;
@VisibleForTesting
- Set<String> mShownSet;
+ Map<OverlayViewController, Integer> mZOrderMap;
+ @VisibleForTesting
+ SortedMap<Integer, OverlayViewController> mZOrderVisibleSortedMap;
+ @VisibleForTesting
+ OverlayViewController mHighestZOrder;
@Inject
public OverlayViewGlobalStateController(
@@ -52,7 +61,8 @@ public class OverlayViewGlobalStateController {
mSystemUIOverlayWindowController = systemUIOverlayWindowController;
mSystemUIOverlayWindowController.attach();
mCarNavigationBarController = carNavigationBarController;
- mShownSet = new HashSet<>();
+ mZOrderMap = new HashMap<>();
+ mZOrderVisibleSortedMap = new TreeMap<>();
}
/**
@@ -66,51 +76,127 @@ public class OverlayViewGlobalStateController {
}
/**
- * Show content in Overlay Window.
+ * Show content in Overlay Window using {@link OverlayPanelViewController}.
+ *
+ * This calls {@link OverlayViewGlobalStateController#showView(OverlayViewController, Runnable)}
+ * where the runnable is nullified since the actual showing of the panel is handled by the
+ * controller itself.
*/
- public void showView(OverlayViewController viewController, Runnable show) {
- if (mShownSet.isEmpty()) {
- mCarNavigationBarController.hideBars();
+ public void showView(OverlayPanelViewController panelViewController) {
+ showView(panelViewController, /* show= */ null);
+ }
+
+ /**
+ * Show content in Overlay Window using {@link OverlayViewController}.
+ */
+ public void showView(OverlayViewController viewController, @Nullable Runnable show) {
+ debugLog();
+ if (mZOrderVisibleSortedMap.isEmpty()) {
setWindowVisible(true);
}
+ if (!(viewController instanceof OverlayPanelViewController)) {
+ inflateView(viewController);
+ }
- inflateView(viewController);
+ if (show != null) {
+ show.run();
+ }
- show.run();
- mShownSet.add(viewController.getClass().getName());
+ updateInternalsWhenShowingView(viewController);
+ refreshNavigationBarVisibility();
Log.d(TAG, "Content shown: " + viewController.getClass().getName());
+ debugLog();
+ }
+
+ private void updateInternalsWhenShowingView(OverlayViewController viewController) {
+ int zOrder;
+ if (mZOrderMap.containsKey(viewController)) {
+ zOrder = mZOrderMap.get(viewController);
+ } else {
+ zOrder = mSystemUIOverlayWindowController.getBaseLayout().indexOfChild(
+ viewController.getLayout());
+ mZOrderMap.put(viewController, zOrder);
+ }
+
+ mZOrderVisibleSortedMap.put(zOrder, viewController);
+
+ refreshHighestZOrderWhenShowingView(viewController);
+ }
+
+ private void refreshHighestZOrderWhenShowingView(OverlayViewController viewController) {
+ if (mZOrderMap.getOrDefault(mHighestZOrder, UNKNOWN_Z_ORDER) < mZOrderMap.get(
+ viewController)) {
+ mHighestZOrder = viewController;
+ }
+ }
+
+ /**
+ * Hide content in Overlay Window using {@link OverlayPanelViewController}.
+ *
+ * This calls {@link OverlayViewGlobalStateController#hideView(OverlayViewController, Runnable)}
+ * where the runnable is nullified since the actual hiding of the panel is handled by the
+ * controller itself.
+ */
+ public void hideView(OverlayPanelViewController panelViewController) {
+ hideView(panelViewController, /* hide= */ null);
}
/**
- * Hide content in Overlay Window.
+ * Hide content in Overlay Window using {@link OverlayViewController}.
*/
- public void hideView(OverlayViewController viewController, Runnable hide) {
+ public void hideView(OverlayViewController viewController, @Nullable Runnable hide) {
+ debugLog();
if (!viewController.isInflated()) {
Log.d(TAG, "Content cannot be hidden since it isn't inflated: "
+ viewController.getClass().getName());
return;
}
- if (!mShownSet.contains(viewController.getClass().getName())) {
- Log.d(TAG, "Content cannot be hidden since it isn't shown: "
+ if (!mZOrderMap.containsKey(viewController)) {
+ Log.d(TAG, "Content cannot be hidden since it has never been shown: "
+ + viewController.getClass().getName());
+ return;
+ }
+ if (!mZOrderVisibleSortedMap.containsKey(mZOrderMap.get(viewController))) {
+ Log.d(TAG, "Content cannot be hidden since it isn't currently shown: "
+ viewController.getClass().getName());
return;
}
- hide.run();
- mShownSet.remove(viewController.getClass().getName());
+ if (hide != null) {
+ hide.run();
+ }
- if (mShownSet.isEmpty()) {
- mCarNavigationBarController.showBars();
+ mZOrderVisibleSortedMap.remove(mZOrderMap.get(viewController));
+ refreshHighestZOrderWhenHidingView(viewController);
+ refreshNavigationBarVisibility();
+
+ if (mZOrderVisibleSortedMap.isEmpty()) {
setWindowVisible(false);
}
Log.d(TAG, "Content hidden: " + viewController.getClass().getName());
+ debugLog();
+ }
+
+ private void refreshHighestZOrderWhenHidingView(OverlayViewController viewController) {
+ if (mZOrderVisibleSortedMap.isEmpty()) {
+ mHighestZOrder = null;
+ return;
+ }
+ if (!mHighestZOrder.equals(viewController)) {
+ return;
+ }
+
+ mHighestZOrder = mZOrderVisibleSortedMap.get(mZOrderVisibleSortedMap.lastKey());
}
- /** Sets the window visibility state. */
- public void setWindowVisible(boolean expanded) {
- mSystemUIOverlayWindowController.setWindowVisible(expanded);
+ private void refreshNavigationBarVisibility() {
+ if (mZOrderVisibleSortedMap.isEmpty() || mHighestZOrder.shouldShowNavigationBar()) {
+ mCarNavigationBarController.showBars();
+ } else {
+ mCarNavigationBarController.hideBars();
+ }
}
/** Returns {@code true} is the window is visible. */
@@ -118,13 +204,14 @@ public class OverlayViewGlobalStateController {
return mSystemUIOverlayWindowController.isWindowVisible();
}
- /** Sets the focusable flag of the sysui overlawy window. */
- public void setWindowFocusable(boolean focusable) {
- mSystemUIOverlayWindowController.setWindowFocusable(focusable);
+ private void setWindowVisible(boolean visible) {
+ mSystemUIOverlayWindowController.setWindowVisible(visible);
}
- /** Sets the {@link android.view.WindowManager.LayoutParams#FLAG_ALT_FOCUSABLE_IM} flag of the
- * sysui overlay window */
+ /**
+ * Sets the {@link android.view.WindowManager.LayoutParams#FLAG_ALT_FOCUSABLE_IM} flag of the
+ * sysui overlay window.
+ */
public void setWindowNeedsInput(boolean needsInput) {
mSystemUIOverlayWindowController.setWindowNeedsInput(needsInput);
}
@@ -134,10 +221,34 @@ public class OverlayViewGlobalStateController {
return mSystemUIOverlayWindowController.isWindowFocusable();
}
+ /** Sets the focusable flag of the sysui overlawy window. */
+ public void setWindowFocusable(boolean focusable) {
+ mSystemUIOverlayWindowController.setWindowFocusable(focusable);
+ }
+
/** Inflates the view controlled by the given view controller. */
public void inflateView(OverlayViewController viewController) {
if (!viewController.isInflated()) {
viewController.inflate(mSystemUIOverlayWindowController.getBaseLayout());
}
}
+
+ /**
+ * Return {@code true} if OverlayWindow is in a state where HUNs should be displayed above it.
+ */
+ public boolean shouldShowHUN() {
+ return mZOrderVisibleSortedMap.isEmpty() || mHighestZOrder.shouldShowHUN();
+ }
+
+ private void debugLog() {
+ if (!DEBUG) {
+ return;
+ }
+
+ Log.d(TAG, "mHighestZOrder: " + mHighestZOrder);
+ Log.d(TAG, "mZOrderVisibleSortedMap.size(): " + mZOrderVisibleSortedMap.size());
+ Log.d(TAG, "mZOrderVisibleSortedMap: " + mZOrderVisibleSortedMap);
+ Log.d(TAG, "mZOrderMap.size(): " + mZOrderMap.size());
+ Log.d(TAG, "mZOrderMap: " + mZOrderMap);
+ }
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayWindowModule.java b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayWindowModule.java
index e1918ceeaea4..484aa63e8bda 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayWindowModule.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayWindowModule.java
@@ -18,6 +18,7 @@ package com.android.systemui.car.window;
import com.android.systemui.car.keyguard.CarKeyguardViewMediator;
import com.android.systemui.car.notification.BottomNotificationPanelViewMediator;
+import com.android.systemui.car.notification.NotificationPanelViewMediator;
import com.android.systemui.car.notification.TopNotificationPanelViewMediator;
import com.android.systemui.car.userswitcher.FullscreenUserSwitcherViewMediator;
@@ -32,6 +33,13 @@ import dagger.multibindings.IntoMap;
@Module
public abstract class OverlayWindowModule {
+ /** Injects NotificationPanelViewMediator. */
+ @Binds
+ @IntoMap
+ @ClassKey(NotificationPanelViewMediator.class)
+ public abstract OverlayViewMediator bindNotificationPanelViewMediator(
+ NotificationPanelViewMediator notificationPanelViewMediator);
+
/** Injects TopNotificationPanelViewMediator. */
@Binds
@IntoMap
diff --git a/packages/CarSystemUI/tests/res/layout/overlay_view_global_state_controller_test.xml b/packages/CarSystemUI/tests/res/layout/overlay_view_global_state_controller_test.xml
new file mode 100644
index 000000000000..03fe0e4fcf2e
--- /dev/null
+++ b/packages/CarSystemUI/tests/res/layout/overlay_view_global_state_controller_test.xml
@@ -0,0 +1,43 @@
+<?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.
+ -->
+
+<!-- Fullscreen views in sysui should be listed here in increasing Z order. -->
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:background="@android:color/transparent"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <ViewStub android:id="@+id/overlay_view_controller_stub_1"
+ android:inflatedId="@+id/overlay_view_controller_1"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout="@layout/overlay_view_controller_stub"/>
+
+ <ViewStub android:id="@+id/overlay_view_controller_stub_2"
+ android:inflatedId="@+id/overlay_view_controller_2"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout="@layout/overlay_view_controller_stub"/>
+
+ <ViewStub android:id="@+id/overlay_view_controller_stub_3"
+ android:inflatedId="@+id/overlay_view_controller_3"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout="@layout/overlay_view_controller_stub"/>
+
+</FrameLayout> \ No newline at end of file
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/keyguard/CarKeyguardViewControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/keyguard/CarKeyguardViewControllerTest.java
index a2192af14758..1b4621f1c279 100644
--- a/packages/CarSystemUI/tests/src/com/android/systemui/car/keyguard/CarKeyguardViewControllerTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/keyguard/CarKeyguardViewControllerTest.java
@@ -16,9 +16,10 @@
package com.android.systemui.car.keyguard;
-import static com.google.common.truth.Truth.assertThat;
-
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
@@ -29,7 +30,6 @@ import android.os.Handler;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.LayoutInflater;
-import android.view.View;
import android.view.ViewGroup;
import com.android.internal.widget.LockPatternUtils;
@@ -40,7 +40,6 @@ import com.android.systemui.SysuiTestCase;
import com.android.systemui.car.CarServiceProvider;
import com.android.systemui.car.navigationbar.CarNavigationBarController;
import com.android.systemui.car.window.OverlayViewGlobalStateController;
-import com.android.systemui.car.window.SystemUIOverlayWindowController;
import com.android.systemui.keyguard.DismissCallbackRegistry;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.statusbar.phone.BiometricUnlockController;
@@ -51,6 +50,7 @@ import com.android.systemui.statusbar.policy.KeyguardStateController;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -61,28 +61,20 @@ import dagger.Lazy;
public class CarKeyguardViewControllerTest extends SysuiTestCase {
private TestableCarKeyguardViewController mCarKeyguardViewController;
- private OverlayViewGlobalStateController mOverlayViewGlobalStateController;
- private ViewGroup mBaseLayout;
@Mock
+ private OverlayViewGlobalStateController mOverlayViewGlobalStateController;
+ @Mock
private KeyguardBouncer mBouncer;
@Mock
private CarNavigationBarController mCarNavigationBarController;
@Mock
- private SystemUIOverlayWindowController mSystemUIOverlayWindowController;
- @Mock
private CarKeyguardViewController.OnKeyguardCancelClickedListener mCancelClickedListener;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- mOverlayViewGlobalStateController = new OverlayViewGlobalStateController(
- mCarNavigationBarController, mSystemUIOverlayWindowController);
- mBaseLayout = (ViewGroup) LayoutInflater.from(mContext).inflate(
- R.layout.sysui_overlay_window, /* root= */ null);
- when(mSystemUIOverlayWindowController.getBaseLayout()).thenReturn(mBaseLayout);
-
mCarKeyguardViewController = new TestableCarKeyguardViewController(
mContext,
Handler.getMain(),
@@ -98,6 +90,8 @@ public class CarKeyguardViewControllerTest extends SysuiTestCase {
mock(FalsingManager.class),
() -> mock(KeyguardBypassController.class)
);
+ mCarKeyguardViewController.inflate((ViewGroup) LayoutInflater.from(mContext).inflate(
+ R.layout.sysui_overlay_window, /* root= */ null));
}
@Test
@@ -113,8 +107,7 @@ public class CarKeyguardViewControllerTest extends SysuiTestCase {
when(mBouncer.isSecure()).thenReturn(true);
mCarKeyguardViewController.show(/* options= */ null);
- assertThat(mBaseLayout.findViewById(R.id.keyguard_container).getVisibility()).isEqualTo(
- View.VISIBLE);
+ verify(mOverlayViewGlobalStateController).showView(eq(mCarKeyguardViewController), any());
}
@Test
@@ -130,8 +123,17 @@ public class CarKeyguardViewControllerTest extends SysuiTestCase {
when(mBouncer.isSecure()).thenReturn(false);
mCarKeyguardViewController.show(/* options= */ null);
- assertThat(mBaseLayout.findViewById(R.id.keyguard_container).getVisibility()).isEqualTo(
- View.GONE);
+ // Here we check for both showView and hideView since the current implementation of show
+ // with bouncer being not secure has the following method execution orders:
+ // 1) show -> start -> showView
+ // 2) show -> reset -> dismissAndCollapse -> hide -> stop -> hideView
+ // Hence, we want to make sure that showView is called before hideView and not in any
+ // other combination.
+ InOrder inOrder = inOrder(mOverlayViewGlobalStateController);
+ inOrder.verify(mOverlayViewGlobalStateController).showView(eq(mCarKeyguardViewController),
+ any());
+ inOrder.verify(mOverlayViewGlobalStateController).hideView(eq(mCarKeyguardViewController),
+ any());
}
@Test
@@ -156,8 +158,11 @@ public class CarKeyguardViewControllerTest extends SysuiTestCase {
mCarKeyguardViewController.show(/* options= */ null);
mCarKeyguardViewController.hide(/* startTime= */ 0, /* fadeoutDelay= */ 0);
- assertThat(mBaseLayout.findViewById(R.id.keyguard_container).getVisibility()).isEqualTo(
- View.GONE);
+ InOrder inOrder = inOrder(mOverlayViewGlobalStateController);
+ inOrder.verify(mOverlayViewGlobalStateController).showView(eq(mCarKeyguardViewController),
+ any());
+ inOrder.verify(mOverlayViewGlobalStateController).hideView(eq(mCarKeyguardViewController),
+ any());
}
@Test
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/notification/CarHeadsUpNotificationSystemContainerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/notification/CarHeadsUpNotificationSystemContainerTest.java
index 6ac72a681bfe..ccaeb458fe54 100644
--- a/packages/CarSystemUI/tests/src/com/android/systemui/car/notification/CarHeadsUpNotificationSystemContainerTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/notification/CarHeadsUpNotificationSystemContainerTest.java
@@ -28,9 +28,9 @@ import android.view.WindowManager;
import androidx.test.filters.SmallTest;
-import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.car.CarDeviceProvisionedController;
+import com.android.systemui.car.window.OverlayViewGlobalStateController;
import org.junit.Before;
import org.junit.Test;
@@ -42,12 +42,11 @@ import org.mockito.MockitoAnnotations;
@TestableLooper.RunWithLooper
@SmallTest
public class CarHeadsUpNotificationSystemContainerTest extends SysuiTestCase {
- private CarHeadsUpNotificationSystemContainer mDefaultController;
- private CarHeadsUpNotificationSystemContainer mOverrideEnabledController;
+ private CarHeadsUpNotificationSystemContainer mCarHeadsUpNotificationSystemContainer;
@Mock
private CarDeviceProvisionedController mCarDeviceProvisionedController;
@Mock
- private NotificationPanelViewController mNotificationPanelViewController;
+ private OverlayViewGlobalStateController mOverlayViewGlobalStateController;
@Mock
private WindowManager mWindowManager;
@@ -58,76 +57,63 @@ public class CarHeadsUpNotificationSystemContainerTest extends SysuiTestCase {
@Before
public void setUp() {
- MockitoAnnotations.initMocks(this);
+ MockitoAnnotations.initMocks(/* testClass= */this);
- when(mNotificationPanelViewController.isPanelExpanded()).thenReturn(false);
- when(mCarDeviceProvisionedController.isCurrentUserSetup()).thenReturn(true);
- when(mCarDeviceProvisionedController.isCurrentUserSetupInProgress()).thenReturn(false);
+ when(mOverlayViewGlobalStateController.shouldShowHUN()).thenReturn(true);
+ when(mCarDeviceProvisionedController.isCurrentUserFullySetup()).thenReturn(true);
TestableResources testableResources = mContext.getOrCreateTestableResources();
- testableResources.addOverride(
- R.bool.config_enableHeadsUpNotificationWhenNotificationShadeOpen, false);
-
- mDefaultController = new CarHeadsUpNotificationSystemContainer(mContext,
- testableResources.getResources(), mCarDeviceProvisionedController, mWindowManager,
- () -> mNotificationPanelViewController);
-
- testableResources.addOverride(
- R.bool.config_enableHeadsUpNotificationWhenNotificationShadeOpen, true);
-
- mOverrideEnabledController = new CarHeadsUpNotificationSystemContainer(mContext,
+ mCarHeadsUpNotificationSystemContainer = new CarHeadsUpNotificationSystemContainer(mContext,
testableResources.getResources(), mCarDeviceProvisionedController, mWindowManager,
- () -> mNotificationPanelViewController);
+ mOverlayViewGlobalStateController);
}
@Test
public void testDisplayNotification_firstNotification_isVisible() {
- mDefaultController.displayNotification(mNotificationView);
- assertThat(mDefaultController.isVisible()).isTrue();
+ mCarHeadsUpNotificationSystemContainer.displayNotification(mNotificationView);
+ assertThat(mCarHeadsUpNotificationSystemContainer.isVisible()).isTrue();
}
@Test
public void testRemoveNotification_lastNotification_isInvisible() {
- mDefaultController.displayNotification(mNotificationView);
- mDefaultController.removeNotification(mNotificationView);
- assertThat(mDefaultController.isVisible()).isFalse();
+ mCarHeadsUpNotificationSystemContainer.displayNotification(mNotificationView);
+ mCarHeadsUpNotificationSystemContainer.removeNotification(mNotificationView);
+ assertThat(mCarHeadsUpNotificationSystemContainer.isVisible()).isFalse();
}
@Test
public void testRemoveNotification_nonLastNotification_isVisible() {
- mDefaultController.displayNotification(mNotificationView);
- mDefaultController.displayNotification(mNotificationView2);
- mDefaultController.removeNotification(mNotificationView);
- assertThat(mDefaultController.isVisible()).isTrue();
+ mCarHeadsUpNotificationSystemContainer.displayNotification(mNotificationView);
+ mCarHeadsUpNotificationSystemContainer.displayNotification(mNotificationView2);
+ mCarHeadsUpNotificationSystemContainer.removeNotification(mNotificationView);
+ assertThat(mCarHeadsUpNotificationSystemContainer.isVisible()).isTrue();
}
@Test
- public void testDisplayNotification_userSetupInProgress_isInvisible() {
- when(mCarDeviceProvisionedController.isCurrentUserSetupInProgress()).thenReturn(true);
- mDefaultController.displayNotification(mNotificationView);
- assertThat(mDefaultController.isVisible()).isFalse();
+ public void testDisplayNotification_userFullySetupTrue_isInvisible() {
+ mCarHeadsUpNotificationSystemContainer.displayNotification(mNotificationView);
+ assertThat(mCarHeadsUpNotificationSystemContainer.isVisible()).isTrue();
}
@Test
- public void testDisplayNotification_userSetupIncomplete_isInvisible() {
- when(mCarDeviceProvisionedController.isCurrentUserSetup()).thenReturn(false);
- mDefaultController.displayNotification(mNotificationView);
- assertThat(mDefaultController.isVisible()).isFalse();
+ public void testDisplayNotification_userFullySetupFalse_isInvisible() {
+ when(mCarDeviceProvisionedController.isCurrentUserFullySetup()).thenReturn(false);
+ mCarHeadsUpNotificationSystemContainer.displayNotification(mNotificationView);
+ assertThat(mCarHeadsUpNotificationSystemContainer.isVisible()).isFalse();
}
@Test
- public void testDisplayNotification_notificationPanelExpanded_isInvisible() {
- when(mNotificationPanelViewController.isPanelExpanded()).thenReturn(true);
- mDefaultController.displayNotification(mNotificationView);
- assertThat(mDefaultController.isVisible()).isFalse();
+ public void testDisplayNotification_overlayWindowStateShouldShowHUNFalse_isInvisible() {
+ when(mOverlayViewGlobalStateController.shouldShowHUN()).thenReturn(false);
+ mCarHeadsUpNotificationSystemContainer.displayNotification(mNotificationView);
+ assertThat(mCarHeadsUpNotificationSystemContainer.isVisible()).isFalse();
}
@Test
- public void testDisplayNotification_notificationPanelExpandedEnabledHUNWhenOpen_isVisible() {
- when(mNotificationPanelViewController.isPanelExpanded()).thenReturn(true);
- mOverrideEnabledController.displayNotification(mNotificationView);
- assertThat(mOverrideEnabledController.isVisible()).isTrue();
+ public void testDisplayNotification_overlayWindowStateShouldShowHUNTrue_isVisible() {
+ mCarHeadsUpNotificationSystemContainer.displayNotification(mNotificationView);
+ assertThat(mCarHeadsUpNotificationSystemContainer.isVisible()).isTrue();
}
}
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayPanelViewControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayPanelViewControllerTest.java
index 8d705a8cca1f..45a05ac69bd7 100644
--- a/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayPanelViewControllerTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayPanelViewControllerTest.java
@@ -339,7 +339,7 @@ public class OverlayPanelViewControllerTest extends SysuiTestCase {
mOverlayPanelViewController.setPanelVisible(true);
- verify(mOverlayViewGlobalStateController).setWindowVisible(true);
+ verify(mOverlayViewGlobalStateController).showView(mOverlayPanelViewController);
}
@Test
@@ -349,7 +349,7 @@ public class OverlayPanelViewControllerTest extends SysuiTestCase {
mOverlayPanelViewController.setPanelVisible(true);
- verify(mOverlayViewGlobalStateController, never()).setWindowVisible(true);
+ verify(mOverlayViewGlobalStateController, never()).showView(mOverlayPanelViewController);
}
@Test
@@ -377,7 +377,7 @@ public class OverlayPanelViewControllerTest extends SysuiTestCase {
mOverlayPanelViewController.setPanelVisible(false);
- verify(mOverlayViewGlobalStateController).setWindowVisible(false);
+ verify(mOverlayViewGlobalStateController).hideView(mOverlayPanelViewController);
}
@Test
@@ -387,7 +387,7 @@ public class OverlayPanelViewControllerTest extends SysuiTestCase {
mOverlayPanelViewController.setPanelVisible(false);
- verify(mOverlayViewGlobalStateController, never()).setWindowVisible(false);
+ verify(mOverlayViewGlobalStateController, never()).hideView(mOverlayPanelViewController);
}
@Test
@@ -428,10 +428,6 @@ public class OverlayPanelViewControllerTest extends SysuiTestCase {
private static class TestOverlayPanelViewController extends OverlayPanelViewController {
- private boolean mShouldAnimateCollapsePanel;
- private boolean mShouldAnimateExpandPanel;
- private boolean mShouldAllowClosingScroll;
-
boolean mOnAnimateCollapsePanelCalled;
boolean mAnimateCollapsePanelCalled;
boolean mOnAnimateExpandPanelCalled;
@@ -440,6 +436,9 @@ public class OverlayPanelViewControllerTest extends SysuiTestCase {
boolean mOnExpandAnimationEndCalled;
boolean mOnOpenScrollStartEnd;
List<Integer> mOnScrollHeights;
+ private boolean mShouldAnimateCollapsePanel;
+ private boolean mShouldAnimateExpandPanel;
+ private boolean mShouldAllowClosingScroll;
TestOverlayPanelViewController(
Context context,
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayViewGlobalStateControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayViewGlobalStateControllerTest.java
index 25dd4f502fb7..9e6e616e3ccf 100644
--- a/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayViewGlobalStateControllerTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayViewGlobalStateControllerTest.java
@@ -24,25 +24,33 @@ import static org.mockito.Mockito.when;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import android.view.LayoutInflater;
+import android.view.View;
import android.view.ViewGroup;
-import android.widget.FrameLayout;
+import android.view.ViewStub;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.car.navigationbar.CarNavigationBarController;
+import com.android.systemui.tests.R;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
+import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
+import java.util.Arrays;
+
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@SmallTest
public class OverlayViewGlobalStateControllerTest extends SysuiTestCase {
- private static final String MOCK_OVERLAY_VIEW_CONTROLLER_NAME = "OverlayViewController";
+ private static final int OVERLAY_VIEW_CONTROLLER_1_Z_ORDER = 0;
+ private static final int OVERLAY_VIEW_CONTROLLER_2_Z_ORDER = 1;
+ private static final int OVERLAY_PANEL_VIEW_CONTROLLER_Z_ORDER = 2;
private OverlayViewGlobalStateController mOverlayViewGlobalStateController;
private ViewGroup mBaseLayout;
@@ -54,7 +62,11 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase {
@Mock
private OverlayViewMediator mOverlayViewMediator;
@Mock
- private OverlayViewController mOverlayViewController;
+ private OverlayViewController mOverlayViewController1;
+ @Mock
+ private OverlayViewController mOverlayViewController2;
+ @Mock
+ private OverlayPanelViewController mOverlayPanelViewController;
@Mock
private Runnable mRunnable;
@@ -62,14 +74,15 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase {
public void setUp() {
MockitoAnnotations.initMocks(/* testClass= */ this);
+ mBaseLayout = (ViewGroup) LayoutInflater.from(mContext).inflate(
+ R.layout.overlay_view_global_state_controller_test, /* root= */ null);
+
+ when(mSystemUIOverlayWindowController.getBaseLayout()).thenReturn(mBaseLayout);
+
mOverlayViewGlobalStateController = new OverlayViewGlobalStateController(
mCarNavigationBarController, mSystemUIOverlayWindowController);
verify(mSystemUIOverlayWindowController).attach();
-
- mBaseLayout = new FrameLayout(mContext);
-
- when(mSystemUIOverlayWindowController.getBaseLayout()).thenReturn(mBaseLayout);
}
@Test
@@ -87,182 +100,445 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase {
}
@Test
- public void showView_nothingAlreadyShown_navigationBarsHidden() {
- mOverlayViewGlobalStateController.showView(mOverlayViewController, mRunnable);
+ public void showView_nothingAlreadyShown_shouldShowNavBarFalse_navigationBarsHidden() {
+ setupOverlayViewController1();
+ when(mOverlayViewController1.shouldShowNavigationBar()).thenReturn(false);
+
+ mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable);
verify(mCarNavigationBarController).hideBars();
}
@Test
- public void showView_nothingAlreadyShown_windowIsExpanded() {
- mOverlayViewGlobalStateController.showView(mOverlayViewController, mRunnable);
+ public void showView_nothingAlreadyShown_shouldShowNavBarTrue_navigationBarsShown() {
+ setupOverlayViewController1();
+ when(mOverlayViewController1.shouldShowNavigationBar()).thenReturn(true);
+
+ mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable);
+
+ verify(mCarNavigationBarController).showBars();
+ }
+
+ @Test
+ public void showView_nothingAlreadyShown_windowIsSetVisible() {
+ setupOverlayViewController1();
+
+ mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable);
verify(mSystemUIOverlayWindowController).setWindowVisible(true);
}
@Test
- public void showView_somethingAlreadyShown_navigationBarsHidden() {
- mOverlayViewGlobalStateController.mShownSet.add(MOCK_OVERLAY_VIEW_CONTROLLER_NAME);
+ public void showView_nothingAlreadyShown_newHighestZOrder() {
+ setupOverlayViewController1();
+
+ mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable);
+
+ assertThat(mOverlayViewGlobalStateController.mHighestZOrder).isEqualTo(
+ mOverlayViewController1);
+ }
+
+ @Test
+ public void showView_nothingAlreadyShown_newHighestZOrder_isVisible() {
+ setupOverlayViewController1();
+
+ mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable);
+
+ assertThat(mOverlayViewGlobalStateController.mZOrderVisibleSortedMap.containsKey(
+ OVERLAY_VIEW_CONTROLLER_1_Z_ORDER)).isTrue();
+ }
+
+ @Test
+ public void showView_newHighestZOrder() {
+ setupOverlayViewController1();
+ setOverlayViewControllerAsShowing(mOverlayViewController1);
+ setupOverlayViewController2();
- mOverlayViewGlobalStateController.showView(mOverlayViewController, mRunnable);
+ mOverlayViewGlobalStateController.showView(mOverlayViewController2, mRunnable);
- verify(mCarNavigationBarController, never()).hideBars();
+ assertThat(mOverlayViewGlobalStateController.mHighestZOrder).isEqualTo(
+ mOverlayViewController2);
}
@Test
- public void showView_somethingAlreadyShown_windowIsExpanded() {
- mOverlayViewGlobalStateController.mShownSet.add(MOCK_OVERLAY_VIEW_CONTROLLER_NAME);
+ public void showView_newHighestZOrder_shouldShowNavBarFalse_navigationBarsHidden() {
+ setupOverlayViewController1();
+ setOverlayViewControllerAsShowing(mOverlayViewController1);
+ setupOverlayViewController2();
+ when(mOverlayViewController2.shouldShowNavigationBar()).thenReturn(false);
+
+ mOverlayViewGlobalStateController.showView(mOverlayViewController2, mRunnable);
- mOverlayViewGlobalStateController.showView(mOverlayViewController, mRunnable);
+ verify(mCarNavigationBarController).hideBars();
+ }
+
+ @Test
+ public void showView_newHighestZOrder_shouldShowNavBarTrue_navigationBarsShown() {
+ setupOverlayViewController1();
+ setOverlayViewControllerAsShowing(mOverlayViewController1);
+ setupOverlayViewController2();
+ when(mOverlayViewController2.shouldShowNavigationBar()).thenReturn(true);
+
+ mOverlayViewGlobalStateController.showView(mOverlayViewController2, mRunnable);
+
+ verify(mCarNavigationBarController).showBars();
+ }
+
+ @Test
+ public void showView_newHighestZOrder_correctViewsShown() {
+ setupOverlayViewController1();
+ setOverlayViewControllerAsShowing(mOverlayViewController1);
+ setupOverlayViewController2();
+
+ mOverlayViewGlobalStateController.showView(mOverlayViewController2, mRunnable);
+
+ assertThat(mOverlayViewGlobalStateController.mZOrderVisibleSortedMap.keySet().toArray())
+ .isEqualTo(Arrays.asList(OVERLAY_VIEW_CONTROLLER_1_Z_ORDER,
+ OVERLAY_VIEW_CONTROLLER_2_Z_ORDER).toArray());
+ }
+
+ @Test
+ public void showView_oldHighestZOrder() {
+ setupOverlayViewController2();
+ setOverlayViewControllerAsShowing(mOverlayViewController2);
+
+ mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable);
+
+ assertThat(mOverlayViewGlobalStateController.mHighestZOrder).isEqualTo(
+ mOverlayViewController2);
+ }
+
+ @Test
+ public void showView_oldHighestZOrder_shouldShowNavBarFalse_navigationBarsHidden() {
+ setupOverlayViewController2();
+ setOverlayViewControllerAsShowing(mOverlayViewController2);
+ when(mOverlayViewController1.shouldShowNavigationBar()).thenReturn(true);
+ when(mOverlayViewController2.shouldShowNavigationBar()).thenReturn(false);
+
+ mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable);
+
+ verify(mCarNavigationBarController).hideBars();
+ }
+
+ @Test
+ public void showView_oldHighestZOrder_shouldShowNavBarTrue_navigationBarsShown() {
+ setupOverlayViewController2();
+ setOverlayViewControllerAsShowing(mOverlayViewController2);
+ when(mOverlayViewController1.shouldShowNavigationBar()).thenReturn(false);
+ when(mOverlayViewController2.shouldShowNavigationBar()).thenReturn(true);
+
+ mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable);
+
+ verify(mCarNavigationBarController).showBars();
+ }
+
+ @Test
+ public void showView_oldHighestZOrder_correctViewsShown() {
+ setupOverlayViewController1();
+ setupOverlayViewController2();
+ setOverlayViewControllerAsShowing(mOverlayViewController2);
+
+ mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable);
+
+ assertThat(mOverlayViewGlobalStateController.mZOrderVisibleSortedMap.keySet().toArray())
+ .isEqualTo(Arrays.asList(OVERLAY_VIEW_CONTROLLER_1_Z_ORDER,
+ OVERLAY_VIEW_CONTROLLER_2_Z_ORDER).toArray());
+ }
+
+ @Test
+ public void showView_somethingAlreadyShown_windowVisibleNotCalled() {
+ setupOverlayViewController1();
+ setOverlayViewControllerAsShowing(mOverlayViewController1);
+ setupOverlayViewController2();
+
+ mOverlayViewGlobalStateController.showView(mOverlayViewController2, mRunnable);
verify(mSystemUIOverlayWindowController, never()).setWindowVisible(true);
}
@Test
public void showView_viewControllerNotInflated_inflateViewController() {
- when(mOverlayViewController.isInflated()).thenReturn(false);
+ setupOverlayViewController2();
+ when(mOverlayViewController2.isInflated()).thenReturn(false);
- mOverlayViewGlobalStateController.showView(mOverlayViewController, mRunnable);
+ mOverlayViewGlobalStateController.showView(mOverlayViewController2, mRunnable);
- verify(mOverlayViewController).inflate(mBaseLayout);
+ verify(mOverlayViewController2).inflate(mBaseLayout);
}
@Test
public void showView_viewControllerInflated_inflateViewControllerNotCalled() {
- when(mOverlayViewController.isInflated()).thenReturn(true);
+ setupOverlayViewController2();
- mOverlayViewGlobalStateController.showView(mOverlayViewController, mRunnable);
+ mOverlayViewGlobalStateController.showView(mOverlayViewController2, mRunnable);
- verify(mOverlayViewController, never()).inflate(mBaseLayout);
+ verify(mOverlayViewController2, never()).inflate(mBaseLayout);
}
@Test
- public void showView_showRunnableCalled() {
- mOverlayViewGlobalStateController.showView(mOverlayViewController, mRunnable);
+ public void showView_panelViewController_inflateViewControllerNotCalled() {
+ setupOverlayPanelViewController();
- verify(mRunnable).run();
+ mOverlayViewGlobalStateController.showView(mOverlayPanelViewController, mRunnable);
+
+ verify(mOverlayPanelViewController, never()).inflate(mBaseLayout);
+ verify(mOverlayPanelViewController, never()).isInflated();
}
@Test
- public void showView_overlayViewControllerAddedToShownSet() {
- mOverlayViewGlobalStateController.showView(mOverlayViewController, mRunnable);
+ public void showView_showRunnableCalled() {
+ setupOverlayViewController1();
+
+ mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable);
- assertThat(mOverlayViewGlobalStateController.mShownSet.contains(
- mOverlayViewController.getClass().getName())).isTrue();
+ verify(mRunnable).run();
}
@Test
public void hideView_viewControllerNotInflated_hideRunnableNotCalled() {
- when(mOverlayViewController.isInflated()).thenReturn(false);
+ when(mOverlayViewController2.isInflated()).thenReturn(false);
- mOverlayViewGlobalStateController.hideView(mOverlayViewController, mRunnable);
+ mOverlayViewGlobalStateController.hideView(mOverlayViewController2, mRunnable);
verify(mRunnable, never()).run();
}
@Test
public void hideView_nothingShown_hideRunnableNotCalled() {
- when(mOverlayViewController.isInflated()).thenReturn(true);
- mOverlayViewGlobalStateController.mShownSet.clear();
+ when(mOverlayViewController2.isInflated()).thenReturn(true);
+ mOverlayViewGlobalStateController.mZOrderMap.clear();
- mOverlayViewGlobalStateController.hideView(mOverlayViewController, mRunnable);
+ mOverlayViewGlobalStateController.hideView(mOverlayViewController2, mRunnable);
verify(mRunnable, never()).run();
}
@Test
public void hideView_viewControllerNotShown_hideRunnableNotCalled() {
- when(mOverlayViewController.isInflated()).thenReturn(true);
- mOverlayViewGlobalStateController.mShownSet.add(MOCK_OVERLAY_VIEW_CONTROLLER_NAME);
+ setupOverlayViewController1();
+ setOverlayViewControllerAsShowing(mOverlayViewController1);
+ when(mOverlayViewController2.isInflated()).thenReturn(true);
- mOverlayViewGlobalStateController.hideView(mOverlayViewController, mRunnable);
+ mOverlayViewGlobalStateController.hideView(mOverlayViewController2, mRunnable);
verify(mRunnable, never()).run();
}
@Test
public void hideView_viewControllerShown_hideRunnableCalled() {
- when(mOverlayViewController.isInflated()).thenReturn(true);
- mOverlayViewGlobalStateController.mShownSet.add(
- mOverlayViewController.getClass().getName());
+ setupOverlayViewController1();
+ setOverlayViewControllerAsShowing(mOverlayViewController1);
- mOverlayViewGlobalStateController.hideView(mOverlayViewController, mRunnable);
+ mOverlayViewGlobalStateController.hideView(mOverlayViewController1, mRunnable);
verify(mRunnable).run();
}
@Test
+ public void hideView_viewControllerOnlyShown_noHighestZOrder() {
+ setupOverlayViewController1();
+ setOverlayViewControllerAsShowing(mOverlayViewController1);
+
+ mOverlayViewGlobalStateController.hideView(mOverlayViewController1, mRunnable);
+
+ assertThat(mOverlayViewGlobalStateController.mHighestZOrder).isNull();
+ }
+
+ @Test
public void hideView_viewControllerOnlyShown_nothingShown() {
- when(mOverlayViewController.isInflated()).thenReturn(true);
- mOverlayViewGlobalStateController.mShownSet.add(
- mOverlayViewController.getClass().getName());
+ setupOverlayViewController1();
+ setOverlayViewControllerAsShowing(mOverlayViewController1);
+
+ mOverlayViewGlobalStateController.hideView(mOverlayViewController1, mRunnable);
+
+ assertThat(mOverlayViewGlobalStateController.mZOrderVisibleSortedMap.isEmpty()).isTrue();
+ }
+
+ @Test
+ public void hideView_viewControllerOnlyShown_viewControllerNotShown() {
+ setupOverlayViewController1();
+ setOverlayViewControllerAsShowing(mOverlayViewController1);
+
+ mOverlayViewGlobalStateController.hideView(mOverlayViewController1, mRunnable);
+
+ assertThat(mOverlayViewGlobalStateController.mZOrderVisibleSortedMap.containsKey(
+ OVERLAY_VIEW_CONTROLLER_1_Z_ORDER)).isFalse();
+ }
+
+ @Test
+ public void hideView_newHighestZOrder_twoViewsShown() {
+ setupOverlayViewController1();
+ setOverlayViewControllerAsShowing(mOverlayViewController1);
+ setupOverlayViewController2();
+ setOverlayViewControllerAsShowing(mOverlayViewController2);
+
+ mOverlayViewGlobalStateController.hideView(mOverlayViewController2, mRunnable);
+
+ assertThat(mOverlayViewGlobalStateController.mHighestZOrder).isEqualTo(
+ mOverlayViewController1);
+ }
+
+ @Test
+ public void hideView_newHighestZOrder_threeViewsShown() {
+ setupOverlayViewController1();
+ setOverlayViewControllerAsShowing(mOverlayViewController1);
+ setupOverlayViewController2();
+ setOverlayViewControllerAsShowing(mOverlayViewController2);
+ setupOverlayPanelViewController();
+ setOverlayViewControllerAsShowing(mOverlayPanelViewController);
+
+ mOverlayViewGlobalStateController.hideView(mOverlayPanelViewController, mRunnable);
+
+ assertThat(mOverlayViewGlobalStateController.mHighestZOrder).isEqualTo(
+ mOverlayViewController2);
+ }
+
+ @Test
+ public void hideView_newHighestZOrder_shouldShowNavBarFalse_navigationBarHidden() {
+ setupOverlayViewController1();
+ setOverlayViewControllerAsShowing(mOverlayViewController1);
+ setupOverlayViewController2();
+ setOverlayViewControllerAsShowing(mOverlayViewController2);
+ when(mOverlayViewController1.shouldShowNavigationBar()).thenReturn(false);
+
+ mOverlayViewGlobalStateController.hideView(mOverlayViewController2, mRunnable);
+
+ verify(mCarNavigationBarController).hideBars();
+ }
+
+ @Test
+ public void hideView_newHighestZOrder_shouldShowNavBarTrue_navigationBarShown() {
+ setupOverlayViewController1();
+ setOverlayViewControllerAsShowing(mOverlayViewController1);
+ setupOverlayViewController2();
+ setOverlayViewControllerAsShowing(mOverlayViewController2);
+ when(mOverlayViewController1.shouldShowNavigationBar()).thenReturn(true);
+
+ mOverlayViewGlobalStateController.hideView(mOverlayViewController2, mRunnable);
- mOverlayViewGlobalStateController.hideView(mOverlayViewController, mRunnable);
+ verify(mCarNavigationBarController).showBars();
+ }
+
+ @Test
+ public void hideView_oldHighestZOrder() {
+ setupOverlayViewController1();
+ setOverlayViewControllerAsShowing(mOverlayViewController1);
+ setupOverlayViewController2();
+ setOverlayViewControllerAsShowing(mOverlayViewController2);
- assertThat(mOverlayViewGlobalStateController.mShownSet.isEmpty()).isTrue();
+ mOverlayViewGlobalStateController.hideView(mOverlayViewController1, mRunnable);
+
+ assertThat(mOverlayViewGlobalStateController.mHighestZOrder).isEqualTo(
+ mOverlayViewController2);
}
@Test
- public void hideView_viewControllerNotOnlyShown_navigationBarNotShown() {
- when(mOverlayViewController.isInflated()).thenReturn(true);
- mOverlayViewGlobalStateController.mShownSet.add(
- mOverlayViewController.getClass().getName());
- mOverlayViewGlobalStateController.mShownSet.add(MOCK_OVERLAY_VIEW_CONTROLLER_NAME);
+ public void hideView_oldHighestZOrder_shouldShowNavBarFalse_navigationBarHidden() {
+ setupOverlayViewController1();
+ setOverlayViewControllerAsShowing(mOverlayViewController1);
+ setupOverlayViewController2();
+ setOverlayViewControllerAsShowing(mOverlayViewController2);
+ when(mOverlayViewController2.shouldShowNavigationBar()).thenReturn(false);
- mOverlayViewGlobalStateController.hideView(mOverlayViewController, mRunnable);
+ mOverlayViewGlobalStateController.hideView(mOverlayViewController1, mRunnable);
- verify(mCarNavigationBarController, never()).showBars();
+ verify(mCarNavigationBarController).hideBars();
+ }
+
+ @Test
+ public void hideView_oldHighestZOrder_shouldShowNavBarTrue_navigationBarShown() {
+ setupOverlayViewController1();
+ setOverlayViewControllerAsShowing(mOverlayViewController1);
+ setupOverlayViewController2();
+ setOverlayViewControllerAsShowing(mOverlayViewController2);
+ when(mOverlayViewController2.shouldShowNavigationBar()).thenReturn(true);
+
+ mOverlayViewGlobalStateController.hideView(mOverlayViewController1, mRunnable);
+
+ verify(mCarNavigationBarController).showBars();
}
@Test
public void hideView_viewControllerNotOnlyShown_windowNotCollapsed() {
- when(mOverlayViewController.isInflated()).thenReturn(true);
- mOverlayViewGlobalStateController.mShownSet.add(
- mOverlayViewController.getClass().getName());
- mOverlayViewGlobalStateController.mShownSet.add(MOCK_OVERLAY_VIEW_CONTROLLER_NAME);
+ setupOverlayViewController1();
+ setOverlayViewControllerAsShowing(mOverlayViewController1);
+ setupOverlayViewController2();
+ setOverlayViewControllerAsShowing(mOverlayViewController2);
- mOverlayViewGlobalStateController.hideView(mOverlayViewController, mRunnable);
+ mOverlayViewGlobalStateController.hideView(mOverlayViewController2, mRunnable);
verify(mSystemUIOverlayWindowController, never()).setWindowVisible(false);
}
@Test
public void hideView_viewControllerOnlyShown_navigationBarShown() {
- when(mOverlayViewController.isInflated()).thenReturn(true);
- mOverlayViewGlobalStateController.mShownSet.add(
- mOverlayViewController.getClass().getName());
+ setupOverlayViewController1();
+ setOverlayViewControllerAsShowing(mOverlayViewController1);
- mOverlayViewGlobalStateController.hideView(mOverlayViewController, mRunnable);
+ mOverlayViewGlobalStateController.hideView(mOverlayViewController1, mRunnable);
verify(mCarNavigationBarController).showBars();
}
@Test
public void hideView_viewControllerOnlyShown_windowCollapsed() {
- when(mOverlayViewController.isInflated()).thenReturn(true);
- mOverlayViewGlobalStateController.mShownSet.add(
- mOverlayViewController.getClass().getName());
+ setupOverlayViewController1();
+ setOverlayViewControllerAsShowing(mOverlayViewController1);
- mOverlayViewGlobalStateController.hideView(mOverlayViewController, mRunnable);
+ mOverlayViewGlobalStateController.hideView(mOverlayViewController1, mRunnable);
verify(mSystemUIOverlayWindowController).setWindowVisible(false);
}
@Test
public void inflateView_notInflated_inflates() {
- when(mOverlayViewController.isInflated()).thenReturn(false);
+ when(mOverlayViewController2.isInflated()).thenReturn(false);
- mOverlayViewGlobalStateController.inflateView(mOverlayViewController);
+ mOverlayViewGlobalStateController.inflateView(mOverlayViewController2);
- verify(mOverlayViewController).inflate(mBaseLayout);
+ verify(mOverlayViewController2).inflate(mBaseLayout);
}
@Test
public void inflateView_alreadyInflated_doesNotInflate() {
- when(mOverlayViewController.isInflated()).thenReturn(true);
+ when(mOverlayViewController2.isInflated()).thenReturn(true);
- mOverlayViewGlobalStateController.inflateView(mOverlayViewController);
+ mOverlayViewGlobalStateController.inflateView(mOverlayViewController2);
+
+ verify(mOverlayViewController2, never()).inflate(mBaseLayout);
+ }
+
+ private void setupOverlayViewController1() {
+ setupOverlayViewController(mOverlayViewController1, R.id.overlay_view_controller_stub_1,
+ R.id.overlay_view_controller_1);
+ }
- verify(mOverlayViewController, never()).inflate(mBaseLayout);
+ private void setupOverlayViewController2() {
+ setupOverlayViewController(mOverlayViewController2, R.id.overlay_view_controller_stub_2,
+ R.id.overlay_view_controller_2);
+ }
+
+ private void setupOverlayPanelViewController() {
+ setupOverlayViewController(mOverlayPanelViewController, R.id.overlay_view_controller_stub_3,
+ R.id.overlay_view_controller_3);
+ }
+
+ private void setupOverlayViewController(OverlayViewController overlayViewController,
+ int stubId, int inflatedId) {
+ ViewStub viewStub = mBaseLayout.findViewById(stubId);
+ View layout;
+ if (viewStub == null) {
+ layout = mBaseLayout.findViewById(inflatedId);
+ } else {
+ layout = viewStub.inflate();
+ }
+ when(overlayViewController.getLayout()).thenReturn(layout);
+ when(overlayViewController.isInflated()).thenReturn(true);
+ }
+
+ private void setOverlayViewControllerAsShowing(OverlayViewController overlayViewController) {
+ mOverlayViewGlobalStateController.showView(overlayViewController, /* show= */ null);
+ Mockito.reset(mCarNavigationBarController, mSystemUIOverlayWindowController);
+ when(mSystemUIOverlayWindowController.getBaseLayout()).thenReturn(mBaseLayout);
}
}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
index a95677d0202f..5675c9986ac9 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
@@ -38,8 +38,6 @@ import android.content.pm.PackageInfo;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.pm.PackageParser;
-import android.content.pm.PackageUserState;
import android.net.Uri;
import android.os.Bundle;
import android.os.Process;
@@ -526,18 +524,16 @@ public class PackageInstallerActivity extends AlertActivity {
case ContentResolver.SCHEME_FILE: {
File sourceFile = new File(packageUri.getPath());
- PackageParser.Package parsed = PackageUtil.getPackageInfo(this, sourceFile);
+ mPkgInfo = PackageUtil.getPackageInfo(this, sourceFile,
+ PackageManager.GET_PERMISSIONS);
// Check for parse errors
- if (parsed == null) {
+ if (mPkgInfo == null) {
Log.w(TAG, "Parse error when parsing manifest. Discontinuing installation");
showDialogInner(DLG_PACKAGE_ERROR);
setPmResult(PackageManager.INSTALL_FAILED_INVALID_APK);
return false;
}
- mPkgInfo = PackageParser.generatePackageInfo(parsed, null,
- PackageManager.GET_PERMISSIONS, 0, 0, null,
- new PackageUserState());
mAppSnippet = PackageUtil.getAppSnippet(this, mPkgInfo.applicationInfo, sourceFile);
} break;
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/PackageUtil.java b/packages/PackageInstaller/src/com/android/packageinstaller/PackageUtil.java
index 0e89f56d2376..d3a9f8fe1196 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/PackageUtil.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/PackageUtil.java
@@ -22,9 +22,8 @@ import android.annotation.Nullable;
import android.app.Activity;
import android.content.Context;
import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
-import android.content.pm.PackageParser;
-import android.content.pm.PackageParser.PackageParserException;
import android.content.res.AssetManager;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
@@ -53,12 +52,12 @@ public class PackageUtil {
/**
* Utility method to get package information for a given {@link File}
*/
- public static PackageParser.Package getPackageInfo(Context context, File sourceFile) {
- final PackageParser parser = new PackageParser();
- parser.setCallback(new PackageParser.CallbackImpl(context.getPackageManager()));
+ @Nullable
+ public static PackageInfo getPackageInfo(Context context, File sourceFile, int flags) {
try {
- return parser.parsePackage(sourceFile, 0);
- } catch (PackageParserException e) {
+ return context.getPackageManager().getPackageArchiveInfo(sourceFile.getAbsolutePath(),
+ flags);
+ } catch (Exception ignored) {
return null;
}
}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/wear/WearPackageInstallerService.java b/packages/PackageInstaller/src/com/android/packageinstaller/wear/WearPackageInstallerService.java
index e5f7613ab52b..06b1c1635644 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/wear/WearPackageInstallerService.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/wear/WearPackageInstallerService.java
@@ -22,11 +22,11 @@ import android.app.NotificationManager;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.ApplicationInfo;
import android.content.pm.FeatureInfo;
import android.content.pm.IPackageDeleteObserver;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
-import android.content.pm.PackageParser;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
@@ -49,6 +49,7 @@ import com.android.packageinstaller.R;
import java.io.File;
import java.io.FileNotFoundException;
+import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
@@ -244,47 +245,50 @@ public class WearPackageInstallerService extends Service {
Log.e(TAG, "Could not create a temp file from FD for " + packageName);
return;
}
- PackageParser.Package pkg = PackageUtil.getPackageInfo(this, tempFile);
- if (pkg == null) {
+ PackageInfo pkgInfo = PackageUtil.getPackageInfo(this, tempFile,
+ PackageManager.GET_PERMISSIONS | PackageManager.GET_CONFIGURATIONS);
+ if (pkgInfo == null) {
Log.e(TAG, "Could not parse apk information for " + packageName);
return;
}
- if (!pkg.packageName.equals(packageName)) {
+ if (!pkgInfo.packageName.equals(packageName)) {
Log.e(TAG, "Wearable Package Name has to match what is provided for " +
packageName);
return;
}
- pkg.applicationInfo.sourceDir = tempFile.getPath();
- pkg.applicationInfo.publicSourceDir = tempFile.getPath();
+ ApplicationInfo appInfo = pkgInfo.applicationInfo;
+ appInfo.sourceDir = tempFile.getPath();
+ appInfo.publicSourceDir = tempFile.getPath();
getLabelAndUpdateNotification(packageName,
- getString(R.string.installing_app, pkg.applicationInfo.loadLabel(pm)));
+ getString(R.string.installing_app, appInfo.loadLabel(pm)));
- List<String> wearablePerms = pkg.requestedPermissions;
+ List<String> wearablePerms = Arrays.asList(pkgInfo.requestedPermissions);
// Log if the installed pkg has a higher version number.
if (existingPkgInfo != null) {
- if (existingPkgInfo.getLongVersionCode() == pkg.getLongVersionCode()) {
+ long longVersionCode = pkgInfo.getLongVersionCode();
+ if (existingPkgInfo.getLongVersionCode() == longVersionCode) {
if (skipIfSameVersion) {
- Log.w(TAG, "Version number (" + pkg.getLongVersionCode() +
+ Log.w(TAG, "Version number (" + longVersionCode +
") of new app is equal to existing app for " + packageName +
"; not installing due to versionCheck");
return;
} else {
- Log.w(TAG, "Version number of new app (" + pkg.getLongVersionCode() +
+ Log.w(TAG, "Version number of new app (" + longVersionCode +
") is equal to existing app for " + packageName);
}
- } else if (existingPkgInfo.getLongVersionCode() > pkg.getLongVersionCode()) {
+ } else if (existingPkgInfo.getLongVersionCode() > longVersionCode) {
if (skipIfLowerVersion) {
// Starting in Feldspar, we are not going to allow downgrades of any app.
- Log.w(TAG, "Version number of new app (" + pkg.getLongVersionCode() +
+ Log.w(TAG, "Version number of new app (" + longVersionCode +
") is lower than existing app ( "
+ existingPkgInfo.getLongVersionCode() +
") for " + packageName + "; not installing due to versionCheck");
return;
} else {
- Log.w(TAG, "Version number of new app (" + pkg.getLongVersionCode() +
+ Log.w(TAG, "Version number of new app (" + longVersionCode +
") is lower than existing app ( "
+ existingPkgInfo.getLongVersionCode() + ") for " + packageName);
}
@@ -309,14 +313,12 @@ public class WearPackageInstallerService extends Service {
// Check that the wearable has all the features.
boolean hasAllFeatures = true;
- if (pkg.reqFeatures != null) {
- for (FeatureInfo feature : pkg.reqFeatures) {
- if (feature.name != null && !pm.hasSystemFeature(feature.name) &&
- (feature.flags & FeatureInfo.FLAG_REQUIRED) != 0) {
- Log.e(TAG, "Wearable does not have required feature: " + feature +
- " for " + packageName);
- hasAllFeatures = false;
- }
+ for (FeatureInfo feature : pkgInfo.reqFeatures) {
+ if (feature.name != null && !pm.hasSystemFeature(feature.name) &&
+ (feature.flags & FeatureInfo.FLAG_REQUIRED) != 0) {
+ Log.e(TAG, "Wearable does not have required feature: " + feature +
+ " for " + packageName);
+ hasAllFeatures = false;
}
}
@@ -328,8 +330,8 @@ public class WearPackageInstallerService extends Service {
// wearable package.
// If the app is targeting API level 23, we will also start a service in ClockworkHome
// which will ultimately prompt the user to accept/reject permissions.
- if (checkPerms && !checkPermissions(pkg, companionSdkVersion, companionDeviceVersion,
- permUri, wearablePerms, tempFile)) {
+ if (checkPerms && !checkPermissions(pkgInfo, companionSdkVersion,
+ companionDeviceVersion, permUri, wearablePerms, tempFile)) {
Log.w(TAG, "Wearable does not have enough permissions.");
return;
}
@@ -382,7 +384,7 @@ public class WearPackageInstallerService extends Service {
}
}
- private boolean checkPermissions(PackageParser.Package pkg, int companionSdkVersion,
+ private boolean checkPermissions(PackageInfo pkgInfo, int companionSdkVersion,
int companionDeviceVersion, Uri permUri, List<String> wearablePermissions,
File apkFile) {
// Assumption: We are running on Android O.
@@ -390,12 +392,12 @@ public class WearPackageInstallerService extends Service {
// app. If the Wear App is then not targeting M, there may be permissions that are not
// granted on the Phone app (by the user) right now and we cannot just grant it for the Wear
// app.
- if (pkg.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.M) {
+ if (pkgInfo.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.M) {
// Install the app if Wear App is ready for the new perms model.
return true;
}
- if (!doesWearHaveUngrantedPerms(pkg.packageName, permUri, wearablePermissions)) {
+ if (!doesWearHaveUngrantedPerms(pkgInfo.packageName, permUri, wearablePermissions)) {
// All permissions requested by the watch are already granted on the phone, no need
// to do anything.
return true;
diff --git a/packages/SettingsLib/res/values/arrays.xml b/packages/SettingsLib/res/values/arrays.xml
index 7b58937049d1..d59d698efba2 100644
--- a/packages/SettingsLib/res/values/arrays.xml
+++ b/packages/SettingsLib/res/values/arrays.xml
@@ -633,4 +633,18 @@
<item>@color/bt_color_bg_7</item>
</integer-array>
+ <!-- Cached apps freezer modes -->
+ <array name="cached_apps_freezer_entries">
+ <item>@string/cached_apps_freezer_device_default</item>
+ <item>@string/cached_apps_freezer_enabled</item>
+ <item>@string/cached_apps_freezer_disabled</item>
+ </array>
+
+ <!-- Values for cached apps freezer modes -->
+ <array name="cached_apps_freezer_values">
+ <item>device_default</item>
+ <item>enabled</item>
+ <item>disabled</item>
+ </array>
+
</resources>
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 7ca0e809143a..934f61091bcc 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -1365,4 +1365,12 @@
<!-- Name for the guest user [CHAR LIMIT=35] -->
<string name="guest_nickname">Guest</string>
+ <!-- List entry in developer settings to choose default device/system behavior for the app freezer [CHAR LIMIT=30]-->
+ <string name="cached_apps_freezer_device_default">Device default</string>
+ <!-- List entry in developer settings to disable the app freezer in developer settings [CHAR LIMIT=30]-->
+ <string name="cached_apps_freezer_disabled">Disabled</string>
+ <!-- List entry in developer settings to enable the app freezer in developer settings [CHAR LIMIT=30]-->
+ <string name="cached_apps_freezer_enabled">Enabled</string>
+ <!-- Developer setting dialog prompting the user to reboot after changing the app freezer setting [CHAR LIMIT=NONE]-->
+ <string name="cached_apps_freezer_reboot_dialog_text">Your device must be rebooted for this change to apply. Reboot now or cancel.</string>
</resources>
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/AppUtils.java b/packages/SettingsLib/src/com/android/settingslib/applications/AppUtils.java
index c4ff71940d20..ae3194df28fe 100644
--- a/packages/SettingsLib/src/com/android/settingslib/applications/AppUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/AppUtils.java
@@ -140,4 +140,15 @@ public class AppUtils {
.isSystemModule(packageName);
}
+ /**
+ * Returns a boolean indicating whether a given package is a mainline module.
+ */
+ public static boolean isMainlineModule(Context context, String packageName) {
+ final PackageManager pm = context.getPackageManager();
+ try {
+ return pm.getModuleInfo(packageName, 0 /* flags */) != null;
+ } catch (PackageManager.NameNotFoundException e) {
+ return false;
+ }
+ }
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
index 436f2fa87394..1d06df08bd3a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
@@ -108,10 +108,8 @@ public class InfoMediaManager extends MediaManager {
final List<RoutingSessionInfo> infos = mRouterManager.getActiveSessions();
if (infos.size() > 0) {
final RoutingSessionInfo info = infos.get(0);
- final MediaRouter2Manager.RoutingController controller =
- mRouterManager.getControllerForSession(info);
+ mRouterManager.transfer(info, device.mRouteInfo);
- controller.transferToRoute(device.mRouteInfo);
isConnected = true;
}
return isConnected;
@@ -131,7 +129,7 @@ public class InfoMediaManager extends MediaManager {
final RoutingSessionInfo info = getRoutingSessionInfo();
if (info != null && info.getSelectableRoutes().contains(device.mRouteInfo.getId())) {
- mRouterManager.getControllerForSession(info).selectRoute(device.mRouteInfo);
+ mRouterManager.selectRoute(info, device.mRouteInfo);
return true;
}
@@ -162,7 +160,7 @@ public class InfoMediaManager extends MediaManager {
final RoutingSessionInfo info = getRoutingSessionInfo();
if (info != null && info.getSelectedRoutes().contains(device.mRouteInfo.getId())) {
- mRouterManager.getControllerForSession(info).deselectRoute(device.mRouteInfo);
+ mRouterManager.deselectRoute(info, device.mRouteInfo);
return true;
}
@@ -207,8 +205,7 @@ public class InfoMediaManager extends MediaManager {
final RoutingSessionInfo info = getRoutingSessionInfo();
if (info != null) {
- for (MediaRoute2Info route : mRouterManager.getControllerForSession(info)
- .getSelectableRoutes()) {
+ for (MediaRoute2Info route : mRouterManager.getSelectableRoutes(info)) {
deviceList.add(new InfoMediaDevice(mContext, mRouterManager,
route, mPackageName));
}
@@ -235,8 +232,7 @@ public class InfoMediaManager extends MediaManager {
final RoutingSessionInfo info = getRoutingSessionInfo();
if (info != null) {
- for (MediaRoute2Info route : mRouterManager.getControllerForSession(info)
- .getSelectedRoutes()) {
+ for (MediaRoute2Info route : mRouterManager.getSelectedRoutes(info)) {
deviceList.add(new InfoMediaDevice(mContext, mRouterManager,
route, mPackageName));
}
@@ -434,7 +430,7 @@ public class InfoMediaManager extends MediaManager {
}
@Override
- public void onControlCategoriesChanged(String packageName, List<String> controlCategories) {
+ public void onPreferredFeaturesChanged(String packageName, List<String> preferredFeatures) {
if (TextUtils.equals(mPackageName, packageName)) {
refreshDevices();
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
index 31ea5b40d756..887a49b95279 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
@@ -27,10 +27,13 @@ import android.util.Log;
import androidx.annotation.IntDef;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.settingslib.bluetooth.A2dpProfile;
import com.android.settingslib.bluetooth.BluetoothCallback;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
+import com.android.settingslib.bluetooth.HearingAidProfile;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
+import com.android.settingslib.bluetooth.LocalBluetoothProfile;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -430,7 +433,8 @@ public class LocalMediaManager implements BluetoothCallback {
cachedDeviceManager.findDevice(device);
if (cachedDevice != null) {
if (cachedDevice.getBondState() == BluetoothDevice.BOND_BONDED
- && !cachedDevice.isConnected()) {
+ && !cachedDevice.isConnected()
+ && isA2dpOrHearingAidDevice(cachedDevice)) {
deviceCount++;
cachedBluetoothDeviceList.add(cachedDevice);
if (deviceCount >= MAX_DISCONNECTED_DEVICE_NUM) {
@@ -454,6 +458,15 @@ public class LocalMediaManager implements BluetoothCallback {
return new ArrayList<>(mDisconnectedMediaDevices);
}
+ private boolean isA2dpOrHearingAidDevice(CachedBluetoothDevice device) {
+ for (LocalBluetoothProfile profile : device.getConnectableProfiles()) {
+ if (profile instanceof A2dpProfile || profile instanceof HearingAidProfile) {
+ return true;
+ }
+ }
+ return false;
+ }
+
@Override
public void onDeviceRemoved(MediaDevice device) {
if (mMediaDevices.contains(device)) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
index f1c0f6b44150..139a12c44e0f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
@@ -31,18 +31,14 @@ import static android.media.MediaRoute2Info.TYPE_WIRED_HEADPHONES;
import static android.media.MediaRoute2Info.TYPE_WIRED_HEADSET;
import android.content.Context;
-import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
import android.media.MediaRoute2Info;
import android.media.MediaRouter2Manager;
import android.text.TextUtils;
-import android.util.Log;
import androidx.annotation.IntDef;
import androidx.annotation.VisibleForTesting;
-import com.android.settingslib.R;
-
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -180,7 +176,7 @@ public abstract class MediaDevice implements Comparable<MediaDevice> {
*/
public void requestSetVolume(int volume) {
- mRouterManager.requestSetVolume(mRouteInfo, volume);
+ mRouterManager.setRouteVolume(mRouteInfo, volume);
}
/**
@@ -215,30 +211,6 @@ public abstract class MediaDevice implements Comparable<MediaDevice> {
*
* @return application label.
*/
- public String getClientAppLabel() {
- final String packageName = mRouteInfo.getClientPackageName();
- if (TextUtils.isEmpty(packageName)) {
- Log.d(TAG, "Client package name is empty");
- return mContext.getResources().getString(R.string.unknown);
- }
- try {
- final PackageManager packageManager = mContext.getPackageManager();
- final String appLabel = packageManager.getApplicationLabel(
- packageManager.getApplicationInfo(packageName, 0)).toString();
- if (!TextUtils.isEmpty(appLabel)) {
- return appLabel;
- }
- } catch (PackageManager.NameNotFoundException e) {
- Log.e(TAG, "unable to find " + packageName);
- }
- return mContext.getResources().getString(R.string.unknown);
- }
-
- /**
- * Get application label from MediaDevice.
- *
- * @return application label.
- */
public int getDeviceType() {
return mType;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java
index 42f2542e5c30..8ea5ff1bf662 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java
@@ -41,7 +41,10 @@ public class PhoneMediaDevice extends MediaDevice {
private static final String TAG = "PhoneMediaDevice";
- public static final String ID = "phone_media_device_id_1";
+ public static final String PHONE_ID = "phone_media_device_id";
+ // For 3.5 mm wired headset
+ public static final String WIRED_HEADSET_ID = "wired_headset_media_device_id";
+ public static final String USB_HEADSET_ID = "usb_headset_media_device_id";
private String mSummary = "";
@@ -109,7 +112,25 @@ public class PhoneMediaDevice extends MediaDevice {
@Override
public String getId() {
- return ID;
+ String id;
+ switch (mRouteInfo.getType()) {
+ case TYPE_WIRED_HEADSET:
+ case TYPE_WIRED_HEADPHONES:
+ id = WIRED_HEADSET_ID;
+ break;
+ case TYPE_USB_DEVICE:
+ case TYPE_USB_HEADSET:
+ case TYPE_USB_ACCESSORY:
+ case TYPE_DOCK:
+ case TYPE_HDMI:
+ id = USB_HEADSET_ID;
+ break;
+ case TYPE_BUILTIN_SPEAKER:
+ default:
+ id = PHONE_ID;
+ break;
+ }
+ return id;
}
@Override
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 2ed9c7b94d89..99c568a4707b 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
@@ -136,7 +136,7 @@ public class InfoMediaManagerTest {
}
@Test
- public void onControlCategoriesChanged_samePackageName_shouldAddMediaDevice() {
+ public void onPreferredFeaturesChanged_samePackageName_shouldAddMediaDevice() {
final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>();
final RoutingSessionInfo sessionInfo = mock(RoutingSessionInfo.class);
routingSessionInfos.add(sessionInfo);
@@ -156,7 +156,7 @@ public class InfoMediaManagerTest {
final MediaDevice mediaDevice = mInfoMediaManager.findMediaDevice(TEST_ID);
assertThat(mediaDevice).isNull();
- mInfoMediaManager.mMediaRouterCallback.onControlCategoriesChanged(TEST_PACKAGE_NAME, null);
+ mInfoMediaManager.mMediaRouterCallback.onPreferredFeaturesChanged(TEST_PACKAGE_NAME, null);
final MediaDevice infoDevice = mInfoMediaManager.mMediaDevices.get(0);
assertThat(infoDevice.getId()).isEqualTo(TEST_ID);
@@ -165,8 +165,8 @@ public class InfoMediaManagerTest {
}
@Test
- public void onControlCategoriesChanged_differentPackageName_doNothing() {
- mInfoMediaManager.mMediaRouterCallback.onControlCategoriesChanged("com.fake.play", null);
+ public void onPreferredFeaturesChanged_differentPackageName_doNothing() {
+ mInfoMediaManager.mMediaRouterCallback.onPreferredFeaturesChanged("com.fake.play", null);
assertThat(mInfoMediaManager.mMediaDevices).hasSize(0);
}
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 77316e91bae2..365a16c50b99 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
@@ -41,6 +41,7 @@ import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
import com.android.settingslib.bluetooth.HearingAidProfile;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
+import com.android.settingslib.bluetooth.LocalBluetoothProfile;
import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
import com.android.settingslib.testutils.shadow.ShadowBluetoothAdapter;
@@ -560,6 +561,10 @@ public class LocalMediaManagerTest {
mLocalMediaManager.mMediaDevices.add(device3);
mLocalMediaManager.mMediaDevices.add(mLocalMediaManager.mPhoneDevice);
+ final List<LocalBluetoothProfile> profiles = new ArrayList<>();
+ final A2dpProfile a2dpProfile = mock(A2dpProfile.class);
+ profiles.add(a2dpProfile);
+
final List<BluetoothDevice> bluetoothDevices = new ArrayList<>();
final BluetoothDevice bluetoothDevice = mock(BluetoothDevice.class);
final CachedBluetoothDevice cachedDevice = mock(CachedBluetoothDevice.class);
@@ -571,6 +576,7 @@ public class LocalMediaManagerTest {
when(cachedManager.findDevice(bluetoothDevice)).thenReturn(cachedDevice);
when(cachedDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
when(cachedDevice.isConnected()).thenReturn(false);
+ when(cachedDevice.getConnectableProfiles()).thenReturn(profiles);
when(device1.getId()).thenReturn(TEST_DEVICE_ID_1);
when(device2.getId()).thenReturn(TEST_DEVICE_ID_2);
@@ -634,6 +640,10 @@ public class LocalMediaManagerTest {
mLocalMediaManager.mMediaDevices.add(device3);
mLocalMediaManager.mMediaDevices.add(mLocalMediaManager.mPhoneDevice);
+ final List<LocalBluetoothProfile> profiles = new ArrayList<>();
+ final A2dpProfile a2dpProfile = mock(A2dpProfile.class);
+ profiles.add(a2dpProfile);
+
final List<BluetoothDevice> bluetoothDevices = new ArrayList<>();
final BluetoothDevice bluetoothDevice = mock(BluetoothDevice.class);
final BluetoothDevice bluetoothDevice2 = mock(BluetoothDevice.class);
@@ -662,6 +672,7 @@ public class LocalMediaManagerTest {
when(cachedDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
when(cachedDevice.isConnected()).thenReturn(false);
when(cachedDevice.getDevice()).thenReturn(bluetoothDevice);
+ when(cachedDevice.getConnectableProfiles()).thenReturn(profiles);
when(bluetoothDevice.getBluetoothClass()).thenReturn(bluetoothClass);
when(bluetoothClass.getDeviceClass()).thenReturn(AUDIO_VIDEO_HEADPHONES);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java
index 6664870a6257..47d4beb752c5 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java
@@ -29,13 +29,9 @@ import static org.mockito.Mockito.when;
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
import android.content.Context;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageStats;
import android.media.MediaRoute2Info;
import android.media.MediaRouter2Manager;
-import com.android.settingslib.R;
import com.android.settingslib.bluetooth.A2dpProfile;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.HearingAidProfile;
@@ -49,8 +45,6 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
-import org.robolectric.Shadows;
-import org.robolectric.shadows.ShadowPackageManager;
import java.util.ArrayList;
import java.util.Collections;
@@ -70,8 +64,6 @@ public class MediaDeviceTest {
private static final String ROUTER_ID_2 = "RouterId_2";
private static final String ROUTER_ID_3 = "RouterId_3";
private static final String TEST_PACKAGE_NAME = "com.test.playmusic";
- private static final String TEST_PACKAGE_NAME2 = "com.test.playmusic2";
- private static final String TEST_APPLICATION_LABEL = "playmusic";
private final BluetoothClass mHeadreeClass =
new BluetoothClass(BluetoothClass.Device.AUDIO_VIDEO_HEADPHONES);
private final BluetoothClass mCarkitClass =
@@ -125,10 +117,6 @@ public class MediaDeviceTest {
private InfoMediaDevice mInfoMediaDevice3;
private List<MediaDevice> mMediaDevices = new ArrayList<>();
private PhoneMediaDevice mPhoneMediaDevice;
- private ShadowPackageManager mShadowPackageManager;
- private ApplicationInfo mAppInfo;
- private PackageInfo mPackageInfo;
- private PackageStats mPackageStats;
@Before
public void setUp() {
@@ -459,41 +447,6 @@ public class MediaDeviceTest {
assertThat(mInfoMediaDevice1.getClientPackageName()).isEqualTo(TEST_PACKAGE_NAME);
}
- private void initPackage() {
- mShadowPackageManager = Shadows.shadowOf(mContext.getPackageManager());
- mAppInfo = new ApplicationInfo();
- mAppInfo.flags = ApplicationInfo.FLAG_INSTALLED;
- mAppInfo.packageName = TEST_PACKAGE_NAME;
- mAppInfo.name = TEST_APPLICATION_LABEL;
- mPackageInfo = new PackageInfo();
- mPackageInfo.packageName = TEST_PACKAGE_NAME;
- mPackageInfo.applicationInfo = mAppInfo;
- mPackageStats = new PackageStats(TEST_PACKAGE_NAME);
- }
-
- @Test
- public void getClientAppLabel_matchedPackageName_returnLabel() {
- initPackage();
- when(mRouteInfo1.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
-
- assertThat(mInfoMediaDevice1.getClientAppLabel()).isEqualTo(
- mContext.getResources().getString(R.string.unknown));
-
- mShadowPackageManager.addPackage(mPackageInfo, mPackageStats);
-
- assertThat(mInfoMediaDevice1.getClientAppLabel()).isEqualTo(TEST_APPLICATION_LABEL);
- }
-
- @Test
- public void getClientAppLabel_noMatchedPackageName_returnDefault() {
- initPackage();
- mShadowPackageManager.addPackage(mPackageInfo, mPackageStats);
- when(mRouteInfo1.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME2);
-
- assertThat(mInfoMediaDevice1.getClientAppLabel()).isEqualTo(
- mContext.getResources().getString(R.string.unknown));
- }
-
@Test
public void setState_verifyGetState() {
mInfoMediaDevice1.setState(LocalMediaManager.MediaDeviceState.STATE_CONNECTED);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/PhoneMediaDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/PhoneMediaDeviceTest.java
index 6f265dd603e5..47f6fe3bce02 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/PhoneMediaDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/PhoneMediaDeviceTest.java
@@ -21,6 +21,10 @@ import static android.media.MediaRoute2Info.TYPE_USB_DEVICE;
import static android.media.MediaRoute2Info.TYPE_WIRED_HEADPHONES;
import static android.media.MediaRoute2Info.TYPE_WIRED_HEADSET;
+import static com.android.settingslib.media.PhoneMediaDevice.PHONE_ID;
+import static com.android.settingslib.media.PhoneMediaDevice.USB_HEADSET_ID;
+import static com.android.settingslib.media.PhoneMediaDevice.WIRED_HEADSET_ID;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.when;
@@ -108,4 +112,22 @@ public class PhoneMediaDeviceTest {
assertThat(mPhoneMediaDevice.getName())
.isEqualTo(mContext.getString(R.string.media_transfer_this_device_name));
}
+
+ @Test
+ public void getId_returnCorrectId() {
+ when(mInfo.getType()).thenReturn(TYPE_WIRED_HEADPHONES);
+
+ assertThat(mPhoneMediaDevice.getId())
+ .isEqualTo(WIRED_HEADSET_ID);
+
+ when(mInfo.getType()).thenReturn(TYPE_USB_DEVICE);
+
+ assertThat(mPhoneMediaDevice.getId())
+ .isEqualTo(USB_HEADSET_ID);
+
+ when(mInfo.getType()).thenReturn(TYPE_BUILTIN_SPEAKER);
+
+ assertThat(mPhoneMediaDevice.getId())
+ .isEqualTo(PHONE_ID);
+ }
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index a5dce6da348f..3d7559b2c1a6 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -376,6 +376,9 @@ class SettingsProtoDumpUtil {
Settings.Global.BUGREPORT_IN_POWER_MENU,
GlobalSettingsProto.BUGREPORT_IN_POWER_MENU);
dumpSetting(s, p,
+ Settings.Global.CACHED_APPS_FREEZER_ENABLED,
+ GlobalSettingsProto.CACHED_APPS_FREEZER_ENABLED);
+ dumpSetting(s, p,
Settings.Global.CALL_AUTO_RETRY,
GlobalSettingsProto.CALL_AUTO_RETRY);
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index be0a8640760f..4a9eba2202e3 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -587,7 +587,8 @@ public class SettingsBackupTest {
Settings.Global.POWER_BUTTON_VERY_LONG_PRESS,
Settings.Global.SHOW_MEDIA_ON_QUICK_SETTINGS, // Temporary for R beta
Settings.Global.INTEGRITY_CHECK_INCLUDES_RULE_PROVIDER,
- Settings.Global.ADVANCED_BATTERY_USAGE_AMOUNT);
+ Settings.Global.ADVANCED_BATTERY_USAGE_AMOUNT,
+ Settings.Global.CACHED_APPS_FREEZER_ENABLED);
private static final Set<String> BACKUP_BLACKLISTED_SECURE_SETTINGS =
newHashSet(
diff --git a/packages/SystemUI/res/drawable/qs_media_background.xml b/packages/SystemUI/res/drawable/qs_media_background.xml
index 2821e4c28bab..e79c9a40918c 100644
--- a/packages/SystemUI/res/drawable/qs_media_background.xml
+++ b/packages/SystemUI/res/drawable/qs_media_background.xml
@@ -14,13 +14,9 @@
~ See the License for the specific language governing permissions and
~ limitations under the License
-->
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
- android:shape="rectangle">
- <solid android:color="?android:attr/colorBackgroundFloating" />
- <corners
- android:bottomLeftRadius="@dimen/qs_media_corner_radius"
- android:topLeftRadius="@dimen/qs_media_corner_radius"
- android:bottomRightRadius="@dimen/qs_media_corner_radius"
- android:topRightRadius="@dimen/qs_media_corner_radius"
- />
-</shape> \ No newline at end of file
+<com.android.systemui.media.IlluminationDrawable
+ xmlns:systemui="http://schemas.android.com/apk/res-auto"
+ systemui:rippleMinSize="30dp"
+ systemui:rippleMaxSize="135dp"
+ systemui:highlight="15"
+ systemui:cornerRadius="@dimen/qs_media_corner_radius" /> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/screenshot_actions_background_protection.xml b/packages/SystemUI/res/drawable/screenshot_actions_background_protection.xml
index 163015b7b0f0..21013c6c7b16 100644
--- a/packages/SystemUI/res/drawable/screenshot_actions_background_protection.xml
+++ b/packages/SystemUI/res/drawable/screenshot_actions_background_protection.xml
@@ -17,6 +17,6 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
<gradient
android:angle="90"
- android:startColor="#1f000000"
+ android:startColor="@color/global_screenshot_background_protection_start"
android:endColor="#00000000"/>
</shape> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/bubble_overflow_view.xml b/packages/SystemUI/res/layout/bubble_overflow_view.xml
index 88a05ec5824a..1ed1f07fb277 100644
--- a/packages/SystemUI/res/layout/bubble_overflow_view.xml
+++ b/packages/SystemUI/res/layout/bubble_overflow_view.xml
@@ -36,6 +36,8 @@
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:maxLines="1"
+ android:lines="2"
+ android:ellipsize="end"
android:layout_gravity="center"
android:paddingTop="@dimen/bubble_overflow_text_padding"
android:gravity="center"/>
diff --git a/packages/SystemUI/res/layout/global_screenshot.xml b/packages/SystemUI/res/layout/global_screenshot.xml
index de19303b4948..1dbb38d5dc7a 100644
--- a/packages/SystemUI/res/layout/global_screenshot.xml
+++ b/packages/SystemUI/res/layout/global_screenshot.xml
@@ -22,7 +22,7 @@
android:layout_height="match_parent">
<ImageView
android:id="@+id/global_screenshot_actions_background"
- android:layout_height="@dimen/global_screenshot_bg_protection_height"
+ android:layout_height="@dimen/screenshot_bg_protection_height"
android:layout_width="match_parent"
android:alpha="0.0"
android:src="@drawable/screenshot_actions_background_protection"
diff --git a/packages/SystemUI/res/layout/keyguard_media_header.xml b/packages/SystemUI/res/layout/keyguard_media_header.xml
index de9ef218083b..20ec10ca1e1b 100644
--- a/packages/SystemUI/res/layout/keyguard_media_header.xml
+++ b/packages/SystemUI/res/layout/keyguard_media_header.xml
@@ -124,7 +124,7 @@
android:layout_gravity="center"
>
<ImageButton
- style="@android:style/Widget.Material.Button.Borderless.Small"
+ style="@style/MediaPlayer.Button"
android:layout_width="48dp"
android:layout_height="48dp"
android:gravity="center"
@@ -132,7 +132,7 @@
android:id="@+id/action0"
/>
<ImageButton
- style="@android:style/Widget.Material.Button.Borderless.Small"
+ style="@style/MediaPlayer.Button"
android:layout_width="48dp"
android:layout_height="48dp"
android:gravity="center"
@@ -140,7 +140,7 @@
android:id="@+id/action1"
/>
<ImageButton
- style="@android:style/Widget.Material.Button.Borderless.Small"
+ style="@style/MediaPlayer.Button"
android:layout_width="48dp"
android:layout_height="48dp"
android:gravity="center"
diff --git a/packages/SystemUI/res/layout/qqs_media_panel.xml b/packages/SystemUI/res/layout/qqs_media_panel.xml
index 403b5dc3a427..2e86732f3cad 100644
--- a/packages/SystemUI/res/layout/qqs_media_panel.xml
+++ b/packages/SystemUI/res/layout/qqs_media_panel.xml
@@ -63,7 +63,7 @@
android:gravity="center"
>
<ImageButton
- style="@android:style/Widget.Material.Button.Borderless.Small"
+ style="@style/MediaPlayer.Button"
android:layout_width="48dp"
android:layout_height="48dp"
android:gravity="center"
@@ -71,7 +71,7 @@
android:id="@+id/action0"
/>
<ImageButton
- style="@android:style/Widget.Material.Button.Borderless.Small"
+ style="@style/MediaPlayer.Button"
android:layout_width="48dp"
android:layout_height="48dp"
android:gravity="center"
@@ -79,7 +79,7 @@
android:id="@+id/action1"
/>
<ImageButton
- style="@android:style/Widget.Material.Button.Borderless.Small"
+ style="@style/MediaPlayer.Button"
android:layout_width="48dp"
android:layout_height="48dp"
android:gravity="center"
diff --git a/packages/SystemUI/res/layout/qs_media_panel.xml b/packages/SystemUI/res/layout/qs_media_panel.xml
index a194569dcca4..d633ff40df9e 100644
--- a/packages/SystemUI/res/layout/qs_media_panel.xml
+++ b/packages/SystemUI/res/layout/qs_media_panel.xml
@@ -197,7 +197,7 @@
android:gravity="center"
>
<ImageButton
- style="@android:style/Widget.Material.Button.Borderless.Small"
+ style="@style/MediaPlayer.Button"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_marginStart="8dp"
@@ -207,7 +207,7 @@
android:id="@+id/action0"
/>
<ImageButton
- style="@android:style/Widget.Material.Button.Borderless.Small"
+ style="@style/MediaPlayer.Button"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_marginStart="8dp"
@@ -217,7 +217,7 @@
android:id="@+id/action1"
/>
<ImageButton
- style="@android:style/Widget.Material.Button.Borderless.Small"
+ style="@style/MediaPlayer.Button"
android:layout_width="52dp"
android:layout_height="52dp"
android:layout_marginStart="8dp"
@@ -227,7 +227,7 @@
android:id="@+id/action2"
/>
<ImageButton
- style="@android:style/Widget.Material.Button.Borderless.Small"
+ style="@style/MediaPlayer.Button"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_marginStart="8dp"
@@ -237,7 +237,7 @@
android:id="@+id/action3"
/>
<ImageButton
- style="@android:style/Widget.Material.Button.Borderless.Small"
+ style="@style/MediaPlayer.Button"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_marginStart="8dp"
diff --git a/packages/SystemUI/res/values-night/colors.xml b/packages/SystemUI/res/values-night/colors.xml
index 2d5101104237..196357c4794e 100644
--- a/packages/SystemUI/res/values-night/colors.xml
+++ b/packages/SystemUI/res/values-night/colors.xml
@@ -79,6 +79,7 @@
<color name="global_screenshot_button_icon">@color/GM2_blue_300</color>
<color name="global_screenshot_dismiss_background">@color/GM2_grey_800</color>
<color name="global_screenshot_dismiss_foreground">#FFFFFF</color>
+ <color name="global_screenshot_background_protection_start">#80000000</color> <!-- 50% black -->
<!-- Biometric dialog colors -->
diff --git a/packages/SystemUI/res/values-night/dimens.xml b/packages/SystemUI/res/values-night/dimens.xml
index 481483991de9..23e323112845 100644
--- a/packages/SystemUI/res/values-night/dimens.xml
+++ b/packages/SystemUI/res/values-night/dimens.xml
@@ -18,4 +18,8 @@
<resources>
<!-- The height of the divider between the individual notifications. -->
<dimen name="notification_divider_height">1dp</dimen>
+
+ <!-- Height of the background gradient behind the screenshot UI (taller in dark mode) -->
+ <dimen name="screenshot_bg_protection_height">375dp</dimen>
+
</resources> \ No newline at end of file
diff --git a/packages/SystemUI/res/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml
index d3256ef34bd7..c4195940d11a 100644
--- a/packages/SystemUI/res/values/attrs.xml
+++ b/packages/SystemUI/res/values/attrs.xml
@@ -154,5 +154,12 @@
<declare-styleable name="CaptionsToggleImageButton">
<attr name="optedOut" format="boolean" />
</declare-styleable>
+
+ <declare-styleable name="IlluminationDrawable">
+ <attr name="highlight" format="integer" />
+ <attr name="cornerRadius" format="dimension" />
+ <attr name="rippleMinSize" format="dimension" />
+ <attr name="rippleMaxSize" format="dimension" />
+ </declare-styleable>
</resources>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 82eda311da6a..b6776005d83e 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -200,6 +200,7 @@
<color name="global_screenshot_button_icon">@color/GM2_blue_500</color>
<color name="global_screenshot_dismiss_background">#FFFFFF</color>
<color name="global_screenshot_dismiss_foreground">@color/GM2_grey_500</color>
+ <color name="global_screenshot_background_protection_start">#40000000</color> <!-- 25% black -->
<!-- GM2 colors -->
<color name="GM2_grey_50">#F8F9FA</color>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index d2f64f930465..14075743ce34 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -530,6 +530,10 @@
<!-- ID for the camera that needs extra protection -->
<string translatable="false" name="config_protectedCameraId"></string>
+ <!-- Comma-separated list of packages to exclude from camera protection e.g.
+ "com.android.systemui,com.android.xyz" -->
+ <string translatable="false" name="config_cameraProtectionExcludedPackages"></string>
+
<!-- Flag to turn on the rendering of the above path or not -->
<bool name="config_enableDisplayCutoutProtection">false</bool>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 2cbb49801aa8..99e347eb1a69 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -306,6 +306,7 @@
<dimen name="global_screenshot_bg_padding">20dp</dimen>
<dimen name="global_screenshot_bg_protection_height">400dp</dimen>
<dimen name="global_screenshot_x_scale">80dp</dimen>
+ <dimen name="screenshot_bg_protection_height">242dp</dimen>
<dimen name="screenshot_preview_elevation">6dp</dimen>
<dimen name="screenshot_offset_y">48dp</dimen>
<dimen name="screenshot_offset_x">16dp</dimen>
@@ -1168,7 +1169,7 @@
<!-- Default (and minimum) height of the expanded view shown when the bubble is expanded -->
<dimen name="bubble_expanded_default_height">180dp</dimen>
<!-- Default height of bubble overflow -->
- <dimen name="bubble_overflow_height">460dp</dimen>
+ <dimen name="bubble_overflow_height">480dp</dimen>
<!-- Bubble overflow padding when there are no bubbles -->
<dimen name="bubble_overflow_empty_state_padding">16dp</dimen>
<!-- Padding of container for overflow bubbles -->
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 96843f187f72..023c77d5db14 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1253,6 +1253,9 @@
<!-- The text for the notification history link. [CHAR LIMIT=40] -->
<string name="manage_notifications_history_text">History</string>
+ <!-- Section title for notifications that have recently appeared. [CHAR LIMIT=40] -->
+ <string name="notification_section_header_incoming">Incoming</string>
+
<!-- Section title for notifications that do not vibrate or make noise. [CHAR LIMIT=40] -->
<string name="notification_section_header_gentle">Silent notifications</string>
@@ -2608,6 +2611,10 @@
<!-- Text used for content description of settings button in the header of expanded bubble
view. [CHAR_LIMIT=NONE] -->
<string name="bubbles_settings_button_description">Settings for <xliff:g id="app_name" example="YouTube">%1$s</xliff:g> bubbles</string>
+ <!-- Content description for button that shows bubble overflow on click [CHAR LIMIT=NONE] -->
+ <string name="bubble_overflow_button_content_description">Overflow</string>
+ <!-- Action to add overflow bubble back to stack. [CHAR LIMIT=NONE] -->
+ <string name="bubble_accessibility_action_add_back">Add back to stack</string>
<!-- The text for the manage bubbles link. [CHAR LIMIT=NONE] -->
<string name="manage_bubbles_text">Manage</string>
<!-- Content description when a bubble is focused. [CHAR LIMIT=NONE] -->
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index b5e7f4bc061e..f0edd6388ae7 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -620,6 +620,10 @@
<item name="rotateButtonScaleX">-1</item>
</style>
+ <style name="MediaPlayer.Button" parent="@android:style/Widget.Material.Button.Borderless.Small">
+ <item name="android:background">@null</item>
+ </style>
+
<!-- Used to style charging animation AVD animation -->
<style name="ChargingAnim" />
diff --git a/packages/SystemUI/src/com/android/systemui/CameraAvailabilityListener.kt b/packages/SystemUI/src/com/android/systemui/CameraAvailabilityListener.kt
index 284074e76ae2..3015710e8a98 100644
--- a/packages/SystemUI/src/com/android/systemui/CameraAvailabilityListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/CameraAvailabilityListener.kt
@@ -37,20 +37,22 @@ class CameraAvailabilityListener(
private val cameraManager: CameraManager,
private val cutoutProtectionPath: Path,
private val targetCameraId: String,
+ excludedPackages: String,
private val executor: Executor
) {
private var cutoutBounds = Rect()
+ private val excludedPackageIds: Set<String>
private val listeners = mutableListOf<CameraTransitionCallback>()
private val availabilityCallback: CameraManager.AvailabilityCallback =
object : CameraManager.AvailabilityCallback() {
- override fun onCameraAvailable(cameraId: String) {
+ override fun onCameraClosed(cameraId: String) {
if (targetCameraId == cameraId) {
notifyCameraInactive()
}
}
- override fun onCameraUnavailable(cameraId: String) {
- if (targetCameraId == cameraId) {
+ override fun onCameraOpened(cameraId: String, packageId: String) {
+ if (targetCameraId == cameraId && !isExcluded(packageId)) {
notifyCameraActive()
}
}
@@ -64,6 +66,7 @@ class CameraAvailabilityListener(
computed.top.roundToInt(),
computed.right.roundToInt(),
computed.bottom.roundToInt())
+ excludedPackageIds = excludedPackages.split(",").toSet()
}
/**
@@ -87,6 +90,10 @@ class CameraAvailabilityListener(
listeners.remove(callback)
}
+ private fun isExcluded(packageId: String): Boolean {
+ return excludedPackageIds.contains(packageId)
+ }
+
private fun registerCameraListener() {
cameraManager.registerAvailabilityCallback(executor, availabilityCallback)
}
@@ -118,9 +125,10 @@ class CameraAvailabilityListener(
val res = context.resources
val pathString = res.getString(R.string.config_frontBuiltInDisplayCutoutProtection)
val cameraId = res.getString(R.string.config_protectedCameraId)
+ val excluded = res.getString(R.string.config_cameraProtectionExcludedPackages)
return CameraAvailabilityListener(
- manager, pathFromString(pathString), cameraId, executor)
+ manager, pathFromString(pathString), cameraId, excluded, executor)
}
private fun pathFromString(pathString: String): Path {
@@ -135,4 +143,4 @@ class CameraAvailabilityListener(
return p
}
}
-} \ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/broadcast/UserBroadcastDispatcher.kt b/packages/SystemUI/src/com/android/systemui/broadcast/UserBroadcastDispatcher.kt
index 4e84f06f51a7..3272fb7545e2 100644
--- a/packages/SystemUI/src/com/android/systemui/broadcast/UserBroadcastDispatcher.kt
+++ b/packages/SystemUI/src/com/android/systemui/broadcast/UserBroadcastDispatcher.kt
@@ -32,6 +32,8 @@ import com.android.internal.util.Preconditions
import com.android.systemui.Dumpable
import java.io.FileDescriptor
import java.io.PrintWriter
+import java.lang.IllegalArgumentException
+import java.lang.IllegalStateException
import java.util.concurrent.atomic.AtomicBoolean
import java.util.concurrent.atomic.AtomicInteger
@@ -211,7 +213,12 @@ class UserBroadcastDispatcher(
*/
override fun run() {
if (registered.get()) {
- context.unregisterReceiver(this@UserBroadcastDispatcher)
+ try {
+ context.unregisterReceiver(this@UserBroadcastDispatcher)
+ } catch (e: IllegalArgumentException) {
+ Log.e(TAG, "Trying to unregister unregistered receiver for user $userId",
+ IllegalStateException(e))
+ }
registered.set(false)
}
// Short interval without receiver, this can be problematic
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleDataRepository.kt b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleDataRepository.kt
index 63dd801be7ca..b9825e1d21e5 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleDataRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleDataRepository.kt
@@ -16,6 +16,7 @@
package com.android.systemui.bubbles
import android.annotation.UserIdInt
+import android.util.Log
import com.android.systemui.bubbles.storage.BubblePersistentRepository
import com.android.systemui.bubbles.storage.BubbleVolatileRepository
import com.android.systemui.bubbles.storage.BubbleXmlEntity
@@ -35,36 +36,39 @@ internal class BubbleDataRepository @Inject constructor(
) {
private val ioScope = CoroutineScope(Dispatchers.IO)
+ private val uiScope = CoroutineScope(Dispatchers.Main)
private var job: Job? = null
/**
* Adds the bubble in memory, then persists the snapshot after adding the bubble to disk
* asynchronously.
*/
- fun addBubble(@UserIdInt userId: Int, bubble: Bubble) {
- volatileRepository.addBubble(
- BubbleXmlEntity(userId, bubble.packageName, bubble.shortcutInfo?.id ?: return))
- persistToDisk()
- }
+ fun addBubble(@UserIdInt userId: Int, bubble: Bubble) = addBubbles(userId, listOf(bubble))
/**
* Adds the bubble in memory, then persists the snapshot after adding the bubble to disk
* asynchronously.
*/
fun addBubbles(@UserIdInt userId: Int, bubbles: List<Bubble>) {
- volatileRepository.addBubbles(bubbles.mapNotNull {
- val shortcutId = it.shortcutInfo?.id ?: return@mapNotNull null
- BubbleXmlEntity(userId, it.packageName, shortcutId)
- })
- persistToDisk()
+ if (DEBUG) Log.d(TAG, "adding ${bubbles.size} bubbles")
+ val entities = transform(userId, bubbles).also(volatileRepository::addBubbles)
+ if (entities.isNotEmpty()) persistToDisk()
}
+ /**
+ * Removes the bubbles from memory, then persists the snapshot to disk asynchronously.
+ */
fun removeBubbles(@UserIdInt userId: Int, bubbles: List<Bubble>) {
- volatileRepository.removeBubbles(bubbles.mapNotNull {
- val shortcutId = it.shortcutInfo?.id ?: return@mapNotNull null
- BubbleXmlEntity(userId, it.packageName, shortcutId)
- })
- persistToDisk()
+ if (DEBUG) Log.d(TAG, "removing ${bubbles.size} bubbles")
+ val entities = transform(userId, bubbles).also(volatileRepository::removeBubbles)
+ if (entities.isNotEmpty()) persistToDisk()
+ }
+
+ private fun transform(userId: Int, bubbles: List<Bubble>): List<BubbleXmlEntity> {
+ return bubbles.mapNotNull { b ->
+ val shortcutId = b.shortcutInfo?.id ?: return@mapNotNull null
+ BubbleXmlEntity(userId, b.packageName, shortcutId)
+ }
}
/**
@@ -92,4 +96,19 @@ internal class BubbleDataRepository @Inject constructor(
persistentRepository.persistsToDisk(volatileRepository.bubbles)
}
}
+
+ /**
+ * Load bubbles from disk.
+ */
+ fun loadBubbles(cb: (List<Bubble>) -> Unit) = ioScope.launch {
+ val bubbleXmlEntities = persistentRepository.readFromDisk()
+ volatileRepository.addBubbles(bubbleXmlEntities)
+ uiScope.launch {
+ // TODO: transform bubbleXmlEntities into bubbles
+ // cb(bubbles)
+ }
+ }
}
+
+private const val TAG = "BubbleDataRepository"
+private const val DEBUG = false
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
index b3c2c6d60708..baf92dc7abe7 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
@@ -57,6 +57,7 @@ import android.view.Gravity;
import android.view.View;
import android.view.WindowInsets;
import android.view.WindowManager;
+import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.LinearLayout;
import com.android.internal.policy.ScreenDecorationsUtils;
@@ -456,6 +457,19 @@ public class BubbleExpandedView extends LinearLayout {
mSettingsIcon.setContentDescription(getResources().getString(
R.string.bubbles_settings_button_description, bubble.getAppName()));
+ mSettingsIcon.setAccessibilityDelegate(
+ new AccessibilityDelegate() {
+ @Override
+ public void onInitializeAccessibilityNodeInfo(View host,
+ AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(host, info);
+ // On focus, have TalkBack say
+ // "Actions available. Use swipe up then right to view."
+ // in addition to the default "double tap to activate".
+ mStackView.setupLocalMenu(info);
+ }
+ });
+
if (isNew) {
mPendingIntent = mBubble.getBubbleIntent();
if (mPendingIntent != null || mBubble.getShortcutInfo() != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.java
index 13669a68defa..e96bef36ba18 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.java
@@ -73,12 +73,13 @@ public class BubbleOverflow implements BubbleViewProvider {
updateIcon(mContext, parentViewGroup);
}
- // TODO(b/149146374) Propagate theme change to bubbles in overflow.
void updateIcon(Context context, ViewGroup parentViewGroup) {
mInflater = LayoutInflater.from(context);
mOverflowBtn = (BadgedImageView) mInflater.inflate(R.layout.bubble_overflow_button,
parentViewGroup /* root */,
false /* attachToRoot */);
+ mOverflowBtn.setContentDescription(mContext.getResources().getString(
+ R.string.bubble_overflow_button_content_description));
TypedArray ta = mContext.obtainStyledAttributes(
new int[]{android.R.attr.colorBackgroundFloating});
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java
index de54c353fc85..c2ca9fad6d43 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java
@@ -21,6 +21,7 @@ import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_BUBBLES;
import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME;
import android.app.Activity;
+import android.app.Notification;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
@@ -32,6 +33,7 @@ import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
@@ -103,7 +105,7 @@ public class BubbleOverflowActivity extends Activity {
- res.getDimensionPixelSize(R.dimen.bubble_overflow_padding);
final int viewHeight = recyclerViewHeight / rows;
- mAdapter = new BubbleOverflowAdapter(mOverflowBubbles,
+ mAdapter = new BubbleOverflowAdapter(getApplicationContext(), mOverflowBubbles,
mBubbleController::promoteBubbleFromOverflow, viewWidth, viewHeight);
mRecyclerView.setAdapter(mAdapter);
@@ -221,13 +223,15 @@ public class BubbleOverflowActivity extends Activity {
}
class BubbleOverflowAdapter extends RecyclerView.Adapter<BubbleOverflowAdapter.ViewHolder> {
+ private Context mContext;
private Consumer<Bubble> mPromoteBubbleFromOverflow;
private List<Bubble> mBubbles;
private int mWidth;
private int mHeight;
- public BubbleOverflowAdapter(List<Bubble> list, Consumer<Bubble> promoteBubble, int width,
- int height) {
+ public BubbleOverflowAdapter(Context context, List<Bubble> list, Consumer<Bubble> promoteBubble,
+ int width, int height) {
+ mContext = context;
mBubbles = list;
mPromoteBubbleFromOverflow = promoteBubble;
mWidth = width;
@@ -260,6 +264,32 @@ class BubbleOverflowAdapter extends RecyclerView.Adapter<BubbleOverflowAdapter.V
mPromoteBubbleFromOverflow.accept(b);
});
+ final CharSequence titleCharSeq =
+ b.getEntry().getSbn().getNotification().extras.getCharSequence(
+ Notification.EXTRA_TITLE);
+ String titleStr = mContext.getResources().getString(R.string.notification_bubble_title);
+ if (titleCharSeq != null) {
+ titleStr = titleCharSeq.toString();
+ }
+ vh.iconView.setContentDescription(mContext.getResources().getString(
+ R.string.bubble_content_description_single, titleStr, b.getAppName()));
+
+ vh.iconView.setAccessibilityDelegate(
+ new View.AccessibilityDelegate() {
+ @Override
+ public void onInitializeAccessibilityNodeInfo(View host,
+ AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(host, info);
+ // Talkback prompts "Double tap to add back to stack"
+ // instead of the default "Double tap to activate"
+ info.addAction(
+ new AccessibilityNodeInfo.AccessibilityAction(
+ AccessibilityNodeInfo.ACTION_CLICK,
+ mContext.getResources().getString(
+ R.string.bubble_accessibility_action_add_back)));
+ }
+ });
+
Bubble.FlyoutMessage message = b.getFlyoutMessage();
if (message != null && message.senderName != null) {
vh.textView.setText(message.senderName);
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index 35ac78df29ab..2cb097f6075e 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -454,7 +454,6 @@ public class BubbleStackView extends FrameLayout
// that means overflow was previously expanded. Set the selected bubble
// internally without going through BubbleData (which would ignore it since it's
// already selected).
- mBubbleData.setShowingOverflow(true);
setSelectedBubble(clickedBubble);
}
} else {
@@ -1076,26 +1075,27 @@ public class BubbleStackView extends FrameLayout
@Override
public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
super.onInitializeAccessibilityNodeInfoInternal(info);
+ setupLocalMenu(info);
+ }
+
+ void setupLocalMenu(AccessibilityNodeInfo info) {
+ Resources res = mContext.getResources();
- // Custom actions.
+ // Custom local actions.
AccessibilityAction moveTopLeft = new AccessibilityAction(R.id.action_move_top_left,
- getContext().getResources()
- .getString(R.string.bubble_accessibility_action_move_top_left));
+ res.getString(R.string.bubble_accessibility_action_move_top_left));
info.addAction(moveTopLeft);
AccessibilityAction moveTopRight = new AccessibilityAction(R.id.action_move_top_right,
- getContext().getResources()
- .getString(R.string.bubble_accessibility_action_move_top_right));
+ res.getString(R.string.bubble_accessibility_action_move_top_right));
info.addAction(moveTopRight);
AccessibilityAction moveBottomLeft = new AccessibilityAction(R.id.action_move_bottom_left,
- getContext().getResources()
- .getString(R.string.bubble_accessibility_action_move_bottom_left));
+ res.getString(R.string.bubble_accessibility_action_move_bottom_left));
info.addAction(moveBottomLeft);
AccessibilityAction moveBottomRight = new AccessibilityAction(R.id.action_move_bottom_right,
- getContext().getResources()
- .getString(R.string.bubble_accessibility_action_move_bottom_right));
+ res.getString(R.string.bubble_accessibility_action_move_bottom_right));
info.addAction(moveBottomRight);
// Default actions.
@@ -1343,7 +1343,10 @@ public class BubbleStackView extends FrameLayout
}
if (bubbleToSelect == null || bubbleToSelect.getKey() != BubbleOverflow.KEY) {
mBubbleData.setShowingOverflow(false);
+ } else {
+ mBubbleData.setShowingOverflow(true);
}
+
final BubbleViewProvider previouslySelected = mExpandedBubble;
mExpandedBubble = bubbleToSelect;
updatePointerPosition();
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/storage/BubblePersistentRepository.kt b/packages/SystemUI/src/com/android/systemui/bubbles/storage/BubblePersistentRepository.kt
index b2495c658948..149e2c4c4022 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/storage/BubblePersistentRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/storage/BubblePersistentRepository.kt
@@ -33,6 +33,7 @@ class BubblePersistentRepository @Inject constructor(
"overflow_bubbles.xml"), "overflow-bubbles")
fun persistsToDisk(bubbles: List<BubbleXmlEntity>): Boolean {
+ if (DEBUG) Log.d(TAG, "persisting ${bubbles.size} bubbles")
synchronized(bubbleFile) {
val stream: FileOutputStream = try { bubbleFile.startWrite() } catch (e: IOException) {
Log.e(TAG, "Failed to save bubble file", e)
@@ -41,6 +42,7 @@ class BubblePersistentRepository @Inject constructor(
try {
writeXml(stream, bubbles)
bubbleFile.finishWrite(stream)
+ if (DEBUG) Log.d(TAG, "persisted ${bubbles.size} bubbles")
return true
} catch (e: Exception) {
Log.e(TAG, "Failed to save bubble file, restoring backup", e)
@@ -49,6 +51,16 @@ class BubblePersistentRepository @Inject constructor(
}
return false
}
+
+ fun readFromDisk(): List<BubbleXmlEntity> {
+ synchronized(bubbleFile) {
+ try { return bubbleFile.openRead().use(::readXml) } catch (e: Throwable) {
+ Log.e(TAG, "Failed to open bubble file", e)
+ }
+ return emptyList()
+ }
+ }
}
private const val TAG = "BubblePersistentRepository"
+private const val DEBUG = false
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/storage/BubbleVolatileRepository.kt b/packages/SystemUI/src/com/android/systemui/bubbles/storage/BubbleVolatileRepository.kt
index 3dba26807485..e1f675b83583 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/storage/BubbleVolatileRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/storage/BubbleVolatileRepository.kt
@@ -39,12 +39,6 @@ class BubbleVolatileRepository @Inject constructor() {
get() = entities.toList()
/**
- * Add the bubble to memory and perform a de-duplication. In case the bubble already exists,
- * the bubble will be moved to the last.
- */
- fun addBubble(bubble: BubbleXmlEntity) = addBubbles(listOf(bubble))
-
- /**
* Add the bubbles to memory and perform a de-duplication. In case a bubble already exists,
* it will be moved to the last.
*/
@@ -55,7 +49,7 @@ class BubbleVolatileRepository @Inject constructor() {
if (entities.size + bubbles.size >= CAPACITY) {
entities.drop(entities.size + bubbles.size - CAPACITY)
}
- entities.addAll(bubbles.reversed())
+ entities.addAll(bubbles)
}
@Synchronized
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/storage/BubbleXmlHelper.kt b/packages/SystemUI/src/com/android/systemui/bubbles/storage/BubbleXmlHelper.kt
index 3192f34b69fd..1e91653c6db7 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/storage/BubbleXmlHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/storage/BubbleXmlHelper.kt
@@ -18,6 +18,10 @@ package com.android.systemui.bubbles.storage
import com.android.internal.util.FastXmlSerializer
import org.xmlpull.v1.XmlSerializer
import java.io.IOException
+import android.util.Xml
+import com.android.internal.util.XmlUtils
+import org.xmlpull.v1.XmlPullParser
+import java.io.InputStream
import java.io.OutputStream
import java.nio.charset.StandardCharsets
@@ -57,4 +61,35 @@ private fun writeXmlEntry(serializer: XmlSerializer, bubble: BubbleXmlEntity) {
} catch (e: IOException) {
throw RuntimeException(e)
}
+}
+
+/**
+ * Reads the bubbles from xml file.
+ */
+fun readXml(stream: InputStream): List<BubbleXmlEntity> {
+ val bubbles = mutableListOf<BubbleXmlEntity>()
+ val parser: XmlPullParser = Xml.newPullParser()
+ parser.setInput(stream, StandardCharsets.UTF_8.name())
+ XmlUtils.beginDocument(parser, TAG_BUBBLES)
+ val outerDepth = parser.depth
+ while (XmlUtils.nextElementWithin(parser, outerDepth)) {
+ bubbles.add(readXmlEntry(parser) ?: continue)
+ }
+ return bubbles
+}
+
+private fun readXmlEntry(parser: XmlPullParser): BubbleXmlEntity? {
+ while (parser.eventType != XmlPullParser.START_TAG) { parser.next() }
+ return BubbleXmlEntity(
+ parser.getAttributeWithName(ATTR_USER_ID)?.toInt() ?: return null,
+ parser.getAttributeWithName(ATTR_PACKAGE) ?: return null,
+ parser.getAttributeWithName(ATTR_SHORTCUT_ID) ?: return null
+ )
+}
+
+private fun XmlPullParser.getAttributeWithName(name: String): String? {
+ for (i in 0 until attributeCount) {
+ if (getAttributeName(i) == name) return getAttributeValue(i)
+ }
+ return null
} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
index 10776c91df84..e1081cd5ef82 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
@@ -41,6 +41,9 @@ import android.util.Log;
import androidx.annotation.VisibleForTesting;
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;
import com.android.systemui.plugins.SensorManagerPlugin;
import com.android.systemui.statusbar.phone.DozeParameters;
@@ -56,8 +59,8 @@ import java.util.function.Consumer;
public class DozeSensors {
private static final boolean DEBUG = DozeService.DEBUG;
-
private static final String TAG = "DozeSensors";
+ private static final UiEventLogger UI_EVENT_LOGGER = new UiEventLoggerImpl();
private final Context mContext;
private final AlarmManager mAlarmManager;
@@ -79,6 +82,23 @@ public class DozeSensors {
private boolean mListening;
private boolean mPaused;
+ @VisibleForTesting
+ public enum DozeSensorsUiEvent implements UiEventLogger.UiEventEnum {
+ @UiEvent(doc = "User performs pickup gesture that activates the ambient display")
+ ACTION_AMBIENT_GESTURE_PICKUP(459);
+
+ private final int mId;
+
+ DozeSensorsUiEvent(int id) {
+ mId = id;
+ }
+
+ @Override
+ public int getId() {
+ return mId;
+ }
+ }
+
public DozeSensors(Context context, AlarmManager alarmManager, AsyncSensorManager sensorManager,
DozeParameters dozeParameters, AmbientDisplayConfiguration config, WakeLock wakeLock,
Callback callback, Consumer<Boolean> proxCallback, DozeLog dozeLog) {
@@ -416,6 +436,7 @@ public class DozeSensors {
MetricsLogger.action(
mContext, MetricsProto.MetricsEvent.ACTION_AMBIENT_GESTURE,
subType);
+ UI_EVENT_LOGGER.log(DozeSensorsUiEvent.ACTION_AMBIENT_GESTURE_PICKUP);
}
mRegistered = false;
diff --git a/packages/SystemUI/src/com/android/systemui/media/IlluminationDrawable.kt b/packages/SystemUI/src/com/android/systemui/media/IlluminationDrawable.kt
new file mode 100644
index 000000000000..937472735bb0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/IlluminationDrawable.kt
@@ -0,0 +1,264 @@
+package com.android.systemui.media
+
+import android.animation.Animator
+import android.animation.AnimatorListenerAdapter
+import android.animation.AnimatorSet
+import android.animation.ValueAnimator
+import android.content.res.ColorStateList
+import android.content.res.Resources
+import android.graphics.Canvas
+import android.graphics.Color
+import android.graphics.ColorFilter
+import android.graphics.Paint
+import android.graphics.PixelFormat
+import android.graphics.RadialGradient
+import android.graphics.Rect
+import android.graphics.Shader
+import android.graphics.drawable.Drawable
+import android.util.AttributeSet
+import android.util.MathUtils
+import android.util.MathUtils.lerp
+import android.view.MotionEvent
+import android.view.View
+import androidx.annotation.Keep
+import com.android.internal.graphics.ColorUtils
+import com.android.internal.graphics.ColorUtils.blendARGB
+import com.android.systemui.Interpolators
+import com.android.systemui.R
+import org.xmlpull.v1.XmlPullParser
+
+private const val BACKGROUND_ANIM_DURATION = 370L
+private const val RIPPLE_ANIM_DURATION = 800L
+private const val RIPPLE_DOWN_PROGRESS = 0.05f
+private const val RIPPLE_CANCEL_DURATION = 200L
+private val GRADIENT_STOPS = floatArrayOf(0.2f, 1f)
+
+private data class RippleData(
+ var x: Float,
+ var y: Float,
+ var alpha: Float,
+ var progress: Float,
+ var minSize: Float,
+ var maxSize: Float,
+ var highlight: Float
+)
+
+/**
+ * Drawable that can draw an animated gradient when tapped.
+ */
+@Keep
+class IlluminationDrawable : Drawable() {
+
+ private var cornerRadius = 0f
+ private var highlightColor = Color.TRANSPARENT
+ private val rippleData = RippleData(0f, 0f, 0f, 0f, 0f, 0f, 0f)
+ private var tmpHsl = floatArrayOf(0f, 0f, 0f)
+ private var paint = Paint()
+
+ private var backgroundColor = Color.TRANSPARENT
+ set(value) {
+ if (value == field) {
+ return
+ }
+ field = value
+ animateBackground()
+ }
+
+ /**
+ * Draw a small highlight under the finger before expanding (or cancelling) it.
+ */
+ private var pressed: Boolean = false
+ set(value) {
+ if (value == field) {
+ return
+ }
+ field = value
+
+ if (value) {
+ rippleAnimation?.cancel()
+ rippleData.alpha = 1f
+ rippleData.progress = RIPPLE_DOWN_PROGRESS
+ } else {
+ rippleAnimation?.cancel()
+ rippleAnimation = ValueAnimator.ofFloat(rippleData.alpha, 0f).apply {
+ duration = RIPPLE_CANCEL_DURATION
+ interpolator = Interpolators.LINEAR_OUT_SLOW_IN
+ addUpdateListener {
+ rippleData.alpha = it.animatedValue as Float
+ invalidateSelf()
+ }
+ addListener(object : AnimatorListenerAdapter() {
+ var cancelled = false
+ override fun onAnimationCancel(animation: Animator?) {
+ cancelled = true;
+ }
+
+ override fun onAnimationEnd(animation: Animator?) {
+ if (cancelled) {
+ return
+ }
+ rippleData.progress = 0f
+ rippleData.alpha = 0f
+ rippleAnimation = null
+ invalidateSelf()
+ }
+ })
+ start()
+ }
+ }
+ invalidateSelf()
+ }
+
+ private var rippleAnimation: Animator? = null
+ private var backgroundAnimation: ValueAnimator? = null
+
+ /**
+ * Draw background and gradient.
+ */
+ override fun draw(canvas: Canvas) {
+ paint.shader = if (rippleData.progress > 0) {
+ val radius = lerp(rippleData.minSize, rippleData.maxSize, rippleData.progress)
+ val centerColor = blendARGB(paint.color, highlightColor, rippleData.alpha)
+ RadialGradient(rippleData.x, rippleData.y, radius, intArrayOf(centerColor, paint.color),
+ GRADIENT_STOPS, Shader.TileMode.CLAMP)
+ } else {
+ null
+ }
+ canvas.drawRoundRect(0f, 0f, bounds.width().toFloat(), bounds.height().toFloat(),
+ cornerRadius, cornerRadius, paint)
+ }
+
+ override fun getOpacity(): Int {
+ return PixelFormat.TRANSPARENT
+ }
+
+ override fun inflate(
+ r: Resources,
+ parser: XmlPullParser,
+ attrs: AttributeSet,
+ theme: Resources.Theme?
+ ) {
+ val a = obtainAttributes(r, theme, attrs, R.styleable.IlluminationDrawable)
+ cornerRadius = a.getDimension(R.styleable.IlluminationDrawable_cornerRadius, cornerRadius)
+ rippleData.minSize = a.getDimension(R.styleable.IlluminationDrawable_rippleMinSize, 0f)
+ rippleData.maxSize = a.getDimension(R.styleable.IlluminationDrawable_rippleMaxSize, 0f)
+ rippleData.highlight = a.getInteger(R.styleable.IlluminationDrawable_highlight, 0) / 100f
+ a.recycle()
+ }
+
+ override fun setColorFilter(p0: ColorFilter?) {
+ throw UnsupportedOperationException("Color filters are not supported")
+ }
+
+ override fun setAlpha(value: Int) {
+ throw UnsupportedOperationException("Alpha is not supported")
+ }
+
+ /**
+ * Cross fade background.
+ * @see setTintList
+ * @see backgroundColor
+ */
+ private fun animateBackground() {
+ ColorUtils.colorToHSL(backgroundColor, tmpHsl)
+ val L = tmpHsl[2]
+ tmpHsl[2] = MathUtils.constrain(if (L < 1f - rippleData.highlight) {
+ L + rippleData.highlight
+ } else {
+ L - rippleData.highlight
+ }, 0f, 1f)
+
+ val initialBackground = paint.color
+ val initialHighlight = highlightColor
+ val finalHighlight = ColorUtils.HSLToColor(tmpHsl)
+
+ backgroundAnimation?.cancel()
+ backgroundAnimation = ValueAnimator.ofFloat(0f, 1f).apply {
+ duration = BACKGROUND_ANIM_DURATION
+ interpolator = Interpolators.FAST_OUT_LINEAR_IN
+ addUpdateListener {
+ val progress = it.animatedValue as Float
+ paint.color = blendARGB(initialBackground, backgroundColor, progress)
+ highlightColor = blendARGB(initialHighlight, finalHighlight, progress)
+ invalidateSelf()
+ }
+ addListener(object : AnimatorListenerAdapter() {
+ override fun onAnimationEnd(animation: Animator?) {
+ backgroundAnimation = null
+ }
+ })
+ start()
+ }
+ }
+
+ override fun setTintList(tint: ColorStateList?) {
+ super.setTintList(tint)
+ backgroundColor = tint!!.defaultColor
+ }
+
+ /**
+ * Draws an animated ripple that expands fading away.
+ */
+ private fun illuminate() {
+ rippleData.alpha = 1f
+ invalidateSelf()
+
+ rippleAnimation?.cancel()
+ rippleAnimation = AnimatorSet().apply {
+ playTogether(ValueAnimator.ofFloat(1f, 0f).apply {
+ startDelay = 133
+ duration = RIPPLE_ANIM_DURATION - startDelay
+ interpolator = Interpolators.LINEAR_OUT_SLOW_IN
+ addUpdateListener {
+ rippleData.alpha = it.animatedValue as Float
+ invalidateSelf()
+ }
+ }, ValueAnimator.ofFloat(rippleData.progress, 1f).apply {
+ duration = RIPPLE_ANIM_DURATION
+ interpolator = Interpolators.LINEAR_OUT_SLOW_IN
+ addUpdateListener {
+ rippleData.progress = it.animatedValue as Float
+ invalidateSelf()
+ }
+ })
+ addListener(object : AnimatorListenerAdapter() {
+ override fun onAnimationEnd(animation: Animator?) {
+ rippleData.progress = 0f
+ rippleAnimation = null
+ invalidateSelf()
+ }
+ })
+ start()
+ }
+ }
+
+ /**
+ * Setup touch events on a view such as tapping it would trigger effects on this drawable.
+ * @param target View receiving touched.
+ * @param container View that holds this drawable.
+ */
+ fun setupTouch(target: View, container: View) {
+ val containerRect = Rect()
+ target.setOnTouchListener { view: View, event: MotionEvent ->
+ container.getGlobalVisibleRect(containerRect)
+ rippleData.x = event.rawX - containerRect.left
+ rippleData.y = event.rawY - containerRect.top
+
+ when (event.action) {
+ MotionEvent.ACTION_DOWN -> {
+ pressed = true
+ }
+ MotionEvent.ACTION_MOVE -> {
+ invalidateSelf()
+ }
+ MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
+ pressed = false
+ if (event.action == MotionEvent.ACTION_UP) {
+ illuminate()
+ }
+ }
+ }
+ false
+ }
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java b/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java
index f322489b8dc2..f72de11a01ed 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java
@@ -376,7 +376,8 @@ public class PipAnimationController {
// NOTE: intentionally does not apply the transaction here.
// this end transaction should get executed synchronously with the final
// WindowContainerTransaction in task organizer
- getSurfaceTransactionHelper().resetScale(tx, leash, getDestinationBounds());
+ getSurfaceTransactionHelper().resetScale(tx, leash, getDestinationBounds())
+ .crop(tx, leash, getDestinationBounds());
}
};
}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
index 8d6ce4718aef..7e2efc04ea8e 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
@@ -552,6 +552,9 @@ public class PipTaskOrganizer extends TaskOrganizer {
? null : destinationBounds;
// As for the final windowing mode, simply reset it to undefined.
wct.setWindowingMode(mToken, WINDOWING_MODE_UNDEFINED);
+ if (mSplitDivider != null && direction == TRANSITION_DIRECTION_TO_SPLIT_SCREEN) {
+ wct.reparent(mToken, mSplitDivider.getSecondaryRoot(), true /* onTop */);
+ }
} else {
taskBounds = destinationBounds;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java b/packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java
index e76cd5116818..174441bdf065 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java
@@ -22,6 +22,7 @@ import android.app.PendingIntent;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.res.ColorStateList;
+import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import android.media.MediaDescription;
@@ -38,6 +39,7 @@ import android.widget.TextView;
import com.android.settingslib.media.LocalMediaManager;
import com.android.systemui.R;
+import com.android.systemui.media.IlluminationDrawable;
import com.android.systemui.media.MediaControlPanel;
import com.android.systemui.media.SeekBarObserver;
import com.android.systemui.media.SeekBarViewModel;
@@ -173,7 +175,7 @@ public class QSMediaPlayer extends MediaControlPanel {
LinearLayout parentActionsLayout = (LinearLayout) actionsContainer;
int i = 0;
for (; i < parentActionsLayout.getChildCount() && i < QS_ACTION_IDS.length; i++) {
- ImageButton thisBtn = mMediaNotifView.findViewById(QS_ACTION_IDS[i]);
+ final ImageButton thisBtn = mMediaNotifView.findViewById(QS_ACTION_IDS[i]);
ImageButton thatBtn = parentActionsLayout.findViewById(NOTIF_ACTION_IDS[i]);
if (thatBtn == null || thatBtn.getDrawable() == null
|| thatBtn.getVisibility() != View.VISIBLE) {
@@ -181,6 +183,11 @@ public class QSMediaPlayer extends MediaControlPanel {
continue;
}
+ if (mMediaNotifView.getBackground() instanceof IlluminationDrawable) {
+ ((IlluminationDrawable) mMediaNotifView.getBackground())
+ .setupTouch(thisBtn, mMediaNotifView);
+ }
+
Drawable thatIcon = thatBtn.getDrawable();
thisBtn.setImageDrawable(thatIcon.mutate());
thisBtn.setVisibility(View.VISIBLE);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSMediaPlayer.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSMediaPlayer.java
index f77ff8cd7949..5cb75e60e22a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSMediaPlayer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSMediaPlayer.java
@@ -28,6 +28,7 @@ import android.widget.ImageButton;
import android.widget.LinearLayout;
import com.android.systemui.R;
+import com.android.systemui.media.IlluminationDrawable;
import com.android.systemui.media.MediaControlPanel;
import com.android.systemui.plugins.ActivityStarter;
@@ -104,6 +105,11 @@ public class QuickQSMediaPlayer extends MediaControlPanel {
continue;
}
+ if (mMediaNotifView.getBackground() instanceof IlluminationDrawable) {
+ ((IlluminationDrawable) mMediaNotifView.getBackground())
+ .setupTouch(thisBtn, mMediaNotifView);
+ }
+
Drawable thatIcon = thatBtn.getDrawable();
thisBtn.setImageDrawable(thatIcon.mutate());
thisBtn.setVisibility(View.VISIBLE);
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
index e67b3d715c84..02a7aca38abe 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
@@ -813,4 +813,12 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks,
updateVisibility(true /* visible */);
}
}
+
+ /** @return the container token for the secondary split root task. */
+ public WindowContainerToken getSecondaryRoot() {
+ if (mSplits == null || mSplits.mSecondary == null) {
+ return null;
+ }
+ return mSplits.mSecondary.token;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java
index 55a20fae4ffd..040dbe320711 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java
@@ -288,7 +288,7 @@ public class InstantAppNotifier extends SystemUI
mContext,
0,
new Intent(Intent.ACTION_VIEW).setData(Uri.parse(helpUrl)),
- 0,
+ PendingIntent.FLAG_IMMUTABLE,
null,
user)
: null;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt
index 9738bcc69279..c78370ec4643 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt
@@ -27,20 +27,17 @@ import com.android.systemui.statusbar.notification.NotificationFilter
import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager
import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier
-import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_IMPORTANT_PERSON
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_NON_PERSON
-import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_PERSON
import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_ALERTING
import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_HEADS_UP
import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_PEOPLE
import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_SILENT
-
+import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.PriorityBucket
import com.android.systemui.statusbar.phone.NotificationGroupManager
import com.android.systemui.statusbar.policy.HeadsUpManager
import dagger.Lazy
-import java.util.Objects;
+import java.util.Objects
import javax.inject.Inject
-import kotlin.Comparator
private const val TAG = "NotifRankingManager"
@@ -140,33 +137,36 @@ open class NotificationRankingManager @Inject constructor(
.filterNot(notifFilter::shouldFilterOut)
.sortedWith(rankingComparator)
.toList()
- for (entry in filtered) {
- assignBucketForEntry(entry)
- }
+ assignBuckets(filtered)
return filtered
}
- private fun assignBucketForEntry(entry: NotificationEntry) {
+ private fun assignBuckets(entries: List<NotificationEntry>) {
+ entries.forEach { it.bucket = getBucketForEntry(it) }
+ if (!usePeopleFiltering) {
+ // If we don't have a Conversation section, just assign buckets normally based on the
+ // content.
+ return
+ }
+ // If HUNs are not continuous with the top section, break out into a new Incoming section.
+ entries.asReversed().asSequence().zipWithNext().forEach { (next, entry) ->
+ if (entry.isRowHeadsUp && entry.bucket > next.bucket) {
+ entry.bucket = BUCKET_HEADS_UP
+ }
+ }
+ }
+
+ @PriorityBucket
+ private fun getBucketForEntry(entry: NotificationEntry): Int {
val isHeadsUp = entry.isRowHeadsUp
val isMedia = isImportantMedia(entry)
val isSystemMax = entry.isSystemMax()
- setBucket(entry, isHeadsUp, isMedia, isSystemMax)
- }
-
- private fun setBucket(
- entry: NotificationEntry,
- isHeadsUp: Boolean,
- isMedia: Boolean,
- isSystemMax: Boolean
- ) {
- if (usePeopleFiltering && isHeadsUp) {
- entry.bucket = BUCKET_HEADS_UP
- } else if (usePeopleFiltering && entry.getPeopleNotificationType() != TYPE_NON_PERSON) {
- entry.bucket = BUCKET_PEOPLE
- } else if (isHeadsUp || isMedia || isSystemMax || entry.isHighPriority()) {
- entry.bucket = BUCKET_ALERTING
- } else {
- entry.bucket = BUCKET_SILENT
+ return when {
+ usePeopleFiltering && entry.getPeopleNotificationType() != TYPE_NON_PERSON ->
+ BUCKET_PEOPLE
+ isHeadsUp || isMedia || isSystemMax || entry.isHighPriority() ->
+ BUCKET_ALERTING
+ else -> BUCKET_SILENT
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/TargetSdkResolver.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/TargetSdkResolver.kt
index 1c1b2bb087f0..a0f9dc91ce68 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/TargetSdkResolver.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/TargetSdkResolver.kt
@@ -28,10 +28,9 @@ import javax.inject.Singleton
@Singleton
class TargetSdkResolver @Inject constructor(
- private val context: Context,
- private val collection: CommonNotifCollection
+ private val context: Context
) {
- init {
+ fun initialize(collection: CommonNotifCollection) {
collection.addCollectionListener(object : NotifCollectionListener {
override fun onEntryBind(entry: NotificationEntry, sbn: StatusBarNotification) {
entry.targetSdk = resolveNotificationSdk(sbn)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
index c9754048e1d1..6e4fcd5f97b1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
@@ -25,8 +25,10 @@ import com.android.systemui.statusbar.notification.NotificationActivityStarter
import com.android.systemui.statusbar.notification.NotificationClicker
import com.android.systemui.statusbar.notification.NotificationEntryManager
import com.android.systemui.statusbar.notification.NotificationListController
+import com.android.systemui.statusbar.notification.collection.NotifPipeline
import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl
import com.android.systemui.statusbar.notification.collection.init.NotifPipelineInitializer
+import com.android.systemui.statusbar.notification.collection.TargetSdkResolver
import com.android.systemui.statusbar.notification.interruption.HeadsUpController
import com.android.systemui.statusbar.notification.row.NotifBindPipelineInitializer
import com.android.systemui.statusbar.notification.stack.NotificationListContainer
@@ -56,6 +58,8 @@ class NotificationsControllerImpl @Inject constructor(
private val featureFlags: FeatureFlags,
private val notificationListener: NotificationListener,
private val entryManager: NotificationEntryManager,
+ private val notifPipeline: Lazy<NotifPipeline>,
+ private val targetSdkResolver: TargetSdkResolver,
private val newNotifPipeline: Lazy<NotifPipelineInitializer>,
private val notifBindPipelineInitializer: NotifBindPipelineInitializer,
private val deviceProvisionedController: DeviceProvisionedController,
@@ -102,8 +106,10 @@ class NotificationsControllerImpl @Inject constructor(
}
if (featureFlags.isNewNotifPipelineRenderingEnabled) {
+ targetSdkResolver.initialize(notifPipeline.get())
// TODO
} else {
+ targetSdkResolver.initialize(entryManager)
remoteInputUriController.attach(entryManager)
groupAlertTransferHelper.bind(entryManager, groupManager)
headsUpManager.addListener(groupManager)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/BindStage.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/BindStage.java
index 29447caa1240..cc917dda2a80 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/BindStage.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/BindStage.java
@@ -18,6 +18,7 @@ package com.android.systemui.statusbar.notification.row;
import android.annotation.MainThread;
import android.util.ArrayMap;
+import android.util.Log;
import androidx.annotation.NonNull;
@@ -66,8 +67,10 @@ public abstract class BindStage<Params> extends BindRequester {
public final Params getStageParams(@NonNull NotificationEntry entry) {
Params params = mContentParams.get(entry);
if (params == null) {
- throw new IllegalStateException(
- String.format("Entry does not have any stage parameters. key: %s",
+ // TODO: This should throw an exception but there are some cases of re-entrant calls
+ // in NotificationEntryManager (e.g. b/155324756) that cause removal in update that
+ // lead to inflation after the notification is "removed".
+ Log.wtf(TAG, String.format("Entry does not have any stage parameters. key: %s",
entry.getKey()));
}
return params;
@@ -92,6 +95,8 @@ public abstract class BindStage<Params> extends BindRequester {
*/
protected abstract Params newStageParams();
+ private static final String TAG = "BindStage";
+
/**
* Interface for callback.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 2e5af9a7414a..f7ad50edb2f6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -2366,6 +2366,11 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
updateChildrenVisibility();
applyChildrenRoundness();
}
+
+ protected void expandNotification() {
+ mExpandClickListener.onClick(this);
+ }
+
/**
* Returns the number of channels covered by the notification row (including its children if
* it's a summary notification).
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
index f8d9c4648ac9..7a6109d2ce78 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
@@ -130,7 +130,13 @@ public class ExpandableNotificationRowController {
mView.setOnDismissRunnable(mOnDismissRunnable);
mView.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
if (mAllowLongPress) {
- mView.setLongPressListener(mNotificationGutsManager::openGuts);
+ mView.setLongPressListener((v, x, y, item) -> {
+ if (mView.isSummaryWithChildren()) {
+ mView.expandNotification();
+ return true;
+ }
+ return mNotificationGutsManager.openGuts(v, x, y, item);
+ });
}
if (ENABLE_REMOTE_INPUT) {
mView.setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipeline.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipeline.java
index e4e3ebcf7671..3ee267362bc0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipeline.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipeline.java
@@ -115,6 +115,9 @@ public final class NotifBindPipeline {
mLogger.logManagedRow(entry.getKey());
final BindEntry bindEntry = getBindEntry(entry);
+ if (bindEntry == null) {
+ return;
+ }
bindEntry.row = row;
if (bindEntry.invalidated) {
requestPipelineRun(entry);
@@ -223,11 +226,6 @@ public final class NotifBindPipeline {
private @NonNull BindEntry getBindEntry(NotificationEntry entry) {
final BindEntry bindEntry = mBindEntries.get(entry);
- if (bindEntry == null) {
- throw new IllegalStateException(
- String.format("Attempting bind on an inactive notification. key: %s",
- entry.getKey()));
- }
return bindEntry;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java
index d02037cf61fd..6eec1ca33e14 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java
@@ -109,6 +109,7 @@ public class NotificationSectionsManager implements StackScrollAlgorithm.Section
@Nullable private View.OnClickListener mOnClearGentleNotifsClickListener;
private SectionHeaderView mAlertingHeader;
+ private SectionHeaderView mIncomingHeader;
private PeopleHubView mPeopleHubView;
private boolean mPeopleHubVisible = false;
@@ -199,6 +200,11 @@ public class NotificationSectionsManager implements StackScrollAlgorithm.Section
mPeopleHubSubscription = mPeopleHubViewAdapter.bindView(mPeopleHubViewBoundary);
}
+ mIncomingHeader = reinflateView(
+ mIncomingHeader, layoutInflater, R.layout.status_bar_notification_section_header);
+ mIncomingHeader.setHeaderText(R.string.notification_section_header_incoming);
+ mIncomingHeader.setOnHeaderClickListener(this::onGentleHeaderClick);
+
if (mMediaControlsView != null) {
mKeyguardMediaPlayer.unbindView();
}
@@ -218,6 +224,7 @@ public class NotificationSectionsManager implements StackScrollAlgorithm.Section
|| view == mMediaControlsView
|| view == mPeopleHubView
|| view == mAlertingHeader
+ || view == mIncomingHeader
|| !Objects.equals(getBucket(view), getBucket(previous));
}
@@ -229,6 +236,8 @@ public class NotificationSectionsManager implements StackScrollAlgorithm.Section
private Integer getBucket(View view) {
if (view == mGentleHeader) {
return BUCKET_SILENT;
+ } else if (view == mIncomingHeader) {
+ return BUCKET_HEADS_UP;
} else if (view == mMediaControlsView) {
return BUCKET_MEDIA_CONTROLS;
} else if (view == mPeopleHubView) {
@@ -267,6 +276,8 @@ public class NotificationSectionsManager implements StackScrollAlgorithm.Section
// Currently, just putting media controls in the front and incrementing the position based
// on the number of heads-up notifs.
int mediaControlsTarget = isKeyguard && usingMediaControls ? 0 : -1;
+ int currentIncomingHeaderIdx = -1;
+ int incomingHeaderTarget = -1;
int currentPeopleHeaderIdx = -1;
int peopleHeaderTarget = -1;
int currentAlertingHeaderIdx = -1;
@@ -281,6 +292,10 @@ public class NotificationSectionsManager implements StackScrollAlgorithm.Section
View child = mParent.getChildAt(i);
// Track the existing positions of the headers
+ if (child == mIncomingHeader) {
+ currentIncomingHeaderIdx = i;
+ continue;
+ }
if (child == mMediaControlsView) {
currentMediaControlsIdx = i;
continue;
@@ -306,6 +321,26 @@ public class NotificationSectionsManager implements StackScrollAlgorithm.Section
// Once we enter a new section, calculate the target position for the header.
switch (row.getEntry().getBucket()) {
case BUCKET_HEADS_UP:
+ if (showHeaders && incomingHeaderTarget == -1) {
+ incomingHeaderTarget = i;
+ // Offset the target if there are other headers before this that will be
+ // moved.
+ if (currentIncomingHeaderIdx != -1) {
+ incomingHeaderTarget--;
+ }
+ if (currentMediaControlsIdx != -1) {
+ incomingHeaderTarget--;
+ }
+ if (currentPeopleHeaderIdx != -1) {
+ incomingHeaderTarget--;
+ }
+ if (currentAlertingHeaderIdx != -1) {
+ incomingHeaderTarget--;
+ }
+ if (currentGentleHeaderIdx != -1) {
+ incomingHeaderTarget--;
+ }
+ }
if (mediaControlsTarget != -1) {
mediaControlsTarget++;
}
@@ -378,8 +413,8 @@ public class NotificationSectionsManager implements StackScrollAlgorithm.Section
alertingHeaderTarget, mAlertingHeader, currentAlertingHeaderIdx);
adjustHeaderVisibilityAndPosition(
peopleHeaderTarget, mPeopleHubView, currentPeopleHeaderIdx);
- adjustViewPosition(
- mediaControlsTarget, mMediaControlsView, currentMediaControlsIdx);
+ adjustViewPosition(mediaControlsTarget, mMediaControlsView, currentMediaControlsIdx);
+ adjustViewPosition(incomingHeaderTarget, mIncomingHeader, currentIncomingHeaderIdx);
// Update headers to reflect state of section contents
mGentleHeader.setAreThereDismissableGentleNotifs(
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 7093dd8b39e3..7f32c004808a 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
@@ -54,6 +54,7 @@ import android.graphics.Rect;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.ServiceManager;
+import android.os.UserHandle;
import android.provider.Settings;
import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
@@ -756,8 +757,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
boolean showFooterView = (showDismissView || hasActiveNotifications())
&& mStatusBarState != StatusBarState.KEYGUARD
&& !mRemoteInputManager.getController().isRemoteInputActive();
- boolean showHistory = Settings.Secure.getInt(mContext.getContentResolver(),
- Settings.Secure.NOTIFICATION_HISTORY_ENABLED, 0) == 1;
+ boolean showHistory = Settings.Secure.getIntForUser(mContext.getContentResolver(),
+ Settings.Secure.NOTIFICATION_HISTORY_ENABLED, 0, UserHandle.USER_CURRENT) == 1;
updateFooterView(showFooterView, showDismissView, showHistory);
}
@@ -5730,8 +5731,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
R.layout.status_bar_no_notifications, this, false);
view.setText(R.string.empty_shade_text);
view.setOnClickListener(v -> {
- final boolean showHistory = Settings.Secure.getInt(mContext.getContentResolver(),
- Settings.Secure.NOTIFICATION_HISTORY_ENABLED, 0) == 1;
+ final boolean showHistory = Settings.Secure.getIntForUser(mContext.getContentResolver(),
+ Settings.Secure.NOTIFICATION_HISTORY_ENABLED, 0, UserHandle.USER_CURRENT) == 1;
Intent intent = showHistory ? new Intent(
Settings.ACTION_NOTIFICATION_HISTORY) : new Intent(
Settings.ACTION_NOTIFICATION_SETTINGS);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowController.java
index fc6a02840891..567ddb680848 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowController.java
@@ -321,7 +321,8 @@ public class NotificationShadeWindowController implements Callback, Dumpable,
|| state.mPanelVisible || state.mKeyguardFadingAway || state.mBouncerShowing
|| state.mHeadsUpShowing
|| state.mScrimsVisibility != ScrimController.TRANSPARENT)
- || state.mBackgroundBlurRadius > 0;
+ || state.mBackgroundBlurRadius > 0
+ || state.mLaunchingActivity;
}
private void applyFitsSystemWindows(State state) {
@@ -485,6 +486,11 @@ public class NotificationShadeWindowController implements Callback, Dumpable,
apply(mCurrentState);
}
+ void setLaunchingActivity(boolean launching) {
+ mCurrentState.mLaunchingActivity = launching;
+ apply(mCurrentState);
+ }
+
public void setScrimsVisibility(int scrimsVisibility) {
mCurrentState.mScrimsVisibility = scrimsVisibility;
apply(mCurrentState);
@@ -645,6 +651,7 @@ public class NotificationShadeWindowController implements Callback, Dumpable,
boolean mForceCollapsed;
boolean mForceDozeBrightness;
boolean mForceUserActivity;
+ boolean mLaunchingActivity;
boolean mBackdropShowing;
boolean mWallpaperSupportsAmbientMode;
boolean mNotTouchable;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java
index 596a607bb8ad..0d2589847bcb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java
@@ -93,6 +93,7 @@ public class NotificationShadeWindowViewController {
private PhoneStatusBarView mStatusBarView;
private PhoneStatusBarTransitions mBarTransitions;
private StatusBar mService;
+ private NotificationShadeWindowController mNotificationShadeWindowController;
private DragDownHelper mDragDownHelper;
private boolean mDoubleTapEnabled;
private boolean mSingleTapEnabled;
@@ -430,10 +431,14 @@ public class NotificationShadeWindowViewController {
public void setExpandAnimationPending(boolean pending) {
mExpandAnimationPending = pending;
+ mNotificationShadeWindowController
+ .setLaunchingActivity(mExpandAnimationPending | mExpandAnimationRunning);
}
public void setExpandAnimationRunning(boolean running) {
mExpandAnimationRunning = running;
+ mNotificationShadeWindowController
+ .setLaunchingActivity(mExpandAnimationPending | mExpandAnimationRunning);
}
public void cancelExpandHelper() {
@@ -456,8 +461,9 @@ public class NotificationShadeWindowViewController {
}
}
- public void setService(StatusBar statusBar) {
+ public void setService(StatusBar statusBar, NotificationShadeWindowController controller) {
mService = statusBar;
+ mNotificationShadeWindowController = controller;
}
@VisibleForTesting
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index c5901398a54b..33997b9a5735 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -440,24 +440,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo
if (!(relevantState && mExpansionAffectsAlpha)) {
return;
}
- applyExpansionToAlpha();
- if (mUpdatePending) {
- return;
- }
- setOrAdaptCurrentAnimation(mScrimBehind);
- setOrAdaptCurrentAnimation(mScrimInFront);
- setOrAdaptCurrentAnimation(mScrimForBubble);
- dispatchScrimState(mScrimBehind.getViewAlpha());
-
- // Reset wallpaper timeout if it's already timeout like expanding panel while PULSING
- // and docking.
- if (mWallpaperVisibilityTimedOut) {
- mWallpaperVisibilityTimedOut = false;
- DejankUtils.postAfterTraversal(() -> {
- mTimeTicker.schedule(mDozeParameters.getWallpaperAodDuration(),
- AlarmTimeout.MODE_IGNORE_IF_SCHEDULED);
- });
- }
+ applyAndDispatchExpansion();
}
}
@@ -513,6 +496,27 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo
}
}
+ private void applyAndDispatchExpansion() {
+ applyExpansionToAlpha();
+ if (mUpdatePending) {
+ return;
+ }
+ setOrAdaptCurrentAnimation(mScrimBehind);
+ setOrAdaptCurrentAnimation(mScrimInFront);
+ setOrAdaptCurrentAnimation(mScrimForBubble);
+ dispatchScrimState(mScrimBehind.getViewAlpha());
+
+ // Reset wallpaper timeout if it's already timeout like expanding panel while PULSING
+ // and docking.
+ if (mWallpaperVisibilityTimedOut) {
+ mWallpaperVisibilityTimedOut = false;
+ DejankUtils.postAfterTraversal(() -> {
+ mTimeTicker.schedule(mDozeParameters.getWallpaperAodDuration(),
+ AlarmTimeout.MODE_IGNORE_IF_SCHEDULED);
+ });
+ }
+ }
+
/**
* Sets the given drawable as the background of the scrim that shows up behind the
* notifications.
@@ -1006,6 +1010,9 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo
public void setExpansionAffectsAlpha(boolean expansionAffectsAlpha) {
mExpansionAffectsAlpha = expansionAffectsAlpha;
+ if (expansionAffectsAlpha) {
+ applyAndDispatchExpansion();
+ }
}
public void setKeyguardOccluded(boolean keyguardOccluded) {
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 bbf83bc2057a..dd54a3d800fb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -1001,7 +1001,7 @@ public class StatusBar extends SystemUI implements DemoMode,
updateTheme();
inflateStatusBarWindow();
- mNotificationShadeWindowViewController.setService(this);
+ mNotificationShadeWindowViewController.setService(this, mNotificationShadeWindowController);
mNotificationShadeWindowView.setOnTouchListener(getStatusBarWindowTouchListener());
// TODO: Deal with the ugliness that comes from having some of the statusbar broken out
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
index d40b5f9728dd..1df617d68374 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
@@ -483,7 +483,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
if (showHistory) {
tsb.addNextIntent(intent);
}
- tsb.startActivities();
+ tsb.startActivities(null, UserHandle.CURRENT);
if (shouldCollapse()) {
// Putting it back on the main thread, since we're touching views
mMainThreadHandler.post(() -> mCommandQueue.animateCollapsePanels(
diff --git a/packages/SystemUI/src/com/android/systemui/wm/SystemWindows.java b/packages/SystemUI/src/com/android/systemui/wm/SystemWindows.java
index 64cac84ff24e..93f45c51f0eb 100644
--- a/packages/SystemUI/src/com/android/systemui/wm/SystemWindows.java
+++ b/packages/SystemUI/src/com/android/systemui/wm/SystemWindows.java
@@ -189,8 +189,8 @@ public class SystemWindows {
public SurfaceControl getViewSurface(View rootView) {
for (int i = 0; i < mPerDisplay.size(); ++i) {
for (int iWm = 0; iWm < mPerDisplay.valueAt(i).mWwms.size(); ++iWm) {
- SurfaceControl out =
- mPerDisplay.valueAt(i).mWwms.get(iWm).getSurfaceControlForWindow(rootView);
+ SurfaceControl out = mPerDisplay.valueAt(i).mWwms.valueAt(iWm)
+ .getSurfaceControlForWindow(rootView);
if (out != null) {
return out;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java
index 9d35e53e7421..128d6e5612f1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java
@@ -25,6 +25,8 @@ import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.content.Context;
+import android.os.UserManager;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
@@ -37,6 +39,7 @@ import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.logging.testing.UiEventLoggerFake;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
+import com.android.systemui.Dependency;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dump.DumpManager;
@@ -46,6 +49,7 @@ import com.android.systemui.qs.customize.QSCustomizer;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.policy.SecurityController;
import com.android.systemui.util.concurrency.DelayableExecutor;
import org.junit.Before;
@@ -100,8 +104,14 @@ public class QSPanelTest extends SysuiTestCase {
@Before
public void setup() throws Exception {
MockitoAnnotations.initMocks(this);
-
mTestableLooper = TestableLooper.get(this);
+
+ // Dependencies for QSSecurityFooter
+ mDependency.injectTestDependency(ActivityStarter.class, mActivityStarter);
+ mDependency.injectMockDependency(SecurityController.class);
+ mDependency.injectTestDependency(Dependency.BG_LOOPER, mTestableLooper.getLooper());
+ mContext.addMockSystemService(Context.USER_SERVICE, mock(UserManager.class));
+
mUiEventLogger = new UiEventLoggerFake();
mTestableLooper.runWithLooper(() -> {
mMetricsLogger = mDependency.injectMockDependency(MetricsLogger.class);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
index 72a65034922c..53ef86660867 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
@@ -23,10 +23,10 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThat;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
@@ -41,6 +41,7 @@ import android.content.pm.ServiceInfo;
import android.provider.Settings;
import android.service.quicksettings.Tile;
import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
import android.text.TextUtils;
import android.util.ArraySet;
@@ -50,10 +51,6 @@ import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.qs.QSTileHost;
-import com.android.systemui.qs.logging.QSLogger;
-import com.android.systemui.qs.tiles.HotspotTile;
-import com.android.systemui.statusbar.policy.DataSaverController;
-import com.android.systemui.statusbar.policy.HotspotController;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.time.FakeSystemClock;
@@ -63,6 +60,7 @@ import org.junit.runner.RunWith;
import org.mockito.Answers;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
+import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -73,6 +71,7 @@ import java.util.Set;
@SmallTest
@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
public class TileQueryHelperTest extends SysuiTestCase {
private static final String CURRENT_TILES = "wifi,dnd,nfc";
private static final String ONLY_STOCK_TILES = "wifi,dnd";
@@ -99,8 +98,6 @@ public class TileQueryHelperTest extends SysuiTestCase {
private QSTileHost mQSTileHost;
@Mock
private PackageManager mPackageManager;
- @Mock
- private QSLogger mQSLogger;
@Captor
private ArgumentCaptor<List<TileQueryHelper.TileInfo>> mCaptor;
@@ -112,8 +109,8 @@ public class TileQueryHelperTest extends SysuiTestCase {
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
+
mContext.setMockPackageManager(mPackageManager);
- when(mQSTileHost.getQSLogger()).thenReturn(mQSLogger);
mState = new QSTile.State();
doAnswer(invocation -> {
@@ -279,11 +276,10 @@ public class TileQueryHelperTest extends SysuiTestCase {
}
@Test
- public void testQueryTiles_notAvailableDestroyed_isNotNullSpec() {
- HotspotController mockHC = mock(HotspotController.class);
- DataSaverController mockDSC = mock(DataSaverController.class);
- when(mockHC.isHotspotSupported()).thenReturn(false);
- HotspotTile t = new HotspotTile(mQSTileHost, mockHC, mockDSC);
+ public void testQueryTiles_notAvailableDestroyed_tileSpecIsSet() {
+ Settings.Secure.putString(mContext.getContentResolver(), Settings.Secure.QS_TILES, null);
+
+ QSTile t = mock(QSTile.class);
when(mQSTileHost.createTile("hotspot")).thenReturn(t);
mContext.getOrCreateTestableResources().addOverride(R.string.quick_settings_tiles_stock,
@@ -292,6 +288,8 @@ public class TileQueryHelperTest extends SysuiTestCase {
mTileQueryHelper.queryTiles(mQSTileHost);
FakeExecutor.exhaustExecutors(mMainExecutor, mBgExecutor);
- verify(mQSLogger).logTileDestroyed(eq("hotspot"), anyString());
+ InOrder verifier = inOrder(t);
+ verifier.verify(t).setTileSpec("hotspot");
+ verifier.verify(t).destroy();
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index 1cb4d898f9d2..c4bd1b281b24 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -37,6 +37,7 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.metrics.LogMaker;
+import android.os.UserHandle;
import android.provider.Settings;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -152,7 +153,8 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
NOTIFICATION_NEW_INTERRUPTION_MODEL, 0);
Settings.Secure.putInt(mContext.getContentResolver(),
NOTIFICATION_NEW_INTERRUPTION_MODEL, 1);
- Settings.Secure.putInt(mContext.getContentResolver(), NOTIFICATION_HISTORY_ENABLED, 1);
+ Settings.Secure.putIntForUser(mContext.getContentResolver(), NOTIFICATION_HISTORY_ENABLED,
+ 1, UserHandle.USER_CURRENT);
// Inject dependencies before initializing the layout
mDependency.injectMockDependency(VisualStabilityManager.class);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java
index cc2d1c25de38..e04d25b17c71 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java
@@ -83,6 +83,7 @@ public class NotificationShadeWindowViewTest extends SysuiTestCase {
@Mock private NotificationStackScrollLayout mNotificationStackScrollLayout;
@Mock private NotificationShadeDepthController mNotificationShadeDepthController;
@Mock private SuperStatusBarViewFactory mStatusBarViewFactory;
+ @Mock private NotificationShadeWindowController mNotificationShadeWindowController;
@Before
public void setUp() {
@@ -121,7 +122,7 @@ public class NotificationShadeWindowViewTest extends SysuiTestCase {
mNotificationPanelViewController,
mStatusBarViewFactory);
mController.setupExpandedStatusBar();
- mController.setService(mStatusBar);
+ mController.setService(mStatusBar, mNotificationShadeWindowController);
mController.setDragDownHelper(mDragDownHelper);
}
diff --git a/packages/Tethering/Android.bp b/packages/Tethering/Android.bp
index bfb65241ec6d..cbc5e14139ac 100644
--- a/packages/Tethering/Android.bp
+++ b/packages/Tethering/Android.bp
@@ -27,7 +27,7 @@ java_defaults {
"androidx.annotation_annotation",
"netd_aidl_interface-V3-java",
"netlink-client",
- "networkstack-aidl-interfaces-unstable-java",
+ "networkstack-aidl-interfaces-java",
"android.hardware.tetheroffload.config-V1.0-java",
"android.hardware.tetheroffload.control-V1.0-java",
"net-utils-framework-common",
@@ -109,6 +109,7 @@ android_app {
manifest: "AndroidManifest_InProcess.xml",
// InProcessTethering is a replacement for Tethering
overrides: ["Tethering"],
+ apex_available: ["com.android.tethering"],
}
// Updatable tethering packaged as an application
diff --git a/packages/Tethering/apex/Android.bp b/packages/Tethering/apex/Android.bp
index 24df5f696077..20ccd2ad6453 100644
--- a/packages/Tethering/apex/Android.bp
+++ b/packages/Tethering/apex/Android.bp
@@ -36,3 +36,12 @@ android_app_certificate {
name: "com.android.tethering.certificate",
certificate: "com.android.tethering",
}
+
+override_apex {
+ name: "com.android.tethering.inprocess",
+ base: "com.android.tethering",
+ package_name: "com.android.tethering.inprocess",
+ apps: [
+ "InProcessTethering",
+ ],
+}
diff --git a/packages/Tethering/common/TetheringLib/Android.bp b/packages/Tethering/common/TetheringLib/Android.bp
index 8ae1593949f1..d029d2bded79 100644
--- a/packages/Tethering/common/TetheringLib/Android.bp
+++ b/packages/Tethering/common/TetheringLib/Android.bp
@@ -94,6 +94,15 @@ droidstubs {
"framework-module-stubs-defaults-publicapi",
"framework-tethering-stubs-defaults",
],
+ check_api: {
+ last_released: {
+ api_file: ":framework-tethering.api.public.latest",
+ removed_api_file: ":framework-tethering-removed.api.public.latest",
+ },
+ api_lint: {
+ new_since: ":framework-tethering.api.public.latest",
+ },
+ },
}
droidstubs {
@@ -102,6 +111,15 @@ droidstubs {
"framework-module-stubs-defaults-systemapi",
"framework-tethering-stubs-defaults",
],
+ check_api: {
+ last_released: {
+ api_file: ":framework-tethering.api.system.latest",
+ removed_api_file: ":framework-tethering-removed.api.system.latest",
+ },
+ api_lint: {
+ new_since: ":framework-tethering.api.system.latest",
+ },
+ },
}
droidstubs {
@@ -110,6 +128,15 @@ droidstubs {
"framework-module-api-defaults-module_libs_api",
"framework-tethering-stubs-defaults",
],
+ check_api: {
+ last_released: {
+ api_file: ":framework-tethering.api.module-lib.latest",
+ removed_api_file: ":framework-tethering-removed.api.module-lib.latest",
+ },
+ api_lint: {
+ new_since: ":framework-tethering.api.module-lib.latest",
+ },
+ },
}
droidstubs {
diff --git a/packages/Tethering/res/values/config.xml b/packages/Tethering/res/values/config.xml
index 83c99d22fda7..9dda7166a293 100644
--- a/packages/Tethering/res/values/config.xml
+++ b/packages/Tethering/res/values/config.xml
@@ -64,6 +64,13 @@
<string-array translatable="false" name="config_tether_dhcp_range">
</string-array>
+ <!-- Used to config periodic polls tether offload stats from tethering offload HAL to make the
+ data warnings work. 5000(ms) by default. If the device doesn't want to poll tether
+ offload stats, this should be -1. Note that this setting could be override by
+ runtime resource overlays.
+ -->
+ <integer translatable="false" name="config_tether_offload_poll_interval">5000</integer>
+
<!-- Array of ConnectivityManager.TYPE_{BLUETOOTH, ETHERNET, MOBILE, MOBILE_DUN, MOBILE_HIPRI,
WIFI} values allowable for tethering.
diff --git a/packages/Tethering/res/values/overlayable.xml b/packages/Tethering/res/values/overlayable.xml
index 16ae8ade19da..4c78a74d5358 100644
--- a/packages/Tethering/res/values/overlayable.xml
+++ b/packages/Tethering/res/values/overlayable.xml
@@ -24,6 +24,7 @@
<item type="array" name="config_tether_bluetooth_regexs"/>
<item type="array" name="config_tether_dhcp_range"/>
<item type="bool" name="config_tether_enable_legacy_dhcp_server"/>
+ <item type="integer" name="config_tether_offload_poll_interval"/>
<item type="array" name="config_tether_upstream_types"/>
<item type="bool" name="config_tether_upstream_automatic"/>
<!-- Configuration values for tethering entitlement check -->
diff --git a/packages/Tethering/src/com/android/networkstack/tethering/OffloadController.java b/packages/Tethering/src/com/android/networkstack/tethering/OffloadController.java
index 1817f35f1dcd..88c77b07e7e3 100644
--- a/packages/Tethering/src/com/android/networkstack/tethering/OffloadController.java
+++ b/packages/Tethering/src/com/android/networkstack/tethering/OffloadController.java
@@ -26,6 +26,8 @@ import static android.net.NetworkStats.UID_TETHERING;
import static android.net.netstats.provider.NetworkStatsProvider.QUOTA_UNLIMITED;
import static android.provider.Settings.Global.TETHER_OFFLOAD_DISABLED;
+import static com.android.networkstack.tethering.TetheringConfiguration.DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.usage.NetworkStatsManager;
@@ -77,7 +79,6 @@ public class OffloadController {
private static final boolean DBG = false;
private static final String ANYIP = "0.0.0.0";
private static final ForwardedStats EMPTY_STATS = new ForwardedStats();
- private static final int DEFAULT_PERFORM_POLL_INTERVAL_MS = 5000;
@VisibleForTesting
enum StatsType {
@@ -134,11 +135,9 @@ public class OffloadController {
private final Dependencies mDeps;
// TODO: Put more parameters in constructor into dependency object.
- static class Dependencies {
- int getPerformPollInterval() {
- // TODO: Consider make this configurable.
- return DEFAULT_PERFORM_POLL_INTERVAL_MS;
- }
+ interface Dependencies {
+ @NonNull
+ TetheringConfiguration getTetherConfig();
}
public OffloadController(Handler h, OffloadHardwareInterface hwi,
@@ -452,12 +451,16 @@ public class OffloadController {
if (mHandler.hasCallbacks(mScheduledPollingTask)) {
mHandler.removeCallbacks(mScheduledPollingTask);
}
- mHandler.postDelayed(mScheduledPollingTask, mDeps.getPerformPollInterval());
+ mHandler.postDelayed(mScheduledPollingTask,
+ mDeps.getTetherConfig().getOffloadPollInterval());
}
private boolean isPollingStatsNeeded() {
return started() && mRemainingAlertQuota > 0
- && !TextUtils.isEmpty(currentUpstreamInterface());
+ && !TextUtils.isEmpty(currentUpstreamInterface())
+ && mDeps.getTetherConfig() != null
+ && mDeps.getTetherConfig().getOffloadPollInterval()
+ >= DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS;
}
private boolean maybeUpdateDataLimit(String iface) {
diff --git a/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java b/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java
index 0a95a5e0073b..b2a43c47d1c6 100644
--- a/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java
+++ b/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java
@@ -62,7 +62,6 @@ import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
import static com.android.networkstack.tethering.TetheringNotificationUpdater.DOWNSTREAM_NONE;
-import android.app.usage.NetworkStatsManager;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothPan;
import android.bluetooth.BluetoothProfile;
@@ -268,12 +267,15 @@ public class Tethering {
mTetherMasterSM = new TetherMasterSM("TetherMaster", mLooper, deps);
mTetherMasterSM.start();
- final NetworkStatsManager statsManager =
- (NetworkStatsManager) mContext.getSystemService(Context.NETWORK_STATS_SERVICE);
mHandler = mTetherMasterSM.getHandler();
- mOffloadController = new OffloadController(mHandler,
- mDeps.getOffloadHardwareInterface(mHandler, mLog), mContext.getContentResolver(),
- statsManager, mLog, new OffloadController.Dependencies());
+ mOffloadController = mDeps.getOffloadController(mHandler, mLog,
+ new OffloadController.Dependencies() {
+
+ @Override
+ public TetheringConfiguration getTetherConfig() {
+ return mConfig;
+ }
+ });
mUpstreamNetworkMonitor = mDeps.getUpstreamNetworkMonitor(mContext, mTetherMasterSM, mLog,
TetherMasterSM.EVENT_UPSTREAM_CALLBACK);
mForwardedDownstreams = new LinkedHashSet<>();
diff --git a/packages/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java b/packages/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java
index aeac437e24e3..9d4e74732729 100644
--- a/packages/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java
+++ b/packages/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java
@@ -78,6 +78,12 @@ public class TetheringConfiguration {
public static final String TETHER_ENABLE_LEGACY_DHCP_SERVER =
"tether_enable_legacy_dhcp_server";
+ /**
+ * Default value that used to periodic polls tether offload stats from tethering offload HAL
+ * to make the data warnings work.
+ */
+ public static final int DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS = 5000;
+
public final String[] tetherableUsbRegexs;
public final String[] tetherableWifiRegexs;
public final String[] tetherableWifiP2pRegexs;
@@ -96,6 +102,8 @@ public class TetheringConfiguration {
public final int activeDataSubId;
+ private final int mOffloadPollInterval;
+
public TetheringConfiguration(Context ctx, SharedLog log, int id) {
final SharedLog configLog = log.forSubComponent("config");
@@ -129,6 +137,10 @@ public class TetheringConfiguration {
R.integer.config_mobile_hotspot_provision_check_period,
0 /* No periodic re-check */);
+ mOffloadPollInterval = getResourceInteger(res,
+ R.integer.config_tether_offload_poll_interval,
+ DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS);
+
configLog.log(toString());
}
@@ -189,6 +201,9 @@ public class TetheringConfiguration {
dumpStringArray(pw, "legacyDhcpRanges", legacyDhcpRanges);
dumpStringArray(pw, "defaultIPv4DNS", defaultIPv4DNS);
+ pw.print("offloadPollInterval: ");
+ pw.println(mOffloadPollInterval);
+
dumpStringArray(pw, "provisioningApp", provisioningApp);
pw.print("provisioningAppNoUi: ");
pw.println(provisioningAppNoUi);
@@ -208,6 +223,7 @@ public class TetheringConfiguration {
makeString(tetherableBluetoothRegexs)));
sj.add(String.format("isDunRequired:%s", isDunRequired));
sj.add(String.format("chooseUpstreamAutomatically:%s", chooseUpstreamAutomatically));
+ sj.add(String.format("offloadPollInterval:%d", mOffloadPollInterval));
sj.add(String.format("preferredUpstreamIfaceTypes:%s",
toIntArray(preferredUpstreamIfaceTypes)));
sj.add(String.format("provisioningApp:%s", makeString(provisioningApp)));
@@ -246,6 +262,10 @@ public class TetheringConfiguration {
return (tm != null) ? tm.isTetheringApnRequired() : false;
}
+ public int getOffloadPollInterval() {
+ return mOffloadPollInterval;
+ }
+
private static Collection<Integer> getUpstreamIfaceTypes(Resources res, boolean dunRequired) {
final int[] ifaceTypes = res.getIntArray(R.array.config_tether_upstream_types);
final ArrayList<Integer> upstreamIfaceTypes = new ArrayList<>(ifaceTypes.length);
diff --git a/packages/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java b/packages/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java
index 9b54b5ff2403..802f2acb3310 100644
--- a/packages/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java
+++ b/packages/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java
@@ -16,6 +16,7 @@
package com.android.networkstack.tethering;
+import android.app.usage.NetworkStatsManager;
import android.bluetooth.BluetoothAdapter;
import android.content.Context;
import android.net.INetd;
@@ -47,6 +48,19 @@ public abstract class TetheringDependencies {
}
/**
+ * Get a reference to the offload controller to be used by tethering.
+ */
+ @NonNull
+ public OffloadController getOffloadController(@NonNull Handler h,
+ @NonNull SharedLog log, @NonNull OffloadController.Dependencies deps) {
+ final NetworkStatsManager statsManager =
+ (NetworkStatsManager) getContext().getSystemService(Context.NETWORK_STATS_SERVICE);
+ return new OffloadController(h, getOffloadHardwareInterface(h, log),
+ getContext().getContentResolver(), statsManager, log, deps);
+ }
+
+
+ /**
* Get a reference to the UpstreamNetworkMonitor to be used by tethering.
*/
public UpstreamNetworkMonitor getUpstreamNetworkMonitor(Context ctx, StateMachine target,
diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadControllerTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadControllerTest.java
index 088a663190b8..b291438937c7 100644
--- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadControllerTest.java
+++ b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadControllerTest.java
@@ -29,15 +29,15 @@ import static android.provider.Settings.Global.TETHER_OFFLOAD_DISABLED;
import static com.android.networkstack.tethering.OffloadController.StatsType.STATS_PER_IFACE;
import static com.android.networkstack.tethering.OffloadController.StatsType.STATS_PER_UID;
import static com.android.networkstack.tethering.OffloadHardwareInterface.ForwardedStats;
+import static com.android.networkstack.tethering.TetheringConfiguration.DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS;
import static com.android.testutils.MiscAssertsKt.assertContainsAll;
import static com.android.testutils.MiscAssertsKt.assertThrows;
-import static com.android.testutils.NetworkStatsUtilsKt.orderInsensitiveEquals;
+import static com.android.testutils.NetworkStatsUtilsKt.assertNetworkStatsEquals;
import static junit.framework.Assert.assertNotNull;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyLong;
import static org.mockito.Matchers.anyObject;
@@ -63,10 +63,10 @@ import android.net.LinkProperties;
import android.net.NetworkStats;
import android.net.NetworkStats.Entry;
import android.net.RouteInfo;
-import android.net.netstats.provider.INetworkStatsProviderCallback;
+import android.net.netstats.provider.NetworkStatsProvider;
import android.net.util.SharedLog;
import android.os.Handler;
-import android.os.Looper;
+import android.os.test.TestLooper;
import android.provider.Settings;
import android.provider.Settings.SettingNotFoundException;
import android.test.mock.MockContentResolver;
@@ -75,7 +75,7 @@ import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.util.test.FakeSettingsProvider;
-import com.android.testutils.HandlerUtilsKt;
+import com.android.testutils.TestableNetworkStatsProviderCbBinder;
import org.junit.After;
import org.junit.Before;
@@ -109,17 +109,20 @@ public class OffloadControllerTest {
@Mock private ApplicationInfo mApplicationInfo;
@Mock private Context mContext;
@Mock private NetworkStatsManager mStatsManager;
- @Mock private INetworkStatsProviderCallback mTetherStatsProviderCb;
+ @Mock private TetheringConfiguration mTetherConfig;
+ // Late init since methods must be called by the thread that created this object.
+ private TestableNetworkStatsProviderCbBinder mTetherStatsProviderCb;
private OffloadController.OffloadTetheringStatsProvider mTetherStatsProvider;
private final ArgumentCaptor<ArrayList> mStringArrayCaptor =
ArgumentCaptor.forClass(ArrayList.class);
private final ArgumentCaptor<OffloadHardwareInterface.ControlCallback> mControlCallbackCaptor =
ArgumentCaptor.forClass(OffloadHardwareInterface.ControlCallback.class);
private MockContentResolver mContentResolver;
+ private final TestLooper mTestLooper = new TestLooper();
private OffloadController.Dependencies mDeps = new OffloadController.Dependencies() {
@Override
- int getPerformPollInterval() {
- return 0;
+ public TetheringConfiguration getTetherConfig() {
+ return mTetherConfig;
}
};
@@ -131,6 +134,7 @@ public class OffloadControllerTest {
mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
when(mContext.getContentResolver()).thenReturn(mContentResolver);
FakeSettingsProvider.clearSettingsProvider();
+ when(mTetherConfig.getOffloadPollInterval()).thenReturn(-1); // Disabled.
}
@After public void tearDown() throws Exception {
@@ -150,12 +154,16 @@ public class OffloadControllerTest {
Settings.Global.putInt(mContentResolver, TETHER_OFFLOAD_DISABLED, 0);
}
+ private void setOffloadPollInterval(int interval) {
+ when(mTetherConfig.getOffloadPollInterval()).thenReturn(interval);
+ }
+
private void waitForIdle() {
- HandlerUtilsKt.waitForIdle(new Handler(Looper.getMainLooper()), WAIT_FOR_IDLE_TIMEOUT);
+ mTestLooper.dispatchAll();
}
private OffloadController makeOffloadController() throws Exception {
- OffloadController offload = new OffloadController(new Handler(Looper.getMainLooper()),
+ OffloadController offload = new OffloadController(new Handler(mTestLooper.getLooper()),
mHardware, mContentResolver, mStatsManager, new SharedLog("test"), mDeps);
final ArgumentCaptor<OffloadController.OffloadTetheringStatsProvider>
tetherStatsProviderCaptor =
@@ -164,6 +172,7 @@ public class OffloadControllerTest {
tetherStatsProviderCaptor.capture());
mTetherStatsProvider = tetherStatsProviderCaptor.getValue();
assertNotNull(mTetherStatsProvider);
+ mTetherStatsProviderCb = new TestableNetworkStatsProviderCbBinder();
mTetherStatsProvider.setProviderCallbackBinder(mTetherStatsProviderCb);
return offload;
}
@@ -352,9 +361,9 @@ public class OffloadControllerTest {
stacked.setInterfaceName("stacked");
stacked.addLinkAddress(new LinkAddress("192.0.2.129/25"));
stacked.addRoute(new RouteInfo(null, InetAddress.getByName("192.0.2.254"), null,
- RTN_UNICAST));
+ RTN_UNICAST));
stacked.addRoute(new RouteInfo(null, InetAddress.getByName("fe80::bad:f00"), null,
- RTN_UNICAST));
+ RTN_UNICAST));
assertTrue(lp.addStackedLink(stacked));
offload.setUpstreamLinkProperties(lp);
// No change in local addresses means no call to setLocalPrefixes().
@@ -459,20 +468,12 @@ public class OffloadControllerTest {
.addEntry(buildTestEntry(STATS_PER_UID, mobileIface, 999, 99999))
.addEntry(buildTestEntry(STATS_PER_UID, ethernetIface, 12345, 54321));
- assertTrue(orderInsensitiveEquals(expectedIfaceStats, ifaceStats));
- assertTrue(orderInsensitiveEquals(expectedUidStats, uidStats));
-
- final ArgumentCaptor<NetworkStats> ifaceStatsCaptor = ArgumentCaptor.forClass(
- NetworkStats.class);
- final ArgumentCaptor<NetworkStats> uidStatsCaptor = ArgumentCaptor.forClass(
- NetworkStats.class);
+ assertNetworkStatsEquals(expectedIfaceStats, ifaceStats);
+ assertNetworkStatsEquals(expectedUidStats, uidStats);
// Force pushing stats update to verify the stats reported.
mTetherStatsProvider.pushTetherStats();
- verify(mTetherStatsProviderCb, times(1))
- .notifyStatsUpdated(anyInt(), ifaceStatsCaptor.capture(), uidStatsCaptor.capture());
- assertTrue(orderInsensitiveEquals(expectedIfaceStats, ifaceStatsCaptor.getValue()));
- assertTrue(orderInsensitiveEquals(expectedUidStats, uidStatsCaptor.getValue()));
+ mTetherStatsProviderCb.expectNotifyStatsUpdated(expectedIfaceStats, expectedUidStats);
when(mHardware.getForwardedStats(eq(ethernetIface))).thenReturn(
new ForwardedStats(100000, 100000));
@@ -498,11 +499,10 @@ public class OffloadControllerTest {
.addEntry(buildTestEntry(STATS_PER_UID, mobileIface, 999, 99999))
.addEntry(buildTestEntry(STATS_PER_UID, ethernetIface, 112345, 154321));
- assertTrue(orderInsensitiveEquals(expectedIfaceStatsAccu, ifaceStatsAccu));
- assertTrue(orderInsensitiveEquals(expectedUidStatsAccu, uidStatsAccu));
+ assertNetworkStatsEquals(expectedIfaceStatsAccu, ifaceStatsAccu);
+ assertNetworkStatsEquals(expectedUidStatsAccu, uidStatsAccu);
// Verify that only diff of stats is reported.
- reset(mTetherStatsProviderCb);
mTetherStatsProvider.pushTetherStats();
final NetworkStats expectedIfaceStatsDiff = new NetworkStats(0L, 2)
.addEntry(buildTestEntry(STATS_PER_IFACE, mobileIface, 0, 0))
@@ -511,10 +511,8 @@ public class OffloadControllerTest {
final NetworkStats expectedUidStatsDiff = new NetworkStats(0L, 2)
.addEntry(buildTestEntry(STATS_PER_UID, mobileIface, 0, 0))
.addEntry(buildTestEntry(STATS_PER_UID, ethernetIface, 100000, 100000));
- verify(mTetherStatsProviderCb, times(1))
- .notifyStatsUpdated(anyInt(), ifaceStatsCaptor.capture(), uidStatsCaptor.capture());
- assertTrue(orderInsensitiveEquals(expectedIfaceStatsDiff, ifaceStatsCaptor.getValue()));
- assertTrue(orderInsensitiveEquals(expectedUidStatsDiff, uidStatsCaptor.getValue()));
+ mTetherStatsProviderCb.expectNotifyStatsUpdated(expectedIfaceStatsDiff,
+ expectedUidStatsDiff);
}
@Test
@@ -591,7 +589,7 @@ public class OffloadControllerTest {
OffloadHardwareInterface.ControlCallback callback = mControlCallbackCaptor.getValue();
callback.onStoppedLimitReached();
- verify(mTetherStatsProviderCb, times(1)).notifyStatsUpdated(anyInt(), any(), any());
+ mTetherStatsProviderCb.expectNotifyStatsUpdated();
}
@Test
@@ -695,8 +693,8 @@ public class OffloadControllerTest {
verify(mHardware, times(1)).getForwardedStats(eq(RMNET0));
verify(mHardware, times(1)).getForwardedStats(eq(WLAN0));
// TODO: verify the exact stats reported.
- verify(mTetherStatsProviderCb, times(1)).notifyStatsUpdated(anyInt(), any(), any());
- verifyNoMoreInteractions(mTetherStatsProviderCb);
+ mTetherStatsProviderCb.expectNotifyStatsUpdated();
+ mTetherStatsProviderCb.assertNoCallback();
verifyNoMoreInteractions(mHardware);
}
@@ -760,8 +758,8 @@ public class OffloadControllerTest {
// Verify forwarded stats behaviour.
verify(mHardware, times(1)).getForwardedStats(eq(RMNET0));
verify(mHardware, times(1)).getForwardedStats(eq(WLAN0));
- verify(mTetherStatsProviderCb, times(1)).notifyStatsUpdated(anyInt(), any(), any());
- verifyNoMoreInteractions(mTetherStatsProviderCb);
+ mTetherStatsProviderCb.expectNotifyStatsUpdated();
+ mTetherStatsProviderCb.assertNoCallback();
// TODO: verify local prefixes and downstreams are also pushed to the HAL.
verify(mHardware, times(1)).setLocalPrefixes(mStringArrayCaptor.capture());
@@ -780,4 +778,50 @@ public class OffloadControllerTest {
verifyNoMoreInteractions(mHardware);
}
+ @Test
+ public void testOnSetAlert() throws Exception {
+ setupFunctioningHardwareInterface();
+ enableOffload();
+ setOffloadPollInterval(DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS);
+ final OffloadController offload = makeOffloadController();
+ offload.start();
+
+ // Initialize with fake eth upstream.
+ final String ethernetIface = "eth1";
+ InOrder inOrder = inOrder(mHardware);
+ final LinkProperties lp = new LinkProperties();
+ lp.setInterfaceName(ethernetIface);
+ offload.setUpstreamLinkProperties(lp);
+ // Previous upstream was null, so no stats are fetched.
+ inOrder.verify(mHardware, never()).getForwardedStats(any());
+
+ // Verify that set quota to 0 will immediately triggers an callback.
+ mTetherStatsProvider.onSetAlert(0);
+ waitForIdle();
+ mTetherStatsProviderCb.expectNotifyAlertReached();
+
+ // Verify that notifyAlertReached never fired if quota is not yet reached.
+ when(mHardware.getForwardedStats(eq(ethernetIface))).thenReturn(
+ new ForwardedStats(0, 0));
+ mTetherStatsProvider.onSetAlert(100);
+ mTestLooper.moveTimeForward(DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS);
+ waitForIdle();
+ mTetherStatsProviderCb.assertNoCallback();
+
+ // Verify that notifyAlertReached fired when quota is reached.
+ when(mHardware.getForwardedStats(eq(ethernetIface))).thenReturn(
+ new ForwardedStats(50, 50));
+ mTestLooper.moveTimeForward(DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS);
+ waitForIdle();
+ mTetherStatsProviderCb.expectNotifyAlertReached();
+
+ // Verify that set quota with UNLIMITED won't trigger any callback, and won't fetch
+ // any stats since the polling is stopped.
+ reset(mHardware);
+ mTetherStatsProvider.onSetAlert(NetworkStatsProvider.QUOTA_UNLIMITED);
+ mTestLooper.moveTimeForward(DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS);
+ waitForIdle();
+ mTetherStatsProviderCb.assertNoCallback();
+ verify(mHardware, never()).getForwardedStats(any());
+ }
}
diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java
index 07ddea43f4e8..e8ba5b8168d7 100644
--- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java
+++ b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java
@@ -115,6 +115,8 @@ public class TetheringConfigurationTest {
when(mResources.getStringArray(R.array.config_tether_dhcp_range)).thenReturn(
new String[0]);
+ when(mResources.getInteger(R.integer.config_tether_offload_poll_interval)).thenReturn(
+ TetheringConfiguration.DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS);
when(mResources.getStringArray(R.array.config_tether_usb_regexs)).thenReturn(new String[0]);
when(mResources.getStringArray(R.array.config_tether_wifi_regexs))
.thenReturn(new String[]{ "test_wlan\\d" });
@@ -314,6 +316,23 @@ public class TetheringConfigurationTest {
}
@Test
+ public void testOffloadIntervalByResource() {
+ final TetheringConfiguration intervalByDefault =
+ new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
+ assertEquals(TetheringConfiguration.DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS,
+ intervalByDefault.getOffloadPollInterval());
+
+ final int[] testOverrides = {0, 3000, -1};
+ for (final int override : testOverrides) {
+ when(mResources.getInteger(R.integer.config_tether_offload_poll_interval)).thenReturn(
+ override);
+ final TetheringConfiguration overrideByRes =
+ new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
+ assertEquals(override, overrideByRes.getOffloadPollInterval());
+ }
+ }
+
+ @Test
public void testGetResourcesBySubId() {
setUpResourceForSubId();
final TetheringConfiguration cfg = new TetheringConfiguration(
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 0363f5f9989f..fff7a70f54d0 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
@@ -150,6 +150,8 @@ import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.util.ArrayList;
@@ -212,6 +214,9 @@ public class TetheringTest {
private Tethering mTethering;
private PhoneStateListener mPhoneStateListener;
private InterfaceConfigurationParcel mInterfaceConfiguration;
+ private TetheringConfiguration mConfig;
+ private EntitlementManager mEntitleMgr;
+ private OffloadController mOffloadCtrl;
private class TestContext extends BroadcastInterceptingContext {
TestContext(Context base) {
@@ -297,8 +302,9 @@ public class TetheringTest {
}
}
- private class MockTetheringConfiguration extends TetheringConfiguration {
- MockTetheringConfiguration(Context ctx, SharedLog log, int id) {
+ // MyTetheringConfiguration is used to override static method for testing.
+ private class MyTetheringConfiguration extends TetheringConfiguration {
+ MyTetheringConfiguration(Context ctx, SharedLog log, int id) {
super(ctx, log, id);
}
@@ -328,6 +334,15 @@ public class TetheringTest {
}
@Override
+ public OffloadController getOffloadController(Handler h, SharedLog log,
+ OffloadController.Dependencies deps) {
+ mOffloadCtrl = spy(super.getOffloadController(h, log, deps));
+ // Return real object here instead of mock because
+ // testReportFailCallbackIfOffloadNotSupported depend on real OffloadController object.
+ return mOffloadCtrl;
+ }
+
+ @Override
public UpstreamNetworkMonitor getUpstreamNetworkMonitor(Context ctx,
StateMachine target, SharedLog log, int what) {
mUpstreamNetworkMonitorMasterSM = target;
@@ -352,6 +367,13 @@ public class TetheringTest {
}
@Override
+ public EntitlementManager getEntitlementManager(Context ctx, StateMachine target,
+ SharedLog log, int what) {
+ mEntitleMgr = spy(super.getEntitlementManager(ctx, target, log, what));
+ return mEntitleMgr;
+ }
+
+ @Override
public boolean isTetheringSupported() {
return true;
}
@@ -359,7 +381,8 @@ public class TetheringTest {
@Override
public TetheringConfiguration generateTetheringConfiguration(Context ctx, SharedLog log,
int subId) {
- return new MockTetheringConfiguration(ctx, log, subId);
+ mConfig = spy(new MyTetheringConfiguration(ctx, log, subId));
+ return mConfig;
}
@Override
@@ -1726,6 +1749,17 @@ public class TetheringTest {
verify(mNotificationUpdater, never()).onUpstreamCapabilitiesChanged(any());
}
+ @Test
+ public void testDumpTetheringLog() throws Exception {
+ final FileDescriptor mockFd = mock(FileDescriptor.class);
+ final PrintWriter mockPw = mock(PrintWriter.class);
+ runUsbTethering(null);
+ mTethering.dump(mockFd, mockPw, new String[0]);
+ verify(mConfig).dump(any());
+ verify(mEntitleMgr).dump(any());
+ verify(mOffloadCtrl).dump(any());
+ }
+
// TODO: Test that a request for hotspot mode doesn't interfere with an
// already operating tethering mode interface.
}
diff --git a/services/Android.bp b/services/Android.bp
index 2e70f1c528ee..882085a7d0ba 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -80,6 +80,8 @@ java_library {
"services.usb",
"services.voiceinteraction",
"services.wifi",
+ "service-blobstore",
+ "service-jobscheduler",
"android.hidl.base-V1.0-java",
],
@@ -135,7 +137,7 @@ droidstubs {
},
last_released: {
api_file: ":android.api.system-server.latest",
- removed_api_file: "api/removed.txt",
+ removed_api_file: ":removed.api.system-server.latest",
baseline_file: ":system-server-api-incompatibilities-with-last-released"
},
api_lint: {
diff --git a/services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java b/services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java
index 0b3899d15993..fdc5f810db22 100644
--- a/services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java
+++ b/services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java
@@ -120,7 +120,7 @@ public class AppPredictionPerUserService extends
this::removeAppPredictionSessionInfo));
}
final boolean serviceExists = resolveService(sessionId, s ->
- s.onCreatePredictionSession(context, sessionId));
+ s.onCreatePredictionSession(context, sessionId), true);
if (!serviceExists) {
mSessionInfos.remove(sessionId);
}
@@ -132,7 +132,7 @@ public class AppPredictionPerUserService extends
@GuardedBy("mLock")
public void notifyAppTargetEventLocked(@NonNull AppPredictionSessionId sessionId,
@NonNull AppTargetEvent event) {
- resolveService(sessionId, s -> s.notifyAppTargetEvent(sessionId, event));
+ resolveService(sessionId, s -> s.notifyAppTargetEvent(sessionId, event), false);
}
/**
@@ -142,7 +142,7 @@ public class AppPredictionPerUserService extends
public void notifyLaunchLocationShownLocked(@NonNull AppPredictionSessionId sessionId,
@NonNull String launchLocation, @NonNull ParceledListSlice targetIds) {
resolveService(sessionId, s ->
- s.notifyLaunchLocationShown(sessionId, launchLocation, targetIds));
+ s.notifyLaunchLocationShown(sessionId, launchLocation, targetIds), false);
}
/**
@@ -151,7 +151,7 @@ public class AppPredictionPerUserService extends
@GuardedBy("mLock")
public void sortAppTargetsLocked(@NonNull AppPredictionSessionId sessionId,
@NonNull ParceledListSlice targets, @NonNull IPredictionCallback callback) {
- resolveService(sessionId, s -> s.sortAppTargets(sessionId, targets, callback));
+ resolveService(sessionId, s -> s.sortAppTargets(sessionId, targets, callback), true);
}
/**
@@ -161,7 +161,7 @@ public class AppPredictionPerUserService extends
public void registerPredictionUpdatesLocked(@NonNull AppPredictionSessionId sessionId,
@NonNull IPredictionCallback callback) {
final boolean serviceExists = resolveService(sessionId, s ->
- s.registerPredictionUpdates(sessionId, callback));
+ s.registerPredictionUpdates(sessionId, callback), false);
final AppPredictionSessionInfo sessionInfo = mSessionInfos.get(sessionId);
if (serviceExists && sessionInfo != null) {
sessionInfo.addCallbackLocked(callback);
@@ -175,7 +175,7 @@ public class AppPredictionPerUserService extends
public void unregisterPredictionUpdatesLocked(@NonNull AppPredictionSessionId sessionId,
@NonNull IPredictionCallback callback) {
final boolean serviceExists = resolveService(sessionId, s ->
- s.unregisterPredictionUpdates(sessionId, callback));
+ s.unregisterPredictionUpdates(sessionId, callback), false);
final AppPredictionSessionInfo sessionInfo = mSessionInfos.get(sessionId);
if (serviceExists && sessionInfo != null) {
sessionInfo.removeCallbackLocked(callback);
@@ -187,7 +187,7 @@ public class AppPredictionPerUserService extends
*/
@GuardedBy("mLock")
public void requestPredictionUpdateLocked(@NonNull AppPredictionSessionId sessionId) {
- resolveService(sessionId, s -> s.requestPredictionUpdate(sessionId));
+ resolveService(sessionId, s -> s.requestPredictionUpdate(sessionId), true);
}
/**
@@ -196,7 +196,7 @@ public class AppPredictionPerUserService extends
@GuardedBy("mLock")
public void onDestroyPredictionSessionLocked(@NonNull AppPredictionSessionId sessionId) {
final boolean serviceExists = resolveService(sessionId, s ->
- s.onDestroyPredictionSession(sessionId));
+ s.onDestroyPredictionSession(sessionId), false);
final AppPredictionSessionInfo sessionInfo = mSessionInfos.get(sessionId);
if (serviceExists && sessionInfo != null) {
sessionInfo.destroy();
@@ -304,7 +304,8 @@ public class AppPredictionPerUserService extends
@GuardedBy("mLock")
@Nullable
protected boolean resolveService(@NonNull final AppPredictionSessionId sessionId,
- @NonNull final AbstractRemoteService.AsyncRequest<IPredictionService> cb) {
+ @NonNull final AbstractRemoteService.AsyncRequest<IPredictionService> cb,
+ boolean sendImmediately) {
final AppPredictionSessionInfo sessionInfo = mSessionInfos.get(sessionId);
if (sessionInfo == null) return false;
if (sessionInfo.mUsesPeopleService) {
@@ -322,7 +323,13 @@ public class AppPredictionPerUserService extends
} else {
final RemoteAppPredictionService service = getRemoteServiceLocked();
if (service != null) {
- service.scheduleOnResolvedService(cb);
+ // TODO(b/155887722): implement a priority system so that latency-sensitive
+ // requests gets executed first.
+ if (sendImmediately) {
+ service.executeOnResolvedService(cb);
+ } else {
+ service.scheduleOnResolvedService(cb);
+ }
}
return service != null;
}
diff --git a/services/appprediction/java/com/android/server/appprediction/RemoteAppPredictionService.java b/services/appprediction/java/com/android/server/appprediction/RemoteAppPredictionService.java
index ceb1cafcebeb..a57ff11fa20f 100644
--- a/services/appprediction/java/com/android/server/appprediction/RemoteAppPredictionService.java
+++ b/services/appprediction/java/com/android/server/appprediction/RemoteAppPredictionService.java
@@ -80,6 +80,13 @@ public class RemoteAppPredictionService extends
}
/**
+ * Execute async request on remote service immediately instead of sending it to Handler queue.
+ */
+ public void executeOnResolvedService(@NonNull AsyncRequest<IPredictionService> request) {
+ executeAsyncRequest(request);
+ }
+
+ /**
* Failure callback
*/
public interface RemoteAppPredictionServiceCallbacks
diff --git a/services/autofill/java/com/android/server/autofill/AutofillInlineSuggestionsRequestSession.java b/services/autofill/java/com/android/server/autofill/AutofillInlineSuggestionsRequestSession.java
index ce11c76e5c6a..22451e1d992e 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillInlineSuggestionsRequestSession.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillInlineSuggestionsRequestSession.java
@@ -37,6 +37,7 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.view.IInlineSuggestionsRequestCallback;
import com.android.internal.view.IInlineSuggestionsResponseCallback;
import com.android.internal.view.InlineSuggestionsRequestInfo;
+import com.android.server.autofill.ui.InlineSuggestionFactory;
import com.android.server.inputmethod.InputMethodManagerInternal;
import java.lang.ref.WeakReference;
@@ -242,7 +243,8 @@ final class AutofillInlineSuggestionsRequestSession {
}
if (sDebug) Log.d(TAG, "Send inline response: " + response.getInlineSuggestions().size());
try {
- mResponseCallback.onInlineSuggestionsResponse(mAutofillId, response);
+ mResponseCallback.onInlineSuggestionsResponse(mAutofillId,
+ InlineSuggestionFactory.copy(response));
} catch (RemoteException e) {
Slog.e(TAG, "RemoteException sending InlineSuggestionsResponse to IME");
}
diff --git a/services/autofill/java/com/android/server/autofill/RemoteInlineSuggestionRenderService.java b/services/autofill/java/com/android/server/autofill/RemoteInlineSuggestionRenderService.java
index 255adcd92da3..617c111c6c38 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteInlineSuggestionRenderService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteInlineSuggestionRenderService.java
@@ -47,7 +47,7 @@ public final class RemoteInlineSuggestionRenderService extends
private static final String TAG = "RemoteInlineSuggestionRenderService";
- private final int mIdleUnbindTimeoutMs = 5000;
+ private final long mIdleUnbindTimeoutMs = PERMANENT_BOUND_TIMEOUT_MS;
RemoteInlineSuggestionRenderService(Context context, ComponentName componentName,
String serviceInterface, int userId, InlineSuggestionRenderCallbacks callback,
diff --git a/services/autofill/java/com/android/server/autofill/ui/InlineContentProviderImpl.java b/services/autofill/java/com/android/server/autofill/ui/InlineContentProviderImpl.java
new file mode 100644
index 000000000000..819f2b813a5e
--- /dev/null
+++ b/services/autofill/java/com/android/server/autofill/ui/InlineContentProviderImpl.java
@@ -0,0 +1,140 @@
+/*
+ * 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.autofill.ui;
+
+import static com.android.server.autofill.Helper.sVerbose;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Handler;
+import android.util.Slog;
+
+import com.android.internal.view.inline.IInlineContentCallback;
+import com.android.internal.view.inline.IInlineContentProvider;
+import com.android.server.FgThread;
+
+/**
+ * We create one instance of this class for each {@link android.view.inputmethod.InlineSuggestion}
+ * instance. Each inline suggestion instance will only be sent to the remote IME process once. In
+ * case of filtering and resending the suggestion when keyboard state changes between hide and
+ * show, a new instance of this class will be created using {@link #copy()}, with the same backing
+ * {@link RemoteInlineSuggestionUi}. When the
+ * {@link #provideContent(int, int, IInlineContentCallback)} is called the first time (it's only
+ * allowed to be called at most once), the passed in width/height is used to determine whether
+ * the existing {@link RemoteInlineSuggestionUi} provided in the constructor can be reused, or a
+ * new one should be created to suit the new size requirement for the view. In normal cases,
+ * we should not expect the size requirement to change, although in theory the public API allows
+ * the IME to do that.
+ *
+ * <p>This design is to enable us to be able to reuse the backing remote view while still keeping
+ * the callbacks relatively well aligned. For example, if we allow multiple remote IME binder
+ * callbacks to call into one instance of this class, then binder A may call in with width/height
+ * X for which we create a view (i.e. {@link RemoteInlineSuggestionUi}) for it,
+ *
+ * See also {@link RemoteInlineSuggestionUi} for relevant information.
+ */
+public final class InlineContentProviderImpl extends IInlineContentProvider.Stub {
+
+ // TODO(b/153615023): consider not holding strong reference to heavy objects in this stub, to
+ // avoid memory leak in case the client app is holding the remote reference for a longer
+ // time than expected. Essentially we need strong reference in the system process to
+ // the member variables, but weak reference to them in the IInlineContentProvider.Stub.
+
+ private static final String TAG = InlineContentProviderImpl.class.getSimpleName();
+
+ private final Handler mHandler = FgThread.getHandler();;
+
+ @NonNull
+ private final RemoteInlineSuggestionViewConnector mRemoteInlineSuggestionViewConnector;
+ @Nullable
+ private RemoteInlineSuggestionUi mRemoteInlineSuggestionUi;
+
+ private boolean mProvideContentCalled = false;
+
+ InlineContentProviderImpl(
+ @NonNull RemoteInlineSuggestionViewConnector remoteInlineSuggestionViewConnector,
+ @Nullable RemoteInlineSuggestionUi remoteInlineSuggestionUi) {
+ mRemoteInlineSuggestionViewConnector = remoteInlineSuggestionViewConnector;
+ mRemoteInlineSuggestionUi = remoteInlineSuggestionUi;
+ }
+
+ /**
+ * Returns a new instance of this class, with the same {@code mInlineSuggestionRenderer} and
+ * {@code mRemoteInlineSuggestionUi}. The latter may or may not be reusable depending on the
+ * size information provided when the client calls {@link #provideContent(int, int,
+ * IInlineContentCallback)}.
+ */
+ @NonNull
+ public InlineContentProviderImpl copy() {
+ return new InlineContentProviderImpl(mRemoteInlineSuggestionViewConnector,
+ mRemoteInlineSuggestionUi);
+ }
+
+ /**
+ * Provides a SurfacePackage associated with the inline suggestion view to the IME. If such
+ * view doesn't exit, then create a new one. This method should be called once per lifecycle
+ * of this object. Any further calls to the method will be ignored.
+ */
+ @Override
+ public void provideContent(int width, int height, IInlineContentCallback callback) {
+ mHandler.post(() -> handleProvideContent(width, height, callback));
+ }
+
+ @Override
+ public void requestSurfacePackage() {
+ mHandler.post(this::handleGetSurfacePackage);
+ }
+
+ @Override
+ public void onSurfacePackageReleased() {
+ mHandler.post(this::handleOnSurfacePackageReleased);
+ }
+
+ private void handleProvideContent(int width, int height, IInlineContentCallback callback) {
+ if (sVerbose) Slog.v(TAG, "handleProvideContent");
+ if (mProvideContentCalled) {
+ // This method should only be called once.
+ return;
+ }
+ mProvideContentCalled = true;
+ if (mRemoteInlineSuggestionUi == null || !mRemoteInlineSuggestionUi.match(width, height)) {
+ mRemoteInlineSuggestionUi = new RemoteInlineSuggestionUi(
+ mRemoteInlineSuggestionViewConnector,
+ width, height, mHandler);
+ }
+ mRemoteInlineSuggestionUi.setInlineContentCallback(callback);
+ mRemoteInlineSuggestionUi.requestSurfacePackage();
+ }
+
+ private void handleGetSurfacePackage() {
+ if (sVerbose) Slog.v(TAG, "handleGetSurfacePackage");
+ if (!mProvideContentCalled || mRemoteInlineSuggestionUi == null) {
+ // provideContent should be called first, and remote UI should not be null.
+ return;
+ }
+ mRemoteInlineSuggestionUi.requestSurfacePackage();
+ }
+
+ private void handleOnSurfacePackageReleased() {
+ if (sVerbose) Slog.v(TAG, "handleOnSurfacePackageReleased");
+ if (!mProvideContentCalled || mRemoteInlineSuggestionUi == null) {
+ // provideContent should be called first, and remote UI should not be null.
+ return;
+ }
+ mRemoteInlineSuggestionUi.surfacePackageReleased();
+ }
+}
diff --git a/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java b/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java
index 79c9efa48d73..e74463a8584b 100644
--- a/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java
+++ b/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java
@@ -24,14 +24,11 @@ import android.annotation.Nullable;
import android.content.Intent;
import android.content.IntentSender;
import android.os.IBinder;
-import android.os.RemoteException;
import android.service.autofill.Dataset;
import android.service.autofill.FillResponse;
-import android.service.autofill.IInlineSuggestionUiCallback;
import android.service.autofill.InlinePresentation;
import android.text.TextUtils;
import android.util.Slog;
-import android.view.SurfaceControlViewHost;
import android.view.autofill.AutofillId;
import android.view.autofill.AutofillManager;
import android.view.autofill.AutofillValue;
@@ -41,12 +38,8 @@ import android.view.inputmethod.InlineSuggestionsRequest;
import android.view.inputmethod.InlineSuggestionsResponse;
import android.widget.inline.InlinePresentationSpec;
-import com.android.internal.view.inline.IInlineContentCallback;
import com.android.internal.view.inline.IInlineContentProvider;
-import com.android.server.LocalServices;
-import com.android.server.UiThread;
import com.android.server.autofill.RemoteInlineSuggestionRenderService;
-import com.android.server.inputmethod.InputMethodManagerInternal;
import java.util.ArrayList;
import java.util.List;
@@ -73,6 +66,27 @@ public final class InlineSuggestionFactory {
}
/**
+ * Returns a copy of the response, that internally copies the {@link IInlineContentProvider}
+ * so that it's not reused by the remote IME process across different inline suggestions.
+ * See {@link InlineContentProviderImpl} for why this is needed.
+ */
+ @NonNull
+ public static InlineSuggestionsResponse copy(@NonNull InlineSuggestionsResponse response) {
+ final ArrayList<InlineSuggestion> copiedInlineSuggestions = new ArrayList<>();
+ for (InlineSuggestion inlineSuggestion : response.getInlineSuggestions()) {
+ final IInlineContentProvider contentProvider = inlineSuggestion.getContentProvider();
+ if (contentProvider instanceof InlineContentProviderImpl) {
+ copiedInlineSuggestions.add(new
+ InlineSuggestion(inlineSuggestion.getInfo(),
+ ((InlineContentProviderImpl) contentProvider).copy()));
+ } else {
+ copiedInlineSuggestions.add(inlineSuggestion);
+ }
+ }
+ return new InlineSuggestionsResponse(copiedInlineSuggestions);
+ }
+
+ /**
* Creates an {@link InlineSuggestionsResponse} with the {@code datasets} provided by the
* autofill service, potentially filtering the datasets.
*/
@@ -276,78 +290,20 @@ public final class InlineSuggestionFactory {
inlinePresentation.isPinned());
}
- private static IInlineContentProvider.Stub createInlineContentProvider(
+ private static IInlineContentProvider createInlineContentProvider(
@NonNull InlinePresentation inlinePresentation, @Nullable Runnable onClickAction,
@NonNull Runnable onErrorCallback,
@NonNull Consumer<IntentSender> intentSenderConsumer,
@Nullable RemoteInlineSuggestionRenderService remoteRenderService,
@Nullable IBinder hostInputToken,
int displayId) {
- return new IInlineContentProvider.Stub() {
- @Override
- public void provideContent(int width, int height, IInlineContentCallback callback) {
- UiThread.getHandler().post(() -> {
- final IInlineSuggestionUiCallback uiCallback = createInlineSuggestionUiCallback(
- callback, onClickAction, onErrorCallback, intentSenderConsumer);
-
- if (remoteRenderService == null) {
- Slog.e(TAG, "RemoteInlineSuggestionRenderService is null");
- return;
- }
-
- remoteRenderService.renderSuggestion(uiCallback, inlinePresentation,
- width, height, hostInputToken, displayId);
- });
- }
- };
- }
-
- private static IInlineSuggestionUiCallback.Stub createInlineSuggestionUiCallback(
- @NonNull IInlineContentCallback callback, @NonNull Runnable onAutofillCallback,
- @NonNull Runnable onErrorCallback,
- @NonNull Consumer<IntentSender> intentSenderConsumer) {
- return new IInlineSuggestionUiCallback.Stub() {
- @Override
- public void onClick() throws RemoteException {
- onAutofillCallback.run();
- callback.onClick();
- }
-
- @Override
- public void onLongClick() throws RemoteException {
- callback.onLongClick();
- }
-
- @Override
- public void onContent(SurfaceControlViewHost.SurfacePackage surface, int width,
- int height)
- throws RemoteException {
- callback.onContent(surface, width, height);
- surface.release();
- }
-
- @Override
- public void onError() throws RemoteException {
- onErrorCallback.run();
- }
-
- @Override
- public void onTransferTouchFocusToImeWindow(IBinder sourceInputToken, int displayId)
- throws RemoteException {
- final InputMethodManagerInternal inputMethodManagerInternal =
- LocalServices.getService(InputMethodManagerInternal.class);
- if (!inputMethodManagerInternal.transferTouchFocusToImeWindow(sourceInputToken,
- displayId)) {
- Slog.e(TAG, "Cannot transfer touch focus from suggestion to IME");
- onErrorCallback.run();
- }
- }
-
- @Override
- public void onStartIntentSender(IntentSender intentSender) {
- intentSenderConsumer.accept(intentSender);
- }
- };
+ RemoteInlineSuggestionViewConnector
+ remoteInlineSuggestionViewConnector = new RemoteInlineSuggestionViewConnector(
+ remoteRenderService, inlinePresentation, hostInputToken, displayId, onClickAction,
+ onErrorCallback, intentSenderConsumer);
+ InlineContentProviderImpl inlineContentProvider = new InlineContentProviderImpl(
+ remoteInlineSuggestionViewConnector, null);
+ return inlineContentProvider;
}
private InlineSuggestionFactory() {
diff --git a/services/autofill/java/com/android/server/autofill/ui/RemoteInlineSuggestionUi.java b/services/autofill/java/com/android/server/autofill/ui/RemoteInlineSuggestionUi.java
new file mode 100644
index 000000000000..00a5283c9b1f
--- /dev/null
+++ b/services/autofill/java/com/android/server/autofill/ui/RemoteInlineSuggestionUi.java
@@ -0,0 +1,291 @@
+/*
+ * 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.autofill.ui;
+
+import static com.android.server.autofill.Helper.sDebug;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.IntentSender;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.service.autofill.IInlineSuggestionUi;
+import android.service.autofill.IInlineSuggestionUiCallback;
+import android.service.autofill.ISurfacePackageResultCallback;
+import android.util.Slog;
+import android.view.SurfaceControlViewHost;
+
+import com.android.internal.view.inline.IInlineContentCallback;
+
+/**
+ * The instance of this class lives in the system server, orchestrating the communication between
+ * the remote process owning embedded view (i.e. ExtServices) and the remote process hosting the
+ * embedded view (i.e. IME). It's also responsible for releasing the embedded view from the owning
+ * process when it's not longer needed in the hosting process.
+ *
+ * <p>An instance of this class may be reused to associate with multiple instances of
+ * {@link InlineContentProviderImpl}s, each of which wraps a callback from the IME. But at any
+ * given time, there is only one active IME callback which this class will callback into.
+ *
+ * <p>This class is thread safe, because all the outside calls are piped into the same single
+ * thread handler to be processed.
+ *
+ * TODO(b/154683107): implement the reference counting in case there are multiple active
+ * SurfacePackages at the same time. This will not happen for now since all the InlineSuggestions
+ * sharing the same UI will be sent to the same IME window, so the previous view will be detached
+ * before the new view are attached to the window.
+ */
+final class RemoteInlineSuggestionUi {
+
+ private static final String TAG = RemoteInlineSuggestionUi.class.getSimpleName();
+
+ // The delay time to release the remote inline suggestion view (in the renderer
+ // process) after receiving a signal about the surface package being released due to being
+ // detached from the window in the host app (in the IME process). The release will be
+ // canceled if the host app reattaches the view to a window within this delay time.
+ // TODO(b/154683107): try out using the Chroreographer to schedule the release right at the
+ // next frame. Basically if the view is not re-attached to the window immediately in the next
+ // frame after it was detached, then it will be released.
+ private static final long RELEASE_REMOTE_VIEW_HOST_DELAY_MS = 200;
+
+ @NonNull
+ private final Handler mHandler;
+ @NonNull
+ private final RemoteInlineSuggestionViewConnector mRemoteInlineSuggestionViewConnector;
+ private final int mWidth;
+ private final int mHeight;
+ @NonNull
+ private final InlineSuggestionUiCallbackImpl mInlineSuggestionUiCallback;
+
+ @Nullable
+ private IInlineContentCallback mInlineContentCallback; // from IME
+
+ /**
+ * Remote inline suggestion view, backed by an instance of {@link SurfaceControlViewHost} in
+ * the render service process. We takes care of releasing it when there is no remote
+ * reference to it (from IME), and we will create a new instance of the view when it's needed
+ * by IME again.
+ */
+ @Nullable
+ private IInlineSuggestionUi mInlineSuggestionUi;
+ private boolean mWaitingForUiCreation = false;
+ private int mActualWidth;
+ private int mActualHeight;
+
+ @Nullable
+ private Runnable mDelayedReleaseViewRunnable;
+
+ RemoteInlineSuggestionUi(
+ @NonNull RemoteInlineSuggestionViewConnector remoteInlineSuggestionViewConnector,
+ int width, int height, Handler handler) {
+ mHandler = handler;
+ mRemoteInlineSuggestionViewConnector = remoteInlineSuggestionViewConnector;
+ mWidth = width;
+ mHeight = height;
+ mInlineSuggestionUiCallback = new InlineSuggestionUiCallbackImpl();
+ }
+
+ /**
+ * Updates the callback from the IME process. It'll swap out the previous IME callback, and
+ * all the subsequent callback events (onClick, onLongClick, touch event transfer, etc) will
+ * be directed to the new callback.
+ */
+ void setInlineContentCallback(@NonNull IInlineContentCallback inlineContentCallback) {
+ mHandler.post(() -> {
+ mInlineContentCallback = inlineContentCallback;
+ });
+ }
+
+ /**
+ * Handles the request from the IME process to get a new surface package. May create a new
+ * view in the renderer process if the existing view is already released.
+ */
+ void requestSurfacePackage() {
+ mHandler.post(this::handleRequestSurfacePackage);
+ }
+
+ /**
+ * Handles the signal from the IME process that the previously sent surface package has been
+ * released.
+ */
+ void surfacePackageReleased() {
+ mHandler.post(this::handleSurfacePackageReleased);
+ }
+
+ /**
+ * Returns true if the provided size matches the remote view's size.
+ */
+ boolean match(int width, int height) {
+ return mWidth == width && mHeight == height;
+ }
+
+ private void handleSurfacePackageReleased() {
+ cancelPendingReleaseViewRequest();
+
+ // Schedule a delayed release view request
+ mDelayedReleaseViewRunnable = () -> {
+ if (mInlineSuggestionUi != null) {
+ try {
+ mInlineSuggestionUi.releaseSurfaceControlViewHost();
+ mInlineSuggestionUi = null;
+ } catch (RemoteException e) {
+ Slog.w(TAG, "RemoteException calling releaseSurfaceControlViewHost");
+ }
+ }
+ mDelayedReleaseViewRunnable = null;
+ };
+ mHandler.postDelayed(mDelayedReleaseViewRunnable, RELEASE_REMOTE_VIEW_HOST_DELAY_MS);
+ }
+
+ private void handleRequestSurfacePackage() {
+ cancelPendingReleaseViewRequest();
+
+ if (mInlineSuggestionUi == null) {
+ if (mWaitingForUiCreation) {
+ // This could happen in the following case: the remote embedded view was released
+ // when previously detached from window. An event after that to re-attached to
+ // the window will cause us calling the renderSuggestion again. Now, before the
+ // render call returns a new surface package, if the view is detached and
+ // re-attached to the window, causing this method to be called again, we will get
+ // to this state. This request will be ignored and the surface package will still
+ // be sent back once the view is rendered.
+ if (sDebug) Slog.d(TAG, "Inline suggestion ui is not ready");
+ } else {
+ mRemoteInlineSuggestionViewConnector.renderSuggestion(mWidth, mHeight,
+ mInlineSuggestionUiCallback);
+ mWaitingForUiCreation = true;
+ }
+ } else {
+ try {
+ mInlineSuggestionUi.getSurfacePackage(new ISurfacePackageResultCallback.Stub() {
+ @Override
+ public void onResult(SurfaceControlViewHost.SurfacePackage result)
+ throws RemoteException {
+ if (sDebug) Slog.d(TAG, "Sending new SurfacePackage to IME");
+ mInlineContentCallback.onContent(result, mActualWidth, mActualHeight);
+ }
+ });
+ } catch (RemoteException e) {
+ Slog.w(TAG, "RemoteException calling getSurfacePackage.");
+ }
+ }
+ }
+
+ private void cancelPendingReleaseViewRequest() {
+ if (mDelayedReleaseViewRunnable != null) {
+ mHandler.removeCallbacks(mDelayedReleaseViewRunnable);
+ mDelayedReleaseViewRunnable = null;
+ }
+ }
+
+ /**
+ * This is called when a new inline suggestion UI is inflated from the ext services.
+ */
+ private void handleInlineSuggestionUiReady(IInlineSuggestionUi content,
+ SurfaceControlViewHost.SurfacePackage surfacePackage, int width, int height) {
+ mInlineSuggestionUi = content;
+ mWaitingForUiCreation = false;
+ mActualWidth = width;
+ mActualHeight = height;
+ if (mInlineContentCallback != null) {
+ try {
+ mInlineContentCallback.onContent(surfacePackage, mActualWidth, mActualHeight);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "RemoteException calling onContent");
+ }
+ }
+ if (surfacePackage != null) {
+ surfacePackage.release();
+ }
+ }
+
+ private void handleOnClick() {
+ // Autofill the value
+ mRemoteInlineSuggestionViewConnector.onClick();
+
+ // Notify the remote process (IME) that hosts the embedded UI that it's clicked
+ if (mInlineContentCallback != null) {
+ try {
+ mInlineContentCallback.onClick();
+ } catch (RemoteException e) {
+ Slog.w(TAG, "RemoteException calling onClick");
+ }
+ }
+ }
+
+ private void handleOnLongClick() {
+ // Notify the remote process (IME) that hosts the embedded UI that it's long clicked
+ if (mInlineContentCallback != null) {
+ try {
+ mInlineContentCallback.onLongClick();
+ } catch (RemoteException e) {
+ Slog.w(TAG, "RemoteException calling onLongClick");
+ }
+ }
+ }
+
+ private void handleOnError() {
+ mRemoteInlineSuggestionViewConnector.onError();
+ }
+
+ private void handleOnTransferTouchFocusToImeWindow(IBinder sourceInputToken, int displayId) {
+ mRemoteInlineSuggestionViewConnector.onTransferTouchFocusToImeWindow(sourceInputToken,
+ displayId);
+ }
+
+ private void handleOnStartIntentSender(IntentSender intentSender) {
+ mRemoteInlineSuggestionViewConnector.onStartIntentSender(intentSender);
+ }
+
+ /**
+ * Responsible for communicating with the inline suggestion view owning process.
+ */
+ private class InlineSuggestionUiCallbackImpl extends IInlineSuggestionUiCallback.Stub {
+
+ @Override
+ public void onClick() {
+ mHandler.post(RemoteInlineSuggestionUi.this::handleOnClick);
+ }
+
+ @Override
+ public void onLongClick() {
+ mHandler.post(RemoteInlineSuggestionUi.this::handleOnLongClick);
+ }
+
+ @Override
+ public void onContent(IInlineSuggestionUi content,
+ SurfaceControlViewHost.SurfacePackage surface, int width, int height) {
+ mHandler.post(() -> handleInlineSuggestionUiReady(content, surface, width, height));
+ }
+
+ @Override
+ public void onError() {
+ mHandler.post(RemoteInlineSuggestionUi.this::handleOnError);
+ }
+
+ @Override
+ public void onTransferTouchFocusToImeWindow(IBinder sourceInputToken, int displayId) {
+ mHandler.post(() -> handleOnTransferTouchFocusToImeWindow(sourceInputToken, displayId));
+ }
+
+ @Override
+ public void onStartIntentSender(IntentSender intentSender) {
+ mHandler.post(() -> handleOnStartIntentSender(intentSender));
+ }
+ }
+}
diff --git a/services/autofill/java/com/android/server/autofill/ui/RemoteInlineSuggestionViewConnector.java b/services/autofill/java/com/android/server/autofill/ui/RemoteInlineSuggestionViewConnector.java
new file mode 100644
index 000000000000..9d23c171800d
--- /dev/null
+++ b/services/autofill/java/com/android/server/autofill/ui/RemoteInlineSuggestionViewConnector.java
@@ -0,0 +1,125 @@
+/*
+ * 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.autofill.ui;
+
+import static com.android.server.autofill.Helper.sDebug;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.IntentSender;
+import android.os.IBinder;
+import android.service.autofill.IInlineSuggestionUiCallback;
+import android.service.autofill.InlinePresentation;
+import android.util.Slog;
+
+import com.android.server.LocalServices;
+import com.android.server.autofill.RemoteInlineSuggestionRenderService;
+import com.android.server.inputmethod.InputMethodManagerInternal;
+
+import java.util.function.Consumer;
+
+/**
+ * Wraps the parameters needed to create a new inline suggestion view in the remote renderer
+ * service, and handles the callback from the events on the created remote view.
+ */
+final class RemoteInlineSuggestionViewConnector {
+ private static final String TAG = RemoteInlineSuggestionViewConnector.class.getSimpleName();
+
+ @Nullable
+ private final RemoteInlineSuggestionRenderService mRemoteRenderService;
+ @NonNull
+ private final InlinePresentation mInlinePresentation;
+ @Nullable
+ private final IBinder mHostInputToken;
+ private final int mDisplayId;
+
+ @NonNull
+ private final Runnable mOnAutofillCallback;
+ @NonNull
+ private final Runnable mOnErrorCallback;
+ @NonNull
+ private final Consumer<IntentSender> mStartIntentSenderFromClientApp;
+
+ RemoteInlineSuggestionViewConnector(
+ @Nullable RemoteInlineSuggestionRenderService remoteRenderService,
+ @NonNull InlinePresentation inlinePresentation,
+ @Nullable IBinder hostInputToken,
+ int displayId,
+ @NonNull Runnable onAutofillCallback,
+ @NonNull Runnable onErrorCallback,
+ @NonNull Consumer<IntentSender> startIntentSenderFromClientApp) {
+ mRemoteRenderService = remoteRenderService;
+ mInlinePresentation = inlinePresentation;
+ mHostInputToken = hostInputToken;
+ mDisplayId = displayId;
+
+ mOnAutofillCallback = onAutofillCallback;
+ mOnErrorCallback = onErrorCallback;
+ mStartIntentSenderFromClientApp = startIntentSenderFromClientApp;
+ }
+
+ /**
+ * Calls the remote renderer service to create a new inline suggestion view.
+ *
+ * @return true if the call is made to the remote renderer service, false otherwise.
+ */
+ public boolean renderSuggestion(int width, int height,
+ @NonNull IInlineSuggestionUiCallback callback) {
+ if (mRemoteRenderService != null) {
+ if (sDebug) Slog.d(TAG, "Request to recreate the UI");
+ mRemoteRenderService.renderSuggestion(callback, mInlinePresentation, width, height,
+ mHostInputToken, mDisplayId);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Handles the callback for the event of remote view being clicked.
+ */
+ public void onClick() {
+ mOnAutofillCallback.run();
+ }
+
+ /**
+ * Handles the callback for the remote error when creating or interacting with the view.
+ */
+ public void onError() {
+ mOnErrorCallback.run();
+ }
+
+ /**
+ * Handles the callback for transferring the touch event on the remote view to the IME
+ * process.
+ */
+ public void onTransferTouchFocusToImeWindow(IBinder sourceInputToken, int displayId) {
+ final InputMethodManagerInternal inputMethodManagerInternal =
+ LocalServices.getService(InputMethodManagerInternal.class);
+ if (!inputMethodManagerInternal.transferTouchFocusToImeWindow(sourceInputToken,
+ displayId)) {
+ Slog.e(TAG, "Cannot transfer touch focus from suggestion to IME");
+ mOnErrorCallback.run();
+ }
+ }
+
+ /**
+ * Handles starting an intent sender from the client app's process.
+ */
+ public void onStartIntentSender(IntentSender intentSender) {
+ mStartIntentSenderFromClientApp.accept(intentSender);
+ }
+}
diff --git a/services/core/java/com/android/server/RescueParty.java b/services/core/java/com/android/server/RescueParty.java
index bfcde97d6c91..829fca66ec0d 100644
--- a/services/core/java/com/android/server/RescueParty.java
+++ b/services/core/java/com/android/server/RescueParty.java
@@ -253,10 +253,7 @@ public class RescueParty {
logCriticalInfo(Log.DEBUG,
"Finished rescue level " + levelToString(level));
} catch (Throwable t) {
- final String msg = ExceptionUtils.getCompleteMessage(t);
- EventLogTags.writeRescueFailure(level, msg);
- logCriticalInfo(Log.ERROR,
- "Failed rescue level " + levelToString(level) + ": " + msg);
+ logRescueException(level, t);
}
}
@@ -274,11 +271,31 @@ public class RescueParty {
resetAllSettings(context, Settings.RESET_MODE_TRUSTED_DEFAULTS, failedPackage);
break;
case LEVEL_FACTORY_RESET:
- RecoverySystem.rebootPromptAndWipeUserData(context, TAG);
+ // Request the reboot from a separate thread to avoid deadlock on PackageWatchdog
+ // when device shutting down.
+ Runnable runnable = new Runnable() {
+ @Override
+ public void run() {
+ try {
+ RecoverySystem.rebootPromptAndWipeUserData(context, TAG);
+ } catch (Throwable t) {
+ logRescueException(level, t);
+ }
+ }
+ };
+ Thread thread = new Thread(runnable);
+ thread.start();
break;
}
}
+ private static void logRescueException(int level, Throwable t) {
+ final String msg = ExceptionUtils.getCompleteMessage(t);
+ EventLogTags.writeRescueFailure(level, msg);
+ logCriticalInfo(Log.ERROR,
+ "Failed rescue level " + levelToString(level) + ": " + msg);
+ }
+
private static int mapRescueLevelToUserImpact(int rescueLevel) {
switch(rescueLevel) {
case LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS:
diff --git a/services/core/java/com/android/server/ServiceWatcher.java b/services/core/java/com/android/server/ServiceWatcher.java
index cfb79aa3a210..b1b5ec01df6a 100644
--- a/services/core/java/com/android/server/ServiceWatcher.java
+++ b/services/core/java/com/android/server/ServiceWatcher.java
@@ -169,7 +169,11 @@ public class ServiceWatcher implements ServiceConnection {
@Override
public String toString() {
- return component.toShortString() + "@" + version + "[u" + userId + "]";
+ if (component == null) {
+ return "none";
+ } else {
+ return component.toShortString() + "@" + version + "[u" + userId + "]";
+ }
}
}
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 14fe0c5e3c22..be539456ae7c 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -1579,6 +1579,14 @@ class StorageManagerService extends IStorageManager.Stub
writeSettingsLocked();
}
}
+
+ if (newState == VolumeInfo.STATE_MOUNTED) {
+ // Private volumes can be unmounted and re-mounted even after a user has
+ // been unlocked; on devices that support encryption keys tied to the filesystem,
+ // this requires setting up the keys again.
+ prepareUserStorageIfNeeded(vol);
+ }
+
// This is a blocking call to Storage Service which needs to process volume state changed
// before notifying other listeners.
// Intentionally called without the mLock to avoid deadlocking from the Storage Service.
@@ -3266,10 +3274,38 @@ class StorageManagerService extends IStorageManager.Stub
}
}
+ private void prepareUserStorageIfNeeded(VolumeInfo vol) {
+ if (vol.type != VolumeInfo.TYPE_PRIVATE) {
+ return;
+ }
+
+ final UserManager um = mContext.getSystemService(UserManager.class);
+ final UserManagerInternal umInternal =
+ LocalServices.getService(UserManagerInternal.class);
+
+ for (UserInfo user : um.getUsers(false /* includeDying */)) {
+ final int flags;
+ if (umInternal.isUserUnlockingOrUnlocked(user.id)) {
+ flags = StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE;
+ } else if (umInternal.isUserRunning(user.id)) {
+ flags = StorageManager.FLAG_STORAGE_DE;
+ } else {
+ continue;
+ }
+
+ prepareUserStorageInternal(vol.fsUuid, user.id, user.serialNumber, flags);
+ }
+ }
+
@Override
public void prepareUserStorage(String volumeUuid, int userId, int serialNumber, int flags) {
enforcePermission(android.Manifest.permission.STORAGE_INTERNAL);
+ prepareUserStorageInternal(volumeUuid, userId, serialNumber, flags);
+ }
+
+ private void prepareUserStorageInternal(String volumeUuid, int userId, int serialNumber,
+ int flags) {
try {
mVold.prepareUserStorage(volumeUuid, userId, serialNumber, flags);
// After preparing user storage, we should check if we should mount data mirror again,
diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java
index 70b639846e1e..26cb208a3d47 100644
--- a/services/core/java/com/android/server/UiModeManagerService.java
+++ b/services/core/java/com/android/server/UiModeManagerService.java
@@ -450,6 +450,14 @@ final class UiModeManagerService extends SystemService {
return oldNightMode != mNightMode;
}
+ private static long toMilliSeconds(LocalTime t) {
+ return t.toNanoOfDay() / 1000;
+ }
+
+ private static LocalTime fromMilliseconds(long t) {
+ return LocalTime.ofNanoOfDay(t * 1000);
+ }
+
private void registerScreenOffEventLocked() {
if (mPowerSave) return;
mWaitForScreenOff = true;
@@ -1385,8 +1393,11 @@ final class UiModeManagerService extends SystemService {
pw.println("UiModeManager service (uimode) commands:");
pw.println(" help");
pw.println(" Print this help text.");
- pw.println(" night [yes|no|auto]");
+ pw.println(" night [yes|no|auto|custom]");
pw.println(" Set or read night mode.");
+ pw.println(" time [start|end] <ISO time>");
+ pw.println(" Set custom start/end schedule time"
+ + " (night mode must be set to custom to apply).");
}
@Override
@@ -1399,6 +1410,8 @@ final class UiModeManagerService extends SystemService {
switch (cmd) {
case "night":
return handleNightMode();
+ case "time":
+ return handleCustomTime();
default:
return handleDefaultCommands(cmd);
}
@@ -1409,6 +1422,34 @@ final class UiModeManagerService extends SystemService {
return -1;
}
+ private int handleCustomTime() throws RemoteException {
+ final String modeStr = getNextArg();
+ if (modeStr == null) {
+ printCustomTime();
+ return 0;
+ }
+ switch (modeStr) {
+ case "start":
+ final String start = getNextArg();
+ mInterface.setCustomNightModeStart(toMilliSeconds(LocalTime.parse(start)));
+ return 0;
+ case "end":
+ final String end = getNextArg();
+ mInterface.setCustomNightModeEnd(toMilliSeconds(LocalTime.parse(end)));
+ return 0;
+ default:
+ getErrPrintWriter().println("command must be in [start|end]");
+ return -1;
+ }
+ }
+
+ private void printCustomTime() throws RemoteException {
+ getOutPrintWriter().println("start " + fromMilliseconds(
+ mInterface.getCustomNightModeStart()).toString());
+ getOutPrintWriter().println("end " + fromMilliseconds(
+ mInterface.getCustomNightModeEnd()).toString());
+ }
+
private int handleNightMode() throws RemoteException {
final PrintWriter err = getErrPrintWriter();
final String modeStr = getNextArg();
@@ -1424,7 +1465,8 @@ final class UiModeManagerService extends SystemService {
return 0;
} else {
err.println("Error: mode must be '" + NIGHT_MODE_STR_YES + "', '"
- + NIGHT_MODE_STR_NO + "', or '" + NIGHT_MODE_STR_AUTO + "'");
+ + NIGHT_MODE_STR_NO + "', or '" + NIGHT_MODE_STR_AUTO
+ + "', or '" + NIGHT_MODE_STR_CUSTOM + "'");
return -1;
}
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 8d3515202126..d914bda3eff4 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -8766,27 +8766,6 @@ public class ActivityManagerService extends IActivityManager.Stub
}
@Override
- public boolean isUidActiveOrForeground(int uid, String callingPackage) {
- if (!hasUsageStatsPermission(callingPackage)) {
- enforceCallingPermission(android.Manifest.permission.PACKAGE_USAGE_STATS,
- "isUidActiveOrForeground");
- }
- synchronized (this) {
- final boolean isActive = isUidActiveLocked(uid);
- if (isActive) {
- return true;
- }
- }
- final boolean isForeground = mAtmInternal.isUidForeground(uid);
- if (isForeground) {
- Slog.wtf(TAG, "isUidActiveOrForeground: isUidActive false but "
- + " isUidForeground true, uid:" + uid
- + " callingPackage:" + callingPackage);
- }
- return isForeground;
- }
-
- @Override
public void setPersistentVrThread(int tid) {
mActivityTaskManager.setPersistentVrThread(tid);
}
@@ -20201,4 +20180,15 @@ public class ActivityManagerService extends IActivityManager.Stub
mUsageStatsService.reportLocusUpdate(activity, userId, locusId, appToken);
}
}
+
+ @Override
+ public boolean isAppFreezerSupported() {
+ final long token = Binder.clearCallingIdentity();
+
+ try {
+ return mOomAdjuster.mCachedAppOptimizer.isFreezerSupported();
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index 86d9028f53dc..f9d204fa008e 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -33,6 +33,7 @@ import android.os.Trace;
import android.provider.DeviceConfig;
import android.provider.DeviceConfig.OnPropertiesChangedListener;
import android.provider.DeviceConfig.Properties;
+import android.provider.Settings;
import android.text.TextUtils;
import android.util.EventLog;
import android.util.Slog;
@@ -407,7 +408,7 @@ public final class CachedAppOptimizer {
/**
* Determines whether the freezer is correctly supported by this system
*/
- public boolean isFreezerSupported() {
+ public static boolean isFreezerSupported() {
boolean supported = false;
FileReader fr = null;
@@ -443,7 +444,13 @@ public final class CachedAppOptimizer {
*/
@GuardedBy("mPhenotypeFlagLock")
private void updateUseFreezer() {
- if (DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER_NATIVE_BOOT,
+ final String configOverride = Settings.Global.getString(mAm.mContext.getContentResolver(),
+ Settings.Global.CACHED_APPS_FREEZER_ENABLED);
+
+ if ("disabled".equals(configOverride)) {
+ mUseFreezer = false;
+ } else if ("enabled".equals(configOverride)
+ || DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER_NATIVE_BOOT,
KEY_USE_FREEZER, DEFAULT_USE_FREEZER)) {
mUseFreezer = isFreezerSupported();
}
diff --git a/services/core/java/com/android/server/appop/TEST_MAPPING b/services/core/java/com/android/server/appop/TEST_MAPPING
index 604b9f1ead6d..9cd903940a83 100644
--- a/services/core/java/com/android/server/appop/TEST_MAPPING
+++ b/services/core/java/com/android/server/appop/TEST_MAPPING
@@ -37,7 +37,12 @@
]
},
{
- "name": "CtsAppTestCases:ActivityManagerApi29Test"
+ "name": "CtsAppTestCases",
+ "options": [
+ {
+ "include-filter": "android.app.cts.ActivityManagerApi29Test"
+ }
+ ]
},
{
"name": "UidAtomTests:testAppOps"
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 7cac376ea7ae..17baead84f9d 100755
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -1217,6 +1217,8 @@ public class AudioService extends IAudioService.Stub
*/
@NonNull
public List<AudioProductStrategy> getAudioProductStrategies() {
+ // verify permissions
+ enforceModifyAudioRoutingPermission();
return AudioProductStrategy.getAudioProductStrategies();
}
@@ -1226,6 +1228,8 @@ public class AudioService extends IAudioService.Stub
*/
@NonNull
public List<AudioVolumeGroup> getAudioVolumeGroups() {
+ // verify permissions
+ enforceModifyAudioRoutingPermission();
return AudioVolumeGroup.getAudioVolumeGroups();
}
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index bafeb77c55e6..9411c5629457 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -1067,12 +1067,22 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
mColorFadeEnabled && mPowerState.getColorFadeLevel() == 1.0f;
final boolean brightnessIsTemporary =
mAppliedTemporaryBrightness || mAppliedTemporaryAutoBrightnessAdjustment;
- if (initialRampSkip || hasBrightnessBuckets
- || wasOrWillBeInVr || !isDisplayContentVisible || brightnessIsTemporary) {
- animateScreenBrightness(brightnessState, SCREEN_ANIMATION_RATE_MINIMUM);
- } else {
- animateScreenBrightness(brightnessState,
- slowChange ? mBrightnessRampRateSlow : mBrightnessRampRateFast);
+ // We only want to animate the brightness if it is between 0.0f and 1.0f.
+ // brightnessState can contain the values -1.0f and NaN, which we do not want to
+ // animate to. To avoid this, we check the value first.
+ // If the brightnessState is off (-1.0f) we still want to animate to the minimum
+ // brightness (0.0f) to accommodate for LED displays, which can appear bright to the
+ // user even when the display is all black.
+ float animateValue = brightnessState == PowerManager.BRIGHTNESS_OFF_FLOAT
+ ? PowerManager.BRIGHTNESS_MIN : brightnessState;
+ if (isValidBrightnessValue(animateValue)) {
+ if (initialRampSkip || hasBrightnessBuckets
+ || wasOrWillBeInVr || !isDisplayContentVisible || brightnessIsTemporary) {
+ animateScreenBrightness(animateValue, SCREEN_ANIMATION_RATE_MINIMUM);
+ } else {
+ animateScreenBrightness(animateValue,
+ slowChange ? mBrightnessRampRateSlow : mBrightnessRampRateFast);
+ }
}
if (!brightnessIsTemporary) {
diff --git a/services/core/java/com/android/server/display/DisplayPowerState.java b/services/core/java/com/android/server/display/DisplayPowerState.java
index 24e1b4edc8a6..4b6430d5197c 100644
--- a/services/core/java/com/android/server/display/DisplayPowerState.java
+++ b/services/core/java/com/android/server/display/DisplayPowerState.java
@@ -146,7 +146,7 @@ final class DisplayPowerState {
/**
* Sets the display brightness.
*
- * @param brightness The brightness, ranges from 0 (minimum / off) to 255 (brightest).
+ * @param brightness The brightness, ranges from 0.0f (minimum / off) to 1.0f (brightest).
*/
public void setScreenBrightness(float brightness) {
if (mScreenBrightness != brightness) {
diff --git a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
index 2cae1d64ca34..905a10bd641b 100644
--- a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
+++ b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
@@ -571,7 +571,7 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub {
// APK signatures is already verified elsewhere in PackageManager. We do not need to
// verify it again since it could cause a timeout for large APKs.
pkg.setSigningDetails(
- ParsingPackageUtils.collectCertificates(pkg, /* skipVerify= */ true));
+ ParsingPackageUtils.getSigningDetails(pkg, /* skipVerify= */ true));
return PackageInfoUtils.generate(
pkg,
null,
diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java
index 4f8708a7599a..ccbe96f30e04 100644
--- a/services/core/java/com/android/server/location/LocationManagerService.java
+++ b/services/core/java/com/android/server/location/LocationManagerService.java
@@ -69,6 +69,7 @@ import android.os.CancellationSignal;
import android.os.Handler;
import android.os.IBinder;
import android.os.ICancellationSignal;
+import android.os.ParcelFileDescriptor;
import android.os.PowerManager;
import android.os.PowerManager.ServiceType;
import android.os.PowerManagerInternal;
@@ -2600,6 +2601,14 @@ public class LocationManagerService extends ILocationManager.Stub {
}
@Override
+ public int handleShellCommand(ParcelFileDescriptor in, ParcelFileDescriptor out,
+ ParcelFileDescriptor err, String[] args) {
+ return new LocationShellCommand(this).exec(
+ this, in.getFileDescriptor(), out.getFileDescriptor(), err.getFileDescriptor(),
+ args);
+ }
+
+ @Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) {
return;
@@ -2658,8 +2667,7 @@ public class LocationManagerService extends ILocationManager.Stub {
mRequestStatistics.statistics);
for (Map.Entry<PackageProviderKey, PackageStatistics> entry
: sorted.entrySet()) {
- PackageProviderKey key = entry.getKey();
- ipw.println(key.mPackageName + ": " + key.mProviderName + ": " + entry.getValue());
+ ipw.println(entry.getKey() + ": " + entry.getValue());
}
ipw.decreaseIndent();
diff --git a/services/core/java/com/android/server/location/LocationRequestStatistics.java b/services/core/java/com/android/server/location/LocationRequestStatistics.java
index dcdf48ba08d2..e629b428d864 100644
--- a/services/core/java/com/android/server/location/LocationRequestStatistics.java
+++ b/services/core/java/com/android/server/location/LocationRequestStatistics.java
@@ -16,6 +16,7 @@
package com.android.server.location;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.SystemClock;
import android.util.Log;
@@ -25,6 +26,7 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.IndentingPrintWriter;
import java.util.ArrayList;
+import java.util.Comparator;
import java.util.HashMap;
import java.util.Objects;
@@ -121,14 +123,30 @@ public class LocationRequestStatistics {
this.mProviderName = providerName;
}
+ @NonNull
+ @Override
+ public String toString() {
+ return mProviderName + ": " + mPackageName
+ + (mFeatureId == null ? "" : ": " + mFeatureId);
+ }
+
+ /**
+ * Sort by provider, then package, then feature
+ */
@Override
public int compareTo(PackageProviderKey other) {
final int providerCompare = mProviderName.compareTo(other.mProviderName);
if (providerCompare != 0) {
return providerCompare;
- } else {
- return mProviderName.compareTo(other.mProviderName);
}
+
+ final int packageCompare = mPackageName.compareTo(other.mPackageName);
+ if (packageCompare != 0) {
+ return packageCompare;
+ }
+
+ return Objects.compare(mFeatureId, other.mFeatureId, Comparator
+ .nullsFirst(String::compareTo));
}
@Override
diff --git a/services/core/java/com/android/server/location/LocationShellCommand.java b/services/core/java/com/android/server/location/LocationShellCommand.java
new file mode 100644
index 000000000000..909873f07993
--- /dev/null
+++ b/services/core/java/com/android/server/location/LocationShellCommand.java
@@ -0,0 +1,92 @@
+/*
+ * 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.location;
+
+import android.os.BasicShellCommandHandler;
+import android.os.UserHandle;
+
+import java.io.PrintWriter;
+import java.util.Objects;
+
+/**
+ * Interprets and executes 'adb shell cmd location [args]'.
+ */
+class LocationShellCommand extends BasicShellCommandHandler {
+
+ private final LocationManagerService mService;
+
+ LocationShellCommand(LocationManagerService service) {
+ mService = Objects.requireNonNull(service);
+ }
+
+ @Override
+ public int onCommand(String cmd) {
+ if (cmd == null) {
+ return handleDefaultCommands(null);
+ }
+
+ switch (cmd) {
+ case "set-location-enabled": {
+ int userId = parseUserId();
+ boolean enabled = Boolean.parseBoolean(getNextArgRequired());
+ mService.setLocationEnabledForUser(enabled, userId);
+ return 0;
+ }
+ case "send-extra-command": {
+ String provider = getNextArgRequired();
+ String command = getNextArgRequired();
+ mService.sendExtraCommand(provider, command, null);
+ return 0;
+ }
+ default:
+ return handleDefaultCommands(cmd);
+ }
+ }
+
+ private int parseUserId() {
+ final String option = getNextOption();
+ if (option != null) {
+ if (option.equals("--user")) {
+ return UserHandle.parseUserArg(getNextArgRequired());
+ } else {
+ throw new IllegalArgumentException(
+ "Expected \"--user\" option, but got \"" + option + "\" instead");
+ }
+ }
+
+ return UserHandle.USER_CURRENT_OR_SELF;
+ }
+
+ @Override
+ public void onHelp() {
+ PrintWriter pw = getOutPrintWriter();
+ pw.println("Location service commands:");
+ pw.println(" help or -h");
+ pw.println(" Print this help text.");
+ pw.println(" set-location-enabled [--user <USER_ID>] true|false");
+ pw.println(" Sets the master location switch enabled state.");
+ pw.println(" send-extra-command <PROVIDER> <COMMAND>");
+ pw.println(" Sends the given extra command to the given provider.");
+ pw.println();
+ pw.println(" Common commands that may be supported by the gps provider, depending on");
+ pw.println(" hardware and software configurations:");
+ pw.println(" delete_aiding_data - requests deletion of any predictive aiding data");
+ pw.println(" force_time_injection - requests NTP time injection to chipset");
+ pw.println(" force_psds_injection - "
+ + "requests predictive aiding data injection to chipset");
+ }
+}
diff --git a/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java b/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java
index a5de90c93aab..a435f1e16b80 100644
--- a/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java
+++ b/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java
@@ -315,9 +315,7 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider
return;
}
- sessionInfo = new RoutingSessionInfo.Builder(sessionInfo)
- .setProviderId(getUniqueId())
- .build();
+ sessionInfo = updateSessionInfo(sessionInfo);
boolean duplicateSessionAlreadyExists = false;
synchronized (mLock) {
@@ -348,9 +346,7 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider
return;
}
- sessionInfo = new RoutingSessionInfo.Builder(sessionInfo)
- .setProviderId(getUniqueId())
- .build();
+ sessionInfo = updateSessionInfo(sessionInfo);
boolean found = false;
synchronized (mLock) {
@@ -380,9 +376,7 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider
return;
}
- sessionInfo = new RoutingSessionInfo.Builder(sessionInfo)
- .setProviderId(getUniqueId())
- .build();
+ sessionInfo = updateSessionInfo(sessionInfo);
boolean found = false;
synchronized (mLock) {
@@ -403,6 +397,13 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider
mCallback.onSessionReleased(this, sessionInfo);
}
+ private RoutingSessionInfo updateSessionInfo(RoutingSessionInfo sessionInfo) {
+ return new RoutingSessionInfo.Builder(sessionInfo)
+ .setOwnerPackageName(mComponentName.getPackageName())
+ .setProviderId(getUniqueId())
+ .build();
+ }
+
private void onRequestFailed(Connection connection, long requestId, int reason) {
if (mActiveConnection != connection) {
return;
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index ec941c8aea59..3283fd9b2c51 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -799,7 +799,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
writePolicyAL();
}
- enableFirewallChainUL(FIREWALL_CHAIN_STANDBY, true);
setRestrictBackgroundUL(mLoadedRestrictBackground, "init_service");
updateRulesForGlobalChangeAL(false);
updateNotificationsNL();
@@ -871,6 +870,9 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
new NetworkRequest.Builder().build(), mNetworkCallback);
mAppStandby.addListener(new NetPolicyAppIdleStateChangeListener());
+ synchronized (mUidRulesFirstLock) {
+ updateRulesForAppIdleParoleUL();
+ }
// Listen for subscriber changes
mContext.getSystemService(SubscriptionManager.class).addOnSubscriptionsChangedListener(
@@ -3893,6 +3895,39 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
}
/**
+ * Toggle the firewall standby chain and inform listeners if the uid rules have effectively
+ * changed.
+ */
+ @GuardedBy("mUidRulesFirstLock")
+ private void updateRulesForAppIdleParoleUL() {
+ final boolean paroled = mAppStandby.isInParole();
+ final boolean enableChain = !paroled;
+ enableFirewallChainUL(FIREWALL_CHAIN_STANDBY, enableChain);
+
+ int ruleCount = mUidFirewallStandbyRules.size();
+ for (int i = 0; i < ruleCount; i++) {
+ final int uid = mUidFirewallStandbyRules.keyAt(i);
+ int oldRules = mUidRules.get(uid);
+ if (enableChain) {
+ // Chain wasn't enabled before and the other power-related
+ // chains are whitelists, so we can clear the
+ // MASK_ALL_NETWORKS part of the rules and re-inform listeners if
+ // the effective rules result in blocking network access.
+ oldRules &= MASK_METERED_NETWORKS;
+ } else {
+ // Skip if it had no restrictions to begin with
+ if ((oldRules & MASK_ALL_NETWORKS) == 0) continue;
+ }
+ final int newUidRules = updateRulesForPowerRestrictionsUL(uid, oldRules, paroled);
+ if (newUidRules == RULE_NONE) {
+ mUidRules.delete(uid);
+ } else {
+ mUidRules.put(uid, newUidRules);
+ }
+ }
+ }
+
+ /**
* Update rules that might be changed by {@link #mRestrictBackground},
* {@link #mRestrictPower}, or {@link #mDeviceIdleMode} value.
*/
@@ -4347,7 +4382,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
private void updateRulesForPowerRestrictionsUL(int uid) {
final int oldUidRules = mUidRules.get(uid, RULE_NONE);
- final int newUidRules = updateRulesForPowerRestrictionsUL(uid, oldUidRules);
+ final int newUidRules = updateRulesForPowerRestrictionsUL(uid, oldUidRules, false);
if (newUidRules == RULE_NONE) {
mUidRules.delete(uid);
@@ -4361,28 +4396,30 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
*
* @param uid the uid of the app to update rules for
* @param oldUidRules the current rules for the uid, in order to determine if there's a change
+ * @param paroled whether to ignore idle state of apps and only look at other restrictions
*
* @return the new computed rules for the uid
*/
- private int updateRulesForPowerRestrictionsUL(int uid, int oldUidRules) {
+ private int updateRulesForPowerRestrictionsUL(int uid, int oldUidRules, boolean paroled) {
if (Trace.isTagEnabled(Trace.TRACE_TAG_NETWORK)) {
Trace.traceBegin(Trace.TRACE_TAG_NETWORK,
- "updateRulesForPowerRestrictionsUL: " + uid + "/" + oldUidRules);
+ "updateRulesForPowerRestrictionsUL: " + uid + "/" + oldUidRules + "/"
+ + (paroled ? "P" : "-"));
}
try {
- return updateRulesForPowerRestrictionsULInner(uid, oldUidRules);
+ return updateRulesForPowerRestrictionsULInner(uid, oldUidRules, paroled);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_NETWORK);
}
}
- private int updateRulesForPowerRestrictionsULInner(int uid, int oldUidRules) {
+ private int updateRulesForPowerRestrictionsULInner(int uid, int oldUidRules, boolean paroled) {
if (!isUidValidForBlacklistRules(uid)) {
if (LOGD) Slog.d(TAG, "no need to update restrict power rules for uid " + uid);
return RULE_NONE;
}
- final boolean isIdle = isUidIdle(uid);
+ final boolean isIdle = !paroled && isUidIdle(uid);
final boolean restrictMode = isIdle || mRestrictPower || mDeviceIdleMode;
final boolean isForeground = isUidForegroundOnRestrictPowerUL(uid);
@@ -4452,6 +4489,14 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
} catch (NameNotFoundException nnfe) {
}
}
+
+ @Override
+ public void onParoleStateChanged(boolean isParoleOn) {
+ synchronized (mUidRulesFirstLock) {
+ mLogger.paroleStateChanged(isParoleOn);
+ updateRulesForAppIdleParoleUL();
+ }
+ }
}
private void dispatchUidRulesChanged(INetworkPolicyListener listener, int uid, int uidRules) {
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index 8e3de1598275..a9fa2b1bd491 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -1382,13 +1382,21 @@ public final class NotificationRecord {
*/
public boolean isConversation() {
Notification notification = getNotification();
- if (mChannel.isDemoted()
- || !Notification.MessagingStyle.class.equals(notification.getNotificationStyle())) {
+ if (!Notification.MessagingStyle.class.equals(notification.getNotificationStyle())) {
+ // very common; don't bother logging
+ return false;
+ }
+ if (mChannel.isDemoted()) {
return false;
}
if (mIsNotConversationOverride) {
return false;
}
+ if (mTargetSdkVersion >= Build.VERSION_CODES.R
+ && Notification.MessagingStyle.class.equals(notification.getNotificationStyle())
+ && mShortcutInfo == null) {
+ return false;
+ }
return true;
}
diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java
index acce6992ea79..92c145275ac8 100644
--- a/services/core/java/com/android/server/pm/ApexManager.java
+++ b/services/core/java/com/android/server/pm/ApexManager.java
@@ -509,7 +509,7 @@ public abstract class ApexManager {
ParsedPackage pp = parseResult.parsedPackage;
try {
pp.setSigningDetails(
- ParsingPackageUtils.collectCertificates(pp, false));
+ ParsingPackageUtils.getSigningDetails(pp, false));
} catch (PackageParserException e) {
throw new IllegalStateException(
"Unable to collect certificates for " + ai.modulePath, e);
diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java
index 118fdcbcb736..70d1adecc6f3 100644
--- a/services/core/java/com/android/server/pm/AppsFilter.java
+++ b/services/core/java/com/android/server/pm/AppsFilter.java
@@ -725,7 +725,7 @@ public class AppsFilter {
}
final PackageSetting callingPkgSetting;
final ArraySet<PackageSetting> callingSharedPkgSettings;
- Trace.beginSection("callingSetting instanceof");
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "callingSetting instanceof");
if (callingSetting instanceof PackageSetting) {
callingPkgSetting = (PackageSetting) callingSetting;
callingSharedPkgSettings = null;
@@ -733,7 +733,7 @@ public class AppsFilter {
callingPkgSetting = null;
callingSharedPkgSettings = ((SharedUserSetting) callingSetting).packages;
}
- Trace.endSection();
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
if (callingPkgSetting != null) {
if (callingPkgSetting.pkg != null
@@ -769,7 +769,7 @@ public class AppsFilter {
return false;
}
final String targetName = targetPkg.getPackageName();
- Trace.beginSection("getAppId");
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "getAppId");
final int callingAppId;
if (callingPkgSetting != null) {
callingAppId = callingPkgSetting.appId;
@@ -777,7 +777,7 @@ public class AppsFilter {
callingAppId = callingSharedPkgSettings.valueAt(0).appId; // all should be the same
}
final int targetAppId = targetPkgSetting.appId;
- Trace.endSection();
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
if (callingAppId == targetAppId) {
if (DEBUG_LOGGING) {
log(callingSetting, targetPkgSetting, "same app id");
@@ -786,7 +786,7 @@ public class AppsFilter {
}
try {
- Trace.beginSection("hasPermission");
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "hasPermission");
if (callingSetting.getPermissionsState().hasPermission(
Manifest.permission.QUERY_ALL_PACKAGES, UserHandle.getUserId(callingUid))) {
if (DEBUG_LOGGING) {
@@ -795,10 +795,10 @@ public class AppsFilter {
return false;
}
} finally {
- Trace.endSection();
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
try {
- Trace.beginSection("mForceQueryable");
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "mForceQueryable");
if (mForceQueryable.contains(targetAppId)) {
if (DEBUG_LOGGING) {
log(callingSetting, targetPkgSetting, "force queryable");
@@ -806,10 +806,10 @@ public class AppsFilter {
return false;
}
} finally {
- Trace.endSection();
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
try {
- Trace.beginSection("mQueriesViaPackage");
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "mQueriesViaPackage");
if (mQueriesViaPackage.contains(callingAppId, targetAppId)) {
if (DEBUG_LOGGING) {
log(callingSetting, targetPkgSetting, "queries package");
@@ -817,10 +817,10 @@ public class AppsFilter {
return false;
}
} finally {
- Trace.endSection();
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
try {
- Trace.beginSection("mQueriesViaComponent");
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "mQueriesViaComponent");
if (mQueriesViaComponent.contains(callingAppId, targetAppId)) {
if (DEBUG_LOGGING) {
log(callingSetting, targetPkgSetting, "queries component");
@@ -828,11 +828,11 @@ public class AppsFilter {
return false;
}
} finally {
- Trace.endSection();
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
try {
- Trace.beginSection("mImplicitlyQueryable");
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "mImplicitlyQueryable");
final int targetUid = UserHandle.getUid(userId, targetAppId);
if (mImplicitlyQueryable.contains(callingUid, targetUid)) {
if (DEBUG_LOGGING) {
@@ -841,11 +841,11 @@ public class AppsFilter {
return false;
}
} finally {
- Trace.endSection();
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
try {
- Trace.beginSection("mOverlayReferenceMapper");
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "mOverlayReferenceMapper");
if (callingSharedPkgSettings != null) {
int size = callingSharedPkgSettings.size();
for (int index = 0; index < size; index++) {
@@ -868,7 +868,7 @@ public class AppsFilter {
}
}
} finally {
- Trace.endSection();
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 3367cd556b2b..5b5f334803e5 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -676,7 +676,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
session = new PackageInstallerSession(mInternalCallback, mContext, mPm, this,
mInstallThread.getLooper(), mStagingManager, sessionId, userId, callingUid,
installSource, params, createdMillis,
- stageDir, stageCid, null, false, false, false, null, SessionInfo.INVALID_ID,
+ stageDir, stageCid, null, false, false, false, false, null, SessionInfo.INVALID_ID,
false, false, false, SessionInfo.STAGED_SESSION_NO_ERROR, "");
synchronized (mSessions) {
@@ -830,7 +830,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
synchronized (mSessions) {
final PackageInstallerSession session = mSessions.get(sessionId);
- return session != null
+ return (session != null && !(session.isStaged() && session.isDestroyed()))
? session.generateInfoForCaller(true /*withIcon*/, Binder.getCallingUid())
: null;
}
@@ -851,7 +851,8 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
synchronized (mSessions) {
for (int i = 0; i < mSessions.size(); i++) {
final PackageInstallerSession session = mSessions.valueAt(i);
- if (session.userId == userId && !session.hasParentSessionId()) {
+ if (session.userId == userId && !session.hasParentSessionId()
+ && !(session.isStaged() && session.isDestroyed())) {
result.add(session.generateInfoForCaller(false, callingUid));
}
}
@@ -1269,7 +1270,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
public void onStagedSessionChanged(PackageInstallerSession session) {
session.markUpdated();
writeSessionsAsync();
- if (mOkToSendBroadcasts) {
+ if (mOkToSendBroadcasts && !session.isDestroyed()) {
// we don't scrub the data here as this is sent only to the installer several
// privileged system packages
mPm.sendSessionUpdatedBroadcast(
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 3df044fab30d..e0d057a8d7f3 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -86,6 +86,8 @@ import android.content.pm.PackageParser.PackageLite;
import android.content.pm.PackageParser.PackageParserException;
import android.content.pm.dex.DexMetadataHelper;
import android.content.pm.parsing.ApkLiteParseUtils;
+import android.content.pm.parsing.result.ParseResult;
+import android.content.pm.parsing.result.ParseTypeImpl;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Binder;
@@ -188,6 +190,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
private static final String ATTR_SESSION_STAGE_CID = "sessionStageCid";
private static final String ATTR_PREPARED = "prepared";
private static final String ATTR_COMMITTED = "committed";
+ private static final String ATTR_DESTROYED = "destroyed";
private static final String ATTR_SEALED = "sealed";
private static final String ATTR_MULTI_PACKAGE = "multiPackage";
private static final String ATTR_PARENT_SESSION_ID = "parentSessionId";
@@ -531,7 +534,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
int sessionId, int userId, int installerUid, @NonNull InstallSource installSource,
SessionParams params, long createdMillis,
File stageDir, String stageCid, InstallationFile[] files, boolean prepared,
- boolean committed, boolean sealed,
+ boolean committed, boolean destroyed, boolean sealed,
@Nullable int[] childSessionIds, int parentSessionId, boolean isReady,
boolean isFailed, boolean isApplied, int stagedSessionErrorCode,
String stagedSessionErrorMessage) {
@@ -577,6 +580,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
mPrepared = prepared;
mCommitted = committed;
+ mDestroyed = destroyed;
mStagedSessionReady = isReady;
mStagedSessionFailed = isFailed;
mStagedSessionApplied = isApplied;
@@ -711,6 +715,13 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
}
+ /** {@hide} */
+ boolean isDestroyed() {
+ synchronized (mLock) {
+ return mDestroyed;
+ }
+ }
+
/** Returns true if a staged session has reached a final state and can be forgotten about */
public boolean isStagedAndInTerminalState() {
synchronized (mLock) {
@@ -1066,6 +1077,19 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
/**
+ * Check if the caller is the owner of this session. Otherwise throw a
+ * {@link SecurityException}.
+ */
+ @GuardedBy("mLock")
+ private void assertCallerIsOwnerOrRootOrSystemLocked() {
+ final int callingUid = Binder.getCallingUid();
+ if (callingUid != Process.ROOT_UID && callingUid != mInstallerUid
+ && callingUid != Process.SYSTEM_UID) {
+ throw new SecurityException("Session does not belong to uid " + callingUid);
+ }
+ }
+
+ /**
* If anybody is reading or writing data of the session, throw an {@link SecurityException}.
*/
@GuardedBy("mLock")
@@ -2016,15 +2040,16 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
// Verify that all staged packages are internally consistent
final ArraySet<String> stagedSplits = new ArraySet<>();
+ ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
for (File addedFile : addedFiles) {
- final ApkLite apk;
- try {
- apk = ApkLiteParseUtils.parseApkLite(
- addedFile, PackageParser.PARSE_COLLECT_CERTIFICATES);
- } catch (PackageParserException e) {
- throw PackageManagerException.from(e);
+ ParseResult<ApkLite> result = ApkLiteParseUtils.parseApkLite(input.reset(),
+ addedFile, PackageParser.PARSE_COLLECT_CERTIFICATES);
+ if (result.isError()) {
+ throw new PackageManagerException(result.getErrorCode(),
+ result.getErrorMessage(), result.getException());
}
+ final ApkLite apk = result.getResult();
if (!stagedSplits.add(apk.splitName)) {
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
"Split " + apk.splitName + " was defined multiple times");
@@ -2113,16 +2138,22 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
} else {
- final PackageLite existing;
- final ApkLite existingBase;
ApplicationInfo appInfo = pkgInfo.applicationInfo;
- try {
- existing = PackageParser.parsePackageLite(new File(appInfo.getCodePath()), 0);
- existingBase = ApkLiteParseUtils.parseApkLite(new File(appInfo.getBaseCodePath()),
- PackageParser.PARSE_COLLECT_CERTIFICATES);
- } catch (PackageParserException e) {
- throw PackageManagerException.from(e);
- }
+ ParseResult<PackageLite> pkgLiteResult = ApkLiteParseUtils.parsePackageLite(
+ input.reset(), new File(appInfo.getCodePath()), 0);
+ if (pkgLiteResult.isError()) {
+ throw new PackageManagerException(PackageManager.INSTALL_FAILED_INTERNAL_ERROR,
+ pkgLiteResult.getErrorMessage(), pkgLiteResult.getException());
+ }
+ final PackageLite existing = pkgLiteResult.getResult();
+ ParseResult<ApkLite> apkLiteResult = ApkLiteParseUtils.parseApkLite(input.reset(),
+ new File(appInfo.getBaseCodePath()),
+ PackageParser.PARSE_COLLECT_CERTIFICATES);
+ if (apkLiteResult.isError()) {
+ throw new PackageManagerException(PackageManager.INSTALL_FAILED_INTERNAL_ERROR,
+ apkLiteResult.getErrorMessage(), apkLiteResult.getException());
+ }
+ final ApkLite existingBase = apkLiteResult.getResult();
assertApkConsistentLocked("Existing base", existingBase);
@@ -2543,7 +2574,13 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
+ mParentSessionId + " and may not be abandoned directly.");
}
synchronized (mLock) {
- assertCallerIsOwnerOrRootLocked();
+ if (params.isStaged && mDestroyed) {
+ // If a user abandons staged session in an unsafe state, then system will try to
+ // abandon the destroyed staged session when it is safe on behalf of the user.
+ assertCallerIsOwnerOrRootOrSystemLocked();
+ } else {
+ assertCallerIsOwnerOrRootLocked();
+ }
if (isStagedAndInTerminalState()) {
// We keep the session in the database if it's in a finalized state. It will be
@@ -2553,11 +2590,12 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
return;
}
if (mCommitted && params.isStaged) {
- synchronized (mLock) {
- mDestroyed = true;
+ mDestroyed = true;
+ if (!mStagingManager.abortCommittedSessionLocked(this)) {
+ // Do not clean up the staged session from system. It is not safe yet.
+ mCallback.onStagedSessionChanged(this);
+ return;
}
- mStagingManager.abortCommittedSession(this);
-
cleanStageDir();
}
@@ -2917,6 +2955,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
/** {@hide} */
void setStagedSessionReady() {
synchronized (mLock) {
+ if (mDestroyed) return; // Do not allow destroyed staged session to change state
mStagedSessionReady = true;
mStagedSessionApplied = false;
mStagedSessionFailed = false;
@@ -2930,6 +2969,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
void setStagedSessionFailed(@StagedSessionErrorCode int errorCode,
String errorMessage) {
synchronized (mLock) {
+ if (mDestroyed) return; // Do not allow destroyed staged session to change state
mStagedSessionReady = false;
mStagedSessionApplied = false;
mStagedSessionFailed = true;
@@ -2944,6 +2984,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
/** {@hide} */
void setStagedSessionApplied() {
synchronized (mLock) {
+ if (mDestroyed) return; // Do not allow destroyed staged session to change state
mStagedSessionReady = false;
mStagedSessionApplied = true;
mStagedSessionFailed = false;
@@ -3188,7 +3229,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
*/
void write(@NonNull XmlSerializer out, @NonNull File sessionsDir) throws IOException {
synchronized (mLock) {
- if (mDestroyed) {
+ if (mDestroyed && !params.isStaged) {
return;
}
@@ -3214,6 +3255,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
writeBooleanAttribute(out, ATTR_PREPARED, isPrepared());
writeBooleanAttribute(out, ATTR_COMMITTED, isCommitted());
+ writeBooleanAttribute(out, ATTR_DESTROYED, isDestroyed());
writeBooleanAttribute(out, ATTR_SEALED, isSealed());
writeBooleanAttribute(out, ATTR_MULTI_PACKAGE, params.isMultiPackage);
@@ -3343,6 +3385,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
final String stageCid = readStringAttribute(in, ATTR_SESSION_STAGE_CID);
final boolean prepared = readBooleanAttribute(in, ATTR_PREPARED, true);
final boolean committed = readBooleanAttribute(in, ATTR_COMMITTED);
+ final boolean destroyed = readBooleanAttribute(in, ATTR_DESTROYED);
final boolean sealed = readBooleanAttribute(in, ATTR_SEALED);
final int parentSessionId = readIntAttribute(in, ATTR_PARENT_SESSION_ID,
SessionInfo.INVALID_ID);
@@ -3464,7 +3507,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
return new PackageInstallerSession(callback, context, pm, sessionProvider,
installerThread, stagingManager, sessionId, userId, installerUid,
installSource, params, createdMillis, stageDir, stageCid, fileArray,
- prepared, committed, sealed, childSessionIdsArray, parentSessionId,
+ prepared, committed, destroyed, sealed, childSessionIdsArray, parentSessionId,
isReady, isFailed, isApplied, stagedSessionErrorCode, stagedSessionErrorMessage);
}
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index f6758d7b4dff..f31dbbf077bb 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -223,6 +223,7 @@ import android.content.pm.VersionedPackage;
import android.content.pm.dex.ArtManager;
import android.content.pm.dex.DexMetadataHelper;
import android.content.pm.dex.IArtManager;
+import android.content.pm.parsing.ApkLiteParseUtils;
import android.content.pm.parsing.ParsingPackageUtils;
import android.content.pm.parsing.component.ParsedActivity;
import android.content.pm.parsing.component.ParsedInstrumentation;
@@ -232,6 +233,8 @@ import android.content.pm.parsing.component.ParsedPermission;
import android.content.pm.parsing.component.ParsedProcess;
import android.content.pm.parsing.component.ParsedProvider;
import android.content.pm.parsing.component.ParsedService;
+import android.content.pm.parsing.result.ParseResult;
+import android.content.pm.parsing.result.ParseTypeImpl;
import android.content.res.Resources;
import android.content.rollback.IRollbackManager;
import android.database.ContentObserver;
@@ -9057,7 +9060,7 @@ public class PackageManagerService extends IPackageManager.Stub
try {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectCertificates");
parsedPackage.setSigningDetails(
- ParsingPackageUtils.collectCertificates(parsedPackage, skipVerify));
+ ParsingPackageUtils.getSigningDetails(parsedPackage, skipVerify));
} catch (PackageParserException e) {
throw PackageManagerException.from(e);
} finally {
@@ -15241,12 +15244,21 @@ public class PackageManagerService extends IPackageManager.Stub
&& mIntegrityVerificationCompleted && mEnableRollbackCompleted) {
if ((installFlags & PackageManager.INSTALL_DRY_RUN) != 0) {
String packageName = "";
- try {
- PackageLite packageInfo =
- new PackageParser().parsePackageLite(origin.file, 0);
- packageName = packageInfo.packageName;
- } catch (PackageParserException e) {
- Slog.e(TAG, "Can't parse package at " + origin.file.getAbsolutePath(), e);
+ ParseResult<PackageLite> result = ApkLiteParseUtils.parsePackageLite(
+ new ParseTypeImpl(
+ (changeId, packageName1, targetSdkVersion) -> {
+ ApplicationInfo appInfo = new ApplicationInfo();
+ appInfo.packageName = packageName1;
+ appInfo.targetSdkVersion = targetSdkVersion;
+ return mPackageParserCallback.isChangeEnabled(changeId,
+ appInfo);
+ }).reset(),
+ origin.file, 0);
+ if (result.isError()) {
+ Slog.e(TAG, "Can't parse package at " + origin.file.getAbsolutePath(),
+ result.getException());
+ } else {
+ packageName = result.getResult().packageName;
}
try {
observer.onPackageInstalled(packageName, mRet, "Dry run", new Bundle());
@@ -17055,7 +17067,7 @@ public class PackageManagerService extends IPackageManager.Stub
parsedPackage.setSigningDetails(args.signingDetails);
} else {
parsedPackage.setSigningDetails(
- ParsingPackageUtils.collectCertificates(parsedPackage, false /* skipVerify */));
+ ParsingPackageUtils.getSigningDetails(parsedPackage, false /* skipVerify */));
}
} catch (PackageParserException e) {
throw new PrepareFailure("Failed collect during installPackageLI", e);
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 88f442c7b6a0..bc94528c07f0 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -51,7 +51,6 @@ import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PackageManagerInternal;
import android.content.pm.PackageParser.ApkLite;
import android.content.pm.PackageParser.PackageLite;
-import android.content.pm.PackageParser.PackageParserException;
import android.content.pm.ParceledListSlice;
import android.content.pm.PermissionGroupInfo;
import android.content.pm.PermissionInfo;
@@ -63,6 +62,8 @@ import android.content.pm.dex.ArtManager;
import android.content.pm.dex.DexMetadataHelper;
import android.content.pm.dex.ISnapshotRuntimeProfileCallback;
import android.content.pm.parsing.ApkLiteParseUtils;
+import android.content.pm.parsing.result.ParseResult;
+import android.content.pm.parsing.result.ParseTypeImpl;
import android.content.res.AssetManager;
import android.content.res.Resources;
import android.content.rollback.IRollbackManager;
@@ -505,6 +506,7 @@ class PackageManagerShellCommand extends ShellCommand {
long sessionSize = 0;
+ ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
for (String inPath : inPaths) {
final ParcelFileDescriptor fd = openFileForSystem(inPath, "r");
if (fd == null) {
@@ -512,12 +514,19 @@ class PackageManagerShellCommand extends ShellCommand {
throw new IllegalArgumentException("Error: Can't open file: " + inPath);
}
try {
- ApkLite baseApk = ApkLiteParseUtils.parseApkLite(fd.getFileDescriptor(), inPath, 0);
- PackageLite pkgLite = new PackageLite(null, baseApk, null, null, null, null,
- null, null);
+ ParseResult<ApkLite> apkLiteResult = ApkLiteParseUtils.parseApkLite(
+ input.reset(), fd.getFileDescriptor(), inPath, 0);
+ if (apkLiteResult.isError()) {
+ throw new IllegalArgumentException(
+ "Error: Failed to parse APK file: " + inPath + ": "
+ + apkLiteResult.getErrorMessage(),
+ apkLiteResult.getException());
+ }
+ PackageLite pkgLite = new PackageLite(null, apkLiteResult.getResult(), null, null,
+ null, null, null, null);
sessionSize += PackageHelper.calculateInstalledSize(pkgLite,
params.sessionParams.abiOverride, fd.getFileDescriptor());
- } catch (PackageParserException | IOException e) {
+ } catch (IOException e) {
getErrPrintWriter().println("Error: Failed to parse APK file: " + inPath);
throw new IllegalArgumentException(
"Error: Failed to parse APK file: " + inPath, e);
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index 9a297d601a6b..a83fa32ec9a9 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -61,6 +61,7 @@ import android.text.TextUtils;
import android.util.IntArray;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
import android.util.apk.ApkSignatureVerifier;
@@ -137,6 +138,9 @@ public class StagingManager {
synchronized (mStagedSessions) {
for (int i = 0; i < mStagedSessions.size(); i++) {
final PackageInstallerSession stagedSession = mStagedSessions.valueAt(i);
+ if (stagedSession.isDestroyed()) {
+ continue;
+ }
result.add(stagedSession.generateInfoForCaller(false /*icon*/, callingUid));
}
}
@@ -202,7 +206,7 @@ public class StagingManager {
final IntArray childSessionIds = new IntArray();
if (session.isMultiPackage()) {
for (int id : session.getChildSessionIds()) {
- if (isApexSession(mStagedSessions.get(id))) {
+ if (isApexSession(getStagedSession(id))) {
childSessionIds.add(id);
}
}
@@ -797,6 +801,8 @@ public class StagingManager {
+ session.sessionId + " [" + errorMessage + "]");
session.setStagedSessionFailed(
SessionInfo.STAGED_SESSION_VERIFICATION_FAILED, errorMessage);
+ mPreRebootVerificationHandler.onPreRebootVerificationComplete(
+ session.sessionId);
return;
}
mPreRebootVerificationHandler.notifyPreRebootVerification_Apk_Complete(
@@ -880,7 +886,8 @@ public class StagingManager {
synchronized (mStagedSessions) {
for (int i = 0; i < mStagedSessions.size(); i++) {
final PackageInstallerSession stagedSession = mStagedSessions.valueAt(i);
- if (!stagedSession.isCommitted() || stagedSession.isStagedAndInTerminalState()) {
+ if (!stagedSession.isCommitted() || stagedSession.isStagedAndInTerminalState()
+ || stagedSession.isDestroyed()) {
continue;
}
if (stagedSession.isMultiPackage()) {
@@ -943,27 +950,68 @@ public class StagingManager {
}
}
- void abortCommittedSession(@NonNull PackageInstallerSession session) {
+ /**
+ * <p>Abort committed staged session
+ *
+ * <p>This method must be called while holding {@link PackageInstallerSession.mLock}.
+ *
+ * <p>The method returns {@code false} to indicate it is not safe to clean up the session from
+ * system yet. When it is safe, the method returns {@code true}.
+ *
+ * <p> When it is safe to clean up, {@link StagingManager} will call
+ * {@link PackageInstallerSession#abandon()} on the session again.
+ *
+ * @return {@code true} if it is safe to cleanup the session resources, otherwise {@code false}.
+ */
+ boolean abortCommittedSessionLocked(@NonNull PackageInstallerSession session) {
+ int sessionId = session.sessionId;
if (session.isStagedSessionApplied()) {
- Slog.w(TAG, "Cannot abort applied session : " + session.sessionId);
- return;
+ Slog.w(TAG, "Cannot abort applied session : " + sessionId);
+ return false;
+ }
+ if (!session.isDestroyed()) {
+ throw new IllegalStateException("Committed session must be destroyed before aborting it"
+ + " from StagingManager");
+ }
+ if (getStagedSession(sessionId) == null) {
+ Slog.w(TAG, "Session " + sessionId + " has been abandoned already");
+ return false;
}
- abortSession(session);
- boolean hasApex = sessionContainsApex(session);
- if (hasApex) {
- ApexSessionInfo apexSession = mApexManager.getStagedSessionInfo(session.sessionId);
- if (apexSession == null || isApexSessionFinalized(apexSession)) {
- Slog.w(TAG,
- "Cannot abort session " + session.sessionId
- + " because it is not active or APEXD is not reachable");
- return;
- }
- try {
- mApexManager.abortStagedSession(session.sessionId);
- } catch (Exception ignore) {
+ // If pre-reboot verification is running, then return false. StagingManager will call
+ // abandon again when pre-reboot verification ends.
+ if (mPreRebootVerificationHandler.isVerificationRunning(sessionId)) {
+ Slog.w(TAG, "Session " + sessionId + " aborted before pre-reboot "
+ + "verification completed.");
+ return false;
+ }
+
+ // A session could be marked ready once its pre-reboot verification ends
+ if (session.isStagedSessionReady()) {
+ if (sessionContainsApex(session)) {
+ try {
+ ApexSessionInfo apexSession =
+ mApexManager.getStagedSessionInfo(session.sessionId);
+ if (apexSession == null || isApexSessionFinalized(apexSession)) {
+ Slog.w(TAG,
+ "Cannot abort session " + session.sessionId
+ + " because it is not active.");
+ } else {
+ mApexManager.abortStagedSession(session.sessionId);
+ }
+ } catch (Exception e) {
+ // Failed to contact apexd service. The apex might still be staged. We can still
+ // safely cleanup the staged session since pre-reboot verification is complete.
+ // Also, cleaning up the stageDir prevents the apex from being activated.
+ Slog.w(TAG, "Could not contact apexd to abort staged session " + sessionId);
+ }
}
}
+
+ // Session was successfully aborted from apexd (if required) and pre-reboot verification
+ // is also complete. It is now safe to clean up the session from system.
+ abortSession(session);
+ return true;
}
private boolean isApexSessionFinalized(ApexSessionInfo session) {
@@ -1042,6 +1090,11 @@ public class StagingManager {
// Final states, nothing to do.
return;
}
+ if (session.isDestroyed()) {
+ // Device rebooted before abandoned session was cleaned up.
+ session.abandon();
+ return;
+ }
if (!session.isStagedSessionReady()) {
// The framework got restarted before the pre-reboot verification could complete,
// restart the verification.
@@ -1124,10 +1177,20 @@ public class StagingManager {
}
}
+ private PackageInstallerSession getStagedSession(int sessionId) {
+ PackageInstallerSession session;
+ synchronized (mStagedSessions) {
+ session = mStagedSessions.get(sessionId);
+ }
+ return session;
+ }
+
private final class PreRebootVerificationHandler extends Handler {
// Hold session ids before handler gets ready to do the verification.
private IntArray mPendingSessionIds;
private boolean mIsReady;
+ @GuardedBy("mVerificationRunning")
+ private final SparseBooleanArray mVerificationRunning = new SparseBooleanArray();
PreRebootVerificationHandler(Looper looper) {
super(looper);
@@ -1155,13 +1218,15 @@ public class StagingManager {
@Override
public void handleMessage(Message msg) {
final int sessionId = msg.arg1;
- final PackageInstallerSession session;
- synchronized (mStagedSessions) {
- session = mStagedSessions.get(sessionId);
- }
- // Maybe session was aborted before pre-reboot verification was complete
+ final PackageInstallerSession session = getStagedSession(sessionId);
if (session == null) {
- Slog.d(TAG, "Stopping pre-reboot verification for sessionId: " + sessionId);
+ Slog.wtf(TAG, "Session disappeared in the middle of pre-reboot verification: "
+ + sessionId);
+ return;
+ }
+ if (session.isDestroyed()) {
+ // No point in running verification on a destroyed session
+ onPreRebootVerificationComplete(sessionId);
return;
}
switch (msg.what) {
@@ -1200,9 +1265,40 @@ public class StagingManager {
mPendingSessionIds.add(sessionId);
return;
}
+
+ PackageInstallerSession session = getStagedSession(sessionId);
+ synchronized (mVerificationRunning) {
+ // Do not start verification on a session that has been abandoned
+ if (session == null || session.isDestroyed()) {
+ return;
+ }
+ Slog.d(TAG, "Starting preRebootVerification for session " + sessionId);
+ mVerificationRunning.put(sessionId, true);
+ }
obtainMessage(MSG_PRE_REBOOT_VERIFICATION_START, sessionId, 0).sendToTarget();
}
+ // Things to do when pre-reboot verification completes for a particular sessionId
+ private void onPreRebootVerificationComplete(int sessionId) {
+ // Remove it from mVerificationRunning so that verification is considered complete
+ synchronized (mVerificationRunning) {
+ Slog.d(TAG, "Stopping preRebootVerification for session " + sessionId);
+ mVerificationRunning.delete(sessionId);
+ }
+ // Check if the session was destroyed while pre-reboot verification was running. If so,
+ // abandon it again.
+ PackageInstallerSession session = getStagedSession(sessionId);
+ if (session != null && session.isDestroyed()) {
+ session.abandon();
+ }
+ }
+
+ private boolean isVerificationRunning(int sessionId) {
+ synchronized (mVerificationRunning) {
+ return mVerificationRunning.get(sessionId);
+ }
+ }
+
private void notifyPreRebootVerification_Start_Complete(int sessionId) {
obtainMessage(MSG_PRE_REBOOT_VERIFICATION_APEX, sessionId, 0).sendToTarget();
}
@@ -1221,8 +1317,6 @@ public class StagingManager {
* See {@link PreRebootVerificationHandler} to see all nodes of pre reboot verification
*/
private void handlePreRebootVerification_Start(@NonNull PackageInstallerSession session) {
- Slog.d(TAG, "Starting preRebootVerification for session " + session.sessionId);
-
if ((session.params.installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) != 0) {
// If rollback is enabled for this session, we call through to the RollbackManager
// with the list of sessions it must enable rollback for. Note that
@@ -1269,6 +1363,7 @@ public class StagingManager {
}
} catch (PackageManagerException e) {
session.setStagedSessionFailed(e.error, e.getMessage());
+ onPreRebootVerificationComplete(session.sessionId);
return;
}
@@ -1301,6 +1396,7 @@ public class StagingManager {
// TODO(b/118865310): abort the session on apexd.
} catch (PackageManagerException e) {
session.setStagedSessionFailed(e.error, e.getMessage());
+ onPreRebootVerificationComplete(session.sessionId);
}
}
@@ -1323,9 +1419,18 @@ public class StagingManager {
Slog.e(TAG, "Failed to get hold of StorageManager", e);
session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_UNKNOWN,
"Failed to get hold of StorageManager");
+ onPreRebootVerificationComplete(session.sessionId);
return;
}
+ // Stop pre-reboot verification before marking session ready. From this point on, if we
+ // abandon the session then it will be cleaned up immediately. If session is abandoned
+ // after this point, then even if for some reason system tries to install the session
+ // or activate its apex, there won't be any files to work with as they will be cleaned
+ // up by the system as part of abandonment. If session is abandoned before this point,
+ // then the session is already destroyed and cannot be marked ready anymore.
+ onPreRebootVerificationComplete(session.sessionId);
+
// Proactively mark session as ready before calling apexd. Although this call order
// looks counter-intuitive, this is the easiest way to ensure that session won't end up
// in the inconsistent state:
@@ -1337,15 +1442,16 @@ public class StagingManager {
// only apex part of the train will be applied, leaving device in an inconsistent state.
Slog.d(TAG, "Marking session " + session.sessionId + " as ready");
session.setStagedSessionReady();
- final boolean hasApex = sessionContainsApex(session);
- if (!hasApex) {
- // Session doesn't contain apex, nothing to do.
- return;
- }
- try {
- mApexManager.markStagedSessionReady(session.sessionId);
- } catch (PackageManagerException e) {
- session.setStagedSessionFailed(e.error, e.getMessage());
+ if (session.isStagedSessionReady()) {
+ final boolean hasApex = sessionContainsApex(session);
+ if (hasApex) {
+ try {
+ mApexManager.markStagedSessionReady(session.sessionId);
+ } catch (PackageManagerException e) {
+ session.setStagedSessionFailed(e.error, e.getMessage());
+ return;
+ }
+ }
}
}
}
diff --git a/services/core/java/com/android/server/pm/TEST_MAPPING b/services/core/java/com/android/server/pm/TEST_MAPPING
index eb79b6ec652a..d3cd1a90b0b6 100644
--- a/services/core/java/com/android/server/pm/TEST_MAPPING
+++ b/services/core/java/com/android/server/pm/TEST_MAPPING
@@ -37,6 +37,9 @@
},
{
"include-filter": "android.content.pm.cts.PackageManagerShellCommandIncrementalTest"
+ },
+ {
+ "include-filter": "android.content.pm.cts.PackageManagerTest"
}
]
},
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index e6af86e52035..16d96d9a5574 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -4516,8 +4516,8 @@ public class UserManagerService extends IUserManager.Stub {
switch(cmd) {
case "list":
return runList(pw, shell);
- case "list-missing-system-packages":
- return runListMissingSystemPackages(pw, shell);
+ case "report-system-user-package-whitelist-problems":
+ return runReportPackageWhitelistProblems(pw, shell);
default:
return shell.handleDefaultCommands(cmd);
}
@@ -4584,17 +4584,22 @@ public class UserManagerService extends IUserManager.Stub {
}
}
- private int runListMissingSystemPackages(PrintWriter pw, Shell shell) {
+ private int runReportPackageWhitelistProblems(PrintWriter pw, Shell shell) {
boolean verbose = false;
- boolean force = false;
+ boolean criticalOnly = false;
+ int mode = UserSystemPackageInstaller.USER_TYPE_PACKAGE_WHITELIST_MODE_NONE;
String opt;
while ((opt = shell.getNextOption()) != null) {
switch (opt) {
case "-v":
+ case "--verbose":
verbose = true;
break;
- case "--force":
- force = true;
+ case "--critical-only":
+ criticalOnly = true;
+ break;
+ case "--mode":
+ mode = Integer.parseInt(shell.getNextArgRequired());
break;
default:
pw.println("Invalid option: " + opt);
@@ -4602,8 +4607,12 @@ public class UserManagerService extends IUserManager.Stub {
}
}
+ Slog.d(LOG_TAG, "runReportPackageWhitelistProblems(): verbose=" + verbose
+ + ", criticalOnly=" + criticalOnly
+ + ", mode=" + UserSystemPackageInstaller.modeToString(mode));
+
try (IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ")) {
- mSystemPackageInstaller.dumpMissingSystemPackages(ipw, force, verbose);
+ mSystemPackageInstaller.dumpPackageWhitelistProblems(ipw, mode, verbose, criticalOnly);
}
return 0;
}
@@ -5176,13 +5185,18 @@ public class UserManagerService extends IUserManager.Stub {
final PrintWriter pw = getOutPrintWriter();
pw.println("User manager (user) commands:");
pw.println(" help");
- pw.println(" Print this help text.");
+ pw.println(" Prints this help text.");
pw.println("");
pw.println(" list [-v] [-all]");
pw.println(" Prints all users on the system.");
- pw.println(" list-missing-system-packages [-v] [--force]");
- pw.println(" Prints all system packages that were not explicitly configured to be "
- + "installed.");
+ pw.println(" report-system-user-package-whitelist-problems [-v | --verbose] "
+ + "[--critical-only] [--mode MODE]");
+ pw.println(" Reports all issues on user-type package whitelist XML files. Options:");
+ pw.println(" -v | --verbose : shows extra info, like number of issues");
+ pw.println(" --critical-only: show only critical issues, excluding warnings");
+ pw.println(" --mode MODE: shows what errors would be if device used mode MODE (where"
+ + " MODE is the whitelist mode integer as defined by "
+ + "config_userTypePackageWhitelistMode)");
}
}
diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
index 0b6024a84f78..1fec8aa0a3ff 100644
--- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
+++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
@@ -208,7 +208,6 @@ public class UserRestrictionsUtils {
Sets.newArraySet(
UserManager.DISALLOW_CONFIG_DATE_TIME,
UserManager.DISALLOW_CAMERA,
- UserManager.DISALLOW_ADD_USER,
UserManager.DISALLOW_BLUETOOTH,
UserManager.DISALLOW_BLUETOOTH_SHARING,
UserManager.DISALLOW_CONFIG_CELL_BROADCASTS,
diff --git a/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java b/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java
index cd1087f5fcd7..9ec03e5e8f5c 100644
--- a/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java
+++ b/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java
@@ -27,7 +27,7 @@ import android.os.SystemProperties;
import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.ArraySet;
-import android.util.Pair;
+import android.util.DebugUtils;
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
@@ -41,6 +41,7 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -111,14 +112,20 @@ class UserSystemPackageInstaller {
* frameworks/base/core/res/res/values/config.xml
*/
static final String PACKAGE_WHITELIST_MODE_PROP = "persist.debug.user.package_whitelist_mode";
- static final int USER_TYPE_PACKAGE_WHITELIST_MODE_DISABLE = 0x00;
- static final int USER_TYPE_PACKAGE_WHITELIST_MODE_ENFORCE = 0x01;
- static final int USER_TYPE_PACKAGE_WHITELIST_MODE_LOG = 0x02;
- static final int USER_TYPE_PACKAGE_WHITELIST_MODE_IMPLICIT_WHITELIST = 0x04;
- static final int USER_TYPE_PACKAGE_WHITELIST_MODE_IMPLICIT_WHITELIST_SYSTEM = 0x08;
- static final int USER_TYPE_PACKAGE_WHITELIST_MODE_IGNORE_OTA = 0x10;
+
+ // NOTE: flags below are public so they can used by DebugUtils.flagsToString. And this class
+ // itself is package-protected, so it doesn't matter...
+ public static final int USER_TYPE_PACKAGE_WHITELIST_MODE_DISABLE = 0x00;
+ public static final int USER_TYPE_PACKAGE_WHITELIST_MODE_ENFORCE = 0x01;
+ public static final int USER_TYPE_PACKAGE_WHITELIST_MODE_LOG = 0x02;
+ public static final int USER_TYPE_PACKAGE_WHITELIST_MODE_IMPLICIT_WHITELIST = 0x04;
+ public static final int USER_TYPE_PACKAGE_WHITELIST_MODE_IMPLICIT_WHITELIST_SYSTEM = 0x08;
+ public static final int USER_TYPE_PACKAGE_WHITELIST_MODE_IGNORE_OTA = 0x10;
static final int USER_TYPE_PACKAGE_WHITELIST_MODE_DEVICE_DEFAULT = -1;
+ // Used by Shell command only
+ static final int USER_TYPE_PACKAGE_WHITELIST_MODE_NONE = -1000;
+
@IntDef(flag = true, prefix = "USER_TYPE_PACKAGE_WHITELIST_MODE_", value = {
USER_TYPE_PACKAGE_WHITELIST_MODE_DISABLE,
USER_TYPE_PACKAGE_WHITELIST_MODE_ENFORCE,
@@ -266,58 +273,56 @@ class UserSystemPackageInstaller {
if (!isLogMode(mode) && !isEnforceMode(mode)) {
return;
}
- final List<Pair<Boolean, String>> warnings = checkSystemPackagesWhitelistWarnings(mode);
- final int size = warnings.size();
- if (size == 0) {
- Slog.v(TAG, "checkWhitelistedSystemPackages(mode=" + mode + "): no warnings");
- return;
+ Slog.v(TAG, "Checking that all system packages are whitelisted.");
+
+ // Check whether all whitelisted packages are indeed on the system.
+ final List<String> warnings = getPackagesWhitelistWarnings();
+ final int numberWarnings = warnings.size();
+ if (numberWarnings == 0) {
+ Slog.v(TAG, "checkWhitelistedSystemPackages(mode=" + modeToString(mode)
+ + ") has no warnings");
+ } else {
+ Slog.w(TAG, "checkWhitelistedSystemPackages(mode=" + modeToString(mode)
+ + ") has " + numberWarnings + " warnings:");
+ for (int i = 0; i < numberWarnings; i++) {
+ Slog.w(TAG, warnings.get(i));
+ }
}
+ // Check whether all system packages are indeed whitelisted.
if (isImplicitWhitelistMode(mode) && !isLogMode(mode)) {
- // Only shows whether all whitelisted packages are indeed on the system.
- for (int i = 0; i < size; i++) {
- final Pair<Boolean, String> pair = warnings.get(i);
- final boolean isSevere = pair.first;
- if (!isSevere) {
- final String msg = pair.second;
- Slog.w(TAG, msg);
- }
- }
return;
}
- Slog.v(TAG, "checkWhitelistedSystemPackages(mode=" + mode + "): " + size + " warnings");
+ final List<String> errors = getPackagesWhitelistErrors(mode);
+ final int numberErrors = errors.size();
+
+ if (numberErrors == 0) {
+ Slog.v(TAG, "checkWhitelistedSystemPackages(mode=" + modeToString(mode)
+ + ") has no errors");
+ return;
+ }
+ Slog.e(TAG, "checkWhitelistedSystemPackages(mode=" + modeToString(mode) + ") has "
+ + numberErrors + " errors:");
+
boolean doWtf = !isImplicitWhitelistMode(mode);
- for (int i = 0; i < size; i++) {
- final Pair<Boolean, String> pair = warnings.get(i);
- final boolean isSevere = pair.first;
- final String msg = pair.second;
- if (isSevere) {
- if (doWtf) {
- Slog.wtf(TAG, msg);
- } else {
- Slog.e(TAG, msg);
- }
+ for (int i = 0; i < numberWarnings; i++) {
+ final String msg = errors.get(i);
+ if (doWtf) {
+ Slog.wtf(TAG, msg);
} else {
- Slog.w(TAG, msg);
+ Slog.e(TAG, msg);
}
}
}
- // TODO: method below was created to refactor the one-time logging logic so it can be used on
- // dump / cmd as well. It could to be further refactored (for example, creating a new
- // structure for the warnings so it doesn't need a Pair).
/**
- * Gets warnings for system user whitelisting.
- *
- * @return list of warnings, where {@code Pair.first} is the severity ({@code true} for WTF,
- * {@code false} for WARN) and {@code Pair.second} the message.
+ * Gets packages that are listed in the whitelist XML but are not present on the system image.
*/
@NonNull
- private List<Pair<Boolean, String>> checkSystemPackagesWhitelistWarnings(
- @PackageWhitelistMode int mode) {
+ private List<String> getPackagesWhitelistWarnings() {
final Set<String> allWhitelistedPackages = getWhitelistedSystemPackages();
- final List<Pair<Boolean, String>> warnings = new ArrayList<>();
+ final List<String> warnings = new ArrayList<>();
final PackageManagerInternal pmInt = LocalServices.getService(PackageManagerInternal.class);
// Check whether all whitelisted packages are indeed on the system.
@@ -326,25 +331,39 @@ class UserSystemPackageInstaller {
for (String pkgName : allWhitelistedPackages) {
final AndroidPackage pkg = pmInt.getPackage(pkgName);
if (pkg == null) {
- warnings.add(new Pair<>(false, String.format(notPresentFmt, pkgName)));
+ warnings.add(String.format(notPresentFmt, pkgName));
} else if (!pkg.isSystem()) {
- warnings.add(new Pair<>(false, String.format(notSystemFmt, pkgName)));
+ warnings.add(String.format(notSystemFmt, pkgName));
}
}
+ return warnings;
+ }
+
+ /**
+ * Gets packages that are not listed in the whitelist XMLs when they should be.
+ */
+ @NonNull
+ private List<String> getPackagesWhitelistErrors(@PackageWhitelistMode int mode) {
+ if ((!isEnforceMode(mode) || isImplicitWhitelistMode(mode)) && !isLogMode(mode)) {
+ return Collections.emptyList();
+ }
+
+ final List<String> errors = new ArrayList<>();
+ final Set<String> allWhitelistedPackages = getWhitelistedSystemPackages();
+ final PackageManagerInternal pmInt = LocalServices.getService(PackageManagerInternal.class);
// Check whether all system packages are indeed whitelisted.
final String logMessageFmt = "System package %s is not whitelisted using "
+ "'install-in-user-type' in SystemConfig for any user types!";
- final boolean isSevere = isEnforceMode(mode);
pmInt.forEachPackage(pkg -> {
if (!pkg.isSystem()) return;
final String pkgName = pkg.getManifestPackageName();
if (!allWhitelistedPackages.contains(pkgName)) {
- warnings.add(new Pair<>(isSevere, String.format(logMessageFmt, pkgName)));
+ errors.add(String.format(logMessageFmt, pkgName));
}
});
- return warnings;
+ return errors;
}
/** Whether to only install system packages in new users for which they are whitelisted. */
@@ -420,10 +439,28 @@ class UserSystemPackageInstaller {
if (runtimeMode != USER_TYPE_PACKAGE_WHITELIST_MODE_DEVICE_DEFAULT) {
return runtimeMode;
}
+ return getDeviceDefaultWhitelistMode();
+ }
+
+ /** Gets the PackageWhitelistMode as defined by {@code config_userTypePackageWhitelistMode}. */
+ private @PackageWhitelistMode int getDeviceDefaultWhitelistMode() {
return Resources.getSystem()
.getInteger(com.android.internal.R.integer.config_userTypePackageWhitelistMode);
}
+ static @NonNull String modeToString(@PackageWhitelistMode int mode) {
+ // Must handle some types separately because they're not bitwise flags
+ switch (mode) {
+ case USER_TYPE_PACKAGE_WHITELIST_MODE_DEVICE_DEFAULT:
+ return "DEVICE_DEFAULT";
+ case USER_TYPE_PACKAGE_WHITELIST_MODE_NONE:
+ return "NONE";
+ default:
+ return DebugUtils.flagsToString(UserSystemPackageInstaller.class,
+ "USER_TYPE_PACKAGE_WHITELIST_MODE_", mode);
+ }
+ }
+
/**
* Gets the system packages names that should be installed on the given user.
* See {@link #getInstallablePackagesForUserType(String)}.
@@ -703,34 +740,44 @@ class UserSystemPackageInstaller {
pw.decreaseIndent(); pw.decreaseIndent();
pw.increaseIndent();
- dumpMissingSystemPackages(pw, /* force= */ true, /* verbose= */ true);
+ dumpPackageWhitelistProblems(pw, mode, /* verbose= */ true, /* criticalOnly= */ false);
pw.decreaseIndent();
}
- void dumpMissingSystemPackages(IndentingPrintWriter pw, boolean force, boolean verbose) {
- final int mode = getWhitelistMode();
- final boolean show = force || (isEnforceMode(mode) && !isImplicitWhitelistMode(mode));
- if (!show) return;
+ void dumpPackageWhitelistProblems(IndentingPrintWriter pw, @PackageWhitelistMode int mode,
+ boolean verbose, boolean criticalOnly) {
+ // Handle special cases first
+ if (mode == USER_TYPE_PACKAGE_WHITELIST_MODE_NONE) {
+ mode = getWhitelistMode();
+ } else if (mode == USER_TYPE_PACKAGE_WHITELIST_MODE_DEVICE_DEFAULT) {
+ mode = getDeviceDefaultWhitelistMode();
+ }
+ Slog.v(TAG, "dumpPackageWhitelistProblems(): using mode " + modeToString(mode));
+
+ final List<String> errors = getPackagesWhitelistErrors(mode);
+ showIssues(pw, verbose, errors, "errors");
- final List<Pair<Boolean, String>> warnings = checkSystemPackagesWhitelistWarnings(mode);
- final int size = warnings.size();
+ if (criticalOnly) return;
+ final List<String> warnings = getPackagesWhitelistWarnings();
+ showIssues(pw, verbose, warnings, "warnings");
+ }
+
+ private static void showIssues(IndentingPrintWriter pw, boolean verbose, List<String> issues,
+ String issueType) {
+ final int size = issues.size();
if (size == 0) {
if (verbose) {
- pw.println("All system packages are accounted for");
+ pw.print("No "); pw.println(issueType);
}
return;
}
-
if (verbose) {
- pw.print(size); pw.println(" warnings for system user:");
+ pw.print(size); pw.print(' '); pw.println(issueType);
pw.increaseIndent();
}
for (int i = 0; i < size; i++) {
- final Pair<Boolean, String> pair = warnings.get(i);
- final String lvl = pair.first ? "WTF" : "WARN";
- final String msg = pair.second;
- pw.print(lvl); pw.print(": "); pw.println(msg);
+ pw.println(issues.get(i));
}
if (verbose) {
pw.decreaseIndent();
diff --git a/services/core/java/com/android/server/pm/parsing/PackageParser2.java b/services/core/java/com/android/server/pm/parsing/PackageParser2.java
index e860c4857bf4..1145057452c2 100644
--- a/services/core/java/com/android/server/pm/parsing/PackageParser2.java
+++ b/services/core/java/com/android/server/pm/parsing/PackageParser2.java
@@ -25,6 +25,7 @@ import android.content.pm.PackageParser;
import android.content.pm.PackageParser.PackageParserException;
import android.content.pm.parsing.ParsingPackage;
import android.content.pm.parsing.ParsingPackageUtils;
+import android.content.pm.parsing.ParsingUtils;
import android.content.pm.parsing.result.ParseInput;
import android.content.pm.parsing.result.ParseResult;
import android.content.pm.parsing.result.ParseTypeImpl;
@@ -35,7 +36,7 @@ import android.os.SystemClock;
import android.util.DisplayMetrics;
import android.util.Slog;
-import com.android.server.compat.PlatformCompat;
+import com.android.internal.compat.IPlatformCompat;
import com.android.server.pm.PackageManagerService;
import com.android.server.pm.parsing.pkg.PackageImpl;
import com.android.server.pm.parsing.pkg.ParsedPackage;
@@ -58,14 +59,21 @@ public class PackageParser2 implements AutoCloseable {
*
* This must be called inside the system process as it relies on {@link ServiceManager}.
*/
+ @NonNull
public static PackageParser2 forParsingFileWithDefaults() {
- PlatformCompat platformCompat =
- (PlatformCompat) ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE);
+ IPlatformCompat platformCompat = IPlatformCompat.Stub.asInterface(
+ ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE));
return new PackageParser2(null /* separateProcesses */, false /* onlyCoreApps */,
null /* displayMetrics */, null /* cacheDir */, new Callback() {
@Override
public boolean isChangeEnabled(long changeId, @NonNull ApplicationInfo appInfo) {
- return platformCompat.isChangeEnabled(changeId, appInfo);
+ try {
+ return platformCompat.isChangeEnabled(changeId, appInfo);
+ } catch (Exception e) {
+ // This shouldn't happen, but assume enforcement if it does
+ Slog.wtf(ParsingUtils.TAG, "IPlatformCompat query failed", e);
+ return true;
+ }
}
@Override
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index f17890334b6d..a967f3d7c4f9 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -149,6 +149,8 @@ import com.android.server.policy.SoftRestrictedPermissionPolicy;
import libcore.util.EmptyArray;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
@@ -417,6 +419,11 @@ public class PermissionManagerService extends IPermissionManager.Stub {
LocalServices.addService(PermissionManagerInternal.class, localService);
}
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ mContext.getSystemService(PermissionControllerManager.class).dump(fd, pw, args);
+ }
+
/**
* Creates and returns an initialized, internal service for use by other components.
* <p>
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 2f84a99774f4..3bc151af3589 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -1222,10 +1222,12 @@ public class PhoneWindowManager implements WindowManagerPolicy {
case LONG_PRESS_POWER_NOTHING:
break;
case LONG_PRESS_POWER_GLOBAL_ACTIONS:
- mPowerKeyHandled = true;
- performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, false,
- "Power - Long Press - Global Actions");
- showGlobalActionsInternal();
+ if (!mPowerKeyHandled) {
+ mPowerKeyHandled = true;
+ performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, false,
+ "Power - Long Press - Global Actions");
+ showGlobalActionsInternal();
+ }
break;
case LONG_PRESS_POWER_SHUT_OFF:
case LONG_PRESS_POWER_SHUT_OFF_NO_CONFIRM:
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
index 6726cc829c81..3336697ef359 100644
--- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
+++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
@@ -37,6 +37,9 @@ import android.content.pm.PackageParser;
import android.content.pm.ParceledListSlice;
import android.content.pm.UserInfo;
import android.content.pm.VersionedPackage;
+import android.content.pm.parsing.ApkLiteParseUtils;
+import android.content.pm.parsing.result.ParseResult;
+import android.content.pm.parsing.result.ParseTypeImpl;
import android.content.rollback.IRollbackManager;
import android.content.rollback.RollbackInfo;
import android.content.rollback.RollbackManager;
@@ -791,13 +794,15 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
}
// Get information about the package to be installed.
- PackageParser.PackageLite newPackage;
- try {
- newPackage = PackageParser.parsePackageLite(new File(session.resolvedBaseCodePath), 0);
- } catch (PackageParser.PackageParserException e) {
- Slog.e(TAG, "Unable to parse new package", e);
+ ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
+ ParseResult<PackageParser.ApkLite> parseResult = ApkLiteParseUtils.parseApkLite(
+ input.reset(), new File(session.resolvedBaseCodePath), 0);
+ if (parseResult.isError()) {
+ Slog.e(TAG, "Unable to parse new package: " + parseResult.getErrorMessage(),
+ parseResult.getException());
return false;
}
+ PackageParser.ApkLite newPackage = parseResult.getResult();
String packageName = newPackage.packageName;
Slog.i(TAG, "Enabling rollback for install of " + packageName
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 b0c702f55821..3b4c4235d8a4 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -30,6 +30,7 @@ import static android.util.MathUtils.abs;
import static android.util.MathUtils.constrain;
import static com.android.internal.util.FrameworkStatsLog.ANNOTATION_ID_IS_UID;
+import static com.android.internal.util.FrameworkStatsLog.ANNOTATION_ID_TRUNCATE_TIMESTAMP;
import static com.android.server.am.MemoryStatUtil.readMemoryStatFromFilesystem;
import static com.android.server.stats.pull.IonMemoryUtil.readProcessSystemIonHeapSizesFromDebugfs;
import static com.android.server.stats.pull.IonMemoryUtil.readSystemIonHeapSizeFromDebugfs;
@@ -755,6 +756,13 @@ public class StatsPullAtomService extends SystemService {
stats.getValues(j, entry);
StatsEvent.Builder e = StatsEvent.newBuilder();
e.setAtomId(atomTag);
+ switch (atomTag) {
+ case FrameworkStatsLog.MOBILE_BYTES_TRANSFER:
+ case FrameworkStatsLog.MOBILE_BYTES_TRANSFER_BY_FG_BG:
+ e.addBooleanAnnotation(ANNOTATION_ID_TRUNCATE_TIMESTAMP, true);
+ break;
+ default:
+ }
e.writeInt(entry.uid);
e.addBooleanAnnotation(ANNOTATION_ID_IS_UID, true);
if (withFgbg) {
diff --git a/services/core/java/com/android/server/tv/TvInputHardwareManager.java b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
index 8f71943129fa..2314afc787c3 100755
--- a/services/core/java/com/android/server/tv/TvInputHardwareManager.java
+++ b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
@@ -46,6 +46,8 @@ import android.media.tv.TvInputHardwareInfo;
import android.media.tv.TvInputInfo;
import android.media.tv.TvInputService.PriorityHintUseCaseType;
import android.media.tv.TvStreamConfig;
+import android.media.tv.tunerresourcemanager.ResourceClientProfile;
+import android.media.tv.tunerresourcemanager.TunerResourceManager;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
@@ -179,7 +181,7 @@ class TvInputHardwareManager implements TvInputHal.Callback {
Slog.e(TAG, "onDeviceUnavailable: Cannot find a connection with " + deviceId);
return;
}
- connection.resetLocked(null, null, null, null, null);
+ connection.resetLocked(null, null, null, null, null, null);
mConnections.remove(deviceId);
buildHardwareListLocked();
TvInputHardwareInfo info = connection.getHardwareInfoLocked();
@@ -369,25 +371,34 @@ class TvInputHardwareManager implements TvInputHal.Callback {
if (callback == null) {
throw new NullPointerException();
}
+ TunerResourceManager trm = (TunerResourceManager) mContext.getSystemService(
+ Context.TV_TUNER_RESOURCE_MGR_SERVICE);
synchronized (mLock) {
Connection connection = mConnections.get(deviceId);
if (connection == null) {
Slog.e(TAG, "Invalid deviceId : " + deviceId);
return null;
}
- // TODO: check with TRM to compare the client's priority with the current holder's
- // priority. If lower, do nothing.
- if (checkUidChangedLocked(connection, callingUid, resolvedUserId)) {
- TvInputHardwareImpl hardware =
- new TvInputHardwareImpl(connection.getHardwareInfoLocked());
- try {
- callback.asBinder().linkToDeath(connection, 0);
- } catch (RemoteException e) {
- hardware.release();
- return null;
- }
- connection.resetLocked(hardware, callback, info, callingUid, resolvedUserId);
+
+ ResourceClientProfile profile =
+ new ResourceClientProfile(tvInputSessionId, priorityHint);
+ ResourceClientProfile holderProfile = connection.getResourceClientProfileLocked();
+ if (holderProfile != null && trm != null
+ && !trm.isHigherPriority(profile, holderProfile)) {
+ Slog.d(TAG, "Acquiring does not show higher priority than the current holder."
+ + " Device id:" + deviceId);
+ return null;
}
+ TvInputHardwareImpl hardware =
+ new TvInputHardwareImpl(connection.getHardwareInfoLocked());
+ try {
+ callback.asBinder().linkToDeath(connection, 0);
+ } catch (RemoteException e) {
+ hardware.release();
+ return null;
+ }
+ connection.resetLocked(hardware, callback, info, callingUid, resolvedUserId,
+ profile);
return connection.getHardwareLocked();
}
}
@@ -411,7 +422,7 @@ class TvInputHardwareManager implements TvInputHal.Callback {
if (callback != null) {
callback.asBinder().unlinkToDeath(connection, 0);
}
- connection.resetLocked(null, null, null, null, null);
+ connection.resetLocked(null, null, null, null, null, null);
}
}
@@ -621,6 +632,7 @@ class TvInputHardwareManager implements TvInputHal.Callback {
private Integer mCallingUid = null;
private Integer mResolvedUserId = null;
private Runnable mOnFirstFrameCaptured;
+ private ResourceClientProfile mResourceClientProfile = null;
public Connection(TvInputHardwareInfo hardwareInfo) {
mHardwareInfo = hardwareInfo;
@@ -629,7 +641,8 @@ class TvInputHardwareManager implements TvInputHal.Callback {
// *Locked methods assume TvInputHardwareManager.mLock is held.
public void resetLocked(TvInputHardwareImpl hardware, ITvInputHardwareCallback callback,
- TvInputInfo info, Integer callingUid, Integer resolvedUserId) {
+ TvInputInfo info, Integer callingUid, Integer resolvedUserId,
+ ResourceClientProfile profile) {
if (mHardware != null) {
try {
mCallback.onReleased();
@@ -644,6 +657,7 @@ class TvInputHardwareManager implements TvInputHal.Callback {
mCallingUid = callingUid;
mResolvedUserId = resolvedUserId;
mOnFirstFrameCaptured = null;
+ mResourceClientProfile = profile;
if (mHardware != null && mCallback != null) {
try {
@@ -698,10 +712,14 @@ class TvInputHardwareManager implements TvInputHal.Callback {
return mOnFirstFrameCaptured;
}
+ public ResourceClientProfile getResourceClientProfileLocked() {
+ return mResourceClientProfile;
+ }
+
@Override
public void binderDied() {
synchronized (mLock) {
- resetLocked(null, null, null, null, null);
+ resetLocked(null, null, null, null, null, null);
}
}
@@ -713,6 +731,7 @@ class TvInputHardwareManager implements TvInputHal.Callback {
+ ", mConfigs: " + Arrays.toString(mConfigs)
+ ", mCallingUid: " + mCallingUid
+ ", mResolvedUserId: " + mResolvedUserId
+ + ", mResourceClientProfile: " + mResourceClientProfile
+ " }";
}
diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
index 2f70840cfc8b..41aa4ee65f30 100644
--- a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
+++ b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
@@ -18,6 +18,8 @@ package com.android.server.tv.tunerresourcemanager;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.ActivityManager;
+import android.app.ActivityManager.RunningAppProcessInfo;
import android.content.Context;
import android.media.tv.TvInputManager;
import android.media.tv.tunerresourcemanager.CasSessionRequest;
@@ -42,6 +44,7 @@ import com.android.server.SystemService;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -71,7 +74,8 @@ public class TunerResourceManagerService extends SystemService {
@GuardedBy("mLock")
private Map<Integer, ResourcesReclaimListenerRecord> mListeners = new HashMap<>();
- private TvInputManager mManager;
+ private TvInputManager mTvInputManager;
+ private ActivityManager mActivityManager;
private UseCasePriorityHints mPriorityCongfig = new UseCasePriorityHints();
// An internal resource request count to help generate resource handle.
@@ -94,7 +98,9 @@ public class TunerResourceManagerService extends SystemService {
if (!isForTesting) {
publishBinderService(Context.TV_TUNER_RESOURCE_MGR_SERVICE, new BinderService());
}
- mManager = (TvInputManager) getContext().getSystemService(Context.TV_INPUT_SERVICE);
+ mTvInputManager = (TvInputManager) getContext().getSystemService(Context.TV_INPUT_SERVICE);
+ mActivityManager =
+ (ActivityManager) getContext().getSystemService(Context.ACTIVITY_SERVICE);
mPriorityCongfig.parse();
}
@@ -204,7 +210,7 @@ public class TunerResourceManagerService extends SystemService {
@Override
public boolean requestDemux(@NonNull TunerDemuxRequest request,
- @NonNull int[] demuxHandle) throws RemoteException {
+ @NonNull int[] demuxHandle) throws RemoteException {
enforceTunerAccessPermission("requestDemux");
enforceTrmAccessPermission("requestDemux");
if (demuxHandle == null) {
@@ -362,14 +368,15 @@ public class TunerResourceManagerService extends SystemService {
@Override
public boolean isHigherPriority(
- ResourceClientProfile challengerProfile, ResourceClientProfile holderProfile) {
+ ResourceClientProfile challengerProfile, ResourceClientProfile holderProfile)
+ throws RemoteException {
enforceTrmAccessPermission("isHigherPriority");
- if (DEBUG) {
- Slog.d(TAG,
- "isHigherPriority(challengerProfile=" + challengerProfile
- + ", holderProfile=" + challengerProfile + ")");
+ if (challengerProfile == null || holderProfile == null) {
+ throw new RemoteException("Client profiles can't be null.");
+ }
+ synchronized (mLock) {
+ return isHigherPriorityInternal(challengerProfile, holderProfile);
}
- return true;
}
}
@@ -381,7 +388,7 @@ public class TunerResourceManagerService extends SystemService {
}
clientId[0] = INVALID_CLIENT_ID;
- if (mManager == null) {
+ if (mTvInputManager == null) {
Slog.e(TAG, "TvInputManager is null. Can't register client profile.");
return;
}
@@ -390,7 +397,7 @@ public class TunerResourceManagerService extends SystemService {
int pid = profile.getTvInputSessionId() == null
? Binder.getCallingPid() /*callingPid*/
- : mManager.getClientPid(profile.getTvInputSessionId()); /*tvAppId*/
+ : mTvInputManager.getClientPid(profile.getTvInputSessionId()); /*tvAppId*/
ClientProfile clientProfile = new ClientProfile.Builder(clientId[0])
.tvInputSessionId(profile.getTvInputSessionId())
@@ -693,6 +700,33 @@ public class TunerResourceManagerService extends SystemService {
}
@VisibleForTesting
+ protected boolean isHigherPriorityInternal(ResourceClientProfile challengerProfile,
+ ResourceClientProfile holderProfile) {
+ if (DEBUG) {
+ Slog.d(TAG,
+ "isHigherPriority(challengerProfile=" + challengerProfile
+ + ", holderProfile=" + challengerProfile + ")");
+ }
+ if (mTvInputManager == null) {
+ Slog.e(TAG, "TvInputManager is null. Can't compare the priority.");
+ // Allow the client to acquire the hardware interface
+ // when the TRM is not able to compare the priority.
+ return true;
+ }
+
+ int challengerPid = challengerProfile.getTvInputSessionId() == null
+ ? Binder.getCallingPid() /*callingPid*/
+ : mTvInputManager.getClientPid(challengerProfile.getTvInputSessionId()); /*tvAppId*/
+ int holderPid = holderProfile.getTvInputSessionId() == null
+ ? Binder.getCallingPid() /*callingPid*/
+ : mTvInputManager.getClientPid(holderProfile.getTvInputSessionId()); /*tvAppId*/
+
+ int challengerPriority = getClientPriority(challengerProfile.getUseCase(), challengerPid);
+ int holderPriority = getClientPriority(holderProfile.getUseCase(), holderPid);
+ return challengerPriority > holderPriority;
+ }
+
+ @VisibleForTesting
protected void releaseFrontendInternal(FrontendResource fe) {
if (DEBUG) {
Slog.d(TAG, "releaseFrontend(id=" + fe.getId() + ")");
@@ -818,8 +852,20 @@ public class TunerResourceManagerService extends SystemService {
@VisibleForTesting
protected boolean isForeground(int pid) {
- // TODO: how to get fg/bg information from pid
- return true;
+ if (mActivityManager == null) {
+ return false;
+ }
+ List<RunningAppProcessInfo> appProcesses = mActivityManager.getRunningAppProcesses();
+ if (appProcesses == null) {
+ return false;
+ }
+ for (RunningAppProcessInfo appProcess : appProcesses) {
+ if (appProcess.pid == pid
+ && appProcess.importance == RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {
+ return true;
+ }
+ }
+ return false;
}
private void updateFrontendClientMappingOnNewGrant(int grantingId, int ownerClientId) {
@@ -1044,7 +1090,7 @@ public class TunerResourceManagerService extends SystemService {
}
private void enforceTrmAccessPermission(String apiName) {
- getContext().enforceCallingPermission("android.permission.TUNER_RESOURCE_ACCESS",
+ getContext().enforceCallingOrSelfPermission("android.permission.TUNER_RESOURCE_ACCESS",
TAG + ": " + apiName);
}
diff --git a/services/core/java/com/android/server/uri/UriGrantsManagerService.java b/services/core/java/com/android/server/uri/UriGrantsManagerService.java
index 6734ce76675d..72cdf4afb007 100644
--- a/services/core/java/com/android/server/uri/UriGrantsManagerService.java
+++ b/services/core/java/com/android/server/uri/UriGrantsManagerService.java
@@ -21,9 +21,8 @@ import static android.Manifest.permission.FORCE_PERSISTABLE_URI_PERMISSIONS;
import static android.Manifest.permission.GET_APP_GRANTED_URI_PERMISSIONS;
import static android.Manifest.permission.INTERACT_ACROSS_USERS;
import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY;
+import static android.content.Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION;
import static android.content.Intent.FLAG_GRANT_PREFIX_URI_PERMISSION;
-import static android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION;
-import static android.content.Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
import static android.content.pm.PackageManager.MATCH_ANY_USER;
import static android.content.pm.PackageManager.MATCH_DEBUG_TRIAGED_MISSING;
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
@@ -1138,8 +1137,8 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub {
targetHoldsPermission = false;
}
- final boolean basicGrant = (modeFlags & ~(FLAG_GRANT_READ_URI_PERMISSION
- | FLAG_GRANT_WRITE_URI_PERMISSION)) == 0;
+ final boolean basicGrant = (modeFlags
+ & (FLAG_GRANT_PERSISTABLE_URI_PERMISSION | FLAG_GRANT_PREFIX_URI_PERMISSION)) == 0;
if (basicGrant && targetHoldsPermission) {
// When caller holds permission, and this is a simple permission
// grant, we can skip generating any bookkeeping; when any advanced
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index f05217c0b47a..e08bfd55011f 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -1281,12 +1281,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
if (stack != null && stack.topRunningActivity() == this) {
- // carry over the PictureInPictureParams to the parent stack without calling
- // TaskOrganizerController#dispatchTaskInfoChanged.
- // this is to ensure the stack holding up-to-dated pinned stack information
- // when activity is re-parented to enter pip mode, see also
- // RootWindowContainer#moveActivityToPinnedStack
- stack.mPictureInPictureParams.copyOnlySet(pictureInPictureArgs);
// make ensure the TaskOrganizer still works after re-parenting
if (firstWindowDrawn) {
stack.setHasBeenVisible(true);
@@ -7769,6 +7763,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
void setPictureInPictureParams(PictureInPictureParams p) {
pictureInPictureArgs.copyOnlySet(p);
- getTask().getRootTask().setPictureInPictureParams(p);
+ getTask().getRootTask().onPictureInPictureParamsChanged();
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index db5e97250cd2..1d7255578759 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -242,12 +242,6 @@ class ActivityStack extends Task {
*/
boolean mInResumeTopActivity = false;
- private boolean mUpdateBoundsDeferred;
- private boolean mUpdateBoundsDeferredCalled;
- private boolean mUpdateDisplayedBoundsDeferredCalled;
- private final Rect mDeferredBounds = new Rect();
- private final Rect mDeferredDisplayedBounds = new Rect();
-
int mCurrentUser;
/** For comparison with DisplayContent bounds. */
@@ -708,8 +702,10 @@ class ActivityStack extends Task {
// Need to make sure windowing mode is supported. If we in the process of creating the stack
// no need to resolve the windowing mode again as it is already resolved to the right mode.
if (!creating) {
- windowingMode = taskDisplayArea.validateWindowingMode(windowingMode,
- null /* ActivityRecord */, topTask, getActivityType());
+ if (!taskDisplayArea.isValidWindowingMode(windowingMode, null /* ActivityRecord */,
+ topTask, getActivityType())) {
+ windowingMode = WINDOWING_MODE_UNDEFINED;
+ }
}
if (taskDisplayArea.getRootSplitScreenPrimaryTask() == this
&& windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY) {
@@ -846,58 +842,6 @@ class ActivityStack extends Task {
return getDisplayContent();
}
- /**
- * Defers updating the bounds of the stack. If the stack was resized/repositioned while
- * deferring, the bounds will update in {@link #continueUpdateBounds()}.
- */
- void deferUpdateBounds() {
- if (!mUpdateBoundsDeferred) {
- mUpdateBoundsDeferred = true;
- mUpdateBoundsDeferredCalled = false;
- }
- }
-
- /**
- * Continues updating bounds after updates have been deferred. If there was a resize attempt
- * between {@link #deferUpdateBounds()} and {@link #continueUpdateBounds()}, the stack will
- * be resized to that bounds.
- */
- void continueUpdateBounds() {
- if (mUpdateBoundsDeferred) {
- mUpdateBoundsDeferred = false;
- if (mUpdateBoundsDeferredCalled) {
- setTaskBounds(mDeferredBounds);
- setBounds(mDeferredBounds);
- }
- }
- }
-
- private boolean updateBoundsAllowed(Rect bounds) {
- if (!mUpdateBoundsDeferred) {
- return true;
- }
- if (bounds != null) {
- mDeferredBounds.set(bounds);
- } else {
- mDeferredBounds.setEmpty();
- }
- mUpdateBoundsDeferredCalled = true;
- return false;
- }
-
- private boolean updateDisplayedBoundsAllowed(Rect bounds) {
- if (!mUpdateBoundsDeferred) {
- return true;
- }
- if (bounds != null) {
- mDeferredDisplayedBounds.set(bounds);
- } else {
- mDeferredDisplayedBounds.setEmpty();
- }
- mUpdateDisplayedBoundsDeferredCalled = true;
- return false;
- }
-
/** @return true if the stack can only contain one task */
boolean isSingleTaskInstance() {
final DisplayContent display = getDisplay();
@@ -2687,10 +2631,6 @@ class ActivityStack extends Task {
// TODO: Can only be called from special methods in ActivityStackSupervisor.
// Need to consolidate those calls points into this resize method so anyone can call directly.
void resize(Rect displayedBounds, boolean preserveWindows, boolean deferResume) {
- if (!updateBoundsAllowed(displayedBounds)) {
- return;
- }
-
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "stack.resize_" + getRootTaskId());
mAtmService.deferWindowLayout();
try {
@@ -2730,10 +2670,6 @@ class ActivityStack extends Task {
* basically resizes both stack and task bounds to the same bounds.
*/
private void setTaskBounds(Rect bounds) {
- if (!updateBoundsAllowed(bounds)) {
- return;
- }
-
final PooledConsumer c = PooledLambda.obtainConsumer(ActivityStack::setTaskBounds,
PooledLambda.__(Task.class), bounds);
forAllLeafTasks(c, true /* traverseTopToBottom */);
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index ed2153960754..33715207c6ce 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -69,7 +69,6 @@ import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLAS
import static com.android.server.wm.ActivityTaskManagerService.ANIMATE;
import static com.android.server.wm.ActivityTaskManagerService.H.FIRST_SUPERVISOR_STACK_MSG;
import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_NONE;
-import static com.android.server.wm.RootWindowContainer.MATCH_TASK_IN_STACKS_ONLY;
import static com.android.server.wm.RootWindowContainer.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS;
import static com.android.server.wm.RootWindowContainer.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE;
import static com.android.server.wm.RootWindowContainer.TAG_STATES;
@@ -125,7 +124,6 @@ import android.os.UserManager;
import android.os.WorkSource;
import android.provider.MediaStore;
import android.util.ArrayMap;
-import android.util.ArraySet;
import android.util.MergedConfiguration;
import android.util.Slog;
import android.util.SparseArray;
@@ -364,11 +362,6 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
*/
boolean mAppVisibilitiesChangedSinceLastPause;
- /**
- * Set of tasks that are in resizing mode during an app transition to fill the "void".
- */
- private final ArraySet<Integer> mResizingTasksDuringAnimation = new ArraySet<>();
-
private KeyguardController mKeyguardController;
private PowerManager mPowerManager;
@@ -1415,29 +1408,6 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
return mLaunchParamsController;
}
- private void deferUpdateRecentsHomeStackBounds() {
- mRootWindowContainer.deferUpdateBounds(ACTIVITY_TYPE_RECENTS);
- mRootWindowContainer.deferUpdateBounds(ACTIVITY_TYPE_HOME);
- }
-
- private void continueUpdateRecentsHomeStackBounds() {
- mRootWindowContainer.continueUpdateBounds(ACTIVITY_TYPE_RECENTS);
- mRootWindowContainer.continueUpdateBounds(ACTIVITY_TYPE_HOME);
- }
-
- void notifyAppTransitionDone() {
- continueUpdateRecentsHomeStackBounds();
- for (int i = mResizingTasksDuringAnimation.size() - 1; i >= 0; i--) {
- final int taskId = mResizingTasksDuringAnimation.valueAt(i);
- final Task task =
- mRootWindowContainer.anyTaskForId(taskId, MATCH_TASK_IN_STACKS_ONLY);
- if (task != null) {
- task.setTaskDockedResizing(false);
- }
- }
- mResizingTasksDuringAnimation.clear();
- }
-
void setSplitScreenResizing(boolean resizing) {
if (resizing == mDockedStackResizing) {
return;
@@ -1470,6 +1440,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
mService.deferWindowLayout();
try {
stack.setWindowingMode(WINDOWING_MODE_UNDEFINED);
+ stack.setBounds(null);
if (toDisplay.getDisplayId() != stack.getDisplayId()) {
stack.reparent(toDisplay.getDefaultTaskDisplayArea(), false /* onTop */);
} else {
@@ -2471,16 +2442,6 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
}
}
- /**
- * Puts a task into resizing mode during the next app transition.
- *
- * @param task The task to put into resizing mode
- */
- void setResizingDuringAnimation(Task task) {
- mResizingTasksDuringAnimation.add(task.mTaskId);
- task.setTaskDockedResizing(true);
- }
-
int startActivityFromRecents(int callingPid, int callingUid, int taskId,
SafeActivityOptions options) {
Task task = null;
@@ -2508,27 +2469,12 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
mService.deferWindowLayout();
try {
- if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
- // Defer updating the stack in which recents is until the app transition is done, to
- // not run into issues where we still need to draw the task in recents but the
- // docked stack is already created.
- deferUpdateRecentsHomeStackBounds();
- // TODO(task-hierarchy): Remove when tiles are in hierarchy.
- // Unset launching windowing mode to prevent creating split-screen-primary stack
- // in RWC#anyTaskForId() below.
- activityOptions.setLaunchWindowingMode(WINDOWING_MODE_UNDEFINED);
- }
-
task = mRootWindowContainer.anyTaskForId(taskId,
MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE, activityOptions, ON_TOP);
if (task == null) {
- continueUpdateRecentsHomeStackBounds();
mWindowManager.executeAppTransition();
throw new IllegalArgumentException(
"startActivityFromRecents: Task " + taskId + " not found.");
- } else if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
- && task.getWindowingMode() != windowingMode) {
- mService.moveTaskToSplitScreenPrimaryTask(task, true /* toTop */);
}
if (windowingMode != WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
@@ -2577,12 +2523,6 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
false /* validateIncomingUser */, null /* originatingPendingIntent */,
false /* allowBackgroundActivityStart */);
} finally {
- if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY && task != null) {
- // If we are launching the task in the docked stack, put it into resizing mode so
- // the window renders full-screen with the background filling the void. Also only
- // call this at the end to make sure that tasks exists on the window manager side.
- setResizingDuringAnimation(task);
- }
mService.continueWindowLayout();
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index 4181f4be30f7..d5df9068e81d 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -152,17 +152,6 @@ public abstract class ActivityTaskManagerInternal {
IVoiceInteractor mInteractor);
/**
- * Callback for window manager to let activity manager know that the app transition was
- * cancelled.
- */
- public abstract void notifyAppTransitionCancelled();
-
- /**
- * Callback for window manager to let activity manager know that the app transition is finished.
- */
- public abstract void notifyAppTransitionFinished();
-
- /**
* Returns the top activity from each of the currently visible stacks. The first entry will be
* the focused activity.
*/
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 6a8d5d905a00..36caeecbfec2 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -6079,20 +6079,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
@Override
- public void notifyAppTransitionFinished() {
- synchronized (mGlobalLock) {
- mStackSupervisor.notifyAppTransitionDone();
- }
- }
-
- @Override
- public void notifyAppTransitionCancelled() {
- synchronized (mGlobalLock) {
- mStackSupervisor.notifyAppTransitionDone();
- }
- }
-
- @Override
public List<IBinder> getTopVisibleActivities() {
synchronized (mGlobalLock) {
return mRootWindowContainer.getTopVisibleActivities();
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index 654ccc80f8a8..67fe9685fd2a 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -166,19 +166,13 @@ public class AppTransitionController {
// done behind a dream window.
final ArraySet<Integer> activityTypes = collectActivityTypes(mDisplayContent.mOpeningApps,
mDisplayContent.mClosingApps, mDisplayContent.mChangingContainers);
- final boolean allowAnimations = mDisplayContent.getDisplayPolicy().allowAppAnimationsLw();
- final ActivityRecord animLpActivity = allowAnimations
- ? findAnimLayoutParamsToken(transit, activityTypes)
- : null;
- final ActivityRecord topOpeningApp = allowAnimations
- ? getTopApp(mDisplayContent.mOpeningApps, false /* ignoreHidden */)
- : null;
- final ActivityRecord topClosingApp = allowAnimations
- ? getTopApp(mDisplayContent.mClosingApps, false /* ignoreHidden */)
- : null;
- final ActivityRecord topChangingApp = allowAnimations
- ? getTopApp(mDisplayContent.mChangingContainers, false /* ignoreHidden */)
- : null;
+ final ActivityRecord animLpActivity = findAnimLayoutParamsToken(transit, activityTypes);
+ final ActivityRecord topOpeningApp =
+ getTopApp(mDisplayContent.mOpeningApps, false /* ignoreHidden */);
+ final ActivityRecord topClosingApp =
+ getTopApp(mDisplayContent.mClosingApps, false /* ignoreHidden */);
+ final ActivityRecord topChangingApp =
+ getTopApp(mDisplayContent.mChangingContainers, false /* ignoreHidden */);
final WindowManager.LayoutParams animLp = getAnimLp(animLpActivity);
overrideWithRemoteAnimationIfSet(animLpActivity, transit, activityTypes);
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 864d96f0fea0..1f10c467e1e6 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -2547,6 +2547,9 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
}
for (int i = mTapExcludedWindows.size() - 1; i >= 0; i--) {
final WindowState win = mTapExcludedWindows.get(i);
+ if (!win.isVisibleLw()) {
+ continue;
+ }
win.getTouchableRegion(mTmpRegion);
mTouchExcludeRegion.op(mTmpRegion, Region.Op.UNION);
}
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 541be0a8b580..53b536cf712f 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -3154,16 +3154,6 @@ public class DisplayPolicy {
return 0;
}
- /**
- * Return true if it is okay to perform animations for an app transition
- * that is about to occur. You may return false for this if, for example,
- * the dream window is currently displayed so the switch should happen
- * immediately.
- */
- public boolean allowAppAnimationsLw() {
- return !mShowingDream;
- }
-
private void requestTransientBars(WindowState swipeTarget) {
if (!mService.mPolicy.isUserSetupComplete()) {
// Swipe-up for navigation bar is disabled during setup
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 8b9e9fe132b7..c93b7354999b 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -2170,7 +2170,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
final boolean singleActivity = task.getChildCount() == 1;
final ActivityStack stack;
if (singleActivity) {
- stack = r.getRootTask();
+ stack = (ActivityStack) task;
} else {
// In the case of multiple activities, we will create a new task for it and then
// move the PIP activity into the task.
@@ -2183,6 +2183,11 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
// up-to-dated pinned stack information on this newly created stack.
r.reparent(stack, MAX_VALUE, reason);
}
+ if (stack.getParent() != taskDisplayArea) {
+ // stack is nested, but pinned tasks need to be direct children of their
+ // display area, so reparent.
+ stack.reparent(taskDisplayArea, true /* onTop */);
+ }
stack.setWindowingMode(WINDOWING_MODE_PINNED);
// Reset the state that indicates it can enter PiP while pausing after we've moved it
@@ -2504,20 +2509,6 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
return list;
}
- void deferUpdateBounds(int activityType) {
- final ActivityStack stack = getStack(WINDOWING_MODE_UNDEFINED, activityType);
- if (stack != null) {
- stack.deferUpdateBounds();
- }
- }
-
- void continueUpdateBounds(int activityType) {
- final ActivityStack stack = getStack(WINDOWING_MODE_UNDEFINED, activityType);
- if (stack != null) {
- stack.continueUpdateBounds();
- }
- }
-
@Override
public void onDisplayAdded(int displayId) {
if (DEBUG_STACK) Slog.v(TAG, "Display added displayId=" + displayId);
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 44a8daaba1b1..85a31610964e 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -82,7 +82,6 @@ import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_TASKS
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.ActivityTaskManagerService.TAG_STACK;
-import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_DOCKED_DIVIDER;
import static com.android.server.wm.IdentifierProto.HASH_CODE;
import static com.android.server.wm.IdentifierProto.TITLE;
import static com.android.server.wm.IdentifierProto.USER_ID;
@@ -110,7 +109,6 @@ import android.app.ActivityManager.TaskSnapshot;
import android.app.ActivityOptions;
import android.app.ActivityTaskManager;
import android.app.AppGlobals;
-import android.app.PictureInPictureParams;
import android.app.TaskInfo;
import android.app.WindowConfiguration;
import android.content.ComponentName;
@@ -491,12 +489,6 @@ class Task extends WindowContainer<WindowContainer> {
boolean mTaskAppearedSent;
/**
- * Last Picture-in-Picture params applicable to the task. Updated when the app
- * enters Picture-in-Picture or when setPictureInPictureParams is called.
- */
- PictureInPictureParams mPictureInPictureParams = new PictureInPictureParams.Builder().build();
-
- /**
* This task was created by the task organizer which has the following implementations.
* <ul>
* <lis>The task won't be removed when it is empty. Removal has to be an explicit request
@@ -2013,7 +2005,7 @@ class Task extends WindowContainer<WindowContainer> {
}
void updateSurfaceSize(SurfaceControl.Transaction transaction) {
- if (mSurfaceControl == null || mCreatedByOrganizer) {
+ if (mSurfaceControl == null || isOrganized()) {
return;
}
@@ -3059,15 +3051,6 @@ class Task extends WindowContainer<WindowContainer> {
return mDragResizeMode;
}
- /**
- * Puts this task into docked drag resizing mode. See {@link DragResizeMode}.
- *
- * @param resizing Whether to put the task into drag resize mode.
- */
- public void setTaskDockedResizing(boolean resizing) {
- setDragResizing(resizing, DRAG_RESIZE_MODE_DOCKED_DIVIDER);
- }
-
void adjustBoundsForDisplayChangeIfNeeded(final DisplayContent displayContent) {
if (displayContent == null) {
return;
@@ -3602,10 +3585,11 @@ class Task extends WindowContainer<WindowContainer> {
info.resizeMode = top != null ? top.mResizeMode : mResizeMode;
info.topActivityType = top.getActivityType();
- if (mPictureInPictureParams.empty()) {
+ ActivityRecord rootActivity = top.getRootActivity();
+ if (rootActivity == null || rootActivity.pictureInPictureArgs.empty()) {
info.pictureInPictureParams = null;
} else {
- info.pictureInPictureParams = mPictureInPictureParams;
+ info.pictureInPictureParams = rootActivity.pictureInPictureArgs;
}
info.topActivityInfo = mReuseActivitiesReport.top != null
? mReuseActivitiesReport.top.info
@@ -4521,8 +4505,7 @@ class Task extends WindowContainer<WindowContainer> {
updateShadowsRadius(hasFocus, getPendingTransaction());
}
- void setPictureInPictureParams(PictureInPictureParams p) {
- mPictureInPictureParams.copyOnlySet(p);
+ void onPictureInPictureParamsChanged() {
if (isOrganized()) {
mAtmService.mTaskOrganizerController.dispatchTaskInfoChanged(this, true /* force */);
}
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index 0a1ee2b79711..37a4c1f6849b 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -21,7 +21,6 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
@@ -1333,16 +1332,16 @@ final class TaskDisplayArea extends DisplayArea<ActivityStack> {
}
/**
- * Check that the requested windowing-mode is appropriate for the specified task and/or activity
+ * Check if the requested windowing-mode is appropriate for the specified task and/or activity
* on this display.
*
* @param windowingMode The windowing-mode to validate.
* @param r The {@link ActivityRecord} to check against.
* @param task The {@link Task} to check against.
* @param activityType An activity type.
- * @return The provided windowingMode or the closest valid mode which is appropriate.
+ * @return {@code true} if windowingMode is valid, {@code false} otherwise.
*/
- int validateWindowingMode(int windowingMode, @Nullable ActivityRecord r, @Nullable Task task,
+ boolean isValidWindowingMode(int windowingMode, @Nullable ActivityRecord r, @Nullable Task task,
int activityType) {
// Make sure the windowing mode we are trying to use makes sense for what is supported.
boolean supportsMultiWindow = mAtmService.mSupportsMultiWindow;
@@ -1362,24 +1361,35 @@ final class TaskDisplayArea extends DisplayArea<ActivityStack> {
}
}
+ return windowingMode != WINDOWING_MODE_UNDEFINED
+ && isWindowingModeSupported(windowingMode, supportsMultiWindow, supportsSplitScreen,
+ supportsFreeform, supportsPip, activityType);
+ }
+
+ /**
+ * Check that the requested windowing-mode is appropriate for the specified task and/or activity
+ * on this display.
+ *
+ * @param windowingMode The windowing-mode to validate.
+ * @param r The {@link ActivityRecord} to check against.
+ * @param task The {@link Task} to check against.
+ * @param activityType An activity type.
+ * @return The provided windowingMode or the closest valid mode which is appropriate.
+ */
+ int validateWindowingMode(int windowingMode, @Nullable ActivityRecord r, @Nullable Task task,
+ int activityType) {
final boolean inSplitScreenMode = isSplitScreenModeActivated();
- if (!inSplitScreenMode
- && windowingMode == WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY) {
+ if (!inSplitScreenMode && windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY) {
// Switch to the display's windowing mode if we are not in split-screen mode and we are
// trying to launch in split-screen secondary.
windowingMode = WINDOWING_MODE_UNDEFINED;
- } else if (inSplitScreenMode && (windowingMode == WINDOWING_MODE_FULLSCREEN
- || windowingMode == WINDOWING_MODE_UNDEFINED)
- && supportsSplitScreen) {
+ } else if (inSplitScreenMode && windowingMode == WINDOWING_MODE_UNDEFINED) {
windowingMode = WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
}
-
- if (windowingMode != WINDOWING_MODE_UNDEFINED
- && isWindowingModeSupported(windowingMode, supportsMultiWindow, supportsSplitScreen,
- supportsFreeform, supportsPip, activityType)) {
- return windowingMode;
+ if (!isValidWindowingMode(windowingMode, r, task, activityType)) {
+ return WINDOWING_MODE_UNDEFINED;
}
- return WINDOWING_MODE_UNDEFINED;
+ return windowingMode;
}
boolean isTopStack(ActivityStack stack) {
diff --git a/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java b/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java
index 79baab6bfbb6..06c2b1687fa4 100644
--- a/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java
+++ b/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java
@@ -48,6 +48,16 @@ public class TaskTapPointerEventListener implements PointerEventListener {
mDisplayContent = displayContent;
}
+ private void restorePointerIcon(int x, int y) {
+ if (mPointerIconType != TYPE_NOT_SPECIFIED) {
+ mPointerIconType = TYPE_NOT_SPECIFIED;
+ // Find the underlying window and ask it to restore the pointer icon.
+ mService.mH.removeMessages(H.RESTORE_POINTER_ICON);
+ mService.mH.obtainMessage(H.RESTORE_POINTER_ICON,
+ x, y, mDisplayContent).sendToTarget();
+ }
+ }
+
@Override
public void onPointerEvent(MotionEvent motionEvent) {
switch (motionEvent.getActionMasked()) {
@@ -67,6 +77,10 @@ public class TaskTapPointerEventListener implements PointerEventListener {
case MotionEvent.ACTION_HOVER_MOVE: {
final int x = (int) motionEvent.getX();
final int y = (int) motionEvent.getY();
+ if (mTouchExcludeRegion.contains(x, y)) {
+ restorePointerIcon(x, y);
+ break;
+ }
final Task task = mDisplayContent.findTaskForResizePoint(x, y);
int iconType = TYPE_NOT_SPECIFIED;
if (task != null) {
@@ -103,13 +117,7 @@ public class TaskTapPointerEventListener implements PointerEventListener {
case MotionEvent.ACTION_HOVER_EXIT: {
final int x = (int) motionEvent.getX();
final int y = (int) motionEvent.getY();
- if (mPointerIconType != TYPE_NOT_SPECIFIED) {
- mPointerIconType = TYPE_NOT_SPECIFIED;
- // Find the underlying window and ask it to restore the pointer icon.
- mService.mH.removeMessages(H.RESTORE_POINTER_ICON);
- mService.mH.obtainMessage(H.RESTORE_POINTER_ICON,
- x, y, mDisplayContent).sendToTarget();
- }
+ restorePointerIcon(x, y);
}
break;
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 07840b5163a4..ae0c9b15689a 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -72,6 +72,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_QS_DIALOG;
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION;
+import static android.view.WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
import static android.view.WindowManager.REMOVE_CONTENT_MODE_UNDEFINED;
import static android.view.WindowManagerGlobal.ADD_OKAY;
@@ -926,8 +927,14 @@ public class WindowManagerService extends IWindowManager.Stub
}
void updateFixedRotationTransform() {
- mIsFixedRotationTransformEnabled = Settings.Global.getInt(mContext.getContentResolver(),
- FIXED_ROTATION_TRANSFORM_SETTING_NAME, 0) != 0;
+ final int enabled = Settings.Global.getInt(mContext.getContentResolver(),
+ FIXED_ROTATION_TRANSFORM_SETTING_NAME, 2);
+ if (enabled == 2) {
+ // Make sure who read the settings won't use inconsistent default value.
+ Settings.Global.putInt(mContext.getContentResolver(),
+ FIXED_ROTATION_TRANSFORM_SETTING_NAME, 1);
+ }
+ mIsFixedRotationTransformEnabled = enabled != 0;
}
}
@@ -1057,12 +1064,10 @@ public class WindowManagerService extends IWindowManager.Stub
@Override
public void onAppTransitionCancelledLocked(int transit) {
- mAtmInternal.notifyAppTransitionCancelled();
}
@Override
public void onAppTransitionFinishedLocked(IBinder token) {
- mAtmInternal.notifyAppTransitionFinished();
final ActivityRecord atoken = mRoot.getActivityRecord(token);
if (atoken == null) {
return;
@@ -1367,6 +1372,7 @@ public class WindowManagerService extends IWindowManager.Stub
case TYPE_NOTIFICATION_SHADE:
case TYPE_NAVIGATION_BAR:
case TYPE_INPUT_METHOD_DIALOG:
+ case TYPE_VOLUME_OVERLAY:
return true;
}
return false;
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 0e83beed6b90..c570cf1d949f 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -846,6 +846,23 @@ class WindowStateAnimator {
}
}
+ private boolean shouldConsumeMainWindowSizeTransaction() {
+ // We only consume the transaction when the client is calling relayout
+ // because this is the only time we know the frameNumber will be valid
+ // due to the client renderer being paused. Put otherwise, only when
+ // mInRelayout is true can we guarantee the next frame will contain
+ // the most recent configuration.
+ if (!mWin.mInRelayout) return false;
+ // Since we can only do this for one window, we focus on the main application window
+ if (mAttrType != TYPE_BASE_APPLICATION) return false;
+ final Task task = mWin.getTask();
+ if (task == null) return false;
+ if (task.getMainWindowSizeChangeTransaction() == null) return false;
+ // Likewise we only focus on the task root, since we can only use one window
+ if (!mWin.mActivityRecord.isRootOfTask()) return false;
+ return true;
+ }
+
void setSurfaceBoundariesLocked(final boolean recoveringMemory) {
if (mSurfaceController == null) {
return;
@@ -886,8 +903,9 @@ class WindowStateAnimator {
clipRect = mTmpClipRect;
}
- if (w.mInRelayout && (mAttrType == TYPE_BASE_APPLICATION) && (task != null)
- && (task.getMainWindowSizeChangeTransaction() != null)) {
+ if (shouldConsumeMainWindowSizeTransaction()) {
+ task.getSurfaceControl().deferTransactionUntil(mWin.getClientViewRootSurface(),
+ mWin.getFrameNumber());
mSurfaceController.deferTransactionUntil(mWin.getClientViewRootSurface(),
mWin.getFrameNumber());
SurfaceControl.mergeToGlobalTransaction(task.getMainWindowSizeChangeTransaction());
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 9bc5d34c11af..20139451e4b9 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -237,27 +237,28 @@ public:
/* --- InputDispatcherPolicyInterface implementation --- */
virtual void notifySwitch(nsecs_t when, uint32_t switchValues, uint32_t switchMask,
- uint32_t policyFlags);
+ uint32_t policyFlags) override;
virtual void notifyConfigurationChanged(nsecs_t when);
- virtual nsecs_t notifyANR(const sp<InputApplicationHandle>& inputApplicationHandle,
- const sp<IBinder>& token,
- const std::string& reason);
+ virtual nsecs_t notifyAnr(const sp<InputApplicationHandle>& inputApplicationHandle,
+ const sp<IBinder>& token, const std::string& reason) override;
virtual void notifyInputChannelBroken(const sp<IBinder>& token);
- virtual void notifyFocusChanged(const sp<IBinder>& oldToken, const sp<IBinder>& newToken);
- virtual bool filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags);
- virtual void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig);
- virtual void interceptKeyBeforeQueueing(const KeyEvent* keyEvent, uint32_t& policyFlags);
+ virtual void notifyFocusChanged(const sp<IBinder>& oldToken,
+ const sp<IBinder>& newToken) override;
+ virtual bool filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags) override;
+ virtual void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) override;
+ virtual void interceptKeyBeforeQueueing(const KeyEvent* keyEvent,
+ uint32_t& policyFlags) override;
virtual void interceptMotionBeforeQueueing(const int32_t displayId, nsecs_t when,
- uint32_t& policyFlags);
- virtual nsecs_t interceptKeyBeforeDispatching(
- const sp<IBinder>& token,
- const KeyEvent* keyEvent, uint32_t policyFlags);
- virtual bool dispatchUnhandledKey(const sp<IBinder>& token,
- const KeyEvent* keyEvent, uint32_t policyFlags, KeyEvent* outFallbackKeyEvent);
- virtual void pokeUserActivity(nsecs_t eventTime, int32_t eventType);
- virtual bool checkInjectEventsPermissionNonReentrant(
- int32_t injectorPid, int32_t injectorUid);
- virtual void onPointerDownOutsideFocus(const sp<IBinder>& touchedToken);
+ uint32_t& policyFlags) override;
+ virtual nsecs_t interceptKeyBeforeDispatching(const sp<IBinder>& token,
+ const KeyEvent* keyEvent,
+ uint32_t policyFlags) override;
+ virtual bool dispatchUnhandledKey(const sp<IBinder>& token, const KeyEvent* keyEvent,
+ uint32_t policyFlags, KeyEvent* outFallbackKeyEvent) override;
+ virtual void pokeUserActivity(nsecs_t eventTime, int32_t eventType) override;
+ virtual bool checkInjectEventsPermissionNonReentrant(int32_t injectorPid,
+ int32_t injectorUid) override;
+ virtual void onPointerDownOutsideFocus(const sp<IBinder>& touchedToken) override;
/* --- PointerControllerPolicyInterface implementation --- */
@@ -692,9 +693,8 @@ static jobject getInputApplicationHandleObjLocalRef(JNIEnv* env,
return handle->getInputApplicationHandleObjLocalRef(env);
}
-
-nsecs_t NativeInputManager::notifyANR(const sp<InputApplicationHandle>& inputApplicationHandle,
- const sp<IBinder>& token, const std::string& reason) {
+nsecs_t NativeInputManager::notifyAnr(const sp<InputApplicationHandle>& inputApplicationHandle,
+ const sp<IBinder>& token, const std::string& reason) {
#if DEBUG_INPUT_DISPATCHER_POLICY
ALOGD("notifyANR");
#endif
@@ -1453,9 +1453,13 @@ static jint nativeInjectInputEvent(JNIEnv* env, jclass /* clazz */,
return INPUT_EVENT_INJECTION_FAILED;
}
- return (jint) im->getInputManager()->getDispatcher()->injectInputEvent(
- & keyEvent, injectorPid, injectorUid, syncMode, timeoutMillis,
- uint32_t(policyFlags));
+ const int32_t result =
+ im->getInputManager()->getDispatcher()->injectInputEvent(&keyEvent, injectorPid,
+ injectorUid, syncMode,
+ std::chrono::milliseconds(
+ timeoutMillis),
+ uint32_t(policyFlags));
+ return static_cast<jint>(result);
} else if (env->IsInstanceOf(inputEventObj, gMotionEventClassInfo.clazz)) {
const MotionEvent* motionEvent = android_view_MotionEvent_getNativePtr(env, inputEventObj);
if (!motionEvent) {
@@ -1463,9 +1467,13 @@ static jint nativeInjectInputEvent(JNIEnv* env, jclass /* clazz */,
return INPUT_EVENT_INJECTION_FAILED;
}
- return (jint) im->getInputManager()->getDispatcher()->injectInputEvent(
- motionEvent, injectorPid, injectorUid, syncMode, timeoutMillis,
- uint32_t(policyFlags));
+ const int32_t result =
+ (jint)im->getInputManager()
+ ->getDispatcher()
+ ->injectInputEvent(motionEvent, injectorPid, injectorUid, syncMode,
+ std::chrono::milliseconds(timeoutMillis),
+ uint32_t(policyFlags));
+ return static_cast<jint>(result);
} else {
jniThrowRuntimeException(env, "Invalid input event type.");
return INPUT_EVENT_INJECTION_FAILED;
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 3323fa4b53e3..966694ad346c 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -4567,9 +4567,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
if (isProfileOwner(adminReceiver, userHandle)) {
if (isProfileOwnerOfOrganizationOwnedDevice(userHandle)) {
+ UserHandle parentUserHandle = UserHandle.of(getProfileParentId(userHandle));
mUserManager.setUserRestriction(UserManager.DISALLOW_REMOVE_MANAGED_PROFILE,
- false,
- UserHandle.of(getProfileParentId(userHandle)));
+ false, parentUserHandle);
+ mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_USER,
+ false, parentUserHandle);
}
final ActiveAdmin admin = getActiveAdminUncheckedLocked(adminReceiver,
userHandle, /* parent */ false);
@@ -7213,6 +7215,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
mUserManager.setUserRestriction(
UserManager.DISALLOW_REMOVE_MANAGED_PROFILE, false,
UserHandle.SYSTEM);
+ mUserManager.setUserRestriction(
+ UserManager.DISALLOW_ADD_USER, false, UserHandle.SYSTEM);
// Device-wide policies set by the profile owner need to be cleaned up here.
mLockPatternUtils.setDeviceOwnerInfo(null);
@@ -13825,6 +13829,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
mUserManager.setUserRestriction(UserManager.DISALLOW_REMOVE_MANAGED_PROFILE, true,
parentUser);
+ mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_USER, true,
+ parentUser);
});
// markProfileOwnerOfOrganizationOwnedDevice will trigger writing of the profile owner
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 0fc333f4b38c..fa3f33067e8e 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -1891,6 +1891,10 @@ public final class SystemServer {
|| mPackageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK)) {
t.traceBegin("StartTvInputManager");
mSystemServiceManager.startService(TvInputManagerService.class);
+ t.traceEnd();
+ }
+
+ if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_TUNER)) {
t.traceBegin("StartTunerResourceManager");
mSystemServiceManager.startService(TunerResourceManagerService.class);
t.traceEnd();
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 6b36bc591b78..ed40fe756ea1 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -1998,7 +1998,6 @@ public class DevicePolicyManagerTest extends DpmTestBase {
private static final Set<String> PROFILE_OWNER_ORGANIZATION_OWNED_GLOBAL_RESTRICTIONS =
Sets.newSet(
UserManager.DISALLOW_CONFIG_DATE_TIME,
- UserManager.DISALLOW_ADD_USER,
UserManager.DISALLOW_BLUETOOTH_SHARING,
UserManager.DISALLOW_CONFIG_CELL_BROADCASTS,
UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS,
@@ -4005,6 +4004,12 @@ public class DevicePolicyManagerTest extends DpmTestBase {
// Any caller should be able to call this method.
assertFalse(dpm.isOrganizationOwnedDeviceWithManagedProfile());
configureProfileOwnerOfOrgOwnedDevice(admin1, CALLER_USER_HANDLE);
+
+ verify(getServices().userManager).setUserRestriction(
+ eq(UserManager.DISALLOW_ADD_USER),
+ eq(true),
+ eq(UserHandle.of(UserHandle.USER_SYSTEM)));
+
assertTrue(dpm.isOrganizationOwnedDeviceWithManagedProfile());
// A random caller from another user should also be able to get the right result.
@@ -4012,6 +4017,35 @@ public class DevicePolicyManagerTest extends DpmTestBase {
assertTrue(dpm.isOrganizationOwnedDeviceWithManagedProfile());
}
+ public void testMarkOrganizationOwnedDevice_baseRestrictionsAdded() throws Exception {
+ addManagedProfile(admin1, DpmMockContext.CALLER_UID, admin1);
+
+ configureProfileOwnerOfOrgOwnedDevice(admin1, CALLER_USER_HANDLE);
+
+ // Base restriction DISALLOW_REMOVE_MANAGED_PROFILE added
+ verify(getServices().userManager).setUserRestriction(
+ eq(UserManager.DISALLOW_REMOVE_MANAGED_PROFILE),
+ eq(true),
+ eq(UserHandle.of(UserHandle.USER_SYSTEM)));
+
+ // Base restriction DISALLOW_ADD_USER added
+ verify(getServices().userManager).setUserRestriction(
+ eq(UserManager.DISALLOW_ADD_USER),
+ eq(true),
+ eq(UserHandle.of(UserHandle.USER_SYSTEM)));
+
+ // Assert base restrictions cannot be added or removed by admin
+ assertExpectException(SecurityException.class, null, () ->
+ parentDpm.addUserRestriction(admin1, UserManager.DISALLOW_REMOVE_MANAGED_PROFILE));
+ assertExpectException(SecurityException.class, null, () ->
+ parentDpm.clearUserRestriction(admin1,
+ UserManager.DISALLOW_REMOVE_MANAGED_PROFILE));
+ assertExpectException(SecurityException.class, null, () ->
+ parentDpm.addUserRestriction(admin1, UserManager.DISALLOW_ADD_USER));
+ assertExpectException(SecurityException.class, null, () ->
+ parentDpm.clearUserRestriction(admin1, UserManager.DISALLOW_ADD_USER));
+ }
+
public void testSetTime() throws Exception {
mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
setupDeviceOwner();
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageInstallerSessionTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageInstallerSessionTest.java
index d4edab44bae3..63d797e9b95c 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageInstallerSessionTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageInstallerSessionTest.java
@@ -178,6 +178,7 @@ public class PackageInstallerSessionTest {
/* files */ null,
/* prepared */ true,
/* committed */ true,
+ /* destroyed */ staged ? true : false,
/* sealed */ false, // Setting to true would trigger some PM logic.
/* childSessionIds */ childSessionIds != null ? childSessionIds : new int[0],
/* parentSessionId */ parentSessionId,
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
index daaf870fa695..b0b5386a49bd 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
@@ -154,18 +154,7 @@ public class PackageParserTest {
@Test
public void test_serializePackage() throws Exception {
- try (PackageParser2 pp = new PackageParser2(null, false, null, mTmpDir,
- new PackageParser2.Callback() {
- @Override
- public boolean isChangeEnabled(long changeId, @NonNull ApplicationInfo appInfo) {
- return true;
- }
-
- @Override
- public boolean hasFeature(String feature) {
- return false;
- }
- })) {
+ try (PackageParser2 pp = PackageParser2.forParsingFileWithDefaults()) {
ParsedPackage pkg = pp.parsePackage(FRAMEWORK, 0 /* parseFlags */,
true /* useCaches */);
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexMetadataHelperTest.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexMetadataHelperTest.java
index 3888ff3e278a..caa8ae5e0e39 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/DexMetadataHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexMetadataHelperTest.java
@@ -28,8 +28,13 @@ import android.content.pm.PackageParser.ApkLite;
import android.content.pm.PackageParser.PackageLite;
import android.content.pm.PackageParser.PackageParserException;
import android.content.pm.dex.DexMetadataHelper;
+import android.content.pm.parsing.ApkLiteParseUtils;
+import android.content.pm.parsing.result.ParseInput;
+import android.content.pm.parsing.result.ParseResult;
+import android.content.pm.parsing.result.ParseTypeImpl;
import android.os.FileUtils;
+import androidx.annotation.NonNull;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -208,9 +213,12 @@ public class DexMetadataHelperTest {
throws IOException, PackageParserException {
copyApkToToTmpDir("install_split_base.apk", R.raw.install_split_base);
File dm = createDexMetadataFile("install_split_base.apk");
- PackageParser.PackageLite pkg = new PackageParser().parsePackageLite(mTmpDir,
- 0 /* flags */);
-
+ ParseResult<PackageLite> result = ApkLiteParseUtils.parsePackageLite(
+ ParseTypeImpl.forDefaultParsing().reset(), mTmpDir, 0 /* flags */);
+ if (result.isError()) {
+ throw new IllegalStateException(result.getErrorMessage(), result.getException());
+ }
+ PackageParser.PackageLite pkg = result.getResult();
Assert.assertEquals(dm.length(), DexMetadataHelper.getPackageDexMetadataSize(pkg));
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingTestBase.kt b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingTestBase.kt
index 6de08fd1251f..086c845fa4b6 100644
--- a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingTestBase.kt
+++ b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingTestBase.kt
@@ -30,10 +30,8 @@ import android.content.pm.PermissionInfo
import android.content.pm.ProviderInfo
import android.os.Debug
import android.os.Environment
-import android.os.ServiceManager
import android.util.SparseArray
import androidx.test.platform.app.InstrumentationRegistry
-import com.android.internal.compat.IPlatformCompat
import com.android.server.pm.PackageManagerService
import com.android.server.pm.PackageSetting
import com.android.server.pm.parsing.pkg.AndroidPackage
@@ -63,27 +61,7 @@ open class AndroidPackageParsingTestBase {
setCallback { false /* hasFeature */ }
}
- private val platformCompat = IPlatformCompat.Stub
- .asInterface(ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE))
-
- protected val packageParser2 = PackageParser2(null /* separateProcesses */,
- false /* onlyCoreApps */, context.resources.displayMetrics, null /* cacheDir */,
- object : PackageParser2.Callback() {
- override fun isChangeEnabled(
- changeId: Long,
- appInfo: ApplicationInfo
- ): Boolean {
- // This test queries PlatformCompat because prebuilts in the tree
- // may not be updated to be compliant with the latest enforcement checks.
- return platformCompat.isChangeEnabled(changeId, appInfo)
- }
-
- // Assume the device doesn't support anything. This will affect permission
- // parsing and will force <uses-permission/> declarations to include all
- // requiredNotFeature permissions and exclude all requiredFeature permissions.
- // This mirrors the old behavior.
- override fun hasFeature(feature: String) = false
- })
+ protected val packageParser2 = PackageParser2.forParsingFileWithDefaults()
/**
* It would be difficult to mock all possibilities, so just use the APKs on device.
diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/PackageParserLegacyCoreTest.java b/services/tests/servicestests/src/com/android/server/pm/parsing/PackageParserLegacyCoreTest.java
index 939b7a0beb49..bb223b33d8aa 100644
--- a/services/tests/servicestests/src/com/android/server/pm/parsing/PackageParserLegacyCoreTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/parsing/PackageParserLegacyCoreTest.java
@@ -29,8 +29,12 @@ import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageParser;
import android.content.pm.PermissionInfo;
+import android.content.pm.parsing.PackageInfoWithoutStateUtils;
+import android.content.pm.parsing.ParsingPackage;
+import android.content.pm.parsing.ParsingPackageUtils;
import android.content.pm.parsing.component.ParsedComponent;
import android.content.pm.parsing.component.ParsedPermission;
+import android.content.pm.parsing.result.ParseResult;
import android.os.Build;
import android.os.Bundle;
import android.os.FileUtils;
@@ -518,10 +522,15 @@ public class PackageParserLegacyCoreTest {
apexInfo.versionCode = 191000070;
int flags = PackageManager.GET_META_DATA | PackageManager.GET_SIGNING_CERTIFICATES;
- PackageParser pp = new PackageParser();
- PackageParser.Package p = pp.parsePackage(apexFile, flags, false);
- PackageParser.collectCertificates(p, false);
- PackageInfo pi = PackageParser.generatePackageInfo(p, apexInfo, flags);
+ ParseResult<ParsingPackage> result = ParsingPackageUtils.parseDefaultOneTime(apexFile,
+ flags, false /*collectCertificates*/);
+ if (result.isError()) {
+ throw new IllegalStateException(result.getErrorMessage(), result.getException());
+ }
+
+ ParsingPackage pkg = result.getResult();
+ pkg.setSigningDetails(ParsingPackageUtils.getSigningDetails(pkg, false));
+ PackageInfo pi = PackageInfoWithoutStateUtils.generate(pkg, apexInfo, flags);
assertEquals("com.google.android.tzdata", pi.applicationInfo.packageName);
assertTrue(pi.applicationInfo.enabled);
diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/PackageParsingDeferErrorTest.kt b/services/tests/servicestests/src/com/android/server/pm/parsing/PackageParsingDeferErrorTest.kt
index 22487071cd71..d8910dec0bcc 100644
--- a/services/tests/servicestests/src/com/android/server/pm/parsing/PackageParsingDeferErrorTest.kt
+++ b/services/tests/servicestests/src/com/android/server/pm/parsing/PackageParsingDeferErrorTest.kt
@@ -64,20 +64,6 @@ class PackageParsingDeferErrorTest {
}
}
- private val parsingCallback = object : ParsingPackageUtils.Callback {
- override fun hasFeature(feature: String?) = true
-
- override fun startParsingPackage(
- packageName: String,
- baseCodePath: String,
- codePath: String,
- manifestArray: TypedArray,
- isCoreApp: Boolean
- ): ParsingPackage {
- return ParsingPackageImpl(packageName, baseCodePath, codePath, manifestArray)
- }
- }
-
@get:Rule
val tempFolder = TemporaryFolder(context.filesDir)
@@ -144,6 +130,7 @@ class PackageParsingDeferErrorTest {
input.copyTo(output)
}
}
- return ParsingPackageUtils.parseDefaultOneTime(file, 0, inputCallback, parsingCallback)
+ return ParsingPackageUtils.parseDefaultOneTime(file, 0 /*flags*/,
+ false /*collectCertificates*/)
}
}
diff --git a/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java
index 21af3563b869..f9343236662b 100644
--- a/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java
@@ -64,6 +64,7 @@ public class TunerResourceManagerServiceTest {
private Context mContextSpy;
@Mock private ITvInputManager mITvInputManagerMock;
private TunerResourceManagerService mTunerResourceManagerService;
+ private boolean mIsForeground;
private static final class TestResourcesReclaimListener extends IResourcesReclaimListener.Stub {
boolean mReclaimed;
@@ -104,7 +105,12 @@ public class TunerResourceManagerServiceTest {
TvInputManager tvInputManager = new TvInputManager(mITvInputManagerMock, 0);
mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getTargetContext()));
when(mContextSpy.getSystemService(Context.TV_INPUT_SERVICE)).thenReturn(tvInputManager);
- mTunerResourceManagerService = new TunerResourceManagerService(mContextSpy);
+ mTunerResourceManagerService = new TunerResourceManagerService(mContextSpy) {
+ @Override
+ protected boolean isForeground(int pid) {
+ return mIsForeground;
+ }
+ };
mTunerResourceManagerService.onStart(true /*isForTesting*/);
}
@@ -737,4 +743,22 @@ public class TunerResourceManagerServiceTest {
.isTrue();
assertThat(mTunerResourceManagerService.getResourceIdFromHandle(desHandle[0])).isEqualTo(0);
}
+
+ @Test
+ public void isHigherPriorityTest() {
+ mIsForeground = false;
+ ResourceClientProfile backgroundPlaybackProfile =
+ new ResourceClientProfile(null /*sessionId*/,
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
+ ResourceClientProfile backgroundRecordProfile =
+ new ResourceClientProfile(null /*sessionId*/,
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_RECORD);
+ int backgroundPlaybackPriority = mTunerResourceManagerService.getClientPriority(
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK, 0);
+ int backgroundRecordPriority = mTunerResourceManagerService.getClientPriority(
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_RECORD, 0);
+ assertThat(mTunerResourceManagerService.isHigherPriorityInternal(backgroundPlaybackProfile,
+ backgroundRecordProfile)).isEqualTo(
+ (backgroundPlaybackPriority > backgroundRecordPriority));
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/uri/UriGrantsManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/uri/UriGrantsManagerServiceTest.java
index 0e48e7ed2682..e86399e1a631 100644
--- a/services/tests/servicestests/src/com/android/server/uri/UriGrantsManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/uri/UriGrantsManagerServiceTest.java
@@ -171,7 +171,7 @@ public class UriGrantsManagerServiceTest {
final Uri uri = Uri.parse("content://" + PKG_COMPLEX + "/");
{
final Intent intent = new Intent(Intent.ACTION_VIEW, uri)
- .addFlags(FLAG_READ);
+ .addFlags(FLAG_READ | Intent.FLAG_ACTIVITY_CLEAR_TASK);
assertNull(mService.checkGrantUriPermissionFromIntent(UID_PRIMARY_COMPLEX, PKG_SOCIAL,
intent, intent.getFlags(), null, USER_PRIMARY));
}
diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
index 39062f017a73..6718db768fdb 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
@@ -366,29 +366,87 @@ public class AppStandbyControllerTests {
mInjector.mElapsedRealtime, false));
}
+ private static class TestParoleListener extends AppIdleStateChangeListener {
+ private boolean mIsParoleOn = false;
+ private CountDownLatch mLatch;
+ private boolean mIsExpecting = false;
+ private boolean mExpectedParoleState;
+
+ boolean getParoleState() {
+ synchronized (this) {
+ return mIsParoleOn;
+ }
+ }
+
+ void rearmLatch(boolean expectedParoleState) {
+ synchronized (this) {
+ mLatch = new CountDownLatch(1);
+ mIsExpecting = true;
+ mExpectedParoleState = expectedParoleState;
+ }
+ }
+
+ void awaitOnLatch(long time) throws Exception {
+ mLatch.await(time, TimeUnit.MILLISECONDS);
+ }
+
+ @Override
+ public void onAppIdleStateChanged(String packageName, int userId, boolean idle,
+ int bucket, int reason) {
+ }
+
+ @Override
+ public void onParoleStateChanged(boolean isParoleOn) {
+ synchronized (this) {
+ // Only record information if it is being looked for
+ if (mLatch != null && mLatch.getCount() > 0) {
+ mIsParoleOn = isParoleOn;
+ if (mIsExpecting && isParoleOn == mExpectedParoleState) {
+ mLatch.countDown();
+ }
+ }
+ }
+ }
+ }
+
@Test
public void testIsAppIdle_Charging() throws Exception {
+ TestParoleListener paroleListener = new TestParoleListener();
+ mController.addListener(paroleListener);
+
setChargingState(mController, false);
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE,
REASON_MAIN_FORCED_BY_SYSTEM);
assertEquals(STANDBY_BUCKET_RARE, getStandbyBucket(mController, PACKAGE_1));
assertTrue(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, 0));
assertTrue(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, false));
+ assertFalse(mController.isInParole());
+ paroleListener.rearmLatch(true);
setChargingState(mController, true);
+ paroleListener.awaitOnLatch(2000);
+ assertTrue(paroleListener.getParoleState());
assertEquals(STANDBY_BUCKET_RARE, getStandbyBucket(mController, PACKAGE_1));
assertFalse(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, 0));
assertFalse(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, false));
+ assertTrue(mController.isInParole());
+ paroleListener.rearmLatch(false);
setChargingState(mController, false);
+ paroleListener.awaitOnLatch(2000);
+ assertFalse(paroleListener.getParoleState());
assertEquals(STANDBY_BUCKET_RARE, getStandbyBucket(mController, PACKAGE_1));
assertTrue(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, 0));
assertTrue(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, false));
+ assertFalse(mController.isInParole());
}
@Test
public void testIsAppIdle_Enabled() throws Exception {
setChargingState(mController, false);
+ TestParoleListener paroleListener = new TestParoleListener();
+ mController.addListener(paroleListener);
+
setAppIdleEnabled(mController, true);
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE,
REASON_MAIN_FORCED_BY_SYSTEM);
@@ -396,11 +454,17 @@ public class AppStandbyControllerTests {
assertTrue(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, 0));
assertTrue(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, false));
+ paroleListener.rearmLatch(false);
setAppIdleEnabled(mController, false);
+ paroleListener.awaitOnLatch(2000);
+ assertTrue(paroleListener.mIsParoleOn);
assertFalse(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, 0));
assertFalse(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, false));
+ paroleListener.rearmLatch(true);
setAppIdleEnabled(mController, true);
+ paroleListener.awaitOnLatch(2000);
+ assertFalse(paroleListener.getParoleState());
assertEquals(STANDBY_BUCKET_RARE, getStandbyBucket(mController, PACKAGE_1));
assertTrue(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, 0));
assertTrue(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, false));
diff --git a/services/tests/uiservicestests/src/com/android/server/UiServiceTestCase.java b/services/tests/uiservicestests/src/com/android/server/UiServiceTestCase.java
index 3c2d55058c3e..182bf949af1f 100644
--- a/services/tests/uiservicestests/src/com/android/server/UiServiceTestCase.java
+++ b/services/tests/uiservicestests/src/com/android/server/UiServiceTestCase.java
@@ -41,6 +41,7 @@ public class UiServiceTestCase {
protected static final String PKG_N_MR1 = "com.example.n_mr1";
protected static final String PKG_O = "com.example.o";
protected static final String PKG_P = "com.example.p";
+ protected static final String PKG_R = "com.example.r";
@Rule
public final TestableContext mContext =
@@ -69,6 +70,8 @@ public class UiServiceTestCase {
return Build.VERSION_CODES.O;
case PKG_P:
return Build.VERSION_CODES.P;
+ case PKG_R:
+ return Build.VERSION_CODES.R;
default:
return Build.VERSION_CODES.CUR_DEVELOPMENT;
}
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 babe80e4b612..289933e5ecb2 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -250,6 +250,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
private static final int NOTIFICATION_LOCATION_UNKNOWN = 0;
+ private static final String VALID_CONVO_SHORTCUT_ID = "shortcut";
+
@Mock
private NotificationListeners mListeners;
@Mock private NotificationAssistants mAssistants;
@@ -471,6 +473,19 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
mShortcutHelper.setLauncherApps(mLauncherApps);
mShortcutHelper.setShortcutServiceInternal(mShortcutServiceInternal);
+ // Pretend the shortcut exists
+ List<ShortcutInfo> shortcutInfos = new ArrayList<>();
+ ShortcutInfo info = mock(ShortcutInfo.class);
+ when(info.getPackage()).thenReturn(PKG);
+ when(info.getId()).thenReturn(VALID_CONVO_SHORTCUT_ID);
+ when(info.getUserId()).thenReturn(USER_SYSTEM);
+ when(info.isLongLived()).thenReturn(true);
+ when(info.isEnabled()).thenReturn(true);
+ shortcutInfos.add(info);
+ when(mLauncherApps.getShortcuts(any(), any())).thenReturn(shortcutInfos);
+ when(mShortcutServiceInternal.isSharingShortcut(anyInt(), anyString(), anyString(),
+ anyString(), anyInt(), any())).thenReturn(true);
+
// Set the testable bubble extractor
RankingHelper rankingHelper = mService.getRankingHelper();
BubbleExtractor extractor = rankingHelper.findExtractor(BubbleExtractor.class);
@@ -704,6 +719,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
)
.setActions(replyAction)
.setSmallIcon(android.R.drawable.sym_def_app_icon)
+ .setShortcutId(VALID_CONVO_SHORTCUT_ID)
.setGroupSummary(isSummary);
if (groupKey != null) {
nb.setGroup(groupKey);
@@ -6100,7 +6116,6 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
@Test
public void testNotificationBubbles_flagRemoved_whenShortcutRemoved()
throws RemoteException {
- final String shortcutId = "someshortcutId";
setUpPrefsForBubbles(PKG, mUid,
true /* global */,
BUBBLE_PREFERENCE_ALL /* app */,
@@ -6111,27 +6126,16 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
// Messaging notification with shortcut info
Notification.BubbleMetadata metadata =
- new Notification.BubbleMetadata.Builder(shortcutId).build();
+ new Notification.BubbleMetadata.Builder(VALID_CONVO_SHORTCUT_ID).build();
Notification.Builder nb = getMessageStyleNotifBuilder(false /* addDefaultMetadata */,
null /* groupKey */, false /* isSummary */);
- nb.setShortcutId(shortcutId);
+ 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);
NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
- // Pretend the shortcut exists
- List<ShortcutInfo> shortcutInfos = new ArrayList<>();
- ShortcutInfo info = mock(ShortcutInfo.class);
- when(info.getPackage()).thenReturn(PKG);
- when(info.getId()).thenReturn(shortcutId);
- when(info.getUserId()).thenReturn(USER_SYSTEM);
- when(info.isLongLived()).thenReturn(true);
- when(info.isEnabled()).thenReturn(true);
- shortcutInfos.add(info);
- when(mLauncherApps.getShortcuts(any(), any())).thenReturn(shortcutInfos);
- when(mShortcutServiceInternal.isSharingShortcut(anyInt(), anyString(), anyString(),
- anyString(), anyInt(), any())).thenReturn(true);
+
// Test: Send the bubble notification
mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.getSbn().getTag(),
@@ -6149,7 +6153,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
// Make sure the shortcut is cached.
verify(mShortcutServiceInternal).cacheShortcuts(
- anyInt(), any(), eq(PKG), eq(Collections.singletonList(shortcutId)),
+ anyInt(), any(), eq(PKG), eq(Collections.singletonList(VALID_CONVO_SHORTCUT_ID)),
eq(USER_SYSTEM));
// Test: Remove the shortcut
@@ -6613,6 +6617,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
convo2.setNotificationChannel(channel2);
convos.add(convo2);
when(mPreferencesHelper.getConversations(anyString(), anyInt())).thenReturn(convos);
+ when(mLauncherApps.getShortcuts(any(), any())).thenReturn(null);
List<ConversationChannelWrapper> conversations =
mBinderService.getConversationsForPackage(PKG_P, mUid).getList();
@@ -6640,6 +6645,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
NotificationRecord nr =
generateMessageBubbleNotifRecord(mTestNotificationChannel,
"testRecordMessages_invalidMsg");
+ when(mLauncherApps.getShortcuts(any(), any())).thenReturn(null);
mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.getSbn().getTag(),
nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId());
waitForIdle();
@@ -6660,17 +6666,6 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
"testRecordMessages_validMsg", mUid, 0, nb.build(), new UserHandle(mUid), null, 0);
NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
- // Pretend the shortcut exists
- List<ShortcutInfo> shortcutInfos = new ArrayList<>();
- ShortcutInfo info = mock(ShortcutInfo.class);
- when(info.getPackage()).thenReturn(PKG);
- when(info.getId()).thenReturn("id");
- when(info.getUserId()).thenReturn(USER_SYSTEM);
- when(info.isLongLived()).thenReturn(true);
- when(info.isEnabled()).thenReturn(true);
- shortcutInfos.add(info);
- when(mLauncherApps.getShortcuts(any(), any())).thenReturn(shortcutInfos);
-
mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.getSbn().getTag(),
nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId());
waitForIdle();
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
index 9f593ce42741..b03596a35c32 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
@@ -39,7 +39,6 @@ import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
-import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.IActivityManager;
import android.app.Notification;
@@ -90,7 +89,7 @@ public class NotificationRecordTest extends UiServiceTestCase {
@Mock private PackageManager mPm;
@Mock private ContentResolver mContentResolver;
- private final String pkg = PKG_N_MR1;
+ private final String mPkg = PKG_O;
private final int uid = 9583;
private final int id1 = 1;
private final String tag1 = "tag1";
@@ -198,10 +197,14 @@ public class NotificationRecordTest extends UiServiceTestCase {
}
Notification n = builder.build();
- return new StatusBarNotification(pkg, pkg, id1, tag1, uid, uid, n, mUser, null, uid);
+ return new StatusBarNotification(mPkg, mPkg, id1, tag1, uid, uid, n, mUser, null, uid);
}
private StatusBarNotification getMessagingStyleNotification() {
+ return getMessagingStyleNotification(mPkg);
+ }
+
+ private StatusBarNotification getMessagingStyleNotification(String pkg) {
final Builder builder = new Builder(mMockContext)
.setContentTitle("foo")
.setSmallIcon(android.R.drawable.sym_def_app_icon);
@@ -658,7 +661,7 @@ public class NotificationRecordTest extends UiServiceTestCase {
Bundle signals = new Bundle();
signals.putInt(Adjustment.KEY_USER_SENTIMENT, USER_SENTIMENT_NEGATIVE);
- record.addAdjustment(new Adjustment(pkg, record.getKey(), signals, null, sbn.getUserId()));
+ record.addAdjustment(new Adjustment(mPkg, record.getKey(), signals, null, sbn.getUserId()));
record.applyAdjustments();
@@ -687,7 +690,7 @@ public class NotificationRecordTest extends UiServiceTestCase {
Bundle signals = new Bundle();
signals.putInt(Adjustment.KEY_USER_SENTIMENT, USER_SENTIMENT_NEGATIVE);
- record.addAdjustment(new Adjustment(pkg, record.getKey(), signals, null, sbn.getUserId()));
+ record.addAdjustment(new Adjustment(mPkg, record.getKey(), signals, null, sbn.getUserId()));
record.applyAdjustments();
assertEquals(USER_SENTIMENT_POSITIVE, record.getUserSentiment());
@@ -705,7 +708,7 @@ public class NotificationRecordTest extends UiServiceTestCase {
Bundle signals = new Bundle();
signals.putInt(Adjustment.KEY_USER_SENTIMENT, USER_SENTIMENT_NEGATIVE);
- record.addAdjustment(new Adjustment(pkg, record.getKey(), signals, null, sbn.getUserId()));
+ record.addAdjustment(new Adjustment(mPkg, record.getKey(), signals, null, sbn.getUserId()));
record.applyAdjustments();
@@ -1134,6 +1137,15 @@ public class NotificationRecordTest extends UiServiceTestCase {
}
@Test
+ public void testIsConversation_noShortcut_targetsR() {
+ StatusBarNotification sbn = getMessagingStyleNotification(PKG_R);
+ NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
+ record.setShortcutInfo(null);
+
+ assertFalse(record.isConversation());
+ }
+
+ @Test
public void testIsConversation_channelDemoted() {
StatusBarNotification sbn = getMessagingStyleNotification();
channel.setDemoted(true);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
index 2ea58a028a0a..fdc5c7bf0ce1 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
@@ -836,7 +836,7 @@ public class WindowOrganizerTests extends WindowTestsBase {
spyOn(record);
doReturn(true).when(record).checkEnterPictureInPictureState(any(), anyBoolean());
- record.getRootTask().setHasBeenVisible(true);
+ record.getTask().setHasBeenVisible(true);
return record;
}
diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java
index a716b37f7efd..56cba1d3f913 100755
--- a/telecomm/java/android/telecom/ConnectionService.java
+++ b/telecomm/java/android/telecom/ConnectionService.java
@@ -2102,12 +2102,12 @@ public abstract class ConnectionService extends Service {
private void abort(String callId) {
- Log.d(this, "abort %s", callId);
+ Log.i(this, "abort %s", callId);
findConnectionForAction(callId, "abort").onAbort();
}
private void answerVideo(String callId, int videoState) {
- Log.d(this, "answerVideo %s", callId);
+ Log.i(this, "answerVideo %s", callId);
if (mConnectionById.containsKey(callId)) {
findConnectionForAction(callId, "answer").onAnswer(videoState);
} else {
@@ -2116,7 +2116,7 @@ public abstract class ConnectionService extends Service {
}
private void answer(String callId) {
- Log.d(this, "answer %s", callId);
+ Log.i(this, "answer %s", callId);
if (mConnectionById.containsKey(callId)) {
findConnectionForAction(callId, "answer").onAnswer();
} else {
@@ -2125,12 +2125,12 @@ public abstract class ConnectionService extends Service {
}
private void deflect(String callId, Uri address) {
- Log.d(this, "deflect %s", callId);
+ Log.i(this, "deflect %s", callId);
findConnectionForAction(callId, "deflect").onDeflect(address);
}
private void reject(String callId) {
- Log.d(this, "reject %s", callId);
+ Log.i(this, "reject %s", callId);
if (mConnectionById.containsKey(callId)) {
findConnectionForAction(callId, "reject").onReject();
} else {
@@ -2139,34 +2139,34 @@ public abstract class ConnectionService extends Service {
}
private void reject(String callId, String rejectWithMessage) {
- Log.d(this, "reject %s with message", callId);
+ Log.i(this, "reject %s with message", callId);
findConnectionForAction(callId, "reject").onReject(rejectWithMessage);
}
private void reject(String callId, @android.telecom.Call.RejectReason int rejectReason) {
- Log.d(this, "reject %s with reason %d", callId, rejectReason);
+ Log.i(this, "reject %s with reason %d", callId, rejectReason);
findConnectionForAction(callId, "reject").onReject(rejectReason);
}
private void transfer(String callId, Uri number, boolean isConfirmationRequired) {
- Log.d(this, "transfer %s", callId);
+ Log.i(this, "transfer %s", callId);
findConnectionForAction(callId, "transfer").onTransfer(number, isConfirmationRequired);
}
private void consultativeTransfer(String callId, String otherCallId) {
- Log.d(this, "consultativeTransfer %s", callId);
+ Log.i(this, "consultativeTransfer %s", callId);
Connection connection1 = findConnectionForAction(callId, "consultativeTransfer");
Connection connection2 = findConnectionForAction(otherCallId, " consultativeTransfer");
connection1.onTransfer(connection2);
}
private void silence(String callId) {
- Log.d(this, "silence %s", callId);
+ Log.i(this, "silence %s", callId);
findConnectionForAction(callId, "silence").onSilence();
}
private void disconnect(String callId) {
- Log.d(this, "disconnect %s", callId);
+ Log.i(this, "disconnect %s", callId);
if (mConnectionById.containsKey(callId)) {
findConnectionForAction(callId, "disconnect").onDisconnect();
} else {
@@ -2175,7 +2175,7 @@ public abstract class ConnectionService extends Service {
}
private void hold(String callId) {
- Log.d(this, "hold %s", callId);
+ Log.i(this, "hold %s", callId);
if (mConnectionById.containsKey(callId)) {
findConnectionForAction(callId, "hold").onHold();
} else {
@@ -2184,7 +2184,7 @@ public abstract class ConnectionService extends Service {
}
private void unhold(String callId) {
- Log.d(this, "unhold %s", callId);
+ Log.i(this, "unhold %s", callId);
if (mConnectionById.containsKey(callId)) {
findConnectionForAction(callId, "unhold").onUnhold();
} else {
@@ -2193,7 +2193,7 @@ public abstract class ConnectionService extends Service {
}
private void onCallAudioStateChanged(String callId, CallAudioState callAudioState) {
- Log.d(this, "onAudioStateChanged %s %s", callId, callAudioState);
+ Log.i(this, "onAudioStateChanged %s %s", callId, callAudioState);
if (mConnectionById.containsKey(callId)) {
findConnectionForAction(callId, "onCallAudioStateChanged").setCallAudioState(
callAudioState);
@@ -2204,7 +2204,7 @@ public abstract class ConnectionService extends Service {
}
private void playDtmfTone(String callId, char digit) {
- Log.d(this, "playDtmfTone %s %c", callId, digit);
+ Log.i(this, "playDtmfTone %s %c", callId, digit);
if (mConnectionById.containsKey(callId)) {
findConnectionForAction(callId, "playDtmfTone").onPlayDtmfTone(digit);
} else {
@@ -2213,7 +2213,7 @@ public abstract class ConnectionService extends Service {
}
private void stopDtmfTone(String callId) {
- Log.d(this, "stopDtmfTone %s", callId);
+ Log.i(this, "stopDtmfTone %s", callId);
if (mConnectionById.containsKey(callId)) {
findConnectionForAction(callId, "stopDtmfTone").onStopDtmfTone();
} else {
@@ -2222,7 +2222,7 @@ public abstract class ConnectionService extends Service {
}
private void conference(String callId1, String callId2) {
- Log.d(this, "conference %s, %s", callId1, callId2);
+ Log.i(this, "conference %s, %s", callId1, callId2);
// Attempt to get second connection or conference.
Connection connection2 = findConnectionForAction(callId2, "conference");
@@ -2269,7 +2269,7 @@ public abstract class ConnectionService extends Service {
}
private void splitFromConference(String callId) {
- Log.d(this, "splitFromConference(%s)", callId);
+ Log.i(this, "splitFromConference(%s)", callId);
Connection connection = findConnectionForAction(callId, "splitFromConference");
if (connection == getNullConnection()) {
@@ -2284,7 +2284,7 @@ public abstract class ConnectionService extends Service {
}
private void mergeConference(String callId) {
- Log.d(this, "mergeConference(%s)", callId);
+ Log.i(this, "mergeConference(%s)", callId);
Conference conference = findConferenceForAction(callId, "mergeConference");
if (conference != null) {
conference.onMerge();
@@ -2292,7 +2292,7 @@ public abstract class ConnectionService extends Service {
}
private void swapConference(String callId) {
- Log.d(this, "swapConference(%s)", callId);
+ Log.i(this, "swapConference(%s)", callId);
Conference conference = findConferenceForAction(callId, "swapConference");
if (conference != null) {
conference.onSwap();
@@ -2300,7 +2300,7 @@ public abstract class ConnectionService extends Service {
}
private void addConferenceParticipants(String callId, List<Uri> participants) {
- Log.d(this, "addConferenceParticipants(%s)", callId);
+ Log.i(this, "addConferenceParticipants(%s)", callId);
if (mConnectionById.containsKey(callId)) {
findConnectionForAction(callId, "addConferenceParticipants")
.onAddConferenceParticipants(participants);
@@ -2318,7 +2318,7 @@ public abstract class ConnectionService extends Service {
* @param callId The ID of the call to pull.
*/
private void pullExternalCall(String callId) {
- Log.d(this, "pullExternalCall(%s)", callId);
+ Log.i(this, "pullExternalCall(%s)", callId);
Connection connection = findConnectionForAction(callId, "pullExternalCall");
if (connection != null) {
connection.onPullExternalCall();
@@ -2335,7 +2335,7 @@ public abstract class ConnectionService extends Service {
* @param extras Extras associated with the event.
*/
private void sendCallEvent(String callId, String event, Bundle extras) {
- Log.d(this, "sendCallEvent(%s, %s)", callId, event);
+ Log.i(this, "sendCallEvent(%s, %s)", callId, event);
Connection connection = findConnectionForAction(callId, "sendCallEvent");
if (connection != null) {
connection.onCallEvent(event, extras);
@@ -2348,7 +2348,7 @@ public abstract class ConnectionService extends Service {
* @param callId The ID of the call which completed handover.
*/
private void notifyHandoverComplete(String callId) {
- Log.d(this, "notifyHandoverComplete(%s)", callId);
+ Log.i(this, "notifyHandoverComplete(%s)", callId);
Connection connection = findConnectionForAction(callId, "notifyHandoverComplete");
if (connection != null) {
connection.onHandoverComplete();
@@ -2368,7 +2368,7 @@ public abstract class ConnectionService extends Service {
* @param extras The new extras bundle.
*/
private void handleExtrasChanged(String callId, Bundle extras) {
- Log.d(this, "handleExtrasChanged(%s, %s)", callId, extras);
+ Log.i(this, "handleExtrasChanged(%s, %s)", callId, extras);
if (mConnectionById.containsKey(callId)) {
findConnectionForAction(callId, "handleExtrasChanged").handleExtrasChanged(extras);
} else if (mConferenceById.containsKey(callId)) {
@@ -2377,7 +2377,7 @@ public abstract class ConnectionService extends Service {
}
private void startRtt(String callId, Connection.RttTextStream rttTextStream) {
- Log.d(this, "startRtt(%s)", callId);
+ Log.i(this, "startRtt(%s)", callId);
if (mConnectionById.containsKey(callId)) {
findConnectionForAction(callId, "startRtt").onStartRtt(rttTextStream);
} else if (mConferenceById.containsKey(callId)) {
@@ -2386,7 +2386,7 @@ public abstract class ConnectionService extends Service {
}
private void stopRtt(String callId) {
- Log.d(this, "stopRtt(%s)", callId);
+ Log.i(this, "stopRtt(%s)", callId);
if (mConnectionById.containsKey(callId)) {
findConnectionForAction(callId, "stopRtt").onStopRtt();
} else if (mConferenceById.containsKey(callId)) {
@@ -2395,7 +2395,7 @@ public abstract class ConnectionService extends Service {
}
private void handleRttUpgradeResponse(String callId, Connection.RttTextStream rttTextStream) {
- Log.d(this, "handleRttUpgradeResponse(%s, %s)", callId, rttTextStream == null);
+ Log.i(this, "handleRttUpgradeResponse(%s, %s)", callId, rttTextStream == null);
if (mConnectionById.containsKey(callId)) {
findConnectionForAction(callId, "handleRttUpgradeResponse")
.handleRttUpgradeResponse(rttTextStream);
@@ -2405,7 +2405,7 @@ public abstract class ConnectionService extends Service {
}
private void onPostDialContinue(String callId, boolean proceed) {
- Log.d(this, "onPostDialContinue(%s)", callId);
+ Log.i(this, "onPostDialContinue(%s)", callId);
findConnectionForAction(callId, "stopDtmfTone").onPostDialContinue(proceed);
}
diff --git a/telecomm/java/android/telecom/Log.java b/telecomm/java/android/telecom/Log.java
index 4f6a9d6450f8..a90d0532b721 100644
--- a/telecomm/java/android/telecom/Log.java
+++ b/telecomm/java/android/telecom/Log.java
@@ -16,7 +16,9 @@
package android.telecom;
+import android.annotation.NonNull;
import android.compat.annotation.UnsupportedAppUsage;
+import android.content.ComponentName;
import android.content.Context;
import android.net.Uri;
import android.os.Build;
@@ -29,8 +31,10 @@ import android.text.TextUtils;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.IndentingPrintWriter;
+import java.util.Arrays;
import java.util.IllegalFormatException;
import java.util.Locale;
+import java.util.stream.Collectors;
/**
* Manages logging for the entire module.
@@ -212,6 +216,16 @@ public class Log {
return getSessionManager().getExternalSession();
}
+ /**
+ * Retrieves external session information, providing a context for the recipient of the session
+ * info where the external session came from.
+ * @param ownerInfo The external owner info.
+ * @return New {@link Session.Info} instance with owner info set.
+ */
+ public static Session.Info getExternalSession(@NonNull String ownerInfo) {
+ return getSessionManager().getExternalSession(ownerInfo);
+ }
+
public static void cancelSubsession(Session subsession) {
getSessionManager().cancelSubsession(subsession);
}
@@ -481,4 +495,34 @@ public class Log {
}
return String.format(Locale.US, "%s: %s%s", prefix, msg, sessionPostfix);
}
+
+ /**
+ * Generates an abbreviated version of the package name from a component.
+ * E.g. com.android.phone becomes cap
+ * @param componentName The component name to abbreviate.
+ * @return Abbreviation of empty string if component is null.
+ * @hide
+ */
+ public static String getPackageAbbreviation(ComponentName componentName) {
+ if (componentName == null) {
+ return "";
+ }
+ return getPackageAbbreviation(componentName.getPackageName());
+ }
+
+ /**
+ * Generates an abbreviated version of the package name.
+ * E.g. com.android.phone becomes cap
+ * @param packageName The packageName name to abbreviate.
+ * @return Abbreviation of empty string if package is null.
+ * @hide
+ */
+ public static String getPackageAbbreviation(String packageName) {
+ if (packageName == null) {
+ return "";
+ }
+ return Arrays.stream(packageName.split("\\."))
+ .map(s -> s.substring(0,1))
+ .collect(Collectors.joining(""));
+ }
}
diff --git a/telecomm/java/android/telecom/Logging/Session.java b/telecomm/java/android/telecom/Logging/Session.java
index 8d3f4e1df8bc..4aa3614fa004 100644
--- a/telecomm/java/android/telecom/Logging/Session.java
+++ b/telecomm/java/android/telecom/Logging/Session.java
@@ -17,6 +17,7 @@
package android.telecom.Logging;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
import android.telecom.Log;
@@ -59,10 +60,12 @@ public class Session {
public static class Info implements Parcelable {
public final String sessionId;
public final String methodPath;
+ public final String ownerInfo;
- private Info(String id, String path) {
+ private Info(String id, String path, String owner) {
sessionId = id;
methodPath = path;
+ ownerInfo = owner;
}
public static Info getInfo (Session s) {
@@ -70,7 +73,28 @@ public class Session {
// not get multiple stacking external sessions (unless we have DEBUG level logging or
// lower).
return new Info(s.getFullSessionId(), s.getFullMethodPath(
- !Log.DEBUG && s.isSessionExternal()));
+ !Log.DEBUG && s.isSessionExternal()), s.getOwnerInfo());
+ }
+
+ public static Info getExternalInfo(Session s, @Nullable String ownerInfo) {
+ // When creating session information for an existing session, the caller may pass in a
+ // context to be passed along to the recipient of the external session info.
+ // So, for example, if telecom has an active session with owner 'cad', and Telecom is
+ // calling into Telephony and providing external session info, it would pass in 'cast'
+ // as the owner info. This would result in Telephony seeing owner info 'cad/cast',
+ // which would make it very clear in the Telephony logs the chain of package calls which
+ // ultimately resulted in the logs.
+ String newInfo = ownerInfo != null && s.getOwnerInfo() != null
+ // If we've got both, concatenate them.
+ ? s.getOwnerInfo() + "/" + ownerInfo
+ // Otherwise use whichever is present.
+ : ownerInfo != null ? ownerInfo : s.getOwnerInfo();
+
+ // Create Info based on the truncated method path if the session is external, so we do
+ // not get multiple stacking external sessions (unless we have DEBUG level logging or
+ // lower).
+ return new Info(s.getFullSessionId(), s.getFullMethodPath(
+ !Log.DEBUG && s.isSessionExternal()), newInfo);
}
/** Responsible for creating Info objects for deserialized Parcels. */
@@ -80,7 +104,8 @@ public class Session {
public Info createFromParcel(Parcel source) {
String id = source.readString();
String methodName = source.readString();
- return new Info(id, methodName);
+ String ownerInfo = source.readString();
+ return new Info(id, methodName, ownerInfo);
}
@Override
@@ -100,6 +125,7 @@ public class Session {
public void writeToParcel(Parcel destination, int flags) {
destination.writeString(sessionId);
destination.writeString(methodPath);
+ destination.writeString(ownerInfo);
}
}
@@ -206,6 +232,14 @@ public class Session {
return Info.getInfo(this);
}
+ public Info getExternalInfo(@Nullable String ownerInfo) {
+ return Info.getExternalInfo(this, ownerInfo);
+ }
+
+ public String getOwnerInfo() {
+ return mOwnerInfo;
+ }
+
@VisibleForTesting
public String getSessionId() {
return mSessionId;
diff --git a/telecomm/java/android/telecom/Logging/SessionManager.java b/telecomm/java/android/telecom/Logging/SessionManager.java
index ac300587cef8..67e5eabf54eb 100644
--- a/telecomm/java/android/telecom/Logging/SessionManager.java
+++ b/telecomm/java/android/telecom/Logging/SessionManager.java
@@ -16,6 +16,7 @@
package android.telecom.Logging;
+import android.annotation.Nullable;
import android.content.Context;
import android.os.Handler;
import android.os.Looper;
@@ -180,7 +181,7 @@ public class SessionManager {
Log.d(LOGGING_TAG, Session.START_EXTERNAL_SESSION);
Session externalSession = new Session(Session.EXTERNAL_INDICATOR + sessionInfo.sessionId,
sessionInfo.methodPath, System.currentTimeMillis(),
- false /*isStartedFromActiveSession*/, null);
+ false /*isStartedFromActiveSession*/, sessionInfo.ownerInfo);
externalSession.setIsExternal(true);
// Mark the external session as already completed, since we have no way of knowing when
// the external session actually has completed.
@@ -224,7 +225,7 @@ public class SessionManager {
// Start execution time of the session will be overwritten in continueSession(...).
Session newSubsession = new Session(threadSession.getNextChildId(),
threadSession.getShortMethodName(), System.currentTimeMillis(),
- isStartedFromActiveSession, null);
+ isStartedFromActiveSession, threadSession.getOwnerInfo());
threadSession.addChild(newSubsession);
newSubsession.setParentSession(threadSession);
@@ -238,12 +239,18 @@ public class SessionManager {
return newSubsession;
}
+ public synchronized Session.Info getExternalSession() {
+ return getExternalSession(null /* ownerInfo */);
+ }
+
/**
* Retrieve the information of the currently active Session. This information is parcelable and
* is used to create an external Session ({@link #startExternalSession(Session.Info, String)}).
* If there is no Session active, this method will return null.
+ * @param ownerInfo Owner information for the session.
+ * @return The session information
*/
- public synchronized Session.Info getExternalSession() {
+ public synchronized Session.Info getExternalSession(@Nullable String ownerInfo) {
int threadId = getCallingThreadId();
Session threadSession = mSessionMapper.get(threadId);
if (threadSession == null) {
@@ -251,8 +258,7 @@ public class SessionManager {
"active.");
return null;
}
-
- return threadSession.getInfo();
+ return threadSession.getExternalInfo(ownerInfo);
}
/**
diff --git a/telecomm/java/android/telecom/RemoteConnection.java b/telecomm/java/android/telecom/RemoteConnection.java
index 05480dc38a0d..f947d34e5baf 100644
--- a/telecomm/java/android/telecom/RemoteConnection.java
+++ b/telecomm/java/android/telecom/RemoteConnection.java
@@ -30,13 +30,16 @@ import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
+import android.telecom.Logging.Session;
import android.view.Surface;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.stream.Collectors;
/**
* A connection provided to a {@link ConnectionService} by another {@code ConnectionService}
@@ -655,6 +658,8 @@ public final class RemoteConnection {
private int mCallerDisplayNamePresentation;
private RemoteConference mConference;
private Bundle mExtras;
+ private String mCallingPackage;
+ private String mCallingPackageAbbreviation;
/**
* @hide
@@ -667,6 +672,13 @@ public final class RemoteConnection {
mConnectionService = connectionService;
mConnected = true;
mState = Connection.STATE_INITIALIZING;
+ if (request != null && request.getExtras() != null
+ && request.getExtras().containsKey(
+ Connection.EXTRA_REMOTE_CONNECTION_ORIGINATING_PACKAGE_NAME)) {
+ mCallingPackage = request.getExtras().getString(
+ Connection.EXTRA_REMOTE_CONNECTION_ORIGINATING_PACKAGE_NAME);
+ mCallingPackageAbbreviation = Log.getPackageAbbreviation(mCallingPackage);
+ }
}
/**
@@ -705,6 +717,8 @@ public final class RemoteConnection {
Bundle newExtras = new Bundle();
newExtras.putString(Connection.EXTRA_ORIGINAL_CONNECTION_ID, callId);
putExtras(newExtras);
+ mCallingPackage = callingPackage;
+ mCallingPackageAbbreviation = Log.getPackageAbbreviation(mCallingPackage);
}
/**
@@ -899,11 +913,14 @@ public final class RemoteConnection {
* Instructs this {@code RemoteConnection} to abort.
*/
public void abort() {
+ Log.startSession("RC.a", getActiveOwnerInfo());
try {
if (mConnected) {
- mConnectionService.abort(mConnectionId, null /*Session.Info*/);
+ mConnectionService.abort(mConnectionId, Log.getExternalSession());
}
} catch (RemoteException ignored) {
+ } finally {
+ Log.endSession();
}
}
@@ -911,11 +928,14 @@ public final class RemoteConnection {
* Instructs this {@link Connection#STATE_RINGING} {@code RemoteConnection} to answer.
*/
public void answer() {
+ Log.startSession("RC.an", getActiveOwnerInfo());
try {
if (mConnected) {
- mConnectionService.answer(mConnectionId, null /*Session.Info*/);
+ mConnectionService.answer(mConnectionId, Log.getExternalSession());
}
} catch (RemoteException ignored) {
+ } finally {
+ Log.endSession();
}
}
@@ -925,11 +945,14 @@ public final class RemoteConnection {
* @hide
*/
public void answer(int videoState) {
+ Log.startSession("RC.an2", getActiveOwnerInfo());
try {
if (mConnected) {
mConnectionService.answerVideo(mConnectionId, videoState, null /*Session.Info*/);
}
} catch (RemoteException ignored) {
+ } finally {
+ Log.endSession();
}
}
@@ -937,11 +960,14 @@ public final class RemoteConnection {
* Instructs this {@link Connection#STATE_RINGING} {@code RemoteConnection} to reject.
*/
public void reject() {
+ Log.startSession("RC.r", getActiveOwnerInfo());
try {
if (mConnected) {
mConnectionService.reject(mConnectionId, null /*Session.Info*/);
}
} catch (RemoteException ignored) {
+ } finally {
+ Log.endSession();
}
}
@@ -949,11 +975,14 @@ public final class RemoteConnection {
* Instructs this {@code RemoteConnection} to go on hold.
*/
public void hold() {
+ Log.startSession("RC.h", getActiveOwnerInfo());
try {
if (mConnected) {
mConnectionService.hold(mConnectionId, null /*Session.Info*/);
}
} catch (RemoteException ignored) {
+ } finally {
+ Log.endSession();
}
}
@@ -961,11 +990,14 @@ public final class RemoteConnection {
* Instructs this {@link Connection#STATE_HOLDING} call to release from hold.
*/
public void unhold() {
+ Log.startSession("RC.u", getActiveOwnerInfo());
try {
if (mConnected) {
mConnectionService.unhold(mConnectionId, null /*Session.Info*/);
}
} catch (RemoteException ignored) {
+ } finally {
+ Log.endSession();
}
}
@@ -973,11 +1005,15 @@ public final class RemoteConnection {
* Instructs this {@code RemoteConnection} to disconnect.
*/
public void disconnect() {
+ Log.startSession("RC.d", getActiveOwnerInfo());
try {
if (mConnected) {
- mConnectionService.disconnect(mConnectionId, null /*Session.Info*/);
+ mConnectionService.disconnect(mConnectionId, Log.getExternalSession(
+ mCallingPackageAbbreviation));
}
} catch (RemoteException ignored) {
+ } finally {
+ Log.endSession();
}
}
@@ -991,11 +1027,14 @@ public final class RemoteConnection {
* value must be one of {@code '0'} through {@code '9'}, {@code '*'} or {@code '#'}.
*/
public void playDtmfTone(char digit) {
+ Log.startSession("RC.pDT", getActiveOwnerInfo());
try {
if (mConnected) {
mConnectionService.playDtmfTone(mConnectionId, digit, null /*Session.Info*/);
}
} catch (RemoteException ignored) {
+ } finally {
+ Log.endSession();
}
}
@@ -1007,11 +1046,14 @@ public final class RemoteConnection {
* currently playing, this method will do nothing.
*/
public void stopDtmfTone() {
+ Log.startSession("RC.sDT", getActiveOwnerInfo());
try {
if (mConnected) {
mConnectionService.stopDtmfTone(mConnectionId, null /*Session.Info*/);
}
} catch (RemoteException ignored) {
+ } finally {
+ Log.endSession();
}
}
@@ -1037,12 +1079,16 @@ public final class RemoteConnection {
* @param proceed Whether or not to continue with the post-dial sequence.
*/
public void postDialContinue(boolean proceed) {
+ Log.startSession("RC.pDC", getActiveOwnerInfo());
try {
if (mConnected) {
mConnectionService.onPostDialContinue(mConnectionId, proceed,
null /*Session.Info*/);
}
} catch (RemoteException ignored) {
+ // bliss
+ } finally {
+ Log.endSession();
}
}
@@ -1052,11 +1098,14 @@ public final class RemoteConnection {
* See {@link Call#pullExternalCall()} for more information.
*/
public void pullExternalCall() {
+ Log.startSession("RC.pEC", getActiveOwnerInfo());
try {
if (mConnected) {
mConnectionService.pullExternalCall(mConnectionId, null /*Session.Info*/);
}
} catch (RemoteException ignored) {
+ } finally {
+ Log.endSession();
}
}
@@ -1079,12 +1128,15 @@ public final class RemoteConnection {
* @param state The audio state of this {@code RemoteConnection}.
*/
public void setCallAudioState(CallAudioState state) {
+ Log.startSession("RC.sCAS", getActiveOwnerInfo());
try {
if (mConnected) {
mConnectionService.onCallAudioStateChanged(mConnectionId, state,
null /*Session.Info*/);
}
} catch (RemoteException ignored) {
+ } finally {
+ Log.endSession();
}
}
@@ -1095,12 +1147,15 @@ public final class RemoteConnection {
* @hide
*/
public void startRtt(@NonNull Connection.RttTextStream rttTextStream) {
+ Log.startSession("RC.sR", getActiveOwnerInfo());
try {
if (mConnected) {
mConnectionService.startRtt(mConnectionId, rttTextStream.getFdFromInCall(),
rttTextStream.getFdToInCall(), null /*Session.Info*/);
}
} catch (RemoteException ignored) {
+ } finally {
+ Log.endSession();
}
}
@@ -1110,11 +1165,14 @@ public final class RemoteConnection {
* @hide
*/
public void stopRtt() {
+ Log.startSession("RC.stR", getActiveOwnerInfo());
try {
if (mConnected) {
mConnectionService.stopRtt(mConnectionId, null /*Session.Info*/);
}
} catch (RemoteException ignored) {
+ } finally {
+ Log.endSession();
}
}
@@ -1128,6 +1186,7 @@ public final class RemoteConnection {
* the in-call app.
*/
public void sendRttUpgradeResponse(@Nullable Connection.RttTextStream rttTextStream) {
+ Log.startSession("RC.sRUR", getActiveOwnerInfo());
try {
if (mConnected) {
if (rttTextStream == null) {
@@ -1140,6 +1199,8 @@ public final class RemoteConnection {
}
}
} catch (RemoteException ignored) {
+ } finally {
+ Log.endSession();
}
}
@@ -1164,6 +1225,22 @@ public final class RemoteConnection {
return mConference;
}
+ /**
+ * Get the owner info for the currently active session. We want to make sure that any owner
+ * info from the original call into the connection manager gets retained so that the full
+ * context of the calls can be traced down to Telephony.
+ * Example: Telecom will provide owner info in it's external session info that indicates
+ * 'cast' as the calling owner.
+ * @return The active owner
+ */
+ private String getActiveOwnerInfo() {
+ Session.Info info = Log.getExternalSession();
+ if (info == null) {
+ return null;
+ }
+ return info.ownerInfo;
+ }
+
/** {@hide} */
String getId() {
return mConnectionId;
diff --git a/telephony/common/com/android/internal/telephony/CellBroadcastUtils.java b/telephony/common/com/android/internal/telephony/CellBroadcastUtils.java
new file mode 100644
index 000000000000..6c6375586225
--- /dev/null
+++ b/telephony/common/com/android/internal/telephony/CellBroadcastUtils.java
@@ -0,0 +1,65 @@
+/*
+ * 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.telephony;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.provider.Telephony;
+import android.text.TextUtils;
+import android.util.Log;
+
+/**
+ * This class provides utility functions related to CellBroadcast.
+ */
+public class CellBroadcastUtils {
+ private static final String TAG = "CellBroadcastUtils";
+ private static final boolean VDBG = false;
+
+ /**
+ * Utility method to query the default CBR's package name.
+ */
+ public static String getDefaultCellBroadcastReceiverPackageName(Context context) {
+ PackageManager packageManager = context.getPackageManager();
+ ResolveInfo resolveInfo = packageManager.resolveActivity(
+ new Intent(Telephony.Sms.Intents.SMS_CB_RECEIVED_ACTION),
+ PackageManager.MATCH_SYSTEM_ONLY);
+ String packageName;
+
+ if (resolveInfo == null) {
+ Log.e(TAG, "getDefaultCellBroadcastReceiverPackageName: no package found");
+ return null;
+ }
+
+ packageName = resolveInfo.activityInfo.applicationInfo.packageName;
+
+ if (VDBG) {
+ Log.d(TAG, "getDefaultCellBroadcastReceiverPackageName: found package: " + packageName);
+ }
+
+ if (TextUtils.isEmpty(packageName) || packageManager.checkPermission(
+ android.Manifest.permission.READ_CELL_BROADCASTS, packageName)
+ == PackageManager.PERMISSION_DENIED) {
+ Log.e(TAG, "getDefaultCellBroadcastReceiverPackageName: returning null; "
+ + "permission check failed for : " + packageName);
+ return null;
+ }
+
+ return packageName;
+ }
+}
diff --git a/wifi/Android.bp b/wifi/Android.bp
index 6a8600a5baa4..1e2c81a60178 100644
--- a/wifi/Android.bp
+++ b/wifi/Android.bp
@@ -143,6 +143,16 @@ droidstubs {
"framework-module-stubs-defaults-publicapi",
"framework-wifi-stubs-srcs-defaults",
],
+ check_api: {
+ last_released: {
+ api_file: ":framework-wifi.api.public.latest",
+ removed_api_file: ":framework-wifi-removed.api.public.latest",
+ },
+ api_lint: {
+ new_since: ":framework-wifi.api.public.latest",
+ baseline_file: "api/lint-baseline.txt",
+ },
+ },
}
droidstubs {
@@ -151,6 +161,16 @@ droidstubs {
"framework-module-stubs-defaults-systemapi",
"framework-wifi-stubs-srcs-defaults",
],
+ check_api: {
+ last_released: {
+ api_file: ":framework-wifi.api.system.latest",
+ removed_api_file: ":framework-wifi-removed.api.system.latest",
+ },
+ api_lint: {
+ new_since: ":framework-wifi.api.system.latest",
+ baseline_file: "api/system-lint-baseline.txt",
+ },
+ },
}
droidstubs {
@@ -159,6 +179,15 @@ droidstubs {
"framework-module-api-defaults-module_libs_api",
"framework-wifi-stubs-srcs-defaults",
],
+ check_api: {
+ last_released: {
+ api_file: ":framework-wifi.api.module-lib.latest",
+ removed_api_file: ":framework-wifi-removed.api.module-lib.latest",
+ },
+ api_lint: {
+ new_since: ":framework-wifi.api.module-lib.latest",
+ },
+ },
}
droidstubs {
diff --git a/wifi/api/lint-baseline.txt b/wifi/api/lint-baseline.txt
new file mode 100644
index 000000000000..892411f8c3a1
--- /dev/null
+++ b/wifi/api/lint-baseline.txt
@@ -0,0 +1,13 @@
+// Baseline format: 1.0
+GenericException: android.net.wifi.WifiManager.LocalOnlyHotspotReservation#finalize():
+ Methods must not throw generic exceptions (`java.lang.Throwable`)
+GenericException: android.net.wifi.WifiManager.MulticastLock#finalize():
+ Methods must not throw generic exceptions (`java.lang.Throwable`)
+GenericException: android.net.wifi.WifiManager.WifiLock#finalize():
+ Methods must not throw generic exceptions (`java.lang.Throwable`)
+
+
+VisiblySynchronized: PsiThisExpression:WifiManager.this:
+ Internal locks must not be exposed (synchronizing on this or class is still externally observable): method android.net.wifi.WifiManager.WifiLock.finalize()
+VisiblySynchronized: android.net.wifi.WifiManager.WifiLock#finalize():
+ Internal locks must not be exposed (synchronizing on this or class is still externally observable): method android.net.wifi.WifiManager.WifiLock.finalize()
diff --git a/wifi/api/system-lint-baseline.txt b/wifi/api/system-lint-baseline.txt
new file mode 100644
index 000000000000..6547ee8a2188
--- /dev/null
+++ b/wifi/api/system-lint-baseline.txt
@@ -0,0 +1,6 @@
+// Baseline format: 1.0
+MissingGetterMatchingBuilder: android.net.wifi.rtt.RangingRequest.Builder#addResponder(android.net.wifi.rtt.ResponderConfig):
+ android.net.wifi.rtt.RangingRequest does not declare a `getResponders()` method matching method android.net.wifi.rtt.RangingRequest.Builder.addResponder(android.net.wifi.rtt.ResponderConfig)
+
+MissingNullability: android.net.wifi.rtt.RangingRequest.Builder#addResponder(android.net.wifi.rtt.ResponderConfig):
+