summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--AconfigFlags.bp15
-rw-r--r--Android.bp154
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/JobStore.java15
-rw-r--r--api/Android.bp1
-rw-r--r--api/StubLibraries.bp2
-rw-r--r--api/api.go7
-rw-r--r--cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java4
-rw-r--r--core/api/current.txt38
-rw-r--r--core/api/system-current.txt5
-rw-r--r--core/java/android/app/SystemServiceRegistry.java5
-rw-r--r--core/java/android/appwidget/AppWidgetManager.java127
-rw-r--r--core/java/android/database/sqlite/SQLiteDatabase.java152
-rw-r--r--core/java/android/hardware/Camera.java131
-rw-r--r--core/java/android/hardware/camera2/CameraDevice.java8
-rw-r--r--core/java/android/hardware/camera2/params/StreamConfigurationMap.java2
-rw-r--r--core/java/android/os/FileUtils.java24
-rw-r--r--core/java/android/os/storage/IStorageManager.aidl1
-rw-r--r--core/java/android/os/storage/StorageManager.java9
-rw-r--r--core/java/android/provider/Settings.java11
-rw-r--r--core/java/android/security/OWNERS10
-rw-r--r--core/java/android/security/net/OWNERS2
-rw-r--r--core/java/android/util/IntArray.java5
-rw-r--r--core/java/android/view/HandwritingInitiator.java10
-rw-r--r--core/java/android/view/View.java17
-rw-r--r--core/java/android/widget/RemoteViews.java41
-rw-r--r--core/java/android/widget/TextView.java6
-rw-r--r--core/java/android/window/flags/windowing_sdk.aconfig10
-rw-r--r--core/res/AndroidManifest.xml8
-rw-r--r--core/res/res/values/config.xml12
-rw-r--r--core/res/res/values/config_telephony.xml2
-rw-r--r--core/res/res/values/symbols.xml1
-rw-r--r--core/tests/coretests/res/values-id/strings.xml5
-rw-r--r--core/tests/coretests/res/values-in/strings.xml5
-rw-r--r--core/tests/coretests/res/values/strings.xml7
-rw-r--r--core/tests/coretests/src/android/content/res/ResourcesLocaleTest.java21
-rw-r--r--core/tests/coretests/src/android/os/FileUtilsTest.java65
-rw-r--r--core/tests/coretests/src/android/service/TEST_MAPPING18
-rw-r--r--core/tests/coretests/src/android/service/euicc/EuiccProfileInfoTest.java2
-rw-r--r--core/tests/coretests/src/android/service/euicc/OWNERS1
-rw-r--r--core/tests/coretests/src/android/view/stylus/HandwritingInitiatorTest.java4
-rw-r--r--core/tests/coretests/src/android/window/flags/WindowFlagsTest.java45
-rw-r--r--core/tests/utiltests/src/android/util/IntArrayTest.java3
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java4
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java13
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleDebugConfig.java13
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java36
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java31
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java22
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubblePopupDrawable.kt2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipAppOpsListener.kt1
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipMediaController.kt1
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipUtils.kt144
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1Module.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/PipModule.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java1
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipDisplayLayoutState.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java1
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java1
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java1
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUtils.java145
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipActionsProvider.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipNotificationController.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTaskOrganizer.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip2/OWNERS3
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/BaseBubbleScreen.kt7
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblePositionerTest.java54
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java4
-rw-r--r--location/Android.bp41
-rw-r--r--location/api/current.txt1
-rw-r--r--location/api/module-lib-current.txt1
-rw-r--r--location/api/module-lib-removed.txt1
-rw-r--r--location/api/removed.txt1
-rw-r--r--location/api/system-current.txt1
-rw-r--r--location/api/system-removed.txt1
-rw-r--r--location/api/test-current.txt1
-rw-r--r--location/api/test-removed.txt1
-rw-r--r--location/placeholder_java/android/location/Placeholder.java27
-rw-r--r--media/TEST_MAPPING11
-rw-r--r--media/java/android/media/RingtoneManager.java190
-rw-r--r--media/tests/ringtone/Android.bp30
-rw-r--r--media/tests/ringtone/AndroidManifest.xml41
-rw-r--r--media/tests/ringtone/TEST_MAPPING20
-rw-r--r--media/tests/ringtone/res/raw/test_haptic_file.ahv17
-rw-r--r--media/tests/ringtone/res/raw/test_sound_file.mp3bin0 -> 4491 bytes
-rw-r--r--media/tests/ringtone/src/com/android/media/RingtoneManagerTest.java233
-rw-r--r--native/android/performance_hint.cpp2
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java1
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java31
-rw-r--r--packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java1
-rw-r--r--packages/SystemUI/accessibility/accessibilitymenu/res/values-night/styles.xml4
-rw-r--r--packages/SystemUI/accessibility/accessibilitymenu/res/values/styles.xml4
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt56
-rw-r--r--packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt21
-rw-r--r--packages/SystemUI/ktfmt_includes.txt1
-rw-r--r--packages/SystemUI/res/color/qs_footer_power_button_overlay_color.xml22
-rw-r--r--packages/SystemUI/res/drawable/qs_footer_action_circle_color.xml6
-rw-r--r--packages/SystemUI/res/layout-land/auth_credential_password_pin_content_view.xml101
-rw-r--r--packages/SystemUI/res/layout-land/auth_credential_password_view.xml80
-rw-r--r--packages/SystemUI/res/layout-land/auth_credential_pin_view.xml28
-rw-r--r--packages/SystemUI/res/layout/auth_credential_password_pin_content_view.xml104
-rw-r--r--packages/SystemUI/res/layout/auth_credential_password_view.xml84
-rw-r--r--packages/SystemUI/res/layout/auth_credential_pin_view.xml28
-rw-r--r--packages/SystemUI/res/values/ids.xml9
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl15
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java12
-rw-r--r--packages/SystemUI/src/com/android/keyguard/ConnectedDisplayKeyguardPresentation.kt2
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/dagger/UdfpsModule.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/udfps/BoundingBoxOverlapDetector.kt22
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/ui/CredentialPasswordView.kt109
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/ui/IPinPad.kt42
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialPasswordViewBinder.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialViewBinder.kt18
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/PinPadViewBinder.kt64
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java60
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractor.kt24
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/qualifiers/KeyguardRootView.kt24
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSurfaceBehindParamsApplier.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/LockscreenSceneModule.kt41
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt46
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java57
-rw-r--r--packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/gesture/GesturePointerEventDetector.kt46
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/gesture/GesturePointerEventListener.kt504
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerImpl.java34
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifLayoutInflaterFactory.kt55
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewsFactory.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java40
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PrecomputedTextViewFactory.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java11
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/BoundingBoxOverlapDetectorTest.kt4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java42
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractorTest.kt94
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt55
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt1
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shared/rotation/RotationButtonControllerTest.kt14
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerTest.kt92
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifLayoutInflaterFactoryTest.kt123
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt54
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/InflatedSmartRepliesTest.java10
-rw-r--r--services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java4
-rw-r--r--services/backup/java/com/android/server/backup/UserBackupManagerService.java21
-rw-r--r--services/backup/java/com/android/server/backup/utils/BackupManagerMonitorDumpsysUtils.java260
-rw-r--r--services/backup/java/com/android/server/backup/utils/BackupManagerMonitorEventSender.java59
-rw-r--r--services/companion/Android.bp1
-rw-r--r--services/companion/java/com/android/server/companion/virtual/Android.bp2
-rw-r--r--services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java7
-rw-r--r--services/companion/java/com/android/server/companion/virtual/VirtualDeviceLog.java152
-rw-r--r--services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java7
-rw-r--r--services/core/java/com/android/server/BinaryTransparencyService.java4
-rw-r--r--services/core/java/com/android/server/StorageManagerService.java13
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerShellCommand.java50
-rw-r--r--services/core/java/com/android/server/am/ContentProviderHelper.java3
-rw-r--r--services/core/java/com/android/server/am/UserController.java102
-rw-r--r--services/core/java/com/android/server/biometrics/AuthenticationStatsCollector.java26
-rw-r--r--services/core/java/com/android/server/display/AutomaticBrightnessController.java24
-rw-r--r--services/core/java/com/android/server/display/ColorFade.java8
-rw-r--r--services/core/java/com/android/server/display/DisplayDeviceConfig.java103
-rw-r--r--services/core/java/com/android/server/display/DisplayPowerController.java10
-rw-r--r--services/core/java/com/android/server/display/DisplayPowerController2.java9
-rw-r--r--services/core/java/com/android/server/display/mode/DisplayModeDirector.java73
-rw-r--r--services/core/java/com/android/server/display/mode/SkinThermalStatusObserver.java28
-rw-r--r--services/core/java/com/android/server/input/GestureMonitorSpyWindow.java2
-rw-r--r--services/core/java/com/android/server/input/InputManagerService.java11
-rw-r--r--services/core/java/com/android/server/inputmethod/HandwritingEventReceiverSurface.java9
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodManagerService.java17
-rw-r--r--services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java53
-rw-r--r--services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java2
-rw-r--r--services/core/java/com/android/server/media/MediaServerUtils.java10
-rw-r--r--services/core/java/com/android/server/media/MediaSessionService.java12
-rwxr-xr-xservices/core/java/com/android/server/notification/NotificationManagerService.java5
-rw-r--r--services/core/java/com/android/server/notification/PreferencesHelper.java34
-rw-r--r--services/core/java/com/android/server/notification/flags.aconfig9
-rw-r--r--services/core/java/com/android/server/pm/DeletePackageHelper.java9
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java18
-rw-r--r--services/core/java/com/android/server/pm/PackageSetting.java13
-rw-r--r--services/core/java/com/android/server/pm/Settings.java5
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerService.java54
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java40
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java69
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerServiceLoggingDecorator.java56
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerServiceTestingShim.java62
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerServiceTracingDecorator.java37
-rw-r--r--services/core/java/com/android/server/policy/PhoneWindowManager.java135
-rw-r--r--services/core/java/com/android/server/power/hint/HintManagerService.java18
-rw-r--r--services/core/java/com/android/server/security/rkp/RemoteProvisioningService.java19
-rw-r--r--services/core/java/com/android/server/security/rkp/RemoteProvisioningShellCommand.java84
-rw-r--r--services/core/java/com/android/server/tv/TvInputManagerService.java77
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java6
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java22
-rw-r--r--services/core/java/com/android/server/wm/InputManagerCallback.java8
-rw-r--r--services/core/java/com/android/server/wm/RootWindowContainer.java2
-rw-r--r--services/core/java/com/android/server/wm/Task.java12
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java10
-rw-r--r--services/core/xsd/display-device-config/display-device-config.xsd18
-rw-r--r--services/core/xsd/display-device-config/schema/current.txt6
-rw-r--r--services/permission/java/com/android/server/permission/access/AccessPolicy.kt2
-rw-r--r--services/permission/java/com/android/server/permission/access/AccessState.kt23
-rw-r--r--services/permission/java/com/android/server/permission/access/AccessUri.kt11
-rw-r--r--services/permission/java/com/android/server/permission/access/permission/DevicePermissionPersistence.kt169
-rw-r--r--services/permission/java/com/android/server/permission/access/permission/DevicePermissionPolicy.kt276
-rw-r--r--services/permission/java/com/android/server/permission/access/permission/PermissionService.kt196
-rw-r--r--services/tests/PackageManagerServiceTests/appenumeration/src/com/android/server/pm/test/appenumeration/CrossUserPackageVisibilityTests.java8
-rw-r--r--services/tests/RemoteProvisioningServiceTests/src/com/android/server/security/rkp/RemoteProvisioningShellCommandTest.java140
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/AutomaticBrightnessControllerTest.java193
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/ColorFadeTest.java19
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java51
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/DisplayPowerController2Test.java3
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java30
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java195
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/whitebalance/AmbientLuxTest.java11
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/backup/utils/BackupManagerMonitorDumpsysUtilsTest.java84
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/backup/utils/BackupManagerMonitorEventSenderTest.java63
-rw-r--r--services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/power/hint/HintManagerServiceTest.java26
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java164
-rw-r--r--services/tests/wmtests/src/com/android/server/policy/PowerKeyGestureTests.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/policy/StemKeyGestureTests.java58
-rw-r--r--services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java44
-rw-r--r--services/usage/java/com/android/server/usage/StorageStatsService.java27
-rw-r--r--telephony/java/android/telephony/CarrierConfigManager.java6
-rw-r--r--telephony/java/android/telephony/SubscriptionManager.java29
-rw-r--r--telephony/java/android/telephony/satellite/SatelliteManager.java6
-rw-r--r--telephony/java/com/android/internal/telephony/ITelephony.aidl2
-rw-r--r--tests/PlatformCompatGating/src/com/android/compat/testing/DummyApi.java8
-rw-r--r--tests/SilkFX/src/com/android/test/silkfx/hdr/GainmapMetadataEditor.kt60
-rw-r--r--tests/SilkFX/src/com/android/test/silkfx/materials/GlassView.kt6
-rw-r--r--tests/utils/testutils/java/com/android/internal/util/test/LocalServiceKeeperRule.java81
-rw-r--r--tests/utils/testutils/tests/src/com/android/internal/util/test/LocalServiceKeeperRuleTest.java119
252 files changed, 6383 insertions, 2233 deletions
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index 16907b3432ec..b753aab33dcd 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -19,6 +19,7 @@ java_defaults {
// Add java_aconfig_libraries to here to add them to the core framework
srcs: [
":com.android.hardware.camera2-aconfig-java{.generated_srcjars}",
+ ":com.android.window.flags.window-aconfig-java{.generated_srcjars}",
],
}
@@ -30,6 +31,7 @@ java_defaults {
libs: ["fake_device_config"],
}
+// Camera
aconfig_declarations {
name: "com.android.hardware.camera2-aconfig",
package: "com.android.hardware.camera2",
@@ -41,3 +43,16 @@ java_aconfig_library {
aconfig_declarations: "com.android.hardware.camera2-aconfig",
defaults: ["framework-minus-apex-aconfig-java-defaults"],
}
+
+// Window
+aconfig_declarations {
+ name: "com.android.window.flags.window-aconfig",
+ package: "com.android.window.flags",
+ srcs: ["core/java/android/window/flags/*.aconfig"],
+}
+
+java_aconfig_library {
+ name: "com.android.window.flags.window-aconfig-java",
+ aconfig_declarations: "com.android.window.flags.window-aconfig",
+ defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
diff --git a/Android.bp b/Android.bp
index 53554bce43b8..431f0b9317ac 100644
--- a/Android.bp
+++ b/Android.bp
@@ -408,21 +408,18 @@ java_defaults {
],
}
-java_library {
- name: "framework-minus-apex",
+// Separated so framework-minus-apex-defaults can be used without the libs dependency
+java_defaults {
+ name: "framework-minus-apex-with-libs-defaults",
defaults: ["framework-minus-apex-defaults"],
- installable: true,
- // For backwards compatibility.
- stem: "framework",
- apex_available: ["//apex_available:platform"],
- visibility: [
- "//frameworks/base",
- // TODO(b/147128803) remove the below lines
- "//frameworks/base/apex/blobstore/framework",
- "//frameworks/base/apex/jobscheduler/framework",
- "//frameworks/base/packages/Tethering/tests/unit",
- "//packages/modules/Connectivity/Tethering/tests/unit",
+ libs: [
+ "framework-virtualization.stubs.module_lib",
+ "framework-location.impl",
],
+}
+
+java_defaults {
+ name: "framework-non-updatable-lint-defaults",
lint: {
extra_check_modules: ["AndroidFrameworkLintChecker"],
disabled_checks: ["ApiMightLeakAppVisibility"],
@@ -436,6 +433,43 @@ java_library {
"UseOfCallerAwareMethodsWithClearedIdentity",
],
},
+}
+
+// we are unfortunately building the turbine jar twice, but more efficient and less complex
+// than generating a similar set of stubs with metalava
+java_library {
+ name: "framework-minus-apex-headers",
+ defaults: ["framework-minus-apex-defaults"],
+ installable: false,
+ // For backwards compatibility.
+ stem: "framework",
+ apex_available: ["//apex_available:platform"],
+ visibility: [
+ "//frameworks/base/location",
+ ],
+ compile_dex: false,
+ headers_only: true,
+}
+
+java_library {
+ name: "framework-minus-apex",
+ defaults: [
+ "framework-minus-apex-with-libs-defaults",
+ "framework-non-updatable-lint-defaults",
+ ],
+ installable: true,
+ // For backwards compatibility.
+ stem: "framework",
+ apex_available: ["//apex_available:platform"],
+ visibility: [
+ "//frameworks/base",
+ "//frameworks/base/location",
+ // TODO(b/147128803) remove the below lines
+ "//frameworks/base/apex/blobstore/framework",
+ "//frameworks/base/apex/jobscheduler/framework",
+ "//frameworks/base/packages/Tethering/tests/unit",
+ "//packages/modules/Connectivity/Tethering/tests/unit",
+ ],
errorprone: {
javacflags: [
"-Xep:AndroidFrameworkCompatChange:ERROR",
@@ -446,7 +480,7 @@ java_library {
java_library {
name: "framework-minus-apex-intdefs",
- defaults: ["framework-minus-apex-defaults"],
+ defaults: ["framework-minus-apex-with-libs-defaults"],
plugins: ["intdef-annotation-processor"],
// Errorprone and android lint will already run on framework-minus-apex, don't rerun them on
@@ -474,6 +508,7 @@ java_library {
installable: false, // this lib is a build-only library
static_libs: [
"app-compat-annotations",
+ "framework-location.impl",
"framework-minus-apex",
"framework-updatable-stubs-module_libs_api",
],
@@ -701,6 +736,97 @@ stubs_defaults {
],
}
+// Defaults for the java_sdk_libraries of unbundled jars from framework.
+// java_sdk_libraries using these defaults should also add themselves to the
+// non_updatable_modules list in frameworks/base/api/api.go
+java_defaults {
+ name: "framework-non-updatable-unbundled-defaults",
+ defaults: ["framework-non-updatable-lint-defaults"],
+
+ sdk_version: "core_platform",
+
+ // Api scope settings
+ public: {
+ enabled: true,
+ sdk_version: "module_current",
+ libs: ["android_module_lib_stubs_current"],
+ },
+ system: {
+ enabled: true,
+ sdk_version: "module_current",
+ libs: ["android_module_lib_stubs_current"],
+ },
+ module_lib: {
+ enabled: true,
+ sdk_version: "module_current",
+ libs: ["android_module_lib_stubs_current"],
+ },
+ test: {
+ enabled: true,
+ sdk_version: "test_frameworks_core_current",
+ libs: ["android_test_frameworks_core_stubs_current"],
+ },
+
+ stub_only_libs: [
+ "framework-protos",
+ ],
+ impl_only_libs: [
+ "framework-minus-apex-headers", // full access to framework-minus-apex including hidden API
+ "framework-annotations-lib",
+ ],
+ visibility: ["//visibility:public"],
+ stubs_library_visibility: ["//visibility:public"],
+ stubs_source_visibility: ["//visibility:private"],
+ impl_library_visibility: [
+ ":__pkg__",
+ "//frameworks/base",
+ "//frameworks/base/api", // For framework-all
+ ],
+ defaults_visibility: [
+ "//frameworks/base/location",
+ ],
+ plugins: [
+ "error_prone_android_framework",
+ ],
+ errorprone: {
+ javacflags: [
+ "-Xep:AndroidFrameworkCompatChange:ERROR",
+ "-Xep:AndroidFrameworkUid:ERROR",
+ ],
+ },
+
+ // Include manual annotations in API txt files
+ merge_annotations_dirs: ["metalava-manual"],
+
+ // Use the source of annotations that affect metalava doc generation, since
+ // the relevant generation instructions are themselves in javadoc, which is
+ // not present in class files.
+ api_srcs: [":framework-metalava-annotations"],
+
+ // Framework modules are not generally shared libraries, i.e. they are not
+ // intended, and must not be allowed, to be used in a <uses-library> manifest
+ // entry.
+ shared_library: false,
+
+ // Prevent dependencies that do not specify an sdk_version from accessing the
+ // implementation library by default and force them to use stubs instead.
+ default_to_stubs: true,
+
+ // Subdirectory for the artifacts that are copied to the dist directory
+ dist_group: "android",
+
+ droiddoc_options: [
+ "--error UnhiddenSystemApi " +
+ "--hide CallbackInterface " +
+ "--hide HiddenTypedefConstant " +
+ "--hide RequiresPermission " +
+ "--enhance-documentation " +
+ "--hide-package com.android.server ",
+ ],
+
+ annotations_enabled: true,
+}
+
build = [
"AconfigFlags.bp",
"ProtoLibraries.bp",
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobStore.java b/apex/jobscheduler/service/java/com/android/server/job/JobStore.java
index 098b2fb67360..d48d84ba6980 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobStore.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobStore.java
@@ -1118,6 +1118,7 @@ public final class JobStore {
}
boolean needFileMigration = false;
long nowElapsed = sElapsedRealtimeClock.millis();
+ int numDuplicates = 0;
synchronized (mLock) {
for (File file : files) {
final AtomicFile aFile = createJobFile(file);
@@ -1126,6 +1127,16 @@ public final class JobStore {
if (jobs != null) {
for (int i = 0; i < jobs.size(); i++) {
JobStatus js = jobs.get(i);
+ final JobStatus existingJob = this.jobSet.get(
+ js.getUid(), js.getNamespace(), js.getJobId());
+ if (existingJob != null) {
+ numDuplicates++;
+ // Jobs are meant to have unique uid-namespace-jobId
+ // combinations, but we've somehow read multiple jobs with the
+ // combination. Drop the latter one since keeping both will
+ // result in other issues.
+ continue;
+ }
js.prepareLocked();
js.enqueueTime = nowElapsed;
this.jobSet.add(js);
@@ -1174,6 +1185,10 @@ public final class JobStore {
migrateJobFilesAsync();
}
+ if (numDuplicates > 0) {
+ Slog.wtf(TAG, "Encountered " + numDuplicates + " duplicate persisted jobs");
+ }
+
// Log the count immediately after loading from boot.
mCurrentJobSetSize = numJobs;
mScheduledJob30MinHighWaterMark = mCurrentJobSetSize;
diff --git a/api/Android.bp b/api/Android.bp
index e9cc40513221..6986ac09f89e 100644
--- a/api/Android.bp
+++ b/api/Android.bp
@@ -87,6 +87,7 @@ combined_apis {
"framework-devicelock",
"framework-graphics",
"framework-healthfitness",
+ "framework-location",
"framework-media",
"framework-mediaprovider",
"framework-ondevicepersonalization",
diff --git a/api/StubLibraries.bp b/api/StubLibraries.bp
index c37ff9761757..e481fe9971fc 100644
--- a/api/StubLibraries.bp
+++ b/api/StubLibraries.bp
@@ -487,7 +487,6 @@ java_library {
static_libs: [
"all-updatable-modules-system-stubs",
"android-non-updatable.stubs.test",
- "private-stub-annotations-jar",
],
defaults: [
"android.jar_defaults",
@@ -613,6 +612,7 @@ java_defaults {
api_contributions: [
"test-api-stubs-docs-non-updatable.api.contribution",
"framework-virtualization.stubs.source.test.api.contribution",
+ "framework-location.stubs.source.test.api.contribution",
],
}
diff --git a/api/api.go b/api/api.go
index d5c6145ba062..6095a9a781d8 100644
--- a/api/api.go
+++ b/api/api.go
@@ -31,6 +31,7 @@ const art = "art.module.public.api"
const conscrypt = "conscrypt.module.public.api"
const i18n = "i18n.module.public.api"
const virtualization = "framework-virtualization"
+const location = "framework-location"
var core_libraries_modules = []string{art, conscrypt, i18n}
@@ -42,7 +43,7 @@ var core_libraries_modules = []string{art, conscrypt, i18n}
// APIs.
// In addition, the modules in this list are allowed to contribute to test APIs
// stubs.
-var non_updatable_modules = []string{virtualization}
+var non_updatable_modules = []string{virtualization, location}
// The intention behind this soong plugin is to generate a number of "merged"
// API-related modules that would otherwise require a large amount of very
@@ -278,8 +279,10 @@ func createMergedFrameworkImpl(ctx android.LoadHookContext, modules []string) {
}
func createMergedFrameworkModuleLibStubs(ctx android.LoadHookContext, modules []string) {
- // The user of this module compiles against the "core" SDK, so remove core libraries to avoid dupes.
+ // The user of this module compiles against the "core" SDK and against non-updatable modules,
+ // so remove to avoid dupes.
modules = removeAll(modules, core_libraries_modules)
+ modules = removeAll(modules, non_updatable_modules)
props := libraryProps{}
props.Name = proptools.StringPtr("framework-updatable-stubs-module_libs_api")
props.Static_libs = transformArray(modules, "", ".stubs.module_lib")
diff --git a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
index b6dc32a29f04..9dedf7025143 100644
--- a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
+++ b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
@@ -192,7 +192,7 @@ public class Bmgr {
}
if ("whitelist".equals(op)) {
- doPrintWhitelist();
+ doPrintAllowlist();
return;
}
@@ -911,7 +911,7 @@ public class Bmgr {
}
}
- private void doPrintWhitelist() {
+ private void doPrintAllowlist() {
try {
final String[] whitelist = mBmgr.getTransportWhitelist();
if (whitelist != null) {
diff --git a/core/api/current.txt b/core/api/current.txt
index 3cb7468c1444..cb929261bde1 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -14341,14 +14341,14 @@ package android.database.sqlite {
method @NonNull public static android.database.sqlite.SQLiteDatabase create(@Nullable android.database.sqlite.SQLiteDatabase.CursorFactory);
method @NonNull public static android.database.sqlite.SQLiteDatabase createInMemory(@NonNull android.database.sqlite.SQLiteDatabase.OpenParams);
method @NonNull public android.database.sqlite.SQLiteRawStatement createRawStatement(@NonNull String);
- method public int delete(String, String, String[]);
+ method public int delete(@NonNull String, @Nullable String, @Nullable String[]);
method public static boolean deleteDatabase(@NonNull java.io.File);
method public void disableWriteAheadLogging();
method public boolean enableWriteAheadLogging();
method public void endTransaction();
method public void execPerConnectionSQL(@NonNull String, @Nullable Object[]) throws android.database.SQLException;
method public void execSQL(String) throws android.database.SQLException;
- method public void execSQL(String, Object[]) throws android.database.SQLException;
+ method public void execSQL(@NonNull String, @NonNull Object[]) throws android.database.SQLException;
method public static String findEditTable(String);
method public java.util.List<android.util.Pair<java.lang.String,java.lang.String>> getAttachedDbs();
method public long getLastChangedRowCount();
@@ -14360,9 +14360,9 @@ package android.database.sqlite {
method public long getTotalChangedRowCount();
method public int getVersion();
method public boolean inTransaction();
- method public long insert(String, String, android.content.ContentValues);
- method public long insertOrThrow(String, String, android.content.ContentValues) throws android.database.SQLException;
- method public long insertWithOnConflict(String, String, android.content.ContentValues, int);
+ method public long insert(@NonNull String, @Nullable String, @Nullable android.content.ContentValues);
+ method public long insertOrThrow(@NonNull String, @Nullable String, @Nullable android.content.ContentValues) throws android.database.SQLException;
+ method public long insertWithOnConflict(@NonNull String, @Nullable String, @Nullable android.content.ContentValues, int);
method public boolean isDatabaseIntegrityOk();
method public boolean isDbLockedByCurrentThread();
method @Deprecated public boolean isDbLockedByOtherThreads();
@@ -14379,19 +14379,19 @@ package android.database.sqlite {
method public static android.database.sqlite.SQLiteDatabase openOrCreateDatabase(@NonNull java.io.File, @Nullable android.database.sqlite.SQLiteDatabase.CursorFactory);
method public static android.database.sqlite.SQLiteDatabase openOrCreateDatabase(@NonNull String, @Nullable android.database.sqlite.SQLiteDatabase.CursorFactory);
method public static android.database.sqlite.SQLiteDatabase openOrCreateDatabase(@NonNull String, @Nullable android.database.sqlite.SQLiteDatabase.CursorFactory, @Nullable android.database.DatabaseErrorHandler);
- method public android.database.Cursor query(boolean, String, String[], String, String[], String, String, String, String);
- method public android.database.Cursor query(boolean, String, String[], String, String[], String, String, String, String, android.os.CancellationSignal);
- method public android.database.Cursor query(String, String[], String, String[], String, String, String);
- method public android.database.Cursor query(String, String[], String, String[], String, String, String, String);
- method public android.database.Cursor queryWithFactory(android.database.sqlite.SQLiteDatabase.CursorFactory, boolean, String, String[], String, String[], String, String, String, String);
- method public android.database.Cursor queryWithFactory(android.database.sqlite.SQLiteDatabase.CursorFactory, boolean, String, String[], String, String[], String, String, String, String, android.os.CancellationSignal);
- method public android.database.Cursor rawQuery(String, String[]);
- method public android.database.Cursor rawQuery(String, String[], android.os.CancellationSignal);
- method public android.database.Cursor rawQueryWithFactory(android.database.sqlite.SQLiteDatabase.CursorFactory, String, String[], String);
- method public android.database.Cursor rawQueryWithFactory(android.database.sqlite.SQLiteDatabase.CursorFactory, String, String[], String, android.os.CancellationSignal);
+ method @NonNull public android.database.Cursor query(boolean, @NonNull String, @Nullable String[], @Nullable String, @Nullable String[], @Nullable String, @Nullable String, @Nullable String, @Nullable String);
+ method @NonNull public android.database.Cursor query(boolean, @NonNull String, @Nullable String[], @Nullable String, @Nullable String[], @Nullable String, @Nullable String, @Nullable String, @Nullable String, @Nullable android.os.CancellationSignal);
+ method @NonNull public android.database.Cursor query(@NonNull String, @Nullable String[], @Nullable String, @Nullable String[], @Nullable String, @Nullable String, @Nullable String);
+ method @NonNull public android.database.Cursor query(@NonNull String, @Nullable String[], @Nullable String, @Nullable String[], @Nullable String, @Nullable String, @Nullable String, @Nullable String);
+ method @NonNull public android.database.Cursor queryWithFactory(@Nullable android.database.sqlite.SQLiteDatabase.CursorFactory, boolean, @NonNull String, @Nullable String[], @Nullable String, @Nullable String[], @Nullable String, @Nullable String, @Nullable String, @Nullable String);
+ method @NonNull public android.database.Cursor queryWithFactory(@Nullable android.database.sqlite.SQLiteDatabase.CursorFactory, boolean, @NonNull String, @Nullable String[], @Nullable String, @Nullable String[], @Nullable String, @Nullable String, @Nullable String, @Nullable String, @Nullable android.os.CancellationSignal);
+ method @NonNull public android.database.Cursor rawQuery(@NonNull String, @Nullable String[]);
+ method @NonNull public android.database.Cursor rawQuery(@NonNull String, @Nullable String[], @Nullable android.os.CancellationSignal);
+ method @NonNull public android.database.Cursor rawQueryWithFactory(@Nullable android.database.sqlite.SQLiteDatabase.CursorFactory, @NonNull String, @Nullable String[], @NonNull String);
+ method @NonNull public android.database.Cursor rawQueryWithFactory(@Nullable android.database.sqlite.SQLiteDatabase.CursorFactory, @NonNull String, @Nullable String[], @NonNull String, @Nullable android.os.CancellationSignal);
method public static int releaseMemory();
- method public long replace(String, String, android.content.ContentValues);
- method public long replaceOrThrow(String, String, android.content.ContentValues) throws android.database.SQLException;
+ method public long replace(@NonNull String, @Nullable String, @Nullable android.content.ContentValues);
+ method public long replaceOrThrow(@NonNull String, @Nullable String, @Nullable android.content.ContentValues) throws android.database.SQLException;
method public void setCustomAggregateFunction(@NonNull String, @NonNull java.util.function.BinaryOperator<java.lang.String>) throws android.database.sqlite.SQLiteException;
method public void setCustomScalarFunction(@NonNull String, @NonNull java.util.function.UnaryOperator<java.lang.String>) throws android.database.sqlite.SQLiteException;
method public void setForeignKeyConstraintsEnabled(boolean);
@@ -14402,8 +14402,8 @@ package android.database.sqlite {
method public void setPageSize(long);
method public void setTransactionSuccessful();
method public void setVersion(int);
- method public int update(String, android.content.ContentValues, String, String[]);
- method public int updateWithOnConflict(String, android.content.ContentValues, String, String[], int);
+ method public int update(@NonNull String, @Nullable android.content.ContentValues, @Nullable String, @Nullable String[]);
+ method public int updateWithOnConflict(@NonNull String, @Nullable android.content.ContentValues, @Nullable String, @Nullable String[], int);
method public void validateSql(@NonNull String, @Nullable android.os.CancellationSignal);
method @Deprecated public boolean yieldIfContended();
method public boolean yieldIfContendedSafely();
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 61415839ea2c..2b3e8e9e0a92 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -301,6 +301,7 @@ package android {
field public static final String RECOVER_KEYSTORE = "android.permission.RECOVER_KEYSTORE";
field public static final String REGISTER_CALL_PROVIDER = "android.permission.REGISTER_CALL_PROVIDER";
field public static final String REGISTER_CONNECTION_MANAGER = "android.permission.REGISTER_CONNECTION_MANAGER";
+ field public static final String REGISTER_NSD_OFFLOAD_ENGINE = "android.permission.REGISTER_NSD_OFFLOAD_ENGINE";
field public static final String REGISTER_SIM_SUBSCRIPTION = "android.permission.REGISTER_SIM_SUBSCRIPTION";
field public static final String REGISTER_STATS_PULL_ATOM = "android.permission.REGISTER_STATS_PULL_ATOM";
field public static final String REMOTE_DISPLAY_PROVIDER = "android.permission.REMOTE_DISPLAY_PROVIDER";
@@ -17233,9 +17234,8 @@ package android.telephony.satellite {
method public void onSatelliteDatagramReceived(long, @NonNull android.telephony.satellite.SatelliteDatagram, int, @NonNull java.util.function.Consumer<java.lang.Void>);
}
- public class SatelliteManager {
+ public final class SatelliteManager {
method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void deprovisionSatelliteService(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
- method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void onDeviceAlignedWithSatellite(boolean);
method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void pollPendingSatelliteDatagrams(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void provisionSatelliteService(@NonNull String, @NonNull byte[], @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int registerForSatelliteDatagram(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.SatelliteDatagramCallback);
@@ -17250,6 +17250,7 @@ package android.telephony.satellite {
method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestSatelliteEnabled(boolean, boolean, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestTimeForNextSatelliteVisibility(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.time.Duration,android.telephony.satellite.SatelliteManager.SatelliteException>);
method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void sendSatelliteDatagram(int, @NonNull android.telephony.satellite.SatelliteDatagram, boolean, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
+ method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void setDeviceAlignedWithSatellite(boolean);
method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void startSatelliteTransmissionUpdates(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>, @NonNull android.telephony.satellite.SatelliteTransmissionUpdateCallback);
method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void stopSatelliteTransmissionUpdates(@NonNull android.telephony.satellite.SatelliteTransmissionUpdateCallback, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void unregisterForSatelliteDatagram(@NonNull android.telephony.satellite.SatelliteDatagramCallback);
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index e57849957f13..fbb97ffea035 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -243,6 +243,7 @@ import android.view.translation.ITranslationManager;
import android.view.translation.TranslationManager;
import android.view.translation.UiTranslationManager;
+import com.android.internal.R;
import com.android.internal.app.IAppOpsService;
import com.android.internal.app.IBatteryStats;
import com.android.internal.app.ISoundTriggerService;
@@ -871,6 +872,10 @@ public final class SystemServiceRegistry {
PackageManager.FEATURE_COMPANION_DEVICE_SETUP)) {
return null;
}
+ if (!ctx.getResources().getBoolean(R.bool.config_enableVirtualDeviceManager)) {
+ return null;
+ }
+
IVirtualDeviceManager service = IVirtualDeviceManager.Stub.asInterface(
ServiceManager.getServiceOrThrow(Context.VIRTUAL_DEVICE_SERVICE));
return new VirtualDeviceManager(service, ctx.getOuterContext());
diff --git a/core/java/android/appwidget/AppWidgetManager.java b/core/java/android/appwidget/AppWidgetManager.java
index 3927b40fdc74..3abe1d9494f0 100644
--- a/core/java/android/appwidget/AppWidgetManager.java
+++ b/core/java/android/appwidget/AppWidgetManager.java
@@ -24,6 +24,7 @@ import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.SystemService;
import android.annotation.TestApi;
+import android.annotation.UiThread;
import android.annotation.UserIdInt;
import android.app.IServiceConnection;
import android.app.PendingIntent;
@@ -39,6 +40,10 @@ import android.content.pm.ShortcutInfo;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
+import android.os.HandlerExecutor;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Process;
import android.os.RemoteException;
import android.os.UserHandle;
import android.util.DisplayMetrics;
@@ -53,6 +58,7 @@ import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.Executor;
/**
* Updates AppWidget state; gets information about installed AppWidget providers and other
@@ -475,6 +481,8 @@ public class AppWidgetManager {
private static final String TAG = "AppWidgetManager";
+ private static Executor sUpdateExecutor;
+
/**
* An intent extra that contains multiple appWidgetIds. These are id values as
* they were provided to the application during a recent restore from backup. It is
@@ -510,6 +518,8 @@ public class AppWidgetManager {
private final IAppWidgetService mService;
private final DisplayMetrics mDisplayMetrics;
+ private boolean mHasPostedLegacyLists = false;
+
/**
* Get the AppWidgetManager instance to use for the supplied {@link android.content.Context
* Context} object.
@@ -552,6 +562,13 @@ public class AppWidgetManager {
});
}
+ private boolean isPostingTaskToBackground(@Nullable RemoteViews views) {
+ return Looper.myLooper() == Looper.getMainLooper()
+ && RemoteViews.isAdapterConversionEnabled()
+ && (mHasPostedLegacyLists = mHasPostedLegacyLists
+ || (views != null && views.hasLegacyLists()));
+ }
+
/**
* Set the RemoteViews to use for the specified appWidgetIds.
* <p>
@@ -575,6 +592,19 @@ public class AppWidgetManager {
if (mService == null) {
return;
}
+
+ if (isPostingTaskToBackground(views)) {
+ createUpdateExecutorIfNull().execute(() -> {
+ try {
+ mService.updateAppWidgetIds(mPackageName, appWidgetIds, views);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error updating app widget views in background", e);
+ }
+ });
+
+ return;
+ }
+
try {
mService.updateAppWidgetIds(mPackageName, appWidgetIds, views);
} catch (RemoteException e) {
@@ -683,6 +713,19 @@ public class AppWidgetManager {
if (mService == null) {
return;
}
+
+ if (isPostingTaskToBackground(views)) {
+ createUpdateExecutorIfNull().execute(() -> {
+ try {
+ mService.partiallyUpdateAppWidgetIds(mPackageName, appWidgetIds, views);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error partially updating app widget views in background", e);
+ }
+ });
+
+ return;
+ }
+
try {
mService.partiallyUpdateAppWidgetIds(mPackageName, appWidgetIds, views);
} catch (RemoteException e) {
@@ -738,6 +781,19 @@ public class AppWidgetManager {
if (mService == null) {
return;
}
+
+ if (isPostingTaskToBackground(views)) {
+ createUpdateExecutorIfNull().execute(() -> {
+ try {
+ mService.updateAppWidgetProvider(provider, views);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error updating app widget view using provider in background", e);
+ }
+ });
+
+ return;
+ }
+
try {
mService.updateAppWidgetProvider(provider, views);
} catch (RemoteException e) {
@@ -797,28 +853,45 @@ public class AppWidgetManager {
if (mService == null) {
return;
}
+
+ if (!RemoteViews.isAdapterConversionEnabled()) {
+ try {
+ mService.notifyAppWidgetViewDataChanged(mPackageName, appWidgetIds, viewId);
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+
+ return;
+ }
+
+ if (Looper.myLooper() == Looper.getMainLooper()) {
+ mHasPostedLegacyLists = true;
+ createUpdateExecutorIfNull().execute(() -> notifyCollectionWidgetChange(appWidgetIds,
+ viewId));
+ } else {
+ notifyCollectionWidgetChange(appWidgetIds, viewId);
+ }
+ }
+
+ private void notifyCollectionWidgetChange(int[] appWidgetIds, int viewId) {
try {
- if (RemoteViews.isAdapterConversionEnabled()) {
- List<CompletableFuture<Void>> updateFutures = new ArrayList<>();
- for (int i = 0; i < appWidgetIds.length; i++) {
- final int widgetId = appWidgetIds[i];
- updateFutures.add(CompletableFuture.runAsync(() -> {
- try {
- RemoteViews views = mService.getAppWidgetViews(mPackageName, widgetId);
- if (views.replaceRemoteCollections(viewId)) {
- updateAppWidget(widgetId, views);
- }
- } catch (Exception e) {
- Log.e(TAG, "Error notifying changes in RemoteViews", e);
+ List<CompletableFuture<Void>> updateFutures = new ArrayList<>();
+ for (int i = 0; i < appWidgetIds.length; i++) {
+ final int widgetId = appWidgetIds[i];
+ updateFutures.add(CompletableFuture.runAsync(() -> {
+ try {
+ RemoteViews views = mService.getAppWidgetViews(mPackageName, widgetId);
+ if (views.replaceRemoteCollections(viewId)) {
+ updateAppWidget(widgetId, views);
}
- }));
- }
- CompletableFuture.allOf(updateFutures.toArray(CompletableFuture[]::new)).join();
- } else {
- mService.notifyAppWidgetViewDataChanged(mPackageName, appWidgetIds, viewId);
+ } catch (Exception e) {
+ Log.e(TAG, "Error notifying changes in RemoteViews", e);
+ }
+ }));
}
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ CompletableFuture.allOf(updateFutures.toArray(CompletableFuture[]::new)).join();
+ } catch (Exception e) {
+ Log.e(TAG, "Error notifying changes for all widgets", e);
}
}
@@ -1360,4 +1433,20 @@ public class AppWidgetManager {
throw e.rethrowFromSystemServer();
}
}
+
+ @UiThread
+ private static @NonNull Executor createUpdateExecutorIfNull() {
+ if (sUpdateExecutor == null) {
+ sUpdateExecutor = new HandlerExecutor(createAndStartNewHandler(
+ "widget_manager_update_helper_thread", Process.THREAD_PRIORITY_FOREGROUND));
+ }
+
+ return sUpdateExecutor;
+ }
+
+ private static @NonNull Handler createAndStartNewHandler(@NonNull String name, int priority) {
+ HandlerThread thread = new HandlerThread(name, priority);
+ thread.start();
+ return thread.getThreadHandler();
+ }
}
diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java
index 5b80e6a0b188..746f2f23fd5c 100644
--- a/core/java/android/database/sqlite/SQLiteDatabase.java
+++ b/core/java/android/database/sqlite/SQLiteDatabase.java
@@ -44,10 +44,12 @@ import android.util.EventLog;
import android.util.Log;
import android.util.Pair;
import android.util.Printer;
+
import com.android.internal.util.Preconditions;
import dalvik.annotation.optimization.NeverCompile;
import dalvik.system.CloseGuard;
+
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
@@ -1473,8 +1475,10 @@ public final class SQLiteDatabase extends SQLiteClosable {
* SQL WHERE clause (excluding the WHERE itself). Passing null
* will return all rows for the given table.
* @param selectionArgs You may include ?s in selection, which will be
- * replaced by the values from selectionArgs, in order that they
+ * replaced by the values from selectionArgs, in the order that they
* appear in the selection. The values will be bound as Strings.
+ * If selection is null or does not contain ?s then selectionArgs
+ * may be null.
* @param groupBy A filter declaring how to group rows, formatted as an SQL
* GROUP BY clause (excluding the GROUP BY itself). Passing null
* will cause the rows to not be grouped.
@@ -1492,9 +1496,11 @@ public final class SQLiteDatabase extends SQLiteClosable {
* {@link Cursor}s are not synchronized, see the documentation for more details.
* @see Cursor
*/
- public Cursor query(boolean distinct, String table, String[] columns,
- String selection, String[] selectionArgs, String groupBy,
- String having, String orderBy, String limit) {
+ @NonNull
+ public Cursor query(boolean distinct, @NonNull String table,
+ @Nullable String[] columns, @Nullable String selection,
+ @Nullable String[] selectionArgs, @Nullable String groupBy, @Nullable String having,
+ @Nullable String orderBy, @Nullable String limit) {
return queryWithFactory(null, distinct, table, columns, selection, selectionArgs,
groupBy, having, orderBy, limit, null);
}
@@ -1511,8 +1517,10 @@ public final class SQLiteDatabase extends SQLiteClosable {
* SQL WHERE clause (excluding the WHERE itself). Passing null
* will return all rows for the given table.
* @param selectionArgs You may include ?s in selection, which will be
- * replaced by the values from selectionArgs, in order that they
+ * replaced by the values from selectionArgs, in the order that they
* appear in the selection. The values will be bound as Strings.
+ * If selection is null or does not contain ?s then selectionArgs
+ * may be null.
* @param groupBy A filter declaring how to group rows, formatted as an SQL
* GROUP BY clause (excluding the GROUP BY itself). Passing null
* will cause the rows to not be grouped.
@@ -1533,9 +1541,12 @@ public final class SQLiteDatabase extends SQLiteClosable {
* {@link Cursor}s are not synchronized, see the documentation for more details.
* @see Cursor
*/
- public Cursor query(boolean distinct, String table, String[] columns,
- String selection, String[] selectionArgs, String groupBy,
- String having, String orderBy, String limit, CancellationSignal cancellationSignal) {
+ @NonNull
+ public Cursor query(boolean distinct, @NonNull String table,
+ @Nullable String[] columns, @Nullable String selection,
+ @Nullable String[] selectionArgs, @Nullable String groupBy, @Nullable String having,
+ @Nullable String orderBy, @Nullable String limit,
+ @Nullable CancellationSignal cancellationSignal) {
return queryWithFactory(null, distinct, table, columns, selection, selectionArgs,
groupBy, having, orderBy, limit, cancellationSignal);
}
@@ -1553,8 +1564,10 @@ public final class SQLiteDatabase extends SQLiteClosable {
* SQL WHERE clause (excluding the WHERE itself). Passing null
* will return all rows for the given table.
* @param selectionArgs You may include ?s in selection, which will be
- * replaced by the values from selectionArgs, in order that they
+ * replaced by the values from selectionArgs, in the order that they
* appear in the selection. The values will be bound as Strings.
+ * If selection is null or does not contain ?s then selectionArgs
+ * may be null.
* @param groupBy A filter declaring how to group rows, formatted as an SQL
* GROUP BY clause (excluding the GROUP BY itself). Passing null
* will cause the rows to not be grouped.
@@ -1572,10 +1585,12 @@ public final class SQLiteDatabase extends SQLiteClosable {
* {@link Cursor}s are not synchronized, see the documentation for more details.
* @see Cursor
*/
- public Cursor queryWithFactory(CursorFactory cursorFactory,
- boolean distinct, String table, String[] columns,
- String selection, String[] selectionArgs, String groupBy,
- String having, String orderBy, String limit) {
+ @SuppressLint("SamShouldBeLast")
+ @NonNull
+ public Cursor queryWithFactory(@Nullable CursorFactory cursorFactory,
+ boolean distinct, @NonNull String table, @Nullable String[] columns,
+ @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String groupBy,
+ @Nullable String having, @Nullable String orderBy, @Nullable String limit) {
return queryWithFactory(cursorFactory, distinct, table, columns, selection,
selectionArgs, groupBy, having, orderBy, limit, null);
}
@@ -1593,8 +1608,10 @@ public final class SQLiteDatabase extends SQLiteClosable {
* SQL WHERE clause (excluding the WHERE itself). Passing null
* will return all rows for the given table.
* @param selectionArgs You may include ?s in selection, which will be
- * replaced by the values from selectionArgs, in order that they
+ * replaced by the values from selectionArgs, in the order that they
* appear in the selection. The values will be bound as Strings.
+ * If selection is null or does not contain ?s then selectionArgs
+ * may be null.
* @param groupBy A filter declaring how to group rows, formatted as an SQL
* GROUP BY clause (excluding the GROUP BY itself). Passing null
* will cause the rows to not be grouped.
@@ -1615,10 +1632,13 @@ public final class SQLiteDatabase extends SQLiteClosable {
* {@link Cursor}s are not synchronized, see the documentation for more details.
* @see Cursor
*/
- public Cursor queryWithFactory(CursorFactory cursorFactory,
- boolean distinct, String table, String[] columns,
- String selection, String[] selectionArgs, String groupBy,
- String having, String orderBy, String limit, CancellationSignal cancellationSignal) {
+ @SuppressLint("SamShouldBeLast")
+ @NonNull
+ public Cursor queryWithFactory(@Nullable CursorFactory cursorFactory,
+ boolean distinct, @NonNull String table, @Nullable String[] columns,
+ @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String groupBy,
+ @Nullable String having, @Nullable String orderBy, @Nullable String limit,
+ @Nullable CancellationSignal cancellationSignal) {
acquireReference();
try {
String sql = SQLiteQueryBuilder.buildQueryString(
@@ -1642,8 +1662,10 @@ public final class SQLiteDatabase extends SQLiteClosable {
* SQL WHERE clause (excluding the WHERE itself). Passing null
* will return all rows for the given table.
* @param selectionArgs You may include ?s in selection, which will be
- * replaced by the values from selectionArgs, in order that they
+ * replaced by the values from selectionArgs, in the order that they
* appear in the selection. The values will be bound as Strings.
+ * If selection is null or does not contain ?s then selectionArgs
+ * may be null.
* @param groupBy A filter declaring how to group rows, formatted as an SQL
* GROUP BY clause (excluding the GROUP BY itself). Passing null
* will cause the rows to not be grouped.
@@ -1659,9 +1681,10 @@ public final class SQLiteDatabase extends SQLiteClosable {
* {@link Cursor}s are not synchronized, see the documentation for more details.
* @see Cursor
*/
- public Cursor query(String table, String[] columns, String selection,
- String[] selectionArgs, String groupBy, String having,
- String orderBy) {
+ @NonNull
+ public Cursor query(@NonNull String table, @Nullable String[] columns,
+ @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String groupBy,
+ @Nullable String having, @Nullable String orderBy) {
return query(false, table, columns, selection, selectionArgs, groupBy,
having, orderBy, null /* limit */);
@@ -1678,8 +1701,10 @@ public final class SQLiteDatabase extends SQLiteClosable {
* SQL WHERE clause (excluding the WHERE itself). Passing null
* will return all rows for the given table.
* @param selectionArgs You may include ?s in selection, which will be
- * replaced by the values from selectionArgs, in order that they
+ * replaced by the values from selectionArgs, in the order that they
* appear in the selection. The values will be bound as Strings.
+ * If selection is null or does not contain ?s then selectionArgs
+ * may be null.
* @param groupBy A filter declaring how to group rows, formatted as an SQL
* GROUP BY clause (excluding the GROUP BY itself). Passing null
* will cause the rows to not be grouped.
@@ -1697,9 +1722,10 @@ public final class SQLiteDatabase extends SQLiteClosable {
* {@link Cursor}s are not synchronized, see the documentation for more details.
* @see Cursor
*/
- public Cursor query(String table, String[] columns, String selection,
- String[] selectionArgs, String groupBy, String having,
- String orderBy, String limit) {
+ @NonNull
+ public Cursor query(@NonNull String table, @Nullable String[] columns,
+ @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String groupBy,
+ @Nullable String having, @Nullable String orderBy, @Nullable String limit) {
return query(false, table, columns, selection, selectionArgs, groupBy,
having, orderBy, limit);
@@ -1711,11 +1737,13 @@ public final class SQLiteDatabase extends SQLiteClosable {
* @param sql the SQL query. The SQL string must not be ; terminated
* @param selectionArgs You may include ?s in where clause in the query,
* which will be replaced by the values from selectionArgs. The
- * values will be bound as Strings.
+ * values will be bound as Strings. If selection is null or does not contain ?s then
+ * selectionArgs may be null.
* @return A {@link Cursor} object, which is positioned before the first entry. Note that
* {@link Cursor}s are not synchronized, see the documentation for more details.
*/
- public Cursor rawQuery(String sql, String[] selectionArgs) {
+ @NonNull
+ public Cursor rawQuery(@NonNull String sql, @Nullable String[] selectionArgs) {
return rawQueryWithFactory(null, sql, selectionArgs, null, null);
}
@@ -1725,15 +1753,17 @@ public final class SQLiteDatabase extends SQLiteClosable {
* @param sql the SQL query. The SQL string must not be ; terminated
* @param selectionArgs You may include ?s in where clause in the query,
* which will be replaced by the values from selectionArgs. The
- * values will be bound as Strings.
+ * values will be bound as Strings. If selection is null or does not contain ?s then
+ * selectionArgs may be null.
* @param cancellationSignal A signal to cancel the operation in progress, or null if none.
* If the operation is canceled, then {@link OperationCanceledException} will be thrown
* when the query is executed.
* @return A {@link Cursor} object, which is positioned before the first entry. Note that
* {@link Cursor}s are not synchronized, see the documentation for more details.
*/
- public Cursor rawQuery(String sql, String[] selectionArgs,
- CancellationSignal cancellationSignal) {
+ @NonNull
+ public Cursor rawQuery(@NonNull String sql, @Nullable String[] selectionArgs,
+ @Nullable CancellationSignal cancellationSignal) {
return rawQueryWithFactory(null, sql, selectionArgs, null, cancellationSignal);
}
@@ -1744,14 +1774,16 @@ public final class SQLiteDatabase extends SQLiteClosable {
* @param sql the SQL query. The SQL string must not be ; terminated
* @param selectionArgs You may include ?s in where clause in the query,
* which will be replaced by the values from selectionArgs. The
- * values will be bound as Strings.
+ * values will be bound as Strings. If selection is null or does not contain ?s then
+ * selectionArgs may be null.
* @param editTable the name of the first table, which is editable
* @return A {@link Cursor} object, which is positioned before the first entry. Note that
* {@link Cursor}s are not synchronized, see the documentation for more details.
*/
+ @NonNull
public Cursor rawQueryWithFactory(
- CursorFactory cursorFactory, String sql, String[] selectionArgs,
- String editTable) {
+ @Nullable CursorFactory cursorFactory, @NonNull String sql,
+ @Nullable String[] selectionArgs, @NonNull String editTable) {
return rawQueryWithFactory(cursorFactory, sql, selectionArgs, editTable, null);
}
@@ -1762,7 +1794,8 @@ public final class SQLiteDatabase extends SQLiteClosable {
* @param sql the SQL query. The SQL string must not be ; terminated
* @param selectionArgs You may include ?s in where clause in the query,
* which will be replaced by the values from selectionArgs. The
- * values will be bound as Strings.
+ * values will be bound as Strings. If selection is null or does not contain ?s then
+ * selectionArgs may be null.
* @param editTable the name of the first table, which is editable
* @param cancellationSignal A signal to cancel the operation in progress, or null if none.
* If the operation is canceled, then {@link OperationCanceledException} will be thrown
@@ -1770,9 +1803,11 @@ public final class SQLiteDatabase extends SQLiteClosable {
* @return A {@link Cursor} object, which is positioned before the first entry. Note that
* {@link Cursor}s are not synchronized, see the documentation for more details.
*/
+ @NonNull
public Cursor rawQueryWithFactory(
- CursorFactory cursorFactory, String sql, String[] selectionArgs,
- String editTable, CancellationSignal cancellationSignal) {
+ @Nullable CursorFactory cursorFactory, @NonNull String sql,
+ @Nullable String[] selectionArgs, @NonNull String editTable,
+ @Nullable CancellationSignal cancellationSignal) {
acquireReference();
try {
SQLiteCursorDriver driver = new SQLiteDirectCursorDriver(this, sql, editTable,
@@ -1800,7 +1835,8 @@ public final class SQLiteDatabase extends SQLiteClosable {
* column values
* @return the row ID of the newly inserted row, or -1 if an error occurred
*/
- public long insert(String table, String nullColumnHack, ContentValues values) {
+ public long insert(@NonNull String table, @Nullable String nullColumnHack,
+ @Nullable ContentValues values) {
try {
return insertWithOnConflict(table, nullColumnHack, values, CONFLICT_NONE);
} catch (SQLException e) {
@@ -1826,8 +1862,8 @@ public final class SQLiteDatabase extends SQLiteClosable {
* @throws SQLException
* @return the row ID of the newly inserted row, or -1 if an error occurred
*/
- public long insertOrThrow(String table, String nullColumnHack, ContentValues values)
- throws SQLException {
+ public long insertOrThrow(@NonNull String table, @Nullable String nullColumnHack,
+ @Nullable ContentValues values) throws SQLException {
return insertWithOnConflict(table, nullColumnHack, values, CONFLICT_NONE);
}
@@ -1847,7 +1883,8 @@ public final class SQLiteDatabase extends SQLiteClosable {
* the row. The keys should be the column names and the values the column values.
* @return the row ID of the newly inserted row, or -1 if an error occurred
*/
- public long replace(String table, String nullColumnHack, ContentValues initialValues) {
+ public long replace(@NonNull String table, @Nullable String nullColumnHack,
+ @Nullable ContentValues initialValues) {
try {
return insertWithOnConflict(table, nullColumnHack, initialValues,
CONFLICT_REPLACE);
@@ -1874,8 +1911,8 @@ public final class SQLiteDatabase extends SQLiteClosable {
* @throws SQLException
* @return the row ID of the newly inserted row, or -1 if an error occurred
*/
- public long replaceOrThrow(String table, String nullColumnHack,
- ContentValues initialValues) throws SQLException {
+ public long replaceOrThrow(@NonNull String table, @Nullable String nullColumnHack,
+ @Nullable ContentValues initialValues) throws SQLException {
return insertWithOnConflict(table, nullColumnHack, initialValues,
CONFLICT_REPLACE);
}
@@ -1899,8 +1936,8 @@ public final class SQLiteDatabase extends SQLiteClosable {
* input parameter <code>conflictAlgorithm</code> = {@link #CONFLICT_IGNORE}
* or an error occurred.
*/
- public long insertWithOnConflict(String table, String nullColumnHack,
- ContentValues initialValues, int conflictAlgorithm) {
+ public long insertWithOnConflict(@NonNull String table, @Nullable String nullColumnHack,
+ @Nullable ContentValues initialValues, int conflictAlgorithm) {
acquireReference();
try {
StringBuilder sql = new StringBuilder();
@@ -1950,12 +1987,14 @@ public final class SQLiteDatabase extends SQLiteClosable {
* Passing null will delete all rows.
* @param whereArgs You may include ?s in the where clause, which
* will be replaced by the values from whereArgs. The values
- * will be bound as Strings.
+ * will be bound as Strings. If whereClause is null or does not
+ * contain ?s then whereArgs may be null.
* @return the number of rows affected if a whereClause is passed in, 0
* otherwise. To remove all rows and get a count pass "1" as the
* whereClause.
*/
- public int delete(String table, String whereClause, String[] whereArgs) {
+ public int delete(@NonNull String table, @Nullable String whereClause,
+ @Nullable String[] whereArgs) {
acquireReference();
try {
SQLiteStatement statement = new SQLiteStatement(this, "DELETE FROM " + table +
@@ -1980,10 +2019,12 @@ public final class SQLiteDatabase extends SQLiteClosable {
* Passing null will update all rows.
* @param whereArgs You may include ?s in the where clause, which
* will be replaced by the values from whereArgs. The values
- * will be bound as Strings.
+ * will be bound as Strings. If whereClause is null or does not
+ * contain ?s then whereArgs may be null.
* @return the number of rows affected
*/
- public int update(String table, ContentValues values, String whereClause, String[] whereArgs) {
+ public int update(@NonNull String table, @Nullable ContentValues values,
+ @Nullable String whereClause, @Nullable String[] whereArgs) {
return updateWithOnConflict(table, values, whereClause, whereArgs, CONFLICT_NONE);
}
@@ -1997,12 +2038,13 @@ public final class SQLiteDatabase extends SQLiteClosable {
* Passing null will update all rows.
* @param whereArgs You may include ?s in the where clause, which
* will be replaced by the values from whereArgs. The values
- * will be bound as Strings.
+ * will be bound as Strings. If whereClause is null or does not
+ * contain ?s then whereArgs may be null.
* @param conflictAlgorithm for update conflict resolver
* @return the number of rows affected
*/
- public int updateWithOnConflict(String table, ContentValues values,
- String whereClause, String[] whereArgs, int conflictAlgorithm) {
+ public int updateWithOnConflict(@NonNull String table, @Nullable ContentValues values,
+ @Nullable String whereClause, @Nullable String[] whereArgs, int conflictAlgorithm) {
if (values == null || values.isEmpty()) {
throw new IllegalArgumentException("Empty values");
}
@@ -2125,7 +2167,8 @@ public final class SQLiteDatabase extends SQLiteClosable {
* @param bindArgs only byte[], String, Long and Double are supported in bindArgs.
* @throws SQLException if the SQL string is invalid
*/
- public void execSQL(String sql, Object[] bindArgs) throws SQLException {
+ public void execSQL(@NonNull String sql, @NonNull Object[] bindArgs)
+ throws SQLException {
if (bindArgs == null) {
throw new IllegalArgumentException("Empty bindArgs");
}
@@ -2133,7 +2176,8 @@ public final class SQLiteDatabase extends SQLiteClosable {
}
/** {@hide} */
- public int executeSql(String sql, Object[] bindArgs) throws SQLException {
+ public int executeSql(@NonNull String sql, @NonNull Object[] bindArgs)
+ throws SQLException {
acquireReference();
try {
final int statementType = DatabaseUtils.getSqlStatementType(sql);
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java
index ccc39b6080d7..039644387715 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -41,11 +41,6 @@ import android.os.Message;
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
-import android.renderscript.Allocation;
-import android.renderscript.Element;
-import android.renderscript.RSIllegalArgumentException;
-import android.renderscript.RenderScript;
-import android.renderscript.Type;
import android.text.TextUtils;
import android.util.Log;
import android.view.Surface;
@@ -1007,132 +1002,6 @@ public class Camera {
private native final void _addCallbackBuffer(
byte[] callbackBuffer, int msgType);
- /**
- * <p>Create a {@link android.renderscript RenderScript}
- * {@link android.renderscript.Allocation Allocation} to use as a
- * destination of preview callback frames. Use
- * {@link #setPreviewCallbackAllocation setPreviewCallbackAllocation} to use
- * the created Allocation as a destination for camera preview frames.</p>
- *
- * <p>The Allocation will be created with a YUV type, and its contents must
- * be accessed within Renderscript with the {@code rsGetElementAtYuv_*}
- * accessor methods. Its size will be based on the current
- * {@link Parameters#getPreviewSize preview size} configured for this
- * camera.</p>
- *
- * @param rs the RenderScript context for this Allocation.
- * @param usage additional usage flags to set for the Allocation. The usage
- * flag {@link android.renderscript.Allocation#USAGE_IO_INPUT} will always
- * be set on the created Allocation, but additional flags may be provided
- * here.
- * @return a new YUV-type Allocation with dimensions equal to the current
- * preview size.
- * @throws RSIllegalArgumentException if the usage flags are not compatible
- * with an YUV Allocation.
- * @see #setPreviewCallbackAllocation
- * @hide
- */
- public final Allocation createPreviewAllocation(RenderScript rs, int usage)
- throws RSIllegalArgumentException {
- Parameters p = getParameters();
- Size previewSize = p.getPreviewSize();
- Type.Builder yuvBuilder = new Type.Builder(rs,
- Element.createPixel(rs,
- Element.DataType.UNSIGNED_8,
- Element.DataKind.PIXEL_YUV));
- // Use YV12 for wide compatibility. Changing this requires also
- // adjusting camera service's format selection.
- yuvBuilder.setYuvFormat(ImageFormat.YV12);
- yuvBuilder.setX(previewSize.width);
- yuvBuilder.setY(previewSize.height);
-
- Allocation a = Allocation.createTyped(rs, yuvBuilder.create(),
- usage | Allocation.USAGE_IO_INPUT);
-
- return a;
- }
-
- /**
- * <p>Set an {@link android.renderscript.Allocation Allocation} as the
- * target of preview callback data. Use this method for efficient processing
- * of camera preview data with RenderScript. The Allocation must be created
- * with the {@link #createPreviewAllocation createPreviewAllocation }
- * method.</p>
- *
- * <p>Setting a preview allocation will disable any active preview callbacks
- * set by {@link #setPreviewCallback setPreviewCallback} or
- * {@link #setPreviewCallbackWithBuffer setPreviewCallbackWithBuffer}, and
- * vice versa. Using a preview allocation still requires an active standard
- * preview target to be set, either with
- * {@link #setPreviewTexture setPreviewTexture} or
- * {@link #setPreviewDisplay setPreviewDisplay}.</p>
- *
- * <p>To be notified when new frames are available to the Allocation, use
- * {@link android.renderscript.Allocation#setIoInputNotificationHandler Allocation.setIoInputNotificationHandler}. To
- * update the frame currently accessible from the Allocation to the latest
- * preview frame, call
- * {@link android.renderscript.Allocation#ioReceive Allocation.ioReceive}.</p>
- *
- * <p>To disable preview into the Allocation, call this method with a
- * {@code null} parameter.</p>
- *
- * <p>Once a preview allocation is set, the preview size set by
- * {@link Parameters#setPreviewSize setPreviewSize} cannot be changed. If
- * you wish to change the preview size, first remove the preview allocation
- * by calling {@code setPreviewCallbackAllocation(null)}, then change the
- * preview size, create a new preview Allocation with
- * {@link #createPreviewAllocation createPreviewAllocation}, and set it as
- * the new preview callback allocation target.</p>
- *
- * <p>If you are using the preview data to create video or still images,
- * strongly consider using {@link android.media.MediaActionSound} to
- * properly indicate image capture or recording start/stop to the user.</p>
- *
- * @param previewAllocation the allocation to use as destination for preview
- * @throws IOException if configuring the camera to use the Allocation for
- * preview fails.
- * @throws IllegalArgumentException if the Allocation's dimensions or other
- * parameters don't meet the requirements.
- * @see #createPreviewAllocation
- * @see #setPreviewCallback
- * @see #setPreviewCallbackWithBuffer
- * @hide
- */
- public final void setPreviewCallbackAllocation(Allocation previewAllocation)
- throws IOException {
- Surface previewSurface = null;
- if (previewAllocation != null) {
- Parameters p = getParameters();
- Size previewSize = p.getPreviewSize();
- if (previewSize.width != previewAllocation.getType().getX() ||
- previewSize.height != previewAllocation.getType().getY()) {
- throw new IllegalArgumentException(
- "Allocation dimensions don't match preview dimensions: " +
- "Allocation is " +
- previewAllocation.getType().getX() +
- ", " +
- previewAllocation.getType().getY() +
- ". Preview is " + previewSize.width + ", " +
- previewSize.height);
- }
- if ((previewAllocation.getUsage() &
- Allocation.USAGE_IO_INPUT) == 0) {
- throw new IllegalArgumentException(
- "Allocation usage does not include USAGE_IO_INPUT");
- }
- if (previewAllocation.getType().getElement().getDataKind() !=
- Element.DataKind.PIXEL_YUV) {
- throw new IllegalArgumentException(
- "Allocation is not of a YUV type");
- }
- previewSurface = previewAllocation.getSurface();
- mUsingPreviewAllocation = true;
- } else {
- mUsingPreviewAllocation = false;
- }
- setPreviewCallbackSurface(previewSurface);
- }
-
private native final void setPreviewCallbackSurface(Surface s);
private class EventHandler extends Handler
diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java
index 0e45787c1340..082a3361be4e 100644
--- a/core/java/android/hardware/camera2/CameraDevice.java
+++ b/core/java/android/hardware/camera2/CameraDevice.java
@@ -541,14 +541,6 @@ public abstract class CameraDevice implements AutoCloseable {
* or configuring it to use one of the supported
* {@link android.media.CamcorderProfile CamcorderProfiles}.</li>
*
- * <li>For efficient YUV processing with {@link android.renderscript}:
- * Create a RenderScript
- * {@link android.renderscript.Allocation Allocation} with a supported YUV
- * type, the IO_INPUT flag, and one of the sizes returned by
- * {@link StreamConfigurationMap#getOutputSizes(Class) getOutputSizes(Allocation.class)},
- * Then obtain the Surface with
- * {@link android.renderscript.Allocation#getSurface}.</li>
- *
* <li>For access to RAW, uncompressed YUV, or compressed JPEG data in the application: Create an
* {@link android.media.ImageReader} object with one of the supported output formats given by
* {@link StreamConfigurationMap#getOutputFormats()}, setting its size to one of the
diff --git a/core/java/android/hardware/camera2/params/StreamConfigurationMap.java b/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
index ef0db7f8a41c..b85d6869a1ff 100644
--- a/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
+++ b/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
@@ -509,8 +509,6 @@ public final class StreamConfigurationMap {
* Recommended for recording video (simple to use)
* <li>{@link android.media.MediaCodec} -
* Recommended for recording video (more complicated to use, with more flexibility)
- * <li>{@link android.renderscript.Allocation} -
- * Recommended for image processing with {@link android.renderscript RenderScript}
* <li>{@link android.view.SurfaceHolder} -
* Recommended for low-power camera preview with {@link android.view.SurfaceView}
* <li>{@link android.graphics.SurfaceTexture} -
diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java
index af09a0662795..5b24dcacbf53 100644
--- a/core/java/android/os/FileUtils.java
+++ b/core/java/android/os/FileUtils.java
@@ -1294,32 +1294,30 @@ public final class FileUtils {
* Round the given size of a storage device to a nice round power-of-two
* value, such as 256MB or 32GB. This avoids showing weird values like
* "29.5GB" in UI.
- *
- * Some storage devices are still using GiB (powers of 1024) over
- * GB (powers of 1000) measurements and this method takes it into account.
- *
* Round ranges:
* ...
- * [256 GiB + 1; 512 GiB] -> 512 GB
- * [512 GiB + 1; 1 TiB] -> 1 TB
- * [1 TiB + 1; 2 TiB] -> 2 TB
+ * (128 GB; 256 GB] -> 256 GB
+ * (256 GB; 512 GB] -> 512 GB
+ * (512 GB; 1000 GB] -> 1000 GB
+ * (1000 GB; 2000 GB] -> 2000 GB
+ * ...
* etc
*
* @hide
*/
public static long roundStorageSize(long size) {
long val = 1;
- long kiloPow = 1;
- long kibiPow = 1;
- while ((val * kibiPow) < size) {
+ long pow = 1;
+ while ((val * pow) < size) {
val <<= 1;
if (val > 512) {
val = 1;
- kibiPow *= 1024;
- kiloPow *= 1000;
+ pow *= 1000;
}
}
- return val * kiloPow;
+
+ Log.d(TAG, String.format("Rounded bytes from %d to %d", size, val * pow));
+ return val * pow;
}
private static long toBytes(long value, String unit) {
diff --git a/core/java/android/os/storage/IStorageManager.aidl b/core/java/android/os/storage/IStorageManager.aidl
index bc52744078ea..369a1932e437 100644
--- a/core/java/android/os/storage/IStorageManager.aidl
+++ b/core/java/android/os/storage/IStorageManager.aidl
@@ -174,4 +174,5 @@ interface IStorageManager {
boolean isAppIoBlocked(in String volumeUuid, int uid, int tid, int reason) = 95;
void setCloudMediaProvider(in String authority) = 96;
String getCloudMediaProvider() = 97;
+ long getInternalStorageBlockDeviceSize() = 98;
} \ No newline at end of file
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 80dd48825ba7..ee387e7c284f 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -1359,6 +1359,15 @@ public class StorageManager {
}
/** {@hide} */
+ public long getInternalStorageBlockDeviceSize() {
+ try {
+ return mStorageManager.getInternalStorageBlockDeviceSize();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /** {@hide} */
public void mkdirs(File file) {
BlockGuard.getVmPolicy().onPathAccess(file.getAbsolutePath());
try {
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 2791c412f5c7..b9a658344e7d 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -4579,6 +4579,16 @@ public final class Settings {
public static final String WEAR_ACCESSIBILITY_GESTURE_ENABLED_DURING_OOBE =
"wear_accessibility_gesture_enabled_during_oobe";
+
+ /**
+ * If the text-to-speech pre-warm is enabled.
+ * Set to 1 for true and 0 for false.
+ *
+ * This setting is used only internally.
+ * @hide
+ */
+ public static final String WEAR_TTS_PREWARM_ENABLED = "wear_tts_prewarm_enabled";
+
/**
* @deprecated Use {@link android.provider.Settings.Global#AIRPLANE_MODE_ON} instead
*/
@@ -6017,6 +6027,7 @@ public final class Settings {
PRIVATE_SETTINGS.add(ADVANCED_SETTINGS);
PRIVATE_SETTINGS.add(WEAR_ACCESSIBILITY_GESTURE_ENABLED);
PRIVATE_SETTINGS.add(WEAR_ACCESSIBILITY_GESTURE_ENABLED_DURING_OOBE);
+ PRIVATE_SETTINGS.add(WEAR_TTS_PREWARM_ENABLED);
PRIVATE_SETTINGS.add(SCREEN_AUTO_BRIGHTNESS_ADJ);
PRIVATE_SETTINGS.add(VIBRATE_INPUT_DEVICES);
PRIVATE_SETTINGS.add(VOLUME_MASTER);
diff --git a/core/java/android/security/OWNERS b/core/java/android/security/OWNERS
index 4e8d6e70091c..22b1f026e2ae 100644
--- a/core/java/android/security/OWNERS
+++ b/core/java/android/security/OWNERS
@@ -1,9 +1,9 @@
# Bug component: 36824
-cbrubaker@google.com
+brambonne@google.com
+brufino@google.com
+jeffv@google.com
-per-file NetworkSecurityPolicy.java = cbrubaker@google.com
-per-file NetworkSecurityPolicy.java = klyubin@google.com
-per-file FrameworkNetworkSecurityPolicy.java = cbrubaker@google.com
-per-file FrameworkNetworkSecurityPolicy.java = klyubin@google.com
+per-file *NetworkSecurityPolicy.java = file:net/OWNERS
per-file Confirmation*.java = file:/keystore/OWNERS
+per-file FileIntegrityManager.java = victorhsieh@google.com
diff --git a/core/java/android/security/net/OWNERS b/core/java/android/security/net/OWNERS
index d8281645738b..1d52eed0cb56 100644
--- a/core/java/android/security/net/OWNERS
+++ b/core/java/android/security/net/OWNERS
@@ -1,4 +1,4 @@
# Bug component: 36824
-cbrubaker@google.com
brambonne@google.com
+jeffv@google.com
diff --git a/core/java/android/util/IntArray.java b/core/java/android/util/IntArray.java
index 511cb2df712d..ac76fc2eb469 100644
--- a/core/java/android/util/IntArray.java
+++ b/core/java/android/util/IntArray.java
@@ -212,6 +212,11 @@ public class IntArray implements Cloneable {
return -1;
}
+ /** Returns {@code true} if this array contains the specified value. */
+ public boolean contains(int value) {
+ return indexOf(value) != -1;
+ }
+
/**
* Removes the value at the specified index from this array.
*/
diff --git a/core/java/android/view/HandwritingInitiator.java b/core/java/android/view/HandwritingInitiator.java
index 6e73a3c93fd7..23afb03569ce 100644
--- a/core/java/android/view/HandwritingInitiator.java
+++ b/core/java/android/view/HandwritingInitiator.java
@@ -346,13 +346,13 @@ public class HandwritingInitiator {
}
private static boolean shouldTriggerStylusHandwritingForView(@NonNull View view) {
- if (!view.isAutoHandwritingEnabled()) {
+ if (!view.shouldInitiateHandwriting()) {
return false;
}
- // The view may be a handwriting initiation delegate, in which case it is not the editor
+ // The view may be a handwriting initiation delegator, in which case it is not the editor
// view for which handwriting would be started. However, in almost all cases, the return
- // values of View#isStylusHandwritingAvailable will be the same for the delegate view and
- // the delegator editor view. So the delegate view can be used to decide whether handwriting
+ // values of View#isStylusHandwritingAvailable will be the same for the delegator view and
+ // the delegate editor view. So the delegator view can be used to decide whether handwriting
// should be triggered.
return view.isStylusHandwritingAvailable();
}
@@ -677,7 +677,7 @@ public class HandwritingInitiator {
/** The helper method to check if the given view is still active for handwriting. */
private static boolean isViewActive(@Nullable View view) {
return view != null && view.isAttachedToWindow() && view.isAggregatedVisible()
- && view.isAutoHandwritingEnabled();
+ && view.shouldInitiateHandwriting();
}
/**
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 5e19c675bb88..87036099239d 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -5488,7 +5488,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
(TEXT_ALIGNMENT_DEFAULT << PFLAG2_TEXT_ALIGNMENT_MASK_SHIFT) |
(PFLAG2_TEXT_ALIGNMENT_RESOLVED_DEFAULT) |
(IMPORTANT_FOR_ACCESSIBILITY_DEFAULT << PFLAG2_IMPORTANT_FOR_ACCESSIBILITY_SHIFT);
- mPrivateFlags4 = PFLAG4_AUTO_HANDWRITING_ENABLED;
final ViewConfiguration configuration = ViewConfiguration.get(context);
mTouchSlop = configuration.getScaledTouchSlop();
@@ -6213,7 +6212,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
setPreferKeepClear(a.getBoolean(attr, false));
break;
case R.styleable.View_autoHandwritingEnabled:
- setAutoHandwritingEnabled(a.getBoolean(attr, true));
+ setAutoHandwritingEnabled(a.getBoolean(attr, false));
break;
case R.styleable.View_handwritingBoundsOffsetLeft:
mHandwritingBoundsOffsetLeft = a.getDimension(attr, 0);
@@ -12150,7 +12149,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
if (getSystemGestureExclusionRects().isEmpty()
&& collectPreferKeepClearRects().isEmpty()
&& collectUnrestrictedPreferKeepClearRects().isEmpty()
- && (info.mHandwritingArea == null || !isAutoHandwritingEnabled())) {
+ && (info.mHandwritingArea == null || !shouldInitiateHandwriting())) {
if (info.mPositionUpdateListener != null) {
mRenderNode.removePositionUpdateListener(info.mPositionUpdateListener);
info.mPositionUpdateListener = null;
@@ -12517,7 +12516,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
void updateHandwritingArea() {
// If autoHandwritingArea is not enabled, do nothing.
- if (!isAutoHandwritingEnabled()) return;
+ if (!shouldInitiateHandwriting()) return;
final AttachInfo ai = mAttachInfo;
if (ai != null) {
ai.mViewRootImpl.getHandwritingInitiator().updateHandwritingAreasForView(this);
@@ -12525,6 +12524,16 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
/**
+ * Returns true if a stylus {@link MotionEvent} within this view's bounds should initiate
+ * handwriting mode, either for this view ({@link #isAutoHandwritingEnabled()} is {@code true})
+ * or for a handwriting delegate view ({@link #getHandwritingDelegatorCallback()} is not {@code
+ * null}).
+ */
+ boolean shouldInitiateHandwriting() {
+ return isAutoHandwritingEnabled() || getHandwritingDelegatorCallback() != null;
+ }
+
+ /**
* Sets a callback which should be called when a stylus {@link MotionEvent} occurs within this
* view's bounds. The callback will be called from the UI thread.
*
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index e1de05bda9df..79acfbb0b39c 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -801,6 +801,11 @@ public class RemoteViews implements Parcelable, Filter {
mActions.set(i, new SetRemoteCollectionItemListAdapterAction(itemsAction.viewId,
itemsAction.mServiceIntent));
isActionReplaced = true;
+ } else if (action instanceof SetRemoteViewsAdapterIntent intentAction
+ && intentAction.viewId == viewId) {
+ mActions.set(i, new SetRemoteCollectionItemListAdapterAction(
+ intentAction.viewId, intentAction.intent));
+ isActionReplaced = true;
} else if (action instanceof ViewGroupActionAdd groupAction
&& groupAction.mNestedViews != null) {
isActionReplaced |= groupAction.mNestedViews.replaceRemoteCollections(viewId);
@@ -822,6 +827,42 @@ public class RemoteViews implements Parcelable, Filter {
return isActionReplaced;
}
+ /**
+ * @return True if has set remote adapter using service intent
+ * @hide
+ */
+ public boolean hasLegacyLists() {
+ if (mActions != null) {
+ for (int i = 0; i < mActions.size(); i++) {
+ Action action = mActions.get(i);
+ if ((action instanceof SetRemoteCollectionItemListAdapterAction itemsAction
+ && itemsAction.mServiceIntent != null)
+ || (action instanceof SetRemoteViewsAdapterIntent intentAction
+ && intentAction.intent != null)
+ || (action instanceof ViewGroupActionAdd groupAction
+ && groupAction.mNestedViews != null
+ && groupAction.mNestedViews.hasLegacyLists())) {
+ return true;
+ }
+ }
+ }
+ if (mSizedRemoteViews != null) {
+ for (int i = 0; i < mSizedRemoteViews.size(); i++) {
+ if (mSizedRemoteViews.get(i).hasLegacyLists()) {
+ return true;
+ }
+ }
+ }
+ if (mLandscape != null && mLandscape.hasLegacyLists()) {
+ return true;
+ }
+ if (mPortrait != null && mPortrait.hasLegacyLists()) {
+ return true;
+ }
+
+ return false;
+ }
+
private static void visitIconUri(Icon icon, @NonNull Consumer<Uri> visitor) {
if (icon != null && (icon.getType() == Icon.TYPE_URI
|| icon.getType() == Icon.TYPE_URI_ADAPTIVE_BITMAP)) {
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 8899f5cd91d4..0001fe6164a3 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -1856,6 +1856,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
boolean clickable = canInputOrMove || isClickable();
boolean longClickable = canInputOrMove || isLongClickable();
int focusable = getFocusable();
+ boolean isAutoHandwritingEnabled = true;
n = a.getIndexCount();
for (int i = 0; i < n; i++) {
@@ -1878,6 +1879,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
case com.android.internal.R.styleable.View_longClickable:
longClickable = a.getBoolean(attr, longClickable);
break;
+
+ case com.android.internal.R.styleable.View_autoHandwritingEnabled:
+ isAutoHandwritingEnabled = a.getBoolean(attr, true);
+ break;
}
}
a.recycle();
@@ -1891,6 +1896,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
setClickable(clickable);
setLongClickable(longClickable);
+ setAutoHandwritingEnabled(isAutoHandwritingEnabled);
if (mEditor != null) mEditor.prepareCursorControllers();
diff --git a/core/java/android/window/flags/windowing_sdk.aconfig b/core/java/android/window/flags/windowing_sdk.aconfig
new file mode 100644
index 000000000000..560e41b1aa33
--- /dev/null
+++ b/core/java/android/window/flags/windowing_sdk.aconfig
@@ -0,0 +1,10 @@
+package: "com.android.window.flags"
+
+# Project link: https://gantry.corp.google.com/projects/android_platform_windowing_sdk/changes
+
+flag {
+ namespace: "windowing_sdk"
+ name: "sync_window_config_update_flag"
+ description: "Whether the feature to sync different window-related config updates is enabled"
+ bug: "260873529"
+}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index f55501ab1a8f..ffd640fe36ea 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2267,6 +2267,14 @@
<permission android:name="android.permission.MANAGE_ETHERNET_NETWORKS"
android:protectionLevel="signature" />
+ <!-- Allows system apps to call methods to register itself as a mDNS offload engine.
+ <p>Not for use by third-party or privileged applications.
+ @SystemApi
+ @hide This should only be used by system apps.
+ -->
+ <permission android:name="android.permission.REGISTER_NSD_OFFLOAD_ENGINE"
+ android:protectionLevel="signature" />
+
<!-- ======================================= -->
<!-- Permissions for short range, peripheral networks -->
<!-- ======================================= -->
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index aeeaacaacf11..281053b6ce7b 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1210,13 +1210,16 @@
<integer name="config_triplePressOnStemPrimaryBehavior">0</integer>
<!-- Control the behavior when the user short presses the stem primary button.
- Stem primary button is only used on watch form factor. If a device is not
- a watch, setting this config is no-op.
- 0 - Nothing
- 1 - Go to launch all apps
+ Stem primary button is only used on watch form factor. If a device is not
+ a watch, setting this config is no-op.
+ 0 - Nothing
+ 1 - Go to launch all apps
+ 2 - Launch target activity defined by config_primaryShortPressTargetActivity if available
-->
<integer name="config_shortPressOnStemPrimaryBehavior">0</integer>
+ <!-- Activity name for the default target activity to be launched. [DO NOT TRANSLATE] -->
+ <string name="config_primaryShortPressTargetActivity" translatable="false"></string>
<!-- Control the behavior of the search key.
0 - Launch default search activity
@@ -5356,6 +5359,7 @@
<item>1,1,1.0,0,1</item>
<item>1,1,1.0,.4,1</item>
<item>1,1,1.0,.15,15</item>
+ <item>0,0,0.7,0,1</item>
</string-array>
<!-- The integer index of the selected option in config_udfps_touch_detection_options -->
diff --git a/core/res/res/values/config_telephony.xml b/core/res/res/values/config_telephony.xml
index bda194add759..6bb87f3c4e99 100644
--- a/core/res/res/values/config_telephony.xml
+++ b/core/res/res/values/config_telephony.xml
@@ -73,7 +73,7 @@
CarrierConfigManager#KEY_AUTO_DATA_SWITCH_RAT_SIGNAL_SCORE_STRING_ARRAY.
If 0, the device always switch to the higher score SIM.
If < 0, the network type and signal strength based auto switch is disabled. -->
- <integer name="auto_data_switch_score_tolerance">3000</integer>
+ <integer name="auto_data_switch_score_tolerance">-1</integer>
<java-symbol type="integer" name="auto_data_switch_score_tolerance" />
<!-- Boolean indicating whether the Iwlan data service supports persistence of iwlan ipsec
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 8330f7bdc251..aa0cbfa92043 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -466,6 +466,7 @@
<java-symbol type="integer" name="config_shortPressOnSleepBehavior" />
<java-symbol type="integer" name="config_longPressOnStemPrimaryBehavior" />
<java-symbol type="integer" name="config_shortPressOnStemPrimaryBehavior" />
+ <java-symbol type="string" name="config_primaryShortPressTargetActivity" />
<java-symbol type="integer" name="config_doublePressOnStemPrimaryBehavior" />
<java-symbol type="integer" name="config_triplePressOnStemPrimaryBehavior" />
<java-symbol type="string" name="config_doublePressOnPowerTargetActivity" />
diff --git a/core/tests/coretests/res/values-id/strings.xml b/core/tests/coretests/res/values-id/strings.xml
new file mode 100644
index 000000000000..6d71c90866cc
--- /dev/null
+++ b/core/tests/coretests/res/values-id/strings.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <!-- Used in ResourcesLocaleTest. -->
+ <string name="locale_test_res_1">Pengujian ID</string>
+</resources>
diff --git a/core/tests/coretests/res/values-in/strings.xml b/core/tests/coretests/res/values-in/strings.xml
new file mode 100644
index 000000000000..63846603ed60
--- /dev/null
+++ b/core/tests/coretests/res/values-in/strings.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <!-- Used in ResourcesLocaleTest. -->
+ <string name="locale_test_res_2">Pengujian IN</string>
+</resources>
diff --git a/core/tests/coretests/res/values/strings.xml b/core/tests/coretests/res/values/strings.xml
index e51eab60a998..09e1c690f4e2 100644
--- a/core/tests/coretests/res/values/strings.xml
+++ b/core/tests/coretests/res/values/strings.xml
@@ -131,6 +131,13 @@
<string name="textview_hebrew_text">&#x05DD;&#x05DE;ab?!</string>
+ <!-- Used in ResourcesLocaleTest. Also defined in values-id. "id" is the new ISO code for Indonesian. -->
+ <string name="locale_test_res_1">Testing ID</string>
+ <!-- Used in ResourcesLocaleTest. Also defined in values-in. "in" is the deprecated ISO code for Indonesian. -->
+ <string name="locale_test_res_2">Testing IN</string>
+ <!-- Used in ResourcesLocaleTest. -->
+ <string name="locale_test_res_3">Testing EN</string>
+
<!-- SizeAdaptiveLayout -->
<string name="first">Four score and seven years ago our fathers brought forth on this continent, a new nation, conceived in Liberty, and dedicated to the proposition that all men are created equal.</string>
<string name="actor">Abe Lincoln</string>
diff --git a/core/tests/coretests/src/android/content/res/ResourcesLocaleTest.java b/core/tests/coretests/src/android/content/res/ResourcesLocaleTest.java
index 25c3db5c6910..26e4349243a5 100644
--- a/core/tests/coretests/src/android/content/res/ResourcesLocaleTest.java
+++ b/core/tests/coretests/src/android/content/res/ResourcesLocaleTest.java
@@ -16,6 +16,7 @@
package android.content.res;
+import android.content.Context;
import android.os.FileUtils;
import android.os.LocaleList;
import android.platform.test.annotations.Presubmit;
@@ -97,4 +98,24 @@ public class ResourcesLocaleTest extends AndroidTestCase {
assertEquals(Locale.forLanguageTag("pl-PL"),
resources.getConfiguration().getLocales().get(0));
}
+
+ @SmallTest
+ public void testDeprecatedISOLanguageCode() {
+ assertResGetString(Locale.US, R.string.locale_test_res_1, "Testing ID");
+ assertResGetString(Locale.forLanguageTag("id"), R.string.locale_test_res_2, "Pengujian IN");
+ assertResGetString(Locale.forLanguageTag("id"), R.string.locale_test_res_3, "Testing EN");
+ assertResGetString(new Locale("id"), R.string.locale_test_res_2, "Pengujian IN");
+ assertResGetString(new Locale("id"), R.string.locale_test_res_3, "Testing EN");
+ // The new ISO code "id" isn't supported yet, and thus the values-id are ignored.
+ assertResGetString(new Locale("id"), R.string.locale_test_res_1, "Testing ID");
+ assertResGetString(Locale.forLanguageTag("id"), R.string.locale_test_res_1, "Testing ID");
+ }
+
+ private void assertResGetString(Locale locale, int resId, String expectedString) {
+ LocaleList locales = new LocaleList(locale);
+ final Configuration config = new Configuration();
+ config.setLocales(locales);
+ Context newContext = getContext().createConfigurationContext(config);
+ assertEquals(expectedString, newContext.getResources().getString(resId));
+ }
}
diff --git a/core/tests/coretests/src/android/os/FileUtilsTest.java b/core/tests/coretests/src/android/os/FileUtilsTest.java
index 394ff0ae9a2e..a0d8183b8da7 100644
--- a/core/tests/coretests/src/android/os/FileUtilsTest.java
+++ b/core/tests/coretests/src/android/os/FileUtilsTest.java
@@ -505,45 +505,32 @@ public class FileUtilsTest {
@Test
public void testRoundStorageSize() throws Exception {
- final long GB1 = DataUnit.GIGABYTES.toBytes(1);
- final long GiB1 = DataUnit.GIBIBYTES.toBytes(1);
- final long GB2 = DataUnit.GIGABYTES.toBytes(2);
- final long GiB2 = DataUnit.GIBIBYTES.toBytes(2);
- final long GiB128 = DataUnit.GIBIBYTES.toBytes(128);
- final long GB256 = DataUnit.GIGABYTES.toBytes(256);
- final long GiB256 = DataUnit.GIBIBYTES.toBytes(256);
- final long GB512 = DataUnit.GIGABYTES.toBytes(512);
- final long GiB512 = DataUnit.GIBIBYTES.toBytes(512);
- final long TB1 = DataUnit.TERABYTES.toBytes(1);
- final long TiB1 = DataUnit.TEBIBYTES.toBytes(1);
- final long TB2 = DataUnit.TERABYTES.toBytes(2);
- final long TiB2 = DataUnit.TEBIBYTES.toBytes(2);
- final long TB4 = DataUnit.TERABYTES.toBytes(4);
- final long TiB4 = DataUnit.TEBIBYTES.toBytes(4);
- final long TB8 = DataUnit.TERABYTES.toBytes(8);
- final long TiB8 = DataUnit.TEBIBYTES.toBytes(8);
-
- assertEquals(GB1, roundStorageSize(GB1 - 1));
- assertEquals(GB1, roundStorageSize(GB1));
- assertEquals(GB1, roundStorageSize(GB1 + 1));
- assertEquals(GB1, roundStorageSize(GiB1 - 1));
- assertEquals(GB1, roundStorageSize(GiB1));
- assertEquals(GB2, roundStorageSize(GiB1 + 1));
- assertEquals(GB2, roundStorageSize(GiB2));
-
- assertEquals(GB256, roundStorageSize(GiB128 + 1));
- assertEquals(GB256, roundStorageSize(GiB256));
- assertEquals(GB512, roundStorageSize(GiB256 + 1));
- assertEquals(GB512, roundStorageSize(GiB512));
- assertEquals(TB1, roundStorageSize(GiB512 + 1));
- assertEquals(TB1, roundStorageSize(TiB1));
- assertEquals(TB2, roundStorageSize(TiB1 + 1));
- assertEquals(TB2, roundStorageSize(TiB2));
- assertEquals(TB4, roundStorageSize(TiB2 + 1));
- assertEquals(TB4, roundStorageSize(TiB4));
- assertEquals(TB8, roundStorageSize(TiB4 + 1));
- assertEquals(TB8, roundStorageSize(TiB8));
- assertEquals(TB1, roundStorageSize(1013077688320L)); // b/268571529
+ final long M256 = DataUnit.MEGABYTES.toBytes(256);
+ final long M512 = DataUnit.MEGABYTES.toBytes(512);
+ final long G1 = DataUnit.GIGABYTES.toBytes(1);
+ final long G2 = DataUnit.GIGABYTES.toBytes(2);
+ final long G32 = DataUnit.GIGABYTES.toBytes(32);
+ final long G64 = DataUnit.GIGABYTES.toBytes(64);
+ final long G512 = DataUnit.GIGABYTES.toBytes(512);
+ final long G1000 = DataUnit.TERABYTES.toBytes(1);
+ final long G2000 = DataUnit.TERABYTES.toBytes(2);
+
+ assertEquals(M256, roundStorageSize(M256 - 1));
+ assertEquals(M256, roundStorageSize(M256));
+ assertEquals(M512, roundStorageSize(M256 + 1));
+ assertEquals(M512, roundStorageSize(M512 - 1));
+ assertEquals(M512, roundStorageSize(M512));
+ assertEquals(G1, roundStorageSize(M512 + 1));
+ assertEquals(G1, roundStorageSize(G1));
+ assertEquals(G2, roundStorageSize(G1 + 1));
+
+ assertEquals(G32, roundStorageSize(G32 - 1));
+ assertEquals(G32, roundStorageSize(G32));
+ assertEquals(G64, roundStorageSize(G32 + 1));
+
+ assertEquals(G512, roundStorageSize(G512 - 1));
+ assertEquals(G1000, roundStorageSize(G512 + 1));
+ assertEquals(G2000, roundStorageSize(G1000 + 1));
}
@Test
diff --git a/core/tests/coretests/src/android/service/TEST_MAPPING b/core/tests/coretests/src/android/service/TEST_MAPPING
new file mode 100644
index 000000000000..fbf8a92e031d
--- /dev/null
+++ b/core/tests/coretests/src/android/service/TEST_MAPPING
@@ -0,0 +1,18 @@
+{
+ "postsubmit": [
+ {
+ "name": "FrameworksCoreTests",
+ "options": [
+ {"include-filter": "android.service.controls"},
+ {"include-filter": "android.service.controls.actions"},
+ {"include-filter": "android.service.controls.templates"},
+ {"include-filter": "android.service.euicc"},
+ {"include-filter": "android.service.notification"},
+ {"include-filter": "android.service.quicksettings"},
+ {"include-filter": "android.service.settings.suggestions"},
+ {"include-filter": "android.service.timezone"},
+ {"exclude-annotation": "org.junit.Ignore"}
+ ]
+ }
+ ]
+}
diff --git a/core/tests/coretests/src/android/service/euicc/EuiccProfileInfoTest.java b/core/tests/coretests/src/android/service/euicc/EuiccProfileInfoTest.java
index 0af8c728aba3..6792d0b91084 100644
--- a/core/tests/coretests/src/android/service/euicc/EuiccProfileInfoTest.java
+++ b/core/tests/coretests/src/android/service/euicc/EuiccProfileInfoTest.java
@@ -239,7 +239,7 @@ public class EuiccProfileInfoTest {
assertNotEquals(p.hashCode(), t.hashCode());
}
- @Test(expected = IllegalStateException.class)
+ @Test(expected = IllegalArgumentException.class)
public void testBuilderBuild_IllegalIccid() {
new EuiccProfileInfo.Builder("abc").build();
}
diff --git a/core/tests/coretests/src/android/service/euicc/OWNERS b/core/tests/coretests/src/android/service/euicc/OWNERS
new file mode 100644
index 000000000000..41fc56b45dde
--- /dev/null
+++ b/core/tests/coretests/src/android/service/euicc/OWNERS
@@ -0,0 +1 @@
+include platform/frameworks/base:/telephony/java/android/service/euicc/OWNERS
diff --git a/core/tests/coretests/src/android/view/stylus/HandwritingInitiatorTest.java b/core/tests/coretests/src/android/view/stylus/HandwritingInitiatorTest.java
index f1eef7551dd5..c46118db617f 100644
--- a/core/tests/coretests/src/android/view/stylus/HandwritingInitiatorTest.java
+++ b/core/tests/coretests/src/android/view/stylus/HandwritingInitiatorTest.java
@@ -245,7 +245,7 @@ public class HandwritingInitiatorTest {
@Test
public void onTouchEvent_tryAcceptDelegation_delegatorCallbackCreatesInputConnection() {
- View delegateView = new View(mContext);
+ View delegateView = new EditText(mContext);
delegateView.setIsHandwritingDelegate(true);
mTestView1.setHandwritingDelegatorCallback(
@@ -266,7 +266,7 @@ public class HandwritingInitiatorTest {
@Test
public void onTouchEvent_tryAcceptDelegation_delegatorCallbackFocusesDelegate() {
- View delegateView = new View(mContext);
+ View delegateView = new EditText(mContext);
delegateView.setIsHandwritingDelegate(true);
mHandwritingInitiator.onInputConnectionCreated(delegateView);
reset(mHandwritingInitiator);
diff --git a/core/tests/coretests/src/android/window/flags/WindowFlagsTest.java b/core/tests/coretests/src/android/window/flags/WindowFlagsTest.java
new file mode 100644
index 000000000000..a8b40325a713
--- /dev/null
+++ b/core/tests/coretests/src/android/window/flags/WindowFlagsTest.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.window.flags;
+
+import static com.android.window.flags.Flags.syncWindowConfigUpdateFlag;
+
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests for {@link com.android.window.flags.Flags}
+ *
+ * Build/Install/Run:
+ * atest FrameworksCoreTests:WindowFlagsTest
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+@Presubmit
+public class WindowFlagsTest {
+
+ @Test
+ public void testSyncWindowConfigUpdateFlag() {
+ // No crash when accessing the flag.
+ syncWindowConfigUpdateFlag();
+ }
+}
diff --git a/core/tests/utiltests/src/android/util/IntArrayTest.java b/core/tests/utiltests/src/android/util/IntArrayTest.java
index caa7312a4475..ad5c4ee83e54 100644
--- a/core/tests/utiltests/src/android/util/IntArrayTest.java
+++ b/core/tests/utiltests/src/android/util/IntArrayTest.java
@@ -55,9 +55,11 @@ public class IntArrayTest {
a.add(5, 20);
assertThat(a.get(5)).isEqualTo(20);
assertThat(a.indexOf(20)).isEqualTo(5);
+ assertThat(a.contains(20)).isTrue();
verify(a, 1, 2, 0, 0, 0, 20, 10, 0, 0);
assertThat(a.indexOf(99)).isEqualTo(-1);
+ assertThat(a.contains(99)).isFalse();
a.resize(15);
a.set(14, 30);
@@ -71,6 +73,7 @@ public class IntArrayTest {
backingArray[2] = 30;
verify(a, backingArray);
assertThat(a.indexOf(30)).isEqualTo(2);
+ assertThat(a.contains(30)).isTrue();
a.resize(2);
assertThat(backingArray[2]).isEqualTo(0);
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
index 81384ca35d52..a5ee19e2d068 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -261,10 +261,6 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
// Updates the Split
final TransactionRecord transactionRecord = mTransactionManager.startNewTransaction();
final WindowContainerTransaction wct = transactionRecord.getTransaction();
-
- mPresenter.setTaskFragmentIsolatedNavigation(wct,
- splitPinContainer.getSecondaryContainer().getTaskFragmentToken(),
- true /* isolatedNav */);
mPresenter.updateSplitContainer(splitPinContainer, wct);
transactionRecord.apply(false /* shouldApplyIndependently */);
updateCallbackIfNecessary();
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
index 5de6acfcc9db..896fe61b5611 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
@@ -382,6 +382,19 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
}
setCompanionTaskFragment(wct, primaryContainer.getTaskFragmentToken(),
secondaryContainer.getTaskFragmentToken(), splitRule, isStacked);
+
+ // Setting isolated navigation and clear non-sticky pinned container if needed.
+ final SplitPinRule splitPinRule =
+ splitRule instanceof SplitPinRule ? (SplitPinRule) splitRule : null;
+ if (splitPinRule == null) {
+ return;
+ }
+
+ setTaskFragmentIsolatedNavigation(wct, secondaryContainer.getTaskFragmentToken(),
+ !isStacked /* isolatedNav */);
+ if (isStacked && !splitPinRule.isSticky()) {
+ secondaryContainer.getTaskContainer().removeSplitPinContainer();
+ }
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
index ff67110634ba..9a2b81243861 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
@@ -851,7 +851,10 @@ public class Bubble implements BubbleViewProvider {
return mAppIntent;
}
- boolean isAppBubble() {
+ /**
+ * Returns whether this bubble is from an app versus a notification.
+ */
+ public boolean isAppBubble() {
return mIsAppBubble;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleDebugConfig.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleDebugConfig.java
index 250e010f4d69..76662c47238f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleDebugConfig.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleDebugConfig.java
@@ -52,6 +52,11 @@ public class BubbleDebugConfig {
private static final boolean FORCE_SHOW_USER_EDUCATION = false;
private static final String FORCE_SHOW_USER_EDUCATION_SETTING =
"force_show_bubbles_user_education";
+ /**
+ * When set to true, bubbles user education flow never shows up.
+ */
+ private static final String FORCE_HIDE_USER_EDUCATION_SETTING =
+ "force_hide_bubbles_user_education";
/**
* @return whether we should force show user education for bubbles. Used for debugging & demos.
@@ -62,6 +67,14 @@ public class BubbleDebugConfig {
return FORCE_SHOW_USER_EDUCATION || forceShow;
}
+ /**
+ * @return whether we should never show user education for bubbles. Used in tests.
+ */
+ static boolean neverShowUserEducation(Context context) {
+ return Settings.Secure.getInt(context.getContentResolver(),
+ FORCE_HIDE_USER_EDUCATION_SETTING, 0) != 0;
+ }
+
static String formatBubblesString(List<Bubble> bubbles, BubbleViewProvider selected) {
StringBuilder sb = new StringBuilder();
for (Bubble bubble : bubbles) {
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 2c100653dae0..ea7053d8ee49 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
@@ -653,14 +653,38 @@ public class BubblePositioner {
}
/**
- * @return the stack position to use if we don't have a saved location or if user education
- * is being shown.
+ * Returns whether the {@link #getRestingPosition()} is equal to the default start position
+ * initialized for bubbles, if {@code true} this means the user hasn't moved the bubble
+ * from the initial start position (or they haven't received a bubble yet).
+ */
+ public boolean hasUserModifiedDefaultPosition() {
+ PointF defaultStart = getDefaultStartPosition();
+ return mRestingStackPosition != null
+ && !mRestingStackPosition.equals(defaultStart);
+ }
+
+ /**
+ * Returns the stack position to use if we don't have a saved location or if user education
+ * is being shown, for a normal bubble.
*/
public PointF getDefaultStartPosition() {
- // Start on the left if we're in LTR, right otherwise.
- final boolean startOnLeft =
- mContext.getResources().getConfiguration().getLayoutDirection()
- != LAYOUT_DIRECTION_RTL;
+ return getDefaultStartPosition(false /* isAppBubble */);
+ }
+
+ /**
+ * The stack position to use if we don't have a saved location or if user education
+ * is being shown.
+ *
+ * @param isAppBubble whether this start position is for an app bubble or not.
+ */
+ public PointF getDefaultStartPosition(boolean isAppBubble) {
+ final int layoutDirection = mContext.getResources().getConfiguration().getLayoutDirection();
+ // Normal bubbles start on the left if we're in LTR, right otherwise.
+ // TODO (b/294284894): update language around "app bubble" here
+ // App bubbles start on the right in RTL, left otherwise.
+ final boolean startOnLeft = isAppBubble
+ ? layoutDirection == LAYOUT_DIRECTION_RTL
+ : layoutDirection != LAYOUT_DIRECTION_RTL;
final RectF allowableStackPositionRegion = getAllowableStackPositionRegion(
1 /* default starts with 1 bubble */);
if (isLargeScreen()) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
index 8ae12c75b3ee..52c9bf8462ec 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
@@ -1284,6 +1284,12 @@ public class BubbleStackView extends FrameLayout
if (BubbleDebugConfig.DEBUG_USER_EDUCATION) {
Log.d(TAG, "Show manage edu: " + shouldShow);
}
+ if (shouldShow && BubbleDebugConfig.neverShowUserEducation(mContext)) {
+ if (BubbleDebugConfig.DEBUG_USER_EDUCATION) {
+ Log.d(TAG, "Want to show manage edu, but it is forced hidden");
+ }
+ return false;
+ }
return shouldShow;
}
@@ -1316,6 +1322,12 @@ public class BubbleStackView extends FrameLayout
if (BubbleDebugConfig.DEBUG_USER_EDUCATION) {
Log.d(TAG, "Show stack edu: " + shouldShow);
}
+ if (shouldShow && BubbleDebugConfig.neverShowUserEducation(mContext)) {
+ if (BubbleDebugConfig.DEBUG_USER_EDUCATION) {
+ Log.d(TAG, "Want to show stack edu, but it is forced hidden");
+ }
+ return false;
+ }
return shouldShow;
}
@@ -1763,13 +1775,26 @@ public class BubbleStackView extends FrameLayout
return;
}
+ if (firstBubble && bubble.isAppBubble() && !mPositioner.hasUserModifiedDefaultPosition()) {
+ // TODO (b/294284894): update language around "app bubble" here
+ // If it's an app bubble and we don't have a previous resting position, update the
+ // controllers to use the default position for the app bubble (it'd be different from
+ // the position initialized with the controllers originally).
+ PointF startPosition = mPositioner.getDefaultStartPosition(true /* isAppBubble */);
+ mStackOnLeftOrWillBe = mPositioner.isStackOnLeft(startPosition);
+ mStackAnimationController.setStackPosition(startPosition);
+ mExpandedAnimationController.setCollapsePoint(startPosition);
+ // Set the translation x so that this bubble will animate in from the same side they
+ // expand / collapse on.
+ bubble.getIconView().setTranslationX(startPosition.x);
+ } else if (firstBubble) {
+ mStackOnLeftOrWillBe = mStackAnimationController.isStackOnLeftSide();
+ }
+
mBubbleContainer.addView(bubble.getIconView(), 0,
new FrameLayout.LayoutParams(mPositioner.getBubbleSize(),
mPositioner.getBubbleSize()));
- if (firstBubble) {
- mStackOnLeftOrWillBe = mStackAnimationController.isStackOnLeftSide();
- }
// Set the dot position to the opposite of the side the stack is resting on, since the stack
// resting slightly off-screen would result in the dot also being off-screen.
bubble.getIconView().setDotBadgeOnLeft(!mStackOnLeftOrWillBe /* onLeft */);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java
index c20733af2ba5..4d7042bbb3d2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java
@@ -131,6 +131,16 @@ public class ExpandedAnimationController
private BubbleStackView mBubbleStackView;
+ /**
+ * Whether the individual bubble has been dragged out of the row of bubbles far enough to cause
+ * the rest of the bubbles to animate to fill the gap.
+ */
+ private boolean mBubbleDraggedOutEnough = false;
+
+ /** End action to run when the lead bubble's expansion animation completes. */
+ @Nullable
+ private Runnable mLeadBubbleEndAction;
+
public ExpandedAnimationController(BubblePositioner positioner,
Runnable onBubbleAnimatedOutAction, BubbleStackView stackView) {
mPositioner = positioner;
@@ -141,14 +151,12 @@ public class ExpandedAnimationController
}
/**
- * Whether the individual bubble has been dragged out of the row of bubbles far enough to cause
- * the rest of the bubbles to animate to fill the gap.
+ * Overrides the collapse location without actually collapsing the stack.
+ * @param point the new collapse location.
*/
- private boolean mBubbleDraggedOutEnough = false;
-
- /** End action to run when the lead bubble's expansion animation completes. */
- @Nullable
- private Runnable mLeadBubbleEndAction;
+ public void setCollapsePoint(PointF point) {
+ mCollapsePoint = point;
+ }
/**
* Animates expanding the bubbles into a row along the top of the screen, optionally running an
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java
index 4bb1ab4d0f54..aad268394305 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java
@@ -297,9 +297,6 @@ public class StackAnimationController extends
/** Whether the stack is on the left side of the screen. */
public boolean isStackOnLeftSide() {
- if (mLayout == null || !isStackPositionSet()) {
- return true; // Default to left, which is where it starts by default.
- }
return mPositioner.isStackOnLeft(mStackPosition);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubblePopupDrawable.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubblePopupDrawable.kt
index 8b5283d83683..1fd22d0a3505 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubblePopupDrawable.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubblePopupDrawable.kt
@@ -85,7 +85,7 @@ class BubblePopupDrawable(private val config: Config) : Drawable() {
canvas.drawPath(path, paint)
}
- override fun onBoundsChange(bounds: Rect?) {
+ override fun onBoundsChange(bounds: Rect) {
requestPathUpdate()
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipAppOpsListener.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipAppOpsListener.kt
index a141ff951684..4abb35c2a428 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipAppOpsListener.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipAppOpsListener.kt
@@ -19,7 +19,6 @@ import android.app.AppOpsManager
import android.content.Context
import android.content.pm.PackageManager
import com.android.wm.shell.common.ShellExecutor
-import com.android.wm.shell.pip.PipUtils
class PipAppOpsListener(
private val mContext: Context,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipMediaController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipMediaController.kt
index 2719cd29009e..427a555eee92 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipMediaController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipMediaController.kt
@@ -33,7 +33,6 @@ import android.os.Handler
import android.os.HandlerExecutor
import android.os.UserHandle
import com.android.wm.shell.R
-import com.android.wm.shell.pip.PipUtils
import java.util.function.Consumer
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipUtils.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipUtils.kt
new file mode 100644
index 000000000000..84feb03e6a40
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipUtils.kt
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.wm.shell.common.pip
+
+import android.app.ActivityTaskManager
+import android.app.RemoteAction
+import android.app.WindowConfiguration
+import android.content.ComponentName
+import android.content.Context
+import android.os.RemoteException
+import android.os.SystemProperties
+import android.util.DisplayMetrics
+import android.util.Log
+import android.util.Pair
+import android.util.TypedValue
+import android.window.TaskSnapshot
+import com.android.internal.protolog.common.ProtoLog
+import com.android.wm.shell.protolog.ShellProtoLogGroup
+import kotlin.math.abs
+
+/** A class that includes convenience methods. */
+object PipUtils {
+ private const val TAG = "PipUtils"
+
+ // Minimum difference between two floats (e.g. aspect ratios) to consider them not equal.
+ private const val EPSILON = 1e-7
+ private const val ENABLE_PIP2_IMPLEMENTATION = "persist.wm.debug.enable_pip2_implementation"
+
+ /**
+ * @return the ComponentName and user id of the top non-SystemUI activity in the pinned stack.
+ * The component name may be null if no such activity exists.
+ */
+ @JvmStatic
+ fun getTopPipActivity(context: Context): Pair<ComponentName?, Int> {
+ try {
+ val sysUiPackageName = context.packageName
+ val pinnedTaskInfo = ActivityTaskManager.getService().getRootTaskInfo(
+ WindowConfiguration.WINDOWING_MODE_PINNED,
+ WindowConfiguration.ACTIVITY_TYPE_UNDEFINED
+ )
+ if (pinnedTaskInfo?.childTaskIds != null && pinnedTaskInfo.childTaskIds.isNotEmpty()) {
+ for (i in pinnedTaskInfo.childTaskNames.indices.reversed()) {
+ val cn = ComponentName.unflattenFromString(
+ pinnedTaskInfo.childTaskNames[i]
+ )
+ if (cn != null && cn.packageName != sysUiPackageName) {
+ return Pair(cn, pinnedTaskInfo.childTaskUserIds[i])
+ }
+ }
+ }
+ } catch (e: RemoteException) {
+ ProtoLog.w(
+ ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: Unable to get pinned stack.", TAG
+ )
+ }
+ return Pair(null, 0)
+ }
+
+ /**
+ * @return the pixels for a given dp value.
+ */
+ @JvmStatic
+ fun dpToPx(dpValue: Float, dm: DisplayMetrics?): Int {
+ return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpValue, dm).toInt()
+ }
+
+ /**
+ * @return true if the aspect ratios differ
+ */
+ @JvmStatic
+ fun aspectRatioChanged(aspectRatio1: Float, aspectRatio2: Float): Boolean {
+ return abs(aspectRatio1 - aspectRatio2) > EPSILON
+ }
+
+ /**
+ * Checks whether title, description and intent match.
+ * Comparing icons would be good, but using equals causes false negatives
+ */
+ @JvmStatic
+ fun remoteActionsMatch(action1: RemoteAction?, action2: RemoteAction?): Boolean {
+ if (action1 === action2) return true
+ if (action1 == null || action2 == null) return false
+ return action1.isEnabled == action2.isEnabled &&
+ action1.shouldShowIcon() == action2.shouldShowIcon() &&
+ action1.title == action2.title &&
+ action1.contentDescription == action2.contentDescription &&
+ action1.actionIntent == action2.actionIntent
+ }
+
+ /**
+ * Returns true if the actions in the lists match each other according to
+ * [ ][PipUtils.remoteActionsMatch], including their position.
+ */
+ @JvmStatic
+ fun remoteActionsChanged(list1: List<RemoteAction?>?, list2: List<RemoteAction?>?): Boolean {
+ if (list1 == null && list2 == null) {
+ return false
+ }
+ if (list1 == null || list2 == null) {
+ return true
+ }
+ if (list1.size != list2.size) {
+ return true
+ }
+ for (i in list1.indices) {
+ if (!remoteActionsMatch(list1[i], list2[i])) {
+ return true
+ }
+ }
+ return false
+ }
+
+ /** @return [TaskSnapshot] for a given task id.
+ */
+ @JvmStatic
+ fun getTaskSnapshot(taskId: Int, isLowResolution: Boolean): TaskSnapshot? {
+ return if (taskId <= 0) null else try {
+ ActivityTaskManager.getService().getTaskSnapshot(
+ taskId, isLowResolution, false /* takeSnapshotIfNeeded */
+ )
+ } catch (e: RemoteException) {
+ Log.e(TAG, "Failed to get task snapshot, taskId=$taskId", e)
+ null
+ }
+ }
+
+ @JvmStatic
+ val isPip2ExperimentEnabled: Boolean
+ get() = SystemProperties.getBoolean(ENABLE_PIP2_IMPLEMENTATION, false)
+} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1Module.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1Module.java
index 24ef44a7c0f4..4e92ca113114 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1Module.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1Module.java
@@ -34,6 +34,7 @@ import com.android.wm.shell.common.pip.PhoneSizeSpecSource;
import com.android.wm.shell.common.pip.PipAppOpsListener;
import com.android.wm.shell.common.pip.PipMediaController;
import com.android.wm.shell.common.pip.PipUiEventLogger;
+import com.android.wm.shell.common.pip.PipUtils;
import com.android.wm.shell.common.pip.SizeSpecSource;
import com.android.wm.shell.dagger.WMShellBaseModule;
import com.android.wm.shell.dagger.WMSingleton;
@@ -50,7 +51,6 @@ import com.android.wm.shell.pip.PipTaskOrganizer;
import com.android.wm.shell.pip.PipTransition;
import com.android.wm.shell.pip.PipTransitionController;
import com.android.wm.shell.pip.PipTransitionState;
-import com.android.wm.shell.pip.PipUtils;
import com.android.wm.shell.pip.phone.PhonePipKeepClearAlgorithm;
import com.android.wm.shell.pip.phone.PhonePipMenuController;
import com.android.wm.shell.pip.phone.PipController;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/PipModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/PipModule.java
index 04032bb17fec..9c9364e17e0e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/PipModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/PipModule.java
@@ -18,9 +18,9 @@ package com.android.wm.shell.dagger.pip;
import android.annotation.Nullable;
+import com.android.wm.shell.common.pip.PipUtils;
import com.android.wm.shell.dagger.WMSingleton;
import com.android.wm.shell.pip.PipTransitionController;
-import com.android.wm.shell.pip.PipUtils;
import dagger.Module;
import dagger.Provides;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
index 633f627e8e71..b0f75c6a1e6d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
@@ -311,7 +311,7 @@ class DesktopTasksController(
)
val wct = WindowContainerTransaction()
wct.setWindowingMode(task.token, WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW)
- wct.setBounds(task.token, null)
+ wct.setBounds(task.token, Rect())
wct.setDensityDpi(task.token, getDefaultDensityDpi())
if (Transitions.ENABLE_SHELL_TRANSITIONS) {
transitions.startTransition(TRANSIT_CHANGE, wct, null /* handler */)
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 ac711eafe3ef..4fef672b2cd8 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
@@ -28,6 +28,7 @@ import android.util.Size;
import android.view.Gravity;
import com.android.wm.shell.R;
+import com.android.wm.shell.common.pip.PipUtils;
import com.android.wm.shell.common.pip.SizeSpecSource;
import java.io.PrintWriter;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipDisplayLayoutState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipDisplayLayoutState.java
index 456f85b52da4..4aa260b44646 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipDisplayLayoutState.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipDisplayLayoutState.java
@@ -16,7 +16,7 @@
package com.android.wm.shell.pip;
-import static com.android.wm.shell.pip.PipUtils.dpToPx;
+import static com.android.wm.shell.common.pip.PipUtils.dpToPx;
import android.content.Context;
import android.content.res.Resources;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index 296857b196cf..ed9ff1c169c6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -83,6 +83,7 @@ import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.annotations.ShellMainThread;
import com.android.wm.shell.common.pip.PipUiEventLogger;
+import com.android.wm.shell.common.pip.PipUtils;
import com.android.wm.shell.pip.phone.PipMotionHelper;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.splitscreen.SplitScreenController;
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 db7e2c0c529f..83e03dc850a1 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
@@ -64,6 +64,7 @@ import androidx.annotation.Nullable;
import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.R;
import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.common.pip.PipUtils;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.splitscreen.SplitScreenController;
import com.android.wm.shell.sysui.ShellInit;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
index 0f74f9e323a5..64bba672a5b5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
@@ -38,6 +38,7 @@ import android.window.WindowContainerTransaction;
import androidx.annotation.NonNull;
import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.common.pip.PipUtils;
import com.android.wm.shell.common.split.SplitScreenUtils;
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.transition.Transitions;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUtils.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUtils.java
deleted file mode 100644
index 3cd9848d5686..000000000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUtils.java
+++ /dev/null
@@ -1,145 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.pip;
-
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import static android.util.TypedValue.COMPLEX_UNIT_DIP;
-
-import android.annotation.Nullable;
-import android.app.ActivityTaskManager;
-import android.app.ActivityTaskManager.RootTaskInfo;
-import android.app.RemoteAction;
-import android.content.ComponentName;
-import android.content.Context;
-import android.os.RemoteException;
-import android.os.SystemProperties;
-import android.util.DisplayMetrics;
-import android.util.Log;
-import android.util.Pair;
-import android.util.TypedValue;
-import android.window.TaskSnapshot;
-
-import com.android.internal.protolog.common.ProtoLog;
-import com.android.wm.shell.protolog.ShellProtoLogGroup;
-
-import java.util.List;
-import java.util.Objects;
-
-/** A class that includes convenience methods. */
-public class PipUtils {
- private static final String TAG = "PipUtils";
-
- // Minimum difference between two floats (e.g. aspect ratios) to consider them not equal.
- private static final double EPSILON = 1e-7;
-
- private static final String ENABLE_PIP2_IMPLEMENTATION =
- "persist.wm.debug.enable_pip2_implementation";
-
- /**
- * @return the ComponentName and user id of the top non-SystemUI activity in the pinned stack.
- * The component name may be null if no such activity exists.
- */
- public static Pair<ComponentName, Integer> getTopPipActivity(Context context) {
- try {
- final String sysUiPackageName = context.getPackageName();
- final RootTaskInfo pinnedTaskInfo = ActivityTaskManager.getService().getRootTaskInfo(
- WINDOWING_MODE_PINNED, ACTIVITY_TYPE_UNDEFINED);
- if (pinnedTaskInfo != null && pinnedTaskInfo.childTaskIds != null
- && pinnedTaskInfo.childTaskIds.length > 0) {
- for (int i = pinnedTaskInfo.childTaskNames.length - 1; i >= 0; i--) {
- ComponentName cn = ComponentName.unflattenFromString(
- pinnedTaskInfo.childTaskNames[i]);
- if (cn != null && !cn.getPackageName().equals(sysUiPackageName)) {
- return new Pair<>(cn, pinnedTaskInfo.childTaskUserIds[i]);
- }
- }
- }
- } catch (RemoteException e) {
- ProtoLog.w(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: Unable to get pinned stack.", TAG);
- }
- return new Pair<>(null, 0);
- }
-
- /**
- * @return the pixels for a given dp value.
- */
- public static int dpToPx(float dpValue, DisplayMetrics dm) {
- return (int) TypedValue.applyDimension(COMPLEX_UNIT_DIP, dpValue, dm);
- }
-
- /**
- * @return true if the aspect ratios differ
- */
- public static boolean aspectRatioChanged(float aspectRatio1, float aspectRatio2) {
- return Math.abs(aspectRatio1 - aspectRatio2) > EPSILON;
- }
-
- /**
- * Checks whether title, description and intent match.
- * Comparing icons would be good, but using equals causes false negatives
- */
- public static boolean remoteActionsMatch(RemoteAction action1, RemoteAction action2) {
- if (action1 == action2) return true;
- if (action1 == null || action2 == null) return false;
- return action1.isEnabled() == action2.isEnabled()
- && action1.shouldShowIcon() == action2.shouldShowIcon()
- && Objects.equals(action1.getTitle(), action2.getTitle())
- && Objects.equals(action1.getContentDescription(), action2.getContentDescription())
- && Objects.equals(action1.getActionIntent(), action2.getActionIntent());
- }
-
- /**
- * Returns true if the actions in the lists match each other according to {@link
- * PipUtils#remoteActionsMatch(RemoteAction, RemoteAction)}, including their position.
- */
- public static boolean remoteActionsChanged(List<RemoteAction> list1, List<RemoteAction> list2) {
- if (list1 == null && list2 == null) {
- return false;
- }
- if (list1 == null || list2 == null) {
- return true;
- }
- if (list1.size() != list2.size()) {
- return true;
- }
- for (int i = 0; i < list1.size(); i++) {
- if (!remoteActionsMatch(list1.get(i), list2.get(i))) {
- return true;
- }
- }
- return false;
- }
-
- /** @return {@link TaskSnapshot} for a given task id. */
- @Nullable
- public static TaskSnapshot getTaskSnapshot(int taskId, boolean isLowResolution) {
- if (taskId <= 0) return null;
- try {
- return ActivityTaskManager.getService().getTaskSnapshot(
- taskId, isLowResolution, false /* takeSnapshotIfNeeded */);
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to get task snapshot, taskId=" + taskId, e);
- return null;
- }
- }
-
- public static boolean isPip2ExperimentEnabled() {
- return SystemProperties.getBoolean(ENABLE_PIP2_IMPLEMENTATION, false);
- }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
index 5c65d7839f07..ddea574c3c89 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
@@ -77,6 +77,7 @@ import com.android.wm.shell.common.TaskStackListenerCallback;
import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.common.pip.PipAppOpsListener;
import com.android.wm.shell.common.pip.PipMediaController;
+import com.android.wm.shell.common.pip.PipUtils;
import com.android.wm.shell.onehanded.OneHandedController;
import com.android.wm.shell.onehanded.OneHandedTransitionCallback;
import com.android.wm.shell.pip.IPip;
@@ -93,7 +94,6 @@ import com.android.wm.shell.pip.PipSnapAlgorithm;
import com.android.wm.shell.pip.PipTaskOrganizer;
import com.android.wm.shell.pip.PipTransitionController;
import com.android.wm.shell.pip.PipTransitionState;
-import com.android.wm.shell.pip.PipUtils;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.sysui.ConfigurationChangeListener;
import com.android.wm.shell.sysui.KeyguardChangeListener;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
index 837426ae3aab..fc34772f2fce 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
@@ -67,7 +67,7 @@ import com.android.wm.shell.R;
import com.android.wm.shell.animation.Interpolators;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.pip.PipUiEventLogger;
-import com.android.wm.shell.pip.PipUtils;
+import com.android.wm.shell.common.pip.PipUtils;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.splitscreen.SplitScreenController;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
index 6055fd9d9593..cf54a7163d04 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
@@ -51,13 +51,13 @@ import com.android.wm.shell.R;
import com.android.wm.shell.common.FloatingContentCoordinator;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.pip.PipUiEventLogger;
+import com.android.wm.shell.common.pip.PipUtils;
import com.android.wm.shell.common.pip.SizeSpecSource;
import com.android.wm.shell.pip.PipAnimationController;
import com.android.wm.shell.pip.PipBoundsAlgorithm;
import com.android.wm.shell.pip.PipBoundsState;
import com.android.wm.shell.pip.PipTaskOrganizer;
import com.android.wm.shell.pip.PipTransitionController;
-import com.android.wm.shell.pip.PipUtils;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.sysui.ShellInit;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipActionsProvider.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipActionsProvider.java
index f1606f6a8ca5..6b890c49b713 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipActionsProvider.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipActionsProvider.java
@@ -36,7 +36,7 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.R;
import com.android.wm.shell.common.pip.PipMediaController;
-import com.android.wm.shell.pip.PipUtils;
+import com.android.wm.shell.common.pip.PipUtils;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import java.util.ArrayList;
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 7c1563787a12..57439a59ccca 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
@@ -55,7 +55,7 @@ import com.android.internal.widget.LinearLayoutManager;
import com.android.internal.widget.RecyclerView;
import com.android.wm.shell.R;
import com.android.wm.shell.common.TvWindowMenuActionButton;
-import com.android.wm.shell.pip.PipUtils;
+import com.android.wm.shell.common.pip.PipUtils;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import java.util.List;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipNotificationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipNotificationController.java
index 39c7a4b220a7..1c94625ddde9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipNotificationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipNotificationController.java
@@ -37,8 +37,8 @@ import com.android.internal.protolog.common.ProtoLog;
import com.android.internal.util.ImageUtils;
import com.android.wm.shell.R;
import com.android.wm.shell.common.pip.PipMediaController;
+import com.android.wm.shell.common.pip.PipUtils;
import com.android.wm.shell.pip.PipParamsChangedForwarder;
-import com.android.wm.shell.pip.PipUtils;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import java.util.List;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTaskOrganizer.java
index dbec607c2ae9..672080407658 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTaskOrganizer.java
@@ -26,6 +26,7 @@ import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.pip.PipUiEventLogger;
+import com.android.wm.shell.common.pip.PipUtils;
import com.android.wm.shell.pip.PipAnimationController;
import com.android.wm.shell.pip.PipBoundsAlgorithm;
import com.android.wm.shell.pip.PipBoundsState;
@@ -36,7 +37,6 @@ import com.android.wm.shell.pip.PipSurfaceTransactionHelper;
import com.android.wm.shell.pip.PipTaskOrganizer;
import com.android.wm.shell.pip.PipTransitionController;
import com.android.wm.shell.pip.PipTransitionState;
-import com.android.wm.shell.pip.PipUtils;
import com.android.wm.shell.splitscreen.SplitScreenController;
import java.util.Objects;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/OWNERS b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/OWNERS
new file mode 100644
index 000000000000..ec09827fa4d1
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/OWNERS
@@ -0,0 +1,3 @@
+# WM shell sub-module pip owner
+hwwang@google.com
+mateuszc@google.com
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/BaseBubbleScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/BaseBubbleScreen.kt
index 97147a3e1672..bc095bbacc5a 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/BaseBubbleScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/BaseBubbleScreen.kt
@@ -31,6 +31,7 @@ import androidx.test.uiautomator.By
import androidx.test.uiautomator.UiObject2
import androidx.test.uiautomator.Until
import com.android.server.wm.flicker.helpers.LaunchBubbleHelper
+import com.android.server.wm.flicker.helpers.MultiWindowUtils
import com.android.wm.shell.flicker.BaseTest
import org.junit.runners.Parameterized
@@ -56,6 +57,9 @@ abstract class BaseBubbleScreen(flicker: LegacyFlickerTest) : BaseTest(flicker)
): FlickerBuilder.() -> Unit {
return {
setup {
+ MultiWindowUtils.executeShellCommand(
+ instrumentation,
+ "settings put secure force_hide_bubbles_user_education 1")
notifyManager.setBubblesAllowed(
testApp.packageName,
uid,
@@ -67,6 +71,9 @@ abstract class BaseBubbleScreen(flicker: LegacyFlickerTest) : BaseTest(flicker)
}
teardown {
+ MultiWindowUtils.executeShellCommand(
+ instrumentation,
+ "settings put secure force_hide_bubbles_user_education 0")
notifyManager.setBubblesAllowed(
testApp.packageName,
uid,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblePositionerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblePositionerTest.java
index 139724f709c7..58d9a6486ff2 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblePositionerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblePositionerTest.java
@@ -203,6 +203,60 @@ public class BubblePositionerTest extends ShellTestCase {
assertThat(restingPosition.y).isEqualTo(getDefaultYPosition());
}
+ /** Test that the default resting position on tablet is middle right. */
+ @Test
+ public void testGetDefaultPosition_appBubble_onTablet() {
+ new WindowManagerConfig().setLargeScreen().setUpConfig();
+ mPositioner.update();
+
+ RectF allowableStackRegion =
+ mPositioner.getAllowableStackPositionRegion(1 /* bubbleCount */);
+ PointF startPosition = mPositioner.getDefaultStartPosition(true /* isAppBubble */);
+
+ assertThat(startPosition.x).isEqualTo(allowableStackRegion.right);
+ assertThat(startPosition.y).isEqualTo(getDefaultYPosition());
+ }
+
+ @Test
+ public void testGetRestingPosition_appBubble_onTablet_RTL() {
+ new WindowManagerConfig().setLargeScreen().setLayoutDirection(
+ LAYOUT_DIRECTION_RTL).setUpConfig();
+ mPositioner.update();
+
+ RectF allowableStackRegion =
+ mPositioner.getAllowableStackPositionRegion(1 /* bubbleCount */);
+ PointF startPosition = mPositioner.getDefaultStartPosition(true /* isAppBubble */);
+
+ assertThat(startPosition.x).isEqualTo(allowableStackRegion.left);
+ assertThat(startPosition.y).isEqualTo(getDefaultYPosition());
+ }
+
+ @Test
+ public void testHasUserModifiedDefaultPosition_false() {
+ new WindowManagerConfig().setLargeScreen().setLayoutDirection(
+ LAYOUT_DIRECTION_RTL).setUpConfig();
+ mPositioner.update();
+
+ assertThat(mPositioner.hasUserModifiedDefaultPosition()).isFalse();
+
+ mPositioner.setRestingPosition(mPositioner.getDefaultStartPosition());
+
+ assertThat(mPositioner.hasUserModifiedDefaultPosition()).isFalse();
+ }
+
+ @Test
+ public void testHasUserModifiedDefaultPosition_true() {
+ new WindowManagerConfig().setLargeScreen().setLayoutDirection(
+ LAYOUT_DIRECTION_RTL).setUpConfig();
+ mPositioner.update();
+
+ assertThat(mPositioner.hasUserModifiedDefaultPosition()).isFalse();
+
+ mPositioner.setRestingPosition(new PointF(0, 100));
+
+ assertThat(mPositioner.hasUserModifiedDefaultPosition()).isTrue();
+ }
+
/**
* Calculates the Y position bubbles should be placed based on the config. Based on
* the calculations in {@link BubblePositioner#getDefaultStartPosition()} and
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
index 99a1ac663286..a57a7bf4c659 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
@@ -99,6 +99,7 @@ import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.TransactionPool;
+import com.android.wm.shell.recents.RecentTasksController;
import com.android.wm.shell.recents.RecentsTransitionHandler;
import com.android.wm.shell.sysui.ShellController;
import com.android.wm.shell.sysui.ShellInit;
@@ -1061,7 +1062,8 @@ public class ShellTransitionTests extends ShellTestCase {
mTransactionPool, createTestDisplayController(), mMainExecutor,
mMainHandler, mAnimExecutor);
final RecentsTransitionHandler recentsHandler =
- new RecentsTransitionHandler(shellInit, transitions, null);
+ new RecentsTransitionHandler(shellInit, transitions,
+ mock(RecentTasksController.class));
transitions.replaceDefaultHandlerForTest(mDefaultHandler);
shellInit.init();
diff --git a/location/Android.bp b/location/Android.bp
new file mode 100644
index 000000000000..46dca74e7e40
--- /dev/null
+++ b/location/Android.bp
@@ -0,0 +1,41 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+//location sources that will populate the new module
+filegroup {
+ name: "framework-location-nonupdatable-sources",
+ srcs: [
+ "placeholder_java/android/location/Placeholder.java",
+ ],
+}
+
+java_sdk_library {
+ name: "framework-location",
+ srcs: [
+ ":framework-location-nonupdatable-sources",
+ ],
+ defaults: ["framework-non-updatable-unbundled-defaults"],
+ permitted_packages: [
+ "android.location",
+ "com.android.internal.location",
+ ],
+ libs: [
+ "app-compat-annotations",
+ "unsupportedappusage", // for android.compat.annotation.UnsupportedAppUsage
+ ],
+ hidden_api_packages: [
+ "com.android.internal.location",
+ ],
+ aidl: {
+ include_dirs: [
+ "frameworks/base/location/java",
+ "frameworks/base/core/java",
+ ],
+ },
+}
diff --git a/location/api/current.txt b/location/api/current.txt
new file mode 100644
index 000000000000..d802177e249b
--- /dev/null
+++ b/location/api/current.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/location/api/module-lib-current.txt b/location/api/module-lib-current.txt
new file mode 100644
index 000000000000..d802177e249b
--- /dev/null
+++ b/location/api/module-lib-current.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/location/api/module-lib-removed.txt b/location/api/module-lib-removed.txt
new file mode 100644
index 000000000000..d802177e249b
--- /dev/null
+++ b/location/api/module-lib-removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/location/api/removed.txt b/location/api/removed.txt
new file mode 100644
index 000000000000..d802177e249b
--- /dev/null
+++ b/location/api/removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/location/api/system-current.txt b/location/api/system-current.txt
new file mode 100644
index 000000000000..d802177e249b
--- /dev/null
+++ b/location/api/system-current.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/location/api/system-removed.txt b/location/api/system-removed.txt
new file mode 100644
index 000000000000..d802177e249b
--- /dev/null
+++ b/location/api/system-removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/location/api/test-current.txt b/location/api/test-current.txt
new file mode 100644
index 000000000000..d802177e249b
--- /dev/null
+++ b/location/api/test-current.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/location/api/test-removed.txt b/location/api/test-removed.txt
new file mode 100644
index 000000000000..d802177e249b
--- /dev/null
+++ b/location/api/test-removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/location/placeholder_java/android/location/Placeholder.java b/location/placeholder_java/android/location/Placeholder.java
new file mode 100644
index 000000000000..f0dbce829174
--- /dev/null
+++ b/location/placeholder_java/android/location/Placeholder.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.location;
+
+/**
+ * Placeholder class so new frameworks-location module isn't empty, will be removed once module is
+ * populated.
+ *
+ * @hide
+ *
+ */
+public class Placeholder {
+}
diff --git a/media/TEST_MAPPING b/media/TEST_MAPPING
index 5ae77b5a8e2f..a9da832b2a5a 100644
--- a/media/TEST_MAPPING
+++ b/media/TEST_MAPPING
@@ -37,6 +37,17 @@
}
],
"file_patterns": ["(?i)drm|crypto"]
+ },
+ {
+ "file_patterns": [
+ "[^/]*(Ringtone)[^/]*\\.java"
+ ],
+ "name": "MediaRingtoneTests",
+ "options": [
+ {"exclude-annotation": "androidx.test.filters.LargeTest"},
+ {"exclude-annotation": "androidx.test.filters.FlakyTest"},
+ {"exclude-annotation": "org.junit.Ignore"}
+ ]
}
]
}
diff --git a/media/java/android/media/RingtoneManager.java b/media/java/android/media/RingtoneManager.java
index 0ff1b1e19811..9234479ea8fd 100644
--- a/media/java/android/media/RingtoneManager.java
+++ b/media/java/android/media/RingtoneManager.java
@@ -16,6 +16,7 @@
package android.media;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
@@ -35,19 +36,20 @@ import android.content.res.AssetFileDescriptor;
import android.database.Cursor;
import android.database.StaleDataException;
import android.net.Uri;
-import android.os.Build;
import android.os.Environment;
import android.os.FileUtils;
import android.os.ParcelFileDescriptor;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
+import android.os.vibrator.persistence.VibrationXmlParser;
import android.provider.BaseColumns;
import android.provider.MediaStore;
import android.provider.MediaStore.Audio.AudioColumns;
import android.provider.MediaStore.MediaColumns;
import android.provider.Settings;
import android.provider.Settings.System;
+import android.text.TextUtils;
import android.util.Log;
import com.android.internal.database.SortCursor;
@@ -58,6 +60,8 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
@@ -209,21 +213,30 @@ public class RingtoneManager {
*/
public static final String EXTRA_RINGTONE_PICKED_URI =
"android.intent.extra.ringtone.PICKED_URI";
-
+
+ /**
+ * Declares the allowed types of media for this RingtoneManager.
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = "MEDIA_", value = {
+ Ringtone.MEDIA_SOUND,
+ Ringtone.MEDIA_VIBRATION,
+ })
+ public @interface MediaType {}
+
// Make sure the column ordering and then ..._COLUMN_INDEX are in sync
- private static final String[] INTERNAL_COLUMNS = new String[] {
+ private static final String[] MEDIA_AUDIO_COLUMNS = new String[] {
MediaStore.Audio.Media._ID,
MediaStore.Audio.Media.TITLE,
MediaStore.Audio.Media.TITLE,
MediaStore.Audio.Media.TITLE_KEY,
};
- private static final String[] MEDIA_COLUMNS = new String[] {
- MediaStore.Audio.Media._ID,
- MediaStore.Audio.Media.TITLE,
- MediaStore.Audio.Media.TITLE,
- MediaStore.Audio.Media.TITLE_KEY,
+ private static final String[] MEDIA_VIBRATION_COLUMNS = new String[]{
+ MediaStore.Files.FileColumns._ID,
+ MediaStore.Files.FileColumns.TITLE,
};
/**
@@ -251,7 +264,9 @@ public class RingtoneManager {
private Cursor mCursor;
private int mType = TYPE_RINGTONE;
-
+ @MediaType
+ private int mMediaType = Ringtone.MEDIA_SOUND;
+
/**
* If a column (item from this list) exists in the Cursor, its value must
* be true (value of 1) for the row to be returned.
@@ -318,6 +333,41 @@ public class RingtoneManager {
}
/**
+ * Sets the media type that will be listed by the RingtoneManager.
+ *
+ * <p>This method should be called before calling {@link RingtoneManager#getCursor()}.
+ *
+ * @hide
+ */
+ public void setMediaType(@MediaType int mediaType) {
+ if (mCursor != null) {
+ throw new IllegalStateException(
+ "Setting media should be done before calling getCursor().");
+ }
+
+ switch (mediaType) {
+ case Ringtone.MEDIA_SOUND:
+ case Ringtone.MEDIA_VIBRATION:
+ mMediaType = mediaType;
+ break;
+ default:
+ throw new IllegalArgumentException("Unsupported media type " + mediaType);
+ }
+ }
+
+ /**
+ * Returns the RingtoneManagers media type.
+ *
+ * @return the media type.
+ * @see #setMediaType
+ * @hide
+ */
+ @MediaType
+ public int getMediaType() {
+ return mMediaType;
+ }
+
+ /**
* Sets which type(s) of ringtones will be listed by this.
*
* @param type The type(s), one or more of {@link #TYPE_RINGTONE},
@@ -454,19 +504,19 @@ public class RingtoneManager {
return mCursor;
}
- ArrayList<Cursor> ringtoneCursors = new ArrayList<Cursor>();
- ringtoneCursors.add(getInternalRingtones());
- ringtoneCursors.add(getMediaRingtones());
+ ArrayList<Cursor> cursors = new ArrayList<>();
+
+ cursors.add(queryMediaStore(/* internal= */ true));
+ cursors.add(queryMediaStore(/* internal= */ false));
if (mIncludeParentRingtones) {
Cursor parentRingtonesCursor = getParentProfileRingtones();
if (parentRingtonesCursor != null) {
- ringtoneCursors.add(parentRingtonesCursor);
+ cursors.add(parentRingtonesCursor);
}
}
-
- return mCursor = new SortCursor(ringtoneCursors.toArray(new Cursor[ringtoneCursors.size()]),
- MediaStore.Audio.Media.DEFAULT_SORT_ORDER);
+ return mCursor = new SortCursor(cursors.toArray(new Cursor[cursors.size()]),
+ getSortOrderForMedia(mMediaType));
}
private Cursor getParentProfileRingtones() {
@@ -478,9 +528,7 @@ public class RingtoneManager {
// We don't need to re-add the internal ringtones for the work profile since
// they are the same as the personal profile. We just need the external
// ringtones.
- final Cursor res = getMediaRingtones(parentContext);
- return new ExternalRingtonesCursorWrapper(res, ContentProvider.maybeAddUserId(
- MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, parentInfo.id));
+ return queryMediaStore(parentContext, /* internal= */ false);
}
}
return null;
@@ -502,7 +550,7 @@ public class RingtoneManager {
Uri positionUri = getRingtoneUri(position);
if (Ringtone.useRingtoneV2()) {
mPreviousRingtone = new Ringtone.Builder(
- mContext, Ringtone.MEDIA_SOUND, getDefaultAudioAttributes(mType))
+ mContext, mMediaType, getDefaultAudioAttributes(mType))
.setUri(positionUri)
.build();
} else {
@@ -675,11 +723,13 @@ public class RingtoneManager {
*/
public static Uri getValidRingtoneUri(Context context) {
final RingtoneManager rm = new RingtoneManager(context);
-
- Uri uri = getValidRingtoneUriFromCursorAndClose(context, rm.getInternalRingtones());
+
+ Uri uri = getValidRingtoneUriFromCursorAndClose(context,
+ rm.queryMediaStore(/* internal= */ true));
if (uri == null) {
- uri = getValidRingtoneUriFromCursorAndClose(context, rm.getMediaRingtones());
+ uri = getValidRingtoneUriFromCursorAndClose(context,
+ rm.queryMediaStore(/* internal= */ false));
}
return uri;
@@ -700,28 +750,26 @@ public class RingtoneManager {
}
}
- @UnsupportedAppUsage
- private Cursor getInternalRingtones() {
- final Cursor res = query(
- MediaStore.Audio.Media.INTERNAL_CONTENT_URI, INTERNAL_COLUMNS,
- constructBooleanTrueWhereClause(mFilterColumns),
- null, MediaStore.Audio.Media.DEFAULT_SORT_ORDER);
- return new ExternalRingtonesCursorWrapper(res, MediaStore.Audio.Media.INTERNAL_CONTENT_URI);
+ private Cursor queryMediaStore(boolean internal) {
+ return queryMediaStore(mContext, internal);
}
- private Cursor getMediaRingtones() {
- final Cursor res = getMediaRingtones(mContext);
- return new ExternalRingtonesCursorWrapper(res, MediaStore.Audio.Media.EXTERNAL_CONTENT_URI);
- }
+ private Cursor queryMediaStore(Context context, boolean internal) {
+ Uri contentUri = getContentUriForMedia(mMediaType, internal);
+ String[] columns =
+ mMediaType == Ringtone.MEDIA_VIBRATION ? MEDIA_VIBRATION_COLUMNS
+ : MEDIA_AUDIO_COLUMNS;
+ String whereClause = getWhereClauseForMedia(mMediaType, mFilterColumns);
+ String sortOrder = getSortOrderForMedia(mMediaType);
+
+ Cursor cursor = query(contentUri, columns, whereClause, /* selectionArgs= */ null,
+ sortOrder, context);
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- private Cursor getMediaRingtones(Context context) {
- // MediaStore now returns ringtones on other storage devices, even when
- // we don't have storage or audio permissions
- return query(
- MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, MEDIA_COLUMNS,
- constructBooleanTrueWhereClause(mFilterColumns), null,
- MediaStore.Audio.Media.DEFAULT_SORT_ORDER, context);
+ if (context.getUserId() != mContext.getUserId()) {
+ contentUri = ContentProvider.maybeAddUserId(contentUri, context.getUserId());
+ }
+
+ return new ExternalRingtonesCursorWrapper(cursor, contentUri);
}
private void setFilterColumnsList(int type) {
@@ -740,6 +788,56 @@ public class RingtoneManager {
columns.add(MediaStore.Audio.AudioColumns.IS_ALARM);
}
}
+
+ /**
+ * Returns the sort order for the specified media.
+ *
+ * @param media The RingtoneManager media type.
+ * @return The sort order column.
+ */
+ private static String getSortOrderForMedia(@MediaType int media) {
+ return media == Ringtone.MEDIA_VIBRATION ? MediaStore.Files.FileColumns.TITLE
+ : MediaStore.Audio.Media.DEFAULT_SORT_ORDER;
+ }
+
+ /**
+ * Returns the content URI based on the specified media and whether it's internal or external
+ * storage.
+ *
+ * @param media The RingtoneManager media type.
+ * @param internal Whether it's for internal or external storage.
+ * @return The media content URI.
+ */
+ private static Uri getContentUriForMedia(@MediaType int media, boolean internal) {
+ switch (media) {
+ case Ringtone.MEDIA_VIBRATION:
+ return MediaStore.Files.getContentUri(
+ internal ? MediaStore.VOLUME_INTERNAL : MediaStore.VOLUME_EXTERNAL);
+ case Ringtone.MEDIA_SOUND:
+ return internal ? MediaStore.Audio.Media.INTERNAL_CONTENT_URI
+ : MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
+ default:
+ throw new IllegalArgumentException("Unsupported media type " + media);
+ }
+ }
+
+ /**
+ * Constructs a where clause based on the media type. This will be used to find all matching
+ * sound or vibration files.
+ *
+ * @param media The RingtoneManager media type.
+ * @param columns The columns that must be true, when media type is {@link Ringtone#MEDIA_SOUND}
+ * @return The where clause.
+ */
+ private static String getWhereClauseForMedia(@MediaType int media, List<String> columns) {
+ // TODO(b/296213309): Filtering by ringtone-type isn't supported yet for vibrations.
+ if (media == Ringtone.MEDIA_VIBRATION) {
+ return TextUtils.formatSimple("(%s='%s')", MediaStore.Files.FileColumns.MIME_TYPE,
+ VibrationXmlParser.APPLICATION_VIBRATION_XML_MIME_TYPE);
+ }
+
+ return constructBooleanTrueWhereClause(columns);
+ }
/**
* Constructs a where clause that consists of at least one column being 1
@@ -769,14 +867,6 @@ public class RingtoneManager {
return sb.toString();
}
-
- private Cursor query(Uri uri,
- String[] projection,
- String selection,
- String[] selectionArgs,
- String sortOrder) {
- return query(uri, projection, selection, selectionArgs, sortOrder, mContext);
- }
private Cursor query(Uri uri,
String[] projection,
diff --git a/media/tests/ringtone/Android.bp b/media/tests/ringtone/Android.bp
new file mode 100644
index 000000000000..55b98c4704b1
--- /dev/null
+++ b/media/tests/ringtone/Android.bp
@@ -0,0 +1,30 @@
+package {
+ // See: http://go/android-license-faq
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test {
+ name: "MediaRingtoneTests",
+
+ srcs: ["src/**/*.java"],
+
+ libs: [
+ "android.test.runner",
+ "android.test.base",
+ ],
+
+ static_libs: [
+ "androidx.test.rules",
+ "testng",
+ "androidx.test.ext.truth",
+ "frameworks-base-testutils",
+ ],
+
+ test_suites: [
+ "device-tests",
+ "automotive-tests",
+ ],
+
+ platform_apis: true,
+ certificate: "platform",
+}
diff --git a/media/tests/ringtone/AndroidManifest.xml b/media/tests/ringtone/AndroidManifest.xml
new file mode 100644
index 000000000000..27eda07cd0d3
--- /dev/null
+++ b/media/tests/ringtone/AndroidManifest.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2023 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.framework.base.media.ringtone.tests">
+
+ <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.MANAGE_USERS" />
+
+ <application android:debuggable="true">
+ <uses-library android:name="android.test.runner" />
+
+ <activity android:name="MediaRingtoneTests"
+ android:label="Media Ringtone Tests"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ </activity>
+
+ </application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.framework.base.media.ringtone.tests"
+ android:label="Media Ringtone Tests"/>
+</manifest>
diff --git a/media/tests/ringtone/TEST_MAPPING b/media/tests/ringtone/TEST_MAPPING
new file mode 100644
index 000000000000..6f25c147076c
--- /dev/null
+++ b/media/tests/ringtone/TEST_MAPPING
@@ -0,0 +1,20 @@
+{
+ "presubmit": [
+ {
+ "name": "MediaRingtoneTests",
+ "options": [
+ {"exclude-annotation": "androidx.test.filters.LargeTest"},
+ {"exclude-annotation": "androidx.test.filters.FlakyTest"},
+ {"exclude-annotation": "org.junit.Ignore"}
+ ]
+ }
+ ],
+ "postsubmit": [
+ {
+ "name": "MediaRingtoneTests",
+ "options": [
+ {"exclude-annotation": "org.junit.Ignore"}
+ ]
+ }
+ ]
+} \ No newline at end of file
diff --git a/media/tests/ringtone/res/raw/test_haptic_file.ahv b/media/tests/ringtone/res/raw/test_haptic_file.ahv
new file mode 100644
index 000000000000..18c99c79814f
--- /dev/null
+++ b/media/tests/ringtone/res/raw/test_haptic_file.ahv
@@ -0,0 +1,17 @@
+<vibration>
+ <waveform-effect>
+ <waveform-entry durationMs="63" amplitude="255"/>
+ <waveform-entry durationMs="63" amplitude="231"/>
+ <waveform-entry durationMs="63" amplitude="208"/>
+ <waveform-entry durationMs="63" amplitude="185"/>
+ <waveform-entry durationMs="63" amplitude="162"/>
+ <waveform-entry durationMs="63" amplitude="139"/>
+ <waveform-entry durationMs="63" amplitude="115"/>
+ <waveform-entry durationMs="63" amplitude="92"/>
+ <waveform-entry durationMs="63" amplitude="69"/>
+ <waveform-entry durationMs="63" amplitude="46"/>
+ <waveform-entry durationMs="63" amplitude="23"/>
+ <waveform-entry durationMs="63" amplitude="0"/>
+ <waveform-entry durationMs="1250" amplitude="0"/>
+ </waveform-effect>
+</vibration>
diff --git a/media/tests/ringtone/res/raw/test_sound_file.mp3 b/media/tests/ringtone/res/raw/test_sound_file.mp3
new file mode 100644
index 000000000000..c1b2fdf93991
--- /dev/null
+++ b/media/tests/ringtone/res/raw/test_sound_file.mp3
Binary files differ
diff --git a/media/tests/ringtone/src/com/android/media/RingtoneManagerTest.java b/media/tests/ringtone/src/com/android/media/RingtoneManagerTest.java
new file mode 100644
index 000000000000..a92b29883ce7
--- /dev/null
+++ b/media/tests/ringtone/src/com/android/media/RingtoneManagerTest.java
@@ -0,0 +1,233 @@
+/*
+ * Copyright 2023 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.media;
+
+import static com.google.android.mms.ContentType.AUDIO_MP3;
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertThrows;
+import static org.junit.Assume.assumeTrue;
+
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.media.Ringtone;
+import android.media.RingtoneManager;
+import android.net.Uri;
+import android.os.Environment;
+import android.os.ParcelFileDescriptor;
+import android.os.SystemClock;
+import android.os.vibrator.persistence.VibrationXmlParser;
+import android.provider.MediaStore;
+import android.text.TextUtils;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.framework.base.media.ringtone.tests.R;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+@RunWith(Parameterized.class)
+public class RingtoneManagerTest {
+ @RingtoneManager.MediaType
+ private final int mMediaType;
+ private final List<Uri> mAddedFilesUri;
+ private Context mContext;
+ private RingtoneManager mRingtoneManager;
+ private long mTimestamp;
+
+ @Parameterized.Parameters(name = "media = {0}")
+ public static Iterable<?> data() {
+ return Arrays.asList(Ringtone.MEDIA_SOUND, Ringtone.MEDIA_VIBRATION);
+ }
+
+ public RingtoneManagerTest(@RingtoneManager.MediaType int mediaType) {
+ mMediaType = mediaType;
+ mAddedFilesUri = new ArrayList<>();
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+ mTimestamp = SystemClock.uptimeMillis();
+ mRingtoneManager = new RingtoneManager(mContext);
+ mRingtoneManager.setMediaType(mMediaType);
+ }
+
+ @After
+ public void tearDown() {
+ // Clean up media store
+ for (Uri fileUri : mAddedFilesUri) {
+ mContext.getContentResolver().delete(fileUri, null);
+ }
+ }
+
+ @Test
+ public void testSetMediaType_withValidValue_setsMediaCorrectly() {
+ mRingtoneManager.setMediaType(mMediaType);
+ assertThat(mRingtoneManager.getMediaType()).isEqualTo(mMediaType);
+ }
+
+ @Test
+ public void testSetMediaType_withInvalidValue_throwsException() {
+ assertThrows(IllegalArgumentException.class, () -> mRingtoneManager.setMediaType(999));
+ }
+
+ @Test
+ public void testSetMediaType_afterCallingGetCursor_throwsException() {
+ mRingtoneManager.getCursor();
+ assertThrows(IllegalStateException.class, () -> mRingtoneManager.setMediaType(mMediaType));
+ }
+
+ @Test
+ public void testGetRingtone_ringtoneHasCorrectTitle() throws Exception {
+ String fileName = generateUniqueFileName("new_file");
+ Ringtone ringtone = addNewRingtoneToMediaStore(mRingtoneManager, fileName);
+
+ assertThat(ringtone.getTitle(mContext)).isEqualTo(fileName);
+ }
+
+ @Test
+ public void testGetRingtone_ringtoneCanBePlayedAndStopped() throws Exception {
+ //TODO(b/261571543) Remove this assumption once we support playing vibrations.
+ assumeTrue(mMediaType == Ringtone.MEDIA_SOUND);
+ String fileName = generateUniqueFileName("new_file");
+ Ringtone ringtone = addNewRingtoneToMediaStore(mRingtoneManager, fileName);
+
+ ringtone.play();
+ assertThat(ringtone.isPlaying()).isTrue();
+
+ ringtone.stop();
+ assertThat(ringtone.isPlaying()).isFalse();
+ }
+
+ @Test
+ public void testGetCursor_withDifferentMedia_returnsCorrectCursor() throws Exception {
+ RingtoneManager audioRingtoneManager = new RingtoneManager(mContext);
+ String audioFileName = generateUniqueFileName("ringtone");
+ addNewRingtoneToMediaStore(audioRingtoneManager, audioFileName);
+
+ RingtoneManager vibrationRingtoneManager = new RingtoneManager(mContext);
+ vibrationRingtoneManager.setMediaType(Ringtone.MEDIA_VIBRATION);
+ String vibrationFileName = generateUniqueFileName("vibration");
+ addNewRingtoneToMediaStore(vibrationRingtoneManager, vibrationFileName);
+
+ Cursor audioCursor = audioRingtoneManager.getCursor();
+ Cursor vibrationCursor = vibrationRingtoneManager.getCursor();
+
+ List<String> audioTitles = extractRecordTitles(audioCursor);
+ List<String> vibrationTitles = extractRecordTitles(vibrationCursor);
+
+ assertThat(audioTitles).contains(audioFileName);
+ assertThat(audioTitles).doesNotContain(vibrationFileName);
+
+ assertThat(vibrationTitles).contains(vibrationFileName);
+ assertThat(vibrationTitles).doesNotContain(audioFileName);
+ }
+
+ private List<String> extractRecordTitles(Cursor cursor) {
+ List<String> titles = new ArrayList<>();
+
+ if (cursor.moveToFirst()) {
+ do {
+ String title = cursor.getString(RingtoneManager.TITLE_COLUMN_INDEX);
+ titles.add(title);
+ } while (cursor.moveToNext());
+ }
+
+ return titles;
+ }
+
+ private Ringtone addNewRingtoneToMediaStore(RingtoneManager ringtoneManager, String fileName)
+ throws Exception {
+ Uri fileUri = ringtoneManager.getMediaType() == Ringtone.MEDIA_SOUND ? addAudioFile(
+ fileName) : addVibrationFile(fileName);
+ mAddedFilesUri.add(fileUri);
+
+ int ringtonePosition = ringtoneManager.getRingtonePosition(fileUri);
+ Ringtone ringtone = ringtoneManager.getRingtone(ringtonePosition);
+ // Validate this is the expected ringtone.
+ assertThat(ringtone.getUri()).isEqualTo(fileUri);
+ return ringtone;
+ }
+
+ private Uri addAudioFile(String fileName) throws Exception {
+ ContentResolver resolver = mContext.getContentResolver();
+ ContentValues contentValues = new ContentValues();
+ contentValues.put(MediaStore.Audio.Media.DISPLAY_NAME, fileName + ".mp3");
+ contentValues.put(MediaStore.Audio.Media.RELATIVE_PATH, Environment.DIRECTORY_RINGTONES);
+ contentValues.put(MediaStore.Audio.Media.MIME_TYPE, AUDIO_MP3);
+ contentValues.put(MediaStore.Audio.Media.TITLE, fileName);
+ contentValues.put(MediaStore.Audio.Media.IS_RINGTONE, 1);
+
+ Uri contentUri = resolver.insert(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
+ contentValues);
+ writeRawDataToFile(resolver, contentUri, R.raw.test_sound_file);
+
+ return resolver.canonicalizeOrElse(contentUri);
+ }
+
+ private Uri addVibrationFile(String fileName) throws Exception {
+ ContentResolver resolver = mContext.getContentResolver();
+ ContentValues contentValues = new ContentValues();
+ contentValues.put(MediaStore.Files.FileColumns.DISPLAY_NAME, fileName + ".ahv");
+ contentValues.put(MediaStore.Files.FileColumns.RELATIVE_PATH,
+ Environment.DIRECTORY_DOWNLOADS);
+ contentValues.put(MediaStore.Files.FileColumns.MIME_TYPE,
+ VibrationXmlParser.APPLICATION_VIBRATION_XML_MIME_TYPE);
+ contentValues.put(MediaStore.Files.FileColumns.TITLE, fileName);
+
+ Uri contentUri = resolver.insert(MediaStore.Files.getContentUri(MediaStore
+ .VOLUME_EXTERNAL), contentValues);
+ writeRawDataToFile(resolver, contentUri, R.raw.test_haptic_file);
+
+ return resolver.canonicalizeOrElse(contentUri);
+ }
+
+ private void writeRawDataToFile(ContentResolver resolver, Uri contentUri, int rawResource)
+ throws Exception {
+ try (ParcelFileDescriptor pfd =
+ resolver.openFileDescriptor(contentUri, "w", null)) {
+ InputStream inputStream = mContext.getResources().openRawResource(rawResource);
+ FileOutputStream outputStream = new FileOutputStream(pfd.getFileDescriptor());
+ outputStream.write(inputStream.readAllBytes());
+
+ inputStream.close();
+ outputStream.flush();
+ outputStream.close();
+
+ } catch (Exception e) {
+ throw new Exception("Failed to write data to file", e);
+ }
+ }
+
+ private String generateUniqueFileName(String prefix) {
+ return TextUtils.formatSimple("%s_%d", prefix, mTimestamp);
+ }
+
+}
diff --git a/native/android/performance_hint.cpp b/native/android/performance_hint.cpp
index c571b742d2d1..c25df6e08fd0 100644
--- a/native/android/performance_hint.cpp
+++ b/native/android/performance_hint.cpp
@@ -144,8 +144,6 @@ APerformanceHintSession* APerformanceHintManager::createSession(
binder::Status ret =
mHintManager->createHintSession(mToken, tids, initialTargetWorkDurationNanos, &session);
if (!ret.isOk() || !session) {
- ALOGE("%s: PerformanceHint cannot create hint session. %s", __FUNCTION__,
- ret.exceptionMessage().c_str());
return nullptr;
}
return new APerformanceHintSession(mHintManager, std::move(session), mPreferredRateNanos,
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
index 80cf6c313316..20740dcedf95 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
@@ -223,6 +223,7 @@ public class SystemSettingsValidators {
VALIDATORS.put(System.NOTIFICATION_LIGHT_PULSE, BOOLEAN_VALIDATOR);
VALIDATORS.put(System.WEAR_ACCESSIBILITY_GESTURE_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(System.WEAR_ACCESSIBILITY_GESTURE_ENABLED_DURING_OOBE, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(System.WEAR_TTS_PREWARM_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(System.CLOCKWORK_BLUETOOTH_SETTINGS_PREF, BOOLEAN_VALIDATOR);
VALIDATORS.put(System.UNREAD_NOTIFICATION_DOT_INDICATOR, BOOLEAN_VALIDATOR);
VALIDATORS.put(System.AUTO_LAUNCH_MEDIA_CONTROLS, BOOLEAN_VALIDATOR);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
index 55527455ca11..0a98032e26d8 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
@@ -845,6 +845,7 @@ final class SettingsState {
private void doWriteState() {
boolean wroteState = false;
+ String settingFailedToBePersisted = null;
final int version;
final ArrayMap<String, Setting> settings;
final ArrayMap<String, String> namespaceBannedHashes;
@@ -895,8 +896,14 @@ final class SettingsState {
}
}
} catch (IOException ex) {
- Slog.e(LOG_TAG, "[SKIPPED PERSISTING]" + setting.getName()
+ Slog.e(LOG_TAG, "[ABORT PERSISTING]" + setting.getName()
+ " due to error writing to disk", ex);
+ // A setting failed to be written. Abort the serialization to avoid leaving
+ // a partially serialized setting on disk, which can cause parsing errors.
+ // Note down the problematic setting, so that we can delete it before trying
+ // again to persist the rest of the settings.
+ settingFailedToBePersisted = setting.getName();
+ throw ex;
}
}
serializer.endTag(null, TAG_SETTINGS);
@@ -922,14 +929,14 @@ final class SettingsState {
Slog.i(LOG_TAG, "[PERSIST END]");
}
} catch (Throwable t) {
- Slog.wtf(LOG_TAG, "Failed to write settings, restoring backup", t);
+ Slog.wtf(LOG_TAG, "Failed to write settings, restoring old file", t);
if (t instanceof IOException) {
- if (DEBUG) {
- // we failed to create a directory, so log the permissions and existence
- // state for the settings file and directory
- logSettingsDirectoryInformation(destination.getBaseFile());
- }
if (t.getMessage().contains("Couldn't create directory")) {
+ if (DEBUG) {
+ // we failed to create a directory, so log the permissions and existence
+ // state for the settings file and directory
+ logSettingsDirectoryInformation(destination.getBaseFile());
+ }
// attempt to create the directory with Files.createDirectories, which
// throws more informative errors than File.mkdirs.
Path parentPath = destination.getBaseFile().getParentFile().toPath();
@@ -950,7 +957,15 @@ final class SettingsState {
}
}
- if (wroteState) {
+ if (!wroteState) {
+ if (settingFailedToBePersisted != null) {
+ synchronized (mLock) {
+ // Delete the problematic setting. This will schedule a write as well.
+ deleteSettingLocked(settingFailedToBePersisted);
+ }
+ }
+ } else {
+ // success
synchronized (mLock) {
addHistoricalOperationLocked(HISTORICAL_OPERATION_PERSIST, null);
}
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index c697c1ff66ec..203efbf76658 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -103,6 +103,7 @@ public class SettingsBackupTest {
Settings.System.PEAK_REFRESH_RATE, // depends on hardware capabilities
Settings.System.SCREEN_BRIGHTNESS_FLOAT,
Settings.System.WEAR_ACCESSIBILITY_GESTURE_ENABLED_DURING_OOBE,
+ Settings.System.WEAR_TTS_PREWARM_ENABLED,
Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ,
Settings.System.MULTI_AUDIO_FOCUS_ENABLED // form-factor/OEM specific
);
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/values-night/styles.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/values-night/styles.xml
index 81b3152375ff..1f5765465075 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/res/values-night/styles.xml
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/values-night/styles.xml
@@ -16,6 +16,10 @@
-->
<resources>
+ <style name="AccessibilityMenuSettings" parent="android:Theme.DeviceDefault.DayNight">
+ <item name="android:windowLightStatusBar">false</item>
+ </style>
+
<!--Adds the theme to support SnackBar component and user configurable theme. -->
<style name="ServiceTheme" parent="android:Theme.DeviceDefault.DayNight">
<item name="android:colorControlNormal">@color/colorControlNormal</item>
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/values/styles.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/values/styles.xml
index 2009cd1df4e5..a2508cdf4f16 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/res/values/styles.xml
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/values/styles.xml
@@ -17,7 +17,9 @@
<resources>
<!--The theme is for preference CollapsingToolbarBaseActivity settings-->
- <style name="AccessibilityMenuSettings" parent="android:Theme.DeviceDefault.DayNight" />
+ <style name="AccessibilityMenuSettings" parent="android:Theme.DeviceDefault.DayNight">
+ <item name="android:windowLightStatusBar">true</item>
+ </style>
<!--Adds the theme to support SnackBar component and user configurable theme. -->
<style name="ServiceTheme" parent="android:Theme.DeviceDefault.Light">
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt
index da48762e1960..0a100babde75 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt
@@ -14,26 +14,20 @@
* limitations under the License.
*/
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
package com.android.systemui.keyguard.ui.composable
-import androidx.compose.foundation.layout.Arrangement
-import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.Row
-import androidx.compose.material3.Button
-import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.Text
+import android.view.View
+import android.view.ViewGroup
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.collectAsState
-import androidx.compose.runtime.getValue
-import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
-import androidx.compose.ui.unit.dp
+import androidx.compose.ui.viewinterop.AndroidView
import com.android.compose.animation.scene.SceneScope
-import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.common.ui.compose.Icon
+import com.android.systemui.R
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.keyguard.qualifiers.KeyguardRootView
import com.android.systemui.keyguard.ui.viewmodel.LockscreenSceneViewModel
import com.android.systemui.scene.shared.model.Direction
import com.android.systemui.scene.shared.model.SceneKey
@@ -42,6 +36,7 @@ import com.android.systemui.scene.shared.model.UserAction
import com.android.systemui.scene.ui.composable.ComposableScene
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.map
@@ -54,6 +49,7 @@ class LockscreenScene
constructor(
@Application private val applicationScope: CoroutineScope,
private val viewModel: LockscreenSceneViewModel,
+ @KeyguardRootView private val viewProvider: () -> @JvmSuppressWildcards View,
) : ComposableScene {
override val key = SceneKey.Lockscreen
@@ -72,6 +68,7 @@ constructor(
) {
LockscreenScene(
viewModel = viewModel,
+ viewProvider = viewProvider,
modifier = modifier,
)
}
@@ -89,25 +86,22 @@ constructor(
@Composable
private fun LockscreenScene(
viewModel: LockscreenSceneViewModel,
+ viewProvider: () -> View,
modifier: Modifier = Modifier,
) {
- // TODO(b/280879610): implement the real UI.
-
- val lockButtonIcon: Icon by viewModel.lockButtonIcon.collectAsState()
-
- Box(modifier = modifier) {
- Column(
- horizontalAlignment = Alignment.CenterHorizontally,
- modifier = Modifier.align(Alignment.Center)
- ) {
- Text("Lockscreen", style = MaterialTheme.typography.headlineMedium)
- Row(
- horizontalArrangement = Arrangement.spacedBy(8.dp),
- ) {
- Button(onClick = { viewModel.onLockButtonClicked() }) { Icon(lockButtonIcon) }
-
- Button(onClick = { viewModel.onContentClicked() }) { Text("Open some content") }
+ AndroidView(
+ factory = { _ ->
+ val keyguardRootView = viewProvider()
+ // Remove the KeyguardRootView from any parent it might already have in legacy code just
+ // in case (a view can't have two parents).
+ (keyguardRootView.parent as? ViewGroup)?.removeView(keyguardRootView)
+ keyguardRootView
+ },
+ update = { keyguardRootView ->
+ keyguardRootView.requireViewById<View>(R.id.lock_icon_view).setOnClickListener {
+ viewModel.onLockButtonClicked()
}
- }
- }
+ },
+ modifier = modifier,
+ )
}
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt
index ab0225d63ac5..966e183fc821 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt
@@ -51,15 +51,18 @@ import kotlinx.coroutines.withContext
private val KEY_TIMESTAMP = "appliedTimestamp"
private val KNOWN_PLUGINS =
mapOf<String, List<ClockMetadata>>(
- "com.android.systemui.falcon.one" to listOf(ClockMetadata("ANALOG_CLOCK_BIGNUM")),
- "com.android.systemui.falcon.two" to listOf(ClockMetadata("DIGITAL_CLOCK_CALLIGRAPHY")),
- "com.android.systemui.falcon.three" to listOf(ClockMetadata("DIGITAL_CLOCK_FLEX")),
- "com.android.systemui.falcon.four" to listOf(ClockMetadata("DIGITAL_CLOCK_GROWTH")),
- "com.android.systemui.falcon.five" to listOf(ClockMetadata("DIGITAL_CLOCK_HANDWRITTEN")),
- "com.android.systemui.falcon.six" to listOf(ClockMetadata("DIGITAL_CLOCK_INFLATE")),
- "com.android.systemui.falcon.seven" to listOf(ClockMetadata("DIGITAL_CLOCK_METRO")),
- "com.android.systemui.falcon.eight" to listOf(ClockMetadata("DIGITAL_CLOCK_NUMBEROVERLAP")),
- "com.android.systemui.falcon.nine" to listOf(ClockMetadata("DIGITAL_CLOCK_WEATHER")),
+ "com.android.systemui.clocks.bignum" to listOf(ClockMetadata("ANALOG_CLOCK_BIGNUM")),
+ "com.android.systemui.clocks.calligraphy" to
+ listOf(ClockMetadata("DIGITAL_CLOCK_CALLIGRAPHY")),
+ "com.android.systemui.clocks.flex" to listOf(ClockMetadata("DIGITAL_CLOCK_FLEX")),
+ "com.android.systemui.clocks.growth" to listOf(ClockMetadata("DIGITAL_CLOCK_GROWTH")),
+ "com.android.systemui.clocks.handwritten" to
+ listOf(ClockMetadata("DIGITAL_CLOCK_HANDWRITTEN")),
+ "com.android.systemui.clocks.inflate" to listOf(ClockMetadata("DIGITAL_CLOCK_INFLATE")),
+ "com.android.systemui.clocks.metro" to listOf(ClockMetadata("DIGITAL_CLOCK_METRO")),
+ "com.android.systemui.clocks.numoverlap" to
+ listOf(ClockMetadata("DIGITAL_CLOCK_NUMBEROVERLAP")),
+ "com.android.systemui.clocks.weather" to listOf(ClockMetadata("DIGITAL_CLOCK_WEATHER")),
)
private fun <TKey : Any, TVal : Any> ConcurrentHashMap<TKey, TVal>.concurrentGetOrPut(
diff --git a/packages/SystemUI/ktfmt_includes.txt b/packages/SystemUI/ktfmt_includes.txt
index 92083b0ba1b9..785177ed0f51 100644
--- a/packages/SystemUI/ktfmt_includes.txt
+++ b/packages/SystemUI/ktfmt_includes.txt
@@ -292,6 +292,7 @@
-packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventCoordinator.kt
-packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt
-packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImpl.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/gesture/GesturePointerEventDetector.kt
-packages/SystemUI/src/com/android/systemui/statusbar/gesture/GenericGestureDetector.kt
-packages/SystemUI/src/com/android/systemui/statusbar/gesture/SwipeUpGestureHandler.kt
-packages/SystemUI/src/com/android/systemui/statusbar/gesture/SwipeUpGestureLogger.kt
diff --git a/packages/SystemUI/res/color/qs_footer_power_button_overlay_color.xml b/packages/SystemUI/res/color/qs_footer_power_button_overlay_color.xml
new file mode 100644
index 000000000000..a8abd793bd00
--- /dev/null
+++ b/packages/SystemUI/res/color/qs_footer_power_button_overlay_color.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2023 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.
+ -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_pressed="true" android:color="?attr/onShadeActive" android:alpha="0.12" />
+ <item android:state_hovered="true" android:color="?attr/onShadeActive" android:alpha="0.09" />
+ <item android:color="@color/transparent" />
+</selector> \ No newline at end of file
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 a8c034986425..47a2965bcfac 100644
--- a/packages/SystemUI/res/drawable/qs_footer_action_circle_color.xml
+++ b/packages/SystemUI/res/drawable/qs_footer_action_circle_color.xml
@@ -32,6 +32,12 @@
<corners android:radius="@dimen/qs_footer_action_corner_radius"/>
</shape>
</item>
+ <item>
+ <shape android:shape="rectangle">
+ <solid android:color="@color/qs_footer_power_button_overlay_color"/>
+ <corners android:radius="@dimen/qs_footer_action_corner_radius"/>
+ </shape>
+ </item>
</ripple>
</inset> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout-land/auth_credential_password_pin_content_view.xml b/packages/SystemUI/res/layout-land/auth_credential_password_pin_content_view.xml
new file mode 100644
index 000000000000..24222f7642be
--- /dev/null
+++ b/packages/SystemUI/res/layout-land/auth_credential_password_pin_content_view.xml
@@ -0,0 +1,101 @@
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<merge xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <RelativeLayout
+ android:id="@+id/auth_credential_header"
+ style="?headerStyle"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1">
+
+ <ImageView
+ android:id="@+id/icon"
+ style="?headerIconStyle"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentTop="true"
+ android:contentDescription="@null"/>
+
+ <TextView
+ android:id="@+id/title"
+ style="?titleTextAppearance"
+ android:layout_below="@id/icon"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
+ <TextView
+ android:id="@+id/subtitle"
+ style="?subTitleTextAppearance"
+ android:layout_below="@id/title"
+ android:layout_alignParentLeft="true"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
+ <TextView
+ android:id="@+id/description"
+ style="?descriptionTextAppearance"
+ android:layout_below="@id/subtitle"
+ android:layout_alignParentLeft="true"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
+ </RelativeLayout>
+
+ <FrameLayout
+ android:id="@+id/auth_credential_input"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:orientation="vertical">
+
+ <ImeAwareEditText
+ android:id="@+id/lockPassword"
+ style="?passwordTextAppearance"
+ android:layout_width="208dp"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:imeOptions="actionNext|flagNoFullscreen|flagForceAscii"
+ android:inputType="textPassword"
+ android:minHeight="48dp"/>
+
+ <TextView
+ android:id="@+id/error"
+ style="?errorTextAppearance"
+ android:layout_gravity="center_horizontal"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+ </LinearLayout>
+
+ <Button
+ android:id="@+id/emergencyCallButton"
+ style="@style/AuthCredentialEmergencyButtonStyle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:visibility="gone"
+ android:layout_gravity="center_horizontal|bottom"
+ android:layout_marginTop="12dp"
+ android:layout_marginBottom="12dp"
+ android:text="@string/work_challenge_emergency_button_text"/>
+ </FrameLayout>
+
+</merge>
diff --git a/packages/SystemUI/res/layout-land/auth_credential_password_view.xml b/packages/SystemUI/res/layout-land/auth_credential_password_view.xml
index e439f775f8ea..8ac7583088f9 100644
--- a/packages/SystemUI/res/layout-land/auth_credential_password_view.xml
+++ b/packages/SystemUI/res/layout-land/auth_credential_password_view.xml
@@ -23,84 +23,6 @@
android:elevation="@dimen/biometric_dialog_elevation"
android:theme="?app:attr/lockPinPasswordStyle">
- <RelativeLayout
- android:id="@+id/auth_credential_header"
- style="?headerStyle"
- android:layout_width="wrap_content"
- android:layout_height="match_parent">
-
- <ImageView
- android:id="@+id/icon"
- style="?headerIconStyle"
- android:layout_alignParentLeft="true"
- android:layout_alignParentTop="true"
- android:contentDescription="@null"/>
-
- <TextView
- android:id="@+id/title"
- style="?titleTextAppearance"
- android:layout_below="@id/icon"
- android:layout_width="match_parent"
- android:layout_height="wrap_content" />
-
- <TextView
- android:id="@+id/subtitle"
- style="?subTitleTextAppearance"
- android:layout_below="@id/title"
- android:layout_alignParentLeft="true"
- android:layout_width="match_parent"
- android:layout_height="wrap_content" />
-
- <TextView
- android:id="@+id/description"
- style="?descriptionTextAppearance"
- android:layout_below="@id/subtitle"
- android:layout_alignParentLeft="true"
- android:layout_width="match_parent"
- android:layout_height="wrap_content" />
-
- </RelativeLayout>
-
- <FrameLayout
- android:id="@+id/auth_credential_input"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
-
- <LinearLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal|top"
- android:orientation="vertical">
-
- <ImeAwareEditText
- android:id="@+id/lockPassword"
- style="?passwordTextAppearance"
- android:layout_width="208dp"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal"
- android:imeOptions="actionNext|flagNoFullscreen|flagForceAscii"
- android:inputType="textPassword"
- android:minHeight="48dp"/>
-
- <TextView
- android:id="@+id/error"
- style="?errorTextAppearance"
- android:layout_gravity="center_horizontal"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
- </LinearLayout>
-
- <Button
- android:id="@+id/emergencyCallButton"
- style="@style/AuthCredentialEmergencyButtonStyle"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:visibility="gone"
- android:layout_gravity="center_horizontal|bottom"
- android:layout_marginTop="12dp"
- android:layout_marginBottom="12dp"
- android:text="@string/work_challenge_emergency_button_text"/>
- </FrameLayout>
+ <include layout="@layout/auth_credential_password_pin_content_view" />
</com.android.systemui.biometrics.ui.CredentialPasswordView> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout-land/auth_credential_pin_view.xml b/packages/SystemUI/res/layout-land/auth_credential_pin_view.xml
new file mode 100644
index 000000000000..8ac7583088f9
--- /dev/null
+++ b/packages/SystemUI/res/layout-land/auth_credential_pin_view.xml
@@ -0,0 +1,28 @@
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<com.android.systemui.biometrics.ui.CredentialPasswordView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="horizontal"
+ android:elevation="@dimen/biometric_dialog_elevation"
+ android:theme="?app:attr/lockPinPasswordStyle">
+
+ <include layout="@layout/auth_credential_password_pin_content_view" />
+
+</com.android.systemui.biometrics.ui.CredentialPasswordView> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/auth_credential_password_pin_content_view.xml b/packages/SystemUI/res/layout/auth_credential_password_pin_content_view.xml
new file mode 100644
index 000000000000..11284fd2237b
--- /dev/null
+++ b/packages/SystemUI/res/layout/auth_credential_password_pin_content_view.xml
@@ -0,0 +1,104 @@
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<merge xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <ScrollView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+
+ <RelativeLayout
+ android:id="@+id/auth_credential_header"
+ style="?headerStyle"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingBottom="0dp">
+
+ <ImageView
+ android:id="@+id/icon"
+ style="?headerIconStyle"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentTop="true"
+ android:contentDescription="@null" />
+
+ <TextView
+ android:id="@+id/title"
+ style="?titleTextAppearance"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/icon" />
+
+ <TextView
+ android:id="@+id/subtitle"
+ style="?subTitleTextAppearance"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/title" />
+
+ <TextView
+ android:id="@+id/description"
+ style="?descriptionTextAppearance"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/subtitle" />
+
+ </RelativeLayout>
+
+ </ScrollView>
+
+ <FrameLayout
+ android:id="@+id/auth_credential_input"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:orientation="vertical">
+
+ <ImeAwareEditText
+ android:id="@+id/lockPassword"
+ style="?passwordTextAppearance"
+ android:layout_width="208dp"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:imeOptions="actionNext|flagNoFullscreen|flagForceAscii"
+ android:inputType="textPassword"
+ android:minHeight="48dp"/>
+
+ <TextView
+ android:id="@+id/error"
+ style="?errorTextAppearance"
+ android:layout_gravity="center_horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"/>
+ </LinearLayout>
+
+ <Button
+ android:id="@+id/emergencyCallButton"
+ style="@style/AuthCredentialEmergencyButtonStyle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:visibility="gone"
+ android:layout_gravity="center_horizontal|bottom"
+ android:layout_marginTop="12dp"
+ android:layout_marginBottom="12dp"
+ android:text="@string/work_challenge_emergency_button_text"/>
+ </FrameLayout>
+
+</merge>
diff --git a/packages/SystemUI/res/layout/auth_credential_password_view.xml b/packages/SystemUI/res/layout/auth_credential_password_view.xml
index 9336845f20f7..f8d9a87d5e54 100644
--- a/packages/SystemUI/res/layout/auth_credential_password_view.xml
+++ b/packages/SystemUI/res/layout/auth_credential_password_view.xml
@@ -23,88 +23,6 @@
android:orientation="vertical"
android:theme="?app:attr/lockPinPasswordStyle">
- <ScrollView
- android:id="@+id/auth_credential_header"
- android:layout_width="match_parent"
- android:layout_height="wrap_content">
-
- <RelativeLayout
- style="?headerStyle"
- android:layout_width="match_parent"
- android:layout_height="wrap_content">
-
- <ImageView
- android:id="@+id/icon"
- style="?headerIconStyle"
- android:layout_alignParentLeft="true"
- android:layout_alignParentTop="true"
- android:contentDescription="@null" />
-
- <TextView
- android:id="@+id/title"
- style="?titleTextAppearance"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_below="@id/icon" />
-
- <TextView
- android:id="@+id/subtitle"
- style="?subTitleTextAppearance"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_below="@id/title" />
-
- <TextView
- android:id="@+id/description"
- style="?descriptionTextAppearance"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_below="@id/subtitle" />
-
- </RelativeLayout>
-
- </ScrollView>
-
- <FrameLayout
- android:id="@+id/auth_credential_input"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal|top"
- android:orientation="vertical">
-
- <ImeAwareEditText
- android:id="@+id/lockPassword"
- style="?passwordTextAppearance"
- android:layout_width="208dp"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal"
- android:imeOptions="actionNext|flagNoFullscreen|flagForceAscii"
- android:inputType="textPassword"
- android:minHeight="48dp"/>
-
- <TextView
- android:id="@+id/error"
- style="?errorTextAppearance"
- android:layout_gravity="center_horizontal"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"/>
- </LinearLayout>
-
- <Button
- android:id="@+id/emergencyCallButton"
- style="@style/AuthCredentialEmergencyButtonStyle"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:visibility="gone"
- android:layout_gravity="center_horizontal|bottom"
- android:layout_marginTop="12dp"
- android:layout_marginBottom="12dp"
- android:text="@string/work_challenge_emergency_button_text"/>
- </FrameLayout>
+ <include layout="@layout/auth_credential_password_pin_content_view" />
</com.android.systemui.biometrics.ui.CredentialPasswordView> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/auth_credential_pin_view.xml b/packages/SystemUI/res/layout/auth_credential_pin_view.xml
new file mode 100644
index 000000000000..a1cf807af088
--- /dev/null
+++ b/packages/SystemUI/res/layout/auth_credential_pin_view.xml
@@ -0,0 +1,28 @@
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<com.android.systemui.biometrics.ui.CredentialPasswordView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:elevation="@dimen/biometric_dialog_elevation"
+ android:orientation="vertical"
+ android:theme="?app:attr/lockPinPasswordStyle">
+
+ <include layout="@layout/auth_credential_password_pin_content_view" />
+
+</com.android.systemui.biometrics.ui.CredentialPasswordView> \ No newline at end of file
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
index 4e72518bc613..04eae64013b3 100644
--- a/packages/SystemUI/res/values/ids.xml
+++ b/packages/SystemUI/res/values/ids.xml
@@ -222,4 +222,13 @@
<!-- Communal mode -->
<item type="id" name="communal_widget_wrapper" />
+
+ <!-- Values assigned to the views in Biometrics Prompt -->
+ <item type="id" name="pin_pad"/>
+
+ <!--
+ Used to tag views programmatically added to the smartspace area so they can be more easily
+ removed later.
+ -->
+ <item type="id" name="tag_smartspace_view" />
</resources>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
index 4bc949116807..33e453cf4354 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
@@ -76,26 +76,11 @@ oneway interface IOverviewProxy {
void onSystemBarAttributesChanged(int displayId, int behavior) = 20;
/**
- * Sent when screen turned on and ready to use (blocker scrim is hidden)
- */
- void onScreenTurnedOn() = 21;
-
- /**
* Sent when the desired dark intensity of the nav buttons has changed
*/
void onNavButtonsDarkIntensityChanged(float darkIntensity) = 22;
/**
- * Sent when screen started turning on.
- */
- void onScreenTurningOn() = 23;
-
- /**
- * Sent when screen started turning off.
- */
- void onScreenTurningOff() = 24;
-
- /**
* Sent when split keyboard shortcut is triggered to enter stage split.
*/
void enterStageSplitFromRunningApp(boolean leftOrTop) = 25;
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java
index cab54d08b3ec..8200e5c84186 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java
@@ -39,6 +39,7 @@ import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.os.Looper;
import android.os.RemoteException;
+import android.os.SystemProperties;
import android.provider.Settings;
import android.util.Log;
import android.view.HapticFeedbackConstants;
@@ -76,6 +77,8 @@ public class RotationButtonController {
private static final String TAG = "RotationButtonController";
private static final int BUTTON_FADE_IN_OUT_DURATION_MS = 100;
private static final int NAVBAR_HIDDEN_PENDING_ICON_TIMEOUT_MS = 20000;
+ private static final boolean OEM_DISALLOW_ROTATION_IN_SUW =
+ SystemProperties.getBoolean("ro.setupwizard.rotation_locked", false);
private static final Interpolator LINEAR_INTERPOLATOR = new LinearInterpolator();
private static final int NUM_ACCEPTED_ROTATION_SUGGESTIONS_FOR_INTRODUCTION = 3;
@@ -375,6 +378,12 @@ public class RotationButtonController {
}
public void onRotationProposal(int rotation, boolean isValid) {
+ boolean isUserSetupComplete = Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.USER_SETUP_COMPLETE, 0) != 0;
+ if (!isUserSetupComplete && OEM_DISALLOW_ROTATION_IN_SUW) {
+ return;
+ }
+
int windowRotation = mWindowRotationProvider.get();
if (!mRotationButton.acceptRotationProposal()) {
@@ -497,8 +506,7 @@ public class RotationButtonController {
boolean canShowRotationButton() {
return mIsNavigationBarShowing
|| mBehavior == WindowInsetsController.BEHAVIOR_DEFAULT
- || isGesturalMode(mNavBarMode)
- || mTaskBarVisible;
+ || isGesturalMode(mNavBarMode);
}
@DrawableRes
diff --git a/packages/SystemUI/src/com/android/keyguard/ConnectedDisplayKeyguardPresentation.kt b/packages/SystemUI/src/com/android/keyguard/ConnectedDisplayKeyguardPresentation.kt
index 899cad89a0be..006974c9fa92 100644
--- a/packages/SystemUI/src/com/android/keyguard/ConnectedDisplayKeyguardPresentation.kt
+++ b/packages/SystemUI/src/com/android/keyguard/ConnectedDisplayKeyguardPresentation.kt
@@ -66,7 +66,7 @@ constructor(
window.isNavigationBarContrastEnforced = false
window.navigationBarColor = Color.TRANSPARENT
- clock = findViewById(R.id.clock)
+ clock = requireViewById(R.id.clock)
keyguardStatusViewController =
keyguardStatusViewComponentFactory.build(clock).keyguardStatusViewController.apply {
setDisplayedOnSecondaryDisplay()
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index 05ace74306bc..6d2880e00203 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -293,7 +293,7 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
int viewIndex = mStatusArea.indexOfChild(ksv);
ksv.setVisibility(View.GONE);
- mSmartspaceController.removeViewsFromParent(mStatusArea);
+ removeViewsFromStatusArea();
addSmartspaceView();
// TODO(b/261757708): add content observer for the Settings toggle and add/remove
// weather according to the Settings.
@@ -325,7 +325,7 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
void onLocaleListChanged() {
if (mSmartspaceController.isEnabled()) {
- mSmartspaceController.removeViewsFromParent(mStatusArea);
+ removeViewsFromStatusArea();
addSmartspaceView();
if (mSmartspaceController.isDateWeatherDecoupled()) {
mDateWeatherView.removeView(mWeatherView);
@@ -620,4 +620,13 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
return ((mCurrentClockSize == LARGE) ? clock.getLargeClock() : clock.getSmallClock())
.getConfig().getHasCustomWeatherDataDisplay();
}
+
+ private void removeViewsFromStatusArea() {
+ for (int i = mStatusArea.getChildCount() - 1; i >= 0; i--) {
+ final View childView = mStatusArea.getChildAt(i);
+ if (childView.getTag(R.id.tag_smartspace_view) != null) {
+ mStatusArea.removeViewAt(i);
+ }
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
index 802a550c4d29..7464c8803c99 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
@@ -499,6 +499,8 @@ public class AuthContainerView extends LinearLayout
R.layout.auth_credential_pattern_view, null, false);
break;
case Utils.CREDENTIAL_PIN:
+ mCredentialView = factory.inflate(R.layout.auth_credential_pin_view, null, false);
+ break;
case Utils.CREDENTIAL_PASSWORD:
mCredentialView = factory.inflate(
R.layout.auth_credential_password_view, null, false);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/dagger/UdfpsModule.kt b/packages/SystemUI/src/com/android/systemui/biometrics/dagger/UdfpsModule.kt
index 20c3e4098e83..f7f910391566 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/dagger/UdfpsModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/dagger/UdfpsModule.kt
@@ -56,10 +56,10 @@ interface UdfpsModule {
)
)
} else {
- BoundingBoxOverlapDetector()
+ BoundingBoxOverlapDetector(values[2])
}
} else {
- return BoundingBoxOverlapDetector()
+ return BoundingBoxOverlapDetector(1f)
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/udfps/BoundingBoxOverlapDetector.kt b/packages/SystemUI/src/com/android/systemui/biometrics/udfps/BoundingBoxOverlapDetector.kt
index cf6044f146b0..9b946db0daf0 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/udfps/BoundingBoxOverlapDetector.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/udfps/BoundingBoxOverlapDetector.kt
@@ -17,16 +17,30 @@
package com.android.systemui.biometrics.udfps
import android.graphics.Rect
+import android.os.Build
+import android.util.Log
import com.android.systemui.dagger.SysUISingleton
/** Returns whether the touch coordinates are within the sensor's bounding box. */
@SysUISingleton
-class BoundingBoxOverlapDetector : OverlapDetector {
+class BoundingBoxOverlapDetector(private val targetSize: Float) : OverlapDetector {
+
+ private val TAG = "BoundingBoxOverlapDetector"
+
override fun isGoodOverlap(
touchData: NormalizedTouchData,
nativeSensorBounds: Rect,
nativeOverlayBounds: Rect,
- ): Boolean =
- touchData.isWithinBounds(nativeOverlayBounds) &&
- touchData.isWithinBounds(nativeSensorBounds)
+ ): Boolean {
+ val scaledRadius = (nativeSensorBounds.width() / 2) * targetSize
+ val scaledSensorBounds =
+ Rect(
+ (nativeSensorBounds.centerX() - scaledRadius).toInt(),
+ (nativeSensorBounds.centerY() - scaledRadius).toInt(),
+ (nativeSensorBounds.centerX() + scaledRadius).toInt(),
+ (nativeSensorBounds.centerY() + scaledRadius).toInt(),
+ )
+
+ return touchData.isWithinBounds(scaledSensorBounds)
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/CredentialPasswordView.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/CredentialPasswordView.kt
index 709fe855fbfc..6c5cc4835787 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/CredentialPasswordView.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/CredentialPasswordView.kt
@@ -8,11 +8,8 @@ import android.view.View
import android.view.WindowInsets
import android.view.WindowInsets.Type.ime
import android.view.accessibility.AccessibilityManager
-import android.widget.ImageView
-import android.widget.ImeAwareEditText
import android.widget.LinearLayout
import android.widget.TextView
-import androidx.core.view.isGone
import com.android.systemui.R
import com.android.systemui.biometrics.AuthPanelController
import com.android.systemui.biometrics.ui.binder.CredentialViewBinder
@@ -22,14 +19,6 @@ import com.android.systemui.biometrics.ui.viewmodel.CredentialViewModel
class CredentialPasswordView(context: Context, attrs: AttributeSet?) :
LinearLayout(context, attrs), CredentialView, View.OnApplyWindowInsetsListener {
- private lateinit var titleView: TextView
- private lateinit var subtitleView: TextView
- private lateinit var descriptionView: TextView
- private lateinit var iconView: ImageView
- private lateinit var passwordField: ImeAwareEditText
- private lateinit var credentialHeader: View
- private lateinit var credentialInput: View
-
private var bottomInset: Int = 0
private val accessibilityManager by lazy {
@@ -48,90 +37,32 @@ class CredentialPasswordView(context: Context, attrs: AttributeSet?) :
override fun onFinishInflate() {
super.onFinishInflate()
-
- titleView = requireViewById(R.id.title)
- subtitleView = requireViewById(R.id.subtitle)
- descriptionView = requireViewById(R.id.description)
- iconView = requireViewById(R.id.icon)
- passwordField = requireViewById(R.id.lockPassword)
- credentialHeader = requireViewById(R.id.auth_credential_header)
- credentialInput = requireViewById(R.id.auth_credential_input)
-
setOnApplyWindowInsetsListener(this)
}
- override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
- super.onLayout(changed, left, top, right, bottom)
-
- val inputLeftBound: Int
- var inputTopBound: Int
- var headerRightBound = right
- var headerTopBounds = top
- var headerBottomBounds = bottom
- val subTitleBottom: Int = if (subtitleView.isGone) titleView.bottom else subtitleView.bottom
- val descBottom = if (descriptionView.isGone) subTitleBottom else descriptionView.bottom
- if (resources.configuration.orientation == ORIENTATION_LANDSCAPE) {
- inputTopBound = (bottom - credentialInput.height) / 2
- inputLeftBound = (right - left) / 2
- headerRightBound = inputLeftBound
- if (descriptionView.bottom > headerBottomBounds) {
- headerTopBounds -= iconView.bottom.coerceAtMost(bottomInset)
- credentialHeader.layout(left, headerTopBounds, headerRightBound, bottom)
- }
- } else {
- inputTopBound = descBottom + (bottom - descBottom - credentialInput.height) / 2
- inputLeftBound = (right - left - credentialInput.width) / 2
-
- if (bottom - inputTopBound < credentialInput.height) {
- inputTopBound = bottom - credentialInput.height
- }
-
- if (descriptionView.bottom > inputTopBound) {
- credentialHeader.layout(left, headerTopBounds, headerRightBound, inputTopBound)
- }
- }
-
- credentialInput.layout(inputLeftBound, inputTopBound, right, bottom)
- }
-
- override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec)
-
- val newWidth = MeasureSpec.getSize(widthMeasureSpec)
- val newHeight = MeasureSpec.getSize(heightMeasureSpec) - bottomInset
-
- setMeasuredDimension(newWidth, newHeight)
-
- val halfWidthSpec = MeasureSpec.makeMeasureSpec(width / 2, MeasureSpec.AT_MOST)
- val fullHeightSpec = MeasureSpec.makeMeasureSpec(newHeight, MeasureSpec.UNSPECIFIED)
- if (resources.configuration.orientation == ORIENTATION_LANDSCAPE) {
- measureChildren(halfWidthSpec, fullHeightSpec)
- } else {
- measureChildren(widthMeasureSpec, fullHeightSpec)
- }
- }
-
override fun onApplyWindowInsets(v: View, insets: WindowInsets): WindowInsets {
- val bottomInsets = insets.getInsets(ime())
- if (bottomInset != bottomInsets.bottom) {
- bottomInset = bottomInsets.bottom
-
- if (bottomInset > 0 && resources.configuration.orientation == ORIENTATION_LANDSCAPE) {
- titleView.isSingleLine = true
- titleView.ellipsize = TextUtils.TruncateAt.MARQUEE
- titleView.marqueeRepeatLimit = -1
- // select to enable marquee unless a screen reader is enabled
- titleView.isSelected = accessibilityManager?.shouldMarquee() ?: false
- } else {
- titleView.isSingleLine = false
- titleView.ellipsize = null
- // select to enable marquee unless a screen reader is enabled
- titleView.isSelected = false
+ val imeBottomInset = insets.getInsets(ime()).bottom
+ if (bottomInset != imeBottomInset) {
+ val titleView: TextView? = findViewById(R.id.title)
+ if (titleView != null) {
+ if (
+ bottomInset > 0 && resources.configuration.orientation == ORIENTATION_LANDSCAPE
+ ) {
+ titleView.isSingleLine = true
+ titleView.ellipsize = TextUtils.TruncateAt.MARQUEE
+ titleView.marqueeRepeatLimit = -1
+ // select to enable marquee unless a screen reader is enabled
+ titleView.isSelected = accessibilityManager.shouldMarquee()
+ } else {
+ titleView.isSingleLine = false
+ titleView.ellipsize = null
+ // select to enable marquee unless a screen reader is enabled
+ titleView.isSelected = false
+ }
}
-
- requestLayout()
}
- return insets
+ setPadding(paddingLeft, paddingTop, paddingRight, imeBottomInset)
+ return insets.inset(0, 0, 0, imeBottomInset)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/IPinPad.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/IPinPad.kt
new file mode 100644
index 000000000000..cf6865c0fe8e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/IPinPad.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2023 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.biometrics.ui
+
+/**
+ * Interface for PinPad in auth_credential_pin_view. This is needed when a custom pin pad is
+ * preferred to the IME To use a PinPad, one needs to implement IPinPad interface and provide it in
+ * auth_credential_pin_view and specify the id as [pin_pad]
+ */
+interface IPinPad {
+ fun setPinPadClickListener(pinPadClickListener: PinPadClickListener)
+}
+
+/** The call back interface for onClick event in the view. */
+interface PinPadClickListener {
+ /**
+ * One of the digit key has been clicked.
+ *
+ * @param digit A String representing a digit between 0 and 9.
+ */
+ fun onDigitKeyClick(digit: String?)
+
+ /** The backspace key has been clicked. */
+ fun onBackspaceClick()
+
+ /** The enter key has been clicked. */
+ fun onEnterKeyClick()
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialPasswordViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialPasswordViewBinder.kt
index c27d71522c2f..996b62e084cb 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialPasswordViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialPasswordViewBinder.kt
@@ -15,6 +15,7 @@ import androidx.lifecycle.repeatOnLifecycle
import com.android.systemui.R
import com.android.systemui.biometrics.ui.CredentialPasswordView
import com.android.systemui.biometrics.ui.CredentialView
+import com.android.systemui.biometrics.ui.IPinPad
import com.android.systemui.biometrics.ui.viewmodel.CredentialViewModel
import com.android.systemui.lifecycle.repeatWhenAttached
import kotlinx.coroutines.awaitCancellation
@@ -53,13 +54,19 @@ object CredentialPasswordViewBinder {
}
)
passwordField.setOnKeyListener(OnBackButtonListener(onBackInvokedCallback))
-
+ val pinPadView = view.findViewById(R.id.pin_pad) as? IPinPad
+ if (pinPadView != null) {
+ PinPadViewBinder.bind(pinPadView, view)
+ }
repeatOnLifecycle(Lifecycle.State.STARTED) {
// dismiss on a valid credential check
launch {
viewModel.validatedAttestation.collect { attestation ->
if (attestation != null) {
- imeManager.hideSoftInputFromWindow(view.windowToken, 0 /* flags */)
+ imeManager.hideSoftInputFromWindow(
+ view.windowToken,
+ 0 // flag
+ )
host.onCredentialMatched(attestation)
} else {
passwordField.setText("")
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialViewBinder.kt
index 4ac9f967920f..25fe61916644 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialViewBinder.kt
@@ -47,6 +47,7 @@ object CredentialViewBinder {
val descriptionView: TextView = view.requireViewById(R.id.description)
val iconView: ImageView? = view.findViewById(R.id.icon)
val errorView: TextView = view.requireViewById(R.id.error)
+ val cancelButton: Button? = view.findViewById(R.id.cancel_button)
val emergencyButtonView: Button = view.requireViewById(R.id.emergencyCallButton)
var errorTimer: Job? = null
@@ -60,7 +61,7 @@ object CredentialViewBinder {
updateForContentDimensions(
containerWidth,
containerHeight,
- 0 /* animateDurationMs */
+ 0 // animateDurationMs
)
}
}
@@ -103,7 +104,18 @@ object CredentialViewBinder {
}
}
}
- .collect { errorView.textOrHide = it }
+ .collect { it ->
+ val hasError = !it.isNullOrBlank()
+ errorView.visibility =
+ if (hasError) {
+ View.VISIBLE
+ } else if (cancelButton != null) {
+ View.INVISIBLE
+ } else {
+ View.GONE
+ }
+ errorView.text = if (hasError) it else ""
+ }
}
// show an extra dialog if the remaining attempts becomes low
@@ -117,6 +129,8 @@ object CredentialViewBinder {
}
}
+ cancelButton?.setOnClickListener { host.onCredentialAborted() }
+
// bind the auth widget
when (view) {
is CredentialPasswordView ->
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/PinPadViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/PinPadViewBinder.kt
new file mode 100644
index 000000000000..906206c27455
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/PinPadViewBinder.kt
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2023 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.biometrics.ui.binder
+
+import android.view.KeyEvent
+import android.widget.ImeAwareEditText
+import com.android.internal.widget.LockscreenCredential
+import com.android.systemui.R
+import com.android.systemui.biometrics.ui.CredentialPasswordView
+import com.android.systemui.biometrics.ui.IPinPad
+import com.android.systemui.biometrics.ui.PinPadClickListener
+
+/** Binder for IPinPad */
+object PinPadViewBinder {
+ /** Implements a PinPadClickListener inside a pin pad */
+ @JvmStatic
+ fun bind(view: IPinPad, credentialPasswordView: CredentialPasswordView) {
+ val passwordField: ImeAwareEditText =
+ credentialPasswordView.requireViewById(R.id.lockPassword)
+ view.setPinPadClickListener(
+ object : PinPadClickListener {
+
+ override fun onDigitKeyClick(digit: String?) {
+ passwordField.append(digit)
+ }
+
+ override fun onBackspaceClick() {
+ val pin = LockscreenCredential.createPinOrNone(passwordField.text)
+ if (pin.size() > 0) {
+ passwordField.text.delete(
+ passwordField.selectionEnd - 1,
+ passwordField.selectionEnd
+ )
+ }
+ pin.zeroize()
+ }
+
+ override fun onEnterKeyClick() {
+ passwordField.dispatchKeyEvent(
+ KeyEvent(0, 0, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER, 0)
+ )
+ passwordField.dispatchKeyEvent(
+ KeyEvent(0, 0, KeyEvent.ACTION_UP, KeyEvent.KEYCODE_ENTER, 0)
+ )
+ }
+ }
+ )
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
index 4dc7720ef447..e8b8f54220aa 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
@@ -289,7 +289,7 @@ constructor(
if (authenticateAfterError) {
showAuthenticating(messageAfterError)
} else {
- showHelp(messageAfterError)
+ showInfo(messageAfterError)
}
}
}
@@ -309,12 +309,15 @@ constructor(
private fun supportsRetry(failedModality: BiometricModality) =
failedModality == BiometricModality.Face
+ suspend fun showHelp(message: String) = showHelp(message, clearIconError = false)
+ suspend fun showInfo(message: String) = showHelp(message, clearIconError = true)
+
/**
* Show a persistent help message.
*
* Will be show even if the user has already authenticated.
*/
- suspend fun showHelp(message: String) {
+ private suspend fun showHelp(message: String, clearIconError: Boolean) {
val alreadyAuthenticated = _isAuthenticated.value.isAuthenticated
if (!alreadyAuthenticated) {
_isAuthenticating.value = false
@@ -329,6 +332,8 @@ constructor(
AuthBiometricView.STATE_PENDING_CONFIRMATION
} else if (alreadyAuthenticated && !isConfirmationRequired.first()) {
AuthBiometricView.STATE_AUTHENTICATED
+ } else if (clearIconError) {
+ AuthBiometricView.STATE_IDLE
} else {
AuthBiometricView.STATE_HELP
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
index 484be9ce1975..1b2a9ebc9ca4 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
@@ -49,6 +49,7 @@ import com.android.systemui.recents.Recents
import com.android.systemui.settings.dagger.MultiUserUtilsModule
import com.android.systemui.shortcut.ShortcutKeyDispatcher
import com.android.systemui.statusbar.ImmersiveModeConfirmation
+import com.android.systemui.statusbar.gesture.GesturePointerEventListener
import com.android.systemui.statusbar.notification.InstantAppNotifier
import com.android.systemui.statusbar.phone.KeyguardLiftController
import com.android.systemui.statusbar.phone.LockscreenWallpaper
@@ -182,6 +183,12 @@ abstract class SystemUICoreStartableModule {
@ClassKey(ScreenDecorations::class)
abstract fun bindScreenDecorations(sysui: ScreenDecorations): CoreStartable
+ /** Inject into GesturePointerEventHandler. */
+ @Binds
+ @IntoMap
+ @ClassKey(GesturePointerEventListener::class)
+ abstract fun bindGesturePointerEventListener(sysui: GesturePointerEventListener): CoreStartable
+
/** Inject into SessionTracker. */
@Binds
@IntoMap
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
index 040ee7938f1d..3eb17400446c 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
@@ -128,11 +128,11 @@ import com.android.systemui.settings.UserTracker;
import com.android.systemui.shade.ShadeController;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.VibratorHelper;
-import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.LightBarController;
import com.android.systemui.statusbar.phone.SystemUIDialog;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.statusbar.window.StatusBarWindowController;
import com.android.systemui.telephony.TelephonyListenerManager;
import com.android.systemui.util.EmergencyDialerConstants;
import com.android.systemui.util.RingerModeTracker;
@@ -141,7 +141,6 @@ import com.android.systemui.util.settings.SecureSettings;
import java.util.ArrayList;
import java.util.List;
-import java.util.Optional;
import java.util.concurrent.Executor;
import javax.inject.Inject;
@@ -244,6 +243,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
private final IStatusBarService mStatusBarService;
protected final LightBarController mLightBarController;
protected final NotificationShadeWindowController mNotificationShadeWindowController;
+ private final StatusBarWindowController mStatusBarWindowController;
private final IWindowManager mIWindowManager;
private final Executor mBackgroundExecutor;
private final RingerModeTracker mRingerModeTracker;
@@ -251,7 +251,6 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
protected Handler mMainHandler;
private int mSmallestScreenWidthDp;
private int mOrientation;
- private final Optional<CentralSurfaces> mCentralSurfacesOptional;
private final ShadeController mShadeController;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private final DialogLaunchAnimator mDialogLaunchAnimator;
@@ -356,13 +355,13 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
IStatusBarService statusBarService,
LightBarController lightBarController,
NotificationShadeWindowController notificationShadeWindowController,
+ StatusBarWindowController statusBarWindowController,
IWindowManager iWindowManager,
@Background Executor backgroundExecutor,
UiEventLogger uiEventLogger,
RingerModeTracker ringerModeTracker,
@Main Handler handler,
PackageManager packageManager,
- Optional<CentralSurfaces> centralSurfacesOptional,
ShadeController shadeController,
KeyguardUpdateMonitor keyguardUpdateMonitor,
DialogLaunchAnimator dialogLaunchAnimator) {
@@ -390,13 +389,13 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
mStatusBarService = statusBarService;
mLightBarController = lightBarController;
mNotificationShadeWindowController = notificationShadeWindowController;
+ mStatusBarWindowController = statusBarWindowController;
mIWindowManager = iWindowManager;
mBackgroundExecutor = backgroundExecutor;
mRingerModeTracker = ringerModeTracker;
mMainHandler = handler;
mSmallestScreenWidthDp = resources.getConfiguration().smallestScreenWidthDp;
mOrientation = resources.getConfiguration().orientation;
- mCentralSurfacesOptional = centralSurfacesOptional;
mShadeController = shadeController;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mDialogLaunchAnimator = dialogLaunchAnimator;
@@ -449,10 +448,6 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
return mUiEventLogger;
}
- protected Optional<CentralSurfaces> getCentralSurfaces() {
- return mCentralSurfacesOptional;
- }
-
protected KeyguardUpdateMonitor getKeyguardUpdateMonitor() {
return mKeyguardUpdateMonitor;
}
@@ -701,12 +696,21 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
protected ActionsDialogLite createDialog() {
initDialogItems();
- ActionsDialogLite dialog = new ActionsDialogLite(mContext,
+ ActionsDialogLite dialog = new ActionsDialogLite(
+ mContext,
com.android.systemui.R.style.Theme_SystemUI_Dialog_GlobalActionsLite,
- mAdapter, mOverflowAdapter, mSysuiColorExtractor, mStatusBarService,
+ mAdapter,
+ mOverflowAdapter,
+ mSysuiColorExtractor,
+ mStatusBarService,
mLightBarController,
- mNotificationShadeWindowController, this::onRefresh, mKeyguardShowing,
- mPowerAdapter, mUiEventLogger, mCentralSurfacesOptional,
+ mKeyguardStateController,
+ mNotificationShadeWindowController,
+ mStatusBarWindowController,
+ this::onRefresh,
+ mKeyguardShowing,
+ mPowerAdapter,
+ mUiEventLogger,
mShadeController,
mKeyguardUpdateMonitor,
mLockPatternUtils);
@@ -2208,13 +2212,14 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
private boolean mKeyguardShowing;
protected float mScrimAlpha;
protected final LightBarController mLightBarController;
+ private final KeyguardStateController mKeyguardStateController;
protected final NotificationShadeWindowController mNotificationShadeWindowController;
+ private final StatusBarWindowController mStatusBarWindowController;
private ListPopupWindow mOverflowPopup;
private Dialog mPowerOptionsDialog;
protected final Runnable mOnRefreshCallback;
private UiEventLogger mUiEventLogger;
private GestureDetector mGestureDetector;
- private Optional<CentralSurfaces> mCentralSurfacesOptional;
private final ShadeController mShadeController;
private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private LockPatternUtils mLockPatternUtils;
@@ -2248,8 +2253,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
float distanceY) {
if (distanceY < 0 && distanceY > distanceX
- && e1.getY() <= mCentralSurfacesOptional.map(
- CentralSurfaces::getStatusBarHeight).orElse(0)) {
+ && e1.getY() <= mStatusBarWindowController.getStatusBarHeight()) {
// Downwards scroll from top
openShadeAndDismiss();
return true;
@@ -2261,8 +2265,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
float velocityY) {
if (velocityY > 0 && Math.abs(velocityY) > Math.abs(velocityX)
- && e1.getY() <= mCentralSurfacesOptional.map(
- CentralSurfaces::getStatusBarHeight).orElse(0)) {
+ && e1.getY() <= mStatusBarWindowController.getStatusBarHeight()) {
// Downwards fling from top
openShadeAndDismiss();
return true;
@@ -2281,14 +2284,20 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
mOverriddenBackDispatcher = mockDispatcher;
}
- ActionsDialogLite(Context context, int themeRes, MyAdapter adapter,
+ ActionsDialogLite(Context context,
+ int themeRes,
+ MyAdapter adapter,
MyOverflowAdapter overflowAdapter,
- SysuiColorExtractor sysuiColorExtractor, IStatusBarService statusBarService,
+ SysuiColorExtractor sysuiColorExtractor,
+ IStatusBarService statusBarService,
LightBarController lightBarController,
+ KeyguardStateController keyguardStateController,
NotificationShadeWindowController notificationShadeWindowController,
- Runnable onRefreshCallback, boolean keyguardShowing,
- MyPowerOptionsAdapter powerAdapter, UiEventLogger uiEventLogger,
- Optional<CentralSurfaces> centralSurfacesOptional,
+ StatusBarWindowController statusBarWindowController,
+ Runnable onRefreshCallback,
+ boolean keyguardShowing,
+ MyPowerOptionsAdapter powerAdapter,
+ UiEventLogger uiEventLogger,
ShadeController shadeController,
KeyguardUpdateMonitor keyguardUpdateMonitor,
LockPatternUtils lockPatternUtils) {
@@ -2302,11 +2311,12 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
mColorExtractor = sysuiColorExtractor;
mStatusBarService = statusBarService;
mLightBarController = lightBarController;
+ mKeyguardStateController = keyguardStateController;
mNotificationShadeWindowController = notificationShadeWindowController;
+ mStatusBarWindowController = statusBarWindowController;
mOnRefreshCallback = onRefreshCallback;
mKeyguardShowing = keyguardShowing;
mUiEventLogger = uiEventLogger;
- mCentralSurfacesOptional = centralSurfacesOptional;
mShadeController = shadeController;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mLockPatternUtils = lockPatternUtils;
@@ -2355,7 +2365,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
private void openShadeAndDismiss() {
mUiEventLogger.log(GlobalActionsEvent.GA_CLOSE_TAP_OUTSIDE);
- if (mCentralSurfacesOptional.map(CentralSurfaces::isKeyguardShowing).orElse(false)) {
+ if (mKeyguardStateController.isShowing()) {
// match existing lockscreen behavior to open QS when swiping from status bar
mShadeController.animateExpandQs();
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractor.kt
index e501ece626b4..635961b0ea01 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractor.kt
@@ -54,11 +54,7 @@ constructor(
if (event.handleAction()) {
when (event.keyCode) {
KeyEvent.KEYCODE_MENU -> return dispatchMenuKeyEvent()
- KeyEvent.KEYCODE_SPACE,
- KeyEvent.KEYCODE_ENTER ->
- if (isDeviceInteractive()) {
- return collapseShadeLockedOrShowPrimaryBouncer()
- }
+ KeyEvent.KEYCODE_SPACE -> return dispatchSpaceEvent()
}
}
return false
@@ -94,22 +90,16 @@ constructor(
(statusBarStateController.state != StatusBarState.SHADE) &&
statusBarKeyguardViewManager.shouldDismissOnMenuPressed()
if (shouldUnlockOnMenuPressed) {
- return collapseShadeLockedOrShowPrimaryBouncer()
+ shadeController.animateCollapseShadeForced()
+ return true
}
return false
}
- private fun collapseShadeLockedOrShowPrimaryBouncer(): Boolean {
- when (statusBarStateController.state) {
- StatusBarState.SHADE -> return false
- StatusBarState.SHADE_LOCKED -> {
- shadeController.animateCollapseShadeForced()
- return true
- }
- StatusBarState.KEYGUARD -> {
- statusBarKeyguardViewManager.showPrimaryBouncer(true)
- return true
- }
+ private fun dispatchSpaceEvent(): Boolean {
+ if (isDeviceInteractive() && statusBarStateController.state != StatusBarState.SHADE) {
+ shadeController.animateCollapseShadeForced()
+ return true
}
return false
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/qualifiers/KeyguardRootView.kt b/packages/SystemUI/src/com/android/systemui/keyguard/qualifiers/KeyguardRootView.kt
new file mode 100644
index 000000000000..c2d2725f4f06
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/qualifiers/KeyguardRootView.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.qualifiers
+
+import javax.inject.Qualifier
+
+@Qualifier
+@MustBeDocumented
+@Retention(AnnotationRetention.RUNTIME)
+annotation class KeyguardRootView
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSurfaceBehindParamsApplier.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSurfaceBehindParamsApplier.kt
index a5b00e09131a..c8dab3223d33 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSurfaceBehindParamsApplier.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSurfaceBehindParamsApplier.kt
@@ -87,7 +87,7 @@ constructor(
}
addListener(
object : AnimatorListenerAdapter() {
- override fun onAnimationEnd(animation: Animator?) {
+ override fun onAnimationEnd(animation: Animator) {
updateIsAnimatingSurface()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/LockscreenSceneModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/LockscreenSceneModule.kt
new file mode 100644
index 000000000000..c88737e6bd70
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/LockscreenSceneModule.kt
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2023 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.
+ */
+
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
+package com.android.systemui.keyguard.ui.view
+
+import android.view.View
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.KeyguardViewConfigurator
+import com.android.systemui.keyguard.qualifiers.KeyguardRootView
+import dagger.Module
+import dagger.Provides
+import javax.inject.Provider
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+
+@Module
+object LockscreenSceneModule {
+
+ @Provides
+ @SysUISingleton
+ @KeyguardRootView
+ fun viewProvider(
+ configurator: Provider<KeyguardViewConfigurator>,
+ ): () -> View {
+ return { configurator.get().getKeyguardRootView() }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt
index 6d3b7f18e974..93c4902332c5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt
@@ -16,41 +16,22 @@
package com.android.systemui.keyguard.ui.viewmodel
-import com.android.systemui.R
import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor
import com.android.systemui.bouncer.domain.interactor.BouncerInteractor
-import com.android.systemui.common.shared.model.ContentDescription
-import com.android.systemui.common.shared.model.Icon
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.scene.shared.model.SceneKey
import javax.inject.Inject
-import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.SharingStarted
-import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.stateIn
/** Models UI state and handles user input for the lockscreen scene. */
@SysUISingleton
class LockscreenSceneViewModel
@Inject
constructor(
- @Application applicationScope: CoroutineScope,
authenticationInteractor: AuthenticationInteractor,
private val bouncerInteractor: BouncerInteractor,
) {
- /** The icon for the "lock" button on the lockscreen. */
- val lockButtonIcon: StateFlow<Icon> =
- authenticationInteractor.isUnlocked
- .map { isUnlocked -> lockIcon(isUnlocked = isUnlocked) }
- .stateIn(
- scope = applicationScope,
- started = SharingStarted.WhileSubscribed(),
- initialValue = lockIcon(isUnlocked = authenticationInteractor.isUnlocked.value),
- )
-
/** The key of the scene we should switch to when swiping up. */
val upDestinationSceneKey: Flow<SceneKey> =
authenticationInteractor.isUnlocked.map { isUnlocked ->
@@ -65,31 +46,4 @@ constructor(
fun onLockButtonClicked() {
bouncerInteractor.showOrUnlockDevice()
}
-
- /** Notifies that some content on the lock screen was clicked. */
- fun onContentClicked() {
- bouncerInteractor.showOrUnlockDevice()
- }
-
- private fun upDestinationSceneKey(
- canSwipeToDismiss: Boolean,
- ): SceneKey {
- return if (canSwipeToDismiss) SceneKey.Gone else SceneKey.Bouncer
- }
-
- private fun lockIcon(
- isUnlocked: Boolean,
- ): Icon {
- return if (isUnlocked) {
- Icon.Resource(
- R.drawable.ic_device_lock_off,
- ContentDescription.Resource(R.string.accessibility_unlock_button)
- )
- } else {
- Icon.Resource(
- R.drawable.ic_device_lock_on,
- ContentDescription.Resource(R.string.accessibility_lock_icon)
- )
- }
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java
index 72352e397806..cbd9a34e9a66 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java
@@ -54,6 +54,7 @@ import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDevicePolicyResolver;
import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDisabledDialog;
+import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.screenrecord.MediaProjectionPermissionDialog;
import com.android.systemui.screenrecord.ScreenShareOption;
import com.android.systemui.statusbar.phone.SystemUIDialog;
@@ -71,6 +72,7 @@ public class MediaProjectionPermissionActivity extends Activity
private final FeatureFlags mFeatureFlags;
private final Lazy<ScreenCaptureDevicePolicyResolver> mScreenCaptureDevicePolicyResolver;
+ private final ActivityStarter mActivityStarter;
private String mPackageName;
private int mUid;
@@ -86,8 +88,10 @@ public class MediaProjectionPermissionActivity extends Activity
@Inject
public MediaProjectionPermissionActivity(FeatureFlags featureFlags,
- Lazy<ScreenCaptureDevicePolicyResolver> screenCaptureDevicePolicyResolver) {
+ Lazy<ScreenCaptureDevicePolicyResolver> screenCaptureDevicePolicyResolver,
+ ActivityStarter activityStarter) {
mFeatureFlags = featureFlags;
+ mActivityStarter = activityStarter;
mScreenCaptureDevicePolicyResolver = screenCaptureDevicePolicyResolver;
}
@@ -306,8 +310,16 @@ public class MediaProjectionPermissionActivity extends Activity
// Start activity from the current foreground user to avoid creating a separate
// SystemUI process without access to recent tasks because it won't have
// WM Shell running inside.
+ // It is also important to make sure the shade is dismissed, otherwise users won't
+ // see the app selector.
mUserSelectingTask = true;
- startActivityAsUser(intent, UserHandle.of(ActivityManager.getCurrentUser()));
+ mActivityStarter.startActivity(
+ intent,
+ /* dismissShade= */ true,
+ /* animationController= */ null,
+ /* showOverLockscreenWhenLocked= */ false,
+ UserHandle.of(ActivityManager.getCurrentUser())
+ );
}
} catch (RemoteException e) {
Log.e(TAG, "Error granting projection permission", e);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index 9f45f66d9971..1e82d44e413e 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -87,7 +87,6 @@ import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
-import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.model.SysUiState;
import com.android.systemui.navigationbar.NavigationBar;
@@ -571,7 +570,6 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
SysUiState sysUiState,
Provider<SceneInteractor> sceneInteractor,
UserTracker userTracker,
- ScreenLifecycle screenLifecycle,
WakefulnessLifecycle wakefulnessLifecycle,
UiEventLogger uiEventLogger,
DisplayTracker displayTracker,
@@ -651,7 +649,6 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
// Listen for user setup
mUserTracker.addCallback(mUserChangedCallback, mMainExecutor);
- screenLifecycle.addObserver(mScreenLifecycleObserver);
wakefulnessLifecycle.addObserver(mWakefulnessLifecycleObserver);
// Connect to the service
updateEnabledAndBinding();
@@ -923,60 +920,6 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
}
}
- private final ScreenLifecycle.Observer mScreenLifecycleObserver =
- new ScreenLifecycle.Observer() {
- /**
- * Notifies the Launcher that screen turned on and ready to use
- */
- @Override
- public void onScreenTurnedOn() {
- try {
- if (mOverviewProxy != null) {
- mOverviewProxy.onScreenTurnedOn();
- } else {
- Log.e(TAG_OPS,
- "Failed to get overview proxy for screen turned on event.");
- }
- } catch (RemoteException e) {
- Log.e(TAG_OPS, "Failed to call onScreenTurnedOn()", e);
- }
- }
-
- /**
- * Notifies the Launcher that screen is starting to turn on.
- */
- @Override
- public void onScreenTurningOff() {
- try {
- if (mOverviewProxy != null) {
- mOverviewProxy.onScreenTurningOff();
- } else {
- Log.e(TAG_OPS,
- "Failed to get overview proxy for screen turning off event.");
- }
- } catch (RemoteException e) {
- Log.e(TAG_OPS, "Failed to call onScreenTurningOff()", e);
- }
- }
-
- /**
- * Notifies the Launcher that screen is starting to turn on.
- */
- @Override
- public void onScreenTurningOn() {
- try {
- if (mOverviewProxy != null) {
- mOverviewProxy.onScreenTurningOn();
- } else {
- Log.e(TAG_OPS,
- "Failed to get overview proxy for screen turning on event.");
- }
- } catch (RemoteException e) {
- Log.e(TAG_OPS, "Failed to call onScreenTurningOn()", e);
- }
- }
- };
-
private final WakefulnessLifecycle.Observer mWakefulnessLifecycleObserver =
new WakefulnessLifecycle.Observer() {
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt b/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt
index 398e64b1981b..714795109454 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt
@@ -16,6 +16,7 @@
package com.android.systemui.scene
+import com.android.systemui.keyguard.ui.view.LockscreenSceneModule
import com.android.systemui.scene.domain.startable.SceneContainerStartableModule
import com.android.systemui.scene.shared.model.SceneContainerConfigModule
import com.android.systemui.scene.ui.composable.SceneModule
@@ -24,6 +25,7 @@ import dagger.Module
@Module(
includes =
[
+ LockscreenSceneModule::class,
SceneContainerConfigModule::class,
SceneContainerStartableModule::class,
SceneModule::class,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/gesture/GesturePointerEventDetector.kt b/packages/SystemUI/src/com/android/systemui/statusbar/gesture/GesturePointerEventDetector.kt
new file mode 100644
index 000000000000..b34c3ac5af41
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/gesture/GesturePointerEventDetector.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.gesture
+
+import android.content.Context
+import android.view.InputEvent
+import android.view.MotionEvent
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.settings.DisplayTracker
+import javax.inject.Inject
+
+/**
+ * A class to detect when a motion event happens. To be notified when the event is detected, add a
+ * callback via [addOnGestureDetectedCallback].
+ */
+@SysUISingleton
+class GesturePointerEventDetector @Inject constructor(
+ private val context: Context,
+ displayTracker: DisplayTracker
+) : GenericGestureDetector(
+ GesturePointerEventDetector::class.simpleName!!,
+ displayTracker.defaultDisplayId
+) {
+ override fun onInputEvent(ev: InputEvent) {
+ if (ev !is MotionEvent) {
+ return
+ }
+ // Pass all events to [gestureDetector], which will then notify [gestureListener] when a tap
+ // is detected.
+ onGestureDetected(ev)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/gesture/GesturePointerEventListener.kt b/packages/SystemUI/src/com/android/systemui/statusbar/gesture/GesturePointerEventListener.kt
new file mode 100644
index 000000000000..8505c5ff32e0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/gesture/GesturePointerEventListener.kt
@@ -0,0 +1,504 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.statusbar.gesture
+
+import android.content.Context
+import android.graphics.Rect
+import android.graphics.Region
+import android.hardware.display.DisplayManagerGlobal
+import android.os.Handler
+import android.os.Looper
+import android.os.SystemClock
+import android.util.Log
+import android.view.DisplayCutout
+import android.view.DisplayInfo
+import android.view.GestureDetector
+import android.view.InputDevice
+import android.view.InputEvent
+import android.view.MotionEvent
+import android.view.MotionEvent.AXIS_GESTURE_SWIPE_FINGER_COUNT
+import android.view.MotionEvent.CLASSIFICATION_MULTI_FINGER_SWIPE
+import android.view.ViewRootImpl.CLIENT_TRANSIENT
+import android.widget.OverScroller
+import com.android.internal.R
+import com.android.systemui.CoreStartable
+import java.io.PrintWriter
+import javax.inject.Inject
+
+/**
+ * Watches for gesture events that may trigger system bar related events and notify the registered
+ * callbacks. Add callback to this listener by calling {@link setCallbacks}.
+ */
+class GesturePointerEventListener
+@Inject
+constructor(context: Context, gestureDetector: GesturePointerEventDetector) : CoreStartable {
+ private val mContext: Context
+ private val mHandler = Handler(Looper.getMainLooper())
+ private var mGestureDetector: GesturePointerEventDetector
+ private var mFlingGestureDetector: GestureDetector? = null
+ private var mDisplayCutoutTouchableRegionSize = 0
+
+ // The thresholds for each edge of the display
+ private val mSwipeStartThreshold = Rect()
+ private var mSwipeDistanceThreshold = 0
+ private var mCallbacks: Callbacks? = null
+ private val mDownPointerId = IntArray(MAX_TRACKED_POINTERS)
+ private val mDownX = FloatArray(MAX_TRACKED_POINTERS)
+ private val mDownY = FloatArray(MAX_TRACKED_POINTERS)
+ private val mDownTime = LongArray(MAX_TRACKED_POINTERS)
+ var screenHeight = 0
+ var screenWidth = 0
+ private var mDownPointers = 0
+ private var mSwipeFireable = false
+ private var mDebugFireable = false
+ private var mMouseHoveringAtLeft = false
+ private var mMouseHoveringAtTop = false
+ private var mMouseHoveringAtRight = false
+ private var mMouseHoveringAtBottom = false
+ private var mLastFlingTime: Long = 0
+
+ init {
+ mContext = checkNull("context", context)
+ mGestureDetector = checkNull("gesture detector", gestureDetector)
+ onConfigurationChanged()
+ }
+
+ override fun start() {
+ if (!CLIENT_TRANSIENT) {
+ return
+ }
+ mGestureDetector.addOnGestureDetectedCallback(TAG) { ev -> onInputEvent(ev) }
+ mGestureDetector.startGestureListening()
+
+ mFlingGestureDetector =
+ object : GestureDetector(mContext, FlingGestureDetector(), mHandler) {}
+ }
+
+ fun onDisplayInfoChanged(info: DisplayInfo) {
+ screenWidth = info.logicalWidth
+ screenHeight = info.logicalHeight
+ onConfigurationChanged()
+ }
+
+ fun onConfigurationChanged() {
+ if (!CLIENT_TRANSIENT) {
+ return
+ }
+ val r = mContext.resources
+ val defaultThreshold = r.getDimensionPixelSize(R.dimen.system_gestures_start_threshold)
+ mSwipeStartThreshold[defaultThreshold, defaultThreshold, defaultThreshold] =
+ defaultThreshold
+ mSwipeDistanceThreshold = defaultThreshold
+ val display = DisplayManagerGlobal.getInstance().getRealDisplay(mContext.displayId)
+ val displayCutout = display.cutout
+ if (displayCutout != null) {
+ // Expand swipe start threshold such that we can catch touches that just start beyond
+ // the notch area
+ mDisplayCutoutTouchableRegionSize =
+ r.getDimensionPixelSize(R.dimen.display_cutout_touchable_region_size)
+ val bounds = displayCutout.boundingRectsAll
+ if (bounds[DisplayCutout.BOUNDS_POSITION_LEFT] != null) {
+ mSwipeStartThreshold.left =
+ Math.max(
+ mSwipeStartThreshold.left,
+ bounds[DisplayCutout.BOUNDS_POSITION_LEFT]!!.width() +
+ mDisplayCutoutTouchableRegionSize
+ )
+ }
+ if (bounds[DisplayCutout.BOUNDS_POSITION_TOP] != null) {
+ mSwipeStartThreshold.top =
+ Math.max(
+ mSwipeStartThreshold.top,
+ bounds[DisplayCutout.BOUNDS_POSITION_TOP]!!.height() +
+ mDisplayCutoutTouchableRegionSize
+ )
+ }
+ if (bounds[DisplayCutout.BOUNDS_POSITION_RIGHT] != null) {
+ mSwipeStartThreshold.right =
+ Math.max(
+ mSwipeStartThreshold.right,
+ bounds[DisplayCutout.BOUNDS_POSITION_RIGHT]!!.width() +
+ mDisplayCutoutTouchableRegionSize
+ )
+ }
+ if (bounds[DisplayCutout.BOUNDS_POSITION_BOTTOM] != null) {
+ mSwipeStartThreshold.bottom =
+ Math.max(
+ mSwipeStartThreshold.bottom,
+ bounds[DisplayCutout.BOUNDS_POSITION_BOTTOM]!!.height() +
+ mDisplayCutoutTouchableRegionSize
+ )
+ }
+ }
+ if (DEBUG)
+ Log.d(
+ TAG,
+ "mSwipeStartThreshold=$mSwipeStartThreshold" +
+ " mSwipeDistanceThreshold=$mSwipeDistanceThreshold"
+ )
+ }
+
+ fun onInputEvent(ev: InputEvent) {
+ if (ev !is MotionEvent) {
+ return
+ }
+ if (DEBUG) Log.d(TAG, "Received motion event $ev")
+ if (ev.isTouchEvent) {
+ mFlingGestureDetector?.onTouchEvent(ev)
+ }
+ when (ev.actionMasked) {
+ MotionEvent.ACTION_DOWN -> {
+ mSwipeFireable = true
+ mDebugFireable = true
+ mDownPointers = 0
+ captureDown(ev, 0)
+ if (mMouseHoveringAtLeft) {
+ mMouseHoveringAtLeft = false
+ mCallbacks?.onMouseLeaveFromLeft()
+ }
+ if (mMouseHoveringAtTop) {
+ mMouseHoveringAtTop = false
+ mCallbacks?.onMouseLeaveFromTop()
+ }
+ if (mMouseHoveringAtRight) {
+ mMouseHoveringAtRight = false
+ mCallbacks?.onMouseLeaveFromRight()
+ }
+ if (mMouseHoveringAtBottom) {
+ mMouseHoveringAtBottom = false
+ mCallbacks?.onMouseLeaveFromBottom()
+ }
+ mCallbacks?.onDown()
+ }
+ MotionEvent.ACTION_POINTER_DOWN -> {
+ captureDown(ev, ev.actionIndex)
+ if (mDebugFireable) {
+ mDebugFireable = ev.pointerCount < 5
+ if (!mDebugFireable) {
+ if (DEBUG) Log.d(TAG, "Firing debug")
+ mCallbacks?.onDebug()
+ }
+ }
+ }
+ MotionEvent.ACTION_MOVE ->
+ if (mSwipeFireable) {
+ val trackpadSwipe = detectTrackpadThreeFingerSwipe(ev)
+ mSwipeFireable = trackpadSwipe == TRACKPAD_SWIPE_NONE
+ if (!mSwipeFireable) {
+ if (trackpadSwipe == TRACKPAD_SWIPE_FROM_TOP) {
+ if (DEBUG) Log.d(TAG, "Firing onSwipeFromTop from trackpad")
+ mCallbacks?.onSwipeFromTop()
+ } else if (trackpadSwipe == TRACKPAD_SWIPE_FROM_BOTTOM) {
+ if (DEBUG) Log.d(TAG, "Firing onSwipeFromBottom from trackpad")
+ mCallbacks?.onSwipeFromBottom()
+ } else if (trackpadSwipe == TRACKPAD_SWIPE_FROM_RIGHT) {
+ if (DEBUG) Log.d(TAG, "Firing onSwipeFromRight from trackpad")
+ mCallbacks?.onSwipeFromRight()
+ } else if (trackpadSwipe == TRACKPAD_SWIPE_FROM_LEFT) {
+ if (DEBUG) Log.d(TAG, "Firing onSwipeFromLeft from trackpad")
+ mCallbacks?.onSwipeFromLeft()
+ }
+ } else {
+ val swipe = detectSwipe(ev)
+ mSwipeFireable = swipe == SWIPE_NONE
+ if (swipe == SWIPE_FROM_TOP) {
+ if (DEBUG) Log.d(TAG, "Firing onSwipeFromTop")
+ mCallbacks?.onSwipeFromTop()
+ } else if (swipe == SWIPE_FROM_BOTTOM) {
+ if (DEBUG) Log.d(TAG, "Firing onSwipeFromBottom")
+ mCallbacks?.onSwipeFromBottom()
+ } else if (swipe == SWIPE_FROM_RIGHT) {
+ if (DEBUG) Log.d(TAG, "Firing onSwipeFromRight")
+ mCallbacks?.onSwipeFromRight()
+ } else if (swipe == SWIPE_FROM_LEFT) {
+ if (DEBUG) Log.d(TAG, "Firing onSwipeFromLeft")
+ mCallbacks?.onSwipeFromLeft()
+ }
+ }
+ }
+ MotionEvent.ACTION_HOVER_MOVE ->
+ if (ev.isFromSource(InputDevice.SOURCE_MOUSE)) {
+ val eventX = ev.x
+ val eventY = ev.y
+ if (!mMouseHoveringAtLeft && eventX == 0f) {
+ mCallbacks?.onMouseHoverAtLeft()
+ mMouseHoveringAtLeft = true
+ } else if (mMouseHoveringAtLeft && eventX > 0) {
+ mCallbacks?.onMouseLeaveFromLeft()
+ mMouseHoveringAtLeft = false
+ }
+ if (!mMouseHoveringAtTop && eventY == 0f) {
+ mCallbacks?.onMouseHoverAtTop()
+ mMouseHoveringAtTop = true
+ } else if (mMouseHoveringAtTop && eventY > 0) {
+ mCallbacks?.onMouseLeaveFromTop()
+ mMouseHoveringAtTop = false
+ }
+ if (!mMouseHoveringAtRight && eventX >= screenWidth - 1) {
+ mCallbacks?.onMouseHoverAtRight()
+ mMouseHoveringAtRight = true
+ } else if (mMouseHoveringAtRight && eventX < screenWidth - 1) {
+ mCallbacks?.onMouseLeaveFromRight()
+ mMouseHoveringAtRight = false
+ }
+ if (!mMouseHoveringAtBottom && eventY >= screenHeight - 1) {
+ mCallbacks?.onMouseHoverAtBottom()
+ mMouseHoveringAtBottom = true
+ } else if (mMouseHoveringAtBottom && eventY < screenHeight - 1) {
+ mCallbacks?.onMouseLeaveFromBottom()
+ mMouseHoveringAtBottom = false
+ }
+ }
+ MotionEvent.ACTION_UP,
+ MotionEvent.ACTION_CANCEL -> {
+ mSwipeFireable = false
+ mDebugFireable = false
+ mCallbacks?.onUpOrCancel()
+ }
+ else -> if (DEBUG) Log.d(TAG, "Ignoring $ev")
+ }
+ }
+
+ fun setCallbacks(callbacks: Callbacks) {
+ mCallbacks = callbacks
+ }
+
+ private fun captureDown(event: MotionEvent, pointerIndex: Int) {
+ val pointerId = event.getPointerId(pointerIndex)
+ val i = findIndex(pointerId)
+ if (DEBUG) Log.d(TAG, "pointer $pointerId down pointerIndex=$pointerIndex trackingIndex=$i")
+ if (i != UNTRACKED_POINTER) {
+ mDownX[i] = event.getX(pointerIndex)
+ mDownY[i] = event.getY(pointerIndex)
+ mDownTime[i] = event.eventTime
+ if (DEBUG)
+ Log.d(TAG, "pointer " + pointerId + " down x=" + mDownX[i] + " y=" + mDownY[i])
+ }
+ }
+
+ protected fun currentGestureStartedInRegion(r: Region): Boolean {
+ return r.contains(mDownX[0].toInt(), mDownY[0].toInt())
+ }
+
+ private fun findIndex(pointerId: Int): Int {
+ for (i in 0 until mDownPointers) {
+ if (mDownPointerId[i] == pointerId) {
+ return i
+ }
+ }
+ if (mDownPointers == MAX_TRACKED_POINTERS || pointerId == MotionEvent.INVALID_POINTER_ID) {
+ return UNTRACKED_POINTER
+ }
+ mDownPointerId[mDownPointers++] = pointerId
+ return mDownPointers - 1
+ }
+
+ private fun detectTrackpadThreeFingerSwipe(move: MotionEvent): Int {
+ if (!isTrackpadThreeFingerSwipe(move)) {
+ return TRACKPAD_SWIPE_NONE
+ }
+ val dx = move.x - mDownX[0]
+ val dy = move.y - mDownY[0]
+ if (Math.abs(dx) < Math.abs(dy)) {
+ if (Math.abs(dy) > mSwipeDistanceThreshold) {
+ return if (dy > 0) TRACKPAD_SWIPE_FROM_TOP else TRACKPAD_SWIPE_FROM_BOTTOM
+ }
+ } else {
+ if (Math.abs(dx) > mSwipeDistanceThreshold) {
+ return if (dx > 0) TRACKPAD_SWIPE_FROM_LEFT else TRACKPAD_SWIPE_FROM_RIGHT
+ }
+ }
+ return TRACKPAD_SWIPE_NONE
+ }
+
+ private fun isTrackpadThreeFingerSwipe(event: MotionEvent): Boolean {
+ return (event.classification == CLASSIFICATION_MULTI_FINGER_SWIPE &&
+ event.getAxisValue(AXIS_GESTURE_SWIPE_FINGER_COUNT) == 3f)
+ }
+ private fun detectSwipe(move: MotionEvent): Int {
+ val historySize = move.historySize
+ val pointerCount = move.pointerCount
+ for (p in 0 until pointerCount) {
+ val pointerId = move.getPointerId(p)
+ val i = findIndex(pointerId)
+ if (i != UNTRACKED_POINTER) {
+ for (h in 0 until historySize) {
+ val time = move.getHistoricalEventTime(h)
+ val x = move.getHistoricalX(p, h)
+ val y = move.getHistoricalY(p, h)
+ val swipe = detectSwipe(i, time, x, y)
+ if (swipe != SWIPE_NONE) {
+ return swipe
+ }
+ }
+ val swipe = detectSwipe(i, move.eventTime, move.getX(p), move.getY(p))
+ if (swipe != SWIPE_NONE) {
+ return swipe
+ }
+ }
+ }
+ return SWIPE_NONE
+ }
+
+ private fun detectSwipe(i: Int, time: Long, x: Float, y: Float): Int {
+ val fromX = mDownX[i]
+ val fromY = mDownY[i]
+ val elapsed = time - mDownTime[i]
+ if (DEBUG)
+ Log.d(
+ TAG,
+ "pointer " +
+ mDownPointerId[i] +
+ " moved (" +
+ fromX +
+ "->" +
+ x +
+ "," +
+ fromY +
+ "->" +
+ y +
+ ") in " +
+ elapsed
+ )
+ if (
+ fromY <= mSwipeStartThreshold.top &&
+ y > fromY + mSwipeDistanceThreshold &&
+ elapsed < SWIPE_TIMEOUT_MS
+ ) {
+ return SWIPE_FROM_TOP
+ }
+ if (
+ fromY >= screenHeight - mSwipeStartThreshold.bottom &&
+ y < fromY - mSwipeDistanceThreshold &&
+ elapsed < SWIPE_TIMEOUT_MS
+ ) {
+ return SWIPE_FROM_BOTTOM
+ }
+ if (
+ fromX >= screenWidth - mSwipeStartThreshold.right &&
+ x < fromX - mSwipeDistanceThreshold &&
+ elapsed < SWIPE_TIMEOUT_MS
+ ) {
+ return SWIPE_FROM_RIGHT
+ }
+ return if (
+ fromX <= mSwipeStartThreshold.left &&
+ x > fromX + mSwipeDistanceThreshold &&
+ elapsed < SWIPE_TIMEOUT_MS
+ ) {
+ SWIPE_FROM_LEFT
+ } else SWIPE_NONE
+ }
+
+ fun dump(pw: PrintWriter, prefix: String) {
+ val inner = "$prefix "
+ pw.println(prefix + TAG + ":")
+ pw.print(inner)
+ pw.print("mDisplayCutoutTouchableRegionSize=")
+ pw.println(mDisplayCutoutTouchableRegionSize)
+ pw.print(inner)
+ pw.print("mSwipeStartThreshold=")
+ pw.println(mSwipeStartThreshold)
+ pw.print(inner)
+ pw.print("mSwipeDistanceThreshold=")
+ pw.println(mSwipeDistanceThreshold)
+ }
+
+ private inner class FlingGestureDetector internal constructor() :
+ GestureDetector.SimpleOnGestureListener() {
+ private val mOverscroller: OverScroller = OverScroller(mContext)
+
+ override fun onSingleTapUp(e: MotionEvent): Boolean {
+ if (!mOverscroller.isFinished) {
+ mOverscroller.forceFinished(true)
+ }
+ return true
+ }
+
+ override fun onFling(
+ down: MotionEvent?,
+ up: MotionEvent,
+ velocityX: Float,
+ velocityY: Float
+ ): Boolean {
+ mOverscroller.computeScrollOffset()
+ val now = SystemClock.uptimeMillis()
+ if (mLastFlingTime != 0L && now > mLastFlingTime + MAX_FLING_TIME_MILLIS) {
+ mOverscroller.forceFinished(true)
+ }
+ mOverscroller.fling(
+ 0,
+ 0,
+ velocityX.toInt(),
+ velocityY.toInt(),
+ Int.MIN_VALUE,
+ Int.MAX_VALUE,
+ Int.MIN_VALUE,
+ Int.MAX_VALUE
+ )
+ var duration = mOverscroller.duration
+ if (duration > MAX_FLING_TIME_MILLIS) {
+ duration = MAX_FLING_TIME_MILLIS
+ }
+ mLastFlingTime = now
+ mCallbacks?.onFling(duration)
+ return true
+ }
+ }
+
+ interface Callbacks {
+ fun onSwipeFromTop()
+ fun onSwipeFromBottom()
+ fun onSwipeFromRight()
+ fun onSwipeFromLeft()
+ fun onFling(durationMs: Int)
+ fun onDown()
+ fun onUpOrCancel()
+ fun onMouseHoverAtLeft()
+ fun onMouseHoverAtTop()
+ fun onMouseHoverAtRight()
+ fun onMouseHoverAtBottom()
+ fun onMouseLeaveFromLeft()
+ fun onMouseLeaveFromTop()
+ fun onMouseLeaveFromRight()
+ fun onMouseLeaveFromBottom()
+ fun onDebug()
+ }
+
+ companion object {
+ private const val TAG = "GesturePointerEventHandler"
+ private const val DEBUG = false
+ private const val SWIPE_TIMEOUT_MS: Long = 500
+ private const val MAX_TRACKED_POINTERS = 32 // max per input system
+ private const val UNTRACKED_POINTER = -1
+ private const val MAX_FLING_TIME_MILLIS = 5000
+ private const val SWIPE_NONE = 0
+ private const val SWIPE_FROM_TOP = 1
+ private const val SWIPE_FROM_BOTTOM = 2
+ private const val SWIPE_FROM_RIGHT = 3
+ private const val SWIPE_FROM_LEFT = 4
+ private const val TRACKPAD_SWIPE_NONE = 0
+ private const val TRACKPAD_SWIPE_FROM_TOP = 1
+ private const val TRACKPAD_SWIPE_FROM_BOTTOM = 2
+ private const val TRACKPAD_SWIPE_FROM_RIGHT = 3
+ private const val TRACKPAD_SWIPE_FROM_LEFT = 4
+
+ private fun <T> checkNull(name: String, arg: T?): T {
+ requireNotNull(arg) { "$name must not be null" }
+ return arg
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
index 634611122184..d667b91ea5ab 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
@@ -388,7 +388,10 @@ constructor(
})
ssView.setFalsingManager(falsingManager)
ssView.setKeyguardBypassEnabled(bypassController.bypassEnabled)
- return (ssView as View).apply { addOnAttachStateChangeListener(stateChangeListener) }
+ return (ssView as View).apply {
+ setTag(R.id.tag_smartspace_view, Any())
+ addOnAttachStateChangeListener(stateChangeListener)
+ }
}
private fun connectSession() {
@@ -451,12 +454,6 @@ constructor(
session?.requestSmartspaceUpdate()
}
- fun removeViewsFromParent(viewGroup: ViewGroup) {
- smartspaceViews.toList().forEach {
- viewGroup.removeView(it as View)
- }
- }
-
/**
* Disconnects the smartspace view from the smartspace service and cleans up any resources.
*/
@@ -597,3 +594,4 @@ constructor(
}
}
}
+
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt
index 62a0d138fd05..5c2f9a8d28ec 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt
@@ -39,7 +39,10 @@ class StackCoordinator @Inject internal constructor(
override fun attach(pipeline: NotifPipeline) {
pipeline.addOnAfterRenderListListener(::onAfterRenderList)
- groupExpansionManagerImpl.attach(pipeline)
+ // TODO(b/282865576): This has an issue where it makes changes to some groups without
+ // notifying listeners. To be fixed in QPR, but for now let's comment it out to avoid the
+ // group expansion bug.
+ // groupExpansionManagerImpl.attach(pipeline)
}
fun onAfterRenderList(entries: List<ListEntry>, controller: NotifStackController) =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerImpl.java
index 5d33804ab6a7..46af03a438f5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerImpl.java
@@ -67,29 +67,18 @@ public class GroupExpansionManagerImpl implements GroupExpansionManager, Dumpabl
* Cleanup entries from mExpandedGroups that no longer exist in the pipeline.
*/
private final OnBeforeRenderListListener mNotifTracker = (entries) -> {
- if (mExpandedGroups.isEmpty()) {
- return; // nothing to do
- }
-
final Set<NotificationEntry> renderingSummaries = new HashSet<>();
for (ListEntry entry : entries) {
if (entry instanceof GroupEntry) {
renderingSummaries.add(entry.getRepresentativeEntry());
}
}
-
- // Create a copy of mExpandedGroups so we can modify it in a thread-safe way.
- final var currentExpandedGroups = new HashSet<>(mExpandedGroups);
- for (NotificationEntry entry : currentExpandedGroups) {
- setExpanded(entry, renderingSummaries.contains(entry));
- }
+ mExpandedGroups.removeIf(expandedGroup -> !renderingSummaries.contains(expandedGroup));
};
public void attach(NotifPipeline pipeline) {
- if (mFeatureFlags.isEnabled(Flags.NOTIFICATION_GROUP_EXPANSION_CHANGE)) {
- mDumpManager.registerDumpable(this);
- pipeline.addOnBeforeRenderListListener(mNotifTracker);
- }
+ mDumpManager.registerDumpable(this);
+ pipeline.addOnBeforeRenderListListener(mNotifTracker);
}
@Override
@@ -105,24 +94,11 @@ public class GroupExpansionManagerImpl implements GroupExpansionManager, Dumpabl
@Override
public void setGroupExpanded(NotificationEntry entry, boolean expanded) {
final NotificationEntry groupSummary = mGroupMembershipManager.getGroupSummary(entry);
- setExpanded(groupSummary, expanded);
- }
-
- /**
- * Add or remove {@code entry} to/from {@code mExpandedGroups} and notify listeners if
- * something changed. This assumes that {@code entry} is a group summary.
- * <p>
- * TODO(b/293434635): Currently, in spite of its docs,
- * {@code mGroupMembershipManager.getGroupSummary(entry)} returns null if {@code entry} is
- * already a summary. Instead of needing this helper method to bypass that, we probably want to
- * move this code back to {@code setGroupExpanded} and use that everywhere.
- */
- private void setExpanded(NotificationEntry entry, boolean expanded) {
boolean changed;
if (expanded) {
- changed = mExpandedGroups.add(entry);
+ changed = mExpandedGroups.add(groupSummary);
} else {
- changed = mExpandedGroups.remove(entry);
+ changed = mExpandedGroups.remove(groupSummary);
}
// Only notify listeners if something changed.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifLayoutInflaterFactory.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifLayoutInflaterFactory.kt
index 4429939a515c..d10b556de0fb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifLayoutInflaterFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifLayoutInflaterFactory.kt
@@ -21,29 +21,25 @@ import android.util.AttributeSet
import android.util.Log
import android.view.LayoutInflater
import android.view.View
-import com.android.systemui.Dumpable
-import com.android.systemui.dump.DumpManager
+import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag
import com.android.systemui.statusbar.notification.row.NotificationRowModule.NOTIF_REMOTEVIEWS_FACTORIES
-import com.android.systemui.util.asIndenting
-import com.android.systemui.util.withIncreasedIndent
-import java.io.PrintWriter
-import javax.inject.Inject
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
import javax.inject.Named
/**
* Implementation of [NotifLayoutInflaterFactory]. This class uses a set of
* [NotifRemoteViewsFactory] objects to create replacement views for Notification RemoteViews.
*/
-open class NotifLayoutInflaterFactory
-@Inject
+class NotifLayoutInflaterFactory
+@AssistedInject
constructor(
- dumpManager: DumpManager,
+ @Assisted private val row: ExpandableNotificationRow,
+ @Assisted @InflationFlag val layoutType: Int,
@Named(NOTIF_REMOTEVIEWS_FACTORIES)
private val remoteViewsFactories: Set<@JvmSuppressWildcards NotifRemoteViewsFactory>
-) : LayoutInflater.Factory2, Dumpable {
- init {
- dumpManager.registerNormalDumpable(TAG, this)
- }
+) : LayoutInflater.Factory2 {
override fun onCreateView(
parent: View?,
@@ -51,41 +47,32 @@ constructor(
context: Context,
attrs: AttributeSet
): View? {
- var view: View? = null
var handledFactory: NotifRemoteViewsFactory? = null
+ var result: View? = null
for (layoutFactory in remoteViewsFactories) {
- view = layoutFactory.instantiate(parent, name, context, attrs)
- if (view != null) {
+ layoutFactory.instantiate(row, layoutType, parent, name, context, attrs)?.run {
check(handledFactory == null) {
- "${layoutFactory.javaClass.name} tries to produce view. However, " +
- "${handledFactory?.javaClass?.name} produced view for $name before."
+ "$layoutFactory tries to produce name:$name with type:$layoutType. " +
+ "However, $handledFactory produced view for $name before."
}
handledFactory = layoutFactory
+ result = this
}
}
- logOnCreateView(name, view, handledFactory)
- return view
+ logOnCreateView(name, result, handledFactory)
+ return result
}
override fun onCreateView(name: String, context: Context, attrs: AttributeSet): View? =
onCreateView(null, name, context, attrs)
- override fun dump(pw: PrintWriter, args: Array<out String>) {
- val indentingPW = pw.asIndenting()
-
- indentingPW.appendLine("$TAG ReplacementFactories:")
- indentingPW.withIncreasedIndent {
- remoteViewsFactories.forEach { indentingPW.appendLine(it.javaClass.simpleName) }
- }
- }
-
private fun logOnCreateView(
name: String,
replacedView: View?,
factory: NotifRemoteViewsFactory?
) {
if (SPEW && replacedView != null && factory != null) {
- Log.d(TAG, "$factory produced view for $name: $replacedView")
+ Log.d(TAG, "$factory produced $replacedView for name:$name with type:$layoutType")
}
}
@@ -93,4 +80,12 @@ constructor(
private const val TAG = "NotifLayoutInflaterFac"
private val SPEW = Log.isLoggable(TAG, Log.VERBOSE)
}
+
+ @AssistedFactory
+ interface Provider {
+ fun provide(
+ row: ExpandableNotificationRow,
+ @InflationFlag layoutType: Int
+ ): NotifLayoutInflaterFactory
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewsFactory.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewsFactory.kt
index eebd4d4e955f..bf740e94f81c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewsFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewsFactory.kt
@@ -19,10 +19,18 @@ package com.android.systemui.statusbar.notification.row
import android.content.Context
import android.util.AttributeSet
import android.view.View
+import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag
/** Interface used to create replacement view instances in Notification RemoteViews. */
interface NotifRemoteViewsFactory {
/** return the replacement view instance for the given view name */
- fun instantiate(parent: View?, name: String, context: Context, attrs: AttributeSet): View?
+ fun instantiate(
+ row: ExpandableNotificationRow,
+ @InflationFlag layoutType: Int,
+ parent: View?,
+ name: String,
+ context: Context,
+ attrs: AttributeSet
+ ): View?
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
index 0ad77bdd866b..86f545dc190e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
@@ -79,7 +79,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
private final ConversationNotificationProcessor mConversationProcessor;
private final Executor mBgExecutor;
private final SmartReplyStateInflater mSmartReplyStateInflater;
- private final NotifLayoutInflaterFactory mNotifLayoutInflaterFactory;
+ private final NotifLayoutInflaterFactory.Provider mNotifLayoutInflaterFactoryProvider;
@Inject
NotificationContentInflater(
@@ -89,14 +89,14 @@ public class NotificationContentInflater implements NotificationRowContentBinder
MediaFeatureFlag mediaFeatureFlag,
@Background Executor bgExecutor,
SmartReplyStateInflater smartRepliesInflater,
- NotifLayoutInflaterFactory notifLayoutInflaterFactory) {
+ NotifLayoutInflaterFactory.Provider notifLayoutInflaterFactoryProvider) {
mRemoteViewCache = remoteViewCache;
mRemoteInputManager = remoteInputManager;
mConversationProcessor = conversationProcessor;
mIsMediaInQS = mediaFeatureFlag.getEnabled();
mBgExecutor = bgExecutor;
mSmartReplyStateInflater = smartRepliesInflater;
- mNotifLayoutInflaterFactory = notifLayoutInflaterFactory;
+ mNotifLayoutInflaterFactoryProvider = notifLayoutInflaterFactoryProvider;
}
@Override
@@ -141,7 +141,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
mRemoteInputManager.getRemoteViewsOnClickHandler(),
mIsMediaInQS,
mSmartReplyStateInflater,
- mNotifLayoutInflaterFactory);
+ mNotifLayoutInflaterFactoryProvider);
if (mInflateSynchronously) {
task.onPostExecute(task.doInBackground());
} else {
@@ -165,7 +165,8 @@ public class NotificationContentInflater implements NotificationRowContentBinder
bindParams.usesIncreasedHeight,
bindParams.usesIncreasedHeadsUpHeight,
packageContext,
- mNotifLayoutInflaterFactory);
+ row,
+ mNotifLayoutInflaterFactoryProvider);
result = inflateSmartReplyViews(result, reInflateFlags, entry,
row.getContext(), packageContext,
@@ -304,7 +305,8 @@ public class NotificationContentInflater implements NotificationRowContentBinder
private static InflationProgress createRemoteViews(@InflationFlag int reInflateFlags,
Notification.Builder builder, boolean isLowPriority, boolean usesIncreasedHeight,
boolean usesIncreasedHeadsUpHeight, Context packageContext,
- NotifLayoutInflaterFactory notifLayoutInflaterFactory) {
+ ExpandableNotificationRow row,
+ NotifLayoutInflaterFactory.Provider notifLayoutInflaterFactoryProvider) {
InflationProgress result = new InflationProgress();
if ((reInflateFlags & FLAG_CONTENT_VIEW_CONTRACTED) != 0) {
@@ -322,7 +324,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
if ((reInflateFlags & FLAG_CONTENT_VIEW_PUBLIC) != 0) {
result.newPublicView = builder.makePublicContentView(isLowPriority);
}
- setNotifsViewsInflaterFactory(result, notifLayoutInflaterFactory);
+ setNotifsViewsInflaterFactory(result, row, notifLayoutInflaterFactoryProvider);
result.packageContext = packageContext;
result.headsUpStatusBarText = builder.getHeadsUpStatusBarText(false /* showingPublic */);
result.headsUpStatusBarTextPublic = builder.getHeadsUpStatusBarText(
@@ -331,12 +333,16 @@ public class NotificationContentInflater implements NotificationRowContentBinder
}
private static void setNotifsViewsInflaterFactory(InflationProgress result,
- NotifLayoutInflaterFactory notifLayoutInflaterFactory) {
- setRemoteViewsInflaterFactory(result.newContentView, notifLayoutInflaterFactory);
+ ExpandableNotificationRow row,
+ NotifLayoutInflaterFactory.Provider notifLayoutInflaterFactoryProvider) {
+ setRemoteViewsInflaterFactory(result.newContentView,
+ notifLayoutInflaterFactoryProvider.provide(row, FLAG_CONTENT_VIEW_CONTRACTED));
setRemoteViewsInflaterFactory(result.newExpandedView,
- notifLayoutInflaterFactory);
- setRemoteViewsInflaterFactory(result.newHeadsUpView, notifLayoutInflaterFactory);
- setRemoteViewsInflaterFactory(result.newPublicView, notifLayoutInflaterFactory);
+ notifLayoutInflaterFactoryProvider.provide(row, FLAG_CONTENT_VIEW_EXPANDED));
+ setRemoteViewsInflaterFactory(result.newHeadsUpView,
+ notifLayoutInflaterFactoryProvider.provide(row, FLAG_CONTENT_VIEW_HEADS_UP));
+ setRemoteViewsInflaterFactory(result.newPublicView,
+ notifLayoutInflaterFactoryProvider.provide(row, FLAG_CONTENT_VIEW_PUBLIC));
}
private static void setRemoteViewsInflaterFactory(RemoteViews remoteViews,
@@ -821,7 +827,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
private final ConversationNotificationProcessor mConversationProcessor;
private final boolean mIsMediaInQS;
private final SmartReplyStateInflater mSmartRepliesInflater;
- private final NotifLayoutInflaterFactory mNotifLayoutInflaterFactory;
+ private final NotifLayoutInflaterFactory.Provider mNotifLayoutInflaterFactoryProvider;
private AsyncInflationTask(
Executor bgExecutor,
@@ -838,7 +844,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
RemoteViews.InteractionHandler remoteViewClickHandler,
boolean isMediaFlagEnabled,
SmartReplyStateInflater smartRepliesInflater,
- NotifLayoutInflaterFactory notifLayoutInflaterFactory) {
+ NotifLayoutInflaterFactory.Provider notifLayoutInflaterFactoryProvider) {
mEntry = entry;
mRow = row;
mBgExecutor = bgExecutor;
@@ -854,7 +860,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
mCallback = callback;
mConversationProcessor = conversationProcessor;
mIsMediaInQS = isMediaFlagEnabled;
- mNotifLayoutInflaterFactory = notifLayoutInflaterFactory;
+ mNotifLayoutInflaterFactoryProvider = notifLayoutInflaterFactoryProvider;
entry.setInflationTask(this);
}
@@ -898,8 +904,8 @@ public class NotificationContentInflater implements NotificationRowContentBinder
}
InflationProgress inflationProgress = createRemoteViews(mReInflateFlags,
recoveredBuilder, mIsLowPriority, mUsesIncreasedHeight,
- mUsesIncreasedHeadsUpHeight, packageContext,
- mNotifLayoutInflaterFactory);
+ mUsesIncreasedHeadsUpHeight, packageContext, mRow,
+ mNotifLayoutInflaterFactoryProvider);
InflatedSmartReplyState previousSmartReplyState = mRow.getExistingSmartReplyState();
InflationProgress result = inflateSmartReplyViews(
inflationProgress,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PrecomputedTextViewFactory.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PrecomputedTextViewFactory.kt
index c27682726da5..96547db1283a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PrecomputedTextViewFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PrecomputedTextViewFactory.kt
@@ -23,10 +23,13 @@ import android.widget.TextView
import com.android.internal.widget.ConversationLayout
import com.android.internal.widget.ImageFloatingTextView
import com.android.internal.widget.MessagingLayout
+import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag
import javax.inject.Inject
class PrecomputedTextViewFactory @Inject constructor() : NotifRemoteViewsFactory {
override fun instantiate(
+ row: ExpandableNotificationRow,
+ @InflationFlag layoutType: Int,
parent: View?,
name: String,
context: Context,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
index af09bf281c0c..d0a093cf905a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
@@ -196,8 +196,6 @@ public interface CentralSurfaces extends Dumpable, LifecycleOwner {
/** Get the Keyguard Message Area that displays auth messages. */
AuthKeyguardMessageArea getKeyguardMessageArea();
- int getStatusBarHeight();
-
boolean isLaunchingActivityOverLockscreen();
void onKeyguardViewManagerStatesUpdated();
@@ -228,9 +226,6 @@ public interface CentralSurfaces extends Dumpable, LifecycleOwner {
/** */
boolean getCommandQueuePanelsEnabled();
- /** */
- int getStatusBarWindowState();
-
BiometricUnlockController getBiometricUnlockController();
void showWirelessChargingAnimation(int batteryLevel);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index ccb87bf44dc8..a1776c667b94 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -1688,11 +1688,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
return getNotificationShadeWindowViewController().getKeyguardMessageArea();
}
- @Override
- public int getStatusBarHeight() {
- return mStatusBarWindowController.getStatusBarHeight();
- }
-
private void updateReportRejectedTouchVisibility() {
if (mReportRejectedTouch == null) {
return;
@@ -1818,11 +1813,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
}
@Override
- public int getStatusBarWindowState() {
- return mStatusBarWindowState;
- }
-
- @Override
public BiometricUnlockController getBiometricUnlockController() {
return mBiometricUnlockController;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
index 4de669c0b34a..cc41bf843565 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
@@ -15,7 +15,6 @@
*/
package com.android.systemui.statusbar.phone
-import android.app.StatusBarManager.WINDOW_STATE_SHOWING
import android.app.StatusBarManager.WINDOW_STATUS_BAR
import android.content.res.Configuration
import android.graphics.Point
@@ -34,6 +33,7 @@ import com.android.systemui.shade.ShadeLogger
import com.android.systemui.shade.ShadeViewController
import com.android.systemui.shared.animation.UnfoldMoveFromCenterAnimator
import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.statusbar.window.StatusBarWindowStateController
import com.android.systemui.unfold.SysUIUnfoldComponent
import com.android.systemui.unfold.UNFOLD_STATUS_BAR
import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider
@@ -53,6 +53,7 @@ class PhoneStatusBarViewController private constructor(
view: PhoneStatusBarView,
@Named(UNFOLD_STATUS_BAR) private val progressProvider: ScopedUnfoldTransitionProgressProvider?,
private val centralSurfaces: CentralSurfaces,
+ private val statusBarWindowStateController: StatusBarWindowStateController,
private val shadeController: ShadeController,
private val shadeViewController: ShadeViewController,
private val windowRootView: Provider<WindowRootView>,
@@ -148,7 +149,7 @@ class PhoneStatusBarViewController private constructor(
/** Called when a touch event occurred on {@link PhoneStatusBarView}. */
fun onTouch(event: MotionEvent) {
- if (centralSurfaces.statusBarWindowState == WINDOW_STATE_SHOWING) {
+ if (statusBarWindowStateController.windowIsShowing()) {
val upOrCancel =
event.action == MotionEvent.ACTION_UP ||
event.action == MotionEvent.ACTION_CANCEL
@@ -242,6 +243,7 @@ class PhoneStatusBarViewController private constructor(
private val featureFlags: FeatureFlags,
private val userChipViewModel: StatusBarUserChipViewModel,
private val centralSurfaces: CentralSurfaces,
+ private val statusBarWindowStateController: StatusBarWindowStateController,
private val shadeController: ShadeController,
private val shadeViewController: ShadeViewController,
private val windowRootView: Provider<WindowRootView>,
@@ -264,6 +266,7 @@ class PhoneStatusBarViewController private constructor(
view,
progressProvider.getOrNull(),
centralSurfaces,
+ statusBarWindowStateController,
shadeController,
shadeViewController,
windowRootView,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index ea57eb46bab4..27b8406e3bf9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -92,6 +92,8 @@ import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.unfold.FoldAodAnimationController;
import com.android.systemui.unfold.SysUIUnfoldComponent;
+import dagger.Lazy;
+
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashSet;
@@ -101,7 +103,6 @@ import java.util.Set;
import javax.inject.Inject;
-import dagger.Lazy;
import kotlinx.coroutines.CoroutineDispatcher;
/**
@@ -450,14 +451,6 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
}
}
- private KeyguardStateController.Callback mKeyguardStateControllerCallback =
- new KeyguardStateController.Callback() {
- @Override
- public void onUnlockedChanged() {
- updateAlternateBouncerShowing(mAlternateBouncerInteractor.maybeHide());
- }
- };
-
private void registerListeners() {
mKeyguardUpdateManager.registerCallback(mUpdateMonitorCallback);
mStatusBarStateController.addCallback(this);
@@ -471,7 +464,6 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
mDockManager.addListener(mDockEventListener);
mIsDocked = mDockManager.isDocked();
}
- mKeyguardStateController.addCallback(mKeyguardStateControllerCallback);
if (mFlags.isEnabled(Flags.KEYGUARD_WM_STATE_REFACTOR)) {
mShadeViewController.postToView(() ->
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
index cd1afc727346..37f032b464b7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
@@ -226,7 +226,7 @@ class UnlockedScreenOffAnimationController @Inject constructor(
val builder = InteractionJankMonitor.Configuration.Builder
.withView(
InteractionJankMonitor.CUJ_SCREEN_OFF_SHOW_AOD,
- notifShadeWindowControllerLazy.get().windowRootView
+ checkNotNull(notifShadeWindowControllerLazy.get().windowRootView)
)
.setTag(statusBarStateControllerImpl.getClockId())
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java
index 98d4d22d59b4..1be87463250f 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java
@@ -23,7 +23,6 @@ import static com.android.systemui.flags.Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLE
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.atLeast;
-import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -136,6 +135,10 @@ public class KeyguardClockSwitchControllerBaseTest extends SysuiTestCase {
public void setup() {
MockitoAnnotations.initMocks(this);
+ mFakeDateView.setTag(R.id.tag_smartspace_view, new Object());
+ mFakeWeatherView.setTag(R.id.tag_smartspace_view, new Object());
+ mFakeSmartspaceView.setTag(R.id.tag_smartspace_view, new Object());
+
when(mView.findViewById(R.id.left_aligned_notification_icon_container))
.thenReturn(mNotificationIcons);
when(mNotificationIcons.getLayoutParams()).thenReturn(
@@ -158,12 +161,6 @@ public class KeyguardClockSwitchControllerBaseTest extends SysuiTestCase {
when(mSmartspaceController.buildAndConnectDateView(any())).thenReturn(mFakeDateView);
when(mSmartspaceController.buildAndConnectWeatherView(any())).thenReturn(mFakeWeatherView);
when(mSmartspaceController.buildAndConnectView(any())).thenReturn(mFakeSmartspaceView);
- doAnswer(invocation -> {
- removeView(mFakeDateView);
- removeView(mFakeWeatherView);
- removeView(mFakeSmartspaceView);
- return null;
- }).when(mSmartspaceController).removeViewsFromParent(any());
mExecutor = new FakeExecutor(new FakeSystemClock());
mFakeFeatureFlags = new FakeFeatureFlags();
mFakeFeatureFlags.set(FACE_AUTH_REFACTOR, false);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/BoundingBoxOverlapDetectorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/BoundingBoxOverlapDetectorTest.kt
index da55d5a099b7..95b72d554896 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/BoundingBoxOverlapDetectorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/BoundingBoxOverlapDetectorTest.kt
@@ -28,7 +28,7 @@ import org.junit.runners.Parameterized.Parameters
@SmallTest
@RunWith(Parameterized::class)
class BoundingBoxOverlapDetectorTest(val testCase: TestCase) : SysuiTestCase() {
- val underTest = BoundingBoxOverlapDetector()
+ val underTest = BoundingBoxOverlapDetector(1f)
@Test
fun isGoodOverlap() {
@@ -83,7 +83,7 @@ private val TOUCH_DATA =
GESTURE_START
)
-private val SENSOR = Rect(100 /* left */, 200 /* top */, 300 /* right */, 500 /* bottom */)
+private val SENSOR = Rect(100 /* left */, 200 /* top */, 300 /* right */, 400 /* bottom */)
private val OVERLAY = Rect(0 /* left */, 100 /* top */, 400 /* right */, 600 /* bottom */)
private fun genTestCases(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
index 11b0b0798ebc..47084c087952 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
@@ -312,10 +312,13 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
assertThat(message).isEqualTo(PromptMessage.Empty)
assertThat(messageVisible).isFalse()
}
+ val clearIconError = !restart
assertThat(legacyState)
.isEqualTo(
if (restart) {
AuthBiometricView.STATE_AUTHENTICATING
+ } else if (clearIconError) {
+ AuthBiometricView.STATE_IDLE
} else {
AuthBiometricView.STATE_HELP
}
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 6aa5a00c36da..b1cf0517ddd1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
@@ -24,6 +24,7 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -70,10 +71,10 @@ import com.android.systemui.settings.UserTracker;
import com.android.systemui.shade.ShadeController;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.VibratorHelper;
-import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.LightBarController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.statusbar.window.StatusBarWindowController;
import com.android.systemui.telephony.TelephonyListenerManager;
import com.android.systemui.util.RingerModeLiveData;
import com.android.systemui.util.RingerModeTracker;
@@ -89,7 +90,6 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.List;
-import java.util.Optional;
import java.util.concurrent.Executor;
@SmallTest
@@ -119,6 +119,7 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase {
@Mock private IStatusBarService mStatusBarService;
@Mock private LightBarController mLightBarController;
@Mock private NotificationShadeWindowController mNotificationShadeWindowController;
+ @Mock private StatusBarWindowController mStatusBarWindowController;
@Mock private IWindowManager mWindowManager;
@Mock private Executor mBackgroundExecutor;
@Mock private UiEventLogger mUiEventLogger;
@@ -128,7 +129,6 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase {
@Mock private Handler mHandler;
@Mock private UserContextProvider mUserContextProvider;
@Mock private VibratorHelper mVibratorHelper;
- @Mock private CentralSurfaces mCentralSurfaces;
@Mock private ShadeController mShadeController;
@Mock private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@Mock private DialogLaunchAnimator mDialogLaunchAnimator;
@@ -172,13 +172,13 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase {
mStatusBarService,
mLightBarController,
mNotificationShadeWindowController,
+ mStatusBarWindowController,
mWindowManager,
mBackgroundExecutor,
mUiEventLogger,
mRingerModeTracker,
mHandler,
mPackageManager,
- Optional.of(mCentralSurfaces),
mShadeController,
mKeyguardUpdateMonitor,
mDialogLaunchAnimator);
@@ -305,7 +305,7 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase {
doReturn(4).when(mGlobalActionsDialogLite).getMaxShownPowerItems();
doReturn(true).when(mGlobalActionsDialogLite).shouldDisplayLockdown(any());
doReturn(true).when(mGlobalActionsDialogLite).shouldShowAction(any());
- doReturn(true).when(mCentralSurfaces).isKeyguardShowing();
+ doReturn(true).when(mKeyguardStateController).isShowing();
String[] actions = {
GlobalActionsDialogLite.GLOBAL_ACTION_KEY_EMERGENCY,
GlobalActionsDialogLite.GLOBAL_ACTION_KEY_LOCKDOWN,
@@ -329,7 +329,7 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase {
doReturn(4).when(mGlobalActionsDialogLite).getMaxShownPowerItems();
doReturn(true).when(mGlobalActionsDialogLite).shouldDisplayLockdown(any());
doReturn(true).when(mGlobalActionsDialogLite).shouldShowAction(any());
- doReturn(false).when(mCentralSurfaces).isKeyguardShowing();
+ doReturn(false).when(mKeyguardStateController).isShowing();
String[] actions = {
GlobalActionsDialogLite.GLOBAL_ACTION_KEY_EMERGENCY,
GlobalActionsDialogLite.GLOBAL_ACTION_KEY_LOCKDOWN,
@@ -348,6 +348,34 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase {
}
@Test
+ public void testSwipeDown_pastStatusBarHeight_shadeNotOpened() {
+ mGlobalActionsDialogLite = spy(mGlobalActionsDialogLite);
+ doReturn(4).when(mGlobalActionsDialogLite).getMaxShownPowerItems();
+ doReturn(true).when(mGlobalActionsDialogLite).shouldDisplayLockdown(any());
+ doReturn(true).when(mGlobalActionsDialogLite).shouldShowAction(any());
+ doReturn(false).when(mKeyguardStateController).isShowing();
+ String[] actions = {
+ GlobalActionsDialogLite.GLOBAL_ACTION_KEY_EMERGENCY,
+ GlobalActionsDialogLite.GLOBAL_ACTION_KEY_LOCKDOWN,
+ GlobalActionsDialogLite.GLOBAL_ACTION_KEY_POWER,
+ GlobalActionsDialogLite.GLOBAL_ACTION_KEY_RESTART,
+ };
+ doReturn(actions).when(mGlobalActionsDialogLite).getDefaultActions();
+ GlobalActionsDialogLite.ActionsDialogLite dialog = mGlobalActionsDialogLite.createDialog();
+
+ doReturn(100).when(mStatusBarWindowController).getStatusBarHeight();
+
+ GestureDetector.SimpleOnGestureListener gestureListener = spy(dialog.mGestureListener);
+ // WHEN the start y is larger than the status bar height
+ MotionEvent start = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 200, 0);
+ MotionEvent end = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 500, 0);
+ gestureListener.onFling(start, end, 0, 1000);
+
+ // THEN the shade isn't opened
+ verify(mShadeController, never()).animateExpandShade();
+ }
+
+ @Test
public void testShouldLogBugreportPress() throws InterruptedException {
GlobalActionsDialogLite.BugReportAction bugReportAction =
mGlobalActionsDialogLite.makeBugReportActionForTesting();
@@ -539,7 +567,7 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase {
doReturn(4).when(mGlobalActionsDialogLite).getMaxShownPowerItems();
doReturn(true).when(mGlobalActionsDialogLite).shouldDisplayLockdown(any());
doReturn(true).when(mGlobalActionsDialogLite).shouldShowAction(any());
- doReturn(false).when(mCentralSurfaces).isKeyguardShowing();
+ doReturn(false).when(mKeyguardStateController).isShowing();
String[] actions = {
GlobalActionsDialogLite.GLOBAL_ACTION_KEY_EMERGENCY,
GlobalActionsDialogLite.GLOBAL_ACTION_KEY_LOCKDOWN,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractorTest.kt
index e0ae0c359f07..a3f7fc5fc8cf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractorTest.kt
@@ -131,12 +131,14 @@ class KeyguardKeyEventInteractorTest : SysuiTestCase() {
}
@Test
- fun dispatchKeyEvent_menuActionUp_interactiveKeyguard_showsPrimaryBouncer() {
+ fun dispatchKeyEvent_menuActionUp_interactiveKeyguard_collapsesShade() {
keyguardInteractorWithDependencies.repository.setWakefulnessModel(awakeWakefulnessMode)
whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD)
whenever(statusBarKeyguardViewManager.shouldDismissOnMenuPressed()).thenReturn(true)
- verifyActionUpShowsPrimaryBouncer(KeyEvent.KEYCODE_MENU)
+ val actionUpMenuKeyEvent = KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_MENU)
+ assertThat(underTest.dispatchKeyEvent(actionUpMenuKeyEvent)).isTrue()
+ verify(shadeController).animateCollapseShadeForced()
}
@Test
@@ -145,48 +147,42 @@ class KeyguardKeyEventInteractorTest : SysuiTestCase() {
whenever(statusBarStateController.state).thenReturn(StatusBarState.SHADE_LOCKED)
whenever(statusBarKeyguardViewManager.shouldDismissOnMenuPressed()).thenReturn(true)
- verifyActionUpCollapsesTheShade(KeyEvent.KEYCODE_MENU)
+ // action down: does NOT collapse the shade
+ val actionDownMenuKeyEvent = KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MENU)
+ assertThat(underTest.dispatchKeyEvent(actionDownMenuKeyEvent)).isFalse()
+ verify(shadeController, never()).animateCollapseShadeForced()
+
+ // action up: collapses the shade
+ val actionUpMenuKeyEvent = KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_MENU)
+ assertThat(underTest.dispatchKeyEvent(actionUpMenuKeyEvent)).isTrue()
+ verify(shadeController).animateCollapseShadeForced()
}
@Test
- fun dispatchKeyEvent_menuActionUp_nonInteractiveKeyguard_doNothing() {
+ fun dispatchKeyEvent_menuActionUp_nonInteractiveKeyguard_neverCollapsesShade() {
keyguardInteractorWithDependencies.repository.setWakefulnessModel(asleepWakefulnessMode)
whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD)
whenever(statusBarKeyguardViewManager.shouldDismissOnMenuPressed()).thenReturn(true)
- verifyActionsDoNothing(KeyEvent.KEYCODE_MENU)
- }
-
- @Test
- fun dispatchKeyEvent_spaceActionUp_interactiveKeyguard_showsPrimaryBouncer() {
- keyguardInteractorWithDependencies.repository.setWakefulnessModel(awakeWakefulnessMode)
- whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD)
-
- verifyActionUpShowsPrimaryBouncer(KeyEvent.KEYCODE_SPACE)
- }
-
- @Test
- fun dispatchKeyEvent_spaceActionUp_shadeLocked_collapsesShade() {
- keyguardInteractorWithDependencies.repository.setWakefulnessModel(awakeWakefulnessMode)
- whenever(statusBarStateController.state).thenReturn(StatusBarState.SHADE_LOCKED)
-
- verifyActionUpCollapsesTheShade(KeyEvent.KEYCODE_SPACE)
+ val actionUpMenuKeyEvent = KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_MENU)
+ assertThat(underTest.dispatchKeyEvent(actionUpMenuKeyEvent)).isFalse()
+ verify(shadeController, never()).animateCollapseShadeForced()
}
@Test
- fun dispatchKeyEvent_enterActionUp_interactiveKeyguard_showsPrimaryBouncer() {
+ fun dispatchKeyEvent_spaceActionUp_interactiveKeyguard_collapsesShade() {
keyguardInteractorWithDependencies.repository.setWakefulnessModel(awakeWakefulnessMode)
whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD)
- verifyActionUpShowsPrimaryBouncer(KeyEvent.KEYCODE_ENTER)
- }
-
- @Test
- fun dispatchKeyEvent_enterActionUp_shadeLocked_collapsesShade() {
- keyguardInteractorWithDependencies.repository.setWakefulnessModel(awakeWakefulnessMode)
- whenever(statusBarStateController.state).thenReturn(StatusBarState.SHADE_LOCKED)
+ // action down: does NOT collapse the shade
+ val actionDownMenuKeyEvent = KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_SPACE)
+ assertThat(underTest.dispatchKeyEvent(actionDownMenuKeyEvent)).isFalse()
+ verify(shadeController, never()).animateCollapseShadeForced()
- verifyActionUpCollapsesTheShade(KeyEvent.KEYCODE_ENTER)
+ // action up: collapses the shade
+ val actionUpMenuKeyEvent = KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_SPACE)
+ assertThat(underTest.dispatchKeyEvent(actionUpMenuKeyEvent)).isTrue()
+ verify(shadeController).animateCollapseShadeForced()
}
@Test
@@ -256,42 +252,4 @@ class KeyguardKeyEventInteractorTest : SysuiTestCase() {
.isFalse()
verify(statusBarKeyguardViewManager, never()).interceptMediaKey(any())
}
-
- private fun verifyActionUpCollapsesTheShade(keycode: Int) {
- // action down: does NOT collapse the shade
- val actionDownMenuKeyEvent = KeyEvent(KeyEvent.ACTION_DOWN, keycode)
- assertThat(underTest.dispatchKeyEvent(actionDownMenuKeyEvent)).isFalse()
- verify(shadeController, never()).animateCollapseShadeForced()
-
- // action up: collapses the shade
- val actionUpMenuKeyEvent = KeyEvent(KeyEvent.ACTION_UP, keycode)
- assertThat(underTest.dispatchKeyEvent(actionUpMenuKeyEvent)).isTrue()
- verify(shadeController).animateCollapseShadeForced()
- }
-
- private fun verifyActionUpShowsPrimaryBouncer(keycode: Int) {
- // action down: does NOT collapse the shade
- val actionDownMenuKeyEvent = KeyEvent(KeyEvent.ACTION_DOWN, keycode)
- assertThat(underTest.dispatchKeyEvent(actionDownMenuKeyEvent)).isFalse()
- verify(statusBarKeyguardViewManager, never()).showPrimaryBouncer(any())
-
- // action up: collapses the shade
- val actionUpMenuKeyEvent = KeyEvent(KeyEvent.ACTION_UP, keycode)
- assertThat(underTest.dispatchKeyEvent(actionUpMenuKeyEvent)).isTrue()
- verify(statusBarKeyguardViewManager).showPrimaryBouncer(eq(true))
- }
-
- private fun verifyActionsDoNothing(keycode: Int) {
- // action down: does nothing
- val actionDownMenuKeyEvent = KeyEvent(KeyEvent.ACTION_DOWN, keycode)
- assertThat(underTest.dispatchKeyEvent(actionDownMenuKeyEvent)).isFalse()
- verify(shadeController, never()).animateCollapseShadeForced()
- verify(statusBarKeyguardViewManager, never()).showPrimaryBouncer(any())
-
- // action up: doesNothing
- val actionUpMenuKeyEvent = KeyEvent(KeyEvent.ACTION_UP, keycode)
- assertThat(underTest.dispatchKeyEvent(actionUpMenuKeyEvent)).isFalse()
- verify(shadeController, never()).animateCollapseShadeForced()
- verify(statusBarKeyguardViewManager, never()).showPrimaryBouncer(any())
- }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt
index 23f243c8ebda..a9f288d3575f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt
@@ -17,10 +17,8 @@
package com.android.systemui.keyguard.ui.viewmodel
import androidx.test.filters.SmallTest
-import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.authentication.data.model.AuthenticationMethodModel
-import com.android.systemui.common.shared.model.Icon
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.scene.SceneTestUtils
import com.android.systemui.scene.shared.model.SceneKey
@@ -48,7 +46,6 @@ class LockscreenSceneViewModelTest : SysuiTestCase() {
private val underTest =
LockscreenSceneViewModel(
- applicationScope = testScope.backgroundScope,
authenticationInteractor = authenticationInteractor,
bouncerInteractor =
utils.bouncerInteractor(
@@ -58,32 +55,6 @@ class LockscreenSceneViewModelTest : SysuiTestCase() {
)
@Test
- fun lockButtonIcon_whenLocked() =
- testScope.runTest {
- val lockButtonIcon by collectLastValue(underTest.lockButtonIcon)
- utils.authenticationRepository.setAuthenticationMethod(
- AuthenticationMethodModel.Password
- )
- utils.authenticationRepository.setUnlocked(false)
-
- assertThat((lockButtonIcon as? Icon.Resource)?.res)
- .isEqualTo(R.drawable.ic_device_lock_on)
- }
-
- @Test
- fun lockButtonIcon_whenUnlocked() =
- testScope.runTest {
- val lockButtonIcon by collectLastValue(underTest.lockButtonIcon)
- utils.authenticationRepository.setAuthenticationMethod(
- AuthenticationMethodModel.Password
- )
- utils.authenticationRepository.setUnlocked(true)
-
- assertThat((lockButtonIcon as? Icon.Resource)?.res)
- .isEqualTo(R.drawable.ic_device_lock_off)
- }
-
- @Test
fun upTransitionSceneKey_canSwipeToUnlock_gone() =
testScope.runTest {
val upTransitionSceneKey by collectLastValue(underTest.upDestinationSceneKey)
@@ -120,32 +91,6 @@ class LockscreenSceneViewModelTest : SysuiTestCase() {
}
@Test
- fun onContentClicked_deviceUnlocked_switchesToGone() =
- testScope.runTest {
- val currentScene by collectLastValue(sceneInteractor.desiredScene)
- utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
- utils.authenticationRepository.setUnlocked(true)
- runCurrent()
-
- underTest.onContentClicked()
-
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone))
- }
-
- @Test
- fun onContentClicked_deviceLockedSecurely_switchesToBouncer() =
- testScope.runTest {
- val currentScene by collectLastValue(sceneInteractor.desiredScene)
- utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
- utils.authenticationRepository.setUnlocked(false)
- runCurrent()
-
- underTest.onContentClicked()
-
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
- }
-
- @Test
fun onLockButtonClicked_deviceUnlocked_switchesToGone() =
testScope.runTest {
val currentScene by collectLastValue(sceneInteractor.desiredScene)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt
index 49ece66e0cfd..ef07fab70bb0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt
@@ -31,7 +31,6 @@ import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dump.DumpManager
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.keyguard.KeyguardUnlockAnimationController
-import com.android.systemui.keyguard.ScreenLifecycle
import com.android.systemui.keyguard.WakefulnessLifecycle
import com.android.systemui.model.SysUiState
import com.android.systemui.navigationbar.NavigationBarController
@@ -84,7 +83,6 @@ class OverviewProxyServiceTest : SysuiTestCase() {
private val fakeSystemClock = FakeSystemClock()
private val sysUiState = SysUiState(displayTracker)
private val featureFlags = FakeFeatureFlags()
- private val screenLifecycle = ScreenLifecycle(dumpManager)
private val wakefulnessLifecycle =
WakefulnessLifecycle(mContext, null, fakeSystemClock, dumpManager)
@@ -142,7 +140,6 @@ class OverviewProxyServiceTest : SysuiTestCase() {
sysUiState,
mock(),
userTracker,
- screenLifecycle,
wakefulnessLifecycle,
uiEventLogger,
displayTracker,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
index 53c04ccbdb38..46cbfacb0044 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
@@ -116,7 +116,6 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() {
private val lockscreenSceneViewModel =
LockscreenSceneViewModel(
- applicationScope = testScope.backgroundScope,
authenticationInteractor = authenticationInteractor,
bouncerInteractor = bouncerInteractor,
)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
index ecaf13711b52..48665fe0c9b0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
@@ -340,9 +340,9 @@ class ClockRegistryTest : SysuiTestCase() {
@Test
fun knownPluginAttached_clockAndListChanged_notLoaded() {
val mockPluginLifecycle1 = mock<PluginLifecycleManager<ClockProviderPlugin>>()
- whenever(mockPluginLifecycle1.getPackage()).thenReturn("com.android.systemui.falcon.one")
+ whenever(mockPluginLifecycle1.getPackage()).thenReturn("com.android.systemui.clocks.metro")
val mockPluginLifecycle2 = mock<PluginLifecycleManager<ClockProviderPlugin>>()
- whenever(mockPluginLifecycle2.getPackage()).thenReturn("com.android.systemui.falcon.two")
+ whenever(mockPluginLifecycle2.getPackage()).thenReturn("com.android.systemui.clocks.bignum")
var changeCallCount = 0
var listChangeCallCount = 0
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/rotation/RotationButtonControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/rotation/RotationButtonControllerTest.kt
index 9393a4f4eead..ee3d87089b6d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/rotation/RotationButtonControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/rotation/RotationButtonControllerTest.kt
@@ -60,18 +60,4 @@ class RotationButtonControllerTest : SysuiTestCase() {
assertThat(mController.canShowRotationButton()).isTrue()
}
-
- @Test
- fun ifTaskbarVisible_showRotationSuggestion() {
- mController.onNavigationBarWindowVisibilityChange( /* showing = */ false)
- mController.onBehaviorChanged(Display.DEFAULT_DISPLAY,
- WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE)
- mController.onNavigationModeChanged(WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON)
- mController.onTaskbarStateChange( /* visible = */ false, /* stashed = */ false)
- assertThat(mController.canShowRotationButton()).isFalse()
-
- mController.onTaskbarStateChange( /* visible = */ true, /* stashed = */ false)
-
- assertThat(mController.canShowRotationButton()).isTrue()
- }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerTest.kt
index 38a8f414b0fb..4a94dc819a9e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerTest.kt
@@ -21,21 +21,11 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
-import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder
-import com.android.systemui.statusbar.notification.collection.ListEntry
-import com.android.systemui.statusbar.notification.collection.NotifPipeline
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
-import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeRenderListListener
-import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager.OnGroupExpansionChangeListener
-import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.mock
-import com.android.systemui.util.mockito.withArgCaptor
import org.junit.Assert
import org.junit.Before
import org.junit.Test
-import org.mockito.Mockito.never
-import org.mockito.Mockito.verify
-import org.mockito.Mockito.verifyNoMoreInteractions
import org.mockito.Mockito.`when` as whenever
@SmallTest
@@ -46,43 +36,13 @@ class GroupExpansionManagerTest : SysuiTestCase() {
private val groupMembershipManager: GroupMembershipManager = mock()
private val featureFlags = FakeFeatureFlags()
- private val pipeline: NotifPipeline = mock()
- private lateinit var beforeRenderListListener: OnBeforeRenderListListener
-
- private val summary1 = notificationEntry("foo", 1)
- private val summary2 = notificationEntry("bar", 1)
- private val entries =
- listOf<ListEntry>(
- GroupEntryBuilder()
- .setSummary(summary1)
- .setChildren(
- listOf(
- notificationEntry("foo", 2),
- notificationEntry("foo", 3),
- notificationEntry("foo", 4)
- )
- )
- .build(),
- GroupEntryBuilder()
- .setSummary(summary2)
- .setChildren(
- listOf(
- notificationEntry("bar", 2),
- notificationEntry("bar", 3),
- notificationEntry("bar", 4)
- )
- )
- .build(),
- notificationEntry("baz", 1)
- )
-
- private fun notificationEntry(pkg: String, id: Int) =
- NotificationEntryBuilder().setPkg(pkg).setId(id).build().apply { row = mock() }
+ private val entry1 = NotificationEntryBuilder().build()
+ private val entry2 = NotificationEntryBuilder().build()
@Before
fun setUp() {
- whenever(groupMembershipManager.getGroupSummary(summary1)).thenReturn(summary1)
- whenever(groupMembershipManager.getGroupSummary(summary2)).thenReturn(summary2)
+ whenever(groupMembershipManager.getGroupSummary(entry1)).thenReturn(entry1)
+ whenever(groupMembershipManager.getGroupSummary(entry2)).thenReturn(entry2)
gem = GroupExpansionManagerImpl(dumpManager, groupMembershipManager, featureFlags)
}
@@ -94,15 +54,15 @@ class GroupExpansionManagerTest : SysuiTestCase() {
var listenerCalledCount = 0
gem.registerGroupExpansionChangeListener { _, _ -> listenerCalledCount++ }
- gem.setGroupExpanded(summary1, false)
+ gem.setGroupExpanded(entry1, false)
Assert.assertEquals(0, listenerCalledCount)
- gem.setGroupExpanded(summary1, true)
+ gem.setGroupExpanded(entry1, true)
Assert.assertEquals(1, listenerCalledCount)
- gem.setGroupExpanded(summary2, true)
+ gem.setGroupExpanded(entry2, true)
Assert.assertEquals(2, listenerCalledCount)
- gem.setGroupExpanded(summary1, true)
+ gem.setGroupExpanded(entry1, true)
Assert.assertEquals(2, listenerCalledCount)
- gem.setGroupExpanded(summary2, false)
+ gem.setGroupExpanded(entry2, false)
Assert.assertEquals(3, listenerCalledCount)
}
@@ -113,39 +73,15 @@ class GroupExpansionManagerTest : SysuiTestCase() {
var listenerCalledCount = 0
gem.registerGroupExpansionChangeListener { _, _ -> listenerCalledCount++ }
- gem.setGroupExpanded(summary1, false)
+ gem.setGroupExpanded(entry1, false)
Assert.assertEquals(1, listenerCalledCount)
- gem.setGroupExpanded(summary1, true)
+ gem.setGroupExpanded(entry1, true)
Assert.assertEquals(2, listenerCalledCount)
- gem.setGroupExpanded(summary2, true)
+ gem.setGroupExpanded(entry2, true)
Assert.assertEquals(3, listenerCalledCount)
- gem.setGroupExpanded(summary1, true)
+ gem.setGroupExpanded(entry1, true)
Assert.assertEquals(4, listenerCalledCount)
- gem.setGroupExpanded(summary2, false)
+ gem.setGroupExpanded(entry2, false)
Assert.assertEquals(5, listenerCalledCount)
}
-
- @Test
- fun testSyncWithPipeline() {
- featureFlags.set(Flags.NOTIFICATION_GROUP_EXPANSION_CHANGE, true)
- gem.attach(pipeline)
- beforeRenderListListener = withArgCaptor {
- verify(pipeline).addOnBeforeRenderListListener(capture())
- }
-
- val listener: OnGroupExpansionChangeListener = mock()
- gem.registerGroupExpansionChangeListener(listener)
-
- beforeRenderListListener.onBeforeRenderList(entries)
- verify(listener, never()).onGroupExpansionChange(any(), any())
-
- // Expand one of the groups.
- gem.setGroupExpanded(summary1, true)
- verify(listener).onGroupExpansionChange(summary1.row, true)
-
- // Empty the pipeline list and verify that the group is no longer expanded.
- beforeRenderListListener.onBeforeRenderList(emptyList())
- verify(listener).onGroupExpansionChange(summary1.row, false)
- verifyNoMoreInteractions(listener)
- }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifLayoutInflaterFactoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifLayoutInflaterFactoryTest.kt
index d5612e8e8007..3f7fc979b1e3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifLayoutInflaterFactoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifLayoutInflaterFactoryTest.kt
@@ -20,19 +20,21 @@ import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
import android.util.AttributeSet
import android.view.View
+import android.widget.Button
import android.widget.FrameLayout
import android.widget.LinearLayout
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.dump.DumpManager
-import junit.framework.Assert.assertEquals
-import junit.framework.Assert.assertNotNull
-import junit.framework.Assert.assertNull
-import org.junit.Before
+import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_CONTRACTED
+import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_EXPANDED
+import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_HEADS_UP
+import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag
+import com.android.systemui.util.mockito.mock
+import com.google.common.truth.Truth.assertThat
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.Mock
-import org.mockito.MockitoAnnotations
+import org.mockito.Mockito.spy
+import org.mockito.Mockito.verify
/** Tests for [NotifLayoutInflaterFactory] */
@SmallTest
@@ -40,87 +42,106 @@ import org.mockito.MockitoAnnotations
@RunWithLooper
class NotifLayoutInflaterFactoryTest : SysuiTestCase() {
- @Mock private lateinit var attrs: AttributeSet
+ private lateinit var inflaterFactory: NotifLayoutInflaterFactory
- @Before
- fun before() {
- MockitoAnnotations.initMocks(this)
+ private val attrs: AttributeSet = mock()
+ private val row: ExpandableNotificationRow = mock()
+ private val textViewExpandedFactory =
+ createReplacementViewFactory("TextView", FLAG_CONTENT_VIEW_EXPANDED) { context, _ ->
+ Button(context)
+ }
+ private val textViewCollapsedFactory =
+ createReplacementViewFactory("TextView", FLAG_CONTENT_VIEW_CONTRACTED) { context, _ ->
+ Button(context)
+ }
+ private val textViewExpandedFactorySpy = spy(textViewExpandedFactory)
+ private val textViewCollapsedFactorySpy = spy(textViewCollapsedFactory)
+ private val viewFactorySpies = setOf(textViewExpandedFactorySpy, textViewCollapsedFactorySpy)
+
+ @Test
+ fun onCreateView_noMatchingViewForName_returnNull() {
+ // GIVEN we have ViewFactories that replaces TextViews in expanded and collapsed layouts
+ val layoutType = FLAG_CONTENT_VIEW_EXPANDED
+ inflaterFactory = NotifLayoutInflaterFactory(row, layoutType, viewFactorySpies)
+
+ // WHEN we try to inflate an ImageView for the expanded layout
+ val createdView = inflaterFactory.onCreateView("ImageView", context, attrs)
+
+ // THEN the inflater factory returns null
+ viewFactorySpies.forEach { viewFactory ->
+ verify(viewFactory).instantiate(row, layoutType, null, "ImageView", context, attrs)
+ }
+ assertThat(createdView).isNull()
}
@Test
- fun onCreateView_notMatchingViews_returnNull() {
- // GIVEN
- val layoutInflaterFactory =
- createNotifLayoutInflaterFactoryImpl(
- setOf(
- createReplacementViewFactory("TextView") { context, attrs ->
- FrameLayout(context)
- }
- )
- )
+ fun onCreateView_noMatchingViewForLayoutType_returnNull() {
+ // GIVEN we have ViewFactories that replaces TextViews in expanded and collapsed layouts
+ val layoutType = FLAG_CONTENT_VIEW_HEADS_UP
+ inflaterFactory = NotifLayoutInflaterFactory(row, layoutType, viewFactorySpies)
- // WHEN
- val createView = layoutInflaterFactory.onCreateView("ImageView", mContext, attrs)
+ // WHEN we try to inflate a TextView for the heads-up layout
+ val createdView = inflaterFactory.onCreateView("TextView", context, attrs)
- // THEN
- assertNull(createView)
+ // THEN the inflater factory returns null
+ viewFactorySpies.forEach { viewFactory ->
+ verify(viewFactory).instantiate(row, layoutType, null, "TextView", context, attrs)
+ }
+ assertThat(createdView).isNull()
}
@Test
fun onCreateView_matchingViews_returnReplacementView() {
- // GIVEN
- val layoutInflaterFactory =
- createNotifLayoutInflaterFactoryImpl(
- setOf(
- createReplacementViewFactory("TextView") { context, attrs ->
- FrameLayout(context)
- }
- )
- )
+ // GIVEN we have ViewFactories that replaces TextViews in expanded and collapsed layouts
+ val layoutType = FLAG_CONTENT_VIEW_EXPANDED
+ inflaterFactory = NotifLayoutInflaterFactory(row, layoutType, viewFactorySpies)
- // WHEN
- val createView = layoutInflaterFactory.onCreateView("TextView", mContext, attrs)
+ // WHEN we try to inflate a TextView for the expanded layout
+ val createdView = inflaterFactory.onCreateView("TextView", context, attrs)
- // THEN
- assertNotNull(createView)
- assertEquals(requireNotNull(createView)::class.java, FrameLayout::class.java)
+ // THEN the expanded viewFactory returns the replaced view
+ verify(textViewCollapsedFactorySpy)
+ .instantiate(row, layoutType, null, "TextView", context, attrs)
+ assertThat(createdView).isInstanceOf(Button::class.java)
}
@Test(expected = IllegalStateException::class)
fun onCreateView_multipleFactory_throwIllegalStateException() {
- // GIVEN
- val layoutInflaterFactory =
- createNotifLayoutInflaterFactoryImpl(
+ // GIVEN we have two factories that replaces TextViews in expanded layouts
+ val layoutType = FLAG_CONTENT_VIEW_EXPANDED
+ inflaterFactory =
+ NotifLayoutInflaterFactory(
+ row,
+ layoutType,
setOf(
- createReplacementViewFactory("TextView") { context, attrs ->
+ createReplacementViewFactory("TextView", layoutType) { context, _ ->
FrameLayout(context)
},
- createReplacementViewFactory("TextView") { context, attrs ->
+ createReplacementViewFactory("TextView", layoutType) { context, _ ->
LinearLayout(context)
}
)
)
- // WHEN
- layoutInflaterFactory.onCreateView("TextView", mContext, attrs)
+ // WHEN we try to inflate a TextView for the expanded layout
+ inflaterFactory.onCreateView("TextView", mContext, attrs)
}
- private fun createNotifLayoutInflaterFactoryImpl(
- replacementViewFactories: Set<@JvmSuppressWildcards NotifRemoteViewsFactory>
- ) = NotifLayoutInflaterFactory(DumpManager(), replacementViewFactories)
-
private fun createReplacementViewFactory(
replacementName: String,
+ @InflationFlag replacementLayoutType: Int,
createView: (context: Context, attrs: AttributeSet) -> View
) =
object : NotifRemoteViewsFactory {
override fun instantiate(
+ row: ExpandableNotificationRow,
+ @InflationFlag layoutType: Int,
parent: View?,
name: String,
context: Context,
attrs: AttributeSet
): View? =
- if (replacementName == name) {
+ if (replacementName == name && replacementLayoutType == layoutType) {
createView(context, attrs)
} else {
null
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
index f55b0a8ff4da..ea87c808debd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
@@ -92,6 +92,7 @@ public class NotificationContentInflaterTest extends SysuiTestCase {
@Mock private ConversationNotificationProcessor mConversationNotificationProcessor;
@Mock private InflatedSmartReplyState mInflatedSmartReplyState;
@Mock private InflatedSmartReplyViewHolder mInflatedSmartReplies;
+ @Mock private NotifLayoutInflaterFactory.Provider mNotifLayoutInflaterFactoryProvider;
@Mock private NotifLayoutInflaterFactory mNotifLayoutInflaterFactory;
private final SmartReplyStateInflater mSmartReplyStateInflater =
@@ -124,6 +125,8 @@ public class NotificationContentInflaterTest extends SysuiTestCase {
TestableLooper.get(this));
ExpandableNotificationRow row = helper.createRow(mBuilder.build());
mRow = spy(row);
+ when(mNotifLayoutInflaterFactoryProvider.provide(any(), any()))
+ .thenReturn(mNotifLayoutInflaterFactory);
mNotificationInflater = new NotificationContentInflater(
mCache,
@@ -132,7 +135,7 @@ public class NotificationContentInflaterTest extends SysuiTestCase {
mock(MediaFeatureFlag.class),
mock(Executor.class),
mSmartReplyStateInflater,
- mNotifLayoutInflaterFactory);
+ mNotifLayoutInflaterFactoryProvider);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
index 1ab2b388de83..3657bdfeed02 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
@@ -175,7 +175,7 @@ public class NotificationTestHelper {
mock(MediaFeatureFlag.class),
mock(Executor.class),
new MockSmartReplyInflater(),
- mock(NotifLayoutInflaterFactory.class));
+ mock(NotifLayoutInflaterFactory.Provider.class));
contentBinder.setInflateSynchronously(true);
mBindStage = new RowContentBindStage(contentBinder,
mock(NotifInflationErrorManager.class),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
index 0b31523e9b98..4c3c3f9d8da5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
@@ -16,6 +16,10 @@
package com.android.systemui.statusbar.phone
+import android.app.StatusBarManager.WINDOW_STATE_HIDDEN
+import android.app.StatusBarManager.WINDOW_STATE_HIDING
+import android.app.StatusBarManager.WINDOW_STATE_SHOWING
+import android.app.StatusBarManager.WINDOW_STATUS_BAR
import android.view.LayoutInflater
import android.view.MotionEvent
import android.view.ViewTreeObserver
@@ -31,12 +35,15 @@ import com.android.systemui.scene.ui.view.WindowRootView
import com.android.systemui.shade.ShadeControllerImpl
import com.android.systemui.shade.ShadeLogger
import com.android.systemui.shade.ShadeViewController
+import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.statusbar.window.StatusBarWindowStateController
import com.android.systemui.unfold.SysUIUnfoldComponent
import com.android.systemui.unfold.config.UnfoldTransitionConfig
import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider
import com.android.systemui.user.ui.viewmodel.StatusBarUserChipViewModel
import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.view.ViewUtil
import com.google.common.truth.Truth.assertThat
@@ -75,6 +82,8 @@ class PhoneStatusBarViewControllerTest : SysuiTestCase() {
@Mock
private lateinit var centralSurfacesImpl: CentralSurfacesImpl
@Mock
+ private lateinit var commandQueue: CommandQueue
+ @Mock
private lateinit var shadeControllerImpl: ShadeControllerImpl
@Mock
private lateinit var windowRootView: Provider<WindowRootView>
@@ -82,6 +91,7 @@ class PhoneStatusBarViewControllerTest : SysuiTestCase() {
private lateinit var shadeLogger: ShadeLogger
@Mock
private lateinit var viewUtil: ViewUtil
+ private lateinit var statusBarWindowStateController: StatusBarWindowStateController
private lateinit var view: PhoneStatusBarView
private lateinit var controller: PhoneStatusBarViewController
@@ -91,6 +101,9 @@ class PhoneStatusBarViewControllerTest : SysuiTestCase() {
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
+
+ statusBarWindowStateController = StatusBarWindowStateController(DISPLAY_ID, commandQueue)
+
`when`(sysuiUnfoldComponent.getStatusBarMoveFromCenterAnimationController())
.thenReturn(moveFromCenterAnimation)
// create the view and controller on main thread as it requires main looper
@@ -186,6 +199,42 @@ class PhoneStatusBarViewControllerTest : SysuiTestCase() {
verify(shadeViewController, never()).handleExternalTouch(any())
}
+ @Test
+ fun onTouch_windowHidden_centralSurfacesNotNotified() {
+ val callback = getCommandQueueCallback()
+ callback.setWindowState(DISPLAY_ID, WINDOW_STATUS_BAR, WINDOW_STATE_HIDDEN)
+
+ controller.onTouch(MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0))
+
+ verify(centralSurfacesImpl, never()).setInteracting(any(), any())
+ }
+
+ @Test
+ fun onTouch_windowHiding_centralSurfacesNotNotified() {
+ val callback = getCommandQueueCallback()
+ callback.setWindowState(DISPLAY_ID, WINDOW_STATUS_BAR, WINDOW_STATE_HIDING)
+
+ controller.onTouch(MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0))
+
+ verify(centralSurfacesImpl, never()).setInteracting(any(), any())
+ }
+
+ @Test
+ fun onTouch_windowShowing_centralSurfacesNotified() {
+ val callback = getCommandQueueCallback()
+ callback.setWindowState(DISPLAY_ID, WINDOW_STATUS_BAR, WINDOW_STATE_SHOWING)
+
+ controller.onTouch(MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0))
+
+ verify(centralSurfacesImpl).setInteracting(any(), any())
+ }
+
+ private fun getCommandQueueCallback(): CommandQueue.Callbacks {
+ val captor = argumentCaptor<CommandQueue.Callbacks>()
+ verify(commandQueue).addCallback(captor.capture())
+ return captor.value!!
+ }
+
private fun createViewMock(): PhoneStatusBarView {
val view = spy(view)
val viewTreeObserver = mock(ViewTreeObserver::class.java)
@@ -201,6 +250,7 @@ class PhoneStatusBarViewControllerTest : SysuiTestCase() {
featureFlags,
userChipViewModel,
centralSurfacesImpl,
+ statusBarWindowStateController,
shadeControllerImpl,
shadeViewController,
windowRootView,
@@ -218,4 +268,8 @@ class PhoneStatusBarViewControllerTest : SysuiTestCase() {
override var isHingeAngleEnabled: Boolean = false
override val halfFoldedTimeoutMillis: Int = 0
}
+
+ private companion object {
+ const val DISPLAY_ID = 1
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/InflatedSmartRepliesTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/InflatedSmartRepliesTest.java
index 85052e600486..e4318659bef9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/InflatedSmartRepliesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/InflatedSmartRepliesTest.java
@@ -64,7 +64,7 @@ import java.util.List;
public class InflatedSmartRepliesTest extends SysuiTestCase {
private static final Intent TEST_INTENT = new Intent("com.android.SMART_REPLY_VIEW_ACTION");
- private static final Intent WHITELISTED_TEST_INTENT =
+ private static final Intent ALLOWLISTED_TEST_INTENT =
new Intent("com.android.WHITELISTED_TEST_ACTION");
@Mock private SmartReplyConstants mSmartReplyConstants;
@@ -343,7 +343,7 @@ public class InflatedSmartRepliesTest extends SysuiTestCase {
assertThat(smartReplyState.getSmartReplies().choices)
.containsExactlyElementsIn(mEntry.getSmartReplies()).inOrder();
- // Since no apps are whitelisted no actions should be shown.
+ // Since no apps are allowlisted no actions should be shown.
assertThat(smartReplyState.getSmartActions().actions).isEmpty();
assertThat(smartReplyState.getSuppressedActions()).isNull();
assertThat(smartReplyState.getHasPhishingAction()).isFalse();
@@ -358,7 +358,7 @@ public class InflatedSmartRepliesTest extends SysuiTestCase {
allowedResolveInfo.activityInfo.packageName = allowedPackage;
when(mPackageManagerWrapper
.resolveActivity(
- argThat(intent -> WHITELISTED_TEST_INTENT.getAction().equals(
+ argThat(intent -> ALLOWLISTED_TEST_INTENT.getAction().equals(
intent.getAction())),
anyInt() /* flags */))
.thenReturn(allowedResolveInfo);
@@ -368,7 +368,7 @@ public class InflatedSmartRepliesTest extends SysuiTestCase {
// suggestions.
setupAppGeneratedReplies(null /* smartReplies */);
ArrayList<Notification.Action> actions = new ArrayList<>();
- actions.add(createAction("allowed action", WHITELISTED_TEST_INTENT));
+ actions.add(createAction("allowed action", ALLOWLISTED_TEST_INTENT));
actions.add(createAction("non-allowed action", TEST_INTENT));
modifyRanking(mEntry)
@@ -379,7 +379,7 @@ public class InflatedSmartRepliesTest extends SysuiTestCase {
InflatedSmartReplyState smartReplyState =
mSmartReplyStateInflater.chooseSmartRepliesAndActions(mEntry);
- // Only the action for the whitelisted package should be allowed.
+ // Only the action for the allowlisted package should be allowed.
assertThat(smartReplyState.getSmartActions().actions)
.containsExactly(mEntry.getSmartActions().get(0));
assertThat(smartReplyState.getSuppressedActions()).isNull();
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index 63a607c8d0d4..5b8bdd57ccbb 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -1413,7 +1413,7 @@ final class AutofillManagerServiceImpl
Slog.v(TAG, "setAugmentedAutofillWhitelistLocked(packages=" + packages + ", activities="
+ activities + ")");
}
- whitelistForAugmentedAutofillPackages(packages, activities);
+ allowlistForAugmentedAutofillPackages(packages, activities);
final String serviceName;
if (mRemoteAugmentedAutofillServiceInfo != null) {
serviceName = mRemoteAugmentedAutofillServiceInfo.getComponentName()
@@ -1477,7 +1477,7 @@ final class AutofillManagerServiceImpl
/**
* @throws IllegalArgumentException if packages or components are empty.
*/
- private void whitelistForAugmentedAutofillPackages(@Nullable List<String> packages,
+ private void allowlistForAugmentedAutofillPackages(@Nullable List<String> packages,
@Nullable List<ComponentName> components) {
// TODO(b/123100824): add CTS test for when it's null
synchronized (mLock) {
diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
index 8cb7deaa00dc..b573800fde18 100644
--- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
@@ -133,6 +133,7 @@ import com.android.server.backup.transport.TransportConnection;
import com.android.server.backup.transport.TransportNotAvailableException;
import com.android.server.backup.transport.TransportNotRegisteredException;
import com.android.server.backup.utils.BackupEligibilityRules;
+import com.android.server.backup.utils.BackupManagerMonitorDumpsysUtils;
import com.android.server.backup.utils.BackupManagerMonitorEventSender;
import com.android.server.backup.utils.BackupObserverUtils;
import com.android.server.backup.utils.SparseArrayUtils;
@@ -142,6 +143,7 @@ import dalvik.annotation.optimization.NeverCompile;
import com.google.android.collect.Sets;
import java.io.BufferedInputStream;
+import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
@@ -150,6 +152,7 @@ import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
+import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.RandomAccessFile;
@@ -4161,6 +4164,7 @@ public class UserBackupManagerService {
}
}
dumpInternal(pw);
+ dumpBMMEvents(pw);
} finally {
Binder.restoreCallingIdentity(identityToken);
}
@@ -4178,6 +4182,23 @@ public class UserBackupManagerService {
}
}
+ private void dumpBMMEvents(PrintWriter pw) {
+ BackupManagerMonitorDumpsysUtils bm =
+ new BackupManagerMonitorDumpsysUtils();
+ File events = bm.getBMMEventsFile();
+ pw.println("START OF BACKUP MANAGER MONITOR EVENTS");
+ try (BufferedReader reader = new BufferedReader(new FileReader(events))) {
+ String line;
+ while ((line = reader.readLine()) != null) {
+ pw.println(line);
+ }
+ } catch (IOException e) {
+ Slog.e(TAG, "IO Exception when reading BMM events from file: " + e);
+ pw.println("IO Exception when reading BMM events from file");
+ }
+ pw.println("END OF BACKUP MANAGER MONITOR EVENTS");
+ }
+
@NeverCompile // Avoid size overhead of debugging code.
private void dumpInternal(PrintWriter pw) {
// Add prefix for only non-system users so that system user dumpsys is the same as before
diff --git a/services/backup/java/com/android/server/backup/utils/BackupManagerMonitorDumpsysUtils.java b/services/backup/java/com/android/server/backup/utils/BackupManagerMonitorDumpsysUtils.java
new file mode 100644
index 000000000000..0b55ca21371b
--- /dev/null
+++ b/services/backup/java/com/android/server/backup/utils/BackupManagerMonitorDumpsysUtils.java
@@ -0,0 +1,260 @@
+/*
+ * Copyright (C) 2023 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.utils;
+
+import android.app.backup.BackupAnnotations;
+import android.app.backup.BackupManagerMonitor;
+import android.app.backup.BackupRestoreEventLogger;
+import android.os.Bundle;
+import android.os.Environment;
+import android.util.Slog;
+
+import com.android.internal.util.FastPrintWriter;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.PrintWriter;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.Map;
+
+
+/*
+ * Util class to parse a BMM event and write it to a text file, to be the printed in
+ * the backup dumpsys
+ *
+ * Note: this class is note thread safe
+ */
+public class BackupManagerMonitorDumpsysUtils {
+
+ private static final String TAG = "BackupManagerMonitorDumpsysUtils";
+ // Name of the subdirectory where the text file containing the BMM events will be stored.
+ // Same as {@link UserBackupManagerFiles}
+ private static final String BACKUP_PERSISTENT_DIR = "backup";
+
+ /**
+ * Parses the BackupManagerMonitor bundle for a RESTORE event in a series of strings that
+ * will be persisted in a text file and printed in the dumpsys.
+ *
+ * If the evenntBundle passed is not a RESTORE event, return early
+ *
+ * Key information related to the event:
+ * - Timestamp (HAS TO ALWAYS BE THE FIRST LINE OF EACH EVENT)
+ * - Event ID
+ * - Event Category
+ * - Operation type
+ * - Package name (can be null)
+ * - Agent logs (if available)
+ *
+ * Example of formatting:
+ * RESTORE Event: [2023-08-18 17:16:00.735] Agent - Agent logging results
+ * Package name: com.android.wallpaperbackup
+ * Agent Logs:
+ * Data Type: wlp_img_system
+ * Item restored: 0/1
+ * Agent Error - Category: no_wallpaper, Count: 1
+ * Data Type: wlp_img_lock
+ * Item restored: 0/1
+ * Agent Error - Category: no_wallpaper, Count: 1
+ */
+ public void parseBackupManagerMonitorRestoreEventForDumpsys(Bundle eventBundle) {
+ if (eventBundle == null) {
+ return;
+ }
+
+ if (!isOpTypeRestore(eventBundle)) {
+ //We only log Restore events
+ return;
+ }
+
+ if (!eventBundle.containsKey(BackupManagerMonitor.EXTRA_LOG_EVENT_ID)
+ || !eventBundle.containsKey(BackupManagerMonitor.EXTRA_LOG_EVENT_CATEGORY)) {
+ Slog.w(TAG, "Event id and category are not optional fields.");
+ return;
+ }
+ File bmmEvents = getBMMEventsFile();
+
+ try (FileOutputStream out = new FileOutputStream(bmmEvents, /*append*/ true);
+ PrintWriter pw = new FastPrintWriter(out);) {
+
+ int eventCategory = eventBundle.getInt(BackupManagerMonitor.EXTRA_LOG_EVENT_CATEGORY);
+ int eventId = eventBundle.getInt(BackupManagerMonitor.EXTRA_LOG_EVENT_ID);
+
+ if (eventId == BackupManagerMonitor.LOG_EVENT_ID_AGENT_LOGGING_RESULTS &&
+ !hasAgentLogging(eventBundle)) {
+ // Do not record an empty agent logging event
+ return;
+ }
+
+ pw.println("RESTORE Event: [" + timestamp() + "] " +
+ getCategory(eventCategory) + " - " +
+ getId(eventId));
+
+ if (eventBundle.containsKey(BackupManagerMonitor.EXTRA_LOG_EVENT_PACKAGE_NAME)) {
+ pw.println("\tPackage name: "
+ + eventBundle.getString(BackupManagerMonitor.EXTRA_LOG_EVENT_PACKAGE_NAME));
+ }
+
+ // TODO(b/296818666): add extras to the events
+ addAgentLogsIfAvailable(eventBundle, pw);
+ } catch (java.io.IOException e) {
+ Slog.e(TAG, "IO Exception when writing BMM events to file: " + e);
+ }
+
+ }
+
+ private boolean hasAgentLogging(Bundle eventBundle) {
+ if (eventBundle.containsKey(BackupManagerMonitor.EXTRA_LOG_AGENT_LOGGING_RESULTS)) {
+ ArrayList<BackupRestoreEventLogger.DataTypeResult> agentLogs =
+ eventBundle.getParcelableArrayList(
+ BackupManagerMonitor.EXTRA_LOG_AGENT_LOGGING_RESULTS);
+
+ return !agentLogs.isEmpty();
+ }
+ return false;
+ }
+
+ /**
+ * Extracts agent logs from the BackupManagerMonitor event. These logs detail:
+ * - the data type for the agent
+ * - the count of successfully restored items
+ * - the count of items that failed to restore
+ * - the metadata associated with this datatype
+ * - any errors
+ */
+ private void addAgentLogsIfAvailable(Bundle eventBundle, PrintWriter pw) {
+ if (hasAgentLogging(eventBundle)) {
+ pw.println("\tAgent Logs:");
+ ArrayList<BackupRestoreEventLogger.DataTypeResult> agentLogs =
+ eventBundle.getParcelableArrayList(
+ BackupManagerMonitor.EXTRA_LOG_AGENT_LOGGING_RESULTS);
+ for (BackupRestoreEventLogger.DataTypeResult result : agentLogs) {
+ int totalItems = result.getFailCount() + result.getSuccessCount();
+ pw.println("\t\tData Type: " + result.getDataType());
+ pw.println("\t\t\tItem restored: " + result.getSuccessCount() + "/" +
+ totalItems);
+ for (Map.Entry<String, Integer> entry : result.getErrors().entrySet()) {
+ pw.println("\t\t\tAgent Error - Category: " +
+ entry.getKey() + ", Count: " + entry.getValue());
+ }
+ }
+ }
+ }
+
+ /*
+ * Get the path of the text files which stores the BMM events
+ */
+ public File getBMMEventsFile() {
+ File dataDir = new File(Environment.getDataDirectory(), BACKUP_PERSISTENT_DIR);
+ File fname = new File(dataDir, "bmmevents.txt");
+ return fname;
+ }
+
+ private String timestamp() {
+ long currentTime = System.currentTimeMillis();
+ Date date = new Date(currentTime);
+ SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
+ return dateFormat.format(date);
+ }
+
+ private String getCategory(int code) {
+ String category = switch (code) {
+ case BackupManagerMonitor.LOG_EVENT_CATEGORY_TRANSPORT -> "Transport";
+ case BackupManagerMonitor.LOG_EVENT_CATEGORY_AGENT -> "Agent";
+ case BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY ->
+ "Backup Manager Policy";
+ default -> "Unknown category code: " + code;
+ };
+ return category;
+ }
+
+ private String getId(int code) {
+ String id = switch (code) {
+ case BackupManagerMonitor.LOG_EVENT_ID_FULL_BACKUP_CANCEL -> "Full backup cancel";
+ case BackupManagerMonitor.LOG_EVENT_ID_ILLEGAL_KEY -> "Illegal key";
+ case BackupManagerMonitor.LOG_EVENT_ID_NO_DATA_TO_SEND -> "No data to send";
+ case BackupManagerMonitor.LOG_EVENT_ID_PACKAGE_INELIGIBLE -> "Package ineligible";
+ case BackupManagerMonitor.LOG_EVENT_ID_PACKAGE_KEY_VALUE_PARTICIPANT ->
+ "Package key-value participant";
+ case BackupManagerMonitor.LOG_EVENT_ID_PACKAGE_STOPPED -> "Package stopped";
+ case BackupManagerMonitor.LOG_EVENT_ID_PACKAGE_NOT_FOUND -> "Package not found";
+ case BackupManagerMonitor.LOG_EVENT_ID_BACKUP_DISABLED -> "Backup disabled";
+ case BackupManagerMonitor.LOG_EVENT_ID_DEVICE_NOT_PROVISIONED ->
+ "Device not provisioned";
+ case BackupManagerMonitor.LOG_EVENT_ID_PACKAGE_TRANSPORT_NOT_PRESENT ->
+ "Package transport not present";
+ case BackupManagerMonitor.LOG_EVENT_ID_ERROR_PREFLIGHT -> "Error preflight";
+ case BackupManagerMonitor.LOG_EVENT_ID_QUOTA_HIT_PREFLIGHT -> "Quota hit preflight";
+ case BackupManagerMonitor.LOG_EVENT_ID_EXCEPTION_FULL_BACKUP -> "Exception full backup";
+ case BackupManagerMonitor.LOG_EVENT_ID_KEY_VALUE_BACKUP_CANCEL ->
+ "Key-value backup cancel";
+ case BackupManagerMonitor.LOG_EVENT_ID_NO_RESTORE_METADATA_AVAILABLE ->
+ "No restore metadata available";
+ case BackupManagerMonitor.LOG_EVENT_ID_NO_PM_METADATA_RECEIVED ->
+ "No PM metadata received";
+ case BackupManagerMonitor.LOG_EVENT_ID_PM_AGENT_HAS_NO_METADATA ->
+ "PM agent has no metadata";
+ case BackupManagerMonitor.LOG_EVENT_ID_LOST_TRANSPORT -> "Lost transport";
+ case BackupManagerMonitor.LOG_EVENT_ID_PACKAGE_NOT_PRESENT -> "Package not present";
+ case BackupManagerMonitor.LOG_EVENT_ID_RESTORE_VERSION_HIGHER ->
+ "Restore version higher";
+ case BackupManagerMonitor.LOG_EVENT_ID_APP_HAS_NO_AGENT -> "App has no agent";
+ case BackupManagerMonitor.LOG_EVENT_ID_SIGNATURE_MISMATCH -> "Signature mismatch";
+ case BackupManagerMonitor.LOG_EVENT_ID_CANT_FIND_AGENT -> "Can't find agent";
+ case BackupManagerMonitor.LOG_EVENT_ID_KEY_VALUE_RESTORE_TIMEOUT ->
+ "Key-value restore timeout";
+ case BackupManagerMonitor.LOG_EVENT_ID_RESTORE_ANY_VERSION -> "Restore any version";
+ case BackupManagerMonitor.LOG_EVENT_ID_VERSIONS_MATCH -> "Versions match";
+ case BackupManagerMonitor.LOG_EVENT_ID_VERSION_OF_BACKUP_OLDER ->
+ "Version of backup older";
+ case BackupManagerMonitor.LOG_EVENT_ID_FULL_RESTORE_SIGNATURE_MISMATCH ->
+ "Full restore signature mismatch";
+ case BackupManagerMonitor.LOG_EVENT_ID_SYSTEM_APP_NO_AGENT -> "System app no agent";
+ case BackupManagerMonitor.LOG_EVENT_ID_FULL_RESTORE_ALLOW_BACKUP_FALSE ->
+ "Full restore allow backup false";
+ case BackupManagerMonitor.LOG_EVENT_ID_APK_NOT_INSTALLED -> "APK not installed";
+ case BackupManagerMonitor.LOG_EVENT_ID_CANNOT_RESTORE_WITHOUT_APK ->
+ "Cannot restore without APK";
+ case BackupManagerMonitor.LOG_EVENT_ID_MISSING_SIGNATURE -> "Missing signature";
+ case BackupManagerMonitor.LOG_EVENT_ID_EXPECTED_DIFFERENT_PACKAGE ->
+ "Expected different package";
+ case BackupManagerMonitor.LOG_EVENT_ID_UNKNOWN_VERSION -> "Unknown version";
+ case BackupManagerMonitor.LOG_EVENT_ID_FULL_RESTORE_TIMEOUT -> "Full restore timeout";
+ case BackupManagerMonitor.LOG_EVENT_ID_CORRUPT_MANIFEST -> "Corrupt manifest";
+ case BackupManagerMonitor.LOG_EVENT_ID_WIDGET_METADATA_MISMATCH ->
+ "Widget metadata mismatch";
+ case BackupManagerMonitor.LOG_EVENT_ID_WIDGET_UNKNOWN_VERSION ->
+ "Widget unknown version";
+ case BackupManagerMonitor.LOG_EVENT_ID_NO_PACKAGES -> "No packages";
+ case BackupManagerMonitor.LOG_EVENT_ID_TRANSPORT_IS_NULL -> "Transport is null";
+ case BackupManagerMonitor.LOG_EVENT_ID_TRANSPORT_NON_INCREMENTAL_BACKUP_REQUIRED ->
+ "Transport non-incremental backup required";
+ case BackupManagerMonitor.LOG_EVENT_ID_AGENT_LOGGING_RESULTS -> "Agent logging results";
+ default -> "Unknown log event ID: " + code;
+ };
+ return id;
+ }
+
+ private boolean isOpTypeRestore(Bundle eventBundle) {
+ return switch (eventBundle.getInt(
+ BackupManagerMonitor.EXTRA_LOG_OPERATION_TYPE, -1)) {
+ case BackupAnnotations.OperationType.RESTORE -> true;
+ default -> false;
+ };
+ }
+}
diff --git a/services/backup/java/com/android/server/backup/utils/BackupManagerMonitorEventSender.java b/services/backup/java/com/android/server/backup/utils/BackupManagerMonitorEventSender.java
index f9286803f167..92e3107b6977 100644
--- a/services/backup/java/com/android/server/backup/utils/BackupManagerMonitorEventSender.java
+++ b/services/backup/java/com/android/server/backup/utils/BackupManagerMonitorEventSender.java
@@ -36,6 +36,7 @@ import android.os.Bundle;
import android.os.RemoteException;
import android.util.Slog;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.infra.AndroidFuture;
import java.util.List;
@@ -54,9 +55,17 @@ public class BackupManagerMonitorEventSender {
*/
private static final int AGENT_LOGGER_RESULTS_TIMEOUT_MILLIS = 500;
@Nullable private IBackupManagerMonitor mMonitor;
-
+ private final BackupManagerMonitorDumpsysUtils mBackupManagerMonitorDumpsysUtils;
public BackupManagerMonitorEventSender(@Nullable IBackupManagerMonitor monitor) {
mMonitor = monitor;
+ mBackupManagerMonitorDumpsysUtils = new BackupManagerMonitorDumpsysUtils();
+ }
+
+ @VisibleForTesting
+ BackupManagerMonitorEventSender(@Nullable IBackupManagerMonitor monitor,
+ BackupManagerMonitorDumpsysUtils backupManagerMonitorDumpsysUtils) {
+ mMonitor = monitor;
+ mBackupManagerMonitorDumpsysUtils = backupManagerMonitorDumpsysUtils;
}
public void setMonitor(IBackupManagerMonitor monitor) {
@@ -82,29 +91,39 @@ public class BackupManagerMonitorEventSender {
PackageInfo pkg,
int category,
Bundle extras) {
- if (mMonitor != null) {
- try {
- Bundle bundle = new Bundle();
- bundle.putInt(BackupManagerMonitor.EXTRA_LOG_EVENT_ID, id);
- bundle.putInt(BackupManagerMonitor.EXTRA_LOG_EVENT_CATEGORY, category);
- if (pkg != null) {
- bundle.putString(EXTRA_LOG_EVENT_PACKAGE_NAME,
- pkg.packageName);
- bundle.putInt(BackupManagerMonitor.EXTRA_LOG_EVENT_PACKAGE_VERSION,
- pkg.versionCode);
- bundle.putLong(BackupManagerMonitor.EXTRA_LOG_EVENT_PACKAGE_LONG_VERSION,
- pkg.getLongVersionCode());
- }
- if (extras != null) {
- bundle.putAll(extras);
+ try {
+ Bundle bundle = new Bundle();
+ bundle.putInt(BackupManagerMonitor.EXTRA_LOG_EVENT_ID, id);
+ bundle.putInt(BackupManagerMonitor.EXTRA_LOG_EVENT_CATEGORY, category);
+ if (pkg != null) {
+ bundle.putString(EXTRA_LOG_EVENT_PACKAGE_NAME,
+ pkg.packageName);
+ bundle.putInt(BackupManagerMonitor.EXTRA_LOG_EVENT_PACKAGE_VERSION,
+ pkg.versionCode);
+ bundle.putLong(BackupManagerMonitor.EXTRA_LOG_EVENT_PACKAGE_LONG_VERSION,
+ pkg.getLongVersionCode());
+ }
+ if (extras != null) {
+ bundle.putAll(extras);
+ if (extras.containsKey(EXTRA_LOG_OPERATION_TYPE) &&
+ extras.getInt(EXTRA_LOG_OPERATION_TYPE) == OperationType.RESTORE){
+ mBackupManagerMonitorDumpsysUtils
+ .parseBackupManagerMonitorRestoreEventForDumpsys(bundle);
}
+ }
+
+ if (mMonitor != null) {
mMonitor.onEvent(bundle);
- } catch (RemoteException e) {
- mMonitor = null;
+ } else {
if (DEBUG) {
- Slog.w(TAG, "backup manager monitor went away");
+ Slog.w(TAG, "backup manager monitor is null unable to send event");
}
}
+ } catch (RemoteException e) {
+ mMonitor = null;
+ if (DEBUG) {
+ Slog.w(TAG, "backup manager monitor went away");
+ }
}
}
@@ -120,7 +139,7 @@ public class BackupManagerMonitorEventSender {
*/
public void monitorAgentLoggingResults(PackageInfo pkg, IBackupAgent agent) {
if (mMonitor == null) {
- return;
+ Slog.i(TAG, "backup manager monitor is null unable to send event"+pkg);
}
try {
diff --git a/services/companion/Android.bp b/services/companion/Android.bp
index a248d9e55a8a..25f57b3c5331 100644
--- a/services/companion/Android.bp
+++ b/services/companion/Android.bp
@@ -26,5 +26,6 @@ java_library_static {
],
static_libs: [
"ukey2_jni",
+ "virtualdevice_flags_lib",
],
}
diff --git a/services/companion/java/com/android/server/companion/virtual/Android.bp b/services/companion/java/com/android/server/companion/virtual/Android.bp
index 50a09b9a8ea9..6526c78d5124 100644
--- a/services/companion/java/com/android/server/companion/virtual/Android.bp
+++ b/services/companion/java/com/android/server/companion/virtual/Android.bp
@@ -9,4 +9,4 @@ aconfig_declarations {
srcs: [
"flags.aconfig",
],
-} \ No newline at end of file
+}
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
index d1be5a9e971d..f23fe2a7bce4 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
@@ -126,6 +126,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
private final VirtualDeviceManagerService mService;
private final PendingTrampolineCallback mPendingTrampolineCallback;
private final int mOwnerUid;
+ private final VirtualDeviceLog mVirtualDeviceLog;
private final String mOwnerPackageName;
private int mDeviceId;
private @Nullable String mPersistentDeviceId;
@@ -197,6 +198,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
Context context,
AssociationInfo associationInfo,
VirtualDeviceManagerService service,
+ VirtualDeviceLog virtualDeviceLog,
IBinder token,
AttributionSource attributionSource,
int deviceId,
@@ -210,6 +212,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
context,
associationInfo,
service,
+ virtualDeviceLog,
token,
attributionSource,
deviceId,
@@ -228,6 +231,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
Context context,
AssociationInfo associationInfo,
VirtualDeviceManagerService service,
+ VirtualDeviceLog virtualDeviceLog,
IBinder token,
AttributionSource attributionSource,
int deviceId,
@@ -240,6 +244,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
VirtualDeviceParams params,
DisplayManagerGlobal displayManager) {
super(PermissionEnforcer.fromContext(context));
+ mVirtualDeviceLog = virtualDeviceLog;
mOwnerPackageName = attributionSource.getPackageName();
UserHandle ownerUserHandle = UserHandle.getUserHandleForUid(attributionSource.getUid());
mContext = context.createContextAsUser(ownerUserHandle, 0);
@@ -271,6 +276,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
+ mVirtualDeviceLog.logCreated(deviceId, mOwnerUid);
}
@VisibleForTesting
@@ -405,6 +411,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
super.close_enforcePermission();
// Remove about-to-be-closed virtual device from the service before butchering it.
boolean removed = mService.removeVirtualDevice(mDeviceId);
+ mVirtualDeviceLog.logClosed(mDeviceId, mOwnerUid);
mDeviceId = Context.DEVICE_ID_INVALID;
// Device is already closed.
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceLog.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceLog.java
new file mode 100644
index 000000000000..c65aa5bd355b
--- /dev/null
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceLog.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2023 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.companion.virtual;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.Binder;
+import android.util.SparseArray;
+
+import java.io.PrintWriter;
+import java.time.Instant;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
+import java.util.ArrayDeque;
+
+final class VirtualDeviceLog {
+ public static int TYPE_CREATED = 0x0;
+ public static int TYPE_CLOSED = 0x1;
+
+ private static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern(
+ "MM-dd HH:mm:ss.SSS").withZone(ZoneId.systemDefault());
+ private static final int MAX_ENTRIES = 16;
+
+ private final Context mContext;
+ private final ArrayDeque<LogEntry> mLogEntries = new ArrayDeque<>();
+
+ VirtualDeviceLog(Context context) {
+ mContext = context;
+ }
+
+ void logCreated(int deviceId, int ownerUid) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ if (!Flags.dumpHistory()) {
+ return;
+ }
+ addEntry(new LogEntry(TYPE_CREATED, deviceId, System.currentTimeMillis(), ownerUid));
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ void logClosed(int deviceId, int ownerUid) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ if (!Flags.dumpHistory()) {
+ return;
+ }
+ addEntry(new LogEntry(TYPE_CLOSED, deviceId, System.currentTimeMillis(), ownerUid));
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ private void addEntry(LogEntry entry) {
+ mLogEntries.push(entry);
+ if (mLogEntries.size() > MAX_ENTRIES) {
+ mLogEntries.removeLast();
+ }
+ }
+
+ void dump(PrintWriter pw) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ if (!Flags.dumpHistory()) {
+ return;
+ }
+ pw.println("VirtualDevice Log:");
+ UidToPackageNameCache packageNameCache = new UidToPackageNameCache(
+ mContext.getPackageManager());
+ for (LogEntry logEntry : mLogEntries) {
+ logEntry.dump(pw, " ", packageNameCache);
+
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ static class LogEntry {
+ private final int mType;
+ private final int mDeviceId;
+ private final long mTimestamp;
+ private final int mOwnerUid;
+
+ LogEntry(int type, int deviceId, long timestamp, int ownerUid) {
+ this.mType = type;
+ this.mDeviceId = deviceId;
+ this.mTimestamp = timestamp;
+ this.mOwnerUid = ownerUid;
+ }
+
+ void dump(PrintWriter pw, String prefix, UidToPackageNameCache packageNameCache) {
+ StringBuilder sb = new StringBuilder(prefix);
+ sb.append(DATE_FORMAT.format(Instant.ofEpochMilli(mTimestamp)));
+ sb.append(" - ");
+ sb.append(mType == TYPE_CREATED ? "START" : "CLOSE");
+ sb.append(" Device ID: ");
+ sb.append(mDeviceId);
+ sb.append(" ");
+ sb.append(mOwnerUid);
+ sb.append(" (");
+ sb.append(packageNameCache.getPackageName(mOwnerUid));
+ sb.append(")");
+ pw.println(sb);
+ }
+ }
+
+ private static class UidToPackageNameCache {
+ private final PackageManager mPackageManager;
+ private final SparseArray<String> mUidToPackagesCache = new SparseArray<>();
+
+ public UidToPackageNameCache(PackageManager packageManager) {
+ mPackageManager = packageManager;
+ }
+
+ String getPackageName(int ownerUid) {
+ String[] packages;
+ if (mUidToPackagesCache.contains(ownerUid)) {
+ return mUidToPackagesCache.get(ownerUid);
+ } else {
+ packages = mPackageManager.getPackagesForUid(ownerUid);
+ String packageName = "";
+ if (packages != null && packages.length > 0) {
+ packageName = packages[0];
+ if (packages.length > 1) {
+ StringBuilder sb = new StringBuilder();
+ sb.append(packageName)
+ .append(",...");
+ packageName = sb.toString();
+ }
+ }
+ mUidToPackagesCache.put(ownerUid, packageName);
+ return packageName;
+ }
+ }
+ }
+}
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
index e558498e4715..7429fbe18a0c 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
@@ -85,6 +85,7 @@ public class VirtualDeviceManagerService extends SystemService {
private final Object mVirtualDeviceManagerLock = new Object();
private final VirtualDeviceManagerImpl mImpl;
private final VirtualDeviceManagerInternal mLocalService;
+ private VirtualDeviceLog mVirtualDeviceLog = new VirtualDeviceLog(getContext());
private final Handler mHandler = new Handler(Looper.getMainLooper());
private final PendingTrampolineMap mPendingTrampolines = new PendingTrampolineMap(mHandler);
@@ -344,9 +345,11 @@ public class VirtualDeviceManagerService extends SystemService {
final Consumer<ArraySet<Integer>> runningAppsChangedCallback =
runningUids -> notifyRunningAppsChanged(deviceId, runningUids);
VirtualDeviceImpl virtualDevice = new VirtualDeviceImpl(getContext(), associationInfo,
- VirtualDeviceManagerService.this, token, attributionSource, deviceId,
+ VirtualDeviceManagerService.this, mVirtualDeviceLog, token, attributionSource,
+ deviceId,
cameraAccessController, mPendingTrampolineCallback, activityListener,
soundEffectListener, runningAppsChangedCallback, params);
+
synchronized (mVirtualDeviceManagerLock) {
if (mVirtualDevices.size() == 0) {
final long callindId = Binder.clearCallingIdentity();
@@ -521,6 +524,8 @@ public class VirtualDeviceManagerService extends SystemService {
for (int i = 0; i < virtualDevicesSnapshot.size(); i++) {
virtualDevicesSnapshot.get(i).dump(fd, fout, args);
}
+
+ mVirtualDeviceLog.dump(fout);
}
}
diff --git a/services/core/java/com/android/server/BinaryTransparencyService.java b/services/core/java/com/android/server/BinaryTransparencyService.java
index 173a75be10b0..7907d616d1b5 100644
--- a/services/core/java/com/android/server/BinaryTransparencyService.java
+++ b/services/core/java/com/android/server/BinaryTransparencyService.java
@@ -80,9 +80,9 @@ import android.util.apk.ApkSignatureVerifier;
import android.util.apk.ApkSigningBlockUtils;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.modules.expresslog.Histogram;
import com.android.internal.os.IBinaryTransparencyService;
import com.android.internal.util.FrameworkStatsLog;
+import com.android.modules.expresslog.Histogram;
import com.android.server.pm.ApexManager;
import com.android.server.pm.pkg.AndroidPackage;
import com.android.server.pm.pkg.AndroidPackageSplit;
@@ -1391,7 +1391,7 @@ public class BinaryTransparencyService extends SystemService {
// Check the flag to determine whether biometric property verification is enabled. It's
// disabled by default.
if (!DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_BIOMETRICS,
- KEY_ENABLE_BIOMETRIC_PROPERTY_VERIFICATION, false)) {
+ KEY_ENABLE_BIOMETRIC_PROPERTY_VERIFICATION, true)) {
if (DEBUG) {
Slog.d(TAG, "Do not collect/verify biometric properties. Feature disabled by "
+ "DeviceConfig");
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 7fb9580b28ab..4d249abafdf1 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -73,8 +73,8 @@ import android.content.pm.ProviderInfo;
import android.content.pm.UserInfo;
import android.content.res.ObbInfo;
import android.database.ContentObserver;
-import android.media.MediaCodecList;
import android.media.MediaCodecInfo;
+import android.media.MediaCodecList;
import android.media.MediaFormat;
import android.net.Uri;
import android.os.BatteryManager;
@@ -219,6 +219,8 @@ class StorageManagerService extends IStorageManager.Stub
@GuardedBy("mLock")
private final Set<Integer> mCeStoragePreparedUsers = new ArraySet<>();
+ private volatile long mInternalStorageSize = 0;
+
public static class Lifecycle extends SystemService {
private StorageManagerService mStorageManagerService;
@@ -3519,6 +3521,15 @@ class StorageManagerService extends IStorageManager.Stub
return authority;
}
+ @Override
+ public long getInternalStorageBlockDeviceSize() throws RemoteException {
+ if (mInternalStorageSize == 0) {
+ mInternalStorageSize = mVold.getStorageSize();
+ }
+
+ return mInternalStorageSize;
+ }
+
/**
* Enforces that the caller is the {@link ExternalStorageService}
*
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index d3bfc9ab6209..942d35a1d842 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -2514,30 +2514,36 @@ final class ActivityManagerShellCommand extends ShellCommand {
StopUserCallback callback = wait ? new StopUserCallback(userId) : null;
Slogf.d(TAG, "Calling stopUser(%d, %b, %s)", userId, force, callback);
- int res = mInterface.stopUser(userId, force, callback);
- if (res != ActivityManager.USER_OP_SUCCESS) {
- String txt = "";
- switch (res) {
- case ActivityManager.USER_OP_IS_CURRENT:
- txt = " (Can't stop current user)";
- break;
- case ActivityManager.USER_OP_UNKNOWN_USER:
- txt = " (Unknown user " + userId + ")";
- break;
- case ActivityManager.USER_OP_ERROR_IS_SYSTEM:
- txt = " (System user cannot be stopped)";
- break;
- case ActivityManager.USER_OP_ERROR_RELATED_USERS_CANNOT_STOP:
- txt = " (Can't stop user " + userId
- + " - one of its related users can't be stopped)";
- break;
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
+ "shell_runStopUser-" + userId + "-[stopUser]");
+ try {
+ int res = mInterface.stopUser(userId, force, callback);
+ if (res != ActivityManager.USER_OP_SUCCESS) {
+ String txt = "";
+ switch (res) {
+ case ActivityManager.USER_OP_IS_CURRENT:
+ txt = " (Can't stop current user)";
+ break;
+ case ActivityManager.USER_OP_UNKNOWN_USER:
+ txt = " (Unknown user " + userId + ")";
+ break;
+ case ActivityManager.USER_OP_ERROR_IS_SYSTEM:
+ txt = " (System user cannot be stopped)";
+ break;
+ case ActivityManager.USER_OP_ERROR_RELATED_USERS_CANNOT_STOP:
+ txt = " (Can't stop user " + userId
+ + " - one of its related users can't be stopped)";
+ break;
+ }
+ getErrPrintWriter().println("Switch failed: " + res + txt);
+ return -1;
+ } else if (callback != null) {
+ callback.waitForFinish();
}
- getErrPrintWriter().println("Switch failed: " + res + txt);
- return -1;
- } else if (callback != null) {
- callback.waitForFinish();
+ return 0;
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
- return 0;
}
int runIsUserStopped(PrintWriter pw) {
diff --git a/services/core/java/com/android/server/am/ContentProviderHelper.java b/services/core/java/com/android/server/am/ContentProviderHelper.java
index 4cc147fcea03..9219623a031d 100644
--- a/services/core/java/com/android/server/am/ContentProviderHelper.java
+++ b/services/core/java/com/android/server/am/ContentProviderHelper.java
@@ -293,7 +293,8 @@ public class ContentProviderHelper {
return holder;
}
- // Don't expose providers between normal apps and instant apps
+ // Don't expose providers between normal apps and instant apps; enforce limited
+ // package visibility (introduced in Android 11); etc.
try {
if (AppGlobals.getPackageManager()
.resolveContentProvider(name, /*flags=*/ 0, userId) == null) {
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index d426c797b981..27708330efd3 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -105,6 +105,7 @@ import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.SystemProperties;
+import android.os.Trace;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.storage.IStorageManager;
@@ -921,13 +922,24 @@ class UserController implements Handler.Callback {
int stopUser(final int userId, final boolean force, boolean allowDelayedLocking,
final IStopUserCallback stopUserCallback, KeyEvictedCallback keyEvictedCallback) {
- checkCallingPermission(INTERACT_ACROSS_USERS_FULL, "stopUser");
- Preconditions.checkArgument(userId >= 0, "Invalid user id %d", userId);
+ TimingsTraceAndSlog t = new TimingsTraceAndSlog();
- enforceShellRestriction(UserManager.DISALLOW_DEBUGGING_FEATURES, userId);
- synchronized (mLock) {
- return stopUsersLU(userId, force, allowDelayedLocking, stopUserCallback,
- keyEvictedCallback);
+ t.traceBegin("UserController"
+ + (force ? "-force" : "")
+ + (allowDelayedLocking ? "-allowDelayedLocking" : "")
+ + (stopUserCallback != null ? "-withStopUserCallback" : "")
+ + "-" + userId + "-[stopUser]");
+ try {
+ checkCallingPermission(INTERACT_ACROSS_USERS_FULL, "stopUser");
+ Preconditions.checkArgument(userId >= 0, "Invalid user id %d", userId);
+
+ enforceShellRestriction(UserManager.DISALLOW_DEBUGGING_FEATURES, userId);
+ synchronized (mLock) {
+ return stopUsersLU(userId, force, allowDelayedLocking, stopUserCallback,
+ keyEvictedCallback);
+ }
+ } finally {
+ t.traceEnd();
}
}
@@ -944,10 +956,10 @@ class UserController implements Handler.Callback {
if (isCurrentUserLU(userId)) {
return USER_OP_IS_CURRENT;
}
+ TimingsTraceAndSlog t = new TimingsTraceAndSlog();
int[] usersToStop = getUsersToStopLU(userId);
// If one of related users is system or current, no related users should be stopped
- for (int i = 0; i < usersToStop.length; i++) {
- int relatedUserId = usersToStop[i];
+ for (int relatedUserId : usersToStop) {
if ((UserHandle.USER_SYSTEM == relatedUserId) || isCurrentUserLU(relatedUserId)) {
if (DEBUG_MU) {
Slogf.i(TAG, "stopUsersLocked cannot stop related user " + relatedUserId);
@@ -956,8 +968,10 @@ class UserController implements Handler.Callback {
if (force) {
Slogf.i(TAG,
"Force stop user " + userId + ". Related users will not be stopped");
+ t.traceBegin("stopSingleUserLU-force-" + userId + "-[stopUser]");
stopSingleUserLU(userId, allowDelayedLocking, stopUserCallback,
keyEvictedCallback);
+ t.traceEnd();
return USER_OP_SUCCESS;
}
return USER_OP_ERROR_RELATED_USERS_CANNOT_STOP;
@@ -965,9 +979,11 @@ class UserController implements Handler.Callback {
}
if (DEBUG_MU) Slogf.i(TAG, "stopUsersLocked usersToStop=" + Arrays.toString(usersToStop));
for (int userIdToStop : usersToStop) {
+ t.traceBegin("stopSingleUserLU-" + userIdToStop + "-[stopUser]");
stopSingleUserLU(userIdToStop, allowDelayedLocking,
userIdToStop == userId ? stopUserCallback : null,
userIdToStop == userId ? keyEvictedCallback : null);
+ t.traceEnd();
}
return USER_OP_SUCCESS;
}
@@ -1047,14 +1063,24 @@ class UserController implements Handler.Callback {
&& uss.state != UserState.STATE_SHUTDOWN) {
uss.setState(UserState.STATE_STOPPING);
UserManagerInternal userManagerInternal = mInjector.getUserManagerInternal();
+ TimingsTraceAndSlog t = new TimingsTraceAndSlog();
+ t.traceBegin("setUserState-STATE_STOPPING-" + userId + "-[stopUser]");
userManagerInternal.setUserState(userId, uss.state);
+ t.traceEnd();
+ t.traceBegin("unassignUserFromDisplayOnStop-" + userId + "-[stopUser]");
userManagerInternal.unassignUserFromDisplayOnStop(userId);
+ t.traceEnd();
updateStartedUserArrayLU();
final boolean allowDelayedLockingCopied = allowDelayedLocking;
Runnable finishUserStoppingAsync = () ->
- mHandler.post(() -> finishUserStopping(userId, uss, allowDelayedLockingCopied));
+ mHandler.post(() -> {
+ TimingsTraceAndSlog t2 = new TimingsTraceAndSlog();
+ t2.traceBegin("finishUserStopping-" + userId + "-[stopUser]");
+ finishUserStopping(userId, uss, allowDelayedLockingCopied);
+ t2.traceEnd();
+ });
if (mInjector.getUserManager().isPreCreated(userId)) {
finishUserStoppingAsync.run();
@@ -1075,12 +1101,18 @@ class UserController implements Handler.Callback {
@Override
public void performReceive(Intent intent, int resultCode, String data,
Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
+ asyncTraceEnd("broadcast-ACTION_USER_STOPPING-" + userId + "-[stopUser]",
+ userId);
finishUserStoppingAsync.run();
}
};
+ TimingsTraceAndSlog t2 = new TimingsTraceAndSlog();
+ t2.traceBegin("clearBroadcastQueueForUser-" + userId + "-[stopUser]");
// Clear broadcast queue for the user to avoid delivering stale broadcasts
mInjector.clearBroadcastQueueForUser(userId);
+ t2.traceEnd();
+ asyncTraceBegin("broadcast-ACTION_USER_STOPPING-" + userId + "-[stopUser]", userId);
// Kick things off.
mInjector.broadcastIntent(stoppingIntent,
null, stoppingReceiver, 0, null, null,
@@ -1111,7 +1143,10 @@ class UserController implements Handler.Callback {
}
uss.setState(UserState.STATE_SHUTDOWN);
}
+ TimingsTraceAndSlog t = new TimingsTraceAndSlog();
+ t.traceBegin("setUserState-STATE_SHUTDOWN-" + userId + "-[stopUser]");
mInjector.getUserManagerInternal().setUserState(userId, uss.state);
+ t.traceEnd();
mInjector.batteryStatsServiceNoteEvent(
BatteryStats.HistoryItem.EVENT_USER_RUNNING_FINISH,
@@ -1119,7 +1154,12 @@ class UserController implements Handler.Callback {
mInjector.getSystemServiceManager().onUserStopping(userId);
Runnable finishUserStoppedAsync = () ->
- mHandler.post(() -> finishUserStopped(uss, allowDelayedLocking));
+ mHandler.post(() -> {
+ TimingsTraceAndSlog t2 = new TimingsTraceAndSlog();
+ t2.traceBegin("finishUserStopped-" + userId + "-[stopUser]");
+ finishUserStopped(uss, allowDelayedLocking);
+ t2.traceEnd();
+ });
if (mInjector.getUserManager().isPreCreated(userId)) {
finishUserStoppedAsync.run();
return;
@@ -1132,9 +1172,11 @@ class UserController implements Handler.Callback {
@Override
public void performReceive(Intent intent, int resultCode, String data,
Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
+ asyncTraceEnd("broadcast-ACTION_SHUTDOWN-" + userId + "-[stopUser]", userId);
finishUserStoppedAsync.run();
}
};
+ asyncTraceBegin("broadcast-ACTION_SHUTDOWN-" + userId + "-[stopUser]", userId);
mInjector.broadcastIntent(shutdownIntent,
null, shutdownReceiver, 0, null, null, null,
AppOpsManager.OP_NONE,
@@ -1185,6 +1227,7 @@ class UserController implements Handler.Callback {
}
}
}
+ TimingsTraceAndSlog t = new TimingsTraceAndSlog();
if (stopped) {
Slogf.i(TAG, "Removing user state from UserManager.mUserStates for user #" + userId
+ " as a result of user being stopped");
@@ -1193,20 +1236,33 @@ class UserController implements Handler.Callback {
mInjector.activityManagerOnUserStopped(userId);
// Clean up all state and processes associated with the user.
// Kill all the processes for the user.
+ t.traceBegin("forceStopUser-" + userId + "-[stopUser]");
forceStopUser(userId, "finish user");
+ t.traceEnd();
}
for (final IStopUserCallback callback : stopCallbacks) {
try {
- if (stopped) callback.userStopped(userId);
- else callback.userStopAborted(userId);
+ if (stopped) {
+ t.traceBegin("stopCallbacks.userStopped-" + userId + "-[stopUser]");
+ callback.userStopped(userId);
+ t.traceEnd();
+ } else {
+ t.traceBegin("stopCallbacks.userStopAborted-" + userId + "-[stopUser]");
+ callback.userStopAborted(userId);
+ t.traceEnd();
+ }
} catch (RemoteException ignored) {
}
}
if (stopped) {
+ t.traceBegin("systemServiceManagerOnUserStopped-" + userId + "-[stopUser]");
mInjector.systemServiceManagerOnUserStopped(userId);
+ t.traceEnd();
+ t.traceBegin("taskSupervisorRemoveUser-" + userId + "-[stopUser]");
mInjector.taskSupervisorRemoveUser(userId);
+ t.traceEnd();
// Remove the user if it is ephemeral.
if (userInfo.isEphemeral() && !userInfo.preCreated) {
@@ -3361,6 +3417,16 @@ class UserController implements Handler.Callback {
return DEFAULT_USER_SWITCH_TIMEOUT_MS;
}
+ private static void asyncTraceBegin(String msg, int cookie) {
+ Slogf.d(TAG, "%s - asyncTraceBegin(%d)", msg, cookie);
+ Trace.asyncTraceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, msg, cookie);
+ }
+
+ private static void asyncTraceEnd(String msg, int cookie) {
+ Trace.asyncTraceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER, msg, cookie);
+ Slogf.d(TAG, "%s - asyncTraceEnd(%d)", msg, cookie);
+ }
+
/**
* Uptime when any user was being unlocked most recently. 0 if no users have been unlocked
* yet. To avoid lock contention (since it's used by OomAdjuster), it's volatile internally.
@@ -3475,12 +3541,16 @@ class UserController implements Handler.Callback {
ordered = false;
}
+ TimingsTraceAndSlog t = new TimingsTraceAndSlog();
// TODO b/64165549 Verify that mLock is not held before calling AMS methods
synchronized (mService) {
- return mService.broadcastIntentLocked(null, null, null, intent, resolvedType,
- resultTo, resultCode, resultData, resultExtras, requiredPermissions, null,
- null, appOp, bOptions, ordered, sticky, callingPid, callingUid,
- realCallingUid, realCallingPid, userId);
+ t.traceBegin("broadcastIntent-" + userId + "-" + intent.getAction());
+ final int result = mService.broadcastIntentLocked(null, null, null, intent,
+ resolvedType, resultTo, resultCode, resultData, resultExtras,
+ requiredPermissions, null, null, appOp, bOptions, ordered, sticky,
+ callingPid, callingUid, realCallingUid, realCallingPid, userId);
+ t.traceEnd();
+ return result;
}
}
diff --git a/services/core/java/com/android/server/biometrics/AuthenticationStatsCollector.java b/services/core/java/com/android/server/biometrics/AuthenticationStatsCollector.java
index 97e5c6fbd8c3..356b30103c2f 100644
--- a/services/core/java/com/android/server/biometrics/AuthenticationStatsCollector.java
+++ b/services/core/java/com/android/server/biometrics/AuthenticationStatsCollector.java
@@ -26,6 +26,7 @@ import android.content.pm.PackageManager;
import android.hardware.face.FaceManager;
import android.hardware.fingerprint.FingerprintManager;
import android.os.UserHandle;
+import android.util.Slog;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
@@ -54,6 +55,7 @@ public class AuthenticationStatsCollector {
private final float mThreshold;
private final int mModality;
+ private boolean mPersisterInitialized = false;
@NonNull private final Map<Integer, AuthenticationStats> mUserAuthenticationStatsMap;
@@ -85,9 +87,15 @@ public class AuthenticationStatsCollector {
}
private void initializeUserAuthenticationStatsMap() {
- mAuthenticationStatsPersister = new AuthenticationStatsPersister(mContext);
- for (AuthenticationStats stats : mAuthenticationStatsPersister.getAllFrrStats(mModality)) {
- mUserAuthenticationStatsMap.put(stats.getUserId(), stats);
+ try {
+ mAuthenticationStatsPersister = new AuthenticationStatsPersister(mContext);
+ for (AuthenticationStats stats :
+ mAuthenticationStatsPersister.getAllFrrStats(mModality)) {
+ mUserAuthenticationStatsMap.put(stats.getUserId(), stats);
+ }
+ mPersisterInitialized = true;
+ } catch (IllegalStateException e) {
+ Slog.w(TAG, "Failed to initialize AuthenticationStatsPersister.", e);
}
}
@@ -108,7 +116,9 @@ public class AuthenticationStatsCollector {
authenticationStats.authenticate(authenticated);
- persistDataIfNeeded(userId);
+ if (mPersisterInitialized) {
+ persistDataIfNeeded(userId);
+ }
sendNotificationIfNeeded(userId);
}
@@ -166,11 +176,13 @@ public class AuthenticationStatsCollector {
}
private void onUserRemoved(final int userId) {
- if (mAuthenticationStatsPersister == null) {
+ if (!mPersisterInitialized) {
initializeUserAuthenticationStatsMap();
}
- mUserAuthenticationStatsMap.remove(userId);
- mAuthenticationStatsPersister.removeFrrStats(userId);
+ if (mPersisterInitialized) {
+ mUserAuthenticationStatsMap.remove(userId);
+ mAuthenticationStatsPersister.removeFrrStats(userId);
+ }
}
/**
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index d647757442e0..80c3a270efdf 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -119,6 +119,8 @@ public class AutomaticBrightnessController {
// hysteresis threshold.
private final long mBrighteningLightDebounceConfig;
private final long mDarkeningLightDebounceConfig;
+ private final long mBrighteningLightDebounceConfigIdle;
+ private final long mDarkeningLightDebounceConfigIdle;
// If true immediately after the screen is turned on the controller will try to adjust the
// brightness based on the current sensor reads. If false, the controller will collect more data
@@ -253,6 +255,7 @@ public class AutomaticBrightnessController {
int lightSensorWarmUpTime, float brightnessMin, float brightnessMax,
float dozeScaleFactor, int lightSensorRate, int initialLightSensorRate,
long brighteningLightDebounceConfig, long darkeningLightDebounceConfig,
+ long brighteningLightDebounceConfigIdle, long darkeningLightDebounceConfigIdle,
boolean resetAmbientLuxAfterWarmUpConfig, HysteresisLevels ambientBrightnessThresholds,
HysteresisLevels screenBrightnessThresholds,
HysteresisLevels ambientBrightnessThresholdsIdle,
@@ -265,7 +268,8 @@ public class AutomaticBrightnessController {
interactiveModeBrightnessMapper,
lightSensorWarmUpTime, brightnessMin, brightnessMax, dozeScaleFactor,
lightSensorRate, initialLightSensorRate, brighteningLightDebounceConfig,
- darkeningLightDebounceConfig, resetAmbientLuxAfterWarmUpConfig,
+ darkeningLightDebounceConfig, brighteningLightDebounceConfigIdle,
+ darkeningLightDebounceConfigIdle, resetAmbientLuxAfterWarmUpConfig,
ambientBrightnessThresholds, screenBrightnessThresholds,
ambientBrightnessThresholdsIdle, screenBrightnessThresholdsIdle, context,
brightnessModeController, brightnessThrottler, idleModeBrightnessMapper,
@@ -280,6 +284,7 @@ public class AutomaticBrightnessController {
int lightSensorWarmUpTime, float brightnessMin, float brightnessMax,
float dozeScaleFactor, int lightSensorRate, int initialLightSensorRate,
long brighteningLightDebounceConfig, long darkeningLightDebounceConfig,
+ long brighteningLightDebounceConfigIdle, long darkeningLightDebounceConfigIdle,
boolean resetAmbientLuxAfterWarmUpConfig, HysteresisLevels ambientBrightnessThresholds,
HysteresisLevels screenBrightnessThresholds,
HysteresisLevels ambientBrightnessThresholdsIdle,
@@ -303,6 +308,8 @@ public class AutomaticBrightnessController {
mCurrentLightSensorRate = -1;
mBrighteningLightDebounceConfig = brighteningLightDebounceConfig;
mDarkeningLightDebounceConfig = darkeningLightDebounceConfig;
+ mBrighteningLightDebounceConfigIdle = brighteningLightDebounceConfigIdle;
+ mDarkeningLightDebounceConfigIdle = darkeningLightDebounceConfigIdle;
mResetAmbientLuxAfterWarmUpConfig = resetAmbientLuxAfterWarmUpConfig;
mAmbientLightHorizonLong = ambientLightHorizonLong;
mAmbientLightHorizonShort = ambientLightHorizonShort;
@@ -366,9 +373,10 @@ public class AutomaticBrightnessController {
}
/**
- * @return The current brightness recommendation calculated from the current conditions.
- * @param brightnessEvent Event object to populate with details about why the specific
- * brightness was chosen.
+ * @param brightnessEvent Holds details about how the brightness is calculated.
+ *
+ * @return The current automatic brightness recommended value. Populates brightnessEvent
+ * parameters with details about how the brightness was calculated.
*/
public float getAutomaticScreenBrightness(BrightnessEvent brightnessEvent) {
if (brightnessEvent != null) {
@@ -560,6 +568,8 @@ public class AutomaticBrightnessController {
pw.println(" mLightSensorWarmUpTimeConfig=" + mLightSensorWarmUpTimeConfig);
pw.println(" mBrighteningLightDebounceConfig=" + mBrighteningLightDebounceConfig);
pw.println(" mDarkeningLightDebounceConfig=" + mDarkeningLightDebounceConfig);
+ pw.println(" mBrighteningLightDebounceConfigIdle=" + mBrighteningLightDebounceConfigIdle);
+ pw.println(" mDarkeningLightDebounceConfigIdle=" + mDarkeningLightDebounceConfigIdle);
pw.println(" mResetAmbientLuxAfterWarmUpConfig=" + mResetAmbientLuxAfterWarmUpConfig);
pw.println(" mAmbientLightHorizonLong=" + mAmbientLightHorizonLong);
pw.println(" mAmbientLightHorizonShort=" + mAmbientLightHorizonShort);
@@ -820,7 +830,8 @@ public class AutomaticBrightnessController {
}
earliestValidTime = mAmbientLightRingBuffer.getTime(i);
}
- return earliestValidTime + mBrighteningLightDebounceConfig;
+ return earliestValidTime + (isInIdleMode()
+ ? mBrighteningLightDebounceConfigIdle : mBrighteningLightDebounceConfig);
}
private long nextAmbientLightDarkeningTransition(long time) {
@@ -832,7 +843,8 @@ public class AutomaticBrightnessController {
}
earliestValidTime = mAmbientLightRingBuffer.getTime(i);
}
- return earliestValidTime + mDarkeningLightDebounceConfig;
+ return earliestValidTime + (isInIdleMode()
+ ? mDarkeningLightDebounceConfigIdle : mDarkeningLightDebounceConfig);
}
private void updateAmbientLux() {
diff --git a/services/core/java/com/android/server/display/ColorFade.java b/services/core/java/com/android/server/display/ColorFade.java
index 46cd496bdcd3..0d6635d5b6e4 100644
--- a/services/core/java/com/android/server/display/ColorFade.java
+++ b/services/core/java/com/android/server/display/ColorFade.java
@@ -40,6 +40,7 @@ import android.view.SurfaceControl;
import android.view.SurfaceControl.Transaction;
import android.window.ScreenCapture;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.server.LocalServices;
import com.android.server.policy.WindowManagerPolicy;
@@ -138,8 +139,13 @@ final class ColorFade {
public static final int MODE_FADE = 2;
public ColorFade(int displayId) {
+ this(displayId, LocalServices.getService(DisplayManagerInternal.class));
+ }
+
+ @VisibleForTesting
+ ColorFade(int displayId, DisplayManagerInternal displayManagerInternal) {
mDisplayId = displayId;
- mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
+ mDisplayManagerInternal = displayManagerInternal;
}
/**
diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
index 01eceda202d2..c0c60a47263a 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
@@ -184,6 +184,7 @@ import javax.xml.datatype.DatatypeConfigurationException;
* <defaultRefreshRateInHbmSunlight>75</defaultRefreshRateInHbmSunlight>
* <lowerBlockingZoneConfigs>
* <defaultRefreshRate>75</defaultRefreshRate>
+ * <refreshRateThermalThrottlingId>id_of_a_throttling_map</refreshRateThermalThrottlingId>
* <blockingZoneThreshold>
* <displayBrightnessPoint>
* <lux>50</lux>
@@ -252,13 +253,19 @@ import javax.xml.datatype.DatatypeConfigurationException;
* <quirk>canSetBrightnessViaHwc</quirk>
* </quirks>
*
- * <autoBrightness enable="true">
+ * <autoBrightness enabled="true">
* <brighteningLightDebounceMillis>
* 2000
* </brighteningLightDebounceMillis>
* <darkeningLightDebounceMillis>
- * 1000
+ * 4000
* </darkeningLightDebounceMillis>
+ * <brighteningLightDebounceIdleMillis>
+ * 2000
+ * </brighteningLightDebounceIdleMillis>
+ * <darkeningLightDebounceIdleMillis>
+ * 1000
+ * </darkeningLightDebounceIdleMillis>
* <displayBrightnessMapping>
* <displayBrightnessPoint>
* <lux>50</lux>
@@ -649,6 +656,14 @@ public class DisplayDeviceConfig {
private long mAutoBrightnessDarkeningLightDebounce =
INVALID_AUTO_BRIGHTNESS_LIGHT_DEBOUNCE;
+ // Represents the auto-brightness brightening light debounce for idle screen brightness mode.
+ private long mAutoBrightnessBrighteningLightDebounceIdle =
+ INVALID_AUTO_BRIGHTNESS_LIGHT_DEBOUNCE;
+
+ // Represents the auto-brightness darkening light debounce for idle screen brightness mode.
+ private long mAutoBrightnessDarkeningLightDebounceIdle =
+ INVALID_AUTO_BRIGHTNESS_LIGHT_DEBOUNCE;
+
// This setting allows non-default displays to have autobrightness enabled.
private boolean mAutoBrightnessAvailable = false;
// This stores the raw value loaded from the config file - true if not written.
@@ -732,6 +747,12 @@ public class DisplayDeviceConfig {
private float[] mHighDisplayBrightnessThresholds = DEFAULT_BRIGHTNESS_THRESHOLDS;
private float[] mHighAmbientBrightnessThresholds = DEFAULT_BRIGHTNESS_THRESHOLDS;
+ /**
+ * Thermal throttling maps for the low and high blocking zones.
+ */
+ private String mLowBlockingZoneThermalMapId = null;
+ private String mHighBlockingZoneThermalMapId = null;
+
private final HashMap<String, ThermalBrightnessThrottlingData>
mThermalBrightnessThrottlingDataMapByThrottlingId = new HashMap<>();
@@ -1440,6 +1461,20 @@ public class DisplayDeviceConfig {
}
/**
+ * @return Auto brightness darkening light debounce for idle screen brightness mode
+ */
+ public long getAutoBrightnessDarkeningLightDebounceIdle() {
+ return mAutoBrightnessDarkeningLightDebounceIdle;
+ }
+
+ /**
+ * @return Auto brightness brightening light debounce for idle screen brightness mode
+ */
+ public long getAutoBrightnessBrighteningLightDebounceIdle() {
+ return mAutoBrightnessBrighteningLightDebounceIdle;
+ }
+
+ /**
* @return Auto brightness brightening ambient lux levels
*/
public float[] getAutoBrightnessBrighteningLevelsLux() {
@@ -1536,6 +1571,13 @@ public class DisplayDeviceConfig {
}
/**
+ * @return The refresh rate thermal map for low blocking zone.
+ */
+ public SparseArray<SurfaceControl.RefreshRateRange> getLowBlockingZoneThermalMap() {
+ return getThermalRefreshRateThrottlingData(mLowBlockingZoneThermalMapId);
+ }
+
+ /**
* @return An array of high display brightness thresholds. This, in combination with high
* ambient brightness thresholds help define buckets in which the refresh rate switching is not
* allowed.
@@ -1558,6 +1600,13 @@ public class DisplayDeviceConfig {
}
/**
+ * @return The refresh rate thermal map for high blocking zone.
+ */
+ public SparseArray<SurfaceControl.RefreshRateRange> getHighBlockingZoneThermalMap() {
+ return getThermalRefreshRateThrottlingData(mHighBlockingZoneThermalMapId);
+ }
+
+ /**
* @return A mapping from screen off brightness sensor readings to lux values. This estimates
* the ambient lux when the screen is off to determine the initial brightness
*/
@@ -1664,6 +1713,10 @@ public class DisplayDeviceConfig {
+ mAutoBrightnessBrighteningLightDebounce
+ ", mAutoBrightnessDarkeningLightDebounce= "
+ mAutoBrightnessDarkeningLightDebounce
+ + ", mAutoBrightnessBrighteningLightDebounceIdle= "
+ + mAutoBrightnessBrighteningLightDebounceIdle
+ + ", mAutoBrightnessDarkeningLightDebounceIdle= "
+ + mAutoBrightnessDarkeningLightDebounceIdle
+ ", mBrightnessLevelsLux= " + Arrays.toString(mBrightnessLevelsLux)
+ ", mBrightnessLevelsNits= " + Arrays.toString(mBrightnessLevelsNits)
+ ", mDdcAutoBrightnessAvailable= " + mDdcAutoBrightnessAvailable
@@ -1677,6 +1730,8 @@ public class DisplayDeviceConfig {
+ ", mDefaultRefreshRateInHbmHdr= " + mDefaultRefreshRateInHbmHdr
+ ", mDefaultRefreshRateInHbmSunlight= " + mDefaultRefreshRateInHbmSunlight
+ ", mRefreshRateThrottlingMap= " + mRefreshRateThrottlingMap
+ + ", mLowBlockingZoneThermalMapId= " + mLowBlockingZoneThermalMapId
+ + ", mHighBlockingZoneThermalMapId= " + mHighBlockingZoneThermalMapId
+ "\n"
+ "mLowDisplayBrightnessThresholds= "
+ Arrays.toString(mLowDisplayBrightnessThresholds)
@@ -2127,9 +2182,13 @@ public class DisplayDeviceConfig {
}
/**
- * Loads the refresh rate configurations pertaining to the upper blocking zones.
+ * Loads the refresh rate configurations pertaining to the lower blocking zones.
*/
private void loadLowerRefreshRateBlockingZones(BlockingZoneConfig lowerBlockingZoneConfig) {
+ if (lowerBlockingZoneConfig != null) {
+ mLowBlockingZoneThermalMapId =
+ lowerBlockingZoneConfig.getRefreshRateThermalThrottlingId();
+ }
loadLowerBlockingZoneDefaultRefreshRate(lowerBlockingZoneConfig);
loadLowerBrightnessThresholds(lowerBlockingZoneConfig);
}
@@ -2138,6 +2197,10 @@ public class DisplayDeviceConfig {
* Loads the refresh rate configurations pertaining to the upper blocking zones.
*/
private void loadHigherRefreshRateBlockingZones(BlockingZoneConfig upperBlockingZoneConfig) {
+ if (upperBlockingZoneConfig != null) {
+ mHighBlockingZoneThermalMapId =
+ upperBlockingZoneConfig.getRefreshRateThermalThrottlingId();
+ }
loadHigherBlockingZoneDefaultRefreshRate(upperBlockingZoneConfig);
loadHigherBrightnessThresholds(upperBlockingZoneConfig);
}
@@ -2277,6 +2340,9 @@ public class DisplayDeviceConfig {
final AutoBrightness autoBrightness = config.getAutoBrightness();
loadAutoBrightnessBrighteningLightDebounce(autoBrightness);
loadAutoBrightnessDarkeningLightDebounce(autoBrightness);
+ // Idle must be called after interactive, since we fall back to it if needed.
+ loadAutoBrightnessBrighteningLightDebounceIdle(autoBrightness);
+ loadAutoBrightnessDarkeningLightDebounceIdle(autoBrightness);
loadAutoBrightnessDisplayBrightnessMapping(autoBrightness);
loadEnableAutoBrightness(autoBrightness);
}
@@ -2312,6 +2378,37 @@ public class DisplayDeviceConfig {
}
/**
+ * Loads the auto-brightness brightening light debounce for idle mode. Internally, this takes
+ * care of loading the value from the display config, and if not present, falls back to
+ * whichever interactive value was chosen.
+ */
+ private void loadAutoBrightnessBrighteningLightDebounceIdle(
+ AutoBrightness autoBrightnessConfig) {
+ if (autoBrightnessConfig == null
+ || autoBrightnessConfig.getBrighteningLightDebounceIdleMillis() == null) {
+ mAutoBrightnessBrighteningLightDebounceIdle = mAutoBrightnessBrighteningLightDebounce;
+ } else {
+ mAutoBrightnessBrighteningLightDebounceIdle =
+ autoBrightnessConfig.getBrighteningLightDebounceIdleMillis().intValue();
+ }
+ }
+
+ /**
+ * Loads the auto-brightness darkening light debounce for idle mode. Internally, this takes
+ * care of loading the value from the display config, and if not present, falls back to
+ * whichever interactive value was chosen.
+ */
+ private void loadAutoBrightnessDarkeningLightDebounceIdle(AutoBrightness autoBrightnessConfig) {
+ if (autoBrightnessConfig == null
+ || autoBrightnessConfig.getDarkeningLightDebounceIdleMillis() == null) {
+ mAutoBrightnessDarkeningLightDebounceIdle = mAutoBrightnessDarkeningLightDebounce;
+ } else {
+ mAutoBrightnessDarkeningLightDebounceIdle =
+ autoBrightnessConfig.getDarkeningLightDebounceIdleMillis().intValue();
+ }
+ }
+
+ /**
* Loads the auto-brightness display brightness mappings. Internally, this takes care of
* loading the value from the display config, and if not present, falls back to config.xml.
*/
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index dd22cfd1f294..79b73430b934 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -1232,6 +1232,10 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
.getAutoBrightnessBrighteningLightDebounce();
long darkeningLightDebounce = mDisplayDeviceConfig
.getAutoBrightnessDarkeningLightDebounce();
+ long brighteningLightDebounceIdle = mDisplayDeviceConfig
+ .getAutoBrightnessBrighteningLightDebounceIdle();
+ long darkeningLightDebounceIdle = mDisplayDeviceConfig
+ .getAutoBrightnessDarkeningLightDebounceIdle();
boolean autoBrightnessResetAmbientLuxAfterWarmUp = resources.getBoolean(
com.android.internal.R.bool.config_autoBrightnessResetAmbientLuxAfterWarmUp);
@@ -1271,7 +1275,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
mInteractiveModeBrightnessMapper, lightSensorWarmUpTimeConfig,
PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX, dozeScaleFactor,
lightSensorRate, initialLightSensorRate, brighteningLightDebounce,
- darkeningLightDebounce, autoBrightnessResetAmbientLuxAfterWarmUp,
+ darkeningLightDebounce, brighteningLightDebounceIdle,
+ darkeningLightDebounceIdle, autoBrightnessResetAmbientLuxAfterWarmUp,
ambientBrightnessThresholds, screenBrightnessThresholds,
ambientBrightnessThresholdsIdle, screenBrightnessThresholdsIdle, mContext,
mBrightnessRangeController, mBrightnessThrottler, mIdleModeBrightnessMapper,
@@ -1906,6 +1911,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
final float currentBrightness = mPowerState.getScreenBrightness();
final float currentSdrBrightness = mPowerState.getSdrScreenBrightness();
+
if (isValidBrightnessValue(animateValue)
&& (animateValue != currentBrightness
|| sdrAnimateValue != currentSdrBrightness)) {
@@ -3536,6 +3542,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
int lightSensorWarmUpTime, float brightnessMin, float brightnessMax,
float dozeScaleFactor, int lightSensorRate, int initialLightSensorRate,
long brighteningLightDebounceConfig, long darkeningLightDebounceConfig,
+ long brighteningLightDebounceConfigIdle, long darkeningLightDebounceConfigIdle,
boolean resetAmbientLuxAfterWarmUpConfig,
HysteresisLevels ambientBrightnessThresholds,
HysteresisLevels screenBrightnessThresholds,
@@ -3549,6 +3556,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
interactiveModeBrightnessMapper, lightSensorWarmUpTime, brightnessMin,
brightnessMax, dozeScaleFactor, lightSensorRate, initialLightSensorRate,
brighteningLightDebounceConfig, darkeningLightDebounceConfig,
+ brighteningLightDebounceConfigIdle, darkeningLightDebounceConfigIdle,
resetAmbientLuxAfterWarmUpConfig, ambientBrightnessThresholds,
screenBrightnessThresholds, ambientBrightnessThresholdsIdle,
screenBrightnessThresholdsIdle, context, brightnessRangeController,
diff --git a/services/core/java/com/android/server/display/DisplayPowerController2.java b/services/core/java/com/android/server/display/DisplayPowerController2.java
index 0f89a6e04cf9..6c2240becbff 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController2.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController2.java
@@ -1043,6 +1043,10 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal
.getAutoBrightnessBrighteningLightDebounce();
long darkeningLightDebounce = mDisplayDeviceConfig
.getAutoBrightnessDarkeningLightDebounce();
+ long brighteningLightDebounceIdle = mDisplayDeviceConfig
+ .getAutoBrightnessBrighteningLightDebounceIdle();
+ long darkeningLightDebounceIdle = mDisplayDeviceConfig
+ .getAutoBrightnessDarkeningLightDebounceIdle();
boolean autoBrightnessResetAmbientLuxAfterWarmUp = resources.getBoolean(
R.bool.config_autoBrightnessResetAmbientLuxAfterWarmUp);
@@ -1082,7 +1086,8 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal
mInteractiveModeBrightnessMapper, lightSensorWarmUpTimeConfig,
PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX, dozeScaleFactor,
lightSensorRate, initialLightSensorRate, brighteningLightDebounce,
- darkeningLightDebounce, autoBrightnessResetAmbientLuxAfterWarmUp,
+ darkeningLightDebounce, brighteningLightDebounceIdle,
+ darkeningLightDebounceIdle, autoBrightnessResetAmbientLuxAfterWarmUp,
ambientBrightnessThresholds, screenBrightnessThresholds,
ambientBrightnessThresholdsIdle, screenBrightnessThresholdsIdle, mContext,
mBrightnessRangeController, mBrightnessThrottler, mIdleModeBrightnessMapper,
@@ -2886,6 +2891,7 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal
int lightSensorWarmUpTime, float brightnessMin, float brightnessMax,
float dozeScaleFactor, int lightSensorRate, int initialLightSensorRate,
long brighteningLightDebounceConfig, long darkeningLightDebounceConfig,
+ long brighteningLightDebounceConfigIdle, long darkeningLightDebounceConfigIdle,
boolean resetAmbientLuxAfterWarmUpConfig,
HysteresisLevels ambientBrightnessThresholds,
HysteresisLevels screenBrightnessThresholds,
@@ -2899,6 +2905,7 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal
interactiveModeBrightnessMapper, lightSensorWarmUpTime, brightnessMin,
brightnessMax, dozeScaleFactor, lightSensorRate, initialLightSensorRate,
brighteningLightDebounceConfig, darkeningLightDebounceConfig,
+ brighteningLightDebounceConfigIdle, darkeningLightDebounceConfigIdle,
resetAmbientLuxAfterWarmUpConfig, ambientBrightnessThresholds,
screenBrightnessThresholds, ambientBrightnessThresholdsIdle,
screenBrightnessThresholdsIdle, context, brightnessModeController,
diff --git a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
index f15f0368974e..f08878f658d3 100644
--- a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
@@ -1540,6 +1540,21 @@ public class DisplayModeDirector {
private final Injector mInjector;
private final Handler mHandler;
+ private final IThermalEventListener.Stub mThermalListener =
+ new IThermalEventListener.Stub() {
+ @Override
+ public void notifyThrottling(Temperature temp) {
+ @Temperature.ThrottlingStatus int currentStatus = temp.getStatus();
+ synchronized (mLock) {
+ if (mThermalStatus != currentStatus) {
+ mThermalStatus = currentStatus;
+ }
+ onBrightnessChangedLocked();
+ }
+ }
+ };
+ private boolean mThermalRegistered;
+
// Enable light sensor only when mShouldObserveAmbientLowChange is true or
// mShouldObserveAmbientHighChange is true, screen is on, peak refresh rate
// changeable and low power mode off. After initialization, these states will
@@ -1548,9 +1563,17 @@ public class DisplayModeDirector {
private boolean mRefreshRateChangeable = false;
private boolean mLowPowerModeEnabled = false;
+ @Nullable
+ private SparseArray<RefreshRateRange> mLowZoneRefreshRateForThermals;
private int mRefreshRateInLowZone;
+
+ @Nullable
+ private SparseArray<RefreshRateRange> mHighZoneRefreshRateForThermals;
private int mRefreshRateInHighZone;
+ @GuardedBy("mLock")
+ private @Temperature.ThrottlingStatus int mThermalStatus = Temperature.THROTTLING_NONE;
+
BrightnessObserver(Context context, Handler handler, Injector injector) {
mContext = context;
mHandler = handler;
@@ -1649,6 +1672,8 @@ public class DisplayModeDirector {
R.integer.config_defaultRefreshRateInZone)
: displayDeviceConfig.getDefaultLowBlockingZoneRefreshRate();
}
+ mLowZoneRefreshRateForThermals = displayDeviceConfig == null ? null
+ : displayDeviceConfig.getLowBlockingZoneThermalMap();
mRefreshRateInLowZone = refreshRateInLowZone;
}
@@ -1668,6 +1693,8 @@ public class DisplayModeDirector {
R.integer.config_fixedRefreshRateInHighZone)
: displayDeviceConfig.getDefaultHighBlockingZoneRefreshRate();
}
+ mHighZoneRefreshRateForThermals = displayDeviceConfig == null ? null
+ : displayDeviceConfig.getHighBlockingZoneThermalMap();
mRefreshRateInHighZone = refreshRateInHighZone;
}
@@ -2117,6 +2144,15 @@ public class DisplayModeDirector {
if (insideLowZone) {
refreshRateVote =
Vote.forPhysicalRefreshRates(mRefreshRateInLowZone, mRefreshRateInLowZone);
+ if (mLowZoneRefreshRateForThermals != null) {
+ RefreshRateRange range = SkinThermalStatusObserver
+ .findBestMatchingRefreshRateRange(mThermalStatus,
+ mLowZoneRefreshRateForThermals);
+ if (range != null) {
+ refreshRateVote =
+ Vote.forPhysicalRefreshRates(range.min, range.max);
+ }
+ }
refreshRateSwitchingVote = Vote.forDisableRefreshRateSwitching();
}
@@ -2126,6 +2162,15 @@ public class DisplayModeDirector {
refreshRateVote =
Vote.forPhysicalRefreshRates(mRefreshRateInHighZone,
mRefreshRateInHighZone);
+ if (mHighZoneRefreshRateForThermals != null) {
+ RefreshRateRange range = SkinThermalStatusObserver
+ .findBestMatchingRefreshRateRange(mThermalStatus,
+ mHighZoneRefreshRateForThermals);
+ if (range != null) {
+ refreshRateVote =
+ Vote.forPhysicalRefreshRates(range.min, range.max);
+ }
+ }
refreshRateSwitchingVote = Vote.forDisableRefreshRateSwitching();
}
@@ -2184,13 +2229,25 @@ public class DisplayModeDirector {
+ mRefreshRateChangeable);
}
+ boolean registerForThermals = false;
if ((mShouldObserveAmbientLowChange || mShouldObserveAmbientHighChange)
&& isDeviceActive() && !mLowPowerModeEnabled && mRefreshRateChangeable) {
registerLightSensor();
-
+ registerForThermals = mLowZoneRefreshRateForThermals != null
+ || mHighZoneRefreshRateForThermals != null;
} else {
unregisterSensorListener();
}
+
+ if (registerForThermals && !mThermalRegistered) {
+ mThermalRegistered = mInjector.registerThermalServiceListener(mThermalListener);
+ } else if (!registerForThermals && mThermalRegistered) {
+ mInjector.unregisterThermalServiceListener(mThermalListener);
+ mThermalRegistered = false;
+ synchronized (mLock) {
+ mThermalStatus = Temperature.THROTTLING_NONE; // reset
+ }
+ }
}
private void registerLightSensor() {
@@ -2821,6 +2878,7 @@ public class DisplayModeDirector {
boolean isDozeState(Display d);
boolean registerThermalServiceListener(IThermalEventListener listener);
+ void unregisterThermalServiceListener(IThermalEventListener listener);
boolean supportsFrameRateOverride();
@@ -2922,6 +2980,19 @@ public class DisplayModeDirector {
}
@Override
+ public void unregisterThermalServiceListener(IThermalEventListener listener) {
+ IThermalService thermalService = getThermalService();
+ if (thermalService == null) {
+ Slog.w(TAG, "Could not unregister thermal status. Service not available");
+ }
+ try {
+ thermalService.unregisterThermalEventListener(listener);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to unregister thermal status listener", e);
+ }
+ }
+
+ @Override
public boolean supportsFrameRateOverride() {
return SurfaceFlingerProperties.enable_frame_rate_override().orElse(true);
}
diff --git a/services/core/java/com/android/server/display/mode/SkinThermalStatusObserver.java b/services/core/java/com/android/server/display/mode/SkinThermalStatusObserver.java
index 58e15503be7d..b29cda88802a 100644
--- a/services/core/java/com/android/server/display/mode/SkinThermalStatusObserver.java
+++ b/services/core/java/com/android/server/display/mode/SkinThermalStatusObserver.java
@@ -64,6 +64,20 @@ final class SkinThermalStatusObserver extends IThermalEventListener.Stub impleme
mHandler = handler;
}
+ @Nullable
+ public static SurfaceControl.RefreshRateRange findBestMatchingRefreshRateRange(
+ @Temperature.ThrottlingStatus int currentStatus,
+ SparseArray<SurfaceControl.RefreshRateRange> throttlingMap) {
+ SurfaceControl.RefreshRateRange foundRange = null;
+ for (int status = currentStatus; status >= 0; status--) {
+ foundRange = throttlingMap.get(status);
+ if (foundRange != null) {
+ break;
+ }
+ }
+ return foundRange;
+ }
+
void observe() {
// if failed to register thermal service listener, don't register display listener
if (!mInjector.registerThermalServiceListener(this)) {
@@ -228,20 +242,6 @@ final class SkinThermalStatusObserver extends IThermalEventListener.Stub impleme
}
}
- @Nullable
- private SurfaceControl.RefreshRateRange findBestMatchingRefreshRateRange(
- @Temperature.ThrottlingStatus int currentStatus,
- SparseArray<SurfaceControl.RefreshRateRange> throttlingMap) {
- SurfaceControl.RefreshRateRange foundRange = null;
- for (int status = currentStatus; status >= 0; status--) {
- foundRange = throttlingMap.get(status);
- if (foundRange != null) {
- break;
- }
- }
- return foundRange;
- }
-
private void fallbackReportThrottlingIfNeeded(int displayId,
@Temperature.ThrottlingStatus int currentStatus) {
Vote vote = null;
diff --git a/services/core/java/com/android/server/input/GestureMonitorSpyWindow.java b/services/core/java/com/android/server/input/GestureMonitorSpyWindow.java
index d238dae634ad..2ede56dcecd9 100644
--- a/services/core/java/com/android/server/input/GestureMonitorSpyWindow.java
+++ b/services/core/java/com/android/server/input/GestureMonitorSpyWindow.java
@@ -67,7 +67,7 @@ class GestureMonitorSpyWindow {
final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
t.setInputWindowInfo(mInputSurface, mWindowHandle);
- t.setLayer(mInputSurface, Integer.MAX_VALUE);
+ t.setLayer(mInputSurface, InputManagerService.INPUT_OVERLAY_LAYER_GESTURE_MONITOR);
t.setPosition(mInputSurface, 0, 0);
t.setCrop(mInputSurface, null /* crop to parent surface */);
t.show(mInputSurface);
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index b8e9d5dfb3bc..62660c4f3c6d 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -381,6 +381,17 @@ public class InputManagerService extends IInputManager.Stub
public static final int SW_CAMERA_LENS_COVER_BIT = 1 << SW_CAMERA_LENS_COVER;
public static final int SW_MUTE_DEVICE_BIT = 1 << SW_MUTE_DEVICE;
+ // The following are layer numbers used for z-ordering the input overlay layers on the display.
+ // This is used for ordering layers inside {@code DisplayContent#getInputOverlayLayer()}.
+ //
+ // The layer where gesture monitors are added.
+ public static final int INPUT_OVERLAY_LAYER_GESTURE_MONITOR = 1;
+ // Place the handwriting layer above gesture monitors so that styluses cannot trigger
+ // system gestures (e.g. navigation bar, edge-back, etc) while there is an active
+ // handwriting session.
+ public static final int INPUT_OVERLAY_LAYER_HANDWRITING_SURFACE = 2;
+
+
private final String mVelocityTrackerStrategy;
/** Whether to use the dev/input/event or uevent subsystem for the audio jack. */
diff --git a/services/core/java/com/android/server/inputmethod/HandwritingEventReceiverSurface.java b/services/core/java/com/android/server/inputmethod/HandwritingEventReceiverSurface.java
index 0c889c2765bb..7726f40fa2ae 100644
--- a/services/core/java/com/android/server/inputmethod/HandwritingEventReceiverSurface.java
+++ b/services/core/java/com/android/server/inputmethod/HandwritingEventReceiverSurface.java
@@ -27,16 +27,13 @@ import android.view.InputWindowHandle;
import android.view.SurfaceControl;
import android.view.WindowManager;
+import com.android.server.input.InputManagerService;
+
final class HandwritingEventReceiverSurface {
public static final String TAG = HandwritingEventReceiverSurface.class.getSimpleName();
static final boolean DEBUG = HandwritingModeController.DEBUG;
- // Place the layer at the highest layer so stylus cannot trigger gesture monitors
- // (e.g. navigation bar, edge-back, etc) while handwriting is ongoing.
- // TODO(b/217538817): Specify the ordering in WM by usage.
- private static final int HANDWRITING_SURFACE_LAYER = Integer.MAX_VALUE;
-
private final InputWindowHandle mWindowHandle;
private final InputChannel mClientChannel;
private final SurfaceControl mInputSurface;
@@ -68,7 +65,7 @@ final class HandwritingEventReceiverSurface {
final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
t.setInputWindowInfo(mInputSurface, mWindowHandle);
- t.setLayer(mInputSurface, HANDWRITING_SURFACE_LAYER);
+ t.setLayer(mInputSurface, InputManagerService.INPUT_OVERLAY_LAYER_HANDWRITING_SURFACE);
t.setPosition(mInputSurface, 0, 0);
t.setCrop(mInputSurface, null /* crop to parent surface */);
t.show(mInputSurface);
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 8e7baf26984a..1ec8b10813cd 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -3275,15 +3275,18 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
} else {
// If subtype is null, try to find the most applicable one from
// getCurrentInputMethodSubtype.
+ subtypeId = NOT_A_SUBTYPE_ID;
newSubtype = getCurrentInputMethodSubtypeLocked();
+ if (newSubtype != null) {
+ for (int i = 0; i < subtypeCount; ++i) {
+ if (Objects.equals(newSubtype, info.getSubtypeAt(i))) {
+ subtypeId = i;
+ break;
+ }
+ }
+ }
}
- if (newSubtype == null || oldSubtype == null) {
- Slog.w(TAG, "Illegal subtype state: old subtype = " + oldSubtype
- + ", new subtype = " + newSubtype);
- notifyInputMethodSubtypeChangedLocked(userId, info, null);
- return;
- }
- if (!newSubtype.equals(oldSubtype)) {
+ if (!Objects.equals(newSubtype, oldSubtype)) {
setSelectedInputMethodAndSubtypeLocked(info, subtypeId, true);
IInputMethodInvoker curMethod = getCurMethodLocked();
if (curMethod != null) {
diff --git a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
index d700c6adfebb..8e9c21f5f35f 100644
--- a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
+++ b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
@@ -77,7 +77,6 @@ import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Set;
-
/**
* A class that manages a user's synthetic password (SP) ({@link #SyntheticPassword}), along with a
* set of SP protectors that are independent ways that the SP is protected.
@@ -552,22 +551,48 @@ class SyntheticPasswordManager {
}
}
- private @Nullable IWeaver getWeaverServiceInternal() {
- // Try to get the AIDL service first
+ private @Nullable IWeaver getWeaverAidlService() {
+ final IWeaver aidlWeaver;
try {
- IWeaver aidlWeaver = IWeaver.Stub.asInterface(
- ServiceManager.waitForDeclaredService(IWeaver.DESCRIPTOR + "/default"));
- if (aidlWeaver != null) {
- Slog.i(TAG, "Using AIDL weaver service");
- try {
- aidlWeaver.asBinder().linkToDeath(new WeaverDiedRecipient(), 0);
- } catch (RemoteException e) {
- Slog.w(TAG, "Unable to register Weaver death recipient", e);
- }
- return aidlWeaver;
- }
+ aidlWeaver =
+ IWeaver.Stub.asInterface(
+ ServiceManager.waitForDeclaredService(IWeaver.DESCRIPTOR + "/default"));
} catch (SecurityException e) {
Slog.w(TAG, "Does not have permissions to get AIDL weaver service");
+ return null;
+ }
+ if (aidlWeaver == null) {
+ return null;
+ }
+ final int aidlVersion;
+ try {
+ aidlVersion = aidlWeaver.getInterfaceVersion();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Cannot get AIDL weaver service version", e);
+ return null;
+ }
+ if (aidlVersion < 2) {
+ Slog.w(TAG,
+ "Ignoring AIDL weaver service v"
+ + aidlVersion
+ + " because only v2 and later are supported");
+ return null;
+ }
+ Slog.i(TAG, "Found AIDL weaver service v" + aidlVersion);
+ return aidlWeaver;
+ }
+
+ private @Nullable IWeaver getWeaverServiceInternal() {
+ // Try to get the AIDL service first
+ IWeaver aidlWeaver = getWeaverAidlService();
+ if (aidlWeaver != null) {
+ Slog.i(TAG, "Using AIDL weaver service");
+ try {
+ aidlWeaver.asBinder().linkToDeath(new WeaverDiedRecipient(), 0);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Unable to register Weaver death recipient", e);
+ }
+ return aidlWeaver;
}
// If the AIDL service can't be found, look for the HIDL service
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index 37bcfbb03899..5b870692e348 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -313,7 +313,7 @@ class MediaRouter2ServiceImpl {
if (linkedItemLandingComponent != null) {
int callingUid = Binder.getCallingUid();
MediaServerUtils.enforcePackageName(
- linkedItemLandingComponent.getPackageName(), callingUid);
+ mContext, linkedItemLandingComponent.getPackageName(), callingUid);
if (!MediaServerUtils.isValidActivityComponentName(
mContext,
linkedItemLandingComponent,
diff --git a/services/core/java/com/android/server/media/MediaServerUtils.java b/services/core/java/com/android/server/media/MediaServerUtils.java
index 60592feb867d..6a954d66d18d 100644
--- a/services/core/java/com/android/server/media/MediaServerUtils.java
+++ b/services/core/java/com/android/server/media/MediaServerUtils.java
@@ -31,6 +31,7 @@ import android.text.TextUtils;
import com.android.server.LocalServices;
import java.io.PrintWriter;
+import java.util.Arrays;
import java.util.List;
/** Util class for media server. */
@@ -63,7 +64,8 @@ import java.util.List;
* @throws IllegalArgumentException If the given {@code packageName} does not correspond to the
* given {@code uid}, and {@code uid} is not the root uid, or the shell uid.
*/
- public static void enforcePackageName(String packageName, int uid) {
+ public static void enforcePackageName(
+ @NonNull Context context, @NonNull String packageName, int uid) {
if (uid == Process.ROOT_UID || uid == Process.SHELL_UID) {
return;
}
@@ -76,12 +78,16 @@ import java.util.List;
packageManagerInternal.getPackageUid(
packageName, 0 /* flags */, UserHandle.getUserId(uid));
if (!UserHandle.isSameApp(uid, actualUid)) {
+ String[] uidPackages = context.getPackageManager().getPackagesForUid(uid);
throw new IllegalArgumentException(
"packageName does not belong to the calling uid; "
+ "pkg="
+ packageName
+ ", uid="
- + uid);
+ + uid
+ + " ("
+ + Arrays.toString(uidPackages)
+ + ")");
}
}
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index f2242bf48dcd..f4c95185210a 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -542,7 +542,7 @@ public class MediaSessionService extends SystemService implements Monitor {
int callingPid, int callingUid, String callingPackage, String reason) {
final long token = Binder.clearCallingIdentity();
try {
- MediaServerUtils.enforcePackageName(callingPackage, callingUid);
+ MediaServerUtils.enforcePackageName(mContext, callingPackage, callingUid);
if (targetUid != callingUid) {
boolean canAllowWhileInUse = mActivityManagerLocal
.canAllowWhileInUsePermissionInFgs(callingPid, callingUid, callingPackage);
@@ -1187,7 +1187,7 @@ public class MediaSessionService extends SystemService implements Monitor {
final int uid = Binder.getCallingUid();
final long token = Binder.clearCallingIdentity();
try {
- MediaServerUtils.enforcePackageName(packageName, uid);
+ MediaServerUtils.enforcePackageName(mContext, packageName, uid);
int resolvedUserId = handleIncomingUser(pid, uid, userId, packageName);
if (cb == null) {
throw new IllegalArgumentException("Controller callback cannot be null");
@@ -1239,7 +1239,7 @@ public class MediaSessionService extends SystemService implements Monitor {
final int userId = userHandle.getIdentifier();
final long token = Binder.clearCallingIdentity();
try {
- MediaServerUtils.enforcePackageName(packageName, uid);
+ MediaServerUtils.enforcePackageName(mContext, packageName, uid);
enforceMediaPermissions(packageName, pid, uid, userId);
MediaSessionRecordImpl record;
@@ -1270,7 +1270,7 @@ public class MediaSessionService extends SystemService implements Monitor {
final int userId = userHandle.getIdentifier();
final long token = Binder.clearCallingIdentity();
try {
- MediaServerUtils.enforcePackageName(packageName, uid);
+ MediaServerUtils.enforcePackageName(mContext, packageName, uid);
enforceMediaPermissions(packageName, pid, uid, userId);
MediaSessionRecordImpl record;
@@ -1596,7 +1596,7 @@ public class MediaSessionService extends SystemService implements Monitor {
final int userId = userHandle.getIdentifier();
final long token = Binder.clearCallingIdentity();
try {
- MediaServerUtils.enforcePackageName(packageName, uid);
+ MediaServerUtils.enforcePackageName(mContext, packageName, uid);
enforceMediaPermissions(packageName, pid, uid, userId);
synchronized (mLock) {
@@ -2110,7 +2110,7 @@ public class MediaSessionService extends SystemService implements Monitor {
// If they gave us a component name verify they own the
// package
packageName = componentName.getPackageName();
- MediaServerUtils.enforcePackageName(packageName, uid);
+ MediaServerUtils.enforcePackageName(mContext, packageName, uid);
}
// Check that they can make calls on behalf of the user and get the final user id
int resolvedUserId = handleIncomingUser(pid, uid, userId, packageName);
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 009cc3b57594..6f0a4b48c09e 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -1928,6 +1928,7 @@ public class NotificationManagerService extends SystemService {
mConditionProviders.onUserRemoved(userId);
mAssistants.onUserRemoved(userId);
mHistoryManager.onUserRemoved(userId);
+ mPreferencesHelper.syncChannelsBypassingDnd();
handleSavePolicyFile();
} else if (action.equals(Intent.ACTION_USER_UNLOCKED)) {
final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, USER_NULL);
@@ -2379,6 +2380,7 @@ public class NotificationManagerService extends SystemService {
});
mPermissionHelper = permissionHelper;
mNotificationChannelLogger = channelLogger;
+ mUserProfiles.updateCache(getContext());
mPreferencesHelper = new PreferencesHelper(getContext(),
mPackageManagerClient,
mRankingHandler,
@@ -2387,6 +2389,7 @@ public class NotificationManagerService extends SystemService {
mPermissionManager,
mNotificationChannelLogger,
mAppOps,
+ mUserProfiles,
new SysUiStatsEvent.BuilderFactory(),
mShowReviewPermissionsNotification);
mRankingHelper = new RankingHelper(getContext(),
@@ -2442,8 +2445,6 @@ public class NotificationManagerService extends SystemService {
mZenModeHelper.initZenMode();
mInterruptionFilter = mZenModeHelper.getZenModeListenerInterruptionFilter();
- mUserProfiles.updateCache(getContext());
-
if (mPackageManagerClient.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
telephonyManager.listen(new PhoneStateListener() {
@Override
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index 3f799dc08a3f..0e37f101ce70 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -38,7 +38,6 @@ import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
-import android.app.ActivityManager;
import android.app.AppOpsManager;
import android.app.Notification;
import android.app.NotificationChannel;
@@ -53,7 +52,6 @@ import android.content.pm.ParceledListSlice;
import android.content.pm.UserInfo;
import android.metrics.LogMaker;
import android.net.Uri;
-import android.os.Binder;
import android.os.Build;
import android.os.Process;
import android.os.UserHandle;
@@ -114,7 +112,6 @@ public class PreferencesHelper implements RankingConfig {
private static final int XML_VERSION_REVIEW_PERMISSIONS_NOTIFICATION = 4;
@VisibleForTesting
static final int UNKNOWN_UID = UserHandle.USER_NULL;
- private static final String NON_BLOCKABLE_CHANNEL_DELIM = ":";
@VisibleForTesting
static final int NOTIFICATION_CHANNEL_COUNT_LIMIT = 5000;
@@ -196,6 +193,7 @@ public class PreferencesHelper implements RankingConfig {
private final PermissionManager mPermissionManager;
private final NotificationChannelLogger mNotificationChannelLogger;
private final AppOpsManager mAppOps;
+ private final ManagedServices.UserProfiles mUserProfiles;
private SparseBooleanArray mBadgingEnabled;
private SparseBooleanArray mBubblesEnabled;
@@ -204,14 +202,12 @@ public class PreferencesHelper implements RankingConfig {
private boolean mIsMediaNotificationFilteringEnabled = DEFAULT_MEDIA_NOTIFICATION_FILTERING;
private boolean mCurrentUserHasChannelsBypassingDnd;
private boolean mHideSilentStatusBarIcons = DEFAULT_HIDE_SILENT_STATUS_BAR_ICONS;
- private boolean mShowReviewPermissionsNotification;
-
- private boolean mAllowInvalidShortcuts = false;
+ private final boolean mShowReviewPermissionsNotification;
public PreferencesHelper(Context context, PackageManager pm, RankingHandler rankingHandler,
ZenModeHelper zenHelper, PermissionHelper permHelper, PermissionManager permManager,
NotificationChannelLogger notificationChannelLogger,
- AppOpsManager appOpsManager,
+ AppOpsManager appOpsManager, ManagedServices.UserProfiles userProfiles,
SysUiStatsEvent.BuilderFactory statsEventBuilderFactory,
boolean showReviewPermissionsNotification) {
mContext = context;
@@ -222,6 +218,7 @@ public class PreferencesHelper implements RankingConfig {
mPm = pm;
mNotificationChannelLogger = notificationChannelLogger;
mAppOps = appOpsManager;
+ mUserProfiles = userProfiles;
mStatsEventBuilderFactory = statsEventBuilderFactory;
mShowReviewPermissionsNotification = showReviewPermissionsNotification;
@@ -435,7 +432,7 @@ public class PreferencesHelper implements RankingConfig {
channel.getConversationId() != null &&
channel.getConversationId().contains(
PLACEHOLDER_CONVERSATION_ID);
- return mAllowInvalidShortcuts || (!mAllowInvalidShortcuts && !isInvalidShortcutChannel);
+ return !isInvalidShortcutChannel;
}
private boolean isDeletionOk(NotificationChannel nc) {
@@ -1790,8 +1787,9 @@ public class PreferencesHelper implements RankingConfig {
* Syncs {@link #mCurrentUserHasChannelsBypassingDnd} with the current user's notification
* policy before updating. Must be called:
* <ul>
- * <li>On system init, after channels and DND configurations are loaded.</li>
- * <li>When the current user changes, after the corresponding DND config is loaded.</li>
+ * <li>On system init, after channels and DND configurations are loaded.
+ * <li>When the current user is switched, after the corresponding DND config is loaded.
+ * <li>If users are removed (the removed user could've been a profile of the current one).
* </ul>
*/
void syncChannelsBypassingDnd() {
@@ -1805,20 +1803,19 @@ public class PreferencesHelper implements RankingConfig {
/**
* Updates the user's NotificationPolicy based on whether the current userId has channels
* bypassing DND. It should be called whenever a channel is created, updated, or deleted, or
- * when the current user is switched.
+ * when the current user (or its profiles) change.
*/
private void updateCurrentUserHasChannelsBypassingDnd(int callingUid,
boolean fromSystemOrSystemUi) {
ArraySet<Pair<String, Integer>> candidatePkgs = new ArraySet<>();
- final int currentUserId = getCurrentUser();
+ final IntArray currentUserIds = mUserProfiles.getCurrentProfileIds();
synchronized (mPackagePreferences) {
final int numPackagePreferences = mPackagePreferences.size();
for (int i = 0; i < numPackagePreferences; i++) {
final PackagePreferences r = mPackagePreferences.valueAt(i);
- // Package isn't associated with the current userId
- if (currentUserId != UserHandle.getUserId(r.uid)) {
- continue;
+ if (!currentUserIds.contains(UserHandle.getUserId(r.uid))) {
+ continue; // Package isn't associated with any profile of the current userId.
}
for (NotificationChannel channel : r.channels.values()) {
@@ -1842,13 +1839,6 @@ public class PreferencesHelper implements RankingConfig {
}
}
- private int getCurrentUser() {
- final long identity = Binder.clearCallingIdentity();
- int currentUserId = ActivityManager.getCurrentUser();
- Binder.restoreCallingIdentity(identity);
- return currentUserId;
- }
-
private boolean channelIsLiveLocked(PackagePreferences pkgPref, NotificationChannel channel) {
// Channel is in a group that's blocked
if (isGroupBlocked(pkgPref.pkg, pkgPref.uid, channel.getGroup())) {
diff --git a/services/core/java/com/android/server/notification/flags.aconfig b/services/core/java/com/android/server/notification/flags.aconfig
index c0bc6d14cd04..1908e4dff234 100644
--- a/services/core/java/com/android/server/notification/flags.aconfig
+++ b/services/core/java/com/android/server/notification/flags.aconfig
@@ -5,4 +5,11 @@ flag {
namespace: "systemui"
description: "This flag controls removing expired notification bitmaps"
bug: "290381858"
-} \ No newline at end of file
+}
+
+flag {
+ name: "polite_notifications"
+ namespace: "systemui"
+ description: "This flag controls the polite notification feature"
+ bug: "270456865"
+}
diff --git a/services/core/java/com/android/server/pm/DeletePackageHelper.java b/services/core/java/com/android/server/pm/DeletePackageHelper.java
index b5ec1366fec6..66a170397b84 100644
--- a/services/core/java/com/android/server/pm/DeletePackageHelper.java
+++ b/services/core/java/com/android/server/pm/DeletePackageHelper.java
@@ -459,8 +459,8 @@ final class DeletePackageHelper {
// Do not uninstall the APK if an app should be cached
boolean keepUninstalledPackage =
mPm.shouldKeepUninstalledPackageLPr(packageName);
- if (ps.isAnyInstalled(
- mUserManagerInternal.getUserIds()) || keepUninstalledPackage) {
+ if (ps.isInstalledOrHasDataOnAnyOtherUser(
+ mUserManagerInternal.getUserIds(), userId) || keepUninstalledPackage) {
// Other users still have this package installed, so all
// we need to do is clear this user's data and save that
// it is uninstalled.
@@ -555,6 +555,7 @@ final class DeletePackageHelper {
if ((flags & PackageManager.DELETE_KEEP_DATA) == 0) {
mAppDataHelper.destroyAppDataLIF(pkg, nextUserId,
FLAG_STORAGE_DE | FLAG_STORAGE_CE | FLAG_STORAGE_EXTERNAL);
+ ps.setCeDataInode(-1, nextUserId);
}
mAppDataHelper.clearKeystoreData(nextUserId, ps.getAppId());
preferredActivityHelper.clearPackagePreferredActivities(ps.getPackageName(),
@@ -615,7 +616,9 @@ final class DeletePackageHelper {
Slog.d(TAG, "Marking package:" + ps.getPackageName()
+ " uninstalled for user:" + nextUserId);
}
- ps.setUserState(nextUserId, 0, COMPONENT_ENABLED_STATE_DEFAULT,
+ ps.setUserState(nextUserId,
+ ps.getCeDataInode(nextUserId),
+ COMPONENT_ENABLED_STATE_DEFAULT,
false /*installed*/,
true /*stopped*/,
true /*notLaunched*/,
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 6c0aeecbf68b..f0bbd3557f85 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -2892,13 +2892,10 @@ public class PackageManagerService implements PackageSender, TestUtilityService
private void notifyPackageUseInternal(String packageName, int reason) {
long time = System.currentTimeMillis();
- synchronized (mLock) {
- final PackageSetting pkgSetting = mSettings.getPackageLPr(packageName);
- if (pkgSetting == null) {
- return;
- }
- pkgSetting.getPkgState().setLastPackageUsageTimeInMills(reason, time);
- }
+ this.commitPackageStateMutation(null, mutator -> {
+ final PackageStateWrite state = mutator.forPackage(packageName);
+ state.setLastPackageUsageTime(reason, time);
+ });
}
/*package*/ DexManager getDexManager() {
@@ -5182,9 +5179,6 @@ public class PackageManagerService implements PackageSender, TestUtilityService
public int getUserMinAspectRatio(@NonNull String packageName, int userId) {
final Computer snapshot = snapshotComputer();
final int callingUid = Binder.getCallingUid();
- snapshot.enforceCrossUserPermission(
- callingUid, userId, false /* requireFullPermission */,
- false /* checkShell */, "getUserMinAspectRatio");
final PackageStateInternal packageState = snapshot
.getPackageStateForInstalledAndFiltered(packageName, callingUid, userId);
return packageState == null ? USER_MIN_ASPECT_RATIO_UNSET
@@ -6681,9 +6675,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService
@Override
public void notifyPackageUse(String packageName, int reason) {
- synchronized (mLock) {
- PackageManagerService.this.notifyPackageUseInternal(packageName, reason);
- }
+ PackageManagerService.this.notifyPackageUseInternal(packageName, reason);
}
@Nullable
diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java
index 14693a67f2a5..7cac3e1b9842 100644
--- a/services/core/java/com/android/server/pm/PackageSetting.java
+++ b/services/core/java/com/android/server/pm/PackageSetting.java
@@ -809,9 +809,16 @@ public class PackageSetting extends SettingBase implements PackageStateInternal
return changed;
}
- boolean isAnyInstalled(int[] users) {
- for (int user: users) {
- if (readUserState(user).isInstalled()) {
+ boolean isInstalledOrHasDataOnAnyOtherUser(int[] allUsers, int currentUser) {
+ for (int user: allUsers) {
+ if (user == currentUser) {
+ continue;
+ }
+ final PackageUserStateInternal userState = readUserState(user);
+ if (userState.isInstalled()) {
+ return true;
+ }
+ if (userState.getCeDataInode() > 0) {
return true;
}
}
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index b6da4627c45a..307867c4e272 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -5186,9 +5186,12 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile
pw.print(" instant=");
pw.print(userState.isInstantApp());
pw.print(" virtual=");
- pw.println(userState.isVirtualPreload());
+ pw.print(userState.isVirtualPreload());
pw.print(" quarantined=");
pw.print(userState.isQuarantined());
+
+ // Dump install state with additional indentation on their own lines.
+ pw.println();
pw.print(" installReason=");
pw.println(userState.getInstallReason());
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index b01a89e672be..789719527c8d 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -72,6 +72,7 @@ import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.Preconditions;
+import com.android.internal.util.function.QuadFunction;
import com.android.internal.util.function.TriFunction;
import com.android.server.LocalServices;
import com.android.server.pm.UserManagerInternal;
@@ -93,7 +94,6 @@ import java.util.WeakHashMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
-import java.util.function.BiFunction;
/**
* Manages all permissions and handles permissions related tasks.
@@ -233,11 +233,11 @@ public class PermissionManagerService extends IPermissionManager.Stub {
}
if (checkPermissionDelegate == null) {
- return mPermissionManagerServiceImpl.checkPermission(
- packageName, permissionName, userId);
+ return mPermissionManagerServiceImpl.checkPermission(packageName, permissionName,
+ deviceId, userId);
}
- return checkPermissionDelegate.checkPermission(packageName, permissionName, userId,
- mPermissionManagerServiceImpl::checkPermission);
+ return checkPermissionDelegate.checkPermission(packageName, permissionName,
+ deviceId, userId, mPermissionManagerServiceImpl::checkPermission);
}
@Override
@@ -254,10 +254,10 @@ public class PermissionManagerService extends IPermissionManager.Stub {
}
if (checkPermissionDelegate == null) {
- return mPermissionManagerServiceImpl.checkUidPermission(uid, permissionName);
+ return mPermissionManagerServiceImpl.checkUidPermission(uid, permissionName, deviceId);
}
return checkPermissionDelegate.checkUidPermission(uid, permissionName,
- mPermissionManagerServiceImpl::checkUidPermission);
+ deviceId, mPermissionManagerServiceImpl::checkUidPermission);
}
@Override
@@ -511,14 +511,14 @@ public class PermissionManagerService extends IPermissionManager.Stub {
public int getPermissionFlags(String packageName, String permissionName, int deviceId,
int userId) {
return mPermissionManagerServiceImpl
- .getPermissionFlags(packageName, permissionName, userId);
+ .getPermissionFlags(packageName, permissionName, deviceId, userId);
}
@Override
public void updatePermissionFlags(String packageName, String permissionName, int flagMask,
int flagValues, boolean checkAdjustPolicyFlagPermission, int deviceId, int userId) {
mPermissionManagerServiceImpl.updatePermissionFlags(packageName, permissionName, flagMask,
- flagValues, checkAdjustPolicyFlagPermission, userId);
+ flagValues, checkAdjustPolicyFlagPermission, deviceId, userId);
}
@Override
@@ -560,14 +560,15 @@ public class PermissionManagerService extends IPermissionManager.Stub {
@Override
public void grantRuntimePermission(String packageName, String permissionName, int deviceId,
int userId) {
- mPermissionManagerServiceImpl.grantRuntimePermission(packageName, permissionName, userId);
+ mPermissionManagerServiceImpl.grantRuntimePermission(packageName, permissionName,
+ deviceId, userId);
}
@Override
public void revokeRuntimePermission(String packageName, String permissionName, int deviceId,
int userId, String reason) {
mPermissionManagerServiceImpl.revokeRuntimePermission(packageName, permissionName,
- userId, reason);
+ deviceId, userId, reason);
}
@Override
@@ -580,14 +581,14 @@ public class PermissionManagerService extends IPermissionManager.Stub {
public boolean shouldShowRequestPermissionRationale(String packageName, String permissionName,
int deviceId, int userId) {
return mPermissionManagerServiceImpl.shouldShowRequestPermissionRationale(packageName,
- permissionName, userId);
+ permissionName, deviceId, userId);
}
@Override
public boolean isPermissionRevokedByPolicy(String packageName, String permissionName,
int deviceId, int userId) {
- return mPermissionManagerServiceImpl
- .isPermissionRevokedByPolicy(packageName, permissionName, userId);
+ return mPermissionManagerServiceImpl.isPermissionRevokedByPolicy(packageName,
+ permissionName, deviceId, userId);
}
@Override
@@ -868,6 +869,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
*
* @param packageName the name of the package to be checked
* @param permissionName the name of the permission to be checked
+ * @param deviceId The device ID
* @param userId the user ID
* @param superImpl the original implementation that can be delegated to
* @return {@link android.content.pm.PackageManager#PERMISSION_GRANTED} if the package has
@@ -876,20 +878,21 @@ public class PermissionManagerService extends IPermissionManager.Stub {
* @see android.content.pm.PackageManager#checkPermission(String, String)
*/
int checkPermission(@NonNull String packageName, @NonNull String permissionName,
- @UserIdInt int userId,
- @NonNull TriFunction<String, String, Integer, Integer> superImpl);
+ int deviceId, @UserIdInt int userId,
+ @NonNull QuadFunction<String, String, Integer, Integer, Integer> superImpl);
/**
* Check whether the given UID has been granted the specified permission.
*
* @param uid the UID to be checked
* @param permissionName the name of the permission to be checked
+ * @param deviceId The device ID
* @param superImpl the original implementation that can be delegated to
* @return {@link android.content.pm.PackageManager#PERMISSION_GRANTED} if the package has
* the permission, or {@link android.content.pm.PackageManager#PERMISSION_DENIED} otherwise
*/
- int checkUidPermission(int uid, @NonNull String permissionName,
- BiFunction<Integer, String, Integer> superImpl);
+ int checkUidPermission(int uid, @NonNull String permissionName, int deviceId,
+ TriFunction<Integer, String, Integer, Integer> superImpl);
/**
* @return list of delegated permissions
@@ -918,31 +921,32 @@ public class PermissionManagerService extends IPermissionManager.Stub {
@Override
public int checkPermission(@NonNull String packageName, @NonNull String permissionName,
- int userId, @NonNull TriFunction<String, String, Integer, Integer> superImpl) {
+ int deviceId, int userId,
+ @NonNull QuadFunction<String, String, Integer, Integer, Integer> superImpl) {
if (mDelegatedPackageName.equals(packageName)
&& isDelegatedPermission(permissionName)) {
final long identity = Binder.clearCallingIdentity();
try {
- return superImpl.apply("com.android.shell", permissionName, userId);
+ return superImpl.apply("com.android.shell", permissionName, deviceId, userId);
} finally {
Binder.restoreCallingIdentity(identity);
}
}
- return superImpl.apply(packageName, permissionName, userId);
+ return superImpl.apply(packageName, permissionName, deviceId, userId);
}
@Override
- public int checkUidPermission(int uid, @NonNull String permissionName,
- @NonNull BiFunction<Integer, String, Integer> superImpl) {
+ public int checkUidPermission(int uid, @NonNull String permissionName, int deviceId,
+ @NonNull TriFunction<Integer, String, Integer, Integer> superImpl) {
if (uid == mDelegatedUid && isDelegatedPermission(permissionName)) {
final long identity = Binder.clearCallingIdentity();
try {
- return superImpl.apply(Process.SHELL_UID, permissionName);
+ return superImpl.apply(Process.SHELL_UID, permissionName, deviceId);
} finally {
Binder.restoreCallingIdentity(identity);
}
}
- return superImpl.apply(uid, permissionName);
+ return superImpl.apply(uid, permissionName, deviceId);
}
@Override
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
index 4353c5787d4b..6764e087ff04 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
@@ -681,7 +681,7 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt
}
@Override
- public int getPermissionFlags(String packageName, String permName, int userId) {
+ public int getPermissionFlags(String packageName, String permName, int deviceId, int userId) {
final int callingUid = Binder.getCallingUid();
return getPermissionFlagsInternal(packageName, permName, callingUid, userId);
}
@@ -724,7 +724,7 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt
@Override
public void updatePermissionFlags(String packageName, String permName, int flagMask,
- int flagValues, boolean checkAdjustPolicyFlagPermission, int userId) {
+ int flagValues, boolean checkAdjustPolicyFlagPermission, int deviceId, int userId) {
final int callingUid = Binder.getCallingUid();
boolean overridePolicy = false;
@@ -908,8 +908,12 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt
}
}
+ private int checkPermission(String pkgName, String permName, int userId) {
+ return checkPermission(pkgName, permName, Context.DEVICE_ID_DEFAULT, userId);
+ }
+
@Override
- public int checkPermission(String pkgName, String permName, int userId) {
+ public int checkPermission(String pkgName, String permName, int deviceId, int userId) {
if (!mUserManagerInt.exists(userId)) {
return PackageManager.PERMISSION_DENIED;
}
@@ -975,8 +979,12 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt
return true;
}
+ private int checkUidPermission(int uid, String permName) {
+ return checkUidPermission(uid, permName, Context.DEVICE_ID_DEFAULT);
+ }
+
@Override
- public int checkUidPermission(int uid, String permName) {
+ public int checkUidPermission(int uid, String permName, int deviceId) {
final int userId = UserHandle.getUserId(uid);
if (!mUserManagerInt.exists(userId)) {
return PackageManager.PERMISSION_DENIED;
@@ -1295,7 +1303,8 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt
}
@Override
- public void grantRuntimePermission(String packageName, String permName, final int userId) {
+ public void grantRuntimePermission(String packageName, String permName, int deviceId,
+ int userId) {
final int callingUid = Binder.getCallingUid();
final boolean overridePolicy =
checkUidPermission(callingUid, ADJUST_RUNTIME_PERMISSIONS_POLICY)
@@ -1468,11 +1477,11 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt
}
@Override
- public void revokeRuntimePermission(String packageName, String permName, int userId,
- String reason) {
+ public void revokeRuntimePermission(String packageName, String permName, int deviceId,
+ int userId, String reason) {
final int callingUid = Binder.getCallingUid();
final boolean overridePolicy =
- checkUidPermission(callingUid, ADJUST_RUNTIME_PERMISSIONS_POLICY)
+ checkUidPermission(callingUid, ADJUST_RUNTIME_PERMISSIONS_POLICY, deviceId)
== PackageManager.PERMISSION_GRANTED;
revokeRuntimePermissionInternal(packageName, permName, overridePolicy, callingUid, userId,
@@ -1859,7 +1868,7 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt
@Override
public boolean shouldShowRequestPermissionRationale(String packageName, String permName,
- @UserIdInt int userId) {
+ int deviceId, @UserIdInt int userId) {
final int callingUid = Binder.getCallingUid();
if (UserHandle.getCallingUserId() != userId) {
mContext.enforceCallingPermission(
@@ -1922,7 +1931,8 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt
}
@Override
- public boolean isPermissionRevokedByPolicy(String packageName, String permName, int userId) {
+ public boolean isPermissionRevokedByPolicy(String packageName, String permName, int deviceId,
+ int userId) {
if (UserHandle.getCallingUserId() != userId) {
mContext.enforceCallingPermission(
android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
@@ -2059,8 +2069,8 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt
continue;
}
boolean isSystemOrPolicyFixed = (getPermissionFlags(newPackage.getPackageName(),
- permInfo.name, userId) & (FLAG_PERMISSION_SYSTEM_FIXED
- | FLAG_PERMISSION_POLICY_FIXED)) != 0;
+ permInfo.name, Context.DEVICE_ID_DEFAULT, userId) & (
+ FLAG_PERMISSION_SYSTEM_FIXED | FLAG_PERMISSION_POLICY_FIXED)) != 0;
if (isSystemOrPolicyFixed) {
continue;
}
@@ -2226,7 +2236,8 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt
for (final int userId : userIds) {
final int permissionState = checkPermission(packageName, permName,
userId);
- final int flags = getPermissionFlags(packageName, permName, userId);
+ final int flags = getPermissionFlags(packageName, permName,
+ Context.DEVICE_ID_DEFAULT, userId);
final int flagMask = FLAG_PERMISSION_SYSTEM_FIXED
| FLAG_PERMISSION_POLICY_FIXED
| FLAG_PERMISSION_GRANTED_BY_DEFAULT
@@ -5122,8 +5133,7 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt
@NonNull
@Override
- public Set<String> getGrantedPermissions(@NonNull String packageName,
- @UserIdInt int userId) {
+ public Set<String> getGrantedPermissions(@NonNull String packageName, @UserIdInt int userId) {
Objects.requireNonNull(packageName, "packageName");
Preconditions.checkArgumentNonNegative(userId, "userId");
return getGrantedPermissionsInternal(packageName, userId);
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java
index 128f847715ab..2d824aa1ba13 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java
@@ -25,7 +25,6 @@ import android.content.pm.PermissionGroupInfo;
import android.content.pm.PermissionInfo;
import android.content.pm.permission.SplitPermissionInfoParcelable;
import android.permission.IOnPermissionsChangeListener;
-import android.permission.PermissionManager;
import android.permission.PermissionManagerInternal;
import com.android.server.pm.pkg.AndroidPackage;
@@ -137,14 +136,16 @@ public interface PermissionManagerServiceInterface extends PermissionManagerInte
void removePermission(String permName);
/**
- * Gets the state flags associated with a permission.
+ * Gets the permission state flags associated with a permission.
*
* @param packageName the package name for which to get the flags
* @param permName the permission for which to get the flags
+ * @param deviceId The device for which to get the flags
* @param userId the user for which to get permission flags
* @return the permission flags
*/
- int getPermissionFlags(String packageName, String permName, int userId);
+ int getPermissionFlags(String packageName, String permName, int deviceId,
+ @UserIdInt int userId);
/**
* Updates the flags associated with a permission by replacing the flags in the specified mask
@@ -154,10 +155,11 @@ public interface PermissionManagerServiceInterface extends PermissionManagerInte
* @param permName The permission for which to update the flags
* @param flagMask The flags which to replace
* @param flagValues The flags with which to replace
+ * @param deviceId The device for which to update the permission flags
* @param userId The user for which to update the permission flags
*/
- void updatePermissionFlags(String packageName, String permName, int flagMask,
- int flagValues, boolean checkAdjustPolicyFlagPermission, int userId);
+ void updatePermissionFlags(String packageName, String permName, int flagMask, int flagValues,
+ boolean checkAdjustPolicyFlagPermission, int deviceId, @UserIdInt int userId);
/**
* Update the permission flags for all packages and runtime permissions of a user in order
@@ -291,11 +293,13 @@ public interface PermissionManagerServiceInterface extends PermissionManagerInte
*
* @param packageName the package to which to grant the permission
* @param permName the permission name to grant
+ * @param deviceId the device for which to grant the permission
* @param userId the user for which to grant the permission
*
- * @see #revokeRuntimePermission(String, String, android.os.UserHandle, String)
+ * @see #revokeRuntimePermission(String, String, int, int, String)
*/
- void grantRuntimePermission(String packageName, String permName, int userId);
+ void grantRuntimePermission(String packageName, String permName, int deviceId,
+ @UserIdInt int userId);
/**
* Revoke a runtime permission that was previously granted by
@@ -310,13 +314,14 @@ public interface PermissionManagerServiceInterface extends PermissionManagerInte
*
* @param packageName the package from which to revoke the permission
* @param permName the permission name to revoke
+ * @param deviceId the device for which to revoke the permission
* @param userId the user for which to revoke the permission
* @param reason the reason for the revoke, or {@code null} for unspecified
*
- * @see #grantRuntimePermission(String, String, android.os.UserHandle)
+ * @see #grantRuntimePermission(String, String, int, int)
*/
- void revokeRuntimePermission(String packageName, String permName, int userId,
- String reason);
+ void revokeRuntimePermission(String packageName, String permName, int deviceId,
+ @UserIdInt int userId, String reason);
/**
* Revoke the POST_NOTIFICATIONS permission, without killing the app. This method must ONLY BE
@@ -333,24 +338,29 @@ public interface PermissionManagerServiceInterface extends PermissionManagerInte
* does not clearly communicate to the user what would be the benefit from grating this
* permission.
*
+ * @param packageName the package name
* @param permName a permission your app wants to request
+ * @param deviceId the device for which to check the permission
+ * @param userId the user for which to check the permission
* @return whether you can show permission rationale UI
*/
boolean shouldShowRequestPermissionRationale(String packageName, String permName,
- @UserIdInt int userId);
+ int deviceId, @UserIdInt int userId);
/**
- * Checks whether a particular permissions has been revoked for a package by policy. Typically
+ * Checks whether a particular permission has been revoked for a package by policy. Typically,
* the device owner or the profile owner may apply such a policy. The user cannot grant policy
* revoked permissions, hence the only way for an app to get such a permission is by a policy
* change.
*
* @param packageName the name of the package you are checking against
* @param permName the name of the permission you are checking for
- *
+ * @param deviceId the device for which you are checking the permission
+ * @param userId the device for which you are checking the permission
* @return whether the permission is restricted by policy
*/
- boolean isPermissionRevokedByPolicy(String packageName, String permName, int userId);
+ boolean isPermissionRevokedByPolicy(String packageName, String permName, int deviceId,
+ @UserIdInt int userId);
/**
* Get set of permissions that have been split into more granular or dependent permissions.
@@ -373,14 +383,25 @@ public interface PermissionManagerServiceInterface extends PermissionManagerInte
List<SplitPermissionInfoParcelable> getSplitPermissions();
/**
- * TODO:theianchen add doc describing this is the old checkPermissionImpl
+ * Check whether a permission is granted or not to a package.
+ *
+ * @param pkgName package name
+ * @param permName permission name
+ * @param deviceId device ID
+ * @param userId user ID
+ * @return permission result {@link PackageManager.PermissionResult}
*/
- int checkPermission(String pkgName, String permName, int userId);
+ int checkPermission(String pkgName, String permName, int deviceId, @UserIdInt int userId);
/**
- * TODO:theianchen add doc describing this is the old checkUidPermissionImpl
+ * Check whether a permission is granted or not to an UID.
+ *
+ * @param uid UID
+ * @param permName permission name
+ * @param deviceId device ID
+ * @return permission result {@link PackageManager.PermissionResult}
*/
- int checkUidPermission(int uid, String permName);
+ int checkUidPermission(int uid, String permName, int deviceId);
/**
* Get all the package names requesting app op permissions.
@@ -400,15 +421,11 @@ public interface PermissionManagerServiceInterface extends PermissionManagerInte
@UserIdInt int userId);
/**
- * Reset the runtime permission state changes for a package.
+ * Reset the runtime permission state changes for a package for all devices.
*
* TODO(zhanghai): Turn this into package change callback?
- *
- * @param pkg the package
- * @param userId the user ID
*/
- void resetRuntimePermissions(@NonNull AndroidPackage pkg,
- @UserIdInt int userId);
+ void resetRuntimePermissions(@NonNull AndroidPackage pkg, @UserIdInt int userId);
/**
* Reset the runtime permission state changes for all packages in a user.
@@ -449,8 +466,8 @@ public interface PermissionManagerServiceInterface extends PermissionManagerInte
/**
* Get all the permissions granted to a package.
*
- * @param packageName the name of the package
- * @param userId the user ID
+ * @param packageName package name
+ * @param userId user ID
* @return the names of the granted permissions
*/
@NonNull
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceLoggingDecorator.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceLoggingDecorator.java
index 7f98e2163178..dacb8c6890a0 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceLoggingDecorator.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceLoggingDecorator.java
@@ -120,21 +120,21 @@ public class PermissionManagerServiceLoggingDecorator implements PermissionManag
}
@Override
- public int getPermissionFlags(String packageName, String permName, int userId) {
+ public int getPermissionFlags(String packageName, String permName, int deviceId, int userId) {
Log.i(LOG_TAG, "getPermissionFlags(packageName = " + packageName + ", permName = "
- + permName + ", userId = " + userId + ")");
- return mService.getPermissionFlags(packageName, permName, userId);
+ + permName + ", deviceId = " + deviceId + ", userId = " + userId + ")");
+ return mService.getPermissionFlags(packageName, permName, deviceId, userId);
}
@Override
public void updatePermissionFlags(String packageName, String permName, int flagMask,
- int flagValues, boolean checkAdjustPolicyFlagPermission, int userId) {
+ int flagValues, boolean checkAdjustPolicyFlagPermission, int deviceId, int userId) {
Log.i(LOG_TAG, "updatePermissionFlags(packageName = " + packageName + ", permName = "
+ permName + ", flagMask = " + flagMask + ", flagValues = " + flagValues
+ ", checkAdjustPolicyFlagPermission = " + checkAdjustPolicyFlagPermission
- + ", userId = " + userId + ")");
+ + ", deviceId = " + deviceId + ", userId = " + userId + ")");
mService.updatePermissionFlags(packageName, permName, flagMask, flagValues,
- checkAdjustPolicyFlagPermission, userId);
+ checkAdjustPolicyFlagPermission, deviceId, userId);
}
@Override
@@ -182,18 +182,20 @@ public class PermissionManagerServiceLoggingDecorator implements PermissionManag
}
@Override
- public void grantRuntimePermission(String packageName, String permName, int userId) {
+ public void grantRuntimePermission(String packageName, String permName, int deviceId,
+ int userId) {
Log.i(LOG_TAG, "grantRuntimePermission(packageName = " + packageName + ", permName = "
- + permName + ", userId = " + userId + ")");
- mService.grantRuntimePermission(packageName, permName, userId);
+ + permName + ", deviceId = " + deviceId + ", userId = " + userId + ")");
+ mService.grantRuntimePermission(packageName, permName, deviceId, userId);
}
@Override
- public void revokeRuntimePermission(String packageName, String permName, int userId,
- String reason) {
+ public void revokeRuntimePermission(String packageName, String permName, int deviceId,
+ int userId, String reason) {
Log.i(LOG_TAG, "revokeRuntimePermission(packageName = " + packageName + ", permName = "
- + permName + ", userId = " + userId + ", reason = " + reason + ")");
- mService.revokeRuntimePermission(packageName, permName, userId, reason);
+ + permName + ", deviceId = " + deviceId + ", userId = " + userId
+ + ", reason = " + reason + ")");
+ mService.revokeRuntimePermission(packageName, permName, deviceId, userId, reason);
}
@Override
@@ -205,17 +207,20 @@ public class PermissionManagerServiceLoggingDecorator implements PermissionManag
@Override
public boolean shouldShowRequestPermissionRationale(String packageName, String permName,
- int userId) {
+ int deviceId, int userId) {
Log.i(LOG_TAG, "shouldShowRequestPermissionRationale(packageName = " + packageName
- + ", permName = " + permName + ", userId = " + userId + ")");
- return mService.shouldShowRequestPermissionRationale(packageName, permName, userId);
+ + ", permName = " + permName + ", deviceId = " + deviceId
+ + ", userId = " + userId + ")");
+ return mService.shouldShowRequestPermissionRationale(packageName, permName, deviceId,
+ userId);
}
@Override
- public boolean isPermissionRevokedByPolicy(String packageName, String permName, int userId) {
+ public boolean isPermissionRevokedByPolicy(String packageName, String permName, int deviceId,
+ int userId) {
Log.i(LOG_TAG, "isPermissionRevokedByPolicy(packageName = " + packageName + ", permName = "
- + permName + ", userId = " + userId + ")");
- return mService.isPermissionRevokedByPolicy(packageName, permName, userId);
+ + permName + ", deviceId = " + deviceId + ", userId = " + userId + ")");
+ return mService.isPermissionRevokedByPolicy(packageName, permName, deviceId, userId);
}
@Override
@@ -225,16 +230,17 @@ public class PermissionManagerServiceLoggingDecorator implements PermissionManag
}
@Override
- public int checkPermission(String pkgName, String permName, int userId) {
+ public int checkPermission(String pkgName, String permName, int deviceId, int userId) {
Log.i(LOG_TAG, "checkPermission(pkgName = " + pkgName + ", permName = " + permName
- + ", userId = " + userId + ")");
- return mService.checkPermission(pkgName, permName, userId);
+ + ", deviceId = " + deviceId + ", userId = " + userId + ")");
+ return mService.checkPermission(pkgName, permName, deviceId, userId);
}
@Override
- public int checkUidPermission(int uid, String permName) {
- Log.i(LOG_TAG, "checkUidPermission(uid = " + uid + ", permName = " + permName + ")");
- return mService.checkUidPermission(uid, permName);
+ public int checkUidPermission(int uid, String permName, int deviceId) {
+ Log.i(LOG_TAG, "checkUidPermission(uid = " + uid + ", permName = " + permName
+ + ", deviceId = " + deviceId + ")");
+ return mService.checkUidPermission(uid, permName, deviceId);
}
@Override
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceTestingShim.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceTestingShim.java
index d4c6d42deeaa..35d165b9b54a 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceTestingShim.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceTestingShim.java
@@ -153,9 +153,10 @@ public class PermissionManagerServiceTestingShim implements PermissionManagerSer
}
@Override
- public int getPermissionFlags(String packageName, String permName, int userId) {
- int oldVal = mOldImplementation.getPermissionFlags(packageName, permName, userId);
- int newVal = mNewImplementation.getPermissionFlags(packageName, permName, userId);
+ public int getPermissionFlags(String packageName, String permName, int deviceId,
+ @UserIdInt int userId) {
+ int oldVal = mOldImplementation.getPermissionFlags(packageName, permName, deviceId, userId);
+ int newVal = mNewImplementation.getPermissionFlags(packageName, permName, deviceId, userId);
if (!Objects.equals(oldVal, newVal)) {
signalImplDifference("getPermissionFlags");
@@ -165,11 +166,12 @@ public class PermissionManagerServiceTestingShim implements PermissionManagerSer
@Override
public void updatePermissionFlags(String packageName, String permName, int flagMask,
- int flagValues, boolean checkAdjustPolicyFlagPermission, int userId) {
+ int flagValues, boolean checkAdjustPolicyFlagPermission, int deviceId,
+ @UserIdInt int userId) {
mOldImplementation.updatePermissionFlags(packageName, permName, flagMask, flagValues,
- checkAdjustPolicyFlagPermission, userId);
+ checkAdjustPolicyFlagPermission, deviceId, userId);
mNewImplementation.updatePermissionFlags(packageName, permName, flagMask, flagValues,
- checkAdjustPolicyFlagPermission, userId);
+ checkAdjustPolicyFlagPermission, deviceId, userId);
}
@Override
@@ -234,16 +236,17 @@ public class PermissionManagerServiceTestingShim implements PermissionManagerSer
}
@Override
- public void grantRuntimePermission(String packageName, String permName, int userId) {
- mOldImplementation.grantRuntimePermission(packageName, permName, userId);
- mNewImplementation.grantRuntimePermission(packageName, permName, userId);
+ public void grantRuntimePermission(String packageName, String permName, int deviceId,
+ @UserIdInt int userId) {
+ mOldImplementation.grantRuntimePermission(packageName, permName, deviceId, userId);
+ mNewImplementation.grantRuntimePermission(packageName, permName, deviceId, userId);
}
@Override
- public void revokeRuntimePermission(String packageName, String permName, int userId,
- String reason) {
- mOldImplementation.grantRuntimePermission(packageName, permName, userId);
- mNewImplementation.grantRuntimePermission(packageName, permName, userId);
+ public void revokeRuntimePermission(String packageName, String permName, int deviceId,
+ @UserIdInt int userId, String reason) {
+ mOldImplementation.revokeRuntimePermission(packageName, permName, deviceId, userId, reason);
+ mNewImplementation.revokeRuntimePermission(packageName, permName, deviceId, userId, reason);
}
@Override
@@ -255,11 +258,11 @@ public class PermissionManagerServiceTestingShim implements PermissionManagerSer
@Override
public boolean shouldShowRequestPermissionRationale(String packageName, String permName,
- int userId) {
- boolean oldVal = mOldImplementation
- .shouldShowRequestPermissionRationale(packageName, permName, userId);
- boolean newVal = mNewImplementation
- .shouldShowRequestPermissionRationale(packageName, permName, userId);
+ int deviceId, @UserIdInt int userId) {
+ boolean oldVal = mOldImplementation.shouldShowRequestPermissionRationale(packageName,
+ permName, deviceId, userId);
+ boolean newVal = mNewImplementation.shouldShowRequestPermissionRationale(packageName,
+ permName, deviceId, userId);
if (!Objects.equals(oldVal, newVal)) {
signalImplDifference("shouldShowRequestPermissionRationale");
@@ -268,11 +271,12 @@ public class PermissionManagerServiceTestingShim implements PermissionManagerSer
}
@Override
- public boolean isPermissionRevokedByPolicy(String packageName, String permName, int userId) {
- boolean oldVal = mOldImplementation
- .isPermissionRevokedByPolicy(packageName, permName, userId);
+ public boolean isPermissionRevokedByPolicy(String packageName, String permName, int deviceId,
+ @UserIdInt int userId) {
+ boolean oldVal = mOldImplementation.isPermissionRevokedByPolicy(packageName, permName,
+ deviceId, userId);
boolean newVal = mNewImplementation.isPermissionRevokedByPolicy(packageName, permName,
- userId);
+ deviceId, userId);
if (!Objects.equals(oldVal, newVal)) {
signalImplDifference("isPermissionRevokedByPolicy");
@@ -292,9 +296,9 @@ public class PermissionManagerServiceTestingShim implements PermissionManagerSer
}
@Override
- public int checkPermission(String pkgName, String permName, int userId) {
- int oldVal = mOldImplementation.checkPermission(pkgName, permName, userId);
- int newVal = mNewImplementation.checkPermission(pkgName, permName, userId);
+ public int checkPermission(String pkgName, String permName, int deviceId, int userId) {
+ int oldVal = mOldImplementation.checkPermission(pkgName, permName, deviceId, userId);
+ int newVal = mNewImplementation.checkPermission(pkgName, permName, deviceId, userId);
if (!Objects.equals(oldVal, newVal)) {
signalImplDifference("checkPermission");
@@ -303,9 +307,9 @@ public class PermissionManagerServiceTestingShim implements PermissionManagerSer
}
@Override
- public int checkUidPermission(int uid, String permName) {
- int oldVal = mOldImplementation.checkUidPermission(uid, permName);
- int newVal = mNewImplementation.checkUidPermission(uid, permName);
+ public int checkUidPermission(int uid, String permName, int deviceId) {
+ int oldVal = mOldImplementation.checkUidPermission(uid, permName, deviceId);
+ int newVal = mNewImplementation.checkUidPermission(uid, permName, deviceId);
if (!Objects.equals(oldVal, newVal)) {
signalImplDifference("checkUidPermission");
@@ -372,7 +376,7 @@ public class PermissionManagerServiceTestingShim implements PermissionManagerSer
@NonNull
@Override
- public Set<String> getGrantedPermissions(@NonNull String packageName, int userId) {
+ public Set<String> getGrantedPermissions(@NonNull String packageName, @UserIdInt int userId) {
Set<String> oldVal = mOldImplementation.getGrantedPermissions(packageName, userId);
Set<String> newVal = mNewImplementation.getGrantedPermissions(packageName, userId);
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceTracingDecorator.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceTracingDecorator.java
index 4e72fae99c9c..cbeede0f425c 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceTracingDecorator.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceTracingDecorator.java
@@ -158,10 +158,10 @@ public class PermissionManagerServiceTracingDecorator implements PermissionManag
}
@Override
- public int getPermissionFlags(String packageName, String permName, int userId) {
+ public int getPermissionFlags(String packageName, String permName, int deviceId, int userId) {
Trace.traceBegin(TRACE_TAG, "TaggedTracingPermissionManagerServiceImpl#getPermissionFlags");
try {
- return mService.getPermissionFlags(packageName, permName, userId);
+ return mService.getPermissionFlags(packageName, permName, deviceId, userId);
} finally {
Trace.traceEnd(TRACE_TAG);
}
@@ -169,12 +169,12 @@ public class PermissionManagerServiceTracingDecorator implements PermissionManag
@Override
public void updatePermissionFlags(String packageName, String permName, int flagMask,
- int flagValues, boolean checkAdjustPolicyFlagPermission, int userId) {
+ int flagValues, boolean checkAdjustPolicyFlagPermission, int deviceId, int userId) {
Trace.traceBegin(TRACE_TAG,
"TaggedTracingPermissionManagerServiceImpl#updatePermissionFlags");
try {
mService.updatePermissionFlags(packageName, permName, flagMask, flagValues,
- checkAdjustPolicyFlagPermission, userId);
+ checkAdjustPolicyFlagPermission, deviceId, userId);
} finally {
Trace.traceEnd(TRACE_TAG);
}
@@ -253,23 +253,24 @@ public class PermissionManagerServiceTracingDecorator implements PermissionManag
}
@Override
- public void grantRuntimePermission(String packageName, String permName, int userId) {
+ public void grantRuntimePermission(String packageName, String permName, int deviceId,
+ int userId) {
Trace.traceBegin(TRACE_TAG,
"TaggedTracingPermissionManagerServiceImpl#grantRuntimePermission");
try {
- mService.grantRuntimePermission(packageName, permName, userId);
+ mService.grantRuntimePermission(packageName, permName, deviceId, userId);
} finally {
Trace.traceEnd(TRACE_TAG);
}
}
@Override
- public void revokeRuntimePermission(String packageName, String permName, int userId,
- String reason) {
+ public void revokeRuntimePermission(String packageName, String permName, int deviceId,
+ int userId, String reason) {
Trace.traceBegin(TRACE_TAG,
"TaggedTracingPermissionManagerServiceImpl#revokeRuntimePermission");
try {
- mService.revokeRuntimePermission(packageName, permName, userId, reason);
+ mService.revokeRuntimePermission(packageName, permName, deviceId, userId, reason);
} finally {
Trace.traceEnd(TRACE_TAG);
}
@@ -288,22 +289,24 @@ public class PermissionManagerServiceTracingDecorator implements PermissionManag
@Override
public boolean shouldShowRequestPermissionRationale(String packageName, String permName,
- int userId) {
+ int deviceId, int userId) {
Trace.traceBegin(TRACE_TAG,
"TaggedTracingPermissionManagerServiceImpl#shouldShowRequestPermissionRationale");
try {
- return mService.shouldShowRequestPermissionRationale(packageName, permName, userId);
+ return mService.shouldShowRequestPermissionRationale(
+ packageName, permName, deviceId, userId);
} finally {
Trace.traceEnd(TRACE_TAG);
}
}
@Override
- public boolean isPermissionRevokedByPolicy(String packageName, String permName, int userId) {
+ public boolean isPermissionRevokedByPolicy(String packageName, String permName, int deviceId,
+ int userId) {
Trace.traceBegin(TRACE_TAG,
"TaggedTracingPermissionManagerServiceImpl#isPermissionRevokedByPolicy");
try {
- return mService.isPermissionRevokedByPolicy(packageName, permName, userId);
+ return mService.isPermissionRevokedByPolicy(packageName, permName, deviceId, userId);
} finally {
Trace.traceEnd(TRACE_TAG);
}
@@ -321,20 +324,20 @@ public class PermissionManagerServiceTracingDecorator implements PermissionManag
}
@Override
- public int checkPermission(String pkgName, String permName, int userId) {
+ public int checkPermission(String pkgName, String permName, int deviceId, int userId) {
Trace.traceBegin(TRACE_TAG, "TaggedTracingPermissionManagerServiceImpl#checkPermission");
try {
- return mService.checkPermission(pkgName, permName, userId);
+ return mService.checkPermission(pkgName, permName, deviceId, userId);
} finally {
Trace.traceEnd(TRACE_TAG);
}
}
@Override
- public int checkUidPermission(int uid, String permName) {
+ public int checkUidPermission(int uid, String permName, int deviceId) {
Trace.traceBegin(TRACE_TAG, "TaggedTracingPermissionManagerServiceImpl#checkUidPermission");
try {
- return mService.checkUidPermission(uid, permName);
+ return mService.checkUidPermission(uid, permName, deviceId);
} finally {
Trace.traceEnd(TRACE_TAG);
}
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 2f68021bf356..b3aa09b8f17b 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -29,6 +29,7 @@ import static android.content.pm.PackageManager.FEATURE_WATCH;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.os.Build.VERSION_CODES.M;
import static android.os.Build.VERSION_CODES.O;
+import static android.os.IInputConstants.INVALID_INPUT_DEVICE_ID;
import static android.provider.Settings.Secure.VOLUME_HUSH_OFF;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
@@ -338,6 +339,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
// The config value can be overridden using Settings.Global.STEM_PRIMARY_BUTTON_SHORT_PRESS
static final int SHORT_PRESS_PRIMARY_NOTHING = 0;
static final int SHORT_PRESS_PRIMARY_LAUNCH_ALL_APPS = 1;
+ static final int SHORT_PRESS_PRIMARY_LAUNCH_TARGET_ACTIVITY = 2;
// Must match: config_longPressOnStemPrimaryBehavior in config.xml
// The config value can be overridden using Settings.Global.STEM_PRIMARY_BUTTON_LONG_PRESS
@@ -609,6 +611,9 @@ public class PhoneWindowManager implements WindowManagerPolicy {
// What we do when the user double-taps on home
private int mDoubleTapOnHomeBehavior;
+ // Must match config_primaryShortPressTargetActivity in config.xml
+ ComponentName mPrimaryShortPressTargetActivity;
+
// Whether to lock the device after the next dreaming transition has finished.
private boolean mLockAfterDreamingTransitionFinished;
@@ -1374,7 +1379,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
mPowerKeyHandled = true;
performHapticFeedback(HapticFeedbackConstants.ASSISTANT_BUTTON, false,
"Power - Long Press - Go To Assistant");
- final int powerKeyDeviceId = Integer.MIN_VALUE;
+ final int powerKeyDeviceId = INVALID_INPUT_DEVICE_ID;
launchAssistAction(null, powerKeyDeviceId, eventTime,
AssistUtils.INVOCATION_TYPE_POWER_BUTTON_LONG_PRESS);
break;
@@ -1461,23 +1466,59 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
private void stemPrimarySinglePressAction(int behavior) {
+ if (DEBUG_INPUT) {
+ Slog.d(TAG, "stemPrimarySinglePressAction: behavior=" + behavior);
+ }
+ if (behavior == SHORT_PRESS_PRIMARY_NOTHING) return;
+
+ final boolean keyguardActive = mKeyguardDelegate != null && mKeyguardDelegate.isShowing();
+ if (keyguardActive) {
+ // If keyguarded then notify the keyguard.
+ mKeyguardDelegate.onSystemKeyPressed(KeyEvent.KEYCODE_STEM_PRIMARY);
+ return;
+ }
switch (behavior) {
- case SHORT_PRESS_PRIMARY_NOTHING:
- break;
case SHORT_PRESS_PRIMARY_LAUNCH_ALL_APPS:
if (DEBUG_INPUT) {
Slog.d(TAG, "Executing stem primary short press action behavior.");
}
- final boolean keyguardActive =
- mKeyguardDelegate != null && mKeyguardDelegate.isShowing();
- if (!keyguardActive) {
- Intent intent = new Intent(Intent.ACTION_ALL_APPS);
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
- | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
- startActivityAsUser(intent, UserHandle.CURRENT_OR_SELF);
+ Intent allAppsIntent = new Intent(Intent.ACTION_ALL_APPS);
+ allAppsIntent.addFlags(
+ Intent.FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+ startActivityAsUser(allAppsIntent, UserHandle.CURRENT_OR_SELF);
+ break;
+ case SHORT_PRESS_PRIMARY_LAUNCH_TARGET_ACTIVITY:
+ if (DEBUG_INPUT) {
+ Slog.d(
+ TAG,
+ "Executing stem primary short press action behavior for launching "
+ + "target activity.");
+ }
+ if (mPrimaryShortPressTargetActivity != null) {
+ Intent targetActivityIntent = new Intent();
+ targetActivityIntent.setComponent(mPrimaryShortPressTargetActivity);
+ ResolveInfo resolveInfo =
+ mContext.getPackageManager()
+ .resolveActivity(targetActivityIntent, /* flags= */ 0);
+ if (resolveInfo != null) {
+ targetActivityIntent.addFlags(
+ Intent.FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
+ | Intent.FLAG_ACTIVITY_TASK_ON_HOME);
+ startActivityAsUser(targetActivityIntent, UserHandle.CURRENT_OR_SELF);
+ } else {
+ Slog.wtf(
+ TAG,
+ "Could not resolve activity with : "
+ + mPrimaryShortPressTargetActivity.flattenToString()
+ + " name.");
+ }
} else {
- // If keyguarded then notify the keyguard.
- mKeyguardDelegate.onSystemKeyPressed(KeyEvent.KEYCODE_STEM_PRIMARY);
+ Slog.wtf(
+ TAG,
+ "mPrimaryShortPressTargetActivity must not be null and correctly"
+ + " specified");
}
break;
}
@@ -1527,7 +1568,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
}
- private void stemPrimaryLongPress() {
+ private void stemPrimaryLongPress(long eventTime) {
if (DEBUG_INPUT) {
Slog.d(TAG, "Executing stem primary long press action behavior.");
}
@@ -1536,7 +1577,12 @@ public class PhoneWindowManager implements WindowManagerPolicy {
case LONG_PRESS_PRIMARY_NOTHING:
break;
case LONG_PRESS_PRIMARY_LAUNCH_VOICE_ASSISTANT:
- launchVoiceAssist(/* allowDuringSetup= */false);
+ final int stemPrimaryKeyDeviceId = INVALID_INPUT_DEVICE_ID;
+ launchAssistAction(
+ null,
+ stemPrimaryKeyDeviceId,
+ eventTime,
+ AssistUtils.INVOCATION_TYPE_UNKNOWN);
break;
}
}
@@ -2159,6 +2205,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
mAppOpsManager = mContext.getSystemService(AppOpsManager.class);
mSensorPrivacyManager = mContext.getSystemService(SensorPrivacyManager.class);
+ mSearchManager = mContext.getSystemService(SearchManager.class);
mDisplayManager = mContext.getSystemService(DisplayManager.class);
mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
mUserManagerInternal = LocalServices.getService(UserManagerInternal.class);
@@ -2295,6 +2342,9 @@ public class PhoneWindowManager implements WindowManagerPolicy {
mPowerDoublePressTargetActivity = ComponentName.unflattenFromString(
mContext.getResources().getString(
com.android.internal.R.string.config_doublePressOnPowerTargetActivity));
+ mPrimaryShortPressTargetActivity = ComponentName.unflattenFromString(
+ mContext.getResources().getString(
+ com.android.internal.R.string.config_primaryShortPressTargetActivity));
mShortPressOnSleepBehavior = mContext.getResources().getInteger(
com.android.internal.R.integer.config_shortPressOnSleepBehavior);
mAllowStartActivityForLongPressOnPowerDuringSetup = mContext.getResources().getBoolean(
@@ -2658,7 +2708,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
@Override
void onLongPress(long eventTime) {
- stemPrimaryLongPress();
+ stemPrimaryLongPress(eventTime);
}
@Override
@@ -3905,7 +3955,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
// Add Intent Extra data.
Bundle args = null;
args = new Bundle();
- if (deviceId > Integer.MIN_VALUE) {
+ if (deviceId != INVALID_INPUT_DEVICE_ID) {
args.putInt(Intent.EXTRA_ASSIST_INPUT_DEVICE_ID, deviceId);
}
if (hint != null) {
@@ -3914,8 +3964,15 @@ public class PhoneWindowManager implements WindowManagerPolicy {
args.putLong(Intent.EXTRA_TIME, eventTime);
args.putInt(AssistUtils.INVOCATION_TYPE_KEY, invocationType);
- ((SearchManager) mContext.createContextAsUser(UserHandle.of(mCurrentUserId), 0)
- .getSystemService(Context.SEARCH_SERVICE)).launchAssist(args);
+ if (mSearchManager != null) {
+ mSearchManager.launchAssist(args);
+ } else {
+ // Fallback to status bar if search manager doesn't exist (e.g. on wear).
+ StatusBarManagerInternal statusBar = getStatusBarManagerInternal();
+ if (statusBar != null) {
+ statusBar.startAssist(args);
+ }
+ }
}
/**
@@ -3926,39 +3983,16 @@ public class PhoneWindowManager implements WindowManagerPolicy {
final boolean keyguardActive =
mKeyguardDelegate != null && mKeyguardDelegate.isShowing();
if (!keyguardActive) {
- if (mHasFeatureWatch && isInRetailMode()) {
- launchRetailVoiceAssist(allowDuringSetup);
- } else {
- startVoiceAssistIntent(allowDuringSetup);
- }
- } else {
- mKeyguardDelegate.dismissKeyguardToLaunch(new Intent(Intent.ACTION_VOICE_ASSIST));
- }
- }
-
- private void launchRetailVoiceAssist(boolean allowDuringSetup) {
- Intent retailIntent = new Intent(ACTION_VOICE_ASSIST_RETAIL);
- ResolveInfo resolveInfo = mContext.getPackageManager().resolveActivity(
- retailIntent, /* flags= */0);
- if (resolveInfo != null) {
- retailIntent.setComponent(
- new ComponentName(resolveInfo.activityInfo.packageName,
- resolveInfo.activityInfo.name));
- startActivityAsUser(retailIntent, null, UserHandle.CURRENT_OR_SELF,
+ startActivityAsUser(
+ new Intent(Intent.ACTION_VOICE_ASSIST),
+ /* bundle= */ null,
+ UserHandle.CURRENT_OR_SELF,
allowDuringSetup);
} else {
- Slog.w(TAG, "Couldn't find an app to process " + ACTION_VOICE_ASSIST_RETAIL
- + ". Fall back to start " + Intent.ACTION_VOICE_ASSIST);
- startVoiceAssistIntent(allowDuringSetup);
+ mKeyguardDelegate.dismissKeyguardToLaunch(new Intent(Intent.ACTION_VOICE_ASSIST));
}
}
- private void startVoiceAssistIntent(boolean allowDuringSetup) {
- Intent intent = new Intent(Intent.ACTION_VOICE_ASSIST);
- startActivityAsUser(intent, null, UserHandle.CURRENT_OR_SELF,
- allowDuringSetup);
- }
-
private boolean isInRetailMode() {
return Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.DEVICE_DEMO_MODE, 0) == 1;
@@ -3981,13 +4015,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
}
- private SearchManager getSearchManager() {
- if (mSearchManager == null) {
- mSearchManager = (SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE);
- }
- return mSearchManager;
- }
-
private void preloadRecentApps() {
mPreloadedRecentApps = true;
StatusBarManagerInternal statusbar = getStatusBarManagerInternal();
@@ -6471,6 +6498,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
return "SHORT_PRESS_PRIMARY_NOTHING";
case SHORT_PRESS_PRIMARY_LAUNCH_ALL_APPS:
return "SHORT_PRESS_PRIMARY_LAUNCH_ALL_APPS";
+ case SHORT_PRESS_PRIMARY_LAUNCH_TARGET_ACTIVITY:
+ return "SHORT_PRESS_PRIMARY_LAUNCH_TARGET_ACTIVITY";
default:
return Integer.toString(behavior);
}
diff --git a/services/core/java/com/android/server/power/hint/HintManagerService.java b/services/core/java/com/android/server/power/hint/HintManagerService.java
index 577468b0c749..33bed3d42e50 100644
--- a/services/core/java/com/android/server/power/hint/HintManagerService.java
+++ b/services/core/java/com/android/server/power/hint/HintManagerService.java
@@ -56,7 +56,6 @@ import java.util.Objects;
public final class HintManagerService extends SystemService {
private static final String TAG = "HintManagerService";
private static final boolean DEBUG = false;
- private static final int MAX_HINT_SESSION_COUNT_PER_UID = 20;
@VisibleForTesting final long mHintSessionPreferredRate;
// Multi-level map storing all active AppHintSessions.
@@ -368,23 +367,6 @@ public final class HintManagerService extends SystemService {
+ " not be empty.");
final int callingUid = Binder.getCallingUid();
- if (callingUid != Process.SYSTEM_UID) {
- int sessionCount = 0;
- synchronized (mLock) {
- ArrayMap<IBinder, ArraySet<AppHintSession>> tokenMap =
- mActiveSessions.get(callingUid);
- if (tokenMap != null) {
- for (ArraySet<AppHintSession> arr : tokenMap.values()) {
- sessionCount += arr.size();
- }
- }
- }
- if (sessionCount >= MAX_HINT_SESSION_COUNT_PER_UID) {
- throw new IllegalStateException(
- "Max session count limit reached: " + sessionCount);
- }
- }
-
final int callingTgid = Process.getThreadGroupLeader(Binder.getCallingPid());
final long identity = Binder.clearCallingIdentity();
try {
diff --git a/services/core/java/com/android/server/security/rkp/RemoteProvisioningService.java b/services/core/java/com/android/server/security/rkp/RemoteProvisioningService.java
index 2bd7383ddde0..1c5838c165a4 100644
--- a/services/core/java/com/android/server/security/rkp/RemoteProvisioningService.java
+++ b/services/core/java/com/android/server/security/rkp/RemoteProvisioningService.java
@@ -105,14 +105,27 @@ public class RemoteProvisioningService extends SystemService {
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) return;
- new RemoteProvisioningShellCommand().dump(pw);
+ final int callerUid = Binder.getCallingUidOrThrow();
+ final long callingIdentity = Binder.clearCallingIdentity();
+ try {
+ new RemoteProvisioningShellCommand(getContext(), callerUid).dump(pw);
+ } finally {
+ Binder.restoreCallingIdentity(callingIdentity);
+ }
}
@Override
public int handleShellCommand(ParcelFileDescriptor in, ParcelFileDescriptor out,
ParcelFileDescriptor err, String[] args) {
- return new RemoteProvisioningShellCommand().exec(this, in.getFileDescriptor(),
- out.getFileDescriptor(), err.getFileDescriptor(), args);
+ final int callerUid = Binder.getCallingUidOrThrow();
+ final long callingIdentity = Binder.clearCallingIdentity();
+ try {
+ return new RemoteProvisioningShellCommand(getContext(), callerUid).exec(this,
+ in.getFileDescriptor(), out.getFileDescriptor(), err.getFileDescriptor(),
+ args);
+ } finally {
+ Binder.restoreCallingIdentity(callingIdentity);
+ }
}
}
}
diff --git a/services/core/java/com/android/server/security/rkp/RemoteProvisioningShellCommand.java b/services/core/java/com/android/server/security/rkp/RemoteProvisioningShellCommand.java
index 187b93931f0b..4a6d74658754 100644
--- a/services/core/java/com/android/server/security/rkp/RemoteProvisioningShellCommand.java
+++ b/services/core/java/com/android/server/security/rkp/RemoteProvisioningShellCommand.java
@@ -16,22 +16,30 @@
package com.android.server.security.rkp;
+import android.content.Context;
import android.hardware.security.keymint.DeviceInfo;
import android.hardware.security.keymint.IRemotelyProvisionedComponent;
import android.hardware.security.keymint.MacedPublicKey;
import android.hardware.security.keymint.ProtectedData;
import android.hardware.security.keymint.RpcHardwareInfo;
+import android.os.CancellationSignal;
+import android.os.OutcomeReceiver;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.ShellCommand;
+import android.security.rkp.service.RegistrationProxy;
+import android.security.rkp.service.RemotelyProvisionedKey;
import android.util.IndentingPrintWriter;
-import com.android.internal.annotations.VisibleForTesting;
-
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.PrintWriter;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateFactory;
+import java.time.Duration;
import java.util.Base64;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.Executor;
import co.nstant.in.cbor.CborDecoder;
import co.nstant.in.cbor.CborEncoder;
@@ -54,16 +62,17 @@ class RemoteProvisioningShellCommand extends ShellCommand {
+ "csr [--challenge CHALLENGE] NAME\n"
+ " Generate and print a base64-encoded CSR from the named\n"
+ " IRemotelyProvisionedComponent. A base64-encoded challenge can be provided,\n"
- + " or else it defaults to an empty challenge.\n";
+ + " or else it defaults to an empty challenge.\n"
+ + "certify NAME\n"
+ + " Output the PEM-encoded certificate chain provisioned for the named\n"
+ + " IRemotelyProvisionedComponent.\n";
- @VisibleForTesting
static final String EEK_ED25519_BASE64 = "goRDoQEnoFgqpAEBAycgBiFYIJm57t1e5FL2hcZMYtw+YatXSH11N"
+ "ymtdoAy0rPLY1jZWEAeIghLpLekyNdOAw7+uK8UTKc7b6XN3Np5xitk/pk5r3bngPpmAIUNB5gqrJFcpyUUS"
+ "QY0dcqKJ3rZ41pJ6wIDhEOhASegWE6lAQECWCDQrsEVyirPc65rzMvRlh1l6LHd10oaN7lDOpfVmd+YCAM4G"
+ "CAEIVggvoXnRsSjQlpA2TY6phXQLFh+PdwzAjLS/F4ehyVfcmBYQJvPkOIuS6vRGLEOjl0gJ0uEWP78MpB+c"
+ "gWDvNeCvvpkeC1UEEvAMb9r6B414vAtzmwvT/L1T6XUg62WovGHWAQ=";
- @VisibleForTesting
static final String EEK_P256_BASE64 = "goRDoQEmoFhNpQECAyYgASFYIPcUituX9MxT79JkEcTjdR9mH6RxDGzP"
+ "+glGgHSHVPKtIlggXn9b9uzk9hnM/xM3/Q+hyJPbGAZ2xF3m12p3hsMtr49YQC+XjkL7vgctlUeFR5NAsB/U"
+ "m0ekxESp8qEHhxDHn8sR9L+f6Dvg5zRMFfx7w34zBfTRNDztAgRgehXgedOK/ySEQ6EBJqBYcaYBAgJYIDVz"
@@ -74,14 +83,20 @@ class RemoteProvisioningShellCommand extends ShellCommand {
private static final int ERROR = -1;
private static final int SUCCESS = 0;
+ private static final Duration BIND_TIMEOUT = Duration.ofSeconds(10);
+ private static final int KEY_ID = 452436;
+
+ private final Context mContext;
+ private final int mCallerUid;
private final Injector mInjector;
- RemoteProvisioningShellCommand() {
- this(new Injector());
+ RemoteProvisioningShellCommand(Context context, int callerUid) {
+ this(context, callerUid, new Injector());
}
- @VisibleForTesting
- RemoteProvisioningShellCommand(Injector injector) {
+ RemoteProvisioningShellCommand(Context context, int callerUid, Injector injector) {
+ mContext = context;
+ mCallerUid = callerUid;
mInjector = injector;
}
@@ -102,6 +117,8 @@ class RemoteProvisioningShellCommand extends ShellCommand {
return list();
case "csr":
return csr();
+ case "certify":
+ return certify();
default:
return handleDefaultCommands(cmd);
}
@@ -232,7 +249,45 @@ class RemoteProvisioningShellCommand extends ShellCommand {
return new CborDecoder(bais).decodeNext();
}
- @VisibleForTesting
+ private int certify() throws Exception {
+ String name = getNextArgRequired();
+
+ Executor executor = mContext.getMainExecutor();
+ CancellationSignal cancellationSignal = new CancellationSignal();
+ OutcomeFuture<RemotelyProvisionedKey> key = new OutcomeFuture<>();
+ mInjector.getRegistrationProxy(mContext, mCallerUid, name, executor)
+ .getKeyAsync(KEY_ID, cancellationSignal, executor, key);
+ byte[] encodedCertChain = key.join().getEncodedCertChain();
+ ByteArrayInputStream is = new ByteArrayInputStream(encodedCertChain);
+ PrintWriter pw = getOutPrintWriter();
+ for (Certificate cert : CertificateFactory.getInstance("X.509").generateCertificates(is)) {
+ String encoded = Base64.getEncoder().encodeToString(cert.getEncoded());
+ pw.println("-----BEGIN CERTIFICATE-----");
+ pw.println(encoded.replaceAll("(.{64})", "$1\n").stripTrailing());
+ pw.println("-----END CERTIFICATE-----");
+ }
+ return SUCCESS;
+ }
+
+ /** Treat an OutcomeReceiver as a future for use in synchronous code. */
+ private static class OutcomeFuture<T> implements OutcomeReceiver<T, Exception> {
+ private CompletableFuture<T> mFuture = new CompletableFuture<>();
+
+ @Override
+ public void onResult(T result) {
+ mFuture.complete(result);
+ }
+
+ @Override
+ public void onError(Exception e) {
+ mFuture.completeExceptionally(e);
+ }
+
+ public T join() {
+ return mFuture.join();
+ }
+ }
+
static class Injector {
String[] getIrpcNames() {
return ServiceManager.getDeclaredInstances(IRemotelyProvisionedComponent.DESCRIPTOR);
@@ -248,5 +303,14 @@ class RemoteProvisioningShellCommand extends ShellCommand {
}
return binder;
}
+
+ RegistrationProxy getRegistrationProxy(
+ Context context, int callerUid, String name, Executor executor) {
+ String irpc = IRemotelyProvisionedComponent.DESCRIPTOR + "/" + name;
+ OutcomeFuture<RegistrationProxy> registration = new OutcomeFuture<>();
+ RegistrationProxy.createAsync(
+ context, callerUid, irpc, BIND_TIMEOUT, executor, registration);
+ return registration.join();
+ }
}
}
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index 5b36cd6b36c1..ffe001055979 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -155,12 +155,12 @@ public final class TvInputManagerService extends SystemService {
// ID of the current user.
@GuardedBy("mLock")
private int mCurrentUserId = UserHandle.USER_SYSTEM;
+ // ID of the current on-screen input.
@GuardedBy("mLock")
- // ID of the current input displayed on the screen.
- private String mCurrentInputId = null;
+ private String mOnScreenInputId = null;
+ // SessionState of the currently active on-screen TIS session.
@GuardedBy("mLock")
- // SessionState of the currently active TIS session.
- private SessionState mCurrentSessionState = null;
+ private SessionState mOnScreenSessionState = null;
// IDs of the running profiles. Their parent user ID should be mCurrentUserId.
@GuardedBy("mLock")
private final Set<Integer> mRunningProfiles = new HashSet<>();
@@ -879,12 +879,12 @@ public final class TvInputManagerService extends SystemService {
sessionState.session = null;
}
}
- if (mCurrentSessionState == sessionState) {
+ if (mOnScreenSessionState == sessionState) {
// only log when releasing the current on-screen session
logExternalInputEvent(FrameworkStatsLog.EXTERNAL_TV_INPUT_EVENT__EVENT_TYPE__RELEASED,
- mCurrentInputId, sessionState);
- mCurrentInputId = null;
- mCurrentSessionState = null;
+ mOnScreenInputId, sessionState);
+ mOnScreenInputId = null;
+ mOnScreenSessionState = null;
}
removeSessionStateLocked(sessionToken, userId);
return sessionState;
@@ -1079,7 +1079,7 @@ public final class TvInputManagerService extends SystemService {
if (currentCecTvInputInfoUpdated) {
logExternalInputEvent(
FrameworkStatsLog.EXTERNAL_TV_INPUT_EVENT__EVENT_TYPE__DEVICE_INFO_UPDATED,
- mCurrentInputId, mCurrentSessionState);
+ mOnScreenInputId, mOnScreenSessionState);
}
int n = userState.mCallbacks.beginBroadcast();
@@ -1096,14 +1096,14 @@ public final class TvInputManagerService extends SystemService {
@GuardedBy("mLock")
private boolean isCurrentCecTvInputInfoUpdate(UserState userState, TvInputInfo newInputInfo) {
if (newInputInfo == null || newInputInfo.getId() == null
- || !newInputInfo.getId().equals(mCurrentInputId)) {
+ || !newInputInfo.getId().equals(mOnScreenInputId)) {
return false;
}
if (newInputInfo.getHdmiDeviceInfo() == null
|| !newInputInfo.getHdmiDeviceInfo().isCecDevice()) {
return false;
}
- TvInputState inputState = userState.inputMap.get(mCurrentInputId);
+ TvInputState inputState = userState.inputMap.get(mOnScreenInputId);
if (inputState == null || inputState.info == null) {
return false;
}
@@ -1133,21 +1133,21 @@ public final class TvInputManagerService extends SystemService {
return;
}
if (oldState != state) {
- if (inputId.equals(mCurrentInputId)) {
+ if (inputId.equals(mOnScreenInputId)) {
logExternalInputEvent(
FrameworkStatsLog
.EXTERNAL_TV_INPUT_EVENT__EVENT_TYPE__CONNECTION_STATE_CHANGED,
- mCurrentInputId, mCurrentSessionState);
- } else if (mCurrentInputId != null) {
- TvInputInfo currentInputInfo = userState.inputMap.get(mCurrentInputId).info;
+ mOnScreenInputId, mOnScreenSessionState);
+ } else if (mOnScreenInputId != null) {
+ TvInputInfo currentInputInfo = userState.inputMap.get(mOnScreenInputId).info;
if (currentInputInfo != null && currentInputInfo.getHdmiDeviceInfo() != null
&& inputId.equals(currentInputInfo.getParentId())) {
logExternalInputEvent(
FrameworkStatsLog
.EXTERNAL_TV_INPUT_EVENT__EVENT_TYPE__CONNECTION_STATE_CHANGED,
- inputId, mCurrentSessionState);
+ inputId, mOnScreenSessionState);
if (state == INPUT_STATE_CONNECTED_STANDBY) {
- mCurrentInputId = currentInputInfo.getParentId();
+ mOnScreenInputId = currentInputInfo.getParentId();
}
}
}
@@ -1814,19 +1814,22 @@ public final class TvInputManagerService extends SystemService {
UserState userState = getOrCreateUserStateLocked(resolvedUserId);
SessionState sessionState = getSessionStateLocked(sessionToken, callingUid,
userState);
- if (mCurrentInputId == null
- || !mCurrentInputId.equals(sessionState.inputId)) {
- mCurrentInputId = sessionState.inputId;
- logExternalInputEvent(
- FrameworkStatsLog.EXTERNAL_TV_INPUT_EVENT__EVENT_TYPE__TUNED,
- sessionState.inputId, sessionState);
- }
if (!sessionState.isCurrent
|| !Objects.equals(sessionState.currentChannel, channelUri)) {
sessionState.isCurrent = true;
sessionState.currentChannel = channelUri;
- mCurrentSessionState = sessionState;
notifyCurrentChannelInfosUpdatedLocked(userState);
+ if (!sessionState.isRecordingSession) {
+ if (mOnScreenInputId == null
+ || !TextUtils.equals(mOnScreenInputId, sessionState.inputId)) {
+ logExternalInputEvent(
+ FrameworkStatsLog
+ .EXTERNAL_TV_INPUT_EVENT__EVENT_TYPE__TUNED,
+ sessionState.inputId, sessionState);
+ }
+ mOnScreenInputId = sessionState.inputId;
+ mOnScreenSessionState = sessionState;
+ }
}
if (TvContract.isChannelUriForPassthroughInput(channelUri)) {
// Do not log the watch history for passthrough inputs.
@@ -3436,21 +3439,21 @@ public final class TvInputManagerService extends SystemService {
synchronized (mLock) {
mTvInputHardwareManager.addHdmiInput(id, inputInfo);
addHardwareInputLocked(inputInfo);
- if (mCurrentInputId != null && mCurrentSessionState != null) {
- if (TextUtils.equals(mCurrentInputId, inputInfo.getParentId())) {
+ if (mOnScreenInputId != null && mOnScreenSessionState != null) {
+ if (TextUtils.equals(mOnScreenInputId, inputInfo.getParentId())) {
// catch the use case when a CEC device is plugged in an HDMI port,
// and TV app does not explicitly call tune() to the added CEC input.
logExternalInputEvent(
FrameworkStatsLog.EXTERNAL_TV_INPUT_EVENT__EVENT_TYPE__TUNED,
- inputInfo.getId(), mCurrentSessionState);
- mCurrentInputId = inputInfo.getId();
- } else if (TextUtils.equals(mCurrentInputId, inputInfo.getId())) {
+ inputInfo.getId(), mOnScreenSessionState);
+ mOnScreenInputId = inputInfo.getId();
+ } else if (TextUtils.equals(mOnScreenInputId, inputInfo.getId())) {
// catch the use case when a CEC device disconnects itself
// and reconnects to update info.
logExternalInputEvent(
FrameworkStatsLog
.EXTERNAL_TV_INPUT_EVENT__EVENT_TYPE__DEVICE_INFO_UPDATED,
- mCurrentInputId, mCurrentSessionState);
+ mOnScreenInputId, mOnScreenSessionState);
}
}
}
@@ -3555,9 +3558,17 @@ public final class TvInputManagerService extends SystemService {
UserState userState = getOrCreateUserStateLocked(mSessionState.userId);
mSessionState.isCurrent = true;
mSessionState.currentChannel = channelUri;
- mCurrentSessionState = mSessionState;
- mCurrentInputId = mSessionState.inputId;
notifyCurrentChannelInfosUpdatedLocked(userState);
+ if (!mSessionState.isRecordingSession) {
+ if (mOnScreenInputId == null
+ || !TextUtils.equals(mOnScreenInputId, mSessionState.inputId)) {
+ logExternalInputEvent(
+ FrameworkStatsLog.EXTERNAL_TV_INPUT_EVENT__EVENT_TYPE__TUNED,
+ mSessionState.inputId, mSessionState);
+ }
+ mOnScreenInputId = mSessionState.inputId;
+ mOnScreenSessionState = mSessionState;
+ }
}
} catch (RemoteException e) {
Slog.e(TAG, "error in onChannelRetuned", e);
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 64c7c6f9875b..649ab8fe1d4d 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -7258,8 +7258,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
void showStartingWindow(boolean taskSwitch) {
- showStartingWindow(null /* prev */, false /* newTask */, taskSwitch,
- false /* startActivity */, null);
+ // Pass the activity which contains starting window already.
+ final ActivityRecord prev = task.getActivity(
+ a -> a != this && a.mStartingData != null && a.showToCurrentUser());
+ showStartingWindow(prev, false /* newTask */, taskSwitch, false /* startActivity */, null);
}
/**
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 5c82dba82031..59677f41123a 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -327,6 +327,12 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
*/
private SurfaceControl mOverlayLayer;
+ /**
+ * A SurfaceControl that contains input overlays used for cases where we need to receive input
+ * over the entire display.
+ */
+ private SurfaceControl mInputOverlayLayer;
+
/** A surfaceControl specifically for accessibility overlays. */
private SurfaceControl mA11yOverlayLayer;
@@ -1327,6 +1333,12 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
transaction.reparent(mOverlayLayer, mSurfaceControl);
}
+ if (mInputOverlayLayer == null) {
+ mInputOverlayLayer = b.setName("Input Overlays").setParent(mSurfaceControl).build();
+ } else {
+ transaction.reparent(mInputOverlayLayer, mSurfaceControl);
+ }
+
if (mA11yOverlayLayer == null) {
mA11yOverlayLayer =
b.setName("Accessibility Overlays").setParent(mSurfaceControl).build();
@@ -1340,7 +1352,9 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
.show(mSurfaceControl)
.setLayer(mOverlayLayer, Integer.MAX_VALUE)
.show(mOverlayLayer)
- .setLayer(mA11yOverlayLayer, Integer.MAX_VALUE - 1)
+ .setLayer(mInputOverlayLayer, Integer.MAX_VALUE - 1)
+ .show(mInputOverlayLayer)
+ .setLayer(mA11yOverlayLayer, Integer.MAX_VALUE - 2)
.show(mA11yOverlayLayer);
}
@@ -3351,6 +3365,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
// -> this DisplayContent.
setRemoteInsetsController(null);
mOverlayLayer.release();
+ mInputOverlayLayer.release();
mA11yOverlayLayer.release();
mWindowingLayer.release();
mInputMonitor.onDisplayRemoved();
@@ -5704,6 +5719,10 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
return mOverlayLayer;
}
+ SurfaceControl getInputOverlayLayer() {
+ return mInputOverlayLayer;
+ }
+
SurfaceControl getA11yOverlayLayer() {
return mA11yOverlayLayer;
}
@@ -7060,6 +7079,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
new Transaction().reparent(sc, getSurfaceControl())
.reparent(mWindowingLayer, null)
.reparent(mOverlayLayer, null)
+ .reparent(mInputOverlayLayer, null)
.reparent(mA11yOverlayLayer, null)
.apply();
}
diff --git a/services/core/java/com/android/server/wm/InputManagerCallback.java b/services/core/java/com/android/server/wm/InputManagerCallback.java
index 73fdfe0d1181..8cf471394c63 100644
--- a/services/core/java/com/android/server/wm/InputManagerCallback.java
+++ b/services/core/java/com/android/server/wm/InputManagerCallback.java
@@ -275,11 +275,17 @@ final class InputManagerCallback implements InputManagerService.WindowManagerCal
+ " - DisplayContent not found.");
return null;
}
+ final SurfaceControl inputOverlay = dc.getInputOverlayLayer();
+ if (inputOverlay == null) {
+ Slog.e(TAG, "Failed to create a gesture monitor on display: " + displayId
+ + " - Input overlay layer is not initialized.");
+ return null;
+ }
return mService.makeSurfaceBuilder(dc.getSession())
.setContainerLayer()
.setName(name)
.setCallsite("createSurfaceForGestureMonitor")
- .setParent(dc.getSurfaceControl())
+ .setParent(inputOverlay)
.build();
}
}
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 2f0c303ec839..4089a0d7622f 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -2277,7 +2277,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
int finishTopCrashedActivities(WindowProcessController app, String reason) {
Task focusedRootTask = getTopDisplayFocusedRootTask();
final Task[] finishedTask = new Task[1];
- forAllTasks(rootTask -> {
+ forAllRootTasks(rootTask -> {
final Task t = rootTask.finishTopCrashedActivityLocked(app, reason);
if (rootTask == focusedRootTask || finishedTask[0] == null) {
finishedTask[0] = t;
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 43430dd1eed0..f9bbc6810835 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -725,7 +725,7 @@ class Task extends TaskFragment {
} catch (RemoteException e) {
}
}
- if (autoRemoveFromRecents(oldParent.asTaskFragment()) || isVoiceSession) {
+ if (shouldAutoRemoveFromRecents(oldParent.asTaskFragment()) || isVoiceSession) {
// Task creator asked to remove this when done, or this task was a voice
// interaction, so it should not remain on the recent tasks list.
mTaskSupervisor.mRecentTasks.remove(this);
@@ -1558,12 +1558,14 @@ class Task extends TaskFragment {
return count > 0;
}
- private boolean autoRemoveFromRecents(TaskFragment oldParentFragment) {
+ private boolean shouldAutoRemoveFromRecents(TaskFragment oldParentFragment) {
// We will automatically remove the task either if it has explicitly asked for
// this, or it is empty and has never contained an activity that got shown to
- // the user, or it was being embedded in another Task.
- return autoRemoveRecents || (!hasChild() && !getHasBeenVisible()
- || (oldParentFragment != null && oldParentFragment.isEmbedded()));
+ // the user, or it was being embedded in another Task, or the display policy
+ // doesn't allow recents,
+ return autoRemoveRecents || (!hasChild() && !getHasBeenVisible())
+ || (oldParentFragment != null && oldParentFragment.isEmbedded())
+ || (mDisplayContent != null && !mDisplayContent.canShowTasksInHostDeviceRecents());
}
private void clearPinnedTaskIfNeed() {
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 03efb1bf705e..439b7193dd4b 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -8338,12 +8338,18 @@ public class WindowManagerService extends IWindowManager.Stub
+ displayId + " - DisplayContent not found.");
return null;
}
- //TODO (b/210039666): Use a method like add/removeDisplayOverlay if available.
+ final SurfaceControl inputOverlay = dc.getInputOverlayLayer();
+ if (inputOverlay == null) {
+ Slog.e(TAG, "Failed to create a gesture monitor on display: " + displayId
+ + " - Input overlay layer is not initialized.");
+ return null;
+ }
+ // TODO(b/210039666): Use a method like add/removeDisplayOverlay if available.
return makeSurfaceBuilder(dc.getSession())
.setContainerLayer()
.setName("IME Handwriting Surface")
.setCallsite("getHandwritingSurfaceForDisplay")
- .setParent(dc.getSurfaceControl())
+ .setParent(inputOverlay)
.build();
}
}
diff --git a/services/core/xsd/display-device-config/display-device-config.xsd b/services/core/xsd/display-device-config/display-device-config.xsd
index 7104a80c668d..d833fbd6ffd2 100644
--- a/services/core/xsd/display-device-config/display-device-config.xsd
+++ b/services/core/xsd/display-device-config/display-device-config.xsd
@@ -459,16 +459,26 @@
<xs:complexType name="autoBrightness">
<xs:attribute name="enabled" type="xs:boolean" use="optional" default="true"/>
<xs:sequence>
- <!-- Sets the debounce for autoBrightness brightening in millis-->
+ <!-- Sets the debounce for autoBrightness brightening in millis -->
<xs:element name="brighteningLightDebounceMillis" type="xs:nonNegativeInteger"
minOccurs="0" maxOccurs="1">
<xs:annotation name="final"/>
</xs:element>
- <!-- Sets the debounce for autoBrightness darkening in millis-->
+ <!-- Sets the debounce for autoBrightness darkening in millis -->
<xs:element name="darkeningLightDebounceMillis" type="xs:nonNegativeInteger"
minOccurs="0" maxOccurs="1">
<xs:annotation name="final"/>
</xs:element>
+ <!-- Sets the debounce for autoBrightness brightening in millis while in idle mode -->
+ <xs:element name="brighteningLightDebounceIdleMillis" type="xs:nonNegativeInteger"
+ minOccurs="0" maxOccurs="1">
+ <xs:annotation name="final"/>
+ </xs:element>
+ <!-- Sets the debounce for autoBrightness darkening in millis while in idle mode -->
+ <xs:element name="darkeningLightDebounceIdleMillis" type="xs:nonNegativeInteger"
+ minOccurs="0" maxOccurs="1">
+ <xs:annotation name="final"/>
+ </xs:element>
<!-- Sets the brightness mapping of the desired screen brightness in nits to the
corresponding lux for the current display -->
<xs:element name="displayBrightnessMapping" type="displayBrightnessMapping"
@@ -579,6 +589,10 @@
minOccurs="1" maxOccurs="1">
<xs:annotation name="final"/>
</xs:element>
+ <xs:element type ="xs:string" name="refreshRateThermalThrottlingId">
+ <xs:annotation name="nullable"/>
+ <xs:annotation name="final"/>
+ </xs:element>
<xs:element name="blockingZoneThreshold" type="blockingZoneThreshold"
minOccurs="1" maxOccurs="1">
<xs:annotation name="final"/>
diff --git a/services/core/xsd/display-device-config/schema/current.txt b/services/core/xsd/display-device-config/schema/current.txt
index 507c9dccda59..d2ac1aae1500 100644
--- a/services/core/xsd/display-device-config/schema/current.txt
+++ b/services/core/xsd/display-device-config/schema/current.txt
@@ -3,11 +3,15 @@ package com.android.server.display.config {
public class AutoBrightness {
ctor public AutoBrightness();
+ method public final java.math.BigInteger getBrighteningLightDebounceIdleMillis();
method public final java.math.BigInteger getBrighteningLightDebounceMillis();
+ method public final java.math.BigInteger getDarkeningLightDebounceIdleMillis();
method public final java.math.BigInteger getDarkeningLightDebounceMillis();
method public final com.android.server.display.config.DisplayBrightnessMapping getDisplayBrightnessMapping();
method public boolean getEnabled();
+ method public final void setBrighteningLightDebounceIdleMillis(java.math.BigInteger);
method public final void setBrighteningLightDebounceMillis(java.math.BigInteger);
+ method public final void setDarkeningLightDebounceIdleMillis(java.math.BigInteger);
method public final void setDarkeningLightDebounceMillis(java.math.BigInteger);
method public final void setDisplayBrightnessMapping(com.android.server.display.config.DisplayBrightnessMapping);
method public void setEnabled(boolean);
@@ -17,8 +21,10 @@ package com.android.server.display.config {
ctor public BlockingZoneConfig();
method public final com.android.server.display.config.BlockingZoneThreshold getBlockingZoneThreshold();
method public final java.math.BigInteger getDefaultRefreshRate();
+ method @Nullable public final String getRefreshRateThermalThrottlingId();
method public final void setBlockingZoneThreshold(com.android.server.display.config.BlockingZoneThreshold);
method public final void setDefaultRefreshRate(java.math.BigInteger);
+ method public final void setRefreshRateThermalThrottlingId(@Nullable String);
}
public class BlockingZoneThreshold {
diff --git a/services/permission/java/com/android/server/permission/access/AccessPolicy.kt b/services/permission/java/com/android/server/permission/access/AccessPolicy.kt
index 17474fbe1de4..6a349e237ffe 100644
--- a/services/permission/java/com/android/server/permission/access/AccessPolicy.kt
+++ b/services/permission/java/com/android/server/permission/access/AccessPolicy.kt
@@ -26,6 +26,7 @@ import com.android.server.permission.access.collection.* // ktlint-disable no-wi
import com.android.server.permission.access.immutable.* // ktlint-disable no-wildcard-imports
import com.android.server.permission.access.immutable.IndexedMap
import com.android.server.permission.access.permission.AppIdPermissionPolicy
+import com.android.server.permission.access.permission.DevicePermissionPolicy
import com.android.server.permission.access.util.attributeInt
import com.android.server.permission.access.util.attributeInterned
import com.android.server.permission.access.util.forEachTag
@@ -46,6 +47,7 @@ class AccessPolicy private constructor(
getOrPut(policy.subjectScheme) { MutableIndexedMap() }[policy.objectScheme] = policy
}
addPolicy(AppIdPermissionPolicy())
+ addPolicy(DevicePermissionPolicy())
addPolicy(AppIdAppOpPolicy())
addPolicy(PackageAppOpPolicy())
} as IndexedMap<String, IndexedMap<String, SchemePolicy>>
diff --git a/services/permission/java/com/android/server/permission/access/AccessState.kt b/services/permission/java/com/android/server/permission/access/AccessState.kt
index 4ec32ea53f28..94c878a453c9 100644
--- a/services/permission/java/com/android/server/permission/access/AccessState.kt
+++ b/services/permission/java/com/android/server/permission/access/AccessState.kt
@@ -329,6 +329,18 @@ typealias MutableAppIdPermissionFlags =
private typealias AppIdPermissionFlagsReference =
MutableReference<AppIdPermissionFlags, MutableAppIdPermissionFlags>
+
+typealias DevicePermissionFlags =
+ IndexedReferenceMap<String, IndexedMap<String, Int>, MutableIndexedMap<String, Int>>
+typealias MutableDevicePermissionFlags =
+ MutableIndexedReferenceMap<String, IndexedMap<String, Int>, MutableIndexedMap<String, Int>>
+typealias AppIdDevicePermissionFlags =
+ IntReferenceMap<DevicePermissionFlags, MutableDevicePermissionFlags>
+typealias MutableAppIdDevicePermissionFlags =
+ MutableIntReferenceMap<DevicePermissionFlags, MutableDevicePermissionFlags>
+private typealias AppIdDevicePermissionFlagsReference =
+ MutableReference<AppIdDevicePermissionFlags, MutableAppIdDevicePermissionFlags>
+
typealias AppIdAppOpModes =
IntReferenceMap<IndexedMap<String, Int>, MutableIndexedMap<String, Int>>
typealias MutableAppIdAppOpModes =
@@ -346,6 +358,7 @@ private typealias PackageAppOpModesReference =
sealed class UserState(
internal val packageVersionsReference: PackageVersionsReference,
internal val appIdPermissionFlagsReference: AppIdPermissionFlagsReference,
+ internal val appIdDevicePermissionFlagsReference: AppIdDevicePermissionFlagsReference,
internal val appIdAppOpModesReference: AppIdAppOpModesReference,
internal val packageAppOpModesReference: PackageAppOpModesReference,
defaultPermissionGrantFingerprint: String?,
@@ -357,6 +370,9 @@ sealed class UserState(
val appIdPermissionFlags: AppIdPermissionFlags
get() = appIdPermissionFlagsReference.get()
+ val appIdDevicePermissionFlags: AppIdDevicePermissionFlags
+ get() = appIdDevicePermissionFlagsReference.get()
+
val appIdAppOpModes: AppIdAppOpModes
get() = appIdAppOpModesReference.get()
@@ -375,6 +391,7 @@ sealed class UserState(
class MutableUserState private constructor(
packageVersionsReference: PackageVersionsReference,
appIdPermissionFlagsReference: AppIdPermissionFlagsReference,
+ appIdDevicePermissionFlagsReference: AppIdDevicePermissionFlagsReference,
appIdAppOpModesReference: AppIdAppOpModesReference,
packageAppOpModesReference: PackageAppOpModesReference,
defaultPermissionGrantFingerprint: String?,
@@ -382,6 +399,7 @@ class MutableUserState private constructor(
) : UserState(
packageVersionsReference,
appIdPermissionFlagsReference,
+ appIdDevicePermissionFlagsReference,
appIdAppOpModesReference,
packageAppOpModesReference,
defaultPermissionGrantFingerprint,
@@ -390,6 +408,7 @@ class MutableUserState private constructor(
constructor() : this(
PackageVersionsReference(MutableIndexedMap<String, Int>()),
AppIdPermissionFlagsReference(MutableAppIdPermissionFlags()),
+ AppIdDevicePermissionFlagsReference(MutableAppIdDevicePermissionFlags()),
AppIdAppOpModesReference(MutableAppIdAppOpModes()),
PackageAppOpModesReference(MutablePackageAppOpModes()),
null,
@@ -399,6 +418,7 @@ class MutableUserState private constructor(
internal constructor(userState: UserState) : this(
userState.packageVersionsReference.toImmutable(),
userState.appIdPermissionFlagsReference.toImmutable(),
+ userState.appIdDevicePermissionFlagsReference.toImmutable(),
userState.appIdAppOpModesReference.toImmutable(),
userState.packageAppOpModesReference.toImmutable(),
userState.defaultPermissionGrantFingerprint,
@@ -410,6 +430,9 @@ class MutableUserState private constructor(
fun mutateAppIdPermissionFlags(): MutableAppIdPermissionFlags =
appIdPermissionFlagsReference.mutate()
+ fun mutateAppIdDevicePermissionFlags(): MutableAppIdDevicePermissionFlags =
+ appIdDevicePermissionFlagsReference.mutate()
+
fun mutateAppIdAppOpModes(): MutableAppIdAppOpModes = appIdAppOpModesReference.mutate()
fun mutatePackageAppOpModes(): MutablePackageAppOpModes = packageAppOpModesReference.mutate()
diff --git a/services/permission/java/com/android/server/permission/access/AccessUri.kt b/services/permission/java/com/android/server/permission/access/AccessUri.kt
index d1abc0455245..1d46ca71fe0f 100644
--- a/services/permission/java/com/android/server/permission/access/AccessUri.kt
+++ b/services/permission/java/com/android/server/permission/access/AccessUri.kt
@@ -65,6 +65,17 @@ data class PermissionUri(
}
}
+data class DevicePermissionUri(
+ val permissionName: String,
+ val deviceId: Int
+) : AccessUri(SCHEME) {
+ override fun toString(): String = "$scheme:///$permissionName/$deviceId"
+
+ companion object {
+ const val SCHEME = "device-permission"
+ }
+}
+
data class UidUri(
val uid: Int
) : AccessUri(SCHEME) {
diff --git a/services/permission/java/com/android/server/permission/access/permission/DevicePermissionPersistence.kt b/services/permission/java/com/android/server/permission/access/permission/DevicePermissionPersistence.kt
new file mode 100644
index 000000000000..37a4a90f8f80
--- /dev/null
+++ b/services/permission/java/com/android/server/permission/access/permission/DevicePermissionPersistence.kt
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2023 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.permission.access.permission
+
+import android.util.Slog
+import com.android.modules.utils.BinaryXmlPullParser
+import com.android.modules.utils.BinaryXmlSerializer
+import com.android.server.permission.access.AccessState
+import com.android.server.permission.access.DevicePermissionFlags
+import com.android.server.permission.access.MutableAccessState
+import com.android.server.permission.access.MutableAppIdDevicePermissionFlags
+import com.android.server.permission.access.MutableDevicePermissionFlags
+import com.android.server.permission.access.WriteMode
+import com.android.server.permission.access.immutable.IndexedMap
+import com.android.server.permission.access.immutable.MutableIndexedMap
+import com.android.server.permission.access.immutable.forEachIndexed
+import com.android.server.permission.access.immutable.forEachReversedIndexed
+import com.android.server.permission.access.immutable.set
+import com.android.server.permission.access.util.andInv
+import com.android.server.permission.access.util.attributeInt
+import com.android.server.permission.access.util.attributeInterned
+import com.android.server.permission.access.util.forEachTag
+import com.android.server.permission.access.util.getAttributeIntOrThrow
+import com.android.server.permission.access.util.getAttributeValueOrThrow
+import com.android.server.permission.access.util.hasBits
+import com.android.server.permission.access.util.tag
+import com.android.server.permission.access.util.tagName
+
+class DevicePermissionPersistence {
+ fun BinaryXmlPullParser.parseUserState(state: MutableAccessState, userId: Int) {
+ when (tagName) {
+ TAG_APP_ID_DEVICE_PERMISSIONS -> parseAppIdDevicePermissions(state, userId)
+ else -> {}
+ }
+ }
+
+ private fun BinaryXmlPullParser.parseAppIdDevicePermissions(
+ state: MutableAccessState,
+ userId: Int
+ ) {
+ val userState = state.mutateUserState(userId, WriteMode.NONE)!!
+ val appIdDevicePermissionFlags = userState.mutateAppIdDevicePermissionFlags()
+ forEachTag {
+ when (tagName) {
+ TAG_APP_ID -> parseAppId(appIdDevicePermissionFlags)
+ else -> Slog.w(LOG_TAG, "Ignoring unknown tag $name when parsing permission state")
+ }
+ }
+
+ appIdDevicePermissionFlags.forEachReversedIndexed { appIdIndex, appId, _ ->
+ if (appId !in state.externalState.appIdPackageNames) {
+ Slog.w(LOG_TAG, "Dropping unknown app ID $appId when parsing permission state")
+ appIdDevicePermissionFlags.removeAt(appIdIndex)
+ userState.requestWriteMode(WriteMode.ASYNCHRONOUS)
+ }
+ }
+ }
+
+ private fun BinaryXmlPullParser.parseAppId(
+ appIdPermissionFlags: MutableAppIdDevicePermissionFlags
+ ) {
+ val appId = getAttributeIntOrThrow(ATTR_ID)
+ val devicePermissionFlags = MutableDevicePermissionFlags()
+ appIdPermissionFlags[appId] = devicePermissionFlags
+ forEachTag {
+ when (tagName) {
+ TAG_DEVICE -> parseDevice(devicePermissionFlags)
+ else -> {
+ Slog.w(LOG_TAG, "Ignoring unknown tag $name when parsing permission state")
+ }
+ }
+ }
+ }
+
+ private fun BinaryXmlPullParser.parseDevice(
+ deviceIdPermissionFlags: MutableDevicePermissionFlags
+ ) {
+ val deviceId = getAttributeValueOrThrow(ATTR_ID)
+ val permissionFlags = MutableIndexedMap<String, Int>()
+ deviceIdPermissionFlags.put(deviceId, permissionFlags)
+ forEachTag {
+ when (tagName) {
+ TAG_PERMISSION -> parsePermission(permissionFlags)
+ else -> Slog.w(LOG_TAG, "Ignoring unknown tag $name when parsing permission state")
+ }
+ }
+ }
+
+ private fun BinaryXmlPullParser.parsePermission(
+ permissionFlags: MutableIndexedMap<String, Int>
+ ) {
+ val name = getAttributeValueOrThrow(ATTR_NAME).intern()
+ val flags = getAttributeIntOrThrow(ATTR_FLAGS)
+ permissionFlags[name] = flags
+ }
+
+ fun BinaryXmlSerializer.serializeUserState(state: AccessState, userId: Int) {
+ val appIdDevicePermissionFlags = state.userStates[userId]!!.appIdDevicePermissionFlags
+ tag(TAG_APP_ID_DEVICE_PERMISSIONS) {
+ appIdDevicePermissionFlags.forEachIndexed { _, appId, devicePermissionFlags ->
+ serializeAppId(appId, devicePermissionFlags)
+ }
+ }
+ }
+
+ private fun BinaryXmlSerializer.serializeAppId(
+ appId: Int,
+ devicePermissionFlags: DevicePermissionFlags
+ ) {
+ tag(TAG_APP_ID) {
+ attributeInt(ATTR_ID, appId)
+ devicePermissionFlags.forEachIndexed { _, deviceId, permissionFlags ->
+ serializeDevice(deviceId, permissionFlags)
+ }
+ }
+ }
+
+ private fun BinaryXmlSerializer.serializeDevice(
+ deviceId: String,
+ permissionFlags: IndexedMap<String, Int>
+ ) {
+ tag(TAG_DEVICE) {
+ attributeInterned(ATTR_ID, deviceId)
+ permissionFlags.forEachIndexed { _, name, flags ->
+ serializePermission(name, flags)
+ }
+ }
+ }
+
+ private fun BinaryXmlSerializer.serializePermission(name: String, flags: Int) {
+ tag(TAG_PERMISSION) {
+ attributeInterned(ATTR_NAME, name)
+ // Never serialize one-time permissions as granted.
+ val serializedFlags = if (flags.hasBits(PermissionFlags.ONE_TIME)) {
+ flags andInv PermissionFlags.RUNTIME_GRANTED
+ } else {
+ flags
+ }
+ attributeInt(ATTR_FLAGS, serializedFlags)
+ }
+ }
+
+ companion object {
+ private val LOG_TAG = DevicePermissionPersistence::class.java.simpleName
+
+ private const val TAG_APP_ID_DEVICE_PERMISSIONS = "app-id-device-permissions"
+ private const val TAG_APP_ID = "app-id"
+ private const val TAG_DEVICE = "device"
+ private const val TAG_PERMISSION = "permission"
+
+ private const val ATTR_ID = "id"
+ private const val ATTR_NAME = "name"
+ private const val ATTR_FLAGS = "flags"
+ }
+} \ No newline at end of file
diff --git a/services/permission/java/com/android/server/permission/access/permission/DevicePermissionPolicy.kt b/services/permission/java/com/android/server/permission/access/permission/DevicePermissionPolicy.kt
new file mode 100644
index 000000000000..c0d7546180bf
--- /dev/null
+++ b/services/permission/java/com/android/server/permission/access/permission/DevicePermissionPolicy.kt
@@ -0,0 +1,276 @@
+/*
+ * Copyright (C) 2023 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.permission.access.permission
+
+import android.util.Slog
+import com.android.modules.utils.BinaryXmlPullParser
+import com.android.modules.utils.BinaryXmlSerializer
+import com.android.server.permission.access.AccessState
+import com.android.server.permission.access.DevicePermissionUri
+import com.android.server.permission.access.GetStateScope
+import com.android.server.permission.access.MutableAccessState
+import com.android.server.permission.access.MutateStateScope
+import com.android.server.permission.access.SchemePolicy
+import com.android.server.permission.access.UidUri
+import com.android.server.permission.access.collection.* // ktlint-disable no-wildcard-imports
+import com.android.server.permission.access.immutable.* // ktlint-disable no-wildcard-imports
+import com.android.server.permission.access.util.andInv
+import com.android.server.pm.pkg.PackageState
+
+class DevicePermissionPolicy : SchemePolicy() {
+ private val persistence = DevicePermissionPersistence()
+
+ @Volatile
+ private var listeners: IndexedListSet<OnDevicePermissionFlagsChangedListener> =
+ MutableIndexedListSet()
+ private val listenersLock = Any()
+
+ override val subjectScheme: String
+ get() = UidUri.SCHEME
+
+ override val objectScheme: String
+ get() = DevicePermissionUri.SCHEME
+
+ override fun GetStateScope.onStateMutated() {
+ listeners.forEachIndexed { _, it -> it.onStateMutated() }
+ }
+
+ override fun MutateStateScope.onAppIdRemoved(appId: Int) {
+ newState.userStates.forEachIndexed { userStateIndex, _, userState ->
+ if (appId in userState.appIdDevicePermissionFlags) {
+ newState.mutateUserStateAt(userStateIndex)
+ .mutateAppIdDevicePermissionFlags() -= appId
+ }
+ }
+ }
+
+ override fun MutateStateScope.onStorageVolumeMounted(
+ volumeUuid: String?,
+ packageNames: List<String>,
+ isSystemUpdated: Boolean
+ ) {
+ packageNames.forEachIndexed { _, packageName ->
+ val packageState = newState.externalState.packageStates[packageName]!!
+ trimPermissionStates(packageState.appId)
+ }
+ }
+
+ override fun MutateStateScope.onPackageAdded(packageState: PackageState) {
+ trimPermissionStates(packageState.appId)
+ }
+
+ override fun MutateStateScope.onPackageRemoved(packageName: String, appId: Int) {
+ if (appId in newState.externalState.appIdPackageNames) {
+ trimPermissionStates(appId)
+ }
+ }
+
+ override fun MutateStateScope.onPackageUninstalled(
+ packageName: String,
+ appId: Int,
+ userId: Int
+ ) {
+ resetPermissionStates(packageName, userId)
+ }
+
+ private fun MutateStateScope.resetPermissionStates(packageName: String, userId: Int) {
+ // It's okay to skip resetting permissions for packages that are removed,
+ // because their states will be trimmed in onPackageRemoved()/onAppIdRemoved()
+ val packageState = newState.externalState.packageStates[packageName] ?: return
+ val androidPackage = packageState.androidPackage ?: return
+ val appId = packageState.appId
+ val appIdPermissionFlags = newState.userStates[userId]!!.appIdDevicePermissionFlags
+ androidPackage.requestedPermissions.forEach { permissionName ->
+ val isRequestedByOtherPackages = anyPackageInAppId(appId) {
+ it.packageName != packageName &&
+ permissionName in it.androidPackage!!.requestedPermissions
+ }
+ if (isRequestedByOtherPackages) {
+ return@forEach
+ }
+ appIdPermissionFlags[appId]?.forEachIndexed { _, deviceId, _ ->
+ setPermissionFlags(appId, deviceId, userId, permissionName, 0)
+ }
+ }
+ }
+
+ private fun MutateStateScope.trimPermissionStates(appId: Int) {
+ val requestedPermissions = MutableIndexedSet<String>()
+ forEachPackageInAppId(appId) {
+ requestedPermissions += it.androidPackage!!.requestedPermissions
+ }
+ newState.userStates.forEachIndexed { _, userId, userState ->
+ userState.appIdDevicePermissionFlags[appId]?.forEachReversedIndexed {
+ _, deviceId, permissionFlags ->
+ permissionFlags.forEachReversedIndexed { _, permissionName, _ ->
+ if (permissionName !in requestedPermissions) {
+ setPermissionFlags(appId, deviceId, userId, permissionName, 0)
+ }
+ }
+ }
+ }
+ }
+
+ private inline fun MutateStateScope.anyPackageInAppId(
+ appId: Int,
+ state: AccessState = newState,
+ predicate: (PackageState) -> Boolean
+ ): Boolean {
+ val packageNames = state.externalState.appIdPackageNames[appId]!!
+ return packageNames.anyIndexed { _, packageName ->
+ val packageState = state.externalState.packageStates[packageName]!!
+ packageState.androidPackage != null && predicate(packageState)
+ }
+ }
+
+ private inline fun MutateStateScope.forEachPackageInAppId(
+ appId: Int,
+ state: AccessState = newState,
+ action: (PackageState) -> Unit
+ ) {
+ val packageNames = state.externalState.appIdPackageNames[appId]!!
+ packageNames.forEachIndexed { _, packageName ->
+ val packageState = state.externalState.packageStates[packageName]!!
+ if (packageState.androidPackage != null) {
+ action(packageState)
+ }
+ }
+ }
+
+ override fun BinaryXmlPullParser.parseUserState(state: MutableAccessState, userId: Int) {
+ with(persistence) { this@parseUserState.parseUserState(state, userId) }
+ }
+
+ override fun BinaryXmlSerializer.serializeUserState(state: AccessState, userId: Int) {
+ with(persistence) { this@serializeUserState.serializeUserState(state, userId) }
+ }
+
+ fun GetStateScope.getPermissionFlags(
+ appId: Int,
+ deviceId: String,
+ userId: Int,
+ permissionName: String
+ ): Int =
+ state.userStates[userId]?.appIdDevicePermissionFlags?.get(appId)?.get(deviceId)
+ ?.getWithDefault(permissionName, 0) ?: 0
+
+ fun MutateStateScope.setPermissionFlags(
+ appId: Int,
+ deviceId: String,
+ userId: Int,
+ permissionName: String,
+ flags: Int
+ ): Boolean =
+ updatePermissionFlags(
+ appId, deviceId, userId, permissionName, PermissionFlags.MASK_ALL, flags
+ )
+
+ private fun MutateStateScope.updatePermissionFlags(
+ appId: Int,
+ deviceId: String,
+ userId: Int,
+ permissionName: String,
+ flagMask: Int,
+ flagValues: Int
+ ): Boolean {
+ if (!isDeviceAwarePermission(permissionName)) {
+ Slog.w(LOG_TAG, "$permissionName is not a device aware permission.")
+ return false
+ }
+ val oldFlags = newState.userStates[userId]!!.appIdDevicePermissionFlags[appId]
+ ?.get(deviceId).getWithDefault(permissionName, 0)
+ val newFlags = (oldFlags andInv flagMask) or (flagValues and flagMask)
+ if (oldFlags == newFlags) {
+ return false
+ }
+ val appIdDevicePermissionFlags =
+ newState.mutateUserState(userId)!!.mutateAppIdDevicePermissionFlags()
+ val devicePermissionFlags = appIdDevicePermissionFlags.mutateOrPut(appId) {
+ MutableIndexedReferenceMap()
+ }
+ val permissionFlags = devicePermissionFlags.mutateOrPut(deviceId) { MutableIndexedMap() }
+ permissionFlags.putWithDefault(permissionName, newFlags, 0)
+ if (permissionFlags.isEmpty()) {
+ devicePermissionFlags -= deviceId
+ if (devicePermissionFlags.isEmpty()) {
+ appIdDevicePermissionFlags -= appId
+ }
+ }
+ listeners.forEachIndexed { _, it ->
+ it.onDevicePermissionFlagsChanged(
+ appId, userId, deviceId, permissionName, oldFlags, newFlags
+ )
+ }
+ return true
+ }
+
+ fun addOnPermissionFlagsChangedListener(listener: OnDevicePermissionFlagsChangedListener) {
+ synchronized(listenersLock) {
+ listeners = listeners + listener
+ }
+ }
+
+ fun removeOnPermissionFlagsChangedListener(listener: OnDevicePermissionFlagsChangedListener) {
+ synchronized(listenersLock) {
+ listeners = listeners - listener
+ }
+ }
+
+ private fun isDeviceAwarePermission(permissionName: String): Boolean =
+ DEVICE_SUPPORTED_PERMISSIONS.contains(permissionName)
+
+ companion object {
+ private val LOG_TAG = DevicePermissionPolicy::class.java.simpleName
+
+ /**
+ * These permissions are supported for virtual devices.
+ */
+ private val DEVICE_SUPPORTED_PERMISSIONS = indexedSetOf(
+ android.Manifest.permission.CAMERA,
+ android.Manifest.permission.RECORD_AUDIO
+ )
+ }
+
+ /**
+ * TODO: b/289355341 - implement listener for permission changes
+ * Listener for permission flags changes.
+ */
+ abstract class OnDevicePermissionFlagsChangedListener {
+ /**
+ * Called when a permission flags change has been made to the upcoming new state.
+ *
+ * Implementations should keep this method fast to avoid stalling the locked state mutation,
+ * and only call external code after [onStateMutated] when the new state has actually become
+ * the current state visible to external code.
+ */
+ abstract fun onDevicePermissionFlagsChanged(
+ appId: Int,
+ userId: Int,
+ deviceId: String,
+ permissionName: String,
+ oldFlags: Int,
+ newFlags: Int
+ )
+
+ /**
+ * Called when the upcoming new state has become the current state.
+ *
+ * Implementations should keep this method fast to avoid stalling the locked state mutation.
+ */
+ abstract fun onStateMutated()
+ }
+} \ No newline at end of file
diff --git a/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt b/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt
index edacf188b333..f0705eda5c43 100644
--- a/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt
+++ b/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt
@@ -64,9 +64,11 @@ import com.android.server.LocalServices
import com.android.server.PermissionThread
import com.android.server.ServiceThread
import com.android.server.SystemConfig
+import com.android.server.companion.virtual.VirtualDeviceManagerInternal
import com.android.server.permission.access.AccessCheckingService
import com.android.server.permission.access.AccessState
import com.android.server.permission.access.AppOpUri
+import com.android.server.permission.access.DevicePermissionUri
import com.android.server.permission.access.GetStateScope
import com.android.server.permission.access.MutateStateScope
import com.android.server.permission.access.PermissionUri
@@ -110,6 +112,9 @@ class PermissionService(
private val policy =
service.getSchemePolicy(UidUri.SCHEME, PermissionUri.SCHEME) as AppIdPermissionPolicy
+ private val devicePolicy =
+ service.getSchemePolicy(UidUri.SCHEME, DevicePermissionUri.SCHEME) as DevicePermissionPolicy
+
private val context = service.context
private lateinit var metricsLogger: MetricsLogger
private lateinit var packageManagerInternal: PackageManagerInternal
@@ -130,6 +135,8 @@ class PermissionService(
@GuardedBy("storageVolumeLock")
private val storageVolumePackageNames = ArrayMap<String?, MutableList<String>>()
+ private var virtualDeviceManagerInternal: VirtualDeviceManagerInternal? = null
+
private lateinit var permissionControllerManager: PermissionControllerManager
/**
@@ -152,7 +159,6 @@ class PermissionService(
systemConfig = SystemConfig.getInstance()
userManagerInternal = LocalServices.getService(UserManagerInternal::class.java)
userManagerService = UserManagerService.getInstance()
-
// The package info cache is the cache for package and permission information.
// Disable the package info and package permission caches locally but leave the
// checkPermission cache active.
@@ -460,7 +466,7 @@ class PermissionService(
return size
}
- override fun checkUidPermission(uid: Int, permissionName: String): Int {
+ override fun checkUidPermission(uid: Int, permissionName: String, deviceId: Int): Int {
val userId = UserHandle.getUserId(uid)
if (!userManagerInternal.exists(userId)) {
return PackageManager.PERMISSION_DENIED
@@ -482,7 +488,7 @@ class PermissionService(
return PackageManager.PERMISSION_DENIED
}
val isPermissionGranted = service.getState {
- isPermissionGranted(packageState, userId, permissionName)
+ isPermissionGranted(packageState, userId, permissionName, deviceId)
}
return if (isPermissionGranted) {
PackageManager.PERMISSION_GRANTED
@@ -515,7 +521,12 @@ class PermissionService(
return false
}
- override fun checkPermission(packageName: String, permissionName: String, userId: Int): Int {
+ override fun checkPermission(
+ packageName: String,
+ permissionName: String,
+ deviceId: Int,
+ userId: Int
+ ): Int {
if (!userManagerInternal.exists(userId)) {
return PackageManager.PERMISSION_DENIED
}
@@ -524,7 +535,7 @@ class PermissionService(
.use { it.getPackageState(packageName) } ?: return PackageManager.PERMISSION_DENIED
val isPermissionGranted = service.getState {
- isPermissionGranted(packageState, userId, permissionName)
+ isPermissionGranted(packageState, userId, permissionName, deviceId)
}
return if (isPermissionGranted) {
PackageManager.PERMISSION_GRANTED
@@ -542,19 +553,21 @@ class PermissionService(
private fun GetStateScope.isPermissionGranted(
packageState: PackageState,
userId: Int,
- permissionName: String
+ permissionName: String,
+ deviceId: Int
): Boolean {
val appId = packageState.appId
// Note that instant apps can't have shared UIDs, so we only need to check the current
// package state.
val isInstantApp = packageState.getUserStateOrDefault(userId).isInstantApp
- if (isSinglePermissionGranted(appId, userId, isInstantApp, permissionName)) {
+ if (isSinglePermissionGranted(appId, userId, isInstantApp, permissionName, deviceId)) {
return true
}
val fullerPermissionName = FULLER_PERMISSIONS[permissionName]
if (fullerPermissionName != null &&
- isSinglePermissionGranted(appId, userId, isInstantApp, fullerPermissionName)) {
+ isSinglePermissionGranted(appId, userId, isInstantApp, fullerPermissionName, deviceId)
+ ) {
return true
}
@@ -568,9 +581,10 @@ class PermissionService(
appId: Int,
userId: Int,
isInstantApp: Boolean,
- permissionName: String
+ permissionName: String,
+ deviceId: Int,
): Boolean {
- val flags = with(policy) { getPermissionFlags(appId, userId, permissionName) }
+ val flags = getPermissionFlagsWithPolicy(appId, userId, permissionName, deviceId)
if (!PermissionFlags.isPermissionGranted(flags)) {
return false
}
@@ -601,7 +615,8 @@ class PermissionService(
?: return emptySet()
return permissionFlags.mapNotNullIndexedTo(ArraySet()) { _, permissionName, _ ->
- if (isPermissionGranted(packageState, userId, permissionName)) {
+ if (isPermissionGranted(
+ packageState, userId, permissionName, Context.DEVICE_ID_DEFAULT)) {
permissionName
} else {
null
@@ -640,18 +655,26 @@ class PermissionService(
}
}
- override fun grantRuntimePermission(packageName: String, permissionName: String, userId: Int) {
- setRuntimePermissionGranted(packageName, userId, permissionName, isGranted = true)
+ override fun grantRuntimePermission(
+ packageName: String,
+ permissionName: String,
+ deviceId: Int,
+ userId: Int
+ ) {
+ setRuntimePermissionGranted(
+ packageName, userId, permissionName, deviceId, isGranted = true
+ )
}
override fun revokeRuntimePermission(
packageName: String,
permissionName: String,
+ deviceId: Int,
userId: Int,
reason: String?
) {
setRuntimePermissionGranted(
- packageName, userId, permissionName, isGranted = false, revokeReason = reason
+ packageName, userId, permissionName, deviceId, isGranted = false, revokeReason = reason
)
}
@@ -660,8 +683,8 @@ class PermissionService(
userId: Int
) {
setRuntimePermissionGranted(
- packageName, userId, Manifest.permission.POST_NOTIFICATIONS, isGranted = false,
- skipKillUid = true
+ packageName, userId, Manifest.permission.POST_NOTIFICATIONS, Context.DEVICE_ID_DEFAULT,
+ isGranted = false, skipKillUid = true
)
}
@@ -673,6 +696,7 @@ class PermissionService(
packageName: String,
userId: Int,
permissionName: String,
+ deviceId: Int,
isGranted: Boolean,
skipKillUid: Boolean = false,
revokeReason: String? = null
@@ -748,7 +772,7 @@ class PermissionService(
}
setRuntimePermissionGranted(
- packageState, userId, permissionName, isGranted, canManageRolePermission,
+ packageState, userId, permissionName, deviceId, isGranted, canManageRolePermission,
overridePolicyFixed, reportError = true, methodName
)
}
@@ -782,14 +806,16 @@ class PermissionService(
if (permissionState ==
PackageInstaller.SessionParams.PERMISSION_STATE_GRANTED) {
setRuntimePermissionGranted(
- packageState, userId, permissionName, isGranted = true,
- canManageRolePermission = false, overridePolicyFixed = false,
- reportError = false, "setRequestedPermissionStates"
+ packageState, userId, permissionName, Context.DEVICE_ID_DEFAULT,
+ isGranted = true, canManageRolePermission = false,
+ overridePolicyFixed = false, reportError = false,
+ "setRequestedPermissionStates"
)
updatePermissionFlags(
packageState.appId, userId, permissionName,
+ Context.DEVICE_ID_DEFAULT,
PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED or
- PackageManager.FLAG_PERMISSION_REVOKED_COMPAT, 0,
+ PackageManager.FLAG_PERMISSION_REVOKED_COMPAT, 0,
canUpdateSystemFlags = false,
reportErrorForUnknownPermission = false,
isPermissionRequested = true, "setRequestedPermissionStates",
@@ -816,6 +842,7 @@ class PermissionService(
packageState: PackageState,
userId: Int,
permissionName: String,
+ deviceId: Int,
isGranted: Boolean,
canManageRolePermission: Boolean,
overridePolicyFixed: Boolean,
@@ -871,7 +898,7 @@ class PermissionService(
}
val appId = packageState.appId
- val oldFlags = with(policy) { getPermissionFlags(appId, userId, permissionName) }
+ val oldFlags = getPermissionFlagsWithPolicy(appId, userId, permissionName, deviceId)
if (permissionName !in androidPackage.requestedPermissions && oldFlags == 0) {
if (reportError) {
@@ -934,7 +961,7 @@ class PermissionService(
return
}
- with(policy) { setPermissionFlags(appId, userId, permissionName, newFlags) }
+ setPermissionFlagsWithPolicy(appId, userId, permissionName, deviceId, newFlags)
if (permission.isRuntime) {
val action = if (isGranted) {
@@ -958,12 +985,17 @@ class PermissionService(
) {
val appOpPolicy = service.getSchemePolicy(UidUri.SCHEME, AppOpUri.SCHEME) as
AppIdAppOpPolicy
- val appOpName = AppOpsManager.permissionToOp(permissionName)
+ val appOpName = checkNotNull(AppOpsManager.permissionToOp(permissionName))
val mode = if (isGranted) AppOpsManager.MODE_ALLOWED else AppOpsManager.MODE_ERRORED
with(appOpPolicy) { setAppOpMode(packageState.appId, userId, appOpName, mode) }
}
- override fun getPermissionFlags(packageName: String, permissionName: String, userId: Int): Int {
+ override fun getPermissionFlags(
+ packageName: String,
+ permissionName: String,
+ deviceId: Int,
+ userId: Int,
+ ): Int {
if (!userManagerInternal.exists(userId)) {
Slog.w(LOG_TAG, "getPermissionFlags: Unknown user $userId")
return 0
@@ -994,7 +1026,8 @@ class PermissionService(
}
val flags =
- with(policy) { getPermissionFlags(packageState.appId, userId, permissionName) }
+ getPermissionFlagsWithPolicy(packageState.appId, userId, permissionName, deviceId)
+
return PermissionFlags.toApiFlags(flags)
}
}
@@ -1002,6 +1035,7 @@ class PermissionService(
override fun isPermissionRevokedByPolicy(
packageName: String,
permissionName: String,
+ deviceId: Int,
userId: Int
): Boolean {
if (!userManagerInternal.exists(userId)) {
@@ -1018,13 +1052,13 @@ class PermissionService(
.use { it.getPackageState(packageName) } ?: return false
service.getState {
- if (isPermissionGranted(packageState, userId, permissionName)) {
+ if (isPermissionGranted(packageState, userId, permissionName, deviceId)) {
return false
}
- val flags = with(policy) {
- getPermissionFlags(packageState.appId, userId, permissionName)
- }
+ val flags =
+ getPermissionFlagsWithPolicy(packageState.appId, userId, permissionName, deviceId)
+
return flags.hasBits(PermissionFlags.POLICY_FIXED)
}
}
@@ -1046,7 +1080,8 @@ class PermissionService(
override fun shouldShowRequestPermissionRationale(
packageName: String,
permissionName: String,
- userId: Int
+ deviceId: Int,
+ userId: Int,
): Boolean {
if (!userManagerInternal.exists(userId)) {
Slog.w(LOG_TAG, "shouldShowRequestPermissionRationale: Unknown user $userId")
@@ -1068,11 +1103,11 @@ class PermissionService(
val flags: Int
service.getState {
- if (isPermissionGranted(packageState, userId, permissionName)) {
+ if (isPermissionGranted(packageState, userId, permissionName, deviceId)) {
return false
}
- flags = with(policy) { getPermissionFlags(appId, userId, permissionName) }
+ flags = getPermissionFlagsWithPolicy(appId, userId, permissionName, deviceId)
}
if (flags.hasAnyBit(UNREQUESTABLE_MASK)) {
return false
@@ -1104,6 +1139,7 @@ class PermissionService(
flagMask: Int,
flagValues: Int,
enforceAdjustPolicyPermission: Boolean,
+ deviceId: Int,
userId: Int
) {
val callingUid = Binder.getCallingUid()
@@ -1199,7 +1235,7 @@ class PermissionService(
val appId = packageState.appId
service.mutateState {
updatePermissionFlags(
- appId, userId, permissionName, flagMask, flagValues, canUpdateSystemFlags,
+ appId, userId, permissionName, deviceId, flagMask, flagValues, canUpdateSystemFlags,
reportErrorForUnknownPermission = true, isPermissionRequested,
"updatePermissionFlags", packageName
)
@@ -1248,8 +1284,9 @@ class PermissionService(
val androidPackage = packageState.androidPackage ?: return@forEach
androidPackage.requestedPermissions.forEach { permissionName ->
updatePermissionFlags(
- packageState.appId, userId, permissionName, flagMask, flagValues,
- canUpdateSystemFlags, reportErrorForUnknownPermission = false,
+ packageState.appId, userId, permissionName, Context.DEVICE_ID_DEFAULT,
+ flagMask, flagValues, canUpdateSystemFlags,
+ reportErrorForUnknownPermission = false,
isPermissionRequested = true, "updatePermissionFlagsForAllApps", packageName
)
}
@@ -1264,6 +1301,7 @@ class PermissionService(
appId: Int,
userId: Int,
permissionName: String,
+ deviceId: Int,
flagMask: Int,
flagValues: Int,
canUpdateSystemFlags: Boolean,
@@ -1298,7 +1336,7 @@ class PermissionService(
return
}
- val oldFlags = with(policy) { getPermissionFlags(appId, userId, permissionName) }
+ val oldFlags = getPermissionFlagsWithPolicy(appId, userId, permissionName, deviceId)
if (!isPermissionRequested && oldFlags == 0) {
Slog.w(
LOG_TAG, "$methodName: Permission $permissionName isn't requested by package" +
@@ -1308,7 +1346,7 @@ class PermissionService(
}
val newFlags = PermissionFlags.updateFlags(permission, oldFlags, flagMask, flagValues)
- with(policy) { setPermissionFlags(appId, userId, permissionName, newFlags) }
+ setPermissionFlagsWithPolicy(appId, userId, permissionName, deviceId, newFlags)
}
override fun getAllowlistedRestrictedPermissions(
@@ -1365,6 +1403,63 @@ class PermissionService(
)
}
+ private fun GetStateScope.getPermissionFlagsWithPolicy(
+ appId: Int,
+ userId: Int,
+ permissionName: String,
+ deviceId: Int,
+ ): Int {
+ return if (deviceId == Context.DEVICE_ID_DEFAULT) {
+ with(policy) { getPermissionFlags(appId, userId, permissionName) }
+ } else {
+ val virtualDeviceManagerInternal = virtualDeviceManagerInternal
+ if (virtualDeviceManagerInternal == null) {
+ Slog.e(LOG_TAG, "Virtual device manager service is not available.")
+ return 0
+ }
+ val persistentDeviceId =
+ virtualDeviceManagerInternal.getPersistentIdForDevice(deviceId)
+ if (persistentDeviceId != null) {
+ with(devicePolicy) {
+ getPermissionFlags(appId, persistentDeviceId, userId, permissionName)
+ }
+ } else {
+ Slog.e(LOG_TAG, "Invalid device ID $deviceId.")
+ 0
+ }
+ }
+ }
+
+ private fun MutateStateScope.setPermissionFlagsWithPolicy(
+ appId: Int,
+ userId: Int,
+ permissionName: String,
+ deviceId: Int,
+ flags: Int
+ ): Boolean {
+ return if (deviceId == Context.DEVICE_ID_DEFAULT) {
+ with(policy) {
+ setPermissionFlags(appId, userId, permissionName, flags)
+ }
+ } else {
+ val virtualDeviceManagerInternal = virtualDeviceManagerInternal
+ if (virtualDeviceManagerInternal == null) {
+ Slog.e(LOG_TAG, "Virtual device manager service is not available.")
+ return false
+ }
+ val persistentDeviceId =
+ virtualDeviceManagerInternal.getPersistentIdForDevice(deviceId)
+ if (persistentDeviceId != null) {
+ with(devicePolicy) {
+ setPermissionFlags(appId, persistentDeviceId, userId, permissionName, flags)
+ }
+ } else {
+ Slog.e(LOG_TAG, "Invalid device ID $deviceId.")
+ false
+ }
+ }
+ }
+
/**
* This method does not enforce checks on the caller, should only be called after
* required checks.
@@ -1539,8 +1634,7 @@ class PermissionService(
) {
service.mutateState {
with(policy) {
- val permissionsFlags =
- getUidPermissionFlags(appId, userId) ?: return@mutateState
+ val permissionsFlags = getUidPermissionFlags(appId, userId) ?: return@mutateState
val permissions = getPermissions()
androidPackage.requestedPermissions.forEachIndexed { _, requestedPermission ->
@@ -1661,8 +1755,6 @@ class PermissionService(
)
}
-
-
override fun getAppOpPermissionPackages(permissionName: String): Array<String> {
requireNotNull(permissionName) { "permissionName cannot be null" }
val packageNames = ArraySet<String>()
@@ -1879,7 +1971,7 @@ class PermissionService(
println("Permissions:")
withIndent {
userState.appIdPermissionFlags[appId]?.forEachIndexed {
- _, permissionName, flags ->
+ _, permissionName, flags ->
val isGranted = PermissionFlags.isPermissionGranted(flags)
println(
"$permissionName: granted=$isGranted, flags=" +
@@ -1888,6 +1980,20 @@ class PermissionService(
}
}
+ userState.appIdDevicePermissionFlags[appId]?.forEachIndexed {
+ _, deviceId, devicePermissionFlags ->
+ println("Permissions (Device $deviceId):")
+ withIndent {
+ devicePermissionFlags.forEachIndexed { _, permissionName, flags ->
+ val isGranted = PermissionFlags.isPermissionGranted(flags)
+ println(
+ "$permissionName: granted=$isGranted, flags=" +
+ PermissionFlags.toString(flags)
+ )
+ }
+ }
+ }
+
println("App ops:")
withIndent {
userState.appIdAppOpModes[appId]?.forEachIndexed {_, appOpName, appOpMode ->
@@ -2003,6 +2109,8 @@ class PermissionService(
override fun onSystemReady() {
service.onSystemReady()
+ virtualDeviceManagerInternal =
+ LocalServices.getService(VirtualDeviceManagerInternal::class.java)
permissionControllerManager = PermissionControllerManager(
context, PermissionThread.getHandler()
)
@@ -2412,7 +2520,7 @@ class PermissionService(
}
private fun isAppBackupAndRestoreRunning(uid: Int): Boolean {
- if (checkUidPermission(uid, Manifest.permission.BACKUP) !=
+ if (checkUidPermission(uid, Manifest.permission.BACKUP, Context.DEVICE_ID_DEFAULT) !=
PackageManager.PERMISSION_GRANTED) {
return false
}
diff --git a/services/tests/PackageManagerServiceTests/appenumeration/src/com/android/server/pm/test/appenumeration/CrossUserPackageVisibilityTests.java b/services/tests/PackageManagerServiceTests/appenumeration/src/com/android/server/pm/test/appenumeration/CrossUserPackageVisibilityTests.java
index b7a0cf389396..e33ca7775e22 100644
--- a/services/tests/PackageManagerServiceTests/appenumeration/src/com/android/server/pm/test/appenumeration/CrossUserPackageVisibilityTests.java
+++ b/services/tests/PackageManagerServiceTests/appenumeration/src/com/android/server/pm/test/appenumeration/CrossUserPackageVisibilityTests.java
@@ -138,14 +138,6 @@ public class CrossUserPackageVisibilityTests {
}
@Test
- public void testGetUserMinAspectRatio_withCrossUserId() {
- final int crossUserId = UserHandle.myUserId() + 1;
- assertThrows(SecurityException.class,
- () -> mIPackageManager.getUserMinAspectRatio(
- mInstrumentation.getContext().getPackageName(), crossUserId));
- }
-
- @Test
public void testIsPackageSignedByKeySet_cannotDetectCrossUserPkg() throws Exception {
final KeySet keySet = mIPackageManager.getSigningKeySet(mContext.getPackageName());
assertThrows(IllegalArgumentException.class,
diff --git a/services/tests/RemoteProvisioningServiceTests/src/com/android/server/security/rkp/RemoteProvisioningShellCommandTest.java b/services/tests/RemoteProvisioningServiceTests/src/com/android/server/security/rkp/RemoteProvisioningShellCommandTest.java
index 2d93120681ec..007c0db1b731 100644
--- a/services/tests/RemoteProvisioningServiceTests/src/com/android/server/security/rkp/RemoteProvisioningShellCommandTest.java
+++ b/services/tests/RemoteProvisioningServiceTests/src/com/android/server/security/rkp/RemoteProvisioningShellCommandTest.java
@@ -21,12 +21,14 @@ import static com.google.common.truth.Truth8.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.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.content.Context;
import android.hardware.security.keymint.DeviceInfo;
import android.hardware.security.keymint.IRemotelyProvisionedComponent;
import android.hardware.security.keymint.MacedPublicKey;
@@ -34,28 +36,35 @@ import android.hardware.security.keymint.ProtectedData;
import android.hardware.security.keymint.RpcHardwareInfo;
import android.os.Binder;
import android.os.FileUtils;
+import android.os.OutcomeReceiver;
+import android.os.Process;
+import android.security.rkp.service.RegistrationProxy;
+import android.security.rkp.service.RemotelyProvisionedKey;
+import androidx.test.core.app.ApplicationProvider;
import androidx.test.runner.AndroidJUnit4;
+import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
+import java.util.Arrays;
import java.util.Base64;
import java.util.Map;
+import java.util.concurrent.Executor;
@RunWith(AndroidJUnit4.class)
public class RemoteProvisioningShellCommandTest {
- private static class Injector extends RemoteProvisioningShellCommand.Injector {
+ private Context mContext;
- private final Map<String, IRemotelyProvisionedComponent> mIrpcs;
+ private static class Injector extends RemoteProvisioningShellCommand.Injector {
- Injector(Map irpcs) {
- mIrpcs = irpcs;
- }
+ Map<String, IRemotelyProvisionedComponent> mIrpcs;
+ Map<String, RegistrationProxy> mRegistrationProxies;
@Override
String[] getIrpcNames() {
@@ -70,6 +79,12 @@ public class RemoteProvisioningShellCommandTest {
}
return irpc;
}
+
+ @Override
+ RegistrationProxy getRegistrationProxy(
+ Context context, int callerUid, String name, Executor executor) {
+ return mRegistrationProxies.get(name);
+ }
}
private static class CommandResult {
@@ -111,10 +126,17 @@ public class RemoteProvisioningShellCommandTest {
code, FileUtils.readTextFile(out, 0, null), FileUtils.readTextFile(err, 0, null));
}
+ @Before
+ public void setUp() {
+ mContext = ApplicationProvider.getApplicationContext();
+ }
+
@Test
public void list_zeroInstances() throws Exception {
+ Injector injector = new Injector();
+ injector.mIrpcs = Map.of();
RemoteProvisioningShellCommand cmd = new RemoteProvisioningShellCommand(
- new Injector(Map.of()));
+ mContext, Process.SHELL_UID, injector);
CommandResult res = exec(cmd, new String[] {"list"});
assertThat(res.getErr()).isEmpty();
assertThat(res.getCode()).isEqualTo(0);
@@ -124,8 +146,10 @@ public class RemoteProvisioningShellCommandTest {
@Test
public void list_oneInstances() throws Exception {
+ Injector injector = new Injector();
+ injector.mIrpcs = Map.of("default", mock(IRemotelyProvisionedComponent.class));
RemoteProvisioningShellCommand cmd = new RemoteProvisioningShellCommand(
- new Injector(Map.of("default", mock(IRemotelyProvisionedComponent.class))));
+ mContext, Process.SHELL_UID, injector);
CommandResult res = exec(cmd, new String[] {"list"});
assertThat(res.getErr()).isEmpty();
assertThat(res.getCode()).isEqualTo(0);
@@ -134,10 +158,12 @@ public class RemoteProvisioningShellCommandTest {
@Test
public void list_twoInstances() throws Exception {
+ Injector injector = new Injector();
+ injector.mIrpcs = Map.of(
+ "default", mock(IRemotelyProvisionedComponent.class),
+ "strongbox", mock(IRemotelyProvisionedComponent.class));
RemoteProvisioningShellCommand cmd = new RemoteProvisioningShellCommand(
- new Injector(Map.of(
- "default", mock(IRemotelyProvisionedComponent.class),
- "strongbox", mock(IRemotelyProvisionedComponent.class))));
+ mContext, Process.SHELL_UID, injector);
CommandResult res = exec(cmd, new String[] {"list"});
assertThat(res.getErr()).isEmpty();
assertThat(res.getCode()).isEqualTo(0);
@@ -158,8 +184,10 @@ public class RemoteProvisioningShellCommandTest {
}).when(defaultMock).generateCertificateRequest(
anyBoolean(), any(), any(), any(), any(), any());
+ Injector injector = new Injector();
+ injector.mIrpcs = Map.of("default", defaultMock);
RemoteProvisioningShellCommand cmd = new RemoteProvisioningShellCommand(
- new Injector(Map.of("default", defaultMock)));
+ mContext, Process.SHELL_UID, injector);
CommandResult res = exec(cmd, new String[] {
"csr", "--challenge", "dGVzdHRlc3R0ZXN0dGVzdA==", "default"});
verify(defaultMock).generateCertificateRequest(
@@ -189,8 +217,10 @@ public class RemoteProvisioningShellCommandTest {
}).when(defaultMock).generateCertificateRequest(
anyBoolean(), any(), any(), any(), any(), any());
+ Injector injector = new Injector();
+ injector.mIrpcs = Map.of("default", defaultMock);
RemoteProvisioningShellCommand cmd = new RemoteProvisioningShellCommand(
- new Injector(Map.of("default", defaultMock)));
+ mContext, Process.SHELL_UID, injector);
CommandResult res = exec(cmd, new String[] {
"csr", "--challenge", "dGVzdHRlc3R0ZXN0dGVzdA==", "default"});
verify(defaultMock).generateCertificateRequest(
@@ -215,8 +245,10 @@ public class RemoteProvisioningShellCommandTest {
when(defaultMock.generateCertificateRequestV2(any(), any()))
.thenReturn(new byte[] {0x68, 0x65, 0x6c, 0x6c, 0x6f});
+ Injector injector = new Injector();
+ injector.mIrpcs = Map.of("default", defaultMock);
RemoteProvisioningShellCommand cmd = new RemoteProvisioningShellCommand(
- new Injector(Map.of("default", defaultMock)));
+ mContext, Process.SHELL_UID, injector);
CommandResult res = exec(cmd, new String[] {"csr", "default"});
verify(defaultMock).generateCertificateRequestV2(new MacedPublicKey[0], new byte[0]);
assertThat(res.getErr()).isEmpty();
@@ -233,8 +265,10 @@ public class RemoteProvisioningShellCommandTest {
when(defaultMock.generateCertificateRequestV2(any(), any()))
.thenReturn(new byte[] {0x68, 0x69});
+ Injector injector = new Injector();
+ injector.mIrpcs = Map.of("default", defaultMock);
RemoteProvisioningShellCommand cmd = new RemoteProvisioningShellCommand(
- new Injector(Map.of("default", defaultMock)));
+ mContext, Process.SHELL_UID, injector);
CommandResult res = exec(cmd, new String[] {"csr", "--challenge", "dHJpYWw=", "default"});
verify(defaultMock).generateCertificateRequestV2(
new MacedPublicKey[0], new byte[] {0x74, 0x72, 0x69, 0x61, 0x6c});
@@ -242,4 +276,82 @@ public class RemoteProvisioningShellCommandTest {
assertThat(res.getCode()).isEqualTo(0);
assertThat(res.getOut()).isEqualTo("aGk=\n");
}
+
+ @Test
+ public void certify_sameOrderAsReceived() throws Exception {
+ String cert1 = "MIIBqDCCAU2gAwIBAgIUI3FFU7xZno/2Xf/wZzKKquP0ov0wCgYIKoZIzj0EAwIw\n"
+ + "KTELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMQ0wCwYDVQQKDARUZXN0MB4XDTIz\n"
+ + "MDgyMjE5MzgxMFoXDTMzMDgxOTE5MzgxMFowKTELMAkGA1UEBhMCVVMxCzAJBgNV\n"
+ + "BAgMAkNBMQ0wCwYDVQQKDARUZXN0MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE\n"
+ + "czOpG6NKOdDjV/yrKjuy0q0jEJvsVLGgTeY+vyKRBJS59OhyRWG6n3aza21bNg5d\n"
+ + "WE9ruz+bcT0IP4kDbiS0y6NTMFEwHQYDVR0OBBYEFHYfJxCUipNI7qRqvczcWsOb\n"
+ + "FIDPMB8GA1UdIwQYMBaAFHYfJxCUipNI7qRqvczcWsObFIDPMA8GA1UdEwEB/wQF\n"
+ + "MAMBAf8wCgYIKoZIzj0EAwIDSQAwRgIhAKm/kpJwlnWkjoLCAddBiSnxbT4EfJIK\n"
+ + "H0j58tg5VazHAiEAnS/kRzU9AbstOZyD7el/ws3gLXkbUNey3pLFutBWsSU=\n";
+ String cert2 = "MIIBpjCCAU2gAwIBAgIUdSzfZzeGr+h70JPO7Sxwdkw99iMwCgYIKoZIzj0EAwIw\n"
+ + "KTELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMQ0wCwYDVQQKDARUZXN0MB4XDTIz\n"
+ + "MDgyMjIwMTcyMFoXDTMzMDgxOTIwMTcyMFowKTELMAkGA1UEBhMCVVMxCzAJBgNV\n"
+ + "BAgMAkNBMQ0wCwYDVQQKDARUZXN0MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE\n"
+ + "voGJi4DxuqH8rzPV6Eq0OVULc0xFzaM0500VBqiQEB7Qt0Ktk2d+3bUrFAb3SZV4\n"
+ + "6TIdb7SkynvaDtr0x45Ng6NTMFEwHQYDVR0OBBYEFMeGjvGV0ADPBJk5/FPoW9HQ\n"
+ + "uTc6MB8GA1UdIwQYMBaAFMeGjvGV0ADPBJk5/FPoW9HQuTc6MA8GA1UdEwEB/wQF\n"
+ + "MAMBAf8wCgYIKoZIzj0EAwIDRwAwRAIgd1gu7iiNOQXaQUn5BT3WwWR0Yk78ndWt\n"
+ + "ew7tRiTOhFcCIFURi6WcNH0oWa6IbwBSMC9aZlo98Fbg+dTwhLAAw+PW\n";
+ byte[] cert1Bytes = Base64.getDecoder().decode(cert1.replaceAll("\\s+", ""));
+ byte[] cert2Bytes = Base64.getDecoder().decode(cert2.replaceAll("\\s+", ""));
+ byte[] certChain = Arrays.copyOf(cert1Bytes, cert1Bytes.length + cert2Bytes.length);
+ System.arraycopy(cert2Bytes, 0, certChain, cert1Bytes.length, cert2Bytes.length);
+ RemotelyProvisionedKey keyMock = mock(RemotelyProvisionedKey.class);
+ when(keyMock.getEncodedCertChain()).thenReturn(certChain);
+ RegistrationProxy defaultMock = mock(RegistrationProxy.class);
+ doAnswer(invocation -> {
+ ((OutcomeReceiver<RemotelyProvisionedKey, Exception>) invocation.getArgument(3))
+ .onResult(keyMock);
+ return null;
+ }).when(defaultMock).getKeyAsync(anyInt(), any(), any(), any());
+
+ Injector injector = new Injector();
+ injector.mRegistrationProxies = Map.of("default", defaultMock);
+ RemoteProvisioningShellCommand cmd = new RemoteProvisioningShellCommand(
+ mContext, Process.SHELL_UID, injector);
+ CommandResult res = exec(cmd, new String[] {"certify", "default"});
+ assertThat(res.getErr()).isEmpty();
+ assertThat(res.getCode()).isEqualTo(0);
+ assertThat(res.getOut()).isEqualTo(
+ "-----BEGIN CERTIFICATE-----\n" + cert1 + "-----END CERTIFICATE-----\n"
+ + "-----BEGIN CERTIFICATE-----\n" + cert2 + "-----END CERTIFICATE-----\n");
+ }
+
+ @Test
+ public void certify_noBlankLineBeforeTrailer() throws Exception {
+ String cert = "MIIB2zCCAYGgAwIBAgIRAOpN7Em1k7gaqLAB2dzXUTYwCgYIKoZIzj0EAwIwKTET\n"
+ + "MBEGA1UEChMKR29vZ2xlIExMQzESMBAGA1UEAxMJRHJvaWQgQ0EzMB4XDTIzMDgx\n"
+ + "ODIzMzI1MloXDTIzMDkyMTIzMzI1MlowOTEMMAoGA1UEChMDVEVFMSkwJwYDVQQD\n"
+ + "EyBlYTRkZWM0OWI1OTNiODFhYThiMDAxZDlkY2Q3NTEzNjBZMBMGByqGSM49AgEG\n"
+ + "CCqGSM49AwEHA0IABHM/cKZblmlw8bdGbDXnX+ZiLiGjSjaLHXYOoHDrVArAMXUi\n"
+ + "L6brhcUPaqSGcVLcfFZbaFMOxXW6TsGdQiwJ0iyjejB4MB0GA1UdDgQWBBTYzft+\n"
+ + "X32TH/Hh+ngwQF6aPhnfXDAfBgNVHSMEGDAWgBQT4JObI9mzNNW2FRsHRcw4zVn2\n"
+ + "8jAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwICBDAVBgorBgEEAdZ5AgEe\n"
+ + "BAehARoABAAAMAoGCCqGSM49BAMCA0gAMEUCIDc0OR7CzIYw0myTr0y/Brl1nZyk\n"
+ + "eGSQp615WpTwYhwxAiEApM10gSIKBIo7Z4/FNzkuiz1zZwW9+Dcqisqxkfe6icQ=\n";
+ byte[] certBytes = Base64.getDecoder().decode(cert.replaceAll("\\s+", ""));
+ RemotelyProvisionedKey keyMock = mock(RemotelyProvisionedKey.class);
+ when(keyMock.getEncodedCertChain()).thenReturn(certBytes);
+ RegistrationProxy defaultMock = mock(RegistrationProxy.class);
+ doAnswer(invocation -> {
+ ((OutcomeReceiver<RemotelyProvisionedKey, Exception>) invocation.getArgument(3))
+ .onResult(keyMock);
+ return null;
+ }).when(defaultMock).getKeyAsync(anyInt(), any(), any(), any());
+
+ Injector injector = new Injector();
+ injector.mRegistrationProxies = Map.of("strongbox", defaultMock);
+ RemoteProvisioningShellCommand cmd = new RemoteProvisioningShellCommand(
+ mContext, Process.SHELL_UID, injector);
+ CommandResult res = exec(cmd, new String[] {"certify", "strongbox"});
+ assertThat(res.getErr()).isEmpty();
+ assertThat(res.getCode()).isEqualTo(0);
+ assertThat(res.getOut()).isEqualTo(
+ "-----BEGIN CERTIFICATE-----\n" + cert + "-----END CERTIFICATE-----\n");
+ }
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/AutomaticBrightnessControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
index a6acd60f3bd7..d16c37a4c07a 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
@@ -63,8 +63,10 @@ public class AutomaticBrightnessControllerTest {
private static final float BRIGHTNESS_MAX_FLOAT = 1.0f;
private static final int LIGHT_SENSOR_RATE = 20;
private static final int INITIAL_LIGHT_SENSOR_RATE = 20;
- private static final int BRIGHTENING_LIGHT_DEBOUNCE_CONFIG = 0;
- private static final int DARKENING_LIGHT_DEBOUNCE_CONFIG = 0;
+ private static final int BRIGHTENING_LIGHT_DEBOUNCE_CONFIG = 2000;
+ private static final int DARKENING_LIGHT_DEBOUNCE_CONFIG = 4000;
+ private static final int BRIGHTENING_LIGHT_DEBOUNCE_CONFIG_IDLE = 1000;
+ private static final int DARKENING_LIGHT_DEBOUNCE_CONFIG_IDLE = 2000;
private static final float DOZE_SCALE_FACTOR = 0.0f;
private static final boolean RESET_AMBIENT_LUX_AFTER_WARMUP_CONFIG = false;
private static final int LIGHT_SENSOR_WARMUP_TIME = 0;
@@ -96,8 +98,9 @@ public class AutomaticBrightnessControllerTest {
mLightSensor = TestUtils.createSensor(Sensor.TYPE_LIGHT, "Light Sensor");
mContext = InstrumentationRegistry.getContext();
- mController = setupController(mLightSensor, BrightnessMappingStrategy.NO_USER_LUX,
- BrightnessMappingStrategy.NO_USER_BRIGHTNESS);
+ setupController(BrightnessMappingStrategy.NO_USER_LUX,
+ BrightnessMappingStrategy.NO_USER_BRIGHTNESS, /* applyDebounce= */ false,
+ /* useHorizon= */ true);
}
@After
@@ -109,12 +112,12 @@ public class AutomaticBrightnessControllerTest {
}
}
- private AutomaticBrightnessController setupController(Sensor lightSensor, float userLux,
- float userBrightness) {
+ private void setupController(float userLux, float userBrightness, boolean applyDebounce,
+ boolean useHorizon) {
mClock = new OffsettableClock.Stopped();
mTestLooper = new TestLooper(mClock::now);
- AutomaticBrightnessController controller = new AutomaticBrightnessController(
+ mController = new AutomaticBrightnessController(
new AutomaticBrightnessController.Injector() {
@Override
public Handler getBackgroundThreadHandler() {
@@ -127,16 +130,19 @@ public class AutomaticBrightnessControllerTest {
}
}, // pass in test looper instead, pass in offsettable clock
- () -> { }, mTestLooper.getLooper(), mSensorManager, lightSensor,
+ () -> { }, mTestLooper.getLooper(), mSensorManager, mLightSensor,
mBrightnessMappingStrategy, LIGHT_SENSOR_WARMUP_TIME, BRIGHTNESS_MIN_FLOAT,
BRIGHTNESS_MAX_FLOAT, DOZE_SCALE_FACTOR, LIGHT_SENSOR_RATE,
- INITIAL_LIGHT_SENSOR_RATE, BRIGHTENING_LIGHT_DEBOUNCE_CONFIG,
- DARKENING_LIGHT_DEBOUNCE_CONFIG, RESET_AMBIENT_LUX_AFTER_WARMUP_CONFIG,
+ INITIAL_LIGHT_SENSOR_RATE, applyDebounce ? BRIGHTENING_LIGHT_DEBOUNCE_CONFIG : 0,
+ applyDebounce ? DARKENING_LIGHT_DEBOUNCE_CONFIG : 0,
+ applyDebounce ? BRIGHTENING_LIGHT_DEBOUNCE_CONFIG_IDLE : 0,
+ applyDebounce ? DARKENING_LIGHT_DEBOUNCE_CONFIG_IDLE : 0,
+ RESET_AMBIENT_LUX_AFTER_WARMUP_CONFIG,
mAmbientBrightnessThresholds, mScreenBrightnessThresholds,
mAmbientBrightnessThresholdsIdle, mScreenBrightnessThresholdsIdle,
mContext, mBrightnessRangeController, mBrightnessThrottler,
- mIdleBrightnessMappingStrategy, AMBIENT_LIGHT_HORIZON_SHORT,
- AMBIENT_LIGHT_HORIZON_LONG, userLux, userBrightness
+ mIdleBrightnessMappingStrategy, useHorizon ? AMBIENT_LIGHT_HORIZON_SHORT : 1,
+ useHorizon ? AMBIENT_LIGHT_HORIZON_LONG : 10000, userLux, userBrightness
);
when(mBrightnessRangeController.getCurrentBrightnessMax()).thenReturn(
@@ -149,12 +155,10 @@ public class AutomaticBrightnessControllerTest {
// Configure the brightness controller and grab an instance of the sensor listener,
// through which we can deliver fake (for test) sensor values.
- controller.configure(AUTO_BRIGHTNESS_ENABLED, null /* configuration= */,
+ mController.configure(AUTO_BRIGHTNESS_ENABLED, null /* configuration= */,
0 /* brightness= */, false /* userChangedBrightness= */, 0 /* adjustment= */,
false /* userChanged= */, DisplayPowerRequest.POLICY_BRIGHT,
/* shouldResetShortTermModel= */ true);
-
- return controller;
}
@Test
@@ -536,8 +540,10 @@ public class AutomaticBrightnessControllerTest {
// Now let's do the same for idle mode
mController.switchToIdleMode();
// Called once for init, and once when switching,
- // setAmbientLux() is called twice and once in updateAutoBrightness()
- verify(mBrightnessMappingStrategy, times(5)).isForIdleMode();
+ // setAmbientLux() is called twice and once in updateAutoBrightness(),
+ // nextAmbientLightBrighteningTransition() and nextAmbientLightDarkeningTransition() are
+ // called twice each.
+ verify(mBrightnessMappingStrategy, times(9)).isForIdleMode();
// Called when switching.
verify(mBrightnessMappingStrategy, times(1)).getShortTermModelTimeout();
verify(mBrightnessMappingStrategy, times(1)).getUserBrightness();
@@ -838,7 +844,158 @@ public class AutomaticBrightnessControllerTest {
float userLux = 1000;
float userBrightness = 0.3f;
- setupController(mLightSensor, userLux, userBrightness);
+ setupController(userLux, userBrightness, /* applyDebounce= */ true,
+ /* useHorizon= */ false);
verify(mBrightnessMappingStrategy).addUserDataPoint(userLux, userBrightness);
}
+
+ @Test
+ public void testBrighteningLightDebounce() throws Exception {
+ clearInvocations(mSensorManager);
+ setupController(BrightnessMappingStrategy.NO_USER_LUX,
+ BrightnessMappingStrategy.NO_USER_BRIGHTNESS, /* applyDebounce= */ true,
+ /* useHorizon= */ false);
+
+ ArgumentCaptor<SensorEventListener> listenerCaptor =
+ ArgumentCaptor.forClass(SensorEventListener.class);
+ verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(mLightSensor),
+ eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class));
+ SensorEventListener listener = listenerCaptor.getValue();
+
+ // t = 0
+ // Initial lux
+ listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 500));
+ assertEquals(500, mController.getAmbientLux(), EPSILON);
+
+ // t = 1000
+ // Lux isn't steady yet
+ mClock.fastForward(1000);
+ listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 1200));
+ assertEquals(500, mController.getAmbientLux(), EPSILON);
+
+ // t = 1500
+ // Lux isn't steady yet
+ mClock.fastForward(500);
+ listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 1200));
+ assertEquals(500, mController.getAmbientLux(), EPSILON);
+
+ // t = 2500
+ // Lux is steady now
+ mClock.fastForward(1000);
+ listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 1200));
+ assertEquals(1200, mController.getAmbientLux(), EPSILON);
+ }
+
+ @Test
+ public void testDarkeningLightDebounce() throws Exception {
+ clearInvocations(mSensorManager);
+ when(mAmbientBrightnessThresholds.getBrighteningThreshold(anyFloat()))
+ .thenReturn(10000f);
+ when(mAmbientBrightnessThresholds.getDarkeningThreshold(anyFloat()))
+ .thenReturn(10000f);
+ setupController(BrightnessMappingStrategy.NO_USER_LUX,
+ BrightnessMappingStrategy.NO_USER_BRIGHTNESS, /* applyDebounce= */ true,
+ /* useHorizon= */ false);
+
+ ArgumentCaptor<SensorEventListener> listenerCaptor =
+ ArgumentCaptor.forClass(SensorEventListener.class);
+ verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(mLightSensor),
+ eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class));
+ SensorEventListener listener = listenerCaptor.getValue();
+
+ // t = 0
+ // Initial lux
+ listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 1200));
+ assertEquals(1200, mController.getAmbientLux(), EPSILON);
+
+ // t = 2000
+ // Lux isn't steady yet
+ mClock.fastForward(2000);
+ listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 500));
+ assertEquals(1200, mController.getAmbientLux(), EPSILON);
+
+ // t = 2500
+ // Lux isn't steady yet
+ mClock.fastForward(500);
+ listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 500));
+ assertEquals(1200, mController.getAmbientLux(), EPSILON);
+
+ // t = 4500
+ // Lux is steady now
+ mClock.fastForward(2000);
+ listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 500));
+ assertEquals(500, mController.getAmbientLux(), EPSILON);
+ }
+
+ @Test
+ public void testBrighteningLightDebounceIdle() throws Exception {
+ clearInvocations(mSensorManager);
+ setupController(BrightnessMappingStrategy.NO_USER_LUX,
+ BrightnessMappingStrategy.NO_USER_BRIGHTNESS, /* applyDebounce= */ true,
+ /* useHorizon= */ false);
+
+ mController.switchToIdleMode();
+ when(mIdleBrightnessMappingStrategy.isForIdleMode()).thenReturn(true);
+
+ ArgumentCaptor<SensorEventListener> listenerCaptor =
+ ArgumentCaptor.forClass(SensorEventListener.class);
+ verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(mLightSensor),
+ eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class));
+ SensorEventListener listener = listenerCaptor.getValue();
+
+ // t = 0
+ // Initial lux
+ listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 500));
+ assertEquals(500, mController.getAmbientLux(), EPSILON);
+
+ // t = 500
+ // Lux isn't steady yet
+ mClock.fastForward(500);
+ listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 1200));
+ assertEquals(500, mController.getAmbientLux(), EPSILON);
+
+ // t = 1500
+ // Lux is steady now
+ mClock.fastForward(1000);
+ listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 1200));
+ assertEquals(1200, mController.getAmbientLux(), EPSILON);
+ }
+
+ @Test
+ public void testDarkeningLightDebounceIdle() throws Exception {
+ clearInvocations(mSensorManager);
+ when(mAmbientBrightnessThresholdsIdle.getBrighteningThreshold(anyFloat()))
+ .thenReturn(10000f);
+ when(mAmbientBrightnessThresholdsIdle.getDarkeningThreshold(anyFloat()))
+ .thenReturn(10000f);
+ setupController(BrightnessMappingStrategy.NO_USER_LUX,
+ BrightnessMappingStrategy.NO_USER_BRIGHTNESS, /* applyDebounce= */ true,
+ /* useHorizon= */ false);
+
+ mController.switchToIdleMode();
+ when(mIdleBrightnessMappingStrategy.isForIdleMode()).thenReturn(true);
+
+ ArgumentCaptor<SensorEventListener> listenerCaptor =
+ ArgumentCaptor.forClass(SensorEventListener.class);
+ verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(mLightSensor),
+ eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class));
+ SensorEventListener listener = listenerCaptor.getValue();
+
+ // t = 0
+ // Initial lux
+ listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 1200));
+ assertEquals(1200, mController.getAmbientLux(), EPSILON);
+
+ // t = 1000
+ // Lux isn't steady yet
+ mClock.fastForward(1000);
+ listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 500));
+ assertEquals(1200, mController.getAmbientLux(), EPSILON);
+
+ // t = 2500
+ // Lux is steady now
+ mClock.fastForward(1500);
+ listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 500));
+ assertEquals(500, mController.getAmbientLux(), EPSILON);
+ }
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/ColorFadeTest.java b/services/tests/displayservicetests/src/com/android/server/display/ColorFadeTest.java
index 53d8de0c2bbb..3c3ef9ae166e 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/ColorFadeTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/ColorFadeTest.java
@@ -25,12 +25,9 @@ import static org.mockito.Mockito.when;
import android.content.Context;
import android.hardware.display.DisplayManagerInternal;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-import com.android.server.LocalServices;
-
-import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -49,25 +46,13 @@ public class ColorFadeTest {
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- addLocalServiceMock(DisplayManagerInternal.class, mDisplayManagerInternalMock);
mContext = getInstrumentation().getTargetContext();
}
- @After
- public void tearDown() {
- LocalServices.removeServiceForTest(DisplayManagerInternal.class);
- }
-
@Test
public void testPrepareColorFadeForInvalidDisplay() {
when(mDisplayManagerInternalMock.getDisplayInfo(eq(DISPLAY_ID))).thenReturn(null);
- ColorFade colorFade = new ColorFade(DISPLAY_ID);
+ ColorFade colorFade = new ColorFade(DISPLAY_ID, mDisplayManagerInternalMock);
assertFalse(colorFade.prepare(mContext, ColorFade.MODE_FADE));
}
-
- private static <T> void addLocalServiceMock(Class<T> clazz, T mock) {
- LocalServices.removeServiceForTest(clazz);
- LocalServices.addService(clazz, mock);
- }
-
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
index d9338a9b12bf..82d00a6fcbca 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
@@ -112,8 +112,6 @@ public final class DisplayDeviceConfigTest {
assertArrayEquals(mDisplayDeviceConfig.getBrightness(), BRIGHTNESS, ZERO_DELTA);
assertArrayEquals(mDisplayDeviceConfig.getNits(), NITS, ZERO_DELTA);
assertArrayEquals(mDisplayDeviceConfig.getBacklight(), BRIGHTNESS, ZERO_DELTA);
- assertEquals(mDisplayDeviceConfig.getAutoBrightnessBrighteningLightDebounce(), 2000);
- assertEquals(mDisplayDeviceConfig.getAutoBrightnessDarkeningLightDebounce(), 1000);
assertArrayEquals(mDisplayDeviceConfig.getAutoBrightnessBrighteningLevelsLux(), new
float[]{0.0f, 50.0f, 80.0f}, ZERO_DELTA);
assertArrayEquals(mDisplayDeviceConfig.getAutoBrightnessBrighteningLevelsNits(), new
@@ -413,6 +411,17 @@ public final class DisplayDeviceConfigTest {
assertArrayEquals(new int[]{ -1, 10, 20, 30, 40 },
mDisplayDeviceConfig.getScreenOffBrightnessSensorValueToLux());
+
+ // Low/High zone thermal maps
+ assertEquals(new SurfaceControl.RefreshRateRange(30, 40),
+ mDisplayDeviceConfig.getLowBlockingZoneThermalMap()
+ .get(Temperature.THROTTLING_CRITICAL));
+ assertEquals(new SurfaceControl.RefreshRateRange(40, 60),
+ mDisplayDeviceConfig.getHighBlockingZoneThermalMap()
+ .get(Temperature.THROTTLING_EMERGENCY));
+
+ // Todo: Add asserts for DensityMapping,
+ // HighBrightnessModeData AmbientLightSensor, RefreshRateLimitations and ProximitySensor.
}
@Test
@@ -540,6 +549,16 @@ public final class DisplayDeviceConfigTest {
assertEquals("", mDisplayDeviceConfig.getAmbientLightSensor().name);
}
+ @Test
+ public void testLightDebounceFromDisplayConfig() throws IOException {
+ setupDisplayDeviceConfigFromDisplayConfigFile();
+
+ assertEquals(mDisplayDeviceConfig.getAutoBrightnessBrighteningLightDebounce(), 2000);
+ assertEquals(mDisplayDeviceConfig.getAutoBrightnessDarkeningLightDebounce(), 1000);
+ assertEquals(mDisplayDeviceConfig.getAutoBrightnessBrighteningLightDebounceIdle(), 2500);
+ assertEquals(mDisplayDeviceConfig.getAutoBrightnessDarkeningLightDebounceIdle(), 1500);
+ }
+
private String getValidLuxThrottling() {
return "<luxThrottling>\n"
+ " <brightnessLimitMap>\n"
@@ -632,6 +651,24 @@ public final class DisplayDeviceConfigTest {
+ " </refreshRateRange>\n"
+ " </refreshRateThrottlingPoint>\n"
+ "</refreshRateThrottlingMap>\n"
+ + "<refreshRateThrottlingMap id=\"thermalLow\">\n"
+ + " <refreshRateThrottlingPoint>\n"
+ + " <thermalStatus>critical</thermalStatus>\n"
+ + " <refreshRateRange>\n"
+ + " <minimum>30</minimum>\n"
+ + " <maximum>40</maximum>\n"
+ + " </refreshRateRange>\n"
+ + " </refreshRateThrottlingPoint>\n"
+ + "</refreshRateThrottlingMap>\n"
+ + "<refreshRateThrottlingMap id=\"thermalHigh\">\n"
+ + " <refreshRateThrottlingPoint>\n"
+ + " <thermalStatus>emergency</thermalStatus>\n"
+ + " <refreshRateRange>\n"
+ + " <minimum>40</minimum>\n"
+ + " <maximum>60</maximum>\n"
+ + " </refreshRateRange>\n"
+ + " </refreshRateThrottlingPoint>\n"
+ + "</refreshRateThrottlingMap>\n"
+ "<refreshRateThrottlingMap id=\"test\">\n"
+ " <refreshRateThrottlingPoint>\n"
+ " <thermalStatus>emergency</thermalStatus>\n"
@@ -704,6 +741,12 @@ public final class DisplayDeviceConfigTest {
+ "<autoBrightness>\n"
+ "<brighteningLightDebounceMillis>2000</brighteningLightDebounceMillis>\n"
+ "<darkeningLightDebounceMillis>1000</darkeningLightDebounceMillis>\n"
+ + "<brighteningLightDebounceIdleMillis>"
+ + "2500"
+ + "</brighteningLightDebounceIdleMillis>\n"
+ + "<darkeningLightDebounceIdleMillis>"
+ + "1500"
+ + "</darkeningLightDebounceIdleMillis>\n"
+ "<displayBrightnessMapping>\n"
+ "<displayBrightnessPoint>\n"
+ "<lux>50</lux>\n"
@@ -969,6 +1012,8 @@ public final class DisplayDeviceConfigTest {
+ "<defaultRefreshRateInHbmSunlight>83</defaultRefreshRateInHbmSunlight>\n"
+ "<lowerBlockingZoneConfigs>\n"
+ "<defaultRefreshRate>75</defaultRefreshRate>\n"
+ + "<refreshRateThermalThrottlingId>thermalLow"
+ + "</refreshRateThermalThrottlingId>\n"
+ "<blockingZoneThreshold>\n"
+ "<displayBrightnessPoint>\n"
+ "<lux>50</lux>\n"
@@ -990,6 +1035,8 @@ public final class DisplayDeviceConfigTest {
+ "</lowerBlockingZoneConfigs>\n"
+ "<higherBlockingZoneConfigs>\n"
+ "<defaultRefreshRate>90</defaultRefreshRate>\n"
+ + "<refreshRateThermalThrottlingId>thermalHigh"
+ + "</refreshRateThermalThrottlingId>\n"
+ "<blockingZoneThreshold>\n"
+ "<displayBrightnessPoint>\n"
+ "<lux>70</lux>\n"
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerController2Test.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerController2Test.java
index 4c3d80a6422b..4cbdd09319cc 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerController2Test.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerController2Test.java
@@ -1061,6 +1061,8 @@ public final class DisplayPowerController2Test {
anyInt(),
anyLong(),
anyLong(),
+ anyLong(),
+ anyLong(),
anyBoolean(),
any(HysteresisLevels.class),
any(HysteresisLevels.class),
@@ -1350,6 +1352,7 @@ public final class DisplayPowerController2Test {
int lightSensorWarmUpTime, float brightnessMin, float brightnessMax,
float dozeScaleFactor, int lightSensorRate, int initialLightSensorRate,
long brighteningLightDebounceConfig, long darkeningLightDebounceConfig,
+ long brighteningLightDebounceConfigIdle, long darkeningLightDebounceConfigIdle,
boolean resetAmbientLuxAfterWarmUpConfig,
HysteresisLevels ambientBrightnessThresholds,
HysteresisLevels screenBrightnessThresholds,
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
index 01e49f2e207d..68bbcbc1df51 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
@@ -63,8 +63,8 @@ import androidx.test.filters.FlakyTest;
import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
+import com.android.internal.util.test.LocalServiceKeeperRule;
import com.android.modules.utils.testing.ExtendedMockitoRule;
-import com.android.server.LocalServices;
import com.android.server.am.BatteryStatsService;
import com.android.server.display.RampAnimator.DualRampAnimator;
import com.android.server.display.brightness.BrightnessEvent;
@@ -74,7 +74,6 @@ import com.android.server.display.whitebalance.DisplayWhiteBalanceController;
import com.android.server.policy.WindowManagerPolicy;
import com.android.server.testutils.OffsettableClock;
-import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -140,6 +139,9 @@ public final class DisplayPowerControllerTest {
.spyStatic(BatteryStatsService.class)
.build();
+ @Rule
+ public LocalServiceKeeperRule mLocalServiceKeeperRule = new LocalServiceKeeperRule();
+
@Before
public void setUp() throws Exception {
mClock = new OffsettableClock.Stopped();
@@ -153,9 +155,10 @@ public final class DisplayPowerControllerTest {
Settings.System.putFloatForUser(mContext.getContentResolver(),
Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ, 0, UserHandle.USER_CURRENT);
- addLocalServiceMock(WindowManagerPolicy.class, mWindowManagerPolicyMock);
- addLocalServiceMock(ColorDisplayService.ColorDisplayServiceInternal.class,
- mCdsiMock);
+ mLocalServiceKeeperRule.overrideLocalService(
+ WindowManagerPolicy.class, mWindowManagerPolicyMock);
+ mLocalServiceKeeperRule.overrideLocalService(
+ ColorDisplayService.ColorDisplayServiceInternal.class, mCdsiMock);
mContext.addMockSystemService(PowerManager.class, mPowerManagerMock);
@@ -167,12 +170,6 @@ public final class DisplayPowerControllerTest {
mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID);
}
- @After
- public void tearDown() {
- LocalServices.removeServiceForTest(WindowManagerPolicy.class);
- LocalServices.removeServiceForTest(ColorDisplayService.ColorDisplayServiceInternal.class);
- }
-
@Test
public void testReleaseProxSuspendBlockersOnExit() throws Exception {
when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON);
@@ -1071,6 +1068,8 @@ public final class DisplayPowerControllerTest {
anyInt(),
anyLong(),
anyLong(),
+ anyLong(),
+ anyLong(),
anyBoolean(),
any(HysteresisLevels.class),
any(HysteresisLevels.class),
@@ -1124,14 +1123,6 @@ public final class DisplayPowerControllerTest {
verify(mDisplayWhiteBalanceControllerMock, times(1)).setStrongModeEnabled(true);
}
- /**
- * Creates a mock and registers it to {@link LocalServices}.
- */
- private static <T> void addLocalServiceMock(Class<T> clazz, T mock) {
- LocalServices.removeServiceForTest(clazz);
- LocalServices.addService(clazz, mock);
- }
-
private void advanceTime(long timeMs) {
mClock.fastForward(timeMs);
mTestLooper.dispatchAll();
@@ -1330,6 +1321,7 @@ public final class DisplayPowerControllerTest {
int lightSensorWarmUpTime, float brightnessMin, float brightnessMax,
float dozeScaleFactor, int lightSensorRate, int initialLightSensorRate,
long brighteningLightDebounceConfig, long darkeningLightDebounceConfig,
+ long brighteningLightDebounceConfigIdle, long darkeningLightDebounceConfigIdle,
boolean resetAmbientLuxAfterWarmUpConfig,
HysteresisLevels ambientBrightnessThresholds,
HysteresisLevels screenBrightnessThresholds,
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
index b088dbfcf13b..168eefcb4f0c 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
@@ -39,6 +39,7 @@ import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@@ -1093,6 +1094,196 @@ public class DisplayModeDirectorTest {
}
@Test
+ public void testLockFpsForHighZoneWithThermalCondition() throws Exception {
+ // First, configure brightness zones or DMD won't register for sensor data.
+ final FakeDeviceConfig config = mInjector.getDeviceConfig();
+ config.setHighDisplayBrightnessThresholds(new int[] { 200 });
+ config.setHighAmbientBrightnessThresholds(new int[] { 8000 });
+
+ DisplayModeDirector director =
+ createDirectorFromRefreshRateArray(new float[] {60.f, 90.f, 120.f}, 0);
+ setPeakRefreshRate(120 /*fps*/);
+ director.getSettingsObserver().setDefaultRefreshRate(120);
+ director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON);
+
+ // Set the thresholds for High Zone
+ DisplayDeviceConfig ddcMock = mock(DisplayDeviceConfig.class);
+ when(ddcMock.getDefaultHighBlockingZoneRefreshRate()).thenReturn(90);
+ when(ddcMock.getHighDisplayBrightnessThresholds()).thenReturn(new float[] { 200 });
+ when(ddcMock.getHighAmbientBrightnessThresholds()).thenReturn(new float[] { 8000 });
+ when(ddcMock.getDefaultLowBlockingZoneRefreshRate()).thenReturn(90);
+ when(ddcMock.getLowDisplayBrightnessThresholds()).thenReturn(new float[] {});
+ when(ddcMock.getLowAmbientBrightnessThresholds()).thenReturn(new float[] {});
+
+ // Set the thermal condition for refresh rate range
+ when(ddcMock.getHighBlockingZoneThermalMap()).thenReturn(
+ new SparseArray<RefreshRateRange>() {{
+ put(Temperature.THROTTLING_CRITICAL, new RefreshRateRange(60, 60));
+ }}
+ );
+ director.defaultDisplayDeviceUpdated(ddcMock); // set the ddc
+
+ Sensor lightSensor = createLightSensor();
+ SensorManager sensorManager = createMockSensorManager(lightSensor);
+ director.start(sensorManager);
+
+ // Get the display listener so that we can send it new brightness events
+ ArgumentCaptor<DisplayListener> displayListenerCaptor =
+ ArgumentCaptor.forClass(DisplayListener.class);
+ verify(mInjector).registerDisplayListener(displayListenerCaptor.capture(),
+ any(Handler.class),
+ eq(DisplayManager.EVENT_FLAG_DISPLAY_CHANGED
+ | DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS));
+ DisplayListener displayListener = displayListenerCaptor.getValue();
+
+ // Get the sensor listener so that we can give it new light sensor events
+ ArgumentCaptor<SensorEventListener> listenerCaptor =
+ ArgumentCaptor.forClass(SensorEventListener.class);
+ verify(sensorManager, Mockito.timeout(TimeUnit.SECONDS.toMillis(1)))
+ .registerListener(
+ listenerCaptor.capture(),
+ eq(lightSensor),
+ anyInt(),
+ any(Handler.class));
+ SensorEventListener sensorListener = listenerCaptor.getValue();
+
+ // Get the thermal listener so that we can give it new thermal conditions
+ ArgumentCaptor<IThermalEventListener> thermalListenerCaptor =
+ ArgumentCaptor.forClass(IThermalEventListener.class);
+ verify(mInjector, atLeastOnce()).registerThermalServiceListener(
+ thermalListenerCaptor.capture());
+ List<IThermalEventListener> thermalListeners = thermalListenerCaptor.getAllValues();
+
+ setBrightness(100, 100, displayListener);
+ // Sensor reads 2000 lux,
+ sensorListener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 2000));
+
+ Vote vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE);
+ assertThat(vote).isNull();
+ vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH);
+ assertThat(vote).isNull();
+
+ // We expect DisplayModeDirector to act on BrightnessInfo.adjustedBrightness; set only this
+ // parameter to the necessary threshold
+ setBrightness(255, 255, displayListener);
+ // Sensor reads 9000 lux,
+ sensorListener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 9000));
+
+ vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE);
+ assertVoteForPhysicalRefreshRate(vote, 90 /*fps*/);
+ vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH);
+ assertThat(vote).isNotNull();
+ assertThat(vote.disableRefreshRateSwitching).isTrue();
+
+ // Set critical and check new refresh rate
+ Temperature temp = getSkinTemp(Temperature.THROTTLING_CRITICAL);
+ for (var listener : thermalListeners) {
+ listener.notifyThrottling(temp);
+ }
+
+ vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE);
+ assertVoteForPhysicalRefreshRate(vote, 60 /*fps*/);
+ vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH);
+ assertThat(vote).isNotNull();
+ assertThat(vote.disableRefreshRateSwitching).isTrue();
+ }
+
+ @Test
+ public void testLockFpsForLowZoneWithThermalCondition() throws Exception {
+ // First, configure brightness zones or DMD won't register for sensor data.
+ final FakeDeviceConfig config = mInjector.getDeviceConfig();
+ config.setHighDisplayBrightnessThresholds(new int[] { 200 });
+ config.setHighAmbientBrightnessThresholds(new int[] { 8000 });
+
+ DisplayModeDirector director =
+ createDirectorFromRefreshRateArray(new float[] {60.f, 90.f, 120.f}, 0);
+ setPeakRefreshRate(120 /*fps*/);
+ director.getSettingsObserver().setDefaultRefreshRate(120);
+ director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON);
+
+ // Set the thresholds for Low Zone
+ DisplayDeviceConfig ddcMock = mock(DisplayDeviceConfig.class);
+ when(ddcMock.getDefaultLowBlockingZoneRefreshRate()).thenReturn(90);
+ when(ddcMock.getHighDisplayBrightnessThresholds()).thenReturn(new float[] { 200 });
+ when(ddcMock.getHighAmbientBrightnessThresholds()).thenReturn(new float[] { 8000 });
+ when(ddcMock.getDefaultLowBlockingZoneRefreshRate()).thenReturn(90);
+ when(ddcMock.getLowDisplayBrightnessThresholds()).thenReturn(new float[] { 10 });
+ when(ddcMock.getLowAmbientBrightnessThresholds()).thenReturn(new float[] { 10 });
+
+ // Set the thermal condition for refresh rate range
+ when(ddcMock.getLowBlockingZoneThermalMap()).thenReturn(
+ new SparseArray<RefreshRateRange>() {{
+ put(Temperature.THROTTLING_CRITICAL, new RefreshRateRange(60, 60));
+ }}
+ );
+ director.defaultDisplayDeviceUpdated(ddcMock); // set the ddc
+
+ Sensor lightSensor = createLightSensor();
+ SensorManager sensorManager = createMockSensorManager(lightSensor);
+ director.start(sensorManager);
+
+ // Get the display listener so that we can send it new brightness events
+ ArgumentCaptor<DisplayListener> displayListenerCaptor =
+ ArgumentCaptor.forClass(DisplayListener.class);
+ verify(mInjector).registerDisplayListener(displayListenerCaptor.capture(),
+ any(Handler.class),
+ eq(DisplayManager.EVENT_FLAG_DISPLAY_CHANGED
+ | DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS));
+ DisplayListener displayListener = displayListenerCaptor.getValue();
+
+ // Get the sensor listener so that we can give it new light sensor events
+ ArgumentCaptor<SensorEventListener> listenerCaptor =
+ ArgumentCaptor.forClass(SensorEventListener.class);
+ verify(sensorManager, Mockito.timeout(TimeUnit.SECONDS.toMillis(1)))
+ .registerListener(
+ listenerCaptor.capture(),
+ eq(lightSensor),
+ anyInt(),
+ any(Handler.class));
+ SensorEventListener sensorListener = listenerCaptor.getValue();
+
+ // Get the thermal listener so that we can give it new thermal conditions
+ ArgumentCaptor<IThermalEventListener> thermalListenerCaptor =
+ ArgumentCaptor.forClass(IThermalEventListener.class);
+ verify(mInjector, atLeastOnce()).registerThermalServiceListener(
+ thermalListenerCaptor.capture());
+ List<IThermalEventListener> thermalListeners = thermalListenerCaptor.getAllValues();
+
+ setBrightness(100, 100, displayListener);
+ // Sensor reads 2000 lux,
+ sensorListener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 2000));
+
+ Vote vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE);
+ assertThat(vote).isNull();
+ vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH);
+ assertThat(vote).isNull();
+
+ // We expect DisplayModeDirector to act on BrightnessInfo.adjustedBrightness; set only this
+ // parameter to the necessary threshold
+ setBrightness(5, 5, displayListener);
+ // Sensor reads 9 lux,
+ sensorListener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 9));
+
+ vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE);
+ assertVoteForPhysicalRefreshRate(vote, 90 /*fps*/);
+ vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH);
+ assertThat(vote).isNotNull();
+ assertThat(vote.disableRefreshRateSwitching).isTrue();
+
+ // Set critical and check new refresh rate
+ Temperature temp = getSkinTemp(Temperature.THROTTLING_CRITICAL);
+ for (var listener : thermalListeners) {
+ listener.notifyThrottling(temp);
+ }
+
+ vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE);
+ assertVoteForPhysicalRefreshRate(vote, 60 /*fps*/);
+ vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH);
+ assertThat(vote).isNotNull();
+ assertThat(vote.disableRefreshRateSwitching).isTrue();
+ }
+
+ @Test
public void testSensorRegistration() {
// First, configure brightness zones or DMD won't register for sensor data.
final FakeDeviceConfig config = mInjector.getDeviceConfig();
@@ -2907,6 +3098,10 @@ public class DisplayModeDirectorTest {
}
@Override
+ public void unregisterThermalServiceListener(IThermalEventListener listener) {
+ }
+
+ @Override
public boolean supportsFrameRateOverride() {
return true;
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/whitebalance/AmbientLuxTest.java b/services/tests/displayservicetests/src/com/android/server/display/whitebalance/AmbientLuxTest.java
index 183a84de0eb0..f9fbf1bd5085 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/whitebalance/AmbientLuxTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/whitebalance/AmbientLuxTest.java
@@ -38,7 +38,7 @@ import android.util.TypedValue;
import androidx.test.InstrumentationRegistry;
import com.android.internal.R;
-import com.android.server.LocalServices;
+import com.android.internal.util.test.LocalServiceKeeperRule;
import com.android.server.display.TestUtils;
import com.android.server.display.color.ColorDisplayService;
import com.android.server.display.utils.AmbientFilter;
@@ -47,6 +47,7 @@ import com.android.server.display.utils.AmbientFilterStubber;
import com.google.common.collect.ImmutableList;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@@ -90,6 +91,9 @@ public final class AmbientLuxTest {
@Mock private TypedArray mStrongDisplayColorTemperatures;
@Mock private ColorDisplayService.ColorDisplayServiceInternal mColorDisplayServiceInternalMock;
+ @Rule
+ public LocalServiceKeeperRule mLocalServiceKeeperRule = new LocalServiceKeeperRule();
+
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
@@ -156,8 +160,9 @@ public final class AmbientLuxTest {
R.array.config_displayWhiteBalanceHighLightAmbientBiasesStrong))
.thenReturn(mHighLightBiasesStrong);
mockThrottler();
- LocalServices.removeServiceForTest(ColorDisplayService.ColorDisplayServiceInternal.class);
- LocalServices.addService(ColorDisplayService.ColorDisplayServiceInternal.class,
+
+ mLocalServiceKeeperRule.overrideLocalService(
+ ColorDisplayService.ColorDisplayServiceInternal.class,
mColorDisplayServiceInternalMock);
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/backup/utils/BackupManagerMonitorDumpsysUtilsTest.java b/services/tests/mockingservicestests/src/com/android/server/backup/utils/BackupManagerMonitorDumpsysUtilsTest.java
new file mode 100644
index 000000000000..8e17b3a58769
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/backup/utils/BackupManagerMonitorDumpsysUtilsTest.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2023 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.utils;
+
+import static org.junit.Assert.assertTrue;
+
+import android.app.backup.BackupManagerMonitor;
+import android.os.Bundle;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+import java.io.File;
+
+public class BackupManagerMonitorDumpsysUtilsTest {
+ private File mTempFile;
+ private TestBackupManagerMonitorDumpsysUtils mBackupManagerMonitorDumpsysUtils;
+ @Rule
+ public TemporaryFolder tmp = new TemporaryFolder();
+
+ @Before
+ public void setUp() throws Exception {
+ mTempFile = tmp.newFile("testbmmevents.txt");
+ mBackupManagerMonitorDumpsysUtils = new TestBackupManagerMonitorDumpsysUtils();
+ }
+
+
+ @Test
+ public void parseBackupManagerMonitorEventForDumpsys_bundleIsNull_noLogsWrittenToFile()
+ throws Exception {
+ mBackupManagerMonitorDumpsysUtils.parseBackupManagerMonitorRestoreEventForDumpsys(null);
+
+ assertTrue(mTempFile.length() == 0);
+
+ }
+
+ @Test
+ public void parseBackupManagerMonitorEventForDumpsys_missingID_noLogsWrittenToFile()
+ throws Exception {
+ Bundle event = new Bundle();
+ event.putInt(BackupManagerMonitor.EXTRA_LOG_EVENT_CATEGORY, 1);
+ mBackupManagerMonitorDumpsysUtils.parseBackupManagerMonitorRestoreEventForDumpsys(event);
+
+ assertTrue(mTempFile.length() == 0);
+ }
+
+ @Test
+ public void parseBackupManagerMonitorEventForDumpsys_missingCategory_noLogsWrittenToFile()
+ throws Exception {
+ Bundle event = new Bundle();
+ event.putInt(BackupManagerMonitor.EXTRA_LOG_EVENT_ID, 1);
+ mBackupManagerMonitorDumpsysUtils.parseBackupManagerMonitorRestoreEventForDumpsys(event);
+
+ assertTrue(mTempFile.length() == 0);
+ }
+
+ private class TestBackupManagerMonitorDumpsysUtils
+ extends BackupManagerMonitorDumpsysUtils {
+ TestBackupManagerMonitorDumpsysUtils() {
+ super();
+ }
+
+ @Override
+ public File getBMMEventsFile() {
+ return mTempFile;
+ }
+ }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/backup/utils/BackupManagerMonitorEventSenderTest.java b/services/tests/mockingservicestests/src/com/android/server/backup/utils/BackupManagerMonitorEventSenderTest.java
index 9d9b03fa6862..3af2932ee937 100644
--- a/services/tests/mockingservicestests/src/com/android/server/backup/utils/BackupManagerMonitorEventSenderTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/backup/utils/BackupManagerMonitorEventSenderTest.java
@@ -30,6 +30,7 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
@@ -63,26 +64,40 @@ import java.util.List;
@RunWith(AndroidJUnit4.class)
public class BackupManagerMonitorEventSenderTest {
@Mock private IBackupManagerMonitor mMonitorMock;
+ @Mock private BackupManagerMonitorDumpsysUtils mBackupManagerMonitorDumpsysUtilsMock;
private BackupManagerMonitorEventSender mBackupManagerMonitorEventSender;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- mBackupManagerMonitorEventSender = new BackupManagerMonitorEventSender(mMonitorMock);
+ mBackupManagerMonitorEventSender = new BackupManagerMonitorEventSender(mMonitorMock,
+ mBackupManagerMonitorDumpsysUtilsMock);
}
@Test
- public void monitorEvent_monitorIsNull_returnsNull() throws Exception {
- mBackupManagerMonitorEventSender = new BackupManagerMonitorEventSender(/* monitor */ null);
+ public void monitorEvent_monitorIsNull_sendBundleToDumpsys() throws Exception {
+ Bundle extras = new Bundle();
+ extras.putInt(EXTRA_LOG_OPERATION_TYPE, OperationType.RESTORE);
+ mBackupManagerMonitorEventSender.setMonitor(null);
+ mBackupManagerMonitorEventSender.monitorEvent(0, null, 0, extras);
+ IBackupManagerMonitor monitor = mBackupManagerMonitorEventSender.getMonitor();
+
+ verify(mBackupManagerMonitorDumpsysUtilsMock).parseBackupManagerMonitorRestoreEventForDumpsys(any(
+ Bundle.class));
+ }
+
+ @Test
+ public void monitorEvent_monitorIsNull_doNotCallOnEvent() throws Exception {
+ mBackupManagerMonitorEventSender = new BackupManagerMonitorEventSender(null);
mBackupManagerMonitorEventSender.monitorEvent(0, null, 0, null);
IBackupManagerMonitor monitor = mBackupManagerMonitorEventSender.getMonitor();
- assertThat(monitor).isNull();
+ verify(mMonitorMock, never()).onEvent(any(Bundle.class));
}
@Test
- public void monitorEvent_monitorOnEventThrows_returnsNull() throws Exception {
+ public void monitorEvent_monitorOnEventThrows_setsMonitorToNull() throws Exception {
doThrow(new RemoteException()).when(mMonitorMock).onEvent(any(Bundle.class));
mBackupManagerMonitorEventSender.monitorEvent(0, null, 0, null);
@@ -93,6 +108,14 @@ public class BackupManagerMonitorEventSenderTest {
}
@Test
+ public void monitorEvent_extrasAreNull_doNotSendBundleToDumpsys() throws Exception {
+ mBackupManagerMonitorEventSender.monitorEvent(1, null, 2, null);
+
+ verify(mBackupManagerMonitorDumpsysUtilsMock, never())
+ .parseBackupManagerMonitorRestoreEventForDumpsys(any(Bundle.class));
+ }
+
+ @Test
public void monitorEvent_packageAndExtrasAreNull_fillsBundleCorrectly() throws Exception {
mBackupManagerMonitorEventSender.monitorEvent(1, null, 2, null);
IBackupManagerMonitor monitor = mBackupManagerMonitorEventSender.getMonitor();
@@ -162,6 +185,36 @@ public class BackupManagerMonitorEventSenderTest {
}
@Test
+ public void monitorEvent_eventOpTypeIsRestore_sendBundleToDumpsys() throws Exception {
+ Bundle extras = new Bundle();
+ extras.putInt(EXTRA_LOG_OPERATION_TYPE, OperationType.RESTORE);
+ mBackupManagerMonitorEventSender.monitorEvent(1, null, 2, extras);
+
+ verify(mBackupManagerMonitorDumpsysUtilsMock).parseBackupManagerMonitorRestoreEventForDumpsys(any(
+ Bundle.class));
+ }
+
+ @Test
+ public void monitorEvent_eventOpTypeIsBackup_doNotSendBundleToDumpsys() throws Exception {
+ Bundle extras = new Bundle();
+ extras.putInt(EXTRA_LOG_OPERATION_TYPE, OperationType.BACKUP);
+ mBackupManagerMonitorEventSender.monitorEvent(1, null, 2, extras);
+
+ verify(mBackupManagerMonitorDumpsysUtilsMock, never())
+ .parseBackupManagerMonitorRestoreEventForDumpsys(any(Bundle.class));
+ }
+
+ @Test
+ public void monitorEvent_eventOpTypeIsUnknown_doNotSendBundleToDumpsys() throws Exception {
+ Bundle extras = new Bundle();
+ extras.putInt(EXTRA_LOG_OPERATION_TYPE, OperationType.UNKNOWN);
+ mBackupManagerMonitorEventSender.monitorEvent(1, null, 2, extras);
+
+ verify(mBackupManagerMonitorDumpsysUtilsMock, never())
+ .parseBackupManagerMonitorRestoreEventForDumpsys(any(Bundle.class));
+ }
+
+ @Test
public void monitorAgentLoggingResults_onBackup_fillsBundleCorrectly() throws Exception {
PackageInfo packageInfo = new PackageInfo();
packageInfo.packageName = "test.package";
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
index 908afc861f8b..00e35ecece8b 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
@@ -226,6 +226,7 @@ public class VirtualDeviceManagerServiceTest {
private VirtualDeviceManagerService mVdms;
private VirtualDeviceManagerInternal mLocalService;
private VirtualDeviceManagerService.VirtualDeviceManagerImpl mVdm;
+ private VirtualDeviceLog mVirtualDeviceLog;
@Mock
private InputController.NativeWrapper mNativeWrapperMock;
@Mock
@@ -370,6 +371,7 @@ public class VirtualDeviceManagerServiceTest {
mVdms = new VirtualDeviceManagerService(mContext);
mLocalService = mVdms.getLocalServiceInstance();
mVdm = mVdms.new VirtualDeviceManagerImpl();
+ mVirtualDeviceLog = new VirtualDeviceLog(mContext);
mDeviceImpl = createVirtualDevice(VIRTUAL_DEVICE_ID_1, DEVICE_OWNER_UID_1);
mSensorController = mDeviceImpl.getSensorControllerForTest();
}
@@ -1730,7 +1732,7 @@ public class VirtualDeviceManagerServiceTest {
private VirtualDeviceImpl createVirtualDevice(int virtualDeviceId, int ownerUid,
VirtualDeviceParams params) {
VirtualDeviceImpl virtualDeviceImpl = new VirtualDeviceImpl(mContext,
- mAssociationInfo, mVdms, new Binder(),
+ mAssociationInfo, mVdms, mVirtualDeviceLog, new Binder(),
new AttributionSource(ownerUid, "com.android.virtualdevice.test", "virtualdevice"),
virtualDeviceId,
mInputController, mCameraAccessController,
diff --git a/services/tests/servicestests/src/com/android/server/power/hint/HintManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/hint/HintManagerServiceTest.java
index ee3ab9744fdd..9fca513e50b9 100644
--- a/services/tests/servicestests/src/com/android/server/power/hint/HintManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/hint/HintManagerServiceTest.java
@@ -149,32 +149,6 @@ public class HintManagerServiceTest {
}
@Test
- public void testCreateHintSession_exceedsLimit() throws Exception {
- HintManagerService service = createService();
- IBinder token1 = new Binder();
- IBinder token2 = new Binder();
-
- for (int i = 0; i < 10; i++) {
- IHintSession a = service.getBinderServiceInstance().createHintSession(token1,
- SESSION_TIDS_A, DEFAULT_TARGET_DURATION);
- assertNotNull(a);
- }
-
- for (int i = 0; i < 10; i++) {
- IHintSession b = service.getBinderServiceInstance().createHintSession(token2,
- SESSION_TIDS_B, DEFAULT_TARGET_DURATION);
- assertNotNull(b);
- }
-
- assertThrows(IllegalStateException.class,
- () -> service.getBinderServiceInstance().createHintSession(token1, SESSION_TIDS_A,
- DEFAULT_TARGET_DURATION));
- assertThrows(IllegalStateException.class,
- () -> service.getBinderServiceInstance().createHintSession(token2, SESSION_TIDS_B,
- DEFAULT_TARGET_DURATION));
- }
-
- @Test
public void testPauseResumeHintSession() throws Exception {
HintManagerService service = createService();
IBinder token = new Binder();
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index 81d939f1f05f..9b745f5aaf4f 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -115,6 +115,7 @@ import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.os.Parcel;
+import android.os.Process;
import android.os.RemoteCallback;
import android.os.RemoteException;
import android.os.UserHandle;
@@ -209,6 +210,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
@Mock Context mContext;
@Mock ZenModeHelper mMockZenModeHelper;
@Mock AppOpsManager mAppOpsManager;
+ @Mock ManagedServices.UserProfiles mUserProfiles;
@Mock PermissionManager mPermissionManager;
private NotificationManager.Policy mTestNotificationPolicy;
@@ -327,10 +329,12 @@ public class PreferencesHelperTest extends UiServiceTestCase {
when(mPermissionHelper.getNotificationPermissionValues(USER_SYSTEM))
.thenReturn(appPermissions);
+ when(mUserProfiles.getCurrentProfileIds()).thenReturn(IntArray.wrap(new int[] {0}));
+
mStatsEventBuilderFactory = new WrappedSysUiStatsEvent.WrappedBuilderFactory();
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager,
+ mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mUserProfiles,
mStatsEventBuilderFactory, false);
resetZenModeHelper();
@@ -678,7 +682,8 @@ public class PreferencesHelperTest extends UiServiceTestCase {
@Test
public void testReadXml_oldXml_migrates() throws Exception {
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mStatsEventBuilderFactory, true);
+ mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mUserProfiles,
+ mStatsEventBuilderFactory, /* showReviewPermissionsNotification= */ true);
String xml = "<ranking version=\"2\">\n"
+ "<package name=\"" + PKG_N_MR1 + "\" uid=\"" + UID_N_MR1
@@ -743,9 +748,6 @@ public class PreferencesHelperTest extends UiServiceTestCase {
@Test
public void testReadXml_oldXml_backup_migratesWhenPkgInstalled() throws Exception {
- mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false);
-
when(mPm.getPackageUidAsUser("pkg1", USER_SYSTEM)).thenReturn(UNKNOWN_UID);
when(mPm.getPackageUidAsUser("pkg2", USER_SYSTEM)).thenReturn(UNKNOWN_UID);
when(mPm.getPackageUidAsUser("pkg3", USER_SYSTEM)).thenReturn(UNKNOWN_UID);
@@ -822,7 +824,8 @@ public class PreferencesHelperTest extends UiServiceTestCase {
@Test
public void testReadXml_newXml_noMigration_showPermissionNotification() throws Exception {
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mStatsEventBuilderFactory, true);
+ mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mUserProfiles,
+ mStatsEventBuilderFactory, /* showReviewPermissionsNotification= */ true);
String xml = "<ranking version=\"3\">\n"
+ "<package name=\"" + PKG_N_MR1 + "\" show_badge=\"true\">\n"
@@ -879,7 +882,8 @@ public class PreferencesHelperTest extends UiServiceTestCase {
@Test
public void testReadXml_newXml_permissionNotificationOff() throws Exception {
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false);
+ mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mUserProfiles,
+ mStatsEventBuilderFactory, /* showReviewPermissionsNotification= */ false);
String xml = "<ranking version=\"3\">\n"
+ "<package name=\"" + PKG_N_MR1 + "\" show_badge=\"true\">\n"
@@ -936,7 +940,8 @@ public class PreferencesHelperTest extends UiServiceTestCase {
@Test
public void testReadXml_newXml_noMigration_noPermissionNotification() throws Exception {
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mStatsEventBuilderFactory, true);
+ mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mUserProfiles,
+ mStatsEventBuilderFactory, /* showReviewPermissionsNotification= */ true);
String xml = "<ranking version=\"4\">\n"
+ "<package name=\"" + PKG_N_MR1 + "\" show_badge=\"true\">\n"
@@ -991,9 +996,6 @@ public class PreferencesHelperTest extends UiServiceTestCase {
@Test
public void testReadXml_oldXml_migration_NoUid() throws Exception {
- mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false);
-
when(mPm.getPackageUidAsUser("something", USER_SYSTEM)).thenReturn(UNKNOWN_UID);
String xml = "<ranking version=\"2\">\n"
+ "<package name=\"something\" show_badge=\"true\">\n"
@@ -1024,9 +1026,6 @@ public class PreferencesHelperTest extends UiServiceTestCase {
@Test
public void testReadXml_newXml_noMigration_NoUid() throws Exception {
- mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false);
-
when(mPm.getPackageUidAsUser("something", USER_SYSTEM)).thenReturn(UNKNOWN_UID);
String xml = "<ranking version=\"3\">\n"
+ "<package name=\"something\" show_badge=\"true\">\n"
@@ -1056,9 +1055,6 @@ public class PreferencesHelperTest extends UiServiceTestCase {
@Test
public void testChannelXmlForNonBackup_postMigration() throws Exception {
- mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false);
-
ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> appPermissions = new ArrayMap<>();
appPermissions.put(new Pair<>(1, "first"), new Pair<>(true, false));
appPermissions.put(new Pair<>(3, "third"), new Pair<>(false, false));
@@ -1142,9 +1138,6 @@ public class PreferencesHelperTest extends UiServiceTestCase {
@Test
public void testChannelXmlForBackup_postMigration() throws Exception {
- mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false);
-
ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> appPermissions = new ArrayMap<>();
appPermissions.put(new Pair<>(1, "first"), new Pair<>(true, false));
appPermissions.put(new Pair<>(3, "third"), new Pair<>(false, false));
@@ -1234,9 +1227,6 @@ public class PreferencesHelperTest extends UiServiceTestCase {
@Test
public void testChannelXmlForBackup_postMigration_noExternal() throws Exception {
- mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false);
-
ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> appPermissions = new ArrayMap<>();
appPermissions.put(new Pair<>(UID_P, PKG_P), new Pair<>(true, false));
appPermissions.put(new Pair<>(UID_O, PKG_O), new Pair<>(false, false));
@@ -1319,9 +1309,6 @@ public class PreferencesHelperTest extends UiServiceTestCase {
@Test
public void testChannelXmlForBackup_postMigration_noLocalSettings() throws Exception {
- mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false);
-
ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> appPermissions = new ArrayMap<>();
appPermissions.put(new Pair<>(1, "first"), new Pair<>(true, false));
appPermissions.put(new Pair<>(3, "third"), new Pair<>(false, false));
@@ -1533,7 +1520,8 @@ public class PreferencesHelperTest extends UiServiceTestCase {
new FileNotFoundException("")).thenReturn(resId);
mHelper = new PreferencesHelper(mContext, mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false);
+ mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mUserProfiles,
+ mStatsEventBuilderFactory, false);
NotificationChannel channel =
new NotificationChannel("id", "name", IMPORTANCE_LOW);
@@ -2477,9 +2465,6 @@ public class PreferencesHelperTest extends UiServiceTestCase {
mTestNotificationPolicy = new NotificationManager.Policy(0, 0, 0, 0,
NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND, 0);
when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy);
- mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mPermissionManager, mLogger,
- mAppOpsManager, mStatsEventBuilderFactory, false);
mHelper.syncChannelsBypassingDnd();
// create notification channel that can bypass dnd, but app is blocked
@@ -2508,9 +2493,6 @@ public class PreferencesHelperTest extends UiServiceTestCase {
mTestNotificationPolicy = new NotificationManager.Policy(0, 0, 0, 0,
NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND, 0);
when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy);
- mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mPermissionManager, mLogger,
- mAppOpsManager, mStatsEventBuilderFactory, false);
mHelper.syncChannelsBypassingDnd();
// create notification channel that can bypass dnd, but app is blocked
@@ -2533,9 +2515,6 @@ public class PreferencesHelperTest extends UiServiceTestCase {
mTestNotificationPolicy = new NotificationManager.Policy(0, 0, 0, 0,
NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND, 0);
when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy);
- mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mPermissionManager, mLogger,
- mAppOpsManager, mStatsEventBuilderFactory, false);
mHelper.syncChannelsBypassingDnd();
// create notification channel that can bypass dnd, but app is blocked
@@ -2588,9 +2567,6 @@ public class PreferencesHelperTest extends UiServiceTestCase {
mTestNotificationPolicy = new NotificationManager.Policy(0, 0, 0, 0,
NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND, 0);
when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy);
- mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mPermissionManager, mLogger,
- mAppOpsManager, mStatsEventBuilderFactory, false);
mHelper.syncChannelsBypassingDnd();
assertFalse(mHelper.areChannelsBypassingDnd());
verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any(), anyInt(), anyBoolean());
@@ -2602,15 +2578,58 @@ public class PreferencesHelperTest extends UiServiceTestCase {
// start notification policy off with mAreChannelsBypassingDnd = false
mTestNotificationPolicy = new NotificationManager.Policy(0, 0, 0, 0, 0, 0);
when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy);
- mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mPermissionManager, mLogger,
- mAppOpsManager, mStatsEventBuilderFactory, false);
assertFalse(mHelper.areChannelsBypassingDnd());
verify(mMockZenModeHelper, never()).setNotificationPolicy(any(), anyInt(), anyBoolean());
resetZenModeHelper();
}
@Test
+ public void syncChannelsBypassingDnd_includesProfilesOfCurrentUser() throws Exception {
+ when(mUserProfiles.getCurrentProfileIds()).thenReturn(IntArray.wrap(new int[] {0, 10}));
+ when(mPermissionHelper.hasPermission(anyInt())).thenReturn(true);
+ ApplicationInfo appInfo = new ApplicationInfo();
+ appInfo.targetSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE;
+ when(mPm.getApplicationInfoAsUser(any(), anyInt(), anyInt())).thenReturn(appInfo);
+
+ NotificationChannel withBypass = new NotificationChannel("1", "with", IMPORTANCE_DEFAULT);
+ withBypass.setBypassDnd(true);
+ NotificationChannel withoutBypass = new NotificationChannel("2", "without",
+ IMPORTANCE_DEFAULT);
+ withoutBypass.setBypassDnd(false);
+ mHelper.createNotificationChannel("com.example", UserHandle.getUid(0, 444), withoutBypass,
+ false, false, Process.SYSTEM_UID, true);
+ mHelper.createNotificationChannel("com.example", UserHandle.getUid(10, 444), withBypass,
+ false, false, Process.SYSTEM_UID, true);
+
+ mHelper.syncChannelsBypassingDnd();
+
+ assertThat(mHelper.areChannelsBypassingDnd()).isTrue();
+ }
+
+ @Test
+ public void syncChannelsBypassingDnd_excludesOtherUsers() throws Exception {
+ when(mUserProfiles.getCurrentProfileIds()).thenReturn(IntArray.wrap(new int[] {0}));
+ when(mPermissionHelper.hasPermission(anyInt())).thenReturn(true);
+ ApplicationInfo appInfo = new ApplicationInfo();
+ appInfo.targetSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE;
+ when(mPm.getApplicationInfoAsUser(any(), anyInt(), anyInt())).thenReturn(appInfo);
+
+ NotificationChannel withBypass = new NotificationChannel("1", "with", IMPORTANCE_DEFAULT);
+ withBypass.setBypassDnd(true);
+ NotificationChannel withoutBypass = new NotificationChannel("2", "without",
+ IMPORTANCE_DEFAULT);
+ withoutBypass.setBypassDnd(false);
+ mHelper.createNotificationChannel("com.example", UserHandle.getUid(0, 444), withoutBypass,
+ false, false, Process.SYSTEM_UID, true);
+ mHelper.createNotificationChannel("com.example", UserHandle.getUid(10, 444), withBypass,
+ false, false, Process.SYSTEM_UID, true);
+
+ mHelper.syncChannelsBypassingDnd();
+
+ assertThat(mHelper.areChannelsBypassingDnd()).isFalse();
+ }
+
+ @Test
public void testCreateDeletedChannel() throws Exception {
long[] vibration = new long[]{100, 67, 145, 156};
NotificationChannel channel =
@@ -3705,9 +3724,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
+ "content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n"
+ "</package>\n"
+ "</ranking>\n";
- mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mPermissionManager, mLogger,
- mAppOpsManager, mStatsEventBuilderFactory, false);
+
loadByteArrayXml(preQXml.getBytes(), true, USER_SYSTEM);
assertEquals(PreferencesHelper.DEFAULT_HIDE_SILENT_STATUS_BAR_ICONS,
@@ -3719,9 +3736,6 @@ public class PreferencesHelperTest extends UiServiceTestCase {
mHelper.setHideSilentStatusIcons(!PreferencesHelper.DEFAULT_HIDE_SILENT_STATUS_BAR_ICONS);
ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL);
- mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mPermissionManager, mLogger,
- mAppOpsManager, mStatsEventBuilderFactory, false);
loadStreamXml(baos, false, UserHandle.USER_ALL);
assertEquals(!PreferencesHelper.DEFAULT_HIDE_SILENT_STATUS_BAR_ICONS,
@@ -3789,9 +3803,6 @@ public class PreferencesHelperTest extends UiServiceTestCase {
mHelper.canShowBadge(PKG_O, UID_O);
ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL);
- mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mPermissionManager, mLogger,
- mAppOpsManager, mStatsEventBuilderFactory, false);
loadStreamXml(baos, false, UserHandle.USER_ALL);
assertNull(mHelper.getNotificationDelegate(PKG_O, UID_O));
@@ -3802,9 +3813,6 @@ public class PreferencesHelperTest extends UiServiceTestCase {
mHelper.setNotificationDelegate(PKG_O, UID_O, "other", 53);
ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL);
- mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mPermissionManager, mLogger,
- mAppOpsManager, mStatsEventBuilderFactory, false);
loadStreamXml(baos, false, UserHandle.USER_ALL);
assertEquals("other", mHelper.getNotificationDelegate(PKG_O, UID_O));
@@ -3816,9 +3824,6 @@ public class PreferencesHelperTest extends UiServiceTestCase {
mHelper.revokeNotificationDelegate(PKG_O, UID_O);
ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL);
- mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mPermissionManager, mLogger,
- mAppOpsManager, mStatsEventBuilderFactory, false);
loadStreamXml(baos, false, UserHandle.USER_ALL);
assertNull(mHelper.getNotificationDelegate(PKG_O, UID_O));
@@ -3832,9 +3837,6 @@ public class PreferencesHelperTest extends UiServiceTestCase {
assertEquals(BUBBLE_PREFERENCE_NONE, mHelper.getBubblePreference(PKG_O, UID_O));
ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL);
- mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mPermissionManager, mLogger,
- mAppOpsManager, mStatsEventBuilderFactory, false);
loadStreamXml(baos, false, UserHandle.USER_ALL);
assertEquals(BUBBLE_PREFERENCE_NONE, mHelper.getBubblePreference(PKG_O, UID_O));
@@ -3888,9 +3890,6 @@ public class PreferencesHelperTest extends UiServiceTestCase {
mHelper.getAppLockedFields(PKG_O, UID_O));
ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL);
- mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mPermissionManager, mLogger,
- mAppOpsManager, mStatsEventBuilderFactory, false);
loadStreamXml(baos, false, UserHandle.USER_ALL);
assertEquals(BUBBLE_PREFERENCE_SELECTED, mHelper.getBubblePreference(PKG_O, UID_O));
@@ -3926,9 +3925,6 @@ public class PreferencesHelperTest extends UiServiceTestCase {
mHelper.getAppLockedFields(PKG_O, UID_O));
ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL);
- mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mPermissionManager, mLogger,
- mAppOpsManager, mStatsEventBuilderFactory, false);
loadStreamXml(baos, false, UserHandle.USER_ALL);
assertEquals(mHelper.getBubblePreference(PKG_O, UID_O), BUBBLE_PREFERENCE_NONE);
@@ -4584,10 +4580,6 @@ public class PreferencesHelperTest extends UiServiceTestCase {
@Test
public void testPlaceholderConversationId_shortcutRequired() throws Exception {
- mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mPermissionManager, mLogger,
- mAppOpsManager, mStatsEventBuilderFactory, false);
-
final String xml = "<ranking version=\"1\">\n"
+ "<package name=\"" + PKG_O + "\" uid=\"" + UID_O + "\" >\n"
+ "<channel id=\"id\" name=\"hi\" importance=\"3\" conv_id=\"foo:placeholder_id\"/>"
@@ -4604,10 +4596,6 @@ public class PreferencesHelperTest extends UiServiceTestCase {
@Test
public void testNormalConversationId_shortcutRequired() throws Exception {
- mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mPermissionManager, mLogger,
- mAppOpsManager, mStatsEventBuilderFactory, false);
-
final String xml = "<ranking version=\"1\">\n"
+ "<package name=\"" + PKG_O + "\" uid=\"" + UID_O + "\" >\n"
+ "<channel id=\"id\" name=\"hi\" importance=\"3\" conv_id=\"other\"/>"
@@ -4624,10 +4612,6 @@ public class PreferencesHelperTest extends UiServiceTestCase {
@Test
public void testNoConversationId_shortcutRequired() throws Exception {
- mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mPermissionManager, mLogger,
- mAppOpsManager, mStatsEventBuilderFactory, false);
-
final String xml = "<ranking version=\"1\">\n"
+ "<package name=\"" + PKG_O + "\" uid=\"" + UID_O + "\" >\n"
+ "<channel id=\"id\" name=\"hi\" importance=\"3\"/>"
@@ -4644,10 +4628,6 @@ public class PreferencesHelperTest extends UiServiceTestCase {
@Test
public void testDeleted_noTime() throws Exception {
- mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mPermissionManager, mLogger,
- mAppOpsManager, mStatsEventBuilderFactory, false);
-
final String xml = "<ranking version=\"1\">\n"
+ "<package name=\"" + PKG_O + "\" uid=\"" + UID_O + "\" >\n"
+ "<channel id=\"id\" name=\"hi\" importance=\"3\" deleted=\"true\"/>"
@@ -4664,13 +4644,10 @@ public class PreferencesHelperTest extends UiServiceTestCase {
@Test
public void testDeleted_twice() throws Exception {
- mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mPermissionManager, mLogger,
- mAppOpsManager, mStatsEventBuilderFactory, false);
-
mHelper.createNotificationChannel(
PKG_P, UID_P, new NotificationChannel("id", "id", 2), true, false,
UID_P, false);
+
assertTrue(mHelper.deleteNotificationChannel(PKG_P, UID_P, "id",
UID_P, false));
assertFalse(mHelper.deleteNotificationChannel(PKG_P, UID_P, "id",
@@ -4679,10 +4656,6 @@ public class PreferencesHelperTest extends UiServiceTestCase {
@Test
public void testDeleted_recentTime() throws Exception {
- mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mPermissionManager, mLogger,
- mAppOpsManager, mStatsEventBuilderFactory, false);
-
mHelper.createNotificationChannel(
PKG_P, UID_P, new NotificationChannel("id", "id", 2), true, false,
UID_P, false);
@@ -4698,9 +4671,6 @@ public class PreferencesHelperTest extends UiServiceTestCase {
parser.setInput(new BufferedInputStream(new ByteArrayInputStream(baos.toByteArray())),
null);
parser.nextTag();
- mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mPermissionManager, mLogger,
- mAppOpsManager, mStatsEventBuilderFactory, false);
mHelper.readXml(parser, true, USER_SYSTEM);
NotificationChannel nc = mHelper.getNotificationChannel(PKG_P, UID_P, "id", true);
@@ -4710,10 +4680,6 @@ public class PreferencesHelperTest extends UiServiceTestCase {
@Test
public void testUnDelete_time() throws Exception {
- mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mPermissionManager, mLogger,
- mAppOpsManager, mStatsEventBuilderFactory, false);
-
mHelper.createNotificationChannel(
PKG_P, UID_P, new NotificationChannel("id", "id", 2), true, false,
UID_P, false);
@@ -4732,10 +4698,6 @@ public class PreferencesHelperTest extends UiServiceTestCase {
@Test
public void testDeleted_longTime() throws Exception {
- mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mPermissionManager, mLogger,
- mAppOpsManager, mStatsEventBuilderFactory, false);
-
long time = System.currentTimeMillis() - (DateUtils.DAY_IN_MILLIS * 30);
final String xml = "<ranking version=\"1\">\n"
diff --git a/services/tests/wmtests/src/com/android/server/policy/PowerKeyGestureTests.java b/services/tests/wmtests/src/com/android/server/policy/PowerKeyGestureTests.java
index 6f6540627f67..05a1482b9be6 100644
--- a/services/tests/wmtests/src/com/android/server/policy/PowerKeyGestureTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/PowerKeyGestureTests.java
@@ -112,7 +112,7 @@ public class PowerKeyGestureTests extends ShortcutKeyTestBase {
// Show assistant.
mPhoneWindowManager.overrideLongPressOnPower(LONG_PRESS_POWER_ASSISTANT);
sendKey(KEYCODE_POWER, true);
- mPhoneWindowManager.assertAssistLaunch();
+ mPhoneWindowManager.assertSearchManagerLaunchAssist();
// Show global actions.
mPhoneWindowManager.overrideLongPressOnPower(LONG_PRESS_POWER_GLOBAL_ACTIONS);
diff --git a/services/tests/wmtests/src/com/android/server/policy/StemKeyGestureTests.java b/services/tests/wmtests/src/com/android/server/policy/StemKeyGestureTests.java
index c433e644a6a0..eab8757b7331 100644
--- a/services/tests/wmtests/src/com/android/server/policy/StemKeyGestureTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/StemKeyGestureTests.java
@@ -16,11 +16,15 @@
package com.android.server.policy;
+import static android.provider.Settings.Global.STEM_PRIMARY_BUTTON_LONG_PRESS;
import static android.provider.Settings.Global.STEM_PRIMARY_BUTTON_SHORT_PRESS;
import static android.view.KeyEvent.KEYCODE_STEM_PRIMARY;
+import static com.android.server.policy.PhoneWindowManager.LONG_PRESS_PRIMARY_LAUNCH_VOICE_ASSISTANT;
import static com.android.server.policy.PhoneWindowManager.SHORT_PRESS_PRIMARY_LAUNCH_ALL_APPS;
+import static com.android.server.policy.PhoneWindowManager.SHORT_PRESS_PRIMARY_LAUNCH_TARGET_ACTIVITY;
+import android.content.ComponentName;
import android.provider.Settings;
import org.junit.Test;
@@ -32,6 +36,9 @@ import org.junit.Test;
* atest WmTests:StemKeyGestureTests
*/
public class StemKeyGestureTests extends ShortcutKeyTestBase {
+
+ private static final String TEST_TARGET_ACTIVITY = "com.android.server.policy/.TestActivity";
+
/**
* Stem single key should not launch behavior during set up.
*/
@@ -63,6 +70,57 @@ public class StemKeyGestureTests extends ShortcutKeyTestBase {
mPhoneWindowManager.assertOpenAllAppView();
}
+ /**
+ * Stem single key should not launch behavior during set up.
+ */
+ @Test
+ public void stemSingleKey_launchTargetActivity() {
+ overrideBehavior(
+ STEM_PRIMARY_BUTTON_SHORT_PRESS,
+ SHORT_PRESS_PRIMARY_LAUNCH_TARGET_ACTIVITY);
+ setUpPhoneWindowManager(/* supportSettingsUpdate= */ true);
+ mPhoneWindowManager.overrideStartActivity();
+ mPhoneWindowManager.setKeyguardServiceDelegateIsShowing(false);
+ mPhoneWindowManager.overrideIsUserSetupComplete(true);
+ mPhoneWindowManager.assumeResolveActivityNotNull();
+
+ ComponentName targetComponent = ComponentName.unflattenFromString(TEST_TARGET_ACTIVITY);
+ mPhoneWindowManager.overrideStemPressTargetActivity(targetComponent);
+
+ sendKey(KEYCODE_STEM_PRIMARY);
+
+ mPhoneWindowManager.assertActivityTargetLaunched(targetComponent);
+ }
+
+ @Test
+ public void stemLongKey_triggerSearchServiceToLaunchAssist() {
+ overrideBehavior(
+ STEM_PRIMARY_BUTTON_LONG_PRESS,
+ LONG_PRESS_PRIMARY_LAUNCH_VOICE_ASSISTANT);
+ setUpPhoneWindowManager(/* supportSettingsUpdate= */ true);
+ mPhoneWindowManager.setupAssistForLaunch();
+ mPhoneWindowManager.overrideIsUserSetupComplete(true);
+
+ sendKey(KEYCODE_STEM_PRIMARY, /* longPress= */ true);
+ mPhoneWindowManager.assertSearchManagerLaunchAssist();
+ }
+
+ @Test
+ public void stemLongKey_whenNoSearchService_triggerStatusBarToStartAssist() {
+ overrideBehavior(
+ STEM_PRIMARY_BUTTON_LONG_PRESS,
+ LONG_PRESS_PRIMARY_LAUNCH_VOICE_ASSISTANT);
+ setUpPhoneWindowManager(/* supportSettingsUpdate= */ true);
+ mPhoneWindowManager.setupAssistForLaunch();
+ mPhoneWindowManager.overrideSearchManager(null);
+ mPhoneWindowManager.overrideStatusBarManagerInternal();
+ mPhoneWindowManager.overrideIsUserSetupComplete(true);
+
+ sendKey(KEYCODE_STEM_PRIMARY, /* longPress= */ true);
+ mPhoneWindowManager.assertStatusBarStartAssist();
+ }
+
+
private void overrideBehavior(String key, int expectedBehavior) {
Settings.Global.putLong(mContext.getContentResolver(), key, expectedBehavior);
}
diff --git a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
index ef3a6edf9759..bc8f06a48ffb 100644
--- a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
+++ b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
@@ -59,9 +59,11 @@ import android.app.ActivityManagerInternal;
import android.app.AppOpsManager;
import android.app.NotificationManager;
import android.app.SearchManager;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
import android.hardware.SensorPrivacyManager;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManagerInternal;
@@ -230,6 +232,7 @@ class TestPhoneWindowManager {
doReturn(mPackageManager).when(mContext).getPackageManager();
doReturn(mSensorPrivacyManager).when(mContext).getSystemService(
eq(SensorPrivacyManager.class));
+ doReturn(mSearchManager).when(mContext).getSystemService(eq(SearchManager.class));
doReturn(false).when(mPackageManager).hasSystemFeature(any());
try {
doThrow(new PackageManager.NameNotFoundException("test")).when(mPackageManager)
@@ -355,11 +358,7 @@ class TestPhoneWindowManager {
case LONG_PRESS_POWER_GO_TO_VOICE_ASSIST:
break;
case LONG_PRESS_POWER_ASSISTANT:
- doNothing().when(mPhoneWindowManager).sendCloseSystemWindows();
- doReturn(true).when(mPhoneWindowManager).isUserSetupComplete();
- doReturn(mContext).when(mContext).createContextAsUser(any(), anyInt());
- doReturn(mSearchManager).when(mContext)
- .getSystemService(eq(Context.SEARCH_SERVICE));
+ setupAssistForLaunch();
mPhoneWindowManager.mLongPressOnPowerAssistantTimeoutMs = 500;
break;
}
@@ -433,6 +432,22 @@ class TestPhoneWindowManager {
doReturn(isShowing).when(mKeyguardServiceDelegate).isShowing();
}
+ void setupAssistForLaunch() {
+ doNothing().when(mPhoneWindowManager).sendCloseSystemWindows();
+ doReturn(true).when(mPhoneWindowManager).isUserSetupComplete();
+ doReturn(mContext).when(mContext).createContextAsUser(any(), anyInt());
+ }
+
+ void overrideSearchManager(SearchManager searchManager) {
+ mPhoneWindowManager.mSearchManager = searchManager;
+ }
+
+ void assumeResolveActivityNotNull() {
+ ResolveInfo resolveInfo = new ResolveInfo();
+ doReturn(resolveInfo).when(mPackageManager).resolveActivity(any(), anyInt());
+ doReturn(mPackageManager).when(mContext).getPackageManager();
+ }
+
void overrideKeyEventSource(int vendorId, int productId) {
InputDevice device = new InputDevice.Builder().setId(1).setVendorId(vendorId).setProductId(
productId).setSources(InputDevice.SOURCE_KEYBOARD).setKeyboardType(
@@ -458,6 +473,10 @@ class TestPhoneWindowManager {
doReturn(true).when(mPhoneWindowManager).isUserSetupComplete();
}
+ void overrideStemPressTargetActivity(ComponentName component) {
+ mPhoneWindowManager.mPrimaryShortPressTargetActivity = component;
+ }
+
/**
* Below functions will check the policy behavior could be invoked.
*/
@@ -517,7 +536,7 @@ class TestPhoneWindowManager {
.interceptPowerKeyDown(any(), anyBoolean(), any());
}
- void assertAssistLaunch() {
+ void assertSearchManagerLaunchAssist() {
waitForIdle();
verify(mSearchManager, timeout(SHORTCUT_KEY_DELAY_MILLIS)).launchAssist(any());
}
@@ -540,6 +559,11 @@ class TestPhoneWindowManager {
verify(mStatusBarManagerInternal).showRecentApps(anyBoolean());
}
+ void assertStatusBarStartAssist() {
+ waitForIdle();
+ verify(mStatusBarManagerInternal).startAssist(any());
+ }
+
void assertSwitchKeyboardLayout(int direction) {
waitForIdle();
if (FeatureFlagUtils.isEnabled(mContext, FeatureFlagUtils.SETTINGS_NEW_KEYBOARD_UI)) {
@@ -613,6 +637,14 @@ class TestPhoneWindowManager {
.startActivityAsUser(any(Intent.class), any(), any(UserHandle.class));
}
+ void assertActivityTargetLaunched(ComponentName targetActivity) {
+ waitForIdle();
+ ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
+ verify(mContext, timeout(TEST_SINGLE_KEY_DELAY_MILLIS))
+ .startActivityAsUser(intentCaptor.capture(), isNull(), any(UserHandle.class));
+ Assert.assertEquals(targetActivity, intentCaptor.getValue().getComponent());
+ }
+
void assertShortcutLogged(int vendorId, int productId, KeyboardLogEvent logEvent,
int expectedKey, int expectedModifierState, String errorMsg) {
waitForIdle();
diff --git a/services/usage/java/com/android/server/usage/StorageStatsService.java b/services/usage/java/com/android/server/usage/StorageStatsService.java
index befc4a0120b1..4c978ad01a81 100644
--- a/services/usage/java/com/android/server/usage/StorageStatsService.java
+++ b/services/usage/java/com/android/server/usage/StorageStatsService.java
@@ -259,7 +259,24 @@ public class StorageStatsService extends IStorageStatsManager.Stub {
// NOTE: No permissions required
if (volumeUuid == StorageManager.UUID_PRIVATE_INTERNAL) {
- return FileUtils.roundStorageSize(mStorage.getPrimaryStorageSize());
+ // As a safety measure, use the original implementation for the devices
+ // with storage size <= 512GB to prevent any potential regressions
+ final long roundedUserspaceBytes = mStorage.getPrimaryStorageSize();
+ if (roundedUserspaceBytes <= DataUnit.GIGABYTES.toBytes(512)) {
+ return roundedUserspaceBytes;
+ }
+
+ // Since 1TB devices can actually have either 1000GB or 1024GB,
+ // get the block device size and do just a small rounding if any at all
+ final long totalBytes = mStorage.getInternalStorageBlockDeviceSize();
+ final long totalBytesRounded = FileUtils.roundStorageSize(totalBytes);
+ // If the storage size is 997GB-999GB, round it to a 1000GB to show
+ // 1TB in UI instead of 0.99TB. Same for 2TB, 4TB, 8TB etc.
+ if (totalBytesRounded - totalBytes <= DataUnit.GIGABYTES.toBytes(3)) {
+ return totalBytesRounded;
+ } else {
+ return totalBytes;
+ }
} else {
final VolumeInfo vol = mStorage.findVolumeByUuid(volumeUuid);
if (vol == null) {
@@ -286,15 +303,19 @@ public class StorageStatsService extends IStorageStatsManager.Stub {
// Free space is usable bytes plus any cached data that we're
// willing to automatically clear. To avoid user confusion, this
// logic should be kept in sync with getAllocatableBytes().
+ long freeBytes;
if (isQuotaSupported(volumeUuid, PLATFORM_PACKAGE_NAME)) {
final long cacheTotal = getCacheBytes(volumeUuid, PLATFORM_PACKAGE_NAME);
final long cacheReserved = mStorage.getStorageCacheBytes(path, 0);
final long cacheClearable = Math.max(0, cacheTotal - cacheReserved);
- return path.getUsableSpace() + cacheClearable;
+ freeBytes = path.getUsableSpace() + cacheClearable;
} else {
- return path.getUsableSpace();
+ freeBytes = path.getUsableSpace();
}
+
+ Slog.d(TAG, "getFreeBytes: " + freeBytes);
+ return freeBytes;
} finally {
Binder.restoreCallingIdentity(token);
}
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 4907134ffe1f..13e5ff15f1d8 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -10408,7 +10408,7 @@ public class CarrierConfigManager {
sDefaults.putIntArray(KEY_DISCONNECT_CAUSE_PLAY_BUSYTONE_INT_ARRAY,
new int[] {4 /* BUSY */});
sDefaults.putBoolean(KEY_PREVENT_CLIR_ACTIVATION_AND_DEACTIVATION_CODE_BOOL, false);
- sDefaults.putLong(KEY_DATA_SWITCH_VALIDATION_TIMEOUT_LONG, 2000);
+ sDefaults.putLong(KEY_DATA_SWITCH_VALIDATION_TIMEOUT_LONG, 5000);
sDefaults.putStringArray(KEY_MMI_TWO_DIGIT_NUMBER_PATTERN_STRING_ARRAY, new String[0]);
sDefaults.putInt(KEY_PARAMETERS_USED_FOR_LTE_SIGNAL_BAR_INT,
CellSignalStrengthLte.USE_RSRP);
@@ -10518,7 +10518,7 @@ public class CarrierConfigManager {
auto_data_switch_rat_signal_score_string_bundle.putIntArray(
"eHRPD", new int[]{10, 400, 600, 800, 1000});
auto_data_switch_rat_signal_score_string_bundle.putIntArray(
- "TD_SCDMA", new int[]{1, 100, 500, 1000});
+ "TD_SCDMA", new int[]{1, 50, 100, 500, 1000});
auto_data_switch_rat_signal_score_string_bundle.putIntArray(
"iDEN", new int[]{1, 2, 10, 50, 100});
auto_data_switch_rat_signal_score_string_bundle.putIntArray(
@@ -10536,7 +10536,7 @@ public class CarrierConfigManager {
auto_data_switch_rat_signal_score_string_bundle.putIntArray(
"EvDo_0", new int[]{300, 600, 1000, 1500, 2000});
auto_data_switch_rat_signal_score_string_bundle.putIntArray(
- "1xRTT", new int[]{50, 60, 70, 80});
+ "1xRTT", new int[]{50, 60, 70, 80, 90});
auto_data_switch_rat_signal_score_string_bundle.putIntArray(
"EDGE", new int[]{154, 169, 183, 192, 267});
auto_data_switch_rat_signal_score_string_bundle.putIntArray(
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index baacb574bde3..12ab5c32ad49 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -3396,15 +3396,11 @@ public class SubscriptionManager {
if (iSub != null) {
groupUuid = iSub.createSubscriptionGroup(subIdArray, pkgForDebug);
} else {
- if (!isSystemProcess()) {
- throw new IllegalStateException("telephony service is null.");
- }
+ throw new IllegalStateException("telephony service is null.");
}
} catch (RemoteException ex) {
loge("createSubscriptionGroup RemoteException " + ex);
- if (!isSystemProcess()) {
- ex.rethrowAsRuntimeException();
- }
+ ex.rethrowAsRuntimeException();
}
return groupUuid;
@@ -3446,15 +3442,11 @@ public class SubscriptionManager {
if (iSub != null) {
iSub.addSubscriptionsIntoGroup(subIdArray, groupUuid, pkgForDebug);
} else {
- if (!isSystemProcess()) {
- throw new IllegalStateException("telephony service is null.");
- }
+ throw new IllegalStateException("telephony service is null.");
}
} catch (RemoteException ex) {
loge("addSubscriptionsIntoGroup RemoteException " + ex);
- if (!isSystemProcess()) {
- ex.rethrowAsRuntimeException();
- }
+ ex.rethrowAsRuntimeException();
}
}
@@ -3497,15 +3489,11 @@ public class SubscriptionManager {
if (iSub != null) {
iSub.removeSubscriptionsFromGroup(subIdArray, groupUuid, callingPackage);
} else {
- if (!isSystemProcess()) {
- throw new IllegalStateException("telephony service is null.");
- }
+ throw new IllegalStateException("telephony service is null.");
}
} catch (RemoteException ex) {
loge("removeSubscriptionsFromGroup RemoteException " + ex);
- if (!isSystemProcess()) {
- ex.rethrowAsRuntimeException();
- }
+ ex.rethrowAsRuntimeException();
}
}
@@ -3562,6 +3550,11 @@ public class SubscriptionManager {
}
}
+ // TODO(b/296125268) Really this method should throw, but it's common enough that for
+ // system callers it's worth having a little magic for the system process until it's
+ // made safer.
+ if (result == null) result = Collections.emptyList();
+
return result;
}
diff --git a/telephony/java/android/telephony/satellite/SatelliteManager.java b/telephony/java/android/telephony/satellite/SatelliteManager.java
index c498216cf75b..9994002a4a37 100644
--- a/telephony/java/android/telephony/satellite/SatelliteManager.java
+++ b/telephony/java/android/telephony/satellite/SatelliteManager.java
@@ -57,7 +57,7 @@ import java.util.function.Consumer;
*/
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_SATELLITE)
@SystemApi
-public class SatelliteManager {
+public final class SatelliteManager {
private static final String TAG = "SatelliteManager";
private static final ConcurrentHashMap<SatelliteDatagramCallback, ISatelliteDatagramCallback>
@@ -1545,11 +1545,11 @@ public class SatelliteManager {
*/
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
- public void onDeviceAlignedWithSatellite(boolean isAligned) {
+ public void setDeviceAlignedWithSatellite(boolean isAligned) {
try {
ITelephony telephony = getITelephony();
if (telephony != null) {
- telephony.onDeviceAlignedWithSatellite(mSubId, isAligned);
+ telephony.setDeviceAlignedWithSatellite(mSubId, isAligned);
} else {
throw new IllegalStateException("telephony service is null.");
}
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 0c3991d07ab6..06071feccf69 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -2978,7 +2978,7 @@ interface ITelephony {
*/
@JavaPassthrough(annotation="@android.annotation.RequiresPermission("
+ "android.Manifest.permission.SATELLITE_COMMUNICATION)")
- void onDeviceAlignedWithSatellite(int subId, in boolean isAligned);
+ void setDeviceAlignedWithSatellite(int subId, in boolean isAligned);
/**
* This API can be used by only CTS to update satellite vendor service package name.
diff --git a/tests/PlatformCompatGating/src/com/android/compat/testing/DummyApi.java b/tests/PlatformCompatGating/src/com/android/compat/testing/DummyApi.java
index 731be8e3d9f0..a77950fe4a2d 100644
--- a/tests/PlatformCompatGating/src/com/android/compat/testing/DummyApi.java
+++ b/tests/PlatformCompatGating/src/com/android/compat/testing/DummyApi.java
@@ -24,7 +24,7 @@ import android.os.ServiceManager;
import com.android.internal.compat.IPlatformCompat;
/**
- * This is a dummy API to test gating
+ * This is a placeholder API to test gating
*
* @hide
*/
@@ -36,7 +36,7 @@ public class DummyApi {
public static final long CHANGE_SYSTEM_SERVER = 666016;
/**
- * Dummy method
+ * Placeholder method
* @return "A" if change is enabled, "B" otherwise.
*/
public static String dummyFunc() {
@@ -47,7 +47,7 @@ public class DummyApi {
}
/**
- * Dummy combined method
+ * Placeholder combined method
* @return "0" if {@link CHANGE_ID_1} is disabled and {@link CHANGE_ID_2} is disabled,
"1" if {@link CHANGE_ID_1} is disabled and {@link CHANGE_ID_2} is enabled,
"2" if {@link CHANGE_ID_1} is enabled and {@link CHANGE_ID_2} is disabled,
@@ -68,7 +68,7 @@ public class DummyApi {
}
/**
- * Dummy api using system server API.
+ * Placeholder api using system server API.
*/
public static boolean dummySystemServer(Context context) {
IPlatformCompat platformCompat = IPlatformCompat.Stub
diff --git a/tests/SilkFX/src/com/android/test/silkfx/hdr/GainmapMetadataEditor.kt b/tests/SilkFX/src/com/android/test/silkfx/hdr/GainmapMetadataEditor.kt
index c4bc6001533e..43debb11013a 100644
--- a/tests/SilkFX/src/com/android/test/silkfx/hdr/GainmapMetadataEditor.kt
+++ b/tests/SilkFX/src/com/android/test/silkfx/hdr/GainmapMetadataEditor.kt
@@ -98,23 +98,23 @@ class GainmapMetadataEditor(val parent: ViewGroup, val renderView: View) {
(view.getParent() as ViewGroup).removeView(view)
parent.addView(view)
- view.findViewById<Button>(R.id.gainmap_metadata_done)!!.setOnClickListener {
+ view.requireViewById<Button>(R.id.gainmap_metadata_done).setOnClickListener {
closeEditor()
}
- view.findViewById<Button>(R.id.gainmap_metadata_reset)!!.setOnClickListener {
+ view.requireViewById<Button>(R.id.gainmap_metadata_reset).setOnClickListener {
resetGainmapMetadata()
}
updateMetadataUi()
- val gainmapMinSeek = view.findViewById<SeekBar>(R.id.gainmap_metadata_gainmapmin)
- val gainmapMaxSeek = view.findViewById<SeekBar>(R.id.gainmap_metadata_gainmapmax)
- val capacityMinSeek = view.findViewById<SeekBar>(R.id.gainmap_metadata_capacitymin)
- val capacityMaxSeek = view.findViewById<SeekBar>(R.id.gainmap_metadata_capacitymax)
- val gammaSeek = view.findViewById<SeekBar>(R.id.gainmap_metadata_gamma)
- val offsetSdrSeek = view.findViewById<SeekBar>(R.id.gainmap_metadata_offsetsdr)
- val offsetHdrSeek = view.findViewById<SeekBar>(R.id.gainmap_metadata_offsethdr)
+ val gainmapMinSeek = view.requireViewById<SeekBar>(R.id.gainmap_metadata_gainmapmin)
+ val gainmapMaxSeek = view.requireViewById<SeekBar>(R.id.gainmap_metadata_gainmapmax)
+ val capacityMinSeek = view.requireViewById<SeekBar>(R.id.gainmap_metadata_capacitymin)
+ val capacityMaxSeek = view.requireViewById<SeekBar>(R.id.gainmap_metadata_capacitymax)
+ val gammaSeek = view.requireViewById<SeekBar>(R.id.gainmap_metadata_gamma)
+ val offsetSdrSeek = view.requireViewById<SeekBar>(R.id.gainmap_metadata_offsetsdr)
+ val offsetHdrSeek = view.requireViewById<SeekBar>(R.id.gainmap_metadata_offsethdr)
arrayOf(gainmapMinSeek, gainmapMaxSeek, capacityMinSeek, capacityMaxSeek, gammaSeek,
offsetSdrSeek, offsetHdrSeek).forEach {
it.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener{
@@ -139,13 +139,13 @@ class GainmapMetadataEditor(val parent: ViewGroup, val renderView: View) {
}
private fun updateMetadataUi() {
- val gainmapMinSeek = parent.findViewById<SeekBar>(R.id.gainmap_metadata_gainmapmin)
- val gainmapMaxSeek = parent.findViewById<SeekBar>(R.id.gainmap_metadata_gainmapmax)
- val capacityMinSeek = parent.findViewById<SeekBar>(R.id.gainmap_metadata_capacitymin)
- val capacityMaxSeek = parent.findViewById<SeekBar>(R.id.gainmap_metadata_capacitymax)
- val gammaSeek = parent.findViewById<SeekBar>(R.id.gainmap_metadata_gamma)
- val offsetSdrSeek = parent.findViewById<SeekBar>(R.id.gainmap_metadata_offsetsdr)
- val offsetHdrSeek = parent.findViewById<SeekBar>(R.id.gainmap_metadata_offsethdr)
+ val gainmapMinSeek = parent.requireViewById<SeekBar>(R.id.gainmap_metadata_gainmapmin)
+ val gainmapMaxSeek = parent.requireViewById<SeekBar>(R.id.gainmap_metadata_gainmapmax)
+ val capacityMinSeek = parent.requireViewById<SeekBar>(R.id.gainmap_metadata_capacitymin)
+ val capacityMaxSeek = parent.requireViewById<SeekBar>(R.id.gainmap_metadata_capacitymax)
+ val gammaSeek = parent.requireViewById<SeekBar>(R.id.gainmap_metadata_gamma)
+ val offsetSdrSeek = parent.requireViewById<SeekBar>(R.id.gainmap_metadata_offsetsdr)
+ val offsetHdrSeek = parent.requireViewById<SeekBar>(R.id.gainmap_metadata_offsethdr)
gainmapMinSeek.setProgress(
((currentMetadata.ratioMin - minRatioMin) / maxRatioMin * maxProgress).toInt())
@@ -165,19 +165,19 @@ class GainmapMetadataEditor(val parent: ViewGroup, val renderView: View) {
((1.0 - Math.log(currentMetadata.offsetHdr.toDouble() / Math.log(3.0)) / -11.0)
.toFloat() * maxProgress).toInt())
- parent.findViewById<TextView>(R.id.gainmap_metadata_gainmapmin_val)!!.setText(
+ parent.requireViewById<TextView>(R.id.gainmap_metadata_gainmapmin_val).setText(
"%.3f".format(currentMetadata.ratioMin))
- parent.findViewById<TextView>(R.id.gainmap_metadata_gainmapmax_val)!!.setText(
+ parent.requireViewById<TextView>(R.id.gainmap_metadata_gainmapmax_val).setText(
"%.3f".format(currentMetadata.ratioMax))
- parent.findViewById<TextView>(R.id.gainmap_metadata_capacitymin_val)!!.setText(
+ parent.requireViewById<TextView>(R.id.gainmap_metadata_capacitymin_val).setText(
"%.3f".format(currentMetadata.capacityMin))
- parent.findViewById<TextView>(R.id.gainmap_metadata_capacitymax_val)!!.setText(
+ parent.requireViewById<TextView>(R.id.gainmap_metadata_capacitymax_val).setText(
"%.3f".format(currentMetadata.capacityMax))
- parent.findViewById<TextView>(R.id.gainmap_metadata_gamma_val)!!.setText(
+ parent.requireViewById<TextView>(R.id.gainmap_metadata_gamma_val).setText(
"%.3f".format(currentMetadata.gamma))
- parent.findViewById<TextView>(R.id.gainmap_metadata_offsetsdr_val)!!.setText(
+ parent.requireViewById<TextView>(R.id.gainmap_metadata_offsetsdr_val).setText(
"%.5f".format(currentMetadata.offsetSdr))
- parent.findViewById<TextView>(R.id.gainmap_metadata_offsethdr_val)!!.setText(
+ parent.requireViewById<TextView>(R.id.gainmap_metadata_offsethdr_val).setText(
"%.5f".format(currentMetadata.offsetHdr))
}
@@ -200,7 +200,7 @@ class GainmapMetadataEditor(val parent: ViewGroup, val renderView: View) {
private fun updateGainmapMin(normalized: Float) {
val newValue = minRatioMin + normalized * (maxRatioMin - minRatioMin)
- parent.findViewById<TextView>(R.id.gainmap_metadata_gainmapmin_val)!!.setText(
+ parent.requireViewById<TextView>(R.id.gainmap_metadata_gainmapmin_val).setText(
"%.3f".format(newValue))
currentMetadata.ratioMin = newValue
gainmap.setRatioMin(newValue, newValue, newValue)
@@ -209,7 +209,7 @@ class GainmapMetadataEditor(val parent: ViewGroup, val renderView: View) {
private fun updateGainmapMax(normalized: Float) {
val newValue = minRatioMax + normalized * (maxRatioMax - minRatioMax)
- parent.findViewById<TextView>(R.id.gainmap_metadata_gainmapmax_val)!!.setText(
+ parent.requireViewById<TextView>(R.id.gainmap_metadata_gainmapmax_val).setText(
"%.3f".format(newValue))
currentMetadata.ratioMax = newValue
gainmap.setRatioMax(newValue, newValue, newValue)
@@ -218,7 +218,7 @@ class GainmapMetadataEditor(val parent: ViewGroup, val renderView: View) {
private fun updateCapacityMin(normalized: Float) {
val newValue = minCapacityMin + normalized * (maxCapacityMin - minCapacityMin)
- parent.findViewById<TextView>(R.id.gainmap_metadata_capacitymin_val)!!.setText(
+ parent.requireViewById<TextView>(R.id.gainmap_metadata_capacitymin_val).setText(
"%.3f".format(newValue))
currentMetadata.capacityMin = newValue
gainmap.setMinDisplayRatioForHdrTransition(newValue)
@@ -227,7 +227,7 @@ class GainmapMetadataEditor(val parent: ViewGroup, val renderView: View) {
private fun updateCapacityMax(normalized: Float) {
val newValue = minCapacityMax + normalized * (maxCapacityMax - minCapacityMax)
- parent.findViewById<TextView>(R.id.gainmap_metadata_capacitymax_val)!!.setText(
+ parent.requireViewById<TextView>(R.id.gainmap_metadata_capacitymax_val).setText(
"%.3f".format(newValue))
currentMetadata.capacityMax = newValue
gainmap.setDisplayRatioForFullHdr(newValue)
@@ -236,7 +236,7 @@ class GainmapMetadataEditor(val parent: ViewGroup, val renderView: View) {
private fun updateGamma(normalized: Float) {
val newValue = minGamma + normalized * (maxGamma - minGamma)
- parent.findViewById<TextView>(R.id.gainmap_metadata_gamma_val)!!.setText(
+ parent.requireViewById<TextView>(R.id.gainmap_metadata_gamma_val).setText(
"%.3f".format(newValue))
currentMetadata.gamma = newValue
gainmap.setGamma(newValue, newValue, newValue)
@@ -248,7 +248,7 @@ class GainmapMetadataEditor(val parent: ViewGroup, val renderView: View) {
if (normalized > 0.0f ) {
newValue = Math.pow(3.0, (1.0 - normalized.toDouble()) * -11.0).toFloat()
}
- parent.findViewById<TextView>(R.id.gainmap_metadata_offsetsdr_val)!!.setText(
+ parent.requireViewById<TextView>(R.id.gainmap_metadata_offsetsdr_val).setText(
"%.5f".format(newValue))
currentMetadata.offsetSdr = newValue
gainmap.setEpsilonSdr(newValue, newValue, newValue)
@@ -260,7 +260,7 @@ class GainmapMetadataEditor(val parent: ViewGroup, val renderView: View) {
if (normalized > 0.0f ) {
newValue = Math.pow(3.0, (1.0 - normalized.toDouble()) * -11.0).toFloat()
}
- parent.findViewById<TextView>(R.id.gainmap_metadata_offsethdr_val)!!.setText(
+ parent.requireViewById<TextView>(R.id.gainmap_metadata_offsethdr_val).setText(
"%.5f".format(newValue))
currentMetadata.offsetHdr = newValue
gainmap.setEpsilonHdr(newValue, newValue, newValue)
diff --git a/tests/SilkFX/src/com/android/test/silkfx/materials/GlassView.kt b/tests/SilkFX/src/com/android/test/silkfx/materials/GlassView.kt
index 2f2578b87f35..41baeadf7a8c 100644
--- a/tests/SilkFX/src/com/android/test/silkfx/materials/GlassView.kt
+++ b/tests/SilkFX/src/com/android/test/silkfx/materials/GlassView.kt
@@ -190,9 +190,9 @@ class GlassView(context: Context, attributeSet: AttributeSet) : FrameLayout(cont
sensorManager?.unregisterListener(sensorListener)
}
- override fun onDraw(canvas: Canvas?) {
+ override fun onDraw(canvas: Canvas) {
updateGlassRenderNode()
- canvas?.drawRenderNode(renderNode)
+ canvas.drawRenderNode(renderNode)
}
fun resetGyroOffsets() {
@@ -227,4 +227,4 @@ class GlassView(context: Context, attributeSet: AttributeSet) : FrameLayout(cont
renderNodeIsDirty = false
}
}
-} \ No newline at end of file
+}
diff --git a/tests/utils/testutils/java/com/android/internal/util/test/LocalServiceKeeperRule.java b/tests/utils/testutils/java/com/android/internal/util/test/LocalServiceKeeperRule.java
new file mode 100644
index 000000000000..7012daf586d8
--- /dev/null
+++ b/tests/utils/testutils/java/com/android/internal/util/test/LocalServiceKeeperRule.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.util.test;
+
+import com.android.server.LocalServices;
+
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * JUnit Rule helps override /restore {@link LocalServices} state.
+ */
+public class LocalServiceKeeperRule implements TestRule {
+
+ private final Map<Class<?>, Object> mOverriddenServices = new HashMap<>();
+ private final List<Class<?>> mAddedServices = new ArrayList<>();
+
+ private volatile boolean mRuleApplied = false;
+
+ /**
+ * Overrides service in LocalServices. Service will be restored to original after test run.
+ */
+ public <T> void overrideLocalService(Class<T> type, T service) {
+ if (!mRuleApplied) {
+ throw new IllegalStateException("Can't override service without applying rule");
+ }
+ if (mOverriddenServices.containsKey(type) || mAddedServices.contains(type)) {
+ throw new IllegalArgumentException("Type already overridden: " + type);
+ }
+
+ T currentService = LocalServices.getService(type);
+ if (currentService != null) {
+ mOverriddenServices.put(type, currentService);
+ LocalServices.removeServiceForTest(type);
+ } else {
+ mAddedServices.add(type);
+ }
+ LocalServices.addService(type, service);
+ }
+
+ @Override
+ public Statement apply(Statement base, Description description) {
+ return new Statement() {
+ public void evaluate() throws Throwable {
+ try {
+ mRuleApplied = true;
+ base.evaluate();
+ } finally {
+ mRuleApplied = false;
+ mAddedServices.forEach(LocalServices::removeServiceForTest);
+ mOverriddenServices.forEach((clazz, service) -> {
+ LocalServices.removeServiceForTest(clazz);
+ LocalServices.addService((Class) clazz, service);
+ });
+ mAddedServices.clear();
+ mOverriddenServices.clear();
+ }
+ }
+ };
+ }
+}
diff --git a/tests/utils/testutils/tests/src/com/android/internal/util/test/LocalServiceKeeperRuleTest.java b/tests/utils/testutils/tests/src/com/android/internal/util/test/LocalServiceKeeperRuleTest.java
new file mode 100644
index 000000000000..12f2fae0a581
--- /dev/null
+++ b/tests/utils/testutils/tests/src/com/android/internal/util/test/LocalServiceKeeperRuleTest.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.util.test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThrows;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.server.LocalServices;
+
+import org.junit.After;
+import org.junit.Test;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+@SmallTest
+public class LocalServiceKeeperRuleTest {
+
+ private final Description mDescription = Description.createSuiteDescription("Description");
+
+ LocalServiceKeeperRule mRule = new LocalServiceKeeperRule();
+
+ @After
+ public void tearDown() {
+ LocalServices.removeServiceForTest(TestService.class);
+ }
+
+ @Test
+ public void testFailedIfCalledOutsideOfTheRule() {
+ TestService service = new TestService() {};
+
+ assertThrows(IllegalStateException.class,
+ () -> mRule.overrideLocalService(TestService.class, service));
+ }
+
+ @Test
+ public void testSetsLocalServiceIfNotPresent() throws Throwable {
+ LocalServices.removeServiceForTest(TestService.class);
+ TestService service = new TestService() {};
+
+ runInRuleApply(() -> {
+ mRule.overrideLocalService(TestService.class, service);
+ assertEquals(service, LocalServices.getService(TestService.class));
+ });
+ }
+
+ @Test
+ public void testOverridesLocalServiceIfPresent() throws Throwable {
+ TestService service = new TestService() {};
+ LocalServices.addService(TestService.class, service);
+ TestService overriddenService = new TestService() {};
+
+ runInRuleApply(() -> {
+ mRule.overrideLocalService(TestService.class, overriddenService);
+ assertEquals(overriddenService, LocalServices.getService(TestService.class));
+ });
+ }
+
+ @Test
+ public void testDoesNotAllowToOverrideSameServiceTwice() throws Throwable {
+ TestService service = new TestService() {};
+
+ runInRuleApply(() -> {
+ mRule.overrideLocalService(TestService.class, service);
+ assertThrows(IllegalArgumentException.class,
+ () -> mRule.overrideLocalService(TestService.class, service));
+ });
+ }
+
+ @Test
+ public void testRestroresLocalServiceAfterTestIfPresent() throws Throwable {
+ TestService expectedService = new TestService() {};
+ LocalServices.addService(TestService.class, expectedService);
+ TestService overriddenService = new TestService() {};
+
+ runInRuleApply(() -> mRule.overrideLocalService(TestService.class, overriddenService));
+
+ assertEquals(expectedService, LocalServices.getService(TestService.class));
+ }
+
+ @Test
+ public void testRemovesLocalServiceAfterTestIfNotPresent() throws Throwable {
+ LocalServices.removeServiceForTest(TestService.class);
+ TestService service = new TestService() {};
+
+ runInRuleApply(() -> mRule.overrideLocalService(TestService.class, service));
+
+ assertNull(LocalServices.getService(TestService.class));
+ }
+
+ private void runInRuleApply(Runnable runnable) throws Throwable {
+ Statement testStatement = new Statement() {
+ @Override
+ public void evaluate() {
+ runnable.run();
+ }
+ };
+ mRule.apply(testStatement, mDescription).evaluate();
+ }
+
+ interface TestService {
+ }
+}