summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.bp33
-rw-r--r--StubLibraries.bp2
-rw-r--r--apex/jobscheduler/framework/java/android/app/tare/EconomyManager.java18
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java2
-rw-r--r--apex/jobscheduler/service/java/com/android/server/tare/Agent.java456
-rw-r--r--apex/jobscheduler/service/java/com/android/server/tare/AlarmManagerEconomicPolicy.java31
-rw-r--r--apex/jobscheduler/service/java/com/android/server/tare/CompleteEconomicPolicy.java15
-rw-r--r--apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java31
-rw-r--r--apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java218
-rw-r--r--apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java31
-rw-r--r--apex/jobscheduler/service/java/com/android/server/tare/Ledger.java9
-rw-r--r--apex/jobscheduler/service/java/com/android/server/tare/PowerSaveModeModifier.java70
-rw-r--r--apex/jobscheduler/service/java/com/android/server/tare/Scribe.java72
-rw-r--r--apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java7
-rw-r--r--api/Android.bp3
-rw-r--r--cmds/idmap2/tests/IdmapTests.cpp1
-rw-r--r--cmds/idmap2/tests/ResourceMappingTests.cpp2
-rw-r--r--core/api/current.txt83
-rw-r--r--core/api/module-lib-current.txt1
-rw-r--r--core/api/system-current.txt113
-rw-r--r--core/api/test-current.txt5
-rw-r--r--core/java/android/app/BroadcastOptions.java2
-rw-r--r--core/java/android/app/GameState.java48
-rw-r--r--core/java/android/app/IActivityManager.aidl1
-rw-r--r--core/java/android/app/KeyguardManager.java84
-rw-r--r--core/java/android/app/LoadedApk.java10
-rw-r--r--core/java/android/app/admin/DevicePolicyManager.java106
-rw-r--r--core/java/android/app/admin/DevicePolicyResources.java2
-rw-r--r--core/java/android/app/admin/IDevicePolicyManager.aidl2
-rw-r--r--core/java/android/app/admin/ProvisioningException.java2
-rw-r--r--core/java/android/app/smartspace/SmartspaceTarget.java38
-rw-r--r--core/java/android/app/smartspace/uitemplatedata/BaseTemplateData.java739
-rw-r--r--core/java/android/app/smartspace/uitemplatedata/CarouselTemplateData.java40
-rw-r--r--core/java/android/app/smartspace/uitemplatedata/CombinedCardsTemplateData.java41
-rw-r--r--core/java/android/app/smartspace/uitemplatedata/HeadToHeadTemplateData.java40
-rw-r--r--core/java/android/app/smartspace/uitemplatedata/SubCardTemplateData.java40
-rw-r--r--core/java/android/app/smartspace/uitemplatedata/SubImageTemplateData.java40
-rw-r--r--core/java/android/app/smartspace/uitemplatedata/SubListTemplateData.java40
-rw-r--r--core/java/android/app/smartspace/uitemplatedata/Text.java9
-rw-r--r--core/java/android/companion/virtual/VirtualDeviceManager.java8
-rw-r--r--core/java/android/hardware/CameraStreamStats.java10
-rw-r--r--core/java/android/hardware/SyncFence.java27
-rw-r--r--core/java/android/hardware/camera2/CameraCharacteristics.java8
-rw-r--r--core/java/android/hardware/camera2/CameraDevice.java4
-rw-r--r--core/java/android/hardware/camera2/impl/CameraMetadataNative.java2
-rw-r--r--core/java/android/hardware/camera2/params/DynamicRangeProfiles.java118
-rw-r--r--core/java/android/hardware/camera2/params/MandatoryStreamCombination.java2
-rw-r--r--core/java/android/hardware/camera2/params/OutputConfiguration.java10
-rw-r--r--core/java/android/hardware/display/DisplayManager.java2
-rw-r--r--core/java/android/hardware/display/DisplayManagerGlobal.java8
-rw-r--r--core/java/android/hardware/input/InputManager.java28
-rw-r--r--core/java/android/hardware/soundtrigger/SoundTriggerModule.java32
-rw-r--r--core/java/android/nfc/cardemulation/CardEmulation.java7
-rw-r--r--core/java/android/os/BadTypeParcelableException.java (renamed from packages/SystemUI/src/com/android/systemui/chooser/ChooserActivity.java)29
-rw-r--r--core/java/android/os/BaseBundle.java147
-rw-r--r--core/java/android/os/Bundle.java152
-rw-r--r--core/java/android/os/Parcel.java205
-rw-r--r--core/java/android/os/PowerManager.java16
-rw-r--r--core/java/android/os/storage/StorageManager.java12
-rw-r--r--core/java/android/provider/Settings.java10
-rw-r--r--core/java/android/service/dreams/DreamOverlayService.java20
-rw-r--r--core/java/android/service/dreams/DreamService.java61
-rw-r--r--core/java/android/service/selectiontoolbar/FloatingToolbarRoot.java4
-rw-r--r--core/java/android/util/DumpableContainer.java3
-rw-r--r--core/java/android/view/IWindowManager.aidl4
-rw-r--r--core/java/android/view/ImeFocusController.java10
-rw-r--r--core/java/android/view/InputDevice.java50
-rw-r--r--core/java/android/view/SurfaceControl.java4
-rw-r--r--core/java/android/view/SurfaceControlViewHost.java3
-rw-r--r--core/java/android/view/View.java2
-rw-r--r--core/java/android/view/ViewRootImpl.java16
-rw-r--r--core/java/android/view/WindowManager.java20
-rw-r--r--core/java/android/view/inputmethod/InputMethodManager.java24
-rw-r--r--core/java/android/widget/TextView.java8
-rw-r--r--core/java/com/android/internal/app/MediaRouteChooserDialog.java49
-rw-r--r--core/java/com/android/internal/app/MediaRouteDialogPresenter.java25
-rw-r--r--core/java/com/android/internal/app/ResolverActivity.java36
-rw-r--r--core/java/com/android/internal/app/chooser/DisplayResolveInfo.java16
-rw-r--r--core/java/com/android/internal/policy/IKeyguardLockedStateListener.aidl21
-rw-r--r--core/java/com/android/internal/util/ArrayUtils.java9
-rw-r--r--core/proto/android/server/windowmanagerservice.proto1
-rw-r--r--core/res/AndroidManifest.xml11
-rw-r--r--core/res/res/layout/media_route_chooser_dialog.xml27
-rw-r--r--core/res/res/values-television/config.xml13
-rw-r--r--core/res/res/values/attrs.xml60
-rw-r--r--core/res/res/values/config.xml20
-rw-r--r--core/res/res/values/dimens.xml13
-rw-r--r--core/res/res/values/symbols.xml10
-rw-r--r--core/res/res/values/vendor_required_attestation_certificates.xml32
-rw-r--r--core/tests/coretests/src/android/content/ManagedUserContentResolverTest.java2
-rw-r--r--core/tests/coretests/src/android/content/SecondaryUserContentResolverTest.java2
-rw-r--r--graphics/java/android/graphics/ColorSpace.java56
-rw-r--r--libs/WindowManager/Shell/res/values-television/config.xml36
-rw-r--r--libs/WindowManager/Shell/res/values/config.xml26
-rw-r--r--libs/WindowManager/Shell/res/values/dimen.xml17
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java34
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java73
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/DismissView.kt58
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java40
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java15
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java138
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java70
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java17
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java26
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java3
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestSupportPairNonResizeableApps.kt15
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/ImeAppHelper.kt4
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt22
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt13
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipTransition.kt11
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaExpandButtonClickTest.kt2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt4
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithDismissButtonTest.kt14
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithSwipeDownTest.kt12
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt13
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt25
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTestShellTransit.kt4
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java15
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java2
-rw-r--r--media/java/android/media/ImageWriter.java38
-rw-r--r--packages/CompanionDeviceManager/Android.bp1
-rw-r--r--packages/CompanionDeviceManager/AndroidManifest.xml1
-rw-r--r--packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java28
-rw-r--r--packages/CompanionDeviceManager/src/com/android/companiondevicemanager/Utils.java13
-rw-r--r--packages/ConnectivityT/framework-t/src/android/net/NetworkStats.java3
-rw-r--r--packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java8
-rw-r--r--packages/DynamicSystemInstallationService/src/com/android/dynsystem/SparseInputStream.java2
-rw-r--r--packages/SettingsLib/res/values/strings.xml2
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/Utils.java12
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/devicestate/AndroidSecureSettings.java54
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManager.java115
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/devicestate/SecureSettings.java30
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/users/AvatarPickerActivity.java1
-rw-r--r--packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManagerTest.java120
-rw-r--r--packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/FakeSecureSettings.java60
-rw-r--r--packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/OWNERS3
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java49
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/enterprise/BiometricActionDisabledByAdminControllerTest.java4
-rw-r--r--packages/Shell/AndroidManifest.xml6
-rw-r--r--packages/SystemUI/AndroidManifest.xml19
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt3
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt6
-rw-r--r--packages/SystemUI/plugin/src/com/android/systemui/plugins/VolumeDialogController.java1
-rw-r--r--packages/SystemUI/res-keyguard/layout/fgs_footer.xml2
-rw-r--r--packages/SystemUI/res-keyguard/values/dimens.xml1
-rw-r--r--packages/SystemUI/res/color/caption_tint_color_selector.xml3
-rw-r--r--packages/SystemUI/res/drawable/qs_dialog_btn_filled_large.xml32
-rw-r--r--packages/SystemUI/res/drawable/qs_footer_action_circle.xml8
-rw-r--r--packages/SystemUI/res/drawable/qs_footer_action_circle_color.xml8
-rw-r--r--packages/SystemUI/res/layout-land-television/volume_dialog.xml3
-rw-r--r--packages/SystemUI/res/layout-land/volume_dialog.xml3
-rw-r--r--packages/SystemUI/res/layout/alert_dialog_button_bar_systemui.xml4
-rw-r--r--packages/SystemUI/res/layout/alert_dialog_systemui.xml10
-rw-r--r--packages/SystemUI/res/layout/dream_overlay_complication_preview.xml29
-rw-r--r--packages/SystemUI/res/layout/dream_overlay_container.xml31
-rw-r--r--packages/SystemUI/res/layout/dream_overlay_status_bar_view.xml96
-rw-r--r--packages/SystemUI/res/layout/user_switcher_fullscreen.xml7
-rw-r--r--packages/SystemUI/res/layout/user_switcher_fullscreen_item.xml4
-rw-r--r--packages/SystemUI/res/layout/volume_dialog.xml3
-rw-r--r--packages/SystemUI/res/values-sw600dp-land/dimens.xml4
-rw-r--r--packages/SystemUI/res/values/attrs.xml8
-rw-r--r--packages/SystemUI/res/values/colors.xml2
-rw-r--r--packages/SystemUI/res/values/dimens.xml8
-rw-r--r--packages/SystemUI/res/values/strings.xml13
-rw-r--r--packages/SystemUI/res/values/styles.xml12
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/flags/Flag.kt11
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/flags/FlagManager.kt6
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/Dependency.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/DisplayCutoutBaseView.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/ScreenDecorHwcLayer.kt184
-rw-r--r--packages/SystemUI/src/com/android/systemui/ScreenDecorations.java18
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java115
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/chooser/ChooserHelper.java55
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java45
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayDotImageView.java128
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarView.java20
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java67
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/SmartSpaceComplication.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutEngine.java24
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/complication/DreamPreviewComplication.java130
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamPreviewComplicationComponent.java117
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java50
-rw-r--r--packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebug.java50
-rw-r--r--packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsRelease.java19
-rw-r--r--packages/SystemUI/src/com/android/systemui/flags/SystemPropertiesHelper.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java37
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt16
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupDialog.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/FooterActionsController.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSFragment.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSPanel.java26
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java68
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java45
-rw-r--r--packages/SystemUI/src/com/android/systemui/touch/TouchInsetManager.java181
-rw-r--r--packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt46
-rw-r--r--packages/SystemUI/src/com/android/systemui/user/UserSwitcherActivity.kt51
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/CaptionsToggleImageButton.java21
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java26
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/ScreenDecorHwcLayerTest.kt182
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java124
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/chooser/ChooserHelperTest.java58
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java26
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java32
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java34
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java54
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationLayoutEngineTest.java22
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsDebugTest.kt26
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsReleaseTest.kt16
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/flags/FlagManagerTest.kt22
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java10
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt104
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java10
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputGroupDialogTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/FooterActionsControllerTest.kt27
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt28
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImplTest.java32
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/FakeConfigurationController.kt31
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/touch/TouchInsetManagerTest.java203
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/user/UserSwitcherActivityTest.kt77
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java67
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java68
-rw-r--r--proto/src/camera.proto2
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java14
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java36
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java8
-rw-r--r--services/backup/backuplib/java/com/android/server/backup/transport/BackupTransportClient.java108
-rw-r--r--services/backup/backuplib/java/com/android/server/backup/transport/TransportConnection.java6
-rw-r--r--services/backup/backuplib/java/com/android/server/backup/transport/TransportStatusCallback.java6
-rw-r--r--services/companion/java/com/android/server/companion/AssociationStoreImpl.java14
-rw-r--r--services/companion/java/com/android/server/companion/CompanionApplicationController.java13
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java61
-rw-r--r--services/core/java/com/android/server/app/GameManagerService.java57
-rw-r--r--services/core/java/com/android/server/audio/BtHelper.java2
-rw-r--r--services/core/java/com/android/server/clipboard/ClipboardService.java2
-rw-r--r--services/core/java/com/android/server/connectivity/Vpn.java3
-rw-r--r--services/core/java/com/android/server/display/LocalDisplayAdapter.java73
-rw-r--r--services/core/java/com/android/server/dreams/DreamController.java22
-rw-r--r--services/core/java/com/android/server/dreams/DreamManagerService.java20
-rw-r--r--services/core/java/com/android/server/inputmethod/ImePlatformCompatUtils.java64
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodBindingController.java5
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodManagerService.java26
-rw-r--r--services/core/java/com/android/server/integrity/parser/RuleBinaryParser.java1
-rw-r--r--services/core/java/com/android/server/locales/LocaleManagerBackupHelper.java112
-rw-r--r--services/core/java/com/android/server/locales/LocaleManagerService.java35
-rw-r--r--services/core/java/com/android/server/locales/LocaleManagerServicePackageMonitor.java49
-rw-r--r--services/core/java/com/android/server/location/LocationManagerService.java1
-rw-r--r--services/core/java/com/android/server/location/contexthub/ContextHubServiceUtil.java13
-rw-r--r--services/core/java/com/android/server/location/provider/LocationProviderManager.java34
-rw-r--r--services/core/java/com/android/server/location/settings/LocationSettings.java31
-rw-r--r--services/core/java/com/android/server/media/MediaSessionRecord.java17
-rw-r--r--services/core/java/com/android/server/net/LockdownVpnTracker.java2
-rw-r--r--services/core/java/com/android/server/net/NetworkPolicyManagerService.java19
-rwxr-xr-xservices/core/java/com/android/server/notification/NotificationManagerService.java6
-rw-r--r--services/core/java/com/android/server/notification/NotificationRecord.java2
-rw-r--r--services/core/java/com/android/server/pm/InstallPackageHelper.java28
-rw-r--r--services/core/java/com/android/server/pm/pkg/parsing/ParsingPackage.java3
-rw-r--r--services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageImpl.java11
-rw-r--r--services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageRead.java7
-rw-r--r--services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java9
-rw-r--r--services/core/java/com/android/server/policy/PhoneWindowManager.java1
-rw-r--r--services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Compat.java24
-rw-r--r--services/core/java/com/android/server/vibrator/AbstractVibratorStep.java148
-rw-r--r--services/core/java/com/android/server/vibrator/CompleteEffectVibratorStep.java114
-rw-r--r--services/core/java/com/android/server/vibrator/ComposePrimitivesVibratorStep.java84
-rw-r--r--services/core/java/com/android/server/vibrator/ComposePwleVibratorStep.java84
-rw-r--r--services/core/java/com/android/server/vibrator/FinishSequentialEffectStep.java73
-rw-r--r--services/core/java/com/android/server/vibrator/PerformPrebakedVibratorStep.java103
-rw-r--r--services/core/java/com/android/server/vibrator/RampOffVibratorStep.java84
-rw-r--r--services/core/java/com/android/server/vibrator/SetAmplitudeVibratorStep.java186
-rw-r--r--services/core/java/com/android/server/vibrator/StartSequentialEffectStep.java370
-rw-r--r--services/core/java/com/android/server/vibrator/Step.java103
-rw-r--r--services/core/java/com/android/server/vibrator/TurnOffVibratorStep.java66
-rw-r--r--services/core/java/com/android/server/vibrator/VibrationStepConductor.java431
-rw-r--r--services/core/java/com/android/server/vibrator/VibrationThread.java1511
-rw-r--r--services/core/java/com/android/server/vibrator/VibratorManagerService.java45
-rw-r--r--services/core/java/com/android/server/wallpaper/WallpaperManagerService.java71
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java8
-rw-r--r--services/core/java/com/android/server/wm/ActivityStarter.java8
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java2
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerService.java8
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskSupervisor.java2
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java6
-rw-r--r--services/core/java/com/android/server/wm/LockTaskController.java4
-rw-r--r--services/core/java/com/android/server/wm/RootWindowContainer.java6
-rw-r--r--services/core/java/com/android/server/wm/Task.java42
-rw-r--r--services/core/java/com/android/server/wm/TaskSnapshotController.java4
-rw-r--r--services/core/java/com/android/server/wm/Transition.java18
-rw-r--r--services/core/java/com/android/server/wm/TransitionController.java12
-rw-r--r--services/core/java/com/android/server/wm/WallpaperController.java7
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java51
-rw-r--r--services/core/java/com/android/server/wm/WindowOrientationListener.java4
-rw-r--r--services/core/java/com/android/server/wm/WindowToken.java7
-rw-r--r--services/core/jni/com_android_server_location_GnssLocationProvider.cpp1272
-rw-r--r--services/core/jni/gnss/Android.bp5
-rw-r--r--services/core/jni/gnss/Gnss.cpp759
-rw-r--r--services/core/jni/gnss/Gnss.h121
-rw-r--r--services/core/jni/gnss/GnssCallback.cpp413
-rw-r--r--services/core/jni/gnss/GnssCallback.h183
-rw-r--r--services/core/jni/gnss/GnssDebug.cpp6
-rw-r--r--services/core/jni/gnss/GnssDebug.h6
-rw-r--r--services/core/jni/gnss/GnssPsds.cpp73
-rw-r--r--services/core/jni/gnss/GnssPsds.h64
-rw-r--r--services/core/jni/gnss/GnssPsdsCallback.cpp59
-rw-r--r--services/core/jni/gnss/GnssPsdsCallback.h76
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java128
-rw-r--r--services/java/com/android/server/SystemServer.java8
-rw-r--r--services/midi/java/com/android/server/midi/MidiService.java1
-rw-r--r--services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt1
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java9
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java12
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/tare/AgentTest.java67
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/tare/ScribeTest.java76
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java24
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java42
-rw-r--r--services/tests/servicestests/src/com/android/server/backup/transport/BackupTransportClientTest.java265
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java70
-rw-r--r--services/tests/servicestests/src/com/android/server/integrity/parser/RuleBinaryParserTest.java38
-rw-r--r--services/tests/servicestests/src/com/android/server/locales/LocaleManagerBackupRestoreTest.java12
-rw-r--r--services/tests/servicestests/src/com/android/server/locales/LocaleManagerServiceTest.java7
-rw-r--r--services/tests/servicestests/src/com/android/server/locales/ShadowLocaleManagerBackupHelper.java6
-rw-r--r--services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundHw2CompatTest.java29
-rw-r--r--services/tests/servicestests/src/com/android/server/soundtrigger_middleware/TestUtil.java69
-rw-r--r--services/tests/servicestests/src/com/android/server/tare/AgentTrendCalculatorTest.java201
-rw-r--r--services/tests/servicestests/src/com/android/server/tare/LedgerTest.java22
-rw-r--r--services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java178
-rw-r--r--services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java28
-rw-r--r--services/tests/servicestests/src/com/android/server/wm/WindowOrientationListenerTest.java11
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java4
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java3
-rw-r--r--telephony/java/android/telephony/TelephonyManager.java7
-rw-r--r--telephony/java/android/telephony/UiccCardInfo.java15
-rw-r--r--telephony/java/android/telephony/UiccPortInfo.java4
-rw-r--r--telephony/java/android/telephony/UiccSlotInfo.java4
-rw-r--r--telephony/java/android/telephony/euicc/EuiccManager.java57
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt2
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt1
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/TwoActivitiesAppHelper.kt2
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt2
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/SwitchImeWindowsFromGestureNavTest.kt21
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt11
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt2
-rw-r--r--tests/InputMethodStressTest/AndroidTest.xml5
-rw-r--r--tools/bit/command.cpp5
375 files changed, 11538 insertions, 6179 deletions
diff --git a/Android.bp b/Android.bp
index f8059475f6f4..933d1aff842b 100644
--- a/Android.bp
+++ b/Android.bp
@@ -476,21 +476,28 @@ java_library {
}
// TODO(b/145644363): move this to under StubLibraries.bp or ApiDocs.bp
-metalava_framework_docs_args = "--manifest $(location core/res/AndroidManifest.xml) " +
- "--hide-package com.android.server " +
- "--hide-package android.audio.policy.configuration.V7_0 " +
- "--error UnhiddenSystemApi " +
- "--hide RequiresPermission " +
- "--hide CallbackInterface " +
- "--hide MissingPermission --hide BroadcastBehavior " +
- "--hide HiddenSuperclass --hide DeprecationMismatch --hide UnavailableSymbol " +
- "--hide SdkConstant --hide HiddenTypeParameter --hide Todo --hide Typo " +
- "--error NoSettingsProvider " +
- "--force-convert-to-warning-nullability-annotations +*:-android.*:+android.icu.*:-dalvik.* " +
+metalava_framework_docs_args = "" +
"--api-lint-ignore-prefix android.icu. " +
"--api-lint-ignore-prefix java. " +
"--api-lint-ignore-prefix junit. " +
- "--api-lint-ignore-prefix org. "
+ "--api-lint-ignore-prefix org. " +
+ "--error NoSettingsProvider " +
+ "--error UnhiddenSystemApi " +
+ "--force-convert-to-warning-nullability-annotations +*:-android.*:+android.icu.*:-dalvik.* " +
+ "--hide BroadcastBehavior " +
+ "--hide CallbackInterface " +
+ "--hide DeprecationMismatch " +
+ "--hide HiddenSuperclass " +
+ "--hide HiddenTypeParameter " +
+ "--hide MissingPermission " +
+ "--hide-package android.audio.policy.configuration.V7_0 " +
+ "--hide-package com.android.server " +
+ "--hide RequiresPermission " +
+ "--hide SdkConstant " +
+ "--hide Todo " +
+ "--hide Typo " +
+ "--hide UnavailableSymbol " +
+ "--manifest $(location core/res/AndroidManifest.xml) "
packages_to_document = [
"android",
@@ -584,7 +591,7 @@ stubs_defaults {
libs: [
"art.module.public.api",
"sdk_module-lib_current_framework-tethering",
- "sdk_module-lib_current_framework-connectivity-tiramisu",
+ "sdk_module-lib_current_framework-connectivity-t",
"sdk_public_current_framework-bluetooth",
// There are a few classes from modules used by the core that
// need to be resolved by metalava. We use a prebuilt stub of the
diff --git a/StubLibraries.bp b/StubLibraries.bp
index 726ab2a70a5e..94f4374594ec 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -258,7 +258,7 @@ java_library {
srcs: [":module-lib-api-stubs-docs-non-updatable"],
libs: [
"sdk_module-lib_current_framework-tethering",
- "sdk_module-lib_current_framework-connectivity-tiramisu",
+ "sdk_module-lib_current_framework-connectivity-t",
"sdk_public_current_framework-bluetooth",
// NOTE: The below can be removed once the prebuilt stub contains bluetooth.
"sdk_system_current_android",
diff --git a/apex/jobscheduler/framework/java/android/app/tare/EconomyManager.java b/apex/jobscheduler/framework/java/android/app/tare/EconomyManager.java
index 8c8d2bfe16e3..88082f7dfa4b 100644
--- a/apex/jobscheduler/framework/java/android/app/tare/EconomyManager.java
+++ b/apex/jobscheduler/framework/java/android/app/tare/EconomyManager.java
@@ -39,7 +39,9 @@ public class EconomyManager {
/** @hide */
public static final String KEY_AM_MAX_SATIATED_BALANCE = "am_max_satiated_balance";
/** @hide */
- public static final String KEY_AM_MAX_CIRCULATION = "am_max_circulation";
+ public static final String KEY_AM_INITIAL_CONSUMPTION_LIMIT = "am_initial_consumption_limit";
+ /** @hide */
+ public static final String KEY_AM_HARD_CONSUMPTION_LIMIT = "am_hard_consumption_limit";
// TODO: Add AlarmManager modifier keys
/** @hide */
public static final String KEY_AM_REWARD_TOP_ACTIVITY_INSTANT =
@@ -163,7 +165,9 @@ public class EconomyManager {
public static final String KEY_JS_MAX_SATIATED_BALANCE =
"js_max_satiated_balance";
/** @hide */
- public static final String KEY_JS_MAX_CIRCULATION = "js_max_circulation";
+ public static final String KEY_JS_INITIAL_CONSUMPTION_LIMIT = "js_initial_consumption_limit";
+ /** @hide */
+ public static final String KEY_JS_HARD_CONSUMPTION_LIMIT = "js_hard_consumption_limit";
// TODO: Add JobScheduler modifier keys
/** @hide */
public static final String KEY_JS_REWARD_TOP_ACTIVITY_INSTANT =
@@ -280,7 +284,9 @@ public class EconomyManager {
/** @hide */
public static final int DEFAULT_AM_MAX_SATIATED_BALANCE = 1440;
/** @hide */
- public static final int DEFAULT_AM_MAX_CIRCULATION = 52000;
+ public static final int DEFAULT_AM_INITIAL_CONSUMPTION_LIMIT = 28800;
+ /** @hide */
+ public static final int DEFAULT_AM_HARD_CONSUMPTION_LIMIT = 52000;
// TODO: add AlarmManager modifier default values
/** @hide */
public static final int DEFAULT_AM_REWARD_TOP_ACTIVITY_INSTANT = 0;
@@ -359,7 +365,7 @@ public class EconomyManager {
// Default values JobScheduler factors
// TODO: add time_since_usage variable to min satiated balance factors
/** @hide */
- public static final int DEFAULT_JS_MIN_SATIATED_BALANCE_EXEMPTED = 50000;
+ public static final int DEFAULT_JS_MIN_SATIATED_BALANCE_EXEMPTED = 20000;
/** @hide */
public static final int DEFAULT_JS_MIN_SATIATED_BALANCE_HEADLESS_SYSTEM_APP = 10000;
/** @hide */
@@ -367,7 +373,9 @@ public class EconomyManager {
/** @hide */
public static final int DEFAULT_JS_MAX_SATIATED_BALANCE = 60000;
/** @hide */
- public static final int DEFAULT_JS_MAX_CIRCULATION = 691200;
+ public static final int DEFAULT_JS_INITIAL_CONSUMPTION_LIMIT = 460_000;
+ /** @hide */
+ public static final int DEFAULT_JS_HARD_CONSUMPTION_LIMIT = 900_000;
// TODO: add JobScheduler modifier default values
/** @hide */
public static final int DEFAULT_JS_REWARD_TOP_ACTIVITY_INSTANT = 0;
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
index 4e73b026abce..b9362789c6c6 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -3007,7 +3007,7 @@ public class JobSchedulerService extends com.android.server.SystemService
}
} else if (BatteryManager.ACTION_DISCHARGING.equals(action)) {
if (DEBUG) {
- Slog.d(TAG, "Disconnected from power @ " + sElapsedRealtimeClock.millis());
+ Slog.d(TAG, "Battery discharging @ " + sElapsedRealtimeClock.millis());
}
if (mCharging) {
mCharging = false;
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/Agent.java b/apex/jobscheduler/service/java/com/android/server/tare/Agent.java
index a6a007f46b58..c0a81487fe62 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/Agent.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/Agent.java
@@ -55,9 +55,6 @@ import com.android.server.pm.UserManagerInternal;
import com.android.server.usage.AppStandbyInternal;
import com.android.server.utils.AlarmQueue;
-import libcore.util.EmptyArray;
-
-import java.util.Comparator;
import java.util.List;
import java.util.Objects;
import java.util.function.Consumer;
@@ -105,54 +102,13 @@ class Agent {
private final BalanceThresholdAlarmQueue mBalanceThresholdAlarmQueue;
/**
- * Comparator to use to sort apps before we distribute ARCs so that we try to give the most
- * important apps ARCs first.
+ * Check the affordability notes of all apps.
*/
- @VisibleForTesting
- final Comparator<PackageInfo> mPackageDistributionComparator =
- new Comparator<PackageInfo>() {
- @Override
- public int compare(PackageInfo pi1, PackageInfo pi2) {
- final ApplicationInfo appInfo1 = pi1.applicationInfo;
- final ApplicationInfo appInfo2 = pi2.applicationInfo;
- // Put any packages that don't declare an application at the end. A missing
- // <application> tag likely means the app won't be doing any work anyway.
- if (appInfo1 == null) {
- if (appInfo2 == null) {
- return 0;
- }
- return 1;
- } else if (appInfo2 == null) {
- return -1;
- }
- // Privileged apps eat first. They're likely required for the device to
- // function properly.
- // TODO: include headless system apps
- if (appInfo1.isPrivilegedApp()) {
- if (!appInfo2.isPrivilegedApp()) {
- return -1;
- }
- } else if (appInfo2.isPrivilegedApp()) {
- return 1;
- }
-
- // Sort by most recently used.
- final long timeSinceLastUsedMs1 =
- mAppStandbyInternal.getTimeSinceLastUsedByUser(
- pi1.packageName, UserHandle.getUserId(pi1.applicationInfo.uid));
- final long timeSinceLastUsedMs2 =
- mAppStandbyInternal.getTimeSinceLastUsedByUser(
- pi2.packageName, UserHandle.getUserId(pi2.applicationInfo.uid));
- if (timeSinceLastUsedMs1 < timeSinceLastUsedMs2) {
- return -1;
- } else if (timeSinceLastUsedMs1 > timeSinceLastUsedMs2) {
- return 1;
- }
- return 0;
- }
- };
-
- private static final int MSG_CHECK_BALANCE = 0;
+ private static final int MSG_CHECK_ALL_AFFORDABILITY = 0;
+ /**
+ * Check the affordability notes of a single app.
+ */
+ private static final int MSG_CHECK_INDIVIDUAL_AFFORDABILITY = 1;
Agent(@NonNull InternalResourceService irs, @NonNull Scribe scribe) {
mLock = irs.getLock();
@@ -179,7 +135,7 @@ class Agent {
@Override
public void accept(OngoingEvent ongoingEvent) {
- mTotal += getActualDeltaLocked(ongoingEvent, mLedger, mNowElapsed, mNow);
+ mTotal += getActualDeltaLocked(ongoingEvent, mLedger, mNowElapsed, mNow).price;
}
}
@@ -204,6 +160,11 @@ class Agent {
}
@GuardedBy("mLock")
+ private boolean isAffordableLocked(long balance, long price, long ctp) {
+ return balance >= price && mScribe.getRemainingConsumableNarcsLocked() >= ctp;
+ }
+
+ @GuardedBy("mLock")
void noteInstantaneousEventLocked(final int userId, @NonNull final String pkgName,
final int eventId, @Nullable String tag) {
if (mIrs.isSystem(userId, pkgName)) {
@@ -218,10 +179,13 @@ class Agent {
final int eventType = getEventType(eventId);
switch (eventType) {
case TYPE_ACTION:
- final long actionCost = economicPolicy.getCostOfAction(eventId, userId, pkgName);
+ final EconomicPolicy.Cost actionCost =
+ economicPolicy.getCostOfAction(eventId, userId, pkgName);
recordTransactionLocked(userId, pkgName, ledger,
- new Ledger.Transaction(now, now, eventId, tag, -actionCost), true);
+ new Ledger.Transaction(now, now, eventId, tag,
+ -actionCost.price, actionCost.costToProduce),
+ true);
break;
case TYPE_REWARD:
@@ -231,7 +195,7 @@ class Agent {
final long rewardVal = Math.max(0,
Math.min(reward.maxDailyReward - rewardSum, reward.instantReward));
recordTransactionLocked(userId, pkgName, ledger,
- new Ledger.Transaction(now, now, eventId, tag, rewardVal), true);
+ new Ledger.Transaction(now, now, eventId, tag, rewardVal, 0), true);
}
break;
@@ -268,11 +232,12 @@ class Agent {
final int eventType = getEventType(eventId);
switch (eventType) {
case TYPE_ACTION:
- final long actionCost = economicPolicy.getCostOfAction(eventId, userId, pkgName);
+ final EconomicPolicy.Cost actionCost =
+ economicPolicy.getCostOfAction(eventId, userId, pkgName);
if (ongoingEvent == null) {
ongoingEvents.add(eventId, tag,
- new OngoingEvent(eventId, tag, null, startElapsed, -actionCost));
+ new OngoingEvent(eventId, tag, startElapsed, actionCost));
} else {
ongoingEvent.refCount++;
}
@@ -283,7 +248,7 @@ class Agent {
if (reward != null) {
if (ongoingEvent == null) {
ongoingEvents.add(eventId, tag, new OngoingEvent(
- eventId, tag, reward, startElapsed, reward.ongoingRewardPerSecond));
+ eventId, tag, startElapsed, reward));
} else {
ongoingEvent.refCount++;
}
@@ -306,52 +271,7 @@ class Agent {
@GuardedBy("mLock")
void onPricingChangedLocked() {
- final long now = getCurrentTimeMillis();
- final long nowElapsed = SystemClock.elapsedRealtime();
- final CompleteEconomicPolicy economicPolicy = mIrs.getCompleteEconomicPolicyLocked();
-
- mCurrentOngoingEvents.forEach((userId, pkgName, ongoingEvents) -> {
- final ArraySet<ActionAffordabilityNote> actionAffordabilityNotes =
- mActionAffordabilityNotes.get(userId, pkgName);
- final boolean[] wasAffordable;
- if (actionAffordabilityNotes != null) {
- final int size = actionAffordabilityNotes.size();
- wasAffordable = new boolean[size];
- for (int i = 0; i < size; ++i) {
- final ActionAffordabilityNote note = actionAffordabilityNotes.valueAt(i);
- final long originalBalance =
- mScribe.getLedgerLocked(userId, pkgName).getCurrentBalance();
- wasAffordable[i] = originalBalance >= note.getCachedModifiedPrice();
- }
- } else {
- wasAffordable = EmptyArray.BOOLEAN;
- }
- ongoingEvents.forEach((ongoingEvent) -> {
- // Disable balance check & affordability notifications here because we're in the
- // middle of updating ongoing action costs/prices and sending out notifications
- // or rescheduling the balance check alarm would be a waste since we'll have to
- // redo them again after all of our internal state is updated.
- stopOngoingActionLocked(userId, pkgName, ongoingEvent.eventId,
- ongoingEvent.tag, nowElapsed, now, false, false);
- noteOngoingEventLocked(userId, pkgName, ongoingEvent.eventId, ongoingEvent.tag,
- nowElapsed, false);
- });
- if (actionAffordabilityNotes != null) {
- final int size = actionAffordabilityNotes.size();
- for (int i = 0; i < size; ++i) {
- final ActionAffordabilityNote note = actionAffordabilityNotes.valueAt(i);
- note.recalculateModifiedPrice(economicPolicy, userId, pkgName);
- final long newBalance =
- mScribe.getLedgerLocked(userId, pkgName).getCurrentBalance();
- final boolean isAffordable = newBalance >= note.getCachedModifiedPrice();
- if (wasAffordable[i] != isAffordable) {
- note.setNewAffordability(isAffordable);
- mIrs.postAffordabilityChanged(userId, pkgName, note);
- }
- }
- }
- scheduleBalanceCheckLocked(userId, pkgName);
- });
+ onAnythingChangedLocked(true);
}
@GuardedBy("mLock")
@@ -365,46 +285,80 @@ class Agent {
SparseArrayMap<String, OngoingEvent> ongoingEvents =
mCurrentOngoingEvents.get(userId, pkgName);
if (ongoingEvents != null) {
+ mOngoingEventUpdater.reset(userId, pkgName, now, nowElapsed);
+ ongoingEvents.forEach(mOngoingEventUpdater);
final ArraySet<ActionAffordabilityNote> actionAffordabilityNotes =
mActionAffordabilityNotes.get(userId, pkgName);
- final boolean[] wasAffordable;
if (actionAffordabilityNotes != null) {
final int size = actionAffordabilityNotes.size();
- wasAffordable = new boolean[size];
+ final long newBalance =
+ mScribe.getLedgerLocked(userId, pkgName).getCurrentBalance();
for (int n = 0; n < size; ++n) {
final ActionAffordabilityNote note = actionAffordabilityNotes.valueAt(n);
- final long originalBalance =
- mScribe.getLedgerLocked(userId, pkgName).getCurrentBalance();
- wasAffordable[n] = originalBalance >= note.getCachedModifiedPrice();
+ note.recalculateCosts(economicPolicy, userId, pkgName);
+ final boolean isAffordable =
+ isAffordableLocked(newBalance,
+ note.getCachedModifiedPrice(), note.getCtp());
+ if (note.isCurrentlyAffordable() != isAffordable) {
+ note.setNewAffordability(isAffordable);
+ mIrs.postAffordabilityChanged(userId, pkgName, note);
+ }
}
- } else {
- wasAffordable = EmptyArray.BOOLEAN;
}
- ongoingEvents.forEach((ongoingEvent) -> {
- // Disable balance check & affordability notifications here because we're in the
- // middle of updating ongoing action costs/prices and sending out notifications
- // or rescheduling the balance check alarm would be a waste since we'll have to
- // redo them again after all of our internal state is updated.
- stopOngoingActionLocked(userId, pkgName, ongoingEvent.eventId,
- ongoingEvent.tag, nowElapsed, now, false, false);
- noteOngoingEventLocked(userId, pkgName, ongoingEvent.eventId, ongoingEvent.tag,
- nowElapsed, false);
- });
+ scheduleBalanceCheckLocked(userId, pkgName);
+ }
+ }
+ }
+
+ @GuardedBy("mLock")
+ private void onAnythingChangedLocked(final boolean updateOngoingEvents) {
+ final long now = getCurrentTimeMillis();
+ final long nowElapsed = SystemClock.elapsedRealtime();
+ final CompleteEconomicPolicy economicPolicy = mIrs.getCompleteEconomicPolicyLocked();
+
+ for (int uIdx = mCurrentOngoingEvents.numMaps() - 1; uIdx >= 0; --uIdx) {
+ final int userId = mCurrentOngoingEvents.keyAt(uIdx);
+
+ for (int pIdx = mCurrentOngoingEvents.numElementsForKey(userId) - 1; pIdx >= 0;
+ --pIdx) {
+ final String pkgName = mCurrentOngoingEvents.keyAt(uIdx, pIdx);
+
+ SparseArrayMap<String, OngoingEvent> ongoingEvents =
+ mCurrentOngoingEvents.valueAt(uIdx, pIdx);
+ if (ongoingEvents != null) {
+ if (updateOngoingEvents) {
+ mOngoingEventUpdater.reset(userId, pkgName, now, nowElapsed);
+ ongoingEvents.forEach(mOngoingEventUpdater);
+ }
+ scheduleBalanceCheckLocked(userId, pkgName);
+ }
+ }
+ }
+ for (int uIdx = mActionAffordabilityNotes.numMaps() - 1; uIdx >= 0; --uIdx) {
+ final int userId = mActionAffordabilityNotes.keyAt(uIdx);
+
+ for (int pIdx = mActionAffordabilityNotes.numElementsForKey(userId) - 1; pIdx >= 0;
+ --pIdx) {
+ final String pkgName = mActionAffordabilityNotes.keyAt(uIdx, pIdx);
+
+ final ArraySet<ActionAffordabilityNote> actionAffordabilityNotes =
+ mActionAffordabilityNotes.valueAt(uIdx, pIdx);
+
if (actionAffordabilityNotes != null) {
final int size = actionAffordabilityNotes.size();
+ final long newBalance = getBalanceLocked(userId, pkgName);
for (int n = 0; n < size; ++n) {
final ActionAffordabilityNote note = actionAffordabilityNotes.valueAt(n);
- note.recalculateModifiedPrice(economicPolicy, userId, pkgName);
- final long newBalance =
- mScribe.getLedgerLocked(userId, pkgName).getCurrentBalance();
- final boolean isAffordable = newBalance >= note.getCachedModifiedPrice();
- if (wasAffordable[n] != isAffordable) {
+ note.recalculateCosts(economicPolicy, userId, pkgName);
+ final boolean isAffordable =
+ isAffordableLocked(newBalance,
+ note.getCachedModifiedPrice(), note.getCtp());
+ if (note.isCurrentlyAffordable() != isAffordable) {
note.setNewAffordability(isAffordable);
mIrs.postAffordabilityChanged(userId, pkgName, note);
}
}
}
- scheduleBalanceCheckLocked(userId, pkgName);
}
}
}
@@ -416,9 +370,9 @@ class Agent {
}
/**
- * @param updateBalanceCheck Whether or not to reschedule the affordability/balance
+ * @param updateBalanceCheck Whether to reschedule the affordability/balance
* check alarm.
- * @param notifyOnAffordabilityChange Whether or not to evaluate the app's ability to afford
+ * @param notifyOnAffordabilityChange Whether to evaluate the app's ability to afford
* registered bills and notify listeners about any changes.
*/
@GuardedBy("mLock")
@@ -453,9 +407,11 @@ class Agent {
if (ongoingEvent.refCount <= 0) {
final long startElapsed = ongoingEvent.startTimeElapsed;
final long startTime = now - (nowElapsed - startElapsed);
- final long actualDelta = getActualDeltaLocked(ongoingEvent, ledger, nowElapsed, now);
+ final EconomicPolicy.Cost actualDelta =
+ getActualDeltaLocked(ongoingEvent, ledger, nowElapsed, now);
recordTransactionLocked(userId, pkgName, ledger,
- new Ledger.Transaction(startTime, now, eventId, tag, actualDelta),
+ new Ledger.Transaction(startTime, now, eventId, tag, actualDelta.price,
+ actualDelta.costToProduce),
notifyOnAffordabilityChange);
ongoingEvents.delete(eventId, tag);
@@ -466,17 +422,20 @@ class Agent {
}
@GuardedBy("mLock")
- private long getActualDeltaLocked(@NonNull OngoingEvent ongoingEvent, @NonNull Ledger ledger,
- long nowElapsed, long now) {
+ @NonNull
+ private EconomicPolicy.Cost getActualDeltaLocked(@NonNull OngoingEvent ongoingEvent,
+ @NonNull Ledger ledger, long nowElapsed, long now) {
final long startElapsed = ongoingEvent.startTimeElapsed;
final long durationSecs = (nowElapsed - startElapsed) / 1000;
- final long computedDelta = durationSecs * ongoingEvent.deltaPerSec;
+ final long computedDelta = durationSecs * ongoingEvent.getDeltaPerSec();
if (ongoingEvent.reward == null) {
- return computedDelta;
+ return new EconomicPolicy.Cost(
+ durationSecs * ongoingEvent.getCtpPerSec(), computedDelta);
}
final long rewardSum = ledger.get24HourSum(ongoingEvent.eventId, now);
- return Math.max(0,
- Math.min(ongoingEvent.reward.maxDailyReward - rewardSum, computedDelta));
+ return new EconomicPolicy.Cost(0,
+ Math.max(0,
+ Math.min(ongoingEvent.reward.maxDailyReward - rewardSum, computedDelta)));
}
@VisibleForTesting
@@ -494,22 +453,6 @@ class Agent {
return;
}
final CompleteEconomicPolicy economicPolicy = mIrs.getCompleteEconomicPolicyLocked();
- final long maxCirculationAllowed = mIrs.getMaxCirculationLocked();
- final long curNarcsInCirculation = mScribe.getNarcsInCirculationLocked();
- final long newArcsInCirculation = curNarcsInCirculation + transaction.delta;
- if (transaction.delta > 0 && newArcsInCirculation > maxCirculationAllowed) {
- // Set lower bound at 0 so we don't accidentally take away credits when we were trying
- // to _give_ the app credits.
- final long newDelta = Math.max(0, maxCirculationAllowed - curNarcsInCirculation);
- Slog.i(TAG, "Would result in too many credits in circulation. Decreasing transaction "
- + eventToString(transaction.eventId)
- + (transaction.tag == null ? "" : ":" + transaction.tag)
- + " for " + appToString(userId, pkgName)
- + " by " + narcToString(transaction.delta - newDelta));
- transaction = new Ledger.Transaction(
- transaction.startTimeMs, transaction.endTimeMs,
- transaction.eventId, transaction.tag, newDelta);
- }
final long originalBalance = ledger.getCurrentBalance();
if (transaction.delta > 0
&& originalBalance + transaction.delta > economicPolicy.getMaxSatiatedBalance()) {
@@ -524,10 +467,10 @@ class Agent {
+ " by " + narcToString(transaction.delta - newDelta));
transaction = new Ledger.Transaction(
transaction.startTimeMs, transaction.endTimeMs,
- transaction.eventId, transaction.tag, newDelta);
+ transaction.eventId, transaction.tag, newDelta, transaction.ctp);
}
ledger.recordTransaction(transaction);
- mScribe.adjustNarcsInCirculationLocked(transaction.delta);
+ mScribe.adjustRemainingConsumableNarcsLocked(-transaction.ctp);
if (transaction.delta != 0 && notifyOnAffordabilityChange) {
final ArraySet<ActionAffordabilityNote> actionAffordabilityNotes =
mActionAffordabilityNotes.get(userId, pkgName);
@@ -535,7 +478,9 @@ class Agent {
final long newBalance = ledger.getCurrentBalance();
for (int i = 0; i < actionAffordabilityNotes.size(); ++i) {
final ActionAffordabilityNote note = actionAffordabilityNotes.valueAt(i);
- final boolean isAffordable = newBalance >= note.getCachedModifiedPrice();
+ final boolean isAffordable =
+ isAffordableLocked(newBalance,
+ note.getCachedModifiedPrice(), note.getCtp());
if (note.isCurrentlyAffordable() != isAffordable) {
note.setNewAffordability(isAffordable);
mIrs.postAffordabilityChanged(userId, pkgName, note);
@@ -543,6 +488,10 @@ class Agent {
}
}
}
+ if (transaction.ctp != 0) {
+ mHandler.sendEmptyMessage(MSG_CHECK_ALL_AFFORDABILITY);
+ mIrs.maybePerformQuantitativeEasingLocked();
+ }
}
/**
@@ -599,8 +548,8 @@ class Agent {
}
recordTransactionLocked(userId, pkgName, ledger,
- new Ledger.Transaction(
- now, now, REGULATION_WEALTH_RECLAMATION, null, -toReclaim),
+ new Ledger.Transaction(now, now, REGULATION_WEALTH_RECLAMATION,
+ null, -toReclaim, 0),
true);
}
}
@@ -648,7 +597,7 @@ class Agent {
final long now = getCurrentTimeMillis();
final Ledger ledger = mScribe.getLedgerLocked(userId, pkgName);
recordTransactionLocked(userId, pkgName, ledger,
- new Ledger.Transaction(now, now, REGULATION_DEMOTION, null, -toReclaim),
+ new Ledger.Transaction(now, now, REGULATION_DEMOTION, null, -toReclaim, 0),
true);
}
}
@@ -665,10 +614,13 @@ class Agent {
return !mIrs.isSystem(userId, packageInfo.packageName);
}
+ void onCreditSupplyChanged() {
+ mHandler.sendEmptyMessage(MSG_CHECK_ALL_AFFORDABILITY);
+ }
+
@GuardedBy("mLock")
void distributeBasicIncomeLocked(int batteryLevel) {
List<PackageInfo> pkgs = mIrs.getInstalledPackages();
- pkgs.sort(mPackageDistributionComparator);
final long now = getCurrentTimeMillis();
for (int i = 0; i < pkgs.size(); ++i) {
@@ -686,7 +638,7 @@ class Agent {
if (shortfall > 0) {
recordTransactionLocked(userId, pkgName, ledger,
new Ledger.Transaction(now, now, REGULATION_BASIC_INCOME,
- null, (long) (perc * shortfall)), true);
+ null, (long) (perc * shortfall), 0), true);
}
}
}
@@ -705,12 +657,8 @@ class Agent {
@GuardedBy("mLock")
void grantBirthrightsLocked(final int userId) {
final List<PackageInfo> pkgs = mIrs.getInstalledPackages(userId);
- final long maxBirthright =
- mIrs.getMaxCirculationLocked() / mIrs.getInstalledPackages().size();
final long now = getCurrentTimeMillis();
- pkgs.sort(mPackageDistributionComparator);
-
for (int i = 0; i < pkgs.size(); ++i) {
final PackageInfo packageInfo = pkgs.get(i);
if (!shouldGiveCredits(packageInfo)) {
@@ -726,7 +674,7 @@ class Agent {
recordTransactionLocked(userId, pkgName, ledger,
new Ledger.Transaction(now, now, REGULATION_BIRTHRIGHT, null,
- Math.min(maxBirthright, mIrs.getMinBalanceLocked(userId, pkgName))),
+ mIrs.getMinBalanceLocked(userId, pkgName), 0),
true);
}
}
@@ -740,14 +688,11 @@ class Agent {
return;
}
- List<PackageInfo> pkgs = mIrs.getInstalledPackages();
- final int numPackages = pkgs.size();
- final long maxBirthright = mIrs.getMaxCirculationLocked() / numPackages;
final long now = getCurrentTimeMillis();
recordTransactionLocked(userId, pkgName, ledger,
new Ledger.Transaction(now, now, REGULATION_BIRTHRIGHT, null,
- Math.min(maxBirthright, mIrs.getMinBalanceLocked(userId, pkgName))), true);
+ mIrs.getMinBalanceLocked(userId, pkgName), 0), true);
}
@GuardedBy("mLock")
@@ -762,7 +707,7 @@ class Agent {
final long now = getCurrentTimeMillis();
recordTransactionLocked(userId, pkgName, ledger,
- new Ledger.Transaction(now, now, REGULATION_PROMOTION, null, missing), true);
+ new Ledger.Transaction(now, now, REGULATION_PROMOTION, null, missing, 0), true);
}
@GuardedBy("mLock")
@@ -779,7 +724,7 @@ class Agent {
private void reclaimAssetsLocked(final int userId, @NonNull final String pkgName) {
final Ledger ledger = mScribe.getLedgerLocked(userId, pkgName);
if (ledger.getCurrentBalance() != 0) {
- mScribe.adjustNarcsInCirculationLocked(-ledger.getCurrentBalance());
+ mScribe.adjustRemainingConsumableNarcsLocked(-ledger.getCurrentBalance());
}
mScribe.discardLedgerLocked(userId, pkgName);
mCurrentOngoingEvents.delete(userId, pkgName);
@@ -803,6 +748,7 @@ class Agent {
static final long WILL_NOT_CROSS_THRESHOLD = -1;
private long mCurBalance;
+ private long mRemainingConsumableCredits;
/**
* The maximum change in credits per second towards the upper threshold
* {@link #mUpperThreshold}. A value of 0 means the current ongoing events will never
@@ -815,15 +761,25 @@ class Agent {
* result in the app crossing the lower threshold.
*/
private long mMaxDeltaPerSecToLowerThreshold;
+ /**
+ * The maximum change in credits per second towards the highest CTP threshold below the
+ * remaining consumable credits (cached in {@link #mCtpThreshold}). A value of 0 means
+ * the current ongoing events will never result in the app crossing the lower threshold.
+ */
+ private long mMaxDeltaPerSecToCtpThreshold;
private long mUpperThreshold;
private long mLowerThreshold;
+ private long mCtpThreshold;
- void reset(long curBalance,
+ void reset(long curBalance, long remainingConsumableCredits,
@Nullable ArraySet<ActionAffordabilityNote> actionAffordabilityNotes) {
mCurBalance = curBalance;
+ mRemainingConsumableCredits = remainingConsumableCredits;
mMaxDeltaPerSecToUpperThreshold = mMaxDeltaPerSecToLowerThreshold = 0;
+ mMaxDeltaPerSecToCtpThreshold = 0;
mUpperThreshold = Long.MIN_VALUE;
mLowerThreshold = Long.MAX_VALUE;
+ mCtpThreshold = 0;
if (actionAffordabilityNotes != null) {
for (int i = 0; i < actionAffordabilityNotes.size(); ++i) {
final ActionAffordabilityNote note = actionAffordabilityNotes.valueAt(i);
@@ -835,6 +791,10 @@ class Agent {
mUpperThreshold = (mUpperThreshold == Long.MIN_VALUE)
? price : Math.min(mUpperThreshold, price);
}
+ final long ctp = note.getCtp();
+ if (ctp <= mRemainingConsumableCredits) {
+ mCtpThreshold = Math.max(mCtpThreshold, ctp);
+ }
}
}
}
@@ -847,13 +807,23 @@ class Agent {
* threshold.
*/
long getTimeToCrossLowerThresholdMs() {
- if (mMaxDeltaPerSecToLowerThreshold == 0) {
- // Will never cross upper threshold based on current events.
+ if (mMaxDeltaPerSecToLowerThreshold == 0 && mMaxDeltaPerSecToCtpThreshold == 0) {
+ // Will never cross lower threshold based on current events.
return WILL_NOT_CROSS_THRESHOLD;
}
- // deltaPerSec is a negative value, so do threshold-balance to cancel out the negative.
- final long minSeconds =
- (mLowerThreshold - mCurBalance) / mMaxDeltaPerSecToLowerThreshold;
+ long minSeconds = Long.MAX_VALUE;
+ if (mMaxDeltaPerSecToLowerThreshold != 0) {
+ // deltaPerSec is a negative value, so do threshold-balance to cancel out the
+ // negative.
+ minSeconds = (mLowerThreshold - mCurBalance) / mMaxDeltaPerSecToLowerThreshold;
+ }
+ if (mMaxDeltaPerSecToCtpThreshold != 0) {
+ minSeconds = Math.min(minSeconds,
+ // deltaPerSec is a negative value, so do threshold-balance to cancel
+ // out the negative.
+ (mCtpThreshold - mRemainingConsumableCredits)
+ / mMaxDeltaPerSecToCtpThreshold);
+ }
return minSeconds * 1000;
}
@@ -876,10 +846,15 @@ class Agent {
@Override
public void accept(OngoingEvent ongoingEvent) {
- if (mCurBalance >= mLowerThreshold && ongoingEvent.deltaPerSec < 0) {
- mMaxDeltaPerSecToLowerThreshold += ongoingEvent.deltaPerSec;
- } else if (mCurBalance < mUpperThreshold && ongoingEvent.deltaPerSec > 0) {
- mMaxDeltaPerSecToUpperThreshold += ongoingEvent.deltaPerSec;
+ final long deltaPerSec = ongoingEvent.getDeltaPerSec();
+ if (mCurBalance >= mLowerThreshold && deltaPerSec < 0) {
+ mMaxDeltaPerSecToLowerThreshold += deltaPerSec;
+ } else if (mCurBalance < mUpperThreshold && deltaPerSec > 0) {
+ mMaxDeltaPerSecToUpperThreshold += deltaPerSec;
+ }
+ final long ctpPerSec = ongoingEvent.getCtpPerSec();
+ if (mRemainingConsumableCredits >= mCtpThreshold && deltaPerSec < 0) {
+ mMaxDeltaPerSecToCtpThreshold -= ctpPerSec;
}
}
}
@@ -896,8 +871,9 @@ class Agent {
mBalanceThresholdAlarmQueue.removeAlarmForKey(new Package(userId, pkgName));
return;
}
- mTrendCalculator.reset(
- getBalanceLocked(userId, pkgName), mActionAffordabilityNotes.get(userId, pkgName));
+ mTrendCalculator.reset(getBalanceLocked(userId, pkgName),
+ mScribe.getRemainingConsumableNarcsLocked(),
+ mActionAffordabilityNotes.get(userId, pkgName));
ongoingEvents.forEach(mTrendCalculator);
final long lowerTimeMs = mTrendCalculator.getTimeToCrossLowerThresholdMs();
final long upperTimeMs = mTrendCalculator.getTimeToCrossUpperThresholdMs();
@@ -931,20 +907,79 @@ class Agent {
public final String tag;
@Nullable
public final EconomicPolicy.Reward reward;
- public final long deltaPerSec;
+ @Nullable
+ public final EconomicPolicy.Cost actionCost;
public int refCount;
- OngoingEvent(int eventId, @Nullable String tag,
- @Nullable EconomicPolicy.Reward reward, long startTimeElapsed, long deltaPerSec) {
+ OngoingEvent(int eventId, @Nullable String tag, long startTimeElapsed,
+ @NonNull EconomicPolicy.Reward reward) {
this.startTimeElapsed = startTimeElapsed;
this.eventId = eventId;
this.tag = tag;
this.reward = reward;
- this.deltaPerSec = deltaPerSec;
+ this.actionCost = null;
+ refCount = 1;
+ }
+
+ OngoingEvent(int eventId, @Nullable String tag, long startTimeElapsed,
+ @NonNull EconomicPolicy.Cost actionCost) {
+ this.startTimeElapsed = startTimeElapsed;
+ this.eventId = eventId;
+ this.tag = tag;
+ this.reward = null;
+ this.actionCost = actionCost;
refCount = 1;
}
+
+ long getDeltaPerSec() {
+ if (actionCost != null) {
+ return -actionCost.price;
+ }
+ if (reward != null) {
+ return reward.ongoingRewardPerSecond;
+ }
+ Slog.wtfStack(TAG, "No action or reward in ongoing event?!??!");
+ return 0;
+ }
+
+ long getCtpPerSec() {
+ if (actionCost != null) {
+ return actionCost.costToProduce;
+ }
+ return 0;
+ }
+ }
+
+ private class OngoingEventUpdater implements Consumer<OngoingEvent> {
+ private int mUserId;
+ private String mPkgName;
+ private long mNow;
+ private long mNowElapsed;
+
+ private void reset(int userId, String pkgName, long now, long nowElapsed) {
+ mUserId = userId;
+ mPkgName = pkgName;
+ mNow = now;
+ mNowElapsed = nowElapsed;
+ }
+
+ @Override
+ public void accept(OngoingEvent ongoingEvent) {
+ // Disable balance check & affordability notifications here because
+ // we're in the middle of updating ongoing action costs/prices and
+ // sending out notifications or rescheduling the balance check alarm
+ // would be a waste since we'll have to redo them again after all of
+ // our internal state is updated.
+ final boolean updateBalanceCheck = false;
+ stopOngoingActionLocked(mUserId, mPkgName, ongoingEvent.eventId, ongoingEvent.tag,
+ mNowElapsed, mNow, updateBalanceCheck, /* notifyOnAffordabilityChange */ false);
+ noteOngoingEventLocked(mUserId, mPkgName, ongoingEvent.eventId, ongoingEvent.tag,
+ mNowElapsed, updateBalanceCheck);
+ }
}
+ private final OngoingEventUpdater mOngoingEventUpdater = new OngoingEventUpdater();
+
private static final class Package {
public final String packageName;
public final int userId;
@@ -996,7 +1031,8 @@ class Agent {
protected void processExpiredAlarms(@NonNull ArraySet<Package> expired) {
for (int i = 0; i < expired.size(); ++i) {
Package p = expired.valueAt(i);
- mHandler.obtainMessage(MSG_CHECK_BALANCE, p.userId, 0, p.packageName)
+ mHandler.obtainMessage(
+ MSG_CHECK_INDIVIDUAL_AFFORDABILITY, p.userId, 0, p.packageName)
.sendToTarget();
}
}
@@ -1023,9 +1059,10 @@ class Agent {
note.setNewAffordability(true);
return;
}
- note.recalculateModifiedPrice(economicPolicy, userId, pkgName);
+ note.recalculateCosts(economicPolicy, userId, pkgName);
note.setNewAffordability(
- getBalanceLocked(userId, pkgName) >= note.getCachedModifiedPrice());
+ isAffordableLocked(getBalanceLocked(userId, pkgName),
+ note.getCachedModifiedPrice(), note.getCtp()));
mIrs.postAffordabilityChanged(userId, pkgName, note);
// Update ongoing alarm
scheduleBalanceCheckLocked(userId, pkgName);
@@ -1052,6 +1089,7 @@ class Agent {
static final class ActionAffordabilityNote {
private final EconomyManagerInternal.ActionBill mActionBill;
private final EconomyManagerInternal.AffordabilityChangeListener mListener;
+ private long mCtp;
private long mModifiedPrice;
private boolean mIsAffordable;
@@ -1086,22 +1124,29 @@ class Agent {
return mModifiedPrice;
}
+ private long getCtp() {
+ return mCtp;
+ }
+
@VisibleForTesting
- long recalculateModifiedPrice(@NonNull EconomicPolicy economicPolicy,
+ void recalculateCosts(@NonNull EconomicPolicy economicPolicy,
int userId, @NonNull String pkgName) {
long modifiedPrice = 0;
+ long ctp = 0;
final List<EconomyManagerInternal.AnticipatedAction> anticipatedActions =
mActionBill.getAnticipatedActions();
for (int i = 0; i < anticipatedActions.size(); ++i) {
final EconomyManagerInternal.AnticipatedAction aa = anticipatedActions.get(i);
- final long actionCost =
+ final EconomicPolicy.Cost actionCost =
economicPolicy.getCostOfAction(aa.actionId, userId, pkgName);
- modifiedPrice += actionCost * aa.numInstantaneousCalls
- + actionCost * (aa.ongoingDurationMs / 1000);
+ modifiedPrice += actionCost.price * aa.numInstantaneousCalls
+ + actionCost.price * (aa.ongoingDurationMs / 1000);
+ ctp += actionCost.costToProduce * aa.numInstantaneousCalls
+ + actionCost.costToProduce * (aa.ongoingDurationMs / 1000);
}
mModifiedPrice = modifiedPrice;
- return modifiedPrice;
+ mCtp = ctp;
}
boolean isCurrentlyAffordable() {
@@ -1138,7 +1183,15 @@ class Agent {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
- case MSG_CHECK_BALANCE: {
+ case MSG_CHECK_ALL_AFFORDABILITY: {
+ synchronized (mLock) {
+ removeMessages(MSG_CHECK_ALL_AFFORDABILITY);
+ onAnythingChangedLocked(false);
+ }
+ }
+ break;
+
+ case MSG_CHECK_INDIVIDUAL_AFFORDABILITY: {
final int userId = msg.arg1;
final String pkgName = (String) msg.obj;
synchronized (mLock) {
@@ -1151,8 +1204,8 @@ class Agent {
for (int i = 0; i < actionAffordabilityNotes.size(); ++i) {
final ActionAffordabilityNote note =
actionAffordabilityNotes.valueAt(i);
- final boolean isAffordable =
- newBalance >= note.getCachedModifiedPrice();
+ final boolean isAffordable = isAffordableLocked(
+ newBalance, note.getCachedModifiedPrice(), note.getCtp());
if (note.isCurrentlyAffordable() != isAffordable) {
note.setNewAffordability(isAffordable);
mIrs.postAffordabilityChanged(userId, pkgName, note);
@@ -1207,7 +1260,12 @@ class Agent {
pw.print(" runtime=");
TimeUtils.formatDuration(nowElapsed - ongoingEvent.startTimeElapsed, pw);
pw.print(" delta/sec=");
- pw.print(ongoingEvent.deltaPerSec);
+ pw.print(narcToString(ongoingEvent.getDeltaPerSec()));
+ final long ctp = ongoingEvent.getCtpPerSec();
+ if (ctp != 0) {
+ pw.print(" ctp/sec=");
+ pw.print(narcToString(ongoingEvent.getCtpPerSec()));
+ }
pw.print(" refCount=");
pw.print(ongoingEvent.refCount);
pw.println();
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/AlarmManagerEconomicPolicy.java b/apex/jobscheduler/service/java/com/android/server/tare/AlarmManagerEconomicPolicy.java
index e1e6e47155aa..71e00cf053e2 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/AlarmManagerEconomicPolicy.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/AlarmManagerEconomicPolicy.java
@@ -33,7 +33,8 @@ import static android.app.tare.EconomyManager.DEFAULT_AM_ACTION_ALARM_INEXACT_NO
import static android.app.tare.EconomyManager.DEFAULT_AM_ACTION_ALARM_INEXACT_NONWAKEUP_CTP;
import static android.app.tare.EconomyManager.DEFAULT_AM_ACTION_ALARM_INEXACT_WAKEUP_BASE_PRICE;
import static android.app.tare.EconomyManager.DEFAULT_AM_ACTION_ALARM_INEXACT_WAKEUP_CTP;
-import static android.app.tare.EconomyManager.DEFAULT_AM_MAX_CIRCULATION;
+import static android.app.tare.EconomyManager.DEFAULT_AM_HARD_CONSUMPTION_LIMIT;
+import static android.app.tare.EconomyManager.DEFAULT_AM_INITIAL_CONSUMPTION_LIMIT;
import static android.app.tare.EconomyManager.DEFAULT_AM_MAX_SATIATED_BALANCE;
import static android.app.tare.EconomyManager.DEFAULT_AM_MIN_SATIATED_BALANCE_EXEMPTED;
import static android.app.tare.EconomyManager.DEFAULT_AM_MIN_SATIATED_BALANCE_OTHER_APP;
@@ -70,7 +71,8 @@ import static android.app.tare.EconomyManager.KEY_AM_ACTION_ALARM_INEXACT_NONWAK
import static android.app.tare.EconomyManager.KEY_AM_ACTION_ALARM_INEXACT_NONWAKEUP_CTP;
import static android.app.tare.EconomyManager.KEY_AM_ACTION_ALARM_INEXACT_WAKEUP_BASE_PRICE;
import static android.app.tare.EconomyManager.KEY_AM_ACTION_ALARM_INEXACT_WAKEUP_CTP;
-import static android.app.tare.EconomyManager.KEY_AM_MAX_CIRCULATION;
+import static android.app.tare.EconomyManager.KEY_AM_HARD_CONSUMPTION_LIMIT;
+import static android.app.tare.EconomyManager.KEY_AM_INITIAL_CONSUMPTION_LIMIT;
import static android.app.tare.EconomyManager.KEY_AM_MAX_SATIATED_BALANCE;
import static android.app.tare.EconomyManager.KEY_AM_MIN_SATIATED_BALANCE_EXEMPTED;
import static android.app.tare.EconomyManager.KEY_AM_MIN_SATIATED_BALANCE_OTHER_APP;
@@ -143,7 +145,8 @@ public class AlarmManagerEconomicPolicy extends EconomicPolicy {
private long mMinSatiatedBalanceExempted;
private long mMinSatiatedBalanceOther;
private long mMaxSatiatedBalance;
- private long mMaxSatiatedCirculation;
+ private long mInitialSatiatedConsumptionLimit;
+ private long mHardSatiatedConsumptionLimit;
private final KeyValueListParser mParser = new KeyValueListParser(',');
private final InternalResourceService mInternalResourceService;
@@ -179,8 +182,13 @@ public class AlarmManagerEconomicPolicy extends EconomicPolicy {
}
@Override
- long getMaxSatiatedCirculation() {
- return mMaxSatiatedCirculation;
+ long getInitialSatiatedConsumptionLimit() {
+ return mInitialSatiatedConsumptionLimit;
+ }
+
+ @Override
+ long getHardSatiatedConsumptionLimit() {
+ return mHardSatiatedConsumptionLimit;
}
@NonNull
@@ -217,8 +225,11 @@ public class AlarmManagerEconomicPolicy extends EconomicPolicy {
DEFAULT_AM_MIN_SATIATED_BALANCE_OTHER_APP));
mMaxSatiatedBalance = arcToNarc(mParser.getInt(KEY_AM_MAX_SATIATED_BALANCE,
DEFAULT_AM_MAX_SATIATED_BALANCE));
- mMaxSatiatedCirculation = arcToNarc(mParser.getInt(KEY_AM_MAX_CIRCULATION,
- DEFAULT_AM_MAX_CIRCULATION));
+ mInitialSatiatedConsumptionLimit = arcToNarc(mParser.getInt(
+ KEY_AM_INITIAL_CONSUMPTION_LIMIT, DEFAULT_AM_INITIAL_CONSUMPTION_LIMIT));
+ mHardSatiatedConsumptionLimit = Math.max(mInitialSatiatedConsumptionLimit,
+ arcToNarc(mParser.getInt(
+ KEY_AM_HARD_CONSUMPTION_LIMIT, DEFAULT_AM_HARD_CONSUMPTION_LIMIT)));
final long exactAllowWhileIdleWakeupBasePrice = arcToNarc(
mParser.getInt(KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_WAKEUP_BASE_PRICE,
@@ -357,7 +368,11 @@ public class AlarmManagerEconomicPolicy extends EconomicPolicy {
pw.print("Other", narcToString(mMinSatiatedBalanceOther)).println();
pw.decreaseIndent();
pw.print("Max satiated balance", narcToString(mMaxSatiatedBalance)).println();
- pw.print("Max satiated circulation", narcToString(mMaxSatiatedCirculation)).println();
+ pw.print("Consumption limits: [");
+ pw.print(narcToString(mInitialSatiatedConsumptionLimit));
+ pw.print(", ");
+ pw.print(narcToString(mHardSatiatedConsumptionLimit));
+ pw.println("]");
pw.println();
pw.println("Actions:");
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/CompleteEconomicPolicy.java b/apex/jobscheduler/service/java/com/android/server/tare/CompleteEconomicPolicy.java
index a4e7b808ca6c..2109a8575777 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/CompleteEconomicPolicy.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/CompleteEconomicPolicy.java
@@ -34,7 +34,7 @@ public class CompleteEconomicPolicy extends EconomicPolicy {
private final SparseArray<Reward> mRewards = new SparseArray<>();
private final int[] mCostModifiers;
private long mMaxSatiatedBalance;
- private long mMaxSatiatedCirculation;
+ private long mConsumptionLimit;
CompleteEconomicPolicy(@NonNull InternalResourceService irs) {
super(irs);
@@ -74,9 +74,9 @@ public class CompleteEconomicPolicy extends EconomicPolicy {
max = 0;
for (int i = 0; i < mEnabledEconomicPolicies.size(); ++i) {
- max += mEnabledEconomicPolicies.valueAt(i).getMaxSatiatedCirculation();
+ max += mEnabledEconomicPolicies.valueAt(i).getInitialSatiatedConsumptionLimit();
}
- mMaxSatiatedCirculation = max;
+ mConsumptionLimit = max;
}
@Override
@@ -94,8 +94,13 @@ public class CompleteEconomicPolicy extends EconomicPolicy {
}
@Override
- long getMaxSatiatedCirculation() {
- return mMaxSatiatedCirculation;
+ long getInitialSatiatedConsumptionLimit() {
+ return mConsumptionLimit;
+ }
+
+ @Override
+ long getHardSatiatedConsumptionLimit() {
+ return mConsumptionLimit;
}
@NonNull
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java b/apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java
index c1177b20bb0e..1e480155f18a 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java
@@ -151,6 +151,16 @@ public abstract class EconomicPolicy {
}
}
+ static class Cost {
+ public final long costToProduce;
+ public final long price;
+
+ Cost(long costToProduce, long price) {
+ this.costToProduce = costToProduce;
+ this.price = price;
+ }
+ }
+
private static final Modifier[] COST_MODIFIER_BY_INDEX = new Modifier[NUM_COST_MODIFIERS];
EconomicPolicy(@NonNull InternalResourceService irs) {
@@ -193,10 +203,18 @@ public abstract class EconomicPolicy {
abstract long getMaxSatiatedBalance();
/**
- * Returns the maximum number of narcs that should be in circulation at once when the device is
- * at 100% battery.
+ * Returns the maximum number of narcs that should be consumed during a full 100% discharge
+ * cycle. This is the initial limit. The system may choose to increase the limit over time,
+ * but the increased limit should never exceed the value returned from
+ * {@link #getHardSatiatedConsumptionLimit()}.
*/
- abstract long getMaxSatiatedCirculation();
+ abstract long getInitialSatiatedConsumptionLimit();
+
+ /**
+ * Returns the maximum number of narcs that should be consumed during a full 100% discharge
+ * cycle. This is the hard limit that should never be exceeded.
+ */
+ abstract long getHardSatiatedConsumptionLimit();
/** Return the set of modifiers that should apply to this policy's costs. */
@NonNull
@@ -211,10 +229,11 @@ public abstract class EconomicPolicy {
void dump(IndentingPrintWriter pw) {
}
- final long getCostOfAction(int actionId, int userId, @NonNull String pkgName) {
+ @NonNull
+ final Cost getCostOfAction(int actionId, int userId, @NonNull String pkgName) {
final Action action = getAction(actionId);
if (action == null) {
- return 0;
+ return new Cost(0, 0);
}
long ctp = action.costToProduce;
long price = action.basePrice;
@@ -235,7 +254,7 @@ public abstract class EconomicPolicy {
(ProcessStateModifier) getModifier(COST_MODIFIER_PROCESS_STATE);
price = processStateModifier.getModifiedPrice(userId, pkgName, ctp, price);
}
- return price;
+ return new Cost(ctp, price);
}
private static void initModifier(@Modifier.CostModifier final int modifierId,
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java b/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
index 36895a5c3a25..c93480700ca7 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
@@ -69,6 +69,7 @@ import com.android.internal.util.DumpUtils;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.pm.UserManagerInternal;
+import com.android.server.tare.EconomicPolicy.Cost;
import com.android.server.tare.EconomyManagerInternal.TareStateChangeListener;
import java.io.FileDescriptor;
@@ -100,6 +101,11 @@ public class InternalResourceService extends SystemService {
private static final long MIN_UNUSED_TIME_MS = 3 * DAY_IN_MILLIS;
/** The amount of time to delay reclamation by after boot. */
private static final long RECLAMATION_STARTUP_DELAY_MS = 30_000L;
+ /**
+ * The battery level above which we may consider quantitative easing (increasing the consumption
+ * limit).
+ */
+ private static final int QUANTITATIVE_EASING_BATTERY_THRESHOLD = 50;
private static final int PACKAGE_QUERY_FLAGS =
PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
| PackageManager.MATCH_APEX;
@@ -122,44 +128,6 @@ public class InternalResourceService extends SystemService {
@GuardedBy("mLock")
private CompleteEconomicPolicy mCompleteEconomicPolicy;
- private static final class ReclamationConfig {
- /**
- * ARC circulation threshold (% circulating vs scaled maximum) above which this config
- * should come into play.
- */
- public final double circulationPercentageThreshold;
- /** @see Agent#reclaimUnusedAssetsLocked(double, long, boolean) */
- public final double reclamationPercentage;
- /** @see Agent#reclaimUnusedAssetsLocked(double, long, boolean) */
- public final long minUsedTimeMs;
- /** @see Agent#reclaimUnusedAssetsLocked(double, long, boolean) */
- public final boolean scaleMinBalance;
-
- ReclamationConfig(double circulationPercentageThreshold, double reclamationPercentage,
- long minUsedTimeMs, boolean scaleMinBalance) {
- this.circulationPercentageThreshold = circulationPercentageThreshold;
- this.reclamationPercentage = reclamationPercentage;
- this.minUsedTimeMs = minUsedTimeMs;
- this.scaleMinBalance = scaleMinBalance;
- }
- }
-
- /**
- * Sorted list of reclamation configs used to determine how many credits to force reclaim when
- * the circulation percentage is too high. The list should *always* be sorted in descending
- * order of {@link ReclamationConfig#circulationPercentageThreshold}.
- */
- @GuardedBy("mLock")
- private final List<ReclamationConfig> mReclamationConfigs = List.of(
- new ReclamationConfig(2, .75, 12 * HOUR_IN_MILLIS, true),
- new ReclamationConfig(1.6, .5, DAY_IN_MILLIS, true),
- new ReclamationConfig(1.4, .25, DAY_IN_MILLIS, true),
- new ReclamationConfig(1.2, .25, 2 * DAY_IN_MILLIS, true),
- new ReclamationConfig(1, .25, MIN_UNUSED_TIME_MS, false),
- new ReclamationConfig(
- .9, DEFAULT_UNUSED_RECLAMATION_PERCENTAGE, MIN_UNUSED_TIME_MS, false)
- );
-
@NonNull
@GuardedBy("mLock")
private final List<PackageInfo> mPkgCache = new ArrayList<>();
@@ -266,8 +234,7 @@ public class InternalResourceService extends SystemService {
private static final int MSG_NOTIFY_AFFORDABILITY_CHANGE_LISTENER = 0;
private static final int MSG_SCHEDULE_UNUSED_WEALTH_RECLAMATION_EVENT = 1;
private static final int MSG_PROCESS_USAGE_EVENT = 2;
- private static final int MSG_MAYBE_FORCE_RECLAIM = 3;
- private static final int MSG_NOTIFY_STATE_CHANGE_LISTENERS = 4;
+ private static final int MSG_NOTIFY_STATE_CHANGE_LISTENERS = 3;
private static final String ALARM_TAG_WEALTH_RECLAMATION = "*tare.reclamation*";
/**
@@ -362,8 +329,8 @@ public class InternalResourceService extends SystemService {
}
@GuardedBy("mLock")
- long getMaxCirculationLocked() {
- return mCurrentBatteryLevel * mCompleteEconomicPolicy.getMaxSatiatedCirculation() / 100;
+ long getConsumptionLimitLocked() {
+ return mCurrentBatteryLevel * mScribe.getSatiatedConsumptionLimitLocked() / 100;
}
@GuardedBy("mLock")
@@ -372,6 +339,11 @@ public class InternalResourceService extends SystemService {
/ 100;
}
+ @GuardedBy("mLock")
+ long getInitialSatiatedConsumptionLimitLocked() {
+ return mCompleteEconomicPolicy.getInitialSatiatedConsumptionLimit();
+ }
+
int getUid(final int userId, @NonNull final String pkgName) {
synchronized (mPackageToUidCache) {
Integer uid = mPackageToUidCache.get(userId, pkgName);
@@ -403,12 +375,15 @@ public class InternalResourceService extends SystemService {
void onBatteryLevelChanged() {
synchronized (mLock) {
final int newBatteryLevel = getCurrentBatteryLevel();
- if (newBatteryLevel > mCurrentBatteryLevel) {
+ final boolean increased = newBatteryLevel > mCurrentBatteryLevel;
+ if (increased) {
mAgent.distributeBasicIncomeLocked(newBatteryLevel);
- } else if (newBatteryLevel < mCurrentBatteryLevel) {
- mHandler.obtainMessage(MSG_MAYBE_FORCE_RECLAIM).sendToTarget();
+ } else if (newBatteryLevel == mCurrentBatteryLevel) {
+ Slog.wtf(TAG, "Battery level stayed the same");
+ return;
}
mCurrentBatteryLevel = newBatteryLevel;
+ adjustCreditSupplyLocked(increased);
}
}
@@ -546,6 +521,31 @@ public class InternalResourceService extends SystemService {
}
}
+ /**
+ * Try to increase the consumption limit if apps are reaching the current limit too quickly.
+ */
+ @GuardedBy("mLock")
+ void maybePerformQuantitativeEasingLocked() {
+ // We don't need to increase the limit if the device runs out of consumable credits
+ // when the battery is low.
+ final long remainingConsumableNarcs = mScribe.getRemainingConsumableNarcsLocked();
+ if (mCurrentBatteryLevel <= QUANTITATIVE_EASING_BATTERY_THRESHOLD
+ || remainingConsumableNarcs > 0) {
+ return;
+ }
+ final long currentConsumptionLimit = mScribe.getSatiatedConsumptionLimitLocked();
+ final long shortfall = (mCurrentBatteryLevel - QUANTITATIVE_EASING_BATTERY_THRESHOLD)
+ * currentConsumptionLimit / 100;
+ final long newConsumptionLimit = Math.min(currentConsumptionLimit + shortfall,
+ mCompleteEconomicPolicy.getHardSatiatedConsumptionLimit());
+ if (newConsumptionLimit != currentConsumptionLimit) {
+ Slog.i(TAG, "Increasing consumption limit from " + narcToString(currentConsumptionLimit)
+ + " to " + narcToString(newConsumptionLimit));
+ mScribe.setConsumptionLimitLocked(newConsumptionLimit);
+ adjustCreditSupplyLocked(/* allowIncrease */ true);
+ }
+ }
+
void postAffordabilityChanged(final int userId, @NonNull final String pkgName,
@NonNull Agent.ActionAffordabilityNote affordabilityNote) {
if (DEBUG) {
@@ -560,6 +560,23 @@ public class InternalResourceService extends SystemService {
}
@GuardedBy("mLock")
+ private void adjustCreditSupplyLocked(boolean allowIncrease) {
+ final long newLimit = getConsumptionLimitLocked();
+ final long remainingConsumableNarcs = mScribe.getRemainingConsumableNarcsLocked();
+ if (remainingConsumableNarcs == newLimit) {
+ return;
+ }
+ if (remainingConsumableNarcs > newLimit) {
+ mScribe.adjustRemainingConsumableNarcsLocked(newLimit - remainingConsumableNarcs);
+ } else if (allowIncrease) {
+ final double perc = mCurrentBatteryLevel / 100d;
+ final long shortfall = newLimit - remainingConsumableNarcs;
+ mScribe.adjustRemainingConsumableNarcsLocked((long) (perc * shortfall));
+ }
+ mAgent.onCreditSupplyChanged();
+ }
+
+ @GuardedBy("mLock")
private void processUsageEventLocked(final int userId, @NonNull UsageEvents.Event event) {
if (!mIsEnabled) {
return;
@@ -650,48 +667,6 @@ public class InternalResourceService extends SystemService {
}
}
- /**
- * Reclaim unused ARCs above apps' minimum balances if there are too many credits currently
- * in circulation.
- */
- @GuardedBy("mLock")
- private void maybeForceReclaimLocked() {
- final long maxCirculation = getMaxCirculationLocked();
- if (maxCirculation == 0) {
- Slog.wtf(TAG, "Max scaled circulation is 0...");
- mAgent.reclaimUnusedAssetsLocked(1, HOUR_IN_MILLIS, true);
- mScribe.setLastReclamationTimeLocked(getCurrentTimeMillis());
- scheduleUnusedWealthReclamationLocked();
- return;
- }
- final long curCirculation = mScribe.getNarcsInCirculationLocked();
- final double circulationPerc = 1.0 * curCirculation / maxCirculation;
- if (DEBUG) {
- Slog.d(TAG, "Circulation %: " + circulationPerc);
- }
- final int numConfigs = mReclamationConfigs.size();
- if (numConfigs == 0) {
- return;
- }
- // The configs are sorted in descending order of circulationPercentageThreshold, so we can
- // short-circuit if the current circulation is lower than the lowest threshold.
- if (circulationPerc
- < mReclamationConfigs.get(numConfigs - 1).circulationPercentageThreshold) {
- return;
- }
- // TODO: maybe exclude apps we think will be launched in the next few hours
- for (int i = 0; i < numConfigs; ++i) {
- final ReclamationConfig config = mReclamationConfigs.get(i);
- if (circulationPerc >= config.circulationPercentageThreshold) {
- mAgent.reclaimUnusedAssetsLocked(
- config.reclamationPercentage, config.minUsedTimeMs, config.scaleMinBalance);
- mScribe.setLastReclamationTimeLocked(getCurrentTimeMillis());
- scheduleUnusedWealthReclamationLocked();
- break;
- }
- }
- }
-
private void registerListeners() {
final IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_BATTERY_LEVEL_CHANGED);
@@ -731,8 +706,18 @@ public class InternalResourceService extends SystemService {
final boolean isFirstSetup = !mScribe.recordExists();
if (isFirstSetup) {
mAgent.grantBirthrightsLocked();
+ mScribe.setConsumptionLimitLocked(
+ mCompleteEconomicPolicy.getInitialSatiatedConsumptionLimit());
} else {
mScribe.loadFromDiskLocked();
+ if (mScribe.getSatiatedConsumptionLimitLocked()
+ < mCompleteEconomicPolicy.getInitialSatiatedConsumptionLimit()
+ || mScribe.getSatiatedConsumptionLimitLocked()
+ > mCompleteEconomicPolicy.getHardSatiatedConsumptionLimit()) {
+ // Reset the consumption limit since several factors may have changed.
+ mScribe.setConsumptionLimitLocked(
+ mCompleteEconomicPolicy.getInitialSatiatedConsumptionLimit());
+ }
}
scheduleUnusedWealthReclamationLocked();
}
@@ -787,14 +772,6 @@ public class InternalResourceService extends SystemService {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
- case MSG_MAYBE_FORCE_RECLAIM: {
- removeMessages(MSG_MAYBE_FORCE_RECLAIM);
- synchronized (mLock) {
- maybeForceReclaimLocked();
- }
- }
- break;
-
case MSG_NOTIFY_AFFORDABILITY_CHANGE_LISTENER: {
final SomeArgs args = (SomeArgs) msg.obj;
final int userId = args.argi1;
@@ -936,12 +913,13 @@ public class InternalResourceService extends SystemService {
synchronized (mLock) {
for (int i = 0; i < projectedActions.size(); ++i) {
AnticipatedAction action = projectedActions.get(i);
- final long cost = mCompleteEconomicPolicy.getCostOfAction(
+ final Cost cost = mCompleteEconomicPolicy.getCostOfAction(
action.actionId, userId, pkgName);
- requiredBalance += cost * action.numInstantaneousCalls
- + cost * (action.ongoingDurationMs / 1000);
+ requiredBalance += cost.price * action.numInstantaneousCalls
+ + cost.price * (action.ongoingDurationMs / 1000);
}
- return mAgent.getBalanceLocked(userId, pkgName) >= requiredBalance;
+ return mAgent.getBalanceLocked(userId, pkgName) >= requiredBalance
+ && mScribe.getRemainingConsumableNarcsLocked() >= requiredBalance;
}
}
@@ -960,14 +938,17 @@ public class InternalResourceService extends SystemService {
synchronized (mLock) {
for (int i = 0; i < projectedActions.size(); ++i) {
AnticipatedAction action = projectedActions.get(i);
- final long cost = mCompleteEconomicPolicy.getCostOfAction(
+ final Cost cost = mCompleteEconomicPolicy.getCostOfAction(
action.actionId, userId, pkgName);
- totalCostPerSecond += cost;
+ totalCostPerSecond += cost.price;
}
if (totalCostPerSecond == 0) {
return FOREVER_MS;
}
- return mAgent.getBalanceLocked(userId, pkgName) * 1000 / totalCostPerSecond;
+ final long minBalance = Math.min(
+ mAgent.getBalanceLocked(userId, pkgName),
+ mScribe.getRemainingConsumableNarcsLocked());
+ return minBalance * 1000 / totalCostPerSecond;
}
}
@@ -1085,10 +1066,20 @@ public class InternalResourceService extends SystemService {
private void updateEconomicPolicy() {
synchronized (mLock) {
+ final long initialLimit =
+ mCompleteEconomicPolicy.getInitialSatiatedConsumptionLimit();
+ final long hardLimit = mCompleteEconomicPolicy.getHardSatiatedConsumptionLimit();
mCompleteEconomicPolicy.tearDown();
mCompleteEconomicPolicy = new CompleteEconomicPolicy(InternalResourceService.this);
if (mIsEnabled && mBootPhase >= PHASE_SYSTEM_SERVICES_READY) {
mCompleteEconomicPolicy.setup();
+ if (initialLimit != mCompleteEconomicPolicy.getInitialSatiatedConsumptionLimit()
+ || hardLimit
+ != mCompleteEconomicPolicy.getHardSatiatedConsumptionLimit()) {
+ // Reset the consumption limit since several factors may have changed.
+ mScribe.setConsumptionLimitLocked(
+ mCompleteEconomicPolicy.getInitialSatiatedConsumptionLimit());
+ }
mAgent.onPricingChangedLocked();
}
}
@@ -1110,18 +1101,23 @@ public class InternalResourceService extends SystemService {
pw.print("Current battery level: ");
pw.println(mCurrentBatteryLevel);
- final long maxCirculation = getMaxCirculationLocked();
- pw.print("Max circulation (current/satiated): ");
- pw.print(narcToString(maxCirculation));
+ final long consumptionLimit = getConsumptionLimitLocked();
+ pw.print("Consumption limit (current/initial-satiated/current-satiated): ");
+ pw.print(narcToString(consumptionLimit));
+ pw.print("/");
+ pw.print(narcToString(mCompleteEconomicPolicy.getInitialSatiatedConsumptionLimit()));
pw.print("/");
- pw.println(narcToString(mCompleteEconomicPolicy.getMaxSatiatedCirculation()));
+ pw.println(narcToString(mScribe.getSatiatedConsumptionLimitLocked()));
- final long currentCirculation = mScribe.getNarcsInCirculationLocked();
- pw.print("Current GDP: ");
- pw.print(narcToString(currentCirculation));
+ final long remainingConsumable = mScribe.getRemainingConsumableNarcsLocked();
+ pw.print("Goods remaining: ");
+ pw.print(narcToString(remainingConsumable));
pw.print(" (");
- pw.print(String.format("%.2f", 100f * currentCirculation / maxCirculation));
- pw.println("% of current max)");
+ pw.print(String.format("%.2f", 100f * remainingConsumable / consumptionLimit));
+ pw.println("% of current limit)");
+
+ pw.print("Device wealth: ");
+ pw.println(narcToString(mScribe.getNarcsInCirculationForLoggingLocked()));
pw.println();
pw.print("Exempted apps", mExemptedApps);
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java b/apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java
index 1f8ce26a3fd9..0eddd2223d23 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java
@@ -38,7 +38,8 @@ import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_MIN_START_BA
import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_MIN_START_CTP;
import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_TIMEOUT_PENALTY_BASE_PRICE;
import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_TIMEOUT_PENALTY_CTP;
-import static android.app.tare.EconomyManager.DEFAULT_JS_MAX_CIRCULATION;
+import static android.app.tare.EconomyManager.DEFAULT_JS_HARD_CONSUMPTION_LIMIT;
+import static android.app.tare.EconomyManager.DEFAULT_JS_INITIAL_CONSUMPTION_LIMIT;
import static android.app.tare.EconomyManager.DEFAULT_JS_MAX_SATIATED_BALANCE;
import static android.app.tare.EconomyManager.DEFAULT_JS_MIN_SATIATED_BALANCE_EXEMPTED;
import static android.app.tare.EconomyManager.DEFAULT_JS_MIN_SATIATED_BALANCE_OTHER_APP;
@@ -79,7 +80,8 @@ import static android.app.tare.EconomyManager.KEY_JS_ACTION_JOB_MIN_START_BASE_P
import static android.app.tare.EconomyManager.KEY_JS_ACTION_JOB_MIN_START_CTP;
import static android.app.tare.EconomyManager.KEY_JS_ACTION_JOB_TIMEOUT_PENALTY_BASE_PRICE;
import static android.app.tare.EconomyManager.KEY_JS_ACTION_JOB_TIMEOUT_PENALTY_CTP;
-import static android.app.tare.EconomyManager.KEY_JS_MAX_CIRCULATION;
+import static android.app.tare.EconomyManager.KEY_JS_HARD_CONSUMPTION_LIMIT;
+import static android.app.tare.EconomyManager.KEY_JS_INITIAL_CONSUMPTION_LIMIT;
import static android.app.tare.EconomyManager.KEY_JS_MAX_SATIATED_BALANCE;
import static android.app.tare.EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_EXEMPTED;
import static android.app.tare.EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_OTHER_APP;
@@ -145,7 +147,8 @@ public class JobSchedulerEconomicPolicy extends EconomicPolicy {
private long mMinSatiatedBalanceExempted;
private long mMinSatiatedBalanceOther;
private long mMaxSatiatedBalance;
- private long mMaxSatiatedCirculation;
+ private long mInitialSatiatedConsumptionLimit;
+ private long mHardSatiatedConsumptionLimit;
private final KeyValueListParser mParser = new KeyValueListParser(',');
private final InternalResourceService mInternalResourceService;
@@ -181,8 +184,13 @@ public class JobSchedulerEconomicPolicy extends EconomicPolicy {
}
@Override
- long getMaxSatiatedCirculation() {
- return mMaxSatiatedCirculation;
+ long getInitialSatiatedConsumptionLimit() {
+ return mInitialSatiatedConsumptionLimit;
+ }
+
+ @Override
+ long getHardSatiatedConsumptionLimit() {
+ return mHardSatiatedConsumptionLimit;
}
@NonNull
@@ -221,8 +229,11 @@ public class JobSchedulerEconomicPolicy extends EconomicPolicy {
DEFAULT_JS_MIN_SATIATED_BALANCE_OTHER_APP));
mMaxSatiatedBalance = arcToNarc(mParser.getInt(KEY_JS_MAX_SATIATED_BALANCE,
DEFAULT_JS_MAX_SATIATED_BALANCE));
- mMaxSatiatedCirculation = arcToNarc(mParser.getInt(KEY_JS_MAX_CIRCULATION,
- DEFAULT_JS_MAX_CIRCULATION));
+ mInitialSatiatedConsumptionLimit = arcToNarc(mParser.getInt(
+ KEY_JS_INITIAL_CONSUMPTION_LIMIT, DEFAULT_JS_INITIAL_CONSUMPTION_LIMIT));
+ mHardSatiatedConsumptionLimit = Math.max(mInitialSatiatedConsumptionLimit,
+ arcToNarc(mParser.getInt(
+ KEY_JS_HARD_CONSUMPTION_LIMIT, DEFAULT_JS_HARD_CONSUMPTION_LIMIT)));
mActions.put(ACTION_JOB_MAX_START, new Action(ACTION_JOB_MAX_START,
arcToNarc(mParser.getInt(KEY_JS_ACTION_JOB_MAX_START_CTP,
@@ -332,7 +343,11 @@ public class JobSchedulerEconomicPolicy extends EconomicPolicy {
pw.print("Other", narcToString(mMinSatiatedBalanceOther)).println();
pw.decreaseIndent();
pw.print("Max satiated balance", narcToString(mMaxSatiatedBalance)).println();
- pw.print("Max satiated circulation", narcToString(mMaxSatiatedCirculation)).println();
+ pw.print("Consumption limits: [");
+ pw.print(narcToString(mInitialSatiatedConsumptionLimit));
+ pw.print(", ");
+ pw.print(narcToString(mHardSatiatedConsumptionLimit));
+ pw.println("]");
pw.println();
pw.println("Actions:");
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/Ledger.java b/apex/jobscheduler/service/java/com/android/server/tare/Ledger.java
index f4917ad82761..dfdc20a32999 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/Ledger.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/Ledger.java
@@ -41,14 +41,16 @@ class Ledger {
@Nullable
public final String tag;
public final long delta;
+ public final long ctp;
Transaction(long startTimeMs, long endTimeMs,
- int eventId, @Nullable String tag, long delta) {
+ int eventId, @Nullable String tag, long delta, long ctp) {
this.startTimeMs = startTimeMs;
this.endTimeMs = endTimeMs;
this.eventId = eventId;
this.tag = tag;
this.delta = delta;
+ this.ctp = ctp;
}
}
@@ -144,7 +146,10 @@ class Ledger {
pw.print(")");
}
pw.print(" --> ");
- pw.println(narcToString(transaction.delta));
+ pw.print(narcToString(transaction.delta));
+ pw.print(" (ctp=");
+ pw.print(narcToString(transaction.ctp));
+ pw.println(")");
}
}
}
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/PowerSaveModeModifier.java b/apex/jobscheduler/service/java/com/android/server/tare/PowerSaveModeModifier.java
index 764a3a8b1ed9..4aaa9f449b4f 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/PowerSaveModeModifier.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/PowerSaveModeModifier.java
@@ -17,27 +17,48 @@
package com.android.server.tare;
import android.annotation.NonNull;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.os.PowerManager;
+import android.os.SystemClock;
import android.util.IndentingPrintWriter;
+import android.util.Log;
+import android.util.Slog;
/** Modifier that makes things more expensive in adaptive and full battery saver are active. */
class PowerSaveModeModifier extends Modifier {
+ private static final String TAG = "TARE-" + PowerSaveModeModifier.class.getSimpleName();
+ private static final boolean DEBUG = InternalResourceService.DEBUG
+ || Log.isLoggable(TAG, Log.DEBUG);
+
private final InternalResourceService mIrs;
- private final PowerManager mPowerManager;
+ private final PowerSaveModeTracker mPowerSaveModeTracker;
PowerSaveModeModifier(@NonNull InternalResourceService irs) {
super();
mIrs = irs;
- mPowerManager = irs.getContext().getSystemService(PowerManager.class);
+ mPowerSaveModeTracker = new PowerSaveModeTracker();
+ }
+
+ @Override
+ public void setup() {
+ mPowerSaveModeTracker.startTracking(mIrs.getContext());
+ }
+
+ @Override
+ public void tearDown() {
+ mPowerSaveModeTracker.stopTracking(mIrs.getContext());
}
@Override
long getModifiedCostToProduce(long ctp) {
- if (mPowerManager.isPowerSaveMode()) {
+ if (mPowerSaveModeTracker.mPowerSaveModeEnabled) {
return (long) (1.5 * ctp);
}
// TODO: get adaptive power save mode
- if (mPowerManager.isPowerSaveMode()) {
+ if (mPowerSaveModeTracker.mPowerSaveModeEnabled) {
return (long) (1.25 * ctp);
}
return ctp;
@@ -46,6 +67,45 @@ class PowerSaveModeModifier extends Modifier {
@Override
void dump(IndentingPrintWriter pw) {
pw.print("power save=");
- pw.println(mPowerManager.isPowerSaveMode());
+ pw.println(mPowerSaveModeTracker.mPowerSaveModeEnabled);
+ }
+
+ // TODO: migrate to relying on PowerSaveState and ServiceType.TARE
+ private final class PowerSaveModeTracker extends BroadcastReceiver {
+ private final PowerManager mPowerManager;
+ private volatile boolean mPowerSaveModeEnabled;
+
+ private PowerSaveModeTracker() {
+ mPowerManager = mIrs.getContext().getSystemService(PowerManager.class);
+ }
+
+ public void startTracking(@NonNull Context context) {
+ final IntentFilter filter = new IntentFilter();
+ filter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED);
+ context.registerReceiver(this, filter);
+
+ // Initialise tracker state.
+ mPowerSaveModeEnabled = mPowerManager.isPowerSaveMode();
+ }
+
+ public void stopTracking(@NonNull Context context) {
+ context.unregisterReceiver(this);
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final String action = intent.getAction();
+ if (PowerManager.ACTION_POWER_SAVE_MODE_CHANGED.equals(action)) {
+ final boolean enabled = mPowerManager.isPowerSaveMode();
+ if (DEBUG) {
+ Slog.d(TAG, "Power save mode changed to " + enabled
+ + ", fired @ " + SystemClock.elapsedRealtime());
+ }
+ if (mPowerSaveModeEnabled != enabled) {
+ mPowerSaveModeEnabled = enabled;
+ mIrs.onDeviceStateChanged();
+ }
+ }
+ }
}
}
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/Scribe.java b/apex/jobscheduler/service/java/com/android/server/tare/Scribe.java
index 86968ef23a49..866211024169 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/Scribe.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/Scribe.java
@@ -73,6 +73,7 @@ public class Scribe {
private static final String XML_TAG_TRANSACTION = "transaction";
private static final String XML_TAG_USER = "user";
+ private static final String XML_ATTR_CTP = "ctp";
private static final String XML_ATTR_DELTA = "delta";
private static final String XML_ATTR_EVENT_ID = "eventId";
private static final String XML_ATTR_TAG = "tag";
@@ -83,6 +84,8 @@ public class Scribe {
private static final String XML_ATTR_USER_ID = "userId";
private static final String XML_ATTR_VERSION = "version";
private static final String XML_ATTR_LAST_RECLAMATION_TIME = "lastReclamationTime";
+ private static final String XML_ATTR_REMAINING_CONSUMABLE_NARCS = "remainingConsumableNarcs";
+ private static final String XML_ATTR_CONSUMPTION_LIMIT = "consumptionLimit";
/** Version of the file schema. */
private static final int STATE_FILE_VERSION = 0;
@@ -95,7 +98,9 @@ public class Scribe {
@GuardedBy("mIrs.getLock()")
private long mLastReclamationTime;
@GuardedBy("mIrs.getLock()")
- private long mNarcsInCirculation;
+ private long mSatiatedConsumptionLimit;
+ @GuardedBy("mIrs.getLock()")
+ private long mRemainingConsumableNarcs;
@GuardedBy("mIrs.getLock()")
private final SparseArrayMap<String, Ledger> mLedgers = new SparseArrayMap<>();
@@ -117,10 +122,10 @@ public class Scribe {
}
@GuardedBy("mIrs.getLock()")
- void adjustNarcsInCirculationLocked(long delta) {
+ void adjustRemainingConsumableNarcsLocked(long delta) {
if (delta != 0) {
// No point doing any work if the change is 0.
- mNarcsInCirculation += delta;
+ mRemainingConsumableNarcs += delta;
postWrite();
}
}
@@ -132,6 +137,11 @@ public class Scribe {
}
@GuardedBy("mIrs.getLock()")
+ long getSatiatedConsumptionLimitLocked() {
+ return mSatiatedConsumptionLimit;
+ }
+
+ @GuardedBy("mIrs.getLock()")
long getLastReclamationTimeLocked() {
return mLastReclamationTime;
}
@@ -153,19 +163,37 @@ public class Scribe {
return mLedgers;
}
- /** Returns the total amount of narcs currently allocated to apps. */
+ /**
+ * Returns the sum of credits granted to all apps on the system. This is expensive so don't
+ * call it for normal operation.
+ */
+ @GuardedBy("mIrs.getLock()")
+ long getNarcsInCirculationForLoggingLocked() {
+ long sum = 0;
+ for (int uIdx = mLedgers.numMaps() - 1; uIdx >= 0; --uIdx) {
+ for (int pIdx = mLedgers.numElementsForKeyAt(uIdx) - 1; pIdx >= 0; --pIdx) {
+ sum += mLedgers.valueAt(uIdx, pIdx).getCurrentBalance();
+ }
+ }
+ return sum;
+ }
+
+ /** Returns the total amount of narcs that remain to be consumed. */
@GuardedBy("mIrs.getLock()")
- long getNarcsInCirculationLocked() {
- return mNarcsInCirculation;
+ long getRemainingConsumableNarcsLocked() {
+ return mRemainingConsumableNarcs;
}
@GuardedBy("mIrs.getLock()")
void loadFromDiskLocked() {
mLedgers.clear();
- mNarcsInCirculation = 0;
if (!recordExists()) {
+ mSatiatedConsumptionLimit = mIrs.getInitialSatiatedConsumptionLimitLocked();
+ mRemainingConsumableNarcs = mIrs.getConsumptionLimitLocked();
return;
}
+ mSatiatedConsumptionLimit = 0;
+ mRemainingConsumableNarcs = 0;
final SparseArray<ArraySet<String>> installedPackagesPerUser = new SparseArray<>();
final List<PackageInfo> installedPackages = mIrs.getInstalledPackages();
@@ -222,6 +250,13 @@ public class Scribe {
case XML_TAG_HIGH_LEVEL_STATE:
mLastReclamationTime =
parser.getAttributeLong(null, XML_ATTR_LAST_RECLAMATION_TIME);
+ mSatiatedConsumptionLimit =
+ parser.getAttributeLong(null, XML_ATTR_CONSUMPTION_LIMIT,
+ mIrs.getInitialSatiatedConsumptionLimitLocked());
+ final long consumptionLimit = mIrs.getConsumptionLimitLocked();
+ mRemainingConsumableNarcs = Math.min(consumptionLimit,
+ parser.getAttributeLong(null, XML_ATTR_REMAINING_CONSUMABLE_NARCS,
+ consumptionLimit));
break;
case XML_TAG_USER:
earliestEndTime = Math.min(earliestEndTime,
@@ -249,6 +284,18 @@ public class Scribe {
}
@GuardedBy("mIrs.getLock()")
+ void setConsumptionLimitLocked(long limit) {
+ if (mRemainingConsumableNarcs > limit) {
+ mRemainingConsumableNarcs = limit;
+ } else if (limit > mSatiatedConsumptionLimit) {
+ final long diff = mSatiatedConsumptionLimit - mRemainingConsumableNarcs;
+ mRemainingConsumableNarcs = (limit - diff);
+ }
+ mSatiatedConsumptionLimit = limit;
+ postWrite();
+ }
+
+ @GuardedBy("mIrs.getLock()")
void setLastReclamationTimeLocked(long time) {
mLastReclamationTime = time;
postWrite();
@@ -259,7 +306,8 @@ public class Scribe {
TareHandlerThread.getHandler().removeCallbacks(mCleanRunnable);
TareHandlerThread.getHandler().removeCallbacks(mWriteRunnable);
mLedgers.clear();
- mNarcsInCirculation = 0;
+ mRemainingConsumableNarcs = 0;
+ mSatiatedConsumptionLimit = 0;
mLastReclamationTime = 0;
}
@@ -339,13 +387,14 @@ public class Scribe {
final long endTime = parser.getAttributeLong(null, XML_ATTR_END_TIME);
final int eventId = parser.getAttributeInt(null, XML_ATTR_EVENT_ID);
final long delta = parser.getAttributeLong(null, XML_ATTR_DELTA);
+ final long ctp = parser.getAttributeLong(null, XML_ATTR_CTP);
if (endTime <= endTimeCutoff) {
if (DEBUG) {
Slog.d(TAG, "Skipping event because it's too old.");
}
continue;
}
- transactions.add(new Ledger.Transaction(startTime, endTime, eventId, tag, delta));
+ transactions.add(new Ledger.Transaction(startTime, endTime, eventId, tag, delta, ctp));
}
if (!isInstalled) {
@@ -395,7 +444,6 @@ public class Scribe {
final Ledger ledger = ledgerData.second;
if (ledger != null) {
mLedgers.add(curUser, ledgerData.first, ledger);
- mNarcsInCirculation += Math.max(0, ledger.getCurrentBalance());
final Ledger.Transaction transaction = ledger.getEarliestTransaction();
if (transaction != null) {
earliestEndTime = Math.min(earliestEndTime, transaction.endTimeMs);
@@ -442,6 +490,9 @@ public class Scribe {
out.startTag(null, XML_TAG_HIGH_LEVEL_STATE);
out.attributeLong(null, XML_ATTR_LAST_RECLAMATION_TIME, mLastReclamationTime);
+ out.attributeLong(null, XML_ATTR_CONSUMPTION_LIMIT, mSatiatedConsumptionLimit);
+ out.attributeLong(null, XML_ATTR_REMAINING_CONSUMABLE_NARCS,
+ mRemainingConsumableNarcs);
out.endTag(null, XML_TAG_HIGH_LEVEL_STATE);
for (int uIdx = mLedgers.numMaps() - 1; uIdx >= 0; --uIdx) {
@@ -505,6 +556,7 @@ public class Scribe {
out.attribute(null, XML_ATTR_TAG, transaction.tag);
}
out.attributeLong(null, XML_ATTR_DELTA, transaction.delta);
+ out.attributeLong(null, XML_ATTR_CTP, transaction.ctp);
out.endTag(null, XML_TAG_TRANSACTION);
}
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 b843dcaaf680..cae6cdcf8f1f 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
+++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
@@ -25,6 +25,7 @@ import static android.app.usage.UsageStatsManager.REASON_MAIN_TIMEOUT;
import static android.app.usage.UsageStatsManager.REASON_MAIN_USAGE;
import static android.app.usage.UsageStatsManager.REASON_SUB_DEFAULT_APP_UPDATE;
import static android.app.usage.UsageStatsManager.REASON_SUB_FORCED_SYSTEM_FLAG_BUGGY;
+import static android.app.usage.UsageStatsManager.REASON_SUB_FORCED_USER_FLAG_INTERACTION;
import static android.app.usage.UsageStatsManager.REASON_SUB_MASK;
import static android.app.usage.UsageStatsManager.REASON_SUB_PREDICTED_RESTORED;
import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_ACTIVE_TIMEOUT;
@@ -1581,7 +1582,11 @@ public class AppStandbyController
// Only user force can bypass the delay restriction. If the user forced the
// app into the RESTRICTED bucket, then a toast confirming the action
// shouldn't be surprising.
- if (Build.IS_DEBUGGABLE) {
+ // Exclude REASON_SUB_FORCED_USER_FLAG_INTERACTION since the RESTRICTED bucket
+ // isn't directly visible in that flow.
+ if (Build.IS_DEBUGGABLE
+ && (reason & REASON_SUB_MASK)
+ != REASON_SUB_FORCED_USER_FLAG_INTERACTION) {
Toast.makeText(mContext,
// Since AppStandbyController sits low in the lock hierarchy,
// make sure not to call out with the lock held.
diff --git a/api/Android.bp b/api/Android.bp
index 66c7823ed416..40472b7e2d69 100644
--- a/api/Android.bp
+++ b/api/Android.bp
@@ -93,7 +93,6 @@ metalava_cmd = "$(location metalava)"
// Silence reflection warnings. See b/168689341
metalava_cmd += " -J--add-opens=java.base/java.util=ALL-UNNAMED "
metalava_cmd += " --quiet --no-banner --format=v2 "
-metalava_cmd += " --hide ChangedThrows "
genrule {
name: "current-api-xml",
@@ -113,7 +112,7 @@ combined_apis {
"framework-appsearch",
"framework-bluetooth",
"framework-connectivity",
- "framework-connectivity-tiramisu",
+ "framework-connectivity-t",
"framework-graphics",
"framework-media",
"framework-mediaprovider",
diff --git a/cmds/idmap2/tests/IdmapTests.cpp b/cmds/idmap2/tests/IdmapTests.cpp
index 9c6402a6e36e..738b9cf237c9 100644
--- a/cmds/idmap2/tests/IdmapTests.cpp
+++ b/cmds/idmap2/tests/IdmapTests.cpp
@@ -37,7 +37,6 @@
#include "idmap2/Idmap.h"
#include "idmap2/LogInfo.h"
-using android::Res_value;
using ::testing::NotNull;
using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags;
diff --git a/cmds/idmap2/tests/ResourceMappingTests.cpp b/cmds/idmap2/tests/ResourceMappingTests.cpp
index 5a1d808af06f..32b3d1326d92 100644
--- a/cmds/idmap2/tests/ResourceMappingTests.cpp
+++ b/cmds/idmap2/tests/ResourceMappingTests.cpp
@@ -29,8 +29,6 @@
#include "idmap2/LogInfo.h"
#include "idmap2/ResourceMapping.h"
-using android::Res_value;
-
using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags;
namespace android::idmap2 {
diff --git a/core/api/current.txt b/core/api/current.txt
index 56415ec14564..f86c56273bfa 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -189,6 +189,7 @@ package android {
field public static final String START_VIEW_APP_FEATURES = "android.permission.START_VIEW_APP_FEATURES";
field public static final String START_VIEW_PERMISSION_USAGE = "android.permission.START_VIEW_PERMISSION_USAGE";
field public static final String STATUS_BAR = "android.permission.STATUS_BAR";
+ field public static final String SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE = "android.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE";
field public static final String SYSTEM_ALERT_WINDOW = "android.permission.SYSTEM_ALERT_WINDOW";
field public static final String TRANSMIT_IR = "android.permission.TRANSMIT_IR";
field public static final String UNINSTALL_SHORTCUT = "com.android.launcher.permission.UNINSTALL_SHORTCUT";
@@ -5559,11 +5560,11 @@ package android.app {
public final class GameState implements android.os.Parcelable {
ctor public GameState(boolean, int);
- ctor public GameState(boolean, int, @Nullable String, @NonNull android.os.Bundle);
+ ctor public GameState(boolean, int, int, int);
method public int describeContents();
- method @Nullable public String getDescription();
- method @NonNull public android.os.Bundle getMetadata();
+ method public int getLabel();
method public int getMode();
+ method public int getQuality();
method public boolean isLoading();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.app.GameState> CREATOR;
@@ -5677,6 +5678,7 @@ package android.app {
}
public class KeyguardManager {
+ method @RequiresPermission(android.Manifest.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE) public void addKeyguardLockedStateListener(@NonNull java.util.concurrent.Executor, @NonNull android.app.KeyguardManager.KeyguardLockedStateListener);
method @Deprecated public android.content.Intent createConfirmDeviceCredentialIntent(CharSequence, CharSequence);
method @Deprecated @RequiresPermission(android.Manifest.permission.DISABLE_KEYGUARD) public void exitKeyguardSecurely(android.app.KeyguardManager.OnKeyguardExitResult);
method @Deprecated public boolean inKeyguardRestrictedInputMode();
@@ -5685,6 +5687,7 @@ package android.app {
method public boolean isKeyguardLocked();
method public boolean isKeyguardSecure();
method @Deprecated public android.app.KeyguardManager.KeyguardLock newKeyguardLock(String);
+ method @RequiresPermission(android.Manifest.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE) public void removeKeyguardLockedStateListener(@NonNull android.app.KeyguardManager.KeyguardLockedStateListener);
method public void requestDismissKeyguard(@NonNull android.app.Activity, @Nullable android.app.KeyguardManager.KeyguardDismissCallback);
}
@@ -5700,6 +5703,10 @@ package android.app {
method @Deprecated @RequiresPermission(android.Manifest.permission.DISABLE_KEYGUARD) public void reenableKeyguard();
}
+ @java.lang.FunctionalInterface public static interface KeyguardManager.KeyguardLockedStateListener {
+ method public void onKeyguardLockedStateChanged(boolean);
+ }
+
@Deprecated public static interface KeyguardManager.OnKeyguardExitResult {
method @Deprecated public void onKeyguardExitResult(boolean);
}
@@ -7800,7 +7807,6 @@ package android.app.admin {
}
public final class DevicePolicyResources {
- ctor public DevicePolicyResources();
}
public static final class DevicePolicyResources.Drawables {
@@ -14391,6 +14397,8 @@ package android.graphics {
method @NonNull @Size(min=3) public abstract float[] fromXyz(@NonNull @Size(min=3) float[]);
method @NonNull public static android.graphics.ColorSpace get(@NonNull android.graphics.ColorSpace.Named);
method @IntRange(from=1, to=4) public int getComponentCount();
+ method public int getDataSpace();
+ method @Nullable public static android.graphics.ColorSpace getFromDataSpace(int);
method @IntRange(from=android.graphics.ColorSpace.MIN_ID, to=android.graphics.ColorSpace.MAX_ID) public int getId();
method public abstract float getMaxValue(@IntRange(from=0, to=3) int);
method public abstract float getMinValue(@IntRange(from=0, to=3) int);
@@ -17390,7 +17398,7 @@ package android.hardware.camera2 {
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> REQUEST_MAX_NUM_OUTPUT_RAW;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> REQUEST_PARTIAL_RESULT_COUNT;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Byte> REQUEST_PIPELINE_MAX_DEPTH;
- field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> REQUEST_RECOMMENDED_TEN_BIT_DYNAMIC_RANGE_PROFILE;
+ field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Long> REQUEST_RECOMMENDED_TEN_BIT_DYNAMIC_RANGE_PROFILE;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Float> SCALER_AVAILABLE_MAX_DIGITAL_ZOOM;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> SCALER_AVAILABLE_ROTATE_AND_CROP_MODES;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> SCALER_AVAILABLE_STREAM_USE_CASES;
@@ -18110,22 +18118,23 @@ package android.hardware.camera2.params {
}
public final class DynamicRangeProfiles {
- ctor public DynamicRangeProfiles(@NonNull int[]);
- method @NonNull public java.util.Set<java.lang.Integer> getProfileCaptureRequestConstraints(int);
- method @NonNull public java.util.Set<java.lang.Integer> getSupportedProfiles();
- field public static final int DOLBY_VISION_10B_HDR_OEM = 64; // 0x40
- field public static final int DOLBY_VISION_10B_HDR_OEM_PO = 128; // 0x80
- field public static final int DOLBY_VISION_10B_HDR_REF = 16; // 0x10
- field public static final int DOLBY_VISION_10B_HDR_REF_PO = 32; // 0x20
- field public static final int DOLBY_VISION_8B_HDR_OEM = 1024; // 0x400
- field public static final int DOLBY_VISION_8B_HDR_OEM_PO = 2048; // 0x800
- field public static final int DOLBY_VISION_8B_HDR_REF = 256; // 0x100
- field public static final int DOLBY_VISION_8B_HDR_REF_PO = 512; // 0x200
- field public static final int HDR10 = 4; // 0x4
- field public static final int HDR10_PLUS = 8; // 0x8
- field public static final int HLG10 = 2; // 0x2
- field public static final int PUBLIC_MAX = 4096; // 0x1000
- field public static final int STANDARD = 1; // 0x1
+ ctor public DynamicRangeProfiles(@NonNull long[]);
+ method @NonNull public java.util.Set<java.lang.Long> getProfileCaptureRequestConstraints(long);
+ method @NonNull public java.util.Set<java.lang.Long> getSupportedProfiles();
+ method public boolean isExtraLatencyPresent(long);
+ field public static final long DOLBY_VISION_10B_HDR_OEM = 64L; // 0x40L
+ field public static final long DOLBY_VISION_10B_HDR_OEM_PO = 128L; // 0x80L
+ field public static final long DOLBY_VISION_10B_HDR_REF = 16L; // 0x10L
+ field public static final long DOLBY_VISION_10B_HDR_REF_PO = 32L; // 0x20L
+ field public static final long DOLBY_VISION_8B_HDR_OEM = 1024L; // 0x400L
+ field public static final long DOLBY_VISION_8B_HDR_OEM_PO = 2048L; // 0x800L
+ field public static final long DOLBY_VISION_8B_HDR_REF = 256L; // 0x100L
+ field public static final long DOLBY_VISION_8B_HDR_REF_PO = 512L; // 0x200L
+ field public static final long HDR10 = 4L; // 0x4L
+ field public static final long HDR10_PLUS = 8L; // 0x8L
+ field public static final long HLG10 = 2L; // 0x2L
+ field public static final long PUBLIC_MAX = 4096L; // 0x1000L
+ field public static final long STANDARD = 1L; // 0x1L
}
public final class ExtensionSessionConfiguration {
@@ -18232,7 +18241,7 @@ package android.hardware.camera2.params {
method @NonNull public static java.util.Collection<android.hardware.camera2.params.OutputConfiguration> createInstancesForMultiResolutionOutput(@NonNull android.hardware.camera2.MultiResolutionImageReader);
method public int describeContents();
method public void enableSurfaceSharing();
- method public int getDynamicRangeProfile();
+ method public long getDynamicRangeProfile();
method public int getMaxSharedSurfaceCount();
method public int getMirrorMode();
method public int getStreamUseCase();
@@ -18242,7 +18251,7 @@ package android.hardware.camera2.params {
method public int getTimestampBase();
method public void removeSensorPixelModeUsed(int);
method public void removeSurface(@NonNull android.view.Surface);
- method public void setDynamicRangeProfile(int);
+ method public void setDynamicRangeProfile(long);
method public void setMirrorMode(int);
method public void setPhysicalCameraId(@Nullable String);
method public void setStreamUseCase(int);
@@ -27446,7 +27455,6 @@ package android.nfc.cardemulation {
field public static final String CATEGORY_PAYMENT = "payment";
field public static final String EXTRA_CATEGORY = "category";
field public static final String EXTRA_SERVICE_COMPONENT = "component";
- field public static final String EXTRA_USERID = "android.nfc.cardemulation.extra.USERID";
field public static final int SELECTION_MODE_ALWAYS_ASK = 1; // 0x1
field public static final int SELECTION_MODE_ASK_IF_CONFLICT = 2; // 0x2
field public static final int SELECTION_MODE_PREFER_DEFAULT = 0; // 0x0
@@ -30686,7 +30694,7 @@ package android.os {
public class BaseBundle {
method public void clear();
method public boolean containsKey(String);
- method @Nullable public Object get(String);
+ method @Deprecated @Nullable public Object get(String);
method public boolean getBoolean(String);
method public boolean getBoolean(String, boolean);
method @Nullable public boolean[] getBooleanArray(@Nullable String);
@@ -30928,16 +30936,21 @@ package android.os {
method public float getFloat(String, float);
method @Nullable public float[] getFloatArray(@Nullable String);
method @Nullable public java.util.ArrayList<java.lang.Integer> getIntegerArrayList(@Nullable String);
- method @Nullable public <T extends android.os.Parcelable> T getParcelable(@Nullable String);
- method @Nullable public android.os.Parcelable[] getParcelableArray(@Nullable String);
- method @Nullable public <T extends android.os.Parcelable> java.util.ArrayList<T> getParcelableArrayList(@Nullable String);
- method @Nullable public java.io.Serializable getSerializable(@Nullable String);
+ method @Deprecated @Nullable public <T extends android.os.Parcelable> T getParcelable(@Nullable String);
+ method @Nullable public <T> T getParcelable(@Nullable String, @NonNull Class<T>);
+ method @Deprecated @Nullable public android.os.Parcelable[] getParcelableArray(@Nullable String);
+ method @Nullable public <T> T[] getParcelableArray(@Nullable String, @NonNull Class<T>);
+ method @Deprecated @Nullable public <T extends android.os.Parcelable> java.util.ArrayList<T> getParcelableArrayList(@Nullable String);
+ method @Nullable public <T> java.util.ArrayList<T> getParcelableArrayList(@Nullable String, @NonNull Class<T>);
+ method @Deprecated @Nullable public java.io.Serializable getSerializable(@Nullable String);
+ method @Nullable public <T extends java.io.Serializable> T getSerializable(@Nullable String, @NonNull Class<T>);
method public short getShort(String);
method public short getShort(String, short);
method @Nullable public short[] getShortArray(@Nullable String);
method @Nullable public android.util.Size getSize(@Nullable String);
method @Nullable public android.util.SizeF getSizeF(@Nullable String);
- method @Nullable public <T extends android.os.Parcelable> android.util.SparseArray<T> getSparseParcelableArray(@Nullable String);
+ method @Deprecated @Nullable public <T extends android.os.Parcelable> android.util.SparseArray<T> getSparseParcelableArray(@Nullable String);
+ method @Nullable public <T> android.util.SparseArray<T> getSparseParcelableArray(@Nullable String, @NonNull Class<T>);
method @Nullable public java.util.ArrayList<java.lang.String> getStringArrayList(@Nullable String);
method public boolean hasFileDescriptors();
method public void putAll(android.os.Bundle);
@@ -31786,7 +31799,7 @@ package android.os {
method public android.os.PowerManager.WakeLock newWakeLock(int, String);
method @RequiresPermission(android.Manifest.permission.REBOOT) public void reboot(@Nullable String);
method public void removeThermalStatusListener(@NonNull android.os.PowerManager.OnThermalStatusChangedListener);
- field public static final int ACQUIRE_CAUSES_WAKEUP = 268435456; // 0x10000000
+ field @Deprecated public static final int ACQUIRE_CAUSES_WAKEUP = 268435456; // 0x10000000
field public static final String ACTION_DEVICE_IDLE_MODE_CHANGED = "android.os.action.DEVICE_IDLE_MODE_CHANGED";
field public static final String ACTION_DEVICE_LIGHT_IDLE_MODE_CHANGED = "android.os.action.LIGHT_DEVICE_IDLE_MODE_CHANGED";
field public static final String ACTION_LOW_POWER_STANDBY_ENABLED_CHANGED = "android.os.action.LOW_POWER_STANDBY_ENABLED_CHANGED";
@@ -35437,8 +35450,8 @@ package android.provider {
field public static final String EXTRA_WIFI_NETWORK_RESULT_LIST = "android.provider.extra.WIFI_NETWORK_RESULT_LIST";
field public static final String INTENT_CATEGORY_USAGE_ACCESS_CONFIG = "android.intent.category.USAGE_ACCESS_CONFIG";
field public static final String METADATA_USAGE_ACCESS_REASON = "android.settings.metadata.USAGE_ACCESS_REASON";
- field public static final String SUPERVISOR_VERIFICATION_SETTING_BIOMETRICS = "supervisor_restricted_biometrics_controller";
- field public static final String SUPERVISOR_VERIFICATION_SETTING_UNKNOWN = "";
+ field public static final int SUPERVISOR_VERIFICATION_SETTING_BIOMETRICS = 1; // 0x1
+ field public static final int SUPERVISOR_VERIFICATION_SETTING_UNKNOWN = 0; // 0x0
}
public static final class Settings.Global extends android.provider.Settings.NameValueTable {
@@ -49287,7 +49300,7 @@ package android.view {
method @NonNull public android.view.SurfaceControl.Transaction setDataSpace(@NonNull android.view.SurfaceControl, int);
method @NonNull public android.view.SurfaceControl.Transaction setFrameRate(@NonNull android.view.SurfaceControl, @FloatRange(from=0.0) float, int);
method @NonNull public android.view.SurfaceControl.Transaction setFrameRate(@NonNull android.view.SurfaceControl, @FloatRange(from=0.0) float, int, int);
- method @NonNull public android.view.SurfaceControl.Transaction setGeometry(@NonNull android.view.SurfaceControl, @Nullable android.graphics.Rect, @Nullable android.graphics.Rect, int);
+ method @Deprecated @NonNull public android.view.SurfaceControl.Transaction setGeometry(@NonNull android.view.SurfaceControl, @Nullable android.graphics.Rect, @Nullable android.graphics.Rect, int);
method @NonNull public android.view.SurfaceControl.Transaction setLayer(@NonNull android.view.SurfaceControl, @IntRange(from=java.lang.Integer.MIN_VALUE, to=java.lang.Integer.MAX_VALUE) int);
method @NonNull public android.view.SurfaceControl.Transaction setOpaque(@NonNull android.view.SurfaceControl, boolean);
method @NonNull public android.view.SurfaceControl.Transaction setPosition(@NonNull android.view.SurfaceControl, float, float);
@@ -53107,7 +53120,7 @@ package android.view.inputmethod {
field public static final int RESULT_SHOWN = 2; // 0x2
field public static final int RESULT_UNCHANGED_HIDDEN = 1; // 0x1
field public static final int RESULT_UNCHANGED_SHOWN = 0; // 0x0
- field public static final int SHOW_FORCED = 2; // 0x2
+ field @Deprecated public static final int SHOW_FORCED = 2; // 0x2
field public static final int SHOW_IMPLICIT = 1; // 0x1
}
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index 594f46b6694b..7ec239d681d0 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -413,6 +413,7 @@ package android.os.storage {
public class StorageManager {
method public long computeStorageCacheBytes(@NonNull java.io.File);
+ method @Nullable public String getCloudMediaProvider();
method public void notifyAppIoBlocked(@NonNull java.util.UUID, int, int, int);
method public void notifyAppIoResumed(@NonNull java.util.UUID, int, int, int);
method public void setCloudMediaProvider(@Nullable String);
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 2c29343a00e0..d209432eb804 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -1075,7 +1075,7 @@ package android.app.admin {
}
public class DevicePolicyManager {
- method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public int checkProvisioningPreCondition(@NonNull String, @NonNull String);
+ method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public int checkProvisioningPrecondition(@NonNull String, @NonNull String);
method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public android.os.UserHandle createAndProvisionManagedProfile(@NonNull android.app.admin.ManagedProfileProvisioningParams) throws android.app.admin.ProvisioningException;
method @Nullable public android.content.Intent createProvisioningIntentFromNfcIntent(@NonNull android.content.Intent);
method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public boolean getBluetoothContactSharingDisabled(@NonNull android.os.UserHandle);
@@ -1128,21 +1128,6 @@ package android.app.admin {
field public static final String ACTION_SET_PROFILE_OWNER = "android.app.action.SET_PROFILE_OWNER";
field @Deprecated public static final String ACTION_STATE_USER_SETUP_COMPLETE = "android.app.action.STATE_USER_SETUP_COMPLETE";
field @RequiresPermission(android.Manifest.permission.LAUNCH_DEVICE_MANAGER_SETUP) public static final String ACTION_UPDATE_DEVICE_MANAGEMENT_ROLE_HOLDER = "android.app.action.UPDATE_DEVICE_MANAGEMENT_ROLE_HOLDER";
- field public static final int CODE_ACCOUNTS_NOT_EMPTY = 6; // 0x6
- field public static final int CODE_CANNOT_ADD_MANAGED_PROFILE = 11; // 0xb
- field public static final int CODE_DEVICE_ADMIN_NOT_SUPPORTED = 13; // 0xd
- field public static final int CODE_HAS_DEVICE_OWNER = 1; // 0x1
- field public static final int CODE_HAS_PAIRED = 8; // 0x8
- field public static final int CODE_MANAGED_USERS_NOT_SUPPORTED = 9; // 0x9
- field public static final int CODE_NONSYSTEM_USER_EXISTS = 5; // 0x5
- field public static final int CODE_NOT_SYSTEM_USER = 7; // 0x7
- field public static final int CODE_OK = 0; // 0x0
- field public static final int CODE_PROVISIONING_NOT_ALLOWED_FOR_NON_DEVELOPER_USERS = 15; // 0xf
- field public static final int CODE_SYSTEM_USER = 10; // 0xa
- field public static final int CODE_UNKNOWN_ERROR = -1; // 0xffffffff
- field public static final int CODE_USER_HAS_PROFILE_OWNER = 2; // 0x2
- field public static final int CODE_USER_NOT_RUNNING = 3; // 0x3
- field public static final int CODE_USER_SETUP_COMPLETED = 4; // 0x4
field public static final String EXTRA_FORCE_UPDATE_ROLE_HOLDER = "android.app.extra.FORCE_UPDATE_ROLE_HOLDER";
field public static final String EXTRA_LOST_MODE_LOCATION = "android.app.extra.LOST_MODE_LOCATION";
field public static final String EXTRA_PROFILE_OWNER_NAME = "android.app.extra.PROFILE_OWNER_NAME";
@@ -1185,6 +1170,21 @@ package android.app.admin {
field public static final int STATE_USER_SETUP_FINALIZED = 3; // 0x3
field public static final int STATE_USER_SETUP_INCOMPLETE = 1; // 0x1
field public static final int STATE_USER_UNMANAGED = 0; // 0x0
+ field public static final int STATUS_ACCOUNTS_NOT_EMPTY = 6; // 0x6
+ field public static final int STATUS_CANNOT_ADD_MANAGED_PROFILE = 11; // 0xb
+ field public static final int STATUS_DEVICE_ADMIN_NOT_SUPPORTED = 13; // 0xd
+ field public static final int STATUS_HAS_DEVICE_OWNER = 1; // 0x1
+ field public static final int STATUS_HAS_PAIRED = 8; // 0x8
+ field public static final int STATUS_MANAGED_USERS_NOT_SUPPORTED = 9; // 0x9
+ field public static final int STATUS_NONSYSTEM_USER_EXISTS = 5; // 0x5
+ field public static final int STATUS_NOT_SYSTEM_USER = 7; // 0x7
+ field public static final int STATUS_OK = 0; // 0x0
+ field public static final int STATUS_PROVISIONING_NOT_ALLOWED_FOR_NON_DEVELOPER_USERS = 15; // 0xf
+ field public static final int STATUS_SYSTEM_USER = 10; // 0xa
+ field public static final int STATUS_UNKNOWN_ERROR = -1; // 0xffffffff
+ field public static final int STATUS_USER_HAS_PROFILE_OWNER = 2; // 0x2
+ field public static final int STATUS_USER_NOT_RUNNING = 3; // 0x3
+ field public static final int STATUS_USER_SETUP_COMPLETED = 4; // 0x4
}
public static final class DevicePolicyResources.Strings {
@@ -2172,23 +2172,41 @@ package android.app.smartspace {
field @NonNull public static final android.os.Parcelable.Creator<android.app.smartspace.SmartspaceTarget> CREATOR;
field public static final int FEATURE_ALARM = 7; // 0x7
field public static final int FEATURE_BEDTIME_ROUTINE = 16; // 0x10
+ field public static final int FEATURE_BLAZE_BUILD_PROGRESS = 40; // 0x28
field public static final int FEATURE_CALENDAR = 2; // 0x2
field public static final int FEATURE_COMMUTE_TIME = 3; // 0x3
field public static final int FEATURE_CONSENT = 11; // 0xb
+ field public static final int FEATURE_CROSS_DEVICE_TIMER = 32; // 0x20
+ field public static final int FEATURE_DOORBELL = 30; // 0x1e
+ field public static final int FEATURE_DRIVING_MODE = 26; // 0x1a
+ field public static final int FEATURE_EARTHQUAKE_ALERT = 38; // 0x26
+ field public static final int FEATURE_EARTHQUAKE_OCCURRED = 41; // 0x29
field public static final int FEATURE_ETA_MONITORING = 18; // 0x12
field public static final int FEATURE_FITNESS_TRACKING = 17; // 0x11
+ field public static final int FEATURE_FLASHLIGHT = 28; // 0x1c
field public static final int FEATURE_FLIGHT = 4; // 0x4
+ field public static final int FEATURE_GAS_STATION_PAYMENT = 24; // 0x18
+ field public static final int FEATURE_HOLIDAY_ALARM = 34; // 0x22
field public static final int FEATURE_LOYALTY_CARD = 14; // 0xe
field public static final int FEATURE_MEDIA = 15; // 0xf
+ field public static final int FEATURE_MEDIA_HEADS_UP = 36; // 0x24
+ field public static final int FEATURE_MEDIA_RESUME = 31; // 0x1f
field public static final int FEATURE_MISSED_CALL = 19; // 0x13
field public static final int FEATURE_ONBOARDING = 8; // 0x8
field public static final int FEATURE_PACKAGE_TRACKING = 20; // 0x14
+ field public static final int FEATURE_PAIRED_DEVICE_STATE = 25; // 0x19
field public static final int FEATURE_REMINDER = 6; // 0x6
+ field public static final int FEATURE_SAFETY_CHECK = 35; // 0x23
+ field public static final int FEATURE_SEVERE_WEATHER_ALERT = 33; // 0x21
field public static final int FEATURE_SHOPPING_LIST = 13; // 0xd
+ field public static final int FEATURE_SLEEP_SUMMARY = 27; // 0x1b
field public static final int FEATURE_SPORTS = 9; // 0x9
+ field public static final int FEATURE_STEP_COUNTING = 37; // 0x25
+ field public static final int FEATURE_STEP_DATE = 39; // 0x27
field public static final int FEATURE_STOCK_PRICE_CHANGE = 12; // 0xc
field public static final int FEATURE_STOPWATCH = 22; // 0x16
field public static final int FEATURE_TIMER = 21; // 0x15
+ field public static final int FEATURE_TIME_TO_LEAVE = 29; // 0x1d
field public static final int FEATURE_TIPS = 5; // 0x5
field public static final int FEATURE_UNDEFINED = 0; // 0x0
field public static final int FEATURE_UPCOMING_ALARM = 23; // 0x17
@@ -2254,22 +2272,12 @@ package android.app.smartspace.uitemplatedata {
public class BaseTemplateData implements android.os.Parcelable {
method public int describeContents();
method public int getLayoutWeight();
- method @Nullable public android.app.smartspace.uitemplatedata.BaseTemplateData.SubItemLoggingInfo getPrimaryLoggingInfo();
- method @Nullable public android.app.smartspace.uitemplatedata.TapAction getPrimaryTapAction();
- method @Nullable public android.app.smartspace.uitemplatedata.Icon getSubtitleIcon();
- method @Nullable public android.app.smartspace.uitemplatedata.Text getSubtitleText();
- method @Nullable public android.app.smartspace.uitemplatedata.Text getSupplementalAlarmText();
- method @Nullable public android.app.smartspace.uitemplatedata.Icon getSupplementalIcon();
- method @Nullable public android.app.smartspace.uitemplatedata.BaseTemplateData.SubItemLoggingInfo getSupplementalLoggingInfo();
- method @Nullable public android.app.smartspace.uitemplatedata.Icon getSupplementalSubtitleIcon();
- method @Nullable public android.app.smartspace.uitemplatedata.BaseTemplateData.SubItemLoggingInfo getSupplementalSubtitleLoggingInfo();
- method @Nullable public android.app.smartspace.uitemplatedata.TapAction getSupplementalSubtitleTapAction();
- method @Nullable public android.app.smartspace.uitemplatedata.Text getSupplementalSubtitleText();
- method @Nullable public android.app.smartspace.uitemplatedata.TapAction getSupplementalTapAction();
- method @Nullable public android.app.smartspace.uitemplatedata.Text getSupplementalText();
+ method @Nullable public android.app.smartspace.uitemplatedata.BaseTemplateData.SubItemInfo getPrimaryItem();
+ method @Nullable public android.app.smartspace.uitemplatedata.BaseTemplateData.SubItemInfo getSubtitleItem();
+ method @Nullable public android.app.smartspace.uitemplatedata.BaseTemplateData.SubItemInfo getSubtitleSupplementalItem();
+ method @Nullable public android.app.smartspace.uitemplatedata.BaseTemplateData.SubItemInfo getSupplementalAlarmItem();
+ method @Nullable public android.app.smartspace.uitemplatedata.BaseTemplateData.SubItemInfo getSupplementalLineItem();
method public int getTemplateType();
- method @Nullable public android.app.smartspace.uitemplatedata.Icon getTitleIcon();
- method @Nullable public android.app.smartspace.uitemplatedata.Text getTitleText();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.app.smartspace.uitemplatedata.BaseTemplateData> CREATOR;
}
@@ -2278,27 +2286,37 @@ package android.app.smartspace.uitemplatedata {
ctor public BaseTemplateData.Builder(int);
method @NonNull public android.app.smartspace.uitemplatedata.BaseTemplateData build();
method @NonNull public android.app.smartspace.uitemplatedata.BaseTemplateData.Builder setLayoutWeight(int);
- method @NonNull public android.app.smartspace.uitemplatedata.BaseTemplateData.Builder setPrimaryLoggingInfo(@NonNull android.app.smartspace.uitemplatedata.BaseTemplateData.SubItemLoggingInfo);
- method @NonNull public android.app.smartspace.uitemplatedata.BaseTemplateData.Builder setPrimaryTapAction(@NonNull android.app.smartspace.uitemplatedata.TapAction);
- method @NonNull public android.app.smartspace.uitemplatedata.BaseTemplateData.Builder setSubtitleIcon(@NonNull android.app.smartspace.uitemplatedata.Icon);
- method @NonNull public android.app.smartspace.uitemplatedata.BaseTemplateData.Builder setSubtitleText(@NonNull android.app.smartspace.uitemplatedata.Text);
- method @NonNull public android.app.smartspace.uitemplatedata.BaseTemplateData.Builder setSupplementalAlarmText(@NonNull android.app.smartspace.uitemplatedata.Text);
- method @NonNull public android.app.smartspace.uitemplatedata.BaseTemplateData.Builder setSupplementalIcon(@NonNull android.app.smartspace.uitemplatedata.Icon);
- method @NonNull public android.app.smartspace.uitemplatedata.BaseTemplateData.Builder setSupplementalLoggingInfo(@NonNull android.app.smartspace.uitemplatedata.BaseTemplateData.SubItemLoggingInfo);
- method @NonNull public android.app.smartspace.uitemplatedata.BaseTemplateData.Builder setSupplementalSubtitleIcon(@NonNull android.app.smartspace.uitemplatedata.Icon);
- method @NonNull public android.app.smartspace.uitemplatedata.BaseTemplateData.Builder setSupplementalSubtitleLoggingInfo(@NonNull android.app.smartspace.uitemplatedata.BaseTemplateData.SubItemLoggingInfo);
- method @NonNull public android.app.smartspace.uitemplatedata.BaseTemplateData.Builder setSupplementalSubtitleTapAction(@NonNull android.app.smartspace.uitemplatedata.TapAction);
- method @NonNull public android.app.smartspace.uitemplatedata.BaseTemplateData.Builder setSupplementalSubtitleText(@NonNull android.app.smartspace.uitemplatedata.Text);
- method @NonNull public android.app.smartspace.uitemplatedata.BaseTemplateData.Builder setSupplementalTapAction(@NonNull android.app.smartspace.uitemplatedata.TapAction);
- method @NonNull public android.app.smartspace.uitemplatedata.BaseTemplateData.Builder setSupplementalText(@NonNull android.app.smartspace.uitemplatedata.Text);
- method @NonNull public android.app.smartspace.uitemplatedata.BaseTemplateData.Builder setTitleIcon(@NonNull android.app.smartspace.uitemplatedata.Icon);
- method @NonNull public android.app.smartspace.uitemplatedata.BaseTemplateData.Builder setTitleText(@NonNull android.app.smartspace.uitemplatedata.Text);
+ method @NonNull public android.app.smartspace.uitemplatedata.BaseTemplateData.Builder setPrimaryItem(@NonNull android.app.smartspace.uitemplatedata.BaseTemplateData.SubItemInfo);
+ method @NonNull public android.app.smartspace.uitemplatedata.BaseTemplateData.Builder setSubtitleItem(@NonNull android.app.smartspace.uitemplatedata.BaseTemplateData.SubItemInfo);
+ method @NonNull public android.app.smartspace.uitemplatedata.BaseTemplateData.Builder setSubtitleSupplementalItem(@NonNull android.app.smartspace.uitemplatedata.BaseTemplateData.SubItemInfo);
+ method @NonNull public android.app.smartspace.uitemplatedata.BaseTemplateData.Builder setSupplementalAlarmItem(@NonNull android.app.smartspace.uitemplatedata.BaseTemplateData.SubItemInfo);
+ method @NonNull public android.app.smartspace.uitemplatedata.BaseTemplateData.Builder setSupplementalLineItem(@NonNull android.app.smartspace.uitemplatedata.BaseTemplateData.SubItemInfo);
+ }
+
+ public static final class BaseTemplateData.SubItemInfo implements android.os.Parcelable {
+ method public int describeContents();
+ method @Nullable public android.app.smartspace.uitemplatedata.Icon getIcon();
+ method @Nullable public android.app.smartspace.uitemplatedata.BaseTemplateData.SubItemLoggingInfo getLoggingInfo();
+ method @Nullable public android.app.smartspace.uitemplatedata.TapAction getTapAction();
+ method @Nullable public android.app.smartspace.uitemplatedata.Text getText();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.app.smartspace.uitemplatedata.BaseTemplateData.SubItemInfo> CREATOR;
+ }
+
+ public static final class BaseTemplateData.SubItemInfo.Builder {
+ ctor public BaseTemplateData.SubItemInfo.Builder();
+ method @NonNull public android.app.smartspace.uitemplatedata.BaseTemplateData.SubItemInfo build();
+ method @NonNull public android.app.smartspace.uitemplatedata.BaseTemplateData.SubItemInfo.Builder setIcon(@NonNull android.app.smartspace.uitemplatedata.Icon);
+ method @NonNull public android.app.smartspace.uitemplatedata.BaseTemplateData.SubItemInfo.Builder setLoggingInfo(@NonNull android.app.smartspace.uitemplatedata.BaseTemplateData.SubItemLoggingInfo);
+ method @NonNull public android.app.smartspace.uitemplatedata.BaseTemplateData.SubItemInfo.Builder setTapAction(@NonNull android.app.smartspace.uitemplatedata.TapAction);
+ method @NonNull public android.app.smartspace.uitemplatedata.BaseTemplateData.SubItemInfo.Builder setText(@NonNull android.app.smartspace.uitemplatedata.Text);
}
public static final class BaseTemplateData.SubItemLoggingInfo implements android.os.Parcelable {
method public int describeContents();
method public int getFeatureType();
method public int getInstanceId();
+ method @Nullable public CharSequence getPackageName();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.app.smartspace.uitemplatedata.BaseTemplateData.SubItemLoggingInfo> CREATOR;
}
@@ -2306,6 +2324,7 @@ package android.app.smartspace.uitemplatedata {
public static final class BaseTemplateData.SubItemLoggingInfo.Builder {
ctor public BaseTemplateData.SubItemLoggingInfo.Builder(int, int);
method @NonNull public android.app.smartspace.uitemplatedata.BaseTemplateData.SubItemLoggingInfo build();
+ method @NonNull public android.app.smartspace.uitemplatedata.BaseTemplateData.SubItemLoggingInfo.Builder setPackageName(@NonNull CharSequence);
}
public final class CarouselTemplateData extends android.app.smartspace.uitemplatedata.BaseTemplateData {
@@ -2793,7 +2812,7 @@ package android.companion.virtual {
method public void addActivityListener(@NonNull android.companion.virtual.VirtualDeviceManager.ActivityListener, @NonNull java.util.concurrent.Executor);
method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void close();
method @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.companion.virtual.audio.VirtualAudioDevice createVirtualAudioDevice(@NonNull android.hardware.display.VirtualDisplay, @Nullable java.util.concurrent.Executor, @Nullable android.companion.virtual.audio.VirtualAudioDevice.AudioConfigurationChangeCallback);
- method @Nullable public android.hardware.display.VirtualDisplay createVirtualDisplay(@IntRange(from=1) int, @IntRange(from=1) int, @IntRange(from=1) int, @Nullable android.view.Surface, int, @NonNull java.util.concurrent.Executor, @Nullable android.hardware.display.VirtualDisplay.Callback);
+ method @Nullable public android.hardware.display.VirtualDisplay createVirtualDisplay(@IntRange(from=1) int, @IntRange(from=1) int, @IntRange(from=1) int, @Nullable android.view.Surface, int, @Nullable java.util.concurrent.Executor, @Nullable android.hardware.display.VirtualDisplay.Callback);
method @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.hardware.input.VirtualKeyboard createVirtualKeyboard(@NonNull android.hardware.display.VirtualDisplay, @NonNull String, int, int);
method @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.hardware.input.VirtualMouse createVirtualMouse(@NonNull android.hardware.display.VirtualDisplay, @NonNull String, int, int);
method @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.hardware.input.VirtualTouchscreen createVirtualTouchscreen(@NonNull android.hardware.display.VirtualDisplay, @NonNull String, int, int);
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 29b8248dc8f2..e64392b3b0c2 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -525,7 +525,6 @@ package android.app.admin {
method @RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_ADMINS) public void setNextOperationSafety(int, int);
field public static final String ACTION_DATA_SHARING_RESTRICTION_APPLIED = "android.app.action.DATA_SHARING_RESTRICTION_APPLIED";
field public static final String ACTION_DEVICE_POLICY_CONSTANTS_CHANGED = "android.app.action.DEVICE_POLICY_CONSTANTS_CHANGED";
- field @Deprecated public static final int CODE_SPLIT_SYSTEM_USER_DEVICE_SYSTEM_USER = 14; // 0xe
field public static final int DEVICE_OWNER_TYPE_DEFAULT = 0; // 0x0
field public static final int DEVICE_OWNER_TYPE_FINANCED = 1; // 0x1
field public static final int OPERATION_CLEAR_APPLICATION_USER_DATA = 23; // 0x17
@@ -569,6 +568,7 @@ package android.app.admin {
field public static final int OPERATION_SWITCH_USER = 2; // 0x2
field public static final int OPERATION_UNINSTALL_CA_CERT = 40; // 0x28
field public static final int OPERATION_WIPE_DATA = 8; // 0x8
+ field @Deprecated public static final int STATUS_SPLIT_SYSTEM_USER_DEVICE_SYSTEM_USER = 14; // 0xe
}
public static final class SecurityLog.SecurityEvent implements android.os.Parcelable {
@@ -2347,6 +2347,8 @@ package android.service.dreams {
public abstract class DreamOverlayService extends android.app.Service {
ctor public DreamOverlayService();
+ method @Nullable public final CharSequence getDreamLabel();
+ method public final boolean isPreviewMode();
method @Nullable public final android.os.IBinder onBind(@NonNull android.content.Intent);
method public abstract void onStartDream(@NonNull android.view.WindowManager.LayoutParams);
method public final void requestExit();
@@ -3082,6 +3084,7 @@ package android.view.inputmethod {
method @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public java.util.List<android.view.inputmethod.InputMethodInfo> getInputMethodListAsUser(int);
method public boolean hasActiveInputConnection(@Nullable android.view.View);
method public boolean isInputMethodPickerShown();
+ field public static final long CLEAR_SHOW_FORCED_FLAG_WHEN_LEAVING = 214016041L; // 0xcc1a029L
}
}
diff --git a/core/java/android/app/BroadcastOptions.java b/core/java/android/app/BroadcastOptions.java
index e31a56690408..8b3c9fa73798 100644
--- a/core/java/android/app/BroadcastOptions.java
+++ b/core/java/android/app/BroadcastOptions.java
@@ -523,6 +523,8 @@ public class BroadcastOptions extends ComponentOptions {
* Sets whether events (such as posting a notification) originating from an app after it
* receives the broadcast while in background should be recorded as responses to the broadcast.
*
+ * <p> Note that this will only be considered when sending explicit broadcast intents.
+ *
* @param id ID to be used for the response events corresponding to this broadcast. If the
* value is {@code 0} (default), then response events will not be recorded. Otherwise,
* they will be recorded with the ID provided.
diff --git a/core/java/android/app/GameState.java b/core/java/android/app/GameState.java
index 979dd34e8276..fe6e5543a662 100644
--- a/core/java/android/app/GameState.java
+++ b/core/java/android/app/GameState.java
@@ -18,8 +18,6 @@ package android.app;
import android.annotation.IntDef;
import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
@@ -88,12 +86,11 @@ public final class GameState implements Parcelable {
// One of the states listed above.
private final @GameStateMode int mMode;
- // This is a game specific description. For example can be level or scene name.
- private final @Nullable String mDescription;
+ // A developer-supplied enum, e.g. to indicate level or scene.
+ private final int mLabel;
- // This contains any other game specific parameters not covered by the fields above. It can be
- // quality parameter data, settings, or game modes.
- private final @NonNull Bundle mMetaData;
+ // The developer-supplied enum, e.g. to indicate the current quality level.
+ private final int mQuality;
/**
* Create a GameState with the specified loading status.
@@ -101,29 +98,28 @@ public final class GameState implements Parcelable {
* @param mode The game state mode of type @GameStateMode.
*/
public GameState(boolean isLoading, @GameStateMode int mode) {
- this(isLoading, mode, null, new Bundle());
+ this(isLoading, mode, -1, -1);
}
/**
* Create a GameState with the given state variables.
* @param isLoading Whether the game is in the loading state.
- * @param mode The game state mode of type @GameStateMode.
- * @param description An optional description of the state.
- * @param metaData Optional metadata.
+ * @param mode The game state mode.
+ * @param label An optional developer-supplied enum e.g. for the current level.
+ * @param quality An optional developer-supplied enum, e.g. for the current quality level.
*/
- public GameState(boolean isLoading, @GameStateMode int mode, @Nullable String description,
- @NonNull Bundle metaData) {
+ public GameState(boolean isLoading, @GameStateMode int mode, int label, int quality) {
mIsLoading = isLoading;
mMode = mode;
- mDescription = description;
- mMetaData = metaData;
+ mLabel = label;
+ mQuality = quality;
}
private GameState(Parcel in) {
mIsLoading = in.readBoolean();
mMode = in.readInt();
- mDescription = in.readString();
- mMetaData = in.readBundle();
+ mLabel = in.readInt();
+ mQuality = in.readInt();
}
/**
@@ -141,17 +137,19 @@ public final class GameState implements Parcelable {
}
/**
- * @return The state description, or null if one is not set.
+ * @return The developer-supplied enum, e.g. to indicate level or scene. The default value (if
+ * not supplied) is -1.
*/
- public @Nullable String getDescription() {
- return mDescription;
+ public int getLabel() {
+ return mLabel;
}
/**
- * @return metadata associated with the state.
+ * @return The developer-supplied enum, e.g. to indicate the current quality level. The default
+ * value (if not suplied) is -1.
*/
- public @NonNull Bundle getMetadata() {
- return mMetaData;
+ public int getQuality() {
+ return mQuality;
}
@Override
@@ -163,8 +161,8 @@ public final class GameState implements Parcelable {
public void writeToParcel(@NonNull Parcel parcel, int flags) {
parcel.writeBoolean(mIsLoading);
parcel.writeInt(mMode);
- parcel.writeString(mDescription);
- parcel.writeBundle(mMetaData);
+ parcel.writeInt(mLabel);
+ parcel.writeInt(mQuality);
}
/**
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 7c48a5738e51..fe0edfea59c8 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -510,7 +510,6 @@ interface IActivityManager {
void noteAlarmFinish(in IIntentSender sender, in WorkSource workSource, int sourceUid, in String tag);
@UnsupportedAppUsage
int getPackageProcessState(in String packageName, in String callingPackage);
- void updateDeviceOwner(in String packageName);
// Start of N transactions
// Start Binder transaction tracking for all applications.
diff --git a/core/java/android/app/KeyguardManager.java b/core/java/android/app/KeyguardManager.java
index 99100003e5b3..87ba197d8052 100644
--- a/core/java/android/app/KeyguardManager.java
+++ b/core/java/android/app/KeyguardManager.java
@@ -52,6 +52,7 @@ import android.view.WindowManager.LayoutParams;
import android.view.WindowManagerGlobal;
import com.android.internal.policy.IKeyguardDismissCallback;
+import com.android.internal.policy.IKeyguardLockedStateListener;
import com.android.internal.util.Preconditions;
import com.android.internal.widget.IWeakEscrowTokenActivatedListener;
import com.android.internal.widget.IWeakEscrowTokenRemovedListener;
@@ -183,6 +184,10 @@ public class KeyguardManager {
})
@interface LockTypes {}
+ // TODO(b/220379118): register only one binder listener and keep a map of listener to executor.
+ private final ArrayMap<KeyguardLockedStateListener, IKeyguardLockedStateListener>
+ mKeyguardLockedStateListeners = new ArrayMap<>();
+
/**
* Get an intent to prompt the user to confirm credentials (pin, pattern, password or biometrics
* if enrolled) for the current user of the device. The caller is expected to launch this
@@ -534,7 +539,7 @@ public class KeyguardManager {
/**
* Return whether the keyguard is currently locked.
*
- * @return true if keyguard is locked.
+ * @return {@code true} if the keyguard is locked.
*/
public boolean isKeyguardLocked() {
try {
@@ -550,7 +555,7 @@ public class KeyguardManager {
*
* <p>See also {@link #isDeviceSecure()} which ignores SIM locked states.
*
- * @return true if a PIN, pattern or password is set or a SIM card is locked.
+ * @return {@code true} if a PIN, pattern or password is set or a SIM card is locked.
*/
public boolean isKeyguardSecure() {
try {
@@ -565,7 +570,7 @@ public class KeyguardManager {
* keyguard password emergency screen). When in such mode, certain keys,
* such as the Home key and the right soft keys, don't work.
*
- * @return true if in keyguard restricted input mode.
+ * @return {@code true} if in keyguard restricted input mode.
* @deprecated Use {@link #isKeyguardLocked()} instead.
*/
public boolean inKeyguardRestrictedInputMode() {
@@ -576,7 +581,7 @@ public class KeyguardManager {
* Returns whether the device is currently locked and requires a PIN, pattern or
* password to unlock.
*
- * @return true if unlocking the device currently requires a PIN, pattern or
+ * @return {@code true} if unlocking the device currently requires a PIN, pattern or
* password.
*/
public boolean isDeviceLocked() {
@@ -603,7 +608,7 @@ public class KeyguardManager {
*
* <p>See also {@link #isKeyguardSecure} which treats SIM locked states as secure.
*
- * @return true if a PIN, pattern or password was set.
+ * @return {@code true} if a PIN, pattern or password was set.
*/
public boolean isDeviceSecure() {
return isDeviceSecure(mContext.getUserId());
@@ -762,7 +767,7 @@ public class KeyguardManager {
* as the output of String#getBytes
* @param complexity - complexity level imposed by the requester
* as defined in {@code DevicePolicyManager.PasswordComplexity}
- * @return true if the password is valid, false otherwise
+ * @return {@code true} if the password is valid, false otherwise
* @hide
*/
@RequiresPermission(Manifest.permission.SET_INITIAL_LOCK)
@@ -821,7 +826,7 @@ public class KeyguardManager {
* as the output of String#getBytes
* @param complexity - complexity level imposed by the requester
* as defined in {@code DevicePolicyManager.PasswordComplexity}
- * @return true if the lock is successfully set, false otherwise
+ * @return {@code true} if the lock is successfully set, false otherwise
* @hide
*/
@RequiresPermission(Manifest.permission.SET_INITIAL_LOCK)
@@ -903,8 +908,8 @@ public class KeyguardManager {
/**
* Remove a weak escrow token.
*
- * @return true if the given handle refers to a valid weak token previously returned from
- * {@link #addWeakEscrowToken}, whether it's active or not. return false otherwise.
+ * @return {@code true} if the given handle refers to a valid weak token previously returned
+ * from {@link #addWeakEscrowToken}, whether it's active or not. return false otherwise.
* @hide
*/
@RequiresFeature(PackageManager.FEATURE_AUTOMOTIVE)
@@ -944,7 +949,7 @@ public class KeyguardManager {
/**
* Register the given WeakEscrowTokenRemovedListener.
*
- * @return true if the listener is registered successfully, return false otherwise.
+ * @return {@code true} if the listener is registered successfully, return false otherwise.
* @hide
*/
@RequiresFeature(PackageManager.FEATURE_AUTOMOTIVE)
@@ -982,7 +987,7 @@ public class KeyguardManager {
/**
* Unregister the given WeakEscrowTokenRemovedListener.
*
- * @return true if the listener is unregistered successfully, return false otherwise.
+ * @return {@code true} if the listener is unregistered successfully, return false otherwise.
* @hide
*/
@RequiresFeature(PackageManager.FEATURE_AUTOMOTIVE)
@@ -1076,4 +1081,61 @@ public class KeyguardManager {
throw new IllegalArgumentException("Unknown lock type " + lockType);
}
}
+
+ /**
+ * Listener for keyguard locked state changes.
+ */
+ @FunctionalInterface
+ public interface KeyguardLockedStateListener {
+ /**
+ * Callback function that executes when the keyguard locked state changes.
+ */
+ void onKeyguardLockedStateChanged(boolean isKeyguardLocked);
+ }
+
+ /**
+ * Registers a listener to execute when the keyguard visibility changes.
+ *
+ * @param listener The listener to add to receive keyguard visibility changes.
+ */
+ @RequiresPermission(Manifest.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE)
+ public void addKeyguardLockedStateListener(@NonNull @CallbackExecutor Executor executor,
+ @NonNull KeyguardLockedStateListener listener) {
+ synchronized (mKeyguardLockedStateListeners) {
+ try {
+ final IKeyguardLockedStateListener innerListener =
+ new IKeyguardLockedStateListener.Stub() {
+ @Override
+ public void onKeyguardLockedStateChanged(boolean isKeyguardLocked) {
+ executor.execute(
+ () -> listener.onKeyguardLockedStateChanged(isKeyguardLocked));
+ }
+ };
+ mWM.addKeyguardLockedStateListener(innerListener);
+ mKeyguardLockedStateListeners.put(listener, innerListener);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ /**
+ * Unregisters a listener that executes when the keyguard visibility changes.
+ */
+ @RequiresPermission(Manifest.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE)
+ public void removeKeyguardLockedStateListener(@NonNull KeyguardLockedStateListener listener) {
+ synchronized (mKeyguardLockedStateListeners) {
+ IKeyguardLockedStateListener innerListener = mKeyguardLockedStateListeners.get(
+ listener);
+ if (innerListener == null) {
+ return;
+ }
+ try {
+ mWM.removeKeyguardLockedStateListener(innerListener);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ mKeyguardLockedStateListeners.remove(listener);
+ }
+ }
}
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index 77c7c6f80f0e..99a523a0e18a 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -1377,18 +1377,16 @@ public final class LoadedApk {
Slog.wtf(TAG, "App instance already created for package=" + mPackageName
+ " instance=" + cached);
}
- // TODO Return the cached one, unless it's for the wrong user?
+ // TODO Return the cached one, unles it's for the wrong user?
// For now, we just add WTF checks.
}
}
Application app = null;
- // Temporarily disable per-process app class to investigate b/185177290
-// final String myProcessName = Process.myProcessName();
-// String appClass = mApplicationInfo.getCustomApplicationClassNameForProcess(
-// myProcessName);
- String appClass = mApplicationInfo.className;
+ final String myProcessName = Process.myProcessName();
+ String appClass = mApplicationInfo.getCustomApplicationClassNameForProcess(
+ myProcessName);
if (forceDefaultAppClass || (appClass == null)) {
appClass = "android.app.Application";
}
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 9a7093ec1806..6664110b49c5 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -2523,7 +2523,7 @@ public class DevicePolicyManager {
public @interface UserProvisioningState {}
/**
- * Result code for {@link #checkProvisioningPreCondition}.
+ * Result code for {@link #checkProvisioningPrecondition}.
*
* <p>Unknown error code returned for {@link #ACTION_PROVISION_MANAGED_DEVICE},
* {@link #ACTION_PROVISION_MANAGED_PROFILE} and {@link #ACTION_PROVISION_MANAGED_USER}.
@@ -2531,10 +2531,10 @@ public class DevicePolicyManager {
* @hide
*/
@SystemApi
- public static final int CODE_UNKNOWN_ERROR = -1;
+ public static final int STATUS_UNKNOWN_ERROR = -1;
/**
- * Result code for {@link #checkProvisioningPreCondition}.
+ * Result code for {@link #checkProvisioningPrecondition}.
*
* <p>Returned for {@link #ACTION_PROVISION_MANAGED_DEVICE},
* {@link #ACTION_PROVISION_MANAGED_PROFILE} and {@link #ACTION_PROVISION_MANAGED_USER}
@@ -2543,10 +2543,10 @@ public class DevicePolicyManager {
* @hide
*/
@SystemApi
- public static final int CODE_OK = 0;
+ public static final int STATUS_OK = 0;
/**
- * Result code for {@link #checkProvisioningPreCondition}.
+ * Result code for {@link #checkProvisioningPrecondition}.
*
* <p>Returned for {@link #ACTION_PROVISION_MANAGED_DEVICE} when the device already has a
* device owner.
@@ -2554,10 +2554,10 @@ public class DevicePolicyManager {
* @hide
*/
@SystemApi
- public static final int CODE_HAS_DEVICE_OWNER = 1;
+ public static final int STATUS_HAS_DEVICE_OWNER = 1;
/**
- * Result code for {@link #checkProvisioningPreCondition}.
+ * Result code for {@link #checkProvisioningPrecondition}.
*
* <p>Returned for {@link #ACTION_PROVISION_MANAGED_DEVICE} when the user has a profile owner
* and for {@link #ACTION_PROVISION_MANAGED_PROFILE} when the profile owner is already set.
@@ -2565,20 +2565,20 @@ public class DevicePolicyManager {
* @hide
*/
@SystemApi
- public static final int CODE_USER_HAS_PROFILE_OWNER = 2;
+ public static final int STATUS_USER_HAS_PROFILE_OWNER = 2;
/**
- * Result code for {@link #checkProvisioningPreCondition}.
+ * Result code for {@link #checkProvisioningPrecondition}.
*
* <p>Returned for {@link #ACTION_PROVISION_MANAGED_DEVICE} when the user isn't running.
*
* @hide
*/
@SystemApi
- public static final int CODE_USER_NOT_RUNNING = 3;
+ public static final int STATUS_USER_NOT_RUNNING = 3;
/**
- * Result code for {@link #checkProvisioningPreCondition}.
+ * Result code for {@link #checkProvisioningPrecondition}.
*
* <p>Returned for {@link #ACTION_PROVISION_MANAGED_DEVICE} if the device has already been
* setup and for {@link #ACTION_PROVISION_MANAGED_USER} if the user has already been setup.
@@ -2586,7 +2586,7 @@ public class DevicePolicyManager {
* @hide
*/
@SystemApi
- public static final int CODE_USER_SETUP_COMPLETED = 4;
+ public static final int STATUS_USER_SETUP_COMPLETED = 4;
/**
* Code used to indicate that the device also has a user other than the system user.
@@ -2594,7 +2594,7 @@ public class DevicePolicyManager {
* @hide
*/
@SystemApi
- public static final int CODE_NONSYSTEM_USER_EXISTS = 5;
+ public static final int STATUS_NONSYSTEM_USER_EXISTS = 5;
/**
* Code used to indicate that device has an account that prevents provisioning.
@@ -2602,20 +2602,20 @@ public class DevicePolicyManager {
* @hide
*/
@SystemApi
- public static final int CODE_ACCOUNTS_NOT_EMPTY = 6;
+ public static final int STATUS_ACCOUNTS_NOT_EMPTY = 6;
/**
- * Result code for {@link #checkProvisioningPreCondition}.
+ * Result code for {@link #checkProvisioningPrecondition}.
*
* <p>Returned for {@link #ACTION_PROVISION_MANAGED_DEVICE} if the user is not a system user.
*
* @hide
*/
@SystemApi
- public static final int CODE_NOT_SYSTEM_USER = 7;
+ public static final int STATUS_NOT_SYSTEM_USER = 7;
/**
- * Result code for {@link #checkProvisioningPreCondition}.
+ * Result code for {@link #checkProvisioningPrecondition}.
*
* <p>Returned for {@link #ACTION_PROVISION_MANAGED_DEVICE} and
* {@link #ACTION_PROVISION_MANAGED_USER} when the device is a watch and is already paired.
@@ -2623,10 +2623,10 @@ public class DevicePolicyManager {
* @hide
*/
@SystemApi
- public static final int CODE_HAS_PAIRED = 8;
+ public static final int STATUS_HAS_PAIRED = 8;
/**
- * Result code for {@link #checkProvisioningPreCondition}.
+ * Result code for {@link #checkProvisioningPrecondition}.
*
* <p>Returned for {@link #ACTION_PROVISION_MANAGED_PROFILE} and
* {@link #ACTION_PROVISION_MANAGED_USER} on devices which do not support managed users.
@@ -2635,10 +2635,10 @@ public class DevicePolicyManager {
* @hide
*/
@SystemApi
- public static final int CODE_MANAGED_USERS_NOT_SUPPORTED = 9;
+ public static final int STATUS_MANAGED_USERS_NOT_SUPPORTED = 9;
/**
- * Result code for {@link #checkProvisioningPreCondition}.
+ * Result code for {@link #checkProvisioningPrecondition}.
*
* <p>Returned for {@link #ACTION_PROVISION_MANAGED_USER} if the user is a system user and
* for {@link #ACTION_PROVISION_MANAGED_DEVICE} on devices running headless system user mode
@@ -2647,10 +2647,10 @@ public class DevicePolicyManager {
* @hide
*/
@SystemApi
- public static final int CODE_SYSTEM_USER = 10;
+ public static final int STATUS_SYSTEM_USER = 10;
/**
- * Result code for {@link #checkProvisioningPreCondition}.
+ * Result code for {@link #checkProvisioningPrecondition}.
*
* <p>Returned for {@link #ACTION_PROVISION_MANAGED_PROFILE} when the user cannot have more
* managed profiles.
@@ -2658,19 +2658,10 @@ public class DevicePolicyManager {
* @hide
*/
@SystemApi
- public static final int CODE_CANNOT_ADD_MANAGED_PROFILE = 11;
+ public static final int STATUS_CANNOT_ADD_MANAGED_PROFILE = 11;
/**
- * TODO (b/137101239): clean up split system user codes
- *
- * @hide
- * @deprecated not used anymore but can't be removed since it's a @TestApi.
- **/
- @Deprecated
- public static final int CODE_NOT_SYSTEM_USER_SPLIT = 12;
-
- /**
- * Result code for {@link #checkProvisioningPreCondition}.
+ * Result code for {@link #checkProvisioningPrecondition}.
*
* <p>Returned for {@link #ACTION_PROVISION_MANAGED_DEVICE},
* {@link #ACTION_PROVISION_MANAGED_PROFILE} on devices which do not support device
@@ -2679,21 +2670,21 @@ public class DevicePolicyManager {
* @hide
*/
@SystemApi
- public static final int CODE_DEVICE_ADMIN_NOT_SUPPORTED = 13;
+ public static final int STATUS_DEVICE_ADMIN_NOT_SUPPORTED = 13;
/**
* TODO (b/137101239): clean up split system user codes
- * Result code for {@link #checkProvisioningPreCondition}.
+ * Result code for {@link #checkProvisioningPrecondition}.
*
* @hide
* @deprecated not used anymore but can't be removed since it's a @TestApi.
*/
@Deprecated
@TestApi
- public static final int CODE_SPLIT_SYSTEM_USER_DEVICE_SYSTEM_USER = 14;
+ public static final int STATUS_SPLIT_SYSTEM_USER_DEVICE_SYSTEM_USER = 14;
/**
- * Result code for {@link #checkProvisioningPreCondition}.
+ * Result code for {@link #checkProvisioningPrecondition}.
*
* <p>Returned for {@link #ACTION_PROVISION_MANAGED_DEVICE} and
* {@link #ACTION_PROVISION_MANAGED_PROFILE} on devices which do not support provisioning.
@@ -2701,24 +2692,24 @@ public class DevicePolicyManager {
* @hide
*/
@SystemApi
- public static final int CODE_PROVISIONING_NOT_ALLOWED_FOR_NON_DEVELOPER_USERS = 15;
+ public static final int STATUS_PROVISIONING_NOT_ALLOWED_FOR_NON_DEVELOPER_USERS = 15;
/**
- * Result codes for {@link #checkProvisioningPreCondition} indicating all the provisioning pre
+ * Result codes for {@link #checkProvisioningPrecondition} indicating all the provisioning pre
* conditions.
*
* @hide
*/
@Retention(RetentionPolicy.SOURCE)
- @IntDef(prefix = { "CODE_" }, value = {
- CODE_UNKNOWN_ERROR, CODE_OK, CODE_HAS_DEVICE_OWNER, CODE_USER_HAS_PROFILE_OWNER,
- CODE_USER_NOT_RUNNING, CODE_USER_SETUP_COMPLETED, CODE_NOT_SYSTEM_USER, CODE_HAS_PAIRED,
- CODE_MANAGED_USERS_NOT_SUPPORTED, CODE_SYSTEM_USER, CODE_CANNOT_ADD_MANAGED_PROFILE,
- CODE_NOT_SYSTEM_USER_SPLIT, CODE_DEVICE_ADMIN_NOT_SUPPORTED,
- CODE_SPLIT_SYSTEM_USER_DEVICE_SYSTEM_USER,
- CODE_PROVISIONING_NOT_ALLOWED_FOR_NON_DEVELOPER_USERS
+ @IntDef(prefix = { "STATUS_" }, value = {
+ STATUS_UNKNOWN_ERROR, STATUS_OK, STATUS_HAS_DEVICE_OWNER, STATUS_USER_HAS_PROFILE_OWNER,
+ STATUS_USER_NOT_RUNNING, STATUS_USER_SETUP_COMPLETED, STATUS_NOT_SYSTEM_USER,
+ STATUS_HAS_PAIRED, STATUS_MANAGED_USERS_NOT_SUPPORTED, STATUS_SYSTEM_USER,
+ STATUS_CANNOT_ADD_MANAGED_PROFILE, STATUS_DEVICE_ADMIN_NOT_SUPPORTED,
+ STATUS_SPLIT_SYSTEM_USER_DEVICE_SYSTEM_USER,
+ STATUS_PROVISIONING_NOT_ALLOWED_FOR_NON_DEVELOPER_USERS
})
- public @interface ProvisioningPreCondition {}
+ public @interface ProvisioningPrecondition {}
/**
* Disable all configurable SystemUI features during LockTask mode. This includes,
@@ -7922,6 +7913,10 @@ public class DevicePolicyManager {
/**
* Returns the current runtime nearby notification streaming policy set by the device or profile
* owner.
+ * <p>
+ * The caller must be the target user's device owner/profile owner or hold the
+ * {@link android.Manifest.permission#READ_NEARBY_STREAMING_POLICY READ_NEARBY_STREAMING_POLICY}
+ * permission.
*/
@RequiresPermission(
value = android.Manifest.permission.READ_NEARBY_STREAMING_POLICY,
@@ -7965,6 +7960,10 @@ public class DevicePolicyManager {
/**
* Returns the current runtime nearby app streaming policy set by the device or profile owner.
+ * <p>
+ * The caller must be the target user's device owner/profile owner or hold the
+ * {@link android.Manifest.permission#READ_NEARBY_STREAMING_POLICY READ_NEARBY_STREAMING_POLICY}
+ * permission.
*/
@RequiresPermission(
value = android.Manifest.permission.READ_NEARBY_STREAMING_POLICY,
@@ -11996,15 +11995,16 @@ public class DevicePolicyManager {
* {@link #ACTION_PROVISION_MANAGED_PROFILE}
* @param packageName The package of the component that would be set as device, user, or profile
* owner.
- * @return A {@link ProvisioningPreCondition} value indicating whether provisioning is allowed.
+ * @return An int constant value indicating whether provisioning is allowed.
* @hide
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS)
- public @ProvisioningPreCondition int checkProvisioningPreCondition(
+ @ProvisioningPrecondition
+ public int checkProvisioningPrecondition(
@NonNull String action, @NonNull String packageName) {
try {
- return mService.checkProvisioningPreCondition(action, packageName);
+ return mService.checkProvisioningPrecondition(action, packageName);
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
@@ -14689,7 +14689,7 @@ public class DevicePolicyManager {
* {@link ManagedProfileProvisioningParams#getProfileAdminComponentName()} as the profile
* owner.
*
- * <p>The method {@link #checkProvisioningPreCondition} must be returning {@link #CODE_OK}
+ * <p>The method {@link #checkProvisioningPrecondition} must be returning {@link #STATUS_OK}
* before calling this method.
*
* @param provisioningParams Params required to provision a managed profile,
@@ -14733,7 +14733,7 @@ public class DevicePolicyManager {
* Provisions a managed device and sets the {@code deviceAdminComponentName} as the device
* owner.
*
- * <p>The method {@link #checkProvisioningPreCondition} must be returning {@link #CODE_OK}
+ * <p>The method {@link #checkProvisioningPrecondition} must be returning {@link #STATUS_OK}
* before calling this method.
*
* @param provisioningParams Params required to provision a fully managed device,
diff --git a/core/java/android/app/admin/DevicePolicyResources.java b/core/java/android/app/admin/DevicePolicyResources.java
index 7f2e5fde70f1..042e407becb4 100644
--- a/core/java/android/app/admin/DevicePolicyResources.java
+++ b/core/java/android/app/admin/DevicePolicyResources.java
@@ -317,6 +317,8 @@ import java.util.Set;
*/
public final class DevicePolicyResources {
+ private DevicePolicyResources() {}
+
/**
* Resource identifiers used to update device management-related system drawable resources.
*
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 0b9d51f0bdda..9d28ddefda7b 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -373,7 +373,7 @@ interface IDevicePolicyManager {
String permission, int grantState, in RemoteCallback resultReceiver);
int getPermissionGrantState(in ComponentName admin, in String callerPackage, String packageName, String permission);
boolean isProvisioningAllowed(String action, String packageName);
- int checkProvisioningPreCondition(String action, String packageName);
+ int checkProvisioningPrecondition(String action, String packageName);
void setKeepUninstalledPackages(in ComponentName admin, in String callerPackage, in List<String> packageList);
List<String> getKeepUninstalledPackages(in ComponentName admin, in String callerPackage);
boolean isManagedProfile(in ComponentName admin);
diff --git a/core/java/android/app/admin/ProvisioningException.java b/core/java/android/app/admin/ProvisioningException.java
index 57a2c50165a0..a457b5fa3937 100644
--- a/core/java/android/app/admin/ProvisioningException.java
+++ b/core/java/android/app/admin/ProvisioningException.java
@@ -46,7 +46,7 @@ public class ProvisioningException extends AndroidException {
/**
* Service-specific error code for {@link DevicePolicyManager#provisionFullyManagedDevice} and
* {@link DevicePolicyManager#createAndProvisionManagedProfile}:
- * Indicates the call to {@link DevicePolicyManager#checkProvisioningPreCondition} returned an
+ * Indicates the call to {@link DevicePolicyManager#checkProvisioningPrecondition} returned an
* error code.
*/
public static final int ERROR_PRE_CONDITION_FAILED = 1;
diff --git a/core/java/android/app/smartspace/SmartspaceTarget.java b/core/java/android/app/smartspace/SmartspaceTarget.java
index fd7088f48aed..be077435b080 100644
--- a/core/java/android/app/smartspace/SmartspaceTarget.java
+++ b/core/java/android/app/smartspace/SmartspaceTarget.java
@@ -159,6 +159,24 @@ public final class SmartspaceTarget implements Parcelable {
public static final int FEATURE_TIMER = 21;
public static final int FEATURE_STOPWATCH = 22;
public static final int FEATURE_UPCOMING_ALARM = 23;
+ public static final int FEATURE_GAS_STATION_PAYMENT = 24;
+ public static final int FEATURE_PAIRED_DEVICE_STATE = 25;
+ public static final int FEATURE_DRIVING_MODE = 26;
+ public static final int FEATURE_SLEEP_SUMMARY = 27;
+ public static final int FEATURE_FLASHLIGHT = 28;
+ public static final int FEATURE_TIME_TO_LEAVE = 29;
+ public static final int FEATURE_DOORBELL = 30;
+ public static final int FEATURE_MEDIA_RESUME = 31;
+ public static final int FEATURE_CROSS_DEVICE_TIMER = 32;
+ public static final int FEATURE_SEVERE_WEATHER_ALERT = 33;
+ public static final int FEATURE_HOLIDAY_ALARM = 34;
+ public static final int FEATURE_SAFETY_CHECK = 35;
+ public static final int FEATURE_MEDIA_HEADS_UP = 36;
+ public static final int FEATURE_STEP_COUNTING = 37;
+ public static final int FEATURE_EARTHQUAKE_ALERT = 38;
+ public static final int FEATURE_STEP_DATE = 39;
+ public static final int FEATURE_BLAZE_BUILD_PROGRESS = 40;
+ public static final int FEATURE_EARTHQUAKE_OCCURRED = 41;
/**
* @hide
@@ -187,7 +205,25 @@ public final class SmartspaceTarget implements Parcelable {
FEATURE_PACKAGE_TRACKING,
FEATURE_TIMER,
FEATURE_STOPWATCH,
- FEATURE_UPCOMING_ALARM
+ FEATURE_UPCOMING_ALARM,
+ FEATURE_GAS_STATION_PAYMENT,
+ FEATURE_PAIRED_DEVICE_STATE,
+ FEATURE_DRIVING_MODE,
+ FEATURE_SLEEP_SUMMARY,
+ FEATURE_FLASHLIGHT,
+ FEATURE_TIME_TO_LEAVE,
+ FEATURE_DOORBELL,
+ FEATURE_MEDIA_RESUME,
+ FEATURE_CROSS_DEVICE_TIMER,
+ FEATURE_SEVERE_WEATHER_ALERT,
+ FEATURE_HOLIDAY_ALARM,
+ FEATURE_SAFETY_CHECK,
+ FEATURE_MEDIA_HEADS_UP,
+ FEATURE_STEP_COUNTING,
+ FEATURE_EARTHQUAKE_ALERT,
+ FEATURE_STEP_DATE,
+ FEATURE_BLAZE_BUILD_PROGRESS,
+ FEATURE_EARTHQUAKE_OCCURRED
})
@Retention(RetentionPolicy.SOURCE)
public @interface FeatureType {
diff --git a/core/java/android/app/smartspace/uitemplatedata/BaseTemplateData.java b/core/java/android/app/smartspace/uitemplatedata/BaseTemplateData.java
index 584b17605a87..e3cb67a8284c 100644
--- a/core/java/android/app/smartspace/uitemplatedata/BaseTemplateData.java
+++ b/core/java/android/app/smartspace/uitemplatedata/BaseTemplateData.java
@@ -20,10 +20,12 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
+import android.app.smartspace.SmartspaceTarget.FeatureType;
import android.app.smartspace.SmartspaceTarget.UiTemplateType;
import android.app.smartspace.SmartspaceUtils;
import android.os.Parcel;
import android.os.Parcelable;
+import android.text.TextUtils;
import java.util.Objects;
@@ -57,81 +59,39 @@ public class BaseTemplateData implements Parcelable {
/**
* Title text and title icon are shown at the first row. When both are absent, the date view
* will be used, which has its own tap action applied to the title area.
+ *
+ * Primary tap action for the entire card, including the blank spaces, except: 1. When title is
+ * absent, the date view's default tap action is used; 2. Subtitle/Supplemental subtitle uses
+ * its own tap action if being set; 3. Secondary card uses its own tap action if being set.
*/
@Nullable
- private final Text mTitleText;
+ private final SubItemInfo mPrimaryItem;
- @Nullable
- private final Icon mTitleIcon;
/** Subtitle text and icon are shown at the second row. */
@Nullable
- private final Text mSubtitleText;
-
- @Nullable
- private final Icon mSubtitleIcon;
-
- /**
- * Primary tap action for the entire card, including the blank spaces, except: 1. When title is
- * absent, the date view's default tap action is used; 2. Supplemental subtitle uses its own tap
- * action if being set; 3. Secondary card uses its own tap action if being set.
- */
- @Nullable
- private final TapAction mPrimaryTapAction;
-
- /**
- * Primary logging info for the entire card. This will only be used when rendering a sub card
- * within the base card. For the base card itself, BcSmartspaceCardLoggingInfo should be used,
- * which has the display-specific info (e.g. display surface).
- */
- @Nullable
- private final SubItemLoggingInfo mPrimaryLoggingInfo;
+ private final SubItemInfo mSubtitleItem;
/**
* Supplemental subtitle text and icon are shown at the second row following the subtitle text.
* Mainly used for weather info on non-weather card.
*/
@Nullable
- private final Text mSupplementalSubtitleText;
-
- @Nullable
- private final Icon mSupplementalSubtitleIcon;
+ private final SubItemInfo mSubtitleSupplementalItem;
/**
- * Tap action for the supplemental subtitle's text and icon. Uses the primary tap action if
- * not being set.
+ * Supplemental line is shown at the third row.
*/
@Nullable
- private final TapAction mSupplementalSubtitleTapAction;
+ private final SubItemInfo mSupplementalLineItem;
/**
- * Logging info for the supplemental subtitle's are. Uses the primary logging info if not being
- * set.
+ * Supplemental alarm item is specifically used for holiday alarm, which is appended to "next
+ * alarm". This is also shown at the third row, but won't be shown the same time with
+ * mSupplementalLineItem.
*/
@Nullable
- private final SubItemLoggingInfo mSupplementalSubtitleLoggingInfo;
-
- @Nullable
- private final Text mSupplementalText;
-
- @Nullable
- private final Icon mSupplementalIcon;
-
- @Nullable
- private final TapAction mSupplementalTapAction;
-
- /**
- * Logging info for the supplemental line. Uses the primary logging info if not being set.
- */
- @Nullable
- private final SubItemLoggingInfo mSupplementalLoggingInfo;
-
- /**
- * Supplemental alarm text is specifically used for holiday alarm, which is appended to "next
- * alarm".
- */
- @Nullable
- private final Text mSupplementalAlarmText;
+ private final SubItemInfo mSupplementalAlarmItem;
/**
* The layout weight info for the card, which indicates how much space it should occupy on the
@@ -141,21 +101,11 @@ public class BaseTemplateData implements Parcelable {
BaseTemplateData(@NonNull Parcel in) {
mTemplateType = in.readInt();
- mTitleText = in.readTypedObject(Text.CREATOR);
- mTitleIcon = in.readTypedObject(Icon.CREATOR);
- mSubtitleText = in.readTypedObject(Text.CREATOR);
- mSubtitleIcon = in.readTypedObject(Icon.CREATOR);
- mPrimaryTapAction = in.readTypedObject(TapAction.CREATOR);
- mPrimaryLoggingInfo = in.readTypedObject(SubItemLoggingInfo.CREATOR);
- mSupplementalSubtitleText = in.readTypedObject(Text.CREATOR);
- mSupplementalSubtitleIcon = in.readTypedObject(Icon.CREATOR);
- mSupplementalSubtitleTapAction = in.readTypedObject(TapAction.CREATOR);
- mSupplementalSubtitleLoggingInfo = in.readTypedObject(SubItemLoggingInfo.CREATOR);
- mSupplementalText = in.readTypedObject(Text.CREATOR);
- mSupplementalIcon = in.readTypedObject(Icon.CREATOR);
- mSupplementalTapAction = in.readTypedObject(TapAction.CREATOR);
- mSupplementalLoggingInfo = in.readTypedObject(SubItemLoggingInfo.CREATOR);
- mSupplementalAlarmText = in.readTypedObject(Text.CREATOR);
+ mPrimaryItem = in.readTypedObject(SubItemInfo.CREATOR);
+ mSubtitleItem = in.readTypedObject(SubItemInfo.CREATOR);
+ mSubtitleSupplementalItem = in.readTypedObject(SubItemInfo.CREATOR);
+ mSupplementalLineItem = in.readTypedObject(SubItemInfo.CREATOR);
+ mSupplementalAlarmItem = in.readTypedObject(SubItemInfo.CREATOR);
mLayoutWeight = in.readInt();
}
@@ -164,38 +114,18 @@ public class BaseTemplateData implements Parcelable {
* SmartspaceDefaultUiTemplateData.Builder.
*/
BaseTemplateData(@UiTemplateType int templateType,
- @Nullable Text titleText,
- @Nullable Icon titleIcon,
- @Nullable Text subtitleText,
- @Nullable Icon subtitleIcon,
- @Nullable TapAction primaryTapAction,
- @Nullable SubItemLoggingInfo primaryLoggingInfo,
- @Nullable Text supplementalSubtitleText,
- @Nullable Icon supplementalSubtitleIcon,
- @Nullable TapAction supplementalSubtitleTapAction,
- @Nullable SubItemLoggingInfo supplementalSubtitleLoggingInfo,
- @Nullable Text supplementalText,
- @Nullable Icon supplementalIcon,
- @Nullable TapAction supplementalTapAction,
- @Nullable SubItemLoggingInfo supplementalLoggingInfo,
- @Nullable Text supplementalAlarmText,
+ @Nullable SubItemInfo primaryItem,
+ @Nullable SubItemInfo subtitleItem,
+ @Nullable SubItemInfo subtitleSupplementalItem,
+ @Nullable SubItemInfo supplementalLineItem,
+ @Nullable SubItemInfo supplementalAlarmItem,
int layoutWeight) {
mTemplateType = templateType;
- mTitleText = titleText;
- mTitleIcon = titleIcon;
- mSubtitleText = subtitleText;
- mSubtitleIcon = subtitleIcon;
- mPrimaryTapAction = primaryTapAction;
- mPrimaryLoggingInfo = primaryLoggingInfo;
- mSupplementalSubtitleText = supplementalSubtitleText;
- mSupplementalSubtitleIcon = supplementalSubtitleIcon;
- mSupplementalSubtitleTapAction = supplementalSubtitleTapAction;
- mSupplementalSubtitleLoggingInfo = supplementalSubtitleLoggingInfo;
- mSupplementalText = supplementalText;
- mSupplementalIcon = supplementalIcon;
- mSupplementalTapAction = supplementalTapAction;
- mSupplementalLoggingInfo = supplementalLoggingInfo;
- mSupplementalAlarmText = supplementalAlarmText;
+ mPrimaryItem = primaryItem;
+ mSubtitleItem = subtitleItem;
+ mSubtitleSupplementalItem = subtitleSupplementalItem;
+ mSupplementalLineItem = supplementalLineItem;
+ mSupplementalAlarmItem = supplementalAlarmItem;
mLayoutWeight = layoutWeight;
}
@@ -205,94 +135,34 @@ public class BaseTemplateData implements Parcelable {
return mTemplateType;
}
- /** Returns the title's text. */
+ /** Returns the primary item (the first line). */
@Nullable
- public Text getTitleText() {
- return mTitleText;
+ public SubItemInfo getPrimaryItem() {
+ return mPrimaryItem;
}
- /** Returns the title's icon. */
+ /** Returns the subtitle item (the second line). */
@Nullable
- public Icon getTitleIcon() {
- return mTitleIcon;
+ public SubItemInfo getSubtitleItem() {
+ return mSubtitleItem;
}
- /** Returns the subtitle's text. */
+ /** Returns the subtitle's supplemental item (the second line following the subtitle). */
@Nullable
- public Text getSubtitleText() {
- return mSubtitleText;
+ public SubItemInfo getSubtitleSupplementalItem() {
+ return mSubtitleSupplementalItem;
}
- /** Returns the subtitle's icon. */
+ /** Returns the supplemental line item (the 3rd line). */
@Nullable
- public Icon getSubtitleIcon() {
- return mSubtitleIcon;
+ public SubItemInfo getSupplementalLineItem() {
+ return mSupplementalLineItem;
}
- /** Returns the card's primary tap action. */
+ /** Returns the supplemental alarm item (the 3rd line). */
@Nullable
- public TapAction getPrimaryTapAction() {
- return mPrimaryTapAction;
- }
-
- /** Returns the card's primary logging info. */
- @Nullable
- public SubItemLoggingInfo getPrimaryLoggingInfo() {
- return mPrimaryLoggingInfo;
- }
-
- /** Returns the supplemental subtitle's text. */
- @Nullable
- public Text getSupplementalSubtitleText() {
- return mSupplementalSubtitleText;
- }
-
- /** Returns the supplemental subtitle's icon. */
- @Nullable
- public Icon getSupplementalSubtitleIcon() {
- return mSupplementalSubtitleIcon;
- }
-
- /** Returns the supplemental subtitle's tap action. Can be null if not being set. */
- @Nullable
- public TapAction getSupplementalSubtitleTapAction() {
- return mSupplementalSubtitleTapAction;
- }
-
- /** Returns the card's supplemental title's logging info. */
- @Nullable
- public SubItemLoggingInfo getSupplementalSubtitleLoggingInfo() {
- return mSupplementalSubtitleLoggingInfo;
- }
-
- /** Returns the supplemental text. */
- @Nullable
- public Text getSupplementalText() {
- return mSupplementalText;
- }
-
- /** Returns the supplemental icon. */
- @Nullable
- public Icon getSupplementalIcon() {
- return mSupplementalIcon;
- }
-
- /** Returns the supplemental line's tap action. Can be null if not being set. */
- @Nullable
- public TapAction getSupplementalTapAction() {
- return mSupplementalTapAction;
- }
-
- /** Returns the card's supplemental line logging info. */
- @Nullable
- public SubItemLoggingInfo getSupplementalLoggingInfo() {
- return mSupplementalLoggingInfo;
- }
-
- /** Returns the supplemental alarm text. */
- @Nullable
- public Text getSupplementalAlarmText() {
- return mSupplementalAlarmText;
+ public SubItemInfo getSupplementalAlarmItem() {
+ return mSupplementalAlarmItem;
}
/** Returns the card layout weight info. Default weight is 0. */
@@ -325,21 +195,11 @@ public class BaseTemplateData implements Parcelable {
@Override
public void writeToParcel(@NonNull Parcel out, int flags) {
out.writeInt(mTemplateType);
- out.writeTypedObject(mTitleText, flags);
- out.writeTypedObject(mTitleIcon, flags);
- out.writeTypedObject(mSubtitleText, flags);
- out.writeTypedObject(mSubtitleIcon, flags);
- out.writeTypedObject(mPrimaryTapAction, flags);
- out.writeTypedObject(mPrimaryLoggingInfo, flags);
- out.writeTypedObject(mSupplementalSubtitleText, flags);
- out.writeTypedObject(mSupplementalSubtitleIcon, flags);
- out.writeTypedObject(mSupplementalSubtitleTapAction, flags);
- out.writeTypedObject(mSupplementalSubtitleLoggingInfo, flags);
- out.writeTypedObject(mSupplementalText, flags);
- out.writeTypedObject(mSupplementalIcon, flags);
- out.writeTypedObject(mSupplementalTapAction, flags);
- out.writeTypedObject(mSupplementalLoggingInfo, flags);
- out.writeTypedObject(mSupplementalAlarmText, flags);
+ out.writeTypedObject(mPrimaryItem, flags);
+ out.writeTypedObject(mSubtitleItem, flags);
+ out.writeTypedObject(mSubtitleSupplementalItem, flags);
+ out.writeTypedObject(mSupplementalLineItem, flags);
+ out.writeTypedObject(mSupplementalAlarmItem, flags);
out.writeInt(mLayoutWeight);
}
@@ -348,58 +208,29 @@ public class BaseTemplateData implements Parcelable {
if (this == o) return true;
if (!(o instanceof BaseTemplateData)) return false;
BaseTemplateData that = (BaseTemplateData) o;
- return mTemplateType == that.mTemplateType && SmartspaceUtils.isEqual(mTitleText,
- that.mTitleText)
- && Objects.equals(mTitleIcon, that.mTitleIcon)
- && SmartspaceUtils.isEqual(mSubtitleText, that.mSubtitleText)
- && Objects.equals(mSubtitleIcon, that.mSubtitleIcon)
- && Objects.equals(mPrimaryTapAction, that.mPrimaryTapAction)
- && Objects.equals(mPrimaryLoggingInfo, that.mPrimaryLoggingInfo)
- && SmartspaceUtils.isEqual(mSupplementalSubtitleText,
- that.mSupplementalSubtitleText)
- && Objects.equals(mSupplementalSubtitleIcon, that.mSupplementalSubtitleIcon)
- && Objects.equals(mSupplementalSubtitleTapAction,
- that.mSupplementalSubtitleTapAction)
- && Objects.equals(mSupplementalSubtitleLoggingInfo,
- that.mSupplementalSubtitleLoggingInfo)
- && SmartspaceUtils.isEqual(mSupplementalText,
- that.mSupplementalText)
- && Objects.equals(mSupplementalIcon, that.mSupplementalIcon)
- && Objects.equals(mSupplementalTapAction, that.mSupplementalTapAction)
- && Objects.equals(mSupplementalLoggingInfo, that.mSupplementalLoggingInfo)
- && SmartspaceUtils.isEqual(mSupplementalAlarmText, that.mSupplementalAlarmText)
- && mLayoutWeight == that.mLayoutWeight;
+ return mTemplateType == that.mTemplateType && mLayoutWeight == that.mLayoutWeight
+ && Objects.equals(mPrimaryItem, that.mPrimaryItem)
+ && Objects.equals(mSubtitleItem, that.mSubtitleItem)
+ && Objects.equals(mSubtitleSupplementalItem, that.mSubtitleSupplementalItem)
+ && Objects.equals(mSupplementalLineItem, that.mSupplementalLineItem)
+ && Objects.equals(mSupplementalAlarmItem, that.mSupplementalAlarmItem);
}
@Override
public int hashCode() {
- return Objects.hash(mTemplateType, mTitleText, mTitleIcon, mSubtitleText, mSubtitleIcon,
- mPrimaryTapAction, mPrimaryLoggingInfo, mSupplementalSubtitleText,
- mSupplementalSubtitleIcon, mSupplementalSubtitleTapAction,
- mSupplementalSubtitleLoggingInfo,
- mSupplementalText, mSupplementalIcon, mSupplementalTapAction,
- mSupplementalLoggingInfo, mSupplementalAlarmText, mLayoutWeight);
+ return Objects.hash(mTemplateType, mPrimaryItem, mSubtitleItem, mSubtitleSupplementalItem,
+ mSupplementalLineItem, mSupplementalAlarmItem, mLayoutWeight);
}
@Override
public String toString() {
- return "SmartspaceDefaultUiTemplateData{"
+ return "BaseTemplateData{"
+ "mTemplateType=" + mTemplateType
- + ", mTitleText=" + mTitleText
- + ", mTitleIcon=" + mTitleIcon
- + ", mSubtitleText=" + mSubtitleText
- + ", mSubTitleIcon=" + mSubtitleIcon
- + ", mPrimaryTapAction=" + mPrimaryTapAction
- + ", mPrimaryLoggingInfo=" + mPrimaryLoggingInfo
- + ", mSupplementalSubtitleText=" + mSupplementalSubtitleText
- + ", mSupplementalSubtitleIcon=" + mSupplementalSubtitleIcon
- + ", mSupplementalSubtitleTapAction=" + mSupplementalSubtitleTapAction
- + ", mSupplementalSubtitleLoggingInfo=" + mSupplementalSubtitleLoggingInfo
- + ", mSupplementalText=" + mSupplementalText
- + ", mSupplementalIcon=" + mSupplementalIcon
- + ", mSupplementalTapAction=" + mSupplementalTapAction
- + ", mSupplementalLoggingInfo=" + mSupplementalLoggingInfo
- + ", mSupplementalAlarmText=" + mSupplementalAlarmText
+ + ", mPrimaryItem=" + mPrimaryItem
+ + ", mSubtitleItem=" + mSubtitleItem
+ + ", mSubtitleSupplementalItem=" + mSubtitleSupplementalItem
+ + ", mSupplementalLineItem=" + mSupplementalLineItem
+ + ", mSupplementalAlarmItem=" + mSupplementalAlarmItem
+ ", mLayoutWeight=" + mLayoutWeight
+ '}';
}
@@ -414,21 +245,12 @@ public class BaseTemplateData implements Parcelable {
public static class Builder {
@UiTemplateType
private final int mTemplateType;
- private Text mTitleText;
- private Icon mTitleIcon;
- private Text mSubtitleText;
- private Icon mSubtitleIcon;
- private TapAction mPrimaryTapAction;
- private SubItemLoggingInfo mPrimaryLoggingInfo;
- private Text mSupplementalSubtitleText;
- private Icon mSupplementalSubtitleIcon;
- private TapAction mSupplementalSubtitleTapAction;
- private SubItemLoggingInfo mSupplementalSubtitleLoggingInfo;
- private Text mSupplementalText;
- private Icon mSupplementalIcon;
- private TapAction mSupplementalTapAction;
- private SubItemLoggingInfo mSupplementalLoggingInfo;
- private Text mSupplementalAlarmText;
+
+ private SubItemInfo mPrimaryItem;
+ private SubItemInfo mSubtitleItem;
+ private SubItemInfo mSubtitleSupplementalItem;
+ private SubItemInfo mSupplementalLineItem;
+ private SubItemInfo mSupplementalAlarmItem;
private int mLayoutWeight;
/**
@@ -451,106 +273,36 @@ public class BaseTemplateData implements Parcelable {
/** Should ONLY be used by the subclasses */
@Nullable
@SuppressLint("GetterOnBuilder")
- Text getTitleText() {
- return mTitleText;
+ SubItemInfo getPrimaryItem() {
+ return mPrimaryItem;
}
/** Should ONLY be used by the subclasses */
@Nullable
@SuppressLint("GetterOnBuilder")
- Icon getTitleIcon() {
- return mTitleIcon;
+ SubItemInfo getSubtitleItem() {
+ return mSubtitleItem;
}
/** Should ONLY be used by the subclasses */
@Nullable
@SuppressLint("GetterOnBuilder")
- Text getSubtitleText() {
- return mSubtitleText;
+ SubItemInfo getSubtitleSupplemtnalItem() {
+ return mSubtitleSupplementalItem;
}
/** Should ONLY be used by the subclasses */
@Nullable
@SuppressLint("GetterOnBuilder")
- Icon getSubtitleIcon() {
- return mSubtitleIcon;
+ SubItemInfo getSupplementalLineItem() {
+ return mSupplementalLineItem;
}
/** Should ONLY be used by the subclasses */
@Nullable
@SuppressLint("GetterOnBuilder")
- TapAction getPrimaryTapAction() {
- return mPrimaryTapAction;
- }
-
- /** Should ONLY be used by the subclasses */
- @Nullable
- @SuppressLint("GetterOnBuilder")
- SubItemLoggingInfo getPrimaryLoggingInfo() {
- return mPrimaryLoggingInfo;
- }
-
- /** Should ONLY be used by the subclasses */
- @Nullable
- @SuppressLint("GetterOnBuilder")
- Text getSupplementalSubtitleText() {
- return mSupplementalSubtitleText;
- }
-
- /** Should ONLY be used by the subclasses */
- @Nullable
- @SuppressLint("GetterOnBuilder")
- Icon getSupplementalSubtitleIcon() {
- return mSupplementalSubtitleIcon;
- }
-
- /** Should ONLY be used by the subclasses */
- @Nullable
- @SuppressLint("GetterOnBuilder")
- TapAction getSupplementalSubtitleTapAction() {
- return mSupplementalSubtitleTapAction;
- }
-
- /** Should ONLY be used by the subclasses */
- @Nullable
- @SuppressLint("GetterOnBuilder")
- SubItemLoggingInfo getSupplementalSubtitleLoggingInfo() {
- return mSupplementalSubtitleLoggingInfo;
- }
-
- /** Should ONLY be used by the subclasses */
- @Nullable
- @SuppressLint("GetterOnBuilder")
- Text getSupplementalText() {
- return mSupplementalText;
- }
-
- /** Should ONLY be used by the subclasses */
- @Nullable
- @SuppressLint("GetterOnBuilder")
- Icon getSupplementalIcon() {
- return mSupplementalIcon;
- }
-
- /** Should ONLY be used by the subclasses */
- @Nullable
- @SuppressLint("GetterOnBuilder")
- TapAction getSupplementalTapAction() {
- return mSupplementalTapAction;
- }
-
- /** Should ONLY be used by the subclasses */
- @Nullable
- @SuppressLint("GetterOnBuilder")
- SubItemLoggingInfo getSupplementalLoggingInfo() {
- return mSupplementalLoggingInfo;
- }
-
- /** Should ONLY be used by the subclasses */
- @Nullable
- @SuppressLint("GetterOnBuilder")
- Text getSupplementalAlarmText() {
- return mSupplementalAlarmText;
+ SubItemInfo getSupplementalAlarmItem() {
+ return mSupplementalAlarmItem;
}
/** Should ONLY be used by the subclasses */
@@ -560,169 +312,255 @@ public class BaseTemplateData implements Parcelable {
}
/**
- * Sets the card title.
+ * Sets the card primary item.
*/
@NonNull
- public Builder setTitleText(@NonNull Text titleText) {
- mTitleText = titleText;
+ public Builder setPrimaryItem(@NonNull SubItemInfo primaryItem) {
+ mPrimaryItem = primaryItem;
return this;
}
/**
- * Sets the card title icon.
+ * Sets the card subtitle item.
*/
@NonNull
- public Builder setTitleIcon(@NonNull Icon titleIcon) {
- mTitleIcon = titleIcon;
+ public Builder setSubtitleItem(@NonNull SubItemInfo subtitleItem) {
+ mSubtitleItem = subtitleItem;
return this;
}
/**
- * Sets the card subtitle.
+ * Sets the card subtitle's supplemental item.
*/
@NonNull
- public Builder setSubtitleText(@NonNull Text subtitleText) {
- mSubtitleText = subtitleText;
+ public Builder setSubtitleSupplementalItem(@NonNull SubItemInfo subtitleSupplementalItem) {
+ mSubtitleSupplementalItem = subtitleSupplementalItem;
return this;
}
/**
- * Sets the card subtitle icon.
+ * Sets the card supplemental line item.
*/
@NonNull
- public Builder setSubtitleIcon(@NonNull Icon subtitleIcon) {
- mSubtitleIcon = subtitleIcon;
+ public Builder setSupplementalLineItem(@NonNull SubItemInfo supplementalLineItem) {
+ mSupplementalLineItem = supplementalLineItem;
return this;
}
/**
- * Sets the card primary tap action.
+ * Sets the card supplemental alarm item.
*/
@NonNull
- public Builder setPrimaryTapAction(@NonNull TapAction primaryTapAction) {
- mPrimaryTapAction = primaryTapAction;
+ public Builder setSupplementalAlarmItem(@NonNull SubItemInfo supplementalAlarmItem) {
+ mSupplementalAlarmItem = supplementalAlarmItem;
return this;
}
/**
- * Sets the card primary logging info.
+ * Sets the layout weight.
*/
@NonNull
- public Builder setPrimaryLoggingInfo(@NonNull SubItemLoggingInfo primaryLoggingInfo) {
- mPrimaryLoggingInfo = primaryLoggingInfo;
+ public Builder setLayoutWeight(int layoutWeight) {
+ mLayoutWeight = layoutWeight;
return this;
}
/**
- * Sets the supplemental subtitle text.
+ * Builds a new SmartspaceDefaultUiTemplateData instance.
*/
@NonNull
- public Builder setSupplementalSubtitleText(
- @NonNull Text supplementalSubtitleText) {
- mSupplementalSubtitleText = supplementalSubtitleText;
- return this;
+ public BaseTemplateData build() {
+ return new BaseTemplateData(
+ mTemplateType,
+ mPrimaryItem,
+ mSubtitleItem,
+ mSubtitleSupplementalItem,
+ mSupplementalLineItem,
+ mSupplementalAlarmItem,
+ mLayoutWeight);
}
+ }
- /**
- * Sets the supplemental subtitle icon.
- */
- @NonNull
- public Builder setSupplementalSubtitleIcon(
- @NonNull Icon supplementalSubtitleIcon) {
- mSupplementalSubtitleIcon = supplementalSubtitleIcon;
- return this;
+ /**
+ * Holds all the rendering and logging info needed for a sub item within the base card.
+ */
+ public static final class SubItemInfo implements Parcelable {
+
+ /** The text information for the subitem, which will be rendered as it's text content. */
+ @Nullable
+ private final Text mText;
+
+ /** The icon for the subitem, which will be rendered as a drawable in front of the text. */
+ @Nullable
+ private final Icon mIcon;
+
+ /** The tap action for the subitem. */
+ @Nullable
+ private final TapAction mTapAction;
+
+ /** The logging info for the subitem. */
+ @Nullable
+ private final SubItemLoggingInfo mLoggingInfo;
+
+ SubItemInfo(@NonNull Parcel in) {
+ mText = in.readTypedObject(Text.CREATOR);
+ mIcon = in.readTypedObject(Icon.CREATOR);
+ mTapAction = in.readTypedObject(TapAction.CREATOR);
+ mLoggingInfo = in.readTypedObject(SubItemLoggingInfo.CREATOR);
}
- /**
- * Sets the supplemental subtitle tap action. {@code mPrimaryTapAction} will be used if not
- * being set.
- */
- @NonNull
- public Builder setSupplementalSubtitleTapAction(
- @NonNull TapAction supplementalSubtitleTapAction) {
- mSupplementalSubtitleTapAction = supplementalSubtitleTapAction;
- return this;
+ private SubItemInfo(@Nullable Text text,
+ @Nullable Icon icon,
+ @Nullable TapAction tapAction,
+ @Nullable SubItemLoggingInfo loggingInfo) {
+ mText = text;
+ mIcon = icon;
+ mTapAction = tapAction;
+ mLoggingInfo = loggingInfo;
}
- /**
- * Sets the card supplemental title's logging info.
- */
- @NonNull
- public Builder setSupplementalSubtitleLoggingInfo(
- @NonNull SubItemLoggingInfo supplementalSubtitleLoggingInfo) {
- mSupplementalSubtitleLoggingInfo = supplementalSubtitleLoggingInfo;
- return this;
+ /** Returns the subitem's text. */
+ @Nullable
+ public Text getText() {
+ return mText;
}
- /**
- * Sets the supplemental text.
- */
- @NonNull
- public Builder setSupplementalText(@NonNull Text supplementalText) {
- mSupplementalText = supplementalText;
- return this;
+ /** Returns the subitem's icon. */
+ @Nullable
+ public Icon getIcon() {
+ return mIcon;
}
- /**
- * Sets the supplemental icon.
- */
- @NonNull
- public Builder setSupplementalIcon(@NonNull Icon supplementalIcon) {
- mSupplementalIcon = supplementalIcon;
- return this;
+ /** Returns the subitem's tap action. */
+ @Nullable
+ public TapAction getTapAction() {
+ return mTapAction;
}
- /**
- * Sets the supplemental line tap action. {@code mPrimaryTapAction} will be used if not
- * being set.
- */
- @NonNull
- public Builder setSupplementalTapAction(@NonNull TapAction supplementalTapAction) {
- mSupplementalTapAction = supplementalTapAction;
- return this;
+ /** Returns the subitem's logging info. */
+ @Nullable
+ public SubItemLoggingInfo getLoggingInfo() {
+ return mLoggingInfo;
}
/**
- * Sets the card supplemental line's logging info.
+ * @see Parcelable.Creator
*/
@NonNull
- public Builder setSupplementalLoggingInfo(
- @NonNull SubItemLoggingInfo supplementalLoggingInfo) {
- mSupplementalLoggingInfo = supplementalLoggingInfo;
- return this;
+ public static final Creator<SubItemInfo> CREATOR =
+ new Creator<SubItemInfo>() {
+ @Override
+ public SubItemInfo createFromParcel(Parcel in) {
+ return new SubItemInfo(in);
+ }
+
+ @Override
+ public SubItemInfo[] newArray(int size) {
+ return new SubItemInfo[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
}
- /**
- * Sets the supplemental alarm text.
- */
- @NonNull
- public Builder setSupplementalAlarmText(@NonNull Text supplementalAlarmText) {
- mSupplementalAlarmText = supplementalAlarmText;
- return this;
+ @Override
+ public void writeToParcel(@NonNull Parcel out, int flags) {
+ out.writeTypedObject(mText, flags);
+ out.writeTypedObject(mIcon, flags);
+ out.writeTypedObject(mTapAction, flags);
+ out.writeTypedObject(mLoggingInfo, flags);
}
- /**
- * Sets the layout weight.
- */
- @NonNull
- public Builder setLayoutWeight(int layoutWeight) {
- mLayoutWeight = layoutWeight;
- return this;
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof SubItemInfo)) return false;
+ SubItemInfo that = (SubItemInfo) o;
+ return SmartspaceUtils.isEqual(mText, that.mText) && Objects.equals(mIcon,
+ that.mIcon) && Objects.equals(mTapAction, that.mTapAction)
+ && Objects.equals(mLoggingInfo, that.mLoggingInfo);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mText, mIcon, mTapAction, mLoggingInfo);
+ }
+
+ @Override
+ public String toString() {
+ return "SubItemInfo{"
+ + "mText=" + mText
+ + ", mIcon=" + mIcon
+ + ", mTapAction=" + mTapAction
+ + ", mLoggingInfo=" + mLoggingInfo
+ + '}';
}
/**
- * Builds a new SmartspaceDefaultUiTemplateData instance.
+ * A builder for {@link SubItemInfo} object.
+ *
+ * @hide
*/
- @NonNull
- public BaseTemplateData build() {
- return new BaseTemplateData(mTemplateType, mTitleText, mTitleIcon,
- mSubtitleText, mSubtitleIcon, mPrimaryTapAction,
- mPrimaryLoggingInfo,
- mSupplementalSubtitleText, mSupplementalSubtitleIcon,
- mSupplementalSubtitleTapAction, mSupplementalSubtitleLoggingInfo,
- mSupplementalText, mSupplementalIcon,
- mSupplementalTapAction, mSupplementalLoggingInfo,
- mSupplementalAlarmText, mLayoutWeight);
+ @SystemApi
+ public static final class Builder {
+
+ private Text mText;
+ private Icon mIcon;
+ private TapAction mTapAction;
+ private SubItemLoggingInfo mLoggingInfo;
+
+ /**
+ * Sets the sub item's text.
+ */
+ @NonNull
+ public Builder setText(@NonNull Text text) {
+ mText = text;
+ return this;
+ }
+
+ /**
+ * Sets the sub item's icon.
+ */
+ @NonNull
+ public Builder setIcon(@NonNull Icon icon) {
+ mIcon = icon;
+ return this;
+ }
+
+ /**
+ * Sets the sub item's tap action.
+ */
+ @NonNull
+ public Builder setTapAction(@NonNull TapAction tapAction) {
+ mTapAction = tapAction;
+ return this;
+ }
+
+ /**
+ * Sets the sub item's logging info.
+ */
+ @NonNull
+ public Builder setLoggingInfo(@NonNull SubItemLoggingInfo loggingInfo) {
+ mLoggingInfo = loggingInfo;
+ return this;
+ }
+
+ /**
+ * Builds a new {@link SubItemInfo} instance.
+ *
+ * @throws IllegalStateException if all the data field is empty.
+ */
+ @NonNull
+ public SubItemInfo build() {
+ if (SmartspaceUtils.isEmpty(mText) && mIcon == null && mTapAction == null
+ && mLoggingInfo == null) {
+ throw new IllegalStateException("SubItem data is empty");
+ }
+
+ return new SubItemInfo(mText, mIcon, mTapAction, mLoggingInfo);
+ }
}
}
@@ -735,27 +573,45 @@ public class BaseTemplateData implements Parcelable {
/** A unique instance id for the sub item. */
private final int mInstanceId;
- /** The feature type for this sub item. */
+ /**
+ * {@link FeatureType} indicating the feature type of this subitem.
+ *
+ * @see FeatureType
+ */
+ @FeatureType
private final int mFeatureType;
+ /** The data source's package name for this sub item. */
+ @Nullable
+ private final CharSequence mPackageName;
+
SubItemLoggingInfo(@NonNull Parcel in) {
mInstanceId = in.readInt();
mFeatureType = in.readInt();
+ mPackageName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
}
- private SubItemLoggingInfo(int instanceId, int featureType) {
+ private SubItemLoggingInfo(int instanceId, @FeatureType int featureType,
+ @Nullable CharSequence packageName) {
mInstanceId = instanceId;
mFeatureType = featureType;
+ mPackageName = packageName;
}
public int getInstanceId() {
return mInstanceId;
}
+ @FeatureType
public int getFeatureType() {
return mFeatureType;
}
+ @Nullable
+ public CharSequence getPackageName() {
+ return mPackageName;
+ }
+
/**
* @see Parcelable.Creator
*/
@@ -782,6 +638,7 @@ public class BaseTemplateData implements Parcelable {
public void writeToParcel(@NonNull Parcel out, int flags) {
out.writeInt(mInstanceId);
out.writeInt(mFeatureType);
+ TextUtils.writeToParcel(mPackageName, out, flags);
}
@Override
@@ -789,12 +646,13 @@ public class BaseTemplateData implements Parcelable {
if (this == o) return true;
if (!(o instanceof SubItemLoggingInfo)) return false;
SubItemLoggingInfo that = (SubItemLoggingInfo) o;
- return mInstanceId == that.mInstanceId && mFeatureType == that.mFeatureType;
+ return mInstanceId == that.mInstanceId && mFeatureType == that.mFeatureType
+ && SmartspaceUtils.isEqual(mPackageName, that.mPackageName);
}
@Override
public int hashCode() {
- return Objects.hash(mInstanceId, mFeatureType);
+ return Objects.hash(mInstanceId, mFeatureType, mPackageName);
}
@Override
@@ -802,6 +660,7 @@ public class BaseTemplateData implements Parcelable {
return "SubItemLoggingInfo{"
+ "mInstanceId=" + mInstanceId
+ ", mFeatureType=" + mFeatureType
+ + ", mPackageName=" + mPackageName
+ '}';
}
@@ -815,22 +674,32 @@ public class BaseTemplateData implements Parcelable {
private final int mInstanceId;
private final int mFeatureType;
+ private CharSequence mPackageName;
/**
* A builder for {@link SubItemLoggingInfo}.
*
* @param instanceId A unique instance id for the sub item
- * @param featureType The feature type for this sub item
+ * @param featureType The feature type id for this sub item
*/
- public Builder(int instanceId, int featureType) {
+ public Builder(int instanceId, @FeatureType int featureType) {
mInstanceId = instanceId;
mFeatureType = featureType;
}
+ /**
+ * Sets the sub item's data source package name.
+ */
+ @NonNull
+ public Builder setPackageName(@NonNull CharSequence packageName) {
+ mPackageName = packageName;
+ return this;
+ }
+
/** Builds a new {@link SubItemLoggingInfo} instance. */
@NonNull
public SubItemLoggingInfo build() {
- return new SubItemLoggingInfo(mInstanceId, mFeatureType);
+ return new SubItemLoggingInfo(mInstanceId, mFeatureType, mPackageName);
}
}
}
diff --git a/core/java/android/app/smartspace/uitemplatedata/CarouselTemplateData.java b/core/java/android/app/smartspace/uitemplatedata/CarouselTemplateData.java
index fbdb7be3de7d..c1378f13543a 100644
--- a/core/java/android/app/smartspace/uitemplatedata/CarouselTemplateData.java
+++ b/core/java/android/app/smartspace/uitemplatedata/CarouselTemplateData.java
@@ -56,31 +56,16 @@ public final class CarouselTemplateData extends BaseTemplateData {
}
private CarouselTemplateData(@SmartspaceTarget.UiTemplateType int templateType,
- @Nullable Text titleText,
- @Nullable Icon titleIcon,
- @Nullable Text subtitleText,
- @Nullable Icon subtitleIcon,
- @Nullable TapAction primaryTapAction,
- @Nullable SubItemLoggingInfo primaryLoggingInfo,
- @Nullable Text supplementalSubtitleText,
- @Nullable Icon supplementalSubtitleIcon,
- @Nullable TapAction supplementalSubtitleTapAction,
- @Nullable SubItemLoggingInfo supplementalSubtitleLoggingInfo,
- @Nullable Text supplementalText,
- @Nullable Icon supplementalIcon,
- @Nullable TapAction supplementalTapAction,
- @Nullable SubItemLoggingInfo supplementalLoggingInfo,
- @Nullable Text supplementalAlarmText,
+ @Nullable SubItemInfo primaryItem,
+ @Nullable SubItemInfo subtitleItem,
+ @Nullable SubItemInfo subtitleSupplementalItem,
+ @Nullable SubItemInfo supplementalLineItem,
+ @Nullable SubItemInfo supplementalAlarmItem,
int layoutWeight,
@NonNull List<CarouselItem> carouselItems,
@Nullable TapAction carouselAction) {
- super(templateType, titleText, titleIcon, subtitleText, subtitleIcon,
- primaryTapAction, primaryLoggingInfo,
- supplementalSubtitleText, supplementalSubtitleIcon,
- supplementalSubtitleTapAction, supplementalSubtitleLoggingInfo,
- supplementalText, supplementalIcon,
- supplementalTapAction, supplementalLoggingInfo,
- supplementalAlarmText, layoutWeight);
+ super(templateType, primaryItem, subtitleItem, subtitleSupplementalItem,
+ supplementalLineItem, supplementalAlarmItem, layoutWeight);
mCarouselItems = carouselItems;
mCarouselAction = carouselAction;
@@ -190,14 +175,9 @@ public final class CarouselTemplateData extends BaseTemplateData {
throw new IllegalStateException("Carousel data is empty");
}
- return new CarouselTemplateData(getTemplateType(), getTitleText(),
- getTitleIcon(), getSubtitleText(), getSubtitleIcon(),
- getPrimaryTapAction(), getPrimaryLoggingInfo(),
- getSupplementalSubtitleText(), getSupplementalSubtitleIcon(),
- getSupplementalSubtitleTapAction(), getSupplementalSubtitleLoggingInfo(),
- getSupplementalText(), getSupplementalIcon(),
- getSupplementalTapAction(), getSupplementalLoggingInfo(),
- getSupplementalAlarmText(), getLayoutWeight(),
+ return new CarouselTemplateData(getTemplateType(), getPrimaryItem(),
+ getSubtitleItem(), getSubtitleSupplemtnalItem(),
+ getSupplementalLineItem(), getSupplementalAlarmItem(), getLayoutWeight(),
mCarouselItems, mCarouselAction);
}
}
diff --git a/core/java/android/app/smartspace/uitemplatedata/CombinedCardsTemplateData.java b/core/java/android/app/smartspace/uitemplatedata/CombinedCardsTemplateData.java
index 1d1306619d11..836f41412c01 100644
--- a/core/java/android/app/smartspace/uitemplatedata/CombinedCardsTemplateData.java
+++ b/core/java/android/app/smartspace/uitemplatedata/CombinedCardsTemplateData.java
@@ -51,30 +51,16 @@ public final class CombinedCardsTemplateData extends BaseTemplateData {
}
private CombinedCardsTemplateData(@SmartspaceTarget.UiTemplateType int templateType,
- @Nullable Text titleText,
- @Nullable Icon titleIcon,
- @Nullable Text subtitleText,
- @Nullable Icon subtitleIcon,
- @Nullable TapAction primaryTapAction,
- @Nullable SubItemLoggingInfo primaryLoggingInfo,
- @Nullable Text supplementalSubtitleText,
- @Nullable Icon supplementalSubtitleIcon,
- @Nullable TapAction supplementalSubtitleTapAction,
- @Nullable SubItemLoggingInfo supplementalSubtitleLoggingInfo,
- @Nullable Text supplementalText,
- @Nullable Icon supplementalIcon,
- @Nullable TapAction supplementalTapAction,
- @Nullable SubItemLoggingInfo supplementalLoggingInfo,
- @Nullable Text supplementalAlarmText,
+ @Nullable SubItemInfo primaryItem,
+ @Nullable SubItemInfo subtitleItem,
+ @Nullable SubItemInfo subtitleSupplementalItem,
+ @Nullable SubItemInfo supplementalLineItem,
+ @Nullable SubItemInfo supplementalAlarmItem,
int layoutWeight,
@NonNull List<BaseTemplateData> combinedCardDataList) {
- super(templateType, titleText, titleIcon, subtitleText, subtitleIcon,
- primaryTapAction, primaryLoggingInfo,
- supplementalSubtitleText, supplementalSubtitleIcon,
- supplementalSubtitleTapAction, supplementalSubtitleLoggingInfo,
- supplementalText, supplementalIcon,
- supplementalTapAction, supplementalLoggingInfo,
- supplementalAlarmText, layoutWeight);
+ super(templateType, primaryItem, subtitleItem, subtitleSupplementalItem,
+ supplementalLineItem, supplementalAlarmItem, layoutWeight);
+
mCombinedCardDataList = combinedCardDataList;
}
@@ -161,14 +147,9 @@ public final class CombinedCardsTemplateData extends BaseTemplateData {
if (mCombinedCardDataList == null) {
throw new IllegalStateException("Please assign a value to all @NonNull args.");
}
- return new CombinedCardsTemplateData(getTemplateType(), getTitleText(),
- getTitleIcon(), getSubtitleText(), getSubtitleIcon(),
- getPrimaryTapAction(), getPrimaryLoggingInfo(),
- getSupplementalSubtitleText(), getSupplementalSubtitleIcon(),
- getSupplementalSubtitleTapAction(), getSupplementalSubtitleLoggingInfo(),
- getSupplementalText(), getSupplementalIcon(),
- getSupplementalTapAction(), getSupplementalLoggingInfo(),
- getSupplementalAlarmText(), getLayoutWeight(),
+ return new CombinedCardsTemplateData(getTemplateType(), getPrimaryItem(),
+ getSubtitleItem(), getSubtitleSupplemtnalItem(),
+ getSupplementalLineItem(), getSupplementalAlarmItem(), getLayoutWeight(),
mCombinedCardDataList);
}
}
diff --git a/core/java/android/app/smartspace/uitemplatedata/HeadToHeadTemplateData.java b/core/java/android/app/smartspace/uitemplatedata/HeadToHeadTemplateData.java
index 19177dfc7649..29df0186b192 100644
--- a/core/java/android/app/smartspace/uitemplatedata/HeadToHeadTemplateData.java
+++ b/core/java/android/app/smartspace/uitemplatedata/HeadToHeadTemplateData.java
@@ -66,21 +66,11 @@ public final class HeadToHeadTemplateData extends BaseTemplateData {
}
private HeadToHeadTemplateData(@SmartspaceTarget.UiTemplateType int templateType,
- @Nullable Text titleText,
- @Nullable Icon titleIcon,
- @Nullable Text subtitleText,
- @Nullable Icon subtitleIcon,
- @Nullable TapAction primaryTapAction,
- @Nullable SubItemLoggingInfo primaryLoggingInfo,
- @Nullable Text supplementalSubtitleText,
- @Nullable Icon supplementalSubtitleIcon,
- @Nullable TapAction supplementalSubtitleTapAction,
- @Nullable SubItemLoggingInfo supplementalSubtitleLoggingInfo,
- @Nullable Text supplementalText,
- @Nullable Icon supplementalIcon,
- @Nullable TapAction supplementalTapAction,
- @Nullable SubItemLoggingInfo supplementalLoggingInfo,
- @Nullable Text supplementalAlarmText,
+ @Nullable SubItemInfo primaryItem,
+ @Nullable SubItemInfo subtitleItem,
+ @Nullable SubItemInfo subtitleSupplementalItem,
+ @Nullable SubItemInfo supplementalLineItem,
+ @Nullable SubItemInfo supplementalAlarmItem,
int layoutWeight,
@Nullable Text headToHeadTitle,
@Nullable Icon headToHeadFirstCompetitorIcon,
@@ -88,13 +78,8 @@ public final class HeadToHeadTemplateData extends BaseTemplateData {
@Nullable Text headToHeadFirstCompetitorText,
@Nullable Text headToHeadSecondCompetitorText,
@Nullable TapAction headToHeadAction) {
- super(templateType, titleText, titleIcon, subtitleText, subtitleIcon,
- primaryTapAction, primaryLoggingInfo,
- supplementalSubtitleText, supplementalSubtitleIcon,
- supplementalSubtitleTapAction, supplementalSubtitleLoggingInfo,
- supplementalText, supplementalIcon,
- supplementalTapAction, supplementalLoggingInfo,
- supplementalAlarmText, layoutWeight);
+ super(templateType, primaryItem, subtitleItem, subtitleSupplementalItem,
+ supplementalLineItem, supplementalAlarmItem, layoutWeight);
mHeadToHeadTitle = headToHeadTitle;
mHeadToHeadFirstCompetitorIcon = headToHeadFirstCompetitorIcon;
@@ -296,14 +281,9 @@ public final class HeadToHeadTemplateData extends BaseTemplateData {
*/
@NonNull
public HeadToHeadTemplateData build() {
- return new HeadToHeadTemplateData(getTemplateType(), getTitleText(),
- getTitleIcon(), getSubtitleText(), getSubtitleIcon(),
- getPrimaryTapAction(), getPrimaryLoggingInfo(),
- getSupplementalSubtitleText(), getSupplementalSubtitleIcon(),
- getSupplementalSubtitleTapAction(), getSupplementalSubtitleLoggingInfo(),
- getSupplementalText(), getSupplementalIcon(),
- getSupplementalTapAction(), getSupplementalLoggingInfo(),
- getSupplementalAlarmText(), getLayoutWeight(),
+ return new HeadToHeadTemplateData(getTemplateType(), getPrimaryItem(),
+ getSubtitleItem(), getSubtitleSupplemtnalItem(),
+ getSupplementalLineItem(), getSupplementalAlarmItem(), getLayoutWeight(),
mHeadToHeadTitle,
mHeadToHeadFirstCompetitorIcon,
mHeadToHeadSecondCompetitorIcon, mHeadToHeadFirstCompetitorText,
diff --git a/core/java/android/app/smartspace/uitemplatedata/SubCardTemplateData.java b/core/java/android/app/smartspace/uitemplatedata/SubCardTemplateData.java
index 48af9c173f1d..b87e5b32fcc5 100644
--- a/core/java/android/app/smartspace/uitemplatedata/SubCardTemplateData.java
+++ b/core/java/android/app/smartspace/uitemplatedata/SubCardTemplateData.java
@@ -59,32 +59,17 @@ public final class SubCardTemplateData extends BaseTemplateData {
}
private SubCardTemplateData(int templateType,
- @Nullable Text titleText,
- @Nullable Icon titleIcon,
- @Nullable Text subtitleText,
- @Nullable Icon subtitleIcon,
- @Nullable TapAction primaryTapAction,
- @Nullable SubItemLoggingInfo primaryLoggingInfo,
- @Nullable Text supplementalSubtitleText,
- @Nullable Icon supplementalSubtitleIcon,
- @Nullable TapAction supplementalSubtitleTapAction,
- @Nullable SubItemLoggingInfo supplementalSubtitleLoggingInfo,
- @Nullable Text supplementalText,
- @Nullable Icon supplementalIcon,
- @Nullable TapAction supplementalTapAction,
- @Nullable SubItemLoggingInfo supplementalLoggingInfo,
- @Nullable Text supplementalAlarmText,
+ @Nullable SubItemInfo primaryItem,
+ @Nullable SubItemInfo subtitleItem,
+ @Nullable SubItemInfo subtitleSupplementalItem,
+ @Nullable SubItemInfo supplementalLineItem,
+ @Nullable SubItemInfo supplementalAlarmItem,
int layoutWeight,
@NonNull Icon subCardIcon,
@Nullable Text subCardText,
@Nullable TapAction subCardAction) {
- super(templateType, titleText, titleIcon, subtitleText, subtitleIcon,
- primaryTapAction, primaryLoggingInfo,
- supplementalSubtitleText, supplementalSubtitleIcon,
- supplementalSubtitleTapAction, supplementalSubtitleLoggingInfo,
- supplementalText, supplementalIcon,
- supplementalTapAction, supplementalLoggingInfo,
- supplementalAlarmText, layoutWeight);
+ super(templateType, primaryItem, subtitleItem, subtitleSupplementalItem,
+ supplementalLineItem, supplementalAlarmItem, layoutWeight);
mSubCardIcon = subCardIcon;
mSubCardText = subCardText;
@@ -207,14 +192,9 @@ public final class SubCardTemplateData extends BaseTemplateData {
*/
@NonNull
public SubCardTemplateData build() {
- return new SubCardTemplateData(getTemplateType(), getTitleText(),
- getTitleIcon(), getSubtitleText(), getSubtitleIcon(),
- getPrimaryTapAction(), getPrimaryLoggingInfo(),
- getSupplementalSubtitleText(), getSupplementalSubtitleIcon(),
- getSupplementalSubtitleTapAction(), getSupplementalSubtitleLoggingInfo(),
- getSupplementalText(), getSupplementalIcon(),
- getSupplementalTapAction(), getSupplementalLoggingInfo(),
- getSupplementalAlarmText(), getLayoutWeight(),
+ return new SubCardTemplateData(getTemplateType(), getPrimaryItem(),
+ getSubtitleItem(), getSubtitleSupplemtnalItem(),
+ getSupplementalLineItem(), getSupplementalAlarmItem(), getLayoutWeight(),
mSubCardIcon,
mSubCardText,
mSubCardAction);
diff --git a/core/java/android/app/smartspace/uitemplatedata/SubImageTemplateData.java b/core/java/android/app/smartspace/uitemplatedata/SubImageTemplateData.java
index 38692cd19df4..430d79c481ed 100644
--- a/core/java/android/app/smartspace/uitemplatedata/SubImageTemplateData.java
+++ b/core/java/android/app/smartspace/uitemplatedata/SubImageTemplateData.java
@@ -60,32 +60,17 @@ public final class SubImageTemplateData extends BaseTemplateData {
}
private SubImageTemplateData(@SmartspaceTarget.UiTemplateType int templateType,
- @Nullable Text titleText,
- @Nullable Icon titleIcon,
- @Nullable Text subtitleText,
- @Nullable Icon subtitleIcon,
- @Nullable TapAction primaryTapAction,
- @Nullable SubItemLoggingInfo primaryLoggingInfo,
- @Nullable Text supplementalSubtitleText,
- @Nullable Icon supplementalSubtitleIcon,
- @Nullable TapAction supplementalSubtitleTapAction,
- @Nullable SubItemLoggingInfo supplementalSubtitleLoggingInfo,
- @Nullable Text supplementalText,
- @Nullable Icon supplementalIcon,
- @Nullable TapAction supplementalTapAction,
- @Nullable SubItemLoggingInfo supplementalLoggingInfo,
- @Nullable Text supplementalAlarmText,
+ @Nullable SubItemInfo primaryItem,
+ @Nullable SubItemInfo subtitleItem,
+ @Nullable SubItemInfo subtitleSupplementalItem,
+ @Nullable SubItemInfo supplementalLineItem,
+ @Nullable SubItemInfo supplementalAlarmItem,
int layoutWeight,
@NonNull List<Text> subImageTexts,
@NonNull List<Icon> subImages,
@Nullable TapAction subImageAction) {
- super(templateType, titleText, titleIcon, subtitleText, subtitleIcon,
- primaryTapAction, primaryLoggingInfo,
- supplementalSubtitleText, supplementalSubtitleIcon,
- supplementalSubtitleTapAction, supplementalSubtitleLoggingInfo,
- supplementalText, supplementalIcon,
- supplementalTapAction, supplementalLoggingInfo,
- supplementalAlarmText, layoutWeight);
+ super(templateType, primaryItem, subtitleItem, subtitleSupplementalItem,
+ supplementalLineItem, supplementalAlarmItem, layoutWeight);
mSubImageTexts = subImageTexts;
mSubImages = subImages;
@@ -204,14 +189,9 @@ public final class SubImageTemplateData extends BaseTemplateData {
*/
@NonNull
public SubImageTemplateData build() {
- return new SubImageTemplateData(getTemplateType(), getTitleText(),
- getTitleIcon(), getSubtitleText(), getSubtitleIcon(),
- getPrimaryTapAction(), getPrimaryLoggingInfo(),
- getSupplementalSubtitleText(), getSupplementalSubtitleIcon(),
- getSupplementalSubtitleTapAction(), getSupplementalSubtitleLoggingInfo(),
- getSupplementalText(), getSupplementalIcon(),
- getSupplementalTapAction(), getSupplementalLoggingInfo(),
- getSupplementalAlarmText(), getLayoutWeight(),
+ return new SubImageTemplateData(getTemplateType(), getPrimaryItem(),
+ getSubtitleItem(), getSubtitleSupplemtnalItem(),
+ getSupplementalLineItem(), getSupplementalAlarmItem(), getLayoutWeight(),
mSubImageTexts,
mSubImages,
mSubImageAction);
diff --git a/core/java/android/app/smartspace/uitemplatedata/SubListTemplateData.java b/core/java/android/app/smartspace/uitemplatedata/SubListTemplateData.java
index b1535f1e3924..ae43fc48c907 100644
--- a/core/java/android/app/smartspace/uitemplatedata/SubListTemplateData.java
+++ b/core/java/android/app/smartspace/uitemplatedata/SubListTemplateData.java
@@ -59,32 +59,17 @@ public final class SubListTemplateData extends BaseTemplateData {
}
private SubListTemplateData(@SmartspaceTarget.UiTemplateType int templateType,
- @Nullable Text titleText,
- @Nullable Icon titleIcon,
- @Nullable Text subtitleText,
- @Nullable Icon subtitleIcon,
- @Nullable TapAction primaryTapAction,
- @Nullable SubItemLoggingInfo primaryLoggingInfo,
- @Nullable Text supplementalSubtitleText,
- @Nullable Icon supplementalSubtitleIcon,
- @Nullable TapAction supplementalSubtitleTapAction,
- @Nullable SubItemLoggingInfo supplementalSubtitleLoggingInfo,
- @Nullable Text supplementalText,
- @Nullable Icon supplementalIcon,
- @Nullable TapAction supplementalTapAction,
- @Nullable SubItemLoggingInfo supplementalLoggingInfo,
- @Nullable Text supplementalAlarmText,
+ @Nullable SubItemInfo primaryItem,
+ @Nullable SubItemInfo subtitleItem,
+ @Nullable SubItemInfo subtitleSupplementalItem,
+ @Nullable SubItemInfo supplementalLineItem,
+ @Nullable SubItemInfo supplementalAlarmItem,
int layoutWeight,
@Nullable Icon subListIcon,
@NonNull List<Text> subListTexts,
@Nullable TapAction subListAction) {
- super(templateType, titleText, titleIcon, subtitleText, subtitleIcon,
- primaryTapAction, primaryLoggingInfo,
- supplementalSubtitleText, supplementalSubtitleIcon,
- supplementalSubtitleTapAction, supplementalSubtitleLoggingInfo,
- supplementalText, supplementalIcon,
- supplementalTapAction, supplementalLoggingInfo,
- supplementalAlarmText, layoutWeight);
+ super(templateType, primaryItem, subtitleItem, subtitleSupplementalItem,
+ supplementalLineItem, supplementalAlarmItem, layoutWeight);
mSubListIcon = subListIcon;
mSubListTexts = subListTexts;
@@ -207,14 +192,9 @@ public final class SubListTemplateData extends BaseTemplateData {
*/
@NonNull
public SubListTemplateData build() {
- return new SubListTemplateData(getTemplateType(), getTitleText(),
- getTitleIcon(), getSubtitleText(), getSubtitleIcon(),
- getPrimaryTapAction(), getPrimaryLoggingInfo(),
- getSupplementalSubtitleText(), getSupplementalSubtitleIcon(),
- getSupplementalSubtitleTapAction(), getSupplementalSubtitleLoggingInfo(),
- getSupplementalText(), getSupplementalIcon(),
- getSupplementalTapAction(), getSupplementalLoggingInfo(),
- getSupplementalAlarmText(), getLayoutWeight(),
+ return new SubListTemplateData(getTemplateType(), getPrimaryItem(),
+ getSubtitleItem(), getSubtitleSupplemtnalItem(),
+ getSupplementalLineItem(), getSupplementalAlarmItem(), getLayoutWeight(),
mSubListIcon,
mSubListTexts,
mSubListAction);
diff --git a/core/java/android/app/smartspace/uitemplatedata/Text.java b/core/java/android/app/smartspace/uitemplatedata/Text.java
index e1afce7148ae..cb2f43202e90 100644
--- a/core/java/android/app/smartspace/uitemplatedata/Text.java
+++ b/core/java/android/app/smartspace/uitemplatedata/Text.java
@@ -109,6 +109,15 @@ public final class Text implements Parcelable {
out.writeInt(mMaxLines);
}
+ @Override
+ public String toString() {
+ return "Text{"
+ + "mText=" + mText
+ + ", mTruncateAtType=" + mTruncateAtType
+ + ", mMaxLines=" + mMaxLines
+ + '}';
+ }
+
/**
* A builder for {@link Text} object.
*
diff --git a/core/java/android/companion/virtual/VirtualDeviceManager.java b/core/java/android/companion/virtual/VirtualDeviceManager.java
index 99ce14743a6f..02d140f047cc 100644
--- a/core/java/android/companion/virtual/VirtualDeviceManager.java
+++ b/core/java/android/companion/virtual/VirtualDeviceManager.java
@@ -48,7 +48,6 @@ import android.os.ResultReceiver;
import android.util.ArrayMap;
import android.view.Surface;
-import java.util.Objects;
import java.util.concurrent.Executor;
/**
@@ -223,7 +222,8 @@ public final class VirtualDeviceManager {
* {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY
* VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY}.
* @param executor The executor on which {@code callback} will be invoked. This is ignored
- * if {@code callback} is {@code null}.
+ * if {@code callback} is {@code null}. If {@code callback} is specified, this executor must
+ * not be null.
* @param callback Callback to call when the state of the {@link VirtualDisplay} changes
* @return The newly created virtual display, or {@code null} if the application could
* not create the virtual display.
@@ -237,7 +237,7 @@ public final class VirtualDeviceManager {
@IntRange(from = 1) int densityDpi,
@Nullable Surface surface,
@VirtualDisplayFlag int flags,
- @NonNull @CallbackExecutor Executor executor,
+ @Nullable @CallbackExecutor Executor executor,
@Nullable VirtualDisplay.Callback callback) {
// TODO(b/205343547): Handle display groups properly instead of creating a new display
// group for every new virtual display created using this API.
@@ -253,7 +253,7 @@ public final class VirtualDeviceManager {
.setFlags(getVirtualDisplayFlags(flags))
.build(),
callback,
- Objects.requireNonNull(executor));
+ executor);
}
/**
diff --git a/core/java/android/hardware/CameraStreamStats.java b/core/java/android/hardware/CameraStreamStats.java
index 85890c1912c5..823d454ee16b 100644
--- a/core/java/android/hardware/CameraStreamStats.java
+++ b/core/java/android/hardware/CameraStreamStats.java
@@ -47,7 +47,7 @@ public class CameraStreamStats implements Parcelable {
private int mHistogramType;
private float[] mHistogramBins;
private long[] mHistogramCounts;
- private int mDynamicRangeProfile;
+ private long mDynamicRangeProfile;
private int mStreamUseCase;
private static final String TAG = "CameraStreamStats";
@@ -70,7 +70,7 @@ public class CameraStreamStats implements Parcelable {
public CameraStreamStats(int width, int height, int format,
int dataSpace, long usage, long requestCount, long errorCount,
- int startLatencyMs, int maxHalBuffers, int maxAppBuffers, int dynamicRangeProfile,
+ int startLatencyMs, int maxHalBuffers, int maxAppBuffers, long dynamicRangeProfile,
int streamUseCase) {
mWidth = width;
mHeight = height;
@@ -130,7 +130,7 @@ public class CameraStreamStats implements Parcelable {
dest.writeInt(mHistogramType);
dest.writeFloatArray(mHistogramBins);
dest.writeLongArray(mHistogramCounts);
- dest.writeInt(mDynamicRangeProfile);
+ dest.writeLong(mDynamicRangeProfile);
dest.writeInt(mStreamUseCase);
}
@@ -148,7 +148,7 @@ public class CameraStreamStats implements Parcelable {
mHistogramType = in.readInt();
mHistogramBins = in.createFloatArray();
mHistogramCounts = in.createLongArray();
- mDynamicRangeProfile = in.readInt();
+ mDynamicRangeProfile = in.readLong();
mStreamUseCase = in.readInt();
}
@@ -204,7 +204,7 @@ public class CameraStreamStats implements Parcelable {
return mHistogramCounts;
}
- public int getDynamicRangeProfile() {
+ public long getDynamicRangeProfile() {
return mDynamicRangeProfile;
}
diff --git a/core/java/android/hardware/SyncFence.java b/core/java/android/hardware/SyncFence.java
index 99791ce3d8ec..cd4bf78f0879 100644
--- a/core/java/android/hardware/SyncFence.java
+++ b/core/java/android/hardware/SyncFence.java
@@ -17,6 +17,8 @@
package android.hardware;
import android.annotation.NonNull;
+import android.media.Image;
+import android.media.ImageWriter;
import android.opengl.EGLDisplay;
import android.opengl.EGLSync;
import android.os.Parcel;
@@ -30,14 +32,29 @@ import java.io.IOException;
import java.time.Duration;
/**
- * A SyncFence represents a synchronization primitive which signals when hardware buffers have
- * completed work on a particular resource.
+ * A SyncFence represents a synchronization primitive which signals when hardware units have
+ * completed work on a particular resource. They initially start in an unsignaled state and make
+ * a one-time transition to either a signaled or error state. SyncFences are created by various
+ * device APIs in response to submitting tasks to the device. They cannot be created nor signaled
+ * by userspace. As a result, this means that a SyncFence will make always make forward progress.
+ *
+ * <p>SyncFence's generally come in one of two varieties. "Presentation fences" refer to
+ * a SyncFence when the writing to a buffer has finished. "Release fences" then refer
+ * to when the reading from a buffer has finished.</p>
*
* <p>For example, a GPU rendering to a framebuffer may generate a synchronization fence,
- * e.g., a VkFence, which signals when rendering has completed.
+ * e.g., an EGLSync or VkFence, which signals when rendering has completed. Once the fence signals,
+ * then the backing storage for the framebuffer may be safely read from, such as for display or
+ * for media encoding. This would be referred to as a "presentation fence."</p>
*
- * Once the fence signals, then the backing storage for the framebuffer may be safely read from,
- * such as for display or for media encoding.</p>
+ * <p>Similarly when using an {@link android.media.ImageWriter} it is possible that an
+ * {@link android.media.Image} returned by {@link ImageWriter#dequeueInputImage()} may already
+ * have a {@link Image#getFence() fence} set on it. This would be what is referred to as either
+ * a "release fence" or an "acqurie fence" and indicates the fence that the writer must wait
+ * on before writing to the underlying buffer. In the case of ImageWriter this is done
+ * automatically when eg {@link Image#getPlanes()} is called, however when using
+ * {@link Image#getHardwareBuffer()} it is the caller's responsibility to ensure the
+ * release fence has signaled before writing to the buffer.</p>
*
* @see android.opengl.EGLExt#eglDupNativeFenceFDANDROID(EGLDisplay, EGLSync)
* @see android.media.Image#getFence()
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 524fe795cb75..7bebe1ff14c3 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -2448,8 +2448,8 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
* @see #REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_MAX
* @hide
*/
- public static final Key<int[]> REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP =
- new Key<int[]>("android.request.availableDynamicRangeProfilesMap", int[].class);
+ public static final Key<long[]> REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP =
+ new Key<long[]>("android.request.availableDynamicRangeProfilesMap", long[].class);
/**
* <p>Recommended 10-bit dynamic range profile.</p>
@@ -2464,8 +2464,8 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
*/
@PublicKey
@NonNull
- public static final Key<Integer> REQUEST_RECOMMENDED_TEN_BIT_DYNAMIC_RANGE_PROFILE =
- new Key<Integer>("android.request.recommendedTenBitDynamicRangeProfile", int.class);
+ public static final Key<Long> REQUEST_RECOMMENDED_TEN_BIT_DYNAMIC_RANGE_PROFILE =
+ new Key<Long>("android.request.recommendedTenBitDynamicRangeProfile", long.class);
/**
* <p>The list of image formats that are supported by this
diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java
index 8f42b1f6f355..73735edbb2ba 100644
--- a/core/java/android/hardware/camera2/CameraDevice.java
+++ b/core/java/android/hardware/camera2/CameraDevice.java
@@ -990,7 +990,7 @@ public abstract class CameraDevice implements AutoCloseable {
* <p>Reprocessing with 10-bit output targets on 10-bit capable
* {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES_DYNAMIC_RANGE_TEN_BIT} devices is
* not supported. Trying to initialize a repreocessable capture session with one ore more
- * output configurations set {@link OutputConfiguration#setDynamicRangeProfile(int)} to use
+ * output configurations set {@link OutputConfiguration#setDynamicRangeProfile} to use
* a 10-bit dynamic range profile {@link android.hardware.camera2.params.DynamicRangeProfiles}
* will trigger {@link IllegalArgumentException}.</p>
*
@@ -1179,7 +1179,7 @@ public abstract class CameraDevice implements AutoCloseable {
* @see #createCaptureSessionByOutputConfigurations
* @see #createReprocessableCaptureSession
* @see #createConstrainedHighSpeedCaptureSession
- * @see OutputConfiguration#setDynamicRangeProfile(int)
+ * @see OutputConfiguration#setDynamicRangeProfile
* @see android.hardware.camera2.params.DynamicRangeProfiles
*/
public void createCaptureSession(
diff --git a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
index 4fb496d66fbd..468e6041eb73 100644
--- a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
+++ b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
@@ -1058,7 +1058,7 @@ public class CameraMetadataNative implements Parcelable {
}
private DynamicRangeProfiles getDynamicRangeProfiles() {
- int[] profileArray = getBase(
+ long[] profileArray = getBase(
CameraCharacteristics.REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP);
if (profileArray == null) {
diff --git a/core/java/android/hardware/camera2/params/DynamicRangeProfiles.java b/core/java/android/hardware/camera2/params/DynamicRangeProfiles.java
index 5c1a4aa120ea..cbd84e77e72a 100644
--- a/core/java/android/hardware/camera2/params/DynamicRangeProfiles.java
+++ b/core/java/android/hardware/camera2/params/DynamicRangeProfiles.java
@@ -16,7 +16,7 @@
package android.hardware.camera2.params;
-import android.annotation.IntDef;
+import android.annotation.LongDef;
import android.annotation.NonNull;
import android.hardware.camera2.CameraMetadata;
@@ -27,7 +27,6 @@ import java.lang.annotation.RetentionPolicy;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
-import java.util.Objects;
import java.util.Set;
/**
@@ -44,20 +43,20 @@ import java.util.Set;
*
* <p>Some devices may not be able to support 8-bit and/or 10-bit output with different dynamic
* range profiles within the same capture request. Such device specific constraints can be queried
- * by calling {@link #getProfileCaptureRequestConstraints(int)}. Do note that unsupported
+ * by calling {@link #getProfileCaptureRequestConstraints}. Do note that unsupported
* combinations will result in {@link IllegalArgumentException} when trying to submit a capture
* request. Capture requests that only reference outputs configured using the same dynamic range
* profile value will never fail due to such constraints.</p>
*
- * @see OutputConfiguration#setDynamicRangeProfile(int)
+ * @see OutputConfiguration#setDynamicRangeProfile
*/
public final class DynamicRangeProfiles {
/**
* This the default 8-bit standard profile that will be used in case where camera clients do not
* explicitly configure a supported dynamic range profile by calling
- * {@link OutputConfiguration#setDynamicRangeProfile(int)}.
+ * {@link OutputConfiguration#setDynamicRangeProfile}.
*/
- public static final int STANDARD =
+ public static final long STANDARD =
CameraMetadata.REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_STANDARD;
/**
@@ -65,7 +64,7 @@ public final class DynamicRangeProfiles {
*
* <p>All 10-bit output capable devices are required to support this profile.</p>
*/
- public static final int HLG10 =
+ public static final long HLG10 =
CameraMetadata.REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_HLG10;
/**
@@ -74,7 +73,7 @@ public final class DynamicRangeProfiles {
* <p>This profile utilizes internal static metadata to increase the quality
* of the capture.</p>
*/
- public static final int HDR10 =
+ public static final long HDR10 =
CameraMetadata.REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_HDR10;
/**
@@ -83,7 +82,7 @@ public final class DynamicRangeProfiles {
* <p>In contrast to HDR10, this profile uses internal per-frame metadata
* to further enhance the quality of the capture.</p>
*/
- public static final int HDR10_PLUS =
+ public static final long HDR10_PLUS =
CameraMetadata.REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_HDR10_PLUS;
/**
@@ -91,13 +90,13 @@ public final class DynamicRangeProfiles {
* accurate capture. This would typically differ from what a specific device
* might want to tune for a consumer optimized Dolby Vision general capture.</p>
*/
- public static final int DOLBY_VISION_10B_HDR_REF =
+ public static final long DOLBY_VISION_10B_HDR_REF =
CameraMetadata.REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_DOLBY_VISION_10B_HDR_REF;
/**
* <p>This is the power optimized mode for 10-bit Dolby Vision HDR Reference Mode.</p>
*/
- public static final int DOLBY_VISION_10B_HDR_REF_PO =
+ public static final long DOLBY_VISION_10B_HDR_REF_PO =
CameraMetadata.REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_DOLBY_VISION_10B_HDR_REF_PO;
/**
@@ -107,52 +106,52 @@ public final class DynamicRangeProfiles {
* that each specific device would have a different look for their default
* Dolby Vision capture.</p>
*/
- public static final int DOLBY_VISION_10B_HDR_OEM =
+ public static final long DOLBY_VISION_10B_HDR_OEM =
CameraMetadata.REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_DOLBY_VISION_10B_HDR_OEM;
/**
* <p>This is the power optimized mode for 10-bit Dolby Vision HDR device specific capture
* Mode.</p>
*/
- public static final int DOLBY_VISION_10B_HDR_OEM_PO =
+ public static final long DOLBY_VISION_10B_HDR_OEM_PO =
CameraMetadata.REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_DOLBY_VISION_10B_HDR_OEM_PO;
/**
* <p>This is the 8-bit version of the Dolby Vision reference capture mode optimized
* for scene accuracy.</p>
*/
- public static final int DOLBY_VISION_8B_HDR_REF =
+ public static final long DOLBY_VISION_8B_HDR_REF =
CameraMetadata.REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_DOLBY_VISION_8B_HDR_REF;
/**
* <p>This is the power optimized mode for 8-bit Dolby Vision HDR Reference Mode.</p>
*/
- public static final int DOLBY_VISION_8B_HDR_REF_PO =
+ public static final long DOLBY_VISION_8B_HDR_REF_PO =
CameraMetadata.REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_DOLBY_VISION_8B_HDR_REF_PO;
/**
* <p>This is the 8-bit version of device specific tuned and optimized Dolby Vision
* capture mode.</p>
*/
- public static final int DOLBY_VISION_8B_HDR_OEM =
+ public static final long DOLBY_VISION_8B_HDR_OEM =
CameraMetadata.REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_DOLBY_VISION_8B_HDR_OEM;
/**
* <p>This is the power optimized mode for 8-bit Dolby Vision HDR device specific
* capture Mode.</p>
*/
- public static final int DOLBY_VISION_8B_HDR_OEM_PO =
+ public static final long DOLBY_VISION_8B_HDR_OEM_PO =
CameraMetadata.REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_DOLBY_VISION_8B_HDR_OEM_PO;
/*
* @hide
*/
- public static final int PUBLIC_MAX =
+ public static final long PUBLIC_MAX =
CameraMetadata.REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_MAX;
/** @hide */
@Retention(RetentionPolicy.SOURCE)
- @IntDef(prefix = {"PROFILE_"}, value =
+ @LongDef(prefix = {"PROFILE_"}, value =
{STANDARD,
HLG10,
HDR10,
@@ -168,7 +167,8 @@ public final class DynamicRangeProfiles {
public @interface Profile {
}
- private final HashMap<Integer, Set<Integer>> mProfileMap = new HashMap<>();
+ private final HashMap<Long, Set<Long>> mProfileMap = new HashMap<>();
+ private final HashMap<Long, Boolean> mLookahedLatencyMap = new HashMap<>();
/**
* Create a new immutable DynamicRangeProfiles instance.
@@ -193,23 +193,23 @@ public final class DynamicRangeProfiles {
* if {@code elements} is {@code null}
*
*/
- public DynamicRangeProfiles(@NonNull final int[] elements) {
- if ((elements.length % 2) != 0) {
+ public DynamicRangeProfiles(@NonNull final long[] elements) {
+ if ((elements.length % 3) != 0) {
throw new IllegalArgumentException("Dynamic range profile map length " +
elements.length + " is not even!");
}
- for (int i = 0; i < elements.length; i += 2) {
+ for (int i = 0; i < elements.length; i += 3) {
checkProfileValue(elements[i]);
// STANDARD is not expected to be included
if (elements[i] == STANDARD) {
throw new IllegalArgumentException("Dynamic range profile map must not include a"
+ " STANDARD profile entry!");
}
- HashSet<Integer> profiles = new HashSet<>();
+ HashSet<Long> profiles = new HashSet<>();
if (elements[i+1] != 0) {
- for (int profile = STANDARD; profile < PUBLIC_MAX; profile <<= 1) {
+ for (long profile = STANDARD; profile < PUBLIC_MAX; profile <<= 1) {
if ((elements[i+1] & profile) != 0) {
profiles.add(profile);
}
@@ -217,12 +217,13 @@ public final class DynamicRangeProfiles {
}
mProfileMap.put(elements[i], profiles);
+ mLookahedLatencyMap.put(elements[i], elements[i+2] != 0L);
}
// Build the STANDARD constraints depending on the advertised 10-bit limitations
- HashSet<Integer> standardConstraints = new HashSet<>();
+ HashSet<Long> standardConstraints = new HashSet<>();
standardConstraints.add(STANDARD);
- for(Integer profile : mProfileMap.keySet()) {
+ for(Long profile : mProfileMap.keySet()) {
if (mProfileMap.get(profile).isEmpty() || mProfileMap.get(profile).contains(STANDARD)) {
standardConstraints.add(profile);
}
@@ -235,24 +236,15 @@ public final class DynamicRangeProfiles {
/**
* @hide
*/
- public static void checkProfileValue(int profile) {
- switch (profile) {
- case STANDARD:
- case HLG10:
- case HDR10:
- case HDR10_PLUS:
- case DOLBY_VISION_10B_HDR_REF:
- case DOLBY_VISION_10B_HDR_REF_PO:
- case DOLBY_VISION_10B_HDR_OEM:
- case DOLBY_VISION_10B_HDR_OEM_PO:
- case DOLBY_VISION_8B_HDR_REF:
- case DOLBY_VISION_8B_HDR_REF_PO:
- case DOLBY_VISION_8B_HDR_OEM:
- case DOLBY_VISION_8B_HDR_OEM_PO:
- //No-op
- break;
- default:
- throw new IllegalArgumentException("Unknown profile " + profile);
+ public static void checkProfileValue(long profile) {
+ if (profile == STANDARD || profile == HLG10 || profile == HDR10 || profile == HDR10_PLUS
+ || profile == DOLBY_VISION_10B_HDR_REF || profile == DOLBY_VISION_10B_HDR_REF_PO
+ || profile == DOLBY_VISION_10B_HDR_OEM || profile == DOLBY_VISION_10B_HDR_OEM_PO
+ || profile == DOLBY_VISION_8B_HDR_REF || profile == DOLBY_VISION_8B_HDR_REF_PO
+ || profile == DOLBY_VISION_8B_HDR_OEM
+ || profile == DOLBY_VISION_8B_HDR_OEM_PO) {//No-op
+ } else {
+ throw new IllegalArgumentException("Unknown profile " + profile);
}
}
@@ -261,7 +253,7 @@ public final class DynamicRangeProfiles {
*
* @return non-modifiable set of dynamic range profiles
*/
- public @NonNull Set<Integer> getSupportedProfiles() {
+ public @NonNull Set<Long> getSupportedProfiles() {
return Collections.unmodifiableSet(mProfileMap.keySet());
}
@@ -272,7 +264,7 @@ public final class DynamicRangeProfiles {
*
* <p>For example if assume that a particular 10-bit output capable device
* returns ({@link #STANDARD}, {@link #HLG10}, {@link #HDR10}) as result from calling
- * {@link #getSupportedProfiles()} and {@link #getProfileCaptureRequestConstraints(int)}
+ * {@link #getSupportedProfiles()} and {@link #getProfileCaptureRequestConstraints}
* returns ({@link #STANDARD}, {@link #HLG10}) when given an argument of {@link #STANDARD}.
* This means that the corresponding camera device will only accept and process capture requests
* that reference outputs configured using {@link #HDR10} dynamic profile or alternatively
@@ -288,14 +280,40 @@ public final class DynamicRangeProfiles {
* within the list returned by
* getSupportedProfiles()
*
- * @see OutputConfiguration#setDynamicRangeProfile(int)
+ * @see OutputConfiguration#setDynamicRangeProfile
*/
- public @NonNull Set<Integer> getProfileCaptureRequestConstraints(@Profile int profile) {
- Set<Integer> ret = mProfileMap.get(profile);
+ public @NonNull Set<Long> getProfileCaptureRequestConstraints(@Profile long profile) {
+ Set<Long> ret = mProfileMap.get(profile);
if (ret == null) {
throw new IllegalArgumentException("Unsupported profile!");
}
return Collections.unmodifiableSet(ret);
}
+
+ /**
+ * Check whether a given dynamic range profile is suitable for latency sensitive use cases.
+ *
+ * <p>Due to internal lookahead logic, camera outputs configured with some dynamic range
+ * profiles may experience additional latency greater than 3 buffers. Using camera outputs
+ * with such profiles for latency sensitive use cases such as camera preview is not
+ * recommended. Profiles that have such extra streaming delay are typically utilized for
+ * scenarios such as offscreen video recording.</p>
+ *
+ * @return true if the given profile is not suitable for latency sensitive use cases, false
+ * otherwise
+ * @throws IllegalArgumentException - If the profile argument is not
+ * within the list returned by
+ * getSupportedProfiles()
+ *
+ * @see OutputConfiguration#setDynamicRangeProfile
+ */
+ public boolean isExtraLatencyPresent(@Profile long profile) {
+ Boolean ret = mLookahedLatencyMap.get(profile);
+ if (ret == null) {
+ throw new IllegalArgumentException("Unsupported profile!");
+ }
+
+ return ret;
+ }
}
diff --git a/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java b/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java
index 8c0dcfcf0693..465abfb55540 100644
--- a/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java
+++ b/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java
@@ -246,7 +246,7 @@ public final class MandatoryStreamCombination {
* @return true if stream is able to output 10-bit pixels
*
* @see android.hardware.camera2.CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_DYNAMIC_RANGE_TEN_BIT
- * @see OutputConfiguration#setDynamicRangeProfile(int)
+ * @see OutputConfiguration#setDynamicRangeProfile
*/
public boolean is10BitCapable() {
return mIs10BitCapable;
diff --git a/core/java/android/hardware/camera2/params/OutputConfiguration.java b/core/java/android/hardware/camera2/params/OutputConfiguration.java
index 80937640e094..2350b7c7a481 100644
--- a/core/java/android/hardware/camera2/params/OutputConfiguration.java
+++ b/core/java/android/hardware/camera2/params/OutputConfiguration.java
@@ -421,7 +421,7 @@ public final class OutputConfiguration implements Parcelable {
* {@link android.media.MediaCodec} etc.)
* or {@link ImageFormat#YCBCR_P010}.</p>
*/
- public void setDynamicRangeProfile(@Profile int profile) {
+ public void setDynamicRangeProfile(@Profile long profile) {
mDynamicRangeProfile = profile;
}
@@ -430,7 +430,7 @@ public final class OutputConfiguration implements Parcelable {
*
* @return the currently set dynamic range profile
*/
- public @Profile int getDynamicRangeProfile() {
+ public @Profile long getDynamicRangeProfile() {
return mDynamicRangeProfile;
}
@@ -1070,7 +1070,7 @@ public final class OutputConfiguration implements Parcelable {
int streamUseCase = source.readInt();
checkArgumentInRange(rotation, ROTATION_0, ROTATION_270, "Rotation constant");
- int dynamicRangeProfile = source.readInt();
+ long dynamicRangeProfile = source.readLong();
DynamicRangeProfiles.checkProfileValue(dynamicRangeProfile);
int timestampBase = source.readInt();
@@ -1217,7 +1217,7 @@ public final class OutputConfiguration implements Parcelable {
dest.writeInt(mIsMultiResolution ? 1 : 0);
// writeList doesn't seem to work well with Integer list.
dest.writeIntArray(convertIntegerToIntList(mSensorPixelModesUsed));
- dest.writeInt(mDynamicRangeProfile);
+ dest.writeLong(mDynamicRangeProfile);
dest.writeInt(mStreamUseCase);
dest.writeInt(mTimestampBase);
dest.writeInt(mMirrorMode);
@@ -1335,7 +1335,7 @@ public final class OutputConfiguration implements Parcelable {
// The sensor pixel modes that this OutputConfiguration will use
private ArrayList<Integer> mSensorPixelModesUsed;
// Dynamic range profile
- private int mDynamicRangeProfile;
+ private long mDynamicRangeProfile;
// Stream use case
private int mStreamUseCase;
// Timestamp base
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index c053c9287fac..c3417310dd40 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -957,7 +957,7 @@ public final class DisplayManager {
public VirtualDisplay createVirtualDisplay(@Nullable IVirtualDevice virtualDevice,
@NonNull VirtualDisplayConfig virtualDisplayConfig,
@Nullable VirtualDisplay.Callback callback,
- @NonNull Executor executor) {
+ @Nullable Executor executor) {
return mGlobal.createVirtualDisplay(mContext, null /* projection */, virtualDevice,
virtualDisplayConfig, callback, executor, null);
}
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index 889100d0f242..a62bbf623000 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -1054,6 +1054,14 @@ public final class DisplayManagerGlobal {
@Nullable private final VirtualDisplay.Callback mCallback;
@Nullable private final Executor mExecutor;
+ /**
+ * Creates a virtual display callback.
+ *
+ * @param callback The callback to call for virtual display events, or {@code null} if the
+ * caller does not wish to receive callback events.
+ * @param executor The executor to call the {@code callback} on. Must not be {@code null} if
+ * the callback is not {@code null}.
+ */
VirtualDisplayCallback(VirtualDisplay.Callback callback, Executor executor) {
mCallback = callback;
mExecutor = mCallback != null ? Objects.requireNonNull(executor) : null;
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index 2fd79cf980c7..c38a847dfb9f 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -1079,16 +1079,24 @@ public final class InputManager {
}
/**
- * Gets the key code produced by the specified location on a US keyboard layout.
- * Key code as defined in {@link android.view.KeyEvent}.
- * This API is only functional for devices with {@link InputDevice#SOURCE_KEYBOARD} available
- * which can alter their key mapping using country specific keyboard layouts.
- *
- * @param deviceId The input device id.
- * @param locationKeyCode The location of a key on a US keyboard layout.
- * @return The key code produced when pressing the key at the specified location, given the
- * active keyboard layout. Returns {@link KeyEvent#KEYCODE_UNKNOWN} if the requested
- * mapping could not be determined, or if an error occurred.
+ * Gets the {@link android.view.KeyEvent key code} produced by the given location on a reference
+ * QWERTY keyboard layout.
+ * <p>
+ * This API is useful for querying the physical location of keys that change the character
+ * produced based on the current locale and keyboard layout.
+ * <p>
+ * @see InputDevice#getKeyCodeForKeyLocation(int) for examples.
+ *
+ * @param locationKeyCode The location of a key specified as a key code on the QWERTY layout.
+ * This provides a consistent way of referring to the physical location of a key independently
+ * of the current keyboard layout. Also see the
+ * <a href="https://www.w3.org/TR/2017/CR-uievents-code-20170601/#key-alphanumeric-writing-system">
+ * hypothetical keyboard</a> provided by the W3C, which may be helpful for identifying the
+ * physical location of a key.
+ * @return The key code produced by the key at the specified location, given the current
+ * keyboard layout. Returns {@link KeyEvent#KEYCODE_UNKNOWN} if the device does not specify
+ * {@link InputDevice#SOURCE_KEYBOARD} or the requested mapping cannot be determined.
+ *
* @hide
*/
public int getKeyCodeForKeyLocation(int deviceId, int locationKeyCode) {
diff --git a/core/java/android/hardware/soundtrigger/SoundTriggerModule.java b/core/java/android/hardware/soundtrigger/SoundTriggerModule.java
index bf4b51431c6f..eed92c12eb83 100644
--- a/core/java/android/hardware/soundtrigger/SoundTriggerModule.java
+++ b/core/java/android/hardware/soundtrigger/SoundTriggerModule.java
@@ -37,6 +37,8 @@ import android.os.Message;
import android.os.RemoteException;
import android.util.Log;
+import java.io.IOException;
+
/**
* The SoundTriggerModule provides APIs to control sound models and sound detection
* on a given sound trigger hardware module.
@@ -137,13 +139,39 @@ public class SoundTriggerModule {
if (model instanceof SoundTrigger.GenericSoundModel) {
SoundModel aidlModel = ConversionUtil.api2aidlGenericSoundModel(
(SoundTrigger.GenericSoundModel) model);
- soundModelHandle[0] = mService.loadModel(aidlModel);
+ try {
+ soundModelHandle[0] = mService.loadModel(aidlModel);
+ } finally {
+ // TODO(b/219825762): We should be able to use the entire object in a
+ // try-with-resources
+ // clause, instead of having to explicitly close internal fields.
+ if (aidlModel.data != null) {
+ try {
+ aidlModel.data.close();
+ } catch (IOException e) {
+ Log.e(TAG, "Failed to close file", e);
+ }
+ }
+ }
return SoundTrigger.STATUS_OK;
}
if (model instanceof SoundTrigger.KeyphraseSoundModel) {
PhraseSoundModel aidlModel = ConversionUtil.api2aidlPhraseSoundModel(
(SoundTrigger.KeyphraseSoundModel) model);
- soundModelHandle[0] = mService.loadPhraseModel(aidlModel);
+ try {
+ soundModelHandle[0] = mService.loadPhraseModel(aidlModel);
+ } finally {
+ // TODO(b/219825762): We should be able to use the entire object in a
+ // try-with-resources
+ // clause, instead of having to explicitly close internal fields.
+ if (aidlModel.common.data != null) {
+ try {
+ aidlModel.common.data.close();
+ } catch (IOException e) {
+ Log.e(TAG, "Failed to close file", e);
+ }
+ }
+ }
return SoundTrigger.STATUS_OK;
}
return SoundTrigger.STATUS_BAD_VALUE;
diff --git a/core/java/android/nfc/cardemulation/CardEmulation.java b/core/java/android/nfc/cardemulation/CardEmulation.java
index 0a9fe90f2524..9a780c8a2935 100644
--- a/core/java/android/nfc/cardemulation/CardEmulation.java
+++ b/core/java/android/nfc/cardemulation/CardEmulation.java
@@ -84,13 +84,6 @@ public final class CardEmulation {
public static final String EXTRA_SERVICE_COMPONENT = "component";
/**
- * The caller userId extra for {@link #ACTION_CHANGE_DEFAULT}.
- *
- * @see #ACTION_CHANGE_DEFAULT
- */
- public static final String EXTRA_USERID = "android.nfc.cardemulation.extra.USERID";
-
- /**
* Category used for NFC payment services.
*/
public static final String CATEGORY_PAYMENT = "payment";
diff --git a/packages/SystemUI/src/com/android/systemui/chooser/ChooserActivity.java b/core/java/android/os/BadTypeParcelableException.java
index 28a38083218e..2ca3bd2adca1 100644
--- a/packages/SystemUI/src/com/android/systemui/chooser/ChooserActivity.java
+++ b/core/java/android/os/BadTypeParcelableException.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,22 +14,17 @@
* limitations under the License.
*/
-package com.android.systemui.chooser;
+package android.os;
-import android.app.Activity;
-import android.os.Bundle;
-
-/**
- * Activity for selecting which application ought to handle an ACTION_SEND intent.
- */
-public final class ChooserActivity extends Activity {
-
- private static final String TAG = "ChooserActivity";
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- ChooserHelper.onChoose(this);
- finish();
+/** Used by Parcel to signal that the type on the payload was not expected by the caller. */
+class BadTypeParcelableException extends BadParcelableException {
+ BadTypeParcelableException(String msg) {
+ super(msg);
+ }
+ BadTypeParcelableException(Exception cause) {
+ super(cause);
+ }
+ BadTypeParcelableException(String msg, Throwable cause) {
+ super(msg, cause);
}
}
diff --git a/core/java/android/os/BaseBundle.java b/core/java/android/os/BaseBundle.java
index 244335d3aa06..45812e551618 100644
--- a/core/java/android/os/BaseBundle.java
+++ b/core/java/android/os/BaseBundle.java
@@ -16,6 +16,8 @@
package android.os;
+import static java.util.Objects.requireNonNull;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
@@ -31,7 +33,7 @@ import com.android.internal.util.IndentingPrintWriter;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Set;
-import java.util.function.Function;
+import java.util.function.BiFunction;
/**
* A mapping from String keys to values of various types. In most cases, you
@@ -254,8 +256,8 @@ public class BaseBundle {
}
try {
return getValueAt(0, String.class);
- } catch (ClassCastException | BadParcelableException e) {
- typeWarning("getPairValue()", /* value */ null, "String", e);
+ } catch (ClassCastException | BadTypeParcelableException e) {
+ typeWarning("getPairValue()", "String", e);
return null;
}
}
@@ -320,28 +322,46 @@ public class BaseBundle {
* This call should always be made after {@link #unparcel()} or inside a lock after making sure
* {@code mMap} is not null.
*
+ * @deprecated Use {@link #getValue(String, Class, Class[])}. This method should only be used in
+ * other deprecated APIs.
+ *
* @hide
*/
+ @Deprecated
+ @Nullable
final Object getValue(String key) {
return getValue(key, /* clazz */ null);
}
+ /** Same as {@link #getValue(String, Class, Class[])} with no item types. */
+ @Nullable
+ final <T> T getValue(String key, @Nullable Class<T> clazz) {
+ // Avoids allocating Class[0] array
+ return getValue(key, clazz, (Class<?>[]) null);
+ }
+
/**
- * Returns the value for key {@code key} for expected return type {@param clazz} (or {@code
+ * Returns the value for key {@code key} for expected return type {@code clazz} (or pass {@code
* null} for no type check).
*
+ * For {@code itemTypes}, see {@link Parcel#readValue(int, ClassLoader, Class, Class[])}.
+ *
* This call should always be made after {@link #unparcel()} or inside a lock after making sure
* {@code mMap} is not null.
*
* @hide
*/
- final <T> T getValue(String key, @Nullable Class<T> clazz) {
+ @Nullable
+ final <T> T getValue(String key, @Nullable Class<T> clazz, @Nullable Class<?>... itemTypes) {
int i = mMap.indexOfKey(key);
- return (i >= 0) ? getValueAt(i, clazz) : null;
+ return (i >= 0) ? getValueAt(i, clazz, itemTypes) : null;
}
/**
- * Returns the value for a certain position in the array map.
+ * Returns the value for a certain position in the array map for expected return type {@code
+ * clazz} (or pass {@code null} for no type check).
+ *
+ * For {@code itemTypes}, see {@link Parcel#readValue(int, ClassLoader, Class, Class[])}.
*
* This call should always be made after {@link #unparcel()} or inside a lock after making sure
* {@code mMap} is not null.
@@ -349,11 +369,12 @@ public class BaseBundle {
* @hide
*/
@SuppressWarnings("unchecked")
- final <T> T getValueAt(int i, @Nullable Class<T> clazz) {
+ @Nullable
+ final <T> T getValueAt(int i, @Nullable Class<T> clazz, @Nullable Class<?>... itemTypes) {
Object object = mMap.valueAt(i);
- if (object instanceof Function<?, ?>) {
+ if (object instanceof BiFunction<?, ?, ?>) {
try {
- object = ((Function<Class<?>, ?>) object).apply(clazz);
+ object = ((BiFunction<Class<?>, Class<?>[], ?>) object).apply(clazz, itemTypes);
} catch (BadParcelableException e) {
if (sShouldDefuse) {
Log.w(TAG, "Failed to parse item " + mMap.keyAt(i) + ", returning null.", e);
@@ -615,7 +636,11 @@ public class BaseBundle {
*
* @param key a String key
* @return an Object, or null
+ *
+ * @deprecated Use the type-safe specific APIs depending on the type of the item to be
+ * retrieved, eg. {@link #getString(String)}.
*/
+ @Deprecated
@Nullable
public Object get(String key) {
unparcel();
@@ -623,6 +648,32 @@ public class BaseBundle {
}
/**
+ * Returns the object of type {@code clazz} for the given {@code key}, or {@code null} if:
+ * <ul>
+ * <li>No mapping of the desired type exists for the given key.
+ * <li>A {@code null} value is explicitly associated with the key.
+ * <li>The object is not of type {@code clazz}.
+ * </ul>
+ *
+ * <p>Use the more specific APIs where possible, especially in the case of containers such as
+ * lists, since those APIs allow you to specify the type of the items.
+ *
+ * @param key String key
+ * @param clazz The type of the object expected
+ * @return an Object, or null
+ */
+ @Nullable
+ <T> T get(@Nullable String key, @NonNull Class<T> clazz) {
+ unparcel();
+ try {
+ return getValue(key, requireNonNull(clazz));
+ } catch (ClassCastException | BadTypeParcelableException e) {
+ typeWarning(key, clazz.getCanonicalName(), e);
+ return null;
+ }
+ }
+
+ /**
* Removes any entry with the given key from the mapping of this Bundle.
*
* @param key a String key
@@ -1006,7 +1057,7 @@ public class BaseBundle {
sb.append(" but value was a ");
sb.append(value.getClass().getName());
} else {
- sb.append(" but value was of a different type ");
+ sb.append(" but value was of a different type");
}
sb.append(". The default value ");
sb.append(defaultValue);
@@ -1019,6 +1070,10 @@ public class BaseBundle {
typeWarning(key, value, className, "<null>", e);
}
+ void typeWarning(String key, String className, RuntimeException e) {
+ typeWarning(key, /* value */ null, className, "<null>", e);
+ }
+
/**
* Returns the value associated with the given key, or defaultValue if
* no mapping of the desired type exists for the given key.
@@ -1358,7 +1413,11 @@ public class BaseBundle {
*
* @param key a String, or null
* @return a Serializable value, or null
+ *
+ * @deprecated Use {@link #getSerializable(String, Class)}. This method should only be used in
+ * other deprecated APIs.
*/
+ @Deprecated
@Nullable
Serializable getSerializable(@Nullable String key) {
unparcel();
@@ -1375,6 +1434,36 @@ public class BaseBundle {
}
/**
+ * Returns the value associated with the given key, or {@code null} if:
+ * <ul>
+ * <li>No mapping of the desired type exists for the given key.
+ * <li>A {@code null} value is explicitly associated with the key.
+ * <li>The object is not of type {@code clazz}.
+ * </ul>
+ *
+ * @param key a String, or null
+ * @param clazz The expected class of the returned type
+ * @return a Serializable value, or null
+ */
+ @Nullable
+ <T extends Serializable> T getSerializable(@Nullable String key, @NonNull Class<T> clazz) {
+ return get(key, clazz);
+ }
+
+
+ @SuppressWarnings("unchecked")
+ @Nullable
+ <T> ArrayList<T> getArrayList(@Nullable String key, @NonNull Class<T> clazz) {
+ unparcel();
+ try {
+ return getValue(key, ArrayList.class, requireNonNull(clazz));
+ } catch (ClassCastException | BadTypeParcelableException e) {
+ typeWarning(key, "ArrayList<" + clazz.getCanonicalName() + ">", e);
+ return null;
+ }
+ }
+
+ /**
* Returns the value associated with the given key, or null if
* no mapping of the desired type exists for the given key or a null
* value is explicitly associated with the key.
@@ -1384,17 +1473,7 @@ public class BaseBundle {
*/
@Nullable
ArrayList<Integer> getIntegerArrayList(@Nullable String key) {
- unparcel();
- Object o = getValue(key);
- if (o == null) {
- return null;
- }
- try {
- return (ArrayList<Integer>) o;
- } catch (ClassCastException e) {
- typeWarning(key, o, "ArrayList<Integer>", e);
- return null;
- }
+ return getArrayList(key, Integer.class);
}
/**
@@ -1407,17 +1486,7 @@ public class BaseBundle {
*/
@Nullable
ArrayList<String> getStringArrayList(@Nullable String key) {
- unparcel();
- Object o = getValue(key);
- if (o == null) {
- return null;
- }
- try {
- return (ArrayList<String>) o;
- } catch (ClassCastException e) {
- typeWarning(key, o, "ArrayList<String>", e);
- return null;
- }
+ return getArrayList(key, String.class);
}
/**
@@ -1430,17 +1499,7 @@ public class BaseBundle {
*/
@Nullable
ArrayList<CharSequence> getCharSequenceArrayList(@Nullable String key) {
- unparcel();
- Object o = getValue(key);
- if (o == null) {
- return null;
- }
- try {
- return (ArrayList<CharSequence>) o;
- } catch (ClassCastException e) {
- typeWarning(key, o, "ArrayList<CharSequence>", e);
- return null;
- }
+ return getArrayList(key, CharSequence.class);
}
/**
diff --git a/core/java/android/os/Bundle.java b/core/java/android/os/Bundle.java
index 2b13f20ffe6d..edbbb59ac77d 100644
--- a/core/java/android/os/Bundle.java
+++ b/core/java/android/os/Bundle.java
@@ -20,6 +20,7 @@ import static java.util.Objects.requireNonNull;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.compat.annotation.UnsupportedAppUsage;
import android.util.ArrayMap;
import android.util.Size;
@@ -876,7 +877,7 @@ public final class Bundle extends BaseBundle implements Cloneable, Parcelable {
@Nullable
public Bundle getBundle(@Nullable String key) {
unparcel();
- Object o = getValue(key);
+ Object o = mMap.get(key);
if (o == null) {
return null;
}
@@ -899,7 +900,11 @@ public final class Bundle extends BaseBundle implements Cloneable, Parcelable {
*
* @param key a String, or {@code null}
* @return a Parcelable value, or {@code null}
+ *
+ * @deprecated Use the type-safer {@link #getParcelable(String, Class)} starting from Android
+ * {@link Build.VERSION_CODES#TIRAMISU}.
*/
+ @Deprecated
@Nullable
public <T extends Parcelable> T getParcelable(@Nullable String key) {
unparcel();
@@ -916,30 +921,28 @@ public final class Bundle extends BaseBundle implements Cloneable, Parcelable {
}
/**
- * Returns the value associated with the given key, or {@code null} if
- * no mapping of the desired type exists for the given key or a {@code null}
- * value is explicitly associated with the key.
+ * Returns the value associated with the given key or {@code null} if:
+ * <ul>
+ * <li>No mapping of the desired type exists for the given key.
+ * <li>A {@code null} value is explicitly associated with the key.
+ * <li>The object is not of type {@code clazz}.
+ * </ul>
*
* <p><b>Note: </b> if the expected value is not a class provided by the Android platform,
* you must call {@link #setClassLoader(ClassLoader)} with the proper {@link ClassLoader} first.
* Otherwise, this method might throw an exception or return {@code null}.
*
* @param key a String, or {@code null}
- * @param clazz The type of the object expected or {@code null} for performing no checks.
+ * @param clazz The type of the object expected
* @return a Parcelable value, or {@code null}
- *
- * @hide
*/
@SuppressWarnings("unchecked")
@Nullable
public <T> T getParcelable(@Nullable String key, @NonNull Class<T> clazz) {
- unparcel();
- try {
- return getValue(key, requireNonNull(clazz));
- } catch (ClassCastException | BadParcelableException e) {
- typeWarning(key, /* value */ null, "Parcelable", e);
- return null;
- }
+ // The reason for not using <T extends Parcelable> is because the caller could provide a
+ // super class to restrict the children that doesn't implement Parcelable itself while the
+ // children do, more details at b/210800751 (same reasoning applies here).
+ return get(key, clazz);
}
/**
@@ -953,7 +956,11 @@ public final class Bundle extends BaseBundle implements Cloneable, Parcelable {
*
* @param key a String, or {@code null}
* @return a Parcelable[] value, or {@code null}
+ *
+ * @deprecated Use the type-safer {@link #getParcelableArray(String, Class)} starting from
+ * Android {@link Build.VERSION_CODES#TIRAMISU}.
*/
+ @Deprecated
@Nullable
public Parcelable[] getParcelableArray(@Nullable String key) {
unparcel();
@@ -970,6 +977,39 @@ public final class Bundle extends BaseBundle implements Cloneable, Parcelable {
}
/**
+ * Returns the value associated with the given key, or {@code null} if:
+ * <ul>
+ * <li>No mapping of the desired type exists for the given key.
+ * <li>A {@code null} value is explicitly associated with the key.
+ * <li>The object is not of type {@code clazz}.
+ * </ul>
+ *
+ * <p><b>Note: </b> if the expected value is not a class provided by the Android platform,
+ * you must call {@link #setClassLoader(ClassLoader)} with the proper {@link ClassLoader} first.
+ * Otherwise, this method might throw an exception or return {@code null}.
+ *
+ * @param key a String, or {@code null}
+ * @param clazz The type of the items inside the array
+ * @return a Parcelable[] value, or {@code null}
+ */
+ @SuppressLint({"ArrayReturn", "NullableCollection"})
+ @SuppressWarnings("unchecked")
+ @Nullable
+ public <T> T[] getParcelableArray(@Nullable String key, @NonNull Class<T> clazz) {
+ // The reason for not using <T extends Parcelable> is because the caller could provide a
+ // super class to restrict the children that doesn't implement Parcelable itself while the
+ // children do, more details at b/210800751 (same reasoning applies here).
+ unparcel();
+ try {
+ // In Java 12, we can pass clazz.arrayType() instead of Parcelable[] and later casting.
+ return (T[]) getValue(key, Parcelable[].class, requireNonNull(clazz));
+ } catch (ClassCastException | BadTypeParcelableException e) {
+ typeWarning(key, clazz.getCanonicalName() + "[]", e);
+ return null;
+ }
+ }
+
+ /**
* Returns the value associated with the given key, or {@code null} if
* no mapping of the desired type exists for the given key or a {@code null}
* value is explicitly associated with the key.
@@ -980,7 +1020,11 @@ public final class Bundle extends BaseBundle implements Cloneable, Parcelable {
*
* @param key a String, or {@code null}
* @return an ArrayList<T> value, or {@code null}
+ *
+ * @deprecated Use the type-safer {@link #getParcelable(String, Class)} starting from Android
+ * {@link Build.VERSION_CODES#TIRAMISU}.
*/
+ @Deprecated
@Nullable
public <T extends Parcelable> ArrayList<T> getParcelableArrayList(@Nullable String key) {
unparcel();
@@ -997,14 +1041,43 @@ public final class Bundle extends BaseBundle implements Cloneable, Parcelable {
}
/**
+ * Returns the value associated with the given key, or {@code null} if:
+ * <ul>
+ * <li>No mapping of the desired type exists for the given key.
+ * <li>A {@code null} value is explicitly associated with the key.
+ * <li>The object is not of type {@code clazz}.
+ * </ul>
+ *
+ * <p><b>Note: </b> if the expected value is not a class provided by the Android platform,
+ * you must call {@link #setClassLoader(ClassLoader)} with the proper {@link ClassLoader} first.
+ * Otherwise, this method might throw an exception or return {@code null}.
+ *
+ * @param key a String, or {@code null}
+ * @param clazz The type of the items inside the array list
+ * @return an ArrayList<T> value, or {@code null}
+ */
+ @SuppressLint("NullableCollection")
+ @SuppressWarnings("unchecked")
+ @Nullable
+ public <T> ArrayList<T> getParcelableArrayList(@Nullable String key, @NonNull Class<T> clazz) {
+ // The reason for not using <T extends Parcelable> is because the caller could provide a
+ // super class to restrict the children that doesn't implement Parcelable itself while the
+ // children do, more details at b/210800751 (same reasoning applies here).
+ return getArrayList(key, clazz);
+ }
+
+ /**
* Returns the value associated with the given key, or null if
* no mapping of the desired type exists for the given key or a null
* value is explicitly associated with the key.
*
* @param key a String, or null
- *
* @return a SparseArray of T values, or null
+ *
+ * @deprecated Use the type-safer {@link #getSparseParcelableArray(String, Class)} starting from
+ * Android {@link Build.VERSION_CODES#TIRAMISU}.
*/
+ @Deprecated
@Nullable
public <T extends Parcelable> SparseArray<T> getSparseParcelableArray(@Nullable String key) {
unparcel();
@@ -1021,13 +1094,44 @@ public final class Bundle extends BaseBundle implements Cloneable, Parcelable {
}
/**
+ * Returns the value associated with the given key, or {@code null} if:
+ * <ul>
+ * <li>No mapping of the desired type exists for the given key.
+ * <li>A {@code null} value is explicitly associated with the key.
+ * <li>The object is not of type {@code clazz}.
+ * </ul>
+ *
+ * @param key a String, or null
+ * @return a SparseArray of T values, or null
+ */
+ @SuppressWarnings("unchecked")
+ @Nullable
+ public <T> SparseArray<T> getSparseParcelableArray(@Nullable String key,
+ @NonNull Class<T> clazz) {
+ // The reason for not using <T extends Parcelable> is because the caller could provide a
+ // super class to restrict the children that doesn't implement Parcelable itself while the
+ // children do, more details at b/210800751 (same reasoning applies here).
+ unparcel();
+ try {
+ return (SparseArray<T>) getValue(key, SparseArray.class, requireNonNull(clazz));
+ } catch (ClassCastException | BadTypeParcelableException e) {
+ typeWarning(key, "SparseArray<" + clazz.getCanonicalName() + ">", e);
+ return null;
+ }
+ }
+
+ /**
* Returns the value associated with the given key, or null if
* no mapping of the desired type exists for the given key or a null
* value is explicitly associated with the key.
*
* @param key a String, or null
* @return a Serializable value, or null
+ *
+ * @deprecated Use the type-safer {@link #getSerializable(String, Class)} starting from Android
+ * {@link Build.VERSION_CODES#TIRAMISU}.
*/
+ @Deprecated
@Override
@Nullable
public Serializable getSerializable(@Nullable String key) {
@@ -1035,6 +1139,24 @@ public final class Bundle extends BaseBundle implements Cloneable, Parcelable {
}
/**
+ * Returns the value associated with the given key, or {@code null} if:
+ * <ul>
+ * <li>No mapping of the desired type exists for the given key.
+ * <li>A {@code null} value is explicitly associated with the key.
+ * <li>The object is not of type {@code clazz}.
+ * </ul>
+ *
+ * @param key a String, or null
+ * @param clazz The expected class of the returned type
+ * @return a Serializable value, or null
+ */
+ @Nullable
+ public <T extends Serializable> T getSerializable(@Nullable String key,
+ @NonNull Class<T> clazz) {
+ return super.getSerializable(key, requireNonNull(clazz));
+ }
+
+ /**
* Returns the value associated with the given key, or null if
* no mapping of the desired type exists for the given key or a null
* value is explicitly associated with the key.
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index ae923530b651..09cfb6e70cba 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -16,6 +16,8 @@
package android.os;
+import static com.android.internal.util.Preconditions.checkArgument;
+
import static java.util.Objects.requireNonNull;
import android.annotation.IntDef;
@@ -65,6 +67,7 @@ import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
+import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.IntFunction;
import java.util.function.Supplier;
@@ -287,26 +290,26 @@ public final class Parcel {
private static final int VAL_NULL = -1;
private static final int VAL_STRING = 0;
private static final int VAL_INTEGER = 1;
- private static final int VAL_MAP = 2;
+ private static final int VAL_MAP = 2; // length-prefixed
private static final int VAL_BUNDLE = 3;
- private static final int VAL_PARCELABLE = 4;
+ private static final int VAL_PARCELABLE = 4; // length-prefixed
private static final int VAL_SHORT = 5;
private static final int VAL_LONG = 6;
private static final int VAL_FLOAT = 7;
private static final int VAL_DOUBLE = 8;
private static final int VAL_BOOLEAN = 9;
private static final int VAL_CHARSEQUENCE = 10;
- private static final int VAL_LIST = 11;
- private static final int VAL_SPARSEARRAY = 12;
+ private static final int VAL_LIST = 11; // length-prefixed
+ private static final int VAL_SPARSEARRAY = 12; // length-prefixed
private static final int VAL_BYTEARRAY = 13;
private static final int VAL_STRINGARRAY = 14;
private static final int VAL_IBINDER = 15;
- private static final int VAL_PARCELABLEARRAY = 16;
- private static final int VAL_OBJECTARRAY = 17;
+ private static final int VAL_PARCELABLEARRAY = 16; // length-prefixed
+ private static final int VAL_OBJECTARRAY = 17; // length-prefixed
private static final int VAL_INTARRAY = 18;
private static final int VAL_LONGARRAY = 19;
private static final int VAL_BYTE = 20;
- private static final int VAL_SERIALIZABLE = 21;
+ private static final int VAL_SERIALIZABLE = 21; // length-prefixed
private static final int VAL_SPARSEBOOLEANARRAY = 22;
private static final int VAL_BOOLEANARRAY = 23;
private static final int VAL_CHARSEQUENCEARRAY = 24;
@@ -3179,8 +3182,7 @@ public final class Parcel {
*/
@Deprecated
public final void readMap(@NonNull Map outVal, @Nullable ClassLoader loader) {
- int n = readInt();
- readMapInternal(outVal, n, loader, /* clazzKey */ null, /* clazzValue */ null);
+ readMapInternal(outVal, loader, /* clazzKey */ null, /* clazzValue */ null);
}
/**
@@ -3195,8 +3197,7 @@ public final class Parcel {
@NonNull Class<V> clazzValue) {
Objects.requireNonNull(clazzKey);
Objects.requireNonNull(clazzValue);
- int n = readInt();
- readMapInternal(outVal, n, loader, clazzKey, clazzValue);
+ readMapInternal(outVal, loader, clazzKey, clazzValue);
}
/**
@@ -3245,13 +3246,7 @@ public final class Parcel {
@Deprecated
@Nullable
public HashMap readHashMap(@Nullable ClassLoader loader) {
- int n = readInt();
- if (n < 0) {
- return null;
- }
- HashMap m = new HashMap(n);
- readMapInternal(m, n, loader, /* clazzKey */ null, /* clazzValue */ null);
- return m;
+ return readHashMapInternal(loader, /* clazzKey */ null, /* clazzValue */ null);
}
/**
@@ -3267,13 +3262,7 @@ public final class Parcel {
@NonNull Class<? extends K> clazzKey, @NonNull Class<? extends V> clazzValue) {
Objects.requireNonNull(clazzKey);
Objects.requireNonNull(clazzValue);
- int n = readInt();
- if (n < 0) {
- return null;
- }
- HashMap<K, V> map = new HashMap<>(n);
- readMapInternal(map, n, loader, clazzKey, clazzValue);
- return map;
+ return readHashMapInternal(loader, clazzKey, clazzValue);
}
/**
@@ -4296,16 +4285,17 @@ public final class Parcel {
/**
- * @param clazz The type of the object expected or {@code null} for performing no checks.
+ * @see #readValue(int, ClassLoader, Class, Class[])
*/
@Nullable
- private <T> T readValue(@Nullable ClassLoader loader, @Nullable Class<T> clazz) {
+ private <T> T readValue(@Nullable ClassLoader loader, @Nullable Class<T> clazz,
+ @Nullable Class<?>... itemTypes) {
int type = readInt();
final T object;
if (isLengthPrefixed(type)) {
int length = readInt();
int start = dataPosition();
- object = readValue(type, loader, clazz);
+ object = readValue(type, loader, clazz, itemTypes);
int actual = dataPosition() - start;
if (actual != length) {
Slog.wtfStack(TAG,
@@ -4313,25 +4303,26 @@ public final class Parcel {
+ " consumed " + actual + " bytes, but " + length + " expected.");
}
} else {
- object = readValue(type, loader, clazz);
+ object = readValue(type, loader, clazz, itemTypes);
}
return object;
}
/**
- * This will return a {@link Function} for length-prefixed types that deserializes the object
- * when {@link Function#apply} is called with the expected class of the return object (or {@code
- * null} for no type check), for other types it will return the object itself.
+ * This will return a {@link BiFunction} for length-prefixed types that deserializes the object
+ * when {@link BiFunction#apply} is called (the arguments correspond to the ones of {@link
+ * #readValue(int, ClassLoader, Class, Class[])} after the class loader), for other types it
+ * will return the object itself.
*
- * <p>After calling {@link Function#apply(Object)} the parcel cursor will not change. Note that
- * you shouldn't recycle the parcel, not at least until all objects have been retrieved. No
+ * <p>After calling {@link BiFunction#apply} the parcel cursor will not change. Note that you
+ * shouldn't recycle the parcel, not at least until all objects have been retrieved. No
* synchronization attempts are made.
*
* </p>The function returned implements {@link #equals(Object)} and {@link #hashCode()}. Two
* function objects are equal if either of the following is true:
* <ul>
- * <li>{@link Function#apply} has been called on both and both objects returned are equal.
- * <li>{@link Function#apply} hasn't been called on either one and everything below is true:
+ * <li>{@link BiFunction#apply} has been called on both and both objects returned are equal.
+ * <li>{@link BiFunction#apply} hasn't been called on either one and everything below is true:
* <ul>
* <li>The {@code loader} parameters used to retrieve each are equal.
* <li>They both have the same type.
@@ -4358,7 +4349,7 @@ public final class Parcel {
}
- private static final class LazyValue implements Function<Class<?>, Object> {
+ private static final class LazyValue implements BiFunction<Class<?>, Class<?>[], Object> {
/**
* | 4B | 4B |
* mSource = Parcel{... | type | length | object | ...}
@@ -4390,7 +4381,7 @@ public final class Parcel {
}
@Override
- public Object apply(@Nullable Class<?> clazz) {
+ public Object apply(@Nullable Class<?> clazz, @Nullable Class<?>[] itemTypes) {
Parcel source = mSource;
if (source != null) {
synchronized (source) {
@@ -4399,7 +4390,7 @@ public final class Parcel {
int restore = source.dataPosition();
try {
source.setDataPosition(mPosition);
- mObject = source.readValue(mLoader, clazz);
+ mObject = source.readValue(mLoader, clazz, itemTypes);
} finally {
source.setDataPosition(restore);
}
@@ -4479,14 +4470,25 @@ public final class Parcel {
}
}
+ /** Same as {@link #readValue(ClassLoader, Class, Class[])} without any item types. */
+ private <T> T readValue(int type, @Nullable ClassLoader loader, @Nullable Class<T> clazz) {
+ // Avoids allocating Class[0] array
+ return readValue(type, loader, clazz, (Class<?>[]) null);
+ }
+
/**
* Reads a value from the parcel of type {@code type}. Does NOT read the int representing the
* type first.
+ *
* @param clazz The type of the object expected or {@code null} for performing no checks.
+ * @param itemTypes If the value is a container, these represent the item types (eg. for a list
+ * it's the item type, for a map, it's the key type, followed by the value
+ * type).
*/
@SuppressWarnings("unchecked")
@Nullable
- private <T> T readValue(int type, @Nullable ClassLoader loader, @Nullable Class<T> clazz) {
+ private <T> T readValue(int type, @Nullable ClassLoader loader, @Nullable Class<T> clazz,
+ @Nullable Class<?>... itemTypes) {
final Object object;
switch (type) {
case VAL_NULL:
@@ -4502,7 +4504,11 @@ public final class Parcel {
break;
case VAL_MAP:
- object = readHashMap(loader);
+ checkTypeToUnparcel(clazz, HashMap.class);
+ Class<?> keyType = ArrayUtils.getOrNull(itemTypes, 0);
+ Class<?> valueType = ArrayUtils.getOrNull(itemTypes, 1);
+ checkArgument((keyType == null) == (valueType == null));
+ object = readHashMapInternal(loader, keyType, valueType);
break;
case VAL_PARCELABLE:
@@ -4533,10 +4539,12 @@ public final class Parcel {
object = readCharSequence();
break;
- case VAL_LIST:
- object = readArrayList(loader);
+ case VAL_LIST: {
+ checkTypeToUnparcel(clazz, ArrayList.class);
+ Class<?> itemType = ArrayUtils.getOrNull(itemTypes, 0);
+ object = readArrayListInternal(loader, itemType);
break;
-
+ }
case VAL_BOOLEANARRAY:
object = createBooleanArray();
break;
@@ -4557,10 +4565,12 @@ public final class Parcel {
object = readStrongBinder();
break;
- case VAL_OBJECTARRAY:
- object = readArray(loader);
+ case VAL_OBJECTARRAY: {
+ Class<?> itemType = ArrayUtils.getOrNull(itemTypes, 0);
+ checkArrayTypeToUnparcel(clazz, (itemType != null) ? itemType : Object.class);
+ object = readArrayInternal(loader, itemType);
break;
-
+ }
case VAL_INTARRAY:
object = createIntArray();
break;
@@ -4577,14 +4587,18 @@ public final class Parcel {
object = readSerializableInternal(loader, clazz);
break;
- case VAL_PARCELABLEARRAY:
- object = readParcelableArray(loader);
+ case VAL_PARCELABLEARRAY: {
+ Class<?> itemType = ArrayUtils.getOrNull(itemTypes, 0);
+ checkArrayTypeToUnparcel(clazz, (itemType != null) ? itemType : Parcelable.class);
+ object = readParcelableArrayInternal(loader, itemType);
break;
-
- case VAL_SPARSEARRAY:
- object = readSparseArray(loader);
+ }
+ case VAL_SPARSEARRAY: {
+ checkTypeToUnparcel(clazz, SparseArray.class);
+ Class<?> itemType = ArrayUtils.getOrNull(itemTypes, 0);
+ object = readSparseArrayInternal(loader, itemType);
break;
-
+ }
case VAL_SPARSEBOOLEANARRAY:
object = readSparseBooleanArray();
break;
@@ -4632,7 +4646,7 @@ public final class Parcel {
+ " at offset " + off);
}
if (object != null && clazz != null && !clazz.isInstance(object)) {
- throw new BadParcelableException("Unparcelled object " + object
+ throw new BadTypeParcelableException("Unparcelled object " + object
+ " is not an instance of required class " + clazz.getName()
+ " provided in the parameter");
}
@@ -4659,6 +4673,38 @@ public final class Parcel {
}
/**
+ * Checks that an array of type T[], where T is {@code componentTypeToUnparcel}, is a subtype of
+ * {@code requiredArrayType}.
+ */
+ private void checkArrayTypeToUnparcel(@Nullable Class<?> requiredArrayType,
+ Class<?> componentTypeToUnparcel) {
+ if (requiredArrayType != null) {
+ // In Java 12, we could use componentTypeToUnparcel.arrayType() for the check
+ Class<?> requiredComponentType = requiredArrayType.getComponentType();
+ if (requiredComponentType == null) {
+ throw new BadTypeParcelableException(
+ "About to unparcel an array but type "
+ + requiredArrayType.getCanonicalName()
+ + " required by caller is not an array.");
+ }
+ checkTypeToUnparcel(requiredComponentType, componentTypeToUnparcel);
+ }
+ }
+
+ /**
+ * Checks that {@code typeToUnparcel} is a subtype of {@code requiredType}, if {@code
+ * requiredType} is not {@code null}.
+ */
+ private void checkTypeToUnparcel(@Nullable Class<?> requiredType, Class<?> typeToUnparcel) {
+ if (requiredType != null && !requiredType.isAssignableFrom(typeToUnparcel)) {
+ throw new BadTypeParcelableException(
+ "About to unparcel a " + typeToUnparcel.getCanonicalName()
+ + ", which is not a subtype of type " + requiredType.getCanonicalName()
+ + " required by caller.");
+ }
+ }
+
+ /**
* Read and return a new Parcelable from the parcel. The given class loader
* will be used to load any enclosed Parcelables. If it is null, the default
* class loader will be used.
@@ -4788,7 +4834,7 @@ public final class Parcel {
if (clazz != null) {
Class<?> parcelableClass = creator.getClass().getEnclosingClass();
if (!clazz.isAssignableFrom(parcelableClass)) {
- throw new BadParcelableException("Parcelable creator " + name + " is not "
+ throw new BadTypeParcelableException("Parcelable creator " + name + " is not "
+ "a subclass of required class " + clazz.getName()
+ " provided in the parameter");
}
@@ -4811,7 +4857,7 @@ public final class Parcel {
}
if (clazz != null) {
if (!clazz.isAssignableFrom(parcelableClass)) {
- throw new BadParcelableException("Parcelable creator " + name + " is not "
+ throw new BadTypeParcelableException("Parcelable creator " + name + " is not "
+ "a subclass of required class " + clazz.getName()
+ " provided in the parameter");
}
@@ -4872,15 +4918,7 @@ public final class Parcel {
@Deprecated
@Nullable
public Parcelable[] readParcelableArray(@Nullable ClassLoader loader) {
- int N = readInt();
- if (N < 0) {
- return null;
- }
- Parcelable[] p = new Parcelable[N];
- for (int i = 0; i < N; i++) {
- p[i] = readParcelable(loader);
- }
- return p;
+ return readParcelableArrayInternal(loader, /* clazz */ null);
}
/**
@@ -4892,14 +4930,20 @@ public final class Parcel {
* trying to instantiate an element.
*/
@SuppressLint({"ArrayReturn", "NullableCollection"})
- @SuppressWarnings("unchecked")
@Nullable
public <T> T[] readParcelableArray(@Nullable ClassLoader loader, @NonNull Class<T> clazz) {
+ return readParcelableArrayInternal(loader, requireNonNull(clazz));
+ }
+
+ @SuppressWarnings("unchecked")
+ @Nullable
+ private <T> T[] readParcelableArrayInternal(@Nullable ClassLoader loader,
+ @Nullable Class<T> clazz) {
int n = readInt();
if (n < 0) {
return null;
}
- T[] p = (T[]) Array.newInstance(clazz, n);
+ T[] p = (T[]) ((clazz == null) ? new Parcelable[n] : Array.newInstance(clazz, n));
for (int i = 0; i < n; i++) {
p[i] = readParcelableInternal(loader, clazz);
}
@@ -4962,7 +5006,7 @@ public final class Parcel {
// the class the same way as ObjectInputStream, using the provided classloader.
Class<?> cl = Class.forName(name, false, loader);
if (!clazz.isAssignableFrom(cl)) {
- throw new BadParcelableException("Serializable object "
+ throw new BadTypeParcelableException("Serializable object "
+ cl.getName() + " is not a subclass of required class "
+ clazz.getName() + " provided in the parameter");
}
@@ -4987,7 +5031,7 @@ public final class Parcel {
// the deserialized object, as we cannot resolve the class the same way as
// ObjectInputStream.
if (!clazz.isAssignableFrom(object.getClass())) {
- throw new BadParcelableException("Serializable object "
+ throw new BadTypeParcelableException("Serializable object "
+ object.getClass().getName() + " is not a subclass of required class "
+ clazz.getName() + " provided in the parameter");
}
@@ -5097,7 +5141,26 @@ public final class Parcel {
readMapInternal(outVal, n, loader, /* clazzKey */null, /* clazzValue */null);
}
- /* package */ <K, V> void readMapInternal(@NonNull Map<? super K, ? super V> outVal, int n,
+ @Nullable
+ private <K, V> HashMap<K, V> readHashMapInternal(@Nullable ClassLoader loader,
+ @NonNull Class<? extends K> clazzKey, @NonNull Class<? extends V> clazzValue) {
+ int n = readInt();
+ if (n < 0) {
+ return null;
+ }
+ HashMap<K, V> map = new HashMap<>(n);
+ readMapInternal(map, n, loader, clazzKey, clazzValue);
+ return map;
+ }
+
+ private <K, V> void readMapInternal(@NonNull Map<? super K, ? super V> outVal,
+ @Nullable ClassLoader loader, @Nullable Class<K> clazzKey,
+ @Nullable Class<V> clazzValue) {
+ int n = readInt();
+ readMapInternal(outVal, n, loader, clazzKey, clazzValue);
+ }
+
+ private <K, V> void readMapInternal(@NonNull Map<? super K, ? super V> outVal, int n,
@Nullable ClassLoader loader, @Nullable Class<K> clazzKey,
@Nullable Class<V> clazzValue) {
while (n > 0) {
@@ -5108,7 +5171,7 @@ public final class Parcel {
}
}
- /* package */ void readArrayMapInternal(@NonNull ArrayMap<? super String, Object> outVal,
+ private void readArrayMapInternal(@NonNull ArrayMap<? super String, Object> outVal,
int size, @Nullable ClassLoader loader) {
readArrayMap(outVal, size, /* sorted */ true, /* lazy */ false, loader);
}
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index 315eef78724e..e3be4d32a014 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -183,25 +183,29 @@ public final class PowerManager {
/**
* Wake lock flag: Turn the screen on when the wake lock is acquired.
* <p>
- * Normally wake locks don't actually wake the device, they just cause
- * the screen to remain on once it's already on. Think of the video player
- * application as the normal behavior. Notifications that pop up and want
- * the device to be on are the exception; use this flag to be like them.
+ * Normally wake locks don't actually wake the device, they just cause the screen to remain on
+ * once it's already on. This flag will cause the device to wake up when the wake lock is
+ * acquired.
* </p><p>
* Android TV playback devices attempt to turn on the HDMI-connected TV via HDMI-CEC on any
* wake-up, including wake-ups triggered by wake locks.
* </p><p>
* Cannot be used with {@link #PARTIAL_WAKE_LOCK}.
* </p>
+ *
+ * @deprecated Most applications should use {@link android.R.attr#turnScreenOn} or
+ * {@link android.app.Activity#setTurnScreenOn(boolean)} instead, as this prevents the previous
+ * foreground app from being resumed first when the screen turns on. Note that this flag may
+ * require a permission in the future.
*/
+ @Deprecated
public static final int ACQUIRE_CAUSES_WAKEUP = 0x10000000;
/**
* Wake lock flag: When this wake lock is released, poke the user activity timer
* so the screen stays on for a little longer.
* <p>
- * Will not turn the screen on if it is not already on.
- * See {@link #ACQUIRE_CAUSES_WAKEUP} if you want that.
+ * This will not turn the screen on if it is not already on.
* </p><p>
* Cannot be used with {@link #PARTIAL_WAKE_LOCK}.
* </p>
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index cc9833997102..48e2827f19e1 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -3022,9 +3022,17 @@ public class StorageManager {
}
}
- /** @hide */
- @TestApi
+ /**
+ * Returns the authority of the current cloud media provider that was set by the
+ * {@link android.service.storage.ExternalStorageService} holding the
+ * {@link android.Manifest.permission#WRITE_MEDIA_STORAGE} permission via
+ * {@link #setCloudMediaProvider(String)}.
+ *
+ * @hide
+ */
@Nullable
+ @TestApi
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
public String getCloudMediaProvider() {
try {
return mStorageManager.getCloudMediaProvider();
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 23e02e9d9968..528156fb8f4e 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -24,7 +24,6 @@ import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
-import android.annotation.StringDef;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.TestApi;
@@ -2128,7 +2127,7 @@ public final class Settings {
/**
* Intent extra: The id of a setting restricted by supervisors.
* <p>
- * Type: String with a value from the SupervisorVerificationSetting annotation below.
+ * Type: Integer with a value from the SupervisorVerificationSetting annotation below.
* <ul>
* <li>{@link #SUPERVISOR_VERIFICATION_SETTING_UNKNOWN}
* <li>{@link #SUPERVISOR_VERIFICATION_SETTING_BIOMETRICS}
@@ -2141,20 +2140,19 @@ public final class Settings {
/**
* Unknown setting.
*/
- public static final String SUPERVISOR_VERIFICATION_SETTING_UNKNOWN = "";
+ public static final int SUPERVISOR_VERIFICATION_SETTING_UNKNOWN = 0;
/**
* Biometric settings for supervisors.
*/
- public static final String SUPERVISOR_VERIFICATION_SETTING_BIOMETRICS =
- "supervisor_restricted_biometrics_controller";
+ public static final int SUPERVISOR_VERIFICATION_SETTING_BIOMETRICS = 1;
/**
* Keys for {@link #EXTRA_SUPERVISOR_RESTRICTED_SETTING_KEY}.
* @hide
*/
@Retention(RetentionPolicy.SOURCE)
- @StringDef(prefix = { "SUPERVISOR_VERIFICATION_SETTING_" }, value = {
+ @IntDef(prefix = { "SUPERVISOR_VERIFICATION_SETTING_" }, value = {
SUPERVISOR_VERIFICATION_SETTING_UNKNOWN,
SUPERVISOR_VERIFICATION_SETTING_BIOMETRICS,
})
diff --git a/core/java/android/service/dreams/DreamOverlayService.java b/core/java/android/service/dreams/DreamOverlayService.java
index 163d6ed4b18b..bfc3b8b39385 100644
--- a/core/java/android/service/dreams/DreamOverlayService.java
+++ b/core/java/android/service/dreams/DreamOverlayService.java
@@ -36,6 +36,9 @@ public abstract class DreamOverlayService extends Service {
private static final String TAG = "DreamOverlayService";
private static final boolean DEBUG = false;
private boolean mShowComplications;
+ private boolean mIsPreviewMode;
+ @Nullable
+ private CharSequence mDreamLabel;
private IDreamOverlay mDreamOverlay = new IDreamOverlay.Stub() {
@Override
@@ -56,6 +59,8 @@ public abstract class DreamOverlayService extends Service {
public final IBinder onBind(@NonNull Intent intent) {
mShowComplications = intent.getBooleanExtra(DreamService.EXTRA_SHOW_COMPLICATIONS,
DreamService.DEFAULT_SHOW_COMPLICATIONS);
+ mIsPreviewMode = intent.getBooleanExtra(DreamService.EXTRA_IS_PREVIEW, false);
+ mDreamLabel = intent.getCharSequenceExtra(DreamService.EXTRA_DREAM_LABEL);
return mDreamOverlay.asBinder();
}
@@ -84,4 +89,19 @@ public abstract class DreamOverlayService extends Service {
public final boolean shouldShowComplications() {
return mShowComplications;
}
+
+ /**
+ * Returns whether the dream is running in preview mode.
+ */
+ public final boolean isPreviewMode() {
+ return mIsPreviewMode;
+ }
+
+ /**
+ * Returns the user-facing label of the currently running dream.
+ */
+ @Nullable
+ public final CharSequence getDreamLabel() {
+ return mDreamLabel;
+ }
}
diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java
index 345917220b6b..db622d39b785 100644
--- a/core/java/android/service/dreams/DreamService.java
+++ b/core/java/android/service/dreams/DreamService.java
@@ -216,6 +216,18 @@ public class DreamService extends Service implements Window.Callback {
"android.service.dreams.SHOW_COMPLICATIONS";
/**
+ * Extra containing a boolean for whether we are showing this dream in preview mode.
+ * @hide
+ */
+ public static final String EXTRA_IS_PREVIEW = "android.service.dreams.IS_PREVIEW";
+
+ /**
+ * The user-facing label of the current dream service.
+ * @hide
+ */
+ public static final String EXTRA_DREAM_LABEL = "android.service.dreams.DREAM_LABEL";
+
+ /**
* The default value for whether to show complications on the overlay.
* @hide
*/
@@ -258,15 +270,19 @@ public class DreamService extends Service implements Window.Callback {
}
public void bind(Context context, @Nullable ComponentName overlayService,
- ComponentName dreamService) {
+ ComponentName dreamService, boolean isPreviewMode) {
if (overlayService == null) {
return;
}
+ final ServiceInfo serviceInfo = fetchServiceInfo(context, dreamService);
+
final Intent overlayIntent = new Intent();
overlayIntent.setComponent(overlayService);
overlayIntent.putExtra(EXTRA_SHOW_COMPLICATIONS,
- fetchShouldShowComplications(context, dreamService));
+ fetchShouldShowComplications(context, serviceInfo));
+ overlayIntent.putExtra(EXTRA_DREAM_LABEL, fetchDreamLabel(context, serviceInfo));
+ overlayIntent.putExtra(EXTRA_IS_PREVIEW, isPreviewMode);
context.bindService(overlayIntent,
this, Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE);
@@ -988,8 +1004,11 @@ public class DreamService extends Service implements Window.Callback {
// Connect to the overlay service if present.
if (!mWindowless) {
- mOverlayConnection.bind(this, intent.getParcelableExtra(EXTRA_DREAM_OVERLAY_COMPONENT),
- new ComponentName(this, getClass()));
+ mOverlayConnection.bind(
+ /* context= */ this,
+ intent.getParcelableExtra(EXTRA_DREAM_OVERLAY_COMPONENT),
+ new ComponentName(this, getClass()),
+ intent.getBooleanExtra(EXTRA_IS_PREVIEW, /* defaultValue= */ false));
}
return mDreamServiceWrapper;
@@ -1111,7 +1130,10 @@ public class DreamService extends Service implements Window.Callback {
* @hide
*/
@Nullable
- public static DreamMetadata getDreamMetadata(Context context, ServiceInfo serviceInfo) {
+ public static DreamMetadata getDreamMetadata(Context context,
+ @Nullable ServiceInfo serviceInfo) {
+ if (serviceInfo == null) return null;
+
final PackageManager pm = context.getPackageManager();
final TypedArray rawMetadata = readMetadata(pm, serviceInfo);
@@ -1350,22 +1372,33 @@ public class DreamService extends Service implements Window.Callback {
* {@link DreamService#DEFAULT_SHOW_COMPLICATIONS}.
*/
private static boolean fetchShouldShowComplications(Context context,
- ComponentName componentName) {
+ @Nullable ServiceInfo serviceInfo) {
+ final DreamMetadata metadata = getDreamMetadata(context, serviceInfo);
+ if (metadata != null) {
+ return metadata.showComplications;
+ }
+ return DEFAULT_SHOW_COMPLICATIONS;
+ }
+
+ @Nullable
+ private static CharSequence fetchDreamLabel(Context context,
+ @Nullable ServiceInfo serviceInfo) {
+ if (serviceInfo == null) return null;
+ final PackageManager pm = context.getPackageManager();
+ return serviceInfo.loadLabel(pm);
+ }
+
+ @Nullable
+ private static ServiceInfo fetchServiceInfo(Context context, ComponentName componentName) {
final PackageManager pm = context.getPackageManager();
try {
- final ServiceInfo si = pm.getServiceInfo(componentName,
+ return pm.getServiceInfo(componentName,
PackageManager.ComponentInfoFlags.of(PackageManager.GET_META_DATA));
- final DreamMetadata metadata = getDreamMetadata(context, si);
-
- if (metadata != null) {
- return metadata.showComplications;
- }
} catch (PackageManager.NameNotFoundException e) {
if (DEBUG) Log.w(TAG, "cannot find component " + componentName.flattenToShortString());
}
-
- return DEFAULT_SHOW_COMPLICATIONS;
+ return null;
}
@Override
diff --git a/core/java/android/service/selectiontoolbar/FloatingToolbarRoot.java b/core/java/android/service/selectiontoolbar/FloatingToolbarRoot.java
index 8fe6f71b598d..adc9251d89be 100644
--- a/core/java/android/service/selectiontoolbar/FloatingToolbarRoot.java
+++ b/core/java/android/service/selectiontoolbar/FloatingToolbarRoot.java
@@ -40,7 +40,7 @@ public class FloatingToolbarRoot extends LinearLayout {
private final IBinder mTargetInputToken;
private final SelectionToolbarRenderService.TransferTouchListener mTransferTouchListener;
- private Rect mContentRect;
+ private final Rect mContentRect = new Rect();
private int mLastDownX = -1;
private int mLastDownY = -1;
@@ -57,7 +57,7 @@ public class FloatingToolbarRoot extends LinearLayout {
* Sets the Rect that shows the selection toolbar content.
*/
public void setContentRect(Rect contentRect) {
- mContentRect = contentRect;
+ mContentRect.set(contentRect);
}
@Override
diff --git a/core/java/android/util/DumpableContainer.java b/core/java/android/util/DumpableContainer.java
index e7b2acd5b500..fef5acd42e4e 100644
--- a/core/java/android/util/DumpableContainer.java
+++ b/core/java/android/util/DumpableContainer.java
@@ -18,8 +18,7 @@ package android.util;
import android.annotation.NonNull;
/**
- * Objects that contains a list of {@link Dumpable}, which will be dumped when the object itself
- * is dumped.
+ * Represents a container that manages {@link Dumpable dumpables}.
*/
public interface DumpableContainer {
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index ce21086931da..53b842a0f3a2 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -18,6 +18,7 @@ package android.view;
import com.android.internal.os.IResultReceiver;
import com.android.internal.policy.IKeyguardDismissCallback;
+import com.android.internal.policy.IKeyguardLockedStateListener;
import com.android.internal.policy.IShortcutService;
import android.app.IAssistDataReceiver;
@@ -199,6 +200,9 @@ interface IWindowManager
boolean isKeyguardSecure(int userId);
void dismissKeyguard(IKeyguardDismissCallback callback, CharSequence message);
+ void addKeyguardLockedStateListener(in IKeyguardLockedStateListener listener);
+ void removeKeyguardLockedStateListener(in IKeyguardLockedStateListener listener);
+
// Requires INTERACT_ACROSS_USERS_FULL permission
void setSwitchingUser(boolean switching);
diff --git a/core/java/android/view/ImeFocusController.java b/core/java/android/view/ImeFocusController.java
index 3fc9f6bcbdeb..b48b5258237f 100644
--- a/core/java/android/view/ImeFocusController.java
+++ b/core/java/android/view/ImeFocusController.java
@@ -54,13 +54,11 @@ public final class ImeFocusController {
@NonNull
private InputMethodManagerDelegate getImmDelegate() {
- InputMethodManagerDelegate delegate = mDelegate;
- if (delegate != null) {
- return delegate;
+ if (mDelegate == null) {
+ mDelegate = mViewRootImpl.mContext.getSystemService(
+ InputMethodManager.class).getDelegate();
}
- delegate = mViewRootImpl.mContext.getSystemService(InputMethodManager.class).getDelegate();
- mDelegate = delegate;
- return delegate;
+ return mDelegate;
}
/** Called when the view root is moved to a different display. */
diff --git a/core/java/android/view/InputDevice.java b/core/java/android/view/InputDevice.java
index 188d7459f9a7..7d5603994efa 100644
--- a/core/java/android/view/InputDevice.java
+++ b/core/java/android/view/InputDevice.java
@@ -737,15 +737,47 @@ public final class InputDevice implements Parcelable {
}
/**
- * Gets the key code produced by the specified location on a US keyboard layout.
- * Key code as defined in {@link android.view.KeyEvent}.
- * This API is only functional for devices with {@link InputDevice#SOURCE_KEYBOARD} available
- * which can alter their key mapping using country specific keyboard layouts.
- *
- * @param locationKeyCode The location of a key on a US keyboard layout.
- * @return The key code produced when pressing the key at the specified location, given the
- * active keyboard layout. Returns {@link KeyEvent#KEYCODE_UNKNOWN} if the requested
- * mapping could not be determined, or if an error occurred.
+ * Gets the {@link android.view.KeyEvent key code} produced by the given location on a reference
+ * QWERTY keyboard layout.
+ * <p>
+ * This API is useful for querying the physical location of keys that change the character
+ * produced based on the current locale and keyboard layout.
+ * <p>
+ * The following table provides a non-exhaustive list of examples:
+ * <table border="2" width="85%" align="center" cellpadding="5">
+ * <thead>
+ * <tr><th>Active Keyboard Layout</th> <th>Input Parameter</th>
+ * <th>Return Value</th></tr>
+ * </thead>
+ *
+ * <tbody>
+ * <tr>
+ * <td>French AZERTY</td>
+ * <td><code>{@link KeyEvent#KEYCODE_Q}</code></td>
+ * <td><code>{@link KeyEvent#KEYCODE_A}</code></td>
+ * </tr>
+ * <tr>
+ * <td>German QWERTZ</td>
+ * <td><code>{@link KeyEvent#KEYCODE_Y}</code></td>
+ * <td><code>{@link KeyEvent#KEYCODE_Z}</code></td>
+ * </tr>
+ * <tr>
+ * <td>US QWERTY</td>
+ * <td><code>{@link KeyEvent#KEYCODE_B}</code></td>
+ * <td><code>{@link KeyEvent#KEYCODE_B}</code></td>
+ * </tr>
+ * </tbody>
+ * </table>
+ *
+ * @param locationKeyCode The location of a key specified as a key code on the QWERTY layout.
+ * This provides a consistent way of referring to the physical location of a key independently
+ * of the current keyboard layout. Also see the
+ * <a href="https://www.w3.org/TR/2017/CR-uievents-code-20170601/#key-alphanumeric-writing-system">
+ * hypothetical keyboard</a> provided by the W3C, which may be helpful for identifying the
+ * physical location of a key.
+ * @return The key code produced by the key at the specified location, given the current
+ * keyboard layout. Returns {@link KeyEvent#KEYCODE_UNKNOWN} if the device does not specify
+ * {@link InputDevice#SOURCE_KEYBOARD} or the requested mapping cannot be determined.
*/
public int getKeyCodeForKeyLocation(int locationKeyCode) {
return InputManager.getInstance().getKeyCodeForKeyLocation(mId, locationKeyCode);
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 98cef95885bd..632af2315bcd 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -3094,6 +3094,10 @@ public final class SurfaceControl implements Parcelable {
* @param destFrame The destination rectangle in parent space. Or null for the source frame.
* @param orientation The buffer rotation
* @return This transaction object.
+ * @deprecated Use {@link #setCrop(SurfaceControl, Rect)},
+ * {@link #setBufferTransform(SurfaceControl, int)},
+ * {@link #setPosition(SurfaceControl, float, float)} and
+ * {@link #setScale(SurfaceControl, float, float)} instead.
*/
@NonNull
public Transaction setGeometry(@NonNull SurfaceControl sc, @Nullable Rect sourceCrop,
diff --git a/core/java/android/view/SurfaceControlViewHost.java b/core/java/android/view/SurfaceControlViewHost.java
index 2edfda5d065c..a13579d0acad 100644
--- a/core/java/android/view/SurfaceControlViewHost.java
+++ b/core/java/android/view/SurfaceControlViewHost.java
@@ -328,7 +328,8 @@ public class SurfaceControlViewHost {
*/
public @Nullable SurfacePackage getSurfacePackage() {
if (mSurfaceControl != null && mAccessibilityEmbeddedConnection != null) {
- return new SurfacePackage(mSurfaceControl, mAccessibilityEmbeddedConnection,
+ return new SurfacePackage(new SurfaceControl(mSurfaceControl, "getSurfacePackage"),
+ mAccessibilityEmbeddedConnection,
mWm.getFocusGrantToken(), mRemoteInterface);
} else {
return null;
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 286b502791c8..34a13868f4d7 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -14173,7 +14173,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
&& isAccessibilityPane()) {
// If the pane isn't visible, content changed events are sufficient unless we're
// reporting that the view just disappeared
- if ((getVisibility() == VISIBLE)
+ if ((isAggregatedVisible())
|| (changeType == AccessibilityEvent.CONTENT_CHANGE_TYPE_PANE_DISAPPEARED)) {
final AccessibilityEvent event = AccessibilityEvent.obtain();
onInitializeAccessibilityEvent(event);
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 844403298cc9..f7539d53da17 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -2721,6 +2721,10 @@ public final class ViewRootImpl implements ViewParent,
// Execute enqueued actions on every traversal in case a detached view enqueued an action
getRunQueue().executeActions(mAttachInfo.mHandler);
+ if (mApplyInsetsRequested && !(mWillMove || mWillResize)) {
+ dispatchApplyInsets(host);
+ }
+
boolean layoutRequested = mLayoutRequested && (!mStopped || mReportNextDraw);
if (layoutRequested) {
@@ -2785,18 +2789,6 @@ public final class ViewRootImpl implements ViewParent,
}
}
- if (mApplyInsetsRequested && !(mWillMove || mWillResize)) {
- dispatchApplyInsets(host);
- if (mLayoutRequested) {
- // Short-circuit catching a new layout request here, so
- // we don't need to go through two layout passes when things
- // change due to fitting system windows, which can happen a lot.
- windowSizeMayChange |= measureHierarchy(host, lp,
- mView.getContext().getResources(),
- desiredWindowWidth, desiredWindowHeight);
- }
- }
-
if (layoutRequested) {
// Clear this now, so that if anything requests a layout in the
// rest of this function we will catch it and re-run a full
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 1c2704632e51..8ee3e432cbbb 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -3222,7 +3222,8 @@ public interface WindowManager extends ViewManager {
/**
* The window is allowed to extend into the {@link DisplayCutout} area, only if the
- * {@link DisplayCutout} is fully contained within a system bar. Otherwise, the window is
+ * {@link DisplayCutout} is fully contained within a system bar or the {@link DisplayCutout}
+ * is not deeper than 16 dp, but this depends on the OEM choice. Otherwise, the window is
* laid out such that it does not overlap with the {@link DisplayCutout} area.
*
* <p>
@@ -3237,6 +3238,13 @@ public interface WindowManager extends ViewManager {
* The usual precautions for not overlapping with the status and navigation bar are
* sufficient for ensuring that no important content overlaps with the DisplayCutout.
*
+ * <p>
+ * Note: OEMs can have an option to allow the window to always extend into the
+ * {@link DisplayCutout} area, no matter the cutout flag set, when the {@link DisplayCutout}
+ * is on the different side from system bars, only if the {@link DisplayCutout} overlaps at
+ * most 16dp with the windows.
+ * In such case, OEMs must provide an opt-in/out affordance for users.
+ *
* @see DisplayCutout
* @see WindowInsets
* @see #layoutInDisplayCutoutMode
@@ -3249,8 +3257,16 @@ public interface WindowManager extends ViewManager {
* The window is always allowed to extend into the {@link DisplayCutout} areas on the short
* edges of the screen.
*
+ * <p>
* The window will never extend into a {@link DisplayCutout} area on the long edges of the
- * screen.
+ * screen, unless the {@link DisplayCutout} is not deeper than 16 dp, but this depends on
+ * the OEM choice.
+ *
+ * <p>
+ * Note: OEMs can have an option to allow the window to extend into the
+ * {@link DisplayCutout} area on the long edge side, only if the cutout overlaps at most
+ * 16dp with the windows. In such case, OEMs must provide an opt-in/out affordance for
+ * users.
*
* <p>
* The window must make sure that no important content overlaps with the
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 27174630bfa3..017b8aa3cf6c 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -46,6 +46,8 @@ import android.annotation.SystemService;
import android.annotation.TestApi;
import android.annotation.UserIdInt;
import android.app.ActivityThread;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledSince;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
import android.content.ContentResolver;
@@ -356,6 +358,21 @@ public final class InputMethodManager {
/** @hide */
public static final int SHOW_IM_PICKER_MODE_EXCLUDE_AUXILIARY_SUBTYPES = 2;
+ /**
+ * Clear {@link #SHOW_FORCED} flag when the next IME focused application changed.
+ *
+ * <p>
+ * Note that when this flag enabled in server side, {@link #SHOW_FORCED} will no longer
+ * affect the next focused application to keep showing IME, in case of unexpected IME visible
+ * when the next focused app isn't be the IME requester. </p>
+ *
+ * @hide
+ */
+ @TestApi
+ @ChangeId
+ @EnabledSince(targetSdkVersion = Build.VERSION_CODES.TIRAMISU)
+ public static final long CLEAR_SHOW_FORCED_FLAG_WHEN_LEAVING = 214016041L; // This is a bug id.
+
@UnsupportedAppUsage
final IInputMethodManager mService;
final Looper mMainLooper;
@@ -1646,7 +1663,14 @@ public final class InputMethodManager {
* Flag for {@link #showSoftInput} to indicate that the user has forced
* the input method open (such as by long-pressing menu) so it should
* not be closed until they explicitly do so.
+ *
+ * @deprecated Use {@link #showSoftInput} without this flag instead. Using this flag can lead
+ * to the soft input remaining visible even when the calling application is closed. The
+ * use of this flag can make the soft input remains visible globally. Starting in
+ * {@link Build.VERSION_CODES#TIRAMISU Android T}, this flag only has an effect while the
+ * caller is currently focused.
*/
+ @Deprecated
public static final int SHOW_FORCED = 0x0002;
/**
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 3dfb4a5a084a..c207af53fab7 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -12774,7 +12774,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
/**
* Called when a context menu option for the text view is selected. Currently
* this will be one of {@link android.R.id#selectAll}, {@link android.R.id#cut},
- * {@link android.R.id#copy}, {@link android.R.id#paste} or {@link android.R.id#shareText}.
+ * {@link android.R.id#copy}, {@link android.R.id#paste},
+ * {@link android.R.id#pasteAsPlainText} (starting at API level 23) or
+ * {@link android.R.id#shareText}.
*
* @return true if the context menu item action was performed.
*/
@@ -12965,6 +12967,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
* method. The default actions can also be removed from the menu using
* {@link android.view.Menu#removeItem(int)} and passing {@link android.R.id#selectAll},
* {@link android.R.id#cut}, {@link android.R.id#copy}, {@link android.R.id#paste},
+ * {@link android.R.id#pasteAsPlainText} (starting at API level 23),
* {@link android.R.id#replaceText} or {@link android.R.id#shareText} ids as parameters.
*
* <p>Returning false from
@@ -13003,7 +13006,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
* {@link android.view.ActionMode.Callback#onPrepareActionMode(android.view.ActionMode,
* android.view.Menu)} method. The default actions can also be removed from the menu using
* {@link android.view.Menu#removeItem(int)} and passing {@link android.R.id#selectAll},
- * {@link android.R.id#paste} or {@link android.R.id#replaceText} ids as parameters.</p>
+ * {@link android.R.id#paste}, {@link android.R.id#pasteAsPlainText} (starting at API
+ * level 23) or {@link android.R.id#replaceText} ids as parameters.</p>
*
* <p>Returning false from
* {@link android.view.ActionMode.Callback#onCreateActionMode(android.view.ActionMode,
diff --git a/core/java/com/android/internal/app/MediaRouteChooserDialog.java b/core/java/com/android/internal/app/MediaRouteChooserDialog.java
index 7108d1443d3a..23d966fdbc0d 100644
--- a/core/java/com/android/internal/app/MediaRouteChooserDialog.java
+++ b/core/java/com/android/internal/app/MediaRouteChooserDialog.java
@@ -16,25 +16,26 @@
package com.android.internal.app;
-import com.android.internal.R;
-
-import android.app.Dialog;
+import android.app.AlertDialog;
import android.content.Context;
import android.media.MediaRouter;
import android.media.MediaRouter.RouteInfo;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.TypedValue;
+import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
-import android.view.Window;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
+import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.TextView;
+import com.android.internal.R;
+
import java.util.Comparator;
/**
@@ -48,9 +49,10 @@ import java.util.Comparator;
*
* TODO: Move this back into the API, as in the support library media router.
*/
-public class MediaRouteChooserDialog extends Dialog {
+public class MediaRouteChooserDialog extends AlertDialog {
private final MediaRouter mRouter;
private final MediaRouterCallback mCallback;
+ private final boolean mShowProgressBarWhenEmpty;
private int mRouteTypes;
private View.OnClickListener mExtendedSettingsClickListener;
@@ -60,10 +62,15 @@ public class MediaRouteChooserDialog extends Dialog {
private boolean mAttachedToWindow;
public MediaRouteChooserDialog(Context context, int theme) {
+ this(context, theme, true);
+ }
+
+ public MediaRouteChooserDialog(Context context, int theme, boolean showProgressBarWhenEmpty) {
super(context, theme);
mRouter = (MediaRouter) context.getSystemService(Context.MEDIA_ROUTER_SERVICE);
mCallback = new MediaRouterCallback();
+ mShowProgressBarWhenEmpty = showProgressBarWhenEmpty;
}
/**
@@ -120,28 +127,38 @@ public class MediaRouteChooserDialog extends Dialog {
@Override
protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
+ // Note: setView must be called before super.onCreate().
+ setView(LayoutInflater.from(getContext()).inflate(R.layout.media_route_chooser_dialog,
+ null));
- getWindow().requestFeature(Window.FEATURE_LEFT_ICON);
-
- setContentView(R.layout.media_route_chooser_dialog);
setTitle(mRouteTypes == MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY
? R.string.media_route_chooser_title_for_remote_display
: R.string.media_route_chooser_title);
- // Must be called after setContentView.
- getWindow().setFeatureDrawableResource(Window.FEATURE_LEFT_ICON,
- isLightTheme(getContext()) ? R.drawable.ic_media_route_off_holo_light
- : R.drawable.ic_media_route_off_holo_dark);
+ setIcon(isLightTheme(getContext()) ? R.drawable.ic_media_route_off_holo_light
+ : R.drawable.ic_media_route_off_holo_dark);
+
+ super.onCreate(savedInstanceState);
+ View emptyView = findViewById(android.R.id.empty);
mAdapter = new RouteAdapter(getContext());
- mListView = (ListView)findViewById(R.id.media_route_list);
+ mListView = (ListView) findViewById(R.id.media_route_list);
mListView.setAdapter(mAdapter);
mListView.setOnItemClickListener(mAdapter);
- mListView.setEmptyView(findViewById(android.R.id.empty));
+ mListView.setEmptyView(emptyView);
- mExtendedSettingsButton = (Button)findViewById(R.id.media_route_extended_settings_button);
+ mExtendedSettingsButton = (Button) findViewById(R.id.media_route_extended_settings_button);
updateExtendedSettingsButton();
+
+ if (!mShowProgressBarWhenEmpty) {
+ findViewById(R.id.media_route_progress_bar).setVisibility(View.GONE);
+
+ // Center the empty view when the progress bar is not shown.
+ LinearLayout.LayoutParams params =
+ (LinearLayout.LayoutParams) emptyView.getLayoutParams();
+ params.gravity = Gravity.CENTER;
+ emptyView.setLayoutParams(params);
+ }
}
private void updateExtendedSettingsButton() {
diff --git a/core/java/com/android/internal/app/MediaRouteDialogPresenter.java b/core/java/com/android/internal/app/MediaRouteDialogPresenter.java
index bb2d7fab9cc1..5628b7ed9d15 100644
--- a/core/java/com/android/internal/app/MediaRouteDialogPresenter.java
+++ b/core/java/com/android/internal/app/MediaRouteDialogPresenter.java
@@ -66,24 +66,37 @@ public abstract class MediaRouteDialogPresenter {
}
}
+ /** Create a media route dialog as appropriate. */
public static Dialog createDialog(Context context,
int routeTypes, View.OnClickListener extendedSettingsClickListener) {
- final MediaRouter router = (MediaRouter)context.getSystemService(
- Context.MEDIA_ROUTER_SERVICE);
-
int theme = MediaRouteChooserDialog.isLightTheme(context)
? android.R.style.Theme_DeviceDefault_Light_Dialog
: android.R.style.Theme_DeviceDefault_Dialog;
+ return createDialog(context, routeTypes, extendedSettingsClickListener, theme);
+ }
+
+ /** Create a media route dialog as appropriate. */
+ public static Dialog createDialog(Context context,
+ int routeTypes, View.OnClickListener extendedSettingsClickListener, int theme) {
+ return createDialog(context, routeTypes, extendedSettingsClickListener, theme,
+ true /* showProgressBarWhenEmpty */);
+ }
+
+ /** Create a media route dialog as appropriate. */
+ public static Dialog createDialog(Context context, int routeTypes,
+ View.OnClickListener extendedSettingsClickListener, int theme,
+ boolean showProgressBarWhenEmpty) {
+ final MediaRouter router = context.getSystemService(MediaRouter.class);
MediaRouter.RouteInfo route = router.getSelectedRoute();
if (route.isDefault() || !route.matchesTypes(routeTypes)) {
- final MediaRouteChooserDialog d = new MediaRouteChooserDialog(context, theme);
+ final MediaRouteChooserDialog d = new MediaRouteChooserDialog(context, theme,
+ showProgressBarWhenEmpty);
d.setRouteTypes(routeTypes);
d.setExtendedSettingsClickListener(extendedSettingsClickListener);
return d;
} else {
- MediaRouteControllerDialog d = new MediaRouteControllerDialog(context, theme);
- return d;
+ return new MediaRouteControllerDialog(context, theme);
}
}
}
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index cdb69e546b8f..e336e96d577e 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -33,7 +33,6 @@ import android.annotation.StringRes;
import android.annotation.UiThread;
import android.app.Activity;
import android.app.ActivityManager;
-import android.app.ActivityTaskManager;
import android.app.ActivityThread;
import android.app.VoiceInteractor.PickOptionRequest;
import android.app.VoiceInteractor.PickOptionRequest.Option;
@@ -54,13 +53,11 @@ import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
import android.content.pm.UserInfo;
import android.content.res.Configuration;
-import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Insets;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
-import android.os.IBinder;
import android.os.PatternMatcher;
import android.os.RemoteException;
import android.os.StrictMode;
@@ -1464,36 +1461,9 @@ public class ResolverActivity extends Activity implements
public boolean startAsCallerImpl(Intent intent, Bundle options, boolean ignoreTargetSecurity,
int userId) {
- // Pass intent to delegate chooser activity with permission token.
- // TODO: This should move to a trampoline Activity in the system when the ChooserActivity
- // moves into systemui
- try {
- // TODO: Once this is a small springboard activity, it can move off the UI process
- // and we can move the request method to ActivityManagerInternal.
- final Intent chooserIntent = new Intent();
- final ComponentName delegateActivity = ComponentName.unflattenFromString(
- Resources.getSystem().getString(R.string.config_chooserActivity));
- IBinder permissionToken = ActivityTaskManager.getService()
- .requestStartActivityPermissionToken(delegateActivity);
- chooserIntent.setClassName(delegateActivity.getPackageName(),
- delegateActivity.getClassName());
- chooserIntent.putExtra(ActivityTaskManager.EXTRA_PERMISSION_TOKEN, permissionToken);
-
- // TODO: These extras will change as chooser activity moves into systemui
- chooserIntent.putExtra(Intent.EXTRA_INTENT, intent);
- chooserIntent.putExtra(ActivityTaskManager.EXTRA_OPTIONS, options);
- chooserIntent.putExtra(ActivityTaskManager.EXTRA_IGNORE_TARGET_SECURITY,
- ignoreTargetSecurity);
- chooserIntent.putExtra(Intent.EXTRA_USER_ID, userId);
- chooserIntent.addFlags(Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP);
-
- // Don't close until the delegate finishes, or the token will be invalidated.
- mAwaitingDelegateResponse = true;
-
- startActivityForResult(chooserIntent, REQUEST_CODE_RETURN_FROM_DELEGATE_CHOOSER);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
- }
+ // Note: this method will be overridden in the delegate implementation to use the passed-in
+ // permission token.
+ startActivityAsCaller(intent, options, null, false, userId);
return true;
}
diff --git a/core/java/com/android/internal/app/chooser/DisplayResolveInfo.java b/core/java/com/android/internal/app/chooser/DisplayResolveInfo.java
index 301de2d3529e..dc53e77466ad 100644
--- a/core/java/com/android/internal/app/chooser/DisplayResolveInfo.java
+++ b/core/java/com/android/internal/app/chooser/DisplayResolveInfo.java
@@ -30,11 +30,9 @@ import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.UserHandle;
-import android.provider.DeviceConfig;
import com.android.internal.app.ResolverActivity;
import com.android.internal.app.ResolverListAdapter.ResolveInfoPresentationGetter;
-import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
import java.util.ArrayList;
import java.util.Arrays;
@@ -45,11 +43,6 @@ import java.util.List;
* resolve it to an activity.
*/
public class DisplayResolveInfo implements TargetInfo, Parcelable {
- private final boolean mEnableChooserDelegate =
- DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.USE_DELEGATE_CHOOSER,
- false);
-
private final ResolveInfo mResolveInfo;
private CharSequence mDisplayLabel;
private Drawable mDisplayIcon;
@@ -180,12 +173,9 @@ public class DisplayResolveInfo implements TargetInfo, Parcelable {
@Override
public boolean startAsCaller(ResolverActivity activity, Bundle options, int userId) {
- if (mEnableChooserDelegate) {
- return activity.startAsCallerImpl(mResolvedIntent, options, false, userId);
- } else {
- activity.startActivityAsCaller(mResolvedIntent, options, null, false, userId);
- return true;
- }
+ // TODO: if the start-as-caller API no longer requires a permission token, this can go back
+ // to inlining the real activity-start call, and we can remove startAsCallerImpl.
+ return activity.startAsCallerImpl(mResolvedIntent, options, false, userId);
}
@Override
diff --git a/core/java/com/android/internal/policy/IKeyguardLockedStateListener.aidl b/core/java/com/android/internal/policy/IKeyguardLockedStateListener.aidl
new file mode 100644
index 000000000000..ee5021918879
--- /dev/null
+++ b/core/java/com/android/internal/policy/IKeyguardLockedStateListener.aidl
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.policy;
+
+oneway interface IKeyguardLockedStateListener {
+ void onKeyguardLockedStateChanged(boolean isKeyguardLocked);
+} \ No newline at end of file
diff --git a/core/java/com/android/internal/util/ArrayUtils.java b/core/java/com/android/internal/util/ArrayUtils.java
index 9bdcddf38c61..1fd04109ae45 100644
--- a/core/java/com/android/internal/util/ArrayUtils.java
+++ b/core/java/com/android/internal/util/ArrayUtils.java
@@ -888,6 +888,15 @@ public class ArrayUtils {
}
}
+ /**
+ * Returns the {@code i}-th item in {@code items}, if it exists and {@code items} is not {@code
+ * null}, otherwise returns {@code null}.
+ */
+ @Nullable
+ public static <T> T getOrNull(@Nullable T[] items, int i) {
+ return (items != null && items.length > i) ? items[i] : null;
+ }
+
public static @Nullable <T> T firstOrNull(T[] items) {
return items.length > 0 ? items[0] : null;
}
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index c33b7c921740..e8f7b9343746 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -382,6 +382,7 @@ message ActivityRecordProto {
optional bool in_size_compat_mode = 32;
optional float min_aspect_ratio = 33;
optional bool provides_max_bounds = 34;
+ optional bool enable_recents_screenshot = 35;
}
/* represents WindowToken */
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index d4c03e412fcb..58a3bb4818df 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -4119,6 +4119,13 @@
<permission android:name="android.permission.MANAGE_HOTWORD_DETECTION"
android:protectionLevel="internal|preinstalled" />
+ <!-- Allows an application to subscribe to keyguard locked (i.e., showing) state.
+ <p>Protection level: internal|role
+ <p>Intended for use by ROLE_ASSISTANT only.
+ -->
+ <permission android:name="android.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE"
+ android:protectionLevel="internal|role"/>
+
<!-- Must be required by a {@link android.service.autofill.AutofillService},
to ensure that only the system can bind to it.
<p>Protection level: signature
@@ -6912,6 +6919,10 @@
android:permission="android.permission.BIND_JOB_SERVICE">
</service>
+ <service android:name="com.android.server.appsearch.contactsindexer.ContactsIndexerMaintenanceService"
+ android:permission="android.permission.BIND_JOB_SERVICE">
+ </service>
+
<service android:name="com.android.server.pm.PackageManagerShellCommandDataLoader"
android:exported="false">
<intent-filter>
diff --git a/core/res/res/layout/media_route_chooser_dialog.xml b/core/res/res/layout/media_route_chooser_dialog.xml
index cd1c74fd7d7d..bf73f4b26c9c 100644
--- a/core/res/res/layout/media_route_chooser_dialog.xml
+++ b/core/res/res/layout/media_route_chooser_dialog.xml
@@ -28,20 +28,21 @@
<!-- Content to show when list is empty. -->
<LinearLayout android:id="@android:id/empty"
- android:layout_width="match_parent"
- android:layout_height="64dp"
- android:orientation="horizontal"
- android:paddingLeft="16dp"
- android:paddingRight="16dp"
- android:visibility="gone">
- <ProgressBar android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center" />
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:paddingLeft="16dp"
+ android:paddingRight="16dp"
+ android:visibility="gone">
+ <ProgressBar android:id="@+id/media_route_progress_bar"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center" />
<TextView android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:paddingStart="16dp"
- android:text="@string/media_route_chooser_searching" />
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:paddingStart="16dp"
+ android:text="@string/media_route_chooser_searching" />
</LinearLayout>
<!-- Settings button. -->
diff --git a/core/res/res/values-television/config.xml b/core/res/res/values-television/config.xml
index 0db08fb751d8..88bf18cb8857 100644
--- a/core/res/res/values-television/config.xml
+++ b/core/res/res/values-television/config.xml
@@ -24,19 +24,6 @@
<!-- Flags enabling default window features. See Window.java -->
<bool name="config_defaultWindowFeatureOptionsPanel">false</bool>
- <!-- The percentage of the screen width to use for the default width or height of
- picture-in-picture windows. Regardless of the percent set here, calculated size will never
- be smaller than @dimen/default_minimal_size_pip_resizable_task. -->
- <item name="config_pictureInPictureDefaultSizePercent" format="float" type="dimen">0.2</item>
-
- <!-- Default insets [LEFT/RIGHTxTOP/BOTTOM] from the screen edge for picture-in-picture windows.
- These values are in DPs and will be converted to pixel sizes internally. -->
- <string translatable="false" name="config_defaultPictureInPictureScreenEdgeInsets">24x24</string>
-
- <!-- The default gravity for the picture-in-picture window.
- Currently, this maps to Gravity.BOTTOM | Gravity.RIGHT -->
- <integer name="config_defaultPictureInPictureGravity">0x55</integer>
-
<!-- The maximum height of the expanded horizontal picture-in-picture window -->
<item name="config_pictureInPictureExpandedHorizontalHeight"
format="dimension" type="dimen">110dp</item>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 7150fca5ab99..689d37c91920 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -7013,30 +7013,42 @@
<!-- Defines the ExtendAnimation used to extend windows during animations -->
<declare-styleable name="ExtendAnimation">
- <!-- Defines the amount a window should be extended outward from the left at
- the start of the animation. -->
- <attr name="fromExtendLeft" format="float|fraction" />
- <!-- Defines the amount a window should be extended outward from the top at
- the start of the animation. -->
- <attr name="fromExtendTop" format="float|fraction" />
- <!-- Defines the amount a window should be extended outward from the right at
- the start of the animation. -->
- <attr name="fromExtendRight" format="float|fraction" />
- <!-- Defines the amount a window should be extended outward from the bottom at
- the start of the animation. -->
- <attr name="fromExtendBottom" format="float|fraction" />
- <!-- Defines the amount a window should be extended outward from the left by
- the end of the animation by transitioning from the fromExtendLeft amount. -->
- <attr name="toExtendLeft" format="float|fraction" />
- <!-- Defines the amount a window should be extended outward from the top by
- the end of the animation by transitioning from the fromExtendTop amount. -->
- <attr name="toExtendTop" format="float|fraction" />
- <!-- Defines the amount a window should be extended outward from the right by
- the end of the animation by transitioning from the fromExtendRight amount. -->
- <attr name="toExtendRight" format="float|fraction" />
- <!-- Defines the amount a window should be extended outward from the bottom by
- the end of the animation by transitioning from the fromExtendBottom amount. -->
- <attr name="toExtendBottom" format="float|fraction" />
+ <!-- Defines the amount a window should be extended outward from the left at the start of
+ the animation in an absolute dimension (interpreted as pixels if no dimension unit is
+ provided) or as a percentage of the animation target's width. -->
+ <attr name="fromExtendLeft" format="float|fraction|dimension" />
+ <!-- Defines the amount a window should be extended outward from the top at the start of
+ the animation in an absolute dimension (interpreted as pixels if no dimension unit is
+ provided) or as a percentage of the animation target's height. -->
+ <attr name="fromExtendTop" format="float|fraction|dimension" />
+ <!-- Defines the amount a window should be extended outward from the right at the start of
+ the animation in an absolute dimension (interpreted as pixels if no dimension unit is
+ provided) or as a percentage of the animation target's width. -->
+ <attr name="fromExtendRight" format="float|fraction|dimension" />
+ <!-- Defines the amount a window should be extended outward from the bottom at the start of
+ the animation in an absolute dimension (interpreted as pixels if no dimension unit is
+ provided) or as a percentage of the animation target's height. -->
+ <attr name="fromExtendBottom" format="float|fraction|dimension" />
+ <!-- Defines the amount a window should be extended outward from the left by the end of the
+ animation by transitioning from the fromExtendLeft amount in an absolute dimension
+ (interpreted as pixels if no dimension unit is provided) or as a percentage of the
+ animation target's width. -->
+ <attr name="toExtendLeft" format="float|fraction|dimension" />
+ <!-- Defines the amount a window should be extended outward from the top by the end of the
+ animation by transitioning from the fromExtendTop amount in an absolute dimension
+ (interpreted as pixels if no dimension unit is provided) or as a percentage of the
+ animation target's height. -->
+ <attr name="toExtendTop" format="float|fraction|dimension" />
+ <!-- Defines the amount a window should be extended outward from the right by the end of
+ the animation by transitioning from the fromExtendRight amount in an absolute
+ dimension (interpreted as pixels if no dimension unit is provided) or as a percentage
+ of the animation target's width. -->
+ <attr name="toExtendRight" format="float|fraction|dimension" />
+ <!-- Defines the amount a window should be extended outward from the bottom by the end of
+ the animation by transitioning from the fromExtendBottom amount in an absolute
+ dimension (interpreted as pixels if no dimension unit is provided) or as a percentage
+ of the animation target's height. -->
+ <attr name="toExtendBottom" format="float|fraction|dimension" />
</declare-styleable>
<declare-styleable name="LayoutAnimation">
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 05894d55358e..5ac30de631c5 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3704,26 +3704,6 @@
snapped to any position between the first target and the last target. -->
<bool name="config_dockedStackDividerFreeSnapMode">false</bool>
- <!-- Default insets [LEFT/RIGHTxTOP/BOTTOM] from the screen edge for picture-in-picture windows.
- These values are in DPs and will be converted to pixel sizes internally. -->
- <string translatable="false" name="config_defaultPictureInPictureScreenEdgeInsets">16x16</string>
-
- <!-- The percentage of the screen width to use for the default width or height of
- picture-in-picture windows. Regardless of the percent set here, calculated size will never
- be smaller than @dimen/default_minimal_size_pip_resizable_task. -->
- <item name="config_pictureInPictureDefaultSizePercent" format="float" type="dimen">0.23</item>
-
- <!-- The default aspect ratio for picture-in-picture windows. -->
- <item name="config_pictureInPictureDefaultAspectRatio" format="float" type="dimen">1.777778</item>
-
- <!-- This is the limit for the max and min aspect ratio (1 / this value) at which the min size
- will be used instead of an adaptive size based loosely on area. -->
- <item name="config_pictureInPictureAspectRatioLimitForMinSize" format="float" type="dimen">1.777778</item>
-
- <!-- The default gravity for the picture-in-picture window.
- Currently, this maps to Gravity.BOTTOM | Gravity.RIGHT -->
- <integer name="config_defaultPictureInPictureGravity">0x55</integer>
-
<!-- The minimum aspect ratio (width/height) that is supported for picture-in-picture. Any
ratio smaller than this is considered too tall and thin to be usable. Currently, this
is the inverse of the max landscape aspect ratio (1:2.39), but this is an extremely
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 744c3dab9510..032d0b954ef1 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -606,6 +606,9 @@
<!-- The padding ratio of the Accessibility icon foreground drawable -->
<item name="accessibility_icon_foreground_padding_ratio" type="dimen">21.88%</item>
+ <!-- The minimum window size of the accessibility window magnifier -->
+ <dimen name="accessibility_window_magnifier_min_size">122dp</dimen>
+
<!-- Margin around the various security views -->
<dimen name="keyguard_muliuser_selector_margin">8dp</dimen>
@@ -714,16 +717,6 @@
<!-- The default minimal size of a resizable task, in both dimensions. -->
<dimen name="default_minimal_size_resizable_task">220dp</dimen>
- <!-- The default minimal size of a PiP task, in both dimensions. -->
- <dimen name="default_minimal_size_pip_resizable_task">108dp</dimen>
-
- <!--
- The overridable minimal size of a PiP task, in both dimensions.
- Different from default_minimal_size_pip_resizable_task, this is to limit the dimension
- when the pinned stack size is overridden by app via minWidth/minHeight.
- -->
- <dimen name="overridable_minimal_size_pip_resizable_task">48dp</dimen>
-
<!-- Height of a task when in minimized mode from the top when launcher is resizable. -->
<dimen name="task_height_of_minimized_mode">80dp</dimen>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index e7eeecc2b516..60a3398c93e6 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -408,11 +408,6 @@
<java-symbol type="array" name="config_localPrivateDisplayPorts" />
<java-symbol type="integer" name="config_defaultDisplayDefaultColorMode" />
<java-symbol type="bool" name="config_enableAppWidgetService" />
- <java-symbol type="string" name="config_defaultPictureInPictureScreenEdgeInsets" />
- <java-symbol type="dimen" name="config_pictureInPictureDefaultSizePercent" />
- <java-symbol type="dimen" name="config_pictureInPictureDefaultAspectRatio" />
- <java-symbol type="dimen" name="config_pictureInPictureAspectRatioLimitForMinSize" />
- <java-symbol type="integer" name="config_defaultPictureInPictureGravity" />
<java-symbol type="dimen" name="config_pictureInPictureMinAspectRatio" />
<java-symbol type="dimen" name="config_pictureInPictureMaxAspectRatio" />
<java-symbol type="integer" name="config_pictureInPictureMaxNumberOfActions" />
@@ -1295,6 +1290,7 @@
<java-symbol type="array" name="vendor_required_apps_managed_user" />
<java-symbol type="array" name="vendor_required_apps_managed_profile" />
<java-symbol type="array" name="vendor_required_apps_managed_device" />
+ <java-symbol type="array" name="vendor_required_attestation_certificates" />
<java-symbol type="array" name="vendor_disallowed_apps_managed_user" />
<java-symbol type="array" name="vendor_disallowed_apps_managed_profile" />
<java-symbol type="array" name="vendor_disallowed_apps_managed_device" />
@@ -1676,6 +1672,7 @@
<java-symbol type="id" name="media_route_volume_slider" />
<java-symbol type="id" name="media_route_control_frame" />
<java-symbol type="id" name="media_route_extended_settings_button" />
+ <java-symbol type="id" name="media_route_progress_bar" />
<java-symbol type="string" name="media_route_chooser_title" />
<java-symbol type="string" name="media_route_chooser_title_for_remote_display" />
<java-symbol type="string" name="media_route_controller_disconnect" />
@@ -2015,8 +2012,6 @@
<java-symbol type="id" name="replace_message" />
<java-symbol type="fraction" name="config_dimBehindFadeDuration" />
<java-symbol type="dimen" name="default_minimal_size_resizable_task" />
- <java-symbol type="dimen" name="default_minimal_size_pip_resizable_task" />
- <java-symbol type="dimen" name="overridable_minimal_size_pip_resizable_task" />
<java-symbol type="dimen" name="task_height_of_minimized_mode" />
<java-symbol type="fraction" name="config_screenAutoBrightnessDozeScaleFactor" />
<java-symbol type="bool" name="config_allowPriorityVibrationsInLowPowerMode" />
@@ -4398,6 +4393,7 @@
<java-symbol type="color" name="accessibility_focus_highlight_color" />
<!-- Width of the outline stroke used by the accessibility focus rectangle -->
<java-symbol type="dimen" name="accessibility_focus_highlight_stroke_width" />
+ <java-symbol type="dimen" name="accessibility_window_magnifier_min_size" />
<java-symbol type="bool" name="config_attachNavBarToAppDuringTransition" />
diff --git a/core/res/res/values/vendor_required_attestation_certificates.xml b/core/res/res/values/vendor_required_attestation_certificates.xml
new file mode 100644
index 000000000000..ce5660f433ff
--- /dev/null
+++ b/core/res/res/values/vendor_required_attestation_certificates.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+<resources>
+ <!-- The PEM-encoded certificates added here are used for verifying attestations.
+ The trustworthiness of the attestation depends on the root certificate of the chain.
+
+ Certificates that can be used can be retrieved from:
+ https://developer.android.com/training/articles/security-key-attestation#root_certificate.
+
+ If not already present in resource overlay, please add
+ vendor_required_attestation_certificates.xml (matching this file) in vendor overlay
+ with <item></item> of the PEM-encoded root certificates.
+ -->
+ <string-array translatable="false" name="vendor_required_attestation_certificates">
+ </string-array>
+</resources>
diff --git a/core/tests/coretests/src/android/content/ManagedUserContentResolverTest.java b/core/tests/coretests/src/android/content/ManagedUserContentResolverTest.java
index 68d4cd4a97e7..1bc46a79f1c0 100644
--- a/core/tests/coretests/src/android/content/ManagedUserContentResolverTest.java
+++ b/core/tests/coretests/src/android/content/ManagedUserContentResolverTest.java
@@ -20,7 +20,6 @@ import android.content.pm.UserInfo;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
-import android.platform.test.annotations.Presubmit;
import androidx.test.filters.LargeTest;
@@ -37,7 +36,6 @@ import androidx.test.filters.LargeTest;
* Run: adb shell am instrument -e class android.content.ManagedUserContentResolverTest -w \
* com.android.frameworks.coretests/androidx.test.runner.AndroidJUnitRunner
*/
-@Presubmit
@LargeTest
public class ManagedUserContentResolverTest extends AbstractCrossUserContentResolverTest {
@Override
diff --git a/core/tests/coretests/src/android/content/SecondaryUserContentResolverTest.java b/core/tests/coretests/src/android/content/SecondaryUserContentResolverTest.java
index de4c5725c190..dbe027800e3f 100644
--- a/core/tests/coretests/src/android/content/SecondaryUserContentResolverTest.java
+++ b/core/tests/coretests/src/android/content/SecondaryUserContentResolverTest.java
@@ -18,7 +18,6 @@ package android.content;
import android.content.pm.UserInfo;
import android.os.RemoteException;
-import android.platform.test.annotations.Presubmit;
import androidx.test.filters.LargeTest;
@@ -35,7 +34,6 @@ import androidx.test.filters.LargeTest;
* Run: adb shell am instrument -e class android.content.SecondaryUserContentResolverTest -w \
* com.android.frameworks.coretests/androidx.test.runner.AndroidJUnitRunner
*/
-@Presubmit
@LargeTest
public class SecondaryUserContentResolverTest extends AbstractCrossUserContentResolverTest {
@Override
diff --git a/graphics/java/android/graphics/ColorSpace.java b/graphics/java/android/graphics/ColorSpace.java
index 2f978fc1fc2d..582488ff8de3 100644
--- a/graphics/java/android/graphics/ColorSpace.java
+++ b/graphics/java/android/graphics/ColorSpace.java
@@ -22,6 +22,10 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.Size;
import android.annotation.SuppressAutoDoc;
+import android.annotation.SuppressLint;
+import android.hardware.DataSpace;
+import android.hardware.DataSpace.NamedDataSpace;
+import android.util.SparseIntArray;
import libcore.util.NativeAllocationRegistry;
@@ -207,6 +211,7 @@ public abstract class ColorSpace {
// See static initialization block next to #get(Named)
private static final ColorSpace[] sNamedColorSpaces = new ColorSpace[Named.values().length];
+ private static final SparseIntArray sDataToColorSpaces = new SparseIntArray();
@NonNull private final String mName;
@NonNull private final Model mModel;
@@ -1389,6 +1394,47 @@ public abstract class ColorSpace {
}
/**
+ * Create a {@link ColorSpace} object using a {@link android.hardware.DataSpace DataSpace}
+ * value.
+ *
+ * <p>This function maps from a dataspace to a {@link Named} ColorSpace.
+ * If no {@link Named} ColorSpace object matching the {@code dataSpace} value can be created,
+ * {@code null} will return.</p>
+ *
+ * @param dataSpace The dataspace value
+ * @return the ColorSpace object or {@code null} if no matching colorspace can be found.
+ */
+ @SuppressLint("MethodNameUnits")
+ @Nullable
+ public static ColorSpace getFromDataSpace(@NamedDataSpace int dataSpace) {
+ int index = sDataToColorSpaces.get(dataSpace, -1);
+ if (index != -1) {
+ return ColorSpace.get(index);
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Retrieve the {@link android.hardware.DataSpace DataSpace} value from a {@link ColorSpace}
+ * object.
+ *
+ * <p>If this {@link ColorSpace} object has no matching {@code dataSpace} value,
+ * {@link android.hardware.DataSpace#DATASPACE_UNKNOWN DATASPACE_UNKNOWN} will return.</p>
+ *
+ * @return the dataspace value.
+ */
+ @SuppressLint("MethodNameUnits")
+ public @NamedDataSpace int getDataSpace() {
+ int index = sDataToColorSpaces.indexOfValue(getId());
+ if (index != -1) {
+ return sDataToColorSpaces.keyAt(index);
+ } else {
+ return DataSpace.DATASPACE_UNKNOWN;
+ }
+ }
+
+ /**
* <p>Returns an instance of {@link ColorSpace} identified by the specified
* name. The list of names provided in the {@link Named} enum gives access
* to a variety of common RGB color spaces.</p>
@@ -1445,6 +1491,7 @@ public abstract class ColorSpace {
SRGB_TRANSFER_PARAMETERS,
Named.SRGB.ordinal()
);
+ sDataToColorSpaces.put(DataSpace.DATASPACE_SRGB, Named.SRGB.ordinal());
sNamedColorSpaces[Named.LINEAR_SRGB.ordinal()] = new ColorSpace.Rgb(
"sRGB IEC61966-2.1 (Linear)",
SRGB_PRIMARIES,
@@ -1453,6 +1500,7 @@ public abstract class ColorSpace {
0.0f, 1.0f,
Named.LINEAR_SRGB.ordinal()
);
+ sDataToColorSpaces.put(DataSpace.DATASPACE_SRGB_LINEAR, Named.LINEAR_SRGB.ordinal());
sNamedColorSpaces[Named.EXTENDED_SRGB.ordinal()] = new ColorSpace.Rgb(
"scRGB-nl IEC 61966-2-2:2003",
SRGB_PRIMARIES,
@@ -1464,6 +1512,7 @@ public abstract class ColorSpace {
SRGB_TRANSFER_PARAMETERS,
Named.EXTENDED_SRGB.ordinal()
);
+ sDataToColorSpaces.put(DataSpace.DATASPACE_SCRGB, Named.EXTENDED_SRGB.ordinal());
sNamedColorSpaces[Named.LINEAR_EXTENDED_SRGB.ordinal()] = new ColorSpace.Rgb(
"scRGB IEC 61966-2-2:2003",
SRGB_PRIMARIES,
@@ -1472,6 +1521,8 @@ public abstract class ColorSpace {
-0.5f, 7.499f,
Named.LINEAR_EXTENDED_SRGB.ordinal()
);
+ sDataToColorSpaces.put(
+ DataSpace.DATASPACE_SCRGB_LINEAR, Named.LINEAR_EXTENDED_SRGB.ordinal());
sNamedColorSpaces[Named.BT709.ordinal()] = new ColorSpace.Rgb(
"Rec. ITU-R BT.709-5",
new float[] { 0.640f, 0.330f, 0.300f, 0.600f, 0.150f, 0.060f },
@@ -1480,6 +1531,7 @@ public abstract class ColorSpace {
new Rgb.TransferParameters(1 / 1.099, 0.099 / 1.099, 1 / 4.5, 0.081, 1 / 0.45),
Named.BT709.ordinal()
);
+ sDataToColorSpaces.put(DataSpace.DATASPACE_BT709, Named.BT709.ordinal());
sNamedColorSpaces[Named.BT2020.ordinal()] = new ColorSpace.Rgb(
"Rec. ITU-R BT.2020-1",
new float[] { 0.708f, 0.292f, 0.170f, 0.797f, 0.131f, 0.046f },
@@ -1488,6 +1540,7 @@ public abstract class ColorSpace {
new Rgb.TransferParameters(1 / 1.0993, 0.0993 / 1.0993, 1 / 4.5, 0.08145, 1 / 0.45),
Named.BT2020.ordinal()
);
+ sDataToColorSpaces.put(DataSpace.DATASPACE_BT2020, Named.BT2020.ordinal());
sNamedColorSpaces[Named.DCI_P3.ordinal()] = new ColorSpace.Rgb(
"SMPTE RP 431-2-2007 DCI (P3)",
new float[] { 0.680f, 0.320f, 0.265f, 0.690f, 0.150f, 0.060f },
@@ -1496,6 +1549,7 @@ public abstract class ColorSpace {
0.0f, 1.0f,
Named.DCI_P3.ordinal()
);
+ sDataToColorSpaces.put(DataSpace.DATASPACE_DCI_P3, Named.DCI_P3.ordinal());
sNamedColorSpaces[Named.DISPLAY_P3.ordinal()] = new ColorSpace.Rgb(
"Display P3",
new float[] { 0.680f, 0.320f, 0.265f, 0.690f, 0.150f, 0.060f },
@@ -1504,6 +1558,7 @@ public abstract class ColorSpace {
SRGB_TRANSFER_PARAMETERS,
Named.DISPLAY_P3.ordinal()
);
+ sDataToColorSpaces.put(DataSpace.DATASPACE_DISPLAY_P3, Named.DISPLAY_P3.ordinal());
sNamedColorSpaces[Named.NTSC_1953.ordinal()] = new ColorSpace.Rgb(
"NTSC (1953)",
NTSC_1953_PRIMARIES,
@@ -1528,6 +1583,7 @@ public abstract class ColorSpace {
0.0f, 1.0f,
Named.ADOBE_RGB.ordinal()
);
+ sDataToColorSpaces.put(DataSpace.DATASPACE_ADOBE_RGB, Named.ADOBE_RGB.ordinal());
sNamedColorSpaces[Named.PRO_PHOTO_RGB.ordinal()] = new ColorSpace.Rgb(
"ROMM RGB ISO 22028-2:2013",
new float[] { 0.7347f, 0.2653f, 0.1596f, 0.8404f, 0.0366f, 0.0001f },
diff --git a/libs/WindowManager/Shell/res/values-television/config.xml b/libs/WindowManager/Shell/res/values-television/config.xml
new file mode 100644
index 000000000000..552048e8be9a
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-television/config.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for TV products. Do not translate. -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+ <!-- The percentage of the screen width to use for the default width or height of
+ picture-in-picture windows. Regardless of the percent set here, calculated size will never
+ be smaller than @dimen/default_minimal_size_pip_resizable_task. -->
+ <item name="config_pictureInPictureDefaultSizePercent" format="float" type="dimen">0.2</item>
+
+ <!-- Default insets [LEFT/RIGHTxTOP/BOTTOM] from the screen edge for picture-in-picture windows.
+ These values are in DPs and will be converted to pixel sizes internally. -->
+ <string translatable="false" name="config_defaultPictureInPictureScreenEdgeInsets">
+ 24x24
+ </string>
+
+ <!-- The default gravity for the picture-in-picture window.
+ Currently, this maps to Gravity.BOTTOM | Gravity.RIGHT -->
+ <integer name="config_defaultPictureInPictureGravity">0x55</integer>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values/config.xml b/libs/WindowManager/Shell/res/values/config.xml
index 1b8032b7077b..d416c060c86c 100644
--- a/libs/WindowManager/Shell/res/values/config.xml
+++ b/libs/WindowManager/Shell/res/values/config.xml
@@ -70,4 +70,30 @@
<!-- Animation duration when exit starting window: reveal app -->
<integer name="starting_window_app_reveal_anim_duration">266</integer>
+
+ <!-- Default insets [LEFT/RIGHTxTOP/BOTTOM] from the screen edge for picture-in-picture windows.
+ These values are in DPs and will be converted to pixel sizes internally. -->
+ <string translatable="false" name="config_defaultPictureInPictureScreenEdgeInsets">
+ 16x16
+ </string>
+
+ <!-- The percentage of the screen width to use for the default width or height of
+ picture-in-picture windows. Regardless of the percent set here, calculated size will never
+ be smaller than @dimen/default_minimal_size_pip_resizable_task. -->
+ <item name="config_pictureInPictureDefaultSizePercent" format="float" type="dimen">0.23</item>
+
+ <!-- The default aspect ratio for picture-in-picture windows. -->
+ <item name="config_pictureInPictureDefaultAspectRatio" format="float" type="dimen">
+ 1.777778
+ </item>
+
+ <!-- This is the limit for the max and min aspect ratio (1 / this value) at which the min size
+ will be used instead of an adaptive size based loosely on area. -->
+ <item name="config_pictureInPictureAspectRatioLimitForMinSize" format="float" type="dimen">
+ 1.777778
+ </item>
+
+ <!-- The default gravity for the picture-in-picture window.
+ Currently, this maps to Gravity.BOTTOM | Gravity.RIGHT -->
+ <integer name="config_defaultPictureInPictureGravity">0x55</integer>
</resources>
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index 8a8231d00195..2c96786bb521 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -18,8 +18,8 @@
<dimen name="dismiss_circle_size">96dp</dimen>
<dimen name="dismiss_circle_small">60dp</dimen>
- <!-- The height of the gradient indicating the dismiss edge when moving a PIP. -->
- <dimen name="floating_dismiss_gradient_height">250dp</dimen>
+ <!-- The height of the gradient indicating the dismiss edge when moving a PIP or bubble. -->
+ <dimen name="floating_dismiss_gradient_height">548dp</dimen>
<!-- The padding around a PiP actions. -->
<dimen name="pip_action_padding">16dp</dimen>
@@ -129,6 +129,9 @@
<dimen name="bubble_dismiss_encircle_size">52dp</dimen>
<!-- Padding around the view displayed when the bubble is expanded -->
<dimen name="bubble_expanded_view_padding">16dp</dimen>
+ <!-- Padding for the edge of the expanded view that is closest to the edge of the screen used
+ when displaying in landscape on a large screen. -->
+ <dimen name="bubble_expanded_view_largescreen_landscape_padding">128dp</dimen>
<!-- This should be at least the size of bubble_expanded_view_padding; it is used to include
a slight touch slop around the expanded view. -->
<dimen name="bubble_expanded_view_slop">8dp</dimen>
@@ -251,4 +254,14 @@
<!-- The distance of the shift icon when early exit starting window. -->
<dimen name="starting_surface_early_exit_icon_distance">32dp</dimen>
+
+ <!-- The default minimal size of a PiP task, in both dimensions. -->
+ <dimen name="default_minimal_size_pip_resizable_task">108dp</dimen>
+
+ <!--
+ The overridable minimal size of a PiP task, in both dimensions.
+ Different from default_minimal_size_pip_resizable_task, this is to limit the dimension
+ when the pinned stack size is overridden by app via minWidth/minHeight.
+ -->
+ <dimen name="overridable_minimal_size_pip_resizable_task">48dp</dimen>
</resources>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index 241f1a7d4d1c..d0138a488295 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -47,7 +47,10 @@ import android.app.ActivityManager;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
import android.content.pm.LauncherApps;
import android.content.pm.PackageManager;
@@ -131,6 +134,10 @@ public class BubbleController {
public static final String RIGHT_POSITION = "Right";
public static final String BOTTOM_POSITION = "Bottom";
+ // Should match with PhoneWindowManager
+ private static final String SYSTEM_DIALOG_REASON_KEY = "reason";
+ private static final String SYSTEM_DIALOG_REASON_GESTURE_NAV = "gestureNav";
+
private final Context mContext;
private final BubblesImpl mImpl = new BubblesImpl();
private Bubbles.BubbleExpandListener mExpandListener;
@@ -675,6 +682,7 @@ public class BubbleController {
try {
mAddedToWindowManager = true;
+ registerBroadcastReceiver();
mBubbleData.getOverflow().initialize(this);
mWindowManager.addView(mStackView, mWmLayoutParams);
mStackView.setOnApplyWindowInsetsListener((view, windowInsets) -> {
@@ -717,6 +725,7 @@ public class BubbleController {
try {
mAddedToWindowManager = false;
+ mContext.unregisterReceiver(mBroadcastReceiver);
if (mStackView != null) {
mWindowManager.removeView(mStackView);
mBubbleData.getOverflow().cleanUpExpandedState();
@@ -730,11 +739,34 @@ public class BubbleController {
}
}
+ private void registerBroadcastReceiver() {
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
+ filter.addAction(Intent.ACTION_SCREEN_OFF);
+ mContext.registerReceiver(mBroadcastReceiver, filter);
+ }
+
+ private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (!isStackExpanded()) return; // Nothing to do
+
+ String action = intent.getAction();
+ String reason = intent.getStringExtra(SYSTEM_DIALOG_REASON_KEY);
+ if ((Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)
+ && SYSTEM_DIALOG_REASON_GESTURE_NAV.equals(reason))
+ || Intent.ACTION_SCREEN_OFF.equals(action)) {
+ mMainExecutor.execute(() -> collapseStack());
+ }
+ }
+ };
+
/**
* Called by the BubbleStackView and whenever all bubbles have animated out, and none have been
* added in the meantime.
*/
- void onAllBubblesAnimatedOut() {
+ @VisibleForTesting
+ public void onAllBubblesAnimatedOut() {
if (mStackView != null) {
mStackView.setVisibility(INVISIBLE);
removeFromWindowManagerMaybe();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
index 75b19fb03e73..bc0db3635e35 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
@@ -67,7 +67,11 @@ public class BubblePositioner {
/** The max percent of screen width to use for the flyout on phone. */
public static final float FLYOUT_MAX_WIDTH_PERCENT = 0.6f;
/** The percent of screen width that should be used for the expanded view on a large screen. **/
- public static final float EXPANDED_VIEW_LARGE_SCREEN_WIDTH_PERCENT = 0.72f;
+ private static final float EXPANDED_VIEW_LARGE_SCREEN_LANDSCAPE_WIDTH_PERCENT = 0.48f;
+ /** The percent of screen width that should be used for the expanded view on a large screen. **/
+ private static final float EXPANDED_VIEW_LARGE_SCREEN_PORTRAIT_WIDTH_PERCENT = 0.70f;
+ /** The percent of screen width that should be used for the expanded view on a small tablet. **/
+ private static final float EXPANDED_VIEW_SMALL_TABLET_WIDTH_PERCENT = 0.72f;
private Context mContext;
private WindowManager mWindowManager;
@@ -77,6 +81,7 @@ public class BubblePositioner {
private boolean mImeVisible;
private int mImeHeight;
private boolean mIsLargeScreen;
+ private boolean mIsSmallTablet;
private Rect mPositionRect;
private int mDefaultMaxBubbles;
@@ -86,7 +91,8 @@ public class BubblePositioner {
private int mExpandedViewMinHeight;
private int mExpandedViewLargeScreenWidth;
- private int mExpandedViewLargeScreenInset;
+ private int mExpandedViewLargeScreenInsetClosestEdge;
+ private int mExpandedViewLargeScreenInsetFurthestEdge;
private int mOverflowWidth;
private int mExpandedViewPadding;
@@ -127,17 +133,26 @@ public class BubblePositioner {
| WindowInsets.Type.statusBars()
| WindowInsets.Type.displayCutout());
- mIsLargeScreen = mContext.getResources().getConfiguration().smallestScreenWidthDp >= 600;
+ final Rect bounds = windowMetrics.getBounds();
+ Configuration config = mContext.getResources().getConfiguration();
+ mIsLargeScreen = config.smallestScreenWidthDp >= 600;
+ if (mIsLargeScreen) {
+ float largestEdgeDp = Math.max(config.screenWidthDp, config.screenHeightDp);
+ mIsSmallTablet = largestEdgeDp < 960;
+ } else {
+ mIsSmallTablet = false;
+ }
if (BubbleDebugConfig.DEBUG_POSITIONER) {
Log.w(TAG, "update positioner:"
+ " rotation: " + mRotation
+ " insets: " + insets
+ " isLargeScreen: " + mIsLargeScreen
- + " bounds: " + windowMetrics.getBounds()
+ + " isSmallTablet: " + mIsSmallTablet
+ + " bounds: " + bounds
+ " showingInTaskbar: " + mShowingInTaskbar);
}
- updateInternal(mRotation, insets, windowMetrics.getBounds());
+ updateInternal(mRotation, insets, bounds);
}
/**
@@ -172,11 +187,31 @@ public class BubblePositioner {
mSpacingBetweenBubbles = res.getDimensionPixelSize(R.dimen.bubble_spacing);
mDefaultMaxBubbles = res.getInteger(R.integer.bubbles_max_rendered);
mExpandedViewPadding = res.getDimensionPixelSize(R.dimen.bubble_expanded_view_padding);
- mExpandedViewLargeScreenWidth = (int) (bounds.width()
- * EXPANDED_VIEW_LARGE_SCREEN_WIDTH_PERCENT);
- mExpandedViewLargeScreenInset = mIsLargeScreen
- ? (bounds.width() - mExpandedViewLargeScreenWidth) / 2
- : mExpandedViewPadding;
+ if (mIsSmallTablet) {
+ mExpandedViewLargeScreenWidth = (int) (bounds.width()
+ * EXPANDED_VIEW_SMALL_TABLET_WIDTH_PERCENT);
+ } else {
+ mExpandedViewLargeScreenWidth = isLandscape()
+ ? (int) (bounds.width() * EXPANDED_VIEW_LARGE_SCREEN_LANDSCAPE_WIDTH_PERCENT)
+ : (int) (bounds.width() * EXPANDED_VIEW_LARGE_SCREEN_PORTRAIT_WIDTH_PERCENT);
+ }
+ if (mIsLargeScreen) {
+ if (isLandscape() && !mIsSmallTablet) {
+ mExpandedViewLargeScreenInsetClosestEdge = res.getDimensionPixelSize(
+ R.dimen.bubble_expanded_view_largescreen_landscape_padding);
+ mExpandedViewLargeScreenInsetFurthestEdge = bounds.width()
+ - mExpandedViewLargeScreenInsetClosestEdge
+ - mExpandedViewLargeScreenWidth;
+ } else {
+ final int centeredInset = (bounds.width() - mExpandedViewLargeScreenWidth) / 2;
+ mExpandedViewLargeScreenInsetClosestEdge = centeredInset;
+ mExpandedViewLargeScreenInsetFurthestEdge = centeredInset;
+ }
+ } else {
+ mExpandedViewLargeScreenInsetClosestEdge = mExpandedViewPadding;
+ mExpandedViewLargeScreenInsetFurthestEdge = mExpandedViewPadding;
+ }
+
mOverflowWidth = mIsLargeScreen
? mExpandedViewLargeScreenWidth
: res.getDimensionPixelSize(
@@ -328,15 +363,18 @@ public class BubblePositioner {
public int[] getExpandedViewContainerPadding(boolean onLeft, boolean isOverflow) {
final int pointerTotalHeight = mPointerHeight - mPointerOverlap;
if (mIsLargeScreen) {
+ // Note:
+ // If we're in portrait OR if we're a small tablet, then the two insets values will
+ // be equal. If we're landscape and a large tablet, the two values will be different.
// [left, top, right, bottom]
mPaddings[0] = onLeft
- ? mExpandedViewLargeScreenInset - pointerTotalHeight
- : mExpandedViewLargeScreenInset;
+ ? mExpandedViewLargeScreenInsetClosestEdge - pointerTotalHeight
+ : mExpandedViewLargeScreenInsetFurthestEdge;
mPaddings[1] = 0;
mPaddings[2] = onLeft
- ? mExpandedViewLargeScreenInset
- : mExpandedViewLargeScreenInset - pointerTotalHeight;
- // Overflow doesn't show manage button / get padding from it so add padding here for it
+ ? mExpandedViewLargeScreenInsetFurthestEdge
+ : mExpandedViewLargeScreenInsetClosestEdge - pointerTotalHeight;
+ // Overflow doesn't show manage button / get padding from it so add padding here
mPaddings[3] = isOverflow ? mExpandedViewPadding : 0;
return mPaddings;
} else {
@@ -494,12 +532,13 @@ public class BubblePositioner {
float x;
float y;
if (showBubblesVertically()) {
+ int inset = mExpandedViewLargeScreenInsetClosestEdge;
y = rowStart + positionInRow;
int left = mIsLargeScreen
- ? mExpandedViewLargeScreenInset - mExpandedViewPadding - mBubbleSize
+ ? inset - mExpandedViewPadding - mBubbleSize
: mPositionRect.left;
int right = mIsLargeScreen
- ? mPositionRect.right - mExpandedViewLargeScreenInset + mExpandedViewPadding
+ ? mPositionRect.right - inset + mExpandedViewPadding
: mPositionRect.right - mBubbleSize;
x = state.onLeft
? left
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/DismissView.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/DismissView.kt
index 74672a336161..063dac3d4109 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/DismissView.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/DismissView.kt
@@ -16,11 +16,16 @@
package com.android.wm.shell.bubbles
+import android.animation.ObjectAnimator
import android.content.Context
-import android.graphics.drawable.TransitionDrawable
+import android.graphics.Color
+import android.graphics.drawable.GradientDrawable
+import android.util.IntProperty
import android.view.Gravity
import android.view.View
import android.view.ViewGroup
+import android.view.WindowManager
+import android.view.WindowInsets
import android.widget.FrameLayout
import androidx.dynamicanimation.animation.DynamicAnimation
import androidx.dynamicanimation.animation.SpringForce.DAMPING_RATIO_LOW_BOUNCY
@@ -28,8 +33,6 @@ import androidx.dynamicanimation.animation.SpringForce.STIFFNESS_LOW
import com.android.wm.shell.R
import com.android.wm.shell.animation.PhysicsAnimator
import com.android.wm.shell.common.DismissCircleView
-import android.view.WindowInsets
-import android.view.WindowManager
/*
* View that handles interactions between DismissCircleView and BubbleStackView.
@@ -41,9 +44,21 @@ class DismissView(context: Context) : FrameLayout(context) {
private val animator = PhysicsAnimator.getInstance(circle)
private val spring = PhysicsAnimator.SpringConfig(STIFFNESS_LOW, DAMPING_RATIO_LOW_BOUNCY)
- private val DISMISS_SCRIM_FADE_MS = 200
+ private val DISMISS_SCRIM_FADE_MS = 200L
private var wm: WindowManager =
context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
+ private var gradientDrawable = createGradient()
+
+ private val GRADIENT_ALPHA: IntProperty<GradientDrawable> =
+ object : IntProperty<GradientDrawable>("alpha") {
+ override fun setValue(d: GradientDrawable, percent: Int) {
+ d.alpha = percent
+ }
+ override fun get(d: GradientDrawable): Int {
+ return d.alpha
+ }
+ }
+
init {
setLayoutParams(LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
@@ -53,8 +68,7 @@ class DismissView(context: Context) : FrameLayout(context) {
setClipToPadding(false)
setClipChildren(false)
setVisibility(View.INVISIBLE)
- setBackgroundResource(
- R.drawable.floating_dismiss_gradient_transition)
+ setBackgroundDrawable(gradientDrawable)
val targetSize: Int = resources.getDimensionPixelSize(R.dimen.dismiss_circle_size)
addView(circle, LayoutParams(targetSize, targetSize,
@@ -71,7 +85,11 @@ class DismissView(context: Context) : FrameLayout(context) {
if (isShowing) return
isShowing = true
setVisibility(View.VISIBLE)
- (getBackground() as TransitionDrawable).startTransition(DISMISS_SCRIM_FADE_MS)
+ val alphaAnim = ObjectAnimator.ofInt(gradientDrawable, GRADIENT_ALPHA,
+ gradientDrawable.alpha, 255)
+ alphaAnim.setDuration(DISMISS_SCRIM_FADE_MS)
+ alphaAnim.start()
+
animator.cancel()
animator
.spring(DynamicAnimation.TRANSLATION_Y, 0f, spring)
@@ -85,7 +103,10 @@ class DismissView(context: Context) : FrameLayout(context) {
fun hide() {
if (!isShowing) return
isShowing = false
- (getBackground() as TransitionDrawable).reverseTransition(DISMISS_SCRIM_FADE_MS)
+ val alphaAnim = ObjectAnimator.ofInt(gradientDrawable, GRADIENT_ALPHA,
+ gradientDrawable.alpha, 0)
+ alphaAnim.setDuration(DISMISS_SCRIM_FADE_MS)
+ alphaAnim.start()
animator
.spring(DynamicAnimation.TRANSLATION_Y, height.toFloat(),
spring)
@@ -93,6 +114,13 @@ class DismissView(context: Context) : FrameLayout(context) {
.start()
}
+ /**
+ * Cancels the animator for the dismiss target.
+ */
+ fun cancelAnimators() {
+ animator.cancel()
+ }
+
fun updateResources() {
updatePadding()
layoutParams.height = resources.getDimensionPixelSize(
@@ -104,6 +132,20 @@ class DismissView(context: Context) : FrameLayout(context) {
circle.requestLayout()
}
+ private fun createGradient(): GradientDrawable {
+ val gradientColor = context.resources.getColor(android.R.color.system_neutral1_900)
+ val alpha = 0.7f * 255
+ val gradientColorWithAlpha = Color.argb(alpha.toInt(),
+ Color.red(gradientColor),
+ Color.green(gradientColor),
+ Color.blue(gradientColor))
+ val gd = GradientDrawable(
+ GradientDrawable.Orientation.BOTTOM_TOP,
+ intArrayOf(gradientColorWithAlpha, Color.TRANSPARENT))
+ gd.setAlpha(0)
+ return gd
+ }
+
private fun updatePadding() {
val insets: WindowInsets = wm.getCurrentWindowMetrics().getWindowInsets()
val navInset = insets.getInsetsIgnoringVisibility(
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
index f7c92fed5522..8a482fbfa1c4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
@@ -144,21 +144,42 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
return Math.max(dividerInset, radius);
}
- /** Gets bounds of the primary split. */
+ /** Gets bounds of the primary split with screen based coordinate. */
public Rect getBounds1() {
return new Rect(mBounds1);
}
- /** Gets bounds of the secondary split. */
+ /** Gets bounds of the primary split with parent based coordinate. */
+ public Rect getRefBounds1() {
+ Rect outBounds = getBounds1();
+ outBounds.offset(-mRootBounds.left, -mRootBounds.top);
+ return outBounds;
+ }
+
+ /** Gets bounds of the secondary split with screen based coordinate. */
public Rect getBounds2() {
return new Rect(mBounds2);
}
- /** Gets bounds of divider window. */
+ /** Gets bounds of the secondary split with parent based coordinate. */
+ public Rect getRefBounds2() {
+ final Rect outBounds = getBounds2();
+ outBounds.offset(-mRootBounds.left, -mRootBounds.top);
+ return outBounds;
+ }
+
+ /** Gets bounds of divider window with screen based coordinate. */
public Rect getDividerBounds() {
return new Rect(mDividerBounds);
}
+ /** Gets bounds of divider window with parent based coordinate. */
+ public Rect getRefDividerBounds() {
+ final Rect outBounds = getDividerBounds();
+ outBounds.offset(-mRootBounds.left, -mRootBounds.top);
+ return outBounds;
+ }
+
/** Returns leash of the current divider bar. */
@Nullable
public SurfaceControl getDividerLeash() {
@@ -452,14 +473,17 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
SurfaceControl leash2, SurfaceControl dimLayer1, SurfaceControl dimLayer2) {
final SurfaceControl dividerLeash = getDividerLeash();
if (dividerLeash != null) {
- t.setPosition(dividerLeash, mDividerBounds.left, mDividerBounds.top);
+ mTempRect.set(getRefDividerBounds());
+ t.setPosition(dividerLeash, mTempRect.left, mTempRect.top);
// Resets layer of divider bar to make sure it is always on top.
t.setLayer(dividerLeash, SPLIT_DIVIDER_LAYER);
}
- t.setPosition(leash1, mBounds1.left, mBounds1.top)
- .setWindowCrop(leash1, mBounds1.width(), mBounds1.height());
- t.setPosition(leash2, mBounds2.left, mBounds2.top)
- .setWindowCrop(leash2, mBounds2.width(), mBounds2.height());
+ mTempRect.set(getRefBounds1());
+ t.setPosition(leash1, mTempRect.left, mTempRect.top)
+ .setWindowCrop(leash1, mTempRect.width(), mTempRect.height());
+ mTempRect.set(getRefBounds2());
+ t.setPosition(leash2, mTempRect.left, mTempRect.top)
+ .setWindowCrop(leash2, mTempRect.width(), mTempRect.height());
if (mImePositionProcessor.adjustSurfaceLayoutForIme(
t, dividerLeash, leash1, leash2, dimLayer1, dimLayer2)) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java
index e29dde2726e3..797df413d262 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java
@@ -32,6 +32,7 @@ import android.util.Size;
import android.util.TypedValue;
import android.view.Gravity;
+import com.android.wm.shell.R;
import com.android.wm.shell.common.DisplayLayout;
import java.io.PrintWriter;
@@ -76,15 +77,15 @@ public class PipBoundsAlgorithm {
protected void reloadResources(Context context) {
final Resources res = context.getResources();
mDefaultAspectRatio = res.getFloat(
- com.android.internal.R.dimen.config_pictureInPictureDefaultAspectRatio);
+ R.dimen.config_pictureInPictureDefaultAspectRatio);
mDefaultStackGravity = res.getInteger(
- com.android.internal.R.integer.config_defaultPictureInPictureGravity);
+ R.integer.config_defaultPictureInPictureGravity);
mDefaultMinSize = res.getDimensionPixelSize(
- com.android.internal.R.dimen.default_minimal_size_pip_resizable_task);
+ R.dimen.default_minimal_size_pip_resizable_task);
mOverridableMinSize = res.getDimensionPixelSize(
- com.android.internal.R.dimen.overridable_minimal_size_pip_resizable_task);
+ R.dimen.overridable_minimal_size_pip_resizable_task);
final String screenEdgeInsetsDpString = res.getString(
- com.android.internal.R.string.config_defaultPictureInPictureScreenEdgeInsets);
+ R.string.config_defaultPictureInPictureScreenEdgeInsets);
final Size screenEdgeInsetsDp = !screenEdgeInsetsDpString.isEmpty()
? Size.parseSize(screenEdgeInsetsDpString)
: null;
@@ -96,9 +97,9 @@ public class PipBoundsAlgorithm {
mMaxAspectRatio = res.getFloat(
com.android.internal.R.dimen.config_pictureInPictureMaxAspectRatio);
mDefaultSizePercent = res.getFloat(
- com.android.internal.R.dimen.config_pictureInPictureDefaultSizePercent);
+ R.dimen.config_pictureInPictureDefaultSizePercent);
mMaxAspectRatioForMinSize = res.getFloat(
- com.android.internal.R.dimen.config_pictureInPictureAspectRatioLimitForMinSize);
+ R.dimen.config_pictureInPictureAspectRatioLimitForMinSize);
mMinAspectRatioForMinSize = 1f / mMaxAspectRatioForMinSize;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java
index 180e3fb48c9d..d7322ce7beda 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java
@@ -138,8 +138,8 @@ public class PipSurfaceTransactionHelper {
// destination are different.
final float scale = srcW <= srcH ? (float) destW / srcW : (float) destH / srcH;
final Rect crop = mTmpDestinationRect;
- crop.set(0, 0, Transitions.ENABLE_SHELL_TRANSITIONS ? destH
- : destW, Transitions.ENABLE_SHELL_TRANSITIONS ? destW : destH);
+ crop.set(0, 0, Transitions.SHELL_TRANSITIONS_ROTATION ? destH
+ : destW, Transitions.SHELL_TRANSITIONS_ROTATION ? destW : destH);
// Inverse scale for crop to fit in screen coordinates.
crop.scale(1 / scale);
crop.offset(insets.left, insets.top);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
index 60aac6806623..91615fe05417 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
@@ -97,7 +97,7 @@ public class PipTransition extends PipTransitionController {
* meaningful if {@link #mInFixedRotation} is true.
*/
@Surface.Rotation
- private int mFixedRotation;
+ private int mEndFixedRotation;
/** Whether the PIP window has fade out for fixed rotation. */
private boolean mHasFadeOut;
@@ -153,7 +153,7 @@ public class PipTransition extends PipTransitionController {
final TransitionInfo.Change currentPipChange = findCurrentPipChange(info);
final TransitionInfo.Change fixedRotationChange = findFixedRotationChange(info);
mInFixedRotation = fixedRotationChange != null;
- mFixedRotation = mInFixedRotation
+ mEndFixedRotation = mInFixedRotation
? fixedRotationChange.getEndFixedRotation()
: ROTATION_UNDEFINED;
@@ -257,7 +257,7 @@ public class PipTransition extends PipTransitionController {
final ActivityManager.RunningTaskInfo taskInfo = mPipOrganizer.getTaskInfo();
if (taskInfo != null) {
startExpandAnimation(taskInfo, mPipOrganizer.getSurfaceControl(),
- new Rect(mExitDestinationBounds));
+ new Rect(mExitDestinationBounds), Surface.ROTATION_0);
}
mExitDestinationBounds.setEmpty();
mCurrentPipTaskToken = null;
@@ -332,30 +332,31 @@ public class PipTransition extends PipTransitionController {
@NonNull SurfaceControl.Transaction finishTransaction,
@NonNull Transitions.TransitionFinishCallback finishCallback,
@NonNull TransitionInfo.Change pipChange) {
- TransitionInfo.Change displayRotationChange = null;
- for (int i = info.getChanges().size() - 1; i >= 0; --i) {
- final TransitionInfo.Change change = info.getChanges().get(i);
- if (change.getMode() == TRANSIT_CHANGE
- && (change.getFlags() & FLAG_IS_DISPLAY) != 0
- && change.getStartRotation() != change.getEndRotation()) {
- displayRotationChange = change;
- break;
- }
- }
-
- if (displayRotationChange != null) {
- // Exiting PIP to fullscreen with orientation change.
- startExpandAndRotationAnimation(info, startTransaction, finishTransaction,
- finishCallback, displayRotationChange, pipChange);
- return;
- }
-
- // When there is no rotation, we can simply expand the PIP window.
mFinishCallback = (wct, wctCB) -> {
mPipOrganizer.onExitPipFinished(pipChange.getTaskInfo());
finishCallback.onTransitionFinished(wct, wctCB);
};
+ // Check if it is Shell rotation.
+ if (Transitions.SHELL_TRANSITIONS_ROTATION) {
+ TransitionInfo.Change displayRotationChange = null;
+ for (int i = info.getChanges().size() - 1; i >= 0; --i) {
+ final TransitionInfo.Change change = info.getChanges().get(i);
+ if (change.getMode() == TRANSIT_CHANGE
+ && (change.getFlags() & FLAG_IS_DISPLAY) != 0
+ && change.getStartRotation() != change.getEndRotation()) {
+ displayRotationChange = change;
+ break;
+ }
+ }
+ if (displayRotationChange != null) {
+ // Exiting PIP to fullscreen with orientation change.
+ startExpandAndRotationAnimation(info, startTransaction, finishTransaction,
+ displayRotationChange, pipChange);
+ return;
+ }
+ }
+
// Set the initial frame as scaling the end to the start.
final Rect destinationBounds = new Rect(pipChange.getEndAbsBounds());
final Point offset = pipChange.getEndRelOffset();
@@ -364,13 +365,41 @@ public class PipTransition extends PipTransitionController {
mSurfaceTransactionHelper.scale(startTransaction, pipChange.getLeash(),
destinationBounds, mPipBoundsState.getBounds());
startTransaction.apply();
- startExpandAnimation(pipChange.getTaskInfo(), pipChange.getLeash(), destinationBounds);
+
+ // Check if it is fixed rotation.
+ final int rotationDelta;
+ if (mInFixedRotation) {
+ final int startRotation = pipChange.getStartRotation();
+ final int endRotation = mEndFixedRotation;
+ rotationDelta = deltaRotation(startRotation, endRotation);
+ final Rect endBounds = new Rect(destinationBounds);
+
+ // Set the end frame since the display won't rotate until fixed rotation is finished
+ // in the next display change transition.
+ rotateBounds(endBounds, destinationBounds, rotationDelta);
+ final int degree, x, y;
+ if (rotationDelta == ROTATION_90) {
+ degree = 90;
+ x = destinationBounds.right;
+ y = destinationBounds.top;
+ } else {
+ degree = -90;
+ x = destinationBounds.left;
+ y = destinationBounds.bottom;
+ }
+ mSurfaceTransactionHelper.rotateAndScaleWithCrop(finishTransaction,
+ pipChange.getLeash(), endBounds, endBounds, new Rect(), degree, x, y,
+ true /* isExpanding */, rotationDelta == ROTATION_270 /* clockwise */);
+ } else {
+ rotationDelta = Surface.ROTATION_0;
+ }
+ startExpandAnimation(pipChange.getTaskInfo(), pipChange.getLeash(), destinationBounds,
+ rotationDelta);
}
private void startExpandAndRotationAnimation(@NonNull TransitionInfo info,
@NonNull SurfaceControl.Transaction startTransaction,
@NonNull SurfaceControl.Transaction finishTransaction,
- @NonNull Transitions.TransitionFinishCallback finishCallback,
@NonNull TransitionInfo.Change displayRotationChange,
@NonNull TransitionInfo.Change pipChange) {
final int rotateDelta = deltaRotation(displayRotationChange.getStartRotation(),
@@ -380,11 +409,6 @@ public class PipTransition extends PipTransitionController {
final CounterRotatorHelper rotator = new CounterRotatorHelper();
rotator.handleClosingChanges(info, startTransaction, displayRotationChange);
- mFinishCallback = (wct, wctCB) -> {
- mPipOrganizer.onExitPipFinished(pipChange.getTaskInfo());
- finishCallback.onTransitionFinished(wct, wctCB);
- };
-
// Get the start bounds in new orientation.
final Rect startBounds = new Rect(pipChange.getStartAbsBounds());
rotateBounds(startBounds, displayRotationChange.getStartAbsBounds(), rotateDelta);
@@ -425,12 +449,11 @@ public class PipTransition extends PipTransitionController {
}
private void startExpandAnimation(final TaskInfo taskInfo, final SurfaceControl leash,
- final Rect destinationBounds) {
- PipAnimationController.PipTransitionAnimator animator =
+ final Rect destinationBounds, final int rotationDelta) {
+ final PipAnimationController.PipTransitionAnimator animator =
mPipAnimationController.getAnimator(taskInfo, leash, mPipBoundsState.getBounds(),
mPipBoundsState.getBounds(), destinationBounds, null,
- TRANSITION_DIRECTION_LEAVE_PIP, 0 /* startingAngle */, Surface.ROTATION_0);
-
+ TRANSITION_DIRECTION_LEAVE_PIP, 0 /* startingAngle */, rotationDelta);
animator.setTransitionDirection(TRANSITION_DIRECTION_LEAVE_PIP)
.setPipAnimationCallback(mPipAnimationCallback)
.setDuration(mEnterExitAnimationDuration)
@@ -526,7 +549,7 @@ public class PipTransition extends PipTransitionController {
mPipTransitionState.setTransitionState(PipTransitionState.ENTERING_PIP);
mFinishCallback = finishCallback;
- final int endRotation = mInFixedRotation ? mFixedRotation : enterPip.getEndRotation();
+ final int endRotation = mInFixedRotation ? mEndFixedRotation : enterPip.getEndRotation();
return startEnterAnimation(enterPip.getTaskInfo(), enterPip.getLeash(),
startTransaction, finishTransaction, enterPip.getStartRotation(),
endRotation);
@@ -545,8 +568,8 @@ public class PipTransition extends PipTransitionController {
taskInfo.pictureInPictureParams, currentBounds);
if (rotationDelta != Surface.ROTATION_0 && mInFixedRotation) {
// Need to get the bounds of new rotation in old rotation for fixed rotation,
- sourceHintRect = computeRotatedBounds(rotationDelta, startRotation, endRotation,
- taskInfo, TRANSITION_DIRECTION_TO_PIP, destinationBounds, sourceHintRect);
+ computeEnterPipRotatedBounds(rotationDelta, startRotation, endRotation, taskInfo,
+ destinationBounds, sourceHintRect);
}
PipAnimationController.PipTransitionAnimator animator;
// Set corner radius for entering pip.
@@ -583,8 +606,6 @@ public class PipTransition extends PipTransitionController {
startTransaction.setMatrix(leash, tmpTransform, new float[9]);
}
if (mOneShotAnimationType == ANIM_TYPE_BOUNDS) {
- // Reverse the rotation for Shell transition animation.
- rotationDelta = deltaRotation(rotationDelta, 0);
animator = mPipAnimationController.getAnimator(taskInfo, leash, currentBounds,
currentBounds, destinationBounds, sourceHintRect, TRANSITION_DIRECTION_TO_PIP,
0 /* startingAngle */, rotationDelta);
@@ -617,33 +638,22 @@ public class PipTransition extends PipTransitionController {
return true;
}
- /** Computes destination bounds in old rotation and returns source hint rect if available. */
- @Nullable
- private Rect computeRotatedBounds(int rotationDelta, int startRotation, int endRotation,
- TaskInfo taskInfo, int direction, Rect outDestinationBounds,
- @Nullable Rect sourceHintRect) {
- if (direction == TRANSITION_DIRECTION_TO_PIP) {
- mPipBoundsState.getDisplayLayout().rotateTo(mContext.getResources(), endRotation);
- final Rect displayBounds = mPipBoundsState.getDisplayBounds();
- outDestinationBounds.set(mPipBoundsAlgorithm.getEntryDestinationBounds());
- // Transform the destination bounds to current display coordinates.
- rotateBounds(outDestinationBounds, displayBounds, endRotation, startRotation);
- // When entering PiP (from button navigation mode), adjust the source rect hint by
- // display cutout if applicable.
- if (sourceHintRect != null && taskInfo.displayCutoutInsets != null) {
- if (rotationDelta == Surface.ROTATION_270) {
- sourceHintRect.offset(taskInfo.displayCutoutInsets.left,
- taskInfo.displayCutoutInsets.top);
- }
+ /** Computes destination bounds in old rotation and updates source hint rect if available. */
+ private void computeEnterPipRotatedBounds(int rotationDelta, int startRotation, int endRotation,
+ TaskInfo taskInfo, Rect outDestinationBounds, @Nullable Rect outSourceHintRect) {
+ mPipBoundsState.getDisplayLayout().rotateTo(mContext.getResources(), endRotation);
+ final Rect displayBounds = mPipBoundsState.getDisplayBounds();
+ outDestinationBounds.set(mPipBoundsAlgorithm.getEntryDestinationBounds());
+ // Transform the destination bounds to current display coordinates.
+ rotateBounds(outDestinationBounds, displayBounds, endRotation, startRotation);
+ // When entering PiP (from button navigation mode), adjust the source rect hint by
+ // display cutout if applicable.
+ if (outSourceHintRect != null && taskInfo.displayCutoutInsets != null) {
+ if (rotationDelta == Surface.ROTATION_270) {
+ outSourceHintRect.offset(taskInfo.displayCutoutInsets.left,
+ taskInfo.displayCutoutInsets.top);
}
- } else if (direction == TRANSITION_DIRECTION_LEAVE_PIP) {
- final Rect rotatedDestinationBounds = new Rect(outDestinationBounds);
- rotateBounds(rotatedDestinationBounds, mPipBoundsState.getDisplayBounds(),
- rotationDelta);
- return PipBoundsAlgorithm.getValidSourceHintRect(taskInfo.pictureInPictureParams,
- rotatedDestinationBounds);
- }
- return sourceHintRect;
+ }
}
private void startExitToSplitAnimation(TransitionInfo info,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java
index 915c5939c34b..3115f8afde3b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java
@@ -20,27 +20,20 @@ import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_M
import android.content.Context;
import android.content.res.Resources;
-import android.graphics.Insets;
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.graphics.Rect;
-import android.graphics.drawable.TransitionDrawable;
-import android.view.Gravity;
import android.view.MotionEvent;
import android.view.SurfaceControl;
import android.view.View;
-import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.WindowInsets;
import android.view.WindowManager;
-import android.widget.FrameLayout;
import androidx.annotation.NonNull;
-import androidx.dynamicanimation.animation.DynamicAnimation;
-import androidx.dynamicanimation.animation.SpringForce;
import com.android.wm.shell.R;
-import com.android.wm.shell.animation.PhysicsAnimator;
+import com.android.wm.shell.bubbles.DismissView;
import com.android.wm.shell.common.DismissCircleView;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.magnetictarget.MagnetizedObject;
@@ -56,9 +49,6 @@ public class PipDismissTargetHandler implements ViewTreeObserver.OnPreDrawListen
/* The multiplier to apply scale the target size by when applying the magnetic field radius */
private static final float MAGNETIC_FIELD_RADIUS_MULTIPLIER = 1.25f;
- /** Duration of the dismiss scrim fading in/out. */
- private static final int DISMISS_TRANSITION_DURATION_MS = 200;
-
/**
* MagnetizedObject wrapper for PIP. This allows the magnetic target library to locate and move
* PIP.
@@ -69,7 +59,7 @@ public class PipDismissTargetHandler implements ViewTreeObserver.OnPreDrawListen
* Container for the dismiss circle, so that it can be animated within the container via
* translation rather than within the WindowManager via slow layout animations.
*/
- private ViewGroup mTargetViewContainer;
+ private DismissView mTargetViewContainer;
/** Circle view used to render the dismiss target. */
private DismissCircleView mTargetView;
@@ -79,16 +69,6 @@ public class PipDismissTargetHandler implements ViewTreeObserver.OnPreDrawListen
*/
private MagnetizedObject.MagneticTarget mMagneticTarget;
- /**
- * PhysicsAnimator instance for animating the dismiss target in/out.
- */
- private PhysicsAnimator<View> mMagneticTargetAnimator;
-
- /** Default configuration to use for springing the dismiss target in/out. */
- private final PhysicsAnimator.SpringConfig mTargetSpringConfig =
- new PhysicsAnimator.SpringConfig(
- SpringForce.STIFFNESS_LOW, SpringForce.DAMPING_RATIO_LOW_BOUNCY);
-
// Allow dragging the PIP to a location to close it
private boolean mEnableDismissDragToEdge;
@@ -125,12 +105,8 @@ public class PipDismissTargetHandler implements ViewTreeObserver.OnPreDrawListen
cleanUpDismissTarget();
}
- mTargetView = new DismissCircleView(mContext);
- mTargetViewContainer = new FrameLayout(mContext);
- mTargetViewContainer.setBackgroundDrawable(
- mContext.getDrawable(R.drawable.floating_dismiss_gradient_transition));
- mTargetViewContainer.setClipChildren(false);
- mTargetViewContainer.addView(mTargetView);
+ mTargetViewContainer = new DismissView(mContext);
+ mTargetView = mTargetViewContainer.getCircle();
mTargetViewContainer.setOnApplyWindowInsetsListener((view, windowInsets) -> {
if (!windowInsets.equals(mWindowInsets)) {
mWindowInsets = windowInsets;
@@ -187,7 +163,6 @@ public class PipDismissTargetHandler implements ViewTreeObserver.OnPreDrawListen
}
});
- mMagneticTargetAnimator = PhysicsAnimator.getInstance(mTargetView);
}
@Override
@@ -213,19 +188,13 @@ public class PipDismissTargetHandler implements ViewTreeObserver.OnPreDrawListen
if (mTargetView == null) {
return;
}
+ if (mTargetViewContainer != null) {
+ mTargetViewContainer.updateResources();
+ }
final Resources res = mContext.getResources();
mTargetSize = res.getDimensionPixelSize(R.dimen.dismiss_circle_size);
mDismissAreaHeight = res.getDimensionPixelSize(R.dimen.floating_dismiss_gradient_height);
- final WindowInsets insets = mWindowManager.getCurrentWindowMetrics().getWindowInsets();
- final Insets navInset = insets.getInsetsIgnoringVisibility(
- WindowInsets.Type.navigationBars());
- final FrameLayout.LayoutParams newParams =
- new FrameLayout.LayoutParams(mTargetSize, mTargetSize);
- newParams.gravity = Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM;
- newParams.bottomMargin = navInset.bottom + mContext.getResources().getDimensionPixelSize(
- R.dimen.floating_dismiss_bottom_margin);
- mTargetView.setLayoutParams(newParams);
// Set the magnetic field radius equal to the target size from the center of the target
setMagneticFieldRadiusPercent(mMagneticFieldRadiusPercent);
@@ -261,7 +230,7 @@ public class PipDismissTargetHandler implements ViewTreeObserver.OnPreDrawListen
/** Adds the magnetic target view to the WindowManager so it's ready to be animated in. */
public void createOrUpdateDismissTarget() {
if (!mTargetViewContainer.isAttachedToWindow()) {
- mMagneticTargetAnimator.cancel();
+ mTargetViewContainer.cancelAnimators();
mTargetViewContainer.setVisibility(View.INVISIBLE);
mTargetViewContainer.getViewTreeObserver().removeOnPreDrawListener(this);
@@ -312,18 +281,8 @@ public class PipDismissTargetHandler implements ViewTreeObserver.OnPreDrawListen
createOrUpdateDismissTarget();
if (mTargetViewContainer.getVisibility() != View.VISIBLE) {
- mTargetView.setTranslationY(mTargetViewContainer.getHeight());
- mTargetViewContainer.setVisibility(View.VISIBLE);
mTargetViewContainer.getViewTreeObserver().addOnPreDrawListener(this);
-
- // Cancel in case we were in the middle of animating it out.
- mMagneticTargetAnimator.cancel();
- mMagneticTargetAnimator
- .spring(DynamicAnimation.TRANSLATION_Y, 0f, mTargetSpringConfig)
- .start();
-
- ((TransitionDrawable) mTargetViewContainer.getBackground()).startTransition(
- DISMISS_TRANSITION_DURATION_MS);
+ mTargetViewContainer.show();
}
}
@@ -332,16 +291,7 @@ public class PipDismissTargetHandler implements ViewTreeObserver.OnPreDrawListen
if (!mEnableDismissDragToEdge) {
return;
}
-
- mMagneticTargetAnimator
- .spring(DynamicAnimation.TRANSLATION_Y,
- mTargetViewContainer.getHeight(),
- mTargetSpringConfig)
- .withEndActions(() -> mTargetViewContainer.setVisibility(View.GONE))
- .start();
-
- ((TransitionDrawable) mTargetViewContainer.getBackground()).reverseTransition(
- DISMISS_TRANSITION_DURATION_MS);
+ mTargetViewContainer.hide();
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
index 5996acd269d2..50691803e8f9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
@@ -43,6 +43,7 @@ import com.android.wm.shell.common.TaskStackListenerCallback;
import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.pip.PinnedStackListenerForwarder;
import com.android.wm.shell.pip.Pip;
+import com.android.wm.shell.pip.PipAnimationController;
import com.android.wm.shell.pip.PipMediaController;
import com.android.wm.shell.pip.PipTaskOrganizer;
import com.android.wm.shell.pip.PipTransitionController;
@@ -289,7 +290,6 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
if (DEBUG) Log.d(TAG, "checkIfPinnedTaskAppeared(), task=" + pinnedTask);
if (pinnedTask == null || pinnedTask.topActivity == null) return;
mPinnedTaskId = pinnedTask.taskId;
- setState(STATE_PIP);
mPipMediaController.onActivityPinned();
mPipNotificationController.show(pinnedTask.topActivity.getPackageName());
@@ -326,6 +326,9 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
@Override
public void onPipTransitionFinished(int direction) {
+ if (PipAnimationController.isInPipDirection(direction) && mState == STATE_NO_PIP) {
+ setState(STATE_PIP);
+ }
if (DEBUG) Log.d(TAG, "onPipTransition_Finished(), state=" + stateToName(mState));
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java
index 69d6c9e0c3bd..32ebe2d6aecf 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java
@@ -33,6 +33,7 @@ import android.os.RemoteException;
import android.util.Log;
import android.view.SurfaceControl;
import android.view.SyncRtSurfaceTransactionApplier;
+import android.view.WindowManagerGlobal;
import androidx.annotation.Nullable;
@@ -143,7 +144,6 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis
mSystemWindows.addView(mPipMenuView,
getPipMenuLayoutParams(MENU_WINDOW_TITLE, 0 /* width */, 0 /* height */),
0, SHELL_ROOT_LAYER_PIP);
- mPipMenuView.setFocusGrantToken(mSystemWindows.getFocusGrantToken(mPipMenuView));
}
@Override
@@ -164,6 +164,7 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis
t.setPosition(menuSurfaceControl, menuBounds.left, menuBounds.top);
t.apply();
}
+ grantPipMenuFocus(true);
mPipMenuView.show(mInMoveMode, mDelegate.getPipGravity());
}
}
@@ -194,8 +195,9 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis
if (DEBUG) Log.d(TAG, "hideMenu()");
}
- mPipMenuView.hide(mInMoveMode);
+ mPipMenuView.hide();
if (!mInMoveMode) {
+ grantPipMenuFocus(false);
mDelegate.closeMenu();
}
}
@@ -453,4 +455,15 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis
void closePip();
}
+
+ private void grantPipMenuFocus(boolean grantFocus) {
+ if (DEBUG) Log.d(TAG, "grantWindowFocus(" + grantFocus + ")");
+
+ try {
+ WindowManagerGlobal.getWindowSession().grantEmbeddedWindowFocus(null /* window */,
+ mSystemWindows.getFocusGrantToken(mPipMenuView), grantFocus);
+ } catch (Exception e) {
+ Log.e(TAG, "Unable to update focus", e);
+ }
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java
index 773e9bfa8977..3090139f6db9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java
@@ -30,7 +30,6 @@ import android.app.RemoteAction;
import android.content.Context;
import android.graphics.Rect;
import android.os.Handler;
-import android.os.IBinder;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Gravity;
@@ -39,7 +38,6 @@ import android.view.SurfaceControl;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewRootImpl;
-import android.view.WindowManagerGlobal;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
@@ -72,7 +70,6 @@ public class TvPipMenuView extends FrameLayout implements View.OnClickListener {
private final ImageView mArrowRight;
private final ImageView mArrowDown;
private final ImageView mArrowLeft;
- private IBinder mFocusGrantToken = null;
private final ViewGroup mScrollView;
private final ViewGroup mHorizontalScrollView;
@@ -152,10 +149,6 @@ public class TvPipMenuView extends FrameLayout implements View.OnClickListener {
mListener = listener;
}
- void setFocusGrantToken(IBinder token) {
- mFocusGrantToken = token;
- }
-
void setExpandedModeEnabled(boolean enabled) {
mExpandButton.setVisibility(enabled ? VISIBLE : GONE);
}
@@ -170,8 +163,6 @@ public class TvPipMenuView extends FrameLayout implements View.OnClickListener {
void show(boolean inMoveMode, int gravity) {
if (DEBUG) Log.d(TAG, "show(), inMoveMode: " + inMoveMode);
- grantWindowFocus(true);
-
if (inMoveMode) {
showMovementHints(gravity);
} else {
@@ -180,15 +171,11 @@ public class TvPipMenuView extends FrameLayout implements View.OnClickListener {
animateAlphaTo(1, mMenuFrameView);
}
- void hide(boolean isInMoveMode) {
+ void hide() {
if (DEBUG) Log.d(TAG, "hide()");
animateAlphaTo(0, mActionButtonsContainer);
animateAlphaTo(0, mMenuFrameView);
hideMovementHints();
-
- if (!isInMoveMode) {
- grantWindowFocus(false);
- }
}
private void animateAlphaTo(float alpha, View view) {
@@ -217,17 +204,6 @@ public class TvPipMenuView extends FrameLayout implements View.OnClickListener {
|| mArrowLeft.getAlpha() != 0f;
}
- private void grantWindowFocus(boolean grantFocus) {
- if (DEBUG) Log.d(TAG, "grantWindowFocus(" + grantFocus + ")");
-
- try {
- WindowManagerGlobal.getWindowSession().grantEmbeddedWindowFocus(null /* window */,
- mFocusGrantToken, grantFocus);
- } catch (Exception e) {
- Log.e(TAG, "Unable to update focus", e);
- }
- }
-
void setAdditionalActions(List<RemoteAction> actions, Handler mainHandler) {
if (DEBUG) Log.d(TAG, "setAdditionalActions()");
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index 81dacdb753a7..76641f0e6c21 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -979,7 +979,8 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
t.setAlpha(dividerLeash, 1);
t.setLayer(dividerLeash, SPLIT_DIVIDER_LAYER);
t.setPosition(dividerLeash,
- mSplitLayout.getDividerBounds().left, mSplitLayout.getDividerBounds().top);
+ mSplitLayout.getRefDividerBounds().left,
+ mSplitLayout.getRefDividerBounds().top);
} else {
t.hide(dividerLeash);
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestSupportPairNonResizeableApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestSupportPairNonResizeableApps.kt
index 65eb9aa991c2..57bcbc093a62 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestSupportPairNonResizeableApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestSupportPairNonResizeableApps.kt
@@ -17,6 +17,7 @@
package com.android.wm.shell.flicker.apppairs
import android.platform.test.annotations.Presubmit
+import android.view.Display
import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
@@ -24,6 +25,7 @@ import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group1
import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.traces.common.WindowManagerConditionsFactory
import com.android.wm.shell.flicker.appPairsDividerIsVisibleAtEnd
import com.android.wm.shell.flicker.helpers.AppPairsHelper
import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.resetMultiWindowConfig
@@ -60,7 +62,18 @@ class AppPairsTestSupportPairNonResizeableApps(
// TODO pair apps through normal UX flow
executeShellCommand(
composePairsCommand(primaryTaskId, nonResizeableTaskId, pair = true))
- nonResizeableApp?.run { wmHelper.waitForFullScreenApp(nonResizeableApp.component) }
+ val waitConditions = mutableListOf(
+ WindowManagerConditionsFactory.isWindowVisible(primaryApp.component),
+ WindowManagerConditionsFactory.isLayerVisible(primaryApp.component),
+ WindowManagerConditionsFactory.isAppTransitionIdle(Display.DEFAULT_DISPLAY))
+
+ nonResizeableApp?.let {
+ waitConditions.add(
+ WindowManagerConditionsFactory.isWindowVisible(nonResizeableApp.component))
+ waitConditions.add(
+ WindowManagerConditionsFactory.isLayerVisible(nonResizeableApp.component))
+ }
+ wmHelper.waitFor(*waitConditions.toTypedArray())
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/ImeAppHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/ImeAppHelper.kt
index 0f00edea136f..cc5b9f9eb26d 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/ImeAppHelper.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/ImeAppHelper.kt
@@ -62,7 +62,7 @@ open class ImeAppHelper(instrumentation: Instrumentation) : BaseAppHelper(
if (wmHelper == null) {
device.waitForIdle()
} else {
- require(wmHelper.waitImeShown()) { "IME did not appear" }
+ wmHelper.waitImeShown()
}
}
@@ -79,7 +79,7 @@ open class ImeAppHelper(instrumentation: Instrumentation) : BaseAppHelper(
if (wmHelper == null) {
uiDevice.waitForIdle()
} else {
- require(wmHelper.waitImeGone()) { "IME did did not close" }
+ wmHelper.waitImeGone()
}
} else {
// While pressing the back button should close the IME on TV as well, it may also lead
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt
index 7e232ea32181..e9d438a569d5 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt
@@ -58,17 +58,27 @@ class PipAppHelper(instrumentation: Instrumentation) : BaseAppHelper(
}
}
- /** {@inheritDoc} */
- override fun launchViaIntent(
+ /**
+ * Launches the app through an intent instead of interacting with the launcher and waits
+ * until the app window is in PIP mode
+ */
+ @JvmOverloads
+ fun launchViaIntentAndWaitForPip(
wmHelper: WindowManagerStateHelper,
- expectedWindowName: String,
- action: String?,
+ expectedWindowName: String = "",
+ action: String? = null,
stringExtras: Map<String, String>
) {
- super.launchViaIntent(wmHelper, expectedWindowName, action, stringExtras)
- wmHelper.waitPipShown()
+ launchViaIntentAndWaitShown(wmHelper, expectedWindowName, action, stringExtras,
+ waitConditions = arrayOf(WindowManagerStateHelper.pipShownCondition))
}
+ /**
+ * Expand the PIP window back to full screen via intent and wait until the app is visible
+ */
+ fun exitPipToFullScreenViaIntent(wmHelper: WindowManagerStateHelper) =
+ launchViaIntentAndWaitShown(wmHelper)
+
private fun focusOnObject(selector: BySelector): Boolean {
// We expect all the focusable UI elements to be arranged in a way so that it is possible
// to "cycle" over all them by clicking the D-Pad DOWN button, going back up to "the top"
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
index f2c50935a3e9..274d34ba3c5b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
@@ -64,7 +64,18 @@ class EnterPipTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) {
* Defines the transition used to run the test
*/
override val transition: FlickerBuilder.() -> Unit
- get() = buildTransition(eachRun = true, stringExtras = emptyMap()) {
+ get() = {
+ setupAndTeardown(this)
+ setup {
+ eachRun {
+ pipApp.launchViaIntent(wmHelper)
+ }
+ }
+ teardown {
+ eachRun {
+ pipApp.exit(wmHelper)
+ }
+ }
transitions {
pipApp.clickEnterPipButton(wmHelper)
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipTransition.kt
index c22d3f65b201..0b4bc761838d 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipTransition.kt
@@ -93,15 +93,4 @@ abstract class ExitPipTransition(testSpec: FlickerTestParameter) : PipTransition
.isVisible(LAUNCHER_COMPONENT)
}
}
-
- /**
- * Checks that the focus doesn't change between windows during the transition
- */
- @Presubmit
- @Test
- open fun focusDoesNotChange() {
- testSpec.assertEventLog {
- this.focusDoesNotChange()
- }
- }
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaExpandButtonClickTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaExpandButtonClickTest.kt
index 4f98b70e222a..e2d08346efb6 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaExpandButtonClickTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaExpandButtonClickTest.kt
@@ -74,7 +74,7 @@ class ExitPipViaExpandButtonClickTest(
// This will bring PipApp to fullscreen
pipApp.expandPipWindowToApp(wmHelper)
// Wait until the other app is no longer visible
- wmHelper.waitForSurfaceAppeared(testApp.component.toWindowName())
+ wmHelper.waitForSurfaceAppeared(testApp.component)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt
index e00d7491839f..3fe6f02eccf7 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt
@@ -72,9 +72,9 @@ class ExitPipViaIntentTest(testSpec: FlickerTestParameter) : ExitPipToAppTransit
}
transitions {
// This will bring PipApp to fullscreen
- pipApp.launchViaIntent(wmHelper)
+ pipApp.exitPipToFullScreenViaIntent(wmHelper)
// Wait until the other app is no longer visible
- wmHelper.waitForSurfaceAppeared(testApp.component.toWindowName())
+ wmHelper.waitForWindowSurfaceDisappeared(testApp.component)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithDismissButtonTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithDismissButtonTest.kt
index 5214daa0ec44..9c095a2a039f 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithDismissButtonTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithDismissButtonTest.kt
@@ -16,6 +16,7 @@
package com.android.wm.shell.flicker.pip
+import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
@@ -72,10 +73,17 @@ class ExitPipWithDismissButtonTest(testSpec: FlickerTestParameter) : ExitPipTran
@Test
override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
- /** {@inheritDoc} */
- @FlakyTest(bugId = 215869110)
+ /**
+ * Checks that the focus changes between the pip menu window and the launcher when clicking the
+ * dismiss button on pip menu to close the pip window.
+ */
+ @Presubmit
@Test
- override fun focusDoesNotChange() = super.focusDoesNotChange()
+ fun focusDoesNotChange() {
+ testSpec.assertEventLog {
+ this.focusChanges("PipMenuView", "NexusLauncherActivity")
+ }
+ }
companion object {
/**
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithSwipeDownTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithSwipeDownTest.kt
index 332bba6ad6ef..ab07ede5bb32 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithSwipeDownTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithSwipeDownTest.kt
@@ -16,6 +16,7 @@
package com.android.wm.shell.flicker.pip
+import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
@@ -81,6 +82,17 @@ class ExitPipWithSwipeDownTest(testSpec: FlickerTestParameter) : ExitPipTransiti
@Test
override fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
+ /**
+ * Checks that the focus doesn't change between windows during the transition
+ */
+ @Presubmit
+ @Test
+ fun focusDoesNotChange() {
+ testSpec.assertEventLog {
+ this.focusDoesNotChange()
+ }
+ }
+
companion object {
/**
* Creates the test configurations.
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt
index bb66f7bbc01f..8d542c8ec9e6 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt
@@ -56,13 +56,6 @@ abstract class PipTransition(protected val testSpec: FlickerTestParameter) {
.sendBroadcast(createIntentWithAction(broadcastAction))
}
- fun requestOrientationForPip(orientation: Int) {
- instrumentation.context.sendBroadcast(
- createIntentWithAction(Components.PipActivity.ACTION_SET_REQUESTED_ORIENTATION)
- .putExtra(Components.PipActivity.EXTRA_PIP_ORIENTATION, orientation.toString())
- )
- }
-
companion object {
// Corresponds to ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
@JvmStatic
@@ -122,15 +115,14 @@ abstract class PipTransition(protected val testSpec: FlickerTestParameter) {
setup {
test {
- removeAllTasksButHome()
if (!eachRun) {
- pipApp.launchViaIntent(wmHelper, stringExtras = stringExtras)
+ pipApp.launchViaIntentAndWaitForPip(wmHelper, stringExtras = stringExtras)
wmHelper.waitPipShown()
}
}
eachRun {
if (eachRun) {
- pipApp.launchViaIntent(wmHelper, stringExtras = stringExtras)
+ pipApp.launchViaIntentAndWaitForPip(wmHelper, stringExtras = stringExtras)
wmHelper.waitPipShown()
}
}
@@ -145,7 +137,6 @@ abstract class PipTransition(protected val testSpec: FlickerTestParameter) {
if (!eachRun) {
pipApp.exit(wmHelper)
}
- removeAllTasksButHome()
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt
index d65388bd8cc5..159caee506f0 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt
@@ -28,8 +28,8 @@ import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.WindowUtils
import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
import com.android.wm.shell.flicker.pip.PipTransition.BroadcastActionTrigger.Companion.ORIENTATION_LANDSCAPE
+import com.android.wm.shell.flicker.testapp.Components
import com.android.wm.shell.flicker.testapp.Components.FixedActivity.EXTRA_FIXED_ORIENTATION
-import com.android.wm.shell.flicker.testapp.Components.PipActivity.EXTRA_ENTER_PIP
import org.junit.Assume
import org.junit.Before
import org.junit.FixMethodOrder
@@ -39,7 +39,7 @@ import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
/**
- * Test Pip with orientation changes.
+ * Test exiting Pip with orientation changes.
* To run this test: `atest WMShellFlickerTests:SetRequestedOrientationWhilePinnedTest`
*/
@RequiresDevice
@@ -65,10 +65,16 @@ open class SetRequestedOrientationWhilePinnedTest(
setup {
eachRun {
- // Launch the PiP activity fixed as landscape
+ // Launch the PiP activity fixed as landscape.
pipApp.launchViaIntent(wmHelper, stringExtras = mapOf(
- EXTRA_FIXED_ORIENTATION to ORIENTATION_LANDSCAPE.toString(),
- EXTRA_ENTER_PIP to "true"))
+ EXTRA_FIXED_ORIENTATION to ORIENTATION_LANDSCAPE.toString()))
+ // Enter PiP.
+ broadcastActionTrigger.doAction(Components.PipActivity.ACTION_ENTER_PIP)
+ wmHelper.waitPipShown()
+ wmHelper.waitForRotation(Surface.ROTATION_0)
+ wmHelper.waitForAppTransitionIdle()
+ // System bar may fade out during fixed rotation.
+ wmHelper.waitForNavBarStatusBarVisible()
}
}
teardown {
@@ -77,14 +83,13 @@ open class SetRequestedOrientationWhilePinnedTest(
}
}
transitions {
- // Request that the orientation is set to landscape
- broadcastActionTrigger.requestOrientationForPip(ORIENTATION_LANDSCAPE)
-
- // Launch the activity back into fullscreen and
- // ensure that it is now in landscape
+ // Launch the activity back into fullscreen and ensure that it is now in landscape
pipApp.launchViaIntent(wmHelper)
wmHelper.waitForFullScreenApp(pipApp.component)
wmHelper.waitForRotation(Surface.ROTATION_90)
+ wmHelper.waitForAppTransitionIdle()
+ // System bar may fade out during fixed rotation.
+ wmHelper.waitForNavBarStatusBarVisible()
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTestShellTransit.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTestShellTransit.kt
index 36e28049864f..8d764a8d0e69 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTestShellTransit.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTestShellTransit.kt
@@ -29,6 +29,10 @@ import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
+/**
+ * Test exiting Pip with orientation changes.
+ * To run this test: `atest WMShellFlickerTests:SetRequestedOrientationWhilePinnedTestShellTransit`
+ */
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java
index 90f898aa09da..0059846c6055 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java
@@ -29,6 +29,7 @@ import android.view.Gravity;
import androidx.test.filters.SmallTest;
+import com.android.wm.shell.R;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.common.DisplayLayout;
@@ -72,16 +73,16 @@ public class PipBoundsAlgorithmTest extends ShellTestCase {
private void initializeMockResources() {
final TestableResources res = mContext.getOrCreateTestableResources();
res.addOverride(
- com.android.internal.R.dimen.config_pictureInPictureDefaultAspectRatio,
+ R.dimen.config_pictureInPictureDefaultAspectRatio,
DEFAULT_ASPECT_RATIO);
res.addOverride(
- com.android.internal.R.integer.config_defaultPictureInPictureGravity,
+ R.integer.config_defaultPictureInPictureGravity,
Gravity.END | Gravity.BOTTOM);
res.addOverride(
- com.android.internal.R.dimen.default_minimal_size_pip_resizable_task,
+ R.dimen.default_minimal_size_pip_resizable_task,
DEFAULT_MIN_EDGE_SIZE);
res.addOverride(
- com.android.internal.R.string.config_defaultPictureInPictureScreenEdgeInsets,
+ R.string.config_defaultPictureInPictureScreenEdgeInsets,
"16x16");
res.addOverride(
com.android.internal.R.dimen.config_pictureInPictureMinAspectRatio,
@@ -107,7 +108,7 @@ public class PipBoundsAlgorithmTest extends ShellTestCase {
public void onConfigurationChanged_reloadResources() {
final float newDefaultAspectRatio = (DEFAULT_ASPECT_RATIO + MAX_ASPECT_RATIO) / 2;
final TestableResources res = mContext.getOrCreateTestableResources();
- res.addOverride(com.android.internal.R.dimen.config_pictureInPictureDefaultAspectRatio,
+ res.addOverride(R.dimen.config_pictureInPictureDefaultAspectRatio,
newDefaultAspectRatio);
mPipBoundsAlgorithm.onConfigurationChanged(mContext);
@@ -463,7 +464,7 @@ public class PipBoundsAlgorithmTest extends ShellTestCase {
private void overrideDefaultAspectRatio(float aspectRatio) {
final TestableResources res = mContext.getOrCreateTestableResources();
res.addOverride(
- com.android.internal.R.dimen.config_pictureInPictureDefaultAspectRatio,
+ R.dimen.config_pictureInPictureDefaultAspectRatio,
aspectRatio);
mPipBoundsAlgorithm.onConfigurationChanged(mContext);
}
@@ -471,7 +472,7 @@ public class PipBoundsAlgorithmTest extends ShellTestCase {
private void overrideDefaultStackGravity(int stackGravity) {
final TestableResources res = mContext.getOrCreateTestableResources();
res.addOverride(
- com.android.internal.R.integer.config_defaultPictureInPictureGravity,
+ R.integer.config_defaultPictureInPictureGravity,
stackGravity);
mPipBoundsAlgorithm.onConfigurationChanged(mContext);
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java
index dda1a8295e51..85527c81ad9e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java
@@ -18,6 +18,7 @@ package com.android.wm.shell.splitscreen;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.window.DisplayAreaOrganizer.FEATURE_DEFAULT_TASK_CONTAINER;
+
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
@@ -51,6 +52,7 @@ public class SplitTestUtils {
final SurfaceControl leash = createMockSurface();
SplitLayout out = mock(SplitLayout.class);
doReturn(dividerBounds).when(out).getDividerBounds();
+ doReturn(dividerBounds).when(out).getRefDividerBounds();
doReturn(leash).when(out).getDividerLeash();
return out;
}
diff --git a/media/java/android/media/ImageWriter.java b/media/java/android/media/ImageWriter.java
index e0f04a119234..9f52bf18f4e3 100644
--- a/media/java/android/media/ImageWriter.java
+++ b/media/java/android/media/ImageWriter.java
@@ -108,7 +108,6 @@ public class ImageWriter implements AutoCloseable {
private long mUsage = HardwareBuffer.USAGE_CPU_WRITE_OFTEN;
private @HardwareBuffer.Format int mHardwareBufferFormat;
private @NamedDataSpace int mDataSpace;
- private boolean mUseLegacyImageFormat;
// Field below is used by native code, do not access or modify.
private int mWriterFormat;
@@ -257,7 +256,6 @@ public class ImageWriter implements AutoCloseable {
+ ", maxImages: " + maxImages);
}
- mUseLegacyImageFormat = useLegacyImageFormat;
// Note that the underlying BufferQueue is working in synchronous mode
// to avoid dropping any buffers.
mNativeContext = nativeInit(new WeakReference<>(this), surface, maxImages, width, height,
@@ -334,12 +332,21 @@ public class ImageWriter implements AutoCloseable {
int hardwareBufferFormat, int dataSpace, int width, int height, long usage) {
mMaxImages = maxImages;
mUsage = usage;
- mHardwareBufferFormat = hardwareBufferFormat;
- mDataSpace = dataSpace;
- int publicFormat = PublicFormatUtils.getPublicFormat(hardwareBufferFormat, dataSpace);
+ int imageFormat;
+ // if useSurfaceImageFormatInfo is true, imageFormat will be set to UNKNOWN
+ // and retrieve corresponding hardwareBufferFormat and dataSpace here.
+ if (useSurfaceImageFormatInfo) {
+ imageFormat = ImageFormat.UNKNOWN;
+ mHardwareBufferFormat = PublicFormatUtils.getHalFormat(imageFormat);
+ mDataSpace = PublicFormatUtils.getHalDataspace(imageFormat);
+ } else {
+ imageFormat = PublicFormatUtils.getPublicFormat(hardwareBufferFormat, dataSpace);
+ mHardwareBufferFormat = hardwareBufferFormat;
+ mDataSpace = dataSpace;
+ }
initializeImageWriter(surface, maxImages, useSurfaceImageFormatInfo, false,
- publicFormat, hardwareBufferFormat, dataSpace, width, height, usage);
+ imageFormat, hardwareBufferFormat, dataSpace, width, height, usage);
}
/**
@@ -884,27 +891,17 @@ public class ImageWriter implements AutoCloseable {
private @HardwareBuffer.Format int mHardwareBufferFormat = HardwareBuffer.RGBA_8888;
private @NamedDataSpace int mDataSpace = DataSpace.DATASPACE_UNKNOWN;
private boolean mUseSurfaceImageFormatInfo = true;
- // set this as true temporarily now as a workaround to get correct format
- // when using surface format by default without overriding the image format
- // in the builder pattern
- private boolean mUseLegacyImageFormat = true;
+ private boolean mUseLegacyImageFormat = false;
/**
* Constructs a new builder for {@link ImageWriter}.
*
- * <p>Uses {@code surface} input parameter to retrieve image format, hal format
- * and hal dataspace value for default. </p>
- *
* @param surface The destination Surface this writer produces Image data into.
*
* @throws IllegalArgumentException if the surface is already abandoned.
*/
public Builder(@NonNull Surface surface) {
mSurface = surface;
- // retrieve format from surface
- mImageFormat = SurfaceUtils.getSurfaceFormat(surface);
- mDataSpace = SurfaceUtils.getSurfaceDataspace(surface);
- mHardwareBufferFormat = PublicFormatUtils.getHalFormat(mImageFormat);
}
/**
@@ -1058,11 +1055,6 @@ public class ImageWriter implements AutoCloseable {
mWidth = writer.mWidth;
mHeight = writer.mHeight;
mDataSpace = writer.mDataSpace;
-
- if (!mOwner.mUseLegacyImageFormat) {
- mFormat = PublicFormatUtils.getPublicFormat(
- mOwner.mHardwareBufferFormat, mDataSpace);
- }
}
@Override
@@ -1083,7 +1075,7 @@ public class ImageWriter implements AutoCloseable {
public int getFormat() {
throwISEIfImageIsInvalid();
- if (mOwner.mUseLegacyImageFormat && mFormat == -1) {
+ if (mFormat == -1) {
mFormat = nativeGetFormat(mDataSpace);
}
return mFormat;
diff --git a/packages/CompanionDeviceManager/Android.bp b/packages/CompanionDeviceManager/Android.bp
index 6ded16371e8a..9f5bfd40e7e3 100644
--- a/packages/CompanionDeviceManager/Android.bp
+++ b/packages/CompanionDeviceManager/Android.bp
@@ -34,6 +34,7 @@ license {
android_app {
name: "CompanionDeviceManager",
defaults: ["platform_app_defaults"],
+ certificate: "platform",
srcs: ["src/**/*.java"],
static_libs: [
diff --git a/packages/CompanionDeviceManager/AndroidManifest.xml b/packages/CompanionDeviceManager/AndroidManifest.xml
index 06f2d9d0f0c8..8b5d214f7a10 100644
--- a/packages/CompanionDeviceManager/AndroidManifest.xml
+++ b/packages/CompanionDeviceManager/AndroidManifest.xml
@@ -31,6 +31,7 @@
<uses-permission android:name="android.permission.RADIO_SCAN_WITHOUT_LOCATION"/>
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/>
<uses-permission android:name="android.permission.HIDE_NON_SYSTEM_OVERLAY_WINDOWS"/>
+ <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS"/>
<application
android:allowClearUserData="true"
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java
index b51d3103caec..a6a8fcf9af62 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java
@@ -37,6 +37,7 @@ import android.companion.AssociationRequest;
import android.companion.CompanionDeviceManager;
import android.companion.IAssociationRequestCallback;
import android.content.Intent;
+import android.content.pm.PackageManager;
import android.net.MacAddress;
import android.os.Bundle;
import android.os.Handler;
@@ -71,6 +72,9 @@ public class CompanionDeviceActivity extends AppCompatActivity {
private static final String EXTRA_ASSOCIATION_REQUEST = "association_request";
private static final String EXTRA_RESULT_RECEIVER = "result_receiver";
+ // Activity result: Internal Error.
+ private static final int RESULT_INTERNAL_ERROR = 2;
+
// AssociationRequestsProcessor -> UI
private static final int RESULT_CODE_ASSOCIATION_CREATED = 0;
private static final String EXTRA_ASSOCIATION = "association";
@@ -191,6 +195,20 @@ public class CompanionDeviceActivity extends AppCompatActivity {
private void initUI() {
if (DEBUG) Log.d(TAG, "initUI(), request=" + mRequest);
+ final String packageName = mRequest.getPackageName();
+ final int userId = mRequest.getUserId();
+ final CharSequence appLabel;
+
+ try {
+ appLabel = getApplicationLabel(this, packageName, userId);
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.w(TAG, "Package u" + userId + "/" + packageName + " not found.");
+
+ CompanionDeviceDiscoveryService.stop(this);
+ setResultAndFinish(null, RESULT_INTERNAL_ERROR);
+ return;
+ }
+
setContentView(R.layout.activity_confirmation);
mTitle = findViewById(R.id.title);
@@ -203,8 +221,6 @@ public class CompanionDeviceActivity extends AppCompatActivity {
mButtonAllow.setOnClickListener(this::onPositiveButtonClick);
findViewById(R.id.btn_negative).setOnClickListener(this::onNegativeButtonClick);
- final CharSequence appLabel = getApplicationLabel(this, mRequest.getPackageName());
-
if (mRequest.isSelfManaged()) {
initUiForSelfManagedAssociation(appLabel);
} else if (mRequest.isSingleDevice()) {
@@ -257,7 +273,7 @@ public class CompanionDeviceActivity extends AppCompatActivity {
if (DEBUG) Log.i(TAG, "onAssociationCreated(), association=" + association);
// Don't need to notify the app, CdmService has already done that. Just finish.
- setResultAndFinish(association);
+ setResultAndFinish(association, RESULT_OK);
}
private void cancel(boolean discoveryTimeout) {
@@ -284,10 +300,10 @@ public class CompanionDeviceActivity extends AppCompatActivity {
}
// ... then set result and finish ("sending" onActivityResult()).
- setResultAndFinish(null);
+ setResultAndFinish(null, RESULT_CANCELED);
}
- private void setResultAndFinish(@Nullable AssociationInfo association) {
+ private void setResultAndFinish(@Nullable AssociationInfo association, int resultCode) {
if (DEBUG) Log.i(TAG, "setResultAndFinish(), association=" + association);
final Intent data = new Intent();
@@ -297,7 +313,7 @@ public class CompanionDeviceActivity extends AppCompatActivity {
data.putExtra(CompanionDeviceManager.EXTRA_DEVICE, mSelectedDevice.getDevice());
}
}
- setResult(association != null ? RESULT_OK : RESULT_CANCELED, data);
+ setResult(resultCode, data);
finish();
}
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/Utils.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/Utils.java
index eab421e48446..e3e563d56e8a 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/Utils.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/Utils.java
@@ -50,14 +50,13 @@ class Utils {
}
static @NonNull CharSequence getApplicationLabel(
- @NonNull Context context, @NonNull String packageName) {
+ @NonNull Context context, @NonNull String packageName, int userId)
+ throws PackageManager.NameNotFoundException {
final PackageManager packageManager = context.getPackageManager();
- final ApplicationInfo appInfo;
- try {
- appInfo = packageManager.getApplicationInfo(packageName, 0);
- } catch (PackageManager.NameNotFoundException e) {
- throw new RuntimeException(e);
- }
+
+ final ApplicationInfo appInfo = packageManager.getApplicationInfoAsUser(
+ packageName, PackageManager.ApplicationInfoFlags.of(0), userId);
+
return packageManager.getApplicationLabel(appInfo);
}
diff --git a/packages/ConnectivityT/framework-t/src/android/net/NetworkStats.java b/packages/ConnectivityT/framework-t/src/android/net/NetworkStats.java
index 9175809d9c7c..f681ba1c3853 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/NetworkStats.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/NetworkStats.java
@@ -28,6 +28,7 @@ import android.os.Parcel;
import android.os.Parcelable;
import android.os.Process;
import android.os.SystemClock;
+import android.text.TextUtils;
import android.util.SparseBooleanArray;
import com.android.internal.annotations.VisibleForTesting;
@@ -500,7 +501,7 @@ public final class NetworkStats implements Parcelable, Iterable<NetworkStats.Ent
&& roaming == e.roaming && defaultNetwork == e.defaultNetwork
&& rxBytes == e.rxBytes && rxPackets == e.rxPackets
&& txBytes == e.txBytes && txPackets == e.txPackets
- && operations == e.operations && iface.equals(e.iface);
+ && operations == e.operations && TextUtils.equals(iface, e.iface);
}
return false;
}
diff --git a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java
index ef9ebb50b642..ef6f39a5c040 100644
--- a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java
+++ b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java
@@ -1887,7 +1887,13 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
private void deleteKernelTagData(int uid) {
try {
mCookieTagMap.forEach((key, value) -> {
- if (value.uid == uid) {
+ // If SkDestroyListener deletes the socket tag while this code is running,
+ // forEach will either restart iteration from the beginning or return null,
+ // depending on when the deletion happens.
+ // If it returns null, continue iteration to delete the data and in fact it would
+ // just iterate from first key because BpfMap#getNextKey would return first key
+ // if the current key is not exist.
+ if (value != null && value.uid == uid) {
try {
mCookieTagMap.deleteEntry(key);
} catch (ErrnoException e) {
diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/SparseInputStream.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/SparseInputStream.java
index 72230b4062e1..4117d0f07e0f 100644
--- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/SparseInputStream.java
+++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/SparseInputStream.java
@@ -177,7 +177,7 @@ public class SparseInputStream extends InputStream {
ret = 0;
break;
case SparseChunk.FILL:
- ret = mCur.fill[(4 - ((int) mLeft & 0x3)) & 0x3];
+ ret = Byte.toUnsignedInt(mCur.fill[(4 - ((int) mLeft & 0x3)) & 0x3]);
break;
default:
throw new IOException("Unsupported Chunk:" + mCur.toString());
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 8965144e7351..014a033a4c01 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -1160,6 +1160,8 @@
<string name="battery_info_status_not_charging">Connected, not charging</string>
<!-- Battery Info screen. Value for a status item. Used for diagnostic info screens, precise translation isn't needed -->
<string name="battery_info_status_full">Charged</string>
+ <!-- [CHAR_LIMIT=40] Battery Info screen. Value for a status item. A state which device is fully charged -->
+ <string name="battery_info_status_full_charged">Fully Charged</string>
<!-- Summary for settings preference disabled by administrator [CHAR LIMIT=50] -->
<string name="disabled_by_admin_summary_text">Controlled by admin</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/Utils.java b/packages/SettingsLib/src/com/android/settingslib/Utils.java
index f6e3557b5a49..19114cf147e4 100644
--- a/packages/SettingsLib/src/com/android/settingslib/Utils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/Utils.java
@@ -194,9 +194,11 @@ public class Utils {
* @param context the context
* @param batteryChangedIntent battery broadcast intent received from {@link
* Intent.ACTION_BATTERY_CHANGED}.
+ * @param compactStatus to present compact battery charging string if {@code true}
* @return battery status string
*/
- public static String getBatteryStatus(Context context, Intent batteryChangedIntent) {
+ public static String getBatteryStatus(Context context, Intent batteryChangedIntent,
+ boolean compactStatus) {
final int status = batteryChangedIntent.getIntExtra(BatteryManager.EXTRA_STATUS,
BatteryManager.BATTERY_STATUS_UNKNOWN);
final Resources res = context.getResources();
@@ -205,10 +207,14 @@ public class Utils {
final BatteryStatus batteryStatus = new BatteryStatus(batteryChangedIntent);
if (batteryStatus.isCharged()) {
- statusString = res.getString(R.string.battery_info_status_full);
+ statusString = res.getString(compactStatus
+ ? R.string.battery_info_status_full_charged
+ : R.string.battery_info_status_full);
} else {
if (status == BatteryManager.BATTERY_STATUS_CHARGING) {
- if (batteryStatus.isPluggedInWired()) {
+ if (compactStatus) {
+ statusString = res.getString(R.string.battery_info_status_charging);
+ } else if (batteryStatus.isPluggedInWired()) {
switch (batteryStatus.getChargingSpeed(context)) {
case BatteryStatus.CHARGING_FAST:
statusString = res.getString(
diff --git a/packages/SettingsLib/src/com/android/settingslib/devicestate/AndroidSecureSettings.java b/packages/SettingsLib/src/com/android/settingslib/devicestate/AndroidSecureSettings.java
new file mode 100644
index 000000000000..8aee576c3d04
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/devicestate/AndroidSecureSettings.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.devicestate;
+
+import android.content.ContentResolver;
+import android.database.ContentObserver;
+import android.provider.Settings;
+
+/**
+ * Implementation of {@link SecureSettings} that uses Android's {@link Settings.Secure}
+ * implementation.
+ */
+class AndroidSecureSettings implements SecureSettings {
+
+ private final ContentResolver mContentResolver;
+
+ AndroidSecureSettings(ContentResolver contentResolver) {
+ mContentResolver = contentResolver;
+ }
+
+ @Override
+ public void putStringForUser(String name, String value, int userHandle) {
+ Settings.Secure.putStringForUser(mContentResolver, name, value, userHandle);
+ }
+
+ @Override
+ public String getStringForUser(String name, int userHandle) {
+ return Settings.Secure.getStringForUser(mContentResolver, name, userHandle);
+ }
+
+ @Override
+ public void registerContentObserver(String name, boolean notifyForDescendants,
+ ContentObserver observer, int userHandle) {
+ mContentResolver.registerContentObserver(
+ Settings.Secure.getUriFor(name),
+ notifyForDescendants,
+ observer,
+ userHandle);
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManager.java b/packages/SettingsLib/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManager.java
index afd3626ab889..4ed7e19f341d 100644
--- a/packages/SettingsLib/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManager.java
@@ -22,8 +22,10 @@ import static android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_UNLOCK
import android.content.ContentResolver;
import android.content.Context;
+import android.content.res.Resources;
import android.database.ContentObserver;
import android.os.Handler;
+import android.os.Looper;
import android.os.UserHandle;
import android.provider.Settings;
import android.text.TextUtils;
@@ -33,7 +35,10 @@ import android.util.SparseIntArray;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
+import java.util.ArrayList;
import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
import java.util.Set;
/**
@@ -47,32 +52,44 @@ public final class DeviceStateRotationLockSettingsManager {
private static DeviceStateRotationLockSettingsManager sSingleton;
- private final ContentResolver mContentResolver;
- private final String[] mDeviceStateRotationLockDefaults;
- private final Handler mMainHandler = Handler.getMain();
+ private final Handler mMainHandler = new Handler(Looper.getMainLooper());
private final Set<DeviceStateRotationLockSettingsListener> mListeners = new HashSet<>();
+ private final SecureSettings mSecureSettings;
+ private String[] mDeviceStateRotationLockDefaults;
private SparseIntArray mDeviceStateRotationLockSettings;
private SparseIntArray mDeviceStateRotationLockFallbackSettings;
+ private String mLastSettingValue;
+ private List<SettableDeviceState> mSettableDeviceStates;
- private DeviceStateRotationLockSettingsManager(Context context) {
- mContentResolver = context.getContentResolver();
+ @VisibleForTesting
+ DeviceStateRotationLockSettingsManager(Context context, SecureSettings secureSettings) {
+ this.mSecureSettings = secureSettings;
mDeviceStateRotationLockDefaults =
context.getResources()
.getStringArray(R.array.config_perDeviceStateRotationLockDefaults);
loadDefaults();
initializeInMemoryMap();
- listenForSettingsChange(context);
+ listenForSettingsChange();
}
/** Returns a singleton instance of this class */
public static synchronized DeviceStateRotationLockSettingsManager getInstance(Context context) {
if (sSingleton == null) {
+ Context applicationContext = context.getApplicationContext();
+ ContentResolver contentResolver = applicationContext.getContentResolver();
+ SecureSettings secureSettings = new AndroidSecureSettings(contentResolver);
sSingleton =
- new DeviceStateRotationLockSettingsManager(context.getApplicationContext());
+ new DeviceStateRotationLockSettingsManager(applicationContext, secureSettings);
}
return sSingleton;
}
+ /** Resets the singleton instance of this class. Only used for testing. */
+ @VisibleForTesting
+ public static synchronized void resetInstance() {
+ sSingleton = null;
+ }
+
/** Returns true if device-state based rotation lock settings are enabled. */
public static boolean isDeviceStateRotationLockEnabled(Context context) {
return context.getResources()
@@ -81,11 +98,11 @@ public final class DeviceStateRotationLockSettingsManager {
> 0;
}
- private void listenForSettingsChange(Context context) {
- context.getContentResolver()
+ private void listenForSettingsChange() {
+ mSecureSettings
.registerContentObserver(
- Settings.Secure.getUriFor(Settings.Secure.DEVICE_STATE_ROTATION_LOCK),
- /* notifyForDescendents= */ false, //NOTYPO
+ Settings.Secure.DEVICE_STATE_ROTATION_LOCK,
+ /* notifyForDescendants= */ false,
new ContentObserver(mMainHandler) {
@Override
public void onChange(boolean selfChange) {
@@ -180,10 +197,15 @@ public final class DeviceStateRotationLockSettingsManager {
return true;
}
+ /** Returns a list of device states and their respective auto-rotation setting availability. */
+ public List<SettableDeviceState> getSettableDeviceStates() {
+ // Returning a copy to make sure that nothing outside can mutate our internal list.
+ return new ArrayList<>(mSettableDeviceStates);
+ }
+
private void initializeInMemoryMap() {
String serializedSetting =
- Settings.Secure.getStringForUser(
- mContentResolver,
+ mSecureSettings.getStringForUser(
Settings.Secure.DEVICE_STATE_ROTATION_LOCK,
UserHandle.USER_CURRENT);
if (TextUtils.isEmpty(serializedSetting)) {
@@ -215,6 +237,17 @@ public final class DeviceStateRotationLockSettingsManager {
}
}
+ /**
+ * Resets the state of the class and saved settings back to the default values provided by the
+ * resources config.
+ */
+ @VisibleForTesting
+ public void resetStateForTesting(Resources resources) {
+ mDeviceStateRotationLockDefaults =
+ resources.getStringArray(R.array.config_perDeviceStateRotationLockDefaults);
+ fallbackOnDefaults();
+ }
+
private void fallbackOnDefaults() {
loadDefaults();
persistSettings();
@@ -222,11 +255,7 @@ public final class DeviceStateRotationLockSettingsManager {
private void persistSettings() {
if (mDeviceStateRotationLockSettings.size() == 0) {
- Settings.Secure.putStringForUser(
- mContentResolver,
- Settings.Secure.DEVICE_STATE_ROTATION_LOCK,
- /* value= */ "",
- UserHandle.USER_CURRENT);
+ persistSettingIfChanged(/* newSettingValue= */ "");
return;
}
@@ -243,14 +272,22 @@ public final class DeviceStateRotationLockSettingsManager {
.append(SEPARATOR_REGEX)
.append(mDeviceStateRotationLockSettings.valueAt(i));
}
- Settings.Secure.putStringForUser(
- mContentResolver,
+ persistSettingIfChanged(stringBuilder.toString());
+ }
+
+ private void persistSettingIfChanged(String newSettingValue) {
+ if (TextUtils.equals(mLastSettingValue, newSettingValue)) {
+ return;
+ }
+ mLastSettingValue = newSettingValue;
+ mSecureSettings.putStringForUser(
Settings.Secure.DEVICE_STATE_ROTATION_LOCK,
- stringBuilder.toString(),
+ /* value= */ newSettingValue,
UserHandle.USER_CURRENT);
}
private void loadDefaults() {
+ mSettableDeviceStates = new ArrayList<>(mDeviceStateRotationLockDefaults.length);
mDeviceStateRotationLockSettings = new SparseIntArray(
mDeviceStateRotationLockDefaults.length);
mDeviceStateRotationLockFallbackSettings = new SparseIntArray(1);
@@ -271,6 +308,8 @@ public final class DeviceStateRotationLockSettingsManager {
+ values.length);
}
}
+ boolean isSettable = rotationLockSetting != DEVICE_STATE_ROTATION_LOCK_IGNORED;
+ mSettableDeviceStates.add(new SettableDeviceState(deviceState, isSettable));
mDeviceStateRotationLockSettings.put(deviceState, rotationLockSetting);
} catch (NumberFormatException e) {
Log.wtf(TAG, "Error parsing settings entry. Entry was: " + entry, e);
@@ -300,4 +339,38 @@ public final class DeviceStateRotationLockSettingsManager {
/** Called whenever the settings have changed. */
void onSettingsChanged();
}
+
+ /** Represents a device state and whether it has an auto-rotation setting. */
+ public static class SettableDeviceState {
+ private final int mDeviceState;
+ private final boolean mIsSettable;
+
+ SettableDeviceState(int deviceState, boolean isSettable) {
+ mDeviceState = deviceState;
+ mIsSettable = isSettable;
+ }
+
+ /** Returns the device state associated with this object. */
+ public int getDeviceState() {
+ return mDeviceState;
+ }
+
+ /** Returns whether there is an auto-rotation setting for this device state. */
+ public boolean isSettable() {
+ return mIsSettable;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof SettableDeviceState)) return false;
+ SettableDeviceState that = (SettableDeviceState) o;
+ return mDeviceState == that.mDeviceState && mIsSettable == that.mIsSettable;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mDeviceState, mIsSettable);
+ }
+ }
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/devicestate/SecureSettings.java b/packages/SettingsLib/src/com/android/settingslib/devicestate/SecureSettings.java
new file mode 100644
index 000000000000..10528739b2b0
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/devicestate/SecureSettings.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.devicestate;
+
+import android.database.ContentObserver;
+
+/** Minimal wrapper interface around {@link android.provider.Settings.Secure} for easier testing. */
+interface SecureSettings {
+
+ void putStringForUser(String name, String value, int userHandle);
+
+ String getStringForUser(String name, int userHandle);
+
+ void registerContentObserver(String name, boolean notifyForDescendants,
+ ContentObserver settingsObserver, int userHandle);
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/users/AvatarPickerActivity.java b/packages/SettingsLib/src/com/android/settingslib/users/AvatarPickerActivity.java
index 93be66ad4882..1e1dfae9f7ac 100644
--- a/packages/SettingsLib/src/com/android/settingslib/users/AvatarPickerActivity.java
+++ b/packages/SettingsLib/src/com/android/settingslib/users/AvatarPickerActivity.java
@@ -81,6 +81,7 @@ public class AvatarPickerActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ setTheme(R.style.SudThemeGlifV3_DayNight);
ThemeHelper.trySetDynamicColor(this);
setContentView(R.layout.avatar_picker);
setUpButtons();
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManagerTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManagerTest.java
new file mode 100644
index 000000000000..81006dd6b011
--- /dev/null
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManagerTest.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.devicestate;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.database.ContentObserver;
+import android.os.UserHandle;
+import android.provider.Settings;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.R;
+import com.android.settingslib.devicestate.DeviceStateRotationLockSettingsManager.SettableDeviceState;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.List;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class DeviceStateRotationLockSettingsManagerTest {
+
+ @Mock private Context mMockContext;
+ @Mock private Resources mMockResources;
+
+ private DeviceStateRotationLockSettingsManager mManager;
+ private int mNumSettingsChanges = 0;
+ private final ContentObserver mContentObserver = new ContentObserver(null) {
+ @Override
+ public void onChange(boolean selfChange) {
+ mNumSettingsChanges++;
+ }
+ };
+ private final FakeSecureSettings mFakeSecureSettings = new FakeSecureSettings();
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ Context context = InstrumentationRegistry.getTargetContext();
+ when(mMockContext.getApplicationContext()).thenReturn(mMockContext);
+ when(mMockContext.getResources()).thenReturn(mMockResources);
+ when(mMockContext.getContentResolver()).thenReturn(context.getContentResolver());
+ mFakeSecureSettings.registerContentObserver(
+ Settings.Secure.DEVICE_STATE_ROTATION_LOCK,
+ /* notifyForDescendents= */ false, //NOTYPO
+ mContentObserver,
+ UserHandle.USER_CURRENT);
+ mManager = new DeviceStateRotationLockSettingsManager(context, mFakeSecureSettings);
+ }
+
+ @Test
+ public void initialization_settingsAreChangedOnce() {
+ assertThat(mNumSettingsChanges).isEqualTo(1);
+ }
+
+ @Test
+ public void updateSetting_multipleTimes_sameValue_settingsAreChangedOnlyOnce() {
+ mNumSettingsChanges = 0;
+
+ mManager.updateSetting(/* deviceState= */ 1, /* rotationLocked= */ true);
+ mManager.updateSetting(/* deviceState= */ 1, /* rotationLocked= */ true);
+ mManager.updateSetting(/* deviceState= */ 1, /* rotationLocked= */ true);
+
+ assertThat(mNumSettingsChanges).isEqualTo(1);
+ }
+
+ @Test
+ public void updateSetting_multipleTimes_differentValues_settingsAreChangedMultipleTimes() {
+ mNumSettingsChanges = 0;
+
+ mManager.updateSetting(/* deviceState= */ 1, /* rotationLocked= */ true);
+ mManager.updateSetting(/* deviceState= */ 1, /* rotationLocked= */ false);
+ mManager.updateSetting(/* deviceState= */ 1, /* rotationLocked= */ true);
+
+ assertThat(mNumSettingsChanges).isEqualTo(3);
+ }
+
+ @Test
+ public void getSettableDeviceStates_returnsExpectedValuesInOriginalOrder() {
+ when(mMockResources.getStringArray(
+ R.array.config_perDeviceStateRotationLockDefaults)).thenReturn(
+ new String[]{"2:2", "4:0", "1:1", "0:0"});
+
+ List<SettableDeviceState> settableDeviceStates =
+ DeviceStateRotationLockSettingsManager.getInstance(
+ mMockContext).getSettableDeviceStates();
+
+ assertThat(settableDeviceStates).containsExactly(
+ new SettableDeviceState(/* deviceState= */ 2, /* isSettable= */ true),
+ new SettableDeviceState(/* deviceState= */ 4, /* isSettable= */ false),
+ new SettableDeviceState(/* deviceState= */ 1, /* isSettable= */ true),
+ new SettableDeviceState(/* deviceState= */ 0, /* isSettable= */ false)
+ ).inOrder();
+ }
+}
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/FakeSecureSettings.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/FakeSecureSettings.java
new file mode 100644
index 000000000000..91baa68a1c49
--- /dev/null
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/FakeSecureSettings.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.devicestate;
+
+import android.database.ContentObserver;
+import android.util.Pair;
+
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.Multimap;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/** Fake implementation of {@link SecureSettings} that stores everything in memory. */
+class FakeSecureSettings implements SecureSettings {
+
+ private final Map<SettingsKey, String> mValues = new HashMap<>();
+ private final Multimap<SettingsKey, ContentObserver> mContentObservers = HashMultimap.create();
+
+ @Override
+ public void putStringForUser(String name, String value, int userHandle) {
+ SettingsKey settingsKey = new SettingsKey(userHandle, name);
+ mValues.put(settingsKey, value);
+ for (ContentObserver observer : mContentObservers.get(settingsKey)) {
+ observer.onChange(/* selfChange= */ false);
+ }
+ }
+
+ @Override
+ public String getStringForUser(String name, int userHandle) {
+ return mValues.getOrDefault(new SettingsKey(userHandle, name), "");
+ }
+
+ @Override
+ public void registerContentObserver(String name, boolean notifyForDescendants,
+ ContentObserver settingsObserver, int userHandle) {
+ mContentObservers.put(new SettingsKey(userHandle, name), settingsObserver);
+ }
+
+ private static class SettingsKey extends Pair<Integer, String> {
+
+ SettingsKey(Integer userHandle, String settingName) {
+ super(userHandle, settingName);
+ }
+ }
+}
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/OWNERS b/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/OWNERS
new file mode 100644
index 000000000000..98f41234feb1
--- /dev/null
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/OWNERS
@@ -0,0 +1,3 @@
+# Default reviewers for this and subdirectories.
+alexflo@google.com
+chrisgollner@google.com
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java
index 62de66ea56fb..09b2a2e73c5b 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java
@@ -320,28 +320,47 @@ public class UtilsTest {
final Intent intent = new Intent().putExtra(BatteryManager.EXTRA_LEVEL, 100);
final Resources resources = mContext.getResources();
- assertThat(Utils.getBatteryStatus(mContext, intent)).isEqualTo(
+ assertThat(Utils.getBatteryStatus(mContext, intent, /* compactStatus= */ false)).isEqualTo(
resources.getString(R.string.battery_info_status_full));
}
@Test
+ public void getBatteryStatus_statusIsFullAndUseCompactStatus_returnFullyChargedString() {
+ final Intent intent = new Intent().putExtra(BatteryManager.EXTRA_LEVEL, 100);
+ final Resources resources = mContext.getResources();
+
+ assertThat(Utils.getBatteryStatus(mContext, intent, /* compactStatus= */ true)).isEqualTo(
+ resources.getString(R.string.battery_info_status_full_charged));
+ }
+
+ @Test
public void getBatteryStatus_batteryLevelIs100_returnFullString() {
final Intent intent = new Intent().putExtra(BatteryManager.EXTRA_STATUS,
BatteryManager.BATTERY_STATUS_FULL);
final Resources resources = mContext.getResources();
- assertThat(Utils.getBatteryStatus(mContext, intent)).isEqualTo(
+ assertThat(Utils.getBatteryStatus(mContext, intent, /* compactStatus= */ false)).isEqualTo(
resources.getString(R.string.battery_info_status_full));
}
@Test
+ public void getBatteryStatus_batteryLevelIs100AndUseCompactStatus_returnFullyString() {
+ final Intent intent = new Intent().putExtra(BatteryManager.EXTRA_STATUS,
+ BatteryManager.BATTERY_STATUS_FULL);
+ final Resources resources = mContext.getResources();
+
+ assertThat(Utils.getBatteryStatus(mContext, intent, /* compactStatus= */ true)).isEqualTo(
+ resources.getString(R.string.battery_info_status_full_charged));
+ }
+
+ @Test
public void getBatteryStatus_batteryLevel99_returnChargingString() {
final Intent intent = new Intent();
intent.putExtra(BatteryManager.EXTRA_STATUS, BatteryManager.BATTERY_STATUS_CHARGING);
intent.putExtra(BatteryManager.EXTRA_PLUGGED, BatteryManager.BATTERY_PLUGGED_USB);
final Resources resources = mContext.getResources();
- assertThat(Utils.getBatteryStatus(mContext, intent)).isEqualTo(
+ assertThat(Utils.getBatteryStatus(mContext, intent, /* compactStatus= */ false)).isEqualTo(
resources.getString(R.string.battery_info_status_charging));
}
@@ -352,7 +371,29 @@ public class UtilsTest {
intent.putExtra(BatteryManager.EXTRA_PLUGGED, BatteryManager.BATTERY_PLUGGED_WIRELESS);
final Resources resources = mContext.getResources();
- assertThat(Utils.getBatteryStatus(mContext, intent)).isEqualTo(
+ assertThat(Utils.getBatteryStatus(mContext, intent, /* compactStatus= */ false)).isEqualTo(
resources.getString(R.string.battery_info_status_charging_wireless));
}
+
+ @Test
+ public void getBatteryStatus_chargingAndUseCompactStatus_returnCompactString() {
+ final Intent intent = new Intent();
+ intent.putExtra(BatteryManager.EXTRA_STATUS, BatteryManager.BATTERY_STATUS_CHARGING);
+ intent.putExtra(BatteryManager.EXTRA_PLUGGED, BatteryManager.BATTERY_PLUGGED_USB);
+ final Resources resources = mContext.getResources();
+
+ assertThat(Utils.getBatteryStatus(mContext, intent, /* compactStatus= */ true)).isEqualTo(
+ resources.getString(R.string.battery_info_status_charging));
+ }
+
+ @Test
+ public void getBatteryStatus_chargingWirelessAndUseCompactStatus_returnCompactString() {
+ final Intent intent = new Intent();
+ intent.putExtra(BatteryManager.EXTRA_STATUS, BatteryManager.BATTERY_STATUS_CHARGING);
+ intent.putExtra(BatteryManager.EXTRA_PLUGGED, BatteryManager.BATTERY_PLUGGED_WIRELESS);
+ final Resources resources = mContext.getResources();
+
+ assertThat(Utils.getBatteryStatus(mContext, intent, /* compactStatus= */ true)).isEqualTo(
+ resources.getString(R.string.battery_info_status_charging));
+ }
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/enterprise/BiometricActionDisabledByAdminControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/enterprise/BiometricActionDisabledByAdminControllerTest.java
index 06b6fc8ef73d..b2258e1d4208 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/enterprise/BiometricActionDisabledByAdminControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/enterprise/BiometricActionDisabledByAdminControllerTest.java
@@ -80,8 +80,8 @@ public class BiometricActionDisabledByAdminControllerTest {
assertEquals(Settings.ACTION_MANAGE_SUPERVISOR_RESTRICTED_SETTING,
intentCaptor.getValue().getAction());
assertEquals(Settings.SUPERVISOR_VERIFICATION_SETTING_BIOMETRICS,
- intentCaptor.getValue().getStringExtra(
- Settings.EXTRA_SUPERVISOR_RESTRICTED_SETTING_KEY));
+ intentCaptor.getValue().getIntExtra(
+ Settings.EXTRA_SUPERVISOR_RESTRICTED_SETTING_KEY, -1));
assertEquals(componentName.getPackageName(), intentCaptor.getValue().getPackage());
}
}
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index f0b180e5cab6..c0e2b2ef361f 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -588,8 +588,14 @@
<uses-permission android:name="android.permission.MANAGE_HOTWORD_DETECTION" />
<uses-permission android:name="android.permission.BIND_HOTWORD_DETECTION_SERVICE" />
+ <!-- Permission required for CTS test - KeyguardLockedStateApiTest -->
+ <uses-permission android:name="android.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE" />
+
<uses-permission android:name="android.permission.MANAGE_APP_HIBERNATION"/>
+ <!-- Permission required for CTS test - MediaCodecResourceTest -->
+ <uses-permission android:name="android.permission.MEDIA_RESOURCE_OVERRIDE_PID" />
+
<!-- Permission required for CTS test - ResourceObserverNativeTest -->
<uses-permission android:name="android.permission.REGISTER_MEDIA_RESOURCE_OBSERVER" />
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index ad6074a93c96..4b1d00bb18e3 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -311,6 +311,9 @@
<uses-permission android:name="android.permission.CHANGE_CONFIGURATION" />
<uses-permission android:name="android.permission.SUPPRESS_CLIPBOARD_ACCESS_NOTIFICATION" />
+ <!-- To change system captions state -->
+ <uses-permission android:name="android.permission.SET_SYSTEM_AUDIO_CAPTION" />
+
<protected-broadcast android:name="com.android.settingslib.action.REGISTER_SLICE_RECEIVER" />
<protected-broadcast android:name="com.android.settingslib.action.UNREGISTER_SLICE_RECEIVER" />
<protected-broadcast android:name="com.android.settings.flashlight.action.FLASHLIGHT_CHANGED" />
@@ -767,22 +770,6 @@
</intent-filter>
</activity>
- <activity android:name=".chooser.ChooserActivity"
- android:theme="@*android:style/Theme.NoDisplay"
- android:finishOnCloseSystemDialogs="true"
- android:excludeFromRecents="true"
- android:documentLaunchMode="never"
- android:relinquishTaskIdentity="true"
- android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation|keyboard|keyboardHidden"
- android:process=":ui"
- android:visibleToInstantApps="true"
- android:exported="true">
- <intent-filter>
- <action android:name="android.intent.action.CHOOSER" />
- <category android:name="android.intent.category.VOICE" />
- </intent-filter>
- </activity>
-
<activity android:name=".clipboardoverlay.EditTextActivity"
android:theme="@style/EditTextActivity"
android:exported="false"
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
index c3f6a5d18b74..0da60f0b3d66 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
@@ -298,6 +298,9 @@ class ActivityLaunchAnimator(
*
* Important: The view must be attached to a [ViewGroup] when calling this function and
* during the animation. For safety, this method will return null when it is not.
+ *
+ * Note: The background of [view] should be a (rounded) rectangle so that it can be
+ * properly animated.
*/
@JvmStatic
fun fromView(view: View, cujType: Int? = null): Controller? {
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
index a3c5649e3fec..50178f470fd8 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
@@ -79,6 +79,9 @@ class DialogLaunchAnimator @JvmOverloads constructor(
* If [animateBackgroundBoundsChange] is true, then the background of the dialog will be
* animated when the dialog bounds change.
*
+ * Note: The background of [view] should be a (rounded) rectangle so that it can be properly
+ * animated.
+ *
* Caveats: When calling this function and [dialog] is not a fullscreen dialog, then it will be
* made fullscreen and 2 views will be inserted between the dialog DecorView and its children.
*/
@@ -153,6 +156,9 @@ class DialogLaunchAnimator @JvmOverloads constructor(
* activity started, when the dialog to app animation is done (or when it is cancelled). If this
* method returns null, then the dialog won't be dismissed.
*
+ * Note: The background of [view] should be a (rounded) rectangle so that it can be properly
+ * animated.
+ *
* @param view any view inside the dialog to animate.
*/
@JvmOverloads
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/VolumeDialogController.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/VolumeDialogController.java
index 0a0530c056af..3d2f570bde87 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/VolumeDialogController.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/VolumeDialogController.java
@@ -60,7 +60,6 @@ public interface VolumeDialogController {
boolean areCaptionsEnabled();
void setCaptionsEnabled(boolean isEnabled);
- boolean isCaptionStreamOptedOut();
void getCaptionsComponentState(boolean fromTooltip);
diff --git a/packages/SystemUI/res-keyguard/layout/fgs_footer.xml b/packages/SystemUI/res-keyguard/layout/fgs_footer.xml
index 59f87da79a11..9d801d2a0ecf 100644
--- a/packages/SystemUI/res-keyguard/layout/fgs_footer.xml
+++ b/packages/SystemUI/res-keyguard/layout/fgs_footer.xml
@@ -28,7 +28,7 @@
android:layout_height="match_parent"
android:layout_marginEnd="@dimen/new_qs_footer_action_inset"
android:background="@drawable/qs_security_footer_background"
- android:layout_gravity="center"
+ android:layout_gravity="end"
android:gravity="center"
android:paddingHorizontal="@dimen/qs_footer_padding"
>
diff --git a/packages/SystemUI/res-keyguard/values/dimens.xml b/packages/SystemUI/res-keyguard/values/dimens.xml
index dad4c19799af..b98f41329c8f 100644
--- a/packages/SystemUI/res-keyguard/values/dimens.xml
+++ b/packages/SystemUI/res-keyguard/values/dimens.xml
@@ -123,6 +123,7 @@
<dimen name="bouncer_user_switcher_icon_size">190dp</dimen>
<dimen name="bouncer_user_switcher_icon_size_plus_margin">222dp</dimen>
+ <dimen name="user_switcher_fullscreen_horizontal_gap">64dp</dimen>
<dimen name="user_switcher_icon_selected_width">8dp</dimen>
<dimen name="user_switcher_fullscreen_button_text_size">14sp</dimen>
<dimen name="user_switcher_fullscreen_button_padding">12dp</dimen>
diff --git a/packages/SystemUI/res/color/caption_tint_color_selector.xml b/packages/SystemUI/res/color/caption_tint_color_selector.xml
index 30843ec70f91..5239d269eb4a 100644
--- a/packages/SystemUI/res/color/caption_tint_color_selector.xml
+++ b/packages/SystemUI/res/color/caption_tint_color_selector.xml
@@ -16,8 +16,5 @@
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:sysui="http://schemas.android.com/apk/res-auto">
- <item sysui:optedOut="true"
- android:color="?android:attr/colorButtonNormal"/>
-
<item android:color="?android:attr/colorAccent"/>
</selector> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/qs_dialog_btn_filled_large.xml b/packages/SystemUI/res/drawable/qs_dialog_btn_filled_large.xml
new file mode 100644
index 000000000000..0544b871fa06
--- /dev/null
+++ b/packages/SystemUI/res/drawable/qs_dialog_btn_filled_large.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ android:color="?android:attr/colorControlHighlight">
+ <item android:id="@android:id/mask">
+ <shape android:shape="rectangle">
+ <solid android:color="@android:color/white"/>
+ <corners android:radius="18dp"/>
+ </shape>
+ </item>
+ <item>
+ <shape android:shape="rectangle">
+ <corners android:radius="18dp"/>
+ <solid android:color="?androidprv:attr/colorAccentPrimary"/>
+ </shape>
+ </item>
+</ripple>
diff --git a/packages/SystemUI/res/drawable/qs_footer_action_circle.xml b/packages/SystemUI/res/drawable/qs_footer_action_circle.xml
index d057f5f32dc4..31a8c3bc2397 100644
--- a/packages/SystemUI/res/drawable/qs_footer_action_circle.xml
+++ b/packages/SystemUI/res/drawable/qs_footer_action_circle.xml
@@ -19,13 +19,17 @@
<ripple
android:color="?android:attr/colorControlHighlight">
<item android:id="@android:id/mask">
- <shape android:shape="oval">
+ <!-- We make this shape a rounded rectangle instead of a oval so that it can animate -->
+ <!-- properly into an app/dialog. -->
+ <shape android:shape="rectangle">
<solid android:color="@android:color/white"/>
+ <corners android:radius="@dimen/qs_footer_action_corner_radius"/>
</shape>
</item>
<item>
- <shape android:shape="oval">
+ <shape android:shape="rectangle">
<solid android:color="?attr/offStateColor"/>
+ <corners android:radius="@dimen/qs_footer_action_corner_radius"/>
</shape>
</item>
diff --git a/packages/SystemUI/res/drawable/qs_footer_action_circle_color.xml b/packages/SystemUI/res/drawable/qs_footer_action_circle_color.xml
index 944061cc8e70..021a85f6a244 100644
--- a/packages/SystemUI/res/drawable/qs_footer_action_circle_color.xml
+++ b/packages/SystemUI/res/drawable/qs_footer_action_circle_color.xml
@@ -19,13 +19,17 @@
<ripple
android:color="?android:attr/colorControlHighlight">
<item android:id="@android:id/mask">
- <shape android:shape="oval">
+ <!-- We make this shape a rounded rectangle instead of a oval so that it can animate -->
+ <!-- properly into an app/dialog. -->
+ <shape android:shape="rectangle">
<solid android:color="@android:color/white"/>
+ <corners android:radius="@dimen/qs_footer_action_corner_radius"/>
</shape>
</item>
<item>
- <shape android:shape="oval">
+ <shape android:shape="rectangle">
<solid android:color="?android:attr/colorAccent"/>
+ <corners android:radius="@dimen/qs_footer_action_corner_radius"/>
</shape>
</item>
diff --git a/packages/SystemUI/res/layout-land-television/volume_dialog.xml b/packages/SystemUI/res/layout-land-television/volume_dialog.xml
index 6b5629facd41..0fbc519ca8dd 100644
--- a/packages/SystemUI/res/layout-land-television/volume_dialog.xml
+++ b/packages/SystemUI/res/layout-land-television/volume_dialog.xml
@@ -73,8 +73,7 @@
android:layout_height="match_parent"
android:tint="@color/caption_tint_color_selector"
android:layout_gravity="center"
- android:soundEffectsEnabled="false"
- sysui:optedOut="false"/>
+ android:soundEffectsEnabled="false"/>
</FrameLayout>
diff --git a/packages/SystemUI/res/layout-land/volume_dialog.xml b/packages/SystemUI/res/layout-land/volume_dialog.xml
index f1cda277f045..3b70dc060e84 100644
--- a/packages/SystemUI/res/layout-land/volume_dialog.xml
+++ b/packages/SystemUI/res/layout-land/volume_dialog.xml
@@ -136,8 +136,7 @@
android:layout_height="match_parent"
android:tint="?android:attr/colorAccent"
android:layout_gravity="center"
- android:soundEffectsEnabled="false"
- sysui:optedOut="false"/>
+ android:soundEffectsEnabled="false" />
</FrameLayout>
</LinearLayout>
diff --git a/packages/SystemUI/res/layout/alert_dialog_button_bar_systemui.xml b/packages/SystemUI/res/layout/alert_dialog_button_bar_systemui.xml
index a3e289a42d05..e06bfdc500da 100644
--- a/packages/SystemUI/res/layout/alert_dialog_button_bar_systemui.xml
+++ b/packages/SystemUI/res/layout/alert_dialog_button_bar_systemui.xml
@@ -22,10 +22,6 @@
android:scrollbarAlwaysDrawVerticalTrack="true"
android:scrollIndicators="top|bottom"
android:fillViewport="true"
- android:paddingTop="@dimen/dialog_button_bar_top_padding"
- android:paddingStart="@dimen/dialog_side_padding"
- android:paddingEnd="@dimen/dialog_side_padding"
- android:paddingBottom="@dimen/dialog_bottom_padding"
style="?android:attr/buttonBarStyle">
<com.android.internal.widget.ButtonBarLayout
android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/layout/alert_dialog_systemui.xml b/packages/SystemUI/res/layout/alert_dialog_systemui.xml
index f280cbd16a0f..ca8fadd9c7da 100644
--- a/packages/SystemUI/res/layout/alert_dialog_systemui.xml
+++ b/packages/SystemUI/res/layout/alert_dialog_systemui.xml
@@ -83,9 +83,15 @@
android:layout_height="wrap_content" />
</FrameLayout>
- <include
+ <FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
- layout="@layout/alert_dialog_button_bar_systemui" />
+ android:paddingStart="@dimen/dialog_side_padding"
+ android:paddingEnd="@dimen/dialog_side_padding">
+ <include
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ layout="@layout/alert_dialog_button_bar_systemui" />
+ </FrameLayout>
</com.android.internal.widget.AlertDialogLayout> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/dream_overlay_complication_preview.xml b/packages/SystemUI/res/layout/dream_overlay_complication_preview.xml
new file mode 100644
index 000000000000..37b8365996fd
--- /dev/null
+++ b/packages/SystemUI/res/layout/dream_overlay_complication_preview.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<TextView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/dream_preview_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textSize="@dimen/dream_overlay_complication_preview_text_size"
+ android:textColor="@android:color/white"
+ android:shadowColor="@color/keyguard_shadow_color"
+ android:shadowRadius="?attr/shadowRadius"
+ android:gravity="center_vertical"
+ android:drawableStart="@drawable/ic_arrow_back"
+ android:drawablePadding="@dimen/dream_overlay_complication_preview_icon_padding"
+ android:drawableTint="@android:color/white"/>
diff --git a/packages/SystemUI/res/layout/dream_overlay_container.xml b/packages/SystemUI/res/layout/dream_overlay_container.xml
index 330f515a29fc..8e83b4abe0dc 100644
--- a/packages/SystemUI/res/layout/dream_overlay_container.xml
+++ b/packages/SystemUI/res/layout/dream_overlay_container.xml
@@ -34,34 +34,5 @@
app:layout_constraintBottom_toBottomOf="parent"
/>
- <com.android.systemui.dreams.DreamOverlayStatusBarView
- android:id="@+id/dream_overlay_status_bar"
- android:layout_width="match_parent"
- android:layout_height="@dimen/dream_overlay_status_bar_height"
- android:paddingEnd="@dimen/dream_overlay_status_bar_margin"
- android:paddingStart="@dimen/dream_overlay_status_bar_margin"
- app:layout_constraintTop_toTopOf="parent">
-
- <androidx.constraintlayout.widget.ConstraintLayout
- android:id="@+id/dream_overlay_system_status"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- app:layout_constraintEnd_toEndOf="parent">
-
- <com.android.systemui.statusbar.AlphaOptimizedImageView
- android:id="@+id/dream_overlay_wifi_status"
- android:layout_width="@dimen/status_bar_wifi_signal_size"
- android:layout_height="match_parent"
- android:layout_marginEnd="@dimen/dream_overlay_status_icon_margin"
- android:visibility="gone"
- app:layout_constraintEnd_toStartOf="@id/dream_overlay_battery" />
-
- <com.android.systemui.battery.BatteryMeterView
- android:id="@+id/dream_overlay_battery"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- app:layout_constraintEnd_toEndOf="parent" />
-
- </androidx.constraintlayout.widget.ConstraintLayout>
- </com.android.systemui.dreams.DreamOverlayStatusBarView>
+ <include layout="@layout/dream_overlay_status_bar_view" />
</com.android.systemui.dreams.DreamOverlayContainerView> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/dream_overlay_status_bar_view.xml b/packages/SystemUI/res/layout/dream_overlay_status_bar_view.xml
new file mode 100644
index 000000000000..813787e8f9d0
--- /dev/null
+++ b/packages/SystemUI/res/layout/dream_overlay_status_bar_view.xml
@@ -0,0 +1,96 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<com.android.systemui.dreams.DreamOverlayStatusBarView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/dream_overlay_status_bar"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/dream_overlay_status_bar_height"
+ android:paddingEnd="@dimen/dream_overlay_status_bar_margin"
+ android:paddingStart="@dimen/dream_overlay_status_bar_margin"
+ app:layout_constraintTop_toTopOf="parent">
+
+ <com.android.systemui.dreams.DreamOverlayDotImageView
+ android:id="@+id/dream_overlay_notification_indicator"
+ android:layout_width="@dimen/dream_overlay_notification_indicator_size"
+ android:layout_height="@dimen/dream_overlay_notification_indicator_size"
+ android:visibility="gone"
+ android:contentDescription="@string/dream_overlay_status_bar_notification_indicator"
+ app:dotColor="@android:color/white"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent" />
+
+ <LinearLayout
+ android:id="@+id/dream_overlay_system_status"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:orientation="horizontal"
+ app:layout_constraintEnd_toEndOf="parent">
+
+ <com.android.systemui.statusbar.AlphaOptimizedImageView
+ android:id="@+id/dream_overlay_assistant_guest_mode_enabled"
+ android:layout_width="@dimen/dream_overlay_status_bar_icon_size"
+ android:layout_height="match_parent"
+ android:layout_marginEnd="@dimen/dream_overlay_status_icon_margin"
+ android:src="@drawable/ic_account_circle"
+ android:tint="@android:color/white"
+ android:visibility="gone"
+ android:contentDescription=
+ "@string/dream_overlay_status_bar_assistant_guest_mode_enabled" />
+
+ <com.android.systemui.statusbar.AlphaOptimizedImageView
+ android:id="@+id/dream_overlay_alarm_set"
+ android:layout_width="@dimen/dream_overlay_status_bar_icon_size"
+ android:layout_height="match_parent"
+ android:layout_marginEnd="@dimen/dream_overlay_status_icon_margin"
+ android:src="@drawable/ic_alarm"
+ android:tint="@android:color/white"
+ android:visibility="gone"
+ android:contentDescription="@string/dream_overlay_status_bar_alarm_set" />
+
+ <com.android.systemui.statusbar.AlphaOptimizedImageView
+ android:id="@+id/dream_overlay_priority_mode"
+ android:layout_width="@dimen/dream_overlay_status_bar_icon_size"
+ android:layout_height="match_parent"
+ android:layout_marginEnd="@dimen/dream_overlay_status_icon_margin"
+ android:src="@drawable/ic_remove_circle"
+ android:tint="@android:color/white"
+ android:visibility="gone"
+ android:contentDescription="@string/dream_overlay_status_bar_priority_mode" />
+
+ <com.android.systemui.statusbar.AlphaOptimizedImageView
+ android:id="@+id/dream_overlay_wifi_status"
+ android:layout_width="@dimen/dream_overlay_status_bar_icon_size"
+ android:layout_height="match_parent"
+ android:layout_marginEnd="@dimen/dream_overlay_status_icon_margin"
+ android:src="@drawable/ic_signal_wifi_off"
+ android:visibility="gone"
+ android:contentDescription="@string/dream_overlay_status_bar_wifi_off" />
+
+ <com.android.systemui.dreams.DreamOverlayDotImageView
+ android:id="@+id/dream_overlay_camera_mic_off"
+ android:layout_width="@dimen/dream_overlay_camera_mic_off_indicator_size"
+ android:layout_height="@dimen/dream_overlay_camera_mic_off_indicator_size"
+ android:layout_gravity="center_vertical"
+ android:layout_marginEnd="@dimen/dream_overlay_status_icon_margin"
+ android:visibility="gone"
+ android:contentDescription="@string/dream_overlay_status_bar_camera_mic_off"
+ app:dotColor="@color/dream_overlay_camera_mic_off_dot_color" />
+
+ </LinearLayout>
+</com.android.systemui.dreams.DreamOverlayStatusBarView>
diff --git a/packages/SystemUI/res/layout/user_switcher_fullscreen.xml b/packages/SystemUI/res/layout/user_switcher_fullscreen.xml
index 2d883bc1477f..6bb6c2d9877c 100644
--- a/packages/SystemUI/res/layout/user_switcher_fullscreen.xml
+++ b/packages/SystemUI/res/layout/user_switcher_fullscreen.xml
@@ -21,9 +21,8 @@
android:id="@+id/user_switcher_root"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:layout_marginBottom="40dp"
- android:layout_marginEnd="60dp"
- android:layout_marginStart="60dp">
+ android:layout_marginVertical="40dp"
+ android:layout_marginHorizontal="60dp">
<androidx.constraintlayout.helper.widget.Flow
android:id="@+id/flow"
@@ -36,7 +35,7 @@
app:flow_horizontalBias="0.5"
app:flow_verticalAlign="center"
app:flow_wrapMode="chain"
- app:flow_horizontalGap="64dp"
+ app:flow_horizontalGap="@dimen/user_switcher_fullscreen_horizontal_gap"
app:flow_verticalGap="44dp"
app:flow_horizontalStyle="packed"/>
diff --git a/packages/SystemUI/res/layout/user_switcher_fullscreen_item.xml b/packages/SystemUI/res/layout/user_switcher_fullscreen_item.xml
index 3319442a1a68..a3d9a69e73c5 100644
--- a/packages/SystemUI/res/layout/user_switcher_fullscreen_item.xml
+++ b/packages/SystemUI/res/layout/user_switcher_fullscreen_item.xml
@@ -21,8 +21,8 @@
<ImageView
android:id="@+id/user_switcher_icon"
android:layout_gravity="center"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content" />
+ android:layout_width="@dimen/bouncer_user_switcher_icon_size_plus_margin"
+ android:layout_height="@dimen/bouncer_user_switcher_icon_size_plus_margin" />
<TextView
style="@style/Bouncer.UserSwitcher.Spinner.Item"
android:id="@+id/user_switcher_text"
diff --git a/packages/SystemUI/res/layout/volume_dialog.xml b/packages/SystemUI/res/layout/volume_dialog.xml
index 51718d9af054..6a192d4b7e05 100644
--- a/packages/SystemUI/res/layout/volume_dialog.xml
+++ b/packages/SystemUI/res/layout/volume_dialog.xml
@@ -135,8 +135,7 @@
android:layout_height="match_parent"
android:tint="?android:attr/colorAccent"
android:layout_gravity="center"
- android:soundEffectsEnabled="false"
- sysui:optedOut="false"/>
+ android:soundEffectsEnabled="false"/>
</FrameLayout>
</LinearLayout>
diff --git a/packages/SystemUI/res/values-sw600dp-land/dimens.xml b/packages/SystemUI/res/values-sw600dp-land/dimens.xml
index c37c804caaa8..8a4516aaa49f 100644
--- a/packages/SystemUI/res/values-sw600dp-land/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp-land/dimens.xml
@@ -42,4 +42,8 @@
the shade (in alpha) -->
<dimen name="lockscreen_shade_scrim_transition_distance">200dp</dimen>
+ <!-- Distance that the full shade transition takes in order for media to fully transition to
+ the shade -->
+ <dimen name="lockscreen_shade_media_transition_distance">200dp</dimen>
+
</resources>
diff --git a/packages/SystemUI/res/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml
index 2992859b1ce7..62903d5a7b35 100644
--- a/packages/SystemUI/res/values/attrs.xml
+++ b/packages/SystemUI/res/values/attrs.xml
@@ -154,10 +154,6 @@
<attr name="showAirplaneMode" format="boolean" />
</declare-styleable>
- <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" />
@@ -199,5 +195,9 @@
</declare-styleable>
<attr name="overlayButtonTextColor" format="color" />
+
+ <declare-styleable name="DreamOverlayDotImageView">
+ <attr name="dotColor" format="color" />
+ </declare-styleable>
</resources>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index f4e7cf3fcf40..dc7470081da2 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -225,4 +225,6 @@
<color name="settingslib_track_off_color">@color/settingslib_track_off</color>
<color name="connected_network_primary_color">#191C18</color>
<color name="connected_network_secondary_color">#41493D</color>
+
+ <color name="dream_overlay_camera_mic_off_dot_color">#FCBE03</color>
</resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 370413400766..debf95b86c0c 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1332,17 +1332,23 @@
<dimen name="fgs_manager_min_width_minor">100%</dimen>
<!-- Dream overlay related dimensions -->
- <dimen name="dream_overlay_status_bar_height">80dp</dimen>
+ <dimen name="dream_overlay_status_bar_height">60dp</dimen>
<dimen name="dream_overlay_status_bar_margin">40dp</dimen>
<dimen name="dream_overlay_status_icon_margin">8dp</dimen>
+ <dimen name="dream_overlay_status_bar_icon_size">
+ @*android:dimen/status_bar_system_icon_size</dimen>
<!-- Height of the area at the top of the dream overlay to allow dragging down the notifications
shade. -->
<dimen name="dream_overlay_notifications_drag_area_height">100dp</dimen>
+ <dimen name="dream_overlay_camera_mic_off_indicator_size">8dp</dimen>
+ <dimen name="dream_overlay_notification_indicator_size">6dp</dimen>
<!-- Dream overlay complications related dimensions -->
<dimen name="dream_overlay_complication_clock_time_text_size">72sp</dimen>
<dimen name="dream_overlay_complication_clock_date_text_size">18sp</dimen>
<dimen name="dream_overlay_complication_weather_text_size">18sp</dimen>
+ <dimen name="dream_overlay_complication_preview_text_size">36sp</dimen>
+ <dimen name="dream_overlay_complication_preview_icon_padding">28dp</dimen>
<!-- The position of the end guide, which dream overlay complications can align their start with
if their end is aligned with the parent end. Represented as the percentage over from the
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 23b252906056..df16b0d45228 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2410,4 +2410,17 @@
<!-- Toast shown when a notification does not support dragging to split [CHAR LIMIT=NONE] -->
<string name="drag_split_not_supported">This notification does not support dragging to Splitscreen.</string>
+
+ <!-- Content description for the Wi-Fi off icon in the dream overlay status bar [CHAR LIMIT=NONE] -->
+ <string name="dream_overlay_status_bar_wifi_off">Wi\u2011Fi unavailable</string>
+ <!-- Content description for the priority mode icon in the dream overlay status bar [CHAR LIMIT=NONE] -->
+ <string name="dream_overlay_status_bar_priority_mode">Priority mode</string>
+ <!-- Content description for the alarm set icon in the dream overlay status bar [CHAR LIMIT=NONE] -->
+ <string name="dream_overlay_status_bar_alarm_set">Alarm set</string>
+ <!-- Content description for the assistant guest mode enabled icon in the dream overlay status bar [CHAR LIMIT=NONE] -->
+ <string name="dream_overlay_status_bar_assistant_guest_mode_enabled">Assistant guest mode enabled</string>
+ <!-- Content description for the camera and mic off icon in the dream overlay status bar [CHAR LIMIT=NONE] -->
+ <string name="dream_overlay_status_bar_camera_mic_off">Camera and mic are off</string>
+ <!-- Content description for the camera and mic off icon in the dream overlay status bar [CHAR LIMIT=NONE] -->
+ <string name="dream_overlay_status_bar_notification_indicator">There are notifications</string>
</resources>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 9d65c3895827..f2eaa75496e8 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -381,12 +381,19 @@
<item name="android:buttonBarNeutralButtonStyle">@style/Widget.Dialog.Button.BorderButton</item>
<item name="android:colorBackground">?androidprv:attr/colorSurface</item>
<item name="android:alertDialogStyle">@style/AlertDialogStyle</item>
+ <item name="android:buttonBarStyle">@style/ButtonBarStyle</item>
+ <item name="android:buttonBarButtonStyle">@style/Widget.Dialog.Button.Large</item>
</style>
<style name="AlertDialogStyle" parent="@androidprv:style/AlertDialog.DeviceDefault">
<item name="android:layout">@layout/alert_dialog_systemui</item>
</style>
+ <style name="ButtonBarStyle" parent="@androidprv:style/DeviceDefault.ButtonBar.AlertDialog">
+ <item name="android:paddingTop">@dimen/dialog_button_bar_top_padding</item>
+ <item name="android:paddingBottom">@dimen/dialog_bottom_padding</item>
+ </style>
+
<style name="Theme.SystemUI.Dialog.Alert" parent="@*android:style/Theme.DeviceDefault.Light.Dialog.Alert" />
<style name="Theme.SystemUI.Dialog.GlobalActions" parent="@android:style/Theme.DeviceDefault.Light.NoActionBar.Fullscreen">
@@ -962,6 +969,11 @@
<item name="android:textColor">?android:attr/textColorPrimary</item>
</style>
+ <style name="Widget.Dialog.Button.Large">
+ <item name="android:background">@drawable/qs_dialog_btn_filled_large</item>
+ <item name="android:minHeight">56dp</item>
+ </style>
+
<style name="MainSwitch.Settingslib" parent="@android:style/Theme.DeviceDefault">
<item name="android:switchMinWidth">@dimen/settingslib_min_switch_width</item>
</style>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/flags/Flag.kt b/packages/SystemUI/shared/src/com/android/systemui/flags/Flag.kt
index b611c9659db8..6ad91612f99b 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/flags/Flag.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/flags/Flag.kt
@@ -35,6 +35,11 @@ interface ResourceFlag<T> : Flag<T> {
val resourceId: Int
}
+interface SysPropFlag<T> : Flag<T> {
+ val name: String
+ val default: T
+}
+
// Consider using the "parcelize" kotlin library.
data class BooleanFlag @JvmOverloads constructor(
@@ -66,6 +71,12 @@ data class ResourceBooleanFlag constructor(
@BoolRes override val resourceId: Int
) : ResourceFlag<Boolean>
+data class SysPropBooleanFlag constructor(
+ override val id: Int,
+ override val name: String,
+ override val default: Boolean = false
+) : SysPropFlag<Boolean>
+
data class StringFlag @JvmOverloads constructor(
override val id: Int,
override val default: String = ""
diff --git a/packages/SystemUI/shared/src/com/android/systemui/flags/FlagManager.kt b/packages/SystemUI/shared/src/com/android/systemui/flags/FlagManager.kt
index ec619dd6eea3..149f6e8e7e9e 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/flags/FlagManager.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/flags/FlagManager.kt
@@ -54,7 +54,7 @@ class FlagManager constructor(
* An action called on restart which takes as an argument whether the listeners requested
* that the restart be suppressed
*/
- var restartAction: Consumer<Boolean>? = null
+ var onSettingsChangedAction: Consumer<Boolean>? = null
var clearCacheAction: Consumer<Int>? = null
private val listeners: MutableSet<PerFlagListener> = mutableSetOf()
private val settingsObserver: ContentObserver = SettingsObserver()
@@ -154,11 +154,11 @@ class FlagManager constructor(
val idStr = parts[parts.size - 1]
val id = try { idStr.toInt() } catch (e: NumberFormatException) { return }
clearCacheAction?.accept(id)
- dispatchListenersAndMaybeRestart(id)
+ dispatchListenersAndMaybeRestart(id, onSettingsChangedAction)
}
}
- fun dispatchListenersAndMaybeRestart(id: Int) {
+ fun dispatchListenersAndMaybeRestart(id: Int, restartAction: Consumer<Boolean>?) {
val filteredListeners: List<FlagListenable.Listener> = synchronized(listeners) {
listeners.mapNotNull { if (it.id == id) it.listener else null }
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
index 08b4d3f68a87..2b1c47f60070 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
@@ -103,8 +103,8 @@ public class QuickStepContract {
// enabled (since it's used to navigate back within the bubbled app, or to collapse the bubble
// stack.
public static final int SYSUI_STATE_BUBBLES_EXPANDED = 1 << 14;
- // The global actions dialog is showing
- public static final int SYSUI_STATE_GLOBAL_ACTIONS_SHOWING = 1 << 15;
+ // A SysUI dialog is showing.
+ public static final int SYSUI_STATE_DIALOG_SHOWING = 1 << 15;
// The one-handed mode is active
public static final int SYSUI_STATE_ONE_HANDED_ACTIVE = 1 << 16;
// Allow system gesture no matter the system bar(s) is visible or not
@@ -140,7 +140,7 @@ public class QuickStepContract {
SYSUI_STATE_TRACING_ENABLED,
SYSUI_STATE_ASSIST_GESTURE_CONSTRAINED,
SYSUI_STATE_BUBBLES_EXPANDED,
- SYSUI_STATE_GLOBAL_ACTIONS_SHOWING,
+ SYSUI_STATE_DIALOG_SHOWING,
SYSUI_STATE_ONE_HANDED_ACTIVE,
SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY,
SYSUI_STATE_IME_SHOWING,
@@ -166,7 +166,7 @@ public class QuickStepContract {
str.add((flags & SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED) != 0
? "keygrd_occluded" : "");
str.add((flags & SYSUI_STATE_BOUNCER_SHOWING) != 0 ? "bouncer_visible" : "");
- str.add((flags & SYSUI_STATE_GLOBAL_ACTIONS_SHOWING) != 0 ? "global_actions" : "");
+ str.add((flags & SYSUI_STATE_DIALOG_SHOWING) != 0 ? "dialog_showing" : "");
str.add((flags & SYSUI_STATE_A11Y_BUTTON_CLICKABLE) != 0 ? "a11y_click" : "");
str.add((flags & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE) != 0 ? "a11y_long_click" : "");
str.add((flags & SYSUI_STATE_TRACING_ENABLED) != 0 ? "tracing" : "");
@@ -256,7 +256,7 @@ public class QuickStepContract {
public static boolean isBackGestureDisabled(int sysuiStateFlags) {
// Always allow when the bouncer/global actions is showing (even on top of the keyguard)
if ((sysuiStateFlags & SYSUI_STATE_BOUNCER_SHOWING) != 0
- || (sysuiStateFlags & SYSUI_STATE_GLOBAL_ACTIONS_SHOWING) != 0) {
+ || (sysuiStateFlags & SYSUI_STATE_DIALOG_SHOWING) != 0) {
return false;
}
if ((sysuiStateFlags & SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY) != 0) {
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index b32c2b639f16..84b6ace17ab9 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -38,6 +38,7 @@ import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.systemui.accessibility.AccessibilityButtonModeObserver;
import com.android.systemui.accessibility.AccessibilityButtonTargetsObserver;
import com.android.systemui.accessibility.floatingmenu.AccessibilityFloatingMenuController;
+import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.appops.AppOpsController;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.broadcast.BroadcastDispatcher;
@@ -107,6 +108,7 @@ import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider;
import com.android.systemui.statusbar.phone.StatusBarIconController;
+import com.android.systemui.statusbar.phone.SystemUIDialogManager;
import com.android.systemui.statusbar.policy.AccessibilityController;
import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
import com.android.systemui.statusbar.policy.BatteryController;
@@ -372,6 +374,8 @@ public class Dependency {
@Inject Lazy<AmbientState> mAmbientStateLazy;
@Inject Lazy<GroupMembershipManager> mGroupMembershipManagerLazy;
@Inject Lazy<GroupExpansionManager> mGroupExpansionManagerLazy;
+ @Inject Lazy<SystemUIDialogManager> mSystemUIDialogManagerLazy;
+ @Inject Lazy<DialogLaunchAnimator> mDialogLaunchAnimatorLazy;
@Inject
public Dependency() {
@@ -592,6 +596,8 @@ public class Dependency {
mProviders.put(AmbientState.class, mAmbientStateLazy::get);
mProviders.put(GroupMembershipManager.class, mGroupMembershipManagerLazy::get);
mProviders.put(GroupExpansionManager.class, mGroupExpansionManagerLazy::get);
+ mProviders.put(SystemUIDialogManager.class, mSystemUIDialogManagerLazy::get);
+ mProviders.put(DialogLaunchAnimator.class, mDialogLaunchAnimatorLazy::get);
Dependency.setInstance(this);
}
diff --git a/packages/SystemUI/src/com/android/systemui/DisplayCutoutBaseView.kt b/packages/SystemUI/src/com/android/systemui/DisplayCutoutBaseView.kt
index 6bec8aacd55f..56046d9c057d 100644
--- a/packages/SystemUI/src/com/android/systemui/DisplayCutoutBaseView.kt
+++ b/packages/SystemUI/src/com/android/systemui/DisplayCutoutBaseView.kt
@@ -49,7 +49,7 @@ open class DisplayCutoutBaseView : View, RegionInterceptableView {
private val shouldDrawCutout: Boolean = DisplayCutout.getFillBuiltInDisplayCutout(
context.resources, context.display?.uniqueId)
private var displayMode: Display.Mode? = null
- private val location = IntArray(2)
+ protected val location = IntArray(2)
protected var displayRotation = 0
@VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
@@ -65,7 +65,8 @@ open class DisplayCutoutBaseView : View, RegionInterceptableView {
@JvmField val protectionPath: Path = Path()
private val protectionRectOrig: RectF = RectF()
private val protectionPathOrig: Path = Path()
- private var cameraProtectionProgress: Float = HIDDEN_CAMERA_PROTECTION_SCALE
+ @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
+ var cameraProtectionProgress: Float = HIDDEN_CAMERA_PROTECTION_SCALE
private var cameraProtectionAnimator: ValueAnimator? = null
constructor(context: Context) : super(context)
@@ -273,4 +274,4 @@ open class DisplayCutoutBaseView : View, RegionInterceptableView {
}
}
}
-} \ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorHwcLayer.kt b/packages/SystemUI/src/com/android/systemui/ScreenDecorHwcLayer.kt
index ee1d9a3aa5de..4b868622ce58 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorHwcLayer.kt
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorHwcLayer.kt
@@ -26,11 +26,22 @@ import android.graphics.PixelFormat
import android.graphics.PorterDuff
import android.graphics.PorterDuffColorFilter
import android.graphics.PorterDuffXfermode
+import android.graphics.Rect
+import android.graphics.Region
import android.graphics.drawable.Drawable
import android.hardware.graphics.common.AlphaInterpretation
import android.hardware.graphics.common.DisplayDecorationSupport
+import android.view.DisplayCutout.BOUNDS_POSITION_BOTTOM
+import android.view.DisplayCutout.BOUNDS_POSITION_LEFT
+import android.view.DisplayCutout.BOUNDS_POSITION_LENGTH
+import android.view.DisplayCutout.BOUNDS_POSITION_TOP
+import android.view.DisplayCutout.BOUNDS_POSITION_RIGHT
import android.view.RoundedCorner
import android.view.RoundedCorners
+import android.view.Surface
+import androidx.annotation.VisibleForTesting
+import kotlin.math.ceil
+import kotlin.math.floor
/**
* When the HWC of the device supports Composition.DISPLAY_DECORATON, we use this layer to draw
@@ -38,13 +49,16 @@ import android.view.RoundedCorners
*/
class ScreenDecorHwcLayer(context: Context, displayDecorationSupport: DisplayDecorationSupport)
: DisplayCutoutBaseView(context) {
- public val colorMode: Int
+ val colorMode: Int
private val useInvertedAlphaColor: Boolean
private val color: Int
private val bgColor: Int
private val cornerFilter: ColorFilter
private val cornerBgFilter: ColorFilter
private val clearPaint: Paint
+ @JvmField val transparentRect: Rect = Rect()
+ private val debugTransparentRegionPaint: Paint?
+ private val tempRect: Rect = Rect()
private var roundedCornerTopSize = 0
private var roundedCornerBottomSize = 0
@@ -61,6 +75,10 @@ class ScreenDecorHwcLayer(context: Context, displayDecorationSupport: DisplayDec
bgColor = Color.TRANSPARENT
colorMode = ActivityInfo.COLOR_MODE_DEFAULT
useInvertedAlphaColor = false
+ debugTransparentRegionPaint = Paint().apply {
+ color = 0x2f00ff00 // semi-transparent green
+ style = Paint.Style.FILL
+ }
} else {
colorMode = ActivityInfo.COLOR_MODE_A8
useInvertedAlphaColor = displayDecorationSupport.alphaInterpretation ==
@@ -72,6 +90,7 @@ class ScreenDecorHwcLayer(context: Context, displayDecorationSupport: DisplayDec
color = Color.BLACK
bgColor = Color.TRANSPARENT
}
+ debugTransparentRegionPaint = null
}
cornerFilter = PorterDuffColorFilter(color, PorterDuff.Mode.SRC_IN)
cornerBgFilter = PorterDuffColorFilter(bgColor, PorterDuff.Mode.SRC_OUT)
@@ -82,6 +101,9 @@ class ScreenDecorHwcLayer(context: Context, displayDecorationSupport: DisplayDec
override fun onAttachedToWindow() {
super.onAttachedToWindow()
+ if (!DEBUG_COLOR) {
+ parent.requestTransparentRegion(this)
+ }
viewRootImpl.setDisplayDecoration(true)
if (useInvertedAlphaColor) {
@@ -93,12 +115,172 @@ class ScreenDecorHwcLayer(context: Context, displayDecorationSupport: DisplayDec
}
override fun onDraw(canvas: Canvas) {
+ // If updating onDraw, also update gatherTransparentRegion
if (useInvertedAlphaColor) {
canvas.drawColor(bgColor)
}
// Cutouts are drawn in DisplayCutoutBaseView.onDraw()
super.onDraw(canvas)
drawRoundedCorners(canvas)
+
+ debugTransparentRegionPaint?.let {
+ calculateTransparentRect()
+ canvas.drawRect(transparentRect, it)
+ }
+ }
+
+ override fun gatherTransparentRegion(region: Region?): Boolean {
+ region?.let {
+ calculateTransparentRect()
+ region.op(transparentRect, Region.Op.INTERSECT)
+ }
+ // Always return false - views underneath this should always be visible.
+ return false
+ }
+
+ /**
+ * The transparent rect is calculated by subtracting the regions of cutouts, cutout protect and
+ * rounded corners from the region with fullscreen display size.
+ */
+ @VisibleForTesting
+ fun calculateTransparentRect() {
+ transparentRect.set(0, 0, width, height)
+
+ // Remove cutout region.
+ removeCutoutFromTransparentRegion()
+
+ // Remove cutout protection region.
+ removeCutoutProtectionFromTransparentRegion()
+
+ // Remove rounded corner region.
+ removeRoundedCornersFromTransparentRegion()
+ }
+
+ private fun removeCutoutFromTransparentRegion() {
+ displayInfo.displayCutout?.let {
+ cutout ->
+ if (!cutout.boundingRectLeft.isEmpty) {
+ transparentRect.left =
+ cutout.boundingRectLeft.right.coerceAtLeast(transparentRect.left)
+ }
+ if (!cutout.boundingRectTop.isEmpty) {
+ transparentRect.top =
+ cutout.boundingRectTop.bottom.coerceAtLeast(transparentRect.top)
+ }
+ if (!cutout.boundingRectRight.isEmpty) {
+ transparentRect.right =
+ cutout.boundingRectRight.left.coerceAtMost(transparentRect.right)
+ }
+ if (!cutout.boundingRectBottom.isEmpty) {
+ transparentRect.bottom =
+ cutout.boundingRectBottom.top.coerceAtMost(transparentRect.bottom)
+ }
+ }
+ }
+
+ private fun removeCutoutProtectionFromTransparentRegion() {
+ if (protectionRect.isEmpty) {
+ return
+ }
+
+ val centerX = protectionRect.centerX()
+ val centerY = protectionRect.centerY()
+ val scaledDistanceX = (centerX - protectionRect.left) * cameraProtectionProgress
+ val scaledDistanceY = (centerY - protectionRect.top) * cameraProtectionProgress
+ tempRect.set(
+ floor(centerX - scaledDistanceX).toInt(),
+ floor(centerY - scaledDistanceY).toInt(),
+ ceil(centerX + scaledDistanceX).toInt(),
+ ceil(centerY + scaledDistanceY).toInt()
+ )
+
+ // Find out which edge the protectionRect belongs and remove that edge from the transparent
+ // region.
+ val leftDistance = tempRect.left
+ val topDistance = tempRect.top
+ val rightDistance = width - tempRect.right
+ val bottomDistance = height - tempRect.bottom
+ val minDistance = minOf(leftDistance, topDistance, rightDistance, bottomDistance)
+ when (minDistance) {
+ leftDistance -> {
+ transparentRect.left = tempRect.right.coerceAtLeast(transparentRect.left)
+ }
+ topDistance -> {
+ transparentRect.top = tempRect.bottom.coerceAtLeast(transparentRect.top)
+ }
+ rightDistance -> {
+ transparentRect.right = tempRect.left.coerceAtMost(transparentRect.right)
+ }
+ bottomDistance -> {
+ transparentRect.bottom = tempRect.top.coerceAtMost(transparentRect.bottom)
+ }
+ }
+ }
+
+ private fun removeRoundedCornersFromTransparentRegion() {
+ var hasTopOrBottomCutouts = false
+ var hasLeftOrRightCutouts = false
+ displayInfo.displayCutout?.let {
+ cutout ->
+ hasTopOrBottomCutouts = !cutout.boundingRectTop.isEmpty ||
+ !cutout.boundingRectBottom.isEmpty
+ hasLeftOrRightCutouts = !cutout.boundingRectLeft.isEmpty ||
+ !cutout.boundingRectRight.isEmpty
+ }
+ // The goal is to remove the rounded corner areas as small as possible so that we can have a
+ // larger transparent region. Therefore, we should always remove from the short edge sides
+ // if possible.
+ val isShortEdgeTopBottom = width < height
+ if (isShortEdgeTopBottom) {
+ // Short edges on top & bottom.
+ if (!hasTopOrBottomCutouts && hasLeftOrRightCutouts) {
+ // If there are cutouts only on left or right edges, remove left and right sides
+ // for rounded corners.
+ transparentRect.left = getRoundedCornerSizeByPosition(BOUNDS_POSITION_LEFT)
+ .coerceAtLeast(transparentRect.left)
+ transparentRect.right =
+ (width - getRoundedCornerSizeByPosition(BOUNDS_POSITION_RIGHT))
+ .coerceAtMost(transparentRect.right)
+ } else {
+ // If there are cutouts on top or bottom edges or no cutout at all, remove top
+ // and bottom sides for rounded corners.
+ transparentRect.top = getRoundedCornerSizeByPosition(BOUNDS_POSITION_TOP)
+ .coerceAtLeast(transparentRect.top)
+ transparentRect.bottom =
+ (height - getRoundedCornerSizeByPosition(BOUNDS_POSITION_BOTTOM))
+ .coerceAtMost(transparentRect.bottom)
+ }
+ } else {
+ // Short edges on left & right.
+ if (hasTopOrBottomCutouts && !hasLeftOrRightCutouts) {
+ // If there are cutouts only on top or bottom edges, remove top and bottom sides
+ // for rounded corners.
+ transparentRect.top = getRoundedCornerSizeByPosition(BOUNDS_POSITION_TOP)
+ .coerceAtLeast(transparentRect.top)
+ transparentRect.bottom =
+ (height - getRoundedCornerSizeByPosition(BOUNDS_POSITION_BOTTOM))
+ .coerceAtMost(transparentRect.bottom)
+ } else {
+ // If there are cutouts on left or right edges or no cutout at all, remove left
+ // and right sides for rounded corners.
+ transparentRect.left = getRoundedCornerSizeByPosition(BOUNDS_POSITION_LEFT)
+ .coerceAtLeast(transparentRect.left)
+ transparentRect.right =
+ (width - getRoundedCornerSizeByPosition(BOUNDS_POSITION_RIGHT))
+ .coerceAtMost(transparentRect.right)
+ }
+ }
+ }
+
+ private fun getRoundedCornerSizeByPosition(position: Int): Int {
+ val delta = displayRotation - Surface.ROTATION_0
+ return when ((position + delta) % BOUNDS_POSITION_LENGTH) {
+ BOUNDS_POSITION_LEFT -> roundedCornerTopSize.coerceAtLeast(roundedCornerBottomSize)
+ BOUNDS_POSITION_TOP -> roundedCornerTopSize
+ BOUNDS_POSITION_RIGHT -> roundedCornerTopSize.coerceAtLeast(roundedCornerBottomSize)
+ BOUNDS_POSITION_BOTTOM -> roundedCornerBottomSize
+ else -> throw IllegalArgumentException("Incorrect position: $position")
+ }
}
private fun drawRoundedCorners(canvas: Canvas) {
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index 2ec5f4f894f9..ae41cae10bba 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -847,14 +847,20 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab
pw.println(" mIsRoundedCornerMultipleRadius:" + mIsRoundedCornerMultipleRadius);
pw.println(" mIsPrivacyDotEnabled:" + isPrivacyDotEnabled());
pw.println(" mPendingRotationChange:" + mPendingRotationChange);
- pw.println(" mHwcScreenDecorationSupport:");
- if (mHwcScreenDecorationSupport == null) {
- pw.println(" null");
- } else {
- pw.println(" format: "
+ if (mHwcScreenDecorationSupport != null) {
+ pw.println(" mHwcScreenDecorationSupport:");
+ pw.println(" format="
+ PixelFormat.formatToString(mHwcScreenDecorationSupport.format));
- pw.println(" alphaInterpretation: "
+ pw.println(" alphaInterpretation="
+ alphaInterpretationToString(mHwcScreenDecorationSupport.alphaInterpretation));
+ } else {
+ pw.println(" mHwcScreenDecorationSupport: null");
+ }
+ if (mScreenDecorHwcLayer != null) {
+ pw.println(" mScreenDecorHwcLayer:");
+ pw.println(" transparentRegion=" + mScreenDecorHwcLayer.transparentRect);
+ } else {
+ pw.println(" mScreenDecorHwcLayer: null");
}
pw.println(" mRoundedDefault(x,y)=(" + mRoundedDefault.x + "," + mRoundedDefault.y + ")");
pw.println(" mRoundedDefaultTop(x,y)=(" + mRoundedDefaultTop.x + "," + mRoundedDefaultTop.y
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
index 0d20403b08f2..de03993a6f17 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
@@ -43,6 +43,7 @@ import android.os.Handler;
import android.os.RemoteException;
import android.util.Log;
import android.util.Range;
+import android.util.Size;
import android.view.Choreographer;
import android.view.Display;
import android.view.Gravity;
@@ -62,6 +63,8 @@ import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
import android.view.accessibility.IRemoteMagnificationAnimationCallback;
+import androidx.core.math.MathUtils;
+
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
import com.android.systemui.R;
@@ -166,6 +169,7 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
private final Rect mMagnificationFrameBoundary = new Rect();
// The top Y of the system gesture rect at the bottom. Set to -1 if it is invalid.
private int mSystemGestureTop = -1;
+ private int mMinWindowSize;
private final WindowMagnificationAnimationController mAnimationController;
private final SfVsyncFrameCallbackProvider mSfVsyncFrameProvider;
@@ -208,8 +212,10 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
mBounceEffectDuration = mResources.getInteger(
com.android.internal.R.integer.config_shortAnimTime);
updateDimensions();
- setMagnificationFrameWith(mWindowBounds, mWindowBounds.width() / 2,
- mWindowBounds.height() / 2);
+
+ final Size windowSize = getDefaultWindowSizeWithWindowBounds(mWindowBounds);
+ setMagnificationFrame(windowSize.getWidth(), windowSize.getHeight(),
+ mWindowBounds.width() / 2, mWindowBounds.height() / 2);
computeBounceAnimationScale();
mMirrorWindowControl = mirrorWindowControl;
@@ -281,6 +287,8 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
R.dimen.magnification_drag_view_size);
mOuterBorderSize = mResources.getDimensionPixelSize(
R.dimen.magnification_outer_border_margin);
+ mMinWindowSize = mResources.getDimensionPixelSize(
+ com.android.internal.R.dimen.accessibility_window_magnifier_min_size);
}
private void computeBounceAnimationScale() {
@@ -414,9 +422,12 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
return false;
}
mWindowBounds.set(currentWindowBounds);
+ final Size windowSize = getDefaultWindowSizeWithWindowBounds(mWindowBounds);
final float newCenterX = (getCenterX()) * mWindowBounds.width() / oldWindowBounds.width();
final float newCenterY = (getCenterY()) * mWindowBounds.height() / oldWindowBounds.height();
- setMagnificationFrameWith(mWindowBounds, (int) newCenterX, (int) newCenterY);
+
+ setMagnificationFrame(windowSize.getWidth(), windowSize.getHeight(), (int) newCenterX,
+ (int) newCenterY);
calculateMagnificationFrameBoundary();
return true;
}
@@ -454,11 +465,6 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
mWindowBounds.set(currentWindowBounds);
- calculateMagnificationFrameBoundary();
-
- if (!isWindowVisible()) {
- return;
- }
// Keep MirrorWindow position on the screen unchanged when device rotates 90°
// clockwise or anti-clockwise.
@@ -469,14 +475,13 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
} else if (rotationDegree == 270) {
matrix.postTranslate(0, mWindowBounds.height());
}
- // The rect of MirrorView is going to be transformed.
- LayoutParams params =
- (LayoutParams) mMirrorView.getLayoutParams();
- mTmpRect.set(params.x, params.y, params.x + params.width, params.y + params.height);
- final RectF transformedRect = new RectF(mTmpRect);
+
+ final RectF transformedRect = new RectF(mMagnificationFrame);
+ // The window frame is going to be transformed by the rotation matrix.
+ transformedRect.inset(-mMirrorSurfaceMargin, -mMirrorSurfaceMargin);
matrix.mapRect(transformedRect);
- moveWindowMagnifier(transformedRect.left - mTmpRect.left,
- transformedRect.top - mTmpRect.top);
+ setWindowSizeAndCenter((int) transformedRect.width(), (int) transformedRect.height(),
+ (int) transformedRect.centerX(), (int) transformedRect.centerY());
}
/** Returns the rotation degree change of two {@link Surface.Rotation} */
@@ -573,16 +578,52 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
}
}
- private void setMagnificationFrameWith(Rect windowBounds, int centerX, int centerY) {
+ /**
+ * Sets the window size with given width and height in pixels without changing the
+ * window center. The width or the height will be clamped in the range
+ * [{@link #mMinWindowSize}, screen width or height].
+ *
+ * @param width the window width in pixels
+ * @param height the window height in pixels.
+ */
+ public void setWindowSize(int width, int height) {
+ setWindowSizeAndCenter(width, height, Float.NaN, Float.NaN);
+ }
+
+ void setWindowSizeAndCenter(int width, int height, float centerX, float centerY) {
+ width = MathUtils.clamp(width, mMinWindowSize, mWindowBounds.width());
+ height = MathUtils.clamp(height, mMinWindowSize, mWindowBounds.height());
+
+ if (Float.isNaN(centerX)) {
+ centerX = mMagnificationFrame.centerX();
+ }
+ if (Float.isNaN(centerX)) {
+ centerY = mMagnificationFrame.centerY();
+ }
+
+ final int frameWidth = width - 2 * mMirrorSurfaceMargin;
+ final int frameHeight = height - 2 * mMirrorSurfaceMargin;
+ setMagnificationFrame(frameWidth, frameHeight, (int) centerX, (int) centerY);
+ calculateMagnificationFrameBoundary();
+ // Correct the frame position to ensure it is inside the boundary.
+ updateMagnificationFramePosition(0, 0);
+ modifyWindowMagnification(true);
+ }
+
+ private void setMagnificationFrame(int width, int height, int centerX, int centerY) {
// Sets the initial frame area for the mirror and place it to the given center on the
// display.
+ final int initX = centerX - width / 2;
+ final int initY = centerY - height / 2;
+ mMagnificationFrame.set(initX, initY, initX + width, initY + height);
+ }
+
+ private Size getDefaultWindowSizeWithWindowBounds(Rect windowBounds) {
int initSize = Math.min(windowBounds.width(), windowBounds.height()) / 2;
initSize = Math.min(mResources.getDimensionPixelSize(R.dimen.magnification_max_frame_size),
initSize);
initSize += 2 * mMirrorSurfaceMargin;
- final int initX = centerX - initSize / 2;
- final int initY = centerY - initSize / 2;
- mMagnificationFrame.set(initX, initY, initX + initSize, initY + initSize);
+ return new Size(initSize, initSize);
}
/**
@@ -596,8 +637,7 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
}
mTransaction.show(mMirrorSurface)
.reparent(mMirrorSurface, mMirrorSurfaceView.getSurfaceControl());
-
- modifyWindowMagnification(mTransaction);
+ modifyWindowMagnification(false);
}
private void addDragTouchListeners() {
@@ -615,18 +655,25 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
}
/**
- * Modifies the placement of the mirrored content when the position of mMirrorView is updated.
+ * Modifies the placement of the mirrored content when the position or size of mMirrorView is
+ * updated.
+ *
+ * @param computeWindowSize set to {@code true} to compute window size with
+ * {@link #mMagnificationFrame}.
*/
- private void modifyWindowMagnification(SurfaceControl.Transaction t) {
+ private void modifyWindowMagnification(boolean computeWindowSize) {
mSfVsyncFrameProvider.postFrameCallback(mMirrorViewGeometryVsyncCallback);
- updateMirrorViewLayout();
+ updateMirrorViewLayout(computeWindowSize);
}
/**
- * Updates the layout params of MirrorView and translates MirrorView position when the view is
- * moved close to the screen edges.
+ * Updates the layout params of MirrorView based on the size of {@link #mMagnificationFrame}
+ * and translates MirrorView position when the view is moved close to the screen edges;
+ *
+ * @param computeWindowSize set to {@code true} to compute window size with
+ * {@link #mMagnificationFrame}.
*/
- private void updateMirrorViewLayout() {
+ private void updateMirrorViewLayout(boolean computeWindowSize) {
if (!isWindowVisible()) {
return;
}
@@ -637,6 +684,10 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
(LayoutParams) mMirrorView.getLayoutParams();
params.x = mMagnificationFrame.left - mMirrorSurfaceMargin;
params.y = mMagnificationFrame.top - mMirrorSurfaceMargin;
+ if (computeWindowSize) {
+ params.width = mMagnificationFrame.width() + 2 * mMirrorSurfaceMargin;
+ params.height = mMagnificationFrame.height() + 2 * mMirrorSurfaceMargin;
+ }
// Translates MirrorView position to make MirrorSurfaceView that is inside MirrorView
// able to move close to the screen edges.
@@ -899,7 +950,7 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
createMirrorWindow();
showControls();
} else {
- modifyWindowMagnification(mTransaction);
+ modifyWindowMagnification(false);
}
}
@@ -930,7 +981,7 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
return;
}
if (updateMagnificationFramePosition((int) offsetX, (int) offsetY)) {
- modifyWindowMagnification(mTransaction);
+ modifyWindowMagnification(false);
}
}
@@ -1014,9 +1065,15 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
pw.println(" mOverlapWithGestureInsets:" + mOverlapWithGestureInsets);
pw.println(" mScale:" + mScale);
pw.println(" mMirrorViewBounds:" + (isWindowVisible() ? mMirrorViewBounds : "empty"));
+ pw.println(" mMagnificationFrameBoundary:"
+ + (isWindowVisible() ? mMagnificationFrameBoundary : "empty"));
+ pw.println(" mMagnificationFrame:"
+ + (isWindowVisible() ? mMagnificationFrame : "empty"));
pw.println(" mSourceBounds:"
+ (isWindowVisible() ? mSourceBounds : "empty"));
pw.println(" mSystemGestureTop:" + mSystemGestureTop);
+ pw.println(" mMagnificationFrameOffsetX:" + mMagnificationFrameOffsetX);
+ pw.println(" mMagnificationFrameOffsetY:" + mMagnificationFrameOffsetY);
}
private class MirrorWindowA11yDelegate extends View.AccessibilityDelegate {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
index 3ece3ccf38fa..24a655c2856d 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
@@ -20,6 +20,7 @@ import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
import android.annotation.NonNull;
import android.content.res.Configuration;
+import android.util.Log;
import android.util.MathUtils;
import android.view.MotionEvent;
@@ -47,6 +48,7 @@ import java.io.PrintWriter;
* Class that coordinates non-HBM animations during keyguard authentication.
*/
public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<UdfpsKeyguardView> {
+ public static final String TAG = "UdfpsKeyguardViewCtrl";
@NonNull private final StatusBarKeyguardViewManager mKeyguardViewManager;
@NonNull private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@NonNull private final LockscreenShadeTransitionController mLockScreenShadeTransitionController;
@@ -246,7 +248,12 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud
}
if (mInputBouncerHiddenAmount < .5f || mIsBouncerVisible) {
- return true;
+ if (!getStatusBarStateController().isDozing()) {
+ return true;
+ } else {
+ Log.e(TAG, "Bouncer state claims visible on doze hiddenAmount="
+ + mInputBouncerHiddenAmount + " bouncerVisible=" + mIsBouncerVisible);
+ }
}
return false;
diff --git a/packages/SystemUI/src/com/android/systemui/chooser/ChooserHelper.java b/packages/SystemUI/src/com/android/systemui/chooser/ChooserHelper.java
deleted file mode 100644
index 6c1da4711fec..000000000000
--- a/packages/SystemUI/src/com/android/systemui/chooser/ChooserHelper.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.chooser;
-
-import android.app.Activity;
-import android.app.ActivityTaskManager;
-import android.content.Intent;
-import android.os.Bundle;
-import android.os.IBinder;
-import android.os.StrictMode;
-
-/**
- * When a target is chosen from the SystemUI Chooser activity, unpack its arguments and
- * startActivityAsCaller to handle the now-chosen intent.
- */
-public class ChooserHelper {
-
- private static final String TAG = "ChooserHelper";
-
- static void onChoose(Activity activity) {
- final Intent thisIntent = activity.getIntent();
- final Bundle thisExtras = thisIntent.getExtras();
- final Intent chosenIntent = thisIntent.getParcelableExtra(Intent.EXTRA_INTENT);
- final Bundle options = thisIntent.getParcelableExtra(ActivityTaskManager.EXTRA_OPTIONS);
- final IBinder permissionToken =
- thisExtras.getBinder(ActivityTaskManager.EXTRA_PERMISSION_TOKEN);
- final boolean ignoreTargetSecurity =
- thisIntent.getBooleanExtra(ActivityTaskManager.EXTRA_IGNORE_TARGET_SECURITY, false);
- final int userId = thisIntent.getIntExtra(Intent.EXTRA_USER_ID, -1);
-
- // We're dispatching intents that might be coming from legacy apps, so
- // (as in com.android.internal.app.ResolverActivity) exempt ourselves from death.
- StrictMode.disableDeathOnFileUriExposure();
- try {
- activity.startActivityAsCaller(
- chosenIntent, options, permissionToken, ignoreTargetSecurity, userId);
- } finally {
- StrictMode.enableDeathOnFileUriExposure();
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
index bd472a48ab3e..71c538d9324b 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
@@ -71,6 +71,7 @@ import android.view.ViewConfiguration;
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.CaptioningManager;
import android.view.inputmethod.InputMethodManager;
import com.android.internal.app.IBatteryStats;
@@ -120,6 +121,12 @@ public class FrameworkServicesModule {
@Provides
@Singleton
+ static CaptioningManager provideCaptioningManager(Context context) {
+ return context.getSystemService(CaptioningManager.class);
+ }
+
+ @Provides
+ @Singleton
static ColorDisplayManager provideColorDisplayManager(Context context) {
return context.getSystemService(ColorDisplayManager.class);
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java
index be76e8fcae37..745010372182 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java
@@ -18,12 +18,9 @@ package com.android.systemui.dreams;
import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInOffset;
-import android.graphics.Rect;
-import android.graphics.Region;
import android.os.Handler;
import android.view.View;
import android.view.ViewGroup;
-import android.view.ViewTreeObserver;
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.R;
@@ -62,43 +59,6 @@ public class DreamOverlayContainerViewController extends ViewController<DreamOve
// Main thread handler used to schedule periodic tasks (e.g. burn-in protection updates).
private final Handler mHandler;
- // A hook into the internal inset calculation where we declare the overlays as the only
- // touchable regions.
- private final ViewTreeObserver.OnComputeInternalInsetsListener
- mOnComputeInternalInsetsListener =
- new ViewTreeObserver.OnComputeInternalInsetsListener() {
- @Override
- public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo inoutInfo) {
- inoutInfo.setTouchableInsets(
- ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
- final Region region = new Region();
- final Rect rect = new Rect();
- final int childCount = mDreamOverlayContentView.getChildCount();
- for (int i = 0; i < childCount; i++) {
- View child = mDreamOverlayContentView.getChildAt(i);
-
- if (mComplicationHostViewController.getView() == child) {
- region.op(mComplicationHostViewController.getTouchRegions(),
- Region.Op.UNION);
- continue;
- }
-
- if (child.getGlobalVisibleRect(rect)) {
- region.op(rect, Region.Op.UNION);
- }
- }
-
- // Add the notifications drag area to the tap region (otherwise the
- // notifications shade can't be dragged down).
- if (mDreamOverlayContentView.getGlobalVisibleRect(rect)) {
- rect.bottom = rect.top + mDreamOverlayNotificationsDragAreaHeight;
- region.op(rect, Region.Op.UNION);
- }
-
- inoutInfo.touchableRegion.set(region);
- }
- };
-
@Inject
public DreamOverlayContainerViewController(
DreamOverlayContainerView containerView,
@@ -136,16 +96,12 @@ public class DreamOverlayContainerViewController extends ViewController<DreamOve
@Override
protected void onViewAttached() {
- mView.getViewTreeObserver()
- .addOnComputeInternalInsetsListener(mOnComputeInternalInsetsListener);
mHandler.postDelayed(this::updateBurnInOffsets, mBurnInProtectionUpdateInterval);
}
@Override
protected void onViewDetached() {
mHandler.removeCallbacks(this::updateBurnInOffsets);
- mView.getViewTreeObserver()
- .removeOnComputeInternalInsetsListener(mOnComputeInternalInsetsListener);
}
View getContainerView() {
@@ -162,6 +118,7 @@ public class DreamOverlayContainerViewController extends ViewController<DreamOve
// so no translation occurs when the values don't change.
mView.setTranslationX(getBurnInOffset(mMaxBurnInOffset * 2, true)
- mMaxBurnInOffset);
+
mView.setTranslationY(getBurnInOffset(mMaxBurnInOffset * 2, false)
- mMaxBurnInOffset);
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayDotImageView.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayDotImageView.java
new file mode 100644
index 000000000000..02a8b39a106a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayDotImageView.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.dreams;
+
+import android.annotation.ColorInt;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.ColorFilter;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.systemui.R;
+import com.android.systemui.statusbar.AlphaOptimizedImageView;
+
+/**
+ * An {@link AlphaOptimizedImageView} that is responsible for rendering a dot. Used by
+ * {@link DreamOverlayStatusBarView}.
+ */
+public class DreamOverlayDotImageView extends AlphaOptimizedImageView {
+ private final @ColorInt int mDotColor;
+
+ public DreamOverlayDotImageView(Context context) {
+ this(context, null);
+ }
+
+ public DreamOverlayDotImageView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public DreamOverlayDotImageView(Context context, AttributeSet attrs, int defStyleAttr) {
+ this(context, attrs, defStyleAttr, 0);
+ }
+
+ public DreamOverlayDotImageView(Context context, AttributeSet attrs, int defStyleAttr,
+ int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ TypedArray a = context.getTheme().obtainStyledAttributes(attrs,
+ R.styleable.DreamOverlayDotImageView, 0, 0);
+
+ try {
+ mDotColor = a.getColor(R.styleable.DreamOverlayDotImageView_dotColor, Color.WHITE);
+ } finally {
+ a.recycle();
+ }
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ setImageDrawable(new DotDrawable(mDotColor));
+ }
+
+ private static class DotDrawable extends Drawable {
+ private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ private Bitmap mDotBitmap;
+ private final Rect mBounds = new Rect();
+ private final @ColorInt int mDotColor;
+
+ DotDrawable(@ColorInt int color) {
+ mDotColor = color;
+ }
+
+ @Override
+ public void draw(@NonNull Canvas canvas) {
+ if (mBounds.isEmpty()) {
+ return;
+ }
+
+ if (mDotBitmap == null) {
+ mDotBitmap = createBitmap(mBounds.width(), mBounds.height());
+ }
+
+ canvas.drawBitmap(mDotBitmap, null, mBounds, mPaint);
+ }
+
+ @Override
+ protected void onBoundsChange(Rect bounds) {
+ super.onBoundsChange(bounds);
+ mBounds.set(bounds.left, bounds.top, bounds.right, bounds.bottom);
+ // Make sure to regenerate the dot bitmap when the bounds change.
+ mDotBitmap = null;
+ }
+
+ @Override
+ public void setAlpha(int alpha) {
+ }
+
+ @Override
+ public void setColorFilter(@Nullable ColorFilter colorFilter) {
+ }
+
+ @Override
+ public int getOpacity() {
+ return 0;
+ }
+
+ private Bitmap createBitmap(int width, int height) {
+ Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(bitmap);
+ Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ paint.setColor(mDotColor);
+ canvas.drawCircle(width / 2.f, height / 2.f, Math.min(width, height) / 2.f, paint);
+ return bitmap;
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
index 338a8b2a2a59..7e1fce298fbd 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
@@ -33,6 +33,7 @@ import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dreams.complication.Complication;
+import com.android.systemui.dreams.complication.DreamPreviewComplication;
import com.android.systemui.dreams.dagger.DreamOverlayComponent;
import com.android.systemui.dreams.touch.DreamOverlayTouchMonitor;
@@ -57,6 +58,7 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ
// content area).
private final DreamOverlayContainerViewController mDreamOverlayContainerViewController;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ private final DreamPreviewComplication mPreviewComplication;
// A reference to the {@link Window} used to hold the dream overlay.
private Window mWindow;
@@ -96,12 +98,14 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ
@Main Executor executor,
DreamOverlayComponent.Factory dreamOverlayComponentFactory,
DreamOverlayStateController stateController,
- KeyguardUpdateMonitor keyguardUpdateMonitor) {
+ KeyguardUpdateMonitor keyguardUpdateMonitor,
+ DreamPreviewComplication previewComplication) {
mContext = context;
mExecutor = executor;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mKeyguardUpdateMonitor.registerCallback(mKeyguardCallback);
mStateController = stateController;
+ mPreviewComplication = previewComplication;
final DreamOverlayComponent component =
dreamOverlayComponentFactory.create(mViewModelStore, mHost);
@@ -125,6 +129,9 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ
windowManager.removeView(mWindow.getDecorView());
}
mStateController.setOverlayActive(false);
+ mPreviewComplication.setDreamLabel(null);
+ mStateController.removeComplication(mPreviewComplication);
+ mStateController.setPreviewMode(false);
super.onDestroy();
}
@@ -133,6 +140,11 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ
setCurrentState(Lifecycle.State.STARTED);
mExecutor.execute(() -> {
mStateController.setShouldShowComplications(shouldShowComplications());
+ mStateController.setPreviewMode(isPreviewMode());
+ if (isPreviewMode()) {
+ mPreviewComplication.setDreamLabel(getDreamLabel());
+ mStateController.addComplication(mPreviewComplication);
+ }
addOverlayWindowLocked(layoutParams);
setCurrentState(Lifecycle.State.RESUMED);
mStateController.setOverlayActive(true);
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java
index fc71e2fb2329..6860998680cb 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java
@@ -50,6 +50,7 @@ public class DreamOverlayStateController implements
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
public static final int STATE_DREAM_OVERLAY_ACTIVE = 1 << 0;
+ public static final int STATE_PREVIEW_MODE = 1 << 1;
private static final int OP_CLEAR_STATE = 1;
private static final int OP_SET_STATE = 2;
@@ -249,4 +250,18 @@ public class DreamOverlayStateController implements
mCallbacks.forEach(Callback::onAvailableComplicationTypesChanged);
});
}
+
+ /**
+ * Sets whether the dream is running in preview mode.
+ */
+ public void setPreviewMode(boolean isPreviewMode) {
+ modifyState(isPreviewMode ? OP_SET_STATE : OP_CLEAR_STATE, STATE_PREVIEW_MODE);
+ }
+
+ /**
+ * Returns whether the dream is running in preview mode.
+ */
+ public boolean isPreviewMode() {
+ return containsState(STATE_PREVIEW_MODE);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarView.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarView.java
index 9847ef633bc1..2d969206b468 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarView.java
@@ -25,17 +25,13 @@ import androidx.constraintlayout.widget.ConstraintLayout;
import com.android.internal.util.Preconditions;
import com.android.systemui.R;
-import com.android.systemui.battery.BatteryMeterView;
-import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback;
/**
* {@link DreamOverlayStatusBarView} is the view responsible for displaying the status bar in a
- * dream. The status bar includes status icons such as battery and wifi.
+ * dream. The status bar displays conditional status icons such as "priority mode" and "no wifi".
*/
-public class DreamOverlayStatusBarView extends ConstraintLayout implements
- BatteryStateChangeCallback {
+public class DreamOverlayStatusBarView extends ConstraintLayout {
- private BatteryMeterView mBatteryView;
private ImageView mWifiStatusView;
public DreamOverlayStatusBarView(Context context) {
@@ -59,20 +55,8 @@ public class DreamOverlayStatusBarView extends ConstraintLayout implements
protected void onFinishInflate() {
super.onFinishInflate();
- mBatteryView = Preconditions.checkNotNull(findViewById(R.id.dream_overlay_battery),
- "R.id.dream_overlay_battery must not be null");
mWifiStatusView = Preconditions.checkNotNull(findViewById(R.id.dream_overlay_wifi_status),
"R.id.dream_overlay_wifi_status must not be null");
-
- mWifiStatusView.setImageDrawable(getContext().getDrawable(R.drawable.ic_signal_wifi_off));
- }
-
- /**
- * Whether to show the battery percent text next to the battery status icons.
- * @param show True if the battery percent text should be shown.
- */
- void showBatteryPercentText(boolean show) {
- mBatteryView.setForceShowPercent(show);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java
index 5674b9f3f9fd..32b2309ee83b 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java
@@ -17,24 +17,20 @@
package com.android.systemui.dreams;
import android.annotation.IntDef;
-import android.content.Context;
import android.net.ConnectivityManager;
import android.net.ConnectivityManager.NetworkCallback;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
-import com.android.systemui.battery.BatteryMeterViewController;
import com.android.systemui.dreams.dagger.DreamOverlayComponent;
-import com.android.systemui.dreams.dagger.DreamOverlayModule;
-import com.android.systemui.statusbar.policy.BatteryController;
+import com.android.systemui.touch.TouchInsetManager;
import com.android.systemui.util.ViewController;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import javax.inject.Inject;
-import javax.inject.Named;
/**
* View controller for {@link DreamOverlayStatusBarView}.
@@ -52,21 +48,8 @@ public class DreamOverlayStatusBarViewController extends ViewController<DreamOve
private static final int WIFI_STATUS_UNAVAILABLE = 1;
private static final int WIFI_STATUS_AVAILABLE = 2;
- @Retention(RetentionPolicy.SOURCE)
- @IntDef(prefix = { "BATTERY_STATUS_" }, value = {
- BATTERY_STATUS_UNKNOWN,
- BATTERY_STATUS_NOT_CHARGING,
- BATTERY_STATUS_CHARGING
- })
- private @interface BatteryStatus {}
- private static final int BATTERY_STATUS_UNKNOWN = 0;
- private static final int BATTERY_STATUS_NOT_CHARGING = 1;
- private static final int BATTERY_STATUS_CHARGING = 2;
-
- private final BatteryController mBatteryController;
- private final BatteryMeterViewController mBatteryMeterViewController;
private final ConnectivityManager mConnectivityManager;
- private final boolean mShowPercentAvailable;
+ private final TouchInsetManager.TouchInsetSession mTouchInsetSession;
private final NetworkRequest mNetworkRequest = new NetworkRequest.Builder()
.clearCapabilities()
@@ -91,43 +74,20 @@ public class DreamOverlayStatusBarViewController extends ViewController<DreamOve
}
};
- private final BatteryController.BatteryStateChangeCallback mBatteryStateChangeCallback =
- new BatteryController.BatteryStateChangeCallback() {
- @Override
- public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) {
- DreamOverlayStatusBarViewController.this.onBatteryLevelChanged(charging);
- }
- };
-
private @WifiStatus int mWifiStatus = WIFI_STATUS_UNKNOWN;
- private @BatteryStatus int mBatteryStatus = BATTERY_STATUS_UNKNOWN;
@Inject
public DreamOverlayStatusBarViewController(
- Context context,
DreamOverlayStatusBarView view,
- BatteryController batteryController,
- @Named(DreamOverlayModule.DREAM_OVERLAY_BATTERY_CONTROLLER)
- BatteryMeterViewController batteryMeterViewController,
- ConnectivityManager connectivityManager) {
+ ConnectivityManager connectivityManager,
+ TouchInsetManager.TouchInsetSession touchInsetSession) {
super(view);
- mBatteryController = batteryController;
- mBatteryMeterViewController = batteryMeterViewController;
mConnectivityManager = connectivityManager;
-
- mShowPercentAvailable = context.getResources().getBoolean(
- com.android.internal.R.bool.config_battery_percentage_setting_available);
- }
-
- @Override
- protected void onInit() {
- super.onInit();
- mBatteryMeterViewController.init();
+ mTouchInsetSession = touchInsetSession;
}
@Override
protected void onViewAttached() {
- mBatteryController.addCallback(mBatteryStateChangeCallback);
mConnectivityManager.registerNetworkCallback(mNetworkRequest, mNetworkCallback);
NetworkCapabilities capabilities =
@@ -136,12 +96,13 @@ public class DreamOverlayStatusBarViewController extends ViewController<DreamOve
onWifiAvailabilityChanged(
capabilities != null
&& capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI));
+ mTouchInsetSession.addViewToTracking(mView);
}
@Override
protected void onViewDetached() {
- mBatteryController.removeCallback(mBatteryStateChangeCallback);
mConnectivityManager.unregisterNetworkCallback(mNetworkCallback);
+ mTouchInsetSession.clear();
}
/**
@@ -155,18 +116,4 @@ public class DreamOverlayStatusBarViewController extends ViewController<DreamOve
mView.showWifiStatus(mWifiStatus == WIFI_STATUS_UNAVAILABLE);
}
}
-
- /**
- * The battery level has changed. Update the battery status icon as appropriate.
- * @param charging Whether the battery is currently charging.
- */
- private void onBatteryLevelChanged(boolean charging) {
- final int newBatteryStatus =
- charging ? BATTERY_STATUS_CHARGING : BATTERY_STATUS_NOT_CHARGING;
- if (mBatteryStatus != newBatteryStatus) {
- mBatteryStatus = newBatteryStatus;
- mView.showBatteryPercentText(
- mBatteryStatus == BATTERY_STATUS_CHARGING && mShowPercentAvailable);
- }
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/SmartSpaceComplication.java b/packages/SystemUI/src/com/android/systemui/dreams/SmartSpaceComplication.java
index 240389a988ae..a5dcd39264df 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/SmartSpaceComplication.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/SmartSpaceComplication.java
@@ -59,7 +59,19 @@ public class SmartSpaceComplication implements Complication {
@Override
public void start() {
- if (mSmartSpaceController.isEnabled()) {
+ addOrRemoveOverlay();
+ mDreamOverlayStateController.addCallback(new DreamOverlayStateController.Callback() {
+ @Override
+ public void onStateChanged() {
+ addOrRemoveOverlay();
+ }
+ });
+ }
+
+ private void addOrRemoveOverlay() {
+ if (mDreamOverlayStateController.isPreviewMode()) {
+ mDreamOverlayStateController.removeComplication(mComplication);
+ } else if (mSmartSpaceController.isEnabled()) {
mDreamOverlayStateController.addComplication(mComplication);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutEngine.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutEngine.java
index 0b80d8a87879..437e799106c5 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutEngine.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutEngine.java
@@ -27,6 +27,7 @@ import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.constraintlayout.widget.Constraints;
import com.android.systemui.R;
+import com.android.systemui.touch.TouchInsetManager;
import java.util.ArrayList;
import java.util.Collections;
@@ -52,6 +53,7 @@ public class ComplicationLayoutEngine {
private static class ViewEntry implements Comparable<ViewEntry> {
private final View mView;
private final ComplicationLayoutParams mLayoutParams;
+ private final TouchInsetManager.TouchInsetSession mTouchInsetSession;
private final Parent mParent;
@Complication.Category
private final int mCategory;
@@ -61,7 +63,8 @@ public class ComplicationLayoutEngine {
* Default constructor. {@link Parent} allows for the {@link ViewEntry}'s surrounding
* view hierarchy to be accessed without traversing the entire view tree.
*/
- ViewEntry(View view, ComplicationLayoutParams layoutParams, int category, Parent parent,
+ ViewEntry(View view, ComplicationLayoutParams layoutParams,
+ TouchInsetManager.TouchInsetSession touchSession, int category, Parent parent,
int margin) {
mView = view;
// Views that are generated programmatically do not have a unique id assigned to them
@@ -70,9 +73,12 @@ public class ComplicationLayoutEngine {
// {@link Complication.ViewHolder} should not reference the root container by id.
mView.setId(View.generateViewId());
mLayoutParams = layoutParams;
+ mTouchInsetSession = touchSession;
mCategory = category;
mParent = parent;
mMargin = margin;
+
+ touchSession.addViewToTracking(mView);
}
/**
@@ -217,6 +223,7 @@ public class ComplicationLayoutEngine {
mParent.removeEntry(this);
((ViewGroup) mView.getParent()).removeView(mView);
+ mTouchInsetSession.removeViewFromTracking(mView);
}
@Override
@@ -242,15 +249,18 @@ public class ComplicationLayoutEngine {
*/
private static class Builder {
private final View mView;
+ private final TouchInsetManager.TouchInsetSession mTouchSession;
private final ComplicationLayoutParams mLayoutParams;
private final int mCategory;
private Parent mParent;
private int mMargin;
- Builder(View view, ComplicationLayoutParams lp, @Complication.Category int category) {
+ Builder(View view, TouchInsetManager.TouchInsetSession touchSession,
+ ComplicationLayoutParams lp, @Complication.Category int category) {
mView = view;
mLayoutParams = lp;
mCategory = category;
+ mTouchSession = touchSession;
}
/**
@@ -291,7 +301,8 @@ public class ComplicationLayoutEngine {
* Builds and returns the resulting {@link ViewEntry}.
*/
ViewEntry build() {
- return new ViewEntry(mView, mLayoutParams, mCategory, mParent, mMargin);
+ return new ViewEntry(mView, mLayoutParams, mTouchSession, mCategory, mParent,
+ mMargin);
}
}
@@ -442,13 +453,16 @@ public class ComplicationLayoutEngine {
private final int mMargin;
private final HashMap<ComplicationId, ViewEntry> mEntries = new HashMap<>();
private final HashMap<Integer, PositionGroup> mPositions = new HashMap<>();
+ private final TouchInsetManager.TouchInsetSession mSession;
/** */
@Inject
public ComplicationLayoutEngine(@Named(SCOPED_COMPLICATIONS_LAYOUT) ConstraintLayout layout,
- @Named(COMPLICATION_MARGIN) int margin) {
+ @Named(COMPLICATION_MARGIN) int margin,
+ TouchInsetManager.TouchInsetSession session) {
mLayout = layout;
mMargin = margin;
+ mSession = session;
}
/**
@@ -468,7 +482,7 @@ public class ComplicationLayoutEngine {
removeComplication(id);
}
- final ViewEntry.Builder entryBuilder = new ViewEntry.Builder(view, lp, category)
+ final ViewEntry.Builder entryBuilder = new ViewEntry.Builder(view, mSession, lp, category)
.setMargin(mMargin);
// Add position group if doesn't already exist
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamPreviewComplication.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamPreviewComplication.java
new file mode 100644
index 000000000000..23343b17232f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamPreviewComplication.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.dreams.complication;
+
+import static com.android.systemui.dreams.complication.dagger.DreamPreviewComplicationComponent.DREAM_LABEL;
+import static com.android.systemui.dreams.complication.dagger.DreamPreviewComplicationComponent.DreamPreviewComplicationModule.DREAM_PREVIEW_COMPLICATION_LAYOUT_PARAMS;
+import static com.android.systemui.dreams.complication.dagger.DreamPreviewComplicationComponent.DreamPreviewComplicationModule.DREAM_PREVIEW_COMPLICATION_VIEW;
+
+import android.text.TextUtils;
+import android.view.View;
+import android.widget.TextView;
+
+import androidx.annotation.Nullable;
+
+import com.android.systemui.dreams.complication.dagger.DreamPreviewComplicationComponent;
+import com.android.systemui.util.ViewController;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+
+/**
+ * Preview complication shown when user is previewing a dream.
+ */
+public class DreamPreviewComplication implements Complication {
+ DreamPreviewComplicationComponent.Factory mComponentFactory;
+ @Nullable
+ private CharSequence mDreamLabel;
+
+ /**
+ * Default constructor for {@link DreamPreviewComplication}.
+ */
+ @Inject
+ public DreamPreviewComplication(
+ DreamPreviewComplicationComponent.Factory componentFactory) {
+ mComponentFactory = componentFactory;
+ }
+
+ /**
+ * Create {@link DreamPreviewViewHolder}.
+ */
+ @Override
+ public ViewHolder createView(ComplicationViewModel model) {
+ return mComponentFactory.create(model, mDreamLabel).getViewHolder();
+ }
+
+ /**
+ * Sets the user-facing label for the current dream.
+ */
+ public void setDreamLabel(@Nullable CharSequence dreamLabel) {
+ mDreamLabel = dreamLabel;
+ }
+
+ /**
+ * ViewHolder to contain value/logic associated with a Preview Complication View.
+ */
+ public static class DreamPreviewViewHolder implements ViewHolder {
+ private final TextView mView;
+ private final ComplicationLayoutParams mLayoutParams;
+ private final DreamPreviewViewController mViewController;
+
+ @Inject
+ DreamPreviewViewHolder(@Named(DREAM_PREVIEW_COMPLICATION_VIEW) TextView view,
+ DreamPreviewViewController controller,
+ @Named(DREAM_PREVIEW_COMPLICATION_LAYOUT_PARAMS)
+ ComplicationLayoutParams layoutParams,
+ @Named(DREAM_LABEL) @Nullable CharSequence dreamLabel) {
+ mView = view;
+ mLayoutParams = layoutParams;
+ mViewController = controller;
+ mViewController.init();
+
+ if (!TextUtils.isEmpty(dreamLabel)) {
+ mView.setText(dreamLabel);
+ }
+ }
+
+ @Override
+ public View getView() {
+ return mView;
+ }
+
+ @Override
+ public ComplicationLayoutParams getLayoutParams() {
+ return mLayoutParams;
+ }
+
+ @Override
+ public int getCategory() {
+ return CATEGORY_SYSTEM;
+ }
+ }
+
+ /**
+ * ViewController to contain value/logic associated with a Preview Complication View.
+ */
+ static class DreamPreviewViewController extends ViewController<TextView> {
+ private final ComplicationViewModel mViewModel;
+
+ @Inject
+ DreamPreviewViewController(@Named(DREAM_PREVIEW_COMPLICATION_VIEW) TextView view,
+ ComplicationViewModel viewModel) {
+ super(view);
+ mViewModel = viewModel;
+ }
+
+ @Override
+ protected void onViewAttached() {
+ mView.setOnClickListener(v -> mViewModel.exitDream());
+ }
+
+ @Override
+ protected void onViewDetached() {
+ mView.setOnClickListener(null);
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamPreviewComplicationComponent.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamPreviewComplicationComponent.java
new file mode 100644
index 000000000000..502e31ed0c7f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamPreviewComplicationComponent.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.dreams.complication.dagger;
+
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import android.view.LayoutInflater;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import androidx.annotation.Nullable;
+
+import com.android.internal.util.Preconditions;
+import com.android.systemui.R;
+import com.android.systemui.dreams.complication.ComplicationLayoutParams;
+import com.android.systemui.dreams.complication.ComplicationViewModel;
+import com.android.systemui.dreams.complication.DreamPreviewComplication.DreamPreviewViewHolder;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+
+import javax.inject.Named;
+import javax.inject.Scope;
+
+import dagger.BindsInstance;
+import dagger.Module;
+import dagger.Provides;
+import dagger.Subcomponent;
+
+/**
+ * {@link DreamPreviewComplicationComponent} is responsible for generating dependencies
+ * surrounding the
+ * Preview {@link com.android.systemui.dreams.complication.Complication}, such as the layout
+ * details.
+ */
+@Subcomponent(modules = {
+ DreamPreviewComplicationComponent.DreamPreviewComplicationModule.class,
+})
+@DreamPreviewComplicationComponent.DreamPreviewComplicationScope
+public interface DreamPreviewComplicationComponent {
+ String DREAM_LABEL = "dream_label";
+
+ /**
+ * Creates {@link DreamPreviewViewHolder}.
+ */
+ DreamPreviewViewHolder getViewHolder();
+
+ @Documented
+ @Retention(RUNTIME)
+ @Scope
+ @interface DreamPreviewComplicationScope {
+ }
+
+ /**
+ * Generates {@link DreamPreviewComplicationComponent}.
+ */
+ @Subcomponent.Factory
+ interface Factory {
+ DreamPreviewComplicationComponent create(
+ @BindsInstance ComplicationViewModel viewModel,
+ @Named(DREAM_LABEL) @BindsInstance @Nullable CharSequence dreamLabel);
+ }
+
+ /**
+ * Scoped values for {@link DreamPreviewComplicationComponent}.
+ */
+ @Module
+ interface DreamPreviewComplicationModule {
+ String DREAM_PREVIEW_COMPLICATION_VIEW = "preview_complication_view";
+ String DREAM_PREVIEW_COMPLICATION_LAYOUT_PARAMS = "preview_complication_layout_params";
+ // Order weight of insert into parent container
+ int INSERT_ORDER_WEIGHT = 1000;
+
+ /**
+ * Provides the complication view.
+ */
+ @Provides
+ @DreamPreviewComplicationScope
+ @Named(DREAM_PREVIEW_COMPLICATION_VIEW)
+ static TextView provideComplicationView(LayoutInflater layoutInflater) {
+ return Preconditions.checkNotNull((TextView)
+ layoutInflater.inflate(R.layout.dream_overlay_complication_preview,
+ null, false),
+ "R.layout.dream_overlay_complication_preview did not properly inflated");
+ }
+
+ /**
+ * Provides the layout parameters for the complication view.
+ */
+ @Provides
+ @DreamPreviewComplicationScope
+ @Named(DREAM_PREVIEW_COMPLICATION_LAYOUT_PARAMS)
+ static ComplicationLayoutParams provideLayoutParams() {
+ return new ComplicationLayoutParams(0,
+ ViewGroup.LayoutParams.WRAP_CONTENT,
+ ComplicationLayoutParams.POSITION_TOP
+ | ComplicationLayoutParams.POSITION_START,
+ ComplicationLayoutParams.DIRECTION_DOWN,
+ INSERT_ORDER_WEIGHT, /* snapToGuide= */ true);
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java
index c61f7968c595..65f060b43c08 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java
@@ -19,6 +19,7 @@ package com.android.systemui.dreams.dagger;
import android.content.Context;
import com.android.settingslib.dream.DreamBackend;
+import com.android.systemui.dreams.complication.dagger.DreamPreviewComplicationComponent;
import com.android.systemui.dreams.complication.dagger.RegisteredComplicationsModule;
import com.android.systemui.dreams.touch.dagger.DreamTouchModule;
@@ -34,6 +35,7 @@ import dagger.Provides;
},
subcomponents = {
DreamOverlayComponent.class,
+ DreamPreviewComplicationComponent.class,
})
public interface DreamModule {
/**
@@ -43,4 +45,4 @@ public interface DreamModule {
static DreamBackend providesDreamBackend(Context context) {
return DreamBackend.getInstance(context);
}
-} \ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java
index 4eb5cb97607a..63676d629cb9 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java
@@ -16,9 +16,7 @@
package com.android.systemui.dreams.dagger;
-import android.content.ContentResolver;
import android.content.res.Resources;
-import android.os.Handler;
import android.view.LayoutInflater;
import android.view.ViewGroup;
@@ -28,15 +26,12 @@ import androidx.lifecycle.LifecycleRegistry;
import com.android.internal.util.Preconditions;
import com.android.systemui.R;
-import com.android.systemui.battery.BatteryMeterView;
-import com.android.systemui.battery.BatteryMeterViewController;
-import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dreams.DreamOverlayContainerView;
import com.android.systemui.dreams.DreamOverlayStatusBarView;
-import com.android.systemui.statusbar.policy.BatteryController;
-import com.android.systemui.statusbar.policy.ConfigurationController;
-import com.android.systemui.tuner.TunerService;
+import com.android.systemui.touch.TouchInsetManager;
+
+import java.util.concurrent.Executor;
import javax.inject.Named;
@@ -47,9 +42,6 @@ import dagger.Provides;
/** Dagger module for {@link DreamOverlayComponent}. */
@Module
public abstract class DreamOverlayModule {
- private static final String DREAM_OVERLAY_BATTERY_VIEW = "dream_overlay_battery_view";
- public static final String DREAM_OVERLAY_BATTERY_CONTROLLER =
- "dream_overlay_battery_controller";
public static final String DREAM_OVERLAY_CONTENT_VIEW = "dream_overlay_content_view";
public static final String MAX_BURN_IN_OFFSET = "max_burn_in_offset";
public static final String BURN_IN_PROTECTION_UPDATE_INTERVAL =
@@ -76,42 +68,26 @@ public abstract class DreamOverlayModule {
/** */
@Provides
- @DreamOverlayComponent.DreamOverlayScope
- public static DreamOverlayStatusBarView providesDreamOverlayStatusBarView(
- DreamOverlayContainerView view) {
- return Preconditions.checkNotNull(view.findViewById(R.id.dream_overlay_status_bar),
- "R.id.status_bar must not be null");
+ public static TouchInsetManager.TouchInsetSession providesTouchInsetSession(
+ TouchInsetManager manager) {
+ return manager.createSession();
}
/** */
@Provides
@DreamOverlayComponent.DreamOverlayScope
- @Named(DREAM_OVERLAY_BATTERY_VIEW)
- static BatteryMeterView providesBatteryMeterView(DreamOverlayContainerView view) {
- return Preconditions.checkNotNull(view.findViewById(R.id.dream_overlay_battery),
- "R.id.battery must not be null");
+ public static TouchInsetManager providesTouchInsetManager(@Main Executor executor,
+ DreamOverlayContainerView view) {
+ return new TouchInsetManager(executor, view);
}
/** */
@Provides
@DreamOverlayComponent.DreamOverlayScope
- @Named(DREAM_OVERLAY_BATTERY_CONTROLLER)
- static BatteryMeterViewController providesBatteryMeterViewController(
- @Named(DREAM_OVERLAY_BATTERY_VIEW) BatteryMeterView batteryMeterView,
- ConfigurationController configurationController,
- TunerService tunerService,
- BroadcastDispatcher broadcastDispatcher,
- @Main Handler mainHandler,
- ContentResolver contentResolver,
- BatteryController batteryController) {
- return new BatteryMeterViewController(
- batteryMeterView,
- configurationController,
- tunerService,
- broadcastDispatcher,
- mainHandler,
- contentResolver,
- batteryController);
+ public static DreamOverlayStatusBarView providesDreamOverlayStatusBarView(
+ DreamOverlayContainerView view) {
+ return Preconditions.checkNotNull(view.findViewById(R.id.dream_overlay_status_bar),
+ "R.id.status_bar must not be null");
}
/** */
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.kt b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.kt
index 96a90dfc7fe9..9d6e3c295100 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.kt
@@ -28,6 +28,9 @@ interface FeatureFlags : FlagListenable {
/** Returns a boolean value for the given flag. */
fun isEnabled(flag: ResourceBooleanFlag): Boolean
+ /** Returns a boolean value for the given flag. */
+ fun isEnabled(flag: SysPropBooleanFlag): Boolean
+
/** Returns a string value for the given flag. */
fun getString(flag: StringFlag): String
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebug.java b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebug.java
index df605990fa81..677990437f3d 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebug.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebug.java
@@ -49,6 +49,7 @@ import java.util.ArrayList;
import java.util.Map;
import java.util.Objects;
import java.util.TreeMap;
+import java.util.function.Consumer;
import java.util.function.Supplier;
import javax.inject.Inject;
@@ -69,6 +70,7 @@ public class FeatureFlagsDebug implements FeatureFlags, Dumpable {
private final FlagManager mFlagManager;
private final SecureSettings mSecureSettings;
private final Resources mResources;
+ private final SystemPropertiesHelper mSystemProperties;
private final Supplier<Map<Integer, Flag<?>>> mFlagsCollector;
private final Map<Integer, Boolean> mBooleanFlagCache = new TreeMap<>();
private final Map<Integer, String> mStringFlagCache = new TreeMap<>();
@@ -79,6 +81,7 @@ public class FeatureFlagsDebug implements FeatureFlags, Dumpable {
FlagManager flagManager,
Context context,
SecureSettings secureSettings,
+ SystemPropertiesHelper systemProperties,
@Main Resources resources,
DumpManager dumpManager,
@Nullable Supplier<Map<Integer, Flag<?>>> flagsCollector,
@@ -86,11 +89,12 @@ public class FeatureFlagsDebug implements FeatureFlags, Dumpable {
mFlagManager = flagManager;
mSecureSettings = secureSettings;
mResources = resources;
+ mSystemProperties = systemProperties;
mFlagsCollector = flagsCollector != null ? flagsCollector : Flags::collectFlags;
IntentFilter filter = new IntentFilter();
filter.addAction(ACTION_SET_FLAG);
filter.addAction(ACTION_GET_FLAGS);
- flagManager.setRestartAction(this::restartSystemUI);
+ flagManager.setOnSettingsChangedAction(this::restartSystemUI);
flagManager.setClearCacheAction(this::removeFromCache);
context.registerReceiver(mReceiver, filter, null, null,
Context.RECEIVER_EXPORTED_UNAUDITED);
@@ -121,6 +125,17 @@ public class FeatureFlagsDebug implements FeatureFlags, Dumpable {
return mBooleanFlagCache.get(id);
}
+ @Override
+ public boolean isEnabled(@NonNull SysPropBooleanFlag flag) {
+ int id = flag.getId();
+ if (!mBooleanFlagCache.containsKey(id)) {
+ mBooleanFlagCache.put(
+ id, mSystemProperties.getBoolean(flag.getName(), flag.getDefault()));
+ }
+
+ return mBooleanFlagCache.get(id);
+ }
+
@NonNull
@Override
public String getString(@NonNull StringFlag flag) {
@@ -180,16 +195,28 @@ public class FeatureFlagsDebug implements FeatureFlags, Dumpable {
mSecureSettings.putString(mFlagManager.idToSettingsKey(id), data);
Log.i(TAG, "Set id " + id + " to " + value);
removeFromCache(id);
- mFlagManager.dispatchListenersAndMaybeRestart(id);
+ mFlagManager.dispatchListenersAndMaybeRestart(id, this::restartSystemUI);
+ }
+
+ private <T> void eraseFlag(Flag<T> flag) {
+ if (flag instanceof SysPropFlag) {
+ mSystemProperties.erase(((SysPropFlag<T>) flag).getName());
+ dispatchListenersAndMaybeRestart(flag.getId(), this::restartAndroid);
+ } else {
+ eraseFlag(flag.getId());
+ }
}
/** Erase a flag's overridden value if there is one. */
- public void eraseFlag(int id) {
+ private void eraseFlag(int id) {
eraseInternal(id);
removeFromCache(id);
- mFlagManager.dispatchListenersAndMaybeRestart(id);
+ dispatchListenersAndMaybeRestart(id, this::restartSystemUI);
}
+ private void dispatchListenersAndMaybeRestart(int id, Consumer<Boolean> restartAction) {
+ mFlagManager.dispatchListenersAndMaybeRestart(id, restartAction);
+ }
/** Works just like {@link #eraseFlag(int)} except that it doesn't restart SystemUI. */
private void eraseInternal(int id) {
// We can't actually "erase" things from sysprops, but we can set them to empty!
@@ -217,7 +244,11 @@ public class FeatureFlagsDebug implements FeatureFlags, Dumpable {
System.exit(0);
}
- private void restartAndroid() {
+ private void restartAndroid(boolean requestSuppress) {
+ if (requestSuppress) {
+ Log.i(TAG, "Android Restart Suppressed");
+ return;
+ }
Log.i(TAG, "Restarting Android");
try {
mBarService.restart();
@@ -273,7 +304,7 @@ public class FeatureFlagsDebug implements FeatureFlags, Dumpable {
Flag<?> flag = flagMap.get(id);
if (!extras.containsKey(EXTRA_VALUE)) {
- eraseFlag(id);
+ eraseFlag(flag);
return;
}
@@ -282,6 +313,10 @@ public class FeatureFlagsDebug implements FeatureFlags, Dumpable {
setFlagValue(id, (Boolean) value, BooleanFlagSerializer.INSTANCE);
} else if (flag instanceof ResourceBooleanFlag && value instanceof Boolean) {
setFlagValue(id, (Boolean) value, BooleanFlagSerializer.INSTANCE);
+ } else if (flag instanceof SysPropBooleanFlag && value instanceof Boolean) {
+ // Store SysProp flags in SystemProperties where they can read by outside parties.
+ mSystemProperties.setBoolean(
+ ((SysPropBooleanFlag) flag).getName(), (Boolean) value);
} else if (flag instanceof StringFlag && value instanceof String) {
setFlagValue(id, (String) value, StringFlagSerializer.INSTANCE);
} else if (flag instanceof ResourceStringFlag && value instanceof String) {
@@ -306,6 +341,9 @@ public class FeatureFlagsDebug implements FeatureFlags, Dumpable {
if (f instanceof ResourceBooleanFlag) {
return new BooleanFlag(f.getId(), isEnabled((ResourceBooleanFlag) f));
}
+ if (f instanceof SysPropBooleanFlag) {
+ return new BooleanFlag(f.getId(), isEnabled((SysPropBooleanFlag) f));
+ }
// TODO: add support for other flag types.
Log.w(TAG, "Unsupported Flag Type. Please file a bug.");
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsRelease.java b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsRelease.java
index 348a8e20122e..1fb1acfae1cb 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsRelease.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsRelease.java
@@ -43,11 +43,17 @@ import javax.inject.Inject;
@SysUISingleton
public class FeatureFlagsRelease implements FeatureFlags, Dumpable {
private final Resources mResources;
+ private final SystemPropertiesHelper mSystemProperties;
SparseBooleanArray mBooleanCache = new SparseBooleanArray();
SparseArray<String> mStringCache = new SparseArray<>();
+
@Inject
- public FeatureFlagsRelease(@Main Resources resources, DumpManager dumpManager) {
+ public FeatureFlagsRelease(
+ @Main Resources resources,
+ SystemPropertiesHelper systemProperties,
+ DumpManager dumpManager) {
mResources = resources;
+ mSystemProperties = systemProperties;
dumpManager.registerDumpable("SysUIFlags", this);
}
@@ -72,6 +78,17 @@ public class FeatureFlagsRelease implements FeatureFlags, Dumpable {
return mBooleanCache.valueAt(cacheIndex);
}
+ @Override
+ public boolean isEnabled(SysPropBooleanFlag flag) {
+ int cacheIndex = mBooleanCache.indexOfKey(flag.getId());
+ if (cacheIndex < 0) {
+ return isEnabled(
+ flag.getId(), mSystemProperties.getBoolean(flag.getName(), flag.getDefault()));
+ }
+
+ return mBooleanCache.valueAt(cacheIndex);
+ }
+
private boolean isEnabled(int key, boolean defaultValue) {
mBooleanCache.append(key, defaultValue);
return defaultValue;
diff --git a/packages/SystemUI/src/com/android/systemui/flags/SystemPropertiesHelper.kt b/packages/SystemUI/src/com/android/systemui/flags/SystemPropertiesHelper.kt
index 1dc5a9f3adc5..6c160975587b 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/SystemPropertiesHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/SystemPropertiesHelper.kt
@@ -34,6 +34,10 @@ open class SystemPropertiesHelper @Inject constructor() {
return SystemProperties.getBoolean(name, default)
}
+ fun setBoolean(name: String, value: Boolean) {
+ SystemProperties.set(name, if (value) "1" else "0")
+ }
+
fun set(name: String, value: String) {
SystemProperties.set(name, value)
}
@@ -41,4 +45,8 @@ open class SystemPropertiesHelper @Inject constructor() {
fun set(name: String, value: Int) {
set(name, value.toString())
}
+
+ fun erase(name: String) {
+ set(name, "")
+ }
} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
index 84fa6a6772fe..e3886cd80a42 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
@@ -27,7 +27,6 @@ import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_2BUTTON;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN;
-import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_GLOBAL_ACTIONS_SHOWING;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -117,7 +116,6 @@ import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.model.SysUiState;
import com.android.systemui.plugins.GlobalActions.GlobalActionsManager;
import com.android.systemui.plugins.GlobalActionsPanelPlugin;
import com.android.systemui.scrim.ScrimDrawable;
@@ -125,7 +123,6 @@ import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.SystemUIDialog;
-import com.android.systemui.statusbar.phone.SystemUIDialogManager;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.telephony.TelephonyListenerManager;
@@ -200,7 +197,6 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
private final TelecomManager mTelecomManager;
private final MetricsLogger mMetricsLogger;
private final UiEventLogger mUiEventLogger;
- private final SysUiState mSysUiState;
// Used for RingerModeTracker
private final LifecycleRegistry mLifecycle = new LifecycleRegistry(this);
@@ -241,7 +237,6 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
protected Handler mMainHandler;
private int mSmallestScreenWidthDp;
private final Optional<StatusBar> mStatusBarOptional;
- private final SystemUIDialogManager mDialogManager;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private final DialogLaunchAnimator mDialogLaunchAnimator;
@@ -347,13 +342,11 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
@Background Executor backgroundExecutor,
UiEventLogger uiEventLogger,
RingerModeTracker ringerModeTracker,
- SysUiState sysUiState,
@Main Handler handler,
PackageManager packageManager,
Optional<StatusBar> statusBarOptional,
KeyguardUpdateMonitor keyguardUpdateMonitor,
- DialogLaunchAnimator dialogLaunchAnimator,
- SystemUIDialogManager dialogManager) {
+ DialogLaunchAnimator dialogLaunchAnimator) {
mContext = context;
mWindowManagerFuncs = windowManagerFuncs;
mAudioManager = audioManager;
@@ -379,13 +372,11 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
mIWindowManager = iWindowManager;
mBackgroundExecutor = backgroundExecutor;
mRingerModeTracker = ringerModeTracker;
- mSysUiState = sysUiState;
mMainHandler = handler;
mSmallestScreenWidthDp = resources.getConfiguration().smallestScreenWidthDp;
mStatusBarOptional = statusBarOptional;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mDialogLaunchAnimator = dialogLaunchAnimator;
- mDialogManager = dialogManager;
// receive broadcasts
IntentFilter filter = new IntentFilter();
@@ -682,11 +673,10 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
ActionsDialogLite dialog = new ActionsDialogLite(mContext,
com.android.systemui.R.style.Theme_SystemUI_Dialog_GlobalActionsLite,
- mAdapter, mOverflowAdapter, mSysuiColorExtractor,
- mStatusBarService, mNotificationShadeWindowController,
- mSysUiState, this::onRefresh, mKeyguardShowing, mPowerAdapter, mUiEventLogger,
- mStatusBarOptional, mKeyguardUpdateMonitor, mLockPatternUtils,
- mDialogManager);
+ mAdapter, mOverflowAdapter, mSysuiColorExtractor, mStatusBarService,
+ mNotificationShadeWindowController, this::onRefresh, mKeyguardShowing,
+ mPowerAdapter, mUiEventLogger, mStatusBarOptional, mKeyguardUpdateMonitor,
+ mLockPatternUtils);
dialog.setOnDismissListener(this);
dialog.setOnShowListener(this);
@@ -2165,7 +2155,6 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
private boolean mKeyguardShowing;
protected float mScrimAlpha;
protected final NotificationShadeWindowController mNotificationShadeWindowController;
- protected final SysUiState mSysUiState;
private ListPopupWindow mOverflowPopup;
private Dialog mPowerOptionsDialog;
protected final Runnable mOnRefreshCallback;
@@ -2226,15 +2215,13 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
MyOverflowAdapter overflowAdapter,
SysuiColorExtractor sysuiColorExtractor, IStatusBarService statusBarService,
NotificationShadeWindowController notificationShadeWindowController,
- SysUiState sysuiState, Runnable onRefreshCallback, boolean keyguardShowing,
+ Runnable onRefreshCallback, boolean keyguardShowing,
MyPowerOptionsAdapter powerAdapter, UiEventLogger uiEventLogger,
- Optional<StatusBar> statusBarOptional,
- KeyguardUpdateMonitor keyguardUpdateMonitor, LockPatternUtils lockPatternUtils,
- SystemUIDialogManager systemUiDialogManager) {
+ Optional<StatusBar> statusBarOptional, KeyguardUpdateMonitor keyguardUpdateMonitor,
+ LockPatternUtils lockPatternUtils) {
// We set dismissOnDeviceLock to false because we have a custom broadcast receiver to
// dismiss this dialog when the device is locked.
- super(context, themeRes, false /* dismissOnDeviceLock */,
- systemUiDialogManager);
+ super(context, themeRes, false /* dismissOnDeviceLock */);
mContext = context;
mAdapter = adapter;
mOverflowAdapter = overflowAdapter;
@@ -2242,7 +2229,6 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
mColorExtractor = sysuiColorExtractor;
mStatusBarService = statusBarService;
mNotificationShadeWindowController = notificationShadeWindowController;
- mSysUiState = sysuiState;
mOnRefreshCallback = onRefreshCallback;
mKeyguardShowing = keyguardShowing;
mUiEventLogger = uiEventLogger;
@@ -2463,8 +2449,6 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
public void show() {
super.show();
mNotificationShadeWindowController.setRequestTopUi(true, TAG);
- mSysUiState.setFlag(SYSUI_STATE_GLOBAL_ACTIONS_SHOWING, true)
- .commitUpdate(mContext.getDisplayId());
// By default this dialog windowAnimationStyle is null, and therefore windowAnimations
// should be equal to 0 which means we need to animate the dialog in-window. If it's not
@@ -2563,9 +2547,6 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
dismissPowerOptions();
mNotificationShadeWindowController.setRequestTopUi(false, TAG);
- mSysUiState.setFlag(SYSUI_STATE_GLOBAL_ACTIONS_SHOWING, false)
- .commitUpdate(mContext.getDisplayId());
-
super.dismiss();
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
index eee39552bcc8..f4b6fbd0324f 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
@@ -795,6 +795,9 @@ class MediaHierarchyManager @Inject constructor(
@TransformationType
fun calculateTransformationType(): Int {
if (isTransitioningToFullShade) {
+ if (inSplitShade) {
+ return TRANSFORMATION_TYPE_TRANSITION
+ }
return TRANSFORMATION_TYPE_FADE
}
if (previousLocation == LOCATION_LOCKSCREEN && desiredLocation == LOCATION_QS ||
@@ -961,6 +964,7 @@ class MediaHierarchyManager @Inject constructor(
(qsExpansion > 0.0f || inSplitShade) && !onLockscreen -> LOCATION_QS
qsExpansion > 0.4f && onLockscreen -> LOCATION_QS
!hasActiveMedia -> LOCATION_QS
+ onLockscreen && isSplitShadeExpanding() -> LOCATION_QS
onLockscreen && isTransformingToFullShadeAndInQQS() -> LOCATION_QQS
onLockscreen && allowedOnLockscreen -> LOCATION_LOCKSCREEN
else -> LOCATION_QQS
@@ -986,6 +990,10 @@ class MediaHierarchyManager @Inject constructor(
return location
}
+ private fun isSplitShadeExpanding(): Boolean {
+ return inSplitShade && isTransitioningToFullShade
+ }
+
/**
* Are we currently transforming to the full shade and already in QQS
*/
@@ -993,6 +1001,10 @@ class MediaHierarchyManager @Inject constructor(
if (!isTransitioningToFullShade) {
return false
}
+ if (inSplitShade) {
+ // Split shade doesn't use QQS.
+ return false
+ }
return fullShadeTransitionProgress > 0.5f
}
@@ -1000,6 +1012,10 @@ class MediaHierarchyManager @Inject constructor(
* Is the current transformationType fading
*/
private fun isCurrentlyFading(): Boolean {
+ if (isSplitShadeExpanding()) {
+ // Split shade always uses transition instead of fade.
+ return false
+ }
if (isTransitioningToFullShade) {
return true
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
index 7bb5454112c4..04a324b87382 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
@@ -56,7 +56,6 @@ import androidx.recyclerview.widget.RecyclerView;
import com.android.systemui.R;
import com.android.systemui.statusbar.phone.SystemUIDialog;
-import com.android.systemui.statusbar.phone.SystemUIDialogManager;
/**
* Base dialog for media output UI
@@ -99,9 +98,8 @@ public abstract class MediaOutputBaseDialog extends SystemUIDialog implements
}
};
- public MediaOutputBaseDialog(Context context, MediaOutputController mediaOutputController,
- SystemUIDialogManager dialogManager) {
- super(context, R.style.Theme_SystemUI_Dialog_Media, dialogManager);
+ public MediaOutputBaseDialog(Context context, MediaOutputController mediaOutputController) {
+ super(context, R.style.Theme_SystemUI_Dialog_Media);
// Save the context that is wrapped with our theme.
mContext = getContext();
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
index 7bc0f5202c91..e929b5e21053 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
@@ -66,7 +66,6 @@ import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
import com.android.systemui.statusbar.phone.ShadeController;
-import com.android.systemui.statusbar.phone.SystemUIDialogManager;
import java.util.ArrayList;
import java.util.Collection;
@@ -91,7 +90,6 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback {
private final ShadeController mShadeController;
private final ActivityStarter mActivityStarter;
private final DialogLaunchAnimator mDialogLaunchAnimator;
- private final SystemUIDialogManager mDialogManager;
private final List<MediaDevice> mGroupMediaDevices = new CopyOnWriteArrayList<>();
private final boolean mAboveStatusbar;
private final boolean mVolumeAdjustmentForRemoteGroupSessions;
@@ -119,7 +117,7 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback {
boolean aboveStatusbar, MediaSessionManager mediaSessionManager, LocalBluetoothManager
lbm, ShadeController shadeController, ActivityStarter starter,
CommonNotifCollection notifCollection, UiEventLogger uiEventLogger,
- DialogLaunchAnimator dialogLaunchAnimator, SystemUIDialogManager dialogManager) {
+ DialogLaunchAnimator dialogLaunchAnimator) {
mContext = context;
mPackageName = packageName;
mMediaSessionManager = mediaSessionManager;
@@ -135,7 +133,6 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback {
mDialogLaunchAnimator = dialogLaunchAnimator;
mVolumeAdjustmentForRemoteGroupSessions = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_volumeAdjustmentForRemoteGroupSessions);
- mDialogManager = dialogManager;
mColorActiveItem = Utils.getColorStateListDefaultColor(mContext,
R.color.media_dialog_active_item_main_content);
mColorInactiveItem = Utils.getColorStateListDefaultColor(mContext,
@@ -610,10 +607,9 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback {
// We show the output group dialog from the output dialog.
MediaOutputController controller = new MediaOutputController(mContext, mPackageName,
mAboveStatusbar, mMediaSessionManager, mLocalBluetoothManager, mShadeController,
- mActivityStarter, mNotifCollection, mUiEventLogger, mDialogLaunchAnimator,
- mDialogManager);
+ mActivityStarter, mNotifCollection, mUiEventLogger, mDialogLaunchAnimator);
MediaOutputGroupDialog dialog = new MediaOutputGroupDialog(mContext, mAboveStatusbar,
- controller, mDialogManager);
+ controller);
mDialogLaunchAnimator.showFromView(dialog, mediaOutputDialog);
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java
index 4e9da55ffbcb..7696a1f63c01 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java
@@ -29,7 +29,6 @@ import com.android.internal.logging.UiEvent;
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.R;
import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.statusbar.phone.SystemUIDialogManager;
/**
* Dialog for media output transferring.
@@ -39,9 +38,8 @@ public class MediaOutputDialog extends MediaOutputBaseDialog {
final UiEventLogger mUiEventLogger;
MediaOutputDialog(Context context, boolean aboveStatusbar, MediaOutputController
- mediaOutputController, UiEventLogger uiEventLogger,
- SystemUIDialogManager dialogManager) {
- super(context, mediaOutputController, dialogManager);
+ mediaOutputController, UiEventLogger uiEventLogger) {
+ super(context, mediaOutputController);
mUiEventLogger = uiEventLogger;
mAdapter = new MediaOutputAdapter(mMediaOutputController, this);
if (!aboveStatusbar) {
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt
index e1e7fa3ebbe0..9e252ea1eddc 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt
@@ -25,7 +25,6 @@ import com.android.systemui.animation.DialogLaunchAnimator
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection
import com.android.systemui.statusbar.phone.ShadeController
-import com.android.systemui.statusbar.phone.SystemUIDialogManager
import javax.inject.Inject
/**
@@ -39,8 +38,7 @@ class MediaOutputDialogFactory @Inject constructor(
private val starter: ActivityStarter,
private val notifCollection: CommonNotifCollection,
private val uiEventLogger: UiEventLogger,
- private val dialogLaunchAnimator: DialogLaunchAnimator,
- private val dialogManager: SystemUIDialogManager
+ private val dialogLaunchAnimator: DialogLaunchAnimator
) {
companion object {
var mediaOutputDialog: MediaOutputDialog? = null
@@ -53,9 +51,8 @@ class MediaOutputDialogFactory @Inject constructor(
val controller = MediaOutputController(context, packageName, aboveStatusBar,
mediaSessionManager, lbm, shadeController, starter, notifCollection,
- uiEventLogger, dialogLaunchAnimator, dialogManager)
- val dialog = MediaOutputDialog(context, aboveStatusBar, controller, uiEventLogger,
- dialogManager)
+ uiEventLogger, dialogLaunchAnimator)
+ val dialog = MediaOutputDialog(context, aboveStatusBar, controller, uiEventLogger)
mediaOutputDialog = dialog
// Show the dialog.
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupDialog.java
index 9f752b92a85e..f1c66016a49a 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupDialog.java
@@ -25,7 +25,6 @@ import android.view.WindowManager;
import androidx.core.graphics.drawable.IconCompat;
import com.android.systemui.R;
-import com.android.systemui.statusbar.phone.SystemUIDialogManager;
/**
* Dialog for media output group.
@@ -34,8 +33,8 @@ import com.android.systemui.statusbar.phone.SystemUIDialogManager;
public class MediaOutputGroupDialog extends MediaOutputBaseDialog {
MediaOutputGroupDialog(Context context, boolean aboveStatusbar, MediaOutputController
- mediaOutputController, SystemUIDialogManager dialogManager) {
- super(context, mediaOutputController, dialogManager);
+ mediaOutputController) {
+ super(context, mediaOutputController);
mMediaOutputController.resetGroupMediaDevices();
mAdapter = new MediaOutputGroupAdapter(mMediaOutputController);
if (!aboveStatusbar) {
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index 8b39e5c24ded..a242df38b62f 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -440,7 +440,9 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
mHomeButtonLongPressDurationMs = Optional.of(
properties.getLong(HOME_BUTTON_LONG_PRESS_DURATION_MS, 0)
).filter(duration -> duration != 0);
- reconfigureHomeLongClick();
+ if (mNavigationBarView != null) {
+ reconfigureHomeLongClick();
+ }
}
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/qs/FooterActionsController.kt b/packages/SystemUI/src/com/android/systemui/qs/FooterActionsController.kt
index a262b8ad2e28..217210a5dafc 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/FooterActionsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/FooterActionsController.kt
@@ -165,6 +165,7 @@ internal class FooterActionsController @Inject constructor(
powerMenuLite.visibility = View.GONE
}
settingsButton.setOnClickListener(onClickListener)
+ multiUserSetting.isListening = true
if (featureFlags.isEnabled(Flags.NEW_FOOTER)) {
val securityFooter = securityFooterController.view as DualHeightHorizontalLinearLayout
securityFootersContainer?.addView(securityFooter)
@@ -215,6 +216,7 @@ internal class FooterActionsController @Inject constructor(
override fun onViewDetached() {
setListening(false)
+ multiUserSetting.isListening = false
}
fun setListening(listening: Boolean) {
@@ -222,7 +224,6 @@ internal class FooterActionsController @Inject constructor(
return
}
this.listening = listening
- multiUserSetting.isListening = listening
if (this.listening) {
userInfoController.addCallback(onUserInfoChangedListener)
updateView()
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index 3c7933f0d218..3ef72202a591 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -513,7 +513,8 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
mContainer.setExpansion(expansion);
final float translationScaleY = (mInSplitShade
? 1 : QSAnimator.SHORT_PARALLAX_AMOUNT) * (expansion - 1);
- boolean onKeyguardAndExpanded = isKeyguardState() && !mShowCollapsedOnKeyguard;
+ boolean onKeyguard = isKeyguardState();
+ boolean onKeyguardAndExpanded = onKeyguard && !mShowCollapsedOnKeyguard;
if (!mHeaderAnimating && !headerWillBeAnimating()) {
getView().setTranslationY(
onKeyguardAndExpanded
@@ -547,6 +548,7 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
mHeader.updateResources();
}
}
+ mQSPanelController.setIsOnKeyguard(onKeyguard);
mFooter.setExpansion(onKeyguardAndExpanded ? 1 : expansion);
mQSFooterActionController.setExpansion(onKeyguardAndExpanded ? 1 : expansion);
mQSPanelController.setRevealExpansion(expansion);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 5126fcb4c34d..b04d75273831 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -110,6 +110,8 @@ public class QSPanel extends LinearLayout implements Tunable {
private final ArrayMap<View, Integer> mChildrenLayoutTop = new ArrayMap<>();
private final Rect mClippingRect = new Rect();
private boolean mUseNewFooter = false;
+ private ViewGroup mMediaHostView;
+ private boolean mShouldMoveMediaOnExpansion = true;
public QSPanel(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -289,9 +291,15 @@ public class QSPanel extends LinearLayout implements Tunable {
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
if (move) {
+ int topOffset;
+ if (child == mMediaHostView && !mShouldMoveMediaOnExpansion) {
+ topOffset = 0;
+ } else {
+ topOffset = tileHeightOffset;
+ }
int top = Objects.requireNonNull(mChildrenLayoutTop.get(child));
- child.setLeftTopRightBottom(child.getLeft(), top + tileHeightOffset,
- child.getRight(), top + tileHeightOffset + child.getHeight());
+ child.setLeftTopRightBottom(child.getLeft(), top + topOffset,
+ child.getRight(), top + topOffset + child.getHeight());
}
if (child == mTileLayout) {
move = true;
@@ -463,6 +471,7 @@ public class QSPanel extends LinearLayout implements Tunable {
if (!mUsingMediaPlayer) {
return;
}
+ mMediaHostView = hostView;
ViewGroup newParent = horizontal ? mHorizontalLinearLayout : this;
ViewGroup currentParent = (ViewGroup) hostView.getParent();
if (currentParent != newParent) {
@@ -656,6 +665,19 @@ public class QSPanel extends LinearLayout implements Tunable {
updatePadding();
}
+ /**
+ * Sets whether the media container should move during the expansion of the QS Panel.
+ *
+ * As the QS Panel expands and the QS unsquish, the views below the QS tiles move to adapt to
+ * the new height of the QS tiles.
+ *
+ * In some cases this might not be wanted for media. One example is when there is a transition
+ * animation of the media container happening on split shade lock screen.
+ */
+ public void setShouldMoveMediaOnExpansion(boolean shouldMoveMediaOnExpansion) {
+ mShouldMoveMediaOnExpansion = shouldMoveMediaOnExpansion;
+ }
+
private class H extends Handler {
private static final int ANNOUNCE_FOR_ACCESSIBILITY = 1;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
index 3172aa9592dd..6572daa91269 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
@@ -419,6 +419,16 @@ public abstract class QSPanelControllerBase<T extends QSPanel> extends ViewContr
return mView.getBrightnessView();
}
+ /** Sets whether we are currently on lock screen. */
+ public void setIsOnKeyguard(boolean isOnKeyguard) {
+ boolean isOnSplitShadeLockscreen = mShouldUseSplitNotificationShade && isOnKeyguard;
+ // When the split shade is expanding on lockscreen, the media container transitions from the
+ // lockscreen to QS.
+ // We have to prevent the media container position from moving during the transition to have
+ // a smooth translation animation without stuttering.
+ mView.setShouldMoveMediaOnExpansion(!isOnSplitShadeLockscreen);
+ }
+
/** */
public static final class TileRecord {
public TileRecord(QSTile tile, com.android.systemui.plugins.qs.QSTileView tileView) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
index e088f548a356..5d6bbae1f14a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
@@ -202,11 +202,12 @@ public class CastTile extends QSTileImpl<BooleanState> {
mActivityStarter
.postStartActivityDismissingKeyguard(getLongClickIntent(), 0,
controller);
- });
+ }, R.style.Theme_SystemUI_Dialog, false /* showProgressBarWhenEmpty */);
holder.init(dialog);
SystemUIDialog.setShowForAllUsers(dialog, true);
SystemUIDialog.registerDismissListener(dialog);
SystemUIDialog.setWindowOnTop(dialog, mKeyguard.isShowing());
+ SystemUIDialog.setDialogSize(dialog);
mUiHandler.post(() -> {
if (view != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
index 8366bddaab9f..8a9d6ddb2e14 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
@@ -342,9 +342,7 @@ class LockscreenShadeTransitionController @Inject constructor(
qS.setTransitionToFullShadeAmount(field, qSDragProgress)
notificationPanelController.setTransitionToFullShadeAmount(field,
false /* animate */, 0 /* delay */)
- // TODO: appear media also in split shade
- val mediaAmount = if (useSplitShade) 0f else field
- mediaHierarchyManager.setTransitionToFullShadeAmount(mediaAmount)
+ mediaHierarchyManager.setTransitionToFullShadeAmount(field)
transitionToShadeAmountCommon(field)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
index b141110a2ec2..8b25c2bc20b9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
@@ -16,9 +16,14 @@
package com.android.systemui.statusbar.phone;
+import android.content.ContentResolver;
+import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
+import android.database.ContentObserver;
import android.hardware.display.AmbientDisplayConfiguration;
+import android.net.Uri;
+import android.os.Handler;
import android.os.PowerManager;
import android.os.SystemProperties;
import android.os.UserHandle;
@@ -34,6 +39,7 @@ import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.systemui.Dumpable;
import com.android.systemui.R;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.doze.AlwaysOnDisplayPolicy;
import com.android.systemui.doze.DozeScreenState;
@@ -86,6 +92,7 @@ public class DozeParameters implements
private boolean mDozeAlwaysOn;
private boolean mControlScreenOffAnimation;
+ private boolean mIsQuickPickupEnabled;
private boolean mKeyguardShowing;
@VisibleForTesting
@@ -101,10 +108,17 @@ public class DozeParameters implements
public void onShadeExpandedChanged(boolean expanded) {
updateControlScreenOff();
}
+
+ @Override
+ public void onUserSwitchComplete(int newUserId) {
+ updateQuickPickupEnabled();
+ }
};
@Inject
protected DozeParameters(
+ Context context,
+ @Background Handler handler,
@Main Resources resources,
AmbientDisplayConfiguration ambientDisplayConfiguration,
AlwaysOnDisplayPolicy alwaysOnDisplayPolicy,
@@ -146,6 +160,14 @@ public class DozeParameters implements
if (mFoldAodAnimationController != null) {
mFoldAodAnimationController.addCallback(this);
}
+
+ SettingsObserver quickPickupSettingsObserver = new SettingsObserver(context, handler);
+ quickPickupSettingsObserver.observe();
+ }
+
+ private void updateQuickPickupEnabled() {
+ mIsQuickPickupEnabled =
+ mAmbientDisplayConfiguration.quickPickupSensorEnabled(UserHandle.USER_CURRENT);
}
public boolean getDisplayStateSupported() {
@@ -239,8 +261,11 @@ public class DozeParameters implements
return mDozeAlwaysOn && !mBatteryController.isAodPowerSave();
}
+ /**
+ * Whether the quick pickup gesture is supported and enabled for the device.
+ */
public boolean isQuickPickupEnabled() {
- return mAmbientDisplayConfiguration.quickPickupSensorEnabled(UserHandle.USER_CURRENT);
+ return mIsQuickPickupEnabled;
}
/**
@@ -436,6 +461,7 @@ public class DozeParameters implements
pw.print("getPickupVibrationThreshold(): "); pw.println(getPickupVibrationThreshold());
pw.print("getSelectivelyRegisterSensorsUsingProx(): ");
pw.println(getSelectivelyRegisterSensorsUsingProx());
+ pw.print("isQuickPickupEnabled(): "); pw.println(isQuickPickupEnabled());
}
private boolean getPostureSpecificBool(
@@ -458,4 +484,44 @@ public class DozeParameters implements
*/
void onAlwaysOnChange();
}
+
+ private final class SettingsObserver extends ContentObserver {
+ private final Uri mQuickPickupGesture =
+ Settings.Secure.getUriFor(Settings.Secure.DOZE_QUICK_PICKUP_GESTURE);
+ private final Uri mPickupGesture =
+ Settings.Secure.getUriFor(Settings.Secure.DOZE_PICK_UP_GESTURE);
+ private final Uri mAlwaysOnEnabled =
+ Settings.Secure.getUriFor(Settings.Secure.DOZE_ALWAYS_ON);
+ private final Context mContext;
+
+ SettingsObserver(Context context, Handler handler) {
+ super(handler);
+ mContext = context;
+ }
+
+ void observe() {
+ ContentResolver resolver = mContext.getContentResolver();
+ resolver.registerContentObserver(mQuickPickupGesture, false, this,
+ UserHandle.USER_ALL);
+ resolver.registerContentObserver(mPickupGesture, false, this, UserHandle.USER_ALL);
+ resolver.registerContentObserver(mAlwaysOnEnabled, false, this, UserHandle.USER_ALL);
+ update(null);
+ }
+
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ update(uri);
+ }
+
+ public void update(Uri uri) {
+ if (uri == null
+ || mQuickPickupGesture.equals(uri)
+ || mPickupGesture.equals(uri)
+ || mAlwaysOnEnabled.equals(uri)) {
+ // the quick pickup gesture is dependent on alwaysOn being disabled and
+ // the pickup gesture being enabled
+ updateQuickPickupEnabled();
+ }
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
index 6d7748388e14..05fba5498ea7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
@@ -28,6 +28,8 @@ import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
+import androidx.annotation.Nullable;
+
import com.android.internal.annotations.VisibleForTesting;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.assist.AssistManager;
@@ -46,8 +48,11 @@ import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+import com.android.systemui.unfold.FoldAodAnimationController;
+import com.android.systemui.unfold.SysUIUnfoldComponent;
import java.util.ArrayList;
+import java.util.Optional;
import javax.inject.Inject;
@@ -73,6 +78,8 @@ public final class DozeServiceHost implements DozeHost {
private final WakefulnessLifecycle mWakefulnessLifecycle;
private final SysuiStatusBarStateController mStatusBarStateController;
private final DeviceProvisionedController mDeviceProvisionedController;
+ @Nullable
+ private final FoldAodAnimationController mFoldAodAnimationController;
private final HeadsUpManagerPhone mHeadsUpManagerPhone;
private final BatteryController mBatteryController;
private final ScrimController mScrimController;
@@ -105,6 +112,7 @@ public final class DozeServiceHost implements DozeHost {
Lazy<AssistManager> assistManagerLazy,
DozeScrimController dozeScrimController, KeyguardUpdateMonitor keyguardUpdateMonitor,
PulseExpansionHandler pulseExpansionHandler,
+ Optional<SysUIUnfoldComponent> sysUIUnfoldComponent,
NotificationShadeWindowController notificationShadeWindowController,
NotificationWakeUpCoordinator notificationWakeUpCoordinator,
AuthController authController,
@@ -128,6 +136,8 @@ public final class DozeServiceHost implements DozeHost {
mNotificationWakeUpCoordinator = notificationWakeUpCoordinator;
mAuthController = authController;
mNotificationIconAreaController = notificationIconAreaController;
+ mFoldAodAnimationController = sysUIUnfoldComponent
+ .map(SysUIUnfoldComponent::getFoldAodAnimationController).orElse(null);
}
// TODO: we should try to not pass status bar in here if we can avoid it.
@@ -215,6 +225,9 @@ public final class DozeServiceHost implements DozeHost {
mStatusBarStateController.setIsDozing(dozing);
mNotificationShadeWindowViewController.setDozing(dozing);
+ if (mFoldAodAnimationController != null) {
+ mFoldAodAnimationController.setIsDozing(dozing);
+ }
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java
index 9f9e7d9a276e..5caf4f604fcd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java
@@ -26,6 +26,7 @@ import static com.android.systemui.statusbar.NotificationRemoteInputManager.ENAB
import android.app.IActivityManager;
import android.content.Context;
import android.content.pm.ActivityInfo;
+import android.content.res.Configuration;
import android.graphics.PixelFormat;
import android.graphics.Region;
import android.os.Binder;
@@ -117,6 +118,7 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW
* @see #batchApplyWindowLayoutParams(Runnable)
*/
private int mDeferWindowLayoutParams;
+ private boolean mLastKeyguardRotationAllowed;
@Inject
public NotificationShadeWindowControllerImpl(Context context, WindowManager windowManager,
@@ -143,7 +145,7 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW
mScreenOffAnimationController = screenOffAnimationController;
dumpManager.registerDumpable(getClass().getName(), this);
mAuthController = authController;
-
+ mLastKeyguardRotationAllowed = mKeyguardStateController.isKeyguardScreenRotationAllowed();
mLockScreenDisplayTimeout = context.getResources()
.getInteger(R.integer.config_lockScreenDisplayTimeout);
((SysuiStatusBarStateController) statusBarStateController)
@@ -779,6 +781,17 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW
setKeyguardDark(useDarkText);
}
+ @Override
+ public void onConfigChanged(Configuration newConfig) {
+ final boolean newScreenRotationAllowed = mKeyguardStateController
+ .isKeyguardScreenRotationAllowed();
+
+ if (mLastKeyguardRotationAllowed != newScreenRotationAllowed) {
+ apply(mCurrentState);
+ mLastKeyguardRotationAllowed = newScreenRotationAllowed;
+ }
+ }
+
/**
* When keyguard will be dismissed but didn't start animation yet.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java
index 72237b1ca6c6..53ef97dc317c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java
@@ -27,7 +27,6 @@ import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationPresenter;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.StatusBarState;
-import com.android.wm.shell.bubbles.Bubbles;
import java.util.ArrayList;
import java.util.Optional;
@@ -50,7 +49,6 @@ public class ShadeControllerImpl implements ShadeController {
private final int mDisplayId;
protected final Lazy<Optional<StatusBar>> mStatusBarOptionalLazy;
private final Lazy<AssistManager> mAssistManagerLazy;
- private final Optional<Bubbles> mBubblesOptional;
private final ArrayList<Runnable> mPostCollapseRunnables = new ArrayList<>();
@@ -62,8 +60,7 @@ public class ShadeControllerImpl implements ShadeController {
StatusBarKeyguardViewManager statusBarKeyguardViewManager,
WindowManager windowManager,
Lazy<Optional<StatusBar>> statusBarOptionalLazy,
- Lazy<AssistManager> assistManagerLazy,
- Optional<Bubbles> bubblesOptional
+ Lazy<AssistManager> assistManagerLazy
) {
mCommandQueue = commandQueue;
mStatusBarStateController = statusBarStateController;
@@ -73,7 +70,6 @@ public class ShadeControllerImpl implements ShadeController {
// TODO: Remove circular reference to StatusBar when possible.
mStatusBarOptionalLazy = statusBarOptionalLazy;
mAssistManagerLazy = assistManagerLazy;
- mBubblesOptional = bubblesOptional;
}
@Override
@@ -131,8 +127,6 @@ public class ShadeControllerImpl implements ShadeController {
getStatusBar().getNotificationShadeWindowViewController().cancelExpandHelper();
getNotificationPanelViewController()
.collapsePanel(true /* animate */, delayed, speedUpFactor);
- } else if (mBubblesOptional.isPresent()) {
- mBubblesOptional.get().collapseStack();
}
}
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 82e0e6726437..cffdc290c041 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -270,6 +270,7 @@ public class StatusBar extends CoreStartable implements
protected static final int MSG_DISMISS_KEYBOARD_SHORTCUTS_MENU = 1027;
// Should match the values in PhoneWindowManager
+ public static final String SYSTEM_DIALOG_REASON_KEY = "reason";
public static final String SYSTEM_DIALOG_REASON_RECENT_APPS = "recentapps";
public static final String SYSTEM_DIALOG_REASON_DREAM = "dream";
static public final String SYSTEM_DIALOG_REASON_SCREENSHOT = "screenshot";
@@ -2661,15 +2662,12 @@ public class StatusBar extends CoreStartable implements
Trace.beginSection("StatusBar#onReceive");
if (DEBUG) Log.v(TAG, "onReceive: " + intent);
String action = intent.getAction();
+ String reason = intent.getStringExtra(SYSTEM_DIALOG_REASON_KEY);
if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) {
KeyboardShortcuts.dismiss();
mRemoteInputManager.closeRemoteInputs();
- if (mBubblesOptional.isPresent() && mBubblesOptional.get().isStackExpanded()) {
- mBubblesOptional.get().collapseStack();
- }
if (mLockscreenUserManager.isCurrentProfile(getSendingUserId())) {
int flags = CommandQueue.FLAG_EXCLUDE_NONE;
- String reason = intent.getStringExtra("reason");
if (reason != null) {
if (reason.equals(SYSTEM_DIALOG_REASON_RECENT_APPS)) {
flags |= CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL;
@@ -2684,19 +2682,13 @@ public class StatusBar extends CoreStartable implements
}
mShadeController.animateCollapsePanels(flags);
}
- }
- else if (Intent.ACTION_SCREEN_OFF.equals(action)) {
+ } else if (Intent.ACTION_SCREEN_OFF.equals(action)) {
if (mNotificationShadeWindowController != null) {
mNotificationShadeWindowController.setNotTouchable(false);
}
- if (mBubblesOptional.isPresent() && mBubblesOptional.get().isStackExpanded()) {
- // Post to main thread, since updating the UI.
- mMainExecutor.execute(() -> mBubblesOptional.get().collapseStack());
- }
finishBarAnimations();
resetUserExpandedStates();
- }
- else if (DevicePolicyManager.ACTION_SHOW_DEVICE_MONITORING_DIALOG.equals(action)) {
+ } else if (DevicePolicyManager.ACTION_SHOW_DEVICE_MONITORING_DIALOG.equals(action)) {
mQSPanelController.showDeviceMonitoringDialog();
}
Trace.endSection();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
index 97225284f208..79d646cdbd13 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
@@ -42,7 +42,10 @@ import androidx.annotation.Nullable;
import com.android.systemui.Dependency;
import com.android.systemui.R;
+import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.model.SysUiState;
+import com.android.systemui.shared.system.QuickStepContract;
import java.util.ArrayList;
import java.util.List;
@@ -64,7 +67,8 @@ public class SystemUIDialog extends AlertDialog implements ViewRootImpl.ConfigCh
private final Context mContext;
@Nullable private final DismissReceiver mDismissReceiver;
private final Handler mHandler = new Handler();
- @Nullable private final SystemUIDialogManager mDialogManager;
+ private final SystemUIDialogManager mDialogManager;
+ private final SysUiState mSysUiState;
private int mLastWidth = Integer.MIN_VALUE;
private int mLastHeight = Integer.MIN_VALUE;
@@ -77,24 +81,11 @@ public class SystemUIDialog extends AlertDialog implements ViewRootImpl.ConfigCh
this(context, R.style.Theme_SystemUI_Dialog);
}
- public SystemUIDialog(Context context, SystemUIDialogManager dialogManager) {
- this(context, R.style.Theme_SystemUI_Dialog, true, dialogManager);
- }
-
public SystemUIDialog(Context context, int theme) {
this(context, theme, true /* dismissOnDeviceLock */);
}
- public SystemUIDialog(Context context, int theme, SystemUIDialogManager dialogManager) {
- this(context, theme, true /* dismissOnDeviceLock */, dialogManager);
- }
-
public SystemUIDialog(Context context, int theme, boolean dismissOnDeviceLock) {
- this(context, theme, dismissOnDeviceLock, null);
- }
-
- public SystemUIDialog(Context context, int theme, boolean dismissOnDeviceLock,
- @Nullable SystemUIDialogManager dialogManager) {
super(context, theme);
mContext = context;
@@ -104,7 +95,12 @@ public class SystemUIDialog extends AlertDialog implements ViewRootImpl.ConfigCh
getWindow().setAttributes(attrs);
mDismissReceiver = dismissOnDeviceLock ? new DismissReceiver(this) : null;
- mDialogManager = dialogManager;
+
+ // TODO(b/219008720): Remove those calls to Dependency.get by introducing a
+ // SystemUIDialogFactory and make all other dialogs create a SystemUIDialog to which we set
+ // the content and attach listeners.
+ mDialogManager = Dependency.get(SystemUIDialogManager.class);
+ mSysUiState = Dependency.get(SysUiState.class);
}
@Override
@@ -174,13 +170,11 @@ public class SystemUIDialog extends AlertDialog implements ViewRootImpl.ConfigCh
mDismissReceiver.register();
}
- if (mDialogManager != null) {
- mDialogManager.setShowing(this, true);
- }
-
// Listen for configuration changes to resize this dialog window. This is mostly necessary
// for foldables that often go from large <=> small screen when folding/unfolding.
ViewRootImpl.addConfigCallback(this);
+ mDialogManager.setShowing(this, true);
+ mSysUiState.setFlag(QuickStepContract.SYSUI_STATE_DIALOG_SHOWING, true);
}
@Override
@@ -191,11 +185,9 @@ public class SystemUIDialog extends AlertDialog implements ViewRootImpl.ConfigCh
mDismissReceiver.unregister();
}
- if (mDialogManager != null) {
- mDialogManager.setShowing(this, false);
- }
-
ViewRootImpl.removeConfigCallback(this);
+ mDialogManager.setShowing(this, false);
+ mSysUiState.setFlag(QuickStepContract.SYSUI_STATE_DIALOG_SHOWING, false);
}
public void setShowForAllUsers(boolean show) {
@@ -401,10 +393,13 @@ public class SystemUIDialog extends AlertDialog implements ViewRootImpl.ConfigCh
private final Dialog mDialog;
private boolean mRegistered;
private final BroadcastDispatcher mBroadcastDispatcher;
+ private final DialogLaunchAnimator mDialogLaunchAnimator;
DismissReceiver(Dialog dialog) {
mDialog = dialog;
+ // TODO(b/219008720): Remove those calls to Dependency.get.
mBroadcastDispatcher = Dependency.get(BroadcastDispatcher.class);
+ mDialogLaunchAnimator = Dependency.get(DialogLaunchAnimator.class);
}
void register() {
@@ -421,6 +416,10 @@ public class SystemUIDialog extends AlertDialog implements ViewRootImpl.ConfigCh
@Override
public void onReceive(Context context, Intent intent) {
+ // These broadcast are usually received when locking the device, swiping up to home
+ // (which collapses the shade), etc. In those cases, we usually don't want to animate
+ // back into the view.
+ mDialogLaunchAnimator.disableAllCurrentDialogsExitAnimations();
mDialog.dismiss();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/touch/TouchInsetManager.java b/packages/SystemUI/src/com/android/systemui/touch/TouchInsetManager.java
new file mode 100644
index 000000000000..de4e1e2f5886
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/touch/TouchInsetManager.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.touch;
+
+import android.graphics.Rect;
+import android.graphics.Region;
+import android.view.View;
+import android.view.ViewRootImpl;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.concurrent.Executor;
+
+/**
+ * {@link TouchInsetManager} handles setting the touchable inset regions for a given View. This
+ * is useful for passing through touch events for all but select areas.
+ */
+public class TouchInsetManager {
+ /**
+ * {@link TouchInsetSession} provides an individualized session with the
+ * {@link TouchInsetManager}, linking any action to the client.
+ */
+ public static class TouchInsetSession {
+ private final TouchInsetManager mManager;
+
+ private final HashSet<View> mTrackedViews;
+ private final Executor mExecutor;
+
+ private final View.OnLayoutChangeListener mOnLayoutChangeListener =
+ (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom)
+ -> updateTouchRegion();
+
+ /**
+ * Default constructor
+ * @param manager The parent {@link TouchInsetManager} which will be affected by actions on
+ * this session.
+ * @param rootView The parent of views that will be tracked.
+ * @param executor An executor for marshalling operations.
+ */
+ TouchInsetSession(TouchInsetManager manager, Executor executor) {
+ mManager = manager;
+ mTrackedViews = new HashSet<>();
+ mExecutor = executor;
+ }
+
+ /**
+ * Adds a descendant of the root view to be tracked.
+ * @param view {@link View} to be tracked.
+ */
+ public void addViewToTracking(View view) {
+ mExecutor.execute(() -> {
+ mTrackedViews.add(view);
+ view.addOnLayoutChangeListener(mOnLayoutChangeListener);
+ updateTouchRegion();
+ });
+ }
+
+ /**
+ * Removes a view from further tracking
+ * @param view {@link View} to be removed.
+ */
+ public void removeViewFromTracking(View view) {
+ mExecutor.execute(() -> {
+ mTrackedViews.remove(view);
+ view.removeOnLayoutChangeListener(mOnLayoutChangeListener);
+ updateTouchRegion();
+ });
+ }
+
+ private void updateTouchRegion() {
+ final Region cumulativeRegion = Region.obtain();
+
+ mTrackedViews.stream().forEach(view -> {
+ final Rect boundaries = new Rect();
+ view.getBoundsOnScreen(boundaries);
+ cumulativeRegion.op(boundaries, Region.Op.UNION);
+ });
+
+ mManager.setTouchRegion(this, cumulativeRegion);
+
+ cumulativeRegion.recycle();
+ }
+
+ /**
+ * Removes all tracked views and updates insets accordingly.
+ */
+ public void clear() {
+ mExecutor.execute(() -> {
+ mManager.clearRegion(this);
+ mTrackedViews.clear();
+ });
+ }
+ }
+
+ private final HashMap<TouchInsetSession, Region> mDefinedRegions = new HashMap<>();
+ private final Executor mExecutor;
+ private final View mRootView;
+
+ private final View.OnAttachStateChangeListener mAttachListener =
+ new View.OnAttachStateChangeListener() {
+ @Override
+ public void onViewAttachedToWindow(View v) {
+ updateTouchInset();
+ }
+
+ @Override
+ public void onViewDetachedFromWindow(View v) {
+ }
+ };
+
+ /**
+ * Default constructor.
+ * @param executor An {@link Executor} to marshal all operations on.
+ * @param rootView The root {@link View} for all views in sessions.
+ */
+ public TouchInsetManager(Executor executor, View rootView) {
+ mExecutor = executor;
+ mRootView = rootView;
+ mRootView.addOnAttachStateChangeListener(mAttachListener);
+
+ }
+
+ /**
+ * Creates a new associated session.
+ */
+ public TouchInsetSession createSession() {
+ return new TouchInsetSession(this, mExecutor);
+ }
+
+ private void updateTouchInset() {
+ final ViewRootImpl viewRootImpl = mRootView.getViewRootImpl();
+
+ if (viewRootImpl == null) {
+ return;
+ }
+
+ final Region aggregateRegion = Region.obtain();
+
+ for (Region region : mDefinedRegions.values()) {
+ aggregateRegion.op(region, Region.Op.UNION);
+ }
+
+ viewRootImpl.setTouchableRegion(aggregateRegion);
+
+ aggregateRegion.recycle();
+ }
+
+ protected void setTouchRegion(TouchInsetSession session, Region region) {
+ final Region introducedRegion = Region.obtain(region);
+ mExecutor.execute(() -> {
+ mDefinedRegions.put(session, introducedRegion);
+ updateTouchInset();
+ });
+ }
+
+ private void clearRegion(TouchInsetSession session) {
+ mExecutor.execute(() -> {
+ final Region storedRegion = mDefinedRegions.remove(session);
+
+ if (storedRegion != null) {
+ storedRegion.recycle();
+ }
+
+ updateTouchInset();
+ });
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt b/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt
index 2a9076e8f848..e2374ada15d5 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt
@@ -16,9 +16,12 @@
package com.android.systemui.unfold
+import android.content.Context
+import android.hardware.devicestate.DeviceStateManager
import android.os.Handler
import android.os.PowerManager
import android.provider.Settings
+import androidx.core.view.OneShotPreDrawListener
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.keyguard.WakefulnessLifecycle
import com.android.systemui.statusbar.LightRevealScrim
@@ -27,6 +30,8 @@ import com.android.systemui.statusbar.phone.StatusBar
import com.android.systemui.statusbar.policy.CallbackController
import com.android.systemui.unfold.FoldAodAnimationController.FoldAodAnimationStatus
import com.android.systemui.util.settings.GlobalSettings
+import java.util.concurrent.Executor
+import java.util.function.Consumer
import javax.inject.Inject
/**
@@ -38,13 +43,21 @@ class FoldAodAnimationController
@Inject
constructor(
@Main private val handler: Handler,
+ @Main private val executor: Executor,
+ private val context: Context,
+ private val deviceStateManager: DeviceStateManager,
private val wakefulnessLifecycle: WakefulnessLifecycle,
private val globalSettings: GlobalSettings
) : CallbackController<FoldAodAnimationStatus>, ScreenOffAnimation, WakefulnessLifecycle.Observer {
+ private lateinit var statusBar: StatusBar
+
+ private var isFolded = false
+ private var isFoldHandled = true
+
private var alwaysOnEnabled: Boolean = false
+ private var isDozing: Boolean = false
private var isScrimOpaque: Boolean = false
- private lateinit var statusBar: StatusBar
private var pendingScrimReadyCallback: Runnable? = null
private var shouldPlayAnimation = false
@@ -62,6 +75,7 @@ constructor(
override fun initialize(statusBar: StatusBar, lightRevealScrim: LightRevealScrim) {
this.statusBar = statusBar
+ deviceStateManager.registerCallback(executor, FoldListener())
wakefulnessLifecycle.addObserver(this)
}
@@ -84,7 +98,7 @@ constructor(
override fun onStartedWakingUp() {
if (isAnimationPlaying) {
handler.removeCallbacks(startAnimationRunnable)
- statusBar.notificationPanelViewController.cancelFoldToAodAnimation();
+ statusBar.notificationPanelViewController.cancelFoldToAodAnimation()
}
setAnimationState(playing = false)
@@ -105,11 +119,24 @@ constructor(
*/
fun onScreenTurningOn(onReady: Runnable) {
if (shouldPlayAnimation) {
+ // The device was not dozing and going to sleep after folding, play the animation
+
if (isScrimOpaque) {
onReady.run()
} else {
pendingScrimReadyCallback = onReady
}
+ } else if (isFolded && !isFoldHandled && alwaysOnEnabled && isDozing) {
+ // Screen turning on for the first time after folding and we are already dozing
+ // We should play the folding to AOD animation
+
+ setAnimationState(playing = true)
+ statusBar.notificationPanelViewController.prepareFoldToAodAnimation()
+
+ // We don't need to wait for the scrim as it is already displayed
+ // but we should wait for the initial animation preparations to be drawn
+ // (setting initial alpha/translation)
+ OneShotPreDrawListener.add(statusBar.notificationPanelViewController.view, onReady)
} else {
// No animation, call ready callback immediately
onReady.run()
@@ -136,6 +163,10 @@ constructor(
}
}
+ fun setIsDozing(dozing: Boolean) {
+ isDozing = dozing
+ }
+
override fun isAnimationPlaying(): Boolean = isAnimationPlaying
override fun isKeyguardHideDelayed(): Boolean = isAnimationPlaying()
@@ -166,4 +197,15 @@ constructor(
interface FoldAodAnimationStatus {
fun onFoldToAodAnimationChanged()
}
+
+ private inner class FoldListener :
+ DeviceStateManager.FoldStateListener(
+ context,
+ Consumer { isFolded ->
+ if (!isFolded) {
+ // We are unfolded now, reset the fold handle status
+ isFoldHandled = false
+ }
+ this.isFolded = isFolded
+ })
}
diff --git a/packages/SystemUI/src/com/android/systemui/user/UserSwitcherActivity.kt b/packages/SystemUI/src/com/android/systemui/user/UserSwitcherActivity.kt
index 14585fb0f58e..c0d7925cf2bb 100644
--- a/packages/SystemUI/src/com/android/systemui/user/UserSwitcherActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/UserSwitcherActivity.kt
@@ -35,9 +35,8 @@ import android.widget.AdapterView
import android.widget.ArrayAdapter
import android.widget.ImageView
import android.widget.TextView
-
import androidx.constraintlayout.helper.widget.Flow
-
+import com.android.internal.annotations.VisibleForTesting
import com.android.internal.util.UserIcons
import com.android.settingslib.Utils
import com.android.systemui.R
@@ -47,12 +46,12 @@ import com.android.systemui.plugins.FalsingManager.LOW_PENALTY
import com.android.systemui.statusbar.phone.ShadeController
import com.android.systemui.statusbar.policy.UserSwitcherController
import com.android.systemui.statusbar.policy.UserSwitcherController.BaseUserAdapter
-import com.android.systemui.statusbar.policy.UserSwitcherController.UserRecord
import com.android.systemui.statusbar.policy.UserSwitcherController.USER_SWITCH_DISABLED_ALPHA
import com.android.systemui.statusbar.policy.UserSwitcherController.USER_SWITCH_ENABLED_ALPHA
+import com.android.systemui.statusbar.policy.UserSwitcherController.UserRecord
import com.android.systemui.util.LifecycleActivity
-
import javax.inject.Inject
+import kotlin.math.ceil
private const val USER_VIEW = "user_view"
@@ -137,6 +136,18 @@ class UserSwitcherActivity @Inject constructor(
return UserIcons.getDefaultUserIcon(resources, item.info.id, false)
}
+ fun getTotalUserViews(): Int {
+ return users.count { item ->
+ !doNotRenderUserView(item)
+ }
+ }
+
+ fun doNotRenderUserView(item: UserRecord): Boolean {
+ return item.isAddUser ||
+ item.isAddSupervisedUser ||
+ item.isGuest && item.info == null
+ }
+
private fun getDrawable(item: UserRecord): Drawable {
var drawable = if (item.isCurrent && item.isGuest) {
getDrawable(R.drawable.ic_avatar_guest_user)
@@ -211,7 +222,8 @@ class UserSwitcherActivity @Inject constructor(
userSwitcherController.init(parent)
initBroadcastReceiver()
- buildUserViews()
+
+ parent.post { buildUserViews() }
}
private fun showPopupMenu() {
@@ -272,16 +284,32 @@ class UserSwitcherActivity @Inject constructor(
}
parent.removeViews(start, count)
addUserRecords.clear()
-
val flow = requireViewById<Flow>(R.id.flow)
+ val totalWidth = parent.width
+ val userViewCount = adapter.getTotalUserViews()
+ val maxColumns = getMaxColumns(userViewCount)
+ val horizontalGap = resources
+ .getDimensionPixelSize(R.dimen.user_switcher_fullscreen_horizontal_gap)
+ val totalWidthOfHorizontalGap = (maxColumns - 1) * horizontalGap
+ val maxWidgetDiameter = (totalWidth - totalWidthOfHorizontalGap) / maxColumns
+
+ flow.setMaxElementsWrap(maxColumns)
+
for (i in 0 until adapter.getCount()) {
val item = adapter.getItem(i)
- if (item.isAddUser ||
- item.isAddSupervisedUser ||
- item.isGuest && item.info == null) {
+ if (adapter.doNotRenderUserView(item)) {
addUserRecords.add(item)
} else {
val userView = adapter.getView(i, null, parent)
+ userView.requireViewById<ImageView>(R.id.user_switcher_icon).apply {
+ val lp = layoutParams
+ if (maxWidgetDiameter < lp.width) {
+ lp.width = maxWidgetDiameter
+ lp.height = maxWidgetDiameter
+ layoutParams = lp
+ }
+ }
+
userView.setId(View.generateViewId())
parent.addView(userView)
@@ -333,6 +361,11 @@ class UserSwitcherActivity @Inject constructor(
broadcastDispatcher.registerReceiver(broadcastReceiver, filter)
}
+ @VisibleForTesting
+ fun getMaxColumns(userCount: Int): Int {
+ return if (userCount < 5) 4 else ceil(userCount / 2.0).toInt()
+ }
+
private class ItemAdapter(
val parentContext: Context,
val resource: Int,
diff --git a/packages/SystemUI/src/com/android/systemui/volume/CaptionsToggleImageButton.java b/packages/SystemUI/src/com/android/systemui/volume/CaptionsToggleImageButton.java
index 1862ed3a4de8..ae23ca64e31e 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/CaptionsToggleImageButton.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/CaptionsToggleImageButton.java
@@ -28,14 +28,11 @@ import androidx.core.view.accessibility.AccessibilityNodeInfoCompat.Accessibilit
import com.android.keyguard.AlphaOptimizedImageButton;
import com.android.systemui.R;
-/** Toggle button in Volume Dialog that allows extra state for when streams are opted-out */
+/** Toggle button in Volume Dialog for controlling system captions state */
public class CaptionsToggleImageButton extends AlphaOptimizedImageButton {
- private static final int[] OPTED_OUT_STATE = new int[] { R.attr.optedOut };
-
private ConfirmedTapListener mConfirmedTapListener;
private boolean mCaptionsEnabled = false;
- private boolean mOptedOut = false;
private GestureDetector mGestureDetector;
private GestureDetector.SimpleOnGestureListener mGestureListener =
@@ -60,11 +57,7 @@ public class CaptionsToggleImageButton extends AlphaOptimizedImageButton {
@Override
public int[] onCreateDrawableState(int extraSpace) {
- int[] state = super.onCreateDrawableState(extraSpace + 1);
- if (mOptedOut) {
- mergeDrawableStates(state, OPTED_OUT_STATE);
- }
- return state;
+ return super.onCreateDrawableState(extraSpace + 1);
}
Runnable setCaptionsEnabled(boolean areCaptionsEnabled) {
@@ -95,16 +88,6 @@ public class CaptionsToggleImageButton extends AlphaOptimizedImageButton {
return this.mCaptionsEnabled;
}
- /** Sets whether or not the current stream has opted out of captions */
- void setOptedOut(boolean isOptedOut) {
- this.mOptedOut = isOptedOut;
- refreshDrawableState();
- }
-
- boolean getOptedOut() {
- return this.mOptedOut;
- }
-
void setOnConfirmedTapListener(ConfirmedTapListener listener, Handler handler) {
mConfirmedTapListener = listener;
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
index 57c7f11b752d..97e03a68205c 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
@@ -54,6 +54,7 @@ import android.util.ArrayMap;
import android.util.Log;
import android.util.Slog;
import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.CaptioningManager;
import androidx.lifecycle.Observer;
@@ -130,6 +131,7 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa
private final Receiver mReceiver = new Receiver();
private final RingerModeObservers mRingerModeObservers;
private final MediaSessions mMediaSessions;
+ private final CaptioningManager mCaptioningManager;
protected C mCallbacks = new C();
private final State mState = new State();
protected final MediaSessionsCallbacks mMediaSessionsCallbacksW;
@@ -175,7 +177,8 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa
IAudioService iAudioService,
AccessibilityManager accessibilityManager,
PackageManager packageManager,
- WakefulnessLifecycle wakefulnessLifecycle) {
+ WakefulnessLifecycle wakefulnessLifecycle,
+ CaptioningManager captioningManager) {
mContext = context.getApplicationContext();
mPackageManager = packageManager;
mWakefulnessLifecycle = wakefulnessLifecycle;
@@ -200,6 +203,7 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa
mVibrator = vibrator;
mHasVibrator = mVibrator.hasVibrator();
mAudioService = iAudioService;
+ mCaptioningManager = captioningManager;
boolean accessibilityVolumeStreamActive = accessibilityManager
.isAccessibilityVolumeStreamActive();
@@ -307,20 +311,11 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa
}
public boolean areCaptionsEnabled() {
- int currentValue = Settings.Secure.getIntForUser(mContext.getContentResolver(),
- Settings.Secure.ODI_CAPTIONS_ENABLED, 0, UserHandle.USER_CURRENT);
- return currentValue == 1;
+ return mCaptioningManager.isSystemAudioCaptioningEnabled();
}
public void setCaptionsEnabled(boolean isEnabled) {
- Settings.Secure.putIntForUser(mContext.getContentResolver(),
- Settings.Secure.ODI_CAPTIONS_ENABLED, isEnabled ? 1 : 0, UserHandle.USER_CURRENT);
- }
-
- @Override
- public boolean isCaptionStreamOptedOut() {
- // TODO(b/129768185): Removing secure setting, to be replaced by sound event listener
- return false;
+ mCaptioningManager.setSystemAudioCaptioningEnabled(isEnabled);
}
public void getCaptionsComponentState(boolean fromTooltip) {
@@ -423,6 +418,13 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa
}
private void onGetCaptionsComponentStateW(boolean fromTooltip) {
+ if (mCaptioningManager.isSystemAudioCaptioningUiEnabled()) {
+ mCallbacks.onCaptionComponentStateChanged(true, fromTooltip);
+ return;
+ }
+
+ // TODO(b/220968335): Remove this check once system captions component migrates
+ // to new CaptioningManager APIs.
try {
String componentNameString = mContext.getString(
com.android.internal.R.string.config_defaultSystemCaptionsService);
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index 58f74a0d2a02..bfdcbd6deb48 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -1180,11 +1180,6 @@ public class VolumeDialogImpl implements VolumeDialog,
if (mODICaptionsIcon.getCaptionsEnabled() != captionsEnabled) {
mHandler.post(mODICaptionsIcon.setCaptionsEnabled(captionsEnabled));
}
-
- boolean isOptedOut = mController.isCaptionStreamOptedOut();
- if (mODICaptionsIcon.getOptedOut() != isOptedOut) {
- mHandler.post(() -> mODICaptionsIcon.setOptedOut(isOptedOut));
- }
}
private void onCaptionIconClicked() {
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
index 6fefce2be19b..b2a79b01fb74 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
@@ -21,7 +21,7 @@ import static android.view.Display.DEFAULT_DISPLAY;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BOUNCER_SHOWING;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BUBBLES_EXPANDED;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BUBBLES_MANAGE_MENU_EXPANDED;
-import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_GLOBAL_ACTIONS_SHOWING;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_DIALOG_SHOWING;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_ONE_HANDED_ACTIVE;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_QUICK_SETTINGS_EXPANDED;
@@ -97,7 +97,7 @@ public final class WMShell extends CoreStartable
implements CommandQueue.Callbacks, ProtoTraceable<SystemUiTraceProto> {
private static final String TAG = WMShell.class.getName();
private static final int INVALID_SYSUI_STATE_MASK =
- SYSUI_STATE_GLOBAL_ACTIONS_SHOWING
+ SYSUI_STATE_DIALOG_SHOWING
| SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING
| SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED
| SYSUI_STATE_BOUNCER_SHOWING
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorHwcLayerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorHwcLayerTest.kt
new file mode 100644
index 000000000000..95aa08dff379
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorHwcLayerTest.kt
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui
+
+import android.graphics.Insets
+import android.graphics.PixelFormat
+import android.graphics.Rect
+import android.graphics.RectF
+import android.hardware.graphics.common.DisplayDecorationSupport
+import android.testing.AndroidTestingRunner
+import android.view.Display
+import android.view.DisplayCutout
+import android.view.DisplayInfo
+import android.view.View
+import androidx.test.filters.SmallTest
+import com.android.internal.R
+import com.android.systemui.util.mockito.eq
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.MockitoAnnotations
+import org.mockito.Mockito.`when` as whenever
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class ScreenDecorHwcLayerTest : SysuiTestCase() {
+
+ @Mock private lateinit var mockDisplay: Display
+ @Mock private lateinit var mockRootView: View
+
+ private val displayWidth = 100
+ private val displayHeight = 200
+ private val cutoutSize = 10
+ private val roundedSizeTop = 15
+ private val roundedSizeBottom = 20
+
+ private lateinit var decorHwcLayer: ScreenDecorHwcLayer
+ private val cutoutTop: DisplayCutout = DisplayCutout.Builder()
+ .setSafeInsets(Insets.of(0, cutoutSize, 0, 0))
+ .setBoundingRectTop(Rect(1, 0, 2, cutoutSize))
+ .build()
+
+ private val cutoutRight: DisplayCutout = DisplayCutout.Builder()
+ .setSafeInsets(Insets.of(0, 0, cutoutSize, 0))
+ .setBoundingRectRight(Rect(displayWidth - cutoutSize, 50, displayWidth, 52))
+ .build()
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ mContext.orCreateTestableResources.addOverride(
+ R.array.config_displayUniqueIdArray, arrayOf<String>())
+ mContext.orCreateTestableResources.addOverride(
+ R.bool.config_fillMainBuiltInDisplayCutout, true)
+
+ val decorationSupport = DisplayDecorationSupport()
+ decorationSupport.format = PixelFormat.R_8
+ decorHwcLayer = Mockito.spy(ScreenDecorHwcLayer(mContext, decorationSupport))
+ whenever(decorHwcLayer.width).thenReturn(displayWidth)
+ whenever(decorHwcLayer.height).thenReturn(displayHeight)
+ whenever(decorHwcLayer.display).thenReturn(mockDisplay)
+ whenever(decorHwcLayer.rootView).thenReturn(mockRootView)
+ whenever(mockRootView.left).thenReturn(0)
+ whenever(mockRootView.top).thenReturn(0)
+ whenever(mockRootView.right).thenReturn(displayWidth)
+ whenever(mockRootView.bottom).thenReturn(displayHeight)
+ }
+
+ @Test
+ fun testTransparentRegion_noCutout_noRoundedCorner_noProtection() {
+ setupConfigs(null, 0, 0, RectF(), 0f)
+
+ decorHwcLayer.calculateTransparentRect()
+
+ assertThat(decorHwcLayer.transparentRect)
+ .isEqualTo(Rect(0, 0, decorHwcLayer.width, decorHwcLayer.height))
+ }
+
+ @Test
+ fun testTransparentRegion_onlyShortEdgeCutout() {
+ setupConfigs(cutoutTop, 0, 0, RectF(), 0f)
+
+ decorHwcLayer.calculateTransparentRect()
+
+ assertThat(decorHwcLayer.transparentRect)
+ .isEqualTo(Rect(0, cutoutSize, decorHwcLayer.width, decorHwcLayer.height))
+ }
+
+ @Test
+ fun testTransparentRegion_onlyLongEdgeCutout() {
+ setupConfigs(cutoutRight, 0, 0, RectF(), 0f)
+
+ decorHwcLayer.calculateTransparentRect()
+
+ assertThat(decorHwcLayer.transparentRect)
+ .isEqualTo(Rect(0, 0, decorHwcLayer.width - cutoutSize, decorHwcLayer.height))
+ }
+
+ @Test
+ fun testTransparentRegion_onlyRoundedCorners() {
+ setupConfigs(null, roundedSizeTop, roundedSizeBottom, RectF(), 0f)
+
+ decorHwcLayer.calculateTransparentRect()
+
+ assertThat(decorHwcLayer.transparentRect)
+ .isEqualTo(Rect(0, roundedSizeTop, decorHwcLayer.width,
+ decorHwcLayer.height - roundedSizeBottom))
+ }
+
+ @Test
+ fun testTransparentRegion_onlyCutoutProtection() {
+ setupConfigs(null, 0, 0, RectF(48f, 1f, 52f, 5f), 0.5f)
+
+ decorHwcLayer.calculateTransparentRect()
+
+ assertThat(decorHwcLayer.transparentRect)
+ .isEqualTo(Rect(0, 4, decorHwcLayer.width, decorHwcLayer.height))
+
+ decorHwcLayer.cameraProtectionProgress = 1f
+
+ decorHwcLayer.calculateTransparentRect()
+
+ assertThat(decorHwcLayer.transparentRect)
+ .isEqualTo(Rect(0, 5, decorHwcLayer.width, decorHwcLayer.height))
+ }
+
+ @Test
+ fun testTransparentRegion_hasShortEdgeCutout_hasRoundedCorner_hasCutoutProtection() {
+ setupConfigs(cutoutTop, roundedSizeTop, roundedSizeBottom, RectF(48f, 1f, 52f, 5f), 1f)
+
+ decorHwcLayer.calculateTransparentRect()
+
+ assertThat(decorHwcLayer.transparentRect)
+ .isEqualTo(Rect(0, 15, decorHwcLayer.width, decorHwcLayer.height - 20))
+ }
+
+ @Test
+ fun testTransparentRegion_hasLongEdgeCutout_hasRoundedCorner_hasCutoutProtection() {
+ setupConfigs(cutoutRight, roundedSizeTop, roundedSizeBottom, RectF(48f, 1f, 52f, 5f), 1f)
+
+ decorHwcLayer.calculateTransparentRect()
+
+ assertThat(decorHwcLayer.transparentRect)
+ .isEqualTo(Rect(20, 5, decorHwcLayer.width - 20, decorHwcLayer.height))
+ }
+
+ private fun setupConfigs(
+ cutout: DisplayCutout?,
+ roundedTop: Int,
+ roundedBottom: Int,
+ protectionRect: RectF,
+ protectionProgress: Float
+ ) {
+ whenever(mockDisplay.getDisplayInfo(eq(decorHwcLayer.displayInfo))
+ ).then {
+ val info = it.getArgument<DisplayInfo>(0)
+ info.displayCutout = cutout
+ return@then true
+ }
+ decorHwcLayer.updateRoundedCornerSize(roundedTop, roundedBottom)
+ decorHwcLayer.protectionRect.set(protectionRect)
+ decorHwcLayer.cameraProtectionProgress = protectionProgress
+ decorHwcLayer.updateCutout()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
index 40632a85d722..7a0db1fd975c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
@@ -42,6 +42,7 @@ import com.android.systemui.dump.DumpManager;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.SmartReplyController;
+import com.android.systemui.statusbar.phone.SystemUIDialogManager;
import org.junit.After;
import org.junit.AfterClass;
@@ -112,6 +113,11 @@ public abstract class SysuiTestCase {
// KeyguardUpdateMonitor to be created (injected).
// TODO(b/1531701009) Clean up NotificationContentView creation to prevent this
mDependency.injectMockDependency(SmartReplyController.class);
+
+ // Make sure that all tests on any SystemUIDialog does not crash because this dependency
+ // is missing (constructing the actual one would throw).
+ // TODO(b/219008720): Remove this.
+ mDependency.injectMockDependency(SystemUIDialogManager.class);
}
@After
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
index 1dd5e227a909..6e5926db519d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
@@ -88,6 +88,7 @@ import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
@LargeTest
@TestableLooper.RunWithLooper
@@ -345,15 +346,17 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
@Test
public void onOrientationChanged_disabled_updateDisplayRotation() {
- final Display display = Mockito.spy(mContext.getDisplay());
- when(display.getRotation()).thenReturn(Surface.ROTATION_90);
- when(mContext.getDisplay()).thenReturn(display);
+ final Rect windowBounds = new Rect(mWindowManager.getCurrentWindowMetrics().getBounds());
+ // Rotate the window clockwise 90 degree.
+ windowBounds.set(windowBounds.top, windowBounds.left, windowBounds.bottom,
+ windowBounds.right);
+ mWindowManager.setWindowBounds(windowBounds);
+ final int newRotation = simulateRotateTheDevice();
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_ORIENTATION);
- });
+ mInstrumentation.runOnMainSync(() -> mWindowMagnificationController.onConfigurationChanged(
+ ActivityInfo.CONFIG_ORIENTATION));
- assertEquals(Surface.ROTATION_90, mWindowMagnificationController.mRotation);
+ assertEquals(newRotation, mWindowMagnificationController.mRotation);
}
@Test
@@ -603,6 +606,113 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
ReferenceTestUtils.waitForCondition(() -> hasMagnificationOverlapFlag());
}
+ @Test
+ public void setMinimumWindowSize_enabled_expectedWindowSize() {
+ final int minimumWindowSize = mResources.getDimensionPixelSize(
+ com.android.internal.R.dimen.accessibility_window_magnifier_min_size);
+ final int expectedWindowHeight = minimumWindowSize;
+ final int expectedWindowWidth = minimumWindowSize;
+ mInstrumentation.runOnMainSync(
+ () -> mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN,
+ Float.NaN, Float.NaN));
+
+ final AtomicInteger actualWindowHeight = new AtomicInteger();
+ final AtomicInteger actualWindowWidth = new AtomicInteger();
+ mInstrumentation.runOnMainSync(() -> {
+ mWindowMagnificationController.setWindowSize(expectedWindowWidth, expectedWindowHeight);
+ actualWindowHeight.set(mWindowManager.getLayoutParamsFromAttachedView().height);
+ actualWindowWidth.set(mWindowManager.getLayoutParamsFromAttachedView().width);
+
+ });
+
+ assertEquals(expectedWindowHeight, actualWindowHeight.get());
+ assertEquals(expectedWindowWidth, actualWindowWidth.get());
+ }
+
+ @Test
+ public void setMinimumWindowSizeThenEnable_expectedWindowSize() {
+ final int minimumWindowSize = mResources.getDimensionPixelSize(
+ com.android.internal.R.dimen.accessibility_window_magnifier_min_size);
+ final int expectedWindowHeight = minimumWindowSize;
+ final int expectedWindowWidth = minimumWindowSize;
+
+ final AtomicInteger actualWindowHeight = new AtomicInteger();
+ final AtomicInteger actualWindowWidth = new AtomicInteger();
+ mInstrumentation.runOnMainSync(() -> {
+ mWindowMagnificationController.setWindowSize(expectedWindowWidth, expectedWindowHeight);
+ mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN,
+ Float.NaN, Float.NaN);
+ actualWindowHeight.set(mWindowManager.getLayoutParamsFromAttachedView().height);
+ actualWindowWidth.set(mWindowManager.getLayoutParamsFromAttachedView().width);
+ });
+
+ assertEquals(expectedWindowHeight, actualWindowHeight.get());
+ assertEquals(expectedWindowWidth, actualWindowWidth.get());
+ }
+
+ @Test
+ public void setWindowSizeLessThanMin_enabled_minimumWindowSize() {
+ final int minimumWindowSize = mResources.getDimensionPixelSize(
+ com.android.internal.R.dimen.accessibility_window_magnifier_min_size);
+ mInstrumentation.runOnMainSync(
+ () -> mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN,
+ Float.NaN, Float.NaN));
+
+ final AtomicInteger actualWindowHeight = new AtomicInteger();
+ final AtomicInteger actualWindowWidth = new AtomicInteger();
+ mInstrumentation.runOnMainSync(() -> {
+ mWindowMagnificationController.setWindowSize(minimumWindowSize - 10,
+ minimumWindowSize - 10);
+ actualWindowHeight.set(mWindowManager.getLayoutParamsFromAttachedView().height);
+ actualWindowWidth.set(mWindowManager.getLayoutParamsFromAttachedView().width);
+ });
+
+ assertEquals(minimumWindowSize, actualWindowHeight.get());
+ assertEquals(minimumWindowSize, actualWindowWidth.get());
+ }
+
+ @Test
+ public void setWindowSizeLargerThanScreenSize_enabled_windowSizeIsScreenSize() {
+ final Rect bounds = mWindowManager.getCurrentWindowMetrics().getBounds();
+ mInstrumentation.runOnMainSync(
+ () -> mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN,
+ Float.NaN, Float.NaN));
+
+ final AtomicInteger actualWindowHeight = new AtomicInteger();
+ final AtomicInteger actualWindowWidth = new AtomicInteger();
+ mInstrumentation.runOnMainSync(() -> {
+ mWindowMagnificationController.setWindowSize(bounds.width() + 10, bounds.height() + 10);
+ actualWindowHeight.set(mWindowManager.getLayoutParamsFromAttachedView().height);
+ actualWindowWidth.set(mWindowManager.getLayoutParamsFromAttachedView().width);
+ });
+
+ assertEquals(bounds.height(), actualWindowHeight.get());
+ assertEquals(bounds.width(), actualWindowWidth.get());
+ }
+
+ @Test
+ public void setWindowCenterOutOfScreen_enabled_magnificationCenterIsInsideTheScreen() {
+
+ final int minimumWindowSize = mResources.getDimensionPixelSize(
+ com.android.internal.R.dimen.accessibility_window_magnifier_min_size);
+ final Rect bounds = mWindowManager.getCurrentWindowMetrics().getBounds();
+ mInstrumentation.runOnMainSync(
+ () -> mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN,
+ Float.NaN, Float.NaN));
+
+ final AtomicInteger magnificationCenterX = new AtomicInteger();
+ final AtomicInteger magnificationCenterY = new AtomicInteger();
+ mInstrumentation.runOnMainSync(() -> {
+ mWindowMagnificationController.setWindowSizeAndCenter(minimumWindowSize,
+ minimumWindowSize, bounds.right, bounds.bottom);
+ magnificationCenterX.set((int) mWindowMagnificationController.getCenterX());
+ magnificationCenterY.set((int) mWindowMagnificationController.getCenterY());
+ });
+
+ assertTrue(magnificationCenterX.get() < bounds.right);
+ assertTrue(magnificationCenterY.get() < bounds.bottom);
+ }
+
private CharSequence getAccessibilityWindowTitle() {
final View mirrorView = mWindowManager.getAttachedView();
if (mirrorView == null) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/chooser/ChooserHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/chooser/ChooserHelperTest.java
deleted file mode 100644
index ecfb9ee4950e..000000000000
--- a/packages/SystemUI/tests/src/com/android/systemui/chooser/ChooserHelperTest.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.chooser;
-
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.anyBoolean;
-import static org.mockito.Mockito.anyInt;
-import static org.mockito.Mockito.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.app.Activity;
-import android.app.ActivityTaskManager;
-import android.content.Intent;
-import android.os.Binder;
-import android.test.suitebuilder.annotation.SmallTest;
-
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.systemui.SysuiTestCase;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class ChooserHelperTest extends SysuiTestCase {
-
- @Test
- public void testOnChoose_CallsStartActivityAsCallerWithToken() {
- final Intent intent = new Intent();
- final Binder token = new Binder();
- intent.putExtra(ActivityTaskManager.EXTRA_PERMISSION_TOKEN, token);
-
- final Activity mockActivity = mock(Activity.class);
- when(mockActivity.getIntent()).thenReturn(intent);
-
- ChooserHelper.onChoose(mockActivity);
- verify(mockActivity, times(1)).startActivityAsCaller(
- any(), any(), eq(token), anyBoolean(), anyInt());
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java
index 7af039b7fa06..8ce10b808f32 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java
@@ -17,7 +17,6 @@
package com.android.systemui.dreams;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.eq;
@@ -118,31 +117,6 @@ public class DreamOverlayContainerViewControllerTest extends SysuiTestCase {
}
@Test
- public void testOnViewAttachedRegistersComputeInsetsListener() {
- mController.onViewAttached();
- verify(mViewTreeObserver).addOnComputeInternalInsetsListener(any());
- }
-
- @Test
- public void testOnViewDetachedUnregistersComputeInsetsListener() {
- mController.onViewDetached();
- verify(mViewTreeObserver).removeOnComputeInternalInsetsListener(any());
- }
-
- @Test
- public void testComputeInsetsListenerReturnsRegion() {
- final ArgumentCaptor<ViewTreeObserver.OnComputeInternalInsetsListener>
- computeInsetsListenerCapture =
- ArgumentCaptor.forClass(ViewTreeObserver.OnComputeInternalInsetsListener.class);
- mController.onViewAttached();
- verify(mViewTreeObserver).addOnComputeInternalInsetsListener(
- computeInsetsListenerCapture.capture());
- final ViewTreeObserver.InternalInsetsInfo info = new ViewTreeObserver.InternalInsetsInfo();
- computeInsetsListenerCapture.getValue().onComputeInternalInsets(info);
- assertNotNull(info.touchableRegion);
- }
-
- @Test
public void testBurnInProtectionStartsWhenContentViewAttached() {
mController.onViewAttached();
verify(mHandler).postDelayed(any(Runnable.class), eq(BURN_IN_PROTECTION_UPDATE_INTERVAL));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java
index 58ffbfa76328..21768edd40e9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java
@@ -38,6 +38,7 @@ import androidx.test.filters.SmallTest;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.dreams.complication.DreamPreviewComplication;
import com.android.systemui.dreams.dagger.DreamOverlayComponent;
import com.android.systemui.dreams.touch.DreamOverlayTouchMonitor;
import com.android.systemui.util.concurrency.FakeExecutor;
@@ -95,6 +96,9 @@ public class DreamOverlayServiceTest extends SysuiTestCase {
@Mock
DreamOverlayStateController mStateController;
+ @Mock
+ DreamPreviewComplication mPreviewComplication;
+
DreamOverlayService mService;
@Before
@@ -119,7 +123,8 @@ public class DreamOverlayServiceTest extends SysuiTestCase {
mService = new DreamOverlayService(mContext, mMainExecutor,
mDreamOverlayComponentFactory,
mStateController,
- mKeyguardUpdateMonitor);
+ mKeyguardUpdateMonitor,
+ mPreviewComplication);
}
@Test
@@ -163,6 +168,31 @@ public class DreamOverlayServiceTest extends SysuiTestCase {
}
@Test
+ public void testPreviewModeFalseByDefault() {
+ mService.onBind(new Intent());
+
+ assertThat(mService.isPreviewMode()).isFalse();
+ }
+
+ @Test
+ public void testPreviewModeSetByIntentExtra() {
+ final Intent intent = new Intent();
+ intent.putExtra(DreamService.EXTRA_IS_PREVIEW, true);
+ mService.onBind(intent);
+
+ assertThat(mService.isPreviewMode()).isTrue();
+ }
+
+ @Test
+ public void testDreamLabel() {
+ final Intent intent = new Intent();
+ intent.putExtra(DreamService.EXTRA_DREAM_LABEL, "TestDream");
+ mService.onBind(intent);
+
+ assertThat(mService.getDreamLabel()).isEqualTo("TestDream");
+ }
+
+ @Test
public void testDestroy() {
mService.onDestroy();
mMainExecutor.runAllReady();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java
index 515a1ac814a9..49da4bd5a825 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java
@@ -61,7 +61,7 @@ public class DreamOverlayStateControllerTest extends SysuiTestCase {
}
@Test
- public void testStateChange() {
+ public void testStateChange_overlayActive() {
final DreamOverlayStateController stateController = new DreamOverlayStateController(
mExecutor);
stateController.addCallback(mCallback);
@@ -83,6 +83,38 @@ public class DreamOverlayStateControllerTest extends SysuiTestCase {
}
@Test
+ public void testStateChange_isPreviewMode() {
+ final DreamOverlayStateController stateController = new DreamOverlayStateController(
+ mExecutor);
+ stateController.addCallback(mCallback);
+ stateController.setPreviewMode(true);
+ mExecutor.runAllReady();
+
+ verify(mCallback).onStateChanged();
+ assertThat(stateController.isPreviewMode()).isTrue();
+
+ Mockito.clearInvocations(mCallback);
+ stateController.setPreviewMode(true);
+ mExecutor.runAllReady();
+ verify(mCallback, never()).onStateChanged();
+ }
+
+ @Test
+ public void testPreviewModeFalseByDefault() {
+ final DreamOverlayStateController stateController = new DreamOverlayStateController(
+ mExecutor);
+ assertThat(stateController.isPreviewMode()).isFalse();
+ }
+
+ @Test
+ public void testPreviewModeSetToTrue() {
+ final DreamOverlayStateController stateController = new DreamOverlayStateController(
+ mExecutor);
+ stateController.setPreviewMode(true);
+ assertThat(stateController.isPreviewMode()).isTrue();
+ }
+
+ @Test
public void testCallback() {
final DreamOverlayStateController stateController = new DreamOverlayStateController(
mExecutor);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java
index 7f72dda441d2..ad8d44d62a5c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java
@@ -29,8 +29,7 @@ import android.testing.AndroidTestingRunner;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.battery.BatteryMeterViewController;
-import com.android.systemui.statusbar.policy.BatteryController;
+import com.android.systemui.touch.TouchInsetManager;
import org.junit.Before;
import org.junit.Test;
@@ -46,37 +45,21 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase {
@Mock
DreamOverlayStatusBarView mView;
@Mock
- BatteryController mBatteryController;
- @Mock
- BatteryMeterViewController mBatteryMeterViewController;
- @Mock
ConnectivityManager mConnectivityManager;
@Mock
NetworkCapabilities mNetworkCapabilities;
@Mock
Network mNetwork;
+ @Mock
+ TouchInsetManager.TouchInsetSession mTouchSession;
DreamOverlayStatusBarViewController mController;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
- mController = new DreamOverlayStatusBarViewController(
- mContext, mView, mBatteryController, mBatteryMeterViewController,
- mConnectivityManager);
- }
-
- @Test
- public void testOnInitInitializesControllers() {
- mController.onInit();
- verify(mBatteryMeterViewController).init();
- }
-
- @Test
- public void testOnViewAttachedAddsBatteryControllerCallback() {
- mController.onViewAttached();
- verify(mBatteryController)
- .addCallback(any(BatteryController.BatteryStateChangeCallback.class));
+ mController = new DreamOverlayStatusBarViewController(mView, mConnectivityManager,
+ mTouchSession);
}
@Test
@@ -113,13 +96,6 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase {
}
@Test
- public void testOnViewDetachedRemovesBatteryControllerCallback() {
- mController.onViewDetached();
- verify(mBatteryController)
- .removeCallback(any(BatteryController.BatteryStateChangeCallback.class));
- }
-
- @Test
public void testOnViewDetachedUnregistersNetworkCallback() {
mController.onViewDetached();
verify(mConnectivityManager)
@@ -127,26 +103,6 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase {
}
@Test
- public void testBatteryPercentTextShownWhenBatteryLevelChangesWhileCharging() {
- final ArgumentCaptor<BatteryController.BatteryStateChangeCallback> callbackCapture =
- ArgumentCaptor.forClass(BatteryController.BatteryStateChangeCallback.class);
- mController.onViewAttached();
- verify(mBatteryController).addCallback(callbackCapture.capture());
- callbackCapture.getValue().onBatteryLevelChanged(1, true, true);
- verify(mView).showBatteryPercentText(true);
- }
-
- @Test
- public void testBatteryPercentTextHiddenWhenBatteryLevelChangesWhileNotCharging() {
- final ArgumentCaptor<BatteryController.BatteryStateChangeCallback> callbackCapture =
- ArgumentCaptor.forClass(BatteryController.BatteryStateChangeCallback.class);
- mController.onViewAttached();
- verify(mBatteryController).addCallback(callbackCapture.capture());
- callbackCapture.getValue().onBatteryLevelChanged(1, true, false);
- verify(mView).showBatteryPercentText(false);
- }
-
- @Test
public void testWifiStatusHiddenWhenWifiBecomesAvailable() {
// Make sure wifi starts out unavailable when onViewAttached is called.
when(mNetworkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationLayoutEngineTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationLayoutEngineTest.java
index 64b267d6e142..51dcf2ec18f4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationLayoutEngineTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationLayoutEngineTest.java
@@ -29,6 +29,7 @@ import androidx.test.filters.SmallTest;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.touch.TouchInsetManager;
import org.junit.Before;
import org.junit.Test;
@@ -46,6 +47,9 @@ public class ComplicationLayoutEngineTest extends SysuiTestCase {
@Mock
ConstraintLayout mLayout;
+ @Mock
+ TouchInsetManager.TouchInsetSession mTouchSession;
+
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
@@ -112,7 +116,8 @@ public class ComplicationLayoutEngineTest extends SysuiTestCase {
Complication.CATEGORY_STANDARD,
mLayout);
- final ComplicationLayoutEngine engine = new ComplicationLayoutEngine(mLayout, 0);
+ final ComplicationLayoutEngine engine =
+ new ComplicationLayoutEngine(mLayout, 0, mTouchSession);
addComplication(engine, firstViewInfo);
// Ensure the view is added to the top end corner
@@ -139,7 +144,8 @@ public class ComplicationLayoutEngineTest extends SysuiTestCase {
Complication.CATEGORY_STANDARD,
mLayout);
- final ComplicationLayoutEngine engine = new ComplicationLayoutEngine(mLayout, 0);
+ final ComplicationLayoutEngine engine =
+ new ComplicationLayoutEngine(mLayout, 0, mTouchSession);
addComplication(engine, firstViewInfo);
// Ensure the view is added to the top end corner
@@ -155,7 +161,8 @@ public class ComplicationLayoutEngineTest extends SysuiTestCase {
*/
@Test
public void testDirectionLayout() {
- final ComplicationLayoutEngine engine = new ComplicationLayoutEngine(mLayout, 0);
+ final ComplicationLayoutEngine engine =
+ new ComplicationLayoutEngine(mLayout, 0, mTouchSession);
final ViewInfo firstViewInfo = new ViewInfo(
new ComplicationLayoutParams(
@@ -203,7 +210,8 @@ public class ComplicationLayoutEngineTest extends SysuiTestCase {
*/
@Test
public void testPositionLayout() {
- final ComplicationLayoutEngine engine = new ComplicationLayoutEngine(mLayout, 0);
+ final ComplicationLayoutEngine engine =
+ new ComplicationLayoutEngine(mLayout, 0, mTouchSession);
final ViewInfo firstViewInfo = new ViewInfo(
new ComplicationLayoutParams(
@@ -290,7 +298,8 @@ public class ComplicationLayoutEngineTest extends SysuiTestCase {
@Test
public void testMargin() {
final int margin = 5;
- final ComplicationLayoutEngine engine = new ComplicationLayoutEngine(mLayout, margin);
+ final ComplicationLayoutEngine engine =
+ new ComplicationLayoutEngine(mLayout, margin, mTouchSession);
final ViewInfo firstViewInfo = new ViewInfo(
new ComplicationLayoutParams(
@@ -364,7 +373,8 @@ public class ComplicationLayoutEngineTest extends SysuiTestCase {
*/
@Test
public void testRemoval() {
- final ComplicationLayoutEngine engine = new ComplicationLayoutEngine(mLayout, 0);
+ final ComplicationLayoutEngine engine =
+ new ComplicationLayoutEngine(mLayout, 0, mTouchSession);
final ViewInfo firstViewInfo = new ViewInfo(
new ComplicationLayoutParams(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsDebugTest.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsDebugTest.kt
index 4cc5673eaf97..23a5b2b2c5b7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsDebugTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsDebugTest.kt
@@ -20,7 +20,7 @@ import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager.NameNotFoundException
import android.content.res.Resources
-import androidx.test.filters.SmallTest
+import android.test.suitebuilder.annotation.SmallTest
import com.android.internal.statusbar.IStatusBarService
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
@@ -35,6 +35,8 @@ import org.junit.Assert
import org.junit.Before
import org.junit.Test
import org.mockito.Mock
+import org.mockito.Mockito.anyBoolean
+import org.mockito.Mockito.anyString
import org.mockito.Mockito.inOrder
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
@@ -57,6 +59,7 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
@Mock private lateinit var mFlagManager: FlagManager
@Mock private lateinit var mMockContext: Context
@Mock private lateinit var mSecureSettings: SecureSettings
+ @Mock private lateinit var mSystemProperties: SystemPropertiesHelper
@Mock private lateinit var mResources: Resources
@Mock private lateinit var mDumpManager: DumpManager
@Mock private lateinit var mBarService: IStatusBarService
@@ -71,12 +74,13 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
mFlagManager,
mMockContext,
mSecureSettings,
+ mSystemProperties,
mResources,
mDumpManager,
{ mFlagMap },
mBarService
)
- verify(mFlagManager).restartAction = any()
+ verify(mFlagManager).onSettingsChangedAction = any()
mBroadcastReceiver = withArgCaptor {
verify(mMockContext).registerReceiver(capture(), any(), nullable(), nullable(),
any())
@@ -123,6 +127,22 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
}
@Test
+ fun testReadSysPropBooleanFlag() {
+ whenever(mSystemProperties.getBoolean(anyString(), anyBoolean())).thenAnswer {
+ if ("b".equals(it.getArgument<String?>(0))) {
+ return@thenAnswer true
+ }
+ return@thenAnswer it.getArgument(1)
+ }
+
+ assertThat(mFeatureFlagsDebug.isEnabled(SysPropBooleanFlag(1, "a"))).isFalse()
+ assertThat(mFeatureFlagsDebug.isEnabled(SysPropBooleanFlag(2, "b"))).isTrue()
+ assertThat(mFeatureFlagsDebug.isEnabled(SysPropBooleanFlag(3, "c", true))).isTrue()
+ assertThat(mFeatureFlagsDebug.isEnabled(SysPropBooleanFlag(4, "d", false))).isFalse()
+ assertThat(mFeatureFlagsDebug.isEnabled(SysPropBooleanFlag(5, "e"))).isFalse()
+ }
+
+ @Test
fun testReadStringFlag() {
whenever(mFlagManager.readFlagValue<String>(eq(3), any())).thenReturn("foo")
whenever(mFlagManager.readFlagValue<String>(eq(4), any())).thenReturn("bar")
@@ -259,7 +279,7 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
verify(mFlagManager, times(numReads)).readFlagValue(eq(id), any<FlagSerializer<*>>())
verify(mFlagManager).idToSettingsKey(eq(id))
verify(mSecureSettings).putString(eq("key-$id"), eq(data))
- verify(mFlagManager).dispatchListenersAndMaybeRestart(eq(id))
+ verify(mFlagManager).dispatchListenersAndMaybeRestart(eq(id), any())
}.verifyNoMoreInteractions()
verifyNoMoreInteractions(mFlagManager, mSecureSettings)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsReleaseTest.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsReleaseTest.kt
index b5e6602594eb..ad304c49bd41 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsReleaseTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsReleaseTest.kt
@@ -17,7 +17,7 @@ package com.android.systemui.flags
import android.content.pm.PackageManager.NameNotFoundException
import android.content.res.Resources
-import androidx.test.filters.SmallTest
+import android.test.suitebuilder.annotation.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
import com.android.systemui.util.mockito.any
@@ -44,12 +44,13 @@ class FeatureFlagsReleaseTest : SysuiTestCase() {
private lateinit var mFeatureFlagsRelease: FeatureFlagsRelease
@Mock private lateinit var mResources: Resources
+ @Mock private lateinit var mSystemProperties: SystemPropertiesHelper
@Mock private lateinit var mDumpManager: DumpManager
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
- mFeatureFlagsRelease = FeatureFlagsRelease(mResources, mDumpManager)
+ mFeatureFlagsRelease = FeatureFlagsRelease(mResources, mSystemProperties, mDumpManager)
}
@After
@@ -87,6 +88,17 @@ class FeatureFlagsReleaseTest : SysuiTestCase() {
}
@Test
+ fun testSysPropBooleanFlag() {
+ val flagId = 213
+ val flagName = "sys_prop_flag"
+ val flagDefault = true
+
+ val flag = SysPropBooleanFlag(flagId, flagName, flagDefault)
+ whenever(mSystemProperties.getBoolean(flagName, flagDefault)).thenReturn(flagDefault)
+ assertThat(mFeatureFlagsRelease.isEnabled(flag)).isEqualTo(flagDefault)
+ }
+
+ @Test
fun testDump() {
val flag1 = BooleanFlag(1, true)
val flag2 = ResourceBooleanFlag(2, 1002)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FlagManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/FlagManagerTest.kt
index 644bd2147611..a2eca81b04ed 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/FlagManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FlagManagerTest.kt
@@ -19,7 +19,7 @@ import android.content.Context
import android.database.ContentObserver
import android.net.Uri
import android.os.Handler
-import androidx.test.filters.SmallTest
+import android.test.suitebuilder.annotation.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.eq
@@ -130,14 +130,14 @@ class FlagManagerTest : SysuiTestCase() {
mFlagManager.addListener(BooleanFlag(1, true), listener1)
mFlagManager.addListener(BooleanFlag(10, true), listener10)
- mFlagManager.dispatchListenersAndMaybeRestart(1)
+ mFlagManager.dispatchListenersAndMaybeRestart(1, null)
val flagEvent1 = withArgCaptor<FlagListenable.FlagEvent> {
verify(listener1).onFlagChanged(capture())
}
assertThat(flagEvent1.flagId).isEqualTo(1)
verifyNoMoreInteractions(listener1, listener10)
- mFlagManager.dispatchListenersAndMaybeRestart(10)
+ mFlagManager.dispatchListenersAndMaybeRestart(10, null)
val flagEvent10 = withArgCaptor<FlagListenable.FlagEvent> {
verify(listener10).onFlagChanged(capture())
}
@@ -151,14 +151,14 @@ class FlagManagerTest : SysuiTestCase() {
mFlagManager.addListener(BooleanFlag(1, true), listener)
mFlagManager.addListener(BooleanFlag(10, true), listener)
- mFlagManager.dispatchListenersAndMaybeRestart(1)
+ mFlagManager.dispatchListenersAndMaybeRestart(1, null)
val flagEvent1 = withArgCaptor<FlagListenable.FlagEvent> {
verify(listener).onFlagChanged(capture())
}
assertThat(flagEvent1.flagId).isEqualTo(1)
verifyNoMoreInteractions(listener)
- mFlagManager.dispatchListenersAndMaybeRestart(10)
+ mFlagManager.dispatchListenersAndMaybeRestart(10, null)
val flagEvent10 = withArgCaptor<FlagListenable.FlagEvent> {
verify(listener, times(2)).onFlagChanged(capture())
}
@@ -169,8 +169,7 @@ class FlagManagerTest : SysuiTestCase() {
@Test
fun testRestartWithNoListeners() {
val restartAction = mock<Consumer<Boolean>>()
- mFlagManager.restartAction = restartAction
- mFlagManager.dispatchListenersAndMaybeRestart(1)
+ mFlagManager.dispatchListenersAndMaybeRestart(1, restartAction)
verify(restartAction).accept(eq(false))
verifyNoMoreInteractions(restartAction)
}
@@ -178,11 +177,10 @@ class FlagManagerTest : SysuiTestCase() {
@Test
fun testListenerCanSuppressRestart() {
val restartAction = mock<Consumer<Boolean>>()
- mFlagManager.restartAction = restartAction
mFlagManager.addListener(BooleanFlag(1, true)) { event ->
event.requestNoRestart()
}
- mFlagManager.dispatchListenersAndMaybeRestart(1)
+ mFlagManager.dispatchListenersAndMaybeRestart(1, restartAction)
verify(restartAction).accept(eq(true))
verifyNoMoreInteractions(restartAction)
}
@@ -190,11 +188,10 @@ class FlagManagerTest : SysuiTestCase() {
@Test
fun testListenerOnlySuppressesRestartForOwnFlag() {
val restartAction = mock<Consumer<Boolean>>()
- mFlagManager.restartAction = restartAction
mFlagManager.addListener(BooleanFlag(10, true)) { event ->
event.requestNoRestart()
}
- mFlagManager.dispatchListenersAndMaybeRestart(1)
+ mFlagManager.dispatchListenersAndMaybeRestart(1, restartAction)
verify(restartAction).accept(eq(false))
verifyNoMoreInteractions(restartAction)
}
@@ -202,14 +199,13 @@ class FlagManagerTest : SysuiTestCase() {
@Test
fun testRestartWhenNotAllListenersRequestSuppress() {
val restartAction = mock<Consumer<Boolean>>()
- mFlagManager.restartAction = restartAction
mFlagManager.addListener(BooleanFlag(10, true)) { event ->
event.requestNoRestart()
}
mFlagManager.addListener(BooleanFlag(10, true)) {
// do not request
}
- mFlagManager.dispatchListenersAndMaybeRestart(1)
+ mFlagManager.dispatchListenersAndMaybeRestart(1, restartAction)
verify(restartAction).accept(eq(false))
verifyNoMoreInteractions(restartAction)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
index 71fc8ee6cce8..953be7d6f002 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
@@ -19,7 +19,6 @@ package com.android.systemui.globalactions;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
@@ -57,13 +56,11 @@ import com.android.systemui.SysuiTestCase;
import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.colorextraction.SysuiColorExtractor;
-import com.android.systemui.model.SysUiState;
import com.android.systemui.plugins.GlobalActions;
import com.android.systemui.settings.UserContextProvider;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.statusbar.phone.StatusBar;
-import com.android.systemui.statusbar.phone.SystemUIDialogManager;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.telephony.TelephonyListenerManager;
@@ -112,7 +109,6 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase {
@Mock private UiEventLogger mUiEventLogger;
@Mock private RingerModeTracker mRingerModeTracker;
@Mock private RingerModeLiveData mRingerModeLiveData;
- @Mock private SysUiState mSysUiState;
@Mock private PackageManager mPackageManager;
@Mock private Handler mHandler;
@Mock private UserContextProvider mUserContextProvider;
@@ -120,7 +116,6 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase {
@Mock private StatusBar mStatusBar;
@Mock private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@Mock private DialogLaunchAnimator mDialogLaunchAnimator;
- @Mock private SystemUIDialogManager mDialogManager;
private TestableLooper mTestableLooper;
@@ -161,19 +156,16 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase {
mBackgroundExecutor,
mUiEventLogger,
mRingerModeTracker,
- mSysUiState,
mHandler,
mPackageManager,
Optional.of(mStatusBar),
mKeyguardUpdateMonitor,
- mDialogLaunchAnimator,
- mDialogManager);
+ mDialogLaunchAnimator);
mGlobalActionsDialogLite.setZeroDialogPressDelayForTesting();
ColorExtractor.GradientColors backdropColors = new ColorExtractor.GradientColors();
backdropColors.setMainColor(Color.BLACK);
when(mColorExtractor.getNeutralColors()).thenReturn(backdropColors);
- when(mSysUiState.setFlag(anyInt(), anyBoolean())).thenReturn(mSysUiState);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt
index e606be179cc6..b359ae5317b1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt
@@ -33,10 +33,11 @@ import com.android.systemui.statusbar.NotificationLockscreenUserManager
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.SysuiStatusBarStateController
import com.android.systemui.statusbar.phone.KeyguardBypassController
-import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.statusbar.policy.FakeConfigurationController
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.animation.UniqueObjectHostView
-import junit.framework.Assert
+import com.android.systemui.util.mockito.any
+import com.google.common.truth.Truth.assertThat
import org.junit.Assert.assertNotNull
import org.junit.Before
import org.junit.Rule
@@ -44,16 +45,16 @@ import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.ArgumentMatchers
+import org.mockito.ArgumentMatchers.anyBoolean
+import org.mockito.ArgumentMatchers.anyLong
import org.mockito.Captor
import org.mockito.Mock
import org.mockito.Mockito.`when`
-import org.mockito.Mockito.any
-import org.mockito.Mockito.anyBoolean
-import org.mockito.Mockito.anyLong
import org.mockito.Mockito.clearInvocations
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.junit.MockitoJUnit
+import org.mockito.Mockito.`when` as whenever
@SmallTest
@RunWith(AndroidTestingRunner::class)
@@ -83,8 +84,6 @@ class MediaHierarchyManagerTest : SysuiTestCase() {
@Mock
private lateinit var keyguardViewController: KeyguardViewController
@Mock
- private lateinit var configurationController: ConfigurationController
- @Mock
private lateinit var uniqueObjectHostView: UniqueObjectHostView
@Mock
private lateinit var dreamOverlayStateController: DreamOverlayStateController
@@ -97,6 +96,7 @@ class MediaHierarchyManagerTest : SysuiTestCase() {
val mockito = MockitoJUnit.rule()
private lateinit var mediaHiearchyManager: MediaHierarchyManager
private lateinit var mediaFrame: ViewGroup
+ private val configurationController = FakeConfigurationController()
@Before
fun setup() {
@@ -176,12 +176,7 @@ class MediaHierarchyManagerTest : SysuiTestCase() {
@Test
fun testGoingToFullShade() {
- // Let's set it onto Lock screen
- `when`(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD)
- `when`(notificationLockscreenUserManager.shouldShowLockscreenNotifications()).thenReturn(
- true)
- statusBarCallback.value.onStatePreChange(StatusBarState.SHADE, StatusBarState.KEYGUARD)
- clearInvocations(mediaCarouselController)
+ goToLockscreen()
// Let's transition all the way to full shade
mediaHiearchyManager.setTransitionToFullShadeAmount(100000f)
@@ -204,41 +199,48 @@ class MediaHierarchyManagerTest : SysuiTestCase() {
// Let's make sure alpha is set
mediaHiearchyManager.setTransitionToFullShadeAmount(2.0f)
- Assert.assertTrue("alpha should not be 1.0f when cross fading", mediaFrame.alpha != 1.0f)
+ assertThat(mediaFrame.alpha).isNotEqualTo(1.0f)
}
@Test
fun testTransformationOnLockScreenIsFading() {
- // Let's set it onto Lock screen
- `when`(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD)
- `when`(notificationLockscreenUserManager.shouldShowLockscreenNotifications()).thenReturn(
- true)
- statusBarCallback.value.onStatePreChange(StatusBarState.SHADE, StatusBarState.KEYGUARD)
- clearInvocations(mediaCarouselController)
+ goToLockscreen()
+ expandQS()
+
+ val transformType = mediaHiearchyManager.calculateTransformationType()
+ assertThat(transformType).isEqualTo(MediaHierarchyManager.TRANSFORMATION_TYPE_FADE)
+ }
+
+ @Test
+ fun calculateTransformationType_onLockShade_inSplitShade_goingToFullShade_returnsTransition() {
+ enableSplitShade()
+ goToLockscreen()
+ expandQS()
+ mediaHiearchyManager.setTransitionToFullShadeAmount(10000f)
+
+ val transformType = mediaHiearchyManager.calculateTransformationType()
+ assertThat(transformType).isEqualTo(MediaHierarchyManager.TRANSFORMATION_TYPE_TRANSITION)
+ }
+
+ @Test
+ fun calculateTransformationType_onLockShade_inSplitShade_notExpanding_returnsFade() {
+ enableSplitShade()
+ goToLockscreen()
+ goToLockedShade()
+ expandQS()
+ mediaHiearchyManager.setTransitionToFullShadeAmount(0f)
- // Let's transition from lockscreen to qs
- mediaHiearchyManager.qsExpansion = 1.0f
val transformType = mediaHiearchyManager.calculateTransformationType()
- Assert.assertTrue("media isn't transforming to qs with a fade",
- transformType == MediaHierarchyManager.TRANSFORMATION_TYPE_FADE)
+ assertThat(transformType).isEqualTo(MediaHierarchyManager.TRANSFORMATION_TYPE_FADE)
}
@Test
fun testTransformationOnLockScreenToQQSisFading() {
- // Let's set it onto Lock screen
- `when`(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD)
- `when`(notificationLockscreenUserManager.shouldShowLockscreenNotifications()).thenReturn(
- true)
- statusBarCallback.value.onStatePreChange(StatusBarState.SHADE, StatusBarState.KEYGUARD)
- clearInvocations(mediaCarouselController)
+ goToLockscreen()
+ goToLockedShade()
- // Let's transition from lockscreen to qs
- `when`(statusBarStateController.state).thenReturn(StatusBarState.SHADE_LOCKED)
- statusBarCallback.value.onStatePreChange(StatusBarState.KEYGUARD,
- StatusBarState.SHADE_LOCKED)
val transformType = mediaHiearchyManager.calculateTransformationType()
- Assert.assertTrue("media isn't transforming to qqswith a fade",
- transformType == MediaHierarchyManager.TRANSFORMATION_TYPE_FADE)
+ assertThat(transformType).isEqualTo(MediaHierarchyManager.TRANSFORMATION_TYPE_FADE)
}
@Test
@@ -254,4 +256,32 @@ class MediaHierarchyManagerTest : SysuiTestCase() {
verify(mediaCarouselController).closeGuts()
}
-} \ No newline at end of file
+
+ private fun enableSplitShade() {
+ context.getOrCreateTestableResources().addOverride(
+ R.bool.config_use_split_notification_shade, true
+ )
+ configurationController.notifyConfigurationChanged()
+ }
+
+ private fun goToLockscreen() {
+ whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD)
+ whenever(notificationLockscreenUserManager.shouldShowLockscreenNotifications()).thenReturn(
+ true
+ )
+ statusBarCallback.value.onStatePreChange(StatusBarState.SHADE, StatusBarState.KEYGUARD)
+ clearInvocations(mediaCarouselController)
+ }
+
+ private fun goToLockedShade() {
+ whenever(statusBarStateController.state).thenReturn(StatusBarState.SHADE_LOCKED)
+ statusBarCallback.value.onStatePreChange(
+ StatusBarState.KEYGUARD,
+ StatusBarState.SHADE_LOCKED
+ )
+ }
+
+ private fun expandQS() {
+ mediaHiearchyManager.qsExpansion = 1.0f
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
index c5c4d79e043d..2be30b39763e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
@@ -45,7 +45,6 @@ import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.phone.ShadeController;
-import com.android.systemui.statusbar.phone.SystemUIDialogManager;
import org.junit.Before;
import org.junit.Test;
@@ -68,7 +67,6 @@ public class MediaOutputBaseDialogTest extends SysuiTestCase {
mock(NotificationEntryManager.class);
private final UiEventLogger mUiEventLogger = mock(UiEventLogger.class);
private final DialogLaunchAnimator mDialogLaunchAnimator = mock(DialogLaunchAnimator.class);
- private final SystemUIDialogManager mDialogManager = mock(SystemUIDialogManager.class);
private MediaOutputBaseDialogImpl mMediaOutputBaseDialogImpl;
private MediaOutputController mMediaOutputController;
@@ -82,7 +80,7 @@ public class MediaOutputBaseDialogTest extends SysuiTestCase {
public void setUp() {
mMediaOutputController = new MediaOutputController(mContext, TEST_PACKAGE, false,
mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter,
- mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator, mDialogManager);
+ mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator);
mMediaOutputBaseDialogImpl = new MediaOutputBaseDialogImpl(mContext,
mMediaOutputController);
mMediaOutputBaseDialogImpl.onCreate(new Bundle());
@@ -175,7 +173,7 @@ public class MediaOutputBaseDialogTest extends SysuiTestCase {
class MediaOutputBaseDialogImpl extends MediaOutputBaseDialog {
MediaOutputBaseDialogImpl(Context context, MediaOutputController mediaOutputController) {
- super(context, mediaOutputController, mDialogManager);
+ super(context, mediaOutputController);
mAdapter = mMediaOutputBaseAdapter;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
index bdc311725880..789822e262d5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
@@ -55,7 +55,6 @@ import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
import com.android.systemui.statusbar.phone.ShadeController;
-import com.android.systemui.statusbar.phone.SystemUIDialogManager;
import org.junit.Before;
import org.junit.Test;
@@ -94,7 +93,6 @@ public class MediaOutputControllerTest extends SysuiTestCase {
private CommonNotifCollection mNotifCollection = mock(CommonNotifCollection.class);
private final UiEventLogger mUiEventLogger = mock(UiEventLogger.class);
private final DialogLaunchAnimator mDialogLaunchAnimator = mock(DialogLaunchAnimator.class);
- private final SystemUIDialogManager mDialogManager = mock(SystemUIDialogManager.class);
private Context mSpyContext;
private MediaOutputController mMediaOutputController;
@@ -117,7 +115,7 @@ public class MediaOutputControllerTest extends SysuiTestCase {
mMediaOutputController = new MediaOutputController(mSpyContext, TEST_PACKAGE_NAME, false,
mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter,
- mNotifCollection, mUiEventLogger, mDialogLaunchAnimator, mDialogManager);
+ mNotifCollection, mUiEventLogger, mDialogLaunchAnimator);
mLocalMediaManager = spy(mMediaOutputController.mLocalMediaManager);
mMediaOutputController.mLocalMediaManager = mLocalMediaManager;
MediaDescription.Builder builder = new MediaDescription.Builder();
@@ -161,7 +159,7 @@ public class MediaOutputControllerTest extends SysuiTestCase {
public void start_withoutPackageName_verifyMediaControllerInit() {
mMediaOutputController = new MediaOutputController(mSpyContext, null, false,
mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter,
- mNotifCollection, mUiEventLogger, mDialogLaunchAnimator, mDialogManager);
+ mNotifCollection, mUiEventLogger, mDialogLaunchAnimator);
mMediaOutputController.start(mCb);
@@ -182,7 +180,7 @@ public class MediaOutputControllerTest extends SysuiTestCase {
public void stop_withoutPackageName_verifyMediaControllerDeinit() {
mMediaOutputController = new MediaOutputController(mSpyContext, null, false,
mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter,
- mNotifCollection, mUiEventLogger, mDialogLaunchAnimator, mDialogManager);
+ mNotifCollection, mUiEventLogger, mDialogLaunchAnimator);
mMediaOutputController.start(mCb);
@@ -453,7 +451,7 @@ public class MediaOutputControllerTest extends SysuiTestCase {
public void getNotificationLargeIcon_withoutPackageName_returnsNull() {
mMediaOutputController = new MediaOutputController(mSpyContext, null, false,
mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter,
- mNotifCollection, mUiEventLogger, mDialogLaunchAnimator, mDialogManager);
+ mNotifCollection, mUiEventLogger, mDialogLaunchAnimator);
assertThat(mMediaOutputController.getNotificationIcon()).isNull();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java
index ada8d3592012..8a3ea562269d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java
@@ -40,7 +40,6 @@ import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.phone.ShadeController;
-import com.android.systemui.statusbar.phone.SystemUIDialogManager;
import org.junit.After;
import org.junit.Before;
@@ -68,7 +67,6 @@ public class MediaOutputDialogTest extends SysuiTestCase {
mock(NotificationEntryManager.class);
private final UiEventLogger mUiEventLogger = mock(UiEventLogger.class);
private final DialogLaunchAnimator mDialogLaunchAnimator = mock(DialogLaunchAnimator.class);
- private final SystemUIDialogManager mDialogManager = mock(SystemUIDialogManager.class);
private MediaOutputDialog mMediaOutputDialog;
private MediaOutputController mMediaOutputController;
@@ -78,10 +76,10 @@ public class MediaOutputDialogTest extends SysuiTestCase {
public void setUp() {
mMediaOutputController = new MediaOutputController(mContext, TEST_PACKAGE, false,
mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter,
- mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator, mDialogManager);
+ mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator);
mMediaOutputController.mLocalMediaManager = mLocalMediaManager;
mMediaOutputDialog = new MediaOutputDialog(mContext, false,
- mMediaOutputController, mUiEventLogger, mDialogManager);
+ mMediaOutputController, mUiEventLogger);
mMediaOutputDialog.show();
when(mLocalMediaManager.getCurrentConnectedDevice()).thenReturn(mMediaDevice);
@@ -127,7 +125,7 @@ public class MediaOutputDialogTest extends SysuiTestCase {
// and verify if the calling times increases.
public void onCreate_ShouldLogVisibility() {
MediaOutputDialog testDialog = new MediaOutputDialog(mContext, false,
- mMediaOutputController, mUiEventLogger, mDialogManager);
+ mMediaOutputController, mUiEventLogger);
testDialog.show();
testDialog.dismissDialog();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputGroupDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputGroupDialogTest.java
index b114452facc3..e8cd6c88956d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputGroupDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputGroupDialogTest.java
@@ -38,7 +38,6 @@ import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.phone.ShadeController;
-import com.android.systemui.statusbar.phone.SystemUIDialogManager;
import org.junit.After;
import org.junit.Before;
@@ -67,7 +66,6 @@ public class MediaOutputGroupDialogTest extends SysuiTestCase {
mock(NotificationEntryManager.class);
private final UiEventLogger mUiEventLogger = mock(UiEventLogger.class);
private final DialogLaunchAnimator mDialogLaunchAnimator = mock(DialogLaunchAnimator.class);
- private final SystemUIDialogManager mDialogManager = mock(SystemUIDialogManager.class);
private MediaOutputGroupDialog mMediaOutputGroupDialog;
private MediaOutputController mMediaOutputController;
@@ -77,10 +75,10 @@ public class MediaOutputGroupDialogTest extends SysuiTestCase {
public void setUp() {
mMediaOutputController = new MediaOutputController(mContext, TEST_PACKAGE, false,
mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter,
- mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator, mDialogManager);
+ mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator);
mMediaOutputController.mLocalMediaManager = mLocalMediaManager;
mMediaOutputGroupDialog = new MediaOutputGroupDialog(mContext, false,
- mMediaOutputController, mDialogManager);
+ mMediaOutputController);
mMediaOutputGroupDialog.show();
when(mLocalMediaManager.getSelectedMediaDevice()).thenReturn(mMediaDevices);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/FooterActionsControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/FooterActionsControllerTest.kt
index 8ce50a680e50..f736f26e5c46 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/FooterActionsControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/FooterActionsControllerTest.kt
@@ -100,7 +100,9 @@ class FooterActionsControllerTest : LeakCheckedTest() {
@After
fun tearDown() {
- ViewUtils.detachView(view)
+ if (view.isAttachedToWindow) {
+ ViewUtils.detachView(view)
+ }
}
@Test
@@ -139,8 +141,7 @@ class FooterActionsControllerTest : LeakCheckedTest() {
@Test
fun testMultiUserSwitchUpdatedWhenSettingChanged() {
- // When expanded, listening is true
- controller.setListening(true)
+ // Always listening to setting while View is attached
testableLooper.processAllMessages()
val multiUserSwitch = view.requireViewById<View>(R.id.multi_user_switch)
@@ -156,4 +157,24 @@ class FooterActionsControllerTest : LeakCheckedTest() {
assertThat(multiUserSwitch.visibility).isEqualTo(View.VISIBLE)
}
+
+ @Test
+ fun testMultiUserSettingNotListenedAfterDetach() {
+ testableLooper.processAllMessages()
+
+ val multiUserSwitch = view.requireViewById<View>(R.id.multi_user_switch)
+ assertThat(multiUserSwitch.visibility).isNotEqualTo(View.VISIBLE)
+
+ ViewUtils.detachView(view)
+
+ // The setting is only used as an indicator for whether the view should refresh. The actual
+ // value of the setting is ignored; isMultiUserEnabled is the source of truth
+ whenever(multiUserSwitchController.isMultiUserEnabled).thenReturn(true)
+
+ // Changing the value of USER_SWITCHER_ENABLED should cause the view to update
+ fakeSettings.putIntForUser(Settings.Global.USER_SWITCHER_ENABLED, 1, userTracker.userId)
+ testableLooper.processAllMessages()
+
+ assertThat(multiUserSwitch.visibility).isNotEqualTo(View.VISIBLE)
+ }
} \ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
index 9076e1607be5..75ccd8f03a01 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
@@ -23,7 +23,7 @@ import com.android.systemui.statusbar.phone.LSShadeTransitionLogger
import com.android.systemui.statusbar.phone.NotificationPanelViewController
import com.android.systemui.statusbar.phone.ScrimController
import com.android.systemui.statusbar.phone.StatusBar
-import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.statusbar.policy.FakeConfigurationController
import org.junit.After
import org.junit.Assert.assertFalse
import org.junit.Assert.assertNotNull
@@ -66,7 +66,6 @@ class LockscreenShadeTransitionControllerTest : SysuiTestCase() {
@Mock lateinit var wakefulnessLifecycle: WakefulnessLifecycle
@Mock lateinit var mediaHierarchyManager: MediaHierarchyManager
@Mock lateinit var scrimController: ScrimController
- @Mock lateinit var configurationController: ConfigurationController
@Mock lateinit var falsingManager: FalsingManager
@Mock lateinit var notificationPanelController: NotificationPanelViewController
@Mock lateinit var nsslController: NotificationStackScrollLayoutController
@@ -77,6 +76,8 @@ class LockscreenShadeTransitionControllerTest : SysuiTestCase() {
@Mock lateinit var qS: QS
@JvmField @Rule val mockito = MockitoJUnit.rule()
+ private val configurationController = FakeConfigurationController()
+
@Before
fun setup() {
val helper = NotificationTestHelper(
@@ -244,4 +245,27 @@ class LockscreenShadeTransitionControllerTest : SysuiTestCase() {
verify(qS).setTransitionToFullShadeAmount(anyFloat(), anyFloat())
verify(depthController).transitionToFullShadeProgress = anyFloat()
}
+
+ @Test
+ fun setDragDownAmount_setsValueOnMediaHierarchyManager() {
+ transitionController.dragDownAmount = 10f
+
+ verify(mediaHierarchyManager).setTransitionToFullShadeAmount(10f)
+ }
+
+ @Test
+ fun setDragDownAmount_inSplitShade_setsValueOnMediaHierarchyManager() {
+ enableSplitShade()
+
+ transitionController.dragDownAmount = 10f
+
+ verify(mediaHierarchyManager).setTransitionToFullShadeAmount(10f)
+ }
+
+ private fun enableSplitShade() {
+ context.getOrCreateTestableResources().addOverride(
+ R.bool.config_use_split_notification_shade, true
+ )
+ configurationController.notifyConfigurationChanged()
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java
index a14ea54fc7e8..5f2bbd341962 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java
@@ -27,6 +27,7 @@ import static org.mockito.Mockito.when;
import android.content.res.Resources;
import android.hardware.display.AmbientDisplayConfiguration;
+import android.os.Handler;
import android.os.PowerManager;
import android.provider.Settings;
import android.test.suitebuilder.annotation.SmallTest;
@@ -61,6 +62,7 @@ import java.util.Optional;
public class DozeParametersTest extends SysuiTestCase {
private DozeParameters mDozeParameters;
+ @Mock Handler mHandler;
@Mock Resources mResources;
@Mock private AmbientDisplayConfiguration mAmbientDisplayConfiguration;
@Mock private AlwaysOnDisplayPolicy mAlwaysOnDisplayPolicy;
@@ -102,6 +104,8 @@ public class DozeParametersTest extends SysuiTestCase {
.thenReturn(mFoldAodAnimationController);
mDozeParameters = new DozeParameters(
+ mContext,
+ mHandler,
mResources,
mAmbientDisplayConfiguration,
mAlwaysOnDisplayPolicy,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java
index 38d7ce76f1b9..6ce3b4b7b6f9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java
@@ -59,6 +59,7 @@ import org.mockito.MockitoAnnotations;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
+import java.util.Optional;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@@ -97,7 +98,7 @@ public class DozeServiceHostTest extends SysuiTestCase {
mStatusBarStateController, mDeviceProvisionedController, mHeadsUpManager,
mBatteryController, mScrimController, () -> mBiometricUnlockController,
mKeyguardViewMediator, () -> mAssistManager, mDozeScrimController,
- mKeyguardUpdateMonitor, mPulseExpansionHandler,
+ mKeyguardUpdateMonitor, mPulseExpansionHandler, Optional.empty(),
mNotificationShadeWindowController, mNotificationWakeUpCoordinator,
mAuthController, mNotificationIconAreaController);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImplTest.java
index 671ab597eb2a..c797bc8bdf60 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImplTest.java
@@ -32,6 +32,8 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.IActivityManager;
+import android.content.pm.ActivityInfo;
+import android.content.res.Configuration;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper.RunWithLooper;
import android.view.View;
@@ -228,6 +230,36 @@ public class NotificationShadeWindowControllerImplTest extends SysuiTestCase {
}
@Test
+ public void rotationBecameAllowed_layoutParamsUpdated() {
+ mNotificationShadeWindowController.setKeyguardShowing(true);
+ when(mKeyguardStateController.isKeyguardScreenRotationAllowed()).thenReturn(false);
+ mNotificationShadeWindowController.onConfigChanged(new Configuration());
+ clearInvocations(mWindowManager);
+
+ when(mKeyguardStateController.isKeyguardScreenRotationAllowed()).thenReturn(true);
+ mNotificationShadeWindowController.onConfigChanged(new Configuration());
+
+ verify(mWindowManager).updateViewLayout(any(), mLayoutParameters.capture());
+ assertThat(mLayoutParameters.getValue().screenOrientation)
+ .isEqualTo(ActivityInfo.SCREEN_ORIENTATION_USER);
+ }
+
+ @Test
+ public void rotationBecameNotAllowed_layoutParamsUpdated() {
+ mNotificationShadeWindowController.setKeyguardShowing(true);
+ when(mKeyguardStateController.isKeyguardScreenRotationAllowed()).thenReturn(true);
+ mNotificationShadeWindowController.onConfigChanged(new Configuration());
+ clearInvocations(mWindowManager);
+
+ when(mKeyguardStateController.isKeyguardScreenRotationAllowed()).thenReturn(false);
+ mNotificationShadeWindowController.onConfigChanged(new Configuration());
+
+ verify(mWindowManager).updateViewLayout(any(), mLayoutParameters.capture());
+ assertThat(mLayoutParameters.getValue().screenOrientation)
+ .isEqualTo(ActivityInfo.SCREEN_ORIENTATION_NOSENSOR);
+ }
+
+ @Test
public void batchApplyWindowLayoutParams_doesNotDispatchEvents() {
mNotificationShadeWindowController.setForceDozeBrightness(true);
verify(mWindowManager).updateViewLayout(any(), any());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index c7db9e43166b..86109367d504 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -381,7 +381,7 @@ public class StatusBarTest extends SysuiTestCase {
mShadeController = new ShadeControllerImpl(mCommandQueue,
mStatusBarStateController, mNotificationShadeWindowController,
mStatusBarKeyguardViewManager, mContext.getSystemService(WindowManager.class),
- () -> Optional.of(mStatusBar), () -> mAssistManager, Optional.of(mBubbles));
+ () -> Optional.of(mStatusBar), () -> mAssistManager);
when(mOperatorNameViewControllerFactory.create(any()))
.thenReturn(mOperatorNameViewController);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/FakeConfigurationController.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/FakeConfigurationController.kt
new file mode 100644
index 000000000000..3a5d9ee16b0a
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/FakeConfigurationController.kt
@@ -0,0 +1,31 @@
+package com.android.systemui.statusbar.policy
+
+import android.content.res.Configuration
+
+/** Fake implementation of [ConfigurationController] for tests. */
+class FakeConfigurationController : ConfigurationController {
+
+ private var listener: ConfigurationController.ConfigurationListener? = null
+
+ override fun addCallback(listener: ConfigurationController.ConfigurationListener) {
+ this.listener = listener
+ }
+
+ override fun removeCallback(listener: ConfigurationController.ConfigurationListener) {
+ this.listener = null
+ }
+
+ override fun onConfigurationChanged(newConfiguration: Configuration?) {
+ listener?.onConfigChanged(newConfiguration)
+ }
+
+ override fun notifyThemeChanged() {
+ listener?.onThemeChanged()
+ }
+
+ fun notifyConfigurationChanged() {
+ onConfigurationChanged(newConfiguration = null)
+ }
+
+ override fun isLayoutRtl(): Boolean = false
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/touch/TouchInsetManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/touch/TouchInsetManagerTest.java
new file mode 100644
index 000000000000..14b9bfb1393f
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/touch/TouchInsetManagerTest.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.touch;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.graphics.Rect;
+import android.graphics.Region;
+import android.testing.AndroidTestingRunner;
+import android.view.View;
+import android.view.ViewRootImpl;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.time.FakeSystemClock;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class TouchInsetManagerTest extends SysuiTestCase {
+ @Mock
+ private View mRootView;
+
+ @Mock
+ private ViewRootImpl mRootViewImpl;
+
+ private FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock());
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ when(mRootView.getViewRootImpl()).thenReturn(mRootViewImpl);
+ }
+
+ @Test
+ public void testRootViewOnAttachedHandling() {
+ // Create inset manager
+ final TouchInsetManager insetManager = new TouchInsetManager(mFakeExecutor,
+ mRootView);
+
+ final ArgumentCaptor<View.OnAttachStateChangeListener> listener =
+ ArgumentCaptor.forClass(View.OnAttachStateChangeListener.class);
+
+ // Ensure manager has registered to listen to attached state of root view.
+ verify(mRootView).addOnAttachStateChangeListener(listener.capture());
+
+ // Trigger attachment and verify touchable region is set.
+ listener.getValue().onViewAttachedToWindow(mRootView);
+ verify(mRootViewImpl).setTouchableRegion(any());
+ }
+
+ @Test
+ public void testInsetRegionPropagation() {
+ // Create inset manager
+ final TouchInsetManager insetManager = new TouchInsetManager(mFakeExecutor,
+ mRootView);
+
+ // Create session
+ final TouchInsetManager.TouchInsetSession session = insetManager.createSession();
+
+ // Add a view to the session.
+ final Rect rect = new Rect(0, 0, 2, 2);
+
+ session.addViewToTracking(createView(rect));
+ mFakeExecutor.runAllReady();
+
+ // Check to see if view was properly accounted for.
+ final Region expectedRegion = Region.obtain();
+ expectedRegion.op(rect, Region.Op.UNION);
+ verify(mRootViewImpl).setTouchableRegion(eq(expectedRegion));
+ }
+
+ @Test
+ public void testMultipleRegions() {
+ // Create inset manager
+ final TouchInsetManager insetManager = new TouchInsetManager(mFakeExecutor,
+ mRootView);
+
+ // Create session
+ final TouchInsetManager.TouchInsetSession session = insetManager.createSession();
+
+ // Add a view to the session.
+ final Rect firstBounds = new Rect(0, 0, 2, 2);
+ session.addViewToTracking(createView(firstBounds));
+
+ mFakeExecutor.runAllReady();
+ clearInvocations(mRootViewImpl);
+
+ // Create second session
+ final TouchInsetManager.TouchInsetSession secondSession = insetManager.createSession();
+
+ // Add a view to the second session.
+ final Rect secondBounds = new Rect(4, 4, 8, 10);
+ secondSession.addViewToTracking(createView(secondBounds));
+
+ mFakeExecutor.runAllReady();
+
+ // Check to see if all views and sessions was properly accounted for.
+ {
+ final Region expectedRegion = Region.obtain();
+ expectedRegion.op(firstBounds, Region.Op.UNION);
+ expectedRegion.op(secondBounds, Region.Op.UNION);
+ verify(mRootViewImpl).setTouchableRegion(eq(expectedRegion));
+ }
+
+
+ clearInvocations(mRootViewImpl);
+
+ // clear first session, ensure second session is still reflected.
+ session.clear();
+ mFakeExecutor.runAllReady();
+ {
+ final Region expectedRegion = Region.obtain();
+ expectedRegion.op(firstBounds, Region.Op.UNION);
+ verify(mRootViewImpl).setTouchableRegion(eq(expectedRegion));
+ }
+ }
+
+ @Test
+ public void testMultipleViews() {
+ // Create inset manager
+ final TouchInsetManager insetManager = new TouchInsetManager(mFakeExecutor,
+ mRootView);
+
+ // Create session
+ final TouchInsetManager.TouchInsetSession session = insetManager.createSession();
+
+ // Add a view to the session.
+ final Rect firstViewBounds = new Rect(0, 0, 2, 2);
+ session.addViewToTracking(createView(firstViewBounds));
+
+ // only capture second invocation.
+ mFakeExecutor.runAllReady();
+ clearInvocations(mRootViewImpl);
+
+ // Add a second view to the session
+ final Rect secondViewBounds = new Rect(4, 4, 9, 10);
+ final View secondView = createView(secondViewBounds);
+ session.addViewToTracking(secondView);
+
+ mFakeExecutor.runAllReady();
+
+ // Check to see if all views and sessions was properly accounted for.
+ {
+ final Region expectedRegion = Region.obtain();
+ expectedRegion.op(firstViewBounds, Region.Op.UNION);
+ expectedRegion.op(secondViewBounds, Region.Op.UNION);
+ verify(mRootViewImpl).setTouchableRegion(eq(expectedRegion));
+ }
+
+ // Remove second view.
+ session.removeViewFromTracking(secondView);
+
+ clearInvocations(mRootViewImpl);
+ mFakeExecutor.runAllReady();
+
+ // Ensure first view still reflected in touch region.
+ {
+ final Region expectedRegion = Region.obtain();
+ expectedRegion.op(firstViewBounds, Region.Op.UNION);
+ verify(mRootViewImpl).setTouchableRegion(eq(expectedRegion));
+ }
+ }
+
+ private View createView(Rect bounds) {
+ final Rect rect = new Rect(bounds);
+ final View view = Mockito.mock(View.class);
+ doAnswer(invocation -> {
+ ((Rect) invocation.getArgument(0)).set(rect);
+ return null;
+ }).when(view).getBoundsOnScreen(any());
+
+ return view;
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/UserSwitcherActivityTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/UserSwitcherActivityTest.kt
new file mode 100644
index 000000000000..d4be881020e1
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/UserSwitcherActivityTest.kt
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.user
+
+import android.os.UserManager
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper.RunWithLooper
+import android.view.LayoutInflater
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.statusbar.phone.ShadeController
+import com.android.systemui.statusbar.policy.UserSwitcherController
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@RunWithLooper(setAsMainLooper = true)
+class UserSwitcherActivityTest : SysuiTestCase() {
+ @Mock
+ private lateinit var activity: UserSwitcherActivity
+ @Mock
+ private lateinit var userSwitcherController: UserSwitcherController
+ @Mock
+ private lateinit var broadcastDispatcher: BroadcastDispatcher
+ @Mock
+ private lateinit var layoutInflater: LayoutInflater
+ @Mock
+ private lateinit var falsingManager: FalsingManager
+ @Mock
+ private lateinit var userManager: UserManager
+ @Mock
+ private lateinit var shadeController: ShadeController
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ activity = UserSwitcherActivity(
+ userSwitcherController,
+ broadcastDispatcher,
+ layoutInflater,
+ falsingManager,
+ userManager,
+ shadeController
+ )
+ }
+
+ @Test
+ fun testMaxColumns() {
+ assertThat(activity.getMaxColumns(3)).isEqualTo(4)
+ assertThat(activity.getMaxColumns(4)).isEqualTo(4)
+ assertThat(activity.getMaxColumns(5)).isEqualTo(3)
+ assertThat(activity.getMaxColumns(6)).isEqualTo(3)
+ assertThat(activity.getMaxColumns(7)).isEqualTo(4)
+ assertThat(activity.getMaxColumns(9)).isEqualTo(5)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java
index b3805533cabd..ec619bb5952a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java
@@ -36,6 +36,7 @@ import android.os.Process;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.CaptioningManager;
import androidx.test.filters.SmallTest;
@@ -88,6 +89,8 @@ public class VolumeDialogControllerImplTest extends SysuiTestCase {
private PackageManager mPackageManager;
@Mock
private WakefulnessLifecycle mWakefullnessLifcycle;
+ @Mock
+ private CaptioningManager mCaptioningManager;
@Before
@@ -109,7 +112,7 @@ public class VolumeDialogControllerImplTest extends SysuiTestCase {
mVolumeController = new TestableVolumeDialogControllerImpl(mContext,
mBroadcastDispatcher, mRingerModeTracker, mThreadFactory, mAudioManager,
mNotificationManager, mVibrator, mIAudioService, mAccessibilityManager,
- mPackageManager, mWakefullnessLifcycle, mCallback);
+ mPackageManager, mWakefullnessLifcycle, mCaptioningManager, mCallback);
mVolumeController.setEnableDialogs(true, true);
}
@@ -184,10 +187,11 @@ public class VolumeDialogControllerImplTest extends SysuiTestCase {
AccessibilityManager accessibilityManager,
PackageManager packageManager,
WakefulnessLifecycle wakefulnessLifecycle,
+ CaptioningManager captioningManager,
C callback) {
super(context, broadcastDispatcher, ringerModeTracker, theadFactory, audioManager,
notificationManager, optionalVibrator, iAudioService, accessibilityManager,
- packageManager, wakefulnessLifecycle);
+ packageManager, wakefulnessLifecycle, captioningManager);
mCallbacks = callback;
ArgumentCaptor<WakefulnessLifecycle.Observer> observerCaptor =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
index 4bc4e6ea98f9..593b97e55739 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -50,8 +50,10 @@ import android.app.IActivityManager;
import android.app.INotificationManager;
import android.app.Notification;
import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
+import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.content.pm.LauncherApps;
import android.content.pm.PackageManager;
@@ -197,6 +199,11 @@ public class BubblesTest extends SysuiTestCase {
private ArgumentCaptor<NotificationRemoveInterceptor> mRemoveInterceptorCaptor;
@Captor
private ArgumentCaptor<List<Bubble>> mBubbleListCaptor;
+ @Captor
+ private ArgumentCaptor<IntentFilter> mFilterArgumentCaptor;
+ @Captor
+ private ArgumentCaptor<BroadcastReceiver> mBroadcastReceiverArgumentCaptor;
+
private BubblesManager mBubblesManager;
// TODO(178618782): Move tests on the controller directly to the shell
@@ -1357,6 +1364,66 @@ public class BubblesTest extends SysuiTestCase {
assertStackCollapsed();
}
+ @Test
+ public void testRegisterUnregisterBroadcastListener() {
+ spyOn(mContext);
+ mBubbleController.updateBubble(mBubbleEntry);
+ verify(mContext).registerReceiver(mBroadcastReceiverArgumentCaptor.capture(),
+ mFilterArgumentCaptor.capture());
+ assertThat(mFilterArgumentCaptor.getValue().getAction(0)).isEqualTo(
+ Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
+ assertThat(mFilterArgumentCaptor.getValue().getAction(1)).isEqualTo(
+ Intent.ACTION_SCREEN_OFF);
+
+ mBubbleData.dismissBubbleWithKey(mBubbleEntry.getKey(), REASON_APP_CANCEL);
+ // TODO: not certain why this isn't called normally when tests are run, perhaps because
+ // it's after an animation in BSV. This calls BubbleController#removeFromWindowManagerMaybe
+ mBubbleController.onAllBubblesAnimatedOut();
+
+ verify(mContext).unregisterReceiver(eq(mBroadcastReceiverArgumentCaptor.getValue()));
+ }
+
+ @Test
+ public void testBroadcastReceiverCloseDialogs_notGestureNav() {
+ spyOn(mContext);
+ mBubbleController.updateBubble(mBubbleEntry);
+ mBubbleData.setExpanded(true);
+ verify(mContext).registerReceiver(mBroadcastReceiverArgumentCaptor.capture(),
+ mFilterArgumentCaptor.capture());
+ Intent i = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
+ mBroadcastReceiverArgumentCaptor.getValue().onReceive(mContext, i);
+
+ assertStackExpanded();
+ }
+
+ @Test
+ public void testBroadcastReceiverCloseDialogs_reasonGestureNav() {
+ spyOn(mContext);
+ mBubbleController.updateBubble(mBubbleEntry);
+ mBubbleData.setExpanded(true);
+
+ verify(mContext).registerReceiver(mBroadcastReceiverArgumentCaptor.capture(),
+ mFilterArgumentCaptor.capture());
+ Intent i = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
+ i.putExtra("reason", "gestureNav");
+ mBroadcastReceiverArgumentCaptor.getValue().onReceive(mContext, i);
+ assertStackCollapsed();
+ }
+
+ @Test
+ public void testBroadcastReceiver_screenOff() {
+ spyOn(mContext);
+ mBubbleController.updateBubble(mBubbleEntry);
+ mBubbleData.setExpanded(true);
+
+ verify(mContext).registerReceiver(mBroadcastReceiverArgumentCaptor.capture(),
+ mFilterArgumentCaptor.capture());
+
+ Intent i = new Intent(Intent.ACTION_SCREEN_OFF);
+ mBroadcastReceiverArgumentCaptor.getValue().onReceive(mContext, i);
+ assertStackCollapsed();
+ }
+
/** Creates a bubble using the userId and package. */
private Bubble createBubble(int userId, String pkg) {
final UserHandle userHandle = new UserHandle(userId);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java
index 75d8453cb071..cc848bc34217 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java
@@ -46,6 +46,9 @@ import android.app.IActivityManager;
import android.app.INotificationManager;
import android.app.Notification;
import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.content.pm.LauncherApps;
import android.hardware.display.AmbientDisplayConfiguration;
import android.os.Handler;
@@ -179,6 +182,10 @@ public class NewNotifPipelineBubblesTest extends SysuiTestCase {
private ArgumentCaptor<NotifCollectionListener> mNotifListenerCaptor;
@Captor
private ArgumentCaptor<List<Bubble>> mBubbleListCaptor;
+ @Captor
+ private ArgumentCaptor<IntentFilter> mFilterArgumentCaptor;
+ @Captor
+ private ArgumentCaptor<BroadcastReceiver> mBroadcastReceiverArgumentCaptor;
private BubblesManager mBubblesManager;
private TestableBubbleController mBubbleController;
@@ -1176,6 +1183,67 @@ public class NewNotifPipelineBubblesTest extends SysuiTestCase {
assertStackCollapsed();
}
+
+ @Test
+ public void testRegisterUnregisterBroadcastListener() {
+ spyOn(mContext);
+ mBubbleController.updateBubble(mBubbleEntry);
+ verify(mContext).registerReceiver(mBroadcastReceiverArgumentCaptor.capture(),
+ mFilterArgumentCaptor.capture());
+ assertThat(mFilterArgumentCaptor.getValue().getAction(0)).isEqualTo(
+ Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
+ assertThat(mFilterArgumentCaptor.getValue().getAction(1)).isEqualTo(
+ Intent.ACTION_SCREEN_OFF);
+
+ mBubbleData.dismissBubbleWithKey(mBubbleEntry.getKey(), REASON_APP_CANCEL);
+ // TODO: not certain why this isn't called normally when tests are run, perhaps because
+ // it's after an animation in BSV. This calls BubbleController#removeFromWindowManagerMaybe
+ mBubbleController.onAllBubblesAnimatedOut();
+
+ verify(mContext).unregisterReceiver(eq(mBroadcastReceiverArgumentCaptor.getValue()));
+ }
+
+ @Test
+ public void testBroadcastReceiverCloseDialogs_notGestureNav() {
+ spyOn(mContext);
+ mBubbleController.updateBubble(mBubbleEntry);
+ mBubbleData.setExpanded(true);
+ verify(mContext).registerReceiver(mBroadcastReceiverArgumentCaptor.capture(),
+ mFilterArgumentCaptor.capture());
+ Intent i = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
+ mBroadcastReceiverArgumentCaptor.getValue().onReceive(mContext, i);
+
+ assertStackExpanded();
+ }
+
+ @Test
+ public void testBroadcastReceiverCloseDialogs_reasonGestureNav() {
+ spyOn(mContext);
+ mBubbleController.updateBubble(mBubbleEntry);
+ mBubbleData.setExpanded(true);
+
+ verify(mContext).registerReceiver(mBroadcastReceiverArgumentCaptor.capture(),
+ mFilterArgumentCaptor.capture());
+ Intent i = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
+ i.putExtra("reason", "gestureNav");
+ mBroadcastReceiverArgumentCaptor.getValue().onReceive(mContext, i);
+ assertStackCollapsed();
+ }
+
+ @Test
+ public void testBroadcastReceiver_screenOff() {
+ spyOn(mContext);
+ mBubbleController.updateBubble(mBubbleEntry);
+ mBubbleData.setExpanded(true);
+
+ verify(mContext).registerReceiver(mBroadcastReceiverArgumentCaptor.capture(),
+ mFilterArgumentCaptor.capture());
+
+ Intent i = new Intent(Intent.ACTION_SCREEN_OFF);
+ mBroadcastReceiverArgumentCaptor.getValue().onReceive(mContext, i);
+ assertStackCollapsed();
+ }
+
/**
* Sets the bubble metadata flags for this entry. These flags are normally set by
* NotificationManagerService when the notification is sent, however, these tests do not
diff --git a/proto/src/camera.proto b/proto/src/camera.proto
index 2d62f32d3941..40821185b61a 100644
--- a/proto/src/camera.proto
+++ b/proto/src/camera.proto
@@ -64,7 +64,7 @@ message CameraStreamProto {
repeated int64 histogram_counts = 13;
// The dynamic range profile of the stream
- optional int32 dynamic_range_profile = 14;
+ optional int64 dynamic_range_profile = 14;
// The stream use case
optional int32 stream_use_case = 15;
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 62da981bd9a7..0e9926590511 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -1363,8 +1363,18 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
* </p>
*
* @param displayId The logical display id
- * @param region the new magnified region, may be empty if
- * magnification is not enabled (e.g. scale is 1)
+ * @param region The magnification region.
+ * If the config mode is
+ * {@link MagnificationConfig#MAGNIFICATION_MODE_FULLSCREEN},
+ * it is the region of the screen currently active for magnification.
+ * the returned region will be empty if the magnification is not active
+ * (e.g. scale is 1. And the magnification is active if magnification
+ * gestures are enabled or if a service is running that can control
+ * magnification.
+ * If the config mode is
+ * {@link MagnificationConfig#MAGNIFICATION_MODE_WINDOW},
+ * it is the region of screen projected on the magnification window.
+ * The region will be empty if magnification is not activated.
* @param config The magnification config. That has magnification mode, the new scale and the
* new screen-relative center position
*/
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
index fe97a46e12f9..a95820966926 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
@@ -50,7 +50,6 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.LocalServices;
-import com.android.server.accessibility.AccessibilityManagerService;
import com.android.server.accessibility.AccessibilityTraceManager;
import com.android.server.wm.WindowManagerInternal;
@@ -374,9 +373,8 @@ public class FullScreenMagnificationController implements
.setScale(getScale())
.setCenterX(getCenterX())
.setCenterY(getCenterY()).build();
- mControllerCtx.getAms().notifyMagnificationChanged(mDisplayId,
- mMagnificationRegion,
- config);
+ mMagnificationInfoChangedCallback.onFullScreenMagnificationChanged(mDisplayId,
+ mMagnificationRegion, config);
if (mUnregisterPending && !isMagnifying()) {
unregister(mDeleteAfterUnregister);
}
@@ -665,10 +663,10 @@ public class FullScreenMagnificationController implements
* FullScreenMagnificationController Constructor
*/
public FullScreenMagnificationController(@NonNull Context context,
- @NonNull AccessibilityManagerService ams, @NonNull Object lock,
+ @NonNull AccessibilityTraceManager traceManager, @NonNull Object lock,
@NonNull MagnificationInfoChangedCallback magnificationInfoChangedCallback,
@NonNull MagnificationScaleProvider scaleProvider) {
- this(new ControllerContext(context, ams,
+ this(new ControllerContext(context, traceManager,
LocalServices.getService(WindowManagerInternal.class),
new Handler(context.getMainLooper()),
context.getResources().getInteger(R.integer.config_longAnimTime)), lock,
@@ -1521,7 +1519,6 @@ public class FullScreenMagnificationController implements
@VisibleForTesting
public static class ControllerContext {
private final Context mContext;
- private final AccessibilityManagerService mAms;
private final AccessibilityTraceManager mTrace;
private final WindowManagerInternal mWindowManager;
private final Handler mHandler;
@@ -1531,13 +1528,12 @@ public class FullScreenMagnificationController implements
* Constructor for ControllerContext.
*/
public ControllerContext(@NonNull Context context,
- @NonNull AccessibilityManagerService ams,
+ @NonNull AccessibilityTraceManager traceManager,
@NonNull WindowManagerInternal windowManager,
@NonNull Handler handler,
long animationDuration) {
mContext = context;
- mAms = ams;
- mTrace = ams.getTraceManager();
+ mTrace = traceManager;
mWindowManager = windowManager;
mHandler = handler;
mAnimationDuration = animationDuration;
@@ -1552,14 +1548,6 @@ public class FullScreenMagnificationController implements
}
/**
- * @return AccessibilityManagerService
- */
- @NonNull
- public AccessibilityManagerService getAms() {
- return mAms;
- }
-
- /**
* @return AccessibilityTraceManager
*/
@NonNull
@@ -1632,5 +1620,17 @@ public class FullScreenMagnificationController implements
* hidden.
*/
void onImeWindowVisibilityChanged(boolean shown);
+
+ /**
+ * Called when the magnification spec changed.
+ *
+ * @param displayId The logical display id
+ * @param region The region of the screen currently active for magnification.
+ * The returned region will be empty if the magnification is not active.
+ * @param config The magnification config. That has magnification mode, the new scale and
+ * the new screen-relative center position
+ */
+ void onFullScreenMagnificationChanged(int displayId, @NonNull Region region,
+ @NonNull MagnificationConfig config);
}
}
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
index c376bf873dc8..09e82c787c90 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
@@ -405,6 +405,12 @@ public class MagnificationController implements WindowMagnificationManager.Callb
mAms.notifyMagnificationChanged(displayId, new Region(bounds), config);
}
+ @Override
+ public void onFullScreenMagnificationChanged(int displayId, @NonNull Region region,
+ @NonNull MagnificationConfig config) {
+ mAms.notifyMagnificationChanged(displayId, region, config);
+ }
+
private void disableFullScreenMagnificationIfNeeded(int displayId) {
final FullScreenMagnificationController fullScreenMagnificationController =
getFullScreenMagnificationController();
@@ -590,7 +596,7 @@ public class MagnificationController implements WindowMagnificationManager.Callb
synchronized (mLock) {
if (mFullScreenMagnificationController == null) {
mFullScreenMagnificationController = new FullScreenMagnificationController(mContext,
- mAms, mLock, this, mScaleProvider);
+ mAms.getTraceManager(), mLock, this, mScaleProvider);
}
}
return mFullScreenMagnificationController;
diff --git a/services/backup/backuplib/java/com/android/server/backup/transport/BackupTransportClient.java b/services/backup/backuplib/java/com/android/server/backup/transport/BackupTransportClient.java
index 89633373b152..7e3ede15aa04 100644
--- a/services/backup/backuplib/java/com/android/server/backup/transport/BackupTransportClient.java
+++ b/services/backup/backuplib/java/com/android/server/backup/transport/BackupTransportClient.java
@@ -22,20 +22,18 @@ import android.app.backup.RestoreDescription;
import android.app.backup.RestoreSet;
import android.content.Intent;
import android.content.pm.PackageInfo;
-import android.os.Binder;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.util.Slog;
-import com.android.internal.annotations.GuardedBy;
import com.android.internal.backup.IBackupTransport;
import com.android.internal.infra.AndroidFuture;
import java.util.ArrayDeque;
-import java.util.Deque;
+import java.util.HashSet;
import java.util.List;
import java.util.Queue;
-import java.util.concurrent.ArrayBlockingQueue;
+import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
@@ -49,17 +47,19 @@ public class BackupTransportClient {
private final IBackupTransport mTransportBinder;
private final TransportStatusCallbackPool mCallbackPool;
+ private final TransportFutures mTransportFutures;
BackupTransportClient(IBackupTransport transportBinder) {
mTransportBinder = transportBinder;
mCallbackPool = new TransportStatusCallbackPool();
+ mTransportFutures = new TransportFutures();
}
/**
* See {@link IBackupTransport#name()}.
*/
public String name() throws RemoteException {
- AndroidFuture<String> resultFuture = new AndroidFuture<>();
+ AndroidFuture<String> resultFuture = mTransportFutures.newFuture();
mTransportBinder.name(resultFuture);
return getFutureResult(resultFuture);
}
@@ -68,7 +68,7 @@ public class BackupTransportClient {
* See {@link IBackupTransport#configurationIntent()}
*/
public Intent configurationIntent() throws RemoteException {
- AndroidFuture<Intent> resultFuture = new AndroidFuture<>();
+ AndroidFuture<Intent> resultFuture = mTransportFutures.newFuture();
mTransportBinder.configurationIntent(resultFuture);
return getFutureResult(resultFuture);
}
@@ -77,7 +77,7 @@ public class BackupTransportClient {
* See {@link IBackupTransport#currentDestinationString()}
*/
public String currentDestinationString() throws RemoteException {
- AndroidFuture<String> resultFuture = new AndroidFuture<>();
+ AndroidFuture<String> resultFuture = mTransportFutures.newFuture();
mTransportBinder.currentDestinationString(resultFuture);
return getFutureResult(resultFuture);
}
@@ -86,7 +86,7 @@ public class BackupTransportClient {
* See {@link IBackupTransport#dataManagementIntent()}
*/
public Intent dataManagementIntent() throws RemoteException {
- AndroidFuture<Intent> resultFuture = new AndroidFuture<>();
+ AndroidFuture<Intent> resultFuture = mTransportFutures.newFuture();
mTransportBinder.dataManagementIntent(resultFuture);
return getFutureResult(resultFuture);
}
@@ -96,7 +96,7 @@ public class BackupTransportClient {
*/
@Nullable
public CharSequence dataManagementIntentLabel() throws RemoteException {
- AndroidFuture<CharSequence> resultFuture = new AndroidFuture<>();
+ AndroidFuture<CharSequence> resultFuture = mTransportFutures.newFuture();
mTransportBinder.dataManagementIntentLabel(resultFuture);
return getFutureResult(resultFuture);
}
@@ -105,7 +105,7 @@ public class BackupTransportClient {
* See {@link IBackupTransport#transportDirName()}
*/
public String transportDirName() throws RemoteException {
- AndroidFuture<String> resultFuture = new AndroidFuture<>();
+ AndroidFuture<String> resultFuture = mTransportFutures.newFuture();
mTransportBinder.transportDirName(resultFuture);
return getFutureResult(resultFuture);
}
@@ -153,7 +153,7 @@ public class BackupTransportClient {
* See {@link IBackupTransport#requestBackupTime()}
*/
public long requestBackupTime() throws RemoteException {
- AndroidFuture<Long> resultFuture = new AndroidFuture<>();
+ AndroidFuture<Long> resultFuture = mTransportFutures.newFuture();
mTransportBinder.requestBackupTime(resultFuture);
Long result = getFutureResult(resultFuture);
return result == null ? BackupTransport.TRANSPORT_ERROR : result;
@@ -177,7 +177,7 @@ public class BackupTransportClient {
* See {@link IBackupTransport#getAvailableRestoreSets()}
*/
public RestoreSet[] getAvailableRestoreSets() throws RemoteException {
- AndroidFuture<List<RestoreSet>> resultFuture = new AndroidFuture<>();
+ AndroidFuture<List<RestoreSet>> resultFuture = mTransportFutures.newFuture();
mTransportBinder.getAvailableRestoreSets(resultFuture);
List<RestoreSet> result = getFutureResult(resultFuture);
return result == null ? null : result.toArray(new RestoreSet[] {});
@@ -187,7 +187,7 @@ public class BackupTransportClient {
* See {@link IBackupTransport#getCurrentRestoreSet()}
*/
public long getCurrentRestoreSet() throws RemoteException {
- AndroidFuture<Long> resultFuture = new AndroidFuture<>();
+ AndroidFuture<Long> resultFuture = mTransportFutures.newFuture();
mTransportBinder.getCurrentRestoreSet(resultFuture);
Long result = getFutureResult(resultFuture);
return result == null ? BackupTransport.TRANSPORT_ERROR : result;
@@ -210,7 +210,7 @@ public class BackupTransportClient {
* See {@link IBackupTransport#nextRestorePackage()}
*/
public RestoreDescription nextRestorePackage() throws RemoteException {
- AndroidFuture<RestoreDescription> resultFuture = new AndroidFuture<>();
+ AndroidFuture<RestoreDescription> resultFuture = mTransportFutures.newFuture();
mTransportBinder.nextRestorePackage(resultFuture);
return getFutureResult(resultFuture);
}
@@ -245,7 +245,7 @@ public class BackupTransportClient {
* See {@link IBackupTransport#requestFullBackupTime()}
*/
public long requestFullBackupTime() throws RemoteException {
- AndroidFuture<Long> resultFuture = new AndroidFuture<>();
+ AndroidFuture<Long> resultFuture = mTransportFutures.newFuture();
mTransportBinder.requestFullBackupTime(resultFuture);
Long result = getFutureResult(resultFuture);
return result == null ? BackupTransport.TRANSPORT_ERROR : result;
@@ -309,7 +309,7 @@ public class BackupTransportClient {
*/
public boolean isAppEligibleForBackup(PackageInfo targetPackage, boolean isFullBackup)
throws RemoteException {
- AndroidFuture<Boolean> resultFuture = new AndroidFuture<>();
+ AndroidFuture<Boolean> resultFuture = mTransportFutures.newFuture();
mTransportBinder.isAppEligibleForBackup(targetPackage, isFullBackup, resultFuture);
Boolean result = getFutureResult(resultFuture);
return result != null && result;
@@ -319,7 +319,7 @@ public class BackupTransportClient {
* See {@link IBackupTransport#getBackupQuota(String, boolean)}
*/
public long getBackupQuota(String packageName, boolean isFullBackup) throws RemoteException {
- AndroidFuture<Long> resultFuture = new AndroidFuture<>();
+ AndroidFuture<Long> resultFuture = mTransportFutures.newFuture();
mTransportBinder.getBackupQuota(packageName, isFullBackup, resultFuture);
Long result = getFutureResult(resultFuture);
return result == null ? BackupTransport.TRANSPORT_ERROR : result;
@@ -355,18 +355,58 @@ public class BackupTransportClient {
* See {@link IBackupTransport#getTransportFlags()}
*/
public int getTransportFlags() throws RemoteException {
- AndroidFuture<Integer> resultFuture = new AndroidFuture<>();
+ AndroidFuture<Integer> resultFuture = mTransportFutures.newFuture();
mTransportBinder.getTransportFlags(resultFuture);
Integer result = getFutureResult(resultFuture);
return result == null ? BackupTransport.TRANSPORT_ERROR : result;
}
+ /**
+ * Allows the {@link TransportConnection} to notify this client
+ * if the underlying transport has become unusable. If that happens
+ * we want to cancel all active futures or callbacks.
+ */
+ void onBecomingUnusable() {
+ mCallbackPool.cancelActiveCallbacks();
+ mTransportFutures.cancelActiveFutures();
+ }
+
private <T> T getFutureResult(AndroidFuture<T> future) {
try {
return future.get(600, TimeUnit.SECONDS);
} catch (InterruptedException | ExecutionException | TimeoutException e) {
Slog.w(TAG, "Failed to get result from transport:", e);
return null;
+ } finally {
+ mTransportFutures.remove(future);
+ }
+ }
+
+ private static class TransportFutures {
+ private final Object mActiveFuturesLock = new Object();
+ private final Set<AndroidFuture<?>> mActiveFutures = new HashSet<>();
+
+ <T> AndroidFuture<T> newFuture() {
+ AndroidFuture<T> future = new AndroidFuture<>();
+ synchronized (mActiveFuturesLock) {
+ mActiveFutures.add(future);
+ }
+ return future;
+ }
+
+ <T> void remove(AndroidFuture<T> future) {
+ synchronized (mActiveFuturesLock) {
+ mActiveFutures.remove(future);
+ }
+ }
+
+ void cancelActiveFutures() {
+ synchronized (mActiveFuturesLock) {
+ for (AndroidFuture<?> future : mActiveFutures) {
+ future.cancel(true);
+ }
+ mActiveFutures.clear();
+ }
}
}
@@ -375,27 +415,47 @@ public class BackupTransportClient {
private final Object mPoolLock = new Object();
private final Queue<TransportStatusCallback> mCallbackPool = new ArrayDeque<>();
+ private final Set<TransportStatusCallback> mActiveCallbacks = new HashSet<>();
TransportStatusCallback acquire() {
synchronized (mPoolLock) {
- if (mCallbackPool.isEmpty()) {
- return new TransportStatusCallback();
- } else {
- return mCallbackPool.poll();
+ TransportStatusCallback callback = mCallbackPool.poll();
+ if (callback == null) {
+ callback = new TransportStatusCallback();
}
+ callback.reset();
+ mActiveCallbacks.add(callback);
+ return callback;
}
}
void recycle(TransportStatusCallback callback) {
synchronized (mPoolLock) {
+ mActiveCallbacks.remove(callback);
if (mCallbackPool.size() > MAX_POOL_SIZE) {
Slog.d(TAG, "TransportStatusCallback pool size exceeded");
return;
}
-
- callback.reset();
mCallbackPool.add(callback);
}
}
+
+ void cancelActiveCallbacks() {
+ synchronized (mPoolLock) {
+ for (TransportStatusCallback callback : mActiveCallbacks) {
+ try {
+ callback.onOperationCompleteWithStatus(BackupTransport.TRANSPORT_ERROR);
+ // This waits for status to propagate before the callback is reset.
+ callback.getOperationStatus();
+ } catch (RemoteException ex) {
+ // Nothing we can do.
+ }
+ if (mCallbackPool.size() < MAX_POOL_SIZE) {
+ mCallbackPool.add(callback);
+ }
+ }
+ mActiveCallbacks.clear();
+ }
+ }
}
}
diff --git a/services/backup/backuplib/java/com/android/server/backup/transport/TransportConnection.java b/services/backup/backuplib/java/com/android/server/backup/transport/TransportConnection.java
index f9a3c36a3220..1009787bebe5 100644
--- a/services/backup/backuplib/java/com/android/server/backup/transport/TransportConnection.java
+++ b/services/backup/backuplib/java/com/android/server/backup/transport/TransportConnection.java
@@ -449,6 +449,9 @@ public class TransportConnection {
private void onServiceDisconnected() {
synchronized (mStateLock) {
log(Priority.ERROR, "Service disconnected: client UNUSABLE");
+ if (mTransport != null) {
+ mTransport.onBecomingUnusable();
+ }
setStateLocked(State.UNUSABLE, null);
try {
// After unbindService() no calls back to mConnection
@@ -473,6 +476,9 @@ public class TransportConnection {
checkStateIntegrityLocked();
log(Priority.ERROR, "Binding died: client UNUSABLE");
+ if (mTransport != null) {
+ mTransport.onBecomingUnusable();
+ }
// After unbindService() no calls back to mConnection
switch (mState) {
case State.UNUSABLE:
diff --git a/services/backup/backuplib/java/com/android/server/backup/transport/TransportStatusCallback.java b/services/backup/backuplib/java/com/android/server/backup/transport/TransportStatusCallback.java
index a55178c27eef..bc5cb0250d56 100644
--- a/services/backup/backuplib/java/com/android/server/backup/transport/TransportStatusCallback.java
+++ b/services/backup/backuplib/java/com/android/server/backup/transport/TransportStatusCallback.java
@@ -75,13 +75,11 @@ public class TransportStatusCallback extends ITransportStatusCallback.Stub {
}
Slog.w(TAG, "Couldn't get operation status from transport");
- return BackupTransport.TRANSPORT_ERROR;
} catch (InterruptedException e) {
Slog.w(TAG, "Couldn't get operation status from transport: ", e);
- return BackupTransport.TRANSPORT_ERROR;
- } finally {
- reset();
}
+
+ return BackupTransport.TRANSPORT_ERROR;
}
synchronized void reset() {
diff --git a/services/companion/java/com/android/server/companion/AssociationStoreImpl.java b/services/companion/java/com/android/server/companion/AssociationStoreImpl.java
index 21a677b8383c..cb28254f70db 100644
--- a/services/companion/java/com/android/server/companion/AssociationStoreImpl.java
+++ b/services/companion/java/com/android/server/companion/AssociationStoreImpl.java
@@ -171,12 +171,20 @@ class AssociationStoreImpl implements AssociationStore {
broadcastChange(CHANGE_TYPE_REMOVED, association);
}
+ /**
+ * @return a "snapshot" of the current state of the existing associations.
+ */
public @NonNull Collection<AssociationInfo> getAssociations() {
- final Collection<AssociationInfo> allAssociations;
synchronized (mLock) {
- allAssociations = mIdMap.values();
+ // IMPORTANT: make and return a COPY of the mIdMap.values(), NOT a "direct" reference.
+ // The HashMap.values() returns a collection which is backed by the HashMap, so changes
+ // to the HashMap are reflected in this collection.
+ // For us this means that if mIdMap is modified while the iteration over mIdMap.values()
+ // is in progress it may lead to "undefined results" (according to the HashMap's
+ // documentation) or cause ConcurrentModificationExceptions in the iterator (according
+ // to the bugreports...).
+ return List.copyOf(mIdMap.values());
}
- return Collections.unmodifiableCollection(allAssociations);
}
public @NonNull List<AssociationInfo> getAssociationsForUser(@UserIdInt int userId) {
diff --git a/services/companion/java/com/android/server/companion/CompanionApplicationController.java b/services/companion/java/com/android/server/companion/CompanionApplicationController.java
index c39b59ae35b3..ec4bfe09e415 100644
--- a/services/companion/java/com/android/server/companion/CompanionApplicationController.java
+++ b/services/companion/java/com/android/server/companion/CompanionApplicationController.java
@@ -118,13 +118,14 @@ class CompanionApplicationController {
serviceConnectors = CollectionUtils.map(companionServices, componentName ->
new CompanionDeviceServiceConnector(mContext, userId, componentName));
- mBoundCompanionApplications.setValueForPackage(userId, packageName, serviceConnectors);
- }
- if (serviceConnectors.isEmpty()) {
- Slog.e(TAG, "Can't find CompanionDeviceService implementer in package: "
- + packageName + ". Please check if they are correctly declared.");
- return;
+ if (serviceConnectors.isEmpty()) {
+ Slog.e(TAG, "Can't find CompanionDeviceService implementer in package: "
+ + packageName + ". Please check if they are correctly declared.");
+ return;
+ }
+
+ mBoundCompanionApplications.setValueForPackage(userId, packageName, serviceConnectors);
}
// The first connector in the list is always the primary connector: set a listener to it.
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 6813f3f83135..64bba79949e9 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -694,12 +694,6 @@ public class ActivityManagerService extends IActivityManager.Stub
return (isFg) ? mFgBroadcastQueue : mBgBroadcastQueue;
}
- /**
- * The package name of the DeviceOwner. This package is not permitted to have its data cleared.
- * <p>Not actually used</p>
- */
- private volatile String mDeviceOwnerName;
-
private volatile int mDeviceOwnerUid = INVALID_UID;
/**
@@ -6238,17 +6232,6 @@ public class ActivityManagerService extends IActivityManager.Stub
}
@Override
- public void updateDeviceOwner(String packageName) {
- final int callingUid = Binder.getCallingUid();
- if (callingUid != 0 && callingUid != SYSTEM_UID) {
- throw new SecurityException("updateDeviceOwner called from non-system process");
- }
- synchronized (this) {
- mDeviceOwnerName = packageName;
- }
- }
-
- @Override
public void updateLockTaskPackages(int userId, String[] packages) {
mActivityTaskManager.updateLockTaskPackages(userId, packages);
}
@@ -9193,6 +9176,7 @@ public class ActivityManagerService extends IActivityManager.Stub
boolean dumpVisibleStacksOnly = false;
boolean dumpFocusedStackOnly = false;
String dumpPackage = null;
+ int dumpUserId = UserHandle.USER_ALL;
int opti = 0;
while (opti < args.length) {
@@ -9224,6 +9208,17 @@ public class ActivityManagerService extends IActivityManager.Stub
dumpCheckinFormat = true;
} else if ("--normal-priority".equals(opt)) {
dumpNormalPriority = true;
+ } else if ("--user".equals(opt)) {
+ if (opti < args.length) {
+ dumpUserId = UserHandle.parseUserArg(args[opti]);
+ if (dumpUserId == UserHandle.USER_CURRENT) {
+ dumpUserId = mUserController.getCurrentUserId();
+ }
+ opti++;
+ } else {
+ pw.println("Error: --user option requires user id argument");
+ return;
+ }
} else if ("-h".equals(opt)) {
ActivityManagerShellCommand.dumpHelp(pw, true);
return;
@@ -9414,29 +9409,17 @@ public class ActivityManagerService extends IActivityManager.Stub
} else if ("service".equals(cmd)) {
String[] newArgs;
String name;
- int[] users = null;
if (opti >= args.length) {
name = null;
newArgs = EMPTY_STRING_ARRAY;
} else {
name = args[opti];
opti++;
- if ("--user".equals(name) && opti < args.length) {
- int userId = UserHandle.parseUserArg(args[opti]);
- opti++;
- if (userId != UserHandle.USER_ALL) {
- if (userId == UserHandle.USER_CURRENT) {
- userId = getCurrentUser().id;
- }
- users = new int[] { userId };
- }
- name = args[opti];
- opti++;
- }
newArgs = new String[args.length - opti];
if (args.length > 2) System.arraycopy(args, opti, newArgs, 0,
args.length - opti);
}
+ int[] users = dumpUserId == UserHandle.USER_ALL ? null : new int[] { dumpUserId };
if (!mServices.dumpService(fd, pw, name, users, newArgs, 0, dumpAll)) {
pw.println("No services match: " + name);
pw.println("Use -h for help.");
@@ -9497,7 +9480,7 @@ public class ActivityManagerService extends IActivityManager.Stub
} else {
// Dumping a single activity?
if (!mAtmInternal.dumpActivity(fd, pw, cmd, args, opti, dumpAll,
- dumpVisibleStacksOnly, dumpFocusedStackOnly)) {
+ dumpVisibleStacksOnly, dumpFocusedStackOnly, dumpUserId)) {
ActivityManagerShellCommand shell = new ActivityManagerShellCommand(this, true);
int res = shell.exec(this, null, fd, null, args, null,
new ResultReceiver(null));
@@ -15623,24 +15606,20 @@ public class ActivityManagerService extends IActivityManager.Stub
}
for (int i = 0, size = processes.size(); i < size; i++) {
ProcessRecord app = processes.get(i);
- pw.println(String.format("------ DUMP RESOURCES %s (%s) ------",
+ pw.println(String.format("Resources History for %s (%s)",
app.processName,
app.info.packageName));
pw.flush();
try {
- TransferPipe tp = new TransferPipe();
+ TransferPipe tp = new TransferPipe(" ");
try {
IApplicationThread thread = app.getThread();
if (thread != null) {
app.getThread().dumpResources(tp.getWriteFd(), null);
tp.go(fd.getFileDescriptor(), 2000);
- pw.println(String.format("------ END DUMP RESOURCES %s (%s) ------",
- app.processName,
- app.info.packageName));
- pw.flush();
} else {
pw.println(String.format(
- "------ DUMP RESOURCES %s (%s) failed, no thread ------",
+ " Resources history for %s (%s) failed, no thread",
app.processName,
app.info.packageName));
}
@@ -15648,11 +15627,7 @@ public class ActivityManagerService extends IActivityManager.Stub
tp.kill();
}
} catch (IOException e) {
- pw.println(String.format(
- "------ EXCEPTION DUMPING RESOURCES for %s (%s): %s ------",
- app.processName,
- app.info.packageName,
- e.getMessage()));
+ pw.println(" " + e.getMessage());
pw.flush();
}
diff --git a/services/core/java/com/android/server/app/GameManagerService.java b/services/core/java/com/android/server/app/GameManagerService.java
index b813bc48118a..0edbea0dbd28 100644
--- a/services/core/java/com/android/server/app/GameManagerService.java
+++ b/services/core/java/com/android/server/app/GameManagerService.java
@@ -90,6 +90,7 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.compat.CompatibilityOverrideConfig;
import com.android.internal.compat.IPlatformCompat;
import com.android.internal.os.BackgroundThread;
+import com.android.internal.util.FrameworkStatsLog;
import com.android.server.LocalServices;
import com.android.server.ServiceThread;
import com.android.server.SystemService;
@@ -268,16 +269,34 @@ public final class GameManagerService extends IGameManagerService.Stub {
break;
}
case SET_GAME_STATE: {
- if (mPowerManagerInternal == null) {
- final Bundle data = msg.getData();
- Slog.d(TAG, "Error setting loading mode for package "
- + data.getString(PACKAGE_NAME_MSG_KEY)
- + " and userId " + data.getInt(USER_ID_MSG_KEY));
- break;
- }
final GameState gameState = (GameState) msg.obj;
final boolean isLoading = gameState.isLoading();
- mPowerManagerInternal.setPowerMode(Mode.GAME_LOADING, isLoading);
+ final Bundle data = msg.getData();
+ final String packageName = data.getString(PACKAGE_NAME_MSG_KEY);
+ final int userId = data.getInt(USER_ID_MSG_KEY);
+
+ // Restrict to games only. Requires performance mode to be enabled.
+ final boolean boostEnabled =
+ getGameMode(packageName, userId) == GameManager.GAME_MODE_PERFORMANCE;
+ int uid;
+ try {
+ uid = mPackageManager.getPackageUidAsUser(packageName, userId);
+ } catch (NameNotFoundException e) {
+ Slog.v(TAG, "Failed to get package metadata");
+ uid = -1;
+ }
+ FrameworkStatsLog.write(FrameworkStatsLog.GAME_STATE_CHANGED, packageName, uid,
+ boostEnabled, gameStateModeToStatsdGameState(gameState.getMode()),
+ isLoading, gameState.getLabel(), gameState.getQuality());
+
+ if (boostEnabled) {
+ if (mPowerManagerInternal == null) {
+ Slog.d(TAG, "Error setting loading mode for package " + packageName
+ + " and userId " + userId);
+ break;
+ }
+ mPowerManagerInternal.setPowerMode(Mode.GAME_LOADING, isLoading);
+ }
break;
}
}
@@ -387,12 +406,6 @@ public final class GameManagerService extends IGameManagerService.Stub {
// Restrict to games only.
return;
}
-
- if (getGameMode(packageName, userId) != GameManager.GAME_MODE_PERFORMANCE) {
- // Requires performance mode to be enabled.
- return;
- }
-
final Message msg = mHandler.obtainMessage(SET_GAME_STATE);
final Bundle data = new Bundle();
data.putString(PACKAGE_NAME_MSG_KEY, packageName);
@@ -1543,6 +1556,22 @@ public final class GameManagerService extends IGameManagerService.Stub {
return out.toString();
}
+ private static int gameStateModeToStatsdGameState(int mode) {
+ switch (mode) {
+ case GameState.MODE_NONE:
+ return FrameworkStatsLog.GAME_STATE_CHANGED__STATE__MODE_NONE;
+ case GameState.MODE_GAMEPLAY_INTERRUPTIBLE:
+ return FrameworkStatsLog.GAME_STATE_CHANGED__STATE__MODE_GAMEPLAY_INTERRUPTIBLE;
+ case GameState.MODE_GAMEPLAY_UNINTERRUPTIBLE:
+ return FrameworkStatsLog.GAME_STATE_CHANGED__STATE__MODE_GAMEPLAY_UNINTERRUPTIBLE;
+ case GameState.MODE_CONTENT:
+ return FrameworkStatsLog.GAME_STATE_CHANGED__STATE__MODE_CONTENT;
+ case GameState.MODE_UNKNOWN:
+ default:
+ return FrameworkStatsLog.GAME_STATE_CHANGED__STATE__MODE_UNKNOWN;
+ }
+ }
+
private static ServiceThread createServiceThread() {
ServiceThread handlerThread = new ServiceThread(TAG,
Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/);
diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java
index 3491cd59ebb7..49a935ebadc9 100644
--- a/services/core/java/com/android/server/audio/BtHelper.java
+++ b/services/core/java/com/android/server/audio/BtHelper.java
@@ -479,6 +479,8 @@ public class BtHelper {
}
if (profile == BluetoothProfile.A2DP) {
mA2dp = (BluetoothA2dp) proxy;
+ } else if (profile == BluetoothProfile.HEARING_AID) {
+ mHearingAid = (BluetoothHearingAid) proxy;
} else if (profile == BluetoothProfile.LE_AUDIO) {
mLeAudio = (BluetoothLeAudio) proxy;
}
diff --git a/services/core/java/com/android/server/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java
index eb2f80b96dce..9f46bd6cf28a 100644
--- a/services/core/java/com/android/server/clipboard/ClipboardService.java
+++ b/services/core/java/com/android/server/clipboard/ClipboardService.java
@@ -389,7 +389,7 @@ public class ClipboardService extends SystemService {
final long oldIdentity = Binder.clearCallingIdentity();
try {
if (DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_CLIPBOARD,
- PROPERTY_AUTO_CLEAR_ENABLED, false)) {
+ PROPERTY_AUTO_CLEAR_ENABLED, true)) {
mClipboardClearHandler.removeEqualMessages(ClipboardClearHandler.MSG_CLEAR,
userId);
Message clearMessage = Message.obtain(mClipboardClearHandler,
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index ea054a5b280c..682f0dfe067c 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -2020,7 +2020,8 @@ public class Vpn {
.setCategory(Notification.CATEGORY_SYSTEM)
.setVisibility(Notification.VISIBILITY_PUBLIC)
.setOngoing(true)
- .setColor(mContext.getColor(R.color.system_notification_accent_color));
+ .setColor(mContext.getColor(
+ android.R.color.system_notification_accent_color));
notificationManager.notify(TAG, SystemMessage.NOTE_VPN_DISCONNECTED, builder.build());
} finally {
Binder.restoreCallingIdentity(token);
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index d233c5ee2ffc..2e80efb957d4 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -255,10 +255,9 @@ final class LocalDisplayAdapter extends DisplayAdapter {
public boolean updateDisplayPropertiesLocked(SurfaceControl.StaticDisplayInfo staticInfo,
SurfaceControl.DynamicDisplayInfo dynamicInfo,
SurfaceControl.DesiredDisplayModeSpecs modeSpecs) {
- boolean changed =
- updateSystemPreferredDisplayMode(dynamicInfo.preferredBootDisplayMode);
- changed |= updateDisplayModesLocked(
- dynamicInfo.supportedDisplayModes, dynamicInfo.activeDisplayModeId, modeSpecs);
+ boolean changed = updateDisplayModesLocked(
+ dynamicInfo.supportedDisplayModes, dynamicInfo.preferredBootDisplayMode,
+ dynamicInfo.activeDisplayModeId, modeSpecs);
changed |= updateStaticInfo(staticInfo);
changed |= updateColorModesLocked(dynamicInfo.supportedColorModes,
dynamicInfo.activeColorMode);
@@ -273,10 +272,12 @@ final class LocalDisplayAdapter extends DisplayAdapter {
}
public boolean updateDisplayModesLocked(
- SurfaceControl.DisplayMode[] displayModes, int activeDisplayModeId,
- SurfaceControl.DesiredDisplayModeSpecs modeSpecs) {
+ SurfaceControl.DisplayMode[] displayModes, int preferredSfDisplayModeId,
+ int activeSfDisplayModeId, SurfaceControl.DesiredDisplayModeSpecs modeSpecs) {
mSfDisplayModes = Arrays.copyOf(displayModes, displayModes.length);
- mActiveSfDisplayMode = getModeById(displayModes, activeDisplayModeId);
+ mActiveSfDisplayMode = getModeById(displayModes, activeSfDisplayModeId);
+ SurfaceControl.DisplayMode preferredSfDisplayMode =
+ getModeById(displayModes, preferredSfDisplayModeId);
// Build an updated list of all existing modes.
ArrayList<DisplayModeRecord> records = new ArrayList<>();
@@ -335,6 +336,27 @@ final class LocalDisplayAdapter extends DisplayAdapter {
}
}
+ boolean preferredModeChanged = false;
+
+ if (preferredSfDisplayModeId != INVALID_MODE_ID && preferredSfDisplayMode != null) {
+ DisplayModeRecord preferredRecord = null;
+ for (DisplayModeRecord record : records) {
+ if (record.hasMatchingMode(preferredSfDisplayMode)) {
+ preferredRecord = record;
+ break;
+ }
+ }
+
+ if (preferredRecord != null) {
+ int preferredModeId = preferredRecord.mMode.getModeId();
+ if (mSurfaceControlProxy.getBootDisplayModeSupport()
+ && mSystemPreferredModeId != preferredModeId) {
+ mSystemPreferredModeId = preferredModeId;
+ preferredModeChanged = true;
+ }
+ }
+ }
+
boolean activeModeChanged = false;
// Check whether SurfaceFlinger or the display device changed the active mode out from
@@ -373,7 +395,7 @@ final class LocalDisplayAdapter extends DisplayAdapter {
boolean recordsChanged = records.size() != mSupportedModes.size() || modesAdded;
// If the records haven't changed then we're done here.
if (!recordsChanged) {
- return activeModeChanged;
+ return activeModeChanged || preferredModeChanged;
}
mSupportedModes.clear();
@@ -386,14 +408,14 @@ final class LocalDisplayAdapter extends DisplayAdapter {
mDefaultModeId = mSystemPreferredModeId != INVALID_MODE_ID
? mSystemPreferredModeId : activeRecord.mMode.getModeId();
mDefaultModeGroup = mSystemPreferredModeId != INVALID_MODE_ID
- ? getModeById(mSfDisplayModes, mSystemPreferredModeId).group
+ ? preferredSfDisplayMode.group
: mActiveSfDisplayMode.group;
} else if (modesAdded && activeModeChanged) {
Slog.d(TAG, "New display modes are added and the active mode has changed, "
+ "use active mode as default mode.");
mDefaultModeId = activeRecord.mMode.getModeId();
mDefaultModeGroup = mActiveSfDisplayMode.group;
- } else if (findDisplayModeIdLocked(mDefaultModeId, mDefaultModeGroup) < 0) {
+ } else if (findSfDisplayModeIdLocked(mDefaultModeId, mDefaultModeGroup) < 0) {
Slog.w(TAG, "Default display mode no longer available, using currently"
+ " active mode as default.");
mDefaultModeId = activeRecord.mMode.getModeId();
@@ -545,15 +567,6 @@ final class LocalDisplayAdapter extends DisplayAdapter {
return true;
}
- private boolean updateSystemPreferredDisplayMode(int modeId) {
- if (!mSurfaceControlProxy.getBootDisplayModeSupport()
- || mSystemPreferredModeId == modeId) {
- return false;
- }
- mSystemPreferredModeId = modeId;
- return true;
- }
-
private SurfaceControl.DisplayMode getModeById(SurfaceControl.DisplayMode[] supportedModes,
int modeId) {
for (SurfaceControl.DisplayMode mode : supportedModes) {
@@ -887,8 +900,10 @@ final class LocalDisplayAdapter extends DisplayAdapter {
if (mUserPreferredMode == null) {
mSurfaceControlProxy.clearBootDisplayMode(getDisplayTokenLocked());
} else {
+ int preferredSfDisplayModeId = findSfDisplayModeIdLocked(
+ mUserPreferredMode.getModeId(), mDefaultModeGroup);
mSurfaceControlProxy.setBootDisplayMode(getDisplayTokenLocked(),
- mUserPreferredMode.getModeId());
+ preferredSfDisplayModeId);
}
}
@@ -923,9 +938,9 @@ final class LocalDisplayAdapter extends DisplayAdapter {
// mode based on vendor requirements.
// Note: We prefer the default mode group over the current one as this is the mode
// group the vendor prefers.
- int baseModeId = findDisplayModeIdLocked(displayModeSpecs.baseModeId,
+ int baseSfModeId = findSfDisplayModeIdLocked(displayModeSpecs.baseModeId,
mDefaultModeGroup);
- if (baseModeId < 0) {
+ if (baseSfModeId < 0) {
// When a display is hotplugged, it's possible for a mode to be removed that was
// previously valid. Because of the way display changes are propagated through the
// framework, and the caching of the display mode specs in LogicalDisplay, it's
@@ -943,7 +958,7 @@ final class LocalDisplayAdapter extends DisplayAdapter {
getHandler().sendMessage(PooledLambda.obtainMessage(
LocalDisplayDevice::setDesiredDisplayModeSpecsAsync, this,
getDisplayTokenLocked(),
- new SurfaceControl.DesiredDisplayModeSpecs(baseModeId,
+ new SurfaceControl.DesiredDisplayModeSpecs(baseSfModeId,
mDisplayModeSpecs.allowGroupSwitching,
mDisplayModeSpecs.primaryRefreshRateRange.min,
mDisplayModeSpecs.primaryRefreshRateRange.max,
@@ -1090,14 +1105,14 @@ final class LocalDisplayAdapter extends DisplayAdapter {
pw.println("mDisplayDeviceConfig=" + mDisplayDeviceConfig);
}
- private int findDisplayModeIdLocked(int modeId, int modeGroup) {
- int matchingModeId = INVALID_MODE_ID;
- DisplayModeRecord record = mSupportedModes.get(modeId);
+ private int findSfDisplayModeIdLocked(int displayModeId, int modeGroup) {
+ int matchingSfDisplayModeId = INVALID_MODE_ID;
+ DisplayModeRecord record = mSupportedModes.get(displayModeId);
if (record != null) {
for (SurfaceControl.DisplayMode mode : mSfDisplayModes) {
if (record.hasMatchingMode(mode)) {
- if (matchingModeId == INVALID_MODE_ID) {
- matchingModeId = mode.id;
+ if (matchingSfDisplayModeId == INVALID_MODE_ID) {
+ matchingSfDisplayModeId = mode.id;
}
// Prefer to return a mode that matches the modeGroup
@@ -1107,7 +1122,7 @@ final class LocalDisplayAdapter extends DisplayAdapter {
}
}
}
- return matchingModeId;
+ return matchingSfDisplayModeId;
}
// Returns a mode with id = modeId.
diff --git a/services/core/java/com/android/server/dreams/DreamController.java b/services/core/java/com/android/server/dreams/DreamController.java
index 76754d3e95d5..4a1a950c6a07 100644
--- a/services/core/java/com/android/server/dreams/DreamController.java
+++ b/services/core/java/com/android/server/dreams/DreamController.java
@@ -104,7 +104,7 @@ final class DreamController {
pw.println(" mCurrentDream:");
pw.println(" mToken=" + mCurrentDream.mToken);
pw.println(" mName=" + mCurrentDream.mName);
- pw.println(" mIsTest=" + mCurrentDream.mIsTest);
+ pw.println(" mIsPreviewMode=" + mCurrentDream.mIsPreviewMode);
pw.println(" mCanDoze=" + mCurrentDream.mCanDoze);
pw.println(" mUserId=" + mCurrentDream.mUserId);
pw.println(" mBound=" + mCurrentDream.mBound);
@@ -117,7 +117,7 @@ final class DreamController {
}
public void startDream(Binder token, ComponentName name,
- boolean isTest, boolean canDoze, int userId, PowerManager.WakeLock wakeLock,
+ boolean isPreviewMode, boolean canDoze, int userId, PowerManager.WakeLock wakeLock,
ComponentName overlayComponentName) {
stopDream(true /*immediate*/, "starting new dream");
@@ -127,10 +127,10 @@ final class DreamController {
mContext.sendBroadcastAsUser(mCloseNotificationShadeIntent, UserHandle.ALL);
Slog.i(TAG, "Starting dream: name=" + name
- + ", isTest=" + isTest + ", canDoze=" + canDoze
+ + ", isPreviewMode=" + isPreviewMode + ", canDoze=" + canDoze
+ ", userId=" + userId);
- mCurrentDream = new DreamRecord(token, name, isTest, canDoze, userId, wakeLock);
+ mCurrentDream = new DreamRecord(token, name, isPreviewMode, canDoze, userId, wakeLock);
mDreamStartTime = SystemClock.elapsedRealtime();
MetricsLogger.visible(mContext,
@@ -140,6 +140,7 @@ final class DreamController {
intent.setComponent(name);
intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
intent.putExtra(DreamService.EXTRA_DREAM_OVERLAY_COMPONENT, overlayComponentName);
+ intent.putExtra(DreamService.EXTRA_IS_PREVIEW, isPreviewMode);
try {
if (!mContext.bindServiceAsUser(intent, mCurrentDream,
Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE,
@@ -190,7 +191,8 @@ final class DreamController {
final DreamRecord oldDream = mCurrentDream;
mCurrentDream = null;
Slog.i(TAG, "Stopping dream: name=" + oldDream.mName
- + ", isTest=" + oldDream.mIsTest + ", canDoze=" + oldDream.mCanDoze
+ + ", isPreviewMode=" + oldDream.mIsPreviewMode
+ + ", canDoze=" + oldDream.mCanDoze
+ ", userId=" + oldDream.mUserId
+ ", reason='" + reason + "'"
+ (mSavedStopReason == null ? "" : "(from '" + mSavedStopReason + "')"));
@@ -247,7 +249,7 @@ final class DreamController {
mCurrentDream.mService = service;
- if (!mCurrentDream.mIsTest) {
+ if (!mCurrentDream.mIsPreviewMode) {
mContext.sendBroadcastAsUser(mDreamingStartedIntent, UserHandle.ALL);
mCurrentDream.mSentStartBroadcast = true;
}
@@ -263,7 +265,7 @@ final class DreamController {
private final class DreamRecord implements DeathRecipient, ServiceConnection {
public final Binder mToken;
public final ComponentName mName;
- public final boolean mIsTest;
+ public final boolean mIsPreviewMode;
public final boolean mCanDoze;
public final int mUserId;
@@ -275,11 +277,11 @@ final class DreamController {
public boolean mWakingGently;
- public DreamRecord(Binder token, ComponentName name,
- boolean isTest, boolean canDoze, int userId, PowerManager.WakeLock wakeLock) {
+ DreamRecord(Binder token, ComponentName name, boolean isPreviewMode,
+ boolean canDoze, int userId, PowerManager.WakeLock wakeLock) {
mToken = token;
mName = name;
- mIsTest = isTest;
+ mIsPreviewMode = isPreviewMode;
mCanDoze = canDoze;
mUserId = userId;
mWakeLock = wakeLock;
diff --git a/services/core/java/com/android/server/dreams/DreamManagerService.java b/services/core/java/com/android/server/dreams/DreamManagerService.java
index f0a6af3c8834..22d32a665611 100644
--- a/services/core/java/com/android/server/dreams/DreamManagerService.java
+++ b/services/core/java/com/android/server/dreams/DreamManagerService.java
@@ -87,7 +87,7 @@ public final class DreamManagerService extends SystemService {
private Binder mCurrentDreamToken;
private ComponentName mCurrentDreamName;
private int mCurrentDreamUserId;
- private boolean mCurrentDreamIsTest;
+ private boolean mCurrentDreamIsPreview;
private boolean mCurrentDreamCanDoze;
private boolean mCurrentDreamIsDozing;
private boolean mCurrentDreamIsWaking;
@@ -169,7 +169,7 @@ public final class DreamManagerService extends SystemService {
pw.println("mCurrentDreamToken=" + mCurrentDreamToken);
pw.println("mCurrentDreamName=" + mCurrentDreamName);
pw.println("mCurrentDreamUserId=" + mCurrentDreamUserId);
- pw.println("mCurrentDreamIsTest=" + mCurrentDreamIsTest);
+ pw.println("mCurrentDreamIsPreview=" + mCurrentDreamIsPreview);
pw.println("mCurrentDreamCanDoze=" + mCurrentDreamCanDoze);
pw.println("mCurrentDreamIsDozing=" + mCurrentDreamIsDozing);
pw.println("mCurrentDreamIsWaking=" + mCurrentDreamIsWaking);
@@ -190,7 +190,7 @@ public final class DreamManagerService extends SystemService {
private boolean isDreamingInternal() {
synchronized (mLock) {
- return mCurrentDreamToken != null && !mCurrentDreamIsTest
+ return mCurrentDreamToken != null && !mCurrentDreamIsPreview
&& !mCurrentDreamIsWaking;
}
}
@@ -235,7 +235,7 @@ public final class DreamManagerService extends SystemService {
private void testDreamInternal(ComponentName dream, int userId) {
synchronized (mLock) {
- startDreamLocked(dream, true /*isTest*/, false /*canDoze*/, userId);
+ startDreamLocked(dream, true /*isPreviewMode*/, false /*canDoze*/, userId);
}
}
@@ -244,7 +244,7 @@ public final class DreamManagerService extends SystemService {
final ComponentName dream = chooseDreamForUser(doze, userId);
if (dream != null) {
synchronized (mLock) {
- startDreamLocked(dream, false /*isTest*/, doze, userId);
+ startDreamLocked(dream, false /*isPreviewMode*/, doze, userId);
}
}
}
@@ -395,10 +395,10 @@ public final class DreamManagerService extends SystemService {
}
private void startDreamLocked(final ComponentName name,
- final boolean isTest, final boolean canDoze, final int userId) {
+ final boolean isPreviewMode, final boolean canDoze, final int userId) {
if (!mCurrentDreamIsWaking
&& Objects.equals(mCurrentDreamName, name)
- && mCurrentDreamIsTest == isTest
+ && mCurrentDreamIsPreview == isPreviewMode
&& mCurrentDreamCanDoze == canDoze
&& mCurrentDreamUserId == userId) {
Slog.i(TAG, "Already in target dream.");
@@ -412,7 +412,7 @@ public final class DreamManagerService extends SystemService {
final Binder newToken = new Binder();
mCurrentDreamToken = newToken;
mCurrentDreamName = name;
- mCurrentDreamIsTest = isTest;
+ mCurrentDreamIsPreview = isPreviewMode;
mCurrentDreamCanDoze = canDoze;
mCurrentDreamUserId = userId;
@@ -424,7 +424,7 @@ public final class DreamManagerService extends SystemService {
.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "startDream");
mHandler.post(wakeLock.wrap(() -> {
mAtmInternal.notifyDreamStateChanged(true);
- mController.startDream(newToken, name, isTest, canDoze, userId, wakeLock,
+ mController.startDream(newToken, name, isPreviewMode, canDoze, userId, wakeLock,
mDreamOverlayServiceName);
}));
}
@@ -457,7 +457,7 @@ public final class DreamManagerService extends SystemService {
}
mCurrentDreamToken = null;
mCurrentDreamName = null;
- mCurrentDreamIsTest = false;
+ mCurrentDreamIsPreview = false;
mCurrentDreamCanDoze = false;
mCurrentDreamUserId = 0;
mCurrentDreamIsWaking = false;
diff --git a/services/core/java/com/android/server/inputmethod/ImePlatformCompatUtils.java b/services/core/java/com/android/server/inputmethod/ImePlatformCompatUtils.java
new file mode 100644
index 000000000000..83ca16d72c9b
--- /dev/null
+++ b/services/core/java/com/android/server/inputmethod/ImePlatformCompatUtils.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.inputmethod;
+
+import static android.inputmethodservice.InputMethodService.FINISH_INPUT_NO_FALLBACK_CONNECTION;
+import static android.view.inputmethod.InputMethodManager.CLEAR_SHOW_FORCED_FLAG_WHEN_LEAVING;
+
+import android.content.Context;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+
+import com.android.internal.compat.IPlatformCompat;
+
+/**
+ * A utility class used by {@link InputMethodManagerService} to manage the platform
+ * compatibility changes on IMF (Input Method Framework) side.
+ */
+final class ImePlatformCompatUtils {
+ private final IPlatformCompat mPlatformCompat = IPlatformCompat.Stub.asInterface(
+ ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE));
+
+ /**
+ * Whether to finish the {@link android.view.inputmethod.InputConnection} when the device
+ * becomes {@link android.os.PowerManager#isInteractive non-interactive}.
+ *
+ * @param imeUid The uid of the IME application
+ */
+ public boolean shouldFinishInputWithReportToIme(int imeUid) {
+ return isChangeEnabledByUid(FINISH_INPUT_NO_FALLBACK_CONNECTION, imeUid);
+ }
+
+ /**
+ * Whether to clear {@link android.view.inputmethod.InputMethodManager#SHOW_FORCED} flag
+ * when the next IME focused application changed.
+ *
+ * @param clientUid The uid of the app
+ */
+ public boolean shouldClearShowForcedFlag(int clientUid) {
+ return isChangeEnabledByUid(CLEAR_SHOW_FORCED_FLAG_WHEN_LEAVING, clientUid);
+ }
+
+ private boolean isChangeEnabledByUid(long changeFlag, int uid) {
+ boolean result = false;
+ try {
+ result = mPlatformCompat.isChangeEnabledByUid(changeFlag, uid);
+ } catch (RemoteException e) {
+ }
+ return result;
+ }
+}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
index b2f500a59ba9..d2d80ffde532 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
@@ -408,6 +408,11 @@ final class InputMethodBindingController {
@GuardedBy("ImfLock.class")
@NonNull
InputBindResult bindCurrentMethod() {
+ if (mSelectedMethodId == null) {
+ Slog.e(TAG, "mSelectedMethodId is null!");
+ return InputBindResult.NO_IME;
+ }
+
InputMethodInfo info = mMethodMap.get(mSelectedMethodId);
if (info == null) {
throw new IllegalArgumentException("Unknown id: " + mSelectedMethodId);
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 0b7e39136feb..eb1de2a9bf91 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -15,7 +15,6 @@
package com.android.server.inputmethod;
-import static android.inputmethodservice.InputMethodService.FINISH_INPUT_NO_FALLBACK_CONNECTION;
import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_CRITICAL;
import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_NORMAL;
import static android.os.IServiceManager.DUMP_FLAG_PROTO;
@@ -148,7 +147,6 @@ import android.view.inputmethod.InputMethodManager;
import android.view.inputmethod.InputMethodSubtype;
import com.android.internal.annotations.GuardedBy;
-import com.android.internal.compat.IPlatformCompat;
import com.android.internal.content.PackageMonitor;
import com.android.internal.infra.AndroidFuture;
import com.android.internal.inputmethod.DirectBootAwareness;
@@ -279,6 +277,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
final WindowManagerInternal mWindowManagerInternal;
final PackageManagerInternal mPackageManagerInternal;
final InputManagerInternal mInputManagerInternal;
+ final ImePlatformCompatUtils mImePlatformCompatUtils;
final boolean mHasFeature;
private final ArrayMap<String, List<InputMethodSubtype>> mAdditionalSubtypeMap =
new ArrayMap<>();
@@ -691,8 +690,6 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
*/
boolean mIsInteractive = true;
- private final IPlatformCompat mPlatformCompat;
-
int mBackDisposition = InputMethodService.BACK_DISPOSITION_DEFAULT;
/**
@@ -1627,6 +1624,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class);
mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
mInputManagerInternal = LocalServices.getService(InputManagerInternal.class);
+ mImePlatformCompatUtils = new ImePlatformCompatUtils();
mImeDisplayValidator = mWindowManagerInternal::getDisplayImePolicy;
mAppOpsManager = mContext.getSystemService(AppOpsManager.class);
mUserManager = mContext.getSystemService(UserManager.class);
@@ -1634,8 +1632,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
mAccessibilityManager = AccessibilityManager.getInstance(context);
mHasFeature = context.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_INPUT_METHODS);
- mPlatformCompat = IPlatformCompat.Stub.asInterface(
- ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE));
+
mSlotIme = mContext.getString(com.android.internal.R.string.status_bar_ime);
Bundle extras = new Bundle();
@@ -3620,6 +3617,14 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
return InputBindResult.INVALID_USER;
}
+ final boolean shouldClearFlag = mImePlatformCompatUtils.shouldClearShowForcedFlag(cs.uid);
+ // In case mShowForced flag affects the next client to keep IME visible, when the current
+ // client is leaving due to the next focused client, we clear mShowForced flag when the
+ // next client's targetSdkVersion is T or higher.
+ if (mCurFocusedWindow != windowToken && mShowForced && shouldClearFlag) {
+ mShowForced = false;
+ }
+
// cross-profile access is always allowed here to allow profile-switching.
if (!mSettings.isCurrentProfile(userId)) {
Slog.w(TAG, "A background user is requesting window. Hiding IME.");
@@ -4728,14 +4733,9 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
// Inform the current client of the change in active status
if (mCurClient != null && mCurClient.client != null) {
- boolean reportToImeController = false;
- try {
- reportToImeController = mPlatformCompat.isChangeEnabledByUid(
- FINISH_INPUT_NO_FALLBACK_CONNECTION, getCurMethodUidLocked());
- } catch (RemoteException e) {
- }
scheduleSetActiveToClient(mCurClient, mIsInteractive, mInFullscreenMode,
- reportToImeController);
+ mImePlatformCompatUtils.shouldFinishInputWithReportToIme(
+ getCurMethodUidLocked()));
}
}
}
diff --git a/services/core/java/com/android/server/integrity/parser/RuleBinaryParser.java b/services/core/java/com/android/server/integrity/parser/RuleBinaryParser.java
index a290eb3a3e2f..ea3a3d5f1c60 100644
--- a/services/core/java/com/android/server/integrity/parser/RuleBinaryParser.java
+++ b/services/core/java/com/android/server/integrity/parser/RuleBinaryParser.java
@@ -168,6 +168,7 @@ public class RuleBinaryParser implements RuleParser {
switch (key) {
case AtomicFormula.PACKAGE_NAME:
case AtomicFormula.APP_CERTIFICATE:
+ case AtomicFormula.APP_CERTIFICATE_LINEAGE:
case AtomicFormula.INSTALLER_NAME:
case AtomicFormula.INSTALLER_CERTIFICATE:
case AtomicFormula.STAMP_CERTIFICATE_HASH:
diff --git a/services/core/java/com/android/server/locales/LocaleManagerBackupHelper.java b/services/core/java/com/android/server/locales/LocaleManagerBackupHelper.java
index 01aee7bc1942..db81393a9ad6 100644
--- a/services/core/java/com/android/server/locales/LocaleManagerBackupHelper.java
+++ b/services/core/java/com/android/server/locales/LocaleManagerBackupHelper.java
@@ -34,7 +34,6 @@ import android.content.pm.PackageManagerInternal;
import android.os.Binder;
import android.os.HandlerThread;
import android.os.LocaleList;
-import android.os.Process;
import android.os.RemoteException;
import android.os.UserHandle;
import android.text.TextUtils;
@@ -45,7 +44,6 @@ import android.util.TypedXmlSerializer;
import android.util.Xml;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.content.PackageMonitor;
import com.android.internal.util.XmlUtils;
import org.xmlpull.v1.XmlPullParser;
@@ -89,32 +87,24 @@ class LocaleManagerBackupHelper {
// SparseArray because it is more memory-efficient than a HashMap.
private final SparseArray<StagedData> mStagedData;
- private final PackageMonitor mPackageMonitor;
private final BroadcastReceiver mUserMonitor;
LocaleManagerBackupHelper(LocaleManagerService localeManagerService,
- PackageManagerInternal pmInternal) {
+ PackageManagerInternal pmInternal, HandlerThread broadcastHandlerThread) {
this(localeManagerService.mContext, localeManagerService, pmInternal, Clock.systemUTC(),
- new SparseArray<>());
+ new SparseArray<>(), broadcastHandlerThread);
}
@VisibleForTesting LocaleManagerBackupHelper(Context context,
LocaleManagerService localeManagerService,
- PackageManagerInternal pmInternal, Clock clock, SparseArray<StagedData> stagedData) {
+ PackageManagerInternal pmInternal, Clock clock, SparseArray<StagedData> stagedData,
+ HandlerThread broadcastHandlerThread) {
mContext = context;
mLocaleManagerService = localeManagerService;
mPackageManagerInternal = pmInternal;
mClock = clock;
mStagedData = stagedData;
- HandlerThread broadcastHandlerThread = new HandlerThread(TAG,
- Process.THREAD_PRIORITY_BACKGROUND);
- broadcastHandlerThread.start();
-
- mPackageMonitor = new PackageMonitorImpl();
- mPackageMonitor.register(context, broadcastHandlerThread.getLooper(),
- UserHandle.ALL,
- true);
mUserMonitor = new UserMonitor();
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_USER_REMOVED);
@@ -127,11 +117,6 @@ class LocaleManagerBackupHelper {
return mUserMonitor;
}
- @VisibleForTesting
- PackageMonitor getPackageMonitor() {
- return mPackageMonitor;
- }
-
/**
* @see LocaleManagerInternal#getBackupPayload(int userId)
*/
@@ -267,6 +252,53 @@ class LocaleManagerBackupHelper {
BackupManager.dataChanged(SYSTEM_BACKUP_PACKAGE_KEY);
}
+ /**
+ * <p><b>Note:</b> This is invoked by service's common monitor
+ * {@link LocaleManagerServicePackageMonitor#onPackageAdded} when a new package is
+ * added on device.
+ */
+ void onPackageAdded(String packageName, int uid) {
+ try {
+ synchronized (mStagedDataLock) {
+ cleanStagedDataForOldEntriesLocked();
+
+ int userId = UserHandle.getUserId(uid);
+ if (mStagedData.contains(userId)) {
+ // Perform lazy restore only if the staged data exists.
+ doLazyRestoreLocked(packageName, userId);
+ }
+ }
+ } catch (Exception e) {
+ Slog.e(TAG, "Exception in onPackageAdded.", e);
+ }
+ }
+
+ /**
+ * <p><b>Note:</b> This is invoked by service's common monitor
+ * {@link LocaleManagerServicePackageMonitor#onPackageDataCleared} when a package's data
+ * is cleared.
+ */
+ void onPackageDataCleared() {
+ try {
+ notifyBackupManager();
+ } catch (Exception e) {
+ Slog.e(TAG, "Exception in onPackageDataCleared.", e);
+ }
+ }
+
+ /**
+ * <p><b>Note:</b> This is invoked by service's common monitor
+ * {@link LocaleManagerServicePackageMonitor#onPackageRemoved} when a package is removed
+ * from device.
+ */
+ void onPackageRemoved() {
+ try {
+ notifyBackupManager();
+ } catch (Exception e) {
+ Slog.e(TAG, "Exception in onPackageRemoved.", e);
+ }
+ }
+
private boolean isPackageInstalledForUser(String packageName, int userId) {
PackageInfo pkgInfo = null;
try {
@@ -395,48 +427,6 @@ class LocaleManagerBackupHelper {
}
/**
- * Helper to monitor package states.
- *
- * <p>We're interested in package added, package data cleared and package removed events.
- */
- private final class PackageMonitorImpl extends PackageMonitor {
- @Override
- public void onPackageAdded(String packageName, int uid) {
- try {
- synchronized (mStagedDataLock) {
- cleanStagedDataForOldEntriesLocked();
-
- int userId = UserHandle.getUserId(uid);
- if (mStagedData.contains(userId)) {
- // Perform lazy restore only if the staged data exists.
- doLazyRestoreLocked(packageName, userId);
- }
- }
- } catch (Exception e) {
- Slog.e(TAG, "Exception in onPackageAdded.", e);
- }
- }
-
- @Override
- public void onPackageDataCleared(String packageName, int uid) {
- try {
- notifyBackupManager();
- } catch (Exception e) {
- Slog.e(TAG, "Exception in onPackageDataCleared.", e);
- }
- }
-
- @Override
- public void onPackageRemoved(String packageName, int uid) {
- try {
- notifyBackupManager();
- } catch (Exception e) {
- Slog.e(TAG, "Exception in onPackageRemoved.", e);
- }
- }
- }
-
- /**
* Performs lazy restore from the staged data.
*
* <p>This is invoked by the package monitor on the package added callback.
diff --git a/services/core/java/com/android/server/locales/LocaleManagerService.java b/services/core/java/com/android/server/locales/LocaleManagerService.java
index d459f8df99b6..c42770555bab 100644
--- a/services/core/java/com/android/server/locales/LocaleManagerService.java
+++ b/services/core/java/com/android/server/locales/LocaleManagerService.java
@@ -29,6 +29,7 @@ import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.os.Binder;
+import android.os.HandlerThread;
import android.os.LocaleList;
import android.os.Process;
import android.os.RemoteException;
@@ -38,14 +39,13 @@ import android.os.UserHandle;
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.DumpUtils;
+import com.android.internal.content.PackageMonitor;
import com.android.internal.util.FrameworkStatsLog;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.wm.ActivityTaskManagerInternal;
import java.io.FileDescriptor;
-import java.io.PrintWriter;
/**
* The implementation of ILocaleManager.aidl.
@@ -62,6 +62,8 @@ public class LocaleManagerService extends SystemService {
private LocaleManagerBackupHelper mBackupHelper;
+ private final PackageMonitor mPackageMonitor;
+
public static final boolean DEBUG = false;
public LocaleManagerService(Context context) {
@@ -71,15 +73,26 @@ public class LocaleManagerService extends SystemService {
mActivityTaskManagerInternal = LocalServices.getService(ActivityTaskManagerInternal.class);
mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
+
+ HandlerThread broadcastHandlerThread = new HandlerThread(TAG,
+ Process.THREAD_PRIORITY_BACKGROUND);
+ broadcastHandlerThread.start();
+
mBackupHelper = new LocaleManagerBackupHelper(this,
- mPackageManagerInternal);
+ mPackageManagerInternal, broadcastHandlerThread);
+
+ mPackageMonitor = new LocaleManagerServicePackageMonitor(mBackupHelper);
+ mPackageMonitor.register(context, broadcastHandlerThread.getLooper(),
+ UserHandle.ALL,
+ true);
}
@VisibleForTesting
LocaleManagerService(Context context, ActivityTaskManagerInternal activityTaskManagerInternal,
ActivityManagerInternal activityManagerInternal,
PackageManagerInternal packageManagerInternal,
- LocaleManagerBackupHelper localeManagerBackupHelper) {
+ LocaleManagerBackupHelper localeManagerBackupHelper,
+ PackageMonitor packageMonitor) {
super(context);
mContext = context;
mBinderService = new LocaleManagerBinderService();
@@ -87,6 +100,7 @@ public class LocaleManagerService extends SystemService {
mActivityManagerInternal = activityManagerInternal;
mPackageManagerInternal = packageManagerInternal;
mBackupHelper = localeManagerBackupHelper;
+ mPackageMonitor = packageMonitor;
}
@Override
@@ -130,11 +144,6 @@ public class LocaleManagerService extends SystemService {
}
@Override
- public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- LocaleManagerService.this.dump(fd, pw, args);
- }
-
- @Override
public void onShellCommand(FileDescriptor in, FileDescriptor out,
FileDescriptor err, String[] args, ShellCallback callback,
ResultReceiver resultReceiver) {
@@ -407,14 +416,6 @@ public class LocaleManagerService extends SystemService {
return null;
}
- /**
- * Dumps useful info related to service.
- */
- public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
- // TODO(b/201766221): Implement when there is state.
- }
-
private void logMetric(@NonNull AppLocaleChangedAtomRecord atomRecordForMetrics) {
FrameworkStatsLog.write(FrameworkStatsLog.APPLICATION_LOCALES_CHANGED,
atomRecordForMetrics.mCallingUid,
diff --git a/services/core/java/com/android/server/locales/LocaleManagerServicePackageMonitor.java b/services/core/java/com/android/server/locales/LocaleManagerServicePackageMonitor.java
new file mode 100644
index 000000000000..b459be768b9f
--- /dev/null
+++ b/services/core/java/com/android/server/locales/LocaleManagerServicePackageMonitor.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.locales;
+
+import com.android.internal.content.PackageMonitor;
+
+/**
+ * Helper to monitor package states inside {@link LocaleManagerService}.
+ *
+ * <p> These listeners forward the call to different aspects of locale service that
+ * handle the business logic.
+ * <p> We're interested in package added, package data cleared and package removed events.
+ */
+final class LocaleManagerServicePackageMonitor extends PackageMonitor {
+ private LocaleManagerBackupHelper mBackupHelper;
+
+ LocaleManagerServicePackageMonitor(LocaleManagerBackupHelper localeManagerBackupHelper) {
+ mBackupHelper = localeManagerBackupHelper;
+ }
+
+ @Override
+ public void onPackageAdded(String packageName, int uid) {
+ mBackupHelper.onPackageAdded(packageName, uid);
+ }
+
+ @Override
+ public void onPackageDataCleared(String packageName, int uid) {
+ mBackupHelper.onPackageDataCleared();
+ }
+
+ @Override
+ public void onPackageRemoved(String packageName, int uid) {
+ mBackupHelper.onPackageRemoved();
+ }
+}
diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java
index 0c3f9f0e26c6..45d9822205ec 100644
--- a/services/core/java/com/android/server/location/LocationManagerService.java
+++ b/services/core/java/com/android/server/location/LocationManagerService.java
@@ -1428,6 +1428,7 @@ public class LocationManagerService extends ILocationManager.Stub implements
ipw.println("Location Settings:");
ipw.increaseIndent();
mInjector.getSettingsHelper().dump(fd, ipw, args);
+ mInjector.getLocationSettings().dump(fd, ipw, args);
ipw.decreaseIndent();
synchronized (mLock) {
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubServiceUtil.java b/services/core/java/com/android/server/location/contexthub/ContextHubServiceUtil.java
index 8fdde2486401..e9bf90f1a82e 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubServiceUtil.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubServiceUtil.java
@@ -16,8 +16,6 @@
package com.android.server.location.contexthub;
-import static android.content.pm.PackageManager.PERMISSION_GRANTED;
-
import android.Manifest;
import android.content.Context;
import android.hardware.contexthub.V1_0.AsyncEventType;
@@ -297,19 +295,14 @@ import java.util.List;
}
/**
- * Checks for location hardware permissions.
+ * Checks for ACCESS_CONTEXT_HUB permissions.
*
* @param context the context of the service
*/
/* package */
static void checkPermissions(Context context) {
- boolean hasAccessContextHubPermission = (context.checkCallingPermission(
- CONTEXT_HUB_PERMISSION) == PERMISSION_GRANTED);
-
- if (!hasAccessContextHubPermission) {
- throw new SecurityException(
- "ACCESS_CONTEXT_HUB permission required to use Context Hub");
- }
+ context.enforceCallingOrSelfPermission(CONTEXT_HUB_PERMISSION,
+ "ACCESS_CONTEXT_HUB permission required to use Context Hub");
}
/**
diff --git a/services/core/java/com/android/server/location/provider/LocationProviderManager.java b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
index d42e2c63e80d..0b8f94c574c6 100644
--- a/services/core/java/com/android/server/location/provider/LocationProviderManager.java
+++ b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
@@ -1607,6 +1607,8 @@ public class LocationProviderManager extends
public @Nullable Location getLastLocation(LastLocationRequest request,
CallerIdentity identity, @PermissionLevel int permissionLevel) {
+ request = calculateLastLocationRequest(request);
+
if (!isActive(request.isBypass(), identity)) {
return null;
}
@@ -1634,6 +1636,38 @@ public class LocationProviderManager extends
return location;
}
+ private LastLocationRequest calculateLastLocationRequest(LastLocationRequest baseRequest) {
+ LastLocationRequest.Builder builder = new LastLocationRequest.Builder(baseRequest);
+
+ boolean locationSettingsIgnored = baseRequest.isLocationSettingsIgnored();
+ if (locationSettingsIgnored) {
+ // if we are not currently allowed use location settings ignored, disable it
+ if (!mSettingsHelper.getIgnoreSettingsAllowlist().contains(
+ getIdentity().getPackageName(), getIdentity().getAttributionTag())
+ && !mLocationManagerInternal.isProvider(null, getIdentity())) {
+ locationSettingsIgnored = false;
+ }
+
+ builder.setLocationSettingsIgnored(locationSettingsIgnored);
+ }
+
+ boolean adasGnssBypass = baseRequest.isAdasGnssBypass();
+ if (adasGnssBypass) {
+ // if we are not currently allowed use adas gnss bypass, disable it
+ if (!GPS_PROVIDER.equals(mName)) {
+ Log.e(TAG, "adas gnss bypass request received in non-gps provider");
+ adasGnssBypass = false;
+ } else if (!mLocationSettings.getUserSettings(
+ getIdentity().getUserId()).isAdasGnssLocationEnabled()) {
+ adasGnssBypass = false;
+ }
+
+ builder.setAdasGnssBypass(adasGnssBypass);
+ }
+
+ return builder.build();
+ }
+
/**
* This function does not perform any permissions or safety checks, by calling it you are
* committing to performing all applicable checks yourself. This always returns a "fine"
diff --git a/services/core/java/com/android/server/location/settings/LocationSettings.java b/services/core/java/com/android/server/location/settings/LocationSettings.java
index d52153893970..be0e7acd2401 100644
--- a/services/core/java/com/android/server/location/settings/LocationSettings.java
+++ b/services/core/java/com/android/server/location/settings/LocationSettings.java
@@ -18,8 +18,11 @@ package com.android.server.location.settings;
import static android.content.pm.PackageManager.FEATURE_AUTOMOTIVE;
+import android.app.ActivityManager;
import android.content.Context;
import android.os.Environment;
+import android.os.RemoteException;
+import android.util.IndentingPrintWriter;
import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
@@ -29,6 +32,7 @@ import com.android.server.FgThread;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.File;
+import java.io.FileDescriptor;
import java.io.IOException;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.function.Function;
@@ -104,6 +108,33 @@ public class LocationSettings {
getUserSettingsStore(userId).update(updater);
}
+ /** Dumps info for debugging. */
+ public final void dump(FileDescriptor fd, IndentingPrintWriter ipw, String[] args) {
+ int[] userIds;
+ try {
+ userIds = ActivityManager.getService().getRunningUserIds();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+
+ if (mContext.getPackageManager().hasSystemFeature(FEATURE_AUTOMOTIVE)) {
+ ipw.print("ADAS Location Setting: ");
+ ipw.increaseIndent();
+ if (userIds.length > 1) {
+ ipw.println();
+ for (int userId : userIds) {
+ ipw.print("[u");
+ ipw.print(userId);
+ ipw.print("] ");
+ ipw.println(getUserSettings(userId).isAdasGnssLocationEnabled());
+ }
+ } else {
+ ipw.println(getUserSettings(userIds[0]).isAdasGnssLocationEnabled());
+ }
+ ipw.decreaseIndent();
+ }
+ }
+
@VisibleForTesting
final void flushFiles() throws InterruptedException {
synchronized (mUserSettings) {
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index 96391ac62530..074d891a9974 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -647,18 +647,17 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
if (mDestroyed) {
return;
}
- toSend = new ArrayList<>();
- if (mQueue != null) {
- toSend.ensureCapacity(mQueue.size());
- toSend.addAll(mQueue);
- }
+ toSend = mQueue == null ? null : new ArrayList<>(mQueue);
}
Collection<ISessionControllerCallbackHolder> deadCallbackHolders = null;
for (ISessionControllerCallbackHolder holder : mControllerCallbackHolders) {
- ParceledListSlice<QueueItem> parcelableQueue = new ParceledListSlice<>(toSend);
- // Limit the size of initial Parcel to prevent binder buffer overflow
- // as onQueueChanged is an async binder call.
- parcelableQueue.setInlineCountLimit(1);
+ ParceledListSlice<QueueItem> parcelableQueue = null;
+ if (toSend != null) {
+ parcelableQueue = new ParceledListSlice<>(toSend);
+ // Limit the size of initial Parcel to prevent binder buffer overflow
+ // as onQueueChanged is an async binder call.
+ parcelableQueue.setInlineCountLimit(1);
+ }
try {
holder.mCallback.onQueueChanged(parcelableQueue);
diff --git a/services/core/java/com/android/server/net/LockdownVpnTracker.java b/services/core/java/com/android/server/net/LockdownVpnTracker.java
index 851ea3d01085..1b7d1ba59b06 100644
--- a/services/core/java/com/android/server/net/LockdownVpnTracker.java
+++ b/services/core/java/com/android/server/net/LockdownVpnTracker.java
@@ -293,7 +293,7 @@ public class LockdownVpnTracker {
.addAction(R.drawable.ic_menu_refresh, mContext.getString(R.string.reset),
mResetIntent)
.setColor(mContext.getColor(
- com.android.internal.R.color.system_notification_accent_color));
+ android.R.color.system_notification_accent_color));
mNotificationManager.notify(null /* tag */, SystemMessage.NOTE_VPN_STATUS,
builder.build());
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index e8fd85ea984f..48ad22cb12ff 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -4157,7 +4157,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
if (mRestrictedNetworkingMode) {
// Note: setUidFirewallRule also updates mUidFirewallRestrictedModeRules.
// In this case, default firewall rules can also be added.
- setUidFirewallRule(FIREWALL_CHAIN_RESTRICTED, uid,
+ setUidFirewallRuleUL(FIREWALL_CHAIN_RESTRICTED, uid,
getRestrictedModeFirewallRule(uidBlockedState));
}
}
@@ -4321,10 +4321,10 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
&& (uidBlockedState.effectiveBlockedReasons & BLOCKED_REASON_LOW_POWER_STANDBY)
== 0) {
mUidFirewallLowPowerStandbyModeRules.put(uid, FIREWALL_RULE_ALLOW);
- setUidFirewallRule(FIREWALL_CHAIN_LOW_POWER_STANDBY, uid, FIREWALL_RULE_ALLOW);
+ setUidFirewallRuleUL(FIREWALL_CHAIN_LOW_POWER_STANDBY, uid, FIREWALL_RULE_ALLOW);
} else {
mUidFirewallLowPowerStandbyModeRules.delete(uid);
- setUidFirewallRule(FIREWALL_CHAIN_LOW_POWER_STANDBY, uid, FIREWALL_RULE_DEFAULT);
+ setUidFirewallRuleUL(FIREWALL_CHAIN_LOW_POWER_STANDBY, uid, FIREWALL_RULE_DEFAULT);
}
}
@@ -4373,9 +4373,9 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
final boolean isWhitelisted = isWhitelistedFromPowerSaveUL(uid,
chain == FIREWALL_CHAIN_DOZABLE);
if (isWhitelisted || isUidForegroundOnRestrictPowerUL(uid)) {
- setUidFirewallRule(chain, uid, FIREWALL_RULE_ALLOW);
+ setUidFirewallRuleUL(chain, uid, FIREWALL_RULE_ALLOW);
} else {
- setUidFirewallRule(chain, uid, FIREWALL_RULE_DEFAULT);
+ setUidFirewallRuleUL(chain, uid, FIREWALL_RULE_DEFAULT);
}
}
}
@@ -4421,10 +4421,10 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
int appId = UserHandle.getAppId(uid);
if (!mPowerSaveTempWhitelistAppIds.get(appId) && isUidIdle(uid)
&& !isUidForegroundOnRestrictPowerUL(uid)) {
- setUidFirewallRule(FIREWALL_CHAIN_STANDBY, uid, FIREWALL_RULE_DENY);
+ setUidFirewallRuleUL(FIREWALL_CHAIN_STANDBY, uid, FIREWALL_RULE_DENY);
if (LOGD) Log.d(TAG, "updateRuleForAppIdleUL DENY " + uid);
} else {
- setUidFirewallRule(FIREWALL_CHAIN_STANDBY, uid, FIREWALL_RULE_DEFAULT);
+ setUidFirewallRuleUL(FIREWALL_CHAIN_STANDBY, uid, FIREWALL_RULE_DEFAULT);
if (LOGD) Log.d(TAG, "updateRuleForAppIdleUL " + uid + " to DEFAULT");
}
} finally {
@@ -5497,10 +5497,11 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
/**
* Add or remove a uid to the firewall denylist for all network ifaces.
*/
- private void setUidFirewallRule(int chain, int uid, int rule) {
+ @GuardedBy("mUidRulesFirstLock")
+ private void setUidFirewallRuleUL(int chain, int uid, int rule) {
if (Trace.isTagEnabled(Trace.TRACE_TAG_NETWORK)) {
Trace.traceBegin(Trace.TRACE_TAG_NETWORK,
- "setUidFirewallRule: " + chain + "/" + uid + "/" + rule);
+ "setUidFirewallRuleUL: " + chain + "/" + uid + "/" + rule);
}
try {
if (chain == FIREWALL_CHAIN_DOZABLE) {
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 16b5fb1e4d7a..a711b442c450 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -775,7 +775,7 @@ public class NotificationManagerService extends SystemService {
ArraySet<String> defaultDnds = mConditionProviders.getDefaultPackages();
for (int i = 0; i < defaultDnds.size(); i++) {
- allowDndPackage(defaultDnds.valueAt(i));
+ allowDndPackage(userId, defaultDnds.valueAt(i));
}
setDefaultAssistantForUser(userId);
@@ -875,9 +875,9 @@ public class NotificationManagerService extends SystemService {
}
}
- private void allowDndPackage(String packageName) {
+ private void allowDndPackage(int userId, String packageName) {
try {
- getBinderService().setNotificationPolicyAccessGranted(packageName, true);
+ getBinderService().setNotificationPolicyAccessGrantedForUser(packageName, userId, true);
} catch (RemoteException e) {
e.printStackTrace();
}
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index 66c7c5080114..bbdea32bed59 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -478,6 +478,7 @@ public final class NotificationRecord {
pw.println(prefix + "opPkg=" + getSbn().getOpPkg());
pw.println(prefix + "icon=" + notification.getSmallIcon());
pw.println(prefix + "flags=0x" + Integer.toHexString(notification.flags));
+ pw.println(prefix + "originalFlags=0x" + Integer.toHexString(mOriginalFlags));
pw.println(prefix + "pri=" + notification.priority);
pw.println(prefix + "key=" + getSbn().getKey());
pw.println(prefix + "seen=" + mStats.hasSeen());
@@ -544,6 +545,7 @@ public final class NotificationRecord {
if (notification == null) {
pw.println(prefix + "None");
return;
+
}
pw.println(prefix + "fullscreenIntent=" + notification.fullScreenIntent);
pw.println(prefix + "contentIntent=" + notification.contentIntent);
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index c09c904ff931..db0b0c58b046 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -183,7 +183,6 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
-import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ExecutorService;
@@ -1596,18 +1595,16 @@ final class InstallPackageHelper {
parsedPackage.setRestrictUpdateHash(oldPackage.getRestrictUpdateHash());
}
- // Check for shared user id changes
- if (!Objects.equals(oldPackage.getSharedUserId(),
- parsedPackage.getSharedUserId())
- // Don't mark as invalid if the app is trying to
- // leave a sharedUserId
- && parsedPackage.getSharedUserId() != null) {
+ // APK should not change its sharedUserId declarations
+ final var oldSharedUid = oldPackage.getSharedUserId() != null
+ ? oldPackage.getSharedUserId() : "<nothing>";
+ final var newSharedUid = parsedPackage.getSharedUserId() != null
+ ? parsedPackage.getSharedUserId() : "<nothing>";
+ if (!oldSharedUid.equals(newSharedUid)) {
throw new PrepareFailure(INSTALL_FAILED_UID_CHANGED,
"Package " + parsedPackage.getPackageName()
+ " shared user changed from "
- + (oldPackage.getSharedUserId() != null
- ? oldPackage.getSharedUserId() : "<nothing>")
- + " to " + parsedPackage.getSharedUserId());
+ + oldSharedUid + " to " + newSharedUid);
}
// In case of rollback, remember per-user/profile install state
@@ -3696,10 +3693,13 @@ final class InstallPackageHelper {
}
disabledPkgSetting = mPm.mSettings.getDisabledSystemPkgLPr(
parsedPackage.getPackageName());
- sharedUserSetting = (parsedPackage.getSharedUserId() != null)
- ? mPm.mSettings.getSharedUserLPw(parsedPackage.getSharedUserId(),
- 0 /*pkgFlags*/, 0 /*pkgPrivateFlags*/, true)
- : null;
+ if (parsedPackage.getSharedUserId() != null && !parsedPackage.isLeavingSharedUid()) {
+ sharedUserSetting = mPm.mSettings.getSharedUserLPw(
+ parsedPackage.getSharedUserId(),
+ 0 /*pkgFlags*/, 0 /*pkgPrivateFlags*/, true /*create*/);
+ } else {
+ sharedUserSetting = null;
+ }
if (DEBUG_PACKAGE_SCANNING
&& (parseFlags & ParsingPackageUtils.PARSE_CHATTY) != 0
&& sharedUserSetting != null) {
diff --git a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackage.java b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackage.java
index cdc2b1245b1e..f6f9faf98c40 100644
--- a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackage.java
+++ b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackage.java
@@ -290,6 +290,9 @@ public interface ParsingPackage extends ParsingPackageRead {
/** @see R#styleable.AndroidManifest_inheritKeyStoreKeys */
ParsingPackage setInheritKeyStoreKeys(boolean inheritKeyStoreKeys);
+ /** @see R#styleable.AndroidManifest_sharedUserMaxSdkVersion */
+ ParsingPackage setLeavingSharedUid(boolean leavingSharedUid);
+
ParsingPackage setLabelRes(int labelRes);
ParsingPackage setLargestWidthLimitDp(int largestWidthLimitDp);
diff --git a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageImpl.java b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageImpl.java
index 177eaca8e06f..67670272ef8b 100644
--- a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageImpl.java
+++ b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageImpl.java
@@ -549,6 +549,7 @@ public class ParsingPackageImpl implements ParsingPackage, ParsingPackageHidden,
private static final long SDK_LIBRARY = 1L << 49;
private static final long INHERIT_KEYSTORE_KEYS = 1L << 50;
private static final long ENABLE_ON_BACK_INVOKED_CALLBACK = 1L << 51;
+ private static final long LEAVING_SHARED_UID = 1L << 52;
}
private ParsingPackageImpl setBoolean(@Booleans.Values long flag, boolean value) {
@@ -2403,6 +2404,11 @@ public class ParsingPackageImpl implements ParsingPackage, ParsingPackageHidden,
}
@Override
+ public boolean isLeavingSharedUid() {
+ return getBoolean(Booleans.LEAVING_SHARED_UID);
+ }
+
+ @Override
public ParsingPackageImpl setBaseRevisionCode(int value) {
baseRevisionCode = value;
return this;
@@ -2551,6 +2557,11 @@ public class ParsingPackageImpl implements ParsingPackage, ParsingPackageHidden,
}
@Override
+ public ParsingPackageImpl setLeavingSharedUid(boolean value) {
+ return setBoolean(Booleans.LEAVING_SHARED_UID, value);
+ }
+
+ @Override
public ParsingPackageImpl setLabelRes(int value) {
labelRes = value;
return this;
diff --git a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageRead.java b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageRead.java
index 428374fa21a8..50033f652bfd 100644
--- a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageRead.java
+++ b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageRead.java
@@ -360,4 +360,11 @@ public interface ParsingPackageRead extends PkgWithoutStateAppInfo, PkgWithoutSt
* @see R.styleable.AndroidManifestApplication_enableOnBackInvokedCallback
*/
boolean isOnBackInvokedCallbackEnabled();
+
+ /**
+ * Returns true if R.styleable#AndroidManifest_sharedUserMaxSdkVersion is set to a value
+ * smaller than the current SDK version.
+ * @see R.styleable#AndroidManifest_sharedUserMaxSdkVersion
+ */
+ boolean isLeavingSharedUid();
}
diff --git a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java
index f30daa930e6c..ed1ab01e1d12 100644
--- a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java
@@ -1032,11 +1032,6 @@ public class ParsingPackageUtils {
private static ParseResult<ParsingPackage> parseSharedUser(ParseInput input,
ParsingPackage pkg, TypedArray sa) {
- int maxSdkVersion = anInteger(0, R.styleable.AndroidManifest_sharedUserMaxSdkVersion, sa);
- if ((maxSdkVersion != 0) && maxSdkVersion < Build.VERSION.RESOURCES_SDK_INT) {
- return input.success(pkg);
- }
-
String str = nonConfigString(0, R.styleable.AndroidManifest_sharedUserId, sa);
if (TextUtils.isEmpty(str)) {
return input.success(pkg);
@@ -1052,7 +1047,11 @@ public class ParsingPackageUtils {
}
}
+ int maxSdkVersion = anInteger(0, R.styleable.AndroidManifest_sharedUserMaxSdkVersion, sa);
+ boolean leaving = (maxSdkVersion != 0) && (maxSdkVersion < Build.VERSION.RESOURCES_SDK_INT);
+
return input.success(pkg
+ .setLeavingSharedUid(leaving)
.setSharedUserId(str.intern())
.setSharedUserLabel(resId(R.styleable.AndroidManifest_sharedUserLabel, sa)));
}
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 4387249fa39e..cc93d4c1c3ff 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -330,6 +330,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
static public final String SYSTEM_DIALOG_REASON_HOME_KEY = "homekey";
static public final String SYSTEM_DIALOG_REASON_ASSIST = "assist";
static public final String SYSTEM_DIALOG_REASON_SCREENSHOT = "screenshot";
+ static public final String SYSTEM_DIALOG_REASON_GESTURE_NAV = "gestureNav";
private static final String TALKBACK_LABEL = "TalkBack";
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Compat.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Compat.java
index c638201bf893..9bbae4bd2db5 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Compat.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Compat.java
@@ -29,7 +29,9 @@ import android.os.IBinder;
import android.os.IHwBinder;
import android.os.RemoteException;
import android.system.OsConstants;
+import android.util.Log;
+import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
@@ -54,6 +56,8 @@ import java.util.concurrent.atomic.AtomicReference;
* </ul>
*/
final class SoundTriggerHw2Compat implements ISoundTriggerHal {
+ private static final String TAG = "SoundTriggerHw2Compat";
+
private final @NonNull Runnable mRebootRunnable;
private final @NonNull IHwBinder mBinder;
private @NonNull android.hardware.soundtrigger.V2_0.ISoundTriggerHw mUnderlying_2_0;
@@ -226,6 +230,16 @@ final class SoundTriggerHw2Compat implements ISoundTriggerHal {
return handle.get();
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
+ } finally {
+ // TODO(b/219825762): We should be able to use the entire object in a try-with-resources
+ // clause, instead of having to explicitly close internal fields.
+ if (hidlModel.data != null) {
+ try {
+ hidlModel.data.close();
+ } catch (IOException e) {
+ Log.e(TAG, "Failed to close file", e);
+ }
+ }
}
}
@@ -252,6 +266,16 @@ final class SoundTriggerHw2Compat implements ISoundTriggerHal {
return handle.get();
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
+ } finally {
+ // TODO(b/219825762): We should be able to use the entire object in a try-with-resources
+ // clause, instead of having to explicitly close internal fields.
+ if (hidlModel.common.data != null) {
+ try {
+ hidlModel.common.data.close();
+ } catch (IOException e) {
+ Log.e(TAG, "Failed to close file", e);
+ }
+ }
}
}
diff --git a/services/core/java/com/android/server/vibrator/AbstractVibratorStep.java b/services/core/java/com/android/server/vibrator/AbstractVibratorStep.java
new file mode 100644
index 000000000000..3550bda282dd
--- /dev/null
+++ b/services/core/java/com/android/server/vibrator/AbstractVibratorStep.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.vibrator;
+
+import android.os.SystemClock;
+import android.os.VibrationEffect;
+import android.util.Slog;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Represent a step on a single vibrator that plays one or more segments from a
+ * {@link VibrationEffect.Composed} effect.
+ */
+abstract class AbstractVibratorStep extends Step {
+ public final VibratorController controller;
+ public final VibrationEffect.Composed effect;
+ public final int segmentIndex;
+ public final long previousStepVibratorOffTimeout;
+
+ long mVibratorOnResult;
+ boolean mVibratorCompleteCallbackReceived;
+
+ /**
+ * @param conductor The VibrationStepConductor for these steps.
+ * @param startTime The time to schedule this step in the
+ * {@link VibrationStepConductor}.
+ * @param controller The vibrator that is playing the effect.
+ * @param effect The effect being played in this step.
+ * @param index The index of the next segment to be played by this step
+ * @param previousStepVibratorOffTimeout The time the vibrator is expected to complete any
+ * previous vibration and turn off. This is used to allow this step to
+ * be triggered when the completion callback is received, and can
+ * be used to play effects back-to-back.
+ */
+ AbstractVibratorStep(VibrationStepConductor conductor, long startTime,
+ VibratorController controller, VibrationEffect.Composed effect, int index,
+ long previousStepVibratorOffTimeout) {
+ super(conductor, startTime);
+ this.controller = controller;
+ this.effect = effect;
+ this.segmentIndex = index;
+ this.previousStepVibratorOffTimeout = previousStepVibratorOffTimeout;
+ }
+
+ public int getVibratorId() {
+ return controller.getVibratorInfo().getId();
+ }
+
+ @Override
+ public long getVibratorOnDuration() {
+ return mVibratorOnResult;
+ }
+
+ @Override
+ public boolean acceptVibratorCompleteCallback(int vibratorId) {
+ boolean isSameVibrator = controller.getVibratorInfo().getId() == vibratorId;
+ mVibratorCompleteCallbackReceived |= isSameVibrator;
+ // Only activate this step if a timeout was set to wait for the vibration to complete,
+ // otherwise we are waiting for the correct time to play the next step.
+ return isSameVibrator && (previousStepVibratorOffTimeout > SystemClock.uptimeMillis());
+ }
+
+ @Override
+ public List<Step> cancel() {
+ return Arrays.asList(new CompleteEffectVibratorStep(conductor, SystemClock.uptimeMillis(),
+ /* cancelled= */ true, controller, previousStepVibratorOffTimeout));
+ }
+
+ @Override
+ public void cancelImmediately() {
+ if (previousStepVibratorOffTimeout > SystemClock.uptimeMillis()) {
+ // Vibrator might be running from previous steps, so turn it off while canceling.
+ stopVibrating();
+ }
+ }
+
+ protected void stopVibrating() {
+ if (VibrationThread.DEBUG) {
+ Slog.d(VibrationThread.TAG,
+ "Turning off vibrator " + getVibratorId());
+ }
+ controller.off();
+ }
+
+ protected void changeAmplitude(float amplitude) {
+ if (VibrationThread.DEBUG) {
+ Slog.d(VibrationThread.TAG,
+ "Amplitude changed on vibrator " + getVibratorId() + " to " + amplitude);
+ }
+ controller.setAmplitude(amplitude);
+ }
+
+ /**
+ * Return the {@link VibrationStepConductor#nextVibrateStep} with same timings, only jumping
+ * the segments.
+ */
+ protected List<Step> skipToNextSteps(int segmentsSkipped) {
+ return nextSteps(startTime, previousStepVibratorOffTimeout, segmentsSkipped);
+ }
+
+ /**
+ * Return the {@link VibrationStepConductor#nextVibrateStep} with same start and off timings
+ * calculated from {@link #getVibratorOnDuration()}, jumping all played segments.
+ *
+ * <p>This method has same behavior as {@link #skipToNextSteps(int)} when the vibrator
+ * result is non-positive, meaning the vibrator has either ignored or failed to turn on.
+ */
+ protected List<Step> nextSteps(int segmentsPlayed) {
+ if (mVibratorOnResult <= 0) {
+ // Vibration was not started, so just skip the played segments and keep timings.
+ return skipToNextSteps(segmentsPlayed);
+ }
+ long nextStartTime = SystemClock.uptimeMillis() + mVibratorOnResult;
+ long nextVibratorOffTimeout =
+ nextStartTime + VibrationStepConductor.CALLBACKS_EXTRA_TIMEOUT;
+ return nextSteps(nextStartTime, nextVibratorOffTimeout, segmentsPlayed);
+ }
+
+ /**
+ * Return the {@link VibrationStepConductor#nextVibrateStep} with given start and off timings,
+ * which might be calculated independently, jumping all played segments.
+ *
+ * <p>This should be used when the vibrator on/off state is not responsible for the steps
+ * execution timings, e.g. while playing the vibrator amplitudes.
+ */
+ protected List<Step> nextSteps(long nextStartTime, long vibratorOffTimeout,
+ int segmentsPlayed) {
+ Step nextStep = conductor.nextVibrateStep(nextStartTime, controller, effect,
+ segmentIndex + segmentsPlayed, vibratorOffTimeout);
+ return nextStep == null ? VibrationStepConductor.EMPTY_STEP_LIST : Arrays.asList(nextStep);
+ }
+}
diff --git a/services/core/java/com/android/server/vibrator/CompleteEffectVibratorStep.java b/services/core/java/com/android/server/vibrator/CompleteEffectVibratorStep.java
new file mode 100644
index 000000000000..8585e3473ef3
--- /dev/null
+++ b/services/core/java/com/android/server/vibrator/CompleteEffectVibratorStep.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.vibrator;
+
+import android.os.SystemClock;
+import android.os.Trace;
+import android.os.VibrationEffect;
+import android.util.Slog;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Represents a step to complete a {@link VibrationEffect}.
+ *
+ * <p>This runs right at the time the vibration is considered to end and will update the pending
+ * vibrators count. This can turn off the vibrator or slowly ramp it down to zero amplitude.
+ */
+final class CompleteEffectVibratorStep extends AbstractVibratorStep {
+ private final boolean mCancelled;
+
+ CompleteEffectVibratorStep(VibrationStepConductor conductor, long startTime, boolean cancelled,
+ VibratorController controller, long previousStepVibratorOffTimeout) {
+ super(conductor, startTime, controller, /* effect= */ null, /* index= */ -1,
+ previousStepVibratorOffTimeout);
+ mCancelled = cancelled;
+ }
+
+ @Override
+ public boolean isCleanUp() {
+ // If the vibration was cancelled then this is just a clean up to ramp off the vibrator.
+ // Otherwise this step is part of the vibration.
+ return mCancelled;
+ }
+
+ @Override
+ public List<Step> cancel() {
+ if (mCancelled) {
+ // Double cancelling will just turn off the vibrator right away.
+ return Arrays.asList(
+ new TurnOffVibratorStep(conductor, SystemClock.uptimeMillis(), controller));
+ }
+ return super.cancel();
+ }
+
+ @Override
+ public List<Step> play() {
+ Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "CompleteEffectVibratorStep");
+ try {
+ if (VibrationThread.DEBUG) {
+ Slog.d(VibrationThread.TAG,
+ "Running " + (mCancelled ? "cancel" : "complete") + " vibration"
+ + " step on vibrator " + controller.getVibratorInfo().getId());
+ }
+ if (mVibratorCompleteCallbackReceived) {
+ // Vibration completion callback was received by this step, just turn if off
+ // and skip any clean-up.
+ stopVibrating();
+ return VibrationStepConductor.EMPTY_STEP_LIST;
+ }
+
+ float currentAmplitude = controller.getCurrentAmplitude();
+ long remainingOnDuration =
+ previousStepVibratorOffTimeout - VibrationStepConductor.CALLBACKS_EXTRA_TIMEOUT
+ - SystemClock.uptimeMillis();
+ long rampDownDuration =
+ Math.min(remainingOnDuration,
+ conductor.vibrationSettings.getRampDownDuration());
+ long stepDownDuration = conductor.vibrationSettings.getRampStepDuration();
+ if (currentAmplitude < VibrationStepConductor.RAMP_OFF_AMPLITUDE_MIN
+ || rampDownDuration <= stepDownDuration) {
+ // No need to ramp down the amplitude, just wait to turn it off.
+ if (mCancelled) {
+ // Vibration is completing because it was cancelled, turn off right away.
+ stopVibrating();
+ return VibrationStepConductor.EMPTY_STEP_LIST;
+ } else {
+ return Arrays.asList(new TurnOffVibratorStep(
+ conductor, previousStepVibratorOffTimeout, controller));
+ }
+ }
+
+ if (VibrationThread.DEBUG) {
+ Slog.d(VibrationThread.TAG,
+ "Ramping down vibrator " + controller.getVibratorInfo().getId()
+ + " from amplitude " + currentAmplitude
+ + " for " + rampDownDuration + "ms");
+ }
+ float amplitudeDelta = currentAmplitude / (rampDownDuration / stepDownDuration);
+ float amplitudeTarget = currentAmplitude - amplitudeDelta;
+ long newVibratorOffTimeout =
+ mCancelled ? rampDownDuration : previousStepVibratorOffTimeout;
+ return Arrays.asList(
+ new RampOffVibratorStep(conductor, startTime, amplitudeTarget, amplitudeDelta,
+ controller, newVibratorOffTimeout));
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/vibrator/ComposePrimitivesVibratorStep.java b/services/core/java/com/android/server/vibrator/ComposePrimitivesVibratorStep.java
new file mode 100644
index 000000000000..d1ea80557419
--- /dev/null
+++ b/services/core/java/com/android/server/vibrator/ComposePrimitivesVibratorStep.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.vibrator;
+
+import android.os.Trace;
+import android.os.VibrationEffect;
+import android.os.vibrator.PrimitiveSegment;
+import android.os.vibrator.VibrationEffectSegment;
+import android.util.Slog;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Represents a step to turn the vibrator on using a composition of primitives.
+ *
+ * <p>This step will use the maximum supported number of consecutive segments of type
+ * {@link PrimitiveSegment} starting at the current index.
+ */
+final class ComposePrimitivesVibratorStep extends AbstractVibratorStep {
+
+ ComposePrimitivesVibratorStep(VibrationStepConductor conductor, long startTime,
+ VibratorController controller, VibrationEffect.Composed effect, int index,
+ long previousStepVibratorOffTimeout) {
+ // This step should wait for the last vibration to finish (with the timeout) and for the
+ // intended step start time (to respect the effect delays).
+ super(conductor, Math.max(startTime, previousStepVibratorOffTimeout), controller, effect,
+ index, previousStepVibratorOffTimeout);
+ }
+
+ @Override
+ public List<Step> play() {
+ Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "ComposePrimitivesStep");
+ try {
+ // Load the next PrimitiveSegments to create a single compose call to the vibrator,
+ // limited to the vibrator composition maximum size.
+ int limit = controller.getVibratorInfo().getCompositionSizeMax();
+ int segmentCount = limit > 0
+ ? Math.min(effect.getSegments().size(), segmentIndex + limit)
+ : effect.getSegments().size();
+ List<PrimitiveSegment> primitives = new ArrayList<>();
+ for (int i = segmentIndex; i < segmentCount; i++) {
+ VibrationEffectSegment segment = effect.getSegments().get(i);
+ if (segment instanceof PrimitiveSegment) {
+ primitives.add((PrimitiveSegment) segment);
+ } else {
+ break;
+ }
+ }
+
+ if (primitives.isEmpty()) {
+ Slog.w(VibrationThread.TAG, "Ignoring wrong segment for a ComposePrimitivesStep: "
+ + effect.getSegments().get(segmentIndex));
+ return skipToNextSteps(/* segmentsSkipped= */ 1);
+ }
+
+ if (VibrationThread.DEBUG) {
+ Slog.d(VibrationThread.TAG, "Compose " + primitives + " primitives on vibrator "
+ + controller.getVibratorInfo().getId());
+ }
+ mVibratorOnResult = controller.on(
+ primitives.toArray(new PrimitiveSegment[primitives.size()]),
+ getVibration().id);
+
+ return nextSteps(/* segmentsPlayed= */ primitives.size());
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/vibrator/ComposePwleVibratorStep.java b/services/core/java/com/android/server/vibrator/ComposePwleVibratorStep.java
new file mode 100644
index 000000000000..73bf933f8bc9
--- /dev/null
+++ b/services/core/java/com/android/server/vibrator/ComposePwleVibratorStep.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.vibrator;
+
+import android.os.Trace;
+import android.os.VibrationEffect;
+import android.os.vibrator.RampSegment;
+import android.os.vibrator.StepSegment;
+import android.os.vibrator.VibrationEffectSegment;
+import android.util.Slog;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Represents a step to turn the vibrator on using a composition of PWLE segments.
+ *
+ * <p>This step will use the maximum supported number of consecutive segments of type
+ * {@link StepSegment} or {@link RampSegment} starting at the current index.
+ */
+final class ComposePwleVibratorStep extends AbstractVibratorStep {
+
+ ComposePwleVibratorStep(VibrationStepConductor conductor, long startTime,
+ VibratorController controller, VibrationEffect.Composed effect, int index,
+ long previousStepVibratorOffTimeout) {
+ // This step should wait for the last vibration to finish (with the timeout) and for the
+ // intended step start time (to respect the effect delays).
+ super(conductor, Math.max(startTime, previousStepVibratorOffTimeout), controller, effect,
+ index, previousStepVibratorOffTimeout);
+ }
+
+ @Override
+ public List<Step> play() {
+ Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "ComposePwleStep");
+ try {
+ // Load the next RampSegments to create a single composePwle call to the vibrator,
+ // limited to the vibrator PWLE maximum size.
+ int limit = controller.getVibratorInfo().getPwleSizeMax();
+ int segmentCount = limit > 0
+ ? Math.min(effect.getSegments().size(), segmentIndex + limit)
+ : effect.getSegments().size();
+ List<RampSegment> pwles = new ArrayList<>();
+ for (int i = segmentIndex; i < segmentCount; i++) {
+ VibrationEffectSegment segment = effect.getSegments().get(i);
+ if (segment instanceof RampSegment) {
+ pwles.add((RampSegment) segment);
+ } else {
+ break;
+ }
+ }
+
+ if (pwles.isEmpty()) {
+ Slog.w(VibrationThread.TAG, "Ignoring wrong segment for a ComposePwleStep: "
+ + effect.getSegments().get(segmentIndex));
+ return skipToNextSteps(/* segmentsSkipped= */ 1);
+ }
+
+ if (VibrationThread.DEBUG) {
+ Slog.d(VibrationThread.TAG, "Compose " + pwles + " PWLEs on vibrator "
+ + controller.getVibratorInfo().getId());
+ }
+ mVibratorOnResult = controller.on(pwles.toArray(new RampSegment[pwles.size()]),
+ getVibration().id);
+
+ return nextSteps(/* segmentsPlayed= */ pwles.size());
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/vibrator/FinishSequentialEffectStep.java b/services/core/java/com/android/server/vibrator/FinishSequentialEffectStep.java
new file mode 100644
index 000000000000..bbbca024214f
--- /dev/null
+++ b/services/core/java/com/android/server/vibrator/FinishSequentialEffectStep.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.vibrator;
+
+import android.os.Trace;
+import android.util.Slog;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Finish a sync vibration started by a {@link StartSequentialEffectStep}.
+ *
+ * <p>This only plays after all active vibrators steps have finished, and adds a {@link
+ * StartSequentialEffectStep} to the queue if the sequential effect isn't finished yet.
+ */
+final class FinishSequentialEffectStep extends Step {
+ public final StartSequentialEffectStep startedStep;
+
+ FinishSequentialEffectStep(StartSequentialEffectStep startedStep) {
+ // No predefined startTime, just wait for all steps in the queue.
+ super(startedStep.conductor, Long.MAX_VALUE);
+ this.startedStep = startedStep;
+ }
+
+ @Override
+ public boolean isCleanUp() {
+ // This step only notes that all the vibrators has been turned off.
+ return true;
+ }
+
+ @Override
+ public List<Step> play() {
+ Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "FinishSequentialEffectStep");
+ try {
+ if (VibrationThread.DEBUG) {
+ Slog.d(VibrationThread.TAG,
+ "FinishSequentialEffectStep for effect #" + startedStep.currentIndex);
+ }
+ conductor.vibratorManagerHooks.noteVibratorOff(conductor.getVibration().uid);
+ Step nextStep = startedStep.nextStep();
+ return nextStep == null ? VibrationStepConductor.EMPTY_STEP_LIST
+ : Arrays.asList(nextStep);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
+ }
+ }
+
+ @Override
+ public List<Step> cancel() {
+ cancelImmediately();
+ return VibrationStepConductor.EMPTY_STEP_LIST;
+ }
+
+ @Override
+ public void cancelImmediately() {
+ conductor.vibratorManagerHooks.noteVibratorOff(conductor.getVibration().uid);
+ }
+}
diff --git a/services/core/java/com/android/server/vibrator/PerformPrebakedVibratorStep.java b/services/core/java/com/android/server/vibrator/PerformPrebakedVibratorStep.java
new file mode 100644
index 000000000000..601ae978f637
--- /dev/null
+++ b/services/core/java/com/android/server/vibrator/PerformPrebakedVibratorStep.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.vibrator;
+
+import android.os.Trace;
+import android.os.VibrationEffect;
+import android.os.vibrator.PrebakedSegment;
+import android.os.vibrator.VibrationEffectSegment;
+import android.util.Slog;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Represents a step to turn the vibrator on with a single prebaked effect.
+ *
+ * <p>This step automatically falls back by replacing the prebaked segment with
+ * {@link VibrationSettings#getFallbackEffect(int)}, if available.
+ */
+final class PerformPrebakedVibratorStep extends AbstractVibratorStep {
+
+ PerformPrebakedVibratorStep(VibrationStepConductor conductor, long startTime,
+ VibratorController controller, VibrationEffect.Composed effect, int index,
+ long previousStepVibratorOffTimeout) {
+ // This step should wait for the last vibration to finish (with the timeout) and for the
+ // intended step start time (to respect the effect delays).
+ super(conductor, Math.max(startTime, previousStepVibratorOffTimeout), controller, effect,
+ index, previousStepVibratorOffTimeout);
+ }
+
+ @Override
+ public List<Step> play() {
+ Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "PerformPrebakedVibratorStep");
+ try {
+ VibrationEffectSegment segment = effect.getSegments().get(segmentIndex);
+ if (!(segment instanceof PrebakedSegment)) {
+ Slog.w(VibrationThread.TAG, "Ignoring wrong segment for a "
+ + "PerformPrebakedVibratorStep: " + segment);
+ return skipToNextSteps(/* segmentsSkipped= */ 1);
+ }
+
+ PrebakedSegment prebaked = (PrebakedSegment) segment;
+ if (VibrationThread.DEBUG) {
+ Slog.d(VibrationThread.TAG, "Perform " + VibrationEffect.effectIdToString(
+ prebaked.getEffectId()) + " on vibrator "
+ + controller.getVibratorInfo().getId());
+ }
+
+ VibrationEffect fallback = getVibration().getFallback(prebaked.getEffectId());
+ mVibratorOnResult = controller.on(prebaked, getVibration().id);
+
+ if (mVibratorOnResult == 0 && prebaked.shouldFallback()
+ && (fallback instanceof VibrationEffect.Composed)) {
+ if (VibrationThread.DEBUG) {
+ Slog.d(VibrationThread.TAG, "Playing fallback for effect "
+ + VibrationEffect.effectIdToString(prebaked.getEffectId()));
+ }
+ AbstractVibratorStep fallbackStep = conductor.nextVibrateStep(startTime, controller,
+ replaceCurrentSegment((VibrationEffect.Composed) fallback),
+ segmentIndex, previousStepVibratorOffTimeout);
+ List<Step> fallbackResult = fallbackStep.play();
+ // Update the result with the fallback result so this step is seamlessly
+ // replaced by the fallback to any outer application of this.
+ mVibratorOnResult = fallbackStep.getVibratorOnDuration();
+ return fallbackResult;
+ }
+
+ return nextSteps(/* segmentsPlayed= */ 1);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
+ }
+ }
+
+ /**
+ * Replace segment at {@link #segmentIndex} in {@link #effect} with given fallback segments.
+ *
+ * @return a copy of {@link #effect} with replaced segment.
+ */
+ private VibrationEffect.Composed replaceCurrentSegment(VibrationEffect.Composed fallback) {
+ List<VibrationEffectSegment> newSegments = new ArrayList<>(effect.getSegments());
+ int newRepeatIndex = effect.getRepeatIndex();
+ newSegments.remove(segmentIndex);
+ newSegments.addAll(segmentIndex, fallback.getSegments());
+ if (segmentIndex < effect.getRepeatIndex()) {
+ newRepeatIndex += fallback.getSegments().size() - 1;
+ }
+ return new VibrationEffect.Composed(newSegments, newRepeatIndex);
+ }
+}
diff --git a/services/core/java/com/android/server/vibrator/RampOffVibratorStep.java b/services/core/java/com/android/server/vibrator/RampOffVibratorStep.java
new file mode 100644
index 000000000000..8cf5fb394d9d
--- /dev/null
+++ b/services/core/java/com/android/server/vibrator/RampOffVibratorStep.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.vibrator;
+
+import android.os.SystemClock;
+import android.os.Trace;
+import android.util.Slog;
+
+import java.util.Arrays;
+import java.util.List;
+
+/** Represents a step to ramp down the vibrator amplitude before turning it off. */
+final class RampOffVibratorStep extends AbstractVibratorStep {
+ private final float mAmplitudeTarget;
+ private final float mAmplitudeDelta;
+
+ RampOffVibratorStep(VibrationStepConductor conductor, long startTime, float amplitudeTarget,
+ float amplitudeDelta, VibratorController controller,
+ long previousStepVibratorOffTimeout) {
+ super(conductor, startTime, controller, /* effect= */ null, /* index= */ -1,
+ previousStepVibratorOffTimeout);
+ mAmplitudeTarget = amplitudeTarget;
+ mAmplitudeDelta = amplitudeDelta;
+ }
+
+ @Override
+ public boolean isCleanUp() {
+ return true;
+ }
+
+ @Override
+ public List<Step> cancel() {
+ return Arrays.asList(
+ new TurnOffVibratorStep(conductor, SystemClock.uptimeMillis(), controller));
+ }
+
+ @Override
+ public List<Step> play() {
+ Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "RampOffVibratorStep");
+ try {
+ if (VibrationThread.DEBUG) {
+ long latency = SystemClock.uptimeMillis() - startTime;
+ Slog.d(VibrationThread.TAG, "Ramp down the vibrator amplitude, step with "
+ + latency + "ms latency.");
+ }
+ if (mVibratorCompleteCallbackReceived) {
+ // Vibration completion callback was received by this step, just turn if off
+ // and skip the rest of the steps to ramp down the vibrator amplitude.
+ stopVibrating();
+ return VibrationStepConductor.EMPTY_STEP_LIST;
+ }
+
+ changeAmplitude(mAmplitudeTarget);
+
+ float newAmplitudeTarget = mAmplitudeTarget - mAmplitudeDelta;
+ if (newAmplitudeTarget < VibrationStepConductor.RAMP_OFF_AMPLITUDE_MIN) {
+ // Vibrator amplitude cannot go further down, just turn it off.
+ return Arrays.asList(new TurnOffVibratorStep(
+ conductor, previousStepVibratorOffTimeout, controller));
+ }
+ return Arrays.asList(new RampOffVibratorStep(
+ conductor,
+ startTime + conductor.vibrationSettings.getRampStepDuration(),
+ newAmplitudeTarget, mAmplitudeDelta, controller,
+ previousStepVibratorOffTimeout));
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/vibrator/SetAmplitudeVibratorStep.java b/services/core/java/com/android/server/vibrator/SetAmplitudeVibratorStep.java
new file mode 100644
index 000000000000..d5c11161bdfa
--- /dev/null
+++ b/services/core/java/com/android/server/vibrator/SetAmplitudeVibratorStep.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.vibrator;
+
+import android.os.SystemClock;
+import android.os.Trace;
+import android.os.VibrationEffect;
+import android.os.vibrator.StepSegment;
+import android.os.vibrator.VibrationEffectSegment;
+import android.util.Slog;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Represents a step to turn the vibrator on and change its amplitude.
+ *
+ * <p>This step ignores vibration completion callbacks and control the vibrator on/off state
+ * and amplitude to simulate waveforms represented by a sequence of {@link StepSegment}.
+ */
+final class SetAmplitudeVibratorStep extends AbstractVibratorStep {
+ private long mNextOffTime;
+
+ SetAmplitudeVibratorStep(VibrationStepConductor conductor, long startTime,
+ VibratorController controller, VibrationEffect.Composed effect, int index,
+ long previousStepVibratorOffTimeout) {
+ // This step has a fixed startTime coming from the timings of the waveform it's playing.
+ super(conductor, startTime, controller, effect, index, previousStepVibratorOffTimeout);
+ mNextOffTime = previousStepVibratorOffTimeout;
+ }
+
+ @Override
+ public boolean acceptVibratorCompleteCallback(int vibratorId) {
+ if (controller.getVibratorInfo().getId() == vibratorId) {
+ mVibratorCompleteCallbackReceived = true;
+ mNextOffTime = SystemClock.uptimeMillis();
+ }
+ // Timings are tightly controlled here, so only trigger this step if the vibrator was
+ // supposed to be ON but has completed prematurely, to turn it back on as soon as
+ // possible.
+ return mNextOffTime < startTime && controller.getCurrentAmplitude() > 0;
+ }
+
+ @Override
+ public List<Step> play() {
+ // TODO: consider separating the "on" steps at the start into a separate Step.
+ // TODO: consider instantiating the step with the required amplitude, rather than
+ // needing to dig into the effect.
+ Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "SetAmplitudeVibratorStep");
+ try {
+ long now = SystemClock.uptimeMillis();
+ long latency = now - startTime;
+ if (VibrationThread.DEBUG) {
+ Slog.d(VibrationThread.TAG,
+ "Running amplitude step with " + latency + "ms latency.");
+ }
+
+ if (mVibratorCompleteCallbackReceived && latency < 0) {
+ // This step was run early because the vibrator turned off prematurely.
+ // Turn it back on and return this same step to run at the exact right time.
+ mNextOffTime = turnVibratorBackOn(/* remainingDuration= */ -latency);
+ return Arrays.asList(new SetAmplitudeVibratorStep(conductor, startTime, controller,
+ effect, segmentIndex, mNextOffTime));
+ }
+
+ VibrationEffectSegment segment = effect.getSegments().get(segmentIndex);
+ if (!(segment instanceof StepSegment)) {
+ Slog.w(VibrationThread.TAG,
+ "Ignoring wrong segment for a SetAmplitudeVibratorStep: " + segment);
+ return skipToNextSteps(/* segmentsSkipped= */ 1);
+ }
+
+ StepSegment stepSegment = (StepSegment) segment;
+ if (stepSegment.getDuration() == 0) {
+ // Skip waveform entries with zero timing.
+ return skipToNextSteps(/* segmentsSkipped= */ 1);
+ }
+
+ float amplitude = stepSegment.getAmplitude();
+ if (amplitude == 0) {
+ if (previousStepVibratorOffTimeout > now) {
+ // Amplitude cannot be set to zero, so stop the vibrator.
+ stopVibrating();
+ mNextOffTime = now;
+ }
+ } else {
+ if (startTime >= mNextOffTime) {
+ // Vibrator is OFF. Turn vibrator back on for the duration of another
+ // cycle before setting the amplitude.
+ long onDuration = getVibratorOnDuration(effect, segmentIndex);
+ if (onDuration > 0) {
+ mVibratorOnResult = startVibrating(onDuration);
+ mNextOffTime = now + onDuration
+ + VibrationStepConductor.CALLBACKS_EXTRA_TIMEOUT;
+ }
+ }
+ changeAmplitude(amplitude);
+ }
+
+ // Use original startTime to avoid propagating latencies to the waveform.
+ long nextStartTime = startTime + segment.getDuration();
+ return nextSteps(nextStartTime, mNextOffTime, /* segmentsPlayed= */ 1);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
+ }
+ }
+
+ private long turnVibratorBackOn(long remainingDuration) {
+ long onDuration = getVibratorOnDuration(effect, segmentIndex);
+ if (onDuration <= 0) {
+ // Vibrator is supposed to go back off when this step starts, so just leave it off.
+ return previousStepVibratorOffTimeout;
+ }
+ onDuration += remainingDuration;
+ float expectedAmplitude = controller.getCurrentAmplitude();
+ mVibratorOnResult = startVibrating(onDuration);
+ if (mVibratorOnResult > 0) {
+ // Set the amplitude back to the value it was supposed to be playing at.
+ changeAmplitude(expectedAmplitude);
+ }
+ return SystemClock.uptimeMillis() + onDuration
+ + VibrationStepConductor.CALLBACKS_EXTRA_TIMEOUT;
+ }
+
+ private long startVibrating(long duration) {
+ if (VibrationThread.DEBUG) {
+ Slog.d(VibrationThread.TAG,
+ "Turning on vibrator " + controller.getVibratorInfo().getId() + " for "
+ + duration + "ms");
+ }
+ return controller.on(duration, getVibration().id);
+ }
+
+ /**
+ * Get the duration the vibrator will be on for a waveform, starting at {@code startIndex}
+ * until the next time it's vibrating amplitude is zero or a different type of segment is
+ * found.
+ */
+ private long getVibratorOnDuration(VibrationEffect.Composed effect, int startIndex) {
+ List<VibrationEffectSegment> segments = effect.getSegments();
+ int segmentCount = segments.size();
+ int repeatIndex = effect.getRepeatIndex();
+ int i = startIndex;
+ long timing = 0;
+ while (i < segmentCount) {
+ VibrationEffectSegment segment = segments.get(i);
+ if (!(segment instanceof StepSegment)
+ || ((StepSegment) segment).getAmplitude() == 0) {
+ break;
+ }
+ timing += segment.getDuration();
+ i++;
+ if (i == segmentCount && repeatIndex >= 0) {
+ i = repeatIndex;
+ // prevent infinite loop
+ repeatIndex = -1;
+ }
+ if (i == startIndex) {
+ // The repeating waveform keeps the vibrator ON all the time. Use a minimum
+ // of 1s duration to prevent short patterns from turning the vibrator ON too
+ // frequently.
+ return Math.max(timing, 1000);
+ }
+ }
+ if (i == segmentCount && effect.getRepeatIndex() < 0) {
+ // Vibration ending at non-zero amplitude, add extra timings to ramp down after
+ // vibration is complete.
+ timing += conductor.vibrationSettings.getRampDownDuration();
+ }
+ return timing;
+ }
+}
diff --git a/services/core/java/com/android/server/vibrator/StartSequentialEffectStep.java b/services/core/java/com/android/server/vibrator/StartSequentialEffectStep.java
new file mode 100644
index 000000000000..080a36cb2a6e
--- /dev/null
+++ b/services/core/java/com/android/server/vibrator/StartSequentialEffectStep.java
@@ -0,0 +1,370 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.vibrator;
+
+import android.annotation.Nullable;
+import android.hardware.vibrator.IVibratorManager;
+import android.os.CombinedVibration;
+import android.os.SystemClock;
+import android.os.Trace;
+import android.os.VibrationEffect;
+import android.os.VibratorInfo;
+import android.os.vibrator.PrebakedSegment;
+import android.os.vibrator.PrimitiveSegment;
+import android.os.vibrator.StepSegment;
+import android.os.vibrator.VibrationEffectSegment;
+import android.util.Slog;
+import android.util.SparseArray;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Starts a sync vibration.
+ *
+ * <p>If this step has successfully started playing a vibration on any vibrator, it will always
+ * add a {@link FinishSequentialEffectStep} to the queue, to be played after all vibrators
+ * have finished all their individual steps.
+ *
+ * <p>If this step does not start any vibrator, it will add a {@link StartSequentialEffectStep} if
+ * the sequential effect isn't finished yet.
+ *
+ * <p>TODO: this step actually does several things: multiple HAL calls to sync the vibrators,
+ * as well as dispatching the underlying vibrator instruction calls (which need to be done before
+ * triggering the synced effects). This role/encapsulation could probably be improved to split up
+ * the grouped HAL calls here, as well as to clarify the role of dispatching VibratorSteps between
+ * this class and the controller.
+ */
+final class StartSequentialEffectStep extends Step {
+ public final CombinedVibration.Sequential sequentialEffect;
+ public final int currentIndex;
+
+ private long mVibratorsOnMaxDuration;
+
+ /** Start a sequential effect at the beginning. */
+ StartSequentialEffectStep(VibrationStepConductor conductor,
+ CombinedVibration.Sequential effect) {
+ this(conductor, SystemClock.uptimeMillis() + effect.getDelays().get(0), effect,
+ /* index= */ 0);
+ }
+
+ /** Continue a SequentialEffect from the specified index. */
+ private StartSequentialEffectStep(VibrationStepConductor conductor, long startTime,
+ CombinedVibration.Sequential effect, int index) {
+ super(conductor, startTime);
+ sequentialEffect = effect;
+ currentIndex = index;
+ }
+
+ @Override
+ public long getVibratorOnDuration() {
+ return mVibratorsOnMaxDuration;
+ }
+
+ @Override
+ public List<Step> play() {
+ Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "StartSequentialEffectStep");
+ List<Step> nextSteps = new ArrayList<>();
+ mVibratorsOnMaxDuration = -1;
+ try {
+ if (VibrationThread.DEBUG) {
+ Slog.d(VibrationThread.TAG,
+ "StartSequentialEffectStep for effect #" + currentIndex);
+ }
+ CombinedVibration effect = sequentialEffect.getEffects().get(currentIndex);
+ DeviceEffectMap effectMapping = createEffectToVibratorMapping(effect);
+ if (effectMapping == null) {
+ // Unable to map effects to vibrators, ignore this step.
+ return nextSteps;
+ }
+
+ mVibratorsOnMaxDuration = startVibrating(effectMapping, nextSteps);
+ if (mVibratorsOnMaxDuration > 0) {
+ conductor.vibratorManagerHooks.noteVibratorOn(conductor.getVibration().uid,
+ mVibratorsOnMaxDuration);
+ }
+ } finally {
+ if (mVibratorsOnMaxDuration >= 0) {
+ // It least one vibrator was started then add a finish step to wait for all
+ // active vibrators to finish their individual steps before going to the next.
+ // Otherwise this step was ignored so just go to the next one.
+ Step nextStep =
+ mVibratorsOnMaxDuration > 0 ? new FinishSequentialEffectStep(this)
+ : nextStep();
+ if (nextStep != null) {
+ nextSteps.add(nextStep);
+ }
+ }
+ Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
+ }
+ return nextSteps;
+ }
+
+ @Override
+ public List<Step> cancel() {
+ return VibrationStepConductor.EMPTY_STEP_LIST;
+ }
+
+ @Override
+ public void cancelImmediately() {
+ }
+
+ /**
+ * Create the next {@link StartSequentialEffectStep} to play this sequential effect, starting at
+ * the time this method is called, or null if sequence is complete.
+ */
+ @Nullable
+ Step nextStep() {
+ int nextIndex = currentIndex + 1;
+ if (nextIndex >= sequentialEffect.getEffects().size()) {
+ return null;
+ }
+ long nextEffectDelay = sequentialEffect.getDelays().get(nextIndex);
+ long nextStartTime = SystemClock.uptimeMillis() + nextEffectDelay;
+ return new StartSequentialEffectStep(conductor, nextStartTime, sequentialEffect,
+ nextIndex);
+ }
+
+ /** Create a mapping of individual {@link VibrationEffect} to available vibrators. */
+ @Nullable
+ private DeviceEffectMap createEffectToVibratorMapping(
+ CombinedVibration effect) {
+ if (effect instanceof CombinedVibration.Mono) {
+ return new DeviceEffectMap((CombinedVibration.Mono) effect);
+ }
+ if (effect instanceof CombinedVibration.Stereo) {
+ return new DeviceEffectMap((CombinedVibration.Stereo) effect);
+ }
+ return null;
+ }
+
+ /**
+ * Starts playing effects on designated vibrators, in sync.
+ *
+ * @param effectMapping The {@link CombinedVibration} mapped to this device vibrators
+ * @param nextSteps An output list to accumulate the future {@link Step
+ * Steps} created
+ * by this method, typically one for each vibrator that has
+ * successfully started vibrating on this step.
+ * @return The duration, in millis, of the {@link CombinedVibration}. Repeating
+ * waveforms return {@link Long#MAX_VALUE}. Zero or negative values indicate the vibrators
+ * have ignored all effects.
+ */
+ private long startVibrating(
+ DeviceEffectMap effectMapping, List<Step> nextSteps) {
+ int vibratorCount = effectMapping.size();
+ if (vibratorCount == 0) {
+ // No effect was mapped to any available vibrator.
+ return 0;
+ }
+
+ AbstractVibratorStep[] steps = new AbstractVibratorStep[vibratorCount];
+ long vibrationStartTime = SystemClock.uptimeMillis();
+ for (int i = 0; i < vibratorCount; i++) {
+ steps[i] = conductor.nextVibrateStep(vibrationStartTime,
+ conductor.getVibrators().get(effectMapping.vibratorIdAt(i)),
+ effectMapping.effectAt(i),
+ /* segmentIndex= */ 0, /* vibratorOffTimeout= */ 0);
+ }
+
+ if (steps.length == 1) {
+ // No need to prepare and trigger sync effects on a single vibrator.
+ return startVibrating(steps[0], nextSteps);
+ }
+
+ // This synchronization of vibrators should be executed one at a time, even if we are
+ // vibrating different sets of vibrators in parallel. The manager can only prepareSynced
+ // one set of vibrators at a time.
+ // This property is guaranteed by there only being one thread (VibrationThread) executing
+ // one Step at a time, so there's no need to hold the state lock. Callbacks will be
+ // delivered asynchronously but enqueued until the step processing is finished.
+ boolean hasPrepared = false;
+ boolean hasTriggered = false;
+ long maxDuration = 0;
+ try {
+ hasPrepared = conductor.vibratorManagerHooks.prepareSyncedVibration(
+ effectMapping.getRequiredSyncCapabilities(),
+ effectMapping.getVibratorIds());
+
+ for (AbstractVibratorStep step : steps) {
+ long duration = startVibrating(step, nextSteps);
+ if (duration < 0) {
+ // One vibrator has failed, fail this entire sync attempt.
+ return maxDuration = -1;
+ }
+ maxDuration = Math.max(maxDuration, duration);
+ }
+
+ // Check if sync was prepared and if any step was accepted by a vibrator,
+ // otherwise there is nothing to trigger here.
+ if (hasPrepared && maxDuration > 0) {
+ hasTriggered = conductor.vibratorManagerHooks.triggerSyncedVibration(
+ getVibration().id);
+ }
+ return maxDuration;
+ } finally {
+ if (hasPrepared && !hasTriggered) {
+ // Trigger has failed or all steps were ignored by the vibrators.
+ conductor.vibratorManagerHooks.cancelSyncedVibration();
+ nextSteps.clear();
+ } else if (maxDuration < 0) {
+ // Some vibrator failed without being prepared so other vibrators might be
+ // active. Cancel and remove every pending step from output list.
+ for (int i = nextSteps.size() - 1; i >= 0; i--) {
+ nextSteps.remove(i).cancelImmediately();
+ }
+ }
+ }
+ }
+
+ private long startVibrating(AbstractVibratorStep step, List<Step> nextSteps) {
+ nextSteps.addAll(step.play());
+ long stepDuration = step.getVibratorOnDuration();
+ if (stepDuration < 0) {
+ // Step failed, so return negative duration to propagate failure.
+ return stepDuration;
+ }
+ // Return the longest estimation for the entire effect.
+ return Math.max(stepDuration, step.effect.getDuration());
+ }
+
+ /**
+ * Map a {@link CombinedVibration} to the vibrators available on the device.
+ *
+ * <p>This contains the logic to find the capabilities required from {@link IVibratorManager} to
+ * play all of the effects in sync.
+ */
+ final class DeviceEffectMap {
+ private final SparseArray<VibrationEffect.Composed> mVibratorEffects;
+ private final int[] mVibratorIds;
+ private final long mRequiredSyncCapabilities;
+
+ DeviceEffectMap(CombinedVibration.Mono mono) {
+ SparseArray<VibratorController> vibrators = conductor.getVibrators();
+ mVibratorEffects = new SparseArray<>(vibrators.size());
+ mVibratorIds = new int[vibrators.size()];
+ for (int i = 0; i < vibrators.size(); i++) {
+ int vibratorId = vibrators.keyAt(i);
+ VibratorInfo vibratorInfo = vibrators.valueAt(i).getVibratorInfo();
+ VibrationEffect effect = conductor.deviceEffectAdapter.apply(
+ mono.getEffect(), vibratorInfo);
+ if (effect instanceof VibrationEffect.Composed) {
+ mVibratorEffects.put(vibratorId, (VibrationEffect.Composed) effect);
+ mVibratorIds[i] = vibratorId;
+ }
+ }
+ mRequiredSyncCapabilities = calculateRequiredSyncCapabilities(mVibratorEffects);
+ }
+
+ DeviceEffectMap(CombinedVibration.Stereo stereo) {
+ SparseArray<VibratorController> vibrators = conductor.getVibrators();
+ SparseArray<VibrationEffect> stereoEffects = stereo.getEffects();
+ mVibratorEffects = new SparseArray<>();
+ for (int i = 0; i < stereoEffects.size(); i++) {
+ int vibratorId = stereoEffects.keyAt(i);
+ if (vibrators.contains(vibratorId)) {
+ VibratorInfo vibratorInfo = vibrators.valueAt(i).getVibratorInfo();
+ VibrationEffect effect = conductor.deviceEffectAdapter.apply(
+ stereoEffects.valueAt(i), vibratorInfo);
+ if (effect instanceof VibrationEffect.Composed) {
+ mVibratorEffects.put(vibratorId, (VibrationEffect.Composed) effect);
+ }
+ }
+ }
+ mVibratorIds = new int[mVibratorEffects.size()];
+ for (int i = 0; i < mVibratorEffects.size(); i++) {
+ mVibratorIds[i] = mVibratorEffects.keyAt(i);
+ }
+ mRequiredSyncCapabilities = calculateRequiredSyncCapabilities(mVibratorEffects);
+ }
+
+ /**
+ * Return the number of vibrators mapped to play the {@link CombinedVibration} on this
+ * device.
+ */
+ public int size() {
+ return mVibratorIds.length;
+ }
+
+ /**
+ * Return all capabilities required to play the {@link CombinedVibration} in
+ * between calls to {@link IVibratorManager#prepareSynced} and
+ * {@link IVibratorManager#triggerSynced}.
+ */
+ public long getRequiredSyncCapabilities() {
+ return mRequiredSyncCapabilities;
+ }
+
+ /** Return all vibrator ids mapped to play the {@link CombinedVibration}. */
+ public int[] getVibratorIds() {
+ return mVibratorIds;
+ }
+
+ /** Return the id of the vibrator at given index. */
+ public int vibratorIdAt(int index) {
+ return mVibratorEffects.keyAt(index);
+ }
+
+ /** Return the {@link VibrationEffect} at given index. */
+ public VibrationEffect.Composed effectAt(int index) {
+ return mVibratorEffects.valueAt(index);
+ }
+
+ /**
+ * Return all capabilities required from the {@link IVibratorManager} to prepare and
+ * trigger all given effects in sync.
+ *
+ * @return {@link IVibratorManager#CAP_SYNC} together with all required
+ * IVibratorManager.CAP_PREPARE_* and IVibratorManager.CAP_MIXED_TRIGGER_* capabilities.
+ */
+ private long calculateRequiredSyncCapabilities(
+ SparseArray<VibrationEffect.Composed> effects) {
+ long prepareCap = 0;
+ for (int i = 0; i < effects.size(); i++) {
+ VibrationEffectSegment firstSegment = effects.valueAt(i).getSegments().get(0);
+ if (firstSegment instanceof StepSegment) {
+ prepareCap |= IVibratorManager.CAP_PREPARE_ON;
+ } else if (firstSegment instanceof PrebakedSegment) {
+ prepareCap |= IVibratorManager.CAP_PREPARE_PERFORM;
+ } else if (firstSegment instanceof PrimitiveSegment) {
+ prepareCap |= IVibratorManager.CAP_PREPARE_COMPOSE;
+ }
+ }
+ int triggerCap = 0;
+ if (requireMixedTriggerCapability(prepareCap, IVibratorManager.CAP_PREPARE_ON)) {
+ triggerCap |= IVibratorManager.CAP_MIXED_TRIGGER_ON;
+ }
+ if (requireMixedTriggerCapability(prepareCap, IVibratorManager.CAP_PREPARE_PERFORM)) {
+ triggerCap |= IVibratorManager.CAP_MIXED_TRIGGER_PERFORM;
+ }
+ if (requireMixedTriggerCapability(prepareCap, IVibratorManager.CAP_PREPARE_COMPOSE)) {
+ triggerCap |= IVibratorManager.CAP_MIXED_TRIGGER_COMPOSE;
+ }
+ return IVibratorManager.CAP_SYNC | prepareCap | triggerCap;
+ }
+
+ /**
+ * Return true if {@code prepareCapabilities} contains this {@code capability} mixed with
+ * different ones, requiring a mixed trigger capability from the vibrator manager for
+ * syncing all effects.
+ */
+ private boolean requireMixedTriggerCapability(long prepareCapabilities, long capability) {
+ return (prepareCapabilities & capability) != 0
+ && (prepareCapabilities & ~capability) != 0;
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/vibrator/Step.java b/services/core/java/com/android/server/vibrator/Step.java
new file mode 100644
index 000000000000..042e8a0e6ad5
--- /dev/null
+++ b/services/core/java/com/android/server/vibrator/Step.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.vibrator;
+
+import android.annotation.NonNull;
+import android.os.CombinedVibration;
+import android.os.SystemClock;
+import android.os.VibrationEffect;
+
+import java.util.List;
+
+/**
+ * Represent a single step for playing a vibration.
+ *
+ * <p>Every step has a start time, which can be used to apply delays between steps while
+ * executing them in sequence.
+ */
+abstract class Step implements Comparable<Step> {
+ public final VibrationStepConductor conductor;
+ public final long startTime;
+
+ Step(VibrationStepConductor conductor, long startTime) {
+ this.conductor = conductor;
+ this.startTime = startTime;
+ }
+
+ protected Vibration getVibration() {
+ return conductor.getVibration();
+ }
+
+ /**
+ * Returns true if this step is a clean up step and not part of a {@link VibrationEffect} or
+ * {@link CombinedVibration}.
+ */
+ public boolean isCleanUp() {
+ return false;
+ }
+
+ /** Play this step, returning a (possibly empty) list of next steps. */
+ @NonNull
+ public abstract List<Step> play();
+
+ /**
+ * Cancel this pending step and return a (possibly empty) list of clean-up steps that should
+ * be played to gracefully cancel this step.
+ */
+ @NonNull
+ public abstract List<Step> cancel();
+
+ /** Cancel this pending step immediately, skipping any clean-up. */
+ public abstract void cancelImmediately();
+
+ /**
+ * Return the duration the vibrator was turned on when this step was played.
+ *
+ * @return A positive duration that the vibrator was turned on for by this step;
+ * Zero if the segment is not supported, the step was not played yet or vibrator was never
+ * turned on by this step; A negative value if the vibrator call has failed.
+ */
+ public long getVibratorOnDuration() {
+ return 0;
+ }
+
+ /**
+ * Return true to run this step right after a vibrator has notified vibration completed,
+ * used to resume steps waiting on vibrator callbacks with a timeout.
+ */
+ public boolean acceptVibratorCompleteCallback(int vibratorId) {
+ return false;
+ }
+
+ /**
+ * Returns the time in millis to wait before playing this step. This is performed
+ * while holding the queue lock, so should not rely on potentially slow operations.
+ */
+ public long calculateWaitTime() {
+ if (startTime == Long.MAX_VALUE) {
+ // This step don't have a predefined start time, it's just marked to be executed
+ // after all other steps have finished.
+ return 0;
+ }
+ return Math.max(0, startTime - SystemClock.uptimeMillis());
+ }
+
+ @Override
+ public int compareTo(Step o) {
+ return Long.compare(startTime, o.startTime);
+ }
+}
diff --git a/services/core/java/com/android/server/vibrator/TurnOffVibratorStep.java b/services/core/java/com/android/server/vibrator/TurnOffVibratorStep.java
new file mode 100644
index 000000000000..297ef5614e84
--- /dev/null
+++ b/services/core/java/com/android/server/vibrator/TurnOffVibratorStep.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.vibrator;
+
+import android.os.SystemClock;
+import android.os.Trace;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Represents a step to turn the vibrator off.
+ *
+ * <p>This runs after a timeout on the expected time the vibrator should have finished playing,
+ * and can be brought forward by vibrator complete callbacks. The step shouldn't be skipped, even
+ * if the vibrator-complete callback was received, as some implementations still rely on the
+ * "off" call to actually stop.
+ */
+final class TurnOffVibratorStep extends AbstractVibratorStep {
+
+ TurnOffVibratorStep(VibrationStepConductor conductor, long startTime,
+ VibratorController controller) {
+ super(conductor, startTime, controller, /* effect= */ null, /* index= */ -1, startTime);
+ }
+
+ @Override
+ public boolean isCleanUp() {
+ return true;
+ }
+
+ @Override
+ public List<Step> cancel() {
+ return Arrays.asList(
+ new TurnOffVibratorStep(conductor, SystemClock.uptimeMillis(), controller));
+ }
+
+ @Override
+ public void cancelImmediately() {
+ stopVibrating();
+ }
+
+ @Override
+ public List<Step> play() {
+ Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "TurnOffVibratorStep");
+ try {
+ stopVibrating();
+ return VibrationStepConductor.EMPTY_STEP_LIST;
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/vibrator/VibrationStepConductor.java b/services/core/java/com/android/server/vibrator/VibrationStepConductor.java
new file mode 100644
index 000000000000..366763110565
--- /dev/null
+++ b/services/core/java/com/android/server/vibrator/VibrationStepConductor.java
@@ -0,0 +1,431 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.vibrator;
+
+import android.annotation.Nullable;
+import android.os.Build;
+import android.os.CombinedVibration;
+import android.os.VibrationEffect;
+import android.os.vibrator.PrebakedSegment;
+import android.os.vibrator.PrimitiveSegment;
+import android.os.vibrator.RampSegment;
+import android.os.vibrator.VibrationEffectSegment;
+import android.util.Slog;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.PriorityQueue;
+import java.util.Queue;
+
+/**
+ * Creates and manages a queue of steps for performing a VibrationEffect, as well as coordinating
+ * dispatch of callbacks.
+ *
+ * <p>In general, methods in this class are intended to be called only by a single instance of
+ * VibrationThread. The only thread-safe methods for calling from other threads are the "notify"
+ * methods (which should never be used from the VibrationThread thread).
+ */
+final class VibrationStepConductor {
+ private static final boolean DEBUG = VibrationThread.DEBUG;
+ private static final String TAG = VibrationThread.TAG;
+
+ /**
+ * Extra timeout added to the end of each vibration step to ensure it finishes even when
+ * vibrator callbacks are lost.
+ */
+ static final long CALLBACKS_EXTRA_TIMEOUT = 1_000;
+ /** Threshold to prevent the ramp off steps from trying to set extremely low amplitudes. */
+ static final float RAMP_OFF_AMPLITUDE_MIN = 1e-3f;
+ static final List<Step> EMPTY_STEP_LIST = new ArrayList<>();
+
+ private final Object mLock = new Object();
+
+ // Used within steps.
+ public final VibrationSettings vibrationSettings;
+ public final DeviceVibrationEffectAdapter deviceEffectAdapter;
+ public final VibrationThread.VibratorManagerHooks vibratorManagerHooks;
+
+ private final Vibration mVibration;
+ private final SparseArray<VibratorController> mVibrators = new SparseArray<>();
+
+ private final PriorityQueue<Step> mNextSteps = new PriorityQueue<>();
+ private final Queue<Step> mPendingOnVibratorCompleteSteps = new LinkedList<>();
+ @GuardedBy("mLock")
+ private Queue<Integer> mCompletionNotifiedVibrators = new LinkedList<>();
+
+ private int mPendingVibrateSteps;
+ private int mRemainingStartSequentialEffectSteps;
+ private int mSuccessfulVibratorOnSteps;
+
+ VibrationStepConductor(Vibration vib, VibrationSettings vibrationSettings,
+ DeviceVibrationEffectAdapter effectAdapter,
+ SparseArray<VibratorController> availableVibrators,
+ VibrationThread.VibratorManagerHooks vibratorManagerHooks) {
+ this.mVibration = vib;
+ this.vibrationSettings = vibrationSettings;
+ this.deviceEffectAdapter = effectAdapter;
+ this.vibratorManagerHooks = vibratorManagerHooks;
+
+ CombinedVibration effect = vib.getEffect();
+ for (int i = 0; i < availableVibrators.size(); i++) {
+ if (effect.hasVibrator(availableVibrators.keyAt(i))) {
+ mVibrators.put(availableVibrators.keyAt(i), availableVibrators.valueAt(i));
+ }
+ }
+ }
+
+ @Nullable
+ AbstractVibratorStep nextVibrateStep(long startTime, VibratorController controller,
+ VibrationEffect.Composed effect, int segmentIndex,
+ long previousStepVibratorOffTimeout) {
+ if (Build.IS_DEBUGGABLE) {
+ expectIsVibrationThread(true);
+ }
+ if (segmentIndex >= effect.getSegments().size()) {
+ segmentIndex = effect.getRepeatIndex();
+ }
+ if (segmentIndex < 0) {
+ // No more segments to play, last step is to complete the vibration on this vibrator.
+ return new CompleteEffectVibratorStep(this, startTime, /* cancelled= */ false,
+ controller, previousStepVibratorOffTimeout);
+ }
+
+ VibrationEffectSegment segment = effect.getSegments().get(segmentIndex);
+ if (segment instanceof PrebakedSegment) {
+ return new PerformPrebakedVibratorStep(this, startTime, controller, effect,
+ segmentIndex, previousStepVibratorOffTimeout);
+ }
+ if (segment instanceof PrimitiveSegment) {
+ return new ComposePrimitivesVibratorStep(this, startTime, controller, effect,
+ segmentIndex, previousStepVibratorOffTimeout);
+ }
+ if (segment instanceof RampSegment) {
+ return new ComposePwleVibratorStep(this, startTime, controller, effect, segmentIndex,
+ previousStepVibratorOffTimeout);
+ }
+ return new SetAmplitudeVibratorStep(this, startTime, controller, effect, segmentIndex,
+ previousStepVibratorOffTimeout);
+ }
+
+ /** Called when this conductor is going to be started running by the VibrationThread. */
+ public void prepareToStart() {
+ if (Build.IS_DEBUGGABLE) {
+ expectIsVibrationThread(true);
+ }
+ CombinedVibration.Sequential sequentialEffect = toSequential(mVibration.getEffect());
+ mPendingVibrateSteps++;
+ // This count is decremented at the completion of the step, so we don't subtract one.
+ mRemainingStartSequentialEffectSteps = sequentialEffect.getEffects().size();
+ mNextSteps.offer(new StartSequentialEffectStep(this, sequentialEffect));
+ }
+
+ public Vibration getVibration() {
+ // No thread assertion: immutable
+ return mVibration;
+ }
+
+ SparseArray<VibratorController> getVibrators() {
+ // No thread assertion: immutable
+ return mVibrators;
+ }
+
+ public boolean isFinished() {
+ if (Build.IS_DEBUGGABLE) {
+ expectIsVibrationThread(true);
+ }
+ // No need to check for vibration complete callbacks - if there were any, they would
+ // have no steps to notify anyway.
+ return mPendingOnVibratorCompleteSteps.isEmpty() && mNextSteps.isEmpty();
+ }
+
+ /**
+ * Calculate the {@link Vibration.Status} based on the current queue state and the expected
+ * number of {@link StartSequentialEffectStep} to be played.
+ */
+ public Vibration.Status calculateVibrationStatus() {
+ if (Build.IS_DEBUGGABLE) {
+ expectIsVibrationThread(true);
+ }
+
+ if (mPendingVibrateSteps > 0
+ || mRemainingStartSequentialEffectSteps > 0) {
+ return Vibration.Status.RUNNING;
+ }
+ // No pending steps, and something happened.
+ if (mSuccessfulVibratorOnSteps > 0) {
+ return Vibration.Status.FINISHED;
+ }
+ // If no step was able to turn the vibrator ON successfully.
+ return Vibration.Status.IGNORED_UNSUPPORTED;
+ }
+
+ /**
+ * Blocks until the next step is due to run. The wait here may be interrupted by calling
+ * {@link #notifyWakeUp} or other "notify" methods.
+ *
+ * <p>This method returns false if the next step is ready to run now. If the method returns
+ * true, then some waiting was done, but may have been interrupted by a wakeUp.
+ *
+ * @return true if the method waited at all, or false if a step is ready to run now.
+ */
+ public boolean waitUntilNextStepIsDue() {
+ if (Build.IS_DEBUGGABLE) {
+ expectIsVibrationThread(true);
+ }
+ // It's necessary to re-process callbacks if they come in after acquiring the lock to
+ // start waiting, but we don't want to hold the lock while processing them.
+ // The loop goes until there are no pending callbacks to process.
+ while (true) {
+ // TODO: cancellation checking could also be integrated here, instead of outside in
+ // VibrationThread.
+ processVibratorCompleteCallbacks();
+ if (!mPendingOnVibratorCompleteSteps.isEmpty()) {
+ // Steps resumed by vibrator complete callback should be played right away.
+ return false;
+ }
+ Step nextStep = mNextSteps.peek();
+ if (nextStep == null) {
+ return false;
+ }
+ long waitMillis = nextStep.calculateWaitTime();
+ if (waitMillis <= 0) {
+ return false;
+ }
+ synchronized (mLock) {
+ // Double check for missed wake-ups before sleeping.
+ if (!mCompletionNotifiedVibrators.isEmpty()) {
+ continue; // Start again: processVibratorCompleteCallbacks will consume it.
+ }
+ try {
+ mLock.wait(waitMillis);
+ } catch (InterruptedException e) {
+ }
+ return true;
+ }
+ }
+ }
+
+ /**
+ * Play and remove the step at the top of this queue, and also adds the next steps generated
+ * to be played next.
+ */
+ public void runNextStep() {
+ if (Build.IS_DEBUGGABLE) {
+ expectIsVibrationThread(true);
+ }
+ // In theory a completion callback could have come in between the wait finishing and
+ // this method starting, but that only means the step is due now anyway, so it's reasonable
+ // to run it before processing callbacks as the window is tiny.
+ Step nextStep = pollNext();
+ if (nextStep != null) {
+ List<Step> nextSteps = nextStep.play();
+ if (nextStep.getVibratorOnDuration() > 0) {
+ mSuccessfulVibratorOnSteps++;
+ }
+ if (nextStep instanceof StartSequentialEffectStep) {
+ mRemainingStartSequentialEffectSteps--;
+ }
+ if (!nextStep.isCleanUp()) {
+ mPendingVibrateSteps--;
+ }
+ for (int i = 0; i < nextSteps.size(); i++) {
+ mPendingVibrateSteps += nextSteps.get(i).isCleanUp() ? 0 : 1;
+ }
+ mNextSteps.addAll(nextSteps);
+ }
+ }
+
+ /**
+ * Wake up the execution thread, which may be waiting until the next step is due.
+ * The caller is responsible for diverting VibrationThread execution.
+ *
+ * <p>At the moment this is used after the signal is set that a cancellation needs to be
+ * processed. The actual cancellation will be invoked from the VibrationThread.
+ */
+ public void notifyWakeUp() {
+ if (Build.IS_DEBUGGABLE) {
+ expectIsVibrationThread(false);
+ }
+
+ synchronized (mLock) {
+ mLock.notify();
+ }
+ }
+
+ /**
+ * Notify the conductor that a vibrator has completed its work.
+ *
+ * <p>This is a lightweight method intended to be called directly via native callbacks.
+ * The state update is recorded for processing on the main execution thread (VibrationThread).
+ */
+ public void notifyVibratorComplete(int vibratorId) {
+ if (Build.IS_DEBUGGABLE) {
+ expectIsVibrationThread(false);
+ }
+
+ if (DEBUG) {
+ Slog.d(TAG, "Vibration complete reported by vibrator " + vibratorId);
+ }
+
+ synchronized (mLock) {
+ mCompletionNotifiedVibrators.offer(vibratorId);
+ mLock.notify();
+ }
+ }
+
+ /**
+ * Notify that a VibratorManager sync operation has completed.
+ *
+ * <p>This is a lightweight method intended to be called directly via native callbacks.
+ * The state update is recorded for processing on the main execution thread
+ * (VibrationThread).
+ */
+ public void notifySyncedVibrationComplete() {
+ if (Build.IS_DEBUGGABLE) {
+ expectIsVibrationThread(false);
+ }
+
+ if (DEBUG) {
+ Slog.d(TAG, "Synced vibration complete reported by vibrator manager");
+ }
+
+ synchronized (mLock) {
+ for (int i = 0; i < mVibrators.size(); i++) {
+ mCompletionNotifiedVibrators.offer(mVibrators.keyAt(i));
+ }
+ mLock.notify();
+ }
+ }
+
+ /**
+ * Cancel the current queue, replacing all remaining steps with respective clean-up steps.
+ *
+ * <p>This will remove all steps and replace them with respective
+ * {@link Step#cancel()}.
+ */
+ public void cancel() {
+ if (Build.IS_DEBUGGABLE) {
+ expectIsVibrationThread(true);
+ }
+
+ // Vibrator callbacks should wait until all steps from the queue are properly cancelled
+ // and clean up steps are added back to the queue, so they can handle the callback.
+ List<Step> cleanUpSteps = new ArrayList<>();
+ Step step;
+ while ((step = pollNext()) != null) {
+ cleanUpSteps.addAll(step.cancel());
+ }
+ // All steps generated by Step.cancel() should be clean-up steps.
+ mPendingVibrateSteps = 0;
+ mNextSteps.addAll(cleanUpSteps);
+ }
+
+ /**
+ * Cancel the current queue immediately, clearing all remaining steps and skipping clean-up.
+ *
+ * <p>This will remove and trigger {@link Step#cancelImmediately()} in all steps, in order.
+ */
+ public void cancelImmediately() {
+ if (Build.IS_DEBUGGABLE) {
+ expectIsVibrationThread(true);
+ }
+
+ Step step;
+ while ((step = pollNext()) != null) {
+ step.cancelImmediately();
+ }
+ mPendingVibrateSteps = 0;
+ }
+
+ @Nullable
+ private Step pollNext() {
+ if (Build.IS_DEBUGGABLE) {
+ expectIsVibrationThread(true);
+ }
+
+ // Prioritize the steps resumed by a vibrator complete callback, irrespective of their
+ // "next run time".
+ if (!mPendingOnVibratorCompleteSteps.isEmpty()) {
+ return mPendingOnVibratorCompleteSteps.poll();
+ }
+ return mNextSteps.poll();
+ }
+
+ /**
+ * Process any notified vibrator completions.
+ *
+ * <p>This assumes only one of the next steps is waiting on this given vibrator, so the
+ * first step found will be resumed by this method, in no particular order.
+ */
+ private void processVibratorCompleteCallbacks() {
+ if (Build.IS_DEBUGGABLE) {
+ expectIsVibrationThread(true);
+ }
+
+ Queue<Integer> vibratorsToProcess;
+ // Swap out the queue of completions to process.
+ synchronized (mLock) {
+ if (mCompletionNotifiedVibrators.isEmpty()) {
+ return; // Nothing to do.
+ }
+
+ vibratorsToProcess = mCompletionNotifiedVibrators;
+ mCompletionNotifiedVibrators = new LinkedList<>();
+ }
+
+ while (!vibratorsToProcess.isEmpty()) {
+ int vibratorId = vibratorsToProcess.poll();
+ Iterator<Step> it = mNextSteps.iterator();
+ while (it.hasNext()) {
+ Step step = it.next();
+ if (step.acceptVibratorCompleteCallback(vibratorId)) {
+ it.remove();
+ mPendingOnVibratorCompleteSteps.offer(step);
+ break;
+ }
+ }
+ }
+ }
+
+ private static CombinedVibration.Sequential toSequential(CombinedVibration effect) {
+ if (effect instanceof CombinedVibration.Sequential) {
+ return (CombinedVibration.Sequential) effect;
+ }
+ return (CombinedVibration.Sequential) CombinedVibration.startSequential()
+ .addNext(effect)
+ .combine();
+ }
+
+ /**
+ * This check is used for debugging and documentation to indicate the thread that's expected
+ * to invoke a given public method on this class. Most methods are only invoked by
+ * VibrationThread, which is where all the steps and HAL calls should be made. Other threads
+ * should only signal to the execution flow being run by VibrationThread.
+ */
+ private static void expectIsVibrationThread(boolean isVibrationThread) {
+ if ((Thread.currentThread() instanceof VibrationThread) != isVibrationThread) {
+ Slog.wtfStack("VibrationStepConductor",
+ "Thread caller assertion failed, expected isVibrationThread="
+ + isVibrationThread);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/vibrator/VibrationThread.java b/services/core/java/com/android/server/vibrator/VibrationThread.java
index 1f1f40b8121d..3fef7f2a747e 100644
--- a/services/core/java/com/android/server/vibrator/VibrationThread.java
+++ b/services/core/java/com/android/server/vibrator/VibrationThread.java
@@ -16,59 +16,21 @@
package com.android.server.vibrator;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.hardware.vibrator.IVibratorManager;
-import android.os.CombinedVibration;
import android.os.IBinder;
import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteException;
-import android.os.SystemClock;
import android.os.Trace;
-import android.os.VibrationEffect;
-import android.os.VibratorInfo;
import android.os.WorkSource;
-import android.os.vibrator.PrebakedSegment;
-import android.os.vibrator.PrimitiveSegment;
-import android.os.vibrator.RampSegment;
-import android.os.vibrator.StepSegment;
-import android.os.vibrator.VibrationEffectSegment;
import android.util.Slog;
import android.util.SparseArray;
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.app.IBatteryStats;
-import com.android.internal.util.FrameworkStatsLog;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
import java.util.NoSuchElementException;
-import java.util.PriorityQueue;
-import java.util.Queue;
/** Plays a {@link Vibration} in dedicated thread. */
final class VibrationThread extends Thread implements IBinder.DeathRecipient {
- private static final String TAG = "VibrationThread";
- private static final boolean DEBUG = false;
-
- /**
- * Extra timeout added to the end of each vibration step to ensure it finishes even when
- * vibrator callbacks are lost.
- */
- private static final long CALLBACKS_EXTRA_TIMEOUT = 1_000;
-
- /** Threshold to prevent the ramp off steps from trying to set extremely low amplitudes. */
- private static final float RAMP_OFF_AMPLITUDE_MIN = 1e-3f;
-
- /** Fixed large duration used to note repeating vibrations to {@link IBatteryStats}. */
- private static final long BATTERY_STATS_REPEATING_VIBRATION_DURATION = 5_000;
-
- private static final List<Step> EMPTY_STEP_LIST = new ArrayList<>();
+ static final String TAG = "VibrationThread";
+ static final boolean DEBUG = false;
/** Calls into VibratorManager functionality needed for playing a {@link Vibration}. */
interface VibratorManagerHooks {
@@ -94,6 +56,15 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient {
void cancelSyncedVibration();
/**
+ * Record that a vibrator was turned on, and may remain on for the specified duration,
+ * on behalf of the given uid.
+ */
+ void noteVibratorOn(int uid, long duration);
+
+ /** Record that a vibrator was turned off, on behalf of the given uid. */
+ void noteVibratorOff(int uid);
+
+ /**
* Tell the manager that the currently active vibration has completed its vibration, from
* the perspective of the Effect. However, the VibrationThread may still be continuing with
* cleanup tasks, and should not be given new work until {@link #onVibrationThreadReleased}
@@ -108,16 +79,10 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient {
void onVibrationThreadReleased();
}
- private final Object mLock = new Object();
- private final WorkSource mWorkSource;
private final PowerManager.WakeLock mWakeLock;
- private final IBatteryStats mBatteryStatsService;
- private final VibrationSettings mVibrationSettings;
- private final DeviceVibrationEffectAdapter mDeviceEffectAdapter;
- private final Vibration mVibration;
- private final VibratorManagerHooks mVibratorManagerHooks;
- private final SparseArray<VibratorController> mVibrators = new SparseArray<>();
- private final StepQueue mStepQueue = new StepQueue();
+ private final VibrationThread.VibratorManagerHooks mVibratorManagerHooks;
+
+ private final VibrationStepConductor mStepConductor;
private volatile boolean mStop;
private volatile boolean mForceStop;
@@ -127,30 +92,15 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient {
VibrationThread(Vibration vib, VibrationSettings vibrationSettings,
DeviceVibrationEffectAdapter effectAdapter,
SparseArray<VibratorController> availableVibrators, PowerManager.WakeLock wakeLock,
- IBatteryStats batteryStatsService, VibratorManagerHooks vibratorManagerHooks) {
- mVibration = vib;
- mVibrationSettings = vibrationSettings;
- mDeviceEffectAdapter = effectAdapter;
+ VibratorManagerHooks vibratorManagerHooks) {
mVibratorManagerHooks = vibratorManagerHooks;
- mWorkSource = new WorkSource(mVibration.uid);
mWakeLock = wakeLock;
- mBatteryStatsService = batteryStatsService;
-
- CombinedVibration effect = vib.getEffect();
- for (int i = 0; i < availableVibrators.size(); i++) {
- if (effect.hasVibrator(availableVibrators.keyAt(i))) {
- mVibrators.put(availableVibrators.keyAt(i), availableVibrators.valueAt(i));
- }
- }
+ mStepConductor = new VibrationStepConductor(vib, vibrationSettings, effectAdapter,
+ availableVibrators, vibratorManagerHooks);
}
Vibration getVibration() {
- return mVibration;
- }
-
- @VisibleForTesting
- SparseArray<VibratorController> getVibrators() {
- return mVibrators;
+ return mStepConductor.getVibration();
}
@Override
@@ -179,12 +129,14 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient {
/** Runs the VibrationThread ensuring that the wake lock is acquired and released. */
private void runWithWakeLock() {
- mWakeLock.setWorkSource(mWorkSource);
+ WorkSource workSource = new WorkSource(mStepConductor.getVibration().uid);
+ mWakeLock.setWorkSource(workSource);
mWakeLock.acquire();
try {
runWithWakeLockAndDeathLink();
} finally {
mWakeLock.release();
+ mWakeLock.setWorkSource(null);
}
}
@@ -193,8 +145,9 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient {
* Called from within runWithWakeLock.
*/
private void runWithWakeLockAndDeathLink() {
+ IBinder vibrationBinderToken = mStepConductor.getVibration().token;
try {
- mVibration.token.linkToDeath(this, 0);
+ vibrationBinderToken.linkToDeath(this, 0);
} catch (RemoteException e) {
Slog.e(TAG, "Error linking vibration to token death", e);
clientVibrationCompleteIfNotAlready(Vibration.Status.IGNORED_ERROR_TOKEN);
@@ -206,7 +159,7 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient {
playVibration();
} finally {
try {
- mVibration.token.unlinkToDeath(this, 0);
+ vibrationBinderToken.unlinkToDeath(this, 0);
} catch (NoSuchElementException e) {
Slog.wtf(TAG, "Failed to unlink token", e);
}
@@ -220,12 +173,10 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient {
return;
}
mStop = true;
- synchronized (mLock) {
- if (DEBUG) {
- Slog.d(TAG, "Vibration cancelled");
- }
- mLock.notify();
+ if (DEBUG) {
+ Slog.d(TAG, "Vibration cancelled");
}
+ mStepConductor.notifyWakeUp();
}
/** Cancel current vibration and shuts off the vibrators immediately. */
@@ -234,37 +185,21 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient {
// Already forced the thread to stop, wait for it to finish.
return;
}
- mStop = mForceStop = true;
- synchronized (mLock) {
- if (DEBUG) {
- Slog.d(TAG, "Vibration cancelled immediately");
- }
- mLock.notify();
+ if (DEBUG) {
+ Slog.d(TAG, "Vibration cancelled immediately");
}
+ mStop = mForceStop = true;
+ mStepConductor.notifyWakeUp();
}
/** Notify current vibration that a synced step has completed. */
public void syncedVibrationComplete() {
- synchronized (mLock) {
- if (DEBUG) {
- Slog.d(TAG, "Synced vibration complete reported by vibrator manager");
- }
- for (int i = 0; i < mVibrators.size(); i++) {
- mStepQueue.notifyVibratorComplete(mVibrators.keyAt(i));
- }
- mLock.notify();
- }
+ mStepConductor.notifySyncedVibrationComplete();
}
/** Notify current vibration that a step has completed on given vibrator. */
public void vibratorComplete(int vibratorId) {
- synchronized (mLock) {
- if (DEBUG) {
- Slog.d(TAG, "Vibration complete reported by vibrator " + vibratorId);
- }
- mStepQueue.notifyVibratorComplete(vibratorId);
- mLock.notify();
- }
+ mStepConductor.notifyVibratorComplete(vibratorId);
}
// Indicate that the vibration is complete. This can be called multiple times only for
@@ -273,1387 +208,51 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient {
private void clientVibrationCompleteIfNotAlready(Vibration.Status completedStatus) {
if (!mCalledVibrationCompleteCallback) {
mCalledVibrationCompleteCallback = true;
- mVibratorManagerHooks.onVibrationCompleted(mVibration.id, completedStatus);
+ mVibratorManagerHooks.onVibrationCompleted(
+ mStepConductor.getVibration().id, completedStatus);
}
}
private void playVibration() {
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "playVibration");
try {
- CombinedVibration.Sequential sequentialEffect = toSequential(mVibration.getEffect());
- final int sequentialEffectSize = sequentialEffect.getEffects().size();
- mStepQueue.initializeForEffect(sequentialEffect);
-
- while (!mStepQueue.isFinished()) {
- long waitMillisBeforeNextStep;
- synchronized (mLock) {
- waitMillisBeforeNextStep = mStepQueue.getWaitMillisBeforeNextStep();
- if (waitMillisBeforeNextStep > 0) {
- try {
- mLock.wait(waitMillisBeforeNextStep);
- } catch (InterruptedException e) {
- }
- }
- }
- // Only run the next vibration step if we didn't have to wait in this loop.
- // If we waited then the queue may have changed, so loop again to re-evaluate
- // the scheduling of the queue top element.
- if (waitMillisBeforeNextStep <= 0) {
+ mStepConductor.prepareToStart();
+
+ while (!mStepConductor.isFinished()) {
+ // Skip wait and next step if mForceStop already happened.
+ boolean waited = mForceStop || mStepConductor.waitUntilNextStepIsDue();
+ // If we waited, don't run the next step, but instead re-evaluate cancellation
+ // status
+ if (!waited) {
if (DEBUG) {
Slog.d(TAG, "Play vibration consuming next step...");
}
// Run the step without holding the main lock, to avoid HAL interactions from
// blocking the thread.
- mStepQueue.runNextStep();
+ mStepConductor.runNextStep();
}
+
+ if (mForceStop) {
+ // Cancel every step and stop playing them right away, even clean-up steps.
+ mStepConductor.cancelImmediately();
+ clientVibrationCompleteIfNotAlready(Vibration.Status.CANCELLED);
+ break;
+ }
+
Vibration.Status status = mStop ? Vibration.Status.CANCELLED
- : mStepQueue.calculateVibrationStatus(sequentialEffectSize);
+ : mStepConductor.calculateVibrationStatus();
+ // This block can only run once due to mCalledVibrationCompleteCallback.
if (status != Vibration.Status.RUNNING && !mCalledVibrationCompleteCallback) {
// First time vibration stopped running, start clean-up tasks and notify
// callback immediately.
clientVibrationCompleteIfNotAlready(status);
if (status == Vibration.Status.CANCELLED) {
- mStepQueue.cancel();
+ mStepConductor.cancel();
}
}
- if (mForceStop) {
- // Cancel every step and stop playing them right away, even clean-up steps.
- mStepQueue.cancelImmediately();
- clientVibrationCompleteIfNotAlready(Vibration.Status.CANCELLED);
- break;
- }
}
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
}
}
-
- private void noteVibratorOn(long duration) {
- try {
- if (duration <= 0) {
- return;
- }
- if (duration == Long.MAX_VALUE) {
- // Repeating duration has started. Report a fixed duration here, noteVibratorOff
- // should be called when this is cancelled.
- duration = BATTERY_STATS_REPEATING_VIBRATION_DURATION;
- }
- mBatteryStatsService.noteVibratorOn(mVibration.uid, duration);
- FrameworkStatsLog.write_non_chained(FrameworkStatsLog.VIBRATOR_STATE_CHANGED,
- mVibration.uid, null, FrameworkStatsLog.VIBRATOR_STATE_CHANGED__STATE__ON,
- duration);
- } catch (RemoteException e) {
- }
- }
-
- private void noteVibratorOff() {
- try {
- mBatteryStatsService.noteVibratorOff(mVibration.uid);
- FrameworkStatsLog.write_non_chained(FrameworkStatsLog.VIBRATOR_STATE_CHANGED,
- mVibration.uid, null, FrameworkStatsLog.VIBRATOR_STATE_CHANGED__STATE__OFF,
- /* duration= */ 0);
- } catch (RemoteException e) {
- }
- }
-
- @Nullable
- private SingleVibratorStep nextVibrateStep(long startTime, VibratorController controller,
- VibrationEffect.Composed effect, int segmentIndex, long vibratorOffTimeout) {
- if (segmentIndex >= effect.getSegments().size()) {
- segmentIndex = effect.getRepeatIndex();
- }
- if (segmentIndex < 0) {
- // No more segments to play, last step is to complete the vibration on this vibrator.
- return new EffectCompleteStep(startTime, /* cancelled= */ false, controller,
- vibratorOffTimeout);
- }
-
- VibrationEffectSegment segment = effect.getSegments().get(segmentIndex);
- if (segment instanceof PrebakedSegment) {
- return new PerformStep(startTime, controller, effect, segmentIndex, vibratorOffTimeout);
- }
- if (segment instanceof PrimitiveSegment) {
- return new ComposePrimitivesStep(startTime, controller, effect, segmentIndex,
- vibratorOffTimeout);
- }
- if (segment instanceof RampSegment) {
- return new ComposePwleStep(startTime, controller, effect, segmentIndex,
- vibratorOffTimeout);
- }
- return new AmplitudeStep(startTime, controller, effect, segmentIndex, vibratorOffTimeout);
- }
-
- private static CombinedVibration.Sequential toSequential(CombinedVibration effect) {
- if (effect instanceof CombinedVibration.Sequential) {
- return (CombinedVibration.Sequential) effect;
- }
- return (CombinedVibration.Sequential) CombinedVibration.startSequential()
- .addNext(effect)
- .combine();
- }
-
- /** Queue for {@link Step Steps}, sorted by their start time. */
- private final class StepQueue {
- @GuardedBy("mLock")
- private final PriorityQueue<Step> mNextSteps = new PriorityQueue<>();
- @GuardedBy("mLock")
- private final Queue<Step> mPendingOnVibratorCompleteSteps = new LinkedList<>();
- @GuardedBy("mLock")
- private final Queue<Integer> mCompletionNotifiedVibrators = new LinkedList<>();
-
- @GuardedBy("mLock")
- private int mPendingVibrateSteps;
- @GuardedBy("mLock")
- private int mConsumedStartVibrateSteps;
- @GuardedBy("mLock")
- private int mSuccessfulVibratorOnSteps;
- @GuardedBy("mLock")
- private boolean mWaitToProcessVibratorCompleteCallbacks;
-
- public void initializeForEffect(@NonNull CombinedVibration.Sequential vibration) {
- synchronized (mLock) {
- mPendingVibrateSteps++;
- mNextSteps.offer(new StartVibrateStep(vibration));
- }
- }
-
- public boolean isFinished() {
- synchronized (mLock) {
- return mPendingOnVibratorCompleteSteps.isEmpty() && mNextSteps.isEmpty();
- }
- }
-
- /**
- * Calculate the {@link Vibration.Status} based on the current queue state and the expected
- * number of {@link StartVibrateStep} to be played.
- */
- public Vibration.Status calculateVibrationStatus(int expectedStartVibrateSteps) {
- synchronized (mLock) {
- if (mPendingVibrateSteps > 0
- || mConsumedStartVibrateSteps < expectedStartVibrateSteps) {
- return Vibration.Status.RUNNING;
- }
- if (mSuccessfulVibratorOnSteps > 0) {
- return Vibration.Status.FINISHED;
- }
- // If no step was able to turn the vibrator ON successfully.
- return Vibration.Status.IGNORED_UNSUPPORTED;
- }
- }
-
- /** Returns the time in millis to wait before calling {@link #runNextStep()}. */
- @GuardedBy("VibrationThread.this.mLock")
- public long getWaitMillisBeforeNextStep() {
- if (!mPendingOnVibratorCompleteSteps.isEmpty()) {
- // Steps resumed by vibrator complete callback should be played right away.
- return 0;
- }
- Step nextStep = mNextSteps.peek();
- return nextStep == null ? 0 : nextStep.calculateWaitTime();
- }
-
- /**
- * Play and remove the step at the top of this queue, and also adds the next steps generated
- * to be played next.
- */
- public void runNextStep() {
- // Vibrator callbacks should wait until the polled step is played and the next steps are
- // added back to the queue, so they can handle the callback.
- markWaitToProcessVibratorCallbacks();
- try {
- Step nextStep = pollNext();
- if (nextStep != null) {
- // This might turn on the vibrator and have a HAL latency. Execute this outside
- // any lock to avoid blocking other interactions with the thread.
- List<Step> nextSteps = nextStep.play();
- synchronized (mLock) {
- if (nextStep.getVibratorOnDuration() > 0) {
- mSuccessfulVibratorOnSteps++;
- }
- if (nextStep instanceof StartVibrateStep) {
- mConsumedStartVibrateSteps++;
- }
- if (!nextStep.isCleanUp()) {
- mPendingVibrateSteps--;
- }
- for (int i = 0; i < nextSteps.size(); i++) {
- mPendingVibrateSteps += nextSteps.get(i).isCleanUp() ? 0 : 1;
- }
- mNextSteps.addAll(nextSteps);
- }
- }
- } finally {
- synchronized (mLock) {
- processVibratorCompleteCallbacks();
- }
- }
- }
-
- /**
- * Notify the vibrator completion.
- *
- * <p>This is a lightweight method that do not trigger any operation from {@link
- * VibratorController}, so it can be called directly from a native callback.
- */
- @GuardedBy("mLock")
- public void notifyVibratorComplete(int vibratorId) {
- mCompletionNotifiedVibrators.offer(vibratorId);
- if (!mWaitToProcessVibratorCompleteCallbacks) {
- // No step is being played or cancelled now, process the callback right away.
- processVibratorCompleteCallbacks();
- }
- }
-
- /**
- * Cancel the current queue, replacing all remaining steps with respective clean-up steps.
- *
- * <p>This will remove all steps and replace them with respective
- * {@link Step#cancel()}.
- */
- public void cancel() {
- // Vibrator callbacks should wait until all steps from the queue are properly cancelled
- // and clean up steps are added back to the queue, so they can handle the callback.
- markWaitToProcessVibratorCallbacks();
- try {
- List<Step> cleanUpSteps = new ArrayList<>();
- Step step;
- while ((step = pollNext()) != null) {
- cleanUpSteps.addAll(step.cancel());
- }
- synchronized (mLock) {
- // All steps generated by Step.cancel() should be clean-up steps.
- mPendingVibrateSteps = 0;
- mNextSteps.addAll(cleanUpSteps);
- }
- } finally {
- synchronized (mLock) {
- processVibratorCompleteCallbacks();
- }
- }
- }
-
- /**
- * Cancel the current queue immediately, clearing all remaining steps and skipping clean-up.
- *
- * <p>This will remove and trigger {@link Step#cancelImmediately()} in all steps, in order.
- */
- public void cancelImmediately() {
- // Vibrator callbacks should wait until all steps from the queue are properly cancelled.
- markWaitToProcessVibratorCallbacks();
- try {
- Step step;
- while ((step = pollNext()) != null) {
- // This might turn off the vibrator and have a HAL latency. Execute this outside
- // any lock to avoid blocking other interactions with the thread.
- step.cancelImmediately();
- }
- synchronized (mLock) {
- mPendingVibrateSteps = 0;
- }
- } finally {
- synchronized (mLock) {
- processVibratorCompleteCallbacks();
- }
- }
- }
-
- @Nullable
- private Step pollNext() {
- synchronized (mLock) {
- // Prioritize the steps resumed by a vibrator complete callback.
- if (!mPendingOnVibratorCompleteSteps.isEmpty()) {
- return mPendingOnVibratorCompleteSteps.poll();
- }
- return mNextSteps.poll();
- }
- }
-
- private void markWaitToProcessVibratorCallbacks() {
- synchronized (mLock) {
- mWaitToProcessVibratorCompleteCallbacks = true;
- }
- }
-
- /**
- * Notify the step in this queue that should be resumed by the vibrator completion
- * callback and keep it separate to be consumed by {@link #runNextStep()}.
- *
- * <p>This is a lightweight method that do not trigger any operation from {@link
- * VibratorController}, so it can be called directly from a native callback.
- *
- * <p>This assumes only one of the next steps is waiting on this given vibrator, so the
- * first step found will be resumed by this method, in no particular order.
- */
- @GuardedBy("mLock")
- private void processVibratorCompleteCallbacks() {
- mWaitToProcessVibratorCompleteCallbacks = false;
- while (!mCompletionNotifiedVibrators.isEmpty()) {
- int vibratorId = mCompletionNotifiedVibrators.poll();
- Iterator<Step> it = mNextSteps.iterator();
- while (it.hasNext()) {
- Step step = it.next();
- if (step.acceptVibratorCompleteCallback(vibratorId)) {
- it.remove();
- mPendingOnVibratorCompleteSteps.offer(step);
- break;
- }
- }
- }
- }
- }
-
- /**
- * Represent a single step for playing a vibration.
- *
- * <p>Every step has a start time, which can be used to apply delays between steps while
- * executing them in sequence.
- */
- private abstract class Step implements Comparable<Step> {
- public final long startTime;
-
- Step(long startTime) {
- this.startTime = startTime;
- }
-
- /**
- * Returns true if this step is a clean up step and not part of a {@link VibrationEffect} or
- * {@link CombinedVibration}.
- */
- public boolean isCleanUp() {
- return false;
- }
-
- /** Play this step, returning a (possibly empty) list of next steps. */
- @NonNull
- public abstract List<Step> play();
-
- /**
- * Cancel this pending step and return a (possibly empty) list of clean-up steps that should
- * be played to gracefully cancel this step.
- */
- @NonNull
- public abstract List<Step> cancel();
-
- /** Cancel this pending step immediately, skipping any clean-up. */
- public abstract void cancelImmediately();
-
- /**
- * Return the duration the vibrator was turned on when this step was played.
- *
- * @return A positive duration that the vibrator was turned on for by this step;
- * Zero if the segment is not supported, the step was not played yet or vibrator was never
- * turned on by this step; A negative value if the vibrator call has failed.
- */
- public long getVibratorOnDuration() {
- return 0;
- }
-
- /**
- * Return true to run this step right after a vibrator has notified vibration completed,
- * used to resume steps waiting on vibrator callbacks with a timeout.
- */
- public boolean acceptVibratorCompleteCallback(int vibratorId) {
- return false;
- }
-
- /**
- * Returns the time in millis to wait before playing this step. This is performed
- * while holding the queue lock, so should not rely on potentially slow operations.
- */
- public long calculateWaitTime() {
- if (startTime == Long.MAX_VALUE) {
- // This step don't have a predefined start time, it's just marked to be executed
- // after all other steps have finished.
- return 0;
- }
- return Math.max(0, startTime - SystemClock.uptimeMillis());
- }
-
- @Override
- public int compareTo(Step o) {
- return Long.compare(startTime, o.startTime);
- }
- }
-
- /**
- * Starts a sync vibration.
- *
- * <p>If this step has successfully started playing a vibration on any vibrator, it will always
- * add a {@link FinishVibrateStep} to the queue, to be played after all vibrators have finished
- * all their individual steps.
- *
- * <p>If this step does not start any vibrator, it will add a {@link StartVibrateStep} if the
- * sequential effect isn't finished yet.
- */
- private final class StartVibrateStep extends Step {
- public final CombinedVibration.Sequential sequentialEffect;
- public final int currentIndex;
-
- private long mVibratorsOnMaxDuration;
-
- StartVibrateStep(CombinedVibration.Sequential effect) {
- this(SystemClock.uptimeMillis() + effect.getDelays().get(0), effect, /* index= */ 0);
- }
-
- StartVibrateStep(long startTime, CombinedVibration.Sequential effect, int index) {
- super(startTime);
- sequentialEffect = effect;
- currentIndex = index;
- }
-
- @Override
- public long getVibratorOnDuration() {
- return mVibratorsOnMaxDuration;
- }
-
- @Override
- public List<Step> play() {
- Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "StartVibrateStep");
- List<Step> nextSteps = new ArrayList<>();
- mVibratorsOnMaxDuration = -1;
- try {
- if (DEBUG) {
- Slog.d(TAG, "StartVibrateStep for effect #" + currentIndex);
- }
- CombinedVibration effect = sequentialEffect.getEffects().get(currentIndex);
- DeviceEffectMap effectMapping = createEffectToVibratorMapping(effect);
- if (effectMapping == null) {
- // Unable to map effects to vibrators, ignore this step.
- return nextSteps;
- }
-
- mVibratorsOnMaxDuration = startVibrating(effectMapping, nextSteps);
- noteVibratorOn(mVibratorsOnMaxDuration);
- } finally {
- if (mVibratorsOnMaxDuration >= 0) {
- // It least one vibrator was started then add a finish step to wait for all
- // active vibrators to finish their individual steps before going to the next.
- // Otherwise this step was ignored so just go to the next one.
- Step nextStep =
- mVibratorsOnMaxDuration > 0 ? new FinishVibrateStep(this) : nextStep();
- if (nextStep != null) {
- nextSteps.add(nextStep);
- }
- }
- Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
- }
- return nextSteps;
- }
-
- @Override
- public List<Step> cancel() {
- return EMPTY_STEP_LIST;
- }
-
- @Override
- public void cancelImmediately() {
- }
-
- /**
- * Create the next {@link StartVibrateStep} to play this sequential effect, starting at the
- * time this method is called, or null if sequence is complete.
- */
- @Nullable
- private Step nextStep() {
- int nextIndex = currentIndex + 1;
- if (nextIndex >= sequentialEffect.getEffects().size()) {
- return null;
- }
- long nextEffectDelay = sequentialEffect.getDelays().get(nextIndex);
- long nextStartTime = SystemClock.uptimeMillis() + nextEffectDelay;
- return new StartVibrateStep(nextStartTime, sequentialEffect, nextIndex);
- }
-
- /** Create a mapping of individual {@link VibrationEffect} to available vibrators. */
- @Nullable
- private DeviceEffectMap createEffectToVibratorMapping(
- CombinedVibration effect) {
- if (effect instanceof CombinedVibration.Mono) {
- return new DeviceEffectMap((CombinedVibration.Mono) effect);
- }
- if (effect instanceof CombinedVibration.Stereo) {
- return new DeviceEffectMap((CombinedVibration.Stereo) effect);
- }
- return null;
- }
-
- /**
- * Starts playing effects on designated vibrators, in sync.
- *
- * @param effectMapping The {@link CombinedVibration} mapped to this device vibrators
- * @param nextSteps An output list to accumulate the future {@link Step Steps} created
- * by this method, typically one for each vibrator that has
- * successfully started vibrating on this step.
- * @return The duration, in millis, of the {@link CombinedVibration}. Repeating
- * waveforms return {@link Long#MAX_VALUE}. Zero or negative values indicate the vibrators
- * have ignored all effects.
- */
- private long startVibrating(DeviceEffectMap effectMapping, List<Step> nextSteps) {
- int vibratorCount = effectMapping.size();
- if (vibratorCount == 0) {
- // No effect was mapped to any available vibrator.
- return 0;
- }
-
- SingleVibratorStep[] steps = new SingleVibratorStep[vibratorCount];
- long vibrationStartTime = SystemClock.uptimeMillis();
- for (int i = 0; i < vibratorCount; i++) {
- steps[i] = nextVibrateStep(vibrationStartTime,
- mVibrators.get(effectMapping.vibratorIdAt(i)),
- effectMapping.effectAt(i),
- /* segmentIndex= */ 0, /* vibratorOffTimeout= */ 0);
- }
-
- if (steps.length == 1) {
- // No need to prepare and trigger sync effects on a single vibrator.
- return startVibrating(steps[0], nextSteps);
- }
-
- // This synchronization of vibrators should be executed one at a time, even if we are
- // vibrating different sets of vibrators in parallel. The manager can only prepareSynced
- // one set of vibrators at a time.
- synchronized (mLock) {
- boolean hasPrepared = false;
- boolean hasTriggered = false;
- long maxDuration = 0;
- try {
- hasPrepared = mVibratorManagerHooks.prepareSyncedVibration(
- effectMapping.getRequiredSyncCapabilities(),
- effectMapping.getVibratorIds());
-
- for (SingleVibratorStep step : steps) {
- long duration = startVibrating(step, nextSteps);
- if (duration < 0) {
- // One vibrator has failed, fail this entire sync attempt.
- return maxDuration = -1;
- }
- maxDuration = Math.max(maxDuration, duration);
- }
-
- // Check if sync was prepared and if any step was accepted by a vibrator,
- // otherwise there is nothing to trigger here.
- if (hasPrepared && maxDuration > 0) {
- hasTriggered = mVibratorManagerHooks.triggerSyncedVibration(mVibration.id);
- }
- return maxDuration;
- } finally {
- if (hasPrepared && !hasTriggered) {
- // Trigger has failed or all steps were ignored by the vibrators.
- mVibratorManagerHooks.cancelSyncedVibration();
- nextSteps.clear();
- } else if (maxDuration < 0) {
- // Some vibrator failed without being prepared so other vibrators might be
- // active. Cancel and remove every pending step from output list.
- for (int i = nextSteps.size() - 1; i >= 0; i--) {
- nextSteps.remove(i).cancelImmediately();
- }
- }
- }
- }
- }
-
- private long startVibrating(SingleVibratorStep step, List<Step> nextSteps) {
- nextSteps.addAll(step.play());
- long stepDuration = step.getVibratorOnDuration();
- if (stepDuration < 0) {
- // Step failed, so return negative duration to propagate failure.
- return stepDuration;
- }
- // Return the longest estimation for the entire effect.
- return Math.max(stepDuration, step.effect.getDuration());
- }
- }
-
- /**
- * Finish a sync vibration started by a {@link StartVibrateStep}.
- *
- * <p>This only plays after all active vibrators steps have finished, and adds a {@link
- * StartVibrateStep} to the queue if the sequential effect isn't finished yet.
- */
- private final class FinishVibrateStep extends Step {
- public final StartVibrateStep startedStep;
-
- FinishVibrateStep(StartVibrateStep startedStep) {
- super(Long.MAX_VALUE); // No predefined startTime, just wait for all steps in the queue.
- this.startedStep = startedStep;
- }
-
- @Override
- public boolean isCleanUp() {
- // This step only notes that all the vibrators has been turned off.
- return true;
- }
-
- @Override
- public List<Step> play() {
- Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "FinishVibrateStep");
- try {
- if (DEBUG) {
- Slog.d(TAG, "FinishVibrateStep for effect #" + startedStep.currentIndex);
- }
- noteVibratorOff();
- Step nextStep = startedStep.nextStep();
- return nextStep == null ? EMPTY_STEP_LIST : Arrays.asList(nextStep);
- } finally {
- Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
- }
- }
-
- @Override
- public List<Step> cancel() {
- cancelImmediately();
- return EMPTY_STEP_LIST;
- }
-
- @Override
- public void cancelImmediately() {
- noteVibratorOff();
- }
- }
-
- /**
- * Represent a step on a single vibrator that plays one or more segments from a
- * {@link VibrationEffect.Composed} effect.
- */
- private abstract class SingleVibratorStep extends Step {
- public final VibratorController controller;
- public final VibrationEffect.Composed effect;
- public final int segmentIndex;
- public final long vibratorOffTimeout;
-
- long mVibratorOnResult;
- boolean mVibratorCompleteCallbackReceived;
-
- /**
- * @param startTime The time to schedule this step in the {@link StepQueue}.
- * @param controller The vibrator that is playing the effect.
- * @param effect The effect being played in this step.
- * @param index The index of the next segment to be played by this step
- * @param vibratorOffTimeout The time the vibrator is expected to complete any previous
- * vibration and turn off. This is used to allow this step to be
- * triggered when the completion callback is received, and can
- * be used play effects back-to-back.
- */
- SingleVibratorStep(long startTime, VibratorController controller,
- VibrationEffect.Composed effect, int index, long vibratorOffTimeout) {
- super(startTime);
- this.controller = controller;
- this.effect = effect;
- this.segmentIndex = index;
- this.vibratorOffTimeout = vibratorOffTimeout;
- }
-
- @Override
- public long getVibratorOnDuration() {
- return mVibratorOnResult;
- }
-
- @Override
- public boolean acceptVibratorCompleteCallback(int vibratorId) {
- boolean isSameVibrator = controller.getVibratorInfo().getId() == vibratorId;
- mVibratorCompleteCallbackReceived |= isSameVibrator;
- // Only activate this step if a timeout was set to wait for the vibration to complete,
- // otherwise we are waiting for the correct time to play the next step.
- return isSameVibrator && (vibratorOffTimeout > SystemClock.uptimeMillis());
- }
-
- @Override
- public List<Step> cancel() {
- return Arrays.asList(new EffectCompleteStep(SystemClock.uptimeMillis(),
- /* cancelled= */ true, controller, vibratorOffTimeout));
- }
-
- @Override
- public void cancelImmediately() {
- if (vibratorOffTimeout > SystemClock.uptimeMillis()) {
- // Vibrator might be running from previous steps, so turn it off while canceling.
- stopVibrating();
- }
- }
-
- void stopVibrating() {
- if (DEBUG) {
- Slog.d(TAG, "Turning off vibrator " + controller.getVibratorInfo().getId());
- }
- controller.off();
- }
-
- void changeAmplitude(float amplitude) {
- if (DEBUG) {
- Slog.d(TAG, "Amplitude changed on vibrator " + controller.getVibratorInfo().getId()
- + " to " + amplitude);
- }
- controller.setAmplitude(amplitude);
- }
-
- /** Return the {@link #nextVibrateStep} with same timings, only jumping the segments. */
- public List<Step> skipToNextSteps(int segmentsSkipped) {
- return nextSteps(startTime, vibratorOffTimeout, segmentsSkipped);
- }
-
- /**
- * Return the {@link #nextVibrateStep} with same start and off timings calculated from
- * {@link #getVibratorOnDuration()}, jumping all played segments.
- *
- * <p>This method has same behavior as {@link #skipToNextSteps(int)} when the vibrator
- * result is non-positive, meaning the vibrator has either ignored or failed to turn on.
- */
- public List<Step> nextSteps(int segmentsPlayed) {
- if (mVibratorOnResult <= 0) {
- // Vibration was not started, so just skip the played segments and keep timings.
- return skipToNextSteps(segmentsPlayed);
- }
- long nextStartTime = SystemClock.uptimeMillis() + mVibratorOnResult;
- long nextVibratorOffTimeout = nextStartTime + CALLBACKS_EXTRA_TIMEOUT;
- return nextSteps(nextStartTime, nextVibratorOffTimeout, segmentsPlayed);
- }
-
- /**
- * Return the {@link #nextVibrateStep} with given start and off timings, which might be
- * calculated independently, jumping all played segments.
- *
- * <p>This should be used when the vibrator on/off state is not responsible for the steps
- * execution timings, e.g. while playing the vibrator amplitudes.
- */
- public List<Step> nextSteps(long nextStartTime, long vibratorOffTimeout,
- int segmentsPlayed) {
- Step nextStep = nextVibrateStep(nextStartTime, controller, effect,
- segmentIndex + segmentsPlayed, vibratorOffTimeout);
- return nextStep == null ? EMPTY_STEP_LIST : Arrays.asList(nextStep);
- }
- }
-
- /**
- * Represent a step turn the vibrator on with a single prebaked effect.
- *
- * <p>This step automatically falls back by replacing the prebaked segment with
- * {@link VibrationSettings#getFallbackEffect(int)}, if available.
- */
- private final class PerformStep extends SingleVibratorStep {
-
- PerformStep(long startTime, VibratorController controller,
- VibrationEffect.Composed effect, int index, long vibratorOffTimeout) {
- // This step should wait for the last vibration to finish (with the timeout) and for the
- // intended step start time (to respect the effect delays).
- super(Math.max(startTime, vibratorOffTimeout), controller, effect, index,
- vibratorOffTimeout);
- }
-
- @Override
- public List<Step> play() {
- Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "PerformStep");
- try {
- VibrationEffectSegment segment = effect.getSegments().get(segmentIndex);
- if (!(segment instanceof PrebakedSegment)) {
- Slog.w(TAG, "Ignoring wrong segment for a PerformStep: " + segment);
- return skipToNextSteps(/* segmentsSkipped= */ 1);
- }
-
- PrebakedSegment prebaked = (PrebakedSegment) segment;
- if (DEBUG) {
- Slog.d(TAG, "Perform " + VibrationEffect.effectIdToString(
- prebaked.getEffectId()) + " on vibrator "
- + controller.getVibratorInfo().getId());
- }
-
- VibrationEffect fallback = mVibration.getFallback(prebaked.getEffectId());
- mVibratorOnResult = controller.on(prebaked, mVibration.id);
-
- if (mVibratorOnResult == 0 && prebaked.shouldFallback()
- && (fallback instanceof VibrationEffect.Composed)) {
- if (DEBUG) {
- Slog.d(TAG, "Playing fallback for effect "
- + VibrationEffect.effectIdToString(prebaked.getEffectId()));
- }
- SingleVibratorStep fallbackStep = nextVibrateStep(startTime, controller,
- replaceCurrentSegment((VibrationEffect.Composed) fallback),
- segmentIndex, vibratorOffTimeout);
- List<Step> fallbackResult = fallbackStep.play();
- // Update the result with the fallback result so this step is seamlessly
- // replaced by the fallback to any outer application of this.
- mVibratorOnResult = fallbackStep.getVibratorOnDuration();
- return fallbackResult;
- }
-
- return nextSteps(/* segmentsPlayed= */ 1);
- } finally {
- Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
- }
- }
-
- /**
- * Replace segment at {@link #segmentIndex} in {@link #effect} with given fallback segments.
- *
- * @return a copy of {@link #effect} with replaced segment.
- */
- private VibrationEffect.Composed replaceCurrentSegment(VibrationEffect.Composed fallback) {
- List<VibrationEffectSegment> newSegments = new ArrayList<>(effect.getSegments());
- int newRepeatIndex = effect.getRepeatIndex();
- newSegments.remove(segmentIndex);
- newSegments.addAll(segmentIndex, fallback.getSegments());
- if (segmentIndex < effect.getRepeatIndex()) {
- newRepeatIndex += fallback.getSegments().size() - 1;
- }
- return new VibrationEffect.Composed(newSegments, newRepeatIndex);
- }
- }
-
- /**
- * Represent a step turn the vibrator on using a composition of primitives.
- *
- * <p>This step will use the maximum supported number of consecutive segments of type
- * {@link PrimitiveSegment} starting at the current index.
- */
- private final class ComposePrimitivesStep extends SingleVibratorStep {
-
- ComposePrimitivesStep(long startTime, VibratorController controller,
- VibrationEffect.Composed effect, int index, long vibratorOffTimeout) {
- // This step should wait for the last vibration to finish (with the timeout) and for the
- // intended step start time (to respect the effect delays).
- super(Math.max(startTime, vibratorOffTimeout), controller, effect, index,
- vibratorOffTimeout);
- }
-
- @Override
- public List<Step> play() {
- Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "ComposePrimitivesStep");
- try {
- // Load the next PrimitiveSegments to create a single compose call to the vibrator,
- // limited to the vibrator composition maximum size.
- int limit = controller.getVibratorInfo().getCompositionSizeMax();
- int segmentCount = limit > 0
- ? Math.min(effect.getSegments().size(), segmentIndex + limit)
- : effect.getSegments().size();
- List<PrimitiveSegment> primitives = new ArrayList<>();
- for (int i = segmentIndex; i < segmentCount; i++) {
- VibrationEffectSegment segment = effect.getSegments().get(i);
- if (segment instanceof PrimitiveSegment) {
- primitives.add((PrimitiveSegment) segment);
- } else {
- break;
- }
- }
-
- if (primitives.isEmpty()) {
- Slog.w(TAG, "Ignoring wrong segment for a ComposePrimitivesStep: "
- + effect.getSegments().get(segmentIndex));
- return skipToNextSteps(/* segmentsSkipped= */ 1);
- }
-
- if (DEBUG) {
- Slog.d(TAG, "Compose " + primitives + " primitives on vibrator "
- + controller.getVibratorInfo().getId());
- }
- mVibratorOnResult = controller.on(
- primitives.toArray(new PrimitiveSegment[primitives.size()]),
- mVibration.id);
-
- return nextSteps(/* segmentsPlayed= */ primitives.size());
- } finally {
- Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
- }
- }
- }
-
- /**
- * Represent a step turn the vibrator on using a composition of PWLE segments.
- *
- * <p>This step will use the maximum supported number of consecutive segments of type
- * {@link StepSegment} or {@link RampSegment} starting at the current index.
- */
- private final class ComposePwleStep extends SingleVibratorStep {
-
- ComposePwleStep(long startTime, VibratorController controller,
- VibrationEffect.Composed effect, int index, long vibratorOffTimeout) {
- // This step should wait for the last vibration to finish (with the timeout) and for the
- // intended step start time (to respect the effect delays).
- super(Math.max(startTime, vibratorOffTimeout), controller, effect, index,
- vibratorOffTimeout);
- }
-
- @Override
- public List<Step> play() {
- Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "ComposePwleStep");
- try {
- // Load the next RampSegments to create a single composePwle call to the vibrator,
- // limited to the vibrator PWLE maximum size.
- int limit = controller.getVibratorInfo().getPwleSizeMax();
- int segmentCount = limit > 0
- ? Math.min(effect.getSegments().size(), segmentIndex + limit)
- : effect.getSegments().size();
- List<RampSegment> pwles = new ArrayList<>();
- for (int i = segmentIndex; i < segmentCount; i++) {
- VibrationEffectSegment segment = effect.getSegments().get(i);
- if (segment instanceof RampSegment) {
- pwles.add((RampSegment) segment);
- } else {
- break;
- }
- }
-
- if (pwles.isEmpty()) {
- Slog.w(TAG, "Ignoring wrong segment for a ComposePwleStep: "
- + effect.getSegments().get(segmentIndex));
- return skipToNextSteps(/* segmentsSkipped= */ 1);
- }
-
- if (DEBUG) {
- Slog.d(TAG, "Compose " + pwles + " PWLEs on vibrator "
- + controller.getVibratorInfo().getId());
- }
- mVibratorOnResult = controller.on(pwles.toArray(new RampSegment[pwles.size()]),
- mVibration.id);
-
- return nextSteps(/* segmentsPlayed= */ pwles.size());
- } finally {
- Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
- }
- }
- }
-
- /**
- * Represents a step to complete a {@link VibrationEffect}.
- *
- * <p>This runs right at the time the vibration is considered to end and will update the pending
- * vibrators count. This can turn off the vibrator or slowly ramp it down to zero amplitude.
- */
- private final class EffectCompleteStep extends SingleVibratorStep {
- private final boolean mCancelled;
-
- EffectCompleteStep(long startTime, boolean cancelled, VibratorController controller,
- long vibratorOffTimeout) {
- super(startTime, controller, /* effect= */ null, /* index= */ -1, vibratorOffTimeout);
- mCancelled = cancelled;
- }
-
- @Override
- public boolean isCleanUp() {
- // If the vibration was cancelled then this is just a clean up to ramp off the vibrator.
- // Otherwise this step is part of the vibration.
- return mCancelled;
- }
-
- @Override
- public List<Step> cancel() {
- if (mCancelled) {
- // Double cancelling will just turn off the vibrator right away.
- return Arrays.asList(new OffStep(SystemClock.uptimeMillis(), controller));
- }
- return super.cancel();
- }
-
- @Override
- public List<Step> play() {
- Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "EffectCompleteStep");
- try {
- if (DEBUG) {
- Slog.d(TAG, "Running " + (mCancelled ? "cancel" : "complete") + " vibration"
- + " step on vibrator " + controller.getVibratorInfo().getId());
- }
- if (mVibratorCompleteCallbackReceived) {
- // Vibration completion callback was received by this step, just turn if off
- // and skip any clean-up.
- stopVibrating();
- return EMPTY_STEP_LIST;
- }
-
- float currentAmplitude = controller.getCurrentAmplitude();
- long remainingOnDuration =
- vibratorOffTimeout - CALLBACKS_EXTRA_TIMEOUT - SystemClock.uptimeMillis();
- long rampDownDuration =
- Math.min(remainingOnDuration, mVibrationSettings.getRampDownDuration());
- long stepDownDuration = mVibrationSettings.getRampStepDuration();
- if (currentAmplitude < RAMP_OFF_AMPLITUDE_MIN
- || rampDownDuration <= stepDownDuration) {
- // No need to ramp down the amplitude, just wait to turn it off.
- if (mCancelled) {
- // Vibration is completing because it was cancelled, turn off right away.
- stopVibrating();
- return EMPTY_STEP_LIST;
- } else {
- return Arrays.asList(new OffStep(vibratorOffTimeout, controller));
- }
- }
-
- if (DEBUG) {
- Slog.d(TAG, "Ramping down vibrator " + controller.getVibratorInfo().getId()
- + " from amplitude " + currentAmplitude
- + " for " + rampDownDuration + "ms");
- }
- float amplitudeDelta = currentAmplitude / (rampDownDuration / stepDownDuration);
- float amplitudeTarget = currentAmplitude - amplitudeDelta;
- long newVibratorOffTimeout = mCancelled ? rampDownDuration : vibratorOffTimeout;
- return Arrays.asList(new RampOffStep(startTime, amplitudeTarget, amplitudeDelta,
- controller, newVibratorOffTimeout));
- } finally {
- Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
- }
- }
- }
-
- /** Represents a step to ramp down the vibrator amplitude before turning it off. */
- private final class RampOffStep extends SingleVibratorStep {
- private final float mAmplitudeTarget;
- private final float mAmplitudeDelta;
-
- RampOffStep(long startTime, float amplitudeTarget, float amplitudeDelta,
- VibratorController controller, long vibratorOffTimeout) {
- super(startTime, controller, /* effect= */ null, /* index= */ -1, vibratorOffTimeout);
- mAmplitudeTarget = amplitudeTarget;
- mAmplitudeDelta = amplitudeDelta;
- }
-
- @Override
- public boolean isCleanUp() {
- return true;
- }
-
- @Override
- public List<Step> cancel() {
- return Arrays.asList(new OffStep(SystemClock.uptimeMillis(), controller));
- }
-
- @Override
- public List<Step> play() {
- Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "RampOffStep");
- try {
- if (DEBUG) {
- long latency = SystemClock.uptimeMillis() - startTime;
- Slog.d(TAG, "Ramp down the vibrator amplitude, step with "
- + latency + "ms latency.");
- }
- if (mVibratorCompleteCallbackReceived) {
- // Vibration completion callback was received by this step, just turn if off
- // and skip the rest of the steps to ramp down the vibrator amplitude.
- stopVibrating();
- return EMPTY_STEP_LIST;
- }
-
- changeAmplitude(mAmplitudeTarget);
-
- float newAmplitudeTarget = mAmplitudeTarget - mAmplitudeDelta;
- if (newAmplitudeTarget < RAMP_OFF_AMPLITUDE_MIN) {
- // Vibrator amplitude cannot go further down, just turn it off.
- return Arrays.asList(new OffStep(vibratorOffTimeout, controller));
- }
- return Arrays.asList(new RampOffStep(
- startTime + mVibrationSettings.getRampStepDuration(), newAmplitudeTarget,
- mAmplitudeDelta, controller, vibratorOffTimeout));
- } finally {
- Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
- }
- }
- }
-
- /**
- * Represents a step to turn the vibrator off.
- *
- * <p>This runs after a timeout on the expected time the vibrator should have finished playing,
- * and can be brought forward by vibrator complete callbacks.
- */
- private final class OffStep extends SingleVibratorStep {
-
- OffStep(long startTime, VibratorController controller) {
- super(startTime, controller, /* effect= */ null, /* index= */ -1, startTime);
- }
-
- @Override
- public boolean isCleanUp() {
- return true;
- }
-
- @Override
- public List<Step> cancel() {
- return Arrays.asList(new OffStep(SystemClock.uptimeMillis(), controller));
- }
-
- @Override
- public void cancelImmediately() {
- stopVibrating();
- }
-
- @Override
- public List<Step> play() {
- Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "OffStep");
- try {
- stopVibrating();
- return EMPTY_STEP_LIST;
- } finally {
- Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
- }
- }
- }
-
- /**
- * Represents a step to turn the vibrator on and change its amplitude.
- *
- * <p>This step ignores vibration completion callbacks and control the vibrator on/off state
- * and amplitude to simulate waveforms represented by a sequence of {@link StepSegment}.
- */
- private final class AmplitudeStep extends SingleVibratorStep {
- private long mNextOffTime;
-
- AmplitudeStep(long startTime, VibratorController controller,
- VibrationEffect.Composed effect, int index, long vibratorOffTimeout) {
- // This step has a fixed startTime coming from the timings of the waveform it's playing.
- super(startTime, controller, effect, index, vibratorOffTimeout);
- mNextOffTime = vibratorOffTimeout;
- }
-
- @Override
- public boolean acceptVibratorCompleteCallback(int vibratorId) {
- if (controller.getVibratorInfo().getId() == vibratorId) {
- mVibratorCompleteCallbackReceived = true;
- mNextOffTime = SystemClock.uptimeMillis();
- }
- // Timings are tightly controlled here, so only trigger this step if the vibrator was
- // supposed to be ON but has completed prematurely, to turn it back on as soon as
- // possible.
- return mNextOffTime < startTime && controller.getCurrentAmplitude() > 0;
- }
-
- @Override
- public List<Step> play() {
- Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "AmplitudeStep");
- try {
- long now = SystemClock.uptimeMillis();
- long latency = now - startTime;
- if (DEBUG) {
- Slog.d(TAG, "Running amplitude step with " + latency + "ms latency.");
- }
-
- if (mVibratorCompleteCallbackReceived && latency < 0) {
- // This step was run early because the vibrator turned off prematurely.
- // Turn it back on and return this same step to run at the exact right time.
- mNextOffTime = turnVibratorBackOn(/* remainingDuration= */ -latency);
- return Arrays.asList(new AmplitudeStep(startTime, controller, effect,
- segmentIndex, mNextOffTime));
- }
-
- VibrationEffectSegment segment = effect.getSegments().get(segmentIndex);
- if (!(segment instanceof StepSegment)) {
- Slog.w(TAG, "Ignoring wrong segment for a AmplitudeStep: " + segment);
- return skipToNextSteps(/* segmentsSkipped= */ 1);
- }
-
- StepSegment stepSegment = (StepSegment) segment;
- if (stepSegment.getDuration() == 0) {
- // Skip waveform entries with zero timing.
- return skipToNextSteps(/* segmentsSkipped= */ 1);
- }
-
- float amplitude = stepSegment.getAmplitude();
- if (amplitude == 0) {
- if (vibratorOffTimeout > now) {
- // Amplitude cannot be set to zero, so stop the vibrator.
- stopVibrating();
- mNextOffTime = now;
- }
- } else {
- if (startTime >= mNextOffTime) {
- // Vibrator is OFF. Turn vibrator back on for the duration of another
- // cycle before setting the amplitude.
- long onDuration = getVibratorOnDuration(effect, segmentIndex);
- if (onDuration > 0) {
- mVibratorOnResult = startVibrating(onDuration);
- mNextOffTime = now + onDuration + CALLBACKS_EXTRA_TIMEOUT;
- }
- }
- changeAmplitude(amplitude);
- }
-
- // Use original startTime to avoid propagating latencies to the waveform.
- long nextStartTime = startTime + segment.getDuration();
- return nextSteps(nextStartTime, mNextOffTime, /* segmentsPlayed= */ 1);
- } finally {
- Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
- }
- }
-
- private long turnVibratorBackOn(long remainingDuration) {
- long onDuration = getVibratorOnDuration(effect, segmentIndex);
- if (onDuration <= 0) {
- // Vibrator is supposed to go back off when this step starts, so just leave it off.
- return vibratorOffTimeout;
- }
- onDuration += remainingDuration;
- float expectedAmplitude = controller.getCurrentAmplitude();
- mVibratorOnResult = startVibrating(onDuration);
- if (mVibratorOnResult > 0) {
- // Set the amplitude back to the value it was supposed to be playing at.
- changeAmplitude(expectedAmplitude);
- }
- return SystemClock.uptimeMillis() + onDuration + CALLBACKS_EXTRA_TIMEOUT;
- }
-
- private long startVibrating(long duration) {
- if (DEBUG) {
- Slog.d(TAG, "Turning on vibrator " + controller.getVibratorInfo().getId() + " for "
- + duration + "ms");
- }
- return controller.on(duration, mVibration.id);
- }
-
- /**
- * Get the duration the vibrator will be on for a waveform, starting at {@code startIndex}
- * until the next time it's vibrating amplitude is zero or a different type of segment is
- * found.
- */
- private long getVibratorOnDuration(VibrationEffect.Composed effect, int startIndex) {
- List<VibrationEffectSegment> segments = effect.getSegments();
- int segmentCount = segments.size();
- int repeatIndex = effect.getRepeatIndex();
- int i = startIndex;
- long timing = 0;
- while (i < segmentCount) {
- VibrationEffectSegment segment = segments.get(i);
- if (!(segment instanceof StepSegment)
- || ((StepSegment) segment).getAmplitude() == 0) {
- break;
- }
- timing += segment.getDuration();
- i++;
- if (i == segmentCount && repeatIndex >= 0) {
- i = repeatIndex;
- // prevent infinite loop
- repeatIndex = -1;
- }
- if (i == startIndex) {
- // The repeating waveform keeps the vibrator ON all the time. Use a minimum
- // of 1s duration to prevent short patterns from turning the vibrator ON too
- // frequently.
- return Math.max(timing, 1000);
- }
- }
- if (i == segmentCount && effect.getRepeatIndex() < 0) {
- // Vibration ending at non-zero amplitude, add extra timings to ramp down after
- // vibration is complete.
- timing += mVibrationSettings.getRampDownDuration();
- }
- return timing;
- }
- }
-
- /**
- * Map a {@link CombinedVibration} to the vibrators available on the device.
- *
- * <p>This contains the logic to find the capabilities required from {@link IVibratorManager} to
- * play all of the effects in sync.
- */
- private final class DeviceEffectMap {
- private final SparseArray<VibrationEffect.Composed> mVibratorEffects;
- private final int[] mVibratorIds;
- private final long mRequiredSyncCapabilities;
-
- DeviceEffectMap(CombinedVibration.Mono mono) {
- mVibratorEffects = new SparseArray<>(mVibrators.size());
- mVibratorIds = new int[mVibrators.size()];
- for (int i = 0; i < mVibrators.size(); i++) {
- int vibratorId = mVibrators.keyAt(i);
- VibratorInfo vibratorInfo = mVibrators.valueAt(i).getVibratorInfo();
- VibrationEffect effect = mDeviceEffectAdapter.apply(mono.getEffect(), vibratorInfo);
- if (effect instanceof VibrationEffect.Composed) {
- mVibratorEffects.put(vibratorId, (VibrationEffect.Composed) effect);
- mVibratorIds[i] = vibratorId;
- }
- }
- mRequiredSyncCapabilities = calculateRequiredSyncCapabilities(mVibratorEffects);
- }
-
- DeviceEffectMap(CombinedVibration.Stereo stereo) {
- SparseArray<VibrationEffect> stereoEffects = stereo.getEffects();
- mVibratorEffects = new SparseArray<>();
- for (int i = 0; i < stereoEffects.size(); i++) {
- int vibratorId = stereoEffects.keyAt(i);
- if (mVibrators.contains(vibratorId)) {
- VibratorInfo vibratorInfo = mVibrators.valueAt(i).getVibratorInfo();
- VibrationEffect effect = mDeviceEffectAdapter.apply(
- stereoEffects.valueAt(i), vibratorInfo);
- if (effect instanceof VibrationEffect.Composed) {
- mVibratorEffects.put(vibratorId, (VibrationEffect.Composed) effect);
- }
- }
- }
- mVibratorIds = new int[mVibratorEffects.size()];
- for (int i = 0; i < mVibratorEffects.size(); i++) {
- mVibratorIds[i] = mVibratorEffects.keyAt(i);
- }
- mRequiredSyncCapabilities = calculateRequiredSyncCapabilities(mVibratorEffects);
- }
-
- /**
- * Return the number of vibrators mapped to play the {@link CombinedVibration} on this
- * device.
- */
- public int size() {
- return mVibratorIds.length;
- }
-
- /**
- * Return all capabilities required to play the {@link CombinedVibration} in
- * between calls to {@link IVibratorManager#prepareSynced} and
- * {@link IVibratorManager#triggerSynced}.
- */
- public long getRequiredSyncCapabilities() {
- return mRequiredSyncCapabilities;
- }
-
- /** Return all vibrator ids mapped to play the {@link CombinedVibration}. */
- public int[] getVibratorIds() {
- return mVibratorIds;
- }
-
- /** Return the id of the vibrator at given index. */
- public int vibratorIdAt(int index) {
- return mVibratorEffects.keyAt(index);
- }
-
- /** Return the {@link VibrationEffect} at given index. */
- public VibrationEffect.Composed effectAt(int index) {
- return mVibratorEffects.valueAt(index);
- }
-
- /**
- * Return all capabilities required from the {@link IVibratorManager} to prepare and
- * trigger all given effects in sync.
- *
- * @return {@link IVibratorManager#CAP_SYNC} together with all required
- * IVibratorManager.CAP_PREPARE_* and IVibratorManager.CAP_MIXED_TRIGGER_* capabilities.
- */
- private long calculateRequiredSyncCapabilities(
- SparseArray<VibrationEffect.Composed> effects) {
- long prepareCap = 0;
- for (int i = 0; i < effects.size(); i++) {
- VibrationEffectSegment firstSegment = effects.valueAt(i).getSegments().get(0);
- if (firstSegment instanceof StepSegment) {
- prepareCap |= IVibratorManager.CAP_PREPARE_ON;
- } else if (firstSegment instanceof PrebakedSegment) {
- prepareCap |= IVibratorManager.CAP_PREPARE_PERFORM;
- } else if (firstSegment instanceof PrimitiveSegment) {
- prepareCap |= IVibratorManager.CAP_PREPARE_COMPOSE;
- }
- }
- int triggerCap = 0;
- if (requireMixedTriggerCapability(prepareCap, IVibratorManager.CAP_PREPARE_ON)) {
- triggerCap |= IVibratorManager.CAP_MIXED_TRIGGER_ON;
- }
- if (requireMixedTriggerCapability(prepareCap, IVibratorManager.CAP_PREPARE_PERFORM)) {
- triggerCap |= IVibratorManager.CAP_MIXED_TRIGGER_PERFORM;
- }
- if (requireMixedTriggerCapability(prepareCap, IVibratorManager.CAP_PREPARE_COMPOSE)) {
- triggerCap |= IVibratorManager.CAP_MIXED_TRIGGER_COMPOSE;
- }
- return IVibratorManager.CAP_SYNC | prepareCap | triggerCap;
- }
-
- /**
- * Return true if {@code prepareCapabilities} contains this {@code capability} mixed with
- * different ones, requiring a mixed trigger capability from the vibrator manager for
- * syncing all effects.
- */
- private boolean requireMixedTriggerCapability(long prepareCapabilities, long capability) {
- return (prepareCapabilities & capability) != 0
- && (prepareCapabilities & ~capability) != 0;
- }
- }
}
diff --git a/services/core/java/com/android/server/vibrator/VibratorManagerService.java b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
index 63f3af3f3095..01f9d0b94879 100644
--- a/services/core/java/com/android/server/vibrator/VibratorManagerService.java
+++ b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
@@ -42,6 +42,7 @@ import android.os.IVibratorStateListener;
import android.os.Looper;
import android.os.PowerManager;
import android.os.Process;
+import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ServiceManager;
import android.os.ShellCallback;
@@ -60,6 +61,7 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IBatteryStats;
import com.android.internal.util.DumpUtils;
+import com.android.internal.util.FrameworkStatsLog;
import com.android.server.LocalServices;
import com.android.server.SystemService;
@@ -88,6 +90,9 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
VibrationAttributes.FLAG_BYPASS_INTERRUPTION_POLICY
| VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF;
+ /** Fixed large duration used to note repeating vibrations to {@link IBatteryStats}. */
+ private static final long BATTERY_STATS_REPEATING_VIBRATION_DURATION = 5_000;
+
/** Lifecycle responsible for initializing this class at the right system server phases. */
public static class Lifecycle extends SystemService {
private VibratorManagerService mService;
@@ -201,8 +206,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
mSystemUiPackage = LocalServices.getService(PackageManagerInternal.class)
.getSystemUiServiceComponent().getPackageName();
- mBatteryStatsService = IBatteryStats.Stub.asInterface(ServiceManager.getService(
- BatteryStats.SERVICE_NAME));
+ mBatteryStatsService = injector.getBatteryStatsService();
mAppOps = mContext.getSystemService(AppOpsManager.class);
@@ -634,7 +638,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
}
VibrationThread vibThread = new VibrationThread(vib, mVibrationSettings,
- mDeviceVibrationEffectAdapter, mVibrators, mWakeLock, mBatteryStatsService,
+ mDeviceVibrationEffectAdapter, mVibrators, mWakeLock,
mVibrationThreadCallbacks);
if (mCurrentVibration == null) {
@@ -1105,6 +1109,11 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
return new Handler(looper);
}
+ IBatteryStats getBatteryStatsService() {
+ return IBatteryStats.Stub.asInterface(ServiceManager.getService(
+ BatteryStats.SERVICE_NAME));
+ }
+
VibratorController createVibratorController(int vibratorId,
VibratorController.OnVibrationCompleteListener listener) {
return new VibratorController(vibratorId, listener);
@@ -1141,6 +1150,36 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
}
@Override
+ public void noteVibratorOn(int uid, long duration) {
+ try {
+ if (duration <= 0) {
+ return;
+ }
+ if (duration == Long.MAX_VALUE) {
+ // Repeating duration has started. Report a fixed duration here, noteVibratorOff
+ // should be called when this is cancelled.
+ duration = BATTERY_STATS_REPEATING_VIBRATION_DURATION;
+ }
+ mBatteryStatsService.noteVibratorOn(uid, duration);
+ FrameworkStatsLog.write_non_chained(FrameworkStatsLog.VIBRATOR_STATE_CHANGED,
+ uid, null, FrameworkStatsLog.VIBRATOR_STATE_CHANGED__STATE__ON,
+ duration);
+ } catch (RemoteException e) {
+ }
+ }
+
+ @Override
+ public void noteVibratorOff(int uid) {
+ try {
+ mBatteryStatsService.noteVibratorOff(uid);
+ FrameworkStatsLog.write_non_chained(FrameworkStatsLog.VIBRATOR_STATE_CHANGED,
+ uid, null, FrameworkStatsLog.VIBRATOR_STATE_CHANGED__STATE__OFF,
+ /* duration= */ 0);
+ } catch (RemoteException e) {
+ }
+ }
+
+ @Override
public void onVibrationCompleted(long vibrationId, Vibration.Status status) {
if (DEBUG) {
Slog.d(TAG, "Vibration " + vibrationId + " finished with status " + status);
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 87c8a79f874a..2da29876da7a 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -62,8 +62,8 @@ import android.content.pm.UserInfo;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
-import android.graphics.BitmapRegionDecoder;
import android.graphics.Color;
+import android.graphics.ImageDecoder;
import android.graphics.Rect;
import android.graphics.RectF;
import android.hardware.display.DisplayManager;
@@ -199,6 +199,8 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
static final String WALLPAPER_LOCK_ORIG = "wallpaper_lock_orig";
static final String WALLPAPER_LOCK_CROP = "wallpaper_lock";
static final String WALLPAPER_INFO = "wallpaper_info.xml";
+ private static final String RECORD_FILE = "decode_record";
+ private static final String RECORD_LOCK_FILE = "decode_lock_record";
// All the various per-user state files we need to be aware of
private static final String[] sPerUserFiles = new String[] {
@@ -689,8 +691,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
}
if (DEBUG) {
- // This is just a quick estimation, may be smaller than it is.
- long estimateSize = options.outWidth * options.outHeight * 4;
+ long estimateSize = (long) options.outWidth * options.outHeight * 4;
Slog.v(TAG, "Null crop of new wallpaper, estimate size="
+ estimateSize + ", success=" + success);
}
@@ -699,9 +700,6 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
FileOutputStream f = null;
BufferedOutputStream bos = null;
try {
- BitmapRegionDecoder decoder = BitmapRegionDecoder.newInstance(
- wallpaper.wallpaperFile.getAbsolutePath(), false);
-
// This actually downsamples only by powers of two, but that's okay; we do
// a proper scaling blit later. This is to minimize transient RAM use.
// We calculate the largest power-of-two under the actual ratio rather than
@@ -755,8 +753,24 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
Slog.v(TAG, " maxTextureSize=" + GLHelper.getMaxTextureSize());
}
- Bitmap cropped = decoder.decodeRegion(cropHint, options);
- decoder.recycle();
+ //Create a record file and will delete if ImageDecoder work well.
+ final String recordName =
+ (wallpaper.wallpaperFile.getName().equals(WALLPAPER)
+ ? RECORD_FILE : RECORD_LOCK_FILE);
+ final File record = new File(getWallpaperDir(wallpaper.userId), recordName);
+ record.createNewFile();
+ Slog.v(TAG, "record path =" + record.getPath()
+ + ", record name =" + record.getName());
+
+ final ImageDecoder.Source srcData =
+ ImageDecoder.createSource(wallpaper.wallpaperFile);
+ final int sampleSize = scale;
+ Bitmap cropped = ImageDecoder.decodeBitmap(srcData, (decoder, info, src) -> {
+ decoder.setTargetSampleSize(sampleSize);
+ decoder.setCrop(estimateCrop);
+ });
+
+ record.delete();
if (cropped == null) {
Slog.e(TAG, "Could not decode new wallpaper");
@@ -1819,6 +1833,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
new UserSwitchObserver() {
@Override
public void onUserSwitching(int newUserId, IRemoteCallback reply) {
+ errorCheck(newUserId);
switchUser(newUserId, reply);
}
}, TAG);
@@ -1856,6 +1871,14 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
@Override
public void onBootPhase(int phase) {
+ // If someone set too large jpg file as wallpaper, system_server may be killed by lmk in
+ // generateCrop(), so we create a file in generateCrop() before ImageDecoder starts working
+ // and delete this file after ImageDecoder finishing. If the specific file exists, that
+ // means ImageDecoder can't handle the original wallpaper file, in order to avoid
+ // system_server restart again and again and rescue party will trigger factory reset,
+ // so we reset default wallpaper in case system_server is trapped into a restart loop.
+ errorCheck(UserHandle.USER_SYSTEM);
+
if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
systemReady();
} else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
@@ -1863,6 +1886,38 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
}
}
+ private static final HashMap<Integer, String> sWallpaperType = new HashMap<Integer, String>() {
+ {
+ put(FLAG_SYSTEM, RECORD_FILE);
+ put(FLAG_LOCK, RECORD_LOCK_FILE);
+ }
+ };
+
+ private void errorCheck(int userID) {
+ sWallpaperType.forEach((type, filename) -> {
+ final File record = new File(getWallpaperDir(userID), filename);
+ if (record.exists()) {
+ Slog.w(TAG, "User:" + userID + ", wallpaper tyep = " + type
+ + ", wallpaper fail detect!! reset to default wallpaper");
+ clearWallpaperData(userID, type);
+ record.delete();
+ }
+ });
+ }
+
+ private void clearWallpaperData(int userID, int wallpaperType) {
+ final WallpaperData wallpaper = new WallpaperData(userID, getWallpaperDir(userID),
+ (wallpaperType == FLAG_LOCK) ? WALLPAPER_LOCK_ORIG : WALLPAPER,
+ (wallpaperType == FLAG_LOCK) ? WALLPAPER_LOCK_CROP : WALLPAPER_CROP);
+ if (wallpaper.sourceExists()) {
+ wallpaper.wallpaperFile.delete();
+ }
+ if (wallpaper.cropExists()) {
+ wallpaper.cropFile.delete();
+ }
+
+ }
+
@Override
public void onUnlockUser(final int userId) {
synchronized (mLock) {
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index b0efa5b283bb..d7725865f609 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -148,6 +148,7 @@ import static com.android.server.wm.ActivityRecordProto.ALL_DRAWN;
import static com.android.server.wm.ActivityRecordProto.APP_STOPPED;
import static com.android.server.wm.ActivityRecordProto.CLIENT_VISIBLE;
import static com.android.server.wm.ActivityRecordProto.DEFER_HIDING_CLIENT;
+import static com.android.server.wm.ActivityRecordProto.ENABLE_RECENTS_SCREENSHOT;
import static com.android.server.wm.ActivityRecordProto.FILLS_PARENT;
import static com.android.server.wm.ActivityRecordProto.FRONT_OF_TASK;
import static com.android.server.wm.ActivityRecordProto.IN_SIZE_COMPAT_MODE;
@@ -771,7 +772,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// Last visibility state we reported to the app token.
boolean reportedVisible;
- boolean mEnablePreviewScreenshots = true;
+ boolean mEnableRecentsScreenshot = true;
// Information about an application starting window if displayed.
// Note: these are de-referenced before the starting window animates away.
@@ -5151,7 +5152,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
* See {@link Activity#setRecentsScreenshotEnabled}.
*/
void setRecentsScreenshotEnabled(boolean enabled) {
- mEnablePreviewScreenshots = enabled;
+ mEnableRecentsScreenshot = enabled;
}
/**
@@ -5163,7 +5164,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
* screenshot.
*/
boolean shouldUseAppThemeSnapshot() {
- return !mEnablePreviewScreenshots || forAllWindows(WindowState::isSecureLocked,
+ return !mEnableRecentsScreenshot || forAllWindows(WindowState::isSecureLocked,
true /* topToBottom */);
}
@@ -9184,6 +9185,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// Only record if max bounds sandboxing is applied, if the caller has the necessary
// permission to access the device configs.
proto.write(PROVIDES_MAX_BOUNDS, providesMaxBounds());
+ proto.write(ENABLE_RECENTS_SCREENSHOT, mEnableRecentsScreenshot);
}
@Override
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index ef0ee1206208..47bec3081dc3 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -2196,8 +2196,9 @@ class ActivityStarter {
// removed from calling performClearTaskLocked (For example, if it is being brought out
// of history or if it is finished immediately), thus disassociating the task. Also note
// that mReuseTask is reset as a result of {@link Task#performClearTaskLocked}
- // launching another activity.
- targetTask.performClearTaskLocked();
+ // launching another activity. Keep the task-overlay activity because the targetTask
+ // will be reused to launch new activity.
+ targetTask.performClearTaskForReuse(true /* excludingTaskOverlay*/);
targetTask.setIntent(mStartActivity);
mAddingToTask = true;
} else if ((mLaunchFlags & FLAG_ACTIVITY_CLEAR_TOP) != 0
@@ -2207,8 +2208,7 @@ class ActivityStarter {
// In this situation we want to remove all activities from the task up to the one
// being started. In most cases this means we are resetting the task to its initial
// state.
- final ActivityRecord top = targetTask.performClearTaskForReuseLocked(mStartActivity,
- mLaunchFlags);
+ final ActivityRecord top = targetTask.performClearTop(mStartActivity, mLaunchFlags);
if (top != null) {
if (top.isRootOfTask()) {
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index 25c4d200d76b..cecfccd1f836 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -471,7 +471,7 @@ public abstract class ActivityTaskManagerInternal {
/** Dump the current activities state. */
public abstract boolean dumpActivity(FileDescriptor fd, PrintWriter pw, String name,
String[] args, int opti, boolean dumpAll, boolean dumpVisibleRootTasksOnly,
- boolean dumpFocusedRootTaskOnly);
+ boolean dumpFocusedRootTaskOnly, @UserIdInt int userId);
/** Dump the current state for inclusion in oom dump. */
public abstract void dumpForOom(PrintWriter pw);
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 049747728ade..fe4eae915422 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -4062,12 +4062,12 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
*/
protected boolean dumpActivity(FileDescriptor fd, PrintWriter pw, String name, String[] args,
int opti, boolean dumpAll, boolean dumpVisibleRootTasksOnly,
- boolean dumpFocusedRootTaskOnly) {
+ boolean dumpFocusedRootTaskOnly, @UserIdInt int userId) {
ArrayList<ActivityRecord> activities;
synchronized (mGlobalLock) {
activities = mRootWindowContainer.getDumpActivities(name, dumpVisibleRootTasksOnly,
- dumpFocusedRootTaskOnly);
+ dumpFocusedRootTaskOnly, userId);
}
if (activities.size() <= 0) {
@@ -6410,9 +6410,9 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
@Override
public boolean dumpActivity(FileDescriptor fd, PrintWriter pw, String name,
String[] args, int opti, boolean dumpAll, boolean dumpVisibleRootTasksOnly,
- boolean dumpFocusedRootTaskOnly) {
+ boolean dumpFocusedRootTaskOnly, @UserIdInt int userId) {
return ActivityTaskManagerService.this.dumpActivity(fd, pw, name, args, opti, dumpAll,
- dumpVisibleRootTasksOnly, dumpFocusedRootTaskOnly);
+ dumpVisibleRootTasksOnly, dumpFocusedRootTaskOnly, userId);
}
@Override
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index 3d7dead7d4c0..5573f161d1c1 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -1605,7 +1605,7 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
task.mTransitionController.requestCloseTransitionIfNeeded(task);
task.mInRemoveTask = true;
try {
- task.performClearTask(reason);
+ task.removeActivities(reason, false /* excludingTaskOverlay */);
cleanUpRemovedTaskLocked(task, killProcess, removeFromRecents);
mService.getLockTaskController().clearLockedTask(task);
mService.getTaskChangeNotificationController().notifyTaskStackChanged();
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 4c5c7054e9a0..83ff2f07a06f 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -5009,6 +5009,10 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
final boolean canImeTargetSetRelativeLayer = imeTarget.getSurfaceControl() != null
&& imeTarget.mToken == imeControlTargetToken
&& !imeTarget.inMultiWindowMode()
+ // We don't need to set relative layer if the IME target in non-multi-window
+ // mode is the activity main window since updateImeParent will ensure the IME
+ // surface be attached on the fullscreen activity.
+ && imeTarget.mAttrs.type != TYPE_BASE_APPLICATION
&& imeTarget.mToken.getActivity(app -> app.isAnimating(TRANSITION | PARENTS,
ANIMATION_TYPE_ALL & ~ANIMATION_TYPE_RECENTS)) == null;
if (canImeTargetSetRelativeLayer) {
@@ -5499,7 +5503,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
* display, which set unrestricted keep-clear areas.
*
* For context on restricted vs unrestricted keep-clear areas, see
- * {@link android.Manifest.permission.USE_UNRESTRICTED_KEEP_CLEAR_AREAS}.
+ * {@link android.Manifest.permission.SET_UNRESTRICTED_KEEP_CLEAR_AREAS}.
*/
void getKeepClearAreas(List<Rect> outRestricted, List<Rect> outUnrestricted) {
final Matrix tmpMatrix = new Matrix();
diff --git a/services/core/java/com/android/server/wm/LockTaskController.java b/services/core/java/com/android/server/wm/LockTaskController.java
index 8a2d11636fe3..160fc95f3f7c 100644
--- a/services/core/java/com/android/server/wm/LockTaskController.java
+++ b/services/core/java/com/android/server/wm/LockTaskController.java
@@ -542,7 +542,7 @@ public class LockTaskController {
if (mLockTaskModeTasks.isEmpty()) {
return;
}
- task.performClearTaskLocked();
+ task.performClearTaskForReuse(false /* excludingTaskOverlay*/);
mSupervisor.mRootWindowContainer.resumeFocusedTasksTopActivities();
}
@@ -740,7 +740,7 @@ public class LockTaskController {
ProtoLog.d(WM_DEBUG_LOCKTASK, "onLockTaskPackagesUpdated: removing %s"
+ " mLockTaskAuth()=%s", lockedTask, lockedTask.lockTaskAuthToString());
removeLockedTask(lockedTask);
- lockedTask.performClearTaskLocked();
+ lockedTask.performClearTaskForReuse(false /* excludingTaskOverlay*/);
taskChanged = true;
}
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 8ab2ee03a1e3..ac5ed7b200f2 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -3434,11 +3434,11 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
* or all visible root tasks if {@param dumpVisibleRootTasksOnly} is true.
*/
ArrayList<ActivityRecord> getDumpActivities(String name, boolean dumpVisibleRootTasksOnly,
- boolean dumpFocusedRootTaskOnly) {
+ boolean dumpFocusedRootTaskOnly, @UserIdInt int userId) {
if (dumpFocusedRootTaskOnly) {
final Task topFocusedRootTask = getTopDisplayFocusedRootTask();
if (topFocusedRootTask != null) {
- return topFocusedRootTask.getDumpActivitiesLocked(name);
+ return topFocusedRootTask.getDumpActivitiesLocked(name, userId);
} else {
return new ArrayList<>();
}
@@ -3446,7 +3446,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
final ArrayList<ActivityRecord> activities = new ArrayList<>();
forAllRootTasks(rootTask -> {
if (!dumpVisibleRootTasksOnly || rootTask.shouldBeVisible(null)) {
- activities.addAll(rootTask.getDumpActivitiesLocked(name));
+ activities.addAll(rootTask.getDumpActivitiesLocked(name, userId));
}
});
return activities;
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index ffa1a601d41b..cc03c60aa6d6 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -137,6 +137,7 @@ import static java.lang.Integer.MAX_VALUE;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.UserIdInt;
import android.app.Activity;
import android.app.ActivityManager;
import android.app.ActivityManager.RecentTaskInfo.PersistedTaskSnapshotData;
@@ -1583,19 +1584,23 @@ class Task extends TaskFragment {
}
/** Completely remove all activities associated with an existing task. */
- void performClearTask(String reason) {
+ void removeActivities(String reason, boolean excludingTaskOverlay) {
clearPinnedTaskIfNeed();
// Broken down into to cases to avoid object create due to capturing mStack.
if (getRootTask() == null) {
forAllActivities((r) -> {
- if (r.finishing) return;
+ if (r.finishing || (excludingTaskOverlay && r.isTaskOverlay())) {
+ return;
+ }
// Task was restored from persistent storage.
r.takeFromHistory();
removeChild(r, reason);
});
} else {
forAllActivities((r) -> {
- if (r.finishing) return;
+ if (r.finishing || (excludingTaskOverlay && r.isTaskOverlay())) {
+ return;
+ }
// Prevent the transition from being executed too early if the top activity is
// resumed but the mVisibleRequested of any other activity is true, the transition
// should wait until next activity resumed.
@@ -1612,26 +1617,24 @@ class Task extends TaskFragment {
/**
* Completely remove all activities associated with an existing task.
*/
- void performClearTaskLocked() {
+ void performClearTaskForReuse(boolean excludingTaskOverlay) {
mReuseTask = true;
mTaskSupervisor.beginDeferResume();
try {
- performClearTask("clear-task-all");
+ removeActivities("clear-task-all", excludingTaskOverlay);
} finally {
mTaskSupervisor.endDeferResume();
mReuseTask = false;
}
}
- ActivityRecord performClearTaskForReuseLocked(ActivityRecord newR, int launchFlags) {
- mReuseTask = true;
+ ActivityRecord performClearTop(ActivityRecord newR, int launchFlags) {
mTaskSupervisor.beginDeferResume();
final ActivityRecord result;
try {
- result = performClearTaskLocked(newR, launchFlags);
+ result = clearTopActivities(newR, launchFlags);
} finally {
mTaskSupervisor.endDeferResume();
- mReuseTask = false;
}
return result;
}
@@ -1647,7 +1650,7 @@ class Task extends TaskFragment {
* @return Returns the old activity that should be continued to be used,
* or {@code null} if none was found.
*/
- private ActivityRecord performClearTaskLocked(ActivityRecord newR, int launchFlags) {
+ private ActivityRecord clearTopActivities(ActivityRecord newR, int launchFlags) {
final ActivityRecord r = findActivityInHistory(newR.mActivityComponent);
if (r == null) return null;
@@ -1674,7 +1677,7 @@ class Task extends TaskFragment {
// Stop operation once we reach the boundary activity.
if (r == boundaryActivity) return true;
- if (!r.finishing) {
+ if (!r.finishing && !r.isTaskOverlay()) {
final ActivityOptions opts = r.getOptions();
if (opts != null) {
r.clearOptionsAnimation();
@@ -5149,10 +5152,13 @@ class Task extends TaskFragment {
// Ensure that we do not trigger entering PiP an activity on the root pinned task
return false;
}
+ final boolean isTransient = opts != null && opts.getTransientLaunch();
final Task targetRootTask = toFrontTask != null
? toFrontTask.getRootTask() : toFrontActivity.getRootTask();
- if (targetRootTask != null && targetRootTask.isActivityTypeAssistant()) {
- // Ensure the task/activity being brought forward is not the assistant
+ if (targetRootTask != null && (targetRootTask.isActivityTypeAssistant() || isTransient)) {
+ // Ensure the task/activity being brought forward is not the assistant and is not
+ // transient. In the case of transient-launch, we want to wait until the end of the
+ // transition and only allow switch if the transient launch was committed.
return false;
}
return true;
@@ -5688,7 +5694,7 @@ class Task extends TaskFragment {
}
}
- ArrayList<ActivityRecord> getDumpActivitiesLocked(String name) {
+ ArrayList<ActivityRecord> getDumpActivitiesLocked(String name, @UserIdInt int userId) {
ArrayList<ActivityRecord> activities = new ArrayList<>();
if ("all".equals(name)) {
@@ -5708,7 +5714,13 @@ class Task extends TaskFragment {
}
});
}
-
+ if (userId != UserHandle.USER_ALL) {
+ for (int i = activities.size() - 1; i >= 0; --i) {
+ if (activities.get(i).mUserId != userId) {
+ activities.remove(i);
+ }
+ }
+ }
return activities;
}
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java
index a9add59be1f6..6a23eb588102 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotController.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java
@@ -310,7 +310,7 @@ class TaskSnapshotController {
}
final ActivityRecord activity = result.first;
final WindowState mainWindow = result.second;
- final Rect contentInsets = getSystemBarInsets(task.getBounds(),
+ final Rect contentInsets = getSystemBarInsets(mainWindow.getFrame(),
mainWindow.getInsetsStateWithVisibilityOverride());
final Rect letterboxInsets = activity.getLetterboxInsets();
InsetUtils.addInsets(contentInsets, letterboxInsets);
@@ -575,7 +575,7 @@ class TaskSnapshotController {
final LayoutParams attrs = mainWindow.getAttrs();
final Rect taskBounds = task.getBounds();
final InsetsState insetsState = mainWindow.getInsetsStateWithVisibilityOverride();
- final Rect systemBarInsets = getSystemBarInsets(taskBounds, insetsState);
+ final Rect systemBarInsets = getSystemBarInsets(mainWindow.getFrame(), insetsState);
final SystemBarBackgroundPainter decorPainter = new SystemBarBackgroundPainter(attrs.flags,
attrs.privateFlags, attrs.insetsFlags.appearance, task.getTaskDescription(),
mHighResTaskSnapshotScale, insetsState);
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 0b965c37a2ba..bf33f86ac672 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -459,9 +459,19 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
// activity in a bad state.
if (!visibleAtTransitionEnd && !ar.isVisibleRequested()) {
boolean commitVisibility = true;
- if (ar.getDeferHidingClient() && ar.getTask() != null) {
+ if (ar.isVisible() && ar.getTask() != null) {
if (ar.pictureInPictureArgs != null
&& ar.pictureInPictureArgs.isAutoEnterEnabled()) {
+ if (mTransientLaunches != null) {
+ for (int j = 0; j < mTransientLaunches.size(); ++j) {
+ if (mTransientLaunches.valueAt(j).isVisibleRequested()) {
+ // force enable pip-on-task-switch now that we've committed
+ // to actually launching to the transient activity.
+ ar.supportsEnterPipOnTaskSwitch = true;
+ break;
+ }
+ }
+ }
mController.mAtm.enterPictureInPictureMode(ar, ar.pictureInPictureArgs);
// Avoid commit visibility to false here, or else we will get a sudden
// "flash" / surface going invisible for a split second.
@@ -1252,7 +1262,11 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
change.setAllowEnterPip(topMostActivity != null
&& topMostActivity.checkEnterPictureInPictureAppOpsState());
final ActivityRecord topRunningActivity = task.topRunningActivity();
- if (topRunningActivity != null && task.mDisplayContent != null) {
+ if (topRunningActivity != null && task.mDisplayContent != null
+ // Display won't be rotated for multi window Task, so the fixed rotation
+ // won't be applied. This can happen when the windowing mode is changed
+ // before the previous fixed rotation is applied.
+ && !task.inMultiWindowMode()) {
// If Activity is in fixed rotation, its will be applied with the next rotation,
// when the Task is still in the previous rotation.
final int taskRotation = task.getWindowConfiguration().getDisplayRotation();
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index c267cbacdf24..c13ae95217b5 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -30,6 +30,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.IApplicationThread;
+import android.app.WindowConfiguration;
import android.os.IBinder;
import android.os.IRemoteCallback;
import android.os.RemoteException;
@@ -296,6 +297,17 @@ class TransitionController {
return ci.mVisible;
}
+ @WindowConfiguration.WindowingMode
+ int getWindowingModeAtStart(@NonNull WindowContainer wc) {
+ if (mCollectingTransition == null) return wc.getWindowingMode();
+ final Transition.ChangeInfo ci = mCollectingTransition.mChanges.get(wc);
+ if (ci == null) {
+ // not part of transition, so use current state.
+ return wc.getWindowingMode();
+ }
+ return ci.mWindowingMode;
+ }
+
@WindowManager.TransitionType
int getCollectingTransitionType() {
return mCollectingTransition != null ? mCollectingTransition.mType : TRANSIT_NONE;
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index 24493e2541e1..14737d44fe22 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -196,8 +196,11 @@ class WallpaperController {
"Win " + w + ": token animating, looking behind.");
}
mFindResults.setIsWallpaperTargetForLetterbox(w.hasWallpaperForLetterboxBackground());
- // Found a target! End search.
- return true;
+ // While the keyguard is going away, both notification shade and a normal activity such
+ // as a launcher can satisfy criteria for a wallpaper target. In this case, we should
+ // chose the normal activity, otherwise wallpaper becomes invisible when a new animation
+ // starts before the keyguard going away animation finishes.
+ return w.mActivityRecord != null;
}
return false;
};
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 5b1021eefc0a..709f885db776 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -200,6 +200,7 @@ import android.os.PowerManager.ServiceType;
import android.os.PowerManagerInternal;
import android.os.PowerSaveState;
import android.os.RemoteCallback;
+import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ServiceManager;
@@ -288,6 +289,7 @@ import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.IResultReceiver;
import com.android.internal.policy.IKeyguardDismissCallback;
+import com.android.internal.policy.IKeyguardLockedStateListener;
import com.android.internal.policy.IShortcutService;
import com.android.internal.policy.KeyInterceptionInfo;
import com.android.internal.protolog.ProtoLogImpl;
@@ -460,6 +462,10 @@ public class WindowManagerService extends IWindowManager.Stub
final private KeyguardDisableHandler mKeyguardDisableHandler;
+ private final RemoteCallbackList<IKeyguardLockedStateListener> mKeyguardLockedStateListeners =
+ new RemoteCallbackList<>();
+ private boolean mDispatchedKeyguardLockedState = false;
+
// VR Vr2d Display Id.
int mVr2dDisplayId = INVALID_DISPLAY;
boolean mVrModeEnabled = false;
@@ -3029,6 +3035,7 @@ public class WindowManagerService extends IWindowManager.Stub
@Override
public void onKeyguardShowingAndNotOccludedChanged() {
mH.sendEmptyMessage(H.RECOMPUTE_FOCUS);
+ dispatchKeyguardLockedStateState();
}
@Override
@@ -3218,6 +3225,50 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
+ @RequiresPermission(Manifest.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE)
+ @Override
+ public void addKeyguardLockedStateListener(IKeyguardLockedStateListener listener) {
+ enforceSubscribeToKeyguardLockedStatePermission();
+ boolean registered = mKeyguardLockedStateListeners.register(listener);
+ if (!registered) {
+ Slog.w(TAG, "Failed to register listener: " + listener);
+ }
+ }
+
+ @RequiresPermission(Manifest.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE)
+ @Override
+ public void removeKeyguardLockedStateListener(IKeyguardLockedStateListener listener) {
+ enforceSubscribeToKeyguardLockedStatePermission();
+ mKeyguardLockedStateListeners.unregister(listener);
+ }
+
+ private void enforceSubscribeToKeyguardLockedStatePermission() {
+ mContext.enforceCallingOrSelfPermission(
+ Manifest.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE,
+ Manifest.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE
+ + " permission required to read keyguard visibility");
+ }
+
+ private void dispatchKeyguardLockedStateState() {
+ mH.post(() -> {
+ final boolean isKeyguardLocked = mPolicy.isKeyguardShowing();
+ if (mDispatchedKeyguardLockedState == isKeyguardLocked) {
+ return;
+ }
+ final int n = mKeyguardLockedStateListeners.beginBroadcast();
+ for (int i = 0; i < n; i++) {
+ try {
+ mKeyguardLockedStateListeners.getBroadcastItem(i).onKeyguardLockedStateChanged(
+ isKeyguardLocked);
+ } catch (RemoteException e) {
+ // Handled by the RemoteCallbackList.
+ }
+ }
+ mKeyguardLockedStateListeners.finishBroadcast();
+ mDispatchedKeyguardLockedState = isKeyguardLocked;
+ });
+ }
+
@Override
public void setSwitchingUser(boolean switching) {
if (!checkCallingPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL,
diff --git a/services/core/java/com/android/server/wm/WindowOrientationListener.java b/services/core/java/com/android/server/wm/WindowOrientationListener.java
index a967ea8fbf8c..de87ab9dcce0 100644
--- a/services/core/java/com/android/server/wm/WindowOrientationListener.java
+++ b/services/core/java/com/android/server/wm/WindowOrientationListener.java
@@ -1167,6 +1167,10 @@ public abstract class WindowOrientationListener {
if (mRotationResolverService == null) {
mRotationResolverService = LocalServices.getService(
RotationResolverInternal.class);
+ if (mRotationResolverService == null) {
+ finalizeRotation(reportedRotation);
+ return;
+ }
}
String packageName = null;
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index ccaa03ae8fd0..a2e8813c998a 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -17,6 +17,7 @@
package com.android.server.wm;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
@@ -645,8 +646,10 @@ class WindowToken extends WindowContainer<WindowState> {
final ActivityRecord r = asActivityRecord();
if (r != null) {
final Task rootTask = r.getRootTask();
- // Don't transform the activity in PiP because the PiP task organizer will handle it.
- if (rootTask != null && rootTask.inPinnedWindowingMode()) {
+ // Don't transform the activity exiting PiP because the PiP task organizer will handle
+ // it.
+ if (rootTask != null && mTransitionController.getWindowingModeAtStart(rootTask)
+ == WINDOWING_MODE_PINNED) {
return;
}
}
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index a9c6b8d67cac..11714dc821af 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -50,6 +50,7 @@
#include "android_runtime/Log.h"
#include "gnss/AGnss.h"
#include "gnss/AGnssRil.h"
+#include "gnss/Gnss.h"
#include "gnss/GnssAntennaInfo.h"
#include "gnss/GnssAntennaInfoCallback.h"
#include "gnss/GnssBatching.h"
@@ -68,18 +69,7 @@
static jclass class_gnssPowerStats;
-static jmethodID method_reportLocation;
-static jmethodID method_reportStatus;
-static jmethodID method_reportSvStatus;
-static jmethodID method_reportNmea;
-static jmethodID method_setTopHalCapabilities;
-static jmethodID method_setGnssYearOfHardware;
-static jmethodID method_setGnssHardwareModelName;
-static jmethodID method_psdsDownloadRequest;
static jmethodID method_reportNiNotification;
-static jmethodID method_requestLocation;
-static jmethodID method_requestUtcTime;
-static jmethodID method_reportGnssServiceDied;
static jmethodID method_reportGnssPowerStats;
static jmethodID method_reportNfwNotification;
static jmethodID method_isInEmergencySession;
@@ -92,7 +82,6 @@ using android::status_t;
using android::String16;
using android::wp;
using android::binder::Status;
-using android::gnss::GnssConfigurationInterface;
using android::hardware::Return;
using android::hardware::Void;
@@ -128,12 +117,8 @@ using android::hardware::gnss::GnssConstellationType;
using android::hardware::gnss::GnssPowerStats;
using android::hardware::gnss::IGnssPowerIndication;
using android::hardware::gnss::IGnssPowerIndicationCallback;
-using android::hardware::gnss::PsdsType;
-using IAGnssAidl = android::hardware::gnss::IAGnss;
-using IAGnssRilAidl = android::hardware::gnss::IAGnssRil;
using IGnssAidl = android::hardware::gnss::IGnss;
-using IGnssCallbackAidl = android::hardware::gnss::IGnssCallback;
using IGnssBatchingAidl = android::hardware::gnss::IGnssBatching;
using IGnssDebugAidl = android::hardware::gnss::IGnssDebug;
using IGnssPsdsAidl = android::hardware::gnss::IGnssPsds;
@@ -142,575 +127,30 @@ using IGnssConfigurationAidl = android::hardware::gnss::IGnssConfiguration;
using GnssLocationAidl = android::hardware::gnss::GnssLocation;
using IGnssAntennaInfoAidl = android::hardware::gnss::IGnssAntennaInfo;
-struct GnssDeathRecipient : virtual public hidl_death_recipient
-{
- // hidl_death_recipient interface
- virtual void serviceDied(uint64_t cookie, const wp<IBase>& who) override {
- ALOGE("IGNSS hidl service failed, trying to recover...");
-
- JNIEnv* env = android::AndroidRuntime::getJNIEnv();
- env->CallVoidMethod(android::mCallbacksObj, method_reportGnssServiceDied);
- }
-};
-
-// Must match the value from GnssMeasurement.java
-static const uint32_t SVID_FLAGS_HAS_BASEBAND_CN0 = (1<<4);
-
-sp<GnssDeathRecipient> gnssHalDeathRecipient = nullptr;
-sp<IGnss_V1_0> gnssHal = nullptr;
-sp<IGnss_V1_1> gnssHal_V1_1 = nullptr;
-sp<IGnss_V2_0> gnssHal_V2_0 = nullptr;
-sp<IGnss_V2_1> gnssHal_V2_1 = nullptr;
-sp<IGnssAidl> gnssHalAidl = nullptr;
-sp<IGnssBatchingAidl> gnssBatchingAidlIface = nullptr;
-sp<IGnssPsdsAidl> gnssPsdsAidlIface = nullptr;
-sp<IGnssXtra> gnssXtraIface = nullptr;
sp<IGnssNi> gnssNiIface = nullptr;
sp<IGnssPowerIndication> gnssPowerIndicationIface = nullptr;
-std::unique_ptr<GnssConfigurationInterface> gnssConfigurationIface = nullptr;
+std::unique_ptr<android::gnss::GnssHal> gnssHal = nullptr;
+std::unique_ptr<android::gnss::AGnssInterface> agnssIface = nullptr;
+std::unique_ptr<android::gnss::AGnssRilInterface> agnssRilIface = nullptr;
+std::unique_ptr<android::gnss::GnssAntennaInfoInterface> gnssAntennaInfoIface = nullptr;
+std::unique_ptr<android::gnss::GnssConfigurationInterface> gnssConfigurationIface = nullptr;
std::unique_ptr<android::gnss::GnssMeasurementInterface> gnssMeasurementIface = nullptr;
std::unique_ptr<android::gnss::GnssNavigationMessageInterface> gnssNavigationMessageIface = nullptr;
std::unique_ptr<android::gnss::GnssBatchingInterface> gnssBatchingIface = nullptr;
-std::unique_ptr<android::gnss::GnssGeofenceInterface> gnssGeofencingIface = nullptr;
-std::unique_ptr<android::gnss::AGnssInterface> agnssIface = nullptr;
std::unique_ptr<android::gnss::GnssDebugInterface> gnssDebugIface = nullptr;
-std::unique_ptr<android::gnss::AGnssRilInterface> agnssRilIface = nullptr;
+std::unique_ptr<android::gnss::GnssGeofenceInterface> gnssGeofencingIface = nullptr;
+std::unique_ptr<android::gnss::GnssPsdsInterface> gnssPsdsIface = nullptr;
std::unique_ptr<android::gnss::GnssVisibilityControlInterface> gnssVisibilityControlIface = nullptr;
-std::unique_ptr<android::gnss::GnssAntennaInfoInterface> gnssAntennaInfoIface = nullptr;
std::unique_ptr<android::gnss::MeasurementCorrectionsInterface> gnssMeasurementCorrectionsIface =
nullptr;
-#define WAKE_LOCK_NAME "GPS"
-
namespace android {
namespace {
-// Returns true if location has lat/long information.
-bool hasLatLong(const GnssLocationAidl& location) {
- return (location.gnssLocationFlags & GnssLocationAidl::HAS_LAT_LONG) != 0;
-}
-
-// Returns true if location has lat/long information.
-bool hasLatLong(const GnssLocation_V1_0& location) {
- return (static_cast<uint32_t>(location.gnssLocationFlags) &
- GnssLocationFlags::HAS_LAT_LONG) != 0;
-}
-
-// Returns true if location has lat/long information.
-bool hasLatLong(const GnssLocation_V2_0& location) {
- return hasLatLong(location.v1_0);
-}
-
-bool isSvStatusRegistered = false;
-bool isNmeaRegistered = false;
-
} // namespace
-static inline jboolean boolToJbool(bool value) {
- return value ? JNI_TRUE : JNI_FALSE;
-}
-
-static GnssLocationAidl createGnssLocation(jint gnssLocationFlags, jdouble latitudeDegrees,
- jdouble longitudeDegrees, jdouble altitudeMeters,
- jfloat speedMetersPerSec, jfloat bearingDegrees,
- jfloat horizontalAccuracyMeters,
- jfloat verticalAccuracyMeters,
- jfloat speedAccuracyMetersPerSecond,
- jfloat bearingAccuracyDegrees, jlong timestamp,
- jint elapsedRealtimeFlags, jlong elapsedRealtimeNanos,
- jdouble elapsedRealtimeUncertaintyNanos) {
- GnssLocationAidl location;
- location.gnssLocationFlags = static_cast<int>(gnssLocationFlags);
- location.latitudeDegrees = static_cast<double>(latitudeDegrees);
- location.longitudeDegrees = static_cast<double>(longitudeDegrees);
- location.altitudeMeters = static_cast<double>(altitudeMeters);
- location.speedMetersPerSec = static_cast<double>(speedMetersPerSec);
- location.bearingDegrees = static_cast<double>(bearingDegrees);
- location.horizontalAccuracyMeters = static_cast<double>(horizontalAccuracyMeters);
- location.verticalAccuracyMeters = static_cast<double>(verticalAccuracyMeters);
- location.speedAccuracyMetersPerSecond = static_cast<double>(speedAccuracyMetersPerSecond);
- location.bearingAccuracyDegrees = static_cast<double>(bearingAccuracyDegrees);
- location.timestampMillis = static_cast<uint64_t>(timestamp);
-
- location.elapsedRealtime.flags = static_cast<int>(elapsedRealtimeFlags);
- location.elapsedRealtime.timestampNs = static_cast<uint64_t>(elapsedRealtimeNanos);
- location.elapsedRealtime.timeUncertaintyNs =
- static_cast<double>(elapsedRealtimeUncertaintyNanos);
-
- return location;
-}
-
-static GnssLocation_V1_0 createGnssLocation_V1_0(
- jint gnssLocationFlags, jdouble latitudeDegrees, jdouble longitudeDegrees,
- jdouble altitudeMeters, jfloat speedMetersPerSec, jfloat bearingDegrees,
- jfloat horizontalAccuracyMeters, jfloat verticalAccuracyMeters,
- jfloat speedAccuracyMetersPerSecond, jfloat bearingAccuracyDegrees,
- jlong timestamp) {
- GnssLocation_V1_0 location;
- location.gnssLocationFlags = static_cast<uint16_t>(gnssLocationFlags);
- location.latitudeDegrees = static_cast<double>(latitudeDegrees);
- location.longitudeDegrees = static_cast<double>(longitudeDegrees);
- location.altitudeMeters = static_cast<double>(altitudeMeters);
- location.speedMetersPerSec = static_cast<float>(speedMetersPerSec);
- location.bearingDegrees = static_cast<float>(bearingDegrees);
- location.horizontalAccuracyMeters = static_cast<float>(horizontalAccuracyMeters);
- location.verticalAccuracyMeters = static_cast<float>(verticalAccuracyMeters);
- location.speedAccuracyMetersPerSecond = static_cast<float>(speedAccuracyMetersPerSecond);
- location.bearingAccuracyDegrees = static_cast<float>(bearingAccuracyDegrees);
- location.timestamp = static_cast<uint64_t>(timestamp);
-
- return location;
-}
-
-static GnssLocation_V2_0 createGnssLocation_V2_0(
- jint gnssLocationFlags, jdouble latitudeDegrees, jdouble longitudeDegrees,
- jdouble altitudeMeters, jfloat speedMetersPerSec, jfloat bearingDegrees,
- jfloat horizontalAccuracyMeters, jfloat verticalAccuracyMeters,
- jfloat speedAccuracyMetersPerSecond, jfloat bearingAccuracyDegrees,
- jlong timestamp, jint elapsedRealtimeFlags, jlong elapsedRealtimeNanos,
- jdouble elapsedRealtimeUncertaintyNanos) {
- GnssLocation_V2_0 location;
- location.v1_0 = createGnssLocation_V1_0(
- gnssLocationFlags, latitudeDegrees, longitudeDegrees, altitudeMeters,
- speedMetersPerSec, bearingDegrees, horizontalAccuracyMeters,
- verticalAccuracyMeters, speedAccuracyMetersPerSecond,
- bearingAccuracyDegrees, timestamp);
-
- location.elapsedRealtime.flags = static_cast<uint16_t>(elapsedRealtimeFlags);
- location.elapsedRealtime.timestampNs = static_cast<uint64_t>(elapsedRealtimeNanos);
- location.elapsedRealtime.timeUncertaintyNs = static_cast<uint64_t>(elapsedRealtimeUncertaintyNanos);
-
- return location;
-}
-
-/*
- * GnssCallback class implements the callback methods for IGnss interface.
- */
-struct GnssCallback : public IGnssCallback_V2_1 {
- Return<void> gnssLocationCb(const GnssLocation_V1_0& location) override;
- Return<void> gnssStatusCb(const IGnssCallback_V1_0::GnssStatusValue status) override;
- Return<void> gnssSvStatusCb(const IGnssCallback_V1_0::GnssSvStatus& svStatus) override {
- return gnssSvStatusCbImpl<IGnssCallback_V1_0::GnssSvStatus, IGnssCallback_V1_0::GnssSvInfo>(
- svStatus);
- }
- Return<void> gnssNmeaCb(int64_t timestamp, const android::hardware::hidl_string& nmea) override;
- Return<void> gnssSetCapabilitesCb(uint32_t capabilities) override;
- Return<void> gnssAcquireWakelockCb() override;
- Return<void> gnssReleaseWakelockCb() override;
- Return<void> gnssRequestTimeCb() override;
- Return<void> gnssRequestLocationCb(const bool independentFromGnss) override;
-
- Return<void> gnssSetSystemInfoCb(const IGnssCallback_V1_0::GnssSystemInfo& info) override;
-
- // New in 1.1
- Return<void> gnssNameCb(const android::hardware::hidl_string& name) override;
-
- // New in 2.0
- Return<void> gnssRequestLocationCb_2_0(const bool independentFromGnss, const bool isUserEmergency)
- override;
- Return<void> gnssSetCapabilitiesCb_2_0(uint32_t capabilities) override;
- Return<void> gnssLocationCb_2_0(const GnssLocation_V2_0& location) override;
- Return<void> gnssSvStatusCb_2_0(const hidl_vec<IGnssCallback_V2_0::GnssSvInfo>& svInfoList) override {
- return gnssSvStatusCbImpl<hidl_vec<IGnssCallback_V2_0::GnssSvInfo>,
- IGnssCallback_V1_0::GnssSvInfo>(svInfoList);
- }
-
- // New in 2.1
- Return<void> gnssSvStatusCb_2_1(const hidl_vec<IGnssCallback_V2_1::GnssSvInfo>& svInfoList) override {
- return gnssSvStatusCbImpl<hidl_vec<IGnssCallback_V2_1::GnssSvInfo>,
- IGnssCallback_V1_0::GnssSvInfo>(svInfoList);
- }
- Return<void> gnssSetCapabilitiesCb_2_1(uint32_t capabilities) override;
-
- // TODO: Reconsider allocation cost vs threadsafety on these statics
- static const char* sNmeaString;
- static size_t sNmeaStringLength;
-
- template <class T>
- static Return<void> gnssLocationCbImpl(const T& location);
-
- template <class T_list, class T_sv_info>
- static Return<void> gnssSvStatusCbImpl(const T_list& svStatus);
-
-private:
- template <class T>
- static uint32_t getHasBasebandCn0DbHzFlag(const T& svStatus) {
- return 0;
- }
-
- template <class T>
- static double getBasebandCn0DbHz(const T& svStatus, size_t i) {
- return 0.0;
- }
-
- template <class T>
- static uint32_t getGnssSvInfoListSize(const T& svInfoList) {
- return svInfoList.size();
- }
-
- static const IGnssCallbackAidl::GnssSvInfo& getGnssSvInfoOfIndex(
- const std::vector<IGnssCallbackAidl::GnssSvInfo>& svInfoList, size_t i) {
- return svInfoList[i];
- }
-
- static const IGnssCallback_V1_0::GnssSvInfo& getGnssSvInfoOfIndex(
- const IGnssCallback_V1_0::GnssSvStatus& svStatus, size_t i) {
- return svStatus.gnssSvList.data()[i];
- }
-
- static const IGnssCallback_V1_0::GnssSvInfo& getGnssSvInfoOfIndex(
- const hidl_vec<IGnssCallback_V2_0::GnssSvInfo>& svInfoList, size_t i) {
- return svInfoList[i].v1_0;
- }
-
- static const IGnssCallback_V1_0::GnssSvInfo& getGnssSvInfoOfIndex(
- const hidl_vec<IGnssCallback_V2_1::GnssSvInfo>& svInfoList, size_t i) {
- return svInfoList[i].v2_0.v1_0;
- }
-
- template <class T>
- static uint32_t getConstellationType(const T& svInfoList, size_t i) {
- return static_cast<uint32_t>(svInfoList[i].constellation);
- }
-};
-
-Return<void> GnssCallback::gnssNameCb(const android::hardware::hidl_string& name) {
- ALOGD("%s: name=%s\n", __func__, name.c_str());
-
- JNIEnv* env = getJniEnv();
- jstring jstringName = env->NewStringUTF(name.c_str());
- env->CallVoidMethod(mCallbacksObj, method_setGnssHardwareModelName, jstringName);
- if (jstringName) {
- env->DeleteLocalRef(jstringName);
- }
- checkAndClearExceptionFromCallback(env, __FUNCTION__);
-
- return Void();
-}
-
-const char* GnssCallback::sNmeaString = nullptr;
-size_t GnssCallback::sNmeaStringLength = 0;
-
-template<class T>
-Return<void> GnssCallback::gnssLocationCbImpl(const T& location) {
- JNIEnv* env = getJniEnv();
-
- jobject jLocation = translateGnssLocation(env, location);
-
- env->CallVoidMethod(mCallbacksObj,
- method_reportLocation,
- boolToJbool(hasLatLong(location)),
- jLocation);
- checkAndClearExceptionFromCallback(env, __FUNCTION__);
- env->DeleteLocalRef(jLocation);
- return Void();
-}
-
-Return<void> GnssCallback::gnssLocationCb(const GnssLocation_V1_0& location) {
- return gnssLocationCbImpl<GnssLocation_V1_0>(location);
-}
-
-Return<void>
-GnssCallback::gnssLocationCb_2_0(const GnssLocation_V2_0& location) {
- return gnssLocationCbImpl<GnssLocation_V2_0>(location);
-}
-
-Return<void> GnssCallback::gnssStatusCb(const IGnssCallback_V2_0::GnssStatusValue status) {
- JNIEnv* env = getJniEnv();
- env->CallVoidMethod(mCallbacksObj, method_reportStatus, status);
- checkAndClearExceptionFromCallback(env, __FUNCTION__);
- return Void();
-}
-
-template<>
-uint32_t GnssCallback::getHasBasebandCn0DbHzFlag(const hidl_vec<IGnssCallback_V2_1::GnssSvInfo>&
- svStatus) {
- return SVID_FLAGS_HAS_BASEBAND_CN0;
-}
-
-template <>
-uint32_t GnssCallback::getHasBasebandCn0DbHzFlag(
- const std::vector<IGnssCallbackAidl::GnssSvInfo>& svStatus) {
- return SVID_FLAGS_HAS_BASEBAND_CN0;
-}
-
-template <>
-double GnssCallback::getBasebandCn0DbHz(
- const std::vector<IGnssCallbackAidl::GnssSvInfo>& svInfoList, size_t i) {
- return svInfoList[i].basebandCN0DbHz;
-}
-
-template<>
-double GnssCallback::getBasebandCn0DbHz(const hidl_vec<IGnssCallback_V2_1::GnssSvInfo>& svInfoList,
- size_t i) {
- return svInfoList[i].basebandCN0DbHz;
-}
-
-template <>
-uint32_t GnssCallback::getGnssSvInfoListSize(const IGnssCallback_V1_0::GnssSvStatus& svStatus) {
- return svStatus.numSvs;
-}
-
-template <>
-uint32_t GnssCallback::getConstellationType(const IGnssCallback_V1_0::GnssSvStatus& svStatus,
- size_t i) {
- return static_cast<uint32_t>(svStatus.gnssSvList.data()[i].constellation);
-}
-
-template <>
-uint32_t GnssCallback::getConstellationType(
- const hidl_vec<IGnssCallback_V2_1::GnssSvInfo>& svInfoList, size_t i) {
- return static_cast<uint32_t>(svInfoList[i].v2_0.constellation);
-}
-
-template <class T_list, class T_sv_info>
-Return<void> GnssCallback::gnssSvStatusCbImpl(const T_list& svStatus) {
- // In HIDL or AIDL v1, if no listener is registered, do not report svInfoList to the framework.
- if (gnssHalAidl == nullptr || gnssHalAidl->getInterfaceVersion() <= 1) {
- if (!isSvStatusRegistered) {
- return Void();
- }
- }
-
- JNIEnv* env = getJniEnv();
-
- uint32_t listSize = getGnssSvInfoListSize(svStatus);
-
- jintArray svidWithFlagArray = env->NewIntArray(listSize);
- jfloatArray cn0Array = env->NewFloatArray(listSize);
- jfloatArray elevArray = env->NewFloatArray(listSize);
- jfloatArray azimArray = env->NewFloatArray(listSize);
- jfloatArray carrierFreqArray = env->NewFloatArray(listSize);
- jfloatArray basebandCn0Array = env->NewFloatArray(listSize);
-
- jint* svidWithFlags = env->GetIntArrayElements(svidWithFlagArray, 0);
- jfloat* cn0s = env->GetFloatArrayElements(cn0Array, 0);
- jfloat* elev = env->GetFloatArrayElements(elevArray, 0);
- jfloat* azim = env->GetFloatArrayElements(azimArray, 0);
- jfloat* carrierFreq = env->GetFloatArrayElements(carrierFreqArray, 0);
- jfloat* basebandCn0s = env->GetFloatArrayElements(basebandCn0Array, 0);
-
- /*
- * Read GNSS SV info.
- */
- for (size_t i = 0; i < listSize; ++i) {
- enum ShiftWidth: uint8_t {
- SVID_SHIFT_WIDTH = 12,
- CONSTELLATION_TYPE_SHIFT_WIDTH = 8
- };
-
- const T_sv_info& info = getGnssSvInfoOfIndex(svStatus, i);
- svidWithFlags[i] = (info.svid << SVID_SHIFT_WIDTH) |
- (getConstellationType(svStatus, i) << CONSTELLATION_TYPE_SHIFT_WIDTH) |
- static_cast<uint32_t>(info.svFlag);
- cn0s[i] = info.cN0Dbhz;
- elev[i] = info.elevationDegrees;
- azim[i] = info.azimuthDegrees;
- carrierFreq[i] = info.carrierFrequencyHz;
- svidWithFlags[i] |= getHasBasebandCn0DbHzFlag(svStatus);
- basebandCn0s[i] = getBasebandCn0DbHz(svStatus, i);
- }
-
- env->ReleaseIntArrayElements(svidWithFlagArray, svidWithFlags, 0);
- env->ReleaseFloatArrayElements(cn0Array, cn0s, 0);
- env->ReleaseFloatArrayElements(elevArray, elev, 0);
- env->ReleaseFloatArrayElements(azimArray, azim, 0);
- env->ReleaseFloatArrayElements(carrierFreqArray, carrierFreq, 0);
- env->ReleaseFloatArrayElements(basebandCn0Array, basebandCn0s, 0);
-
- env->CallVoidMethod(mCallbacksObj, method_reportSvStatus,
- static_cast<jint>(listSize), svidWithFlagArray, cn0Array, elevArray, azimArray,
- carrierFreqArray, basebandCn0Array);
-
- env->DeleteLocalRef(svidWithFlagArray);
- env->DeleteLocalRef(cn0Array);
- env->DeleteLocalRef(elevArray);
- env->DeleteLocalRef(azimArray);
- env->DeleteLocalRef(carrierFreqArray);
- env->DeleteLocalRef(basebandCn0Array);
-
- checkAndClearExceptionFromCallback(env, __FUNCTION__);
- return Void();
-}
-
-Return<void> GnssCallback::gnssNmeaCb(int64_t timestamp,
- const ::android::hardware::hidl_string& nmea) {
- // In HIDL, if no listener is registered, do not report nmea to the framework.
- if (!isNmeaRegistered) {
- return Void();
- }
- JNIEnv* env = getJniEnv();
- /*
- * The Java code will call back to read these values.
- * We do this to avoid creating unnecessary String objects.
- */
- sNmeaString = nmea.c_str();
- sNmeaStringLength = nmea.size();
-
- env->CallVoidMethod(mCallbacksObj, method_reportNmea, timestamp);
- checkAndClearExceptionFromCallback(env, __FUNCTION__);
- return Void();
-}
-
-Return<void> GnssCallback::gnssSetCapabilitesCb(uint32_t capabilities) {
- ALOGD("%s: %du\n", __func__, capabilities);
-
- JNIEnv* env = getJniEnv();
- env->CallVoidMethod(mCallbacksObj, method_setTopHalCapabilities, capabilities);
- checkAndClearExceptionFromCallback(env, __FUNCTION__);
- return Void();
-}
-
-Return<void> GnssCallback::gnssSetCapabilitiesCb_2_0(uint32_t capabilities) {
- return GnssCallback::gnssSetCapabilitesCb(capabilities);
-}
-
-Return<void> GnssCallback::gnssSetCapabilitiesCb_2_1(uint32_t capabilities) {
- return GnssCallback::gnssSetCapabilitesCb(capabilities);
-}
-
-Return<void> GnssCallback::gnssAcquireWakelockCb() {
- acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_NAME);
- return Void();
-}
-
-Return<void> GnssCallback::gnssReleaseWakelockCb() {
- release_wake_lock(WAKE_LOCK_NAME);
- return Void();
-}
-
-Return<void> GnssCallback::gnssRequestTimeCb() {
- JNIEnv* env = getJniEnv();
- env->CallVoidMethod(mCallbacksObj, method_requestUtcTime);
- checkAndClearExceptionFromCallback(env, __FUNCTION__);
- return Void();
-}
-
-Return<void> GnssCallback::gnssRequestLocationCb(const bool independentFromGnss) {
- return GnssCallback::gnssRequestLocationCb_2_0(independentFromGnss, /* isUserEmergency= */
- false);
-}
-
-Return<void> GnssCallback::gnssRequestLocationCb_2_0(const bool independentFromGnss, const bool
- isUserEmergency) {
- JNIEnv* env = getJniEnv();
- env->CallVoidMethod(mCallbacksObj, method_requestLocation, boolToJbool(independentFromGnss),
- boolToJbool(isUserEmergency));
- checkAndClearExceptionFromCallback(env, __FUNCTION__);
- return Void();
-}
-
-Return<void> GnssCallback::gnssSetSystemInfoCb(const IGnssCallback_V2_0::GnssSystemInfo& info) {
- ALOGD("%s: yearOfHw=%d\n", __func__, info.yearOfHw);
-
- JNIEnv* env = getJniEnv();
- env->CallVoidMethod(mCallbacksObj, method_setGnssYearOfHardware,
- info.yearOfHw);
- checkAndClearExceptionFromCallback(env, __FUNCTION__);
- return Void();
-}
-
-class GnssCallbackAidl : public android::hardware::gnss::BnGnssCallback {
-public:
- Status gnssSetCapabilitiesCb(const int capabilities) override;
- Status gnssStatusCb(const GnssStatusValue status) override;
- Status gnssSvStatusCb(const std::vector<GnssSvInfo>& svInfoList) override;
- Status gnssLocationCb(const GnssLocationAidl& location) override;
- Status gnssNmeaCb(const int64_t timestamp, const std::string& nmea) override;
- Status gnssAcquireWakelockCb() override;
- Status gnssReleaseWakelockCb() override;
- Status gnssSetSystemInfoCb(const GnssSystemInfo& info) override;
- Status gnssRequestTimeCb() override;
- Status gnssRequestLocationCb(const bool independentFromGnss,
- const bool isUserEmergency) override;
-};
-
-Status GnssCallbackAidl::gnssSetCapabilitiesCb(const int capabilities) {
- ALOGD("GnssCallbackAidl::%s: %du\n", __func__, capabilities);
- JNIEnv* env = getJniEnv();
- env->CallVoidMethod(mCallbacksObj, method_setTopHalCapabilities, capabilities);
- checkAndClearExceptionFromCallback(env, __FUNCTION__);
- return Status::ok();
-}
-
-Status GnssCallbackAidl::gnssStatusCb(const GnssStatusValue status) {
- JNIEnv* env = getJniEnv();
- env->CallVoidMethod(mCallbacksObj, method_reportStatus, status);
- checkAndClearExceptionFromCallback(env, __FUNCTION__);
- return Status::ok();
-}
-
-Status GnssCallbackAidl::gnssSvStatusCb(const std::vector<GnssSvInfo>& svInfoList) {
- GnssCallback::gnssSvStatusCbImpl<std::vector<GnssSvInfo>, GnssSvInfo>(svInfoList);
- return Status::ok();
-}
-
-Status GnssCallbackAidl::gnssLocationCb(const GnssLocationAidl& location) {
- GnssCallback::gnssLocationCbImpl<GnssLocationAidl>(location);
- return Status::ok();
-}
-
-Status GnssCallbackAidl::gnssNmeaCb(const int64_t timestamp, const std::string& nmea) {
- // In AIDL v1, if no listener is registered, do not report nmea to the framework.
- if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() <= 1) {
- if (!isNmeaRegistered) {
- return Status::ok();
- }
- }
- JNIEnv* env = getJniEnv();
- /*
- * The Java code will call back to read these values.
- * We do this to avoid creating unnecessary String objects.
- */
- GnssCallback::sNmeaString = nmea.c_str();
- GnssCallback::sNmeaStringLength = nmea.size();
-
- env->CallVoidMethod(mCallbacksObj, method_reportNmea, timestamp);
- checkAndClearExceptionFromCallback(env, __FUNCTION__);
- return Status::ok();
-}
-
-Status GnssCallbackAidl::gnssAcquireWakelockCb() {
- acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_NAME);
- return Status::ok();
-}
-
-Status GnssCallbackAidl::gnssReleaseWakelockCb() {
- release_wake_lock(WAKE_LOCK_NAME);
- return Status::ok();
-}
-
-Status GnssCallbackAidl::gnssSetSystemInfoCb(const GnssSystemInfo& info) {
- ALOGD("%s: yearOfHw=%d, name=%s\n", __func__, info.yearOfHw, info.name.c_str());
- JNIEnv* env = getJniEnv();
- env->CallVoidMethod(mCallbacksObj, method_setGnssYearOfHardware, info.yearOfHw);
- jstring jstringName = env->NewStringUTF(info.name.c_str());
- env->CallVoidMethod(mCallbacksObj, method_setGnssHardwareModelName, jstringName);
- if (jstringName) {
- env->DeleteLocalRef(jstringName);
- }
- checkAndClearExceptionFromCallback(env, __FUNCTION__);
- return Status::ok();
-}
-
-Status GnssCallbackAidl::gnssRequestTimeCb() {
- JNIEnv* env = getJniEnv();
- env->CallVoidMethod(mCallbacksObj, method_requestUtcTime);
- checkAndClearExceptionFromCallback(env, __FUNCTION__);
- return Status::ok();
-}
-
-Status GnssCallbackAidl::gnssRequestLocationCb(const bool independentFromGnss,
- const bool isUserEmergency) {
- JNIEnv* env = getJniEnv();
- env->CallVoidMethod(mCallbacksObj, method_requestLocation, boolToJbool(independentFromGnss),
- boolToJbool(isUserEmergency));
- checkAndClearExceptionFromCallback(env, __FUNCTION__);
- return Status::ok();
-}
-
/*
* GnssPowerIndicationCallback class implements the callback methods for the IGnssPowerIndication
* interface.
@@ -756,35 +196,6 @@ Status GnssPowerIndicationCallback::gnssPowerStatsCb(const GnssPowerStats& data)
}
/*
- * GnssPsdsCallback class implements the callback methods for the IGnssPsds
- * interface.
- */
-struct GnssPsdsCallbackAidl : public android::hardware::gnss::BnGnssPsdsCallback {
- Status downloadRequestCb(PsdsType psdsType) override {
- ALOGD("%s. psdsType: %d", __func__, static_cast<int32_t>(psdsType));
- JNIEnv* env = getJniEnv();
- env->CallVoidMethod(mCallbacksObj, method_psdsDownloadRequest, psdsType);
- checkAndClearExceptionFromCallback(env, __FUNCTION__);
- return Status::ok();
- }
-};
-
-/**
- * GnssXtraCallback class implements the callback methods for the IGnssXtra
- * interface.
- */
-class GnssXtraCallback : public IGnssXtraCallback {
- Return<void> downloadRequestCb() override;
-};
-
-Return<void> GnssXtraCallback::downloadRequestCb() {
- JNIEnv* env = getJniEnv();
- env->CallVoidMethod(mCallbacksObj, method_psdsDownloadRequest, /* psdsType= */ 1);
- checkAndClearExceptionFromCallback(env, __FUNCTION__);
- return Void();
-}
-
-/*
* GnssNiCallback implements callback methods required by the IGnssNi interface.
*/
struct GnssNiCallback : public IGnssNiCallback {
@@ -822,41 +233,7 @@ Return<void> GnssNiCallback::niNotifyCb(
/* Initializes the GNSS service handle. */
static void android_location_gnss_hal_GnssNative_set_gps_service_handle() {
- gnssHalAidl = waitForVintfService<IGnssAidl>();
- if (gnssHalAidl != nullptr) {
- ALOGD("Successfully got GNSS AIDL handle. Version=%d.", gnssHalAidl->getInterfaceVersion());
- if (gnssHalAidl->getInterfaceVersion() >= 2) {
- return;
- }
- }
-
- ALOGD("Trying IGnss_V2_1::getService()");
- gnssHal_V2_1 = IGnss_V2_1::getService();
- if (gnssHal_V2_1 != nullptr) {
- gnssHal = gnssHal_V2_1;
- gnssHal_V2_0 = gnssHal_V2_1;
- gnssHal_V1_1 = gnssHal_V2_1;
- gnssHal = gnssHal_V2_1;
- return;
- }
-
- ALOGD("gnssHal 2.1 was null, trying 2.0");
- gnssHal_V2_0 = IGnss_V2_0::getService();
- if (gnssHal_V2_0 != nullptr) {
- gnssHal = gnssHal_V2_0;
- gnssHal_V1_1 = gnssHal_V2_0;
- return;
- }
-
- ALOGD("gnssHal 2.0 was null, trying 1.1");
- gnssHal_V1_1 = IGnss_V1_1::getService();
- if (gnssHal_V1_1 != nullptr) {
- gnssHal = gnssHal_V1_1;
- return;
- }
-
- ALOGD("gnssHal 1.1 was null, trying 1.0");
- gnssHal = IGnss_V1_0::getService();
+ gnssHal = std::make_unique<gnss::GnssHal>();
}
/* One time initialization at system boot */
@@ -865,21 +242,10 @@ static void android_location_gnss_hal_GnssNative_class_init_once(JNIEnv* env, jc
android_location_gnss_hal_GnssNative_set_gps_service_handle();
// Cache methodIDs and class IDs.
- method_reportLocation = env->GetMethodID(clazz, "reportLocation",
- "(ZLandroid/location/Location;)V");
- method_reportStatus = env->GetMethodID(clazz, "reportStatus", "(I)V");
- method_reportSvStatus = env->GetMethodID(clazz, "reportSvStatus", "(I[I[F[F[F[F[F)V");
- method_reportNmea = env->GetMethodID(clazz, "reportNmea", "(J)V");
- method_setTopHalCapabilities = env->GetMethodID(clazz, "setTopHalCapabilities", "(I)V");
- method_setGnssYearOfHardware = env->GetMethodID(clazz, "setGnssYearOfHardware", "(I)V");
- method_setGnssHardwareModelName = env->GetMethodID(clazz, "setGnssHardwareModelName",
- "(Ljava/lang/String;)V");
- method_psdsDownloadRequest = env->GetMethodID(clazz, "psdsDownloadRequest", "(I)V");
+
method_reportNiNotification = env->GetMethodID(clazz, "reportNiNotification",
"(IIIIILjava/lang/String;Ljava/lang/String;II)V");
- method_requestLocation = env->GetMethodID(clazz, "requestLocation", "(ZZ)V");
- method_requestUtcTime = env->GetMethodID(clazz, "requestUtcTime", "()V");
- method_reportGnssServiceDied = env->GetMethodID(clazz, "reportGnssServiceDied", "()V");
+
method_reportNfwNotification = env->GetMethodID(clazz, "reportNfwNotification",
"(Ljava/lang/String;BLjava/lang/String;BLjava/lang/String;BZZ)V");
method_reportGnssPowerStats =
@@ -894,17 +260,19 @@ static void android_location_gnss_hal_GnssNative_class_init_once(JNIEnv* env, jc
class_gnssPowerStats = (jclass)env->NewGlobalRef(gnssPowerStatsClass);
method_gnssPowerStatsCtor = env->GetMethodID(class_gnssPowerStats, "<init>", "(IJDDDDDD[D)V");
+ gnss::AGnss_class_init_once(env, clazz);
+ gnss::AGnssRil_class_init_once(env, clazz);
+ gnss::Gnss_class_init_once(env, clazz);
gnss::GnssAntennaInfo_class_init_once(env, clazz);
gnss::GnssBatching_class_init_once(env, clazz);
gnss::GnssConfiguration_class_init_once(env);
gnss::GnssGeofence_class_init_once(env, clazz);
gnss::GnssMeasurement_class_init_once(env, clazz);
gnss::GnssNavigationMessage_class_init_once(env, clazz);
+ gnss::GnssPsds_class_init_once(env, clazz);
gnss::GnssVisibilityControl_class_init_once(env, clazz);
gnss::MeasurementCorrections_class_init_once(env, clazz);
gnss::MeasurementCorrectionsCallback_class_init_once(env, clazz);
- gnss::AGnss_class_init_once(env, clazz);
- gnss::AGnssRil_class_init_once(env, clazz);
gnss::Utils_class_init_once(env);
}
@@ -923,313 +291,26 @@ static void android_location_gnss_hal_GnssNative_init_once(JNIEnv* env, jobject
android_location_gnss_hal_GnssNative_set_gps_service_handle();
}
- if (gnssHal == nullptr && gnssHalAidl == nullptr) {
+ if (gnssHal == nullptr || !gnssHal->isSupported()) {
ALOGE("Unable to get GPS service\n");
return;
}
- // TODO: linkToDeath for AIDL HAL
-
- if (gnssHal != nullptr) {
- gnssHalDeathRecipient = new GnssDeathRecipient();
- hardware::Return<bool> linked = gnssHal->linkToDeath(gnssHalDeathRecipient, /*cookie*/ 0);
- if (!linked.isOk()) {
- ALOGE("Transaction error in linking to GnssHAL death: %s",
- linked.description().c_str());
- } else if (!linked) {
- ALOGW("Unable to link to GnssHal death notifications");
- } else {
- ALOGD("Link to death notification successful");
- }
- }
-
- if (gnssHalAidl != nullptr) {
- sp<IGnssPsdsAidl> gnssPsdsAidl;
- auto status = gnssHalAidl->getExtensionPsds(&gnssPsdsAidl);
- if (status.isOk()) {
- gnssPsdsAidlIface = gnssPsdsAidl;
- } else {
- ALOGD("Unable to get a handle to PSDS AIDL interface.");
- }
- } else if (gnssHal != nullptr) {
- auto gnssXtra = gnssHal->getExtensionXtra();
- if (!gnssXtra.isOk()) {
- ALOGD("Unable to get a handle to Xtra");
- } else {
- gnssXtraIface = gnssXtra;
- }
- }
-
- if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
- sp<IAGnssRilAidl> agnssRilAidl;
- auto status = gnssHalAidl->getExtensionAGnssRil(&agnssRilAidl);
- if (checkAidlStatus(status, "Unable to get a handle to AGnssRil interface.")) {
- agnssRilIface = std::make_unique<gnss::AGnssRil>(agnssRilAidl);
- }
- } else if (gnssHal_V2_0 != nullptr) {
- auto agnssRil_V2_0 = gnssHal_V2_0->getExtensionAGnssRil_2_0();
- if (checkHidlReturn(agnssRil_V2_0, "Unable to get a handle to AGnssRil_V2_0")) {
- agnssRilIface = std::make_unique<gnss::AGnssRil_V2_0>(agnssRil_V2_0);
- }
- } else if (gnssHal != nullptr) {
- auto agnssRil_V1_0 = gnssHal->getExtensionAGnssRil();
- if (checkHidlReturn(agnssRil_V1_0, "Unable to get a handle to AGnssRil_V1_0")) {
- agnssRilIface = std::make_unique<gnss::AGnssRil_V1_0>(agnssRil_V1_0);
- }
- }
-
- if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
- sp<IAGnssAidl> agnssAidl;
- auto status = gnssHalAidl->getExtensionAGnss(&agnssAidl);
- if (checkAidlStatus(status, "Unable to get a handle to AGnss interface.")) {
- agnssIface = std::make_unique<gnss::AGnss>(agnssAidl);
- }
- } else if (gnssHal_V2_0 != nullptr) {
- auto agnss_V2_0 = gnssHal_V2_0->getExtensionAGnss_2_0();
- if (checkHidlReturn(agnss_V2_0, "Unable to get a handle to AGnss_V2_0")) {
- agnssIface = std::make_unique<gnss::AGnss_V2_0>(agnss_V2_0);
- }
- } else if (gnssHal != nullptr) {
- auto agnss_V1_0 = gnssHal->getExtensionAGnss();
- if (checkHidlReturn(agnss_V1_0, "Unable to get a handle to AGnss_V1_0")) {
- agnssIface = std::make_unique<gnss::AGnss_V1_0>(agnss_V1_0);
- }
- }
-
- if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
- sp<hardware::gnss::IGnssNavigationMessageInterface> gnssNavigationMessage;
- auto status = gnssHalAidl->getExtensionGnssNavigationMessage(&gnssNavigationMessage);
- if (checkAidlStatus(status,
- "Unable to get a handle to GnssNavigationMessage AIDL interface.")) {
- gnssNavigationMessageIface =
- std::make_unique<gnss::GnssNavigationMessageAidl>(gnssNavigationMessage);
- }
- } else if (gnssHal != nullptr) {
- auto gnssNavigationMessage = gnssHal->getExtensionGnssNavigationMessage();
- if (checkHidlReturn(gnssNavigationMessage,
- "Unable to get a handle to GnssNavigationMessage interface.")) {
- gnssNavigationMessageIface =
- std::make_unique<gnss::GnssNavigationMessageHidl>(gnssNavigationMessage);
- }
- }
-
- // Allow all causal combinations between IGnss.hal and IGnssMeasurement.hal. That means,
- // 2.1@IGnss can be paired with {1.0, 1,1, 2.0, 2.1}@IGnssMeasurement
- // 2.0@IGnss can be paired with {1.0, 1,1, 2.0}@IGnssMeasurement
- // 1.1@IGnss can be paired {1.0, 1.1}@IGnssMeasurement
- // 1.0@IGnss is paired with 1.0@IGnssMeasurement
- gnssMeasurementIface = nullptr;
- if (gnssHalAidl != nullptr) {
- sp<hardware::gnss::IGnssMeasurementInterface> gnssMeasurement;
- auto status = gnssHalAidl->getExtensionGnssMeasurement(&gnssMeasurement);
- if (checkAidlStatus(status, "Unable to get a handle to GnssMeasurement AIDL interface.")) {
- gnssMeasurementIface =
- std::make_unique<android::gnss::GnssMeasurement>(gnssMeasurement);
- }
- }
- if (gnssHal_V2_1 != nullptr && gnssMeasurementIface == nullptr) {
- auto gnssMeasurement = gnssHal_V2_1->getExtensionGnssMeasurement_2_1();
- if (checkHidlReturn(gnssMeasurement, "Unable to get a handle to GnssMeasurement_V2_1")) {
- gnssMeasurementIface =
- std::make_unique<android::gnss::GnssMeasurement_V2_1>(gnssMeasurement);
- }
- }
- if (gnssHal_V2_0 != nullptr && gnssMeasurementIface == nullptr) {
- auto gnssMeasurement = gnssHal_V2_0->getExtensionGnssMeasurement_2_0();
- if (checkHidlReturn(gnssMeasurement, "Unable to get a handle to GnssMeasurement_V2_0")) {
- gnssMeasurementIface =
- std::make_unique<android::gnss::GnssMeasurement_V2_0>(gnssMeasurement);
- }
- }
- if (gnssHal_V1_1 != nullptr && gnssMeasurementIface == nullptr) {
- auto gnssMeasurement = gnssHal_V1_1->getExtensionGnssMeasurement_1_1();
- if (checkHidlReturn(gnssMeasurement, "Unable to get a handle to GnssMeasurement_V1_1")) {
- gnssMeasurementIface =
- std::make_unique<android::gnss::GnssMeasurement_V1_1>(gnssMeasurement);
- }
- }
- if (gnssHal != nullptr && gnssMeasurementIface == nullptr) {
- auto gnssMeasurement = gnssHal->getExtensionGnssMeasurement();
- if (checkHidlReturn(gnssMeasurement, "Unable to get a handle to GnssMeasurement_V1_0")) {
- gnssMeasurementIface =
- std::make_unique<android::gnss::GnssMeasurement_V1_0>(gnssMeasurement);
- }
- }
-
- if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
- sp<IGnssAntennaInfoAidl> gnssAntennaInfoAidl;
- auto status = gnssHalAidl->getExtensionGnssAntennaInfo(&gnssAntennaInfoAidl);
- if (checkAidlStatus(status, "Unable to get a handle to GnssAntennaInfo interface.")) {
- gnssAntennaInfoIface = std::make_unique<gnss::GnssAntennaInfoAidl>(gnssAntennaInfoAidl);
- }
- } else if (gnssHal_V2_1 != nullptr) {
- auto gnssAntennaInfo_V2_1 = gnssHal_V2_1->getExtensionGnssAntennaInfo();
- if (checkHidlReturn(gnssAntennaInfo_V2_1,
- "Unable to get a handle to GnssAntennaInfo_V2_1")) {
- gnssAntennaInfoIface =
- std::make_unique<gnss::GnssAntennaInfo_V2_1>(gnssAntennaInfo_V2_1);
- }
- }
-
- if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
- sp<android::hardware::gnss::measurement_corrections::IMeasurementCorrectionsInterface>
- gnssMeasurementCorrectionsAidl;
- auto status =
- gnssHalAidl->getExtensionMeasurementCorrections(&gnssMeasurementCorrectionsAidl);
- if (checkAidlStatus(status,
- "Unable to get a handle to GnssVisibilityControl AIDL interface.")) {
- gnssMeasurementCorrectionsIface =
- std::make_unique<gnss::MeasurementCorrectionsIface_Aidl>(
- gnssMeasurementCorrectionsAidl);
- }
- }
- if (gnssHal_V2_1 != nullptr && gnssMeasurementCorrectionsIface == nullptr) {
- auto gnssCorrections = gnssHal_V2_1->getExtensionMeasurementCorrections_1_1();
- if (checkHidlReturn(gnssCorrections,
- "Unable to get a handle to GnssMeasurementCorrections HIDL "
- "interface")) {
- gnssMeasurementCorrectionsIface =
- std::make_unique<gnss::MeasurementCorrectionsIface_V1_1>(gnssCorrections);
- }
- }
- if (gnssHal_V2_0 != nullptr && gnssMeasurementCorrectionsIface == nullptr) {
- auto gnssCorrections = gnssHal_V2_0->getExtensionMeasurementCorrections();
- if (checkHidlReturn(gnssCorrections,
- "Unable to get a handle to GnssMeasurementCorrections HIDL "
- "interface")) {
- gnssMeasurementCorrectionsIface =
- std::make_unique<gnss::MeasurementCorrectionsIface_V1_0>(gnssCorrections);
- }
- }
-
- // Allow all causal combinations between IGnss.hal and IGnssDebug.hal. That means,
- // 2.0@IGnss can be paired with {1.0, 2.0}@IGnssDebug
- // 1.0@IGnss is paired with 1.0@IGnssDebug
-
- if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
- sp<IGnssDebugAidl> gnssDebugAidl;
- auto status = gnssHalAidl->getExtensionGnssDebug(&gnssDebugAidl);
- if (checkAidlStatus(status, "Unable to get a handle to GnssDebug interface.")) {
- gnssDebugIface = std::make_unique<gnss::GnssDebug>(gnssDebugAidl);
- }
- }
- if (gnssHal_V2_0 != nullptr && gnssDebugIface == nullptr) {
- auto gnssDebug_V2_0 = gnssHal_V2_0->getExtensionGnssDebug_2_0();
- if (checkHidlReturn(gnssDebug_V2_0, "Unable to get a handle to GnssDebug_V2_0.")) {
- gnssDebugIface = std::make_unique<gnss::GnssDebug_V2_0>(gnssDebug_V2_0);
- }
- }
- if (gnssHal != nullptr && gnssDebugIface == nullptr) {
- auto gnssDebug_V1_0 = gnssHal->getExtensionGnssDebug();
- if (checkHidlReturn(gnssDebug_V1_0, "Unable to get a handle to GnssDebug_V1_0.")) {
- gnssDebugIface = std::make_unique<gnss::GnssDebug_V1_0>(gnssDebug_V1_0);
- }
- }
-
- if (gnssHal != nullptr) {
- auto gnssNi = gnssHal->getExtensionGnssNi();
- if (!gnssNi.isOk()) {
- ALOGD("Unable to get a handle to GnssNi");
- } else {
- gnssNiIface = gnssNi;
- }
- }
-
- if (gnssHalAidl != nullptr) {
- sp<IGnssConfigurationAidl> gnssConfigurationAidl;
- auto status = gnssHalAidl->getExtensionGnssConfiguration(&gnssConfigurationAidl);
- if (checkAidlStatus(status,
- "Unable to get a handle to GnssConfiguration AIDL interface.")) {
- gnssConfigurationIface =
- std::make_unique<android::gnss::GnssConfiguration>(gnssConfigurationAidl);
- }
- } else if (gnssHal_V2_1 != nullptr) {
- auto gnssConfiguration = gnssHal_V2_1->getExtensionGnssConfiguration_2_1();
- if (checkHidlReturn(gnssConfiguration,
- "Unable to get a handle to GnssConfiguration_V2_1")) {
- gnssConfigurationIface =
- std::make_unique<android::gnss::GnssConfiguration_V2_1>(gnssConfiguration);
- }
- } else if (gnssHal_V2_0 != nullptr) {
- auto gnssConfiguration = gnssHal_V2_0->getExtensionGnssConfiguration_2_0();
- if (checkHidlReturn(gnssConfiguration,
- "Unable to get a handle to GnssConfiguration_V2_0")) {
- gnssConfigurationIface =
- std::make_unique<android::gnss::GnssConfiguration_V2_0>(gnssConfiguration);
- }
- } else if (gnssHal_V1_1 != nullptr) {
- auto gnssConfiguration = gnssHal_V1_1->getExtensionGnssConfiguration_1_1();
- if (checkHidlReturn(gnssConfiguration,
- "Unable to get a handle to GnssConfiguration_V1_1")) {
- gnssConfigurationIface =
- std::make_unique<gnss::GnssConfiguration_V1_1>(gnssConfiguration);
- }
- } else if (gnssHal != nullptr) {
- auto gnssConfiguration = gnssHal->getExtensionGnssConfiguration();
- if (checkHidlReturn(gnssConfiguration,
- "Unable to get a handle to GnssConfiguration_V1_0")) {
- gnssConfigurationIface =
- std::make_unique<gnss::GnssConfiguration_V1_0>(gnssConfiguration);
- }
- }
-
- if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
- sp<hardware::gnss::IGnssGeofence> gnssGeofence;
- auto status = gnssHalAidl->getExtensionGnssGeofence(&gnssGeofence);
- if (checkAidlStatus(status, "Unable to get a handle to GnssGeofence AIDL interface.")) {
- gnssGeofencingIface = std::make_unique<gnss::GnssGeofenceAidl>(gnssGeofence);
- }
- } else if (gnssHal != nullptr) {
- auto gnssGeofencing = gnssHal->getExtensionGnssGeofencing();
- if (checkHidlReturn(gnssGeofencing, "Unable to get a handle to GnssGeofencing")) {
- gnssGeofencingIface = std::make_unique<gnss::GnssGeofenceHidl>(gnssGeofencing);
- }
- }
-
- if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
- sp<android::hardware::gnss::IGnssBatching> gnssBatchingAidl;
- auto status = gnssHalAidl->getExtensionGnssBatching(&gnssBatchingAidl);
- if (checkAidlStatus(status, "Unable to get a handle to GnssBatching interface.")) {
- gnssBatchingIface = std::make_unique<gnss::GnssBatching>(gnssBatchingAidl);
- }
- } else if (gnssHal_V2_0 != nullptr) {
- auto gnssBatching_V2_0 = gnssHal_V2_0->getExtensionGnssBatching_2_0();
- if (checkHidlReturn(gnssBatching_V2_0, "Unable to get a handle to GnssBatching_V2_0")) {
- gnssBatchingIface = std::make_unique<gnss::GnssBatching_V2_0>(gnssBatching_V2_0);
- }
- }
- if (gnssHal != nullptr && gnssBatchingIface == nullptr) {
- auto gnssBatching_V1_0 = gnssHal->getExtensionGnssBatching();
- if (checkHidlReturn(gnssBatching_V1_0, "Unable to get a handle to GnssBatching")) {
- gnssBatchingIface = std::make_unique<gnss::GnssBatching_V1_0>(gnssBatching_V1_0);
- }
- }
-
- if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
- sp<android::hardware::gnss::visibility_control::IGnssVisibilityControl>
- gnssVisibilityControlAidl;
- auto status = gnssHalAidl->getExtensionGnssVisibilityControl(&gnssVisibilityControlAidl);
- if (checkAidlStatus(status,
- "Unable to get a handle to GnssVisibilityControl AIDL interface.")) {
- gnssVisibilityControlIface =
- std::make_unique<gnss::GnssVisibilityControlAidl>(gnssVisibilityControlAidl);
- }
- } else if (gnssHal_V2_0 != nullptr) {
- auto gnssVisibilityControlHidl = gnssHal_V2_0->getExtensionVisibilityControl();
- if (checkHidlReturn(gnssVisibilityControlHidl,
- "Unable to get a handle to GnssVisibilityControl HIDL interface")) {
- gnssVisibilityControlIface =
- std::make_unique<gnss::GnssVisibilityControlHidl>(gnssVisibilityControlHidl);
- }
- }
-
- if (gnssHalAidl != nullptr) {
- sp<IGnssPowerIndication> gnssPowerIndication;
- auto status = gnssHalAidl->getExtensionGnssPowerIndication(&gnssPowerIndication);
- if (checkAidlStatus(status, "Unable to get a handle to GnssPowerIndication interface.")) {
- gnssPowerIndicationIface = gnssPowerIndication;
- }
- }
+ gnssHal->linkToDeath();
+ gnssPsdsIface = gnssHal->getGnssPsdsInterface();
+ agnssRilIface = gnssHal->getAGnssRilInterface();
+ agnssIface = gnssHal->getAGnssInterface();
+ gnssNavigationMessageIface = gnssHal->getGnssNavigationMessageInterface();
+ gnssMeasurementIface = gnssHal->getGnssMeasurementInterface();
+ gnssAntennaInfoIface = gnssHal->getGnssAntennaInfoInterface();
+ gnssMeasurementCorrectionsIface = gnssHal->getMeasurementCorrectionsInterface();
+ gnssDebugIface = gnssHal->getGnssDebugInterface();
+ gnssNiIface = gnssHal->getGnssNiInterface();
+ gnssConfigurationIface = gnssHal->getGnssConfigurationInterface();
+ gnssGeofencingIface = gnssHal->getGnssGeofenceInterface();
+ gnssBatchingIface = gnssHal->getGnssBatchingInterface();
+ gnssVisibilityControlIface = gnssHal->getGnssVisibilityControlInterface();
+ gnssPowerIndicationIface = gnssHal->getGnssPowerIndicationInterface();
if (mCallbacksObj) {
ALOGE("Callbacks already initialized");
@@ -1239,7 +320,7 @@ static void android_location_gnss_hal_GnssNative_init_once(JNIEnv* env, jobject
}
static jboolean android_location_gnss_hal_GnssNative_is_supported(JNIEnv* /* env */, jclass) {
- return (gnssHalAidl != nullptr || gnssHal != nullptr) ? JNI_TRUE : JNI_FALSE;
+ return (gnssHal != nullptr && gnssHal->isSupported()) ? JNI_TRUE : JNI_FALSE;
}
static jboolean android_location_GnssNetworkConnectivityHandler_is_agps_ril_supported(
@@ -1268,52 +349,18 @@ static jboolean android_location_gnss_hal_GnssNative_init(JNIEnv* /* env */, jcl
/*
* Fail if the main interface fails to initialize
*/
- if (gnssHal == nullptr && gnssHalAidl == nullptr) {
+ if (!gnssHal->isSupported()) {
ALOGE("Unable to initialize GNSS HAL.");
return JNI_FALSE;
}
- // Set top level IGnss.hal callback.
- if (gnssHal != nullptr) {
- Return<bool> result = false;
- sp<IGnssCallback_V2_1> gnssCbIface = new GnssCallback();
- if (gnssHal_V2_1 != nullptr) {
- result = gnssHal_V2_1->setCallback_2_1(gnssCbIface);
- } else if (gnssHal_V2_0 != nullptr) {
- result = gnssHal_V2_0->setCallback_2_0(gnssCbIface);
- } else if (gnssHal_V1_1 != nullptr) {
- result = gnssHal_V1_1->setCallback_1_1(gnssCbIface);
- } else {
- result = gnssHal->setCallback(gnssCbIface);
- }
- if (!checkHidlReturn(result, "IGnss setCallback() failed.")) {
- return JNI_FALSE;
- }
- }
-
- if (gnssHalAidl != nullptr) {
- sp<IGnssCallbackAidl> gnssCbIfaceAidl = new GnssCallbackAidl();
- auto status = gnssHalAidl->setCallback(gnssCbIfaceAidl);
- if (!checkAidlStatus(status, "IGnssAidl setCallback() failed.")) {
- return JNI_FALSE;
- }
- }
+ // Set top level IGnss HAL callback.
+ gnssHal->setCallback();
- // Set IGnssPsds or IGnssXtra callback.
- if (gnssPsdsAidlIface != nullptr) {
- sp<IGnssPsdsCallbackAidl> gnssPsdsCallbackAidl = new GnssPsdsCallbackAidl();
- auto status = gnssPsdsAidlIface->setCallback(gnssPsdsCallbackAidl);
- if (!checkAidlStatus(status, "IGnssPsdsAidl setCallback() failed.")) {
- gnssPsdsAidlIface = nullptr;
- }
- } else if (gnssXtraIface != nullptr) {
- sp<IGnssXtraCallback> gnssXtraCbIface = new GnssXtraCallback();
- auto result = gnssXtraIface->setCallback(gnssXtraCbIface);
- if (!checkHidlReturn(result, "IGnssXtra setCallback() failed.")) {
- gnssXtraIface = nullptr;
- } else {
- ALOGI("Unable to initialize IGnssXtra interface.");
- }
+ // Set IGnssPsds callback.
+ if (gnssPsdsIface == nullptr ||
+ !gnssPsdsIface->setCallback(std::make_unique<gnss::GnssPsdsCallback>())) {
+ ALOGI("Unable to initialize IGnssPsds interface.");
}
// Set IAGnss callback.
@@ -1373,145 +420,47 @@ static jboolean android_location_gnss_hal_GnssNative_init(JNIEnv* /* env */, jcl
}
static void android_location_gnss_hal_GnssNative_cleanup(JNIEnv* /* env */, jclass) {
- if (gnssHalAidl != nullptr) {
- auto status = gnssHalAidl->close();
- checkAidlStatus(status, "IGnssAidl close() failed.");
- }
-
- if (gnssHal != nullptr) {
- auto result = gnssHal->cleanup();
- checkHidlReturn(result, "IGnss cleanup() failed.");
- }
+ gnssHal->close();
}
static jboolean android_location_gnss_hal_GnssNative_set_position_mode(
JNIEnv* /* env */, jclass, jint mode, jint recurrence, jint min_interval,
jint preferred_accuracy, jint preferred_time, jboolean low_power_mode) {
- if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
- IGnssAidl::PositionModeOptions options;
- options.mode = static_cast<IGnssAidl::GnssPositionMode>(mode);
- options.recurrence = static_cast<IGnssAidl::GnssPositionRecurrence>(recurrence);
- options.minIntervalMs = min_interval;
- options.preferredAccuracyMeters = preferred_accuracy;
- options.preferredTimeMs = preferred_time;
- options.lowPowerMode = low_power_mode;
- auto status = gnssHalAidl->setPositionMode(options);
- return checkAidlStatus(status, "IGnssAidl setPositionMode() failed.");
- }
-
- Return<bool> result = false;
- if (gnssHal_V1_1 != nullptr) {
- result = gnssHal_V1_1->setPositionMode_1_1(static_cast<IGnss_V1_0::GnssPositionMode>(mode),
- static_cast<IGnss_V1_0::GnssPositionRecurrence>(recurrence),
- min_interval,
- preferred_accuracy,
- preferred_time,
- low_power_mode);
- } else if (gnssHal != nullptr) {
- result = gnssHal->setPositionMode(static_cast<IGnss_V1_0::GnssPositionMode>(mode),
- static_cast<IGnss_V1_0::GnssPositionRecurrence>(recurrence),
- min_interval,
- preferred_accuracy,
- preferred_time);
- }
-
- return checkHidlReturn(result, "IGnss setPositionMode() failed.");
+ return gnssHal->setPositionMode(mode, recurrence, min_interval, preferred_accuracy,
+ preferred_time, low_power_mode);
}
static jboolean android_location_gnss_hal_GnssNative_start(JNIEnv* /* env */, jclass) {
- if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
- auto status = gnssHalAidl->start();
- return checkAidlStatus(status, "IGnssAidl start() failed.");
- }
-
- if (gnssHal == nullptr) {
- return JNI_FALSE;
- }
-
- auto result = gnssHal->start();
- return checkHidlReturn(result, "IGnss start() failed.");
+ return gnssHal->start();
}
static jboolean android_location_gnss_hal_GnssNative_stop(JNIEnv* /* env */, jclass) {
- if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
- auto status = gnssHalAidl->stop();
- return checkAidlStatus(status, "IGnssAidl stop() failed.");
- }
-
- if (gnssHal == nullptr) {
- return JNI_FALSE;
- }
-
- auto result = gnssHal->stop();
- return checkHidlReturn(result, "IGnss stop() failed.");
+ return gnssHal->stop();
}
static jboolean android_location_gnss_hal_GnssNative_start_sv_status_collection(JNIEnv* /* env */,
jclass) {
- isSvStatusRegistered = true;
- if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
- auto status = gnssHalAidl->startSvStatus();
- return checkAidlStatus(status, "IGnssAidl startSvStatus() failed.");
- }
- if (gnssHal == nullptr) {
- return JNI_FALSE;
- }
- return JNI_TRUE;
+ return gnssHal->startSvStatus();
}
static jboolean android_location_gnss_hal_GnssNative_stop_sv_status_collection(JNIEnv* /* env */,
jclass) {
- isSvStatusRegistered = false;
- if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
- auto status = gnssHalAidl->stopSvStatus();
- return checkAidlStatus(status, "IGnssAidl stopSvStatus() failed.");
- }
- if (gnssHal == nullptr) {
- return JNI_FALSE;
- }
- return JNI_TRUE;
+ return gnssHal->stopSvStatus();
}
static jboolean android_location_gnss_hal_GnssNative_start_nmea_message_collection(
JNIEnv* /* env */, jclass) {
- isNmeaRegistered = true;
- if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
- auto status = gnssHalAidl->startNmea();
- return checkAidlStatus(status, "IGnssAidl startNmea() failed.");
- }
- if (gnssHal == nullptr) {
- return JNI_FALSE;
- }
- return JNI_TRUE;
+ return gnssHal->startNmea();
}
static jboolean android_location_gnss_hal_GnssNative_stop_nmea_message_collection(JNIEnv* /* env */,
jclass) {
- isNmeaRegistered = false;
- if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
- auto status = gnssHalAidl->stopNmea();
- return checkAidlStatus(status, "IGnssAidl stopNmea() failed.");
- }
- if (gnssHal == nullptr) {
- return JNI_FALSE;
- }
- return JNI_TRUE;
+ return gnssHal->stopNmea();
}
static void android_location_gnss_hal_GnssNative_delete_aiding_data(JNIEnv* /* env */, jclass,
jint flags) {
- if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
- auto status = gnssHalAidl->deleteAidingData(static_cast<IGnssAidl::GnssAidingData>(flags));
- checkAidlStatus(status, "IGnssAidl deleteAidingData() failed.");
- return;
- }
-
- if (gnssHal == nullptr) {
- return;
- }
-
- auto result = gnssHal->deleteAidingData(static_cast<IGnss_V1_0::GnssAidingData>(flags));
- checkHidlReturn(result, "IGnss deleteAidingData() failed.");
+ gnssHal->deleteAidingData(flags);
}
static void android_location_gnss_hal_GnssNative_agps_set_reference_location_cellid(
@@ -1535,30 +484,13 @@ static void android_location_gnss_hal_GnssNative_agps_set_id(JNIEnv* env, jclass
static jint android_location_gnss_hal_GnssNative_read_nmea(JNIEnv* env, jclass,
jbyteArray nmeaArray, jint buffer_size) {
- // this should only be called from within a call to reportNmea
- jbyte* nmea = reinterpret_cast<jbyte *>(env->GetPrimitiveArrayCritical(nmeaArray, 0));
- int length = GnssCallback::sNmeaStringLength;
- if (length > buffer_size)
- length = buffer_size;
- memcpy(nmea, GnssCallback::sNmeaString, length);
- env->ReleasePrimitiveArrayCritical(nmeaArray, nmea, JNI_ABORT);
- return (jint) length;
+ return gnssHal->readNmea(nmeaArray, buffer_size);
}
static void android_location_gnss_hal_GnssNative_inject_time(JNIEnv* /* env */, jclass, jlong time,
jlong timeReference,
jint uncertainty) {
- if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
- auto status = gnssHalAidl->injectTime(time, timeReference, uncertainty);
- checkAidlStatus(status, "IGnssAidl injectTime() failed.");
- return;
- }
-
- if (gnssHal == nullptr) {
- return;
- }
- auto result = gnssHal->injectTime(time, timeReference, uncertainty);
- checkHidlReturn(result, "IGnss injectTime() failed.");
+ gnssHal->injectTime(time, timeReference, uncertainty);
}
static void android_location_gnss_hal_GnssNative_inject_best_location(
@@ -1568,58 +500,12 @@ static void android_location_gnss_hal_GnssNative_inject_best_location(
jfloat speedAccuracyMetersPerSecond, jfloat bearingAccuracyDegrees, jlong timestamp,
jint elapsedRealtimeFlags, jlong elapsedRealtimeNanos,
jdouble elapsedRealtimeUncertaintyNanos) {
- if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
- GnssLocationAidl location =
- createGnssLocation(gnssLocationFlags, latitudeDegrees, longitudeDegrees,
- altitudeMeters, speedMetersPerSec, bearingDegrees,
- horizontalAccuracyMeters, verticalAccuracyMeters,
- speedAccuracyMetersPerSecond, bearingAccuracyDegrees, timestamp,
- elapsedRealtimeFlags, elapsedRealtimeNanos,
- elapsedRealtimeUncertaintyNanos);
- auto status = gnssHalAidl->injectBestLocation(location);
- checkAidlStatus(status, "IGnssAidl injectBestLocation() failed.");
- return;
- }
-
- if (gnssHal_V2_0 != nullptr) {
- GnssLocation_V2_0 location = createGnssLocation_V2_0(
- gnssLocationFlags,
- latitudeDegrees,
- longitudeDegrees,
- altitudeMeters,
- speedMetersPerSec,
- bearingDegrees,
- horizontalAccuracyMeters,
- verticalAccuracyMeters,
- speedAccuracyMetersPerSecond,
- bearingAccuracyDegrees,
- timestamp,
- elapsedRealtimeFlags,
- elapsedRealtimeNanos,
- elapsedRealtimeUncertaintyNanos);
- auto result = gnssHal_V2_0->injectBestLocation_2_0(location);
- checkHidlReturn(result, "IGnss injectBestLocation_2_0() failed.");
- return;
- }
-
- if (gnssHal_V1_1 != nullptr) {
- GnssLocation_V1_0 location = createGnssLocation_V1_0(
- gnssLocationFlags,
- latitudeDegrees,
- longitudeDegrees,
- altitudeMeters,
- speedMetersPerSec,
- bearingDegrees,
- horizontalAccuracyMeters,
- verticalAccuracyMeters,
- speedAccuracyMetersPerSecond,
- bearingAccuracyDegrees,
- timestamp);
- auto result = gnssHal_V1_1->injectBestLocation(location);
- checkHidlReturn(result, "IGnss injectBestLocation() failed.");
- }
-
- ALOGE("IGnss injectBestLocation() is called but gnssHal_V1_1 is not available.");
+ gnssHal->injectBestLocation(gnssLocationFlags, latitudeDegrees, longitudeDegrees,
+ altitudeMeters, speedMetersPerSec, bearingDegrees,
+ horizontalAccuracyMeters, verticalAccuracyMeters,
+ speedAccuracyMetersPerSecond, bearingAccuracyDegrees, timestamp,
+ elapsedRealtimeFlags, elapsedRealtimeNanos,
+ elapsedRealtimeUncertaintyNanos);
}
static void android_location_gnss_hal_GnssNative_inject_location(
@@ -1629,51 +515,25 @@ static void android_location_gnss_hal_GnssNative_inject_location(
jfloat speedAccuracyMetersPerSecond, jfloat bearingAccuracyDegrees, jlong timestamp,
jint elapsedRealtimeFlags, jlong elapsedRealtimeNanos,
jdouble elapsedRealtimeUncertaintyNanos) {
- if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
- GnssLocationAidl location =
- createGnssLocation(gnssLocationFlags, latitudeDegrees, longitudeDegrees,
- altitudeMeters, speedMetersPerSec, bearingDegrees,
- horizontalAccuracyMeters, verticalAccuracyMeters,
- speedAccuracyMetersPerSecond, bearingAccuracyDegrees, timestamp,
- elapsedRealtimeFlags, elapsedRealtimeNanos,
- elapsedRealtimeUncertaintyNanos);
- auto status = gnssHalAidl->injectLocation(location);
- checkAidlStatus(status, "IGnssAidl injectLocation() failed.");
- return;
- }
-
- if (gnssHal == nullptr) {
- return;
- }
- auto result =
- gnssHal->injectLocation(latitudeDegrees, longitudeDegrees, horizontalAccuracyMeters);
- checkHidlReturn(result, "IGnss injectLocation() failed.");
+ gnssHal->injectLocation(gnssLocationFlags, latitudeDegrees, longitudeDegrees, altitudeMeters,
+ speedMetersPerSec, bearingDegrees, horizontalAccuracyMeters,
+ verticalAccuracyMeters, speedAccuracyMetersPerSecond,
+ bearingAccuracyDegrees, timestamp, elapsedRealtimeFlags,
+ elapsedRealtimeNanos, elapsedRealtimeUncertaintyNanos);
}
static jboolean android_location_gnss_hal_GnssNative_supports_psds(JNIEnv* /* env */, jclass) {
- return (gnssPsdsAidlIface != nullptr || gnssXtraIface != nullptr) ? JNI_TRUE : JNI_FALSE;
+ return (gnssPsdsIface != nullptr) ? JNI_TRUE : JNI_FALSE;
}
static void android_location_gnss_hal_GnssNative_inject_psds_data(JNIEnv* env, jclass,
jbyteArray data, jint length,
jint psdsType) {
- if (gnssPsdsAidlIface == nullptr && gnssXtraIface == nullptr) {
- ALOGE("%s: IGnssPsdsAidl or IGnssXtra interface not available.", __func__);
+ if (gnssPsdsIface == nullptr) {
+ ALOGE("%s: IGnssPsds or IGnssXtra interface not available.", __func__);
return;
}
-
- jbyte* bytes = reinterpret_cast<jbyte *>(env->GetPrimitiveArrayCritical(data, 0));
- if (gnssPsdsAidlIface != nullptr) {
- auto status = gnssPsdsAidlIface->injectPsdsData(static_cast<PsdsType>(psdsType),
- std::vector<uint8_t>((const uint8_t*)bytes,
- (const uint8_t*)bytes +
- length));
- checkAidlStatus(status, "IGnssPsdsAidl injectPsdsData() failed.");
- } else if (gnssXtraIface != nullptr) {
- auto result = gnssXtraIface->injectXtraData(std::string((const char*)bytes, length));
- checkHidlReturn(result, "IGnssXtra injectXtraData() failed.");
- }
- env->ReleasePrimitiveArrayCritical(data, bytes, JNI_ABORT);
+ gnssPsdsIface->injectPsdsData(data, length, psdsType);
}
static void android_location_GnssNetworkConnectivityHandler_agps_data_conn_open(
diff --git a/services/core/jni/gnss/Android.bp b/services/core/jni/gnss/Android.bp
index e52df15b973a..0531ae2a8ccd 100644
--- a/services/core/jni/gnss/Android.bp
+++ b/services/core/jni/gnss/Android.bp
@@ -28,6 +28,8 @@ cc_library_shared {
"AGnssRil.cpp",
"AGnssRilCallback.cpp",
"GnssAntennaInfo.cpp",
+ "Gnss.cpp",
+ "GnssCallback.cpp",
"GnssAntennaInfoCallback.cpp",
"GnssBatching.cpp",
"GnssBatchingCallback.cpp",
@@ -39,6 +41,8 @@ cc_library_shared {
"GnssMeasurementCallback.cpp",
"GnssNavigationMessage.cpp",
"GnssNavigationMessageCallback.cpp",
+ "GnssPsds.cpp",
+ "GnssPsdsCallback.cpp",
"GnssVisibilityControl.cpp",
"GnssVisibilityControlCallback.cpp",
"MeasurementCorrections.cpp",
@@ -55,6 +59,7 @@ cc_defaults {
"libhidlbase",
"liblog",
"libnativehelper",
+ "libhardware_legacy",
"libutils",
"android.hardware.gnss-V2-cpp",
"android.hardware.gnss@1.0",
diff --git a/services/core/jni/gnss/Gnss.cpp b/services/core/jni/gnss/Gnss.cpp
new file mode 100644
index 000000000000..f6459eaa444e
--- /dev/null
+++ b/services/core/jni/gnss/Gnss.cpp
@@ -0,0 +1,759 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Define LOG_TAG before <log/log.h> to overwrite the default value.
+#define LOG_TAG "GnssJni"
+
+#include "Gnss.h"
+
+#include <binder/IServiceManager.h>
+
+#include "Utils.h"
+
+namespace android::gnss {
+
+using hardware::Return;
+
+using GnssLocationAidl = hardware::gnss::GnssLocation;
+using GnssLocation_V1_0 = hardware::gnss::V1_0::GnssLocation;
+using GnssLocation_V2_0 = hardware::gnss::V2_0::GnssLocation;
+using IAGnssAidl = hardware::gnss::IAGnss;
+using IAGnssRilAidl = hardware::gnss::IAGnssRil;
+using IGnssAidl = hardware::gnss::IGnss;
+using IGnss_V1_0 = hardware::gnss::V1_0::IGnss;
+using IGnss_V1_1 = hardware::gnss::V1_1::IGnss;
+using IGnss_V2_0 = hardware::gnss::V2_0::IGnss;
+using IGnss_V2_1 = hardware::gnss::V2_1::IGnss;
+using IGnssAntennaInfoAidl = hardware::gnss::IGnssAntennaInfo;
+using IGnssCallbackAidl = hardware::gnss::IGnssCallback;
+using IGnssCallback_V1_0 = hardware::gnss::V1_0::IGnssCallback;
+using IGnssCallback_V2_0 = hardware::gnss::V2_0::IGnssCallback;
+using IGnssCallback_V2_1 = hardware::gnss::V2_1::IGnssCallback;
+using IGnssConfigurationAidl = android::hardware::gnss::IGnssConfiguration;
+using IGnssDebugAidl = hardware::gnss::IGnssDebug;
+using android::hardware::gnss::IGnssPsds;
+
+namespace {
+
+GnssLocationAidl createGnssLocation(jint gnssLocationFlags, jdouble latitudeDegrees,
+ jdouble longitudeDegrees, jdouble altitudeMeters,
+ jfloat speedMetersPerSec, jfloat bearingDegrees,
+ jfloat horizontalAccuracyMeters, jfloat verticalAccuracyMeters,
+ jfloat speedAccuracyMetersPerSecond,
+ jfloat bearingAccuracyDegrees, jlong timestamp,
+ jint elapsedRealtimeFlags, jlong elapsedRealtimeNanos,
+ jdouble elapsedRealtimeUncertaintyNanos) {
+ GnssLocationAidl location;
+ location.gnssLocationFlags = static_cast<int>(gnssLocationFlags);
+ location.latitudeDegrees = static_cast<double>(latitudeDegrees);
+ location.longitudeDegrees = static_cast<double>(longitudeDegrees);
+ location.altitudeMeters = static_cast<double>(altitudeMeters);
+ location.speedMetersPerSec = static_cast<double>(speedMetersPerSec);
+ location.bearingDegrees = static_cast<double>(bearingDegrees);
+ location.horizontalAccuracyMeters = static_cast<double>(horizontalAccuracyMeters);
+ location.verticalAccuracyMeters = static_cast<double>(verticalAccuracyMeters);
+ location.speedAccuracyMetersPerSecond = static_cast<double>(speedAccuracyMetersPerSecond);
+ location.bearingAccuracyDegrees = static_cast<double>(bearingAccuracyDegrees);
+ location.timestampMillis = static_cast<uint64_t>(timestamp);
+
+ location.elapsedRealtime.flags = static_cast<int>(elapsedRealtimeFlags);
+ location.elapsedRealtime.timestampNs = static_cast<uint64_t>(elapsedRealtimeNanos);
+ location.elapsedRealtime.timeUncertaintyNs =
+ static_cast<double>(elapsedRealtimeUncertaintyNanos);
+
+ return location;
+}
+
+GnssLocation_V1_0 createGnssLocation_V1_0(jint gnssLocationFlags, jdouble latitudeDegrees,
+ jdouble longitudeDegrees, jdouble altitudeMeters,
+ jfloat speedMetersPerSec, jfloat bearingDegrees,
+ jfloat horizontalAccuracyMeters,
+ jfloat verticalAccuracyMeters,
+ jfloat speedAccuracyMetersPerSecond,
+ jfloat bearingAccuracyDegrees, jlong timestamp) {
+ GnssLocation_V1_0 location;
+ location.gnssLocationFlags = static_cast<uint16_t>(gnssLocationFlags);
+ location.latitudeDegrees = static_cast<double>(latitudeDegrees);
+ location.longitudeDegrees = static_cast<double>(longitudeDegrees);
+ location.altitudeMeters = static_cast<double>(altitudeMeters);
+ location.speedMetersPerSec = static_cast<float>(speedMetersPerSec);
+ location.bearingDegrees = static_cast<float>(bearingDegrees);
+ location.horizontalAccuracyMeters = static_cast<float>(horizontalAccuracyMeters);
+ location.verticalAccuracyMeters = static_cast<float>(verticalAccuracyMeters);
+ location.speedAccuracyMetersPerSecond = static_cast<float>(speedAccuracyMetersPerSecond);
+ location.bearingAccuracyDegrees = static_cast<float>(bearingAccuracyDegrees);
+ location.timestamp = static_cast<uint64_t>(timestamp);
+
+ return location;
+}
+
+GnssLocation_V2_0 createGnssLocation_V2_0(jint gnssLocationFlags, jdouble latitudeDegrees,
+ jdouble longitudeDegrees, jdouble altitudeMeters,
+ jfloat speedMetersPerSec, jfloat bearingDegrees,
+ jfloat horizontalAccuracyMeters,
+ jfloat verticalAccuracyMeters,
+ jfloat speedAccuracyMetersPerSecond,
+ jfloat bearingAccuracyDegrees, jlong timestamp,
+ jint elapsedRealtimeFlags, jlong elapsedRealtimeNanos,
+ jdouble elapsedRealtimeUncertaintyNanos) {
+ GnssLocation_V2_0 location;
+ location.v1_0 = createGnssLocation_V1_0(gnssLocationFlags, latitudeDegrees, longitudeDegrees,
+ altitudeMeters, speedMetersPerSec, bearingDegrees,
+ horizontalAccuracyMeters, verticalAccuracyMeters,
+ speedAccuracyMetersPerSecond, bearingAccuracyDegrees,
+ timestamp);
+
+ location.elapsedRealtime.flags = static_cast<uint16_t>(elapsedRealtimeFlags);
+ location.elapsedRealtime.timestampNs = static_cast<uint64_t>(elapsedRealtimeNanos);
+ location.elapsedRealtime.timeUncertaintyNs =
+ static_cast<uint64_t>(elapsedRealtimeUncertaintyNanos);
+
+ return location;
+}
+
+} // anonymous namespace
+
+// Implementation of GnssHal, which unifies all versions of GNSS HALs
+
+GnssHal::GnssHal() {
+ gnssHalAidl = waitForVintfService<IGnssAidl>();
+ if (gnssHalAidl != nullptr) {
+ ALOGD("Successfully got GNSS AIDL handle. Version=%d.", gnssHalAidl->getInterfaceVersion());
+ if (gnssHalAidl->getInterfaceVersion() >= 2) {
+ return;
+ }
+ }
+
+ ALOGD("Trying IGnss_V2_1::getService()");
+ gnssHal_V2_1 = IGnss_V2_1::getService();
+ if (gnssHal_V2_1 != nullptr) {
+ gnssHal_V2_0 = gnssHal_V2_1;
+ gnssHal_V1_1 = gnssHal_V2_1;
+ gnssHal = gnssHal_V2_1;
+ return;
+ }
+
+ ALOGD("gnssHal 2.1 was null, trying 2.0");
+ gnssHal_V2_0 = IGnss_V2_0::getService();
+ if (gnssHal_V2_0 != nullptr) {
+ gnssHal_V1_1 = gnssHal_V2_0;
+ gnssHal = gnssHal_V2_0;
+ return;
+ }
+
+ ALOGD("gnssHal 2.0 was null, trying 1.1");
+ gnssHal_V1_1 = IGnss_V1_1::getService();
+ if (gnssHal_V1_1 != nullptr) {
+ gnssHal = gnssHal_V1_1;
+ return;
+ }
+
+ ALOGD("gnssHal 1.1 was null, trying 1.0");
+ gnssHal = IGnss_V1_0::getService();
+}
+
+jboolean GnssHal::isSupported() {
+ return (gnssHalAidl != nullptr || gnssHal != nullptr) ? JNI_TRUE : JNI_FALSE;
+}
+
+void GnssHal::linkToDeath() {
+ // TODO: linkToDeath for AIDL HAL
+
+ if (gnssHal != nullptr) {
+ gnssHalDeathRecipient = new GnssDeathRecipient();
+ hardware::Return<bool> linked = gnssHal->linkToDeath(gnssHalDeathRecipient, /*cookie*/ 0);
+ if (!linked.isOk()) {
+ ALOGE("Transaction error in linking to GnssHAL death: %s",
+ linked.description().c_str());
+ } else if (!linked) {
+ ALOGW("Unable to link to GnssHal death notifications");
+ } else {
+ ALOGD("Link to death notification successful");
+ }
+ }
+}
+
+jboolean GnssHal::setCallback() {
+ if (gnssHalAidl != nullptr) {
+ sp<IGnssCallbackAidl> gnssCbIfaceAidl = new GnssCallbackAidl();
+ auto status = gnssHalAidl->setCallback(gnssCbIfaceAidl);
+ if (!checkAidlStatus(status, "IGnssAidl setCallback() failed.")) {
+ return JNI_FALSE;
+ }
+ }
+ if (gnssHal != nullptr) {
+ Return<bool> result = false;
+ sp<IGnssCallback_V2_1> gnssCbIface = new GnssCallbackHidl();
+ if (gnssHal_V2_1 != nullptr) {
+ result = gnssHal_V2_1->setCallback_2_1(gnssCbIface);
+ } else if (gnssHal_V2_0 != nullptr) {
+ result = gnssHal_V2_0->setCallback_2_0(gnssCbIface);
+ } else if (gnssHal_V1_1 != nullptr) {
+ result = gnssHal_V1_1->setCallback_1_1(gnssCbIface);
+ } else {
+ result = gnssHal->setCallback(gnssCbIface);
+ }
+ if (!checkHidlReturn(result, "IGnss setCallback() failed.")) {
+ return JNI_FALSE;
+ }
+ }
+ return JNI_TRUE;
+}
+
+void GnssHal::close() {
+ if (gnssHalAidl != nullptr) {
+ auto status = gnssHalAidl->close();
+ checkAidlStatus(status, "IGnssAidl close() failed.");
+ }
+
+ if (gnssHal != nullptr) {
+ auto result = gnssHal->cleanup();
+ checkHidlReturn(result, "IGnss cleanup() failed.");
+ }
+}
+
+jboolean GnssHal::start() {
+ if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
+ auto status = gnssHalAidl->start();
+ return checkAidlStatus(status, "IGnssAidl start() failed.");
+ }
+
+ if (gnssHal == nullptr) {
+ return JNI_FALSE;
+ }
+
+ auto result = gnssHal->start();
+ return checkHidlReturn(result, "IGnss start() failed.");
+}
+
+jboolean GnssHal::stop() {
+ if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
+ auto status = gnssHalAidl->stop();
+ return checkAidlStatus(status, "IGnssAidl stop() failed.");
+ }
+
+ if (gnssHal == nullptr) {
+ return JNI_FALSE;
+ }
+
+ auto result = gnssHal->stop();
+ return checkHidlReturn(result, "IGnss stop() failed.");
+}
+
+jboolean GnssHal::startSvStatus() {
+ isSvStatusRegistered = true;
+ if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
+ auto status = gnssHalAidl->startSvStatus();
+ return checkAidlStatus(status, "IGnssAidl startSvStatus() failed.");
+ }
+ if (gnssHal == nullptr) {
+ return JNI_FALSE;
+ }
+ return JNI_TRUE;
+}
+
+jboolean GnssHal::stopSvStatus() {
+ isSvStatusRegistered = false;
+ if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
+ auto status = gnssHalAidl->stopSvStatus();
+ return checkAidlStatus(status, "IGnssAidl stopSvStatus() failed.");
+ }
+ if (gnssHal == nullptr) {
+ return JNI_FALSE;
+ }
+ return JNI_TRUE;
+}
+
+jboolean GnssHal::startNmea() {
+ isNmeaRegistered = true;
+ if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
+ auto status = gnssHalAidl->startNmea();
+ return checkAidlStatus(status, "IGnssAidl startNmea() failed.");
+ }
+ if (gnssHal == nullptr) {
+ return JNI_FALSE;
+ }
+ return JNI_TRUE;
+}
+
+jboolean GnssHal::stopNmea() {
+ isNmeaRegistered = false;
+ if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
+ auto status = gnssHalAidl->stopNmea();
+ return checkAidlStatus(status, "IGnssAidl stopNmea() failed.");
+ }
+ if (gnssHal == nullptr) {
+ return JNI_FALSE;
+ }
+ return JNI_TRUE;
+}
+
+jint GnssHal::readNmea(jbyteArray& nmeaArray, jint& buffer_size) {
+ // this should only be called from within a call to reportNmea
+ JNIEnv* env = getJniEnv();
+ jbyte* nmea = reinterpret_cast<jbyte*>(env->GetPrimitiveArrayCritical(nmeaArray, 0));
+ int length = GnssCallbackHidl::sNmeaStringLength;
+ if (length > buffer_size) {
+ length = buffer_size;
+ }
+ memcpy(nmea, GnssCallbackHidl::sNmeaString, length);
+ env->ReleasePrimitiveArrayCritical(nmeaArray, nmea, JNI_ABORT);
+ return (jint)length;
+}
+
+jboolean GnssHal::setPositionMode(jint mode, jint recurrence, jint min_interval,
+ jint preferred_accuracy, jint preferred_time,
+ jboolean low_power_mode) {
+ if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
+ IGnssAidl::PositionModeOptions options;
+ options.mode = static_cast<IGnssAidl::GnssPositionMode>(mode);
+ options.recurrence = static_cast<IGnssAidl::GnssPositionRecurrence>(recurrence);
+ options.minIntervalMs = min_interval;
+ options.preferredAccuracyMeters = preferred_accuracy;
+ options.preferredTimeMs = preferred_time;
+ options.lowPowerMode = low_power_mode;
+ auto status = gnssHalAidl->setPositionMode(options);
+ return checkAidlStatus(status, "IGnssAidl setPositionMode() failed.");
+ }
+
+ Return<bool> result = false;
+ if (gnssHal_V1_1 != nullptr) {
+ result = gnssHal_V1_1->setPositionMode_1_1(static_cast<IGnss_V1_0::GnssPositionMode>(mode),
+ static_cast<IGnss_V1_0::GnssPositionRecurrence>(
+ recurrence),
+ min_interval, preferred_accuracy, preferred_time,
+ low_power_mode);
+ } else if (gnssHal != nullptr) {
+ result = gnssHal->setPositionMode(static_cast<IGnss_V1_0::GnssPositionMode>(mode),
+ static_cast<IGnss_V1_0::GnssPositionRecurrence>(
+ recurrence),
+ min_interval, preferred_accuracy, preferred_time);
+ }
+ return checkHidlReturn(result, "IGnss setPositionMode() failed.");
+}
+
+void GnssHal::deleteAidingData(jint flags) {
+ if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
+ auto status = gnssHalAidl->deleteAidingData(static_cast<IGnssAidl::GnssAidingData>(flags));
+ checkAidlStatus(status, "IGnssAidl deleteAidingData() failed.");
+ return;
+ }
+
+ if (gnssHal == nullptr) {
+ return;
+ }
+
+ auto result = gnssHal->deleteAidingData(static_cast<IGnss_V1_0::GnssAidingData>(flags));
+ checkHidlReturn(result, "IGnss deleteAidingData() failed.");
+}
+
+void GnssHal::injectTime(jlong time, jlong timeReference, jint uncertainty) {
+ if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
+ auto status = gnssHalAidl->injectTime(time, timeReference, uncertainty);
+ checkAidlStatus(status, "IGnssAidl injectTime() failed.");
+ return;
+ }
+
+ if (gnssHal == nullptr) {
+ return;
+ }
+ auto result = gnssHal->injectTime(time, timeReference, uncertainty);
+ checkHidlReturn(result, "IGnss injectTime() failed.");
+}
+
+void GnssHal::injectLocation(jint gnssLocationFlags, jdouble latitudeDegrees,
+ jdouble longitudeDegrees, jdouble altitudeMeters,
+ jfloat speedMetersPerSec, jfloat bearingDegrees,
+ jfloat horizontalAccuracyMeters, jfloat verticalAccuracyMeters,
+ jfloat speedAccuracyMetersPerSecond, jfloat bearingAccuracyDegrees,
+ jlong timestamp, jint elapsedRealtimeFlags, jlong elapsedRealtimeNanos,
+ jdouble elapsedRealtimeUncertaintyNanos) {
+ if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
+ GnssLocationAidl location =
+ createGnssLocation(gnssLocationFlags, latitudeDegrees, longitudeDegrees,
+ altitudeMeters, speedMetersPerSec, bearingDegrees,
+ horizontalAccuracyMeters, verticalAccuracyMeters,
+ speedAccuracyMetersPerSecond, bearingAccuracyDegrees, timestamp,
+ elapsedRealtimeFlags, elapsedRealtimeNanos,
+ elapsedRealtimeUncertaintyNanos);
+ auto status = gnssHalAidl->injectLocation(location);
+ checkAidlStatus(status, "IGnssAidl injectLocation() failed.");
+ return;
+ }
+
+ if (gnssHal == nullptr) {
+ return;
+ }
+ auto result =
+ gnssHal->injectLocation(latitudeDegrees, longitudeDegrees, horizontalAccuracyMeters);
+ checkHidlReturn(result, "IGnss injectLocation() failed.");
+}
+
+void GnssHal::injectBestLocation(jint gnssLocationFlags, jdouble latitudeDegrees,
+ jdouble longitudeDegrees, jdouble altitudeMeters,
+ jfloat speedMetersPerSec, jfloat bearingDegrees,
+ jfloat horizontalAccuracyMeters, jfloat verticalAccuracyMeters,
+ jfloat speedAccuracyMetersPerSecond, jfloat bearingAccuracyDegrees,
+ jlong timestamp, jint elapsedRealtimeFlags,
+ jlong elapsedRealtimeNanos,
+ jdouble elapsedRealtimeUncertaintyNanos) {
+ if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
+ GnssLocationAidl location =
+ createGnssLocation(gnssLocationFlags, latitudeDegrees, longitudeDegrees,
+ altitudeMeters, speedMetersPerSec, bearingDegrees,
+ horizontalAccuracyMeters, verticalAccuracyMeters,
+ speedAccuracyMetersPerSecond, bearingAccuracyDegrees, timestamp,
+ elapsedRealtimeFlags, elapsedRealtimeNanos,
+ elapsedRealtimeUncertaintyNanos);
+ auto status = gnssHalAidl->injectBestLocation(location);
+ checkAidlStatus(status, "IGnssAidl injectBestLocation() failed.");
+ return;
+ }
+
+ if (gnssHal_V2_0 != nullptr) {
+ GnssLocation_V2_0 location =
+ createGnssLocation_V2_0(gnssLocationFlags, latitudeDegrees, longitudeDegrees,
+ altitudeMeters, speedMetersPerSec, bearingDegrees,
+ horizontalAccuracyMeters, verticalAccuracyMeters,
+ speedAccuracyMetersPerSecond, bearingAccuracyDegrees,
+ timestamp, elapsedRealtimeFlags, elapsedRealtimeNanos,
+ elapsedRealtimeUncertaintyNanos);
+ auto result = gnssHal_V2_0->injectBestLocation_2_0(location);
+ checkHidlReturn(result, "IGnss injectBestLocation_2_0() failed.");
+ return;
+ }
+
+ if (gnssHal_V1_1 != nullptr) {
+ GnssLocation_V1_0 location =
+ createGnssLocation_V1_0(gnssLocationFlags, latitudeDegrees, longitudeDegrees,
+ altitudeMeters, speedMetersPerSec, bearingDegrees,
+ horizontalAccuracyMeters, verticalAccuracyMeters,
+ speedAccuracyMetersPerSecond, bearingAccuracyDegrees,
+ timestamp);
+ auto result = gnssHal_V1_1->injectBestLocation(location);
+ checkHidlReturn(result, "IGnss injectBestLocation() failed.");
+ return;
+ }
+
+ ALOGE("IGnss injectBestLocation() is called but gnssHal_V1_1 is not available.");
+}
+
+std::unique_ptr<AGnssInterface> GnssHal::getAGnssInterface() {
+ if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
+ sp<IAGnssAidl> agnssAidl;
+ auto status = gnssHalAidl->getExtensionAGnss(&agnssAidl);
+ if (checkAidlStatus(status, "Unable to get a handle to AGnss interface.")) {
+ return std::make_unique<gnss::AGnss>(agnssAidl);
+ }
+ } else if (gnssHal_V2_0 != nullptr) {
+ auto agnss_V2_0 = gnssHal_V2_0->getExtensionAGnss_2_0();
+ if (checkHidlReturn(agnss_V2_0, "Unable to get a handle to AGnss_V2_0")) {
+ return std::make_unique<gnss::AGnss_V2_0>(agnss_V2_0);
+ }
+ } else if (gnssHal != nullptr) {
+ auto agnss_V1_0 = gnssHal->getExtensionAGnss();
+ if (checkHidlReturn(agnss_V1_0, "Unable to get a handle to AGnss_V1_0")) {
+ return std::make_unique<gnss::AGnss_V1_0>(agnss_V1_0);
+ }
+ }
+ return nullptr;
+}
+
+std::unique_ptr<AGnssRilInterface> GnssHal::getAGnssRilInterface() {
+ if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
+ sp<IAGnssRilAidl> agnssRilAidl;
+ auto status = gnssHalAidl->getExtensionAGnssRil(&agnssRilAidl);
+ if (checkAidlStatus(status, "Unable to get a handle to AGnssRil interface.")) {
+ return std::make_unique<gnss::AGnssRil>(agnssRilAidl);
+ }
+ } else if (gnssHal_V2_0 != nullptr) {
+ auto agnssRil_V2_0 = gnssHal_V2_0->getExtensionAGnssRil_2_0();
+ if (checkHidlReturn(agnssRil_V2_0, "Unable to get a handle to AGnssRil_V2_0")) {
+ return std::make_unique<gnss::AGnssRil_V2_0>(agnssRil_V2_0);
+ }
+ } else if (gnssHal != nullptr) {
+ auto agnssRil_V1_0 = gnssHal->getExtensionAGnssRil();
+ if (checkHidlReturn(agnssRil_V1_0, "Unable to get a handle to AGnssRil_V1_0")) {
+ return std::make_unique<gnss::AGnssRil_V1_0>(agnssRil_V1_0);
+ }
+ }
+ return nullptr;
+}
+
+std::unique_ptr<GnssNavigationMessageInterface> GnssHal::getGnssNavigationMessageInterface() {
+ if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
+ sp<hardware::gnss::IGnssNavigationMessageInterface> gnssNavigationMessage;
+ auto status = gnssHalAidl->getExtensionGnssNavigationMessage(&gnssNavigationMessage);
+ if (checkAidlStatus(status,
+ "Unable to get a handle to GnssNavigationMessage AIDL interface.")) {
+ return std::make_unique<gnss::GnssNavigationMessageAidl>(gnssNavigationMessage);
+ }
+ } else if (gnssHal != nullptr) {
+ auto gnssNavigationMessage = gnssHal->getExtensionGnssNavigationMessage();
+ if (checkHidlReturn(gnssNavigationMessage,
+ "Unable to get a handle to GnssNavigationMessage interface.")) {
+ return std::make_unique<gnss::GnssNavigationMessageHidl>(gnssNavigationMessage);
+ }
+ }
+ return nullptr;
+}
+
+std::unique_ptr<GnssMeasurementInterface> GnssHal::getGnssMeasurementInterface() {
+ // Allow all causal combinations between IGnss.hal and IGnssMeasurement.hal. That means,
+ // 2.1@IGnss can be paired with {1.0, 1,1, 2.0, 2.1}@IGnssMeasurement
+ // 2.0@IGnss can be paired with {1.0, 1,1, 2.0}@IGnssMeasurement
+ // 1.1@IGnss can be paired {1.0, 1.1}@IGnssMeasurement
+ // 1.0@IGnss is paired with 1.0@IGnssMeasurement
+ if (gnssHalAidl != nullptr) {
+ sp<hardware::gnss::IGnssMeasurementInterface> gnssMeasurement;
+ auto status = gnssHalAidl->getExtensionGnssMeasurement(&gnssMeasurement);
+ if (checkAidlStatus(status, "Unable to get a handle to GnssMeasurement AIDL interface.")) {
+ return std::make_unique<android::gnss::GnssMeasurement>(gnssMeasurement);
+ }
+ }
+ if (gnssHal_V2_1 != nullptr) {
+ auto gnssMeasurement = gnssHal_V2_1->getExtensionGnssMeasurement_2_1();
+ if (checkHidlReturn(gnssMeasurement, "Unable to get a handle to GnssMeasurement_V2_1")) {
+ return std::make_unique<android::gnss::GnssMeasurement_V2_1>(gnssMeasurement);
+ }
+ }
+ if (gnssHal_V2_0 != nullptr) {
+ auto gnssMeasurement = gnssHal_V2_0->getExtensionGnssMeasurement_2_0();
+ if (checkHidlReturn(gnssMeasurement, "Unable to get a handle to GnssMeasurement_V2_0")) {
+ return std::make_unique<android::gnss::GnssMeasurement_V2_0>(gnssMeasurement);
+ }
+ }
+ if (gnssHal_V1_1 != nullptr) {
+ auto gnssMeasurement = gnssHal_V1_1->getExtensionGnssMeasurement_1_1();
+ if (checkHidlReturn(gnssMeasurement, "Unable to get a handle to GnssMeasurement_V1_1")) {
+ return std::make_unique<android::gnss::GnssMeasurement_V1_1>(gnssMeasurement);
+ }
+ }
+ if (gnssHal != nullptr) {
+ auto gnssMeasurement = gnssHal->getExtensionGnssMeasurement();
+ if (checkHidlReturn(gnssMeasurement, "Unable to get a handle to GnssMeasurement_V1_0")) {
+ return std::make_unique<android::gnss::GnssMeasurement_V1_0>(gnssMeasurement);
+ }
+ }
+ return nullptr;
+}
+
+std::unique_ptr<GnssDebugInterface> GnssHal::getGnssDebugInterface() {
+ // Allow all causal combinations between IGnss.hal and IGnssDebug.hal. That means,
+ // 2.0@IGnss can be paired with {1.0, 2.0}@IGnssDebug
+ // 1.0@IGnss is paired with 1.0@IGnssDebug
+ if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
+ sp<IGnssDebugAidl> gnssDebugAidl;
+ auto status = gnssHalAidl->getExtensionGnssDebug(&gnssDebugAidl);
+ if (checkAidlStatus(status, "Unable to get a handle to GnssDebug interface.")) {
+ return std::make_unique<gnss::GnssDebug>(gnssDebugAidl);
+ }
+ }
+ if (gnssHal_V2_0 != nullptr) {
+ auto gnssDebug_V2_0 = gnssHal_V2_0->getExtensionGnssDebug_2_0();
+ if (checkHidlReturn(gnssDebug_V2_0, "Unable to get a handle to GnssDebug_V2_0.")) {
+ return std::make_unique<gnss::GnssDebug_V2_0>(gnssDebug_V2_0);
+ }
+ }
+ if (gnssHal != nullptr) {
+ auto gnssDebug_V1_0 = gnssHal->getExtensionGnssDebug();
+ if (checkHidlReturn(gnssDebug_V1_0, "Unable to get a handle to GnssDebug_V1_0.")) {
+ return std::make_unique<gnss::GnssDebug_V1_0>(gnssDebug_V1_0);
+ }
+ }
+ return nullptr;
+}
+
+std::unique_ptr<GnssConfigurationInterface> GnssHal::getGnssConfigurationInterface() {
+ if (gnssHalAidl != nullptr) {
+ sp<IGnssConfigurationAidl> gnssConfigurationAidl;
+ auto status = gnssHalAidl->getExtensionGnssConfiguration(&gnssConfigurationAidl);
+ if (checkAidlStatus(status,
+ "Unable to get a handle to GnssConfiguration AIDL interface.")) {
+ return std::make_unique<android::gnss::GnssConfiguration>(gnssConfigurationAidl);
+ }
+ } else if (gnssHal_V2_1 != nullptr) {
+ auto gnssConfiguration = gnssHal_V2_1->getExtensionGnssConfiguration_2_1();
+ if (checkHidlReturn(gnssConfiguration,
+ "Unable to get a handle to GnssConfiguration_V2_1")) {
+ return std::make_unique<android::gnss::GnssConfiguration_V2_1>(gnssConfiguration);
+ }
+ } else if (gnssHal_V2_0 != nullptr) {
+ auto gnssConfiguration = gnssHal_V2_0->getExtensionGnssConfiguration_2_0();
+ if (checkHidlReturn(gnssConfiguration,
+ "Unable to get a handle to GnssConfiguration_V2_0")) {
+ return std::make_unique<android::gnss::GnssConfiguration_V2_0>(gnssConfiguration);
+ }
+ } else if (gnssHal_V1_1 != nullptr) {
+ auto gnssConfiguration = gnssHal_V1_1->getExtensionGnssConfiguration_1_1();
+ if (checkHidlReturn(gnssConfiguration,
+ "Unable to get a handle to GnssConfiguration_V1_1")) {
+ return std::make_unique<gnss::GnssConfiguration_V1_1>(gnssConfiguration);
+ }
+ } else if (gnssHal != nullptr) {
+ auto gnssConfiguration = gnssHal->getExtensionGnssConfiguration();
+ if (checkHidlReturn(gnssConfiguration,
+ "Unable to get a handle to GnssConfiguration_V1_0")) {
+ return std::make_unique<gnss::GnssConfiguration_V1_0>(gnssConfiguration);
+ }
+ }
+ return nullptr;
+}
+
+std::unique_ptr<GnssGeofenceInterface> GnssHal::getGnssGeofenceInterface() {
+ if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
+ sp<hardware::gnss::IGnssGeofence> gnssGeofence;
+ auto status = gnssHalAidl->getExtensionGnssGeofence(&gnssGeofence);
+ if (checkAidlStatus(status, "Unable to get a handle to GnssGeofence AIDL interface.")) {
+ return std::make_unique<gnss::GnssGeofenceAidl>(gnssGeofence);
+ }
+ } else if (gnssHal != nullptr) {
+ auto gnssGeofencing = gnssHal->getExtensionGnssGeofencing();
+ if (checkHidlReturn(gnssGeofencing, "Unable to get a handle to GnssGeofencing")) {
+ return std::make_unique<gnss::GnssGeofenceHidl>(gnssGeofencing);
+ }
+ }
+ return nullptr;
+}
+
+std::unique_ptr<GnssBatchingInterface> GnssHal::getGnssBatchingInterface() {
+ if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
+ sp<android::hardware::gnss::IGnssBatching> gnssBatchingAidl;
+ auto status = gnssHalAidl->getExtensionGnssBatching(&gnssBatchingAidl);
+ if (checkAidlStatus(status, "Unable to get a handle to GnssBatching interface.")) {
+ return std::make_unique<gnss::GnssBatching>(gnssBatchingAidl);
+ }
+ }
+ if (gnssHal_V2_0 != nullptr) {
+ auto gnssBatching_V2_0 = gnssHal_V2_0->getExtensionGnssBatching_2_0();
+ if (checkHidlReturn(gnssBatching_V2_0, "Unable to get a handle to GnssBatching_V2_0")) {
+ return std::make_unique<gnss::GnssBatching_V2_0>(gnssBatching_V2_0);
+ }
+ }
+ if (gnssHal != nullptr) {
+ auto gnssBatching_V1_0 = gnssHal->getExtensionGnssBatching();
+ if (checkHidlReturn(gnssBatching_V1_0, "Unable to get a handle to GnssBatching")) {
+ return std::make_unique<gnss::GnssBatching_V1_0>(gnssBatching_V1_0);
+ }
+ }
+ return nullptr;
+}
+
+std::unique_ptr<MeasurementCorrectionsInterface> GnssHal::getMeasurementCorrectionsInterface() {
+ if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
+ sp<android::hardware::gnss::measurement_corrections::IMeasurementCorrectionsInterface>
+ gnssMeasurementCorrectionsAidl;
+ auto status =
+ gnssHalAidl->getExtensionMeasurementCorrections(&gnssMeasurementCorrectionsAidl);
+ if (checkAidlStatus(status,
+ "Unable to get a handle to GnssVisibilityControl AIDL interface.")) {
+ return std::make_unique<gnss::MeasurementCorrectionsIface_Aidl>(
+ gnssMeasurementCorrectionsAidl);
+ }
+ }
+ if (gnssHal_V2_1 != nullptr) {
+ auto gnssCorrections = gnssHal_V2_1->getExtensionMeasurementCorrections_1_1();
+ if (checkHidlReturn(gnssCorrections,
+ "Unable to get a handle to GnssMeasurementCorrections HIDL "
+ "interface")) {
+ return std::make_unique<gnss::MeasurementCorrectionsIface_V1_1>(gnssCorrections);
+ }
+ }
+ if (gnssHal_V2_0 != nullptr) {
+ auto gnssCorrections = gnssHal_V2_0->getExtensionMeasurementCorrections();
+ if (checkHidlReturn(gnssCorrections,
+ "Unable to get a handle to GnssMeasurementCorrections HIDL "
+ "interface")) {
+ return std::make_unique<gnss::MeasurementCorrectionsIface_V1_0>(gnssCorrections);
+ }
+ }
+ return nullptr;
+}
+
+std::unique_ptr<GnssVisibilityControlInterface> GnssHal::getGnssVisibilityControlInterface() {
+ if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
+ sp<android::hardware::gnss::visibility_control::IGnssVisibilityControl>
+ gnssVisibilityControlAidl;
+ auto status = gnssHalAidl->getExtensionGnssVisibilityControl(&gnssVisibilityControlAidl);
+ if (checkAidlStatus(status,
+ "Unable to get a handle to GnssVisibilityControl AIDL interface.")) {
+ return std::make_unique<gnss::GnssVisibilityControlAidl>(gnssVisibilityControlAidl);
+ }
+ } else if (gnssHal_V2_0 != nullptr) {
+ auto gnssVisibilityControlHidl = gnssHal_V2_0->getExtensionVisibilityControl();
+ if (checkHidlReturn(gnssVisibilityControlHidl,
+ "Unable to get a handle to GnssVisibilityControl HIDL interface")) {
+ return std::make_unique<gnss::GnssVisibilityControlHidl>(gnssVisibilityControlHidl);
+ }
+ }
+ return nullptr;
+}
+
+std::unique_ptr<GnssAntennaInfoInterface> GnssHal::getGnssAntennaInfoInterface() {
+ if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
+ sp<IGnssAntennaInfoAidl> gnssAntennaInfoAidl;
+ auto status = gnssHalAidl->getExtensionGnssAntennaInfo(&gnssAntennaInfoAidl);
+ if (checkAidlStatus(status, "Unable to get a handle to GnssAntennaInfo interface.")) {
+ return std::make_unique<gnss::GnssAntennaInfoAidl>(gnssAntennaInfoAidl);
+ }
+ } else if (gnssHal_V2_1 != nullptr) {
+ auto gnssAntennaInfo_V2_1 = gnssHal_V2_1->getExtensionGnssAntennaInfo();
+ if (checkHidlReturn(gnssAntennaInfo_V2_1,
+ "Unable to get a handle to GnssAntennaInfo_V2_1")) {
+ return std::make_unique<gnss::GnssAntennaInfo_V2_1>(gnssAntennaInfo_V2_1);
+ }
+ }
+ return nullptr;
+}
+
+std::unique_ptr<GnssPsdsInterface> GnssHal::getGnssPsdsInterface() {
+ if (gnssHalAidl != nullptr) {
+ sp<IGnssPsds> gnssPsdsAidl;
+ auto status = gnssHalAidl->getExtensionPsds(&gnssPsdsAidl);
+ if (checkAidlStatus(status, "Unable to get a handle to PSDS interface.")) {
+ return std::make_unique<gnss::GnssPsdsAidl>(gnssPsdsAidl);
+ }
+ } else if (gnssHal != nullptr) {
+ auto gnssXtra = gnssHal->getExtensionXtra();
+ if (checkHidlReturn(gnssXtra, "Unable to get a handle to XTRA interface.")) {
+ return std::make_unique<gnss::GnssPsdsHidl>(gnssXtra);
+ }
+ }
+ return nullptr;
+}
+
+sp<hardware::gnss::IGnssPowerIndication> GnssHal::getGnssPowerIndicationInterface() {
+ if (gnssHalAidl != nullptr) {
+ sp<hardware::gnss::IGnssPowerIndication> gnssPowerIndication;
+ auto status = gnssHalAidl->getExtensionGnssPowerIndication(&gnssPowerIndication);
+ if (checkAidlStatus(status, "Unable to get a handle to GnssPowerIndication")) {
+ return gnssPowerIndication;
+ }
+ }
+ return nullptr;
+}
+
+sp<hardware::gnss::V1_0::IGnssNi> GnssHal::getGnssNiInterface() {
+ if (gnssHal != nullptr) {
+ auto gnssNi = gnssHal->getExtensionGnssNi();
+ if (checkHidlReturn(gnssNi, "Unable to get a handle to GnssNi")) {
+ return gnssNi;
+ }
+ }
+ return nullptr;
+}
+
+} // namespace android::gnss
diff --git a/services/core/jni/gnss/Gnss.h b/services/core/jni/gnss/Gnss.h
new file mode 100644
index 000000000000..c6743d62af45
--- /dev/null
+++ b/services/core/jni/gnss/Gnss.h
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _ANDROID_SERVER_GNSS_GNSS_H
+#define _ANDROID_SERVER_GNSS_GNSS_H
+
+#pragma once
+
+#ifndef LOG_TAG
+#error LOG_TAG must be defined before including this file.
+#endif
+
+#include <android/hardware/gnss/1.0/IGnss.h>
+#include <android/hardware/gnss/1.1/IGnss.h>
+#include <android/hardware/gnss/2.0/IGnss.h>
+#include <android/hardware/gnss/2.1/IGnss.h>
+#include <android/hardware/gnss/BnGnss.h>
+#include <log/log.h>
+
+#include "AGnss.h"
+#include "AGnssRil.h"
+#include "GnssAntennaInfo.h"
+#include "GnssBatching.h"
+#include "GnssCallback.h"
+#include "GnssConfiguration.h"
+#include "GnssDebug.h"
+#include "GnssGeofence.h"
+#include "GnssMeasurement.h"
+#include "GnssNavigationMessage.h"
+#include "GnssPsds.h"
+#include "GnssVisibilityControl.h"
+#include "MeasurementCorrections.h"
+#include "jni.h"
+
+namespace android::gnss {
+
+struct GnssDeathRecipient : virtual public hardware::hidl_death_recipient {
+ // hidl_death_recipient interface
+ virtual void serviceDied(uint64_t cookie, const wp<hidl::base::V1_0::IBase>& who) override {
+ ALOGE("IGNSS hidl service failed, trying to recover...");
+
+ JNIEnv* env = android::AndroidRuntime::getJNIEnv();
+ env->CallVoidMethod(android::mCallbacksObj, method_reportGnssServiceDied);
+ }
+};
+
+class GnssHal {
+public:
+ GnssHal();
+ ~GnssHal() {}
+
+ jboolean isSupported();
+ jboolean setCallback();
+ jboolean start();
+ jboolean stop();
+ jboolean setPositionMode(jint mode, jint recurrence, jint min_interval, jint preferred_accuracy,
+ jint preferred_time, jboolean low_power_mode);
+ jboolean startSvStatus();
+ jboolean stopSvStatus();
+ jboolean startNmea();
+ jboolean stopNmea();
+ jint readNmea(jbyteArray& nmeaArray, jint& buffer_size);
+ void linkToDeath();
+ void close();
+ void deleteAidingData(jint flags);
+ void injectTime(jlong time, jlong timeReference, jint uncertainty);
+ void injectLocation(jint gnssLocationFlags, jdouble latitudeDegrees, jdouble longitudeDegrees,
+ jdouble altitudeMeters, jfloat speedMetersPerSec, jfloat bearingDegrees,
+ jfloat horizontalAccuracyMeters, jfloat verticalAccuracyMeters,
+ jfloat speedAccuracyMetersPerSecond, jfloat bearingAccuracyDegrees,
+ jlong timestamp, jint elapsedRealtimeFlags, jlong elapsedRealtimeNanos,
+ jdouble elapsedRealtimeUncertaintyNanos);
+ void injectBestLocation(jint gnssLocationFlags, jdouble latitudeDegrees,
+ jdouble longitudeDegrees, jdouble altitudeMeters,
+ jfloat speedMetersPerSec, jfloat bearingDegrees,
+ jfloat horizontalAccuracyMeters, jfloat verticalAccuracyMeters,
+ jfloat speedAccuracyMetersPerSecond, jfloat bearingAccuracyDegrees,
+ jlong timestamp, jint elapsedRealtimeFlags, jlong elapsedRealtimeNanos,
+ jdouble elapsedRealtimeUncertaintyNanos);
+
+ std::unique_ptr<AGnssInterface> getAGnssInterface();
+ std::unique_ptr<AGnssRilInterface> getAGnssRilInterface();
+ std::unique_ptr<GnssNavigationMessageInterface> getGnssNavigationMessageInterface();
+ std::unique_ptr<GnssMeasurementInterface> getGnssMeasurementInterface();
+ std::unique_ptr<GnssDebugInterface> getGnssDebugInterface();
+ std::unique_ptr<GnssConfigurationInterface> getGnssConfigurationInterface();
+ std::unique_ptr<GnssGeofenceInterface> getGnssGeofenceInterface();
+ std::unique_ptr<GnssBatchingInterface> getGnssBatchingInterface();
+ std::unique_ptr<MeasurementCorrectionsInterface> getMeasurementCorrectionsInterface();
+ std::unique_ptr<GnssVisibilityControlInterface> getGnssVisibilityControlInterface();
+ std::unique_ptr<GnssAntennaInfoInterface> getGnssAntennaInfoInterface();
+ std::unique_ptr<GnssPsdsInterface> getGnssPsdsInterface();
+
+ sp<hardware::gnss::IGnssPowerIndication> getGnssPowerIndicationInterface();
+ sp<hardware::gnss::V1_0::IGnssNi> getGnssNiInterface();
+
+private:
+ sp<GnssDeathRecipient> gnssHalDeathRecipient = nullptr;
+ sp<hardware::gnss::V1_0::IGnss> gnssHal = nullptr;
+ sp<hardware::gnss::V1_1::IGnss> gnssHal_V1_1 = nullptr;
+ sp<hardware::gnss::V2_0::IGnss> gnssHal_V2_0 = nullptr;
+ sp<hardware::gnss::V2_1::IGnss> gnssHal_V2_1 = nullptr;
+ sp<hardware::gnss::IGnss> gnssHalAidl = nullptr;
+};
+
+} // namespace android::gnss
+
+#endif // _ANDROID_SERVER_GNSS_Gnss_H
diff --git a/services/core/jni/gnss/GnssCallback.cpp b/services/core/jni/gnss/GnssCallback.cpp
new file mode 100644
index 000000000000..b931e915a753
--- /dev/null
+++ b/services/core/jni/gnss/GnssCallback.cpp
@@ -0,0 +1,413 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "GnssCallbckJni"
+
+#include "GnssCallback.h"
+
+#include <hardware_legacy/power.h>
+
+#define WAKE_LOCK_NAME "GPS"
+
+namespace android::gnss {
+
+using android::hardware::gnss::V1_0::GnssLocationFlags;
+using binder::Status;
+using hardware::hidl_vec;
+using hardware::Return;
+using hardware::Void;
+
+using GnssLocationAidl = android::hardware::gnss::GnssLocation;
+using GnssLocation_V1_0 = android::hardware::gnss::V1_0::GnssLocation;
+using GnssLocation_V2_0 = android::hardware::gnss::V2_0::GnssLocation;
+using IGnssCallbackAidl = android::hardware::gnss::IGnssCallback;
+using IGnssCallback_V1_0 = android::hardware::gnss::V1_0::IGnssCallback;
+using IGnssCallback_V2_0 = android::hardware::gnss::V2_0::IGnssCallback;
+using IGnssCallback_V2_1 = android::hardware::gnss::V2_1::IGnssCallback;
+
+jmethodID method_reportGnssServiceDied;
+
+namespace {
+
+jmethodID method_reportLocation;
+jmethodID method_reportStatus;
+jmethodID method_reportSvStatus;
+jmethodID method_reportNmea;
+jmethodID method_setTopHalCapabilities;
+jmethodID method_setGnssYearOfHardware;
+jmethodID method_setGnssHardwareModelName;
+jmethodID method_requestLocation;
+jmethodID method_requestUtcTime;
+
+// Returns true if location has lat/long information.
+inline bool hasLatLong(const GnssLocationAidl& location) {
+ return (location.gnssLocationFlags & hardware::gnss::GnssLocation::HAS_LAT_LONG) != 0;
+}
+
+// Returns true if location has lat/long information.
+inline bool hasLatLong(const GnssLocation_V1_0& location) {
+ return (static_cast<uint32_t>(location.gnssLocationFlags) & GnssLocationFlags::HAS_LAT_LONG) !=
+ 0;
+}
+
+// Returns true if location has lat/long information.
+inline bool hasLatLong(const GnssLocation_V2_0& location) {
+ return hasLatLong(location.v1_0);
+}
+
+inline jboolean boolToJbool(bool value) {
+ return value ? JNI_TRUE : JNI_FALSE;
+}
+
+// Must match the value from GnssMeasurement.java
+const uint32_t SVID_FLAGS_HAS_BASEBAND_CN0 = (1 << 4);
+
+} // anonymous namespace
+
+bool isSvStatusRegistered = false;
+bool isNmeaRegistered = false;
+
+void Gnss_class_init_once(JNIEnv* env, jclass& clazz) {
+ method_reportLocation =
+ env->GetMethodID(clazz, "reportLocation", "(ZLandroid/location/Location;)V");
+ method_reportStatus = env->GetMethodID(clazz, "reportStatus", "(I)V");
+ method_reportSvStatus = env->GetMethodID(clazz, "reportSvStatus", "(I[I[F[F[F[F[F)V");
+ method_reportNmea = env->GetMethodID(clazz, "reportNmea", "(J)V");
+
+ method_setTopHalCapabilities = env->GetMethodID(clazz, "setTopHalCapabilities", "(I)V");
+ method_setGnssYearOfHardware = env->GetMethodID(clazz, "setGnssYearOfHardware", "(I)V");
+ method_setGnssHardwareModelName =
+ env->GetMethodID(clazz, "setGnssHardwareModelName", "(Ljava/lang/String;)V");
+
+ method_requestLocation = env->GetMethodID(clazz, "requestLocation", "(ZZ)V");
+ method_requestUtcTime = env->GetMethodID(clazz, "requestUtcTime", "()V");
+ method_reportGnssServiceDied = env->GetMethodID(clazz, "reportGnssServiceDied", "()V");
+}
+
+Status GnssCallbackAidl::gnssSetCapabilitiesCb(const int capabilities) {
+ ALOGD("GnssCallbackAidl::%s: %du\n", __func__, capabilities);
+ JNIEnv* env = getJniEnv();
+ env->CallVoidMethod(mCallbacksObj, method_setTopHalCapabilities, capabilities);
+ checkAndClearExceptionFromCallback(env, __FUNCTION__);
+ return Status::ok();
+}
+
+Status GnssCallbackAidl::gnssStatusCb(const GnssStatusValue status) {
+ JNIEnv* env = getJniEnv();
+ env->CallVoidMethod(mCallbacksObj, method_reportStatus, status);
+ checkAndClearExceptionFromCallback(env, __FUNCTION__);
+ return Status::ok();
+}
+
+Status GnssCallbackAidl::gnssSvStatusCb(const std::vector<GnssSvInfo>& svInfoList) {
+ GnssCallbackHidl::gnssSvStatusCbImpl<std::vector<GnssSvInfo>, GnssSvInfo>(svInfoList);
+ return Status::ok();
+}
+
+Status GnssCallbackAidl::gnssLocationCb(const hardware::gnss::GnssLocation& location) {
+ GnssCallbackHidl::gnssLocationCbImpl<hardware::gnss::GnssLocation>(location);
+ return Status::ok();
+}
+
+Status GnssCallbackAidl::gnssNmeaCb(const int64_t timestamp, const std::string& nmea) {
+ // In AIDL v1, if no listener is registered, do not report nmea to the framework.
+ if (getInterfaceVersion() <= 1) {
+ if (!isNmeaRegistered) {
+ return Status::ok();
+ }
+ }
+ JNIEnv* env = getJniEnv();
+ /*
+ * The Java code will call back to read these values.
+ * We do this to avoid creating unnecessary String objects.
+ */
+ GnssCallbackHidl::sNmeaString = nmea.c_str();
+ GnssCallbackHidl::sNmeaStringLength = nmea.size();
+
+ env->CallVoidMethod(mCallbacksObj, method_reportNmea, timestamp);
+ checkAndClearExceptionFromCallback(env, __FUNCTION__);
+ return Status::ok();
+}
+
+Status GnssCallbackAidl::gnssAcquireWakelockCb() {
+ acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_NAME);
+ return Status::ok();
+}
+
+Status GnssCallbackAidl::gnssReleaseWakelockCb() {
+ release_wake_lock(WAKE_LOCK_NAME);
+ return Status::ok();
+}
+
+Status GnssCallbackAidl::gnssSetSystemInfoCb(const GnssSystemInfo& info) {
+ ALOGD("%s: yearOfHw=%d, name=%s\n", __func__, info.yearOfHw, info.name.c_str());
+ JNIEnv* env = getJniEnv();
+ env->CallVoidMethod(mCallbacksObj, method_setGnssYearOfHardware, info.yearOfHw);
+ jstring jstringName = env->NewStringUTF(info.name.c_str());
+ env->CallVoidMethod(mCallbacksObj, method_setGnssHardwareModelName, jstringName);
+ if (jstringName) {
+ env->DeleteLocalRef(jstringName);
+ }
+ checkAndClearExceptionFromCallback(env, __FUNCTION__);
+ return Status::ok();
+}
+
+Status GnssCallbackAidl::gnssRequestTimeCb() {
+ JNIEnv* env = getJniEnv();
+ env->CallVoidMethod(mCallbacksObj, method_requestUtcTime);
+ checkAndClearExceptionFromCallback(env, __FUNCTION__);
+ return Status::ok();
+}
+
+Status GnssCallbackAidl::gnssRequestLocationCb(const bool independentFromGnss,
+ const bool isUserEmergency) {
+ JNIEnv* env = getJniEnv();
+ env->CallVoidMethod(mCallbacksObj, method_requestLocation, boolToJbool(independentFromGnss),
+ boolToJbool(isUserEmergency));
+ checkAndClearExceptionFromCallback(env, __FUNCTION__);
+ return Status::ok();
+}
+
+// Implementation of IGnssCallbackHidl
+
+Return<void> GnssCallbackHidl::gnssNameCb(const android::hardware::hidl_string& name) {
+ ALOGD("%s: name=%s\n", __func__, name.c_str());
+
+ JNIEnv* env = getJniEnv();
+ jstring jstringName = env->NewStringUTF(name.c_str());
+ env->CallVoidMethod(mCallbacksObj, method_setGnssHardwareModelName, jstringName);
+ if (jstringName) {
+ env->DeleteLocalRef(jstringName);
+ }
+ checkAndClearExceptionFromCallback(env, __FUNCTION__);
+
+ return Void();
+}
+
+const char* GnssCallbackHidl::sNmeaString = nullptr;
+size_t GnssCallbackHidl::sNmeaStringLength = 0;
+
+template <class T>
+Return<void> GnssCallbackHidl::gnssLocationCbImpl(const T& location) {
+ JNIEnv* env = getJniEnv();
+
+ jobject jLocation = translateGnssLocation(env, location);
+
+ env->CallVoidMethod(mCallbacksObj, method_reportLocation, boolToJbool(hasLatLong(location)),
+ jLocation);
+ checkAndClearExceptionFromCallback(env, __FUNCTION__);
+ env->DeleteLocalRef(jLocation);
+ return Void();
+}
+
+Return<void> GnssCallbackHidl::gnssLocationCb(const GnssLocation_V1_0& location) {
+ return gnssLocationCbImpl<GnssLocation_V1_0>(location);
+}
+
+Return<void> GnssCallbackHidl::gnssLocationCb_2_0(const GnssLocation_V2_0& location) {
+ return gnssLocationCbImpl<GnssLocation_V2_0>(location);
+}
+
+Return<void> GnssCallbackHidl::gnssStatusCb(const IGnssCallback_V2_0::GnssStatusValue status) {
+ JNIEnv* env = getJniEnv();
+ env->CallVoidMethod(mCallbacksObj, method_reportStatus, status);
+ checkAndClearExceptionFromCallback(env, __FUNCTION__);
+ return Void();
+}
+
+template <>
+uint32_t GnssCallbackHidl::getHasBasebandCn0DbHzFlag(
+ const hidl_vec<IGnssCallback_V2_1::GnssSvInfo>& svStatus) {
+ return SVID_FLAGS_HAS_BASEBAND_CN0;
+}
+
+template <>
+uint32_t GnssCallbackHidl::getHasBasebandCn0DbHzFlag(
+ const std::vector<IGnssCallbackAidl::GnssSvInfo>& svStatus) {
+ return SVID_FLAGS_HAS_BASEBAND_CN0;
+}
+
+template <>
+double GnssCallbackHidl::getBasebandCn0DbHz(
+ const std::vector<IGnssCallbackAidl::GnssSvInfo>& svInfoList, size_t i) {
+ return svInfoList[i].basebandCN0DbHz;
+}
+
+template <>
+double GnssCallbackHidl::getBasebandCn0DbHz(
+ const hidl_vec<IGnssCallback_V2_1::GnssSvInfo>& svInfoList, size_t i) {
+ return svInfoList[i].basebandCN0DbHz;
+}
+
+template <>
+uint32_t GnssCallbackHidl::getGnssSvInfoListSize(const IGnssCallback_V1_0::GnssSvStatus& svStatus) {
+ return svStatus.numSvs;
+}
+
+template <>
+uint32_t GnssCallbackHidl::getConstellationType(const IGnssCallback_V1_0::GnssSvStatus& svStatus,
+ size_t i) {
+ return static_cast<uint32_t>(svStatus.gnssSvList.data()[i].constellation);
+}
+
+template <>
+uint32_t GnssCallbackHidl::getConstellationType(
+ const hidl_vec<IGnssCallback_V2_1::GnssSvInfo>& svInfoList, size_t i) {
+ return static_cast<uint32_t>(svInfoList[i].v2_0.constellation);
+}
+
+template <class T_list, class T_sv_info>
+Return<void> GnssCallbackHidl::gnssSvStatusCbImpl(const T_list& svStatus) {
+ // In HIDL or AIDL v1, if no listener is registered, do not report svInfoList to the framework.
+ if (!isSvStatusRegistered) {
+ return Void();
+ }
+
+ JNIEnv* env = getJniEnv();
+
+ uint32_t listSize = getGnssSvInfoListSize(svStatus);
+
+ jintArray svidWithFlagArray = env->NewIntArray(listSize);
+ jfloatArray cn0Array = env->NewFloatArray(listSize);
+ jfloatArray elevArray = env->NewFloatArray(listSize);
+ jfloatArray azimArray = env->NewFloatArray(listSize);
+ jfloatArray carrierFreqArray = env->NewFloatArray(listSize);
+ jfloatArray basebandCn0Array = env->NewFloatArray(listSize);
+
+ jint* svidWithFlags = env->GetIntArrayElements(svidWithFlagArray, 0);
+ jfloat* cn0s = env->GetFloatArrayElements(cn0Array, 0);
+ jfloat* elev = env->GetFloatArrayElements(elevArray, 0);
+ jfloat* azim = env->GetFloatArrayElements(azimArray, 0);
+ jfloat* carrierFreq = env->GetFloatArrayElements(carrierFreqArray, 0);
+ jfloat* basebandCn0s = env->GetFloatArrayElements(basebandCn0Array, 0);
+
+ /*
+ * Read GNSS SV info.
+ */
+ for (size_t i = 0; i < listSize; ++i) {
+ enum ShiftWidth : uint8_t { SVID_SHIFT_WIDTH = 12, CONSTELLATION_TYPE_SHIFT_WIDTH = 8 };
+
+ const T_sv_info& info = getGnssSvInfoOfIndex(svStatus, i);
+ svidWithFlags[i] = (info.svid << SVID_SHIFT_WIDTH) |
+ (getConstellationType(svStatus, i) << CONSTELLATION_TYPE_SHIFT_WIDTH) |
+ static_cast<uint32_t>(info.svFlag);
+ cn0s[i] = info.cN0Dbhz;
+ elev[i] = info.elevationDegrees;
+ azim[i] = info.azimuthDegrees;
+ carrierFreq[i] = info.carrierFrequencyHz;
+ svidWithFlags[i] |= getHasBasebandCn0DbHzFlag(svStatus);
+ basebandCn0s[i] = getBasebandCn0DbHz(svStatus, i);
+ }
+
+ env->ReleaseIntArrayElements(svidWithFlagArray, svidWithFlags, 0);
+ env->ReleaseFloatArrayElements(cn0Array, cn0s, 0);
+ env->ReleaseFloatArrayElements(elevArray, elev, 0);
+ env->ReleaseFloatArrayElements(azimArray, azim, 0);
+ env->ReleaseFloatArrayElements(carrierFreqArray, carrierFreq, 0);
+ env->ReleaseFloatArrayElements(basebandCn0Array, basebandCn0s, 0);
+
+ env->CallVoidMethod(mCallbacksObj, method_reportSvStatus, static_cast<jint>(listSize),
+ svidWithFlagArray, cn0Array, elevArray, azimArray, carrierFreqArray,
+ basebandCn0Array);
+
+ env->DeleteLocalRef(svidWithFlagArray);
+ env->DeleteLocalRef(cn0Array);
+ env->DeleteLocalRef(elevArray);
+ env->DeleteLocalRef(azimArray);
+ env->DeleteLocalRef(carrierFreqArray);
+ env->DeleteLocalRef(basebandCn0Array);
+
+ checkAndClearExceptionFromCallback(env, __FUNCTION__);
+ return Void();
+}
+
+Return<void> GnssCallbackHidl::gnssNmeaCb(int64_t timestamp,
+ const ::android::hardware::hidl_string& nmea) {
+ // In HIDL, if no listener is registered, do not report nmea to the framework.
+ if (!isNmeaRegistered) {
+ return Void();
+ }
+ JNIEnv* env = getJniEnv();
+ /*
+ * The Java code will call back to read these values.
+ * We do this to avoid creating unnecessary String objects.
+ */
+ sNmeaString = nmea.c_str();
+ sNmeaStringLength = nmea.size();
+
+ env->CallVoidMethod(mCallbacksObj, method_reportNmea, timestamp);
+ checkAndClearExceptionFromCallback(env, __FUNCTION__);
+ return Void();
+}
+
+Return<void> GnssCallbackHidl::gnssSetCapabilitesCb(uint32_t capabilities) {
+ ALOGD("%s: %du\n", __func__, capabilities);
+
+ JNIEnv* env = getJniEnv();
+ env->CallVoidMethod(mCallbacksObj, method_setTopHalCapabilities, capabilities);
+ checkAndClearExceptionFromCallback(env, __FUNCTION__);
+ return Void();
+}
+
+Return<void> GnssCallbackHidl::gnssSetCapabilitiesCb_2_0(uint32_t capabilities) {
+ return GnssCallbackHidl::gnssSetCapabilitesCb(capabilities);
+}
+
+Return<void> GnssCallbackHidl::gnssSetCapabilitiesCb_2_1(uint32_t capabilities) {
+ return GnssCallbackHidl::gnssSetCapabilitesCb(capabilities);
+}
+
+Return<void> GnssCallbackHidl::gnssAcquireWakelockCb() {
+ acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_NAME);
+ return Void();
+}
+
+Return<void> GnssCallbackHidl::gnssReleaseWakelockCb() {
+ release_wake_lock(WAKE_LOCK_NAME);
+ return Void();
+}
+
+Return<void> GnssCallbackHidl::gnssRequestTimeCb() {
+ JNIEnv* env = getJniEnv();
+ env->CallVoidMethod(mCallbacksObj, method_requestUtcTime);
+ checkAndClearExceptionFromCallback(env, __FUNCTION__);
+ return Void();
+}
+
+Return<void> GnssCallbackHidl::gnssRequestLocationCb(const bool independentFromGnss) {
+ return GnssCallbackHidl::gnssRequestLocationCb_2_0(independentFromGnss, /* isUserEmergency= */
+ false);
+}
+
+Return<void> GnssCallbackHidl::gnssRequestLocationCb_2_0(const bool independentFromGnss,
+ const bool isUserEmergency) {
+ JNIEnv* env = getJniEnv();
+ env->CallVoidMethod(mCallbacksObj, method_requestLocation, boolToJbool(independentFromGnss),
+ boolToJbool(isUserEmergency));
+ checkAndClearExceptionFromCallback(env, __FUNCTION__);
+ return Void();
+}
+
+Return<void> GnssCallbackHidl::gnssSetSystemInfoCb(const IGnssCallback_V2_0::GnssSystemInfo& info) {
+ ALOGD("%s: yearOfHw=%d\n", __func__, info.yearOfHw);
+
+ JNIEnv* env = getJniEnv();
+ env->CallVoidMethod(mCallbacksObj, method_setGnssYearOfHardware, info.yearOfHw);
+ checkAndClearExceptionFromCallback(env, __FUNCTION__);
+ return Void();
+}
+
+} // namespace android::gnss \ No newline at end of file
diff --git a/services/core/jni/gnss/GnssCallback.h b/services/core/jni/gnss/GnssCallback.h
new file mode 100644
index 000000000000..a7f96fb0f5c7
--- /dev/null
+++ b/services/core/jni/gnss/GnssCallback.h
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _ANDROID_SERVER_GNSS_GNSSCALLBACK_H
+#define _ANDROID_SERVER_GNSS_GNSSCALLBACK_H
+
+#pragma once
+
+#ifndef LOG_TAG
+#error LOG_TAG must be defined before including this file.
+#endif
+
+#include <android/hardware/gnss/2.1/IGnss.h>
+#include <android/hardware/gnss/BnGnssCallback.h>
+#include <log/log.h>
+
+#include <vector>
+
+#include "Utils.h"
+#include "jni.h"
+
+namespace android::gnss {
+
+namespace {
+
+extern jmethodID method_reportLocation;
+extern jmethodID method_reportStatus;
+extern jmethodID method_reportSvStatus;
+extern jmethodID method_reportNmea;
+extern jmethodID method_setTopHalCapabilities;
+extern jmethodID method_setGnssYearOfHardware;
+extern jmethodID method_setGnssHardwareModelName;
+extern jmethodID method_requestLocation;
+extern jmethodID method_requestUtcTime;
+
+} // anonymous namespace
+
+extern bool isSvStatusRegistered;
+extern bool isNmeaRegistered;
+
+extern jmethodID method_reportGnssServiceDied;
+
+void Gnss_class_init_once(JNIEnv* env, jclass& clazz);
+
+/*
+ * GnssCallbackAidl class implements the callback methods for AIDL IGnssCallback interface.
+ */
+class GnssCallbackAidl : public hardware::gnss::BnGnssCallback {
+public:
+ binder::Status gnssSetCapabilitiesCb(const int capabilities) override;
+ binder::Status gnssStatusCb(const GnssStatusValue status) override;
+ binder::Status gnssSvStatusCb(const std::vector<GnssSvInfo>& svInfoList) override;
+ binder::Status gnssLocationCb(const hardware::gnss::GnssLocation& location) override;
+ binder::Status gnssNmeaCb(const int64_t timestamp, const std::string& nmea) override;
+ binder::Status gnssAcquireWakelockCb() override;
+ binder::Status gnssReleaseWakelockCb() override;
+ binder::Status gnssSetSystemInfoCb(const GnssSystemInfo& info) override;
+ binder::Status gnssRequestTimeCb() override;
+ binder::Status gnssRequestLocationCb(const bool independentFromGnss,
+ const bool isUserEmergency) override;
+};
+
+/*
+ * GnssCallbackHidl class implements the callback methods for HIDL IGnssCallback interface.
+ */
+struct GnssCallbackHidl : public hardware::gnss::V2_1::IGnssCallback {
+ hardware::Return<void> gnssLocationCb(
+ const hardware::gnss::V1_0::GnssLocation& location) override;
+ hardware::Return<void> gnssStatusCb(
+ const hardware::gnss::V1_0::IGnssCallback::GnssStatusValue status) override;
+ hardware::Return<void> gnssSvStatusCb(
+ const hardware::gnss::V1_0::IGnssCallback::GnssSvStatus& svStatus) override {
+ return gnssSvStatusCbImpl<hardware::gnss::V1_0::IGnssCallback::GnssSvStatus,
+ hardware::gnss::V1_0::IGnssCallback::GnssSvInfo>(svStatus);
+ }
+ hardware::Return<void> gnssNmeaCb(int64_t timestamp,
+ const hardware::hidl_string& nmea) override;
+ hardware::Return<void> gnssSetCapabilitesCb(uint32_t capabilities) override;
+ hardware::Return<void> gnssAcquireWakelockCb() override;
+ hardware::Return<void> gnssReleaseWakelockCb() override;
+ hardware::Return<void> gnssRequestTimeCb() override;
+ hardware::Return<void> gnssRequestLocationCb(const bool independentFromGnss) override;
+
+ hardware::Return<void> gnssSetSystemInfoCb(
+ const hardware::gnss::V1_0::IGnssCallback::GnssSystemInfo& info) override;
+
+ // New in 1.1
+ hardware::Return<void> gnssNameCb(const hardware::hidl_string& name) override;
+
+ // New in 2.0
+ hardware::Return<void> gnssRequestLocationCb_2_0(const bool independentFromGnss,
+ const bool isUserEmergency) override;
+ hardware::Return<void> gnssSetCapabilitiesCb_2_0(uint32_t capabilities) override;
+ hardware::Return<void> gnssLocationCb_2_0(
+ const hardware::gnss::V2_0::GnssLocation& location) override;
+ hardware::Return<void> gnssSvStatusCb_2_0(
+ const hardware::hidl_vec<hardware::gnss::V2_0::IGnssCallback::GnssSvInfo>& svInfoList)
+ override {
+ return gnssSvStatusCbImpl<
+ hardware::hidl_vec<hardware::gnss::V2_0::IGnssCallback::GnssSvInfo>,
+ hardware::gnss::V1_0::IGnssCallback::GnssSvInfo>(svInfoList);
+ }
+
+ // New in 2.1
+ hardware::Return<void> gnssSvStatusCb_2_1(
+ const hardware::hidl_vec<hardware::gnss::V2_1::IGnssCallback::GnssSvInfo>& svInfoList)
+ override {
+ return gnssSvStatusCbImpl<
+ hardware::hidl_vec<hardware::gnss::V2_1::IGnssCallback::GnssSvInfo>,
+ hardware::gnss::V1_0::IGnssCallback::GnssSvInfo>(svInfoList);
+ }
+ hardware::Return<void> gnssSetCapabilitiesCb_2_1(uint32_t capabilities) override;
+
+ // TODO: Reconsider allocation cost vs threadsafety on these statics
+ static const char* sNmeaString;
+ static size_t sNmeaStringLength;
+
+ template <class T>
+ static hardware::Return<void> gnssLocationCbImpl(const T& location);
+
+ template <class T_list, class T_sv_info>
+ static hardware::Return<void> gnssSvStatusCbImpl(const T_list& svStatus);
+
+private:
+ template <class T>
+ static uint32_t getHasBasebandCn0DbHzFlag(const T& svStatus) {
+ return 0;
+ }
+
+ template <class T>
+ static double getBasebandCn0DbHz(const T& svStatus, size_t i) {
+ return 0.0;
+ }
+
+ template <class T>
+ static uint32_t getGnssSvInfoListSize(const T& svInfoList) {
+ return svInfoList.size();
+ }
+
+ static const hardware::gnss::IGnssCallback::GnssSvInfo& getGnssSvInfoOfIndex(
+ const std::vector<hardware::gnss::IGnssCallback::GnssSvInfo>& svInfoList, size_t i) {
+ return svInfoList[i];
+ }
+
+ static const hardware::gnss::V1_0::IGnssCallback::GnssSvInfo& getGnssSvInfoOfIndex(
+ const hardware::gnss::V1_0::IGnssCallback::GnssSvStatus& svStatus, size_t i) {
+ return svStatus.gnssSvList.data()[i];
+ }
+
+ static const hardware::gnss::V1_0::IGnssCallback::GnssSvInfo& getGnssSvInfoOfIndex(
+ const hardware::hidl_vec<hardware::gnss::V2_0::IGnssCallback::GnssSvInfo>& svInfoList,
+ size_t i) {
+ return svInfoList[i].v1_0;
+ }
+
+ static const hardware::gnss::V1_0::IGnssCallback::GnssSvInfo& getGnssSvInfoOfIndex(
+ const hardware::hidl_vec<hardware::gnss::V2_1::IGnssCallback::GnssSvInfo>& svInfoList,
+ size_t i) {
+ return svInfoList[i].v2_0.v1_0;
+ }
+
+ template <class T>
+ static uint32_t getConstellationType(const T& svInfoList, size_t i) {
+ return static_cast<uint32_t>(svInfoList[i].constellation);
+ }
+};
+
+} // namespace android::gnss
+
+#endif // _ANDROID_SERVER_GNSS_GNSSCALLBACK_H \ No newline at end of file
diff --git a/services/core/jni/gnss/GnssDebug.cpp b/services/core/jni/gnss/GnssDebug.cpp
index da5331760e5c..263a6c635f28 100644
--- a/services/core/jni/gnss/GnssDebug.cpp
+++ b/services/core/jni/gnss/GnssDebug.cpp
@@ -104,4 +104,10 @@ const android::hardware::gnss::IGnssDebug::SatelliteData& GnssDebugUtil::getSate
return satelliteDataArray[i];
}
+template <>
+int64_t GnssDebugUtil::getTimeEstimateMs(
+ const android::hardware::gnss::IGnssDebug::DebugData& data) {
+ return data.time.timeEstimateMs;
+}
+
} // namespace android::gnss
diff --git a/services/core/jni/gnss/GnssDebug.h b/services/core/jni/gnss/GnssDebug.h
index 1e1a7b4c1abd..e02c2ac838e7 100644
--- a/services/core/jni/gnss/GnssDebug.h
+++ b/services/core/jni/gnss/GnssDebug.h
@@ -113,12 +113,6 @@ int64_t GnssDebugUtil::getTimeEstimateMs(const T& data) {
return data.time.timeEstimate;
}
-template <>
-int64_t GnssDebugUtil::getTimeEstimateMs(
- const android::hardware::gnss::IGnssDebug::DebugData& data) {
- return data.time.timeEstimateMs;
-}
-
template <class T_DebugData, class T_SatelliteData>
jstring GnssDebugUtil::parseDebugData(JNIEnv* env, std::stringstream& internalState,
const T_DebugData& data) {
diff --git a/services/core/jni/gnss/GnssPsds.cpp b/services/core/jni/gnss/GnssPsds.cpp
new file mode 100644
index 000000000000..51a1450c0303
--- /dev/null
+++ b/services/core/jni/gnss/GnssPsds.cpp
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Define LOG_TAG before <log/log.h> to overwrite the default value.
+#define LOG_TAG "GnssPsdsJni"
+
+#include "GnssPsds.h"
+
+#include "Utils.h"
+
+using android::hardware::hidl_bitfield;
+using android::hardware::gnss::PsdsType;
+using IGnssPsdsAidl = android::hardware::gnss::IGnssPsds;
+using IGnssPsdsHidl = android::hardware::gnss::V1_0::IGnssXtra;
+
+namespace android::gnss {
+
+// Implementation of GnssPsds (AIDL HAL)
+
+GnssPsdsAidl::GnssPsdsAidl(const sp<IGnssPsdsAidl>& iGnssPsds) : mIGnssPsds(iGnssPsds) {
+ assert(mIGnssPsds != nullptr);
+}
+
+jboolean GnssPsdsAidl::setCallback(const std::unique_ptr<GnssPsdsCallback>& callback) {
+ auto status = mIGnssPsds->setCallback(callback->getAidl());
+ return checkAidlStatus(status, "IGnssPsdsAidl setCallback() failed.");
+}
+
+void GnssPsdsAidl::injectPsdsData(const jbyteArray& data, const jint& length,
+ const jint& psdsType) {
+ JNIEnv* env = getJniEnv();
+ jbyte* bytes = reinterpret_cast<jbyte*>(env->GetPrimitiveArrayCritical(data, 0));
+ auto status = mIGnssPsds->injectPsdsData(static_cast<PsdsType>(psdsType),
+ std::vector<uint8_t>((const uint8_t*)bytes,
+ (const uint8_t*)bytes + length));
+ checkAidlStatus(status, "IGnssPsdsAidl injectPsdsData() failed.");
+ env->ReleasePrimitiveArrayCritical(data, bytes, JNI_ABORT);
+}
+
+// Implementation of GnssPsdsHidl
+
+GnssPsdsHidl::GnssPsdsHidl(const sp<android::hardware::gnss::V1_0::IGnssXtra>& iGnssXtra)
+ : mIGnssXtra(iGnssXtra) {
+ assert(mIGnssXtra != nullptr);
+}
+
+jboolean GnssPsdsHidl::setCallback(const std::unique_ptr<GnssPsdsCallback>& callback) {
+ auto result = mIGnssXtra->setCallback(callback->getHidl());
+ return checkHidlReturn(result, "IGnssPsdsHidl setCallback() failed.");
+}
+
+void GnssPsdsHidl::injectPsdsData(const jbyteArray& data, const jint& length, const jint&) {
+ JNIEnv* env = getJniEnv();
+ jbyte* bytes = reinterpret_cast<jbyte*>(env->GetPrimitiveArrayCritical(data, 0));
+ auto result = mIGnssXtra->injectXtraData(std::string((const char*)bytes, length));
+ checkHidlReturn(result, "IGnssXtra injectXtraData() failed.");
+ env->ReleasePrimitiveArrayCritical(data, bytes, JNI_ABORT);
+}
+
+} // namespace android::gnss
diff --git a/services/core/jni/gnss/GnssPsds.h b/services/core/jni/gnss/GnssPsds.h
new file mode 100644
index 000000000000..6b65ee8614f8
--- /dev/null
+++ b/services/core/jni/gnss/GnssPsds.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _ANDROID_SERVER_GNSS_GNSSPSDS_H
+#define _ANDROID_SERVER_GNSS_GNSSPSDS_H
+
+#pragma once
+
+#ifndef LOG_TAG
+#error LOG_TAG must be defined before including this file.
+#endif
+
+#include <android/hardware/gnss/1.0/IGnssXtra.h>
+#include <android/hardware/gnss/BnGnssPsds.h>
+#include <log/log.h>
+
+#include "GnssPsdsCallback.h"
+#include "jni.h"
+
+namespace android::gnss {
+
+class GnssPsdsInterface {
+public:
+ virtual ~GnssPsdsInterface() {}
+ virtual jboolean setCallback(const std::unique_ptr<GnssPsdsCallback>& callback);
+ virtual void injectPsdsData(const jbyteArray& data, const jint& length, const jint& psdsType);
+};
+
+class GnssPsdsAidl : public GnssPsdsInterface {
+public:
+ GnssPsdsAidl(const sp<android::hardware::gnss::IGnssPsds>& iGnssPsds);
+ jboolean setCallback(const std::unique_ptr<GnssPsdsCallback>& callback) override;
+ void injectPsdsData(const jbyteArray& data, const jint& length, const jint& psdsType) override;
+
+private:
+ const sp<android::hardware::gnss::IGnssPsds> mIGnssPsds;
+};
+
+class GnssPsdsHidl : public GnssPsdsInterface {
+public:
+ GnssPsdsHidl(const sp<android::hardware::gnss::V1_0::IGnssXtra>& iGnssXtra);
+ jboolean setCallback(const std::unique_ptr<GnssPsdsCallback>& callback) override;
+ void injectPsdsData(const jbyteArray& data, const jint& length, const jint& psdsType) override;
+
+private:
+ const sp<android::hardware::gnss::V1_0::IGnssXtra> mIGnssXtra;
+};
+
+} // namespace android::gnss
+
+#endif // _ANDROID_SERVER_GNSS_GNSSPSDS_H
diff --git a/services/core/jni/gnss/GnssPsdsCallback.cpp b/services/core/jni/gnss/GnssPsdsCallback.cpp
new file mode 100644
index 000000000000..1dd7022c66ee
--- /dev/null
+++ b/services/core/jni/gnss/GnssPsdsCallback.cpp
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "GnssPsdsCbJni"
+
+#include "GnssPsdsCallback.h"
+
+#include <vector>
+
+#include "Utils.h"
+
+namespace android::gnss {
+
+namespace {
+jmethodID method_psdsDownloadRequest;
+} // anonymous namespace
+
+using binder::Status;
+using hardware::Return;
+using hardware::Void;
+using hardware::gnss::PsdsType;
+
+void GnssPsds_class_init_once(JNIEnv* env, jclass clazz) {
+ method_psdsDownloadRequest = env->GetMethodID(clazz, "psdsDownloadRequest", "(I)V");
+}
+
+// Implementation of android::hardware::gnss::IGnssPsdsCallback
+
+Status GnssPsdsCallbackAidl::downloadRequestCb(PsdsType psdsType) {
+ ALOGD("%s. psdsType: %d", __func__, static_cast<int32_t>(psdsType));
+ JNIEnv* env = getJniEnv();
+ env->CallVoidMethod(mCallbacksObj, method_psdsDownloadRequest, psdsType);
+ checkAndClearExceptionFromCallback(env, __FUNCTION__);
+ return Status::ok();
+}
+
+// Implementation of android::hardware::gnss::V1_0::IGnssPsdsCallback
+
+Return<void> GnssPsdsCallbackHidl::downloadRequestCb() {
+ JNIEnv* env = getJniEnv();
+ env->CallVoidMethod(mCallbacksObj, method_psdsDownloadRequest, /* psdsType= */ 1);
+ checkAndClearExceptionFromCallback(env, __FUNCTION__);
+ return Void();
+}
+
+} // namespace android::gnss
diff --git a/services/core/jni/gnss/GnssPsdsCallback.h b/services/core/jni/gnss/GnssPsdsCallback.h
new file mode 100644
index 000000000000..3ac7473c42a9
--- /dev/null
+++ b/services/core/jni/gnss/GnssPsdsCallback.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _ANDROID_SERVER_GNSS_GNSSPSDSCALLBACK_H
+#define _ANDROID_SERVER_GNSS_GNSSPSDSCALLBACK_H
+
+#pragma once
+
+#ifndef LOG_TAG
+#error LOG_TAG must be defined before including this file.
+#endif
+
+#include <android/hardware/gnss/1.0/IGnssXtraCallback.h>
+#include <android/hardware/gnss/BnGnssPsdsCallback.h>
+#include <log/log.h>
+
+#include "jni.h"
+
+namespace android::gnss {
+
+namespace {
+extern jmethodID method_psdsDownloadRequest;
+} // anonymous namespace
+
+void GnssPsds_class_init_once(JNIEnv* env, jclass clazz);
+
+class GnssPsdsCallbackAidl : public hardware::gnss::BnGnssPsdsCallback {
+public:
+ GnssPsdsCallbackAidl() {}
+ binder::Status downloadRequestCb(hardware::gnss::PsdsType psdsType) override;
+};
+
+class GnssPsdsCallbackHidl : public hardware::gnss::V1_0::IGnssXtraCallback {
+public:
+ GnssPsdsCallbackHidl() {}
+ hardware::Return<void> downloadRequestCb() override;
+};
+
+class GnssPsdsCallback {
+public:
+ GnssPsdsCallback() {}
+ sp<GnssPsdsCallbackAidl> getAidl() {
+ if (callbackAidl == nullptr) {
+ callbackAidl = sp<GnssPsdsCallbackAidl>::make();
+ }
+ return callbackAidl;
+ }
+
+ sp<GnssPsdsCallbackHidl> getHidl() {
+ if (callbackHidl == nullptr) {
+ callbackHidl = sp<GnssPsdsCallbackHidl>::make();
+ }
+ return callbackHidl;
+ }
+
+private:
+ sp<GnssPsdsCallbackAidl> callbackAidl;
+ sp<GnssPsdsCallbackHidl> callbackHidl;
+};
+
+} // namespace android::gnss
+
+#endif // _ANDROID_SERVER_GNSS_GNSSPSDSCALLBACK_H \ No newline at end of file
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 091d879a1876..5aa3dfea3ea4 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -31,20 +31,6 @@ import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_DEV
import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE;
import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_USER;
import static android.app.admin.DevicePolicyManager.ACTION_SYSTEM_UPDATE_POLICY_CHANGED;
-import static android.app.admin.DevicePolicyManager.CODE_ACCOUNTS_NOT_EMPTY;
-import static android.app.admin.DevicePolicyManager.CODE_CANNOT_ADD_MANAGED_PROFILE;
-import static android.app.admin.DevicePolicyManager.CODE_DEVICE_ADMIN_NOT_SUPPORTED;
-import static android.app.admin.DevicePolicyManager.CODE_HAS_DEVICE_OWNER;
-import static android.app.admin.DevicePolicyManager.CODE_HAS_PAIRED;
-import static android.app.admin.DevicePolicyManager.CODE_MANAGED_USERS_NOT_SUPPORTED;
-import static android.app.admin.DevicePolicyManager.CODE_NONSYSTEM_USER_EXISTS;
-import static android.app.admin.DevicePolicyManager.CODE_NOT_SYSTEM_USER;
-import static android.app.admin.DevicePolicyManager.CODE_OK;
-import static android.app.admin.DevicePolicyManager.CODE_PROVISIONING_NOT_ALLOWED_FOR_NON_DEVELOPER_USERS;
-import static android.app.admin.DevicePolicyManager.CODE_SYSTEM_USER;
-import static android.app.admin.DevicePolicyManager.CODE_USER_HAS_PROFILE_OWNER;
-import static android.app.admin.DevicePolicyManager.CODE_USER_NOT_RUNNING;
-import static android.app.admin.DevicePolicyManager.CODE_USER_SETUP_COMPLETED;
import static android.app.admin.DevicePolicyManager.DELEGATION_APP_RESTRICTIONS;
import static android.app.admin.DevicePolicyManager.DELEGATION_BLOCK_UNINSTALL;
import static android.app.admin.DevicePolicyManager.DELEGATION_CERT_INSTALL;
@@ -101,6 +87,20 @@ import static android.app.admin.DevicePolicyManager.PRIVATE_DNS_SET_ERROR_FAILUR
import static android.app.admin.DevicePolicyManager.PRIVATE_DNS_SET_NO_ERROR;
import static android.app.admin.DevicePolicyManager.PROFILE_KEYGUARD_FEATURES_AFFECT_OWNER;
import static android.app.admin.DevicePolicyManager.STATE_USER_UNMANAGED;
+import static android.app.admin.DevicePolicyManager.STATUS_ACCOUNTS_NOT_EMPTY;
+import static android.app.admin.DevicePolicyManager.STATUS_CANNOT_ADD_MANAGED_PROFILE;
+import static android.app.admin.DevicePolicyManager.STATUS_DEVICE_ADMIN_NOT_SUPPORTED;
+import static android.app.admin.DevicePolicyManager.STATUS_HAS_DEVICE_OWNER;
+import static android.app.admin.DevicePolicyManager.STATUS_HAS_PAIRED;
+import static android.app.admin.DevicePolicyManager.STATUS_MANAGED_USERS_NOT_SUPPORTED;
+import static android.app.admin.DevicePolicyManager.STATUS_NONSYSTEM_USER_EXISTS;
+import static android.app.admin.DevicePolicyManager.STATUS_NOT_SYSTEM_USER;
+import static android.app.admin.DevicePolicyManager.STATUS_OK;
+import static android.app.admin.DevicePolicyManager.STATUS_PROVISIONING_NOT_ALLOWED_FOR_NON_DEVELOPER_USERS;
+import static android.app.admin.DevicePolicyManager.STATUS_SYSTEM_USER;
+import static android.app.admin.DevicePolicyManager.STATUS_USER_HAS_PROFILE_OWNER;
+import static android.app.admin.DevicePolicyManager.STATUS_USER_NOT_RUNNING;
+import static android.app.admin.DevicePolicyManager.STATUS_USER_SETUP_COMPLETED;
import static android.app.admin.DevicePolicyManager.WIPE_EUICC;
import static android.app.admin.DevicePolicyManager.WIPE_EXTERNAL_STORAGE;
import static android.app.admin.DevicePolicyManager.WIPE_RESET_PROTECTION_DATA;
@@ -1999,9 +1999,6 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
mOwners.load();
setDeviceOwnershipSystemPropertyLocked();
findOwnerComponentIfNecessaryLocked();
-
- // TODO PO may not have a class name either due to b/17652534. Address that too.
- updateDeviceOwnerLocked();
}
}
@@ -3142,23 +3139,6 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
}
- private void updateDeviceOwnerLocked() {
- long ident = mInjector.binderClearCallingIdentity();
- try {
- // TODO This is to prevent DO from getting "clear data"ed, but it should also check the
- // user id and also protect all other DAs too.
- final ComponentName deviceOwnerComponent = mOwners.getDeviceOwnerComponent();
- if (deviceOwnerComponent != null) {
- mInjector.getIActivityManager()
- .updateDeviceOwner(deviceOwnerComponent.getPackageName());
- }
- } catch (RemoteException e) {
- // Not gonna happen.
- } finally {
- mInjector.binderRestoreCallingIdentity(ident);
- }
- }
-
static void validateQualityConstant(int quality) {
switch (quality) {
case PASSWORD_QUALITY_UNSPECIFIED:
@@ -8590,7 +8570,6 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
mOwners.setDeviceOwner(admin, ownerName, userId);
mOwners.writeDeviceOwner();
- updateDeviceOwnerLocked();
setDeviceOwnershipSystemPropertyLocked();
//TODO(b/180371154): when provisionFullyManagedDevice is used in tests, remove this
@@ -8951,7 +8930,6 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
mOwners.clearDeviceOwner();
mOwners.writeDeviceOwner();
- updateDeviceOwnerLocked();
clearDeviceOwnerUserRestriction(UserHandle.of(userId));
mInjector.securityLogSetLoggingEnabledProperty(false);
@@ -9658,7 +9636,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
final int code = checkDeviceOwnerProvisioningPreConditionLocked(owner,
/* deviceOwnerUserId= */ deviceOwnerUserId, /* callingUserId*/ caller.getUserId(),
isAdb(caller), hasIncompatibleAccountsOrNonAdb);
- if (code != CODE_OK) {
+ if (code != STATUS_OK) {
throw new IllegalStateException(
computeProvisioningErrorString(code, deviceOwnerUserId));
}
@@ -9666,25 +9644,25 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
private static String computeProvisioningErrorString(int code, @UserIdInt int userId) {
switch (code) {
- case CODE_OK:
+ case STATUS_OK:
return "OK";
- case CODE_HAS_DEVICE_OWNER:
+ case STATUS_HAS_DEVICE_OWNER:
return "Trying to set the device owner, but device owner is already set.";
- case CODE_USER_HAS_PROFILE_OWNER:
+ case STATUS_USER_HAS_PROFILE_OWNER:
return "Trying to set the device owner, but the user already has a profile owner.";
- case CODE_USER_NOT_RUNNING:
+ case STATUS_USER_NOT_RUNNING:
return "User " + userId + " not running.";
- case CODE_NOT_SYSTEM_USER:
+ case STATUS_NOT_SYSTEM_USER:
return "User " + userId + " is not system user.";
- case CODE_USER_SETUP_COMPLETED:
+ case STATUS_USER_SETUP_COMPLETED:
return "Cannot set the device owner if the device is already set-up.";
- case CODE_NONSYSTEM_USER_EXISTS:
+ case STATUS_NONSYSTEM_USER_EXISTS:
return "Not allowed to set the device owner because there are already several"
+ " users on the device.";
- case CODE_ACCOUNTS_NOT_EMPTY:
+ case STATUS_ACCOUNTS_NOT_EMPTY:
return "Not allowed to set the device owner because there are already some accounts"
+ " on the device.";
- case CODE_HAS_PAIRED:
+ case STATUS_HAS_PAIRED:
return "Not allowed to set the device owner because this device has already "
+ "paired.";
default:
@@ -14159,30 +14137,30 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
mInjector.binderRestoreCallingIdentity(ident);
}
- return checkProvisioningPreConditionSkipPermission(action, packageName) == CODE_OK;
+ return checkProvisioningPreconditionSkipPermission(action, packageName) == STATUS_OK;
}
@Override
- public int checkProvisioningPreCondition(String action, String packageName) {
+ public int checkProvisioningPrecondition(String action, String packageName) {
Objects.requireNonNull(packageName, "packageName is null");
Preconditions.checkCallAuthorization(
hasCallingOrSelfPermission(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS));
- return checkProvisioningPreConditionSkipPermission(action, packageName);
+ return checkProvisioningPreconditionSkipPermission(action, packageName);
}
- private int checkProvisioningPreConditionSkipPermission(String action,
+ private int checkProvisioningPreconditionSkipPermission(String action,
String packageName) {
if (!mHasFeature) {
logMissingFeatureAction("Cannot check provisioning for action " + action);
- return CODE_DEVICE_ADMIN_NOT_SUPPORTED;
+ return STATUS_DEVICE_ADMIN_NOT_SUPPORTED;
}
if (!isProvisioningAllowed()) {
- return CODE_PROVISIONING_NOT_ALLOWED_FOR_NON_DEVELOPER_USERS;
+ return STATUS_PROVISIONING_NOT_ALLOWED_FOR_NON_DEVELOPER_USERS;
}
final int code = checkProvisioningPreConditionSkipPermissionNoLog(action, packageName);
- if (code != CODE_OK) {
+ if (code != STATUS_OK) {
Slogf.d(LOG_TAG, "checkProvisioningPreCondition(" + action + ", " + packageName
+ ") failed: "
+ computeProvisioningErrorString(code, mInjector.userHandleGetCallingUserId()));
@@ -14230,27 +14208,27 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@UserIdInt int deviceOwnerUserId, @UserIdInt int callingUserId, boolean isAdb,
boolean hasIncompatibleAccountsOrNonAdb) {
if (mOwners.hasDeviceOwner()) {
- return CODE_HAS_DEVICE_OWNER;
+ return STATUS_HAS_DEVICE_OWNER;
}
if (mOwners.hasProfileOwner(deviceOwnerUserId)) {
- return CODE_USER_HAS_PROFILE_OWNER;
+ return STATUS_USER_HAS_PROFILE_OWNER;
}
boolean isHeadlessSystemUserMode = mInjector.userManagerIsHeadlessSystemUserMode();
// System user is always running in headless system user mode.
if (!isHeadlessSystemUserMode
&& !mUserManager.isUserRunning(new UserHandle(deviceOwnerUserId))) {
- return CODE_USER_NOT_RUNNING;
+ return STATUS_USER_NOT_RUNNING;
}
if (mIsWatch && hasPaired(UserHandle.USER_SYSTEM)) {
- return CODE_HAS_PAIRED;
+ return STATUS_HAS_PAIRED;
}
if (isHeadlessSystemUserMode) {
if (deviceOwnerUserId != UserHandle.USER_SYSTEM) {
Slogf.e(LOG_TAG, "In headless system user mode, "
+ "device owner can only be set on headless system user.");
- return CODE_NOT_SYSTEM_USER;
+ return STATUS_NOT_SYSTEM_USER;
}
}
@@ -14265,7 +14243,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
int maxNumberOfExistingUsers = isHeadlessSystemUserMode ? 2 : 1;
if (mUserManager.getUserCount() > maxNumberOfExistingUsers) {
- return CODE_NONSYSTEM_USER_EXISTS;
+ return STATUS_NONSYSTEM_USER_EXISTS;
}
int currentForegroundUser = getCurrentForegroundUserId();
@@ -14274,23 +14252,23 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
&& currentForegroundUser == UserHandle.USER_SYSTEM) {
Slogf.wtf(LOG_TAG, "In headless system user mode, "
+ "current user cannot be system user when setting device owner");
- return CODE_SYSTEM_USER;
+ return STATUS_SYSTEM_USER;
}
if (hasIncompatibleAccountsOrNonAdb) {
- return CODE_ACCOUNTS_NOT_EMPTY;
+ return STATUS_ACCOUNTS_NOT_EMPTY;
}
}
- return CODE_OK;
+ return STATUS_OK;
} else {
// DO has to be user 0
if (deviceOwnerUserId != UserHandle.USER_SYSTEM) {
- return CODE_NOT_SYSTEM_USER;
+ return STATUS_NOT_SYSTEM_USER;
}
// Only provision DO before setup wizard completes
if (hasUserSetupCompleted(UserHandle.USER_SYSTEM)) {
- return CODE_USER_SETUP_COMPLETED;
+ return STATUS_USER_SETUP_COMPLETED;
}
- return CODE_OK;
+ return STATUS_OK;
}
}
@@ -14311,11 +14289,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
private int checkManagedProfileProvisioningPreCondition(String packageName,
@UserIdInt int callingUserId) {
if (!hasFeatureManagedUsers()) {
- return CODE_MANAGED_USERS_NOT_SUPPORTED;
+ return STATUS_MANAGED_USERS_NOT_SUPPORTED;
}
if (getProfileOwnerAsUser(callingUserId) != null) {
// Managed user cannot have a managed profile.
- return CODE_USER_HAS_PROFILE_OWNER;
+ return STATUS_USER_HAS_PROFILE_OWNER;
}
final long ident = mInjector.binderClearCallingIdentity();
@@ -14334,7 +14312,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
callingUserId);
// The check is called from inside a managed profile. A managed profile cannot
// be provisioned from within another managed profile.
- return CODE_CANNOT_ADD_MANAGED_PROFILE;
+ return STATUS_CANNOT_ADD_MANAGED_PROFILE;
}
// If there's a device owner, the restriction on adding a managed profile must be set.
@@ -14346,19 +14324,19 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (addingProfileRestricted) {
Slogf.i(LOG_TAG, "Adding a profile is restricted: User %s Has device owner? %b",
callingUserHandle, hasDeviceOwner);
- return CODE_CANNOT_ADD_MANAGED_PROFILE;
+ return STATUS_CANNOT_ADD_MANAGED_PROFILE;
}
// Bail out if we are trying to provision a work profile but one already exists.
if (!mUserManager.canAddMoreManagedProfiles(
callingUserId, /* allowedToRemoveOne= */ false)) {
Slogf.i(LOG_TAG, "A work profile already exists.");
- return CODE_CANNOT_ADD_MANAGED_PROFILE;
+ return STATUS_CANNOT_ADD_MANAGED_PROFILE;
}
} finally {
mInjector.binderRestoreCallingIdentity(ident);
}
- return CODE_OK;
+ return STATUS_OK;
}
private void checkIsDeviceOwner(CallerIdentity caller) {
@@ -17697,9 +17675,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
UserInfo userInfo = null;
final long identity = Binder.clearCallingIdentity();
try {
- final int result = checkProvisioningPreConditionSkipPermission(
+ final int result = checkProvisioningPreconditionSkipPermission(
ACTION_PROVISION_MANAGED_PROFILE, admin.getPackageName());
- if (result != CODE_OK) {
+ if (result != STATUS_OK) {
throw new ServiceSpecificException(
ERROR_PRE_CONDITION_FAILED,
"Provisioning preconditions failed with result: " + result);
@@ -18076,9 +18054,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
final long identity = Binder.clearCallingIdentity();
try {
- int result = checkProvisioningPreConditionSkipPermission(
+ int result = checkProvisioningPreconditionSkipPermission(
ACTION_PROVISION_MANAGED_DEVICE, deviceAdmin.getPackageName());
- if (result != CODE_OK) {
+ if (result != STATUS_OK) {
throw new ServiceSpecificException(
ERROR_PRE_CONDITION_FAILED,
"Provisioning preconditions failed with result: " + result);
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index e895d377f607..5098abe4bb55 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -386,8 +386,8 @@ public final class SystemServer implements Dumpable {
"com.android.server.DeviceIdleController";
private static final String BLOB_STORE_MANAGER_SERVICE_CLASS =
"com.android.server.blob.BlobStoreManagerService";
- private static final String APP_SEARCH_MANAGER_SERVICE_CLASS =
- "com.android.server.appsearch.AppSearchManagerService";
+ private static final String APPSEARCH_MODULE_LIFECYCLE_CLASS =
+ "com.android.server.appsearch.AppSearchModule$Lifecycle";
private static final String ISOLATED_COMPILATION_SERVICE_CLASS =
"com.android.server.compos.IsolatedCompilationService";
private static final String ROLLBACK_MANAGER_SERVICE_CLASS =
@@ -2764,8 +2764,8 @@ public final class SystemServer implements Dumpable {
mSystemServiceManager.startService(SAFETY_CENTER_SERVICE_CLASS);
t.traceEnd();
- t.traceBegin("AppSearchManagerService");
- mSystemServiceManager.startService(APP_SEARCH_MANAGER_SERVICE_CLASS);
+ t.traceBegin("AppSearchModule");
+ mSystemServiceManager.startService(APPSEARCH_MODULE_LIFECYCLE_CLASS);
t.traceEnd();
if (SystemProperties.getBoolean("ro.config.isolated_compilation_enabled", false)) {
diff --git a/services/midi/java/com/android/server/midi/MidiService.java b/services/midi/java/com/android/server/midi/MidiService.java
index 2d082e157692..bcdbc5dd9657 100644
--- a/services/midi/java/com/android/server/midi/MidiService.java
+++ b/services/midi/java/com/android/server/midi/MidiService.java
@@ -768,7 +768,6 @@ public class MidiService extends IMidiManager.Stub {
synchronized (mDevicesByInfo) {
for (Device device : mDevicesByInfo.values()) {
if (device.isUidAllowed(uid)) {
- deviceInfos.add(device.getDeviceInfo());
// UMP devices have protocols that are not PROTOCOL_UNKNOWN
if (transport == MidiManager.TRANSPORT_UNIVERSAL_MIDI_PACKETS) {
if (device.getDeviceInfo().getDefaultProtocol()
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt
index cd2d0fce6691..83ccabf03935 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt
@@ -238,6 +238,7 @@ class AndroidPackageTest : ParcelableComponentTest(AndroidPackage::class, Packag
AndroidPackage::isVendor,
AndroidPackage::isVisibleToInstantApps,
AndroidPackage::isVmSafeMode,
+ AndroidPackage::isLeavingSharedUid,
AndroidPackage::isResetEnabledSettingsOnAppDataCleared,
AndroidPackage::getMaxAspectRatio,
AndroidPackage::getMinAspectRatio,
diff --git a/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java b/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java
index d2358a08624d..023608c68ee3 100644
--- a/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java
@@ -1167,7 +1167,14 @@ public class GameManagerServiceTests {
startUser(gameManagerService, USER_ID_1);
gameManagerService.setGameMode(
mPackageName, GameManager.GAME_MODE_PERFORMANCE, USER_ID_1);
- GameState gameState = new GameState(isLoading, GameState.MODE_NONE);
+ int testMode = GameState.MODE_NONE;
+ int testLabel = 99;
+ int testQuality = 123;
+ GameState gameState = new GameState(isLoading, testMode, testLabel, testQuality);
+ assertEquals(isLoading, gameState.isLoading());
+ assertEquals(testMode, gameState.getMode());
+ assertEquals(testLabel, gameState.getLabel());
+ assertEquals(testQuality, gameState.getQuality());
gameManagerService.setGameState(mPackageName, gameState, USER_ID_1);
mTestLooper.dispatchAll();
verify(mMockPowerManager, times(1)).setPowerMode(Mode.GAME_LOADING, isLoading);
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
index 890a5495ef16..4e4854c6688d 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
@@ -333,6 +333,10 @@ public class LocationProviderManagerTest {
@Test
public void testGetLastLocation_Bypass() {
+ mInjector.getSettingsHelper().setIgnoreSettingsAllowlist(
+ new PackageTagsList.Builder().add(
+ IDENTITY.getPackageName()).build());
+
assertThat(mManager.getLastLocation(new LastLocationRequest.Builder().build(), IDENTITY,
PERMISSION_FINE)).isNull();
assertThat(mManager.getLastLocation(
@@ -381,6 +385,14 @@ public class LocationProviderManagerTest {
new LastLocationRequest.Builder().setLocationSettingsIgnored(true).build(),
IDENTITY, PERMISSION_FINE)).isEqualTo(
loc);
+
+ mInjector.getSettingsHelper().setIgnoreSettingsAllowlist(
+ new PackageTagsList.Builder().build());
+ mProvider.setProviderAllowed(false);
+
+ assertThat(mManager.getLastLocation(
+ new LastLocationRequest.Builder().setLocationSettingsIgnored(true).build(),
+ IDENTITY, PERMISSION_FINE)).isNull();
}
@Test
diff --git a/services/tests/mockingservicestests/src/com/android/server/tare/AgentTest.java b/services/tests/mockingservicestests/src/com/android/server/tare/AgentTest.java
index 6751b804ad9e..41d46f223f4b 100644
--- a/services/tests/mockingservicestests/src/com/android/server/tare/AgentTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/tare/AgentTest.java
@@ -73,6 +73,7 @@ public class AgentTest {
.startMocking();
when(mIrs.getContext()).thenReturn(mContext);
when(mIrs.getCompleteEconomicPolicyLocked()).thenReturn(mEconomicPolicy);
+ when(mIrs.getLock()).thenReturn(mIrs);
when(mContext.getSystemService(Context.ALARM_SERVICE)).thenReturn(mock(AlarmManager.class));
mScribe = new MockScribe(mIrs);
}
@@ -89,75 +90,75 @@ public class AgentTest {
Agent agent = new Agent(mIrs, mScribe);
Ledger ledger = new Ledger();
- doReturn(1_000_000L).when(mIrs).getMaxCirculationLocked();
+ doReturn(1_000_000L).when(mIrs).getConsumptionLimitLocked();
doReturn(1_000_000L).when(mEconomicPolicy).getMaxSatiatedBalance();
- Ledger.Transaction transaction = new Ledger.Transaction(0, 0, 0, null, 5);
+ Ledger.Transaction transaction = new Ledger.Transaction(0, 0, 0, null, 5, 0);
agent.recordTransactionLocked(0, "com.test", ledger, transaction, false);
assertEquals(5, ledger.getCurrentBalance());
- transaction = new Ledger.Transaction(0, 0, 0, null, 995);
+ transaction = new Ledger.Transaction(0, 0, 0, null, 995, 0);
agent.recordTransactionLocked(0, "com.test", ledger, transaction, false);
assertEquals(1000, ledger.getCurrentBalance());
- transaction = new Ledger.Transaction(0, 0, 0, null, -500);
+ transaction = new Ledger.Transaction(0, 0, 0, null, -500, 250);
agent.recordTransactionLocked(0, "com.test", ledger, transaction, false);
assertEquals(500, ledger.getCurrentBalance());
- transaction = new Ledger.Transaction(0, 0, 0, null, 999_500L);
+ transaction = new Ledger.Transaction(0, 0, 0, null, 999_500L, 500);
agent.recordTransactionLocked(0, "com.test", ledger, transaction, false);
assertEquals(1_000_000L, ledger.getCurrentBalance());
- transaction = new Ledger.Transaction(0, 0, 0, null, -1_000_001L);
+ transaction = new Ledger.Transaction(0, 0, 0, null, -1_000_001L, 1000);
agent.recordTransactionLocked(0, "com.test", ledger, transaction, false);
assertEquals(-1, ledger.getCurrentBalance());
}
@Test
- public void testRecordTransaction_MaxCirculation() {
+ public void testRecordTransaction_MaxConsumptionLimit() {
Agent agent = new Agent(mIrs, mScribe);
Ledger ledger = new Ledger();
- doReturn(1000L).when(mIrs).getMaxCirculationLocked();
- doReturn(1000L).when(mEconomicPolicy).getMaxSatiatedBalance();
+ doReturn(1000L).when(mIrs).getConsumptionLimitLocked();
+ doReturn(1_000_000L).when(mEconomicPolicy).getMaxSatiatedBalance();
- Ledger.Transaction transaction = new Ledger.Transaction(0, 0, 0, null, 5);
+ Ledger.Transaction transaction = new Ledger.Transaction(0, 0, 0, null, 5, 0);
agent.recordTransactionLocked(0, "com.test", ledger, transaction, false);
assertEquals(5, ledger.getCurrentBalance());
- transaction = new Ledger.Transaction(0, 0, 0, null, 995);
+ transaction = new Ledger.Transaction(0, 0, 0, null, 995, 0);
agent.recordTransactionLocked(0, "com.test", ledger, transaction, false);
assertEquals(1000, ledger.getCurrentBalance());
- transaction = new Ledger.Transaction(0, 0, 0, null, -500);
+ transaction = new Ledger.Transaction(0, 0, 0, null, -500, 250);
agent.recordTransactionLocked(0, "com.test", ledger, transaction, false);
assertEquals(500, ledger.getCurrentBalance());
- transaction = new Ledger.Transaction(0, 0, 0, null, 2000);
+ transaction = new Ledger.Transaction(0, 0, 0, null, 2000, 0);
agent.recordTransactionLocked(0, "com.test", ledger, transaction, false);
- assertEquals(1000, ledger.getCurrentBalance());
+ assertEquals(2500, ledger.getCurrentBalance());
- // MaxCirculation can change as the battery level changes. Any already allocated ARCSs
- // shouldn't be removed by recordTransaction().
- doReturn(900L).when(mIrs).getMaxCirculationLocked();
+ // ConsumptionLimit can change as the battery level changes. Ledger balances shouldn't be
+ // affected.
+ doReturn(900L).when(mIrs).getConsumptionLimitLocked();
- transaction = new Ledger.Transaction(0, 0, 0, null, 100);
+ transaction = new Ledger.Transaction(0, 0, 0, null, 100, 0);
agent.recordTransactionLocked(0, "com.test", ledger, transaction, false);
- assertEquals(1000, ledger.getCurrentBalance());
+ assertEquals(2600, ledger.getCurrentBalance());
- transaction = new Ledger.Transaction(0, 0, 0, null, -50);
+ transaction = new Ledger.Transaction(0, 0, 0, null, -50, 50);
agent.recordTransactionLocked(0, "com.test", ledger, transaction, false);
- assertEquals(950, ledger.getCurrentBalance());
+ assertEquals(2550, ledger.getCurrentBalance());
- transaction = new Ledger.Transaction(0, 0, 0, null, -200);
+ transaction = new Ledger.Transaction(0, 0, 0, null, -200, 100);
agent.recordTransactionLocked(0, "com.test", ledger, transaction, false);
- assertEquals(750, ledger.getCurrentBalance());
+ assertEquals(2350, ledger.getCurrentBalance());
- doReturn(800L).when(mIrs).getMaxCirculationLocked();
+ doReturn(800L).when(mIrs).getConsumptionLimitLocked();
- transaction = new Ledger.Transaction(0, 0, 0, null, 100);
+ transaction = new Ledger.Transaction(0, 0, 0, null, 100, 0);
agent.recordTransactionLocked(0, "com.test", ledger, transaction, false);
- assertEquals(800, ledger.getCurrentBalance());
+ assertEquals(2450, ledger.getCurrentBalance());
}
@Test
@@ -165,33 +166,33 @@ public class AgentTest {
Agent agent = new Agent(mIrs, mScribe);
Ledger ledger = new Ledger();
- doReturn(1_000_000L).when(mIrs).getMaxCirculationLocked();
+ doReturn(1_000_000L).when(mIrs).getConsumptionLimitLocked();
doReturn(1000L).when(mEconomicPolicy).getMaxSatiatedBalance();
- Ledger.Transaction transaction = new Ledger.Transaction(0, 0, 0, null, 5);
+ Ledger.Transaction transaction = new Ledger.Transaction(0, 0, 0, null, 5, 0);
agent.recordTransactionLocked(0, "com.test", ledger, transaction, false);
assertEquals(5, ledger.getCurrentBalance());
- transaction = new Ledger.Transaction(0, 0, 0, null, 995);
+ transaction = new Ledger.Transaction(0, 0, 0, null, 995, 0);
agent.recordTransactionLocked(0, "com.test", ledger, transaction, false);
assertEquals(1000, ledger.getCurrentBalance());
- transaction = new Ledger.Transaction(0, 0, 0, null, -500);
+ transaction = new Ledger.Transaction(0, 0, 0, null, -500, 250);
agent.recordTransactionLocked(0, "com.test", ledger, transaction, false);
assertEquals(500, ledger.getCurrentBalance());
- transaction = new Ledger.Transaction(0, 0, 0, null, 999_500L);
+ transaction = new Ledger.Transaction(0, 0, 0, null, 999_500L, 1000);
agent.recordTransactionLocked(0, "com.test", ledger, transaction, false);
assertEquals(1_000, ledger.getCurrentBalance());
// Shouldn't change in normal operation, but adding test case in case it does.
doReturn(900L).when(mEconomicPolicy).getMaxSatiatedBalance();
- transaction = new Ledger.Transaction(0, 0, 0, null, 500);
+ transaction = new Ledger.Transaction(0, 0, 0, null, 500, 0);
agent.recordTransactionLocked(0, "com.test", ledger, transaction, false);
assertEquals(1_000, ledger.getCurrentBalance());
- transaction = new Ledger.Transaction(0, 0, 0, null, -1001);
+ transaction = new Ledger.Transaction(0, 0, 0, null, -1001, 500);
agent.recordTransactionLocked(0, "com.test", ledger, transaction, false);
assertEquals(-1, ledger.getCurrentBalance());
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/tare/ScribeTest.java b/services/tests/mockingservicestests/src/com/android/server/tare/ScribeTest.java
index b72fc23bc16c..ab29e5903f02 100644
--- a/services/tests/mockingservicestests/src/com/android/server/tare/ScribeTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/tare/ScribeTest.java
@@ -107,20 +107,26 @@ public class ScribeTest {
@Test
public void testWriteHighLevelStateToDisk() {
long lastReclamationTime = System.currentTimeMillis();
- long narcsInCirculation = 2000L;
+ long remainingConsumableNarcs = 2000L;
+ long consumptionLimit = 500_000L;
Ledger ledger = mScribeUnderTest.getLedgerLocked(TEST_USER_ID, TEST_PACKAGE);
- ledger.recordTransaction(new Ledger.Transaction(0, 1000L, 1, null, 2000));
+ ledger.recordTransaction(new Ledger.Transaction(0, 1000L, 1, null, 2000, 0));
// Negative ledger balance shouldn't affect the total circulation value.
ledger = mScribeUnderTest.getLedgerLocked(TEST_USER_ID + 1, TEST_PACKAGE);
- ledger.recordTransaction(new Ledger.Transaction(0, 1000L, 1, null, -5000));
+ ledger.recordTransaction(new Ledger.Transaction(0, 1000L, 1, null, -5000, 3000));
mScribeUnderTest.setLastReclamationTimeLocked(lastReclamationTime);
+ mScribeUnderTest.setConsumptionLimitLocked(consumptionLimit);
+ mScribeUnderTest.adjustRemainingConsumableNarcsLocked(
+ remainingConsumableNarcs - consumptionLimit);
mScribeUnderTest.writeImmediatelyForTesting();
mScribeUnderTest.loadFromDiskLocked();
assertEquals(lastReclamationTime, mScribeUnderTest.getLastReclamationTimeLocked());
- assertEquals(narcsInCirculation, mScribeUnderTest.getNarcsInCirculationLocked());
+ assertEquals(remainingConsumableNarcs,
+ mScribeUnderTest.getRemainingConsumableNarcsLocked());
+ assertEquals(consumptionLimit, mScribeUnderTest.getSatiatedConsumptionLimitLocked());
}
@Test
@@ -135,9 +141,9 @@ public class ScribeTest {
@Test
public void testWritingPopulatedLedgerToDisk() {
final Ledger ogLedger = mScribeUnderTest.getLedgerLocked(TEST_USER_ID, TEST_PACKAGE);
- ogLedger.recordTransaction(new Ledger.Transaction(0, 1000, 1, null, 51));
- ogLedger.recordTransaction(new Ledger.Transaction(1500, 2000, 2, "green", 52));
- ogLedger.recordTransaction(new Ledger.Transaction(2500, 3000, 3, "blue", 3));
+ ogLedger.recordTransaction(new Ledger.Transaction(0, 1000, 1, null, 51, 0));
+ ogLedger.recordTransaction(new Ledger.Transaction(1500, 2000, 2, "green", 52, -1));
+ ogLedger.recordTransaction(new Ledger.Transaction(2500, 3000, 3, "blue", 3, 12));
mScribeUnderTest.writeImmediatelyForTesting();
mScribeUnderTest.loadFromDiskLocked();
@@ -156,11 +162,11 @@ public class ScribeTest {
addInstalledPackage(userId, pkgName);
final Ledger ledger = mScribeUnderTest.getLedgerLocked(userId, pkgName);
ledger.recordTransaction(new Ledger.Transaction(
- 0, 1000L * u + l, 1, null, 51L * u + l));
+ 0, 1000L * u + l, 1, null, -51L * u + l, 50));
ledger.recordTransaction(new Ledger.Transaction(
- 1500L * u + l, 2000L * u + l, 2 * u + l, "green" + u + l, 52L * u + l));
+ 1500L * u + l, 2000L * u + l, 2 * u + l, "green" + u + l, 52L * u + l, 0));
ledger.recordTransaction(new Ledger.Transaction(
- 2500L * u + l, 3000L * u + l, 3 * u + l, "blue" + u + l, 3L * u + l));
+ 2500L * u + l, 3000L * u + l, 3 * u + l, "blue" + u + l, 3L * u + l, 0));
ledgers.add(userId, pkgName, ledger);
}
}
@@ -174,9 +180,9 @@ public class ScribeTest {
@Test
public void testDiscardLedgerFromDisk() {
final Ledger ogLedger = mScribeUnderTest.getLedgerLocked(TEST_USER_ID, TEST_PACKAGE);
- ogLedger.recordTransaction(new Ledger.Transaction(0, 1000, 1, null, 51));
- ogLedger.recordTransaction(new Ledger.Transaction(1500, 2000, 2, "green", 52));
- ogLedger.recordTransaction(new Ledger.Transaction(2500, 3000, 3, "blue", 3));
+ ogLedger.recordTransaction(new Ledger.Transaction(0, 1000, 1, null, 51, 1));
+ ogLedger.recordTransaction(new Ledger.Transaction(1500, 2000, 2, "green", 52, 0));
+ ogLedger.recordTransaction(new Ledger.Transaction(2500, 3000, 3, "blue", 3, 1));
mScribeUnderTest.writeImmediatelyForTesting();
mScribeUnderTest.loadFromDiskLocked();
@@ -195,9 +201,9 @@ public class ScribeTest {
public void testLoadingMissingPackageFromDisk() {
final String pkgName = TEST_PACKAGE + ".uninstalled";
final Ledger ogLedger = mScribeUnderTest.getLedgerLocked(TEST_USER_ID, pkgName);
- ogLedger.recordTransaction(new Ledger.Transaction(0, 1000, 1, null, 51));
- ogLedger.recordTransaction(new Ledger.Transaction(1500, 2000, 2, "green", 52));
- ogLedger.recordTransaction(new Ledger.Transaction(2500, 3000, 3, "blue", 3));
+ ogLedger.recordTransaction(new Ledger.Transaction(0, 1000, 1, null, 51, 1));
+ ogLedger.recordTransaction(new Ledger.Transaction(1500, 2000, 2, "green", 52, 2));
+ ogLedger.recordTransaction(new Ledger.Transaction(2500, 3000, 3, "blue", 3, 3));
mScribeUnderTest.writeImmediatelyForTesting();
// Package isn't installed, so make sure it's not saved to memory after loading.
@@ -209,9 +215,9 @@ public class ScribeTest {
public void testLoadingMissingUserFromDisk() {
final int userId = TEST_USER_ID + 1;
final Ledger ogLedger = mScribeUnderTest.getLedgerLocked(userId, TEST_PACKAGE);
- ogLedger.recordTransaction(new Ledger.Transaction(0, 1000, 1, null, 51));
- ogLedger.recordTransaction(new Ledger.Transaction(1500, 2000, 2, "green", 52));
- ogLedger.recordTransaction(new Ledger.Transaction(2500, 3000, 3, "blue", 3));
+ ogLedger.recordTransaction(new Ledger.Transaction(0, 1000, 1, null, 51, 0));
+ ogLedger.recordTransaction(new Ledger.Transaction(1500, 2000, 2, "green", 52, 1));
+ ogLedger.recordTransaction(new Ledger.Transaction(2500, 3000, 3, "blue", 3, 3));
mScribeUnderTest.writeImmediatelyForTesting();
// User doesn't show up with any packages, so make sure nothing is saved after loading.
@@ -219,6 +225,37 @@ public class ScribeTest {
assertLedgersEqual(new Ledger(), mScribeUnderTest.getLedgerLocked(userId, TEST_PACKAGE));
}
+ @Test
+ public void testChangingConsumable() {
+ assertEquals(0, mScribeUnderTest.getSatiatedConsumptionLimitLocked());
+ assertEquals(0, mScribeUnderTest.getRemainingConsumableNarcsLocked());
+
+ // Limit increased, so remaining value should be adjusted as well
+ mScribeUnderTest.setConsumptionLimitLocked(1000);
+ assertEquals(1000, mScribeUnderTest.getSatiatedConsumptionLimitLocked());
+ assertEquals(1000, mScribeUnderTest.getRemainingConsumableNarcsLocked());
+
+ // Limit decreased below remaining, so remaining value should be adjusted as well
+ mScribeUnderTest.setConsumptionLimitLocked(500);
+ assertEquals(500, mScribeUnderTest.getSatiatedConsumptionLimitLocked());
+ assertEquals(500, mScribeUnderTest.getRemainingConsumableNarcsLocked());
+
+ mScribeUnderTest.adjustRemainingConsumableNarcsLocked(-100);
+ assertEquals(500, mScribeUnderTest.getSatiatedConsumptionLimitLocked());
+ assertEquals(400, mScribeUnderTest.getRemainingConsumableNarcsLocked());
+
+ // Limit increased, so remaining value should be adjusted by the difference as well
+ mScribeUnderTest.setConsumptionLimitLocked(1000);
+ assertEquals(1000, mScribeUnderTest.getSatiatedConsumptionLimitLocked());
+ assertEquals(900, mScribeUnderTest.getRemainingConsumableNarcsLocked());
+
+
+ // Limit decreased, but above remaining, so remaining value should left alone
+ mScribeUnderTest.setConsumptionLimitLocked(950);
+ assertEquals(950, mScribeUnderTest.getSatiatedConsumptionLimitLocked());
+ assertEquals(900, mScribeUnderTest.getRemainingConsumableNarcsLocked());
+ }
+
private void assertLedgersEqual(Ledger expected, Ledger actual) {
if (expected == null) {
assertNull(actual);
@@ -245,6 +282,7 @@ public class ScribeTest {
assertEquals(expected.eventId, actual.eventId);
assertEquals(expected.tag, actual.tag);
assertEquals(expected.delta, actual.delta);
+ assertEquals(expected.ctp, actual.ctp);
}
private void addInstalledPackage(int userId, String pkgName) {
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java
index a9b7cfb5e338..3ce2ed84d3e8 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java
@@ -52,7 +52,6 @@ import android.view.accessibility.MagnificationAnimationCallback;
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
-import com.android.server.accessibility.AccessibilityManagerService;
import com.android.server.accessibility.AccessibilityTraceManager;
import com.android.server.accessibility.test.MessageCapturingHandler;
import com.android.server.wm.WindowManagerInternal;
@@ -94,7 +93,6 @@ public class FullScreenMagnificationControllerTest {
final FullScreenMagnificationController.ControllerContext mMockControllerCtx =
mock(FullScreenMagnificationController.ControllerContext.class);
final Context mMockContext = mock(Context.class);
- final AccessibilityManagerService mMockAms = mock(AccessibilityManagerService.class);
final AccessibilityTraceManager mMockTraceManager = mock(AccessibilityTraceManager.class);
final WindowManagerInternal mMockWindowManager = mock(WindowManagerInternal.class);
private final MagnificationAnimationCallback mAnimationCallback = mock(
@@ -121,12 +119,10 @@ public class FullScreenMagnificationControllerTest {
// Pretending ID of the Thread associated with looper as main thread ID in controller
when(mMockContext.getMainLooper()).thenReturn(looper);
when(mMockControllerCtx.getContext()).thenReturn(mMockContext);
- when(mMockControllerCtx.getAms()).thenReturn(mMockAms);
when(mMockControllerCtx.getTraceManager()).thenReturn(mMockTraceManager);
when(mMockControllerCtx.getWindowManager()).thenReturn(mMockWindowManager);
when(mMockControllerCtx.getHandler()).thenReturn(mMessageCapturingHandler);
when(mMockControllerCtx.getAnimationDuration()).thenReturn(1000L);
- when(mMockAms.getTraceManager()).thenReturn(mMockTraceManager);
initMockWindowManager();
mFullScreenMagnificationController = new FullScreenMagnificationController(
@@ -357,8 +353,8 @@ public class FullScreenMagnificationControllerTest {
assertEquals(newCenter.x, mFullScreenMagnificationController.getCenterX(displayId), 0.5);
assertEquals(newCenter.y, mFullScreenMagnificationController.getCenterY(displayId), 0.5);
assertThat(getCurrentMagnificationSpec(displayId), closeTo(endSpec));
- verify(mMockAms).notifyMagnificationChanged(eq(displayId), eq(INITIAL_MAGNIFICATION_REGION),
- mConfigCaptor.capture());
+ verify(mRequestObserver).onFullScreenMagnificationChanged(eq(displayId),
+ eq(INITIAL_MAGNIFICATION_REGION), mConfigCaptor.capture());
assertConfigEquals(config, mConfigCaptor.getValue());
verify(mMockValueAnimator).start();
verify(mRequestObserver).onRequestMagnificationSpec(displayId, SERVICE_ID_1);
@@ -501,7 +497,7 @@ public class FullScreenMagnificationControllerTest {
mMessageCapturingHandler.sendAllMessages();
MagnificationConfig config = buildConfig(1.0f, OTHER_MAGNIFICATION_BOUNDS.centerX(),
OTHER_MAGNIFICATION_BOUNDS.centerY());
- verify(mMockAms).notifyMagnificationChanged(eq(displayId), eq(OTHER_REGION),
+ verify(mRequestObserver).onFullScreenMagnificationChanged(eq(displayId), eq(OTHER_REGION),
mConfigCaptor.capture());
assertConfigEquals(config, mConfigCaptor.getValue());
}
@@ -655,9 +651,9 @@ public class FullScreenMagnificationControllerTest {
register(displayId);
zoomIn2xToMiddle(displayId);
mMessageCapturingHandler.sendAllMessages();
- reset(mMockAms);
+ reset(mRequestObserver);
assertTrue(mFullScreenMagnificationController.resetIfNeeded(displayId, false));
- verify(mMockAms).notifyMagnificationChanged(eq(displayId),
+ verify(mRequestObserver).onFullScreenMagnificationChanged(eq(displayId),
eq(INITIAL_MAGNIFICATION_REGION), any(MagnificationConfig.class));
assertFalse(mFullScreenMagnificationController.isMagnifying(displayId));
assertFalse(mFullScreenMagnificationController.resetIfNeeded(displayId, false));
@@ -676,8 +672,8 @@ public class FullScreenMagnificationControllerTest {
assertFalse(mFullScreenMagnificationController.reset(displayId, mAnimationCallback));
mMessageCapturingHandler.sendAllMessages();
- verify(mMockAms, never()).notifyMagnificationChanged(eq(displayId), any(Region.class),
- any(MagnificationConfig.class));
+ verify(mRequestObserver, never()).onFullScreenMagnificationChanged(eq(displayId),
+ any(Region.class), any(MagnificationConfig.class));
verify(mAnimationCallback).onResult(true);
}
@@ -1072,8 +1068,8 @@ public class FullScreenMagnificationControllerTest {
when(mMockValueAnimator.getAnimatedFraction()).thenReturn(0.0f);
mTargetAnimationListener.onAnimationUpdate(mMockValueAnimator);
verify(mMockWindowManager).setMagnificationSpec(eq(displayId), eq(startSpec));
- verify(mMockAms).notifyMagnificationChanged(eq(displayId), eq(INITIAL_MAGNIFICATION_REGION),
- mConfigCaptor.capture());
+ verify(mRequestObserver).onFullScreenMagnificationChanged(eq(displayId),
+ eq(INITIAL_MAGNIFICATION_REGION), mConfigCaptor.capture());
assertConfigEquals(config, mConfigCaptor.getValue());
Mockito.reset(mMockWindowManager);
@@ -1097,7 +1093,7 @@ public class FullScreenMagnificationControllerTest {
// Animation should have been restarted
verify(mMockValueAnimator, times(2)).start();
- verify(mMockAms, times(2)).notifyMagnificationChanged(eq(displayId),
+ verify(mRequestObserver, times(2)).onFullScreenMagnificationChanged(eq(displayId),
eq(INITIAL_MAGNIFICATION_REGION), mConfigCaptor.capture());
assertConfigEquals(newConfig, mConfigCaptor.getValue());
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
index 2060223f6f98..0fed89b831d6 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
@@ -151,8 +151,6 @@ public class FullScreenMagnificationGestureHandlerTest {
mock(FullScreenMagnificationController.ControllerContext.class);
final WindowManagerInternal mockWindowManager = mock(WindowManagerInternal.class);
when(mockController.getContext()).thenReturn(mContext);
- when(mockController.getAms()).thenReturn(mMockAccessibilityManagerService);
- when(mMockAccessibilityManagerService.getTraceManager()).thenReturn(mMockTraceManager);
when(mockController.getTraceManager()).thenReturn(mMockTraceManager);
when(mockController.getWindowManager()).thenReturn(mockWindowManager);
when(mockController.getHandler()).thenReturn(new Handler(mContext.getMainLooper()));
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
index 3fcce92fe141..ec59090240f3 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
@@ -145,9 +145,10 @@ public class MagnificationControllerTest {
mCallbackDelegate, mTraceManager, mScaleProvider));
mMockConnection = new MockWindowMagnificationConnection(true);
mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
- new FullScreenMagnificationControllerStubber(mScreenMagnificationController);
mMagnificationController = new MagnificationController(mService, globalLock, mContext,
mScreenMagnificationController, mWindowMagnificationManager, mScaleProvider);
+ new FullScreenMagnificationControllerStubber(mScreenMagnificationController,
+ mMagnificationController);
mMagnificationController.setMagnificationCapabilities(
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL);
@@ -451,6 +452,29 @@ public class MagnificationControllerTest {
}
@Test
+ public void onFullScreenMagnificationChanged_fullScreenEnabled_notifyMagnificationChanged()
+ throws RemoteException {
+ setMagnificationEnabled(MODE_FULLSCREEN);
+
+ final MagnificationConfig config = obtainMagnificationConfig(MODE_FULLSCREEN);
+ mScreenMagnificationController.setScaleAndCenter(TEST_DISPLAY,
+ config.getScale(), config.getCenterX(), config.getCenterY(),
+ true, TEST_SERVICE_ID);
+
+ // The first time is triggered when setting magnification enabled. And the second time is
+ // triggered when calling setScaleAndCenter.
+ final ArgumentCaptor<MagnificationConfig> configCaptor = ArgumentCaptor.forClass(
+ MagnificationConfig.class);
+ verify(mService, times(2)).notifyMagnificationChanged(eq(TEST_DISPLAY),
+ eq(FullScreenMagnificationControllerStubber.MAGNIFICATION_REGION),
+ configCaptor.capture());
+ final MagnificationConfig actualConfig = configCaptor.getValue();
+ assertEquals(config.getCenterX(), actualConfig.getCenterX(), 0);
+ assertEquals(config.getCenterY(), actualConfig.getCenterY(), 0);
+ assertEquals(config.getScale(), actualConfig.getScale(), 0);
+ }
+
+ @Test
public void onAccessibilityActionPerformed_magnifierEnabled_showMagnificationButton()
throws RemoteException {
setMagnificationEnabled(MODE_WINDOW);
@@ -679,7 +703,7 @@ public class MagnificationControllerTest {
throws RemoteException {
setMagnificationEnabled(MODE_FULLSCREEN);
mScreenMagnificationController.setScaleAndCenter(TEST_DISPLAY,
- /* scale= */1, MAGNIFIED_CENTER_X, MAGNIFIED_CENTER_Y,
+ /* scale= */ 1, MAGNIFIED_CENTER_X, MAGNIFIED_CENTER_Y,
true, TEST_SERVICE_ID);
mMagnificationController.onFullScreenMagnificationActivationState(TEST_DISPLAY, false);
@@ -884,6 +908,8 @@ public class MagnificationControllerTest {
private static class FullScreenMagnificationControllerStubber {
private static final Region MAGNIFICATION_REGION = new Region(0, 0, 500, 600);
private final FullScreenMagnificationController mScreenMagnificationController;
+ private final FullScreenMagnificationController.MagnificationInfoChangedCallback
+ mMagnificationChangedCallback;
private boolean mIsMagnifying = false;
private float mScale = 1.0f;
private float mCenterX = MAGNIFICATION_REGION.getBounds().exactCenterX();
@@ -891,8 +917,10 @@ public class MagnificationControllerTest {
private int mServiceId = -1;
FullScreenMagnificationControllerStubber(
- FullScreenMagnificationController screenMagnificationController) {
+ FullScreenMagnificationController screenMagnificationController,
+ FullScreenMagnificationController.MagnificationInfoChangedCallback callback) {
mScreenMagnificationController = screenMagnificationController;
+ mMagnificationChangedCallback = callback;
stubMethods();
}
@@ -930,6 +958,14 @@ public class MagnificationControllerTest {
} else {
reset();
}
+
+
+ final MagnificationConfig config = new MagnificationConfig.Builder().setMode(
+ MODE_FULLSCREEN).setScale(mScale).setCenterX(mCenterX).setCenterY(
+ mCenterY).build();
+ mMagnificationChangedCallback.onFullScreenMagnificationChanged(TEST_DISPLAY,
+ FullScreenMagnificationControllerStubber.MAGNIFICATION_REGION,
+ config);
return true;
};
doAnswer(setScaleAndCenterStubAnswer).when(
diff --git a/services/tests/servicestests/src/com/android/server/backup/transport/BackupTransportClientTest.java b/services/tests/servicestests/src/com/android/server/backup/transport/BackupTransportClientTest.java
new file mode 100644
index 000000000000..b154d6f6db0c
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/backup/transport/BackupTransportClientTest.java
@@ -0,0 +1,265 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.transport;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.fail;
+
+import android.app.backup.BackupTransport;
+import android.app.backup.RestoreDescription;
+import android.app.backup.RestoreSet;
+import android.content.Intent;
+import android.content.pm.PackageInfo;
+import android.os.IBinder;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.backup.IBackupTransport;
+import com.android.internal.backup.ITransportStatusCallback;
+import com.android.internal.infra.AndroidFuture;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.List;
+import java.util.concurrent.CancellationException;
+
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class BackupTransportClientTest {
+
+ private static class TestFuturesFakeTransportBinder extends FakeTransportBinderBase {
+ public final Object mLock = new Object();
+
+ public String mNameCompletedImmediately;
+
+ @GuardedBy("mLock")
+ public AndroidFuture<String> mNameCompletedInFuture;
+
+ @Override public void name(AndroidFuture<String> name) throws RemoteException {
+ name.complete(mNameCompletedImmediately);
+ }
+ @Override public void transportDirName(AndroidFuture<String> name) throws RemoteException {
+ synchronized (mLock) {
+ mNameCompletedInFuture = name;
+ mLock.notifyAll();
+ }
+ }
+ }
+
+ @Test
+ public void testName_completesImmediately_returnsName() throws Exception {
+ TestFuturesFakeTransportBinder binder = new TestFuturesFakeTransportBinder();
+ binder.mNameCompletedImmediately = "fake name";
+
+ BackupTransportClient client = new BackupTransportClient(binder);
+ String name = client.name();
+
+ assertThat(name).isEqualTo("fake name");
+ }
+
+ @Test
+ public void testTransportDirName_completesLater_returnsName() throws Exception {
+ TestFuturesFakeTransportBinder binder = new TestFuturesFakeTransportBinder();
+ BackupTransportClient client = new BackupTransportClient(binder);
+
+ Thread thread = new Thread(() -> {
+ try {
+ String name = client.transportDirName();
+ assertThat(name).isEqualTo("fake name");
+ } catch (Exception ex) {
+ fail("unexpected Exception: " + ex.getClass().getCanonicalName());
+ }
+ });
+
+ thread.start();
+
+ synchronized (binder.mLock) {
+ while (binder.mNameCompletedInFuture == null) {
+ binder.mLock.wait();
+ }
+ assertThat(binder.mNameCompletedInFuture.complete("fake name")).isTrue();
+ }
+
+ thread.join();
+ }
+
+ @Test
+ public void testTransportDirName_canceledBeforeCompletion_throwsException() throws Exception {
+ TestFuturesFakeTransportBinder binder = new TestFuturesFakeTransportBinder();
+ BackupTransportClient client = new BackupTransportClient(binder);
+
+ Thread thread = new Thread(() -> {
+ try {
+ /*String name =*/ client.transportDirName();
+ fail("transportDirName should be cancelled");
+ } catch (CancellationException ex) {
+ // This is expected.
+ } catch (Exception ex) {
+ fail("unexpected Exception: " + ex.getClass().getCanonicalName());
+ }
+ });
+
+ thread.start();
+
+ synchronized (binder.mLock) {
+ while (binder.mNameCompletedInFuture == null) {
+ binder.mLock.wait();
+ }
+ client.onBecomingUnusable();
+ }
+
+ thread.join();
+ }
+
+ private static class TestCallbacksFakeTransportBinder extends FakeTransportBinderBase {
+ public final Object mLock = new Object();
+
+ public int mStatusCompletedImmediately;
+
+ @GuardedBy("mLock")
+ public ITransportStatusCallback mStatusCompletedInFuture;
+
+ @Override public void initializeDevice(ITransportStatusCallback c) throws RemoteException {
+ c.onOperationCompleteWithStatus(mStatusCompletedImmediately);
+ }
+ @Override public void finishBackup(ITransportStatusCallback c) throws RemoteException {
+ synchronized (mLock) {
+ mStatusCompletedInFuture = c;
+ mLock.notifyAll();
+ }
+ }
+ }
+
+ @Test
+ public void testInitializeDevice_completesImmediately_returnsStatus() throws Exception {
+ TestCallbacksFakeTransportBinder binder = new TestCallbacksFakeTransportBinder();
+ binder.mStatusCompletedImmediately = 123;
+
+ BackupTransportClient client = new BackupTransportClient(binder);
+ int status = client.initializeDevice();
+
+ assertThat(status).isEqualTo(123);
+ }
+
+
+ @Test
+ public void testFinishBackup_completesLater_returnsStatus() throws Exception {
+ TestCallbacksFakeTransportBinder binder = new TestCallbacksFakeTransportBinder();
+ BackupTransportClient client = new BackupTransportClient(binder);
+
+ Thread thread = new Thread(() -> {
+ try {
+ int status = client.finishBackup();
+ assertThat(status).isEqualTo(456);
+ } catch (Exception ex) {
+ fail("unexpected Exception: " + ex.getClass().getCanonicalName());
+ }
+ });
+
+ thread.start();
+
+ synchronized (binder.mLock) {
+ while (binder.mStatusCompletedInFuture == null) {
+ binder.mLock.wait();
+ }
+ binder.mStatusCompletedInFuture.onOperationCompleteWithStatus(456);
+ }
+
+ thread.join();
+ }
+
+ @Test
+ public void testFinishBackup_canceledBeforeCompletion_throwsException() throws Exception {
+ TestCallbacksFakeTransportBinder binder = new TestCallbacksFakeTransportBinder();
+ BackupTransportClient client = new BackupTransportClient(binder);
+
+ Thread thread = new Thread(() -> {
+ try {
+ int status = client.finishBackup();
+ assertThat(status).isEqualTo(BackupTransport.TRANSPORT_ERROR);
+ } catch (Exception ex) {
+ fail("unexpected Exception: " + ex.getClass().getCanonicalName());
+ }
+ });
+
+ thread.start();
+
+ synchronized (binder.mLock) {
+ while (binder.mStatusCompletedInFuture == null) {
+ binder.mLock.wait();
+ }
+ client.onBecomingUnusable();
+ }
+
+ thread.join();
+ }
+
+ // Convenience layer so we only need to fake specific methods useful for each test case.
+ private static class FakeTransportBinderBase implements IBackupTransport {
+ @Override public void name(AndroidFuture<String> f) throws RemoteException {}
+ @Override public void transportDirName(AndroidFuture<String> f) throws RemoteException {}
+ @Override public void configurationIntent(AndroidFuture<Intent> f) throws RemoteException {}
+ @Override public void currentDestinationString(AndroidFuture<String> f)
+ throws RemoteException {}
+ @Override public void dataManagementIntent(AndroidFuture<Intent> f)
+ throws RemoteException {}
+ @Override public void dataManagementIntentLabel(AndroidFuture<CharSequence> f)
+ throws RemoteException {}
+ @Override public void initializeDevice(ITransportStatusCallback c) throws RemoteException {}
+ @Override public void clearBackupData(PackageInfo i, ITransportStatusCallback c)
+ throws RemoteException {}
+ @Override public void finishBackup(ITransportStatusCallback c) throws RemoteException {}
+ @Override public void requestBackupTime(AndroidFuture<Long> f) throws RemoteException {}
+ @Override public void performBackup(PackageInfo i, ParcelFileDescriptor fd, int f,
+ ITransportStatusCallback c) throws RemoteException {}
+ @Override public void getAvailableRestoreSets(AndroidFuture<List<RestoreSet>> f)
+ throws RemoteException {}
+ @Override public void getCurrentRestoreSet(AndroidFuture<Long> f) throws RemoteException {}
+ @Override public void startRestore(long t, PackageInfo[] p, ITransportStatusCallback c)
+ throws RemoteException {}
+ @Override public void nextRestorePackage(AndroidFuture<RestoreDescription> f)
+ throws RemoteException {}
+ @Override public void getRestoreData(ParcelFileDescriptor fd, ITransportStatusCallback c)
+ throws RemoteException {}
+ @Override public void finishRestore(ITransportStatusCallback c) throws RemoteException {}
+ @Override public void requestFullBackupTime(AndroidFuture<Long> f) throws RemoteException {}
+ @Override public void performFullBackup(PackageInfo i, ParcelFileDescriptor fd, int f,
+ ITransportStatusCallback c) throws RemoteException {}
+ @Override public void checkFullBackupSize(long s, ITransportStatusCallback c)
+ throws RemoteException {}
+ @Override public void sendBackupData(int n, ITransportStatusCallback c)
+ throws RemoteException {}
+ @Override public void cancelFullBackup(ITransportStatusCallback c) throws RemoteException {}
+ @Override public void isAppEligibleForBackup(PackageInfo p, boolean b,
+ AndroidFuture<Boolean> f) throws RemoteException {}
+ @Override public void getBackupQuota(String s, boolean b, AndroidFuture<Long> f)
+ throws RemoteException {}
+ @Override public void getNextFullRestoreDataChunk(ParcelFileDescriptor fd,
+ ITransportStatusCallback c) throws RemoteException {}
+ @Override public void abortFullRestore(ITransportStatusCallback c) throws RemoteException {}
+ @Override public void getTransportFlags(AndroidFuture<Integer> f) throws RemoteException {}
+ @Override public IBinder asBinder() {
+ return null;
+ }
+ }
+}
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 fe110e5398ca..b601d14f1f58 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -1137,9 +1137,6 @@ public class DevicePolicyManagerTest extends DpmTestBase {
mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
// Verify internal calls.
- verify(getServices().iactivityManager, times(1)).updateDeviceOwner(
- eq(admin1.getPackageName()));
-
verify(getServices().userManager, times(1)).setUserRestriction(
eq(UserManager.DISALLOW_ADD_MANAGED_PROFILE),
eq(true), eq(UserHandle.SYSTEM));
@@ -1205,9 +1202,6 @@ public class DevicePolicyManagerTest extends DpmTestBase {
assertThat(dpm.getDeviceOwnerComponentOnAnyUser()).isEqualTo(admin1);
// Verify internal calls.
- verify(getServices().iactivityManager).updateDeviceOwner(
- eq(admin1.getPackageName()));
-
verify(mContext.spiedContext).sendBroadcastAsUser(
MockUtils.checkIntentAction(DevicePolicyManager.ACTION_DEVICE_OWNER_CHANGED),
MockUtils.checkUserHandle(UserHandle.USER_SYSTEM));
@@ -1392,11 +1386,6 @@ public class DevicePolicyManagerTest extends DpmTestBase {
setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_SYSTEM_USER_UID);
dpm.setActiveAdmin(admin1, /* replace =*/ false);
assertThat(dpm.setDeviceOwner(admin1, "owner-name", UserHandle.USER_SYSTEM)).isTrue();
-
- // Verify internal calls.
- verify(getServices().iactivityManager, times(1)).updateDeviceOwner(
- eq(admin1.getPackageName()));
-
assertThat(dpm.getDeviceOwnerComponentOnAnyUser()).isEqualTo(admin1);
dpm.addUserRestriction(admin1, UserManager.DISALLOW_ADD_USER);
@@ -1501,11 +1490,6 @@ public class DevicePolicyManagerTest extends DpmTestBase {
setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_SYSTEM_USER_UID);
dpm.setActiveAdmin(admin1, /* replace =*/ false);
assertThat(dpm.setDeviceOwner(admin1, "owner-name", UserHandle.USER_SYSTEM)).isTrue();
-
- // Verify internal calls.
- verify(getServices().iactivityManager, times(1)).updateDeviceOwner(
- eq(admin1.getPackageName()));
-
assertThat(dpm.getDeviceOwnerComponentOnAnyUser()).isEqualTo(admin1);
// Now call clear from the secondary user, which should throw.
@@ -3575,11 +3559,11 @@ public class DevicePolicyManagerTest extends DpmTestBase {
setup_DeviceAdminFeatureOff();
mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
assertCheckProvisioningPreCondition(DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE,
- DevicePolicyManager.CODE_DEVICE_ADMIN_NOT_SUPPORTED);
+ DevicePolicyManager.STATUS_DEVICE_ADMIN_NOT_SUPPORTED);
assertCheckProvisioningPreCondition(DevicePolicyManager.ACTION_PROVISION_FINANCED_DEVICE,
- DevicePolicyManager.CODE_DEVICE_ADMIN_NOT_SUPPORTED);
+ DevicePolicyManager.STATUS_DEVICE_ADMIN_NOT_SUPPORTED);
assertCheckProvisioningPreCondition(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE,
- DevicePolicyManager.CODE_DEVICE_ADMIN_NOT_SUPPORTED);
+ DevicePolicyManager.STATUS_DEVICE_ADMIN_NOT_SUPPORTED);
}
private void setup_ManagedProfileFeatureOff() throws Exception {
@@ -3611,11 +3595,11 @@ public class DevicePolicyManagerTest extends DpmTestBase {
setup_ManagedProfileFeatureOff();
mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
assertCheckProvisioningPreCondition(DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE,
- DevicePolicyManager.CODE_OK);
+ DevicePolicyManager.STATUS_OK);
assertCheckProvisioningPreCondition(DevicePolicyManager.ACTION_PROVISION_FINANCED_DEVICE,
- DevicePolicyManager.CODE_OK);
+ DevicePolicyManager.STATUS_OK);
assertCheckProvisioningPreCondition(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE,
- DevicePolicyManager.CODE_MANAGED_USERS_NOT_SUPPORTED);
+ DevicePolicyManager.STATUS_MANAGED_USERS_NOT_SUPPORTED);
}
private void setup_firstBoot_systemUser() throws Exception {
@@ -3653,15 +3637,15 @@ public class DevicePolicyManagerTest extends DpmTestBase {
when(getServices().userManagerForMock.isHeadlessSystemUserMode()).thenReturn(false);
assertCheckProvisioningPreCondition(DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE,
- DevicePolicyManager.CODE_OK);
+ DevicePolicyManager.STATUS_OK);
assertCheckProvisioningPreCondition(DevicePolicyManager.ACTION_PROVISION_FINANCED_DEVICE,
- DevicePolicyManager.CODE_OK);
+ DevicePolicyManager.STATUS_OK);
assertCheckProvisioningPreCondition(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE,
- DevicePolicyManager.CODE_OK);
+ DevicePolicyManager.STATUS_OK);
when(getServices().userManagerForMock.isHeadlessSystemUserMode()).thenReturn(true);
assertCheckProvisioningPreCondition(DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE,
- DevicePolicyManager.CODE_OK);
+ DevicePolicyManager.STATUS_OK);
}
private void setup_systemUserSetupComplete_systemUser() throws Exception {
@@ -3708,11 +3692,11 @@ public class DevicePolicyManagerTest extends DpmTestBase {
setup_systemUserSetupComplete_systemUser();
mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
assertCheckProvisioningPreCondition(DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE,
- DevicePolicyManager.CODE_USER_SETUP_COMPLETED);
+ DevicePolicyManager.STATUS_USER_SETUP_COMPLETED);
assertCheckProvisioningPreCondition(DevicePolicyManager.ACTION_PROVISION_FINANCED_DEVICE,
- DevicePolicyManager.CODE_USER_SETUP_COMPLETED);
+ DevicePolicyManager.STATUS_USER_SETUP_COMPLETED);
assertCheckProvisioningPreCondition(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE,
- DevicePolicyManager.CODE_OK);
+ DevicePolicyManager.STATUS_OK);
}
@Test
@@ -3722,22 +3706,22 @@ public class DevicePolicyManagerTest extends DpmTestBase {
mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
assertCheckProvisioningPreCondition(DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE,
- DevicePolicyManager.CODE_HAS_DEVICE_OWNER);
+ DevicePolicyManager.STATUS_HAS_DEVICE_OWNER);
assertCheckProvisioningPreCondition(DevicePolicyManager.ACTION_PROVISION_FINANCED_DEVICE,
- DevicePolicyManager.CODE_HAS_DEVICE_OWNER);
+ DevicePolicyManager.STATUS_HAS_DEVICE_OWNER);
assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE, false);
assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_FINANCED_DEVICE, false);
// COMP mode NOT is allowed.
assertCheckProvisioningPreCondition(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE,
- DevicePolicyManager.CODE_CANNOT_ADD_MANAGED_PROFILE);
+ DevicePolicyManager.STATUS_CANNOT_ADD_MANAGED_PROFILE);
assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, false);
// And other DPCs can NOT provision a managed profile.
assertCheckProvisioningPreCondition(
DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE,
DpmMockContext.ANOTHER_PACKAGE_NAME,
- DevicePolicyManager.CODE_CANNOT_ADD_MANAGED_PROFILE);
+ DevicePolicyManager.STATUS_CANNOT_ADD_MANAGED_PROFILE);
assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, false,
DpmMockContext.ANOTHER_PACKAGE_NAME, DpmMockContext.ANOTHER_UID);
}
@@ -3759,13 +3743,13 @@ public class DevicePolicyManagerTest extends DpmTestBase {
eq(UserHandle.getUserHandleForUid(mContext.binder.callingUid))))
.thenReturn(UserManager.RESTRICTION_SOURCE_SYSTEM);
assertCheckProvisioningPreCondition(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE,
- DevicePolicyManager.CODE_CANNOT_ADD_MANAGED_PROFILE);
+ DevicePolicyManager.STATUS_CANNOT_ADD_MANAGED_PROFILE);
assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, false);
assertCheckProvisioningPreCondition(
DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE,
DpmMockContext.ANOTHER_PACKAGE_NAME,
- DevicePolicyManager.CODE_CANNOT_ADD_MANAGED_PROFILE);
+ DevicePolicyManager.STATUS_CANNOT_ADD_MANAGED_PROFILE);
assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, false,
DpmMockContext.ANOTHER_PACKAGE_NAME, DpmMockContext.ANOTHER_UID);
}
@@ -3794,12 +3778,12 @@ public class DevicePolicyManagerTest extends DpmTestBase {
// We can delete the managed profile to create a new one, so provisioning is allowed.
assertCheckProvisioningPreCondition(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE,
- DevicePolicyManager.CODE_CANNOT_ADD_MANAGED_PROFILE);
+ DevicePolicyManager.STATUS_CANNOT_ADD_MANAGED_PROFILE);
assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, false);
assertCheckProvisioningPreCondition(
DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE,
DpmMockContext.ANOTHER_PACKAGE_NAME,
- DevicePolicyManager.CODE_CANNOT_ADD_MANAGED_PROFILE);
+ DevicePolicyManager.STATUS_CANNOT_ADD_MANAGED_PROFILE);
assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, false,
DpmMockContext.ANOTHER_PACKAGE_NAME, DpmMockContext.ANOTHER_UID);
}
@@ -3823,13 +3807,13 @@ public class DevicePolicyManagerTest extends DpmTestBase {
assertCheckProvisioningPreCondition(
DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE,
DpmMockContext.ANOTHER_PACKAGE_NAME,
- DevicePolicyManager.CODE_CANNOT_ADD_MANAGED_PROFILE);
+ DevicePolicyManager.STATUS_CANNOT_ADD_MANAGED_PROFILE);
assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, false,
DpmMockContext.ANOTHER_PACKAGE_NAME, DpmMockContext.ANOTHER_UID);
// But the device owner can still do it because it has set the restriction itself.
assertCheckProvisioningPreCondition(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE,
- DevicePolicyManager.CODE_CANNOT_ADD_MANAGED_PROFILE);
+ DevicePolicyManager.STATUS_CANNOT_ADD_MANAGED_PROFILE);
assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, false);
}
@@ -3901,7 +3885,7 @@ public class DevicePolicyManagerTest extends DpmTestBase {
// COMP mode is NOT allowed.
assertCheckProvisioningPreCondition(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE,
- DevicePolicyManager.CODE_CANNOT_ADD_MANAGED_PROFILE);
+ DevicePolicyManager.STATUS_CANNOT_ADD_MANAGED_PROFILE);
}
private void setup_provisionManagedProfileOneAlreadyExist_primaryUser() throws Exception {
@@ -3935,14 +3919,14 @@ public class DevicePolicyManagerTest extends DpmTestBase {
setup_provisionManagedProfileOneAlreadyExist_primaryUser();
mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
assertCheckProvisioningPreCondition(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE,
- DevicePolicyManager.CODE_CANNOT_ADD_MANAGED_PROFILE);
+ DevicePolicyManager.STATUS_CANNOT_ADD_MANAGED_PROFILE);
}
@Test
public void testCheckProvisioningPreCondition_permission() {
// GIVEN the permission MANAGE_PROFILE_AND_DEVICE_OWNERS is not granted
assertExpectException(SecurityException.class, /* messageRegex =*/ null,
- () -> dpm.checkProvisioningPreCondition(
+ () -> dpm.checkProvisioningPrecondition(
DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, "some.package"));
}
@@ -8696,7 +8680,7 @@ public class DevicePolicyManagerTest extends DpmTestBase {
private void assertCheckProvisioningPreCondition(
String action, String packageName, int provisioningCondition) {
assertWithMessage("checkProvisioningPreCondition(%s, %s) returning unexpected result",
- action, packageName).that(dpm.checkProvisioningPreCondition(action, packageName))
+ action, packageName).that(dpm.checkProvisioningPrecondition(action, packageName))
.isEqualTo(provisioningCondition);
}
diff --git a/services/tests/servicestests/src/com/android/server/integrity/parser/RuleBinaryParserTest.java b/services/tests/servicestests/src/com/android/server/integrity/parser/RuleBinaryParserTest.java
index ab21ab05ab5f..03363a100841 100644
--- a/services/tests/servicestests/src/com/android/server/integrity/parser/RuleBinaryParserTest.java
+++ b/services/tests/servicestests/src/com/android/server/integrity/parser/RuleBinaryParserTest.java
@@ -71,9 +71,11 @@ public class RuleBinaryParserTest {
private static final String PACKAGE_NAME = getBits(AtomicFormula.PACKAGE_NAME, KEY_BITS);
private static final String APP_CERTIFICATE = getBits(AtomicFormula.APP_CERTIFICATE, KEY_BITS);
+ private static final String APP_CERTIFICATE_LINEAGE =
+ getBits(AtomicFormula.APP_CERTIFICATE_LINEAGE, KEY_BITS);
private static final String VERSION_CODE = getBits(AtomicFormula.VERSION_CODE, KEY_BITS);
private static final String PRE_INSTALLED = getBits(AtomicFormula.PRE_INSTALLED, KEY_BITS);
- private static final int INVALID_KEY_VALUE = 8;
+ private static final int INVALID_KEY_VALUE = 9;
private static final String INVALID_KEY = getBits(INVALID_KEY_VALUE, KEY_BITS);
private static final String EQ = getBits(AtomicFormula.EQ, OPERATOR_BITS);
@@ -337,6 +339,40 @@ public class RuleBinaryParserTest {
}
@Test
+ public void testBinaryString_validAtomicFormulaWithCertificateLineage() throws Exception {
+ String appCertificate = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
+ String ruleBits =
+ START_BIT
+ + ATOMIC_FORMULA_START_BITS
+ + APP_CERTIFICATE_LINEAGE
+ + EQ
+ + IS_HASHED
+ + getBits(appCertificate.length(), VALUE_SIZE_BITS)
+ + getValueBits(appCertificate)
+ + DENY
+ + END_BIT;
+ byte[] ruleBytes = getBytes(ruleBits);
+ ByteBuffer rule =
+ ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes.length);
+ rule.put(DEFAULT_FORMAT_VERSION_BYTES);
+ rule.put(ruleBytes);
+
+ RuleParser binaryParser = new RuleBinaryParser();
+ Rule expectedRule =
+ new Rule(
+ new AtomicFormula.StringAtomicFormula(
+ AtomicFormula.APP_CERTIFICATE_LINEAGE,
+ IntegrityUtils.getHexDigest(
+ appCertificate.getBytes(StandardCharsets.UTF_8)),
+ /* isHashedValue= */ true),
+ Rule.DENY);
+
+ List<Rule> rules = binaryParser.parse(rule.array());
+
+ assertThat(rules).isEqualTo(Collections.singletonList(expectedRule));
+ }
+
+ @Test
public void testBinaryString_validAtomicFormula_integerValue_noIndexing() throws Exception {
int versionCode = 1;
String ruleBits =
diff --git a/services/tests/servicestests/src/com/android/server/locales/LocaleManagerBackupRestoreTest.java b/services/tests/servicestests/src/com/android/server/locales/LocaleManagerBackupRestoreTest.java
index c77100045118..0287510041be 100644
--- a/services/tests/servicestests/src/com/android/server/locales/LocaleManagerBackupRestoreTest.java
+++ b/services/tests/servicestests/src/com/android/server/locales/LocaleManagerBackupRestoreTest.java
@@ -41,7 +41,9 @@ import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.os.Binder;
+import android.os.HandlerThread;
import android.os.LocaleList;
+import android.os.Process;
import android.os.RemoteException;
import android.os.SimpleClock;
import android.util.SparseArray;
@@ -78,6 +80,7 @@ import java.util.Map;
*/
@RunWith(AndroidJUnit4.class)
public class LocaleManagerBackupRestoreTest {
+ private static final String TAG = "LocaleManagerBackupRestoreTest";
private static final String DEFAULT_PACKAGE_NAME = "com.android.myapp";
private static final String DEFAULT_LOCALE_TAGS = "en-XC,ar-XB";
private static final String TEST_LOCALES_XML_TAG = "locales";
@@ -131,12 +134,17 @@ public class LocaleManagerBackupRestoreTest {
doReturn(mMockPackageManager).when(mMockContext).getPackageManager();
+ HandlerThread broadcastHandlerThread = new HandlerThread(TAG,
+ Process.THREAD_PRIORITY_BACKGROUND);
+ broadcastHandlerThread.start();
+
mBackupHelper = spy(new ShadowLocaleManagerBackupHelper(mMockContext,
- mMockLocaleManagerService, mMockPackageManagerInternal, mClock, STAGE_DATA));
+ mMockLocaleManagerService, mMockPackageManagerInternal, mClock, STAGE_DATA,
+ broadcastHandlerThread));
doNothing().when(mBackupHelper).notifyBackupManager();
mUserMonitor = mBackupHelper.getUserMonitor();
- mPackageMonitor = mBackupHelper.getPackageMonitor();
+ mPackageMonitor = new LocaleManagerServicePackageMonitor(mBackupHelper);
setCurrentTimeMillis(DEFAULT_CREATION_TIME_MILLIS);
}
diff --git a/services/tests/servicestests/src/com/android/server/locales/LocaleManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/locales/LocaleManagerServiceTest.java
index ca5b0cb1112f..0b3ef4542cbf 100644
--- a/services/tests/servicestests/src/com/android/server/locales/LocaleManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/locales/LocaleManagerServiceTest.java
@@ -46,6 +46,7 @@ import android.os.LocaleList;
import androidx.test.ext.junit.runners.AndroidJUnit4;
+import com.android.internal.content.PackageMonitor;
import com.android.server.wm.ActivityTaskManagerInternal;
import com.android.server.wm.ActivityTaskManagerInternal.PackageConfig;
@@ -86,6 +87,8 @@ public class LocaleManagerServiceTest {
private ActivityTaskManagerInternal mMockActivityTaskManager;
@Mock
private ActivityManagerInternal mMockActivityManager;
+ @Mock
+ PackageMonitor mMockPackageMonitor;
@Before
public void setUp() throws Exception {
@@ -93,6 +96,7 @@ public class LocaleManagerServiceTest {
mMockActivityTaskManager = mock(ActivityTaskManagerInternal.class);
mMockActivityManager = mock(ActivityManagerInternal.class);
mMockPackageManagerInternal = mock(PackageManagerInternal.class);
+ mMockPackageMonitor = mock(PackageMonitor.class);
// For unit tests, set the default installer info
PackageManager mockPackageManager = mock(PackageManager.class);
@@ -113,7 +117,8 @@ public class LocaleManagerServiceTest {
mMockBackupHelper = mock(ShadowLocaleManagerBackupHelper.class);
mLocaleManagerService = new LocaleManagerService(mMockContext, mMockActivityTaskManager,
- mMockActivityManager, mMockPackageManagerInternal, mMockBackupHelper);
+ mMockActivityManager, mMockPackageManagerInternal,
+ mMockBackupHelper, mMockPackageMonitor);
}
@Test(expected = SecurityException.class)
diff --git a/services/tests/servicestests/src/com/android/server/locales/ShadowLocaleManagerBackupHelper.java b/services/tests/servicestests/src/com/android/server/locales/ShadowLocaleManagerBackupHelper.java
index b0fc6363b701..ad9be0d5fe8d 100644
--- a/services/tests/servicestests/src/com/android/server/locales/ShadowLocaleManagerBackupHelper.java
+++ b/services/tests/servicestests/src/com/android/server/locales/ShadowLocaleManagerBackupHelper.java
@@ -18,6 +18,7 @@ package com.android.server.locales;
import android.content.Context;
import android.content.pm.PackageManagerInternal;
+import android.os.HandlerThread;
import android.util.SparseArray;
import java.time.Clock;
@@ -31,7 +32,8 @@ public class ShadowLocaleManagerBackupHelper extends LocaleManagerBackupHelper {
ShadowLocaleManagerBackupHelper(Context context,
LocaleManagerService localeManagerService,
PackageManagerInternal pmInternal, Clock clock,
- SparseArray<LocaleManagerBackupHelper.StagedData> stagedData) {
- super(context, localeManagerService, pmInternal, clock, stagedData);
+ SparseArray<LocaleManagerBackupHelper.StagedData> stagedData,
+ HandlerThread broadcastHandlerThread) {
+ super(context, localeManagerService, pmInternal, clock, stagedData, broadcastHandlerThread);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundHw2CompatTest.java b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundHw2CompatTest.java
index 16cfd13b18c2..6bdd88c6a712 100644
--- a/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundHw2CompatTest.java
+++ b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundHw2CompatTest.java
@@ -31,7 +31,6 @@ import static org.mockito.Mockito.atMost;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
@@ -49,7 +48,6 @@ import android.os.IBinder;
import android.os.IHwBinder;
import android.os.IHwInterface;
import android.os.RemoteException;
-import android.system.OsConstants;
import org.junit.After;
import org.junit.Before;
@@ -60,6 +58,7 @@ import org.mockito.ArgumentCaptor;
import java.util.LinkedList;
import java.util.List;
+import java.util.concurrent.atomic.AtomicReference;
@RunWith(Parameterized.class)
public class SoundHw2CompatTest {
@@ -240,14 +239,15 @@ public class SoundHw2CompatTest {
(android.hardware.soundtrigger.V2_1.ISoundTriggerHw) mHalDriver;
final int handle = 29;
- ArgumentCaptor<android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel> modelCaptor =
- ArgumentCaptor.forClass(
- android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel.class);
+ AtomicReference<android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel> model =
+ new AtomicReference<>();
ArgumentCaptor<android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback> callbackCaptor =
ArgumentCaptor.forClass(
android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.class);
doAnswer(invocation -> {
+ // We need to dup the model, as it gets invalidated after the call returns.
+ model.set(TestUtil.dupModel_2_1(invocation.getArgument(0)));
android.hardware.soundtrigger.V2_1.ISoundTriggerHw.loadSoundModel_2_1Callback
resultCallback = invocation.getArgument(3);
@@ -259,10 +259,9 @@ public class SoundHw2CompatTest {
assertEquals(handle,
mCanonical.loadSoundModel(TestUtil.createGenericSoundModel(), canonicalCallback));
- verify(driver_2_1).loadSoundModel_2_1(modelCaptor.capture(), callbackCaptor.capture(),
- anyInt(), any());
+ verify(driver_2_1).loadSoundModel_2_1(any(), callbackCaptor.capture(), anyInt(), any());
- TestUtil.validateGenericSoundModel_2_1(modelCaptor.getValue());
+ TestUtil.validateGenericSoundModel_2_1(model.get());
validateCallback_2_1(callbackCaptor.getValue(), canonicalCallback);
return handle;
}
@@ -355,14 +354,16 @@ public class SoundHw2CompatTest {
(android.hardware.soundtrigger.V2_1.ISoundTriggerHw) mHalDriver;
final int handle = 29;
- ArgumentCaptor<android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel>
- modelCaptor = ArgumentCaptor.forClass(
- android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel.class);
+ AtomicReference<android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel> model =
+ new AtomicReference<>();
ArgumentCaptor<android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback> callbackCaptor =
ArgumentCaptor.forClass(
android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.class);
doAnswer(invocation -> {
+ // We need to dup the model, as it gets invalidated after the call returns.
+ model.set(TestUtil.dupPhraseModel_2_1(invocation.getArgument(0)));
+
android.hardware.soundtrigger.V2_1.ISoundTriggerHw.loadPhraseSoundModel_2_1Callback
resultCallback = invocation.getArgument(3);
@@ -374,10 +375,10 @@ public class SoundHw2CompatTest {
assertEquals(handle, mCanonical.loadPhraseSoundModel(TestUtil.createPhraseSoundModel(),
canonicalCallback));
- verify(driver_2_1).loadPhraseSoundModel_2_1(modelCaptor.capture(), callbackCaptor.capture(),
- anyInt(), any());
+ verify(driver_2_1).loadPhraseSoundModel_2_1(any(), callbackCaptor.capture(), anyInt(),
+ any());
- TestUtil.validatePhraseSoundModel_2_1(modelCaptor.getValue());
+ TestUtil.validatePhraseSoundModel_2_1(model.get());
validateCallback_2_1(callbackCaptor.getValue(), canonicalCallback);
return handle;
}
diff --git a/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/TestUtil.java b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/TestUtil.java
index e687a2adeb39..30b4a59b32b1 100644
--- a/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/TestUtil.java
+++ b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/TestUtil.java
@@ -47,6 +47,7 @@ import android.os.HidlMemoryUtil;
import android.os.ParcelFileDescriptor;
import android.os.SharedMemory;
+import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.List;
@@ -82,6 +83,19 @@ class TestUtil {
HidlMemoryUtil.hidlMemoryToByteArray(model.data));
}
+ static android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel dupModel_2_1(
+ android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel model) {
+ android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel dup =
+ new android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel();
+ dup.header = model.header;
+ try {
+ dup.data = model.data.dup();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ return dup;
+ }
+
private static void validateSoundModel_2_0(
android.hardware.soundtrigger.V2_0.ISoundTriggerHw.SoundModel model, int type) {
assertEquals(type, model.type);
@@ -121,6 +135,15 @@ class TestUtil {
validatePhrases_2_0(model.phrases);
}
+ static android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel dupPhraseModel_2_1(
+ android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel model) {
+ android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel dup =
+ new android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel();
+ dup.common = dupModel_2_1(model.common);
+ dup.phrases = model.phrases;
+ return dup;
+ }
+
static void validatePhraseSoundModel_2_0(
android.hardware.soundtrigger.V2_0.ISoundTriggerHw.PhraseSoundModel model) {
validateSoundModel_2_0(model.common,
@@ -190,31 +213,27 @@ class TestUtil {
properties.maxKeyPhrases = 567;
properties.maxUsers = 678;
properties.recognitionModes =
- RecognitionMode.VOICE_TRIGGER
- | RecognitionMode.USER_IDENTIFICATION
- | RecognitionMode.USER_AUTHENTICATION
- | RecognitionMode.GENERIC_TRIGGER;
+ RecognitionMode.VOICE_TRIGGER | RecognitionMode.USER_IDENTIFICATION
+ | RecognitionMode.USER_AUTHENTICATION | RecognitionMode.GENERIC_TRIGGER;
properties.captureTransition = true;
properties.maxBufferMs = 321;
properties.concurrentCapture = supportConcurrentCapture;
properties.triggerInEvent = true;
properties.powerConsumptionMw = 432;
properties.supportedModelArch = "supportedModelArch";
- properties.audioCapabilities = AudioCapabilities.ECHO_CANCELLATION
- | AudioCapabilities.NOISE_SUPPRESSION;
+ properties.audioCapabilities =
+ AudioCapabilities.ECHO_CANCELLATION | AudioCapabilities.NOISE_SUPPRESSION;
return properties;
}
- static void validateDefaultProperties(Properties properties,
- boolean supportConcurrentCapture) {
+ static void validateDefaultProperties(Properties properties, boolean supportConcurrentCapture) {
validateDefaultProperties(properties, supportConcurrentCapture,
AudioCapabilities.ECHO_CANCELLATION | AudioCapabilities.NOISE_SUPPRESSION,
"supportedModelArch");
}
- static void validateDefaultProperties(Properties properties,
- boolean supportConcurrentCapture, @AudioCapabilities int audioCapabilities,
- @NonNull String supportedModelArch) {
+ static void validateDefaultProperties(Properties properties, boolean supportConcurrentCapture,
+ @AudioCapabilities int audioCapabilities, @NonNull String supportedModelArch) {
assertEquals("implementor", properties.implementor);
assertEquals("description", properties.description);
assertEquals(123, properties.version);
@@ -222,10 +241,9 @@ class TestUtil {
assertEquals(456, properties.maxSoundModels);
assertEquals(567, properties.maxKeyPhrases);
assertEquals(678, properties.maxUsers);
- assertEquals(RecognitionMode.GENERIC_TRIGGER
- | RecognitionMode.USER_AUTHENTICATION
- | RecognitionMode.USER_IDENTIFICATION
- | RecognitionMode.VOICE_TRIGGER, properties.recognitionModes);
+ assertEquals(RecognitionMode.GENERIC_TRIGGER | RecognitionMode.USER_AUTHENTICATION
+ | RecognitionMode.USER_IDENTIFICATION | RecognitionMode.VOICE_TRIGGER,
+ properties.recognitionModes);
assertTrue(properties.captureTransition);
assertEquals(321, properties.maxBufferMs);
assertEquals(supportConcurrentCapture, properties.concurrentCapture);
@@ -246,8 +264,8 @@ class TestUtil {
config.phraseRecognitionExtras[0].levels[0].userId = 234;
config.phraseRecognitionExtras[0].levels[0].levelPercent = 34;
config.data = new byte[]{5, 4, 3, 2, 1};
- config.audioCapabilities = AudioCapabilities.ECHO_CANCELLATION
- | AudioCapabilities.NOISE_SUPPRESSION;
+ config.audioCapabilities =
+ AudioCapabilities.ECHO_CANCELLATION | AudioCapabilities.NOISE_SUPPRESSION;
return config;
}
@@ -295,13 +313,12 @@ class TestUtil {
int captureHandle) {
validateRecognitionConfig_2_1(config.base, captureDevice, captureHandle);
- assertEquals(AudioCapabilities.ECHO_CANCELLATION
- | AudioCapabilities.NOISE_SUPPRESSION, config.audioCapabilities);
+ assertEquals(AudioCapabilities.ECHO_CANCELLATION | AudioCapabilities.NOISE_SUPPRESSION,
+ config.audioCapabilities);
}
static android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.RecognitionEvent createRecognitionEvent_2_0(
- int hwHandle,
- int status) {
+ int hwHandle, int status) {
android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.RecognitionEvent halEvent =
new android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.RecognitionEvent();
halEvent.status = status;
@@ -351,8 +368,7 @@ class TestUtil {
return event;
}
- static ISoundTriggerHwCallback.RecognitionEvent createRecognitionEvent_2_1(
- int hwHandle,
+ static ISoundTriggerHwCallback.RecognitionEvent createRecognitionEvent_2_1(int hwHandle,
int status) {
ISoundTriggerHwCallback.RecognitionEvent halEvent =
new ISoundTriggerHwCallback.RecognitionEvent();
@@ -386,8 +402,7 @@ class TestUtil {
PhraseRecognitionExtra extra = new PhraseRecognitionExtra();
extra.id = 123;
extra.confidenceLevel = 52;
- extra.recognitionModes = RecognitionMode.VOICE_TRIGGER
- | RecognitionMode.GENERIC_TRIGGER;
+ extra.recognitionModes = RecognitionMode.VOICE_TRIGGER | RecognitionMode.GENERIC_TRIGGER;
ConfidenceLevel level = new ConfidenceLevel();
level.userId = 31;
level.levelPercent = 43;
@@ -396,8 +411,8 @@ class TestUtil {
return event;
}
- static android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.PhraseRecognitionEvent
- createPhraseRecognitionEvent_2_0(int hwHandle, int status) {
+ static android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.PhraseRecognitionEvent createPhraseRecognitionEvent_2_0(
+ int hwHandle, int status) {
android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.PhraseRecognitionEvent halEvent =
new android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.PhraseRecognitionEvent();
halEvent.common = createRecognitionEvent_2_0(hwHandle, status);
diff --git a/services/tests/servicestests/src/com/android/server/tare/AgentTrendCalculatorTest.java b/services/tests/servicestests/src/com/android/server/tare/AgentTrendCalculatorTest.java
index b1b53fc7ca4c..9e986be99e95 100644
--- a/services/tests/servicestests/src/com/android/server/tare/AgentTrendCalculatorTest.java
+++ b/services/tests/servicestests/src/com/android/server/tare/AgentTrendCalculatorTest.java
@@ -65,7 +65,12 @@ public class AgentTrendCalculatorTest {
}
@Override
- long getMaxSatiatedCirculation() {
+ long getInitialSatiatedConsumptionLimit() {
+ return 0;
+ }
+
+ @Override
+ long getHardSatiatedConsumptionLimit() {
return 0;
}
@@ -102,7 +107,7 @@ public class AgentTrendCalculatorTest {
TrendCalculator trendCalculator = new TrendCalculator();
mEconomicPolicy.mEventCosts.put(JobSchedulerEconomicPolicy.ACTION_JOB_TIMEOUT, 20);
- trendCalculator.reset(0, null);
+ trendCalculator.reset(0, 0, null);
assertEquals("Expected not to cross lower threshold",
TrendCalculator.WILL_NOT_CROSS_THRESHOLD,
trendCalculator.getTimeToCrossLowerThresholdMs());
@@ -115,10 +120,10 @@ public class AgentTrendCalculatorTest {
new AnticipatedAction(JobSchedulerEconomicPolicy.ACTION_JOB_TIMEOUT, 1, 0))),
mock(AffordabilityChangeListener.class), mEconomicPolicy));
for (ActionAffordabilityNote note : affordabilityNotes) {
- note.recalculateModifiedPrice(mEconomicPolicy, 0, "com.test.app");
+ note.recalculateCosts(mEconomicPolicy, 0, "com.test.app");
}
- trendCalculator.reset(1234, affordabilityNotes);
+ trendCalculator.reset(1234, 1234, affordabilityNotes);
assertEquals("Expected not to cross lower threshold",
TrendCalculator.WILL_NOT_CROSS_THRESHOLD,
trendCalculator.getTimeToCrossLowerThresholdMs());
@@ -133,32 +138,28 @@ public class AgentTrendCalculatorTest {
OngoingEvent[] events = new OngoingEvent[]{
new OngoingEvent(JobSchedulerEconomicPolicy.ACTION_JOB_DEFAULT_RUNNING, "1",
- null, 1, 1),
+ 1, new EconomicPolicy.Cost(1, 4)),
new OngoingEvent(JobSchedulerEconomicPolicy.ACTION_JOB_HIGH_RUNNING, "2",
- null, 2, 3),
- new OngoingEvent(EconomicPolicy.REWARD_TOP_ACTIVITY, "3",
- null, 3, -3),
+ 2, new EconomicPolicy.Cost(3, 6)),
+ new OngoingEvent(EconomicPolicy.REWARD_TOP_ACTIVITY, "3", 3,
+ new EconomicPolicy.Reward(EconomicPolicy.REWARD_TOP_ACTIVITY, 0, 3, 3)),
};
- trendCalculator.reset(0, null);
+ trendCalculator.reset(0, 100, null);
for (OngoingEvent event : events) {
trendCalculator.accept(event);
}
- assertEquals("Expected not to cross lower threshold",
- TrendCalculator.WILL_NOT_CROSS_THRESHOLD,
- trendCalculator.getTimeToCrossLowerThresholdMs());
+ assertEquals(25_000, trendCalculator.getTimeToCrossLowerThresholdMs());
assertEquals("Expected not to cross upper threshold",
TrendCalculator.WILL_NOT_CROSS_THRESHOLD,
trendCalculator.getTimeToCrossUpperThresholdMs());
ArraySet<ActionAffordabilityNote> affordabilityNotes = new ArraySet<>();
- trendCalculator.reset(1234, affordabilityNotes);
+ trendCalculator.reset(1234, 1234, affordabilityNotes);
for (OngoingEvent event : events) {
trendCalculator.accept(event);
}
- assertEquals("Expected not to cross lower threshold",
- TrendCalculator.WILL_NOT_CROSS_THRESHOLD,
- trendCalculator.getTimeToCrossLowerThresholdMs());
+ assertEquals(308_000, trendCalculator.getTimeToCrossLowerThresholdMs());
assertEquals("Expected not to cross upper threshold",
TrendCalculator.WILL_NOT_CROSS_THRESHOLD,
trendCalculator.getTimeToCrossUpperThresholdMs());
@@ -174,18 +175,19 @@ public class AgentTrendCalculatorTest {
new AnticipatedAction(JobSchedulerEconomicPolicy.ACTION_JOB_MAX_RUNNING, 0, 1000))),
mock(AffordabilityChangeListener.class), mEconomicPolicy));
for (ActionAffordabilityNote note : affordabilityNotes) {
- note.recalculateModifiedPrice(mEconomicPolicy, 0, "com.test.app");
+ note.recalculateCosts(mEconomicPolicy, 0, "com.test.app");
}
// Balance is already above threshold and events are all positive delta.
// There should be no time to report.
- trendCalculator.reset(1234, affordabilityNotes);
+ trendCalculator.reset(1234, 1234, affordabilityNotes);
trendCalculator.accept(
- new OngoingEvent(JobSchedulerEconomicPolicy.ACTION_JOB_DEFAULT_RUNNING, "1",
- null, 1, 1));
+ new OngoingEvent(EconomicPolicy.REWARD_TOP_ACTIVITY, "1", 1,
+ new EconomicPolicy.Reward(EconomicPolicy.REWARD_TOP_ACTIVITY, 1, 1, 1)));
trendCalculator.accept(
- new OngoingEvent(JobSchedulerEconomicPolicy.ACTION_JOB_HIGH_RUNNING, "2",
- null, 2, 3));
+ new OngoingEvent(EconomicPolicy.REWARD_OTHER_USER_INTERACTION, "2", 2,
+ new EconomicPolicy.Reward(EconomicPolicy.REWARD_OTHER_USER_INTERACTION,
+ 3, 3, 3)));
assertEquals("Expected not to cross lower threshold",
TrendCalculator.WILL_NOT_CROSS_THRESHOLD,
@@ -196,16 +198,16 @@ public class AgentTrendCalculatorTest {
// Balance is already below threshold and events are all negative delta.
// There should be no time to report.
- trendCalculator.reset(1, affordabilityNotes);
+ trendCalculator.reset(1, 0, affordabilityNotes);
trendCalculator.accept(
new OngoingEvent(JobSchedulerEconomicPolicy.ACTION_JOB_DEFAULT_RUNNING, "1",
- null, 1, -1));
+ 1, new EconomicPolicy.Cost(1, 1)));
trendCalculator.accept(
new OngoingEvent(JobSchedulerEconomicPolicy.ACTION_JOB_HIGH_RUNNING, "2",
- null, 2, -3));
+ 2, new EconomicPolicy.Cost(3, 3)));
assertEquals("Expected not to cross lower threshold",
- TrendCalculator.WILL_NOT_CROSS_THRESHOLD,
+ 0,
trendCalculator.getTimeToCrossLowerThresholdMs());
assertEquals("Expected not to cross upper threshold",
TrendCalculator.WILL_NOT_CROSS_THRESHOLD,
@@ -213,7 +215,7 @@ public class AgentTrendCalculatorTest {
}
@Test
- public void testSimpleTrendToThreshold() {
+ public void testSimpleTrendToThreshold_Balance() {
TrendCalculator trendCalculator = new TrendCalculator();
mEconomicPolicy.mEventCosts.put(JobSchedulerEconomicPolicy.ACTION_JOB_MAX_START, 20);
@@ -222,18 +224,19 @@ public class AgentTrendCalculatorTest {
new AnticipatedAction(JobSchedulerEconomicPolicy.ACTION_JOB_MAX_START, 1, 0))),
mock(AffordabilityChangeListener.class), mEconomicPolicy));
for (ActionAffordabilityNote note : affordabilityNotes) {
- note.recalculateModifiedPrice(mEconomicPolicy, 0, "com.test.app");
+ note.recalculateCosts(mEconomicPolicy, 0, "com.test.app");
}
// Balance is below threshold and events are all positive delta.
// Should report the correct time to the upper threshold.
- trendCalculator.reset(0, affordabilityNotes);
+ trendCalculator.reset(0, 1000, affordabilityNotes);
trendCalculator.accept(
- new OngoingEvent(EconomicPolicy.REWARD_TOP_ACTIVITY, "1",
- null, 1, 1));
+ new OngoingEvent(EconomicPolicy.REWARD_TOP_ACTIVITY, "1", 1,
+ new EconomicPolicy.Reward(EconomicPolicy.REWARD_TOP_ACTIVITY, 1, 1, 1)));
trendCalculator.accept(
- new OngoingEvent(EconomicPolicy.REWARD_OTHER_USER_INTERACTION, "2",
- null, 2, 3));
+ new OngoingEvent(EconomicPolicy.REWARD_OTHER_USER_INTERACTION, "2", 2,
+ new EconomicPolicy.Reward(EconomicPolicy.REWARD_OTHER_USER_INTERACTION,
+ 3, 3, 3)));
assertEquals("Expected not to cross lower threshold",
TrendCalculator.WILL_NOT_CROSS_THRESHOLD,
@@ -242,13 +245,13 @@ public class AgentTrendCalculatorTest {
// Balance is above the threshold and events are all negative delta.
// Should report the correct time to the lower threshold.
- trendCalculator.reset(40, affordabilityNotes);
+ trendCalculator.reset(40, 100, affordabilityNotes);
trendCalculator.accept(
new OngoingEvent(JobSchedulerEconomicPolicy.ACTION_JOB_DEFAULT_RUNNING, "1",
- null, 1, -1));
+ 1, new EconomicPolicy.Cost(1, 1)));
trendCalculator.accept(
new OngoingEvent(JobSchedulerEconomicPolicy.ACTION_JOB_HIGH_RUNNING, "2",
- null, 2, -3));
+ 2, new EconomicPolicy.Cost(3, 3)));
assertEquals(5_000, trendCalculator.getTimeToCrossLowerThresholdMs());
assertEquals("Expected not to cross upper threshold",
@@ -257,7 +260,7 @@ public class AgentTrendCalculatorTest {
}
@Test
- public void testSelectCorrectThreshold() {
+ public void testSelectCorrectThreshold_Balance() {
TrendCalculator trendCalculator = new TrendCalculator();
mEconomicPolicy.mEventCosts.put(JobSchedulerEconomicPolicy.ACTION_JOB_MAX_START, 20);
mEconomicPolicy.mEventCosts.put(JobSchedulerEconomicPolicy.ACTION_JOB_HIGH_START, 15);
@@ -278,15 +281,15 @@ public class AgentTrendCalculatorTest {
new AnticipatedAction(JobSchedulerEconomicPolicy.ACTION_JOB_MIN_START, 1, 0))),
mock(AffordabilityChangeListener.class), mEconomicPolicy));
for (ActionAffordabilityNote note : affordabilityNotes) {
- note.recalculateModifiedPrice(mEconomicPolicy, 0, "com.test.app");
+ note.recalculateCosts(mEconomicPolicy, 0, "com.test.app");
}
// Balance is below threshold and events are all positive delta.
// Should report the correct time to the correct upper threshold.
- trendCalculator.reset(0, affordabilityNotes);
+ trendCalculator.reset(0, 10_000, affordabilityNotes);
trendCalculator.accept(
- new OngoingEvent(EconomicPolicy.REWARD_TOP_ACTIVITY, "1",
- null, 1, 1));
+ new OngoingEvent(EconomicPolicy.REWARD_TOP_ACTIVITY, "1", 1,
+ new EconomicPolicy.Reward(EconomicPolicy.REWARD_TOP_ACTIVITY, 1, 1, 1)));
assertEquals("Expected not to cross lower threshold",
TrendCalculator.WILL_NOT_CROSS_THRESHOLD,
@@ -295,10 +298,10 @@ public class AgentTrendCalculatorTest {
// Balance is above the threshold and events are all negative delta.
// Should report the correct time to the correct lower threshold.
- trendCalculator.reset(30, affordabilityNotes);
+ trendCalculator.reset(30, 500, affordabilityNotes);
trendCalculator.accept(
new OngoingEvent(JobSchedulerEconomicPolicy.ACTION_JOB_DEFAULT_RUNNING, "1",
- null, 1, -1));
+ 1, new EconomicPolicy.Cost(1, 1)));
assertEquals(10_000, trendCalculator.getTimeToCrossLowerThresholdMs());
assertEquals("Expected not to cross upper threshold",
@@ -307,7 +310,7 @@ public class AgentTrendCalculatorTest {
}
@Test
- public void testTrendsToBothThresholds() {
+ public void testTrendsToBothThresholds_Balance() {
TrendCalculator trendCalculator = new TrendCalculator();
mEconomicPolicy.mEventCosts.put(JobSchedulerEconomicPolicy.ACTION_JOB_MAX_START, 20);
mEconomicPolicy.mEventCosts.put(AlarmManagerEconomicPolicy.ACTION_ALARM_CLOCK, 50);
@@ -320,26 +323,120 @@ public class AgentTrendCalculatorTest {
new AnticipatedAction(AlarmManagerEconomicPolicy.ACTION_ALARM_CLOCK, 1, 0))),
mock(AffordabilityChangeListener.class), mEconomicPolicy));
for (ActionAffordabilityNote note : affordabilityNotes) {
- note.recalculateModifiedPrice(mEconomicPolicy, 0, "com.test.app");
+ note.recalculateCosts(mEconomicPolicy, 0, "com.test.app");
}
// Balance is between both thresholds and events are mixed positive/negative delta.
// Should report the correct time to each threshold.
- trendCalculator.reset(35, affordabilityNotes);
+ trendCalculator.reset(35, 10_000, affordabilityNotes);
trendCalculator.accept(
- new OngoingEvent(EconomicPolicy.REWARD_TOP_ACTIVITY, "1",
- null, 1, 3));
+ new OngoingEvent(EconomicPolicy.REWARD_TOP_ACTIVITY, "1", 1,
+ new EconomicPolicy.Reward(EconomicPolicy.REWARD_TOP_ACTIVITY, 3, 3, 3)));
trendCalculator.accept(
- new OngoingEvent(EconomicPolicy.REWARD_OTHER_USER_INTERACTION, "2",
- null, 2, 2));
+ new OngoingEvent(EconomicPolicy.REWARD_OTHER_USER_INTERACTION, "2", 2,
+ new EconomicPolicy.Reward(EconomicPolicy.REWARD_OTHER_USER_INTERACTION, 2,
+ 2, 2)));
trendCalculator.accept(
new OngoingEvent(JobSchedulerEconomicPolicy.ACTION_JOB_LOW_RUNNING, "3",
- null, 3, -2));
+ 3, new EconomicPolicy.Cost(2, 2)));
trendCalculator.accept(
new OngoingEvent(JobSchedulerEconomicPolicy.ACTION_JOB_DEFAULT_RUNNING, "4",
- null, 4, -3));
+ 4, new EconomicPolicy.Cost(3, 3)));
assertEquals(3_000, trendCalculator.getTimeToCrossLowerThresholdMs());
assertEquals(3_000, trendCalculator.getTimeToCrossUpperThresholdMs());
}
+
+ @Test
+ public void testSimpleTrendToThreshold_ConsumptionLimit() {
+ TrendCalculator trendCalculator = new TrendCalculator();
+ mEconomicPolicy.mEventCosts.put(JobSchedulerEconomicPolicy.ACTION_JOB_MAX_START, 20);
+
+ ArraySet<ActionAffordabilityNote> affordabilityNotes = new ArraySet<>();
+ affordabilityNotes.add(new ActionAffordabilityNote(new ActionBill(List.of(
+ new AnticipatedAction(JobSchedulerEconomicPolicy.ACTION_JOB_MAX_START, 1, 0))),
+ mock(AffordabilityChangeListener.class), mEconomicPolicy));
+ for (ActionAffordabilityNote note : affordabilityNotes) {
+ note.recalculateCosts(mEconomicPolicy, 0, "com.test.app");
+ }
+
+ // Events are all negative delta. Consumable credits will run out before app's balance.
+ // Should report the correct time to the lower threshold.
+ trendCalculator.reset(10000, 40, affordabilityNotes);
+ trendCalculator.accept(
+ new OngoingEvent(JobSchedulerEconomicPolicy.ACTION_JOB_DEFAULT_RUNNING, "1",
+ 1, new EconomicPolicy.Cost(1, 10)));
+ trendCalculator.accept(
+ new OngoingEvent(JobSchedulerEconomicPolicy.ACTION_JOB_HIGH_RUNNING, "2",
+ 2, new EconomicPolicy.Cost(3, 40)));
+
+ assertEquals(10_000, trendCalculator.getTimeToCrossLowerThresholdMs());
+ assertEquals("Expected not to cross upper threshold",
+ TrendCalculator.WILL_NOT_CROSS_THRESHOLD,
+ trendCalculator.getTimeToCrossUpperThresholdMs());
+ }
+
+ @Test
+ public void testSelectCorrectThreshold() {
+ TrendCalculator trendCalculator = new TrendCalculator();
+ mEconomicPolicy.mEventCosts.put(JobSchedulerEconomicPolicy.ACTION_JOB_MAX_START, 20);
+ mEconomicPolicy.mEventCosts.put(JobSchedulerEconomicPolicy.ACTION_JOB_HIGH_START, 15);
+ mEconomicPolicy.mEventCosts.put(JobSchedulerEconomicPolicy.ACTION_JOB_LOW_START, 10);
+ mEconomicPolicy.mEventCosts.put(JobSchedulerEconomicPolicy.ACTION_JOB_MIN_START, 5);
+
+ ArraySet<ActionAffordabilityNote> affordabilityNotes = new ArraySet<>();
+ affordabilityNotes.add(new ActionAffordabilityNote(new ActionBill(List.of(
+ new AnticipatedAction(JobSchedulerEconomicPolicy.ACTION_JOB_MAX_START, 1, 0))),
+ mock(AffordabilityChangeListener.class), mEconomicPolicy));
+ affordabilityNotes.add(new ActionAffordabilityNote(new ActionBill(List.of(
+ new AnticipatedAction(JobSchedulerEconomicPolicy.ACTION_JOB_HIGH_START, 1, 0))),
+ mock(AffordabilityChangeListener.class), mEconomicPolicy));
+ affordabilityNotes.add(new ActionAffordabilityNote(new ActionBill(List.of(
+ new AnticipatedAction(JobSchedulerEconomicPolicy.ACTION_JOB_LOW_START, 1, 0))),
+ mock(AffordabilityChangeListener.class), mEconomicPolicy));
+ affordabilityNotes.add(new ActionAffordabilityNote(new ActionBill(List.of(
+ new AnticipatedAction(JobSchedulerEconomicPolicy.ACTION_JOB_MIN_START, 1, 0))),
+ mock(AffordabilityChangeListener.class), mEconomicPolicy));
+ for (ActionAffordabilityNote note : affordabilityNotes) {
+ note.recalculateCosts(mEconomicPolicy, 0, "com.test.app");
+ }
+
+ // Balance is above threshold, consumable credits is 0, and events are all positive delta.
+ // There should be no time to the upper threshold since consumable credits is the limiting
+ // factor.
+ trendCalculator.reset(10_000, 0, affordabilityNotes);
+ trendCalculator.accept(
+ new OngoingEvent(EconomicPolicy.REWARD_TOP_ACTIVITY, "1", 1,
+ new EconomicPolicy.Reward(EconomicPolicy.REWARD_TOP_ACTIVITY, 1, 1, 1)));
+
+ assertEquals("Expected not to cross lower threshold",
+ TrendCalculator.WILL_NOT_CROSS_THRESHOLD,
+ trendCalculator.getTimeToCrossLowerThresholdMs());
+ assertEquals("Expected not to cross upper threshold",
+ TrendCalculator.WILL_NOT_CROSS_THRESHOLD,
+ trendCalculator.getTimeToCrossUpperThresholdMs());
+
+ // Balance is above threshold, consumable credits is low, and events are all negative delta.
+ trendCalculator.reset(10_000, 4, affordabilityNotes);
+ trendCalculator.accept(
+ new OngoingEvent(JobSchedulerEconomicPolicy.ACTION_JOB_DEFAULT_RUNNING, "1",
+ 1, new EconomicPolicy.Cost(1, 10)));
+
+ assertEquals(4000, trendCalculator.getTimeToCrossLowerThresholdMs());
+ assertEquals("Expected not to cross upper threshold",
+ TrendCalculator.WILL_NOT_CROSS_THRESHOLD,
+ trendCalculator.getTimeToCrossUpperThresholdMs());
+
+ // Balance is above threshold, consumable credits is 0, and events are all negative delta.
+ // Time to the lower threshold should be 0 since consumable credits is already 0.
+ trendCalculator.reset(10_000, 0, affordabilityNotes);
+ trendCalculator.accept(
+ new OngoingEvent(JobSchedulerEconomicPolicy.ACTION_JOB_DEFAULT_RUNNING, "1",
+ 1, new EconomicPolicy.Cost(1, 10)));
+
+ assertEquals(0, trendCalculator.getTimeToCrossLowerThresholdMs());
+ assertEquals("Expected not to cross upper threshold",
+ TrendCalculator.WILL_NOT_CROSS_THRESHOLD,
+ trendCalculator.getTimeToCrossUpperThresholdMs());
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/tare/LedgerTest.java b/services/tests/servicestests/src/com/android/server/tare/LedgerTest.java
index 4a253234b59e..22dcf842906c 100644
--- a/services/tests/servicestests/src/com/android/server/tare/LedgerTest.java
+++ b/services/tests/servicestests/src/com/android/server/tare/LedgerTest.java
@@ -54,13 +54,13 @@ public class LedgerTest {
@Test
public void testMultipleTransactions() {
final Ledger ledger = new Ledger();
- ledger.recordTransaction(new Ledger.Transaction(0, 1000, 1, null, 5));
+ ledger.recordTransaction(new Ledger.Transaction(0, 1000, 1, null, 5, 0));
assertEquals(5, ledger.getCurrentBalance());
assertEquals(5, ledger.get24HourSum(1, 60_000));
- ledger.recordTransaction(new Ledger.Transaction(2000, 2000, 1, null, 25));
+ ledger.recordTransaction(new Ledger.Transaction(2000, 2000, 1, null, 25, 0));
assertEquals(30, ledger.getCurrentBalance());
assertEquals(30, ledger.get24HourSum(1, 60_000));
- ledger.recordTransaction(new Ledger.Transaction(5000, 5500, 1, null, -10));
+ ledger.recordTransaction(new Ledger.Transaction(5000, 5500, 1, null, -10, 5));
assertEquals(20, ledger.getCurrentBalance());
assertEquals(20, ledger.get24HourSum(1, 60_000));
}
@@ -68,13 +68,13 @@ public class LedgerTest {
@Test
public void test24HourSum() {
final Ledger ledger = new Ledger();
- ledger.recordTransaction(new Ledger.Transaction(0, 1000, 1, null, 500));
+ ledger.recordTransaction(new Ledger.Transaction(0, 1000, 1, null, 500, 0));
assertEquals(500, ledger.get24HourSum(1, 24 * HOUR_IN_MILLIS));
ledger.recordTransaction(
- new Ledger.Transaction(2 * HOUR_IN_MILLIS, 3 * HOUR_IN_MILLIS, 1, null, 2500));
+ new Ledger.Transaction(2 * HOUR_IN_MILLIS, 3 * HOUR_IN_MILLIS, 1, null, 2500, 0));
assertEquals(3000, ledger.get24HourSum(1, 24 * HOUR_IN_MILLIS));
ledger.recordTransaction(
- new Ledger.Transaction(4 * HOUR_IN_MILLIS, 4 * HOUR_IN_MILLIS, 1, null, 1));
+ new Ledger.Transaction(4 * HOUR_IN_MILLIS, 4 * HOUR_IN_MILLIS, 1, null, 1, 0));
assertEquals(3001, ledger.get24HourSum(1, 24 * HOUR_IN_MILLIS));
assertEquals(2501, ledger.get24HourSum(1, 25 * HOUR_IN_MILLIS));
assertEquals(2501, ledger.get24HourSum(1, 26 * HOUR_IN_MILLIS));
@@ -93,17 +93,17 @@ public class LedgerTest {
final long now = getCurrentTimeMillis();
Ledger.Transaction transaction1 = new Ledger.Transaction(
- now - 48 * HOUR_IN_MILLIS, now - 40 * HOUR_IN_MILLIS, 1, null, 4800);
+ now - 48 * HOUR_IN_MILLIS, now - 40 * HOUR_IN_MILLIS, 1, null, 4800, 0);
Ledger.Transaction transaction2 = new Ledger.Transaction(
- now - 24 * HOUR_IN_MILLIS, now - 23 * HOUR_IN_MILLIS, 1, null, 600);
+ now - 24 * HOUR_IN_MILLIS, now - 23 * HOUR_IN_MILLIS, 1, null, 600, 0);
Ledger.Transaction transaction3 = new Ledger.Transaction(
- now - 22 * HOUR_IN_MILLIS, now - 21 * HOUR_IN_MILLIS, 1, null, 600);
+ now - 22 * HOUR_IN_MILLIS, now - 21 * HOUR_IN_MILLIS, 1, null, 600, 0);
// Instant event
Ledger.Transaction transaction4 = new Ledger.Transaction(
- now - 20 * HOUR_IN_MILLIS, now - 20 * HOUR_IN_MILLIS, 1, null, 500);
+ now - 20 * HOUR_IN_MILLIS, now - 20 * HOUR_IN_MILLIS, 1, null, 500, 0);
// Recent event
Ledger.Transaction transaction5 = new Ledger.Transaction(
- now - 5 * MINUTE_IN_MILLIS, now - MINUTE_IN_MILLIS, 1, null, 400);
+ now - 5 * MINUTE_IN_MILLIS, now - MINUTE_IN_MILLIS, 1, null, 400, 0);
ledger.recordTransaction(transaction1);
ledger.recordTransaction(transaction2);
ledger.recordTransaction(transaction3);
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java
index e1a4989e5a05..3d24a814c7cd 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java
@@ -61,8 +61,6 @@ import android.util.SparseArray;
import androidx.test.InstrumentationRegistry;
-import com.android.internal.app.IBatteryStats;
-
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
@@ -106,8 +104,6 @@ public class VibrationThreadTest {
@Mock
private IBinder mVibrationToken;
@Mock
- private IBatteryStats mIBatteryStatsMock;
- @Mock
private VibrationConfig mVibrationConfigMock;
private final Map<Integer, FakeVibratorControllerProvider> mVibratorProviders = new HashMap<>();
@@ -117,6 +113,9 @@ public class VibrationThreadTest {
private TestLooper mTestLooper;
private TestLooperAutoDispatcher mCustomTestLooperDispatcher;
+ // Setup from the providers when VibrationThread is initialized.
+ private SparseArray<VibratorController> mControllers;
+
@Before
public void setUp() throws Exception {
mTestLooper = new TestLooper();
@@ -178,11 +177,11 @@ public class VibrationThreadTest {
VibrationThread thread = startThreadAndDispatcher(vibrationId, effect);
waitForCompletion(thread);
- verify(mIBatteryStatsMock).noteVibratorOn(eq(UID), eq(10L));
- verify(mIBatteryStatsMock).noteVibratorOff(eq(UID));
+ verify(mManagerHooks).noteVibratorOn(eq(UID), eq(10L));
+ verify(mManagerHooks).noteVibratorOff(eq(UID));
verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
- assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating());
+ assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
assertEquals(Arrays.asList(expectedOneShot(10)),
mVibratorProviders.get(VIBRATOR_ID).getEffectSegments());
@@ -197,11 +196,11 @@ public class VibrationThreadTest {
VibrationThread thread = startThreadAndDispatcher(vibrationId, effect);
waitForCompletion(thread);
- verify(mIBatteryStatsMock).noteVibratorOn(eq(UID), eq(10L));
- verify(mIBatteryStatsMock).noteVibratorOff(eq(UID));
+ verify(mManagerHooks).noteVibratorOn(eq(UID), eq(10L));
+ verify(mManagerHooks).noteVibratorOff(eq(UID));
verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
- assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating());
+ assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
assertEquals(Arrays.asList(expectedOneShot(10)),
mVibratorProviders.get(VIBRATOR_ID).getEffectSegments());
@@ -219,11 +218,11 @@ public class VibrationThreadTest {
VibrationThread thread = startThreadAndDispatcher(vibrationId, effect);
waitForCompletion(thread);
- verify(mIBatteryStatsMock).noteVibratorOn(eq(UID), eq(15L));
- verify(mIBatteryStatsMock).noteVibratorOff(eq(UID));
+ verify(mManagerHooks).noteVibratorOn(eq(UID), eq(15L));
+ verify(mManagerHooks).noteVibratorOff(eq(UID));
verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
- assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating());
+ assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
assertEquals(Arrays.asList(expectedOneShot(15)),
mVibratorProviders.get(VIBRATOR_ID).getEffectSegments());
@@ -247,15 +246,15 @@ public class VibrationThreadTest {
thread, TEST_TIMEOUT_MILLIS));
// Vibration still running after 2 cycles.
assertTrue(thread.isAlive());
- assertTrue(thread.getVibrators().get(VIBRATOR_ID).isVibrating());
+ assertTrue(mControllers.get(VIBRATOR_ID).isVibrating());
thread.cancel();
waitForCompletion(thread);
- verify(mIBatteryStatsMock).noteVibratorOn(eq(UID), anyLong());
- verify(mIBatteryStatsMock).noteVibratorOff(eq(UID));
+ verify(mManagerHooks).noteVibratorOn(eq(UID), anyLong());
+ verify(mManagerHooks).noteVibratorOff(eq(UID));
verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED);
- assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating());
+ assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
List<Float> playedAmplitudes = fakeVibrator.getAmplitudes();
assertFalse(fakeVibrator.getEffectSegments().isEmpty());
@@ -284,7 +283,7 @@ public class VibrationThreadTest {
waitForCompletion(thread);
verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED);
- assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating());
+ assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
assertEquals(Arrays.asList(expectedOneShot(1000)), fakeVibrator.getEffectSegments());
}
@@ -306,7 +305,7 @@ public class VibrationThreadTest {
waitForCompletion(thread);
verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED);
- assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating());
+ assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
assertEquals(Arrays.asList(expectedOneShot(5550)), fakeVibrator.getEffectSegments());
}
@@ -329,7 +328,7 @@ public class VibrationThreadTest {
waitForCompletion(thread);
verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED);
- assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating());
+ assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
assertEquals(2, fakeVibrator.getEffectSegments().size());
// First time turn vibrator ON for minimum of 1s.
assertEquals(1000L, fakeVibrator.getEffectSegments().get(0).getDuration());
@@ -354,7 +353,7 @@ public class VibrationThreadTest {
.compose();
VibrationThread vibrationThread = startThreadAndDispatcher(vibrationId, effect);
- assertTrue(waitUntil(t -> t.getVibrators().get(VIBRATOR_ID).isVibrating(), vibrationThread,
+ assertTrue(waitUntil(t -> mControllers.get(VIBRATOR_ID).isVibrating(), vibrationThread,
TEST_TIMEOUT_MILLIS));
assertTrue(vibrationThread.isAlive());
@@ -367,7 +366,7 @@ public class VibrationThreadTest {
waitForCompletion(cancellingThread);
verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED);
- assertFalse(vibrationThread.getVibrators().get(VIBRATOR_ID).isVibrating());
+ assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
}
@Test
@@ -379,7 +378,7 @@ public class VibrationThreadTest {
VibrationEffect effect = VibrationEffect.createWaveform(new long[]{100}, new int[]{100}, 0);
VibrationThread vibrationThread = startThreadAndDispatcher(vibrationId, effect);
- assertTrue(waitUntil(t -> t.getVibrators().get(VIBRATOR_ID).isVibrating(), vibrationThread,
+ assertTrue(waitUntil(t -> mControllers.get(VIBRATOR_ID).isVibrating(), vibrationThread,
TEST_TIMEOUT_MILLIS));
assertTrue(vibrationThread.isAlive());
@@ -392,7 +391,7 @@ public class VibrationThreadTest {
waitForCompletion(cancellingThread);
verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED);
- assertFalse(vibrationThread.getVibrators().get(VIBRATOR_ID).isVibrating());
+ assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
}
@Test
@@ -404,11 +403,11 @@ public class VibrationThreadTest {
VibrationThread thread = startThreadAndDispatcher(vibrationId, effect);
waitForCompletion(thread);
- verify(mIBatteryStatsMock).noteVibratorOn(eq(UID), eq(20L));
- verify(mIBatteryStatsMock).noteVibratorOff(eq(UID));
+ verify(mManagerHooks).noteVibratorOn(eq(UID), eq(20L));
+ verify(mManagerHooks).noteVibratorOff(eq(UID));
verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
- assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating());
+ assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
assertEquals(Arrays.asList(expectedPrebaked(VibrationEffect.EFFECT_THUD)),
mVibratorProviders.get(VIBRATOR_ID).getEffectSegments());
@@ -427,11 +426,11 @@ public class VibrationThreadTest {
VibrationThread thread = startThreadAndDispatcher(vibration);
waitForCompletion(thread);
- verify(mIBatteryStatsMock).noteVibratorOn(eq(UID), eq(10L));
- verify(mIBatteryStatsMock).noteVibratorOff(eq(UID));
+ verify(mManagerHooks).noteVibratorOn(eq(UID), eq(10L));
+ verify(mManagerHooks).noteVibratorOff(eq(UID));
verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
- assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating());
+ assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
assertEquals(Arrays.asList(expectedOneShot(10)),
mVibratorProviders.get(VIBRATOR_ID).getEffectSegments());
@@ -446,8 +445,8 @@ public class VibrationThreadTest {
VibrationThread thread = startThreadAndDispatcher(vibrationId, effect);
waitForCompletion(thread);
- verify(mIBatteryStatsMock, never()).noteVibratorOn(eq(UID), anyLong());
- verify(mIBatteryStatsMock, never()).noteVibratorOff(eq(UID));
+ verify(mManagerHooks, never()).noteVibratorOn(eq(UID), anyLong());
+ verify(mManagerHooks, never()).noteVibratorOff(eq(UID));
verify(mControllerCallbacks, never()).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
verifyCallbacksTriggered(vibrationId, Vibration.Status.IGNORED_UNSUPPORTED);
assertTrue(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments().isEmpty());
@@ -466,11 +465,11 @@ public class VibrationThreadTest {
VibrationThread thread = startThreadAndDispatcher(vibrationId, effect);
waitForCompletion(thread);
- verify(mIBatteryStatsMock).noteVibratorOn(eq(UID), eq(40L));
- verify(mIBatteryStatsMock).noteVibratorOff(eq(UID));
+ verify(mManagerHooks).noteVibratorOn(eq(UID), eq(40L));
+ verify(mManagerHooks).noteVibratorOff(eq(UID));
verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
- assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating());
+ assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
assertEquals(Arrays.asList(
expectedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1, 0),
expectedPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 0.5f, 0)),
@@ -486,8 +485,8 @@ public class VibrationThreadTest {
VibrationThread thread = startThreadAndDispatcher(vibrationId, effect);
waitForCompletion(thread);
- verify(mIBatteryStatsMock, never()).noteVibratorOn(eq(UID), anyLong());
- verify(mIBatteryStatsMock, never()).noteVibratorOff(eq(UID));
+ verify(mManagerHooks, never()).noteVibratorOn(eq(UID), anyLong());
+ verify(mManagerHooks, never()).noteVibratorOff(eq(UID));
verify(mControllerCallbacks, never()).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
verifyCallbacksTriggered(vibrationId, Vibration.Status.IGNORED_UNSUPPORTED);
assertTrue(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments().isEmpty());
@@ -536,11 +535,11 @@ public class VibrationThreadTest {
waitForCompletion(thread);
// Use first duration the vibrator is turned on since we cannot estimate the clicks.
- verify(mIBatteryStatsMock).noteVibratorOn(eq(UID), eq(10L));
- verify(mIBatteryStatsMock).noteVibratorOff(eq(UID));
+ verify(mManagerHooks).noteVibratorOn(eq(UID), eq(10L));
+ verify(mManagerHooks).noteVibratorOff(eq(UID));
verify(mControllerCallbacks, times(4)).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
- assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating());
+ assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
assertEquals(Arrays.asList(
expectedOneShot(10),
expectedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1, 0),
@@ -573,11 +572,11 @@ public class VibrationThreadTest {
VibrationThread thread = startThreadAndDispatcher(vibrationId, effect);
waitForCompletion(thread);
- verify(mIBatteryStatsMock).noteVibratorOn(eq(UID), eq(100L));
- verify(mIBatteryStatsMock).noteVibratorOff(eq(UID));
+ verify(mManagerHooks).noteVibratorOn(eq(UID), eq(100L));
+ verify(mManagerHooks).noteVibratorOff(eq(UID));
verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
- assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating());
+ assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
assertEquals(Arrays.asList(
expectedRamp(/* amplitude= */ 1, /* frequencyHz= */ 150, /* duration= */ 10),
expectedRamp(/* startAmplitude= */ 1, /* endAmplitude= */ 0,
@@ -630,11 +629,11 @@ public class VibrationThreadTest {
TEST_TIMEOUT_MILLIS));
// Vibration still running after 2 cycles.
assertTrue(thread.isAlive());
- assertTrue(thread.getVibrators().get(VIBRATOR_ID).isVibrating());
+ assertTrue(mControllers.get(VIBRATOR_ID).isVibrating());
thread.binderDied();
waitForCompletion(thread);
- assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating());
+ assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED);
}
@@ -666,12 +665,12 @@ public class VibrationThreadTest {
VibrationThread thread = startThreadAndDispatcher(vibrationId, effect);
waitForCompletion(thread);
- verify(mIBatteryStatsMock).noteVibratorOn(eq(UID), eq(20L));
- verify(mIBatteryStatsMock).noteVibratorOff(eq(UID));
+ verify(mManagerHooks).noteVibratorOn(eq(UID), eq(20L));
+ verify(mManagerHooks).noteVibratorOff(eq(UID));
verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
verify(mControllerCallbacks, never()).onComplete(eq(2), eq(vibrationId));
verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
- assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating());
+ assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
assertEquals(Arrays.asList(expectedPrebaked(VibrationEffect.EFFECT_TICK)),
mVibratorProviders.get(VIBRATOR_ID).getEffectSegments());
@@ -690,15 +689,15 @@ public class VibrationThreadTest {
VibrationThread thread = startThreadAndDispatcher(vibrationId, effect);
waitForCompletion(thread);
- verify(mIBatteryStatsMock).noteVibratorOn(eq(UID), eq(20L));
- verify(mIBatteryStatsMock).noteVibratorOff(eq(UID));
+ verify(mManagerHooks).noteVibratorOn(eq(UID), eq(20L));
+ verify(mManagerHooks).noteVibratorOff(eq(UID));
verify(mControllerCallbacks).onComplete(eq(1), eq(vibrationId));
verify(mControllerCallbacks).onComplete(eq(2), eq(vibrationId));
verify(mControllerCallbacks).onComplete(eq(3), eq(vibrationId));
verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
- assertFalse(thread.getVibrators().get(1).isVibrating());
- assertFalse(thread.getVibrators().get(2).isVibrating());
- assertFalse(thread.getVibrators().get(3).isVibrating());
+ assertFalse(mControllers.get(1).isVibrating());
+ assertFalse(mControllers.get(2).isVibrating());
+ assertFalse(mControllers.get(3).isVibrating());
VibrationEffectSegment expected = expectedPrebaked(VibrationEffect.EFFECT_CLICK);
assertEquals(Arrays.asList(expected), mVibratorProviders.get(1).getEffectSegments());
@@ -728,17 +727,17 @@ public class VibrationThreadTest {
VibrationThread thread = startThreadAndDispatcher(vibrationId, effect);
waitForCompletion(thread);
- verify(mIBatteryStatsMock).noteVibratorOn(eq(UID), eq(20L));
- verify(mIBatteryStatsMock).noteVibratorOff(eq(UID));
+ verify(mManagerHooks).noteVibratorOn(eq(UID), eq(20L));
+ verify(mManagerHooks).noteVibratorOff(eq(UID));
verify(mControllerCallbacks).onComplete(eq(1), eq(vibrationId));
verify(mControllerCallbacks).onComplete(eq(2), eq(vibrationId));
verify(mControllerCallbacks).onComplete(eq(3), eq(vibrationId));
verify(mControllerCallbacks).onComplete(eq(4), eq(vibrationId));
verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
- assertFalse(thread.getVibrators().get(1).isVibrating());
- assertFalse(thread.getVibrators().get(2).isVibrating());
- assertFalse(thread.getVibrators().get(3).isVibrating());
- assertFalse(thread.getVibrators().get(4).isVibrating());
+ assertFalse(mControllers.get(1).isVibrating());
+ assertFalse(mControllers.get(2).isVibrating());
+ assertFalse(mControllers.get(3).isVibrating());
+ assertFalse(mControllers.get(4).isVibrating());
assertEquals(Arrays.asList(expectedPrebaked(VibrationEffect.EFFECT_CLICK)),
mVibratorProviders.get(1).getEffectSegments());
@@ -777,18 +776,18 @@ public class VibrationThreadTest {
controllerVerifier.verify(mControllerCallbacks).onComplete(eq(1), eq(vibrationId));
controllerVerifier.verify(mControllerCallbacks).onComplete(eq(2), eq(vibrationId));
- InOrder batterVerifier = inOrder(mIBatteryStatsMock);
- batterVerifier.verify(mIBatteryStatsMock).noteVibratorOn(eq(UID), eq(20L));
- batterVerifier.verify(mIBatteryStatsMock).noteVibratorOff(eq(UID));
- batterVerifier.verify(mIBatteryStatsMock).noteVibratorOn(eq(UID), eq(10L));
- batterVerifier.verify(mIBatteryStatsMock).noteVibratorOff(eq(UID));
- batterVerifier.verify(mIBatteryStatsMock).noteVibratorOn(eq(UID), eq(20L));
- batterVerifier.verify(mIBatteryStatsMock).noteVibratorOff(eq(UID));
+ InOrder batteryVerifier = inOrder(mManagerHooks);
+ batteryVerifier.verify(mManagerHooks).noteVibratorOn(eq(UID), eq(20L));
+ batteryVerifier.verify(mManagerHooks).noteVibratorOff(eq(UID));
+ batteryVerifier.verify(mManagerHooks).noteVibratorOn(eq(UID), eq(10L));
+ batteryVerifier.verify(mManagerHooks).noteVibratorOff(eq(UID));
+ batteryVerifier.verify(mManagerHooks).noteVibratorOn(eq(UID), eq(20L));
+ batteryVerifier.verify(mManagerHooks).noteVibratorOff(eq(UID));
verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
- assertFalse(thread.getVibrators().get(1).isVibrating());
- assertFalse(thread.getVibrators().get(2).isVibrating());
- assertFalse(thread.getVibrators().get(3).isVibrating());
+ assertFalse(mControllers.get(1).isVibrating());
+ assertFalse(mControllers.get(2).isVibrating());
+ assertFalse(mControllers.get(3).isVibrating());
assertEquals(Arrays.asList(expectedOneShot(10)),
mVibratorProviders.get(1).getEffectSegments());
@@ -945,22 +944,22 @@ public class VibrationThreadTest {
// All vibrators are turned on in parallel.
assertTrue(waitUntil(
- t -> t.getVibrators().get(1).isVibrating()
- && t.getVibrators().get(2).isVibrating()
- && t.getVibrators().get(3).isVibrating(),
+ t -> mControllers.get(1).isVibrating()
+ && mControllers.get(2).isVibrating()
+ && mControllers.get(3).isVibrating(),
thread, TEST_TIMEOUT_MILLIS));
waitForCompletion(thread);
- verify(mIBatteryStatsMock).noteVibratorOn(eq(UID), eq(80L));
- verify(mIBatteryStatsMock).noteVibratorOff(eq(UID));
+ verify(mManagerHooks).noteVibratorOn(eq(UID), eq(80L));
+ verify(mManagerHooks).noteVibratorOff(eq(UID));
verify(mControllerCallbacks).onComplete(eq(1), eq(vibrationId));
verify(mControllerCallbacks).onComplete(eq(2), eq(vibrationId));
verify(mControllerCallbacks).onComplete(eq(3), eq(vibrationId));
verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
- assertFalse(thread.getVibrators().get(1).isVibrating());
- assertFalse(thread.getVibrators().get(2).isVibrating());
- assertFalse(thread.getVibrators().get(3).isVibrating());
+ assertFalse(mControllers.get(1).isVibrating());
+ assertFalse(mControllers.get(2).isVibrating());
+ assertFalse(mControllers.get(3).isVibrating());
assertEquals(Arrays.asList(expectedOneShot(25)),
mVibratorProviders.get(1).getEffectSegments());
@@ -1035,7 +1034,7 @@ public class VibrationThreadTest {
// After the vibrator call ends the vibration is cancelled and the vibrator is turned off.
waitForCompletion(vibrationThread, /* timeout= */ latency + TEST_TIMEOUT_MILLIS);
verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED);
- assertFalse(vibrationThread.getVibrators().get(VIBRATOR_ID).isVibrating());
+ assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
}
@Test
@@ -1055,7 +1054,7 @@ public class VibrationThreadTest {
.combine();
VibrationThread vibrationThread = startThreadAndDispatcher(vibrationId, effect);
- assertTrue(waitUntil(t -> t.getVibrators().get(2).isVibrating(), vibrationThread,
+ assertTrue(waitUntil(t -> mControllers.get(2).isVibrating(), vibrationThread,
TEST_TIMEOUT_MILLIS));
assertTrue(vibrationThread.isAlive());
@@ -1068,8 +1067,8 @@ public class VibrationThreadTest {
waitForCompletion(cancellingThread);
verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED);
- assertFalse(vibrationThread.getVibrators().get(1).isVibrating());
- assertFalse(vibrationThread.getVibrators().get(2).isVibrating());
+ assertFalse(mControllers.get(1).isVibrating());
+ assertFalse(mControllers.get(2).isVibrating());
}
@Test
@@ -1086,8 +1085,8 @@ public class VibrationThreadTest {
.combine();
VibrationThread vibrationThread = startThreadAndDispatcher(vibrationId, effect);
- assertTrue(waitUntil(t -> t.getVibrators().get(1).isVibrating()
- && t.getVibrators().get(2).isVibrating(),
+ assertTrue(waitUntil(t -> mControllers.get(1).isVibrating()
+ && mControllers.get(2).isVibrating(),
vibrationThread, TEST_TIMEOUT_MILLIS));
assertTrue(vibrationThread.isAlive());
@@ -1100,8 +1099,8 @@ public class VibrationThreadTest {
waitForCompletion(cancellingThread);
verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED);
- assertFalse(vibrationThread.getVibrators().get(1).isVibrating());
- assertFalse(vibrationThread.getVibrators().get(2).isVibrating());
+ assertFalse(mControllers.get(1).isVibrating());
+ assertFalse(mControllers.get(2).isVibrating());
}
@Test
@@ -1110,7 +1109,7 @@ public class VibrationThreadTest {
VibrationEffect effect = VibrationEffect.createWaveform(new long[]{5}, new int[]{100}, 0);
VibrationThread thread = startThreadAndDispatcher(vibrationId, effect);
- assertTrue(waitUntil(t -> t.getVibrators().get(VIBRATOR_ID).isVibrating(), thread,
+ assertTrue(waitUntil(t -> mControllers.get(VIBRATOR_ID).isVibrating(), thread,
TEST_TIMEOUT_MILLIS));
assertTrue(thread.isAlive());
@@ -1121,7 +1120,7 @@ public class VibrationThreadTest {
verify(mVibrationToken).unlinkToDeath(same(thread), eq(0));
verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED);
assertFalse(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments().isEmpty());
- assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating());
+ assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
}
@Test
@@ -1192,7 +1191,7 @@ public class VibrationThreadTest {
long vibrationId = 1;
VibrationEffect effect = VibrationEffect.createOneShot(10_000, 240);
VibrationThread thread = startThreadAndDispatcher(vibrationId, effect);
- assertTrue(waitUntil(t -> t.getVibrators().get(VIBRATOR_ID).isVibrating(), thread,
+ assertTrue(waitUntil(t -> mControllers.get(VIBRATOR_ID).isVibrating(), thread,
TEST_TIMEOUT_MILLIS));
thread.cancel();
waitForCompletion(thread);
@@ -1299,8 +1298,9 @@ public class VibrationThreadTest {
}
private VibrationThread startThreadAndDispatcher(Vibration vib) {
+ mControllers = createVibratorControllers();
VibrationThread thread = new VibrationThread(vib, mVibrationSettings, mEffectAdapter,
- createVibratorControllers(), mWakeLock, mIBatteryStatsMock, mManagerHooks);
+ mControllers, mWakeLock, mManagerHooks);
doAnswer(answer -> {
thread.vibratorComplete(answer.getArgument(0));
return null;
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java
index 52975ef8bfe1..19111e5d16e9 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java
@@ -71,6 +71,7 @@ import android.os.VibratorInfo;
import android.os.test.TestLooper;
import android.os.vibrator.PrebakedSegment;
import android.os.vibrator.PrimitiveSegment;
+import android.os.vibrator.VibrationConfig;
import android.os.vibrator.VibrationEffectSegment;
import android.platform.test.annotations.Presubmit;
import android.provider.Settings;
@@ -78,6 +79,7 @@ import android.view.InputDevice;
import androidx.test.InstrumentationRegistry;
+import com.android.internal.app.IBatteryStats;
import com.android.internal.util.test.FakeSettingsProvider;
import com.android.internal.util.test.FakeSettingsProviderRule;
import com.android.server.LocalServices;
@@ -144,6 +146,8 @@ public class VibratorManagerServiceTest {
private AppOpsManager mAppOpsManagerMock;
@Mock
private IInputManager mIInputManagerMock;
+ @Mock
+ private IBatteryStats mBatteryStatsMock;
private final Map<Integer, FakeVibratorControllerProvider> mVibratorProviders = new HashMap<>();
@@ -152,12 +156,14 @@ public class VibratorManagerServiceTest {
private FakeVibrator mVibrator;
private PowerManagerInternal.LowPowerModeListener mRegisteredPowerModeListener;
private VibratorManagerService.ExternalVibratorService mExternalVibratorService;
+ private VibrationConfig mVibrationConfig;
@Before
public void setUp() throws Exception {
mTestLooper = new TestLooper();
mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getContext()));
InputManager inputManager = InputManager.resetInstance(mIInputManagerMock);
+ mVibrationConfig = new VibrationConfig(mContextSpy.getResources());
ContentResolver contentResolver = mSettingsProviderRule.mockContentResolver(mContextSpy);
when(mContextSpy.getContentResolver()).thenReturn(contentResolver);
@@ -222,6 +228,11 @@ public class VibratorManagerServiceTest {
}
@Override
+ IBatteryStats getBatteryStatsService() {
+ return mBatteryStatsMock;
+ }
+
+ @Override
VibratorController createVibratorController(int vibratorId,
VibratorController.OnVibrationCompleteListener listener) {
return mVibratorProviders.get(vibratorId)
@@ -382,6 +393,11 @@ public class VibratorManagerServiceTest {
inOrderVerifier.verify(listenerMock).onVibrating(eq(true));
inOrderVerifier.verify(listenerMock).onVibrating(eq(false));
inOrderVerifier.verifyNoMoreInteractions();
+
+ InOrder batteryVerifier = inOrder(mBatteryStatsMock);
+ batteryVerifier.verify(mBatteryStatsMock)
+ .noteVibratorOn(UID, 40 + mVibrationConfig.getRampDownDurationMs());
+ batteryVerifier.verify(mBatteryStatsMock).noteVibratorOff(UID);
}
@Test
@@ -731,6 +747,12 @@ public class VibratorManagerServiceTest {
// Wait before checking it never played a second effect.
assertFalse(waitUntil(s -> mVibratorProviders.get(1).getEffectSegments().size() > 1,
service, /* timeout= */ 50));
+
+ // The time estimate is recorded when the vibration starts, repeating vibrations
+ // are capped at BATTERY_STATS_REPEATING_VIBRATION_DURATION (=5000).
+ verify(mBatteryStatsMock).noteVibratorOn(UID, 5000);
+ // The second vibration shouldn't have recorded that the vibrators were turned on.
+ verify(mBatteryStatsMock, times(1)).noteVibratorOn(anyInt(), anyLong());
}
@Test
@@ -765,6 +787,9 @@ public class VibratorManagerServiceTest {
when(mIInputManagerMock.getVibratorIds(eq(1))).thenReturn(new int[]{1});
when(mIInputManagerMock.getInputDevice(eq(1))).thenReturn(createInputDeviceWithVibrator(1));
setUserSetting(Settings.System.VIBRATE_INPUT_DEVICES, 1);
+ // Mock alarm intensity equals to default value to avoid scaling in this test.
+ setUserSetting(Settings.System.ALARM_VIBRATION_INTENSITY,
+ mVibrator.getDefaultVibrationIntensity(VibrationAttributes.USAGE_ALARM));
VibratorManagerService service = createSystemReadyService();
CombinedVibration effect = CombinedVibration.createParallel(
@@ -804,6 +829,9 @@ public class VibratorManagerServiceTest {
mockVibrators(1, 2);
mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
mVibratorProviders.get(2).setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
+ // Mock alarm intensity equals to default value to avoid scaling in this test.
+ setUserSetting(Settings.System.ALARM_VIBRATION_INTENSITY,
+ mVibrator.getDefaultVibrationIntensity(VibrationAttributes.USAGE_ALARM));
VibratorManagerService service = createSystemReadyService();
// The native callback will be dispatched manually in this test.
mTestLooper.stopAutoDispatchAndIgnoreExceptions();
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowOrientationListenerTest.java b/services/tests/servicestests/src/com/android/server/wm/WindowOrientationListenerTest.java
index 2794d4837101..c64ff9e128e6 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowOrientationListenerTest.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowOrientationListenerTest.java
@@ -114,7 +114,7 @@ public class WindowOrientationListenerTest {
}
@Test
- public void testSensorChanged_normalCase2() {
+ public void testOnSensorChanged_normalCase2() {
mWindowOrientationListener.mOrientationJudge.onSensorChanged(mFakeSensorEvent);
mFakeRotationResolverInternal.callbackWithFailureResult(
@@ -123,6 +123,15 @@ public class WindowOrientationListenerTest {
assertThat(mFinalizedRotation).isEqualTo(DEFAULT_SENSOR_ROTATION);
}
+ @Test
+ public void testOnSensorChanged_rotationResolverServiceIsNull_useSensorResult() {
+ mWindowOrientationListener.mRotationResolverService = null;
+
+ mWindowOrientationListener.mOrientationJudge.onSensorChanged(mFakeSensorEvent);
+
+ assertThat(mFinalizedRotation).isEqualTo(DEFAULT_SENSOR_ROTATION);
+ }
+
static final class TestableRotationResolver extends RotationResolverInternal {
@Surface.Rotation
RotationResolverCallbackInternal mCallback;
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
index 0d6794685f09..72521fd06245 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
@@ -120,7 +120,7 @@ public class AppTransitionControllerTest extends WindowTestsBase {
behind.setState(ActivityRecord.State.STARTED, "test");
behind.mVisibleRequested = true;
- task.performClearTask("test");
+ task.removeActivities("test", false /* excludingTaskOverlay */);
assertFalse(mDisplayContent.mAppTransition.isReady());
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java
index cc1869e72b34..9fc9489e3c2e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java
@@ -557,7 +557,7 @@ public class LockTaskControllerTest {
mLockTaskController.updateLockTaskPackages(TEST_USER_ID, allowlist);
// THEN the task running that package should be stopped
- verify(tr2).performClearTaskLocked();
+ verify(tr2).performClearTaskForReuse(false /* excludingTaskOverlay*/);
assertFalse(mLockTaskController.isTaskLocked(tr2));
// THEN the other task should remain locked
assertEquals(LOCK_TASK_MODE_LOCKED, mLockTaskController.getLockTaskModeState());
@@ -569,7 +569,7 @@ public class LockTaskControllerTest {
mLockTaskController.updateLockTaskPackages(TEST_USER_ID, allowlist);
// THEN the last task should be cleared, and the system should quit LockTask mode
- verify(tr1).performClearTaskLocked();
+ verify(tr1).performClearTaskForReuse(false /* excludingTaskOverlay*/);
assertFalse(mLockTaskController.isTaskLocked(tr1));
assertEquals(LOCK_TASK_MODE_NONE, mLockTaskController.getLockTaskModeState());
verifyLockTaskStopped(times(1));
diff --git a/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java b/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
index 0f223ca037ee..eea3f844b40f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
@@ -293,7 +293,8 @@ public class ZOrderingTests extends WindowTestsBase {
public void testAssignWindowLayers_ForImeWithAppTargetAndAppAbove() {
final WindowState appBelowImeTarget = createWindow("appBelowImeTarget");
final WindowState imeAppTarget = createWindow("imeAppTarget");
- final WindowState appAboveImeTarget = createWindow("appAboveImeTarget");
+ final WindowState appAboveImeTarget = createWindow(imeAppTarget, TYPE_APPLICATION,
+ "appAboveImeTarget");
mDisplayContent.setImeLayeringTarget(imeAppTarget);
mDisplayContent.setImeControlTarget(imeAppTarget);
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index f57c32c959d9..0394a546388d 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -4189,7 +4189,8 @@ public class TelephonyManager {
* {@link UiccSlotMapping} which consist of both physical slot index and port index.
* Logical slot is the slot that is seen by modem. Physical slot is the actual physical slot.
* Port index is the index (enumerated value) for the associated port available on the SIM.
- * Each physical slot can have multiple ports if multi-enabled profile(MEP) is supported.
+ * Each physical slot can have multiple ports if
+ * {@link PackageManager#FEATURE_TELEPHONY_EUICC_MEP} is supported.
*
* Example: no. of logical slots 1 and physical slots 2 do not support MEP, each physical slot
* has one port:
@@ -4285,11 +4286,11 @@ public class TelephonyManager {
/**
* Get the mapping from logical slots to physical sim slots and port indexes. Initially the
* logical slot index was mapped to physical slot index, but with support for multi-enabled
- * profile(MEP) logical slot is now mapped to port index.
+ * profile(MEP){@link PackageManager#FEATURE_TELEPHONY_EUICC_MEP},logical slot is now mapped to
+ * port index.
*
* @return a collection of {@link UiccSlotMapping} which indicates the mapping from logical
* slots to ports and physical slots.
- *
* @hide
*/
@SystemApi
diff --git a/telephony/java/android/telephony/UiccCardInfo.java b/telephony/java/android/telephony/UiccCardInfo.java
index 30ca1627953f..3843a6240b43 100644
--- a/telephony/java/android/telephony/UiccCardInfo.java
+++ b/telephony/java/android/telephony/UiccCardInfo.java
@@ -17,6 +17,7 @@ package android.telephony;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.pm.PackageManager;
import android.os.Parcel;
import android.os.Parcelable;
@@ -147,9 +148,10 @@ public final class UiccCardInfo implements Parcelable {
* Note that this field may be omitted if the caller does not have the correct permissions
* (see {@link TelephonyManager#getUiccCardsInfo()}).
*
- * @deprecated with support for MEP(multiple enabled profile), a SIM card can have more than one
- * ICCID active at the same time.Instead use {@link UiccPortInfo#getIccId()} to retrieve ICCID.
- * To find {@link UiccPortInfo} use {@link UiccCardInfo#getPorts()}
+ * @deprecated with support for MEP(multiple enabled profile)
+ * {@link PackageManager#FEATURE_TELEPHONY_EUICC_MEP}, a SIM card can have more than one
+ * ICCID active at the same time. Instead use {@link UiccPortInfo#getIccId()} to retrieve ICCID.
+ * To find {@link UiccPortInfo} use {@link UiccCardInfo#getPorts()}.
*
* @throws UnsupportedOperationException if the calling app's target SDK is T and beyond.
*/
@@ -192,11 +194,11 @@ public final class UiccCardInfo implements Parcelable {
}
/*
- * Whether the UICC card supports multiple enable profile(MEP)
+ * Whether the UICC card supports multiple enabled profile(MEP)
* UICCs are generally MEP disabled, there can be only one active profile on the physical
* sim card.
*
- * @return {@code true} if the eUICC is supporting multiple enabled profile(MEP).
+ * @return {@code true} if the UICC is supporting multiple enabled profile(MEP).
*/
public boolean isMultipleEnabledProfilesSupported() {
return mIsMultipleEnabledProfilesSupported;
@@ -205,6 +207,9 @@ public final class UiccCardInfo implements Parcelable {
/**
* Get information regarding port, ICCID and its active status.
*
+ * For device which support {@link PackageManager#FEATURE_TELEPHONY_EUICC_MEP}, it should return
+ * more than one {@link UiccPortInfo} object if the card is eUICC.
+ *
* @return Collection of {@link UiccPortInfo}
*/
public @NonNull Collection<UiccPortInfo> getPorts() {
diff --git a/telephony/java/android/telephony/UiccPortInfo.java b/telephony/java/android/telephony/UiccPortInfo.java
index d1838c0b91f4..6fb0470d6225 100644
--- a/telephony/java/android/telephony/UiccPortInfo.java
+++ b/telephony/java/android/telephony/UiccPortInfo.java
@@ -29,7 +29,9 @@ import java.util.Objects;
* Per GSMA SGP.22 V3.0, a port is a logical entity to which an active UICC profile can be bound on
* a UICC card. If UICC supports 2 ports, then the port index is numbered 0,1.
* Each port index is unique within an UICC, but not necessarily unique across UICC’s.
- * For UICC's does not support MEP(Multi-enabled profile), just return the default port index 0.
+ * For UICC's does not support MEP(Multi-enabled profile)
+ * {@link android.content.pm.PackageManager#FEATURE_TELEPHONY_EUICC_MEP}, just return the default
+ * port index 0.
*/
public final class UiccPortInfo implements Parcelable{
private final String mIccId;
diff --git a/telephony/java/android/telephony/UiccSlotInfo.java b/telephony/java/android/telephony/UiccSlotInfo.java
index 17f34db4e44a..17ce45063d41 100644
--- a/telephony/java/android/telephony/UiccSlotInfo.java
+++ b/telephony/java/android/telephony/UiccSlotInfo.java
@@ -19,6 +19,7 @@ import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
+import android.content.pm.PackageManager;
import android.os.Parcel;
import android.os.Parcelable;
@@ -225,6 +226,9 @@ public class UiccSlotInfo implements Parcelable {
/**
* Get Information regarding port, iccid and its active status.
*
+ * For device which support {@link PackageManager#FEATURE_TELEPHONY_EUICC_MEP}, it should return
+ * more than one {@link UiccPortInfo} object if the card is eUICC.
+ *
* @return Collection of {@link UiccPortInfo}
*/
public @NonNull Collection<UiccPortInfo> getPorts() {
diff --git a/telephony/java/android/telephony/euicc/EuiccManager.java b/telephony/java/android/telephony/euicc/EuiccManager.java
index b6ae53017f64..4820d332de0f 100644
--- a/telephony/java/android/telephony/euicc/EuiccManager.java
+++ b/telephony/java/android/telephony/euicc/EuiccManager.java
@@ -37,6 +37,7 @@ import android.os.RemoteException;
import android.telephony.SubscriptionInfo;
import android.telephony.TelephonyFrameworkInitializer;
import android.telephony.TelephonyManager;
+import android.telephony.UiccCardInfo;
import android.telephony.euicc.EuiccCardManager.ResetOption;
import android.util.Log;
@@ -931,6 +932,21 @@ public class EuiccManager {
* intent to prompt the user to accept the download. The caller should also be authorized to
* manage the subscription to be downloaded.
*
+ * <p>If device support {@link PackageManager#FEATURE_TELEPHONY_EUICC_MEP} and
+ * switchAfterDownload is {@code true}, the subscription will be enabled on an esim port based
+ * on the following selection rules:
+ * <ul>
+ * <li>In SS(Single SIM) mode, if the embedded slot already has an active port, then download
+ * and enable the subscription on this port.
+ * <li>In SS mode, if the embedded slot is not active, then try to download and enable the
+ * subscription on the default port 0 of eUICC.
+ * <li>In DSDS mode, find first available port to download and enable the subscription.
+ * (see {@link #isSimPortAvailable(int)})
+ *</ul>
+ * If there is no available port, an {@link #EMBEDDED_SUBSCRIPTION_RESULT_RESOLVABLE_ERROR}
+ * will be returned in the callback intent to prompt the user to disable an already-active
+ * subscription.
+ *
* @param subscription the subscription to download.
* @param switchAfterDownload if true, the profile will be activated upon successful download.
* @param callbackIntent a PendingIntent to launch when the operation completes.
@@ -1141,14 +1157,25 @@ public class EuiccManager {
* intent to prompt the user to accept the download. The caller should also be authorized to
* manage the subscription to be enabled.
*
- * <p> From Android T, devices might support MEP(Multiple Enabled Profiles), the subscription
- * can be installed on different port from the eUICC. Calling apps with carrier privilege
- * (see {@link TelephonyManager#hasCarrierPrivileges}) over the currently active subscriptions
- * can use {@link #switchToSubscription(int, int, PendingIntent)} to specify which port to
- * enable the subscription. Otherwise, use this API to enable the subscription on the eUICC
- * and the platform will internally resolve a port. If there is no available port,
- * an {@link #EMBEDDED_SUBSCRIPTION_RESULT_RESOLVABLE_ERROR} might be returned in the callback
- * intent to prompt the user to disable an already-active subscription.
+ * <p> From Android T, devices might support {@link PackageManager#FEATURE_TELEPHONY_EUICC_MEP},
+ * the subscription can be installed on different port from the eUICC. Calling apps with
+ * carrier privilege (see {@link TelephonyManager#hasCarrierPrivileges}) over the currently
+ * active subscriptions can use {@link #switchToSubscription(int, int, PendingIntent)} to
+ * specify which port to enable the subscription. Otherwise, use this API to enable the
+ * subscription on the eUICC and the platform will internally resolve a port based on following
+ * rules:
+ * <ul>
+ * <li>always use the default port 0 is eUICC does not support MEP.
+ * <li>In SS(Single SIM) mode, if the embedded slot already has an active port, then enable
+ * the subscription on this port.
+ * <li>In SS mode, if the embedded slot is not active, then try to enable the subscription on
+ * the default port 0 of eUICC.
+ * <li>In DSDS mode, find first available port to enable the subscription.
+ * (see {@link #isSimPortAvailable(int)})
+ *</ul>
+ * If there is no available port, an {@link #EMBEDDED_SUBSCRIPTION_RESULT_RESOLVABLE_ERROR}
+ * will be returned in the callback intent to prompt the user to disable an already-active
+ * subscription.
*
* @param subscriptionId the ID of the subscription to enable. May be
* {@link android.telephony.SubscriptionManager#INVALID_SUBSCRIPTION_ID} to deactivate the
@@ -1197,7 +1224,15 @@ public class EuiccManager {
*
* <p> If the caller is passing invalid port index,
* an {@link #EMBEDDED_SUBSCRIPTION_RESULT_ERROR} with detailed error code
- * {@link #ERROR_INVALID_PORT} will be returned.
+ * {@link #ERROR_INVALID_PORT} will be returned. The port index is invalid if one of the
+ * following requirements is met:
+ * <ul>
+ * <li>index is beyond the range of {@link UiccCardInfo#getPorts()}.
+ * <li>In SS(Single SIM) mode, the embedded slot already has an active port with different
+ * port index.
+ * <li>In DSDS mode, if the psim slot is active and the embedded slot already has an active
+ * empty port with different port index.
+ * </ul>
*
* <p> Depending on the target port and permission check,
* an {@link #EMBEDDED_SUBSCRIPTION_RESULT_RESOLVABLE_ERROR} might be returned to the callback
@@ -1522,8 +1557,8 @@ public class EuiccManager {
/**
* Returns whether the passing portIndex is available.
- * A port is available if it has no profiles enabled on it or calling app has carrier privilege
- * over the profile installed on the selected port.
+ * A port is available if it is active without enabled profile on it or
+ * calling app has carrier privilege over the profile installed on the selected port.
* Always returns false if the cardId is a physical card.
*
* @param portIndex is an enumeration of the ports available on the UICC.
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt
index 8d60466eff95..4cddd8506df7 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt
@@ -60,7 +60,7 @@ abstract class CloseAppTransition(protected val testSpec: FlickerTestParameter)
}
teardown {
test {
- testApp.exit()
+ testApp.exit(wmHelper)
}
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt
index 7ee6451b2797..5bd365c7eefd 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt
@@ -64,7 +64,6 @@ open class ImeAppHelper @JvmOverloads constructor(
device.waitForIdle()
} else {
wmHelper.waitImeShown()
- wmHelper.waitForAppTransitionIdle()
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/TwoActivitiesAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/TwoActivitiesAppHelper.kt
index b66c45c7c9f0..a135e0af067b 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/TwoActivitiesAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/TwoActivitiesAppHelper.kt
@@ -53,8 +53,8 @@ class TwoActivitiesAppHelper @JvmOverloads constructor(
button.click()
device.wait(Until.gone(launchActivityButton), FIND_TIMEOUT)
- wmHelper.waitForFullScreenApp(secondActivityComponent)
wmHelper.waitFor(
+ WindowManagerStateHelper.isAppFullScreen(secondActivityComponent),
WindowManagerConditionsFactory.isAppTransitionIdle(Display.DEFAULT_DISPLAY),
WindowManagerConditionsFactory.hasLayersAnimating().negate()
)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt
index ba5698cafa15..a9564fdd2332 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt
@@ -88,7 +88,7 @@ class ReOpenImeWindowTest(private val testSpec: FlickerTestParameter) {
}
transitions {
device.reopenAppFromOverview(wmHelper)
- require(wmHelper.waitImeShown()) { "IME didn't show in time" }
+ wmHelper.waitImeShown()
}
teardown {
test {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/SwitchImeWindowsFromGestureNavTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/SwitchImeWindowsFromGestureNavTest.kt
index 19e2c92304d4..7e3ed8252f4c 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/SwitchImeWindowsFromGestureNavTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/SwitchImeWindowsFromGestureNavTest.kt
@@ -18,6 +18,7 @@ package com.android.server.wm.flicker.ime
import android.app.Instrumentation
import android.platform.test.annotations.Presubmit
+import android.view.Display
import android.view.Surface
import android.view.WindowManagerPolicyConstants
import androidx.test.filters.RequiresDevice
@@ -35,6 +36,8 @@ import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.navBarWindowIsVisible
import com.android.server.wm.flicker.statusBarWindowIsVisible
import com.android.server.wm.traces.common.FlickerComponentName
+import com.android.server.wm.traces.common.WindowManagerConditionsFactory
+import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import org.junit.FixMethodOrder
import org.junit.Test
@@ -64,12 +67,22 @@ class SwitchImeWindowsFromGestureNavTest(private val testSpec: FlickerTestParame
eachRun {
this.setRotation(testSpec.startRotation)
testApp.launchViaIntent(wmHelper)
- wmHelper.waitForFullScreenApp(testApp.component)
- wmHelper.waitForAppTransitionIdle()
+ val testAppVisible = wmHelper.waitFor(
+ WindowManagerStateHelper.isAppFullScreen(testApp.component),
+ WindowManagerConditionsFactory.isAppTransitionIdle(
+ Display.DEFAULT_DISPLAY))
+ require(testAppVisible) {
+ "Expected ${testApp.component.toWindowName()} to be visible"
+ }
imeTestApp.launchViaIntent(wmHelper)
- wmHelper.waitForFullScreenApp(testApp.component)
- wmHelper.waitForAppTransitionIdle()
+ val imeAppVisible = wmHelper.waitFor(
+ WindowManagerStateHelper.isAppFullScreen(imeTestApp.component),
+ WindowManagerConditionsFactory.isAppTransitionIdle(
+ Display.DEFAULT_DISPLAY))
+ require(imeAppVisible) {
+ "Expected ${imeTestApp.component.toWindowName()} to be visible"
+ }
imeTestApp.openIME(device, wmHelper)
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt
index b5e13be7dca0..cc808a0ce871 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt
@@ -18,6 +18,7 @@ package com.android.server.wm.flicker.launch
import android.app.Instrumentation
import android.platform.test.annotations.Presubmit
+import android.view.Display
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
import com.android.server.wm.flicker.entireScreenCovered
@@ -30,7 +31,9 @@ import com.android.server.wm.flicker.annotation.Group4
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.TwoActivitiesAppHelper
import com.android.server.wm.flicker.testapp.ActivityOptions
+import com.android.server.wm.traces.common.WindowManagerConditionsFactory
import com.android.server.wm.traces.parser.toFlickerComponent
+import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -77,14 +80,16 @@ class ActivitiesTransitionTest(val testSpec: FlickerTestParameter) {
}
teardown {
test {
- testApp.exit()
+ testApp.exit(wmHelper)
}
}
transitions {
testApp.openSecondActivity(device, wmHelper)
device.pressBack()
- wmHelper.waitForAppTransitionIdle()
- wmHelper.waitForFullScreenApp(testApp.component)
+ val firstActivityVisible = wmHelper.waitFor(
+ WindowManagerConditionsFactory.isAppTransitionIdle(Display.DEFAULT_DISPLAY),
+ WindowManagerStateHelper.isAppFullScreen(testApp.component))
+ require(firstActivityVisible) { "Expected ${testApp.component} to be visible" }
}
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt
index 53560cc14c44..4313b8dbc883 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt
@@ -56,7 +56,7 @@ abstract class OpenAppTransition(protected val testSpec: FlickerTestParameter) {
}
teardown {
test {
- testApp.exit()
+ testApp.exit(wmHelper)
}
}
}
diff --git a/tests/InputMethodStressTest/AndroidTest.xml b/tests/InputMethodStressTest/AndroidTest.xml
index b194010a985a..fc54ca645a2e 100644
--- a/tests/InputMethodStressTest/AndroidTest.xml
+++ b/tests/InputMethodStressTest/AndroidTest.xml
@@ -18,6 +18,11 @@
<target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer" />
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <option name="run-command" value="adb shell settings put secure show_ime_with_hard_keyboard 1" />
+ <option name="teardown-command" value="adb shell settings delete secure show_ime_with_hard_keyboard" />
+ </target_preparer>
+
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
<option name="test-file-name" value="InputMethodStressTest.apk" />
diff --git a/tools/bit/command.cpp b/tools/bit/command.cpp
index f95ea117a96e..6c68e0b0ff6b 100644
--- a/tools/bit/command.cpp
+++ b/tools/bit/command.cpp
@@ -192,10 +192,11 @@ exec_with_path_search(const char* prog, char const* const* argv, char const* con
if (strchr(prog, '/') != NULL) {
return execve(prog, (char*const*)argv, (char*const*)envp);
} else {
- char* pathEnv = strdup(getenv("PATH"));
- if (pathEnv == NULL) {
+ const char* pathEnvRaw = getenv("PATH");
+ if (pathEnvRaw == NULL) {
return 1;
}
+ char* pathEnv = strdup(pathEnvRaw);
char* dir = pathEnv;
while (dir) {
char* next = strchr(dir, ':');