summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.bp2
-rw-r--r--ApiDocs.bp1
-rw-r--r--apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java16
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java16
-rw-r--r--apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java51
-rw-r--r--apex/jobscheduler/service/java/com/android/server/usage/TEST_MAPPING4
-rw-r--r--apex/media/framework/Android.bp3
-rw-r--r--apex/media/framework/lint-baseline.xml324
-rw-r--r--apex/media/service/Android.bp4
-rw-r--r--apex/media/service/java/com/android/server/media/MediaCommunicationService.java3
-rw-r--r--apex/media/service/java/com/android/server/media/SessionPriorityList.java6
-rw-r--r--apex/media/service/lint-baseline.xml35
-rw-r--r--api/Android.bp2
-rw-r--r--api/api.go14
-rw-r--r--boot/Android.bp8
-rw-r--r--cmds/incidentd/src/WorkDirectory.cpp2
-rw-r--r--core/api/current.txt127
-rw-r--r--core/api/module-lib-current.txt53
-rw-r--r--core/api/system-current.txt286
-rw-r--r--core/api/system-lint-baseline.txt1
-rw-r--r--core/api/test-current.txt35
-rw-r--r--core/java/Android.bp6
-rw-r--r--core/java/android/accessibilityservice/TouchInteractionController.java2
-rw-r--r--core/java/android/app/Activity.java82
-rw-r--r--core/java/android/app/ActivityClient.java6
-rw-r--r--core/java/android/app/ActivityOptions.java50
-rw-r--r--core/java/android/app/ActivityThread.java160
-rw-r--r--core/java/android/app/AppOpsManager.java32
-rw-r--r--core/java/android/app/ApplicationPackageManager.java70
-rw-r--r--core/java/android/app/AsyncNotedAppOp.java30
-rw-r--r--core/java/android/app/ClientTransactionHandler.java5
-rw-r--r--core/java/android/app/ConfigurationController.java9
-rw-r--r--core/java/android/app/ContextImpl.java11
-rw-r--r--core/java/android/app/IActivityClientController.aidl2
-rw-r--r--core/java/android/app/IActivityManager.aidl2
-rw-r--r--core/java/android/app/IApplicationThread.aidl1
-rw-r--r--core/java/android/app/KeyguardManager.java4
-rw-r--r--core/java/android/app/LoadedApk.java4
-rw-r--r--core/java/android/app/Notification.java18
-rw-r--r--core/java/android/app/PropertyInvalidatedCache.java327
-rw-r--r--core/java/android/app/ResourcesManager.java68
-rw-r--r--core/java/android/app/SyncNotedAppOp.java25
-rw-r--r--core/java/android/app/SystemServiceRegistry.java7
-rw-r--r--core/java/android/app/TaskInfo.java15
-rw-r--r--core/java/android/app/WindowConfiguration.java51
-rw-r--r--core/java/android/app/admin/DevicePolicyManager.java111
-rw-r--r--core/java/android/app/admin/DevicePolicyResources.java1539
-rw-r--r--core/java/android/app/admin/FullyManagedDeviceProvisioningParams.java42
-rw-r--r--core/java/android/app/admin/ManagedProfileProvisioningParams.java38
-rw-r--r--core/java/android/app/admin/ParcelableResource.java24
-rw-r--r--core/java/android/app/servertransaction/FixedRotationAdjustmentsItem.java113
-rw-r--r--core/java/android/app/servertransaction/LaunchActivityItem.java22
-rw-r--r--core/java/android/app/smartspace/SmartspaceTarget.java67
-rw-r--r--core/java/android/app/smartspace/SmartspaceUtils.java37
-rw-r--r--core/java/android/app/smartspace/uitemplatedata/SmartspaceCarouselUiTemplateData.java360
-rw-r--r--core/java/android/app/smartspace/uitemplatedata/SmartspaceCombinedCardsUiTemplateData.java155
-rw-r--r--core/java/android/app/smartspace/uitemplatedata/SmartspaceDefaultUiTemplateData.java459
-rw-r--r--core/java/android/app/smartspace/uitemplatedata/SmartspaceHeadToHeadUiTemplateData.java286
-rw-r--r--core/java/android/app/smartspace/uitemplatedata/SmartspaceIcon.java148
-rw-r--r--core/java/android/app/smartspace/uitemplatedata/SmartspaceSubCardUiTemplateData.java198
-rw-r--r--core/java/android/app/smartspace/uitemplatedata/SmartspaceSubImageUiTemplateData.java192
-rw-r--r--core/java/android/app/smartspace/uitemplatedata/SmartspaceSubListUiTemplateData.java196
-rw-r--r--core/java/android/app/smartspace/uitemplatedata/SmartspaceTapAction.java232
-rw-r--r--core/java/android/apphibernation/AppHibernationManager.java37
-rw-r--r--core/java/android/apphibernation/HibernationStats.aidl19
-rw-r--r--core/java/android/apphibernation/HibernationStats.java70
-rw-r--r--core/java/android/apphibernation/IAppHibernationService.aidl4
-rw-r--r--core/java/android/companion/virtual/VirtualDeviceParams.java22
-rw-r--r--core/java/android/content/Context.java57
-rw-r--r--core/java/android/content/Intent.java5
-rw-r--r--core/java/android/content/pm/CrossProfileApps.java3
-rw-r--r--core/java/android/content/pm/PackageInfoLite.java10
-rw-r--r--core/java/android/content/pm/PackageManager.java8
-rw-r--r--core/java/android/content/pm/PermissionInfo.java3
-rw-r--r--core/java/android/content/pm/SharedLibraryInfo.java20
-rw-r--r--core/java/android/content/pm/TEST_MAPPING63
-rw-r--r--core/java/android/content/res/ApkAssets.java7
-rw-r--r--core/java/android/content/res/AssetManager.java10
-rw-r--r--core/java/android/content/res/IResourcesManager.aidl30
-rw-r--r--core/java/android/content/res/Resources.java65
-rw-r--r--core/java/android/content/res/ResourcesImpl.java7
-rw-r--r--core/java/android/database/CursorWindow.java5
-rw-r--r--core/java/android/database/CursorWindowAllocationException.java6
-rw-r--r--core/java/android/hardware/SensorPrivacyManager.java71
-rw-r--r--core/java/android/hardware/biometrics/IBiometricContextListener.aidl27
-rw-r--r--core/java/android/hardware/devicestate/DeviceStateManager.java13
-rw-r--r--core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java39
-rw-r--r--core/java/android/hardware/devicestate/DeviceStateRequest.java3
-rw-r--r--core/java/android/hardware/devicestate/IDeviceStateManager.aidl16
-rw-r--r--core/java/android/hardware/devicestate/IDeviceStateManagerCallback.aidl10
-rw-r--r--core/java/android/hardware/display/BrightnessInfo.java46
-rw-r--r--core/java/android/hardware/hdmi/HdmiClient.java20
-rw-r--r--core/java/android/hardware/hdmi/HdmiControlManager.java6
-rw-r--r--core/java/android/hardware/hdmi/HdmiControlServiceWrapper.java6
-rw-r--r--core/java/android/hardware/hdmi/IHdmiControlService.aidl2
-rw-r--r--core/java/android/hardware/input/IInputManager.aidl4
-rw-r--r--core/java/android/hardware/input/InputManager.java17
-rw-r--r--core/java/android/hardware/usb/UsbManager.java1
-rw-r--r--core/java/android/inputmethodservice/IInputMethodWrapper.java36
-rw-r--r--core/java/android/inputmethodservice/InputMethodService.java31
-rw-r--r--core/java/android/inputmethodservice/NavigationBarController.java164
-rw-r--r--core/java/android/inputmethodservice/SoftInputWindow.java10
-rw-r--r--core/java/android/inputmethodservice/navigationbar/DeadZone.java17
-rw-r--r--core/java/android/net/NetworkPolicyManager.java6
-rw-r--r--core/java/android/net/netstats/NetworkStatsDataMigrationUtils.java72
-rw-r--r--core/java/android/os/BatteryConsumer.java23
-rw-r--r--core/java/android/os/BatteryStats.java126
-rw-r--r--core/java/android/os/BatteryStatsManager.java38
-rw-r--r--core/java/android/os/IPowerManager.aidl11
-rw-r--r--core/java/android/os/IWakeLockCallback.aidl24
-rw-r--r--core/java/android/os/PowerComponents.java18
-rw-r--r--core/java/android/os/PowerManager.java180
-rw-r--r--core/java/android/os/PowerManagerInternal.java15
-rw-r--r--core/java/android/os/VibrationAttributes.java32
-rw-r--r--core/java/android/os/VibrationEffect.java177
-rw-r--r--core/java/android/os/Vibrator.java59
-rw-r--r--core/java/android/os/storage/StorageManager.java19
-rw-r--r--core/java/android/os/storage/StorageVolume.java8
-rw-r--r--core/java/android/provider/Settings.java123
-rw-r--r--core/java/android/service/autofill/AutofillService.java8
-rw-r--r--core/java/android/service/autofill/Dataset.java258
-rw-r--r--core/java/android/service/autofill/Field.java165
-rw-r--r--core/java/android/service/autofill/FillRequest.java80
-rw-r--r--core/java/android/service/autofill/FillResponse.java230
-rw-r--r--core/java/android/service/autofill/Presentations.java278
-rw-r--r--core/java/android/service/games/GameService.java3
-rw-r--r--core/java/android/service/games/GameSession.java2
-rw-r--r--core/java/android/service/persistentdata/IPersistentDataBlockService.aidl1
-rw-r--r--core/java/android/service/persistentdata/PersistentDataBlockManager.java15
-rw-r--r--core/java/android/service/tracing/TraceReportService.java165
-rw-r--r--core/java/android/service/wallpaper/WallpaperService.java14
-rw-r--r--core/java/android/tracing/ITracingServiceProxy.aidl12
-rw-r--r--core/java/android/tracing/TraceReportParams.aidl59
-rw-r--r--core/java/android/util/IconDrawableFactory.java29
-rw-r--r--core/java/android/util/LauncherIcons.java11
-rw-r--r--core/java/android/view/Display.java101
-rw-r--r--core/java/android/view/DisplayAdjustments.java189
-rw-r--r--core/java/android/view/IDisplayWindowListener.aidl2
-rw-r--r--core/java/android/view/SurfaceControlViewHost.java31
-rw-r--r--core/java/android/view/View.java4
-rw-r--r--core/java/android/view/ViewRootImpl.java15
-rw-r--r--core/java/android/view/Window.java62
-rw-r--r--core/java/android/view/accessibility/AccessibilityNodeInfo.java41
-rw-r--r--core/java/android/view/animation/Animation.java27
-rw-r--r--core/java/android/view/animation/AnimationSet.java31
-rw-r--r--core/java/android/view/animation/AnimationUtils.java2
-rw-r--r--core/java/android/view/animation/ExtendAnimation.java176
-rw-r--r--core/java/android/view/animation/Transformation.java46
-rw-r--r--core/java/android/view/autofill/AutofillManager.java94
-rw-r--r--core/java/android/view/autofill/IAutoFillManagerClient.aidl5
-rw-r--r--core/java/android/view/contentcapture/ContentCaptureSession.java2
-rw-r--r--core/java/android/view/inputmethod/InputMethod.java20
-rw-r--r--core/java/android/webkit/WebSettings.java10
-rw-r--r--core/java/android/widget/RemoteViews.java49
-rw-r--r--core/java/android/widget/TextView.java5
-rw-r--r--core/java/android/window/ConfigurationHelper.java16
-rw-r--r--core/java/android/window/TransitionInfo.java12
-rw-r--r--core/java/com/android/internal/app/BlockedAppActivity.java44
-rw-r--r--core/java/com/android/internal/app/IBatteryStats.aidl4
-rw-r--r--core/java/com/android/internal/content/InstallLocationUtils.java (renamed from core/java/com/android/internal/content/PackageHelper.java)82
-rw-r--r--core/java/com/android/internal/os/BatteryStatsImpl.java335
-rw-r--r--core/java/com/android/internal/os/BinderCallsStats.java11
-rw-r--r--core/java/com/android/internal/policy/DecorView.java11
-rw-r--r--core/java/com/android/internal/policy/TransitionAnimation.java10
-rw-r--r--core/java/com/android/internal/statusbar/IStatusBar.aidl3
-rw-r--r--core/java/com/android/internal/statusbar/IStatusBarService.aidl3
-rw-r--r--core/java/com/android/internal/util/UserIcons.java24
-rw-r--r--core/java/com/android/internal/view/IInputMethod.aidl8
-rw-r--r--core/jni/Android.bp3
-rw-r--r--core/jni/AndroidRuntime.cpp2
-rw-r--r--core/jni/OWNERS1
-rw-r--r--core/jni/android/opengl/OWNERS1
-rw-r--r--core/jni/android/opengl/util.cpp6
-rw-r--r--core/jni/android_hardware_HardwareBuffer.cpp2
-rw-r--r--core/jni/android_hardware_SensorManager.cpp15
-rw-r--r--core/jni/android_media_AudioSystem.cpp103
-rw-r--r--core/jni/android_server_NetworkManagementSocketTagger.cpp91
-rw-r--r--core/proto/android/hardware/sensorprivacy.proto52
-rw-r--r--core/proto/android/server/Android.bp28
-rw-r--r--core/proto/android/server/powermanagerservice.proto42
-rw-r--r--core/res/AndroidManifest.xml109
-rw-r--r--core/res/res/drawable/perm_group_read_media_aural.xml26
-rw-r--r--core/res/res/drawable/perm_group_read_media_visual.xml26
-rw-r--r--core/res/res/layout/autofill_fill_dialog.xml115
-rw-r--r--core/res/res/values-af/strings.xml6
-rw-r--r--core/res/res/values-am/strings.xml6
-rw-r--r--core/res/res/values-ar/strings.xml6
-rw-r--r--core/res/res/values-as/strings.xml6
-rw-r--r--core/res/res/values-az/strings.xml6
-rw-r--r--core/res/res/values-b+sr+Latn/strings.xml6
-rw-r--r--core/res/res/values-be/strings.xml6
-rw-r--r--core/res/res/values-bg/strings.xml6
-rw-r--r--core/res/res/values-bn/strings.xml6
-rw-r--r--core/res/res/values-bs/strings.xml6
-rw-r--r--core/res/res/values-ca/strings.xml6
-rw-r--r--core/res/res/values-cs/strings.xml6
-rw-r--r--core/res/res/values-da/strings.xml6
-rw-r--r--core/res/res/values-de/strings.xml8
-rw-r--r--core/res/res/values-el/strings.xml6
-rw-r--r--core/res/res/values-en-rAU/strings.xml6
-rw-r--r--core/res/res/values-en-rCA/strings.xml6
-rw-r--r--core/res/res/values-en-rGB/strings.xml6
-rw-r--r--core/res/res/values-en-rIN/strings.xml6
-rw-r--r--core/res/res/values-en-rXC/strings.xml4
-rw-r--r--core/res/res/values-es-rUS/strings.xml6
-rw-r--r--core/res/res/values-es/strings.xml8
-rw-r--r--core/res/res/values-et/strings.xml6
-rw-r--r--core/res/res/values-eu/strings.xml6
-rw-r--r--core/res/res/values-fa/strings.xml6
-rw-r--r--core/res/res/values-fi/strings.xml6
-rw-r--r--core/res/res/values-fr-rCA/strings.xml6
-rw-r--r--core/res/res/values-fr/strings.xml6
-rw-r--r--core/res/res/values-gl/strings.xml6
-rw-r--r--core/res/res/values-gu/strings.xml6
-rw-r--r--core/res/res/values-hi/strings.xml6
-rw-r--r--core/res/res/values-hr/strings.xml6
-rw-r--r--core/res/res/values-hu/strings.xml6
-rw-r--r--core/res/res/values-hy/strings.xml6
-rw-r--r--core/res/res/values-in/strings.xml6
-rw-r--r--core/res/res/values-is/strings.xml6
-rw-r--r--core/res/res/values-it/strings.xml6
-rw-r--r--core/res/res/values-iw/strings.xml6
-rw-r--r--core/res/res/values-ja/strings.xml6
-rw-r--r--core/res/res/values-ka/strings.xml6
-rw-r--r--core/res/res/values-kk/strings.xml6
-rw-r--r--core/res/res/values-km/strings.xml6
-rw-r--r--core/res/res/values-kn/strings.xml6
-rw-r--r--core/res/res/values-ko/strings.xml6
-rw-r--r--core/res/res/values-ky/strings.xml6
-rw-r--r--core/res/res/values-lo/strings.xml6
-rw-r--r--core/res/res/values-lt/strings.xml6
-rw-r--r--core/res/res/values-lv/strings.xml10
-rw-r--r--core/res/res/values-mk/strings.xml6
-rw-r--r--core/res/res/values-ml/strings.xml6
-rw-r--r--core/res/res/values-mn/strings.xml6
-rw-r--r--core/res/res/values-mr/strings.xml6
-rw-r--r--core/res/res/values-ms/strings.xml6
-rw-r--r--core/res/res/values-my/strings.xml6
-rw-r--r--core/res/res/values-nb/strings.xml6
-rw-r--r--core/res/res/values-ne/strings.xml6
-rw-r--r--core/res/res/values-nl/strings.xml6
-rw-r--r--core/res/res/values-or/strings.xml6
-rw-r--r--core/res/res/values-pa/strings.xml6
-rw-r--r--core/res/res/values-pl/strings.xml6
-rw-r--r--core/res/res/values-pt-rBR/strings.xml6
-rw-r--r--core/res/res/values-pt-rPT/strings.xml6
-rw-r--r--core/res/res/values-pt/strings.xml6
-rw-r--r--core/res/res/values-ro/strings.xml6
-rw-r--r--core/res/res/values-ru/strings.xml6
-rw-r--r--core/res/res/values-si/strings.xml6
-rw-r--r--core/res/res/values-sk/strings.xml6
-rw-r--r--core/res/res/values-sl/strings.xml6
-rw-r--r--core/res/res/values-sq/strings.xml6
-rw-r--r--core/res/res/values-sr/strings.xml6
-rw-r--r--core/res/res/values-sv/strings.xml6
-rw-r--r--core/res/res/values-sw/strings.xml6
-rw-r--r--core/res/res/values-ta/strings.xml6
-rw-r--r--core/res/res/values-te/strings.xml54
-rw-r--r--core/res/res/values-th/strings.xml6
-rw-r--r--core/res/res/values-tl/strings.xml6
-rw-r--r--core/res/res/values-tr/strings.xml6
-rw-r--r--core/res/res/values-uk/strings.xml6
-rw-r--r--core/res/res/values-ur/strings.xml6
-rw-r--r--core/res/res/values-uz/strings.xml6
-rw-r--r--core/res/res/values-vi/strings.xml6
-rw-r--r--core/res/res/values-zh-rCN/strings.xml6
-rw-r--r--core/res/res/values-zh-rHK/strings.xml6
-rw-r--r--core/res/res/values-zh-rTW/strings.xml6
-rw-r--r--core/res/res/values-zu/strings.xml6
-rw-r--r--core/res/res/values/attrs.xml32
-rw-r--r--core/res/res/values/config.xml38
-rw-r--r--core/res/res/values/dimens.xml3
-rw-r--r--core/res/res/values/ids.xml6
-rw-r--r--core/res/res/values/public.xml10
-rw-r--r--core/res/res/values/strings.xml39
-rw-r--r--core/res/res/values/symbols.xml24
-rw-r--r--core/tests/coretests/res/layout/remote_view_relative_layout.xml24
-rw-r--r--core/tests/coretests/res/layout/remote_view_relative_layout_with_theme.xml25
-rw-r--r--core/tests/coretests/res/layout/remote_views_light_background_text.xml21
-rw-r--r--core/tests/coretests/res/layout/remote_views_list.xml21
-rw-r--r--core/tests/coretests/res/values/styles.xml10
-rw-r--r--core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java161
-rw-r--r--core/tests/coretests/src/android/app/servertransaction/TestUtils.java11
-rw-r--r--core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java29
-rw-r--r--core/tests/coretests/src/android/content/pm/CrossProfileAppsTest.java6
-rw-r--r--core/tests/coretests/src/android/content/pm/InstallLocationUtilsTests.java (renamed from core/tests/coretests/src/android/content/pm/PackageHelperTests.java)80
-rw-r--r--core/tests/coretests/src/android/content/res/ResourcesManagerTest.java31
-rw-r--r--core/tests/coretests/src/android/os/VibrationEffectTest.java54
-rw-r--r--core/tests/coretests/src/android/view/DisplayAdjustmentsTests.java47
-rw-r--r--core/tests/coretests/src/android/view/OWNERS3
-rw-r--r--core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java2
-rw-r--r--core/tests/coretests/src/android/view/stylus/HandwritingInitiatorTest.java (renamed from core/tests/coretests/src/android/view/HandwritingInitiatorTest.java)12
-rw-r--r--core/tests/coretests/src/android/widget/RemoteViewsTest.java191
-rw-r--r--core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java212
-rw-r--r--core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java35
-rw-r--r--core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java5
-rw-r--r--core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java3
-rw-r--r--core/tests/mockingcoretests/src/android/view/DisplayTest.java125
-rw-r--r--data/etc/com.android.systemui.xml1
-rw-r--r--data/etc/platform.xml12
-rw-r--r--data/etc/privapp-permissions-platform.xml1
-rw-r--r--data/fonts/fonts.xml14
-rw-r--r--graphics/java/android/graphics/Bitmap.java48
-rw-r--r--identity/java/android/security/identity/IdentityCredential.java6
-rw-r--r--identity/java/android/security/identity/WritableIdentityCredential.java3
-rw-r--r--libs/WindowManager/Shell/res/values-te/strings.xml8
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java14
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java38
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java33
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DropZoneView.java138
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java1
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java247
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java6
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java41
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java7
-rw-r--r--libs/hwui/HardwareBitmapUploader.cpp51
-rw-r--r--libs/hwui/HardwareBitmapUploader.h2
-rw-r--r--libs/hwui/apex/android_bitmap.cpp7
-rw-r--r--libs/hwui/hwui/ImageDecoder.cpp2
-rw-r--r--libs/hwui/jni/BitmapRegionDecoder.cpp11
-rw-r--r--libs/hwui/jni/Graphics.cpp32
-rw-r--r--libs/hwui/jni/GraphicsJNI.h21
-rw-r--r--libs/hwui/renderthread/DrawFrameTask.h9
-rw-r--r--libs/tracingproxy/Android.bp1
-rw-r--r--location/java/android/location/ILocationManager.aidl4
-rw-r--r--location/java/android/location/LastLocationRequest.java9
-rw-r--r--location/java/android/location/LocationManager.java42
-rw-r--r--location/java/android/location/LocationRequest.java12
-rw-r--r--media/java/android/media/AudioManager.java25
-rw-r--r--media/java/android/media/BtProfileConnectionInfo.java24
-rw-r--r--media/java/android/media/IMediaRouter2Manager.aidl4
-rw-r--r--media/java/android/media/MediaRoute2Info.java85
-rw-r--r--media/java/android/media/MediaRouter2.java51
-rw-r--r--media/java/android/media/MediaRouter2Manager.java168
-rw-r--r--media/java/android/media/RouteDiscoveryPreference.java130
-rw-r--r--media/java/android/media/tv/TvInputManager.java1
-rw-r--r--media/java/android/media/tv/tuner/Tuner.java21
-rw-r--r--media/java/android/media/tv/tuner/frontend/FrontendStatusReadiness.java2
-rw-r--r--media/jni/Android.bp1
-rw-r--r--media/native/midi/amidi.cpp6
-rw-r--r--native/android/libandroid.map.txt12
-rw-r--r--native/android/performance_hint.cpp7
-rw-r--r--native/android/tests/performance_hint/PerformanceHintNativeTest.cpp2
-rw-r--r--native/graphics/jni/imagedecoder.cpp8
-rw-r--r--omapi/OWNERS5
-rw-r--r--packages/ConnectivityT/framework-t/Android.bp32
-rw-r--r--packages/ConnectivityT/framework-t/jni/android_net_TrafficStats.cpp46
-rw-r--r--packages/ConnectivityT/framework-t/jni/onload.cpp39
-rw-r--r--packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStatsManager.java12
-rw-r--r--packages/ConnectivityT/framework-t/src/android/net/NetworkIdentity.java6
-rw-r--r--packages/ConnectivityT/framework-t/src/android/net/NetworkTemplate.java5
-rw-r--r--packages/ConnectivityT/framework-t/src/android/net/TrafficStats.java86
-rw-r--r--packages/ConnectivityT/framework-t/src/com/android/server/NetworkManagementSocketTagger.java146
-rw-r--r--packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsFactory.java15
-rw-r--r--packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java25
-rw-r--r--packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsSubscriptionsMonitor.java4
-rwxr-xr-xpackages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java6
-rw-r--r--packages/SettingsLib/Android.bp1
-rw-r--r--packages/SettingsLib/AndroidManifest.xml6
-rw-r--r--packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java20
-rw-r--r--packages/SettingsLib/res/drawable/add_a_photo_circled.xml28
-rw-r--r--packages/SettingsLib/res/drawable/avatar_choose_photo_circled.xml30
-rw-r--r--packages/SettingsLib/res/drawable/avatar_selector.xml (renamed from core/res/res/drawable-car/car_checkbox_background.xml)22
-rw-r--r--packages/SettingsLib/res/drawable/avatar_take_photo_circled.xml30
-rw-r--r--packages/SettingsLib/res/drawable/ic_account_circle_outline.xml25
-rw-r--r--packages/SettingsLib/res/drawable/ic_add_a_photo.xml24
-rw-r--r--packages/SettingsLib/res/drawable/ic_avatar_choose_photo.xml24
-rw-r--r--packages/SettingsLib/res/drawable/ic_avatar_take_photo.xml24
-rw-r--r--packages/SettingsLib/res/layout/avatar_item.xml (renamed from core/res/res/drawable-car/car_checkbox.xml)23
-rw-r--r--packages/SettingsLib/res/layout/avatar_picker.xml38
-rw-r--r--packages/SettingsLib/res/layout/edit_user_info_dialog_content.xml32
-rw-r--r--packages/SettingsLib/res/values-w1280dp-land/dimens.xml21
-rw-r--r--packages/SettingsLib/res/values-w1440dp-land/dimens.xml21
-rw-r--r--packages/SettingsLib/res/values-w1600dp-land/dimens.xml21
-rw-r--r--packages/SettingsLib/res/values-w480dp-port/dimens.xml21
-rw-r--r--packages/SettingsLib/res/values-w600dp-port/dimens.xml21
-rw-r--r--packages/SettingsLib/res/values-w720dp-port/dimens.xml21
-rw-r--r--packages/SettingsLib/res/values-w840dp-port/dimens.xml21
-rw-r--r--packages/SettingsLib/res/values-w960dp-land/dimens.xml21
-rw-r--r--packages/SettingsLib/res/values/arrays.xml2
-rw-r--r--packages/SettingsLib/res/values/dimens.xml11
-rw-r--r--packages/SettingsLib/res/values/strings.xml17
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java24
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java29
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java70
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/Utils.java6
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/applications/AppIconCacheManager.java112
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/applications/AppUtils.java81
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java46
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManager.java (renamed from packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingsManager.java)2
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java39
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/users/AvatarPhotoController.java297
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/users/AvatarPickerActivity.java334
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/users/EditUserInfoController.java37
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/users/EditUserPhotoController.java437
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/users/PhotoCapabilityUtils.java4
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/AppIconCacheManagerTest.java109
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/AppUtilsTest.java167
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java23
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/ConnectivityPreferenceControllerTest.java5
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/EditUserInfoControllerTest.java10
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceTest.java14
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java6
-rw-r--r--packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java2
-rw-r--r--packages/Shell/AndroidManifest.xml4
-rw-r--r--packages/SystemUI/Android.bp2
-rw-r--r--packages/SystemUI/AndroidManifest.xml17
-rw-r--r--packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java13
-rw-r--r--packages/SystemUI/res/layout/clipboard_overlay.xml6
-rw-r--r--packages/SystemUI/res/layout/controls_fullscreen.xml3
-rw-r--r--packages/SystemUI/res/layout/dream_overlay_complication_clock_date.xml30
-rw-r--r--packages/SystemUI/res/layout/dream_overlay_complication_clock_time.xml29
-rw-r--r--packages/SystemUI/res/layout/dream_overlay_complication_weather.xml27
-rw-r--r--packages/SystemUI/res/layout/dream_overlay_complications_layer.xml42
-rw-r--r--packages/SystemUI/res/layout/dream_overlay_container.xml2
-rw-r--r--packages/SystemUI/res/layout/status_bar_expanded.xml2
-rw-r--r--packages/SystemUI/res/values-land/config.xml3
-rw-r--r--packages/SystemUI/res/values-land/dimens.xml2
-rw-r--r--packages/SystemUI/res/values-sw600dp-land/config.xml3
-rw-r--r--packages/SystemUI/res/values-sw600dp-land/dimens.xml4
-rw-r--r--packages/SystemUI/res/values-sw600dp-port/config.xml1
-rw-r--r--packages/SystemUI/res/values-sw600dp/config.xml3
-rw-r--r--packages/SystemUI/res/values-sw600dp/dimens.xml2
-rw-r--r--packages/SystemUI/res/values-sw720dp-land/config.xml3
-rw-r--r--packages/SystemUI/res/values-sw720dp-land/dimens.xml21
-rw-r--r--packages/SystemUI/res/values-w500dp/config.xml20
-rw-r--r--packages/SystemUI/res/values-w500dp/dimens.xml21
-rw-r--r--packages/SystemUI/res/values-w850dp/config.xml20
-rw-r--r--packages/SystemUI/res/values-w850dp/dimens.xml21
-rw-r--r--packages/SystemUI/res/values/attrs.xml2
-rw-r--r--packages/SystemUI/res/values/dimens.xml62
-rw-r--r--packages/SystemUI/res/values/strings.xml12
-rw-r--r--packages/SystemUI/res/values/styles.xml6
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimator.kt96
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/communal/ICommunalHost.aidl33
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/communal/ICommunalSource.aidl34
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java6
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardBiometricLockoutLogger.kt29
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java26
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt99
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java15
-rw-r--r--packages/SystemUI/src/com/android/keyguard/LockIconViewController.java29
-rw-r--r--packages/SystemUI/src/com/android/systemui/Dependency.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/DualToneHandler.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/ExpandHelper.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java32
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java20
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsHapticsSimulator.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardListener.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java31
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt14
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java40
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java34
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java111
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/SmartSpaceComplication.java113
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/complication/Complication.java32
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationCollectionLiveData.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutEngine.java35
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutParams.java54
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationUtils.java53
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/complication/DreamClockDateComplication.java106
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/complication/DreamClockTimeComplication.java106
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/complication/DreamWeatherComplication.java180
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamClockDateComplicationComponent.java111
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamClockTimeComplicationComponent.java111
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamWeatherComplicationComponent.java111
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/RegisteredComplicationsModule.java33
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayComponent.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java273
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitor.java368
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/touch/DreamTouchHandler.java92
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/touch/InputSession.java90
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/touch/dagger/BouncerSwipeModule.java128
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/touch/dagger/DreamTouchModule.java32
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/touch/dagger/InputSessionComponent.java51
-rw-r--r--packages/SystemUI/src/com/android/systemui/flags/Flags.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/hdmi/HdmiCecSetMenuLanguageActivity.java98
-rw-r--r--packages/SystemUI/src/com/android/systemui/hdmi/HdmiCecSetMenuLanguageHelper.java97
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java82
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt29
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/dream/MediaComplicationViewController.java68
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/dream/MediaDreamComplication.java43
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/dream/MediaDreamSentinel.java97
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/dream/MediaViewHolder.java58
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/dream/dagger/MediaComplicationComponent.java100
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/buttons/DeadZone.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java21
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java116
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/VibratorHelper.java74
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java39
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt387
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorLogger.kt62
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinator.kt30
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java51
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifStabilityManager.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifPanelEventSource.kt128
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDiffer.kt21
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDifferLogger.kt13
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java28
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java56
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaContainerView.java46
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaContainerView.kt84
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java63
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java20
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateLogger.kt16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java24
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java29
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenGestureLogger.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java24
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelUnfoldAnimationController.kt52
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java67
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQSContainerController.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java26
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ScreenOffAnimationController.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java23
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java60
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarComponent.java24
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/tv/TvSystemUIBinder.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/service/Observer.java45
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/service/PackageObserver.java107
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/service/PersistentConnectionManager.java155
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/service/dagger/ObservableServiceModule.java65
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/service/dagger/PackageObserverComponent.java42
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java17
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardBiometricLockoutLoggerTest.kt30
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardUnfoldTransitionTest.kt95
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java41
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlActionCoordinatorImplTest.kt17
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java24
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java68
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/dreams/SmartSpaceComplicationTest.java77
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationCollectionLiveDataTest.java8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationLayoutEngineTest.java29
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationLayoutParamsTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationUtilsTest.java55
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamClockDateComplicationTest.java66
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamClockTimeComplicationTest.java66
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamWeatherComplicationTest.java79
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java252
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitorTest.java369
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/dump/LogBufferHelper.kt35
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/hdmi/HdmiCecSetMenuLanguageHelperTest.java104
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/dream/MediaComplicationViewControllerTest.java68
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/dream/MediaDreamSentinelTest.java93
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimatorTest.kt117
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/VibratorHelperTest.kt93
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java69
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt367
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinatorTest.kt34
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java72
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDifferTest.java11
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/MediaContainerViewTest.kt38
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt153
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java43
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java44
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerLegacyTest.java16
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupTestHelper.java14
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationQSContainerControllerTest.kt12
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java1
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchControllerTest.kt147
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt12
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/util/service/PackageObserverTest.java77
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/util/service/PersistentConnectionManagerTest.java138
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java10
-rw-r--r--proto/src/system_messages.proto4
-rw-r--r--services/api/current.txt1
-rw-r--r--services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java6
-rw-r--r--services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java8
-rw-r--r--services/autofill/java/com/android/server/autofill/Session.java270
-rw-r--r--services/autofill/java/com/android/server/autofill/ViewState.java2
-rw-r--r--services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java84
-rw-r--r--services/autofill/java/com/android/server/autofill/ui/DialogFillUi.java629
-rw-r--r--services/companion/java/com/android/server/companion/AssociationStoreImpl.java2
-rw-r--r--services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java2
-rw-r--r--services/companion/java/com/android/server/companion/presence/BleCompanionDeviceScanner.java14
-rw-r--r--services/companion/java/com/android/server/companion/presence/BluetoothCompanionDeviceConnectionListener.java2
-rw-r--r--services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java13
-rw-r--r--services/companion/java/com/android/server/companion/virtual/InputController.java102
-rw-r--r--services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java16
-rw-r--r--services/core/java/android/app/usage/UsageStatsManagerInternal.java4
-rw-r--r--services/core/java/android/content/pm/PackageManagerInternal.java16
-rw-r--r--services/core/java/com/android/server/DockObserver.java189
-rw-r--r--services/core/java/com/android/server/OWNERS3
-rw-r--r--services/core/java/com/android/server/PersistentDataBlockService.java6
-rw-r--r--services/core/java/com/android/server/StorageManagerService.java61
-rw-r--r--services/core/java/com/android/server/SystemServiceManager.java24
-rw-r--r--services/core/java/com/android/server/am/ActiveServices.java43
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerLocal.java23
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java144
-rw-r--r--services/core/java/com/android/server/am/AppBatteryTracker.java160
-rw-r--r--services/core/java/com/android/server/am/AppRestrictionController.java2
-rw-r--r--services/core/java/com/android/server/am/BatteryStatsService.java30
-rw-r--r--services/core/java/com/android/server/am/BroadcastQueue.java11
-rw-r--r--services/core/java/com/android/server/am/DataConnectionStats.java2
-rw-r--r--services/core/java/com/android/server/am/ServiceRecord.java11
-rw-r--r--services/core/java/com/android/server/app/GameManagerService.java27
-rw-r--r--services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java13
-rw-r--r--services/core/java/com/android/server/apphibernation/AppHibernationService.java50
-rw-r--r--services/core/java/com/android/server/audio/BtHelper.java4
-rw-r--r--services/core/java/com/android/server/biometrics/AuthSession.java31
-rw-r--r--services/core/java/com/android/server/biometrics/log/BiometricContext.java59
-rw-r--r--services/core/java/com/android/server/biometrics/log/BiometricContextProvider.java184
-rw-r--r--services/core/java/com/android/server/biometrics/log/BiometricFrameworkStatsLogger.java48
-rw-r--r--services/core/java/com/android/server/biometrics/log/BiometricLogger.java43
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java17
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java15
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java31
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/EnrollClient.java9
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java9
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/HalClientMonitor.java46
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java22
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java11
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/InvalidationClient.java10
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/InvalidationRequesterClient.java8
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/RemovalClient.java11
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/RevokeChallengeClient.java9
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/StartUserClient.java7
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/StopUserClient.java7
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/FaceService.java3
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/AidlSession.java4
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java50
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceDetectClient.java35
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java21
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGenerateChallengeClient.java8
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetAuthenticatorIdClient.java7
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetFeatureClient.java9
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInternalCleanupClient.java18
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInternalEnumerateClient.java8
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInvalidationClient.java6
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java68
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceRemovalClient.java6
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java7
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceRevokeChallengeClient.java8
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceSetFeatureClient.java11
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceStartUserClient.java5
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceStopUserClient.java5
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java14
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java67
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java10
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceEnrollClient.java9
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGenerateChallengeClient.java8
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGetFeatureClient.java11
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceInternalCleanupClient.java18
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceInternalEnumerateClient.java8
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceRemovalClient.java11
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceResetLockoutClient.java7
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceRevokeChallengeClient.java7
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceSetFeatureClient.java11
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceUpdateActiveUserClient.java9
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java7
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/AidlSession.java4
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java46
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java20
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java73
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGenerateChallengeClient.java8
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGetAuthenticatorIdClient.java12
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClient.java23
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalEnumerateClient.java8
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInvalidationClient.java6
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java62
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRemovalClient.java6
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java7
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRevokeChallengeClient.java8
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintStartUserClient.java5
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintStopUserClient.java8
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java16
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java64
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java11
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java13
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java16
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java8
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintGenerateChallengeClient.java8
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintInternalCleanupClient.java21
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintInternalEnumerateClient.java8
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintRemovalClient.java6
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintResetLockoutClient.java10
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintRevokeChallengeClient.java7
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintUpdateActiveUserClient.java10
-rw-r--r--services/core/java/com/android/server/camera/CameraServiceProxy.java3
-rw-r--r--services/core/java/com/android/server/clipboard/ClipboardService.java2
-rw-r--r--services/core/java/com/android/server/devicestate/DeviceStateManagerService.java69
-rw-r--r--services/core/java/com/android/server/devicestate/DeviceStateManagerShellCommand.java5
-rw-r--r--services/core/java/com/android/server/devicestate/OverrideRequestController.java215
-rw-r--r--services/core/java/com/android/server/display/BrightnessThrottler.java262
-rw-r--r--services/core/java/com/android/server/display/BrightnessUtils.java83
-rw-r--r--services/core/java/com/android/server/display/DisplayDeviceConfig.java155
-rw-r--r--services/core/java/com/android/server/display/DisplayManagerService.java23
-rw-r--r--services/core/java/com/android/server/display/DisplayManagerShellCommand.java3
-rw-r--r--services/core/java/com/android/server/display/DisplayPowerController.java77
-rw-r--r--services/core/java/com/android/server/display/HighBrightnessModeController.java54
-rw-r--r--services/core/java/com/android/server/display/RampAnimator.java20
-rw-r--r--services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java106
-rw-r--r--services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceFactory.java11
-rw-r--r--services/core/java/com/android/server/dreams/DreamManagerService.java7
-rwxr-xr-xservices/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java15
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java25
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecStandbyModeHandler.java1
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiControlService.java27
-rw-r--r--services/core/java/com/android/server/input/InputManagerService.java29
-rw-r--r--services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java21
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java12
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodManagerService.java42
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodMenuController.java2
-rw-r--r--services/core/java/com/android/server/location/LocationManagerService.java41
-rw-r--r--services/core/java/com/android/server/location/LocationPermissions.java25
-rw-r--r--services/core/java/com/android/server/location/LocationShellCommand.java30
-rw-r--r--services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java28
-rw-r--r--services/core/java/com/android/server/location/gnss/GnssLocationProvider.java4
-rw-r--r--services/core/java/com/android/server/location/gnss/GnssManagerService.java8
-rw-r--r--services/core/java/com/android/server/locksettings/LockSettingsService.java11
-rw-r--r--services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java23
-rw-r--r--services/core/java/com/android/server/net/NetworkPolicyManagerService.java68
-rw-r--r--services/core/java/com/android/server/notification/NotificationComparator.java20
-rw-r--r--services/core/java/com/android/server/notification/PermissionHelper.java5
-rw-r--r--services/core/java/com/android/server/notification/ZenModeFiltering.java133
-rw-r--r--services/core/java/com/android/server/pm/AppDataHelper.java7
-rw-r--r--services/core/java/com/android/server/pm/AppsFilter.java18
-rw-r--r--services/core/java/com/android/server/pm/ChangedPackagesTracker.java114
-rw-r--r--services/core/java/com/android/server/pm/ComponentResolver.java147
-rw-r--r--services/core/java/com/android/server/pm/Computer.java25
-rw-r--r--services/core/java/com/android/server/pm/ComputerEngine.java71
-rw-r--r--services/core/java/com/android/server/pm/ComputerLocked.java43
-rw-r--r--services/core/java/com/android/server/pm/ComputerTracker.java145
-rw-r--r--services/core/java/com/android/server/pm/DeletePackageHelper.java31
-rw-r--r--services/core/java/com/android/server/pm/DexOptHelper.java35
-rw-r--r--services/core/java/com/android/server/pm/DumpHelper.java75
-rw-r--r--services/core/java/com/android/server/pm/IncrementalProgressListener.java37
-rw-r--r--services/core/java/com/android/server/pm/InstallPackageHelper.java18
-rw-r--r--services/core/java/com/android/server/pm/InstallParams.java119
-rw-r--r--services/core/java/com/android/server/pm/InstantAppRegistry.java795
-rw-r--r--services/core/java/com/android/server/pm/KeySetManagerService.java7
-rw-r--r--services/core/java/com/android/server/pm/OtaDexoptService.java15
-rw-r--r--services/core/java/com/android/server/pm/PackageHandler.java9
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerService.java6
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerSession.java4
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java1876
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java1
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerServiceUtils.java43
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerShellCommand.java12
-rw-r--r--services/core/java/com/android/server/pm/PackageObserverHelper.java86
-rw-r--r--services/core/java/com/android/server/pm/PackageSessionVerifier.java10
-rw-r--r--services/core/java/com/android/server/pm/PackageSetting.java104
-rw-r--r--services/core/java/com/android/server/pm/PreferredActivityHelper.java20
-rw-r--r--services/core/java/com/android/server/pm/SELinuxMMAC.java17
-rw-r--r--services/core/java/com/android/server/pm/SettingBase.java2
-rw-r--r--services/core/java/com/android/server/pm/Settings.java56
-rw-r--r--services/core/java/com/android/server/pm/SharedLibrariesImpl.java60
-rw-r--r--services/core/java/com/android/server/pm/SharedLibrariesRead.java2
-rw-r--r--services/core/java/com/android/server/pm/SharedUserSetting.java88
-rw-r--r--services/core/java/com/android/server/pm/ShortcutPackage.java23
-rw-r--r--services/core/java/com/android/server/pm/ShortcutService.java19
-rw-r--r--services/core/java/com/android/server/pm/StagingManager.java8
-rw-r--r--services/core/java/com/android/server/pm/SuspendPackageHelper.java254
-rw-r--r--services/core/java/com/android/server/pm/UserRestrictionsUtils.java22
-rw-r--r--services/core/java/com/android/server/pm/UserSystemPackageInstaller.java84
-rw-r--r--services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java26
-rw-r--r--services/core/java/com/android/server/pm/parsing/ParsedComponentStateUtils.java2
-rw-r--r--services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageUtils.java2
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerService.java16
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java2
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java11
-rw-r--r--services/core/java/com/android/server/pm/pkg/PackageStateInternal.java3
-rw-r--r--services/core/java/com/android/server/pm/pkg/PackageStateUnserialized.java97
-rw-r--r--services/core/java/com/android/server/pm/pkg/SharedUserApi.java68
-rw-r--r--services/core/java/com/android/server/pm/pkg/mutate/PackageStateMutator.java72
-rw-r--r--services/core/java/com/android/server/pm/pkg/mutate/PackageStateWrite.java19
-rw-r--r--services/core/java/com/android/server/pm/pkg/mutate/PackageUserStateWrite.java8
-rw-r--r--services/core/java/com/android/server/power/LowPowerStandbyController.java608
-rw-r--r--services/core/java/com/android/server/power/LowPowerStandbyControllerInternal.java (renamed from packages/SystemUI/shared/src/com/android/systemui/shared/communal/ICommunalSurfaceCallback.aidl)31
-rw-r--r--services/core/java/com/android/server/power/Notifier.java38
-rw-r--r--services/core/java/com/android/server/power/PowerManagerService.java344
-rw-r--r--services/core/java/com/android/server/power/WakeLockLog.java7
-rw-r--r--services/core/java/com/android/server/resources/ResourcesManagerService.java102
-rw-r--r--services/core/java/com/android/server/resources/ResourcesManagerShellCommand.java94
-rw-r--r--services/core/java/com/android/server/sensorprivacy/AllSensorStateController.java155
-rw-r--r--services/core/java/com/android/server/sensorprivacy/OWNERS1
-rw-r--r--services/core/java/com/android/server/sensorprivacy/PersistedState.java494
-rw-r--r--services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java (renamed from services/core/java/com/android/server/SensorPrivacyService.java)613
-rw-r--r--services/core/java/com/android/server/sensorprivacy/SensorPrivacyStateController.java158
-rw-r--r--services/core/java/com/android/server/sensorprivacy/SensorPrivacyStateControllerImpl.java142
-rw-r--r--services/core/java/com/android/server/sensorprivacy/SensorState.java79
-rw-r--r--services/core/java/com/android/server/statusbar/StatusBarManagerService.java18
-rw-r--r--services/core/java/com/android/server/tracing/TracingServiceProxy.java162
-rw-r--r--services/core/java/com/android/server/wm/ActivityClientController.java5
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java61
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskSupervisor.java3
-rw-r--r--services/core/java/com/android/server/wm/AppTransition.java6
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java38
-rw-r--r--services/core/java/com/android/server/wm/DisplayWindowListenerController.java5
-rw-r--r--services/core/java/com/android/server/wm/LetterboxConfiguration.java28
-rw-r--r--services/core/java/com/android/server/wm/RecentTasks.java7
-rw-r--r--services/core/java/com/android/server/wm/Session.java5
-rw-r--r--services/core/java/com/android/server/wm/Task.java3
-rw-r--r--services/core/java/com/android/server/wm/Transition.java2
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerShellCommand.java36
-rw-r--r--services/core/java/com/android/server/wm/WindowToken.java57
-rw-r--r--services/core/jni/Android.bp1
-rw-r--r--services/core/jni/com_android_server_companion_virtual_InputController.cpp36
-rw-r--r--services/core/jni/com_android_server_input_InputManagerService.cpp4
-rw-r--r--services/core/jni/com_android_server_location_GnssLocationProvider.cpp13
-rw-r--r--services/core/jni/com_android_server_net_NetworkStatsService.cpp15
-rw-r--r--services/core/jni/gnss/AGnssRil.cpp2
-rw-r--r--services/core/jni/gnss/GnssAntennaInfoCallback.cpp23
-rw-r--r--services/core/jni/gnss/GnssMeasurementCallback.cpp90
-rw-r--r--services/core/jni/gnss/GnssMeasurementCallback.h4
-rw-r--r--services/core/jni/gnss/Utils.cpp7
-rw-r--r--services/core/jni/gnss/Utils.h2
-rw-r--r--services/core/xsd/display-device-config/display-device-config.xsd35
-rw-r--r--services/core/xsd/display-device-config/schema/current.txt21
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java604
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/OverlayPackagesProvider.java28
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/Owners.java8
-rw-r--r--services/java/com/android/server/SystemServer.java23
-rw-r--r--services/midi/java/com/android/server/midi/MidiService.java26
-rw-r--r--services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt76
-rw-r--r--services/tests/mockingservicestests/assets/SensorPrivacyServiceMockingTest/persisted_file1.xml4
-rw-r--r--services/tests/mockingservicestests/assets/SensorPrivacyServiceMockingTest/persisted_file7.xml5
-rw-r--r--services/tests/mockingservicestests/assets/SensorPrivacyServiceMockingTest/persisted_file8.xml5
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java2
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java49
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java95
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt22
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/pm/PackageFreezerTest.kt3
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/pm/PackageManagerServiceBootTest.kt3
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/pm/PackageManagerServiceHibernationTests.kt3
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/pm/SharedLibrariesImplTest.kt34
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java6
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt9
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/sensorprivacy/SensorPrivacyServiceMockingTest.java405
-rw-r--r--services/tests/servicestests/src/com/android/server/DockObserverTest.java134
-rw-r--r--services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java58
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/log/BiometricContextProviderTest.java218
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/log/BiometricLoggerTest.java58
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/AcquisitionClientTest.java9
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/BaseClientMonitorTest.java5
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerOperationTest.java6
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java19
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/CompositeCallbackTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/UserAwareBiometricSchedulerTest.java25
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClientTest.java131
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceDetectClientTest.java118
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClientTest.java127
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceProviderTest.java10
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/SensorTest.java9
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/Face10Test.java6
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/FaceGenerateChallengeClientTest.java8
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java288
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClientTest.java123
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java282
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java10
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/SensorTest.java9
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21Test.java10
-rw-r--r--services/tests/servicestests/src/com/android/server/companion/virtual/InputControllerTest.java21
-rw-r--r--services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java48
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java322
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/MockUtils.java17
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/OverlayPackagesProviderTest.java22
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/OwnersTest.java9
-rw-r--r--services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java19
-rw-r--r--services/tests/servicestests/src/com/android/server/devicestate/OverrideRequestControllerTest.java93
-rw-r--r--services/tests/servicestests/src/com/android/server/display/BrightnessThrottlerTest.java321
-rw-r--r--services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java49
-rw-r--r--services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java123
-rw-r--r--services/tests/servicestests/src/com/android/server/display/whitebalance/AmbientLuxTest.java55
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java109
-rw-r--r--services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java28
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java13
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/PackageManagerTests.java36
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java14
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/ScanTests.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/power/LowPowerStandbyControllerTest.java410
-rw-r--r--services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java152
-rw-r--r--services/tests/servicestests/src/com/android/server/power/WakeLockLogTest.java19
-rw-r--r--services/tests/servicestests/utils/com/android/server/testutils/TestHandler.java7
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/NotificationComparatorTest.java9
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java10
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/ZenModeFilteringTest.java133
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java42
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java3
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java29
-rw-r--r--services/usage/java/com/android/server/usage/BroadcastResponseStatsTracker.java49
-rw-r--r--services/usage/java/com/android/server/usage/UsageStatsService.java9
-rw-r--r--services/usb/java/com/android/server/usb/UsbUniversalMidiDevice.java11
-rw-r--r--services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java34
-rwxr-xr-xtelecomm/java/android/telecom/ConnectionService.java25
-rw-r--r--telephony/java/android/telephony/CarrierConfigManager.java34
-rw-r--r--tests/ApkVerityTest/Android.bp15
-rw-r--r--tests/ApkVerityTest/block_device_writer/Android.bp39
-rw-r--r--tests/ApkVerityTest/block_device_writer/src/com/android/blockdevicewriter/BlockDeviceWriter.java2
-rw-r--r--tools/lint/README.md3
935 files changed, 36841 insertions, 8949 deletions
diff --git a/Android.bp b/Android.bp
index 8bc5d7aa0cd9..e03f8449891a 100644
--- a/Android.bp
+++ b/Android.bp
@@ -169,7 +169,6 @@ java_library_with_nonpublic_deps {
"framework-statsd.impl",
"framework-supplementalprocess.impl",
"framework-tethering.impl",
- "framework-nearby.impl",
"framework-uwb.impl",
"framework-wifi.impl",
"updatable-media",
@@ -177,6 +176,7 @@ java_library_with_nonpublic_deps {
soong_config_variables: {
include_nonpublic_framework_api: {
static_libs: [
+ "framework-auxiliary.impl",
"framework-supplementalapi.impl",
],
},
diff --git a/ApiDocs.bp b/ApiDocs.bp
index da6a2f648b5f..b5acfb28c0a0 100644
--- a/ApiDocs.bp
+++ b/ApiDocs.bp
@@ -80,6 +80,7 @@ stubs_defaults {
":i18n.module.public.api{.public.stubs.source}",
":framework-appsearch-sources",
+ ":framework-auxiliary-sources",
":framework-connectivity-sources",
":framework-bluetooth-sources",
":framework-connectivity-tiramisu-updatable-sources",
diff --git a/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java b/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java
index 9b1f2d07d58a..13ecd25d429a 100644
--- a/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java
+++ b/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java
@@ -3,6 +3,7 @@ package com.android.server.usage;
import android.annotation.CurrentTimeMillisLong;
import android.annotation.NonNull;
import android.annotation.UserIdInt;
+import android.app.ActivityManager.ProcessState;
import android.app.usage.AppStandbyInfo;
import android.app.usage.UsageStatsManager.ForcedReasons;
import android.app.usage.UsageStatsManager.StandbyBuckets;
@@ -216,4 +217,19 @@ public interface AppStandbyInternal {
void dumpState(String[] args, PrintWriter pw);
boolean isAppIdleEnabled();
+
+ /**
+ * Returns the duration (in millis) for the window where events occurring will be
+ * considered as broadcast response, starting from the point when an app receives
+ * a broadcast.
+ */
+ long getBroadcastResponseWindowDurationMs();
+
+ /**
+ * Returns the process state threshold that should be used for deciding whether or not an app
+ * is in the background in the context of recording broadcast response stats. Apps whose
+ * process state is higher than this threshold state should be considered to be in background.
+ */
+ @ProcessState
+ int getBroadcastResponseFgThresholdState();
}
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
index d93ad3c0d8bc..b2ae8ee62bc7 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -1209,18 +1209,16 @@ public class JobSchedulerService extends com.android.server.SystemService
synchronized (mLock) {
mStartedUsers = ArrayUtils.appendInt(mStartedUsers, user.getUserIdentifier());
}
- // The user is starting but credential encrypted storage is still locked.
- // Only direct-boot-aware jobs can safely run.
- // Let's kick off any eligible jobs for this user.
- mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
}
+ /** Start jobs after user is available, delayed by a few seconds since non-urgent. */
@Override
- public void onUserUnlocked(@NonNull TargetUser user) {
- // The user is fully unlocked and credential encrypted storage is now decrypted.
- // Direct-boot-UNaware jobs can now safely run.
- // Let's kick off any outstanding jobs for this user.
- mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
+ public void onUserCompletedEvent(@NonNull TargetUser user, UserCompletedEventType eventType) {
+ if (eventType.includesOnUserStarting() || eventType.includesOnUserUnlocked()) {
+ // onUserStarting: direct-boot-aware jobs can safely run
+ // onUserUnlocked: direct-boot-UNaware jobs can safely run.
+ mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
+ }
}
@Override
diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
index 8b2639781181..b843dcaaf680 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
+++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
@@ -347,6 +347,22 @@ public class AppStandbyController
*/
boolean mLinkCrossProfileApps =
ConstantsObserver.DEFAULT_CROSS_PROFILE_APPS_SHARE_STANDBY_BUCKETS;
+
+ /**
+ * Duration (in millis) for the window where events occurring will be considered as
+ * broadcast response, starting from the point when an app receives a broadcast.
+ */
+ volatile long mBroadcastResponseWindowDurationMillis =
+ ConstantsObserver.DEFAULT_BROADCAST_RESPONSE_WINDOW_DURATION_MS;
+
+ /**
+ * Process state threshold that is used for deciding whether or not an app is in the background
+ * in the context of recording broadcast response stats. Apps whose process state is higher
+ * than this threshold state will be considered to be in background.
+ */
+ volatile int mBroadcastResponseFgThresholdState =
+ ConstantsObserver.DEFAULT_BROADCAST_RESPONSE_FG_THRESHOLD_STATE;
+
/**
* Whether we should allow apps into the
* {@link android.app.usage.UsageStatsManager#STANDBY_BUCKET_RESTRICTED} bucket or not.
@@ -1774,6 +1790,15 @@ public class AppStandbyController
}
}
+ @Override
+ public long getBroadcastResponseWindowDurationMs() {
+ return mBroadcastResponseWindowDurationMillis;
+ }
+
+ @Override
+ public int getBroadcastResponseFgThresholdState() {
+ return mBroadcastResponseFgThresholdState;
+ }
@Override
public void flushToDisk() {
@@ -2042,6 +2067,14 @@ public class AppStandbyController
TimeUtils.formatDuration(mSystemUpdateUsageTimeoutMillis, pw);
pw.println();
+ pw.print(" mBroadcastResponseWindowDurationMillis=");
+ TimeUtils.formatDuration(mBroadcastResponseWindowDurationMillis, pw);
+ pw.println();
+
+ pw.print(" mBroadcastResponseFgThresholdState=");
+ pw.print(ActivityManager.procStateToString(mBroadcastResponseFgThresholdState));
+ pw.println();
+
pw.println();
pw.print("mAppIdleEnabled="); pw.print(mAppIdleEnabled);
pw.print(" mAllowRestrictedBucket=");
@@ -2473,6 +2506,10 @@ public class AppStandbyController
KEY_PREFIX_ELAPSED_TIME_THRESHOLD + "rare",
KEY_PREFIX_ELAPSED_TIME_THRESHOLD + "restricted"
};
+ private static final String KEY_BROADCAST_RESPONSE_WINDOW_DURATION_MS =
+ "broadcast_response_window_timeout_ms";
+ private static final String KEY_BROADCAST_RESPONSE_FG_THRESHOLD_STATE =
+ "broadcast_response_fg_threshold_state";
public static final long DEFAULT_CHECK_IDLE_INTERVAL_MS =
COMPRESS_TIME ? ONE_MINUTE : 4 * ONE_HOUR;
public static final long DEFAULT_STRONG_USAGE_TIMEOUT =
@@ -2502,6 +2539,10 @@ public class AppStandbyController
public static final long DEFAULT_AUTO_RESTRICTED_BUCKET_DELAY_MS =
COMPRESS_TIME ? ONE_MINUTE : ONE_DAY;
public static final boolean DEFAULT_CROSS_PROFILE_APPS_SHARE_STANDBY_BUCKETS = true;
+ public static final long DEFAULT_BROADCAST_RESPONSE_WINDOW_DURATION_MS =
+ 2 * ONE_MINUTE;
+ public static final int DEFAULT_BROADCAST_RESPONSE_FG_THRESHOLD_STATE =
+ ActivityManager.PROCESS_STATE_TOP;
ConstantsObserver(Handler handler) {
super(handler);
@@ -2619,6 +2660,16 @@ public class AppStandbyController
KEY_UNEXEMPTED_SYNC_SCHEDULED_HOLD_DURATION,
DEFAULT_UNEXEMPTED_SYNC_SCHEDULED_TIMEOUT);
break;
+ case KEY_BROADCAST_RESPONSE_WINDOW_DURATION_MS:
+ mBroadcastResponseWindowDurationMillis = properties.getLong(
+ KEY_BROADCAST_RESPONSE_WINDOW_DURATION_MS,
+ DEFAULT_BROADCAST_RESPONSE_WINDOW_DURATION_MS);
+ break;
+ case KEY_BROADCAST_RESPONSE_FG_THRESHOLD_STATE:
+ mBroadcastResponseFgThresholdState = properties.getInt(
+ KEY_BROADCAST_RESPONSE_FG_THRESHOLD_STATE,
+ DEFAULT_BROADCAST_RESPONSE_FG_THRESHOLD_STATE);
+ break;
default:
if (!timeThresholdsUpdated
&& (name.startsWith(KEY_PREFIX_SCREEN_TIME_THRESHOLD)
diff --git a/apex/jobscheduler/service/java/com/android/server/usage/TEST_MAPPING b/apex/jobscheduler/service/java/com/android/server/usage/TEST_MAPPING
index c5dc51cc9c24..e407e3126058 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/TEST_MAPPING
+++ b/apex/jobscheduler/service/java/com/android/server/usage/TEST_MAPPING
@@ -5,7 +5,9 @@
"options": [
{"include-filter": "android.app.usage.cts.UsageStatsTest"},
{"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
- {"exclude-annotation": "androidx.test.filters.FlakyTest"}
+ {"exclude-annotation": "androidx.test.filters.FlakyTest"},
+ {"exclude-annotation": "androidx.test.filters.MediumTest"},
+ {"exclude-annotation": "androidx.test.filters.LargeTest"}
]
},
{
diff --git a/apex/media/framework/Android.bp b/apex/media/framework/Android.bp
index d963e68d80ec..b4edd39894f9 100644
--- a/apex/media/framework/Android.bp
+++ b/apex/media/framework/Android.bp
@@ -61,6 +61,9 @@ java_library {
"test_com.android.media",
],
min_sdk_version: "29",
+ lint: {
+ strict_updatability_linting: true,
+ },
visibility: [
"//frameworks/av/apex:__subpackages__",
"//frameworks/base", // For framework-all
diff --git a/apex/media/framework/lint-baseline.xml b/apex/media/framework/lint-baseline.xml
index e1b145083f80..95eea45069ef 100644
--- a/apex/media/framework/lint-baseline.xml
+++ b/apex/media/framework/lint-baseline.xml
@@ -1,312 +1,70 @@
<?xml version="1.0" encoding="UTF-8"?>
-<issues format="5" by="lint 4.1.0" client="cli" variant="all" version="4.1.0">
+<issues format="6" by="lint 7.2.0-dev" type="baseline" client="" dependencies="true" name="" variant="all" version="7.2.0-dev">
<issue
- id="NewApi"
- message="Call requires API level 31 (current min is 29): `new android.media.ApplicationMediaCapabilities.Builder`"
- errorLine1=" new ApplicationMediaCapabilities.Builder();"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ id="DefaultLocale"
+ message="Implicitly using the default locale is a common source of bugs: Use `toLowerCase(Locale)` instead. For strings meant to be internal use `Locale.ROOT`, otherwise `Locale.getDefault()`."
+ errorLine1=" if (mSupportedVideoMimeTypes.contains(videoMime.toLowerCase())) {"
+ errorLine2=" ~~~~~~~~~~~">
<location
file="frameworks/base/apex/media/framework/java/android/media/ApplicationMediaCapabilities.java"
- line="208"
- column="29"/>
+ line="121"
+ column="57"/>
</issue>
<issue
- id="NewApi"
- message="Call requires API level 31 (current min is 29): `new android.media.ApplicationMediaCapabilities.Builder`"
- errorLine1=" ApplicationMediaCapabilities.Builder builder = new ApplicationMediaCapabilities.Builder();"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ id="DefaultLocale"
+ message="Implicitly using the default locale is a common source of bugs: Use `String.format(Locale, ...)` instead"
+ errorLine1=" return String.format(&quot; session: {id: %d, status: %s, result: %s, progress: %d}&quot;,"
+ errorLine2=" ^">
<location
- file="frameworks/base/apex/media/framework/java/android/media/ApplicationMediaCapabilities.java"
- line="314"
- column="56"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Call requires API level R (current min is 29): `android.os.RemoteException#rethrowFromSystemServer`"
- errorLine1=" e.rethrowFromSystemServer();"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="frameworks/base/apex/media/framework/java/android/media/MediaCommunicationManager.java"
- line="110"
- column="15"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Call requires API level R (current min is 29): `android.os.Parcel#writeParcelableCreator`"
- errorLine1=" dest.writeParcelableCreator((Parcelable) parcelable);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="frameworks/base/apex/media/framework/java/android/media/MediaParceledListSlice.java"
- line="77"
- column="14"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Call requires API level R (current min is 29): `android.os.Parcel#readParcelableCreator`"
- errorLine1=" return from.readParcelableCreator(loader);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="frameworks/base/apex/media/framework/java/android/media/MediaParceledListSlice.java"
- line="82"
- column="21"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Field requires API level R (current min is 29): `android.media.MediaParser.TrackData#mediaFormat`"
- errorLine1=" this.mediaFormat = mediaFormat;"
- errorLine2=" ~~~~~~~~~~~~~~~~">
- <location
- file="frameworks/base/apex/media/framework/java/android/media/MediaParser.java"
- line="273"
- column="13"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Field requires API level R (current min is 29): `android.media.MediaParser.TrackData#drmInitData`"
- errorLine1=" this.drmInitData = drmInitData;"
- errorLine2=" ~~~~~~~~~~~~~~~~">
- <location
- file="frameworks/base/apex/media/framework/java/android/media/MediaParser.java"
- line="274"
- column="13"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Field requires API level R (current min is 29): `android.media.MediaParser.SeekPoint#timeMicros`"
- errorLine1=" this.timeMicros = timeMicros;"
- errorLine2=" ~~~~~~~~~~~~~~~">
- <location
- file="frameworks/base/apex/media/framework/java/android/media/MediaParser.java"
- line="295"
- column="13"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Field requires API level R (current min is 29): `android.media.MediaParser.SeekPoint#position`"
- errorLine1=" this.position = position;"
- errorLine2=" ~~~~~~~~~~~~~">
- <location
- file="frameworks/base/apex/media/framework/java/android/media/MediaParser.java"
- line="296"
- column="13"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Field requires API level R (current min is 29): `android.media.MediaParser.SeekPoint#position`"
- errorLine1=" return &quot;[timeMicros=&quot; + timeMicros + &quot;, position=&quot; + position + &quot;]&quot;;"
- errorLine2=" ~~~~~~~~">
- <location
- file="frameworks/base/apex/media/framework/java/android/media/MediaParser.java"
- line="302"
- column="66"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Field requires API level R (current min is 29): `android.media.MediaParser.SeekPoint#timeMicros`"
- errorLine1=" return &quot;[timeMicros=&quot; + timeMicros + &quot;, position=&quot; + position + &quot;]&quot;;"
- errorLine2=" ~~~~~~~~~~">
- <location
- file="frameworks/base/apex/media/framework/java/android/media/MediaParser.java"
- line="302"
- column="37"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Class requires API level R (current min is 29): `android.media.MediaParser.SeekPoint`"
- errorLine1=" SeekPoint other = (SeekPoint) obj;"
- errorLine2=" ~~~~~~~~~">
- <location
- file="frameworks/base/apex/media/framework/java/android/media/MediaParser.java"
- line="313"
- column="32"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Field requires API level R (current min is 29): `android.media.MediaParser.SeekPoint#position`"
- errorLine1=" return timeMicros == other.timeMicros &amp;&amp; position == other.position;"
- errorLine2=" ~~~~~~~~">
- <location
- file="frameworks/base/apex/media/framework/java/android/media/MediaParser.java"
- line="314"
- column="54"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Field requires API level R (current min is 29): `android.media.MediaParser.SeekPoint#position`"
- errorLine1=" return timeMicros == other.timeMicros &amp;&amp; position == other.position;"
- errorLine2=" ~~~~~~~~~~~~~~">
- <location
- file="frameworks/base/apex/media/framework/java/android/media/MediaParser.java"
- line="314"
- column="66"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Field requires API level R (current min is 29): `android.media.MediaParser.SeekPoint#timeMicros`"
- errorLine1=" return timeMicros == other.timeMicros &amp;&amp; position == other.position;"
- errorLine2=" ~~~~~~~~~~">
- <location
- file="frameworks/base/apex/media/framework/java/android/media/MediaParser.java"
- line="314"
+ file="frameworks/base/apex/media/framework/java/android/media/MediaTranscodingManager.java"
+ line="1651"
column="20"/>
</issue>
<issue
- id="NewApi"
- message="Field requires API level R (current min is 29): `android.media.MediaParser.SeekPoint#timeMicros`"
- errorLine1=" return timeMicros == other.timeMicros &amp;&amp; position == other.position;"
- errorLine2=" ~~~~~~~~~~~~~~~~">
- <location
- file="frameworks/base/apex/media/framework/java/android/media/MediaParser.java"
- line="314"
- column="34"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Field requires API level R (current min is 29): `android.media.MediaParser.SeekPoint#timeMicros`"
- errorLine1=" int result = (int) timeMicros;"
- errorLine2=" ~~~~~~~~~~">
- <location
- file="frameworks/base/apex/media/framework/java/android/media/MediaParser.java"
- line="319"
- column="32"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Field requires API level R (current min is 29): `android.media.MediaParser.SeekPoint#position`"
- errorLine1=" result = 31 * result + (int) position;"
- errorLine2=" ~~~~~~~~">
- <location
- file="frameworks/base/apex/media/framework/java/android/media/MediaParser.java"
- line="320"
- column="42"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Class requires API level R (current min is 29): `android.media.MediaParser.InputReader`"
- errorLine1=" public interface SeekableInputReader extends InputReader {"
- errorLine2=" ~~~~~~~~~~~">
- <location
- file="frameworks/base/apex/media/framework/java/android/media/MediaParser.java"
- line="352"
- column="50"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Field requires API level 31 (current min is 29): `android.media.metrics.LogSessionId#LOG_SESSION_ID_NONE`"
- errorLine1=" @NonNull private LogSessionId mLogSessionId = LogSessionId.LOG_SESSION_ID_NONE;"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="frameworks/base/apex/media/framework/java/android/media/MediaParser.java"
- line="1071"
- column="51"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Cast from `SeekableInputReader` to `InputReader` requires API level 30 (current min is 29)"
- errorLine1=" mExoDataReader.mInputReader = seekableInputReader;"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~">
- <location
- file="frameworks/base/apex/media/framework/java/android/media/MediaParser.java"
- line="1201"
- column="39"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Field requires API level R (current min is 29): `android.media.MediaParser.SeekPoint#position`"
- errorLine1=" mPendingSeekPosition = seekPoint.position;"
- errorLine2=" ~~~~~~~~~~~~~~~~~~">
- <location
- file="frameworks/base/apex/media/framework/java/android/media/MediaParser.java"
- line="1287"
- column="36"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Field requires API level R (current min is 29): `android.media.MediaParser.SeekPoint#timeMicros`"
- errorLine1=" mPendingSeekTimeMicros = seekPoint.timeMicros;"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
- <location
- file="frameworks/base/apex/media/framework/java/android/media/MediaParser.java"
- line="1288"
- column="38"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Field requires API level R (current min is 29): `android.media.MediaParser.SeekPoint#position`"
- errorLine1=" mExtractor.seek(seekPoint.position, seekPoint.timeMicros);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~">
- <location
- file="frameworks/base/apex/media/framework/java/android/media/MediaParser.java"
- line="1290"
- column="29"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Field requires API level R (current min is 29): `android.media.MediaParser.SeekPoint#timeMicros`"
- errorLine1=" mExtractor.seek(seekPoint.position, seekPoint.timeMicros);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
+ id="ParcelClassLoader"
+ message="Passing null here (to use the default class loader) will not work if you are restoring your own classes. Consider using for example `getClass().getClassLoader()` instead."
+ errorLine1=" Bundle out = parcel.readBundle(null);"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
<location
- file="frameworks/base/apex/media/framework/java/android/media/MediaParser.java"
- line="1290"
- column="49"/>
+ file="frameworks/base/apex/media/framework/java/android/media/MediaSession2.java"
+ line="303"
+ column="33"/>
</issue>
<issue
- id="NewApi"
- message="Field requires API level R (current min is 29): `android.media.DrmInitData.SchemeInitData#uuid`"
- errorLine1=" if (schemeInitData.uuid.equals(schemeUuid)) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~">
+ id="ParcelClassLoader"
+ message="Using the default class loader will not work if you are restoring your own classes. Consider using for example `readBundle(getClass().getClassLoader())` instead."
+ errorLine1=" mCustomExtras = in.readBundle();"
+ errorLine2=" ~~~~~~~~~~~~">
<location
- file="frameworks/base/apex/media/framework/java/android/media/MediaParser.java"
- line="1579"
- column="21"/>
+ file="frameworks/base/apex/media/framework/java/android/media/Session2Command.java"
+ line="104"
+ column="28"/>
</issue>
<issue
- id="NewApi"
- message="Class requires API level R (current min is 29): `android.media.MediaParser.InputReader`"
- errorLine1=" private static final class DataReaderAdapter implements InputReader {"
- errorLine2=" ~~~~~~~~~~~">
+ id="ParcelClassLoader"
+ message="Passing null here (to use the default class loader) will not work if you are restoring your own classes. Consider using for example `getClass().getClassLoader()` instead."
+ errorLine1=" mSessionLink = in.readParcelable(null);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
<location
- file="frameworks/base/apex/media/framework/java/android/media/MediaParser.java"
- line="1872"
- column="61"/>
+ file="frameworks/base/apex/media/framework/java/android/media/Session2Token.java"
+ line="141"
+ column="27"/>
</issue>
<issue
- id="NewApi"
- message="Class requires API level R (current min is 29): `android.media.MediaParser.InputReader`"
- errorLine1=" private static final class ParsableByteArrayAdapter implements InputReader {"
- errorLine2=" ~~~~~~~~~~~">
+ id="ParcelClassLoader"
+ message="Using the default class loader will not work if you are restoring your own classes. Consider using for example `readBundle(getClass().getClassLoader())` instead."
+ errorLine1=" Bundle extras = in.readBundle();"
+ errorLine2=" ~~~~~~~~~~~~">
<location
- file="frameworks/base/apex/media/framework/java/android/media/MediaParser.java"
- line="1905"
- column="68"/>
+ file="frameworks/base/apex/media/framework/java/android/media/Session2Token.java"
+ line="144"
+ column="28"/>
</issue>
</issues>
diff --git a/apex/media/service/Android.bp b/apex/media/service/Android.bp
index cf384acccb12..834e5cbbd330 100644
--- a/apex/media/service/Android.bp
+++ b/apex/media/service/Android.bp
@@ -39,6 +39,7 @@ java_sdk_library {
":service-media-s-sources",
],
libs: [
+ "androidx.annotation_annotation",
"updatable-media",
"modules-annotation-minsdk",
"modules-utils-build",
@@ -46,6 +47,9 @@ java_sdk_library {
jarjar_rules: "jarjar_rules.txt",
sdk_version: "system_server_current",
min_sdk_version: "29", // TODO: We may need to bump this at some point.
+ lint: {
+ strict_updatability_linting: true,
+ },
apex_available: [
"com.android.media",
],
diff --git a/apex/media/service/java/com/android/server/media/MediaCommunicationService.java b/apex/media/service/java/com/android/server/media/MediaCommunicationService.java
index 7d47e250f99d..4223fa65fd53 100644
--- a/apex/media/service/java/com/android/server/media/MediaCommunicationService.java
+++ b/apex/media/service/java/com/android/server/media/MediaCommunicationService.java
@@ -46,6 +46,8 @@ import android.util.SparseArray;
import android.util.SparseIntArray;
import android.view.KeyEvent;
+import androidx.annotation.RequiresApi;
+
import com.android.internal.annotations.GuardedBy;
import com.android.modules.annotation.MinSdk;
import com.android.server.SystemService;
@@ -63,6 +65,7 @@ import java.util.concurrent.Executors;
* @hide
*/
@MinSdk(Build.VERSION_CODES.S)
+@RequiresApi(Build.VERSION_CODES.S)
public class MediaCommunicationService extends SystemService {
private static final String TAG = "MediaCommunicationSrv";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
diff --git a/apex/media/service/java/com/android/server/media/SessionPriorityList.java b/apex/media/service/java/com/android/server/media/SessionPriorityList.java
index 47b14b63ddba..814586139123 100644
--- a/apex/media/service/java/com/android/server/media/SessionPriorityList.java
+++ b/apex/media/service/java/com/android/server/media/SessionPriorityList.java
@@ -18,9 +18,13 @@ package com.android.server.media;
import android.annotation.Nullable;
import android.media.Session2Token;
+import android.os.Build;
import android.util.Log;
+import androidx.annotation.RequiresApi;
+
import com.android.internal.annotations.GuardedBy;
+import com.android.modules.annotation.MinSdk;
import com.android.server.media.MediaCommunicationService.Session2Record;
import java.util.ArrayList;
@@ -33,6 +37,8 @@ import java.util.List;
* Higher priority session has more chance to be selected as media button session,
* which receives the media button events.
*/
+@MinSdk(Build.VERSION_CODES.S)
+@RequiresApi(Build.VERSION_CODES.S)
class SessionPriorityList {
private static final String TAG = "SessionPriorityList";
private final Object mLock = new Object();
diff --git a/apex/media/service/lint-baseline.xml b/apex/media/service/lint-baseline.xml
index 05ce17c26872..def6baf0ff4f 100644
--- a/apex/media/service/lint-baseline.xml
+++ b/apex/media/service/lint-baseline.xml
@@ -1,37 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 7.1.0-dev" type="baseline" client="" dependencies="true" name="" variant="all" version="7.1.0-dev">
-
- <issue
- id="NewApi"
- message="Call requires API level S (current min is 29): `MediaParceledListSlice`"
- errorLine1=" new MediaParceledListSlice&lt;>(getSession2TokensLocked(ALL.getIdentifier()));"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="frameworks/base/apex/media/service/java/com/android/server/media/MediaCommunicationService.java"
- line="242"
- column="21"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Call requires API level S (current min is 29): `MediaParceledListSlice`"
- errorLine1=" userSession2Tokens = new MediaParceledListSlice&lt;>(getSession2TokensLocked(userId));"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="frameworks/base/apex/media/service/java/com/android/server/media/MediaCommunicationService.java"
- line="243"
- column="34"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Call requires API level S (current min is 29): `MediaParceledListSlice`"
- errorLine1=" MediaParceledListSlice parceledListSlice = new MediaParceledListSlice&lt;>(result);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="frameworks/base/apex/media/service/java/com/android/server/media/MediaCommunicationService.java"
- line="386"
- column="60"/>
- </issue>
+<issues format="6" by="lint 7.2.0-dev" type="baseline" client="" dependencies="true" name="" variant="all" version="7.2.0-dev">
</issues>
diff --git a/api/Android.bp b/api/Android.bp
index d8727f98a22c..d57f5dbdf948 100644
--- a/api/Android.bp
+++ b/api/Android.bp
@@ -116,7 +116,6 @@ combined_apis {
"framework-graphics",
"framework-media",
"framework-mediaprovider",
- "framework-nearby",
"framework-permission",
"framework-permission-s",
"framework-scheduling",
@@ -129,6 +128,7 @@ combined_apis {
"i18n.module.public.api",
],
conditional_bootclasspath: [
+ "framework-auxiliary",
"framework-supplementalapi",
],
system_server_classpath: [
diff --git a/api/api.go b/api/api.go
index aa9e399e7272..17649e80e81a 100644
--- a/api/api.go
+++ b/api/api.go
@@ -27,7 +27,6 @@ import (
const art = "art.module.public.api"
const conscrypt = "conscrypt.module.public.api"
const i18n = "i18n.module.public.api"
-var modules_with_only_public_scope = []string{i18n, conscrypt}
// 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
@@ -149,8 +148,6 @@ func createMergedStubsSrcjar(ctx android.LoadHookContext, modules []string) {
// This produces the same annotations.zip as framework-doc-stubs, but by using
// outputs from individual modules instead of all the source code.
func createMergedAnnotations(ctx android.LoadHookContext, modules []string) {
- // Conscrypt and i18n currently do not enable annotations
- modules = removeAll(modules, []string{conscrypt, i18n})
props := genruleProps{}
props.Name = proptools.StringPtr("sdk-annotations.zip")
props.Tools = []string{"merge_annotation_zips", "soong_zip"}
@@ -195,11 +192,8 @@ func createMergedPublicStubs(ctx android.LoadHookContext, modules []string) {
func createMergedSystemStubs(ctx android.LoadHookContext, modules []string) {
props := libraryProps{}
- modules_with_system_stubs := removeAll(modules, modules_with_only_public_scope)
props.Name = proptools.StringPtr("all-modules-system-stubs")
- props.Static_libs = append(
- transformArray(modules_with_only_public_scope, "", ".stubs"),
- transformArray(modules_with_system_stubs, "", ".stubs.system")...)
+ props.Static_libs = transformArray(modules, "", ".stubs.system")
props.Sdk_version = proptools.StringPtr("module_current")
props.Visibility = []string{"//frameworks/base"}
ctx.CreateModule(java.LibraryFactory, &props)
@@ -226,8 +220,6 @@ func createPublicStubsSourceFilegroup(ctx android.LoadHookContext, modules []str
func createMergedTxts(ctx android.LoadHookContext, bootclasspath, system_server_classpath []string) {
var textFiles []MergedTxtDefinition
- // Two module libraries currently do not support @SystemApi so only have the public scope.
- bcpWithSystemApi := removeAll(bootclasspath, modules_with_only_public_scope)
tagSuffix := []string{".api.txt}", ".removed-api.txt}"}
for i, f := range []string{"current.txt", "removed.txt"} {
@@ -241,14 +233,14 @@ func createMergedTxts(ctx android.LoadHookContext, bootclasspath, system_server_
textFiles = append(textFiles, MergedTxtDefinition{
TxtFilename: f,
BaseTxt: ":non-updatable-system-" + f,
- Modules: bcpWithSystemApi,
+ Modules: bootclasspath,
ModuleTag: "{.system" + tagSuffix[i],
Scope: "system",
})
textFiles = append(textFiles, MergedTxtDefinition{
TxtFilename: f,
BaseTxt: ":non-updatable-module-lib-" + f,
- Modules: bcpWithSystemApi,
+ Modules: bootclasspath,
ModuleTag: "{.module-lib" + tagSuffix[i],
Scope: "module-lib",
})
diff --git a/boot/Android.bp b/boot/Android.bp
index 3273f2c6ed8e..55ffe7c3dda3 100644
--- a/boot/Android.bp
+++ b/boot/Android.bp
@@ -56,6 +56,10 @@ platform_bootclasspath {
module: "art-bootclasspath-fragment",
},
{
+ apex: "com.android.auxiliary",
+ module: "com.android.auxiliary-bootclasspath-fragment",
+ },
+ {
apex: "com.android.conscrypt",
module: "com.android.conscrypt-bootclasspath-fragment",
},
@@ -76,10 +80,6 @@ platform_bootclasspath {
module: "com.android.mediaprovider-bootclasspath-fragment",
},
{
- apex: "com.android.nearby",
- module: "com.android.nearby-bootclasspath-fragment",
- },
- {
apex: "com.android.os.statsd",
module: "com.android.os.statsd-bootclasspath-fragment",
},
diff --git a/cmds/incidentd/src/WorkDirectory.cpp b/cmds/incidentd/src/WorkDirectory.cpp
index dd33fdfc28c3..c8d2e0ec4aca 100644
--- a/cmds/incidentd/src/WorkDirectory.cpp
+++ b/cmds/incidentd/src/WorkDirectory.cpp
@@ -280,7 +280,7 @@ void ReportFile::addReport(const IncidentReportArgs& args) {
// Lower privacy policy (less restrictive) wins.
report->set_privacy_policy(args.getPrivacyPolicy());
}
- report->set_all_sections(report->all_sections() | args.all());
+ report->set_all_sections(report->all_sections() || args.all());
for (int section: args.sections()) {
if (!has_section(*report, section)) {
report->add_section(section);
diff --git a/core/api/current.txt b/core/api/current.txt
index e1d05be57847..fee4944b19e4 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -135,9 +135,6 @@ package android {
field public static final String READ_HOME_APP_SEARCH_DATA = "android.permission.READ_HOME_APP_SEARCH_DATA";
field @Deprecated public static final String READ_INPUT_STATE = "android.permission.READ_INPUT_STATE";
field public static final String READ_LOGS = "android.permission.READ_LOGS";
- field public static final String READ_MEDIA_AUDIO = "android.permission.READ_MEDIA_AUDIO";
- field public static final String READ_MEDIA_IMAGE = "android.permission.READ_MEDIA_IMAGE";
- field public static final String READ_MEDIA_VIDEO = "android.permission.READ_MEDIA_VIDEO";
field public static final String READ_NEARBY_STREAMING_POLICY = "android.permission.READ_NEARBY_STREAMING_POLICY";
field public static final String READ_PHONE_NUMBERS = "android.permission.READ_PHONE_NUMBERS";
field public static final String READ_PHONE_STATE = "android.permission.READ_PHONE_STATE";
@@ -223,8 +220,6 @@ package android {
field public static final String NEARBY_DEVICES = "android.permission-group.NEARBY_DEVICES";
field public static final String NOTIFICATIONS = "android.permission-group.NOTIFICATIONS";
field public static final String PHONE = "android.permission-group.PHONE";
- field public static final String READ_MEDIA_AURAL = "android.permission-group.READ_MEDIA_AURAL";
- field public static final String READ_MEDIA_VISUAL = "android.permission-group.READ_MEDIA_VISUAL";
field public static final String SENSORS = "android.permission-group.SENSORS";
field public static final String SMS = "android.permission-group.SMS";
field public static final String STORAGE = "android.permission-group.STORAGE";
@@ -734,6 +729,10 @@ package android {
field public static final int freezesText = 16843116; // 0x101016c
field public static final int fromAlpha = 16843210; // 0x10101ca
field public static final int fromDegrees = 16843187; // 0x10101b3
+ field public static final int fromExtendBottom;
+ field public static final int fromExtendLeft;
+ field public static final int fromExtendRight;
+ field public static final int fromExtendTop;
field public static final int fromId = 16843850; // 0x101044a
field public static final int fromScene = 16843741; // 0x10103dd
field public static final int fromXDelta = 16843206; // 0x10101c6
@@ -1579,6 +1578,10 @@ package android {
field public static final int titleTextStyle = 16843512; // 0x10102f8
field public static final int toAlpha = 16843211; // 0x10101cb
field public static final int toDegrees = 16843188; // 0x10101b4
+ field public static final int toExtendBottom;
+ field public static final int toExtendLeft;
+ field public static final int toExtendRight;
+ field public static final int toExtendTop;
field public static final int toId = 16843849; // 0x1010449
field public static final int toScene = 16843742; // 0x10103de
field public static final int toXDelta = 16843207; // 0x10101c7
@@ -2108,6 +2111,8 @@ package android {
field public static final int icon_frame = 16908350; // 0x102003e
field public static final int input = 16908297; // 0x1020009
field public static final int inputArea = 16908318; // 0x102001e
+ field public static final int inputExtractAccessories;
+ field public static final int inputExtractAction;
field public static final int inputExtractEditText = 16908325; // 0x1020025
field @Deprecated public static final int keyboardView = 16908326; // 0x1020026
field public static final int list = 16908298; // 0x102000a
@@ -4181,6 +4186,7 @@ package android.app {
method public void openContextMenu(android.view.View);
method public void openOptionsMenu();
method public void overridePendingTransition(int, int);
+ method public void overridePendingTransition(int, int, int);
method public void postponeEnterTransition();
method public void recreate();
method public void registerActivityLifecycleCallbacks(@NonNull android.app.Application.ActivityLifecycleCallbacks);
@@ -4484,6 +4490,7 @@ package android.app {
method public static android.app.ActivityOptions makeBasic();
method public static android.app.ActivityOptions makeClipRevealAnimation(android.view.View, int, int, int, int);
method public static android.app.ActivityOptions makeCustomAnimation(android.content.Context, int, int);
+ method @NonNull public static android.app.ActivityOptions makeCustomAnimation(@NonNull android.content.Context, int, int, int);
method public static android.app.ActivityOptions makeScaleUpAnimation(android.view.View, int, int, int, int);
method public static android.app.ActivityOptions makeSceneTransitionAnimation(android.app.Activity, android.view.View, String);
method @java.lang.SafeVarargs public static android.app.ActivityOptions makeSceneTransitionAnimation(android.app.Activity, android.util.Pair<android.view.View,java.lang.String>...);
@@ -7307,10 +7314,10 @@ package android.app.admin {
method @Nullable public java.util.List<java.lang.String> getDelegatePackages(@NonNull android.content.ComponentName, @NonNull String);
method @NonNull public java.util.List<java.lang.String> getDelegatedScopes(@Nullable android.content.ComponentName, @NonNull String);
method public CharSequence getDeviceOwnerLockScreenInfo();
- method @NonNull public android.graphics.drawable.Drawable getDrawable(@NonNull String, @NonNull String, @NonNull java.util.concurrent.Callable<android.graphics.drawable.Drawable>);
- method @NonNull public android.graphics.drawable.Drawable getDrawable(@NonNull String, @NonNull String, @NonNull String, @NonNull java.util.concurrent.Callable<android.graphics.drawable.Drawable>);
- method @NonNull public android.graphics.drawable.Drawable getDrawableForDensity(@NonNull String, @NonNull String, int, @NonNull java.util.concurrent.Callable<android.graphics.drawable.Drawable>);
- method @NonNull public android.graphics.drawable.Drawable getDrawableForDensity(@NonNull String, @NonNull String, @NonNull String, int, @NonNull java.util.concurrent.Callable<android.graphics.drawable.Drawable>);
+ method @Nullable public android.graphics.drawable.Drawable getDrawable(@NonNull String, @NonNull String, @NonNull java.util.concurrent.Callable<android.graphics.drawable.Drawable>);
+ method @Nullable public android.graphics.drawable.Drawable getDrawable(@NonNull String, @NonNull String, @NonNull String, @NonNull java.util.concurrent.Callable<android.graphics.drawable.Drawable>);
+ method @Nullable public android.graphics.drawable.Drawable getDrawableForDensity(@NonNull String, @NonNull String, int, @NonNull java.util.concurrent.Callable<android.graphics.drawable.Drawable>);
+ method @Nullable public android.graphics.drawable.Drawable getDrawableForDensity(@NonNull String, @NonNull String, @NonNull String, int, @NonNull java.util.concurrent.Callable<android.graphics.drawable.Drawable>);
method public CharSequence getEndUserSessionMessage(@NonNull android.content.ComponentName);
method @NonNull public String getEnrollmentSpecificId();
method @Nullable public android.app.admin.FactoryResetProtectionPolicy getFactoryResetProtectionPolicy(@Nullable android.content.ComponentName);
@@ -11886,6 +11893,7 @@ package android.content.pm {
field public static final String FEATURE_SENSOR_AMBIENT_TEMPERATURE = "android.hardware.sensor.ambient_temperature";
field public static final String FEATURE_SENSOR_BAROMETER = "android.hardware.sensor.barometer";
field public static final String FEATURE_SENSOR_COMPASS = "android.hardware.sensor.compass";
+ field public static final String FEATURE_SENSOR_DYNAMIC_HEAD_TRACKER = "android.hardware.sensor.dynamic.head_tracker";
field public static final String FEATURE_SENSOR_GYROSCOPE = "android.hardware.sensor.gyroscope";
field public static final String FEATURE_SENSOR_HEART_RATE = "android.hardware.sensor.heartrate";
field public static final String FEATURE_SENSOR_HEART_RATE_ECG = "android.hardware.sensor.heartrate.ecg";
@@ -12220,7 +12228,7 @@ package android.content.pm {
field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.SharedLibraryInfo> CREATOR;
field public static final int TYPE_BUILTIN = 0; // 0x0
field public static final int TYPE_DYNAMIC = 1; // 0x1
- field public static final int TYPE_SDK = 3; // 0x3
+ field public static final int TYPE_SDK_PACKAGE = 3; // 0x3
field public static final int TYPE_STATIC = 2; // 0x2
field public static final int VERSION_UNDEFINED = -1; // 0xffffffff
}
@@ -12948,6 +12956,10 @@ package android.database {
field @NonNull public static final android.os.Parcelable.Creator<android.database.CursorWindow> CREATOR;
}
+ public class CursorWindowAllocationException extends java.lang.RuntimeException {
+ ctor public CursorWindowAllocationException(@NonNull String);
+ }
+
public class CursorWrapper implements android.database.Cursor {
ctor public CursorWrapper(android.database.Cursor);
method public void close();
@@ -13979,6 +13991,7 @@ package android.graphics {
enum_constant @Deprecated public static final android.graphics.Bitmap.Config ARGB_4444;
enum_constant public static final android.graphics.Bitmap.Config ARGB_8888;
enum_constant public static final android.graphics.Bitmap.Config HARDWARE;
+ enum_constant public static final android.graphics.Bitmap.Config RGBA_1010102;
enum_constant public static final android.graphics.Bitmap.Config RGBA_F16;
enum_constant public static final android.graphics.Bitmap.Config RGB_565;
}
@@ -22718,6 +22731,7 @@ package android.media {
method public int describeContents();
method @Nullable public String getClientPackageName();
method public int getConnectionState();
+ method @NonNull public java.util.Set<java.lang.String> getDeduplicationIds();
method @Nullable public CharSequence getDescription();
method @Nullable public android.os.Bundle getExtras();
method @NonNull public java.util.List<java.lang.String> getFeatures();
@@ -22751,6 +22765,7 @@ package android.media {
method @NonNull public android.media.MediaRoute2Info.Builder clearFeatures();
method @NonNull public android.media.MediaRoute2Info.Builder setClientPackageName(@Nullable String);
method @NonNull public android.media.MediaRoute2Info.Builder setConnectionState(int);
+ method @NonNull public android.media.MediaRoute2Info.Builder setDeduplicationIds(@NonNull java.util.Set<java.lang.String>);
method @NonNull public android.media.MediaRoute2Info.Builder setDescription(@Nullable CharSequence);
method @NonNull public android.media.MediaRoute2Info.Builder setExtras(@Nullable android.os.Bundle);
method @NonNull public android.media.MediaRoute2Info.Builder setIconUri(@Nullable android.net.Uri);
@@ -23277,8 +23292,12 @@ package android.media {
public final class RouteDiscoveryPreference implements android.os.Parcelable {
method public int describeContents();
+ method @NonNull public java.util.List<java.lang.String> getAllowedPackages();
+ method @NonNull public java.util.List<java.lang.String> getDeduplicationPackageOrder();
method @NonNull public java.util.List<java.lang.String> getPreferredFeatures();
+ method @NonNull public java.util.List<java.lang.String> getRequiredFeatures();
method public boolean shouldPerformActiveScan();
+ method public boolean shouldRemoveDuplicates();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.media.RouteDiscoveryPreference> CREATOR;
}
@@ -23287,7 +23306,10 @@ package android.media {
ctor public RouteDiscoveryPreference.Builder(@NonNull java.util.List<java.lang.String>, boolean);
ctor public RouteDiscoveryPreference.Builder(@NonNull android.media.RouteDiscoveryPreference);
method @NonNull public android.media.RouteDiscoveryPreference build();
+ method @NonNull public android.media.RouteDiscoveryPreference.Builder setAllowedPackages(@NonNull java.util.List<java.lang.String>);
+ method @NonNull public android.media.RouteDiscoveryPreference.Builder setDeduplicationPackageOrder(@NonNull java.util.List<java.lang.String>);
method @NonNull public android.media.RouteDiscoveryPreference.Builder setPreferredFeatures(@NonNull java.util.List<java.lang.String>);
+ method @NonNull public android.media.RouteDiscoveryPreference.Builder setRequiredFeatures(@NonNull java.util.List<java.lang.String>);
method @NonNull public android.media.RouteDiscoveryPreference.Builder setShouldPerformActiveScan(boolean);
}
@@ -31678,6 +31700,7 @@ package android.os {
method public boolean isDeviceLightIdleMode();
method public boolean isIgnoringBatteryOptimizations(String);
method public boolean isInteractive();
+ method public boolean isLowPowerStandbyEnabled();
method public boolean isPowerSaveMode();
method public boolean isRebootingUserspaceSupported();
method @Deprecated public boolean isScreenOn();
@@ -31689,6 +31712,7 @@ package android.os {
field public static final int ACQUIRE_CAUSES_WAKEUP = 268435456; // 0x10000000
field public static final String ACTION_DEVICE_IDLE_MODE_CHANGED = "android.os.action.DEVICE_IDLE_MODE_CHANGED";
field public static final String ACTION_DEVICE_LIGHT_IDLE_MODE_CHANGED = "android.os.action.LIGHT_DEVICE_IDLE_MODE_CHANGED";
+ field public static final String ACTION_LOW_POWER_STANDBY_ENABLED_CHANGED = "android.os.action.LOW_POWER_STANDBY_ENABLED_CHANGED";
field public static final String ACTION_POWER_SAVE_MODE_CHANGED = "android.os.action.POWER_SAVE_MODE_CHANGED";
field @Deprecated public static final int FULL_WAKE_LOCK = 26; // 0x1a
field public static final int LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF = 2; // 0x2
@@ -31722,9 +31746,14 @@ package android.os {
method public void release();
method public void release(int);
method public void setReferenceCounted(boolean);
+ method public void setStateListener(@NonNull java.util.concurrent.Executor, @Nullable android.os.PowerManager.WakeLockStateListener);
method public void setWorkSource(android.os.WorkSource);
}
+ public static interface PowerManager.WakeLockStateListener {
+ method public void onStateChanged(boolean);
+ }
+
public class Process {
ctor public Process();
method public static final long getElapsedCpuTime();
@@ -32413,6 +32442,7 @@ package android.os.storage {
method @Nullable public android.os.storage.StorageVolume getStorageVolume(@NonNull java.io.File);
method @NonNull public android.os.storage.StorageVolume getStorageVolume(@NonNull android.net.Uri);
method @NonNull public java.util.List<android.os.storage.StorageVolume> getStorageVolumes();
+ method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_EXTERNAL_STORAGE) public java.util.List<android.os.storage.StorageVolume> getStorageVolumesIncludingSharedProfiles();
method @NonNull public java.util.UUID getUuidForPath(@NonNull java.io.File) throws java.io.IOException;
method public boolean isAllocationSupported(@NonNull java.io.FileDescriptor);
method public boolean isCacheBehaviorGroup(java.io.File) throws java.io.IOException;
@@ -32446,6 +32476,7 @@ package android.os.storage {
method public String getDescription(android.content.Context);
method @Nullable public java.io.File getDirectory();
method @Nullable public String getMediaStoreVolumeName();
+ method @NonNull public android.os.UserHandle getOwner();
method public String getState();
method @Nullable public java.util.UUID getStorageUuid();
method @Nullable public String getUuid();
@@ -37873,6 +37904,7 @@ package android.service.autofill {
method public abstract void onFillRequest(@NonNull android.service.autofill.FillRequest, @NonNull android.os.CancellationSignal, @NonNull android.service.autofill.FillCallback);
method public abstract void onSaveRequest(@NonNull android.service.autofill.SaveRequest, @NonNull android.service.autofill.SaveCallback);
method public void onSavedDatasetsInfoRequest(@NonNull android.service.autofill.SavedDatasetsInfoCallback);
+ field public static final String EXTRA_FILL_RESPONSE = "android.service.autofill.extra.FILL_RESPONSE";
field public static final String SERVICE_INTERFACE = "android.service.autofill.AutofillService";
field public static final String SERVICE_META_DATA = "android.autofill";
}
@@ -37923,21 +37955,23 @@ package android.service.autofill {
}
public static final class Dataset.Builder {
- ctor public Dataset.Builder(@NonNull android.widget.RemoteViews);
+ ctor @Deprecated public Dataset.Builder(@NonNull android.widget.RemoteViews);
+ ctor public Dataset.Builder(@NonNull android.service.autofill.Presentations);
ctor public Dataset.Builder();
method @NonNull public android.service.autofill.Dataset build();
method @NonNull public android.service.autofill.Dataset.Builder setAuthentication(@Nullable android.content.IntentSender);
+ method @NonNull public android.service.autofill.Dataset.Builder setField(@NonNull android.view.autofill.AutofillId, @Nullable android.service.autofill.Field);
method @NonNull public android.service.autofill.Dataset.Builder setId(@Nullable String);
- method @NonNull public android.service.autofill.Dataset.Builder setInlinePresentation(@NonNull android.service.autofill.InlinePresentation);
- method @NonNull public android.service.autofill.Dataset.Builder setInlinePresentation(@NonNull android.service.autofill.InlinePresentation, @NonNull android.service.autofill.InlinePresentation);
- method @NonNull public android.service.autofill.Dataset.Builder setValue(@NonNull android.view.autofill.AutofillId, @Nullable android.view.autofill.AutofillValue);
- method @NonNull public android.service.autofill.Dataset.Builder setValue(@NonNull android.view.autofill.AutofillId, @Nullable android.view.autofill.AutofillValue, @NonNull android.widget.RemoteViews);
- method @NonNull public android.service.autofill.Dataset.Builder setValue(@NonNull android.view.autofill.AutofillId, @Nullable android.view.autofill.AutofillValue, @Nullable java.util.regex.Pattern);
- method @NonNull public android.service.autofill.Dataset.Builder setValue(@NonNull android.view.autofill.AutofillId, @Nullable android.view.autofill.AutofillValue, @Nullable java.util.regex.Pattern, @NonNull android.widget.RemoteViews);
- method @NonNull public android.service.autofill.Dataset.Builder setValue(@NonNull android.view.autofill.AutofillId, @Nullable android.view.autofill.AutofillValue, @NonNull android.widget.RemoteViews, @NonNull android.service.autofill.InlinePresentation);
- method @NonNull public android.service.autofill.Dataset.Builder setValue(@NonNull android.view.autofill.AutofillId, @Nullable android.view.autofill.AutofillValue, @NonNull android.widget.RemoteViews, @NonNull android.service.autofill.InlinePresentation, @NonNull android.service.autofill.InlinePresentation);
- method @NonNull public android.service.autofill.Dataset.Builder setValue(@NonNull android.view.autofill.AutofillId, @Nullable android.view.autofill.AutofillValue, @Nullable java.util.regex.Pattern, @NonNull android.widget.RemoteViews, @NonNull android.service.autofill.InlinePresentation);
- method @NonNull public android.service.autofill.Dataset.Builder setValue(@NonNull android.view.autofill.AutofillId, @Nullable android.view.autofill.AutofillValue, @Nullable java.util.regex.Pattern, @NonNull android.widget.RemoteViews, @NonNull android.service.autofill.InlinePresentation, @NonNull android.service.autofill.InlinePresentation);
+ method @Deprecated @NonNull public android.service.autofill.Dataset.Builder setInlinePresentation(@NonNull android.service.autofill.InlinePresentation);
+ method @Deprecated @NonNull public android.service.autofill.Dataset.Builder setInlinePresentation(@NonNull android.service.autofill.InlinePresentation, @NonNull android.service.autofill.InlinePresentation);
+ method @Deprecated @NonNull public android.service.autofill.Dataset.Builder setValue(@NonNull android.view.autofill.AutofillId, @Nullable android.view.autofill.AutofillValue);
+ method @Deprecated @NonNull public android.service.autofill.Dataset.Builder setValue(@NonNull android.view.autofill.AutofillId, @Nullable android.view.autofill.AutofillValue, @NonNull android.widget.RemoteViews);
+ method @Deprecated @NonNull public android.service.autofill.Dataset.Builder setValue(@NonNull android.view.autofill.AutofillId, @Nullable android.view.autofill.AutofillValue, @Nullable java.util.regex.Pattern);
+ method @Deprecated @NonNull public android.service.autofill.Dataset.Builder setValue(@NonNull android.view.autofill.AutofillId, @Nullable android.view.autofill.AutofillValue, @Nullable java.util.regex.Pattern, @NonNull android.widget.RemoteViews);
+ method @Deprecated @NonNull public android.service.autofill.Dataset.Builder setValue(@NonNull android.view.autofill.AutofillId, @Nullable android.view.autofill.AutofillValue, @NonNull android.widget.RemoteViews, @NonNull android.service.autofill.InlinePresentation);
+ method @Deprecated @NonNull public android.service.autofill.Dataset.Builder setValue(@NonNull android.view.autofill.AutofillId, @Nullable android.view.autofill.AutofillValue, @NonNull android.widget.RemoteViews, @NonNull android.service.autofill.InlinePresentation, @NonNull android.service.autofill.InlinePresentation);
+ method @Deprecated @NonNull public android.service.autofill.Dataset.Builder setValue(@NonNull android.view.autofill.AutofillId, @Nullable android.view.autofill.AutofillValue, @Nullable java.util.regex.Pattern, @NonNull android.widget.RemoteViews, @NonNull android.service.autofill.InlinePresentation);
+ method @Deprecated @NonNull public android.service.autofill.Dataset.Builder setValue(@NonNull android.view.autofill.AutofillId, @Nullable android.view.autofill.AutofillValue, @Nullable java.util.regex.Pattern, @NonNull android.widget.RemoteViews, @NonNull android.service.autofill.InlinePresentation, @NonNull android.service.autofill.InlinePresentation);
}
public final class DateTransformation implements android.os.Parcelable android.service.autofill.Transformation {
@@ -37954,6 +37988,19 @@ package android.service.autofill {
field @NonNull public static final android.os.Parcelable.Creator<android.service.autofill.DateValueSanitizer> CREATOR;
}
+ public final class Field {
+ method @Nullable public android.service.autofill.Presentations getPresentations();
+ method @Nullable public android.view.autofill.AutofillValue getValue();
+ }
+
+ public static final class Field.Builder {
+ ctor public Field.Builder();
+ method @NonNull public android.service.autofill.Field build();
+ method @NonNull public android.service.autofill.Field.Builder setFilter(@Nullable java.util.regex.Pattern);
+ method @NonNull public android.service.autofill.Field.Builder setPresentations(@NonNull android.service.autofill.Presentations);
+ method @NonNull public android.service.autofill.Field.Builder setValue(@NonNull android.view.autofill.AutofillValue);
+ }
+
public final class FieldClassification {
method @NonNull public java.util.List<android.service.autofill.FieldClassification.Match> getMatches();
}
@@ -38013,6 +38060,7 @@ package android.service.autofill {
public final class FillRequest implements android.os.Parcelable {
method public int describeContents();
method @Nullable public android.os.Bundle getClientState();
+ method @Nullable public android.content.IntentSender getDelayedFillIntentSender();
method @NonNull public java.util.List<android.service.autofill.FillContext> getFillContexts();
method public int getFlags();
method public int getId();
@@ -38027,6 +38075,7 @@ package android.service.autofill {
method public int describeContents();
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.service.autofill.FillResponse> CREATOR;
+ field public static final int FLAG_DELAY_FILL = 4; // 0x4
field public static final int FLAG_DISABLE_ACTIVITY_ONLY = 2; // 0x2
field public static final int FLAG_TRACK_CONTEXT_COMMITED = 1; // 0x1
}
@@ -38036,11 +38085,14 @@ package android.service.autofill {
method @NonNull public android.service.autofill.FillResponse.Builder addDataset(@Nullable android.service.autofill.Dataset);
method @NonNull public android.service.autofill.FillResponse build();
method @NonNull public android.service.autofill.FillResponse.Builder disableAutofill(long);
- method @NonNull public android.service.autofill.FillResponse.Builder setAuthentication(@NonNull android.view.autofill.AutofillId[], @Nullable android.content.IntentSender, @Nullable android.widget.RemoteViews);
- method @NonNull public android.service.autofill.FillResponse.Builder setAuthentication(@NonNull android.view.autofill.AutofillId[], @Nullable android.content.IntentSender, @Nullable android.widget.RemoteViews, @Nullable android.service.autofill.InlinePresentation);
- method @NonNull public android.service.autofill.FillResponse.Builder setAuthentication(@NonNull android.view.autofill.AutofillId[], @Nullable android.content.IntentSender, @Nullable android.widget.RemoteViews, @Nullable android.service.autofill.InlinePresentation, @Nullable android.service.autofill.InlinePresentation);
+ method @Deprecated @NonNull public android.service.autofill.FillResponse.Builder setAuthentication(@NonNull android.view.autofill.AutofillId[], @Nullable android.content.IntentSender, @Nullable android.widget.RemoteViews);
+ method @Deprecated @NonNull public android.service.autofill.FillResponse.Builder setAuthentication(@NonNull android.view.autofill.AutofillId[], @Nullable android.content.IntentSender, @Nullable android.widget.RemoteViews, @Nullable android.service.autofill.InlinePresentation);
+ method @Deprecated @NonNull public android.service.autofill.FillResponse.Builder setAuthentication(@NonNull android.view.autofill.AutofillId[], @Nullable android.content.IntentSender, @Nullable android.widget.RemoteViews, @Nullable android.service.autofill.InlinePresentation, @Nullable android.service.autofill.InlinePresentation);
+ method @NonNull public android.service.autofill.FillResponse.Builder setAuthentication(@NonNull android.view.autofill.AutofillId[], @Nullable android.content.IntentSender, @Nullable android.service.autofill.Presentations);
method @NonNull public android.service.autofill.FillResponse.Builder setClientState(@Nullable android.os.Bundle);
+ method @NonNull public android.service.autofill.FillResponse.Builder setDialogHeader(@NonNull android.widget.RemoteViews);
method @NonNull public android.service.autofill.FillResponse.Builder setFieldClassificationIds(@NonNull android.view.autofill.AutofillId...);
+ method @NonNull public android.service.autofill.FillResponse.Builder setFillDialogTriggerIds(@NonNull android.view.autofill.AutofillId...);
method @NonNull public android.service.autofill.FillResponse.Builder setFlags(int);
method @NonNull public android.service.autofill.FillResponse.Builder setFooter(@NonNull android.widget.RemoteViews);
method @NonNull public android.service.autofill.FillResponse.Builder setHeader(@NonNull android.widget.RemoteViews);
@@ -38085,6 +38137,22 @@ package android.service.autofill {
public interface OnClickAction {
}
+ public final class Presentations {
+ method @Nullable public android.widget.RemoteViews getDialogPresentation();
+ method @Nullable public android.service.autofill.InlinePresentation getInlinePresentation();
+ method @Nullable public android.service.autofill.InlinePresentation getInlineTooltipPresentation();
+ method @Nullable public android.widget.RemoteViews getMenuPresentation();
+ }
+
+ public static final class Presentations.Builder {
+ ctor public Presentations.Builder();
+ method @NonNull public android.service.autofill.Presentations build();
+ method @NonNull public android.service.autofill.Presentations.Builder setDialogPresentation(@NonNull android.widget.RemoteViews);
+ method @NonNull public android.service.autofill.Presentations.Builder setInlinePresentation(@NonNull android.service.autofill.InlinePresentation);
+ method @NonNull public android.service.autofill.Presentations.Builder setInlineTooltipPresentation(@NonNull android.service.autofill.InlinePresentation);
+ method @NonNull public android.service.autofill.Presentations.Builder setMenuPresentation(@NonNull android.widget.RemoteViews);
+ }
+
public final class RegexValidator implements android.os.Parcelable android.service.autofill.Validator {
ctor public RegexValidator(@NonNull android.view.autofill.AutofillId, @NonNull java.util.regex.Pattern);
method public int describeContents();
@@ -41153,6 +41221,7 @@ package android.telephony {
field public static final String KEY_IMS_CONFERENCE_SIZE_LIMIT_INT = "ims_conference_size_limit_int";
field public static final String KEY_IMS_DTMF_TONE_DELAY_INT = "ims_dtmf_tone_delay_int";
field public static final String KEY_IS_IMS_CONFERENCE_SIZE_ENFORCED_BOOL = "is_ims_conference_size_enforced_bool";
+ field public static final String KEY_IS_OPPORTUNISTIC_SUBSCRIPTION_BOOL = "is_opportunistic_subscription_bool";
field public static final String KEY_LTE_ENABLED_BOOL = "lte_enabled_bool";
field public static final String KEY_LTE_RSRQ_THRESHOLDS_INT_ARRAY = "lte_rsrq_thresholds_int_array";
field public static final String KEY_LTE_RSSNR_THRESHOLDS_INT_ARRAY = "lte_rssnr_thresholds_int_array";
@@ -41237,6 +41306,7 @@ package android.telephony {
field public static final String KEY_SIM_NETWORK_UNLOCK_ALLOW_DISMISS_BOOL = "sim_network_unlock_allow_dismiss_bool";
field public static final String KEY_SMDP_SERVER_ADDRESS_STRING = "smdp_server_address_string";
field public static final String KEY_SMS_REQUIRES_DESTINATION_NUMBER_CONVERSION_BOOL = "sms_requires_destination_number_conversion_bool";
+ field public static final String KEY_SUBSCRIPTION_GROUP_UUID_STRING = "subscription_group_uuid_string";
field public static final String KEY_SUPPORTS_CALL_COMPOSER_BOOL = "supports_call_composer_bool";
field public static final String KEY_SUPPORTS_DEVICE_TO_DEVICE_COMMUNICATION_USING_DTMF_BOOL = "supports_device_to_device_communication_using_dtmf_bool";
field public static final String KEY_SUPPORTS_DEVICE_TO_DEVICE_COMMUNICATION_USING_RTP_BOOL = "supports_device_to_device_communication_using_rtp_bool";
@@ -41280,6 +41350,7 @@ package android.telephony {
field public static final String KEY_WFC_EMERGENCY_ADDRESS_CARRIER_APP_STRING = "wfc_emergency_address_carrier_app_string";
field public static final String KEY_WORLD_MODE_ENABLED_BOOL = "world_mode_enabled_bool";
field public static final String KEY_WORLD_PHONE_BOOL = "world_phone_bool";
+ field public static final String REMOVE_GROUP_UUID_STRING = "00000000-0000-0000-0000-000000000000";
field public static final int SERVICE_CLASS_NONE = 0; // 0x0
field public static final int SERVICE_CLASS_VOICE = 1; // 0x1
field public static final int USSD_OVER_CS_ONLY = 2; // 0x2
@@ -49163,6 +49234,8 @@ package android.view {
public static final class SurfaceControlViewHost.SurfacePackage implements android.os.Parcelable {
ctor public SurfaceControlViewHost.SurfacePackage(@NonNull android.view.SurfaceControlViewHost.SurfacePackage);
method public int describeContents();
+ method public void notifyConfigurationChanged(@NonNull android.content.res.Configuration);
+ method public void notifyDetachedFromWindow();
method public void release();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.view.SurfaceControlViewHost.SurfacePackage> CREATOR;
@@ -51598,6 +51671,7 @@ package android.view.accessibility {
method public boolean isSelected();
method public boolean isShowingHintText();
method public boolean isTextEntryKey();
+ method public boolean isTextSelectable();
method public boolean isVisibleToUser();
method @Deprecated public static android.view.accessibility.AccessibilityNodeInfo obtain(android.view.View);
method @Deprecated public static android.view.accessibility.AccessibilityNodeInfo obtain(android.view.View, int);
@@ -51661,6 +51735,7 @@ package android.view.accessibility {
method public void setStateDescription(@Nullable CharSequence);
method public void setText(CharSequence);
method public void setTextEntryKey(boolean);
+ method public void setTextSelectable(boolean);
method public void setTextSelection(int, int);
method public void setTooltipText(@Nullable CharSequence);
method public void setTouchDelegateInfo(@NonNull android.view.accessibility.AccessibilityNodeInfo.TouchDelegateInfo);
@@ -54297,7 +54372,6 @@ package android.webkit {
public abstract class WebSettings {
ctor public WebSettings();
method @Deprecated public abstract boolean enableSmoothTransition();
- method public boolean getAllowAlgorithmicDarkening();
method public abstract boolean getAllowContentAccess();
method public abstract boolean getAllowFileAccess();
method public abstract boolean getAllowFileAccessFromFileURLs();
@@ -54342,7 +54416,8 @@ package android.webkit {
method public abstract int getTextZoom();
method public abstract boolean getUseWideViewPort();
method public abstract String getUserAgentString();
- method public void setAllowAlgorithmicDarkening(boolean);
+ method public boolean isAlgorithmicDarkeningAllowed();
+ method public void setAlgorithmicDarkeningAllowed(boolean);
method public abstract void setAllowContentAccess(boolean);
method public abstract void setAllowFileAccess(boolean);
method @Deprecated public abstract void setAllowFileAccessFromFileURLs(boolean);
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index 1f40e5904ccd..6cde547e6571 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -2,6 +2,7 @@
package android {
public static final class Manifest.permission {
+ field public static final String CONTROL_AUTOMOTIVE_GNSS = "android.permission.CONTROL_AUTOMOTIVE_GNSS";
field public static final String GET_INTENT_SENDER_INTENT = "android.permission.GET_INTENT_SENDER_INTENT";
}
@@ -53,6 +54,21 @@ package android.app {
method public void onCanceled(@NonNull android.app.PendingIntent);
}
+ public class PropertyInvalidatedCache<Query, Result> {
+ ctor public PropertyInvalidatedCache(int, int, @NonNull String, @NonNull String, @NonNull android.app.PropertyInvalidatedCache.QueryHandler<Query,Result>);
+ method public final void disableForCurrentProcess();
+ method public final void invalidateCache();
+ method public static void invalidateCache(int, @NonNull String);
+ method @Nullable public final Result query(@NonNull Query);
+ field public static final int MODULE_BLUETOOTH = 2; // 0x2
+ }
+
+ public abstract static class PropertyInvalidatedCache.QueryHandler<Q, R> {
+ ctor public PropertyInvalidatedCache.QueryHandler();
+ method @Nullable public abstract R apply(@NonNull Q);
+ method public boolean shouldBypassCache(@NonNull Q);
+ }
+
public class StatusBarManager {
method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public void setExpansionDisabledForSimNetworkLock(boolean);
}
@@ -162,6 +178,8 @@ package android.location {
public class LocationManager {
method @RequiresPermission(allOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.ACCESS_FINE_LOCATION}) public boolean injectLocation(@NonNull android.location.Location);
+ method @RequiresPermission(android.Manifest.permission.CONTROL_AUTOMOTIVE_GNSS) public boolean isAutomotiveGnssSuspended();
+ method @RequiresPermission(android.Manifest.permission.CONTROL_AUTOMOTIVE_GNSS) public void setAutomotiveGnssSuspended(boolean);
}
}
@@ -443,6 +461,17 @@ package android.net {
}
+package android.net.netstats {
+
+ public class NetworkStatsDataMigrationUtils {
+ method @NonNull public static android.net.NetworkStatsCollection readPlatformCollection(@NonNull String, long) throws java.io.IOException;
+ field public static final String PREFIX_UID = "uid";
+ field public static final String PREFIX_UID_TAG = "uid_tag";
+ field public static final String PREFIX_XT = "xt";
+ }
+
+}
+
package android.os {
public final class BatteryStatsManager {
@@ -538,10 +567,6 @@ package android.os.storage {
field public static final int APP_IO_BLOCKED_REASON_UNKNOWN = 0; // 0x0
}
- public final class StorageVolume implements android.os.Parcelable {
- method @NonNull public android.os.UserHandle getOwner();
- }
-
}
package android.provider {
@@ -556,6 +581,26 @@ package android.provider {
field public static final String NAMESPACE_DEVICE_IDLE = "device_idle";
}
+ public static final class Settings.Global extends android.provider.Settings.NameValueTable {
+ field public static final String BLE_SCAN_ALWAYS_AVAILABLE = "ble_scan_always_enabled";
+ field public static final String BLE_SCAN_BACKGROUND_MODE = "ble_scan_background_mode";
+ field public static final String BLE_SCAN_BALANCED_INTERVAL_MS = "ble_scan_balanced_interval_ms";
+ field public static final String BLE_SCAN_BALANCED_WINDOW_MS = "ble_scan_balanced_window_ms";
+ field public static final String BLE_SCAN_LOW_LATENCY_INTERVAL_MS = "ble_scan_low_latency_interval_ms";
+ field public static final String BLE_SCAN_LOW_LATENCY_WINDOW_MS = "ble_scan_low_latency_window_ms";
+ field public static final String BLE_SCAN_LOW_POWER_INTERVAL_MS = "ble_scan_low_power_interval_ms";
+ field public static final String BLE_SCAN_LOW_POWER_WINDOW_MS = "ble_scan_low_power_window_ms";
+ field public static final String BLUETOOTH_BTSNOOP_DEFAULT_MODE = "bluetooth_btsnoop_default_mode";
+ field public static final String BLUETOOTH_CLASS_OF_DEVICE = "bluetooth_class_of_device";
+ field public static final String BLUETOOTH_DISABLED_PROFILES = "bluetooth_disabled_profiles";
+ }
+
+ public static final class Settings.Secure extends android.provider.Settings.NameValueTable {
+ field public static final String BLUETOOTH_ADDRESS = "bluetooth_address";
+ field public static final String BLUETOOTH_ADDR_VALID = "bluetooth_addr_valid";
+ field public static final String BLUETOOTH_NAME = "bluetooth_name";
+ }
+
}
package android.telephony {
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index d231a5a3f175..c7be8d3c48d7 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -37,7 +37,6 @@ package android {
field public static final String AMBIENT_WALLPAPER = "android.permission.AMBIENT_WALLPAPER";
field public static final String APPROVE_INCIDENT_REPORTS = "android.permission.APPROVE_INCIDENT_REPORTS";
field public static final String ASSOCIATE_COMPANION_DEVICES = "android.permission.ASSOCIATE_COMPANION_DEVICES";
- field public static final String AUTOMOTIVE_GNSS_CONTROLS = "android.permission.AUTOMOTIVE_GNSS_CONTROLS";
field public static final String BACKGROUND_CAMERA = "android.permission.BACKGROUND_CAMERA";
field public static final String BACKUP = "android.permission.BACKUP";
field public static final String BATTERY_PREDICTION = "android.permission.BATTERY_PREDICTION";
@@ -73,6 +72,7 @@ package android {
field public static final String BIND_TELEPHONY_NETWORK_SERVICE = "android.permission.BIND_TELEPHONY_NETWORK_SERVICE";
field public static final String BIND_TEXTCLASSIFIER_SERVICE = "android.permission.BIND_TEXTCLASSIFIER_SERVICE";
field public static final String BIND_TIME_ZONE_PROVIDER_SERVICE = "android.permission.BIND_TIME_ZONE_PROVIDER_SERVICE";
+ field public static final String BIND_TRACE_REPORT_SERVICE = "android.permission.BIND_TRACE_REPORT_SERVICE";
field public static final String BIND_TRANSLATION_SERVICE = "android.permission.BIND_TRANSLATION_SERVICE";
field public static final String BIND_TRUST_AGENT = "android.permission.BIND_TRUST_AGENT";
field public static final String BIND_TV_REMOTE_SERVICE = "android.permission.BIND_TV_REMOTE_SERVICE";
@@ -146,6 +146,7 @@ package android {
field public static final String KILL_UID = "android.permission.KILL_UID";
field public static final String LAUNCH_DEVICE_MANAGER_SETUP = "android.permission.LAUNCH_DEVICE_MANAGER_SETUP";
field public static final String LOCAL_MAC_ADDRESS = "android.permission.LOCAL_MAC_ADDRESS";
+ field public static final String LOCATION_BYPASS = "android.permission.LOCATION_BYPASS";
field public static final String LOCK_DEVICE = "android.permission.LOCK_DEVICE";
field public static final String LOOP_RADIO = "android.permission.LOOP_RADIO";
field public static final String MANAGE_ACCESSIBILITY = "android.permission.MANAGE_ACCESSIBILITY";
@@ -165,9 +166,11 @@ package android {
field public static final String MANAGE_DEBUGGING = "android.permission.MANAGE_DEBUGGING";
field public static final String MANAGE_DEVICE_ADMINS = "android.permission.MANAGE_DEVICE_ADMINS";
field public static final String MANAGE_FACTORY_RESET_PROTECTION = "android.permission.MANAGE_FACTORY_RESET_PROTECTION";
+ field public static final String MANAGE_GAME_ACTIVITY = "android.permission.MANAGE_GAME_ACTIVITY";
field public static final String MANAGE_GAME_MODE = "android.permission.MANAGE_GAME_MODE";
field public static final String MANAGE_HOTWORD_DETECTION = "android.permission.MANAGE_HOTWORD_DETECTION";
field public static final String MANAGE_IPSEC_TUNNELS = "android.permission.MANAGE_IPSEC_TUNNELS";
+ field public static final String MANAGE_LOW_POWER_STANDBY = "android.permission.MANAGE_LOW_POWER_STANDBY";
field public static final String MANAGE_MUSIC_RECOGNITION = "android.permission.MANAGE_MUSIC_RECOGNITION";
field public static final String MANAGE_NOTIFICATION_LISTENERS = "android.permission.MANAGE_NOTIFICATION_LISTENERS";
field public static final String MANAGE_ONE_TIME_PERMISSION_SESSIONS = "android.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS";
@@ -200,6 +203,7 @@ package android {
field public static final String MODIFY_PARENTAL_CONTROLS = "android.permission.MODIFY_PARENTAL_CONTROLS";
field public static final String MODIFY_QUIET_MODE = "android.permission.MODIFY_QUIET_MODE";
field public static final String MODIFY_SETTINGS_OVERRIDEABLE_BY_RESTORE = "android.permission.MODIFY_SETTINGS_OVERRIDEABLE_BY_RESTORE";
+ field public static final String MODIFY_TOUCH_MODE_STATE = "android.permission.MODIFY_TOUCH_MODE_STATE";
field public static final String MOVE_PACKAGE = "android.permission.MOVE_PACKAGE";
field public static final String NETWORK_AIRPLANE_MODE = "android.permission.NETWORK_AIRPLANE_MODE";
field public static final String NETWORK_CARRIER_PROVISIONING = "android.permission.NETWORK_CARRIER_PROVISIONING";
@@ -257,6 +261,7 @@ package android {
field public static final String READ_WALLPAPER_INTERNAL = "android.permission.READ_WALLPAPER_INTERNAL";
field public static final String READ_WIFI_CREDENTIAL = "android.permission.READ_WIFI_CREDENTIAL";
field public static final String REAL_GET_TASKS = "android.permission.REAL_GET_TASKS";
+ field public static final String RECEIVE_BLUETOOTH_MAP = "android.permission.RECEIVE_BLUETOOTH_MAP";
field public static final String RECEIVE_DATA_ACTIVITY_CHANGE = "android.permission.RECEIVE_DATA_ACTIVITY_CHANGE";
field public static final String RECEIVE_DEVICE_CUSTOMIZATION_READY = "android.permission.RECEIVE_DEVICE_CUSTOMIZATION_READY";
field public static final String RECEIVE_EMERGENCY_BROADCAST = "android.permission.RECEIVE_EMERGENCY_BROADCAST";
@@ -301,6 +306,7 @@ package android {
field public static final String SET_POINTER_SPEED = "android.permission.SET_POINTER_SPEED";
field public static final String SET_SCREEN_COMPATIBILITY = "android.permission.SET_SCREEN_COMPATIBILITY";
field public static final String SET_SYSTEM_AUDIO_CAPTION = "android.permission.SET_SYSTEM_AUDIO_CAPTION";
+ field public static final String SET_UNRESTRICTED_KEEP_CLEAR_AREAS = "android.permission.SET_UNRESTRICTED_KEEP_CLEAR_AREAS";
field public static final String SET_VOLUME_KEY_LONG_PRESS_LISTENER = "android.permission.SET_VOLUME_KEY_LONG_PRESS_LISTENER";
field public static final String SET_WALLPAPER_COMPONENT = "android.permission.SET_WALLPAPER_COMPONENT";
field public static final String SET_WALLPAPER_DIM_AMOUNT = "android.permission.SET_WALLPAPER_DIM_AMOUNT";
@@ -347,6 +353,7 @@ package android {
field public static final String WRITE_EMBEDDED_SUBSCRIPTIONS = "android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS";
field @Deprecated public static final String WRITE_MEDIA_STORAGE = "android.permission.WRITE_MEDIA_STORAGE";
field public static final String WRITE_OBB = "android.permission.WRITE_OBB";
+ field public static final String WRITE_SMS = "android.permission.WRITE_SMS";
}
public static final class Manifest.permission_group {
@@ -1056,8 +1063,8 @@ package android.app.admin {
method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.QUERY_ADMIN_POLICY}) public java.util.List<java.lang.String> getPermittedInputMethodsForCurrentUser();
method @Nullable public android.content.ComponentName getProfileOwner() throws java.lang.IllegalArgumentException;
method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS}) public String getProfileOwnerNameAsUser(int) throws java.lang.IllegalArgumentException;
- method @NonNull public String getString(@NonNull String, @NonNull java.util.concurrent.Callable<java.lang.String>);
- method @NonNull public String getString(@NonNull String, @NonNull java.util.concurrent.Callable<java.lang.String>, @NonNull java.lang.Object...);
+ method @Nullable public String getString(@NonNull String, @NonNull java.util.concurrent.Callable<java.lang.String>);
+ method @Nullable public String getString(@NonNull String, @NonNull java.util.concurrent.Callable<java.lang.String>, @NonNull java.lang.Object...);
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS}) public int getUserProvisioningState();
method public boolean isDeviceManaged();
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isDeviceProvisioned();
@@ -1181,6 +1188,15 @@ package android.app.admin {
field public static final String WORK_PROFILE_PAUSED_TITLE = "MediaProvider.WORK_PROFILE_PAUSED_TITLE";
}
+ public static final class DevicePolicyResources.Strings.PermissionController {
+ field public static final String BACKGROUND_ACCESS_DISABLED_BY_ADMIN_MESSAGE = "PermissionController.BACKGROUND_ACCESS_DISABLED_BY_ADMIN_MESSAGE";
+ field public static final String BACKGROUND_ACCESS_ENABLED_BY_ADMIN_MESSAGE = "PermissionController.BACKGROUND_ACCESS_ENABLED_BY_ADMIN_MESSAGE";
+ field public static final String FOREGROUND_ACCESS_ENABLED_BY_ADMIN_MESSAGE = "PermissionController.FOREGROUND_ACCESS_ENABLED_BY_ADMIN_MESSAGE";
+ field public static final String HOME_MISSING_WORK_PROFILE_SUPPORT_MESSAGE = "PermissionController.HOME_MISSING_WORK_PROFILE_SUPPORT_MESSAGE";
+ field public static final String LOCATION_AUTO_GRANTED_MESSAGE = "PermissionController.LOCATION_AUTO_GRANTED_MESSAGE";
+ field public static final String WORK_PROFILE_DEFAULT_APPS_TITLE = "PermissionController.WORK_PROFILE_DEFAULT_APPS_TITLE";
+ }
+
public final class DevicePolicyStringResource implements android.os.Parcelable {
ctor public DevicePolicyStringResource(@NonNull android.content.Context, @NonNull String, @StringRes int);
method public int describeContents();
@@ -1193,6 +1209,7 @@ package android.app.admin {
public final class FullyManagedDeviceProvisioningParams implements android.os.Parcelable {
method public boolean canDeviceOwnerGrantSensorsPermissions();
method public int describeContents();
+ method @NonNull public android.os.PersistableBundle getAdminExtras();
method @NonNull public android.content.ComponentName getDeviceAdminComponentName();
method public long getLocalTime();
method @Nullable public java.util.Locale getLocale();
@@ -1206,6 +1223,7 @@ package android.app.admin {
public static final class FullyManagedDeviceProvisioningParams.Builder {
ctor public FullyManagedDeviceProvisioningParams.Builder(@NonNull android.content.ComponentName, @NonNull String);
method @NonNull public android.app.admin.FullyManagedDeviceProvisioningParams build();
+ method @NonNull public android.app.admin.FullyManagedDeviceProvisioningParams.Builder setAdminExtras(@NonNull android.os.PersistableBundle);
method @NonNull public android.app.admin.FullyManagedDeviceProvisioningParams.Builder setCanDeviceOwnerGrantSensorsPermissions(boolean);
method @NonNull public android.app.admin.FullyManagedDeviceProvisioningParams.Builder setLeaveAllSystemAppsEnabled(boolean);
method @NonNull public android.app.admin.FullyManagedDeviceProvisioningParams.Builder setLocalTime(long);
@@ -1216,6 +1234,7 @@ package android.app.admin {
public final class ManagedProfileProvisioningParams implements android.os.Parcelable {
method public int describeContents();
method @Nullable public android.accounts.Account getAccountToMigrate();
+ method @NonNull public android.os.PersistableBundle getAdminExtras();
method @NonNull public String getOwnerName();
method @NonNull public android.content.ComponentName getProfileAdminComponentName();
method @Nullable public String getProfileName();
@@ -1230,6 +1249,7 @@ package android.app.admin {
ctor public ManagedProfileProvisioningParams.Builder(@NonNull android.content.ComponentName, @NonNull String);
method @NonNull public android.app.admin.ManagedProfileProvisioningParams build();
method @NonNull public android.app.admin.ManagedProfileProvisioningParams.Builder setAccountToMigrate(@Nullable android.accounts.Account);
+ method @NonNull public android.app.admin.ManagedProfileProvisioningParams.Builder setAdminExtras(@NonNull android.os.PersistableBundle);
method @NonNull public android.app.admin.ManagedProfileProvisioningParams.Builder setKeepingAccountOnMigration(boolean);
method @NonNull public android.app.admin.ManagedProfileProvisioningParams.Builder setLeaveAllSystemAppsEnabled(boolean);
method @NonNull public android.app.admin.ManagedProfileProvisioningParams.Builder setOrganizationOwnedProvisioning(boolean);
@@ -2112,6 +2132,7 @@ package android.app.smartspace {
method @Nullable public android.net.Uri getSliceUri();
method @NonNull public String getSmartspaceTargetId();
method @Nullable public String getSourceNotificationKey();
+ method @Nullable public android.app.smartspace.uitemplatedata.SmartspaceDefaultUiTemplateData getTemplateData();
method @NonNull public android.os.UserHandle getUserHandle();
method @Nullable public android.appwidget.AppWidgetProviderInfo getWidget();
method public boolean isSensitive();
@@ -2142,6 +2163,14 @@ package android.app.smartspace {
field public static final int FEATURE_UPCOMING_ALARM = 23; // 0x17
field public static final int FEATURE_WEATHER = 1; // 0x1
field public static final int FEATURE_WEATHER_ALERT = 10; // 0xa
+ field public static final int UI_TEMPLATE_CAROUSEL = 4; // 0x4
+ field public static final int UI_TEMPLATE_COMBINED_CARDS = 6; // 0x6
+ field public static final int UI_TEMPLATE_DEFAULT = 1; // 0x1
+ field public static final int UI_TEMPLATE_HEAD_TO_HEAD = 5; // 0x5
+ field public static final int UI_TEMPLATE_SUB_CARD = 7; // 0x7
+ field public static final int UI_TEMPLATE_SUB_IMAGE = 2; // 0x2
+ field public static final int UI_TEMPLATE_SUB_LIST = 3; // 0x3
+ field public static final int UI_TEMPLATE_UNDEFINED = 0; // 0x0
}
public static final class SmartspaceTarget.Builder {
@@ -2160,6 +2189,7 @@ package android.app.smartspace {
method @NonNull public android.app.smartspace.SmartspaceTarget.Builder setShouldShowExpanded(boolean);
method @NonNull public android.app.smartspace.SmartspaceTarget.Builder setSliceUri(@NonNull android.net.Uri);
method @NonNull public android.app.smartspace.SmartspaceTarget.Builder setSourceNotificationKey(@NonNull String);
+ method @NonNull public android.app.smartspace.SmartspaceTarget.Builder setTemplateData(@Nullable android.app.smartspace.uitemplatedata.SmartspaceDefaultUiTemplateData);
method @NonNull public android.app.smartspace.SmartspaceTarget.Builder setWidget(@NonNull android.appwidget.AppWidgetProviderInfo);
}
@@ -2188,6 +2218,177 @@ package android.app.smartspace {
}
+package android.app.smartspace.uitemplatedata {
+
+ public final class SmartspaceCarouselUiTemplateData extends android.app.smartspace.uitemplatedata.SmartspaceDefaultUiTemplateData {
+ method @Nullable public android.app.smartspace.uitemplatedata.SmartspaceTapAction getCarouselAction();
+ method @NonNull public java.util.List<android.app.smartspace.uitemplatedata.SmartspaceCarouselUiTemplateData.CarouselItem> getCarouselItems();
+ field @NonNull public static final android.os.Parcelable.Creator<android.app.smartspace.uitemplatedata.SmartspaceCarouselUiTemplateData> CREATOR;
+ }
+
+ public static final class SmartspaceCarouselUiTemplateData.Builder extends android.app.smartspace.uitemplatedata.SmartspaceDefaultUiTemplateData.Builder {
+ ctor public SmartspaceCarouselUiTemplateData.Builder(@NonNull java.util.List<android.app.smartspace.uitemplatedata.SmartspaceCarouselUiTemplateData.CarouselItem>);
+ method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceCarouselUiTemplateData build();
+ method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceCarouselUiTemplateData.Builder setCarouselAction(@NonNull android.app.smartspace.uitemplatedata.SmartspaceTapAction);
+ }
+
+ public static final class SmartspaceCarouselUiTemplateData.CarouselItem implements android.os.Parcelable {
+ method public int describeContents();
+ method @Nullable public android.app.smartspace.uitemplatedata.SmartspaceIcon getImage();
+ method @Nullable public CharSequence getLowerText();
+ method @Nullable public android.app.smartspace.uitemplatedata.SmartspaceTapAction getTapAction();
+ method @Nullable public CharSequence getUpperText();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.app.smartspace.uitemplatedata.SmartspaceCarouselUiTemplateData.CarouselItem> CREATOR;
+ }
+
+ public static final class SmartspaceCarouselUiTemplateData.CarouselItem.Builder {
+ ctor public SmartspaceCarouselUiTemplateData.CarouselItem.Builder();
+ method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceCarouselUiTemplateData.CarouselItem build();
+ method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceCarouselUiTemplateData.CarouselItem.Builder setImage(@Nullable android.app.smartspace.uitemplatedata.SmartspaceIcon);
+ method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceCarouselUiTemplateData.CarouselItem.Builder setLowerText(@Nullable CharSequence);
+ method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceCarouselUiTemplateData.CarouselItem.Builder setTapAction(@Nullable android.app.smartspace.uitemplatedata.SmartspaceTapAction);
+ method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceCarouselUiTemplateData.CarouselItem.Builder setUpperText(@Nullable CharSequence);
+ }
+
+ public final class SmartspaceCombinedCardsUiTemplateData extends android.app.smartspace.uitemplatedata.SmartspaceDefaultUiTemplateData {
+ method @NonNull public java.util.List<android.app.smartspace.uitemplatedata.SmartspaceDefaultUiTemplateData> getCombinedCardDataList();
+ field @NonNull public static final android.os.Parcelable.Creator<android.app.smartspace.uitemplatedata.SmartspaceCombinedCardsUiTemplateData> CREATOR;
+ }
+
+ public static final class SmartspaceCombinedCardsUiTemplateData.Builder extends android.app.smartspace.uitemplatedata.SmartspaceDefaultUiTemplateData.Builder {
+ ctor public SmartspaceCombinedCardsUiTemplateData.Builder(@NonNull java.util.List<android.app.smartspace.uitemplatedata.SmartspaceDefaultUiTemplateData>);
+ method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceCombinedCardsUiTemplateData build();
+ }
+
+ public class SmartspaceDefaultUiTemplateData implements android.os.Parcelable {
+ method public int describeContents();
+ method @Nullable public android.app.smartspace.uitemplatedata.SmartspaceTapAction getPrimaryTapAction();
+ method @Nullable public android.app.smartspace.uitemplatedata.SmartspaceIcon getSubTitleIcon();
+ method @Nullable public CharSequence getSubtitleText();
+ method @Nullable public CharSequence getSupplementalAlarmText();
+ method @Nullable public android.app.smartspace.uitemplatedata.SmartspaceIcon getSupplementalSubtitleIcon();
+ method @Nullable public android.app.smartspace.uitemplatedata.SmartspaceTapAction getSupplementalSubtitleTapAction();
+ method @Nullable public CharSequence getSupplementalSubtitleText();
+ method public int getTemplateType();
+ method @Nullable public android.app.smartspace.uitemplatedata.SmartspaceIcon getTitleIcon();
+ method @Nullable public CharSequence getTitleText();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.app.smartspace.uitemplatedata.SmartspaceDefaultUiTemplateData> CREATOR;
+ }
+
+ public static class SmartspaceDefaultUiTemplateData.Builder {
+ ctor public SmartspaceDefaultUiTemplateData.Builder(int);
+ method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceDefaultUiTemplateData build();
+ method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceDefaultUiTemplateData.Builder setPrimaryTapAction(@NonNull android.app.smartspace.uitemplatedata.SmartspaceTapAction);
+ method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceDefaultUiTemplateData.Builder setSubTitleIcon(@NonNull android.app.smartspace.uitemplatedata.SmartspaceIcon);
+ method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceDefaultUiTemplateData.Builder setSubtitleText(@NonNull CharSequence);
+ method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceDefaultUiTemplateData.Builder setSupplementalAlarmText(@NonNull CharSequence);
+ method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceDefaultUiTemplateData.Builder setSupplementalSubtitleIcon(@NonNull android.app.smartspace.uitemplatedata.SmartspaceIcon);
+ method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceDefaultUiTemplateData.Builder setSupplementalSubtitleTapAction(@NonNull android.app.smartspace.uitemplatedata.SmartspaceTapAction);
+ method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceDefaultUiTemplateData.Builder setSupplementalSubtitleText(@NonNull CharSequence);
+ method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceDefaultUiTemplateData.Builder setTitleIcon(@NonNull android.app.smartspace.uitemplatedata.SmartspaceIcon);
+ method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceDefaultUiTemplateData.Builder setTitleText(@NonNull CharSequence);
+ }
+
+ public final class SmartspaceHeadToHeadUiTemplateData extends android.app.smartspace.uitemplatedata.SmartspaceDefaultUiTemplateData {
+ method @Nullable public android.app.smartspace.uitemplatedata.SmartspaceTapAction getHeadToHeadAction();
+ method @Nullable public android.app.smartspace.uitemplatedata.SmartspaceIcon getHeadToHeadFirstCompetitorIcon();
+ method @Nullable public CharSequence getHeadToHeadFirstCompetitorText();
+ method @Nullable public android.app.smartspace.uitemplatedata.SmartspaceIcon getHeadToHeadSecondCompetitorIcon();
+ method @Nullable public CharSequence getHeadToHeadSecondCompetitorText();
+ method @Nullable public CharSequence getHeadToHeadTitle();
+ field @NonNull public static final android.os.Parcelable.Creator<android.app.smartspace.uitemplatedata.SmartspaceHeadToHeadUiTemplateData> CREATOR;
+ }
+
+ public static final class SmartspaceHeadToHeadUiTemplateData.Builder extends android.app.smartspace.uitemplatedata.SmartspaceDefaultUiTemplateData.Builder {
+ ctor public SmartspaceHeadToHeadUiTemplateData.Builder();
+ method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceHeadToHeadUiTemplateData build();
+ method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceHeadToHeadUiTemplateData.Builder setHeadToHeadAction(@Nullable android.app.smartspace.uitemplatedata.SmartspaceTapAction);
+ method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceHeadToHeadUiTemplateData.Builder setHeadToHeadFirstCompetitorIcon(@Nullable android.app.smartspace.uitemplatedata.SmartspaceIcon);
+ method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceHeadToHeadUiTemplateData.Builder setHeadToHeadFirstCompetitorText(@Nullable CharSequence);
+ method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceHeadToHeadUiTemplateData.Builder setHeadToHeadSecondCompetitorIcon(@Nullable android.app.smartspace.uitemplatedata.SmartspaceIcon);
+ method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceHeadToHeadUiTemplateData.Builder setHeadToHeadSecondCompetitorText(@Nullable CharSequence);
+ method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceHeadToHeadUiTemplateData.Builder setHeadToHeadTitle(@Nullable CharSequence);
+ }
+
+ public final class SmartspaceIcon implements android.os.Parcelable {
+ method public int describeContents();
+ method @Nullable public CharSequence getContentDescription();
+ method @NonNull public android.graphics.drawable.Icon getIcon();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.app.smartspace.uitemplatedata.SmartspaceIcon> CREATOR;
+ }
+
+ public static final class SmartspaceIcon.Builder {
+ ctor public SmartspaceIcon.Builder(@NonNull android.graphics.drawable.Icon);
+ method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceIcon build();
+ method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceIcon.Builder setContentDescription(@NonNull CharSequence);
+ }
+
+ public final class SmartspaceSubCardUiTemplateData extends android.app.smartspace.uitemplatedata.SmartspaceDefaultUiTemplateData {
+ method @Nullable public android.app.smartspace.uitemplatedata.SmartspaceTapAction getSubCardAction();
+ method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceIcon getSubCardIcon();
+ method @Nullable public CharSequence getSubCardText();
+ field @NonNull public static final android.os.Parcelable.Creator<android.app.smartspace.uitemplatedata.SmartspaceSubCardUiTemplateData> CREATOR;
+ }
+
+ public static final class SmartspaceSubCardUiTemplateData.Builder extends android.app.smartspace.uitemplatedata.SmartspaceDefaultUiTemplateData.Builder {
+ ctor public SmartspaceSubCardUiTemplateData.Builder(@NonNull android.app.smartspace.uitemplatedata.SmartspaceIcon);
+ method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceSubCardUiTemplateData build();
+ method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceSubCardUiTemplateData.Builder setSubCardAction(@NonNull CharSequence);
+ method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceSubCardUiTemplateData.Builder setSubCardAction(@NonNull android.app.smartspace.uitemplatedata.SmartspaceTapAction);
+ }
+
+ public final class SmartspaceSubImageUiTemplateData extends android.app.smartspace.uitemplatedata.SmartspaceDefaultUiTemplateData {
+ method @Nullable public android.app.smartspace.uitemplatedata.SmartspaceTapAction getSubImageAction();
+ method @NonNull public java.util.List<java.lang.CharSequence> getSubImageTexts();
+ method @NonNull public java.util.List<android.app.smartspace.uitemplatedata.SmartspaceIcon> getSubImages();
+ field @NonNull public static final android.os.Parcelable.Creator<android.app.smartspace.uitemplatedata.SmartspaceSubImageUiTemplateData> CREATOR;
+ }
+
+ public static final class SmartspaceSubImageUiTemplateData.Builder extends android.app.smartspace.uitemplatedata.SmartspaceDefaultUiTemplateData.Builder {
+ ctor public SmartspaceSubImageUiTemplateData.Builder(@NonNull java.util.List<java.lang.CharSequence>, @NonNull java.util.List<android.app.smartspace.uitemplatedata.SmartspaceIcon>);
+ method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceSubImageUiTemplateData build();
+ method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceSubImageUiTemplateData.Builder setCarouselAction(@NonNull android.app.smartspace.uitemplatedata.SmartspaceTapAction);
+ }
+
+ public final class SmartspaceSubListUiTemplateData extends android.app.smartspace.uitemplatedata.SmartspaceDefaultUiTemplateData {
+ method @Nullable public android.app.smartspace.uitemplatedata.SmartspaceTapAction getSubListAction();
+ method @Nullable public android.app.smartspace.uitemplatedata.SmartspaceIcon getSubListIcon();
+ method @NonNull public java.util.List<java.lang.CharSequence> getSubListTexts();
+ field @NonNull public static final android.os.Parcelable.Creator<android.app.smartspace.uitemplatedata.SmartspaceSubListUiTemplateData> CREATOR;
+ }
+
+ public static final class SmartspaceSubListUiTemplateData.Builder extends android.app.smartspace.uitemplatedata.SmartspaceDefaultUiTemplateData.Builder {
+ ctor public SmartspaceSubListUiTemplateData.Builder(@NonNull java.util.List<java.lang.CharSequence>);
+ method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceSubListUiTemplateData build();
+ method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceSubListUiTemplateData.Builder setCarouselAction(@NonNull android.app.smartspace.uitemplatedata.SmartspaceTapAction);
+ method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceSubListUiTemplateData.Builder setSubListIcon(@NonNull android.app.smartspace.uitemplatedata.SmartspaceIcon);
+ }
+
+ public final class SmartspaceTapAction implements android.os.Parcelable {
+ method public int describeContents();
+ method @Nullable public android.os.Bundle getExtras();
+ method @Nullable public CharSequence getId();
+ method @Nullable public android.content.Intent getIntent();
+ method @Nullable public android.app.PendingIntent getPendingIntent();
+ method @Nullable public android.os.UserHandle getUserHandle();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.app.smartspace.uitemplatedata.SmartspaceTapAction> CREATOR;
+ }
+
+ public static final class SmartspaceTapAction.Builder {
+ ctor public SmartspaceTapAction.Builder(@NonNull CharSequence);
+ method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceTapAction build();
+ method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceTapAction.Builder setExtras(@NonNull android.os.Bundle);
+ method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceTapAction.Builder setIntent(@NonNull android.content.Intent);
+ method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceTapAction.Builder setPendingIntent(@NonNull android.app.PendingIntent);
+ method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceTapAction.Builder setUserHandle(@Nullable android.os.UserHandle);
+ }
+
+}
+
package android.app.time {
public final class Capabilities {
@@ -2357,12 +2558,21 @@ package android.apphibernation {
public class AppHibernationManager {
method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_APP_HIBERNATION) public java.util.List<java.lang.String> getHibernatingPackagesForUser();
+ method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_APP_HIBERNATION) public java.util.Map<java.lang.String,android.apphibernation.HibernationStats> getHibernationStatsForUser(@NonNull java.util.Set<java.lang.String>);
+ method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_APP_HIBERNATION) public java.util.Map<java.lang.String,android.apphibernation.HibernationStats> getHibernationStatsForUser();
method @RequiresPermission(android.Manifest.permission.MANAGE_APP_HIBERNATION) public boolean isHibernatingForUser(@NonNull String);
method @RequiresPermission(android.Manifest.permission.MANAGE_APP_HIBERNATION) public boolean isHibernatingGlobally(@NonNull String);
method @RequiresPermission(android.Manifest.permission.MANAGE_APP_HIBERNATION) public void setHibernatingForUser(@NonNull String, boolean);
method @RequiresPermission(android.Manifest.permission.MANAGE_APP_HIBERNATION) public void setHibernatingGlobally(@NonNull String, boolean);
}
+ public final class HibernationStats implements android.os.Parcelable {
+ method public int describeContents();
+ method public long getDiskBytesSaved();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.apphibernation.HibernationStats> CREATOR;
+ }
+
}
package android.companion {
@@ -2419,6 +2629,8 @@ package android.companion.virtual {
public final class VirtualDeviceParams implements android.os.Parcelable {
method public int describeContents();
+ method @Nullable public java.util.Set<android.content.ComponentName> getAllowedActivities();
+ method @Nullable public java.util.Set<android.content.ComponentName> getBlockedActivities();
method public int getLockState();
method @NonNull public java.util.Set<android.os.UserHandle> getUsersWithMatchingAccounts();
method public void writeToParcel(@NonNull android.os.Parcel, int);
@@ -2430,6 +2642,8 @@ package android.companion.virtual {
public static final class VirtualDeviceParams.Builder {
ctor public VirtualDeviceParams.Builder();
method @NonNull public android.companion.virtual.VirtualDeviceParams build();
+ method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setAllowedActivities(@Nullable java.util.Set<android.content.ComponentName>);
+ method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setBlockedActivities(@Nullable java.util.Set<android.content.ComponentName>);
method @NonNull @RequiresPermission(value=android.Manifest.permission.ADD_ALWAYS_UNLOCKED_DISPLAY, conditional=true) public android.companion.virtual.VirtualDeviceParams.Builder setLockState(int);
method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setUsersWithMatchingAccounts(@NonNull java.util.Set<android.os.UserHandle>);
}
@@ -2487,6 +2701,7 @@ package android.content {
method @Nullable @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public android.content.Intent registerReceiverForAllUsers(@Nullable android.content.BroadcastReceiver, @NonNull android.content.IntentFilter, @Nullable String, @Nullable android.os.Handler, int);
method public abstract void sendBroadcast(android.content.Intent, @Nullable String, @Nullable android.os.Bundle);
method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public abstract void sendBroadcastAsUser(@RequiresPermission android.content.Intent, android.os.UserHandle, @Nullable String, @Nullable android.os.Bundle);
+ method public void sendBroadcastMultiplePermissions(@NonNull android.content.Intent, @NonNull String[], @Nullable android.app.BroadcastOptions);
method public abstract void sendOrderedBroadcast(@NonNull android.content.Intent, @Nullable String, @Nullable android.os.Bundle, @Nullable android.content.BroadcastReceiver, @Nullable android.os.Handler, int, @Nullable String, @Nullable android.os.Bundle);
method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public void startActivityAsUser(@NonNull @RequiresPermission android.content.Intent, @NonNull android.os.UserHandle);
field public static final String AMBIENT_CONTEXT_SERVICE = "ambient_context";
@@ -2497,7 +2712,7 @@ package android.content {
field public static final String BATTERY_STATS_SERVICE = "batterystats";
field @Deprecated public static final int BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS = 1048576; // 0x100000
field public static final int BIND_ALLOW_FOREGROUND_SERVICE_STARTS_FROM_BACKGROUND = 262144; // 0x40000
- field public static final String CLOUDSEARCH_SERVICE = "cloudsearch_service";
+ field public static final String CLOUDSEARCH_SERVICE = "cloudsearch";
field public static final String CONTENT_SUGGESTIONS_SERVICE = "content_suggestions";
field public static final String CONTEXTHUB_SERVICE = "contexthub";
field public static final String ETHERNET_SERVICE = "ethernet";
@@ -2590,6 +2805,7 @@ package android.content {
field public static final String ACTION_UPGRADE_SETUP = "android.intent.action.UPGRADE_SETUP";
field public static final String ACTION_USER_ADDED = "android.intent.action.USER_ADDED";
field public static final String ACTION_USER_REMOVED = "android.intent.action.USER_REMOVED";
+ field public static final String ACTION_USER_SWITCHED = "android.intent.action.USER_SWITCHED";
field @RequiresPermission(android.Manifest.permission.START_VIEW_APP_FEATURES) public static final String ACTION_VIEW_APP_FEATURES = "android.intent.action.VIEW_APP_FEATURES";
field public static final String ACTION_VOICE_ASSIST = "android.intent.action.VOICE_ASSIST";
field public static final String CATEGORY_LEANBACK_SETTINGS = "android.intent.category.LEANBACK_SETTINGS";
@@ -2612,7 +2828,9 @@ package android.content {
field public static final String EXTRA_ROLE_NAME = "android.intent.extra.ROLE_NAME";
field public static final String EXTRA_SHOWING_ATTRIBUTION = "android.intent.extra.SHOWING_ATTRIBUTION";
field public static final String EXTRA_UNKNOWN_INSTANT_APP = "android.intent.extra.UNKNOWN_INSTANT_APP";
+ field public static final String EXTRA_USER_HANDLE = "android.intent.extra.user_handle";
field public static final String EXTRA_VERIFICATION_BUNDLE = "android.intent.extra.VERIFICATION_BUNDLE";
+ field public static final int FLAG_RECEIVER_INCLUDE_BACKGROUND = 16777216; // 0x1000000
field public static final int FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT = 67108864; // 0x4000000
field public static final String METADATA_SETUP_VERSION = "android.SETUP_VERSION";
}
@@ -3495,6 +3713,7 @@ package android.hardware.hdmi {
method public void sendKeyEvent(int, boolean);
method public void sendVendorCommand(int, byte[], boolean);
method public void setVendorCommandListener(@NonNull android.hardware.hdmi.HdmiControlManager.VendorCommandListener);
+ method public void setVendorCommandListener(@NonNull android.hardware.hdmi.HdmiControlManager.VendorCommandListener, int);
}
public static interface HdmiClient.OnDeviceSelectedListener {
@@ -5343,9 +5562,9 @@ package android.location {
ctor public LastLocationRequest.Builder();
ctor public LastLocationRequest.Builder(@NonNull android.location.LastLocationRequest);
method @NonNull public android.location.LastLocationRequest build();
- method @NonNull @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public android.location.LastLocationRequest.Builder setAdasGnssBypass(boolean);
+ method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.WRITE_SECURE_SETTINGS, android.Manifest.permission.LOCATION_BYPASS}) public android.location.LastLocationRequest.Builder setAdasGnssBypass(boolean);
method @NonNull @RequiresPermission(android.Manifest.permission.UPDATE_APP_OPS_STATS) public android.location.LastLocationRequest.Builder setHiddenFromAppOps(boolean);
- method @NonNull @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public android.location.LastLocationRequest.Builder setLocationSettingsIgnored(boolean);
+ method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.WRITE_SECURE_SETTINGS, android.Manifest.permission.LOCATION_BYPASS}) public android.location.LastLocationRequest.Builder setLocationSettingsIgnored(boolean);
}
public class Location implements android.os.Parcelable {
@@ -5363,7 +5582,6 @@ package android.location {
method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public android.location.Location getLastKnownLocation(@NonNull String, @NonNull android.location.LastLocationRequest);
method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public void injectGnssMeasurementCorrections(@NonNull android.location.GnssMeasurementCorrections);
method public boolean isAdasGnssLocationEnabled();
- method @RequiresPermission(android.Manifest.permission.AUTOMOTIVE_GNSS_CONTROLS) public boolean isAutoGnssSuspended();
method public boolean isExtraLocationControllerPackageEnabled();
method public boolean isLocationEnabledForUser(@NonNull android.os.UserHandle);
method public boolean isProviderEnabledForUser(@NonNull String, @NonNull android.os.UserHandle);
@@ -5375,8 +5593,7 @@ package android.location {
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@Nullable android.location.LocationRequest, @NonNull android.location.LocationListener, @Nullable android.os.Looper);
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@Nullable android.location.LocationRequest, @NonNull java.util.concurrent.Executor, @NonNull android.location.LocationListener);
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@Nullable android.location.LocationRequest, @NonNull android.app.PendingIntent);
- method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setAdasGnssLocationEnabled(boolean);
- method @RequiresPermission(android.Manifest.permission.AUTOMOTIVE_GNSS_CONTROLS) public void setAutoGnssSuspended(boolean);
+ method @RequiresPermission(anyOf={android.Manifest.permission.WRITE_SECURE_SETTINGS, android.Manifest.permission.LOCATION_BYPASS}) public void setAdasGnssLocationEnabled(boolean);
method @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void setExtraLocationControllerPackage(@Nullable String);
method @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void setExtraLocationControllerPackageEnabled(boolean);
method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setLocationEnabledForUser(boolean, @NonNull android.os.UserHandle);
@@ -5409,7 +5626,7 @@ package android.location {
method @Deprecated @NonNull public android.location.LocationRequest setFastestInterval(long);
method @Deprecated public void setHideFromAppOps(boolean);
method @Deprecated @NonNull public android.location.LocationRequest setInterval(long);
- method @Deprecated @NonNull @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public android.location.LocationRequest setLocationSettingsIgnored(boolean);
+ method @Deprecated @NonNull @RequiresPermission(anyOf={android.Manifest.permission.WRITE_SECURE_SETTINGS, android.Manifest.permission.LOCATION_BYPASS}) public android.location.LocationRequest setLocationSettingsIgnored(boolean);
method @Deprecated @NonNull public android.location.LocationRequest setLowPowerMode(boolean);
method @Deprecated @NonNull public android.location.LocationRequest setNumUpdates(int);
method @Deprecated @NonNull public android.location.LocationRequest setProvider(@NonNull String);
@@ -5425,9 +5642,9 @@ package android.location {
}
public static final class LocationRequest.Builder {
- method @NonNull @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public android.location.LocationRequest.Builder setAdasGnssBypass(boolean);
+ method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.WRITE_SECURE_SETTINGS, android.Manifest.permission.LOCATION_BYPASS}) public android.location.LocationRequest.Builder setAdasGnssBypass(boolean);
method @NonNull @RequiresPermission(android.Manifest.permission.UPDATE_APP_OPS_STATS) public android.location.LocationRequest.Builder setHiddenFromAppOps(boolean);
- method @NonNull @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public android.location.LocationRequest.Builder setLocationSettingsIgnored(boolean);
+ method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.WRITE_SECURE_SETTINGS, android.Manifest.permission.LOCATION_BYPASS}) public android.location.LocationRequest.Builder setLocationSettingsIgnored(boolean);
method @NonNull @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public android.location.LocationRequest.Builder setLowPower(boolean);
method @NonNull @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public android.location.LocationRequest.Builder setWorkSource(@Nullable android.os.WorkSource);
}
@@ -5679,6 +5896,7 @@ package android.media {
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void unregisterAudioPolicyAsync(@NonNull android.media.audiopolicy.AudioPolicy);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void unregisterMuteAwaitConnectionCallback(@NonNull android.media.AudioManager.MuteAwaitConnectionCallback);
method public void unregisterVolumeGroupCallback(@NonNull android.media.AudioManager.VolumeGroupCallback);
+ field public static final String ACTION_VOLUME_CHANGED = "android.media.VOLUME_CHANGED_ACTION";
field public static final int AUDIOFOCUS_FLAG_DELAY_OK = 1; // 0x1
field public static final int AUDIOFOCUS_FLAG_LOCK = 4; // 0x4
field public static final int AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS = 2; // 0x2
@@ -5687,7 +5905,11 @@ package android.media {
field public static final int DEVICE_VOLUME_BEHAVIOR_FIXED = 2; // 0x2
field public static final int DEVICE_VOLUME_BEHAVIOR_FULL = 1; // 0x1
field public static final int DEVICE_VOLUME_BEHAVIOR_VARIABLE = 0; // 0x0
+ field public static final String EXTRA_VOLUME_STREAM_TYPE = "android.media.EXTRA_VOLUME_STREAM_TYPE";
+ field public static final String EXTRA_VOLUME_STREAM_VALUE = "android.media.EXTRA_VOLUME_STREAM_VALUE";
+ field public static final int FLAG_BLUETOOTH_ABS_VOLUME = 64; // 0x40
field @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static final int STREAM_ASSISTANT = 11; // 0xb
+ field public static final int STREAM_BLUETOOTH_SCO = 6; // 0x6
field public static final int SUCCESS = 0; // 0x0
}
@@ -6287,6 +6509,7 @@ package android.media.tv {
method @RequiresPermission(android.Manifest.permission.CAPTURE_TV_INPUT) public boolean captureFrame(String, android.view.Surface, android.media.tv.TvStreamConfig);
method @NonNull public java.util.List<java.lang.String> getAvailableExtensionInterfaceNames(@NonNull String);
method @RequiresPermission(android.Manifest.permission.CAPTURE_TV_INPUT) public java.util.List<android.media.tv.TvStreamConfig> getAvailableTvStreamConfigList(String);
+ method @RequiresPermission("android.permission.TUNER_RESOURCE_ACCESS") public int getClientPid(@NonNull String);
method public int getClientPriority(int, @Nullable String);
method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_TUNED_INFO) public java.util.List<android.media.tv.TunedInfo> getCurrentTunedInfos();
method @NonNull @RequiresPermission("android.permission.DVB_DEVICE") public java.util.List<android.media.tv.DvbDeviceInfo> getDvbDeviceList();
@@ -6459,7 +6682,7 @@ package android.media.tv.tuner {
method @Nullable public android.media.tv.tuner.DemuxCapabilities getDemuxCapabilities();
method @Nullable public android.media.tv.tuner.frontend.FrontendInfo getFrontendInfo();
method @Nullable public android.media.tv.tuner.frontend.FrontendStatus getFrontendStatus(@NonNull int[]);
- method @Nullable public android.media.tv.tuner.frontend.FrontendStatusReadiness[] getFrontendStatusReadiness(@NonNull int[]);
+ method @NonNull public java.util.List<android.media.tv.tuner.frontend.FrontendStatusReadiness> getFrontendStatusReadiness(@NonNull int[]);
method @IntRange(from=0xffffffff) public int getMaxNumberOfFrontends(int);
method @RequiresPermission("android.permission.TUNER_RESOURCE_ACCESS") public boolean hasUnusedFrontend(int);
method public boolean isLowestPriority(int);
@@ -7704,7 +7927,7 @@ package android.media.tv.tuner.frontend {
method public boolean isLocked();
}
- public class FrontendStatusReadiness {
+ public final class FrontendStatusReadiness {
method public int getStatusReadiness();
method public int getStatusType();
field public static final int FRONTEND_STATUS_READINESS_STABLE = 3; // 0x3
@@ -8728,6 +8951,8 @@ package android.os {
method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void reportBleScanResults(@NonNull android.os.WorkSource, int);
method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void reportBleScanStarted(@NonNull android.os.WorkSource, boolean);
method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void reportBleScanStopped(@NonNull android.os.WorkSource, boolean);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public void reportBluetoothOff(int, int, @NonNull String);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public void reportBluetoothOn(int, int, @NonNull String);
method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void reportFullWifiLockAcquiredFromSource(@NonNull android.os.WorkSource);
method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void reportFullWifiLockReleasedFromSource(@NonNull android.os.WorkSource);
method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void reportMobileRadioPowerState(boolean, int);
@@ -9125,11 +9350,14 @@ package android.os {
method @RequiresPermission(android.Manifest.permission.READ_DREAM_STATE) public boolean isAmbientDisplayAvailable();
method @RequiresPermission(android.Manifest.permission.READ_DREAM_STATE) public boolean isAmbientDisplaySuppressed();
method @RequiresPermission(android.Manifest.permission.READ_DREAM_STATE) public boolean isAmbientDisplaySuppressedForToken(@NonNull String);
+ method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_LOW_POWER_STANDBY, android.Manifest.permission.DEVICE_POWER}) public boolean isLowPowerStandbySupported();
method @RequiresPermission(anyOf={android.Manifest.permission.DEVICE_POWER, android.Manifest.permission.POWER_SAVER}) public boolean setAdaptivePowerSaveEnabled(boolean);
method @RequiresPermission(anyOf={android.Manifest.permission.DEVICE_POWER, android.Manifest.permission.POWER_SAVER}) public boolean setAdaptivePowerSavePolicy(@NonNull android.os.BatterySaverPolicyConfig);
method @RequiresPermission(anyOf={android.Manifest.permission.BATTERY_PREDICTION, android.Manifest.permission.DEVICE_POWER}) public void setBatteryDischargePrediction(@NonNull java.time.Duration, boolean);
method @RequiresPermission(android.Manifest.permission.POWER_SAVER) public boolean setDynamicPowerSaveHint(boolean, int);
method @RequiresPermission(anyOf={android.Manifest.permission.DEVICE_POWER, android.Manifest.permission.POWER_SAVER}) public boolean setFullPowerSavePolicy(@NonNull android.os.BatterySaverPolicyConfig);
+ method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_LOW_POWER_STANDBY, android.Manifest.permission.DEVICE_POWER}) public void setLowPowerStandbyActiveDuringMaintenance(boolean);
+ method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_LOW_POWER_STANDBY, android.Manifest.permission.DEVICE_POWER}) public void setLowPowerStandbyEnabled(boolean);
method @RequiresPermission(anyOf={android.Manifest.permission.DEVICE_POWER, android.Manifest.permission.POWER_SAVER}) public boolean setPowerSaveModeEnabled(boolean);
method @RequiresPermission(android.Manifest.permission.WRITE_DREAM_STATE) public void suppressAmbientDisplay(@NonNull String, boolean);
method @RequiresPermission(anyOf={android.Manifest.permission.DEVICE_POWER, android.Manifest.permission.USER_ACTIVITY}) public void userActivity(long, int, int);
@@ -10026,6 +10254,7 @@ package android.provider {
method @Deprecated public static boolean checkAndNoteWriteSettingsOperation(@NonNull android.content.Context, int, @NonNull String, boolean);
method public static boolean checkAndNoteWriteSettingsOperation(@NonNull android.content.Context, int, @NonNull String, @Nullable String, boolean);
field public static final String ACTION_ACCESSIBILITY_DETAILS_SETTINGS = "android.settings.ACCESSIBILITY_DETAILS_SETTINGS";
+ field public static final String ACTION_BEDTIME_SETTINGS = "android.settings.BEDTIME_SETTINGS";
field public static final String ACTION_BUGREPORT_HANDLER_SETTINGS = "android.settings.BUGREPORT_HANDLER_SETTINGS";
field public static final String ACTION_ENTERPRISE_PRIVACY_SETTINGS = "android.settings.ENTERPRISE_PRIVACY_SETTINGS";
field public static final String ACTION_LOCATION_CONTROLLER_EXTRA_PACKAGE_SETTINGS = "android.settings.LOCATION_CONTROLLER_EXTRA_PACKAGE_SETTINGS";
@@ -10066,6 +10295,8 @@ package android.provider {
}
public static final class Settings.Secure extends android.provider.Settings.NameValueTable {
+ method public static int getIntForUser(@NonNull android.content.ContentResolver, @NonNull String, int, int);
+ method @Nullable public static String getStringForUser(@NonNull android.content.ContentResolver, @NonNull String, int);
method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public static boolean putString(@NonNull android.content.ContentResolver, @NonNull String, @Nullable String, @Nullable String, boolean);
method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public static void resetToDefaults(@NonNull android.content.ContentResolver, @Nullable String);
field @Deprecated public static final String ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED = "accessibility_display_magnification_navbar_enabled";
@@ -10457,9 +10688,9 @@ package android.service.autofill {
}
public static final class Dataset.Builder {
- ctor public Dataset.Builder(@NonNull android.service.autofill.InlinePresentation);
+ ctor @Deprecated public Dataset.Builder(@NonNull android.service.autofill.InlinePresentation);
method @NonNull public android.service.autofill.Dataset.Builder setContent(@NonNull android.view.autofill.AutofillId, @Nullable android.content.ClipData);
- method @NonNull public android.service.autofill.Dataset.Builder setFieldInlinePresentation(@NonNull android.view.autofill.AutofillId, @Nullable android.view.autofill.AutofillValue, @Nullable java.util.regex.Pattern, @NonNull android.service.autofill.InlinePresentation);
+ method @Deprecated @NonNull public android.service.autofill.Dataset.Builder setFieldInlinePresentation(@NonNull android.view.autofill.AutofillId, @Nullable android.view.autofill.AutofillValue, @Nullable java.util.regex.Pattern, @NonNull android.service.autofill.InlinePresentation);
}
public abstract class InlineSuggestionRenderService extends android.app.Service {
@@ -10848,7 +11079,7 @@ package android.service.games {
public class GameService extends android.app.Service {
ctor public GameService();
- method public final void createGameSession(@IntRange(from=0) int);
+ method @RequiresPermission(android.Manifest.permission.MANAGE_GAME_ACTIVITY) public final void createGameSession(@IntRange(from=0) int);
method @Nullable public final android.os.IBinder onBind(@Nullable android.content.Intent);
method public void onConnected();
method public void onDisconnected();
@@ -10862,7 +11093,7 @@ package android.service.games {
method public void onCreate();
method public void onDestroy();
method public void onGameTaskFocusChanged(boolean);
- method @RequiresPermission(android.Manifest.permission.FORCE_STOP_PACKAGES) public final boolean restartGame();
+ method @RequiresPermission(android.Manifest.permission.MANAGE_GAME_ACTIVITY) public final boolean restartGame();
method public void setTaskOverlayView(@NonNull android.view.View, @NonNull android.view.ViewGroup.LayoutParams);
method public void takeScreenshot(@NonNull java.util.concurrent.Executor, @NonNull android.service.games.GameSession.ScreenshotCallback);
}
@@ -11011,7 +11242,7 @@ package android.service.persistentdata {
method @android.service.persistentdata.PersistentDataBlockManager.FlashLockState @RequiresPermission(anyOf={android.Manifest.permission.READ_OEM_UNLOCK_STATE, "android.permission.OEM_UNLOCK_STATE"}) public int getFlashLockState();
method public long getMaximumDataBlockSize();
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_OEM_UNLOCK_STATE, "android.permission.OEM_UNLOCK_STATE"}) public boolean getOemUnlockEnabled();
- method @NonNull public String getPersistentDataPackageName();
+ method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_PDB_STATE) public String getPersistentDataPackageName();
method public byte[] read();
method @Deprecated @RequiresPermission("android.permission.OEM_UNLOCK_STATE") public void setOemUnlockEnabled(boolean);
method @RequiresPermission("android.permission.OEM_UNLOCK_STATE") public void wipe();
@@ -11249,6 +11480,22 @@ package android.service.timezone {
}
+package android.service.tracing {
+
+ public class TraceReportService extends android.app.Service {
+ ctor public TraceReportService();
+ method @Nullable public android.os.IBinder onBind(@NonNull android.content.Intent);
+ method public boolean onMessage(@NonNull android.os.Message);
+ method public void onReportTrace(@NonNull android.service.tracing.TraceReportService.TraceParams);
+ }
+
+ public static final class TraceReportService.TraceParams {
+ method @NonNull public android.os.ParcelFileDescriptor getFd();
+ method @NonNull public java.util.UUID getUuid();
+ }
+
+}
+
package android.service.translation {
public abstract class TranslationService extends android.app.Service {
@@ -11641,6 +11888,7 @@ package android.telecom {
public abstract class ConnectionService extends android.app.Service {
method public final void addExistingConnection(@NonNull android.telecom.PhoneAccountHandle, @NonNull android.telecom.Connection, @NonNull android.telecom.Conference);
+ method @Nullable @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public android.telecom.Connection onCreateUnknownConnection(@NonNull android.telecom.PhoneAccountHandle, @NonNull android.telecom.ConnectionRequest);
}
public abstract class InCallService extends android.app.Service {
diff --git a/core/api/system-lint-baseline.txt b/core/api/system-lint-baseline.txt
index dbb427467a68..e17a9bb1512c 100644
--- a/core/api/system-lint-baseline.txt
+++ b/core/api/system-lint-baseline.txt
@@ -294,7 +294,6 @@ SamShouldBeLast: android.webkit.WebChromeClient#onShowFileChooser(android.webkit
ServiceName: android.content.Context#CLOUDSEARCH_SERVICE:
- Inconsistent service value; expected `cloudsearch`, was `cloudsearch_service` (Note: Do not change the name of already released services, which will break tools using `adb shell dumpsys`. Instead add `@SuppressLint("ServiceName"))`
UserHandleName: android.app.search.SearchAction.Builder#setUserHandle(android.os.UserHandle):
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 0bdbd1037b7e..52a180b3c9e0 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -105,6 +105,7 @@ package android.app {
@UiContext public class Activity extends android.view.ContextThemeWrapper implements android.content.ComponentCallbacks2 android.view.KeyEvent.Callback android.view.LayoutInflater.Factory2 android.view.OnBackInvokedDispatcherOwner android.view.View.OnCreateContextMenuListener android.view.Window.Callback {
method public final boolean addDumpable(@NonNull android.util.Dumpable);
+ method public void dumpInternal(@NonNull String, @Nullable java.io.FileDescriptor, @NonNull java.io.PrintWriter, @Nullable String[]);
method public void onMovedToDisplay(int, android.content.res.Configuration);
}
@@ -152,7 +153,7 @@ package android.app {
public class ActivityOptions {
method @NonNull public static android.app.ActivityOptions fromBundle(@NonNull android.os.Bundle);
- method @NonNull public static android.app.ActivityOptions makeCustomAnimation(@NonNull android.content.Context, int, int, @Nullable android.os.Handler, @Nullable android.app.ActivityOptions.OnAnimationStartedListener, @Nullable android.app.ActivityOptions.OnAnimationFinishedListener);
+ method @NonNull public static android.app.ActivityOptions makeCustomAnimation(@NonNull android.content.Context, int, int, int, @Nullable android.os.Handler, @Nullable android.app.ActivityOptions.OnAnimationStartedListener, @Nullable android.app.ActivityOptions.OnAnimationFinishedListener);
method @NonNull @RequiresPermission(android.Manifest.permission.START_TASKS_FROM_RECENTS) public static android.app.ActivityOptions makeCustomTaskAnimation(@NonNull android.content.Context, int, int, @Nullable android.os.Handler, @Nullable android.app.ActivityOptions.OnAnimationStartedListener, @Nullable android.app.ActivityOptions.OnAnimationFinishedListener);
method public static void setExitTransitionTimeout(long);
method public void setLaunchActivityType(int);
@@ -354,6 +355,32 @@ package android.app {
ctor public PictureInPictureUiState(boolean);
}
+ public class PropertyInvalidatedCache<Query, Result> {
+ ctor public PropertyInvalidatedCache(int, int, @NonNull String, @NonNull String, @NonNull android.app.PropertyInvalidatedCache.QueryHandler<Query,Result>);
+ method @NonNull public static String createPropertyName(int, @NonNull String);
+ method public final void disableForCurrentProcess();
+ method public static void disableForTestMode();
+ method public final void disableInstance();
+ method public final void disableSystemWide();
+ method public final void forgetDisableLocal();
+ method public boolean getDisabledState();
+ method public final void invalidateCache();
+ method public static void invalidateCache(int, @NonNull String);
+ method public final boolean isDisabled();
+ method @Nullable public final Result query(@NonNull Query);
+ method public static void setTestMode(boolean);
+ method public void testPropertyName();
+ field public static final int MODULE_BLUETOOTH = 2; // 0x2
+ field public static final int MODULE_SYSTEM = 1; // 0x1
+ field public static final int MODULE_TEST = 0; // 0x0
+ }
+
+ public abstract static class PropertyInvalidatedCache.QueryHandler<Q, R> {
+ ctor public PropertyInvalidatedCache.QueryHandler();
+ method @Nullable public abstract R apply(@NonNull Q);
+ method public boolean shouldBypassCache(@NonNull Q);
+ }
+
public class StatusBarManager {
method public void cancelRequestAddTile(@NonNull String);
method public void clickNotification(@Nullable String, int, int, boolean);
@@ -461,6 +488,7 @@ package android.app.admin {
method @RequiresPermission(android.Manifest.permission.FORCE_DEVICE_POLICY_MANAGER_LOGS) public long forceSecurityLogs();
method public void forceUpdateUserSetupComplete(int);
method @NonNull public java.util.Set<java.lang.String> getDefaultCrossProfilePackages();
+ method public int getDeviceOwnerType(@NonNull android.content.ComponentName);
method @NonNull public java.util.Set<java.lang.String> getDisallowedSystemApps(@NonNull android.content.ComponentName, int, @NonNull String);
method public long getLastBugReportRequestTime();
method public long getLastNetworkLogRetrievalTime();
@@ -476,6 +504,7 @@ package android.app.admin {
method @RequiresPermission(allOf={android.Manifest.permission.MANAGE_DEVICE_ADMINS, android.Manifest.permission.INTERACT_ACROSS_USERS_FULL}) public void setActiveAdmin(@NonNull android.content.ComponentName, boolean, int);
method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public boolean setDeviceOwner(@NonNull android.content.ComponentName, @Nullable String, int);
method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public boolean setDeviceOwnerOnly(@NonNull android.content.ComponentName, @Nullable String, int);
+ method public void setDeviceOwnerType(@NonNull android.content.ComponentName, int);
method @RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_ADMINS) public void setNextOperationSafety(int, int);
field public static final String ACTION_DATA_SHARING_RESTRICTION_APPLIED = "android.app.action.DATA_SHARING_RESTRICTION_APPLIED";
field public static final String ACTION_DEVICE_POLICY_CONSTANTS_CHANGED = "android.app.action.DEVICE_POLICY_CONSTANTS_CHANGED";
@@ -1091,7 +1120,7 @@ package android.hardware.camera2 {
package android.hardware.devicestate {
public final class DeviceStateManager {
- method @RequiresPermission(value=android.Manifest.permission.CONTROL_DEVICE_STATE, conditional=true) public void cancelRequest(@NonNull android.hardware.devicestate.DeviceStateRequest);
+ method @RequiresPermission(value=android.Manifest.permission.CONTROL_DEVICE_STATE, conditional=true) public void cancelStateRequest();
method @NonNull public int[] getSupportedStates();
method public void registerCallback(@NonNull java.util.concurrent.Executor, @NonNull android.hardware.devicestate.DeviceStateManager.DeviceStateCallback);
method @RequiresPermission(value=android.Manifest.permission.CONTROL_DEVICE_STATE, conditional=true) public void requestState(@NonNull android.hardware.devicestate.DeviceStateRequest, @Nullable java.util.concurrent.Executor, @Nullable android.hardware.devicestate.DeviceStateRequest.Callback);
@@ -1718,7 +1747,9 @@ package android.os {
}
public final class PowerManager {
+ method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_LOW_POWER_STANDBY, android.Manifest.permission.DEVICE_POWER}) public void forceLowPowerStandbyActive(boolean);
field public static final String ACTION_ENHANCED_DISCHARGE_PREDICTION_CHANGED = "android.os.action.ENHANCED_DISCHARGE_PREDICTION_CHANGED";
+ field @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public static final int SYSTEM_WAKELOCK = -2147483648; // 0x80000000
}
public class Process {
diff --git a/core/java/Android.bp b/core/java/Android.bp
index a2259f3ef7e2..5649c5f1f7db 100644
--- a/core/java/Android.bp
+++ b/core/java/Android.bp
@@ -38,6 +38,11 @@ filegroup {
srcs: ["android/tracing/ITracingServiceProxy.aidl"],
}
+filegroup {
+ name: "TraceReportParams.aidl",
+ srcs: ["android/tracing/TraceReportParams.aidl"],
+}
+
// These are subset of framework-core-sources that are needed by the
// android.test.mock library. The implementation of android.test.mock references
// private members of various components to allow mocking of classes that cannot
@@ -123,6 +128,7 @@ filegroup {
"android/os/IThermalStatusListener.aidl",
"android/os/IThermalService.aidl",
"android/os/IPowerManager.aidl",
+ "android/os/IWakeLockCallback.aidl",
],
}
diff --git a/core/java/android/accessibilityservice/TouchInteractionController.java b/core/java/android/accessibilityservice/TouchInteractionController.java
index bb2b8d45fd61..735df805b872 100644
--- a/core/java/android/accessibilityservice/TouchInteractionController.java
+++ b/core/java/android/accessibilityservice/TouchInteractionController.java
@@ -376,7 +376,7 @@ public final class TouchInteractionController {
throw new IllegalStateException(
"State transitions are not allowed without first adding a callback.");
}
- if (mState != STATE_TOUCH_INTERACTING) {
+ if (mState != STATE_TOUCH_INTERACTING && mState != STATE_DRAGGING) {
throw new IllegalStateException(
"State transitions are not allowed in " + stateToString(mState));
}
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 38138d81885d..530666b8f1d7 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -33,12 +33,16 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.StyleRes;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.annotation.UiContext;
import android.app.VoiceInteractor.Request;
import android.app.admin.DevicePolicyManager;
import android.app.assist.AssistContent;
+import android.app.compat.CompatChanges;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledSince;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentCallbacks2;
import android.content.ComponentName;
@@ -788,6 +792,16 @@ public class Activity extends ContextThemeWrapper
private static final int LOG_AM_ON_TOP_RESUMED_GAINED_CALLED = 30064;
private static final int LOG_AM_ON_TOP_RESUMED_LOST_CALLED = 30065;
+ /**
+ * After {@link Build.VERSION_CODES#TIRAMISU},
+ * {@link #dump(String, FileDescriptor, PrintWriter, String[])} is not called if
+ * {@code dumpsys activity} is called with some special arguments.
+ */
+ @ChangeId
+ @EnabledSince(targetSdkVersion = Build.VERSION_CODES.TIRAMISU)
+ @VisibleForTesting
+ private static final long DUMP_IGNORES_SPECIAL_ARGS = 149254050L;
+
private static class ManagedDialog {
Dialog mDialog;
Bundle mArgs;
@@ -6131,8 +6145,31 @@ public class Activity extends ContextThemeWrapper
* the outgoing activity. Use 0 for no animation.
*/
public void overridePendingTransition(int enterAnim, int exitAnim) {
- ActivityClient.getInstance().overridePendingTransition(mToken, getPackageName(),
- enterAnim, exitAnim);
+ overridePendingTransition(enterAnim, exitAnim, 0);
+ }
+
+ /**
+ * Call immediately after one of the flavors of {@link #startActivity(Intent)}
+ * or {@link #finish} to specify an explicit transition animation to
+ * perform next.
+ *
+ * <p>As of {@link android.os.Build.VERSION_CODES#JELLY_BEAN} an alternative
+ * to using this with starting activities is to supply the desired animation
+ * information through a {@link ActivityOptions} bundle to
+ * {@link #startActivity(Intent, Bundle)} or a related function. This allows
+ * you to specify a custom animation even when starting an activity from
+ * outside the context of the current top activity.
+ *
+ * @param enterAnim A resource ID of the animation resource to use for
+ * the incoming activity. Use 0 for no animation.
+ * @param exitAnim A resource ID of the animation resource to use for
+ * the outgoing activity. Use 0 for no animation.
+ * @param backgroundColor The background color to use for the background during the animation if
+ * the animation requires a background. Set to 0 to not override the default color.
+ */
+ public void overridePendingTransition(int enterAnim, int exitAnim, int backgroundColor) {
+ ActivityClient.getInstance().overridePendingTransition(mToken, getPackageName(), enterAnim,
+ exitAnim, backgroundColor);
}
/**
@@ -7079,7 +7116,18 @@ public class Activity extends ContextThemeWrapper
/**
* Print the Activity's state into the given stream. This gets invoked if
- * you run "adb shell dumpsys activity &lt;activity_component_name&gt;".
+ * you run <code>adb shell dumpsys activity &lt;activity_component_name&gt;</code>.
+ *
+ * <p>This method won't be called if the app targets
+ * {@link android.os.Build.VERSION_CODES#TIRAMISU} or later if the dump request starts with one
+ * of the following arguments:
+ * <ul>
+ * <li>--autofill
+ * <li>--contentcapture
+ * <li>--translation
+ * <li>--list-dumpables
+ * <li>--dump-dumpable
+ * </ul>
*
* @param prefix Desired prefix to prepend at each line of output.
* @param fd The raw file descriptor that the dump is being sent to.
@@ -7106,11 +7154,20 @@ public class Activity extends ContextThemeWrapper
return mDumpableContainer.addDumpable(dumpable);
}
- void dumpInner(@NonNull String prefix, @Nullable FileDescriptor fd,
+ /**
+ * This is the real method called by {@code ActivityThread}, but it's also exposed so
+ * CTS can test for the special args cases.
+ *
+ * @hide
+ */
+ @TestApi
+ @VisibleForTesting
+ @SuppressLint("OnNameExpected")
+ public void dumpInternal(@NonNull String prefix,
+ @SuppressLint("UseParcelFileDescriptor") @Nullable FileDescriptor fd,
@NonNull PrintWriter writer, @Nullable String[] args) {
- String innerPrefix = prefix + " ";
-
- if (args != null && args.length > 0) {
+ if (args != null && args.length > 0
+ && CompatChanges.isChangeEnabled(DUMP_IGNORES_SPECIAL_ARGS)) {
// Handle special cases
switch (args[0]) {
case "--autofill":
@@ -7145,6 +7202,12 @@ public class Activity extends ContextThemeWrapper
return;
}
}
+ dump(prefix, fd, writer, args);
+ }
+
+ void dumpInner(@NonNull String prefix, @Nullable FileDescriptor fd,
+ @NonNull PrintWriter writer, @Nullable String[] args) {
+ String innerPrefix = prefix + " ";
writer.print(prefix); writer.print("Local Activity ");
writer.print(Integer.toHexString(System.identityHashCode(this)));
@@ -7161,11 +7224,6 @@ public class Activity extends ContextThemeWrapper
writer.println(mChangingConfigurations);
writer.print(innerPrefix); writer.print("mCurrentConfig=");
writer.println(mCurrentConfig);
- if (getResources().hasOverrideDisplayAdjustments()) {
- writer.print(innerPrefix);
- writer.print("FixedRotationAdjustments=");
- writer.println(getResources().getDisplayAdjustments().getFixedRotationAdjustments());
- }
mFragments.dumpLoaders(innerPrefix, fd, writer, args);
mFragments.getFragmentManager().dump(innerPrefix, fd, writer, args);
diff --git a/core/java/android/app/ActivityClient.java b/core/java/android/app/ActivityClient.java
index eb4a355c8ae7..605a1fa82254 100644
--- a/core/java/android/app/ActivityClient.java
+++ b/core/java/android/app/ActivityClient.java
@@ -428,11 +428,11 @@ public class ActivityClient {
}
}
- void overridePendingTransition(IBinder token, String packageName,
- int enterAnim, int exitAnim) {
+ void overridePendingTransition(IBinder token, String packageName, int enterAnim, int exitAnim,
+ int backgroundColor) {
try {
getActivityClientController().overridePendingTransition(token, packageName,
- enterAnim, exitAnim);
+ enterAnim, exitAnim, backgroundColor);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index 5e5649f4eadf..e405b60c8daa 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -126,6 +126,12 @@ public class ActivityOptions extends ComponentOptions {
public static final String KEY_ANIM_IN_PLACE_RES_ID = "android:activity.animInPlaceRes";
/**
+ * Custom background color for animation.
+ * @hide
+ */
+ public static final String KEY_ANIM_BACKGROUND_COLOR = "android:activity.backgroundColor";
+
+ /**
* Bitmap for thumbnail animation.
* @hide
*/
@@ -389,6 +395,7 @@ public class ActivityOptions extends ComponentOptions {
private int mCustomEnterResId;
private int mCustomExitResId;
private int mCustomInPlaceResId;
+ private int mCustomBackgroundColor;
private Bitmap mThumbnail;
private int mStartX;
private int mStartY;
@@ -453,7 +460,27 @@ public class ActivityOptions extends ComponentOptions {
*/
public static ActivityOptions makeCustomAnimation(Context context,
int enterResId, int exitResId) {
- return makeCustomAnimation(context, enterResId, exitResId, null, null, null);
+ return makeCustomAnimation(context, enterResId, exitResId, 0, null, null);
+ }
+
+ /**
+ * Create an ActivityOptions specifying a custom animation to run when
+ * the activity is displayed.
+ *
+ * @param context Who is defining this. This is the application that the
+ * animation resources will be loaded from.
+ * @param enterResId A resource ID of the animation resource to use for
+ * the incoming activity. Use 0 for no animation.
+ * @param exitResId A resource ID of the animation resource to use for
+ * the outgoing activity. Use 0 for no animation.
+ * @param backgroundColor The background color to use for the background during the animation if
+ * the animation requires a background. Set to 0 to not override the default color.
+ * @return Returns a new ActivityOptions object that you can use to
+ * supply these options as the options Bundle when starting an activity.
+ */
+ public static @NonNull ActivityOptions makeCustomAnimation(@NonNull Context context,
+ int enterResId, int exitResId, int backgroundColor) {
+ return makeCustomAnimation(context, enterResId, exitResId, backgroundColor, null, null);
}
/**
@@ -477,12 +504,14 @@ public class ActivityOptions extends ComponentOptions {
*/
@UnsupportedAppUsage
public static ActivityOptions makeCustomAnimation(Context context,
- int enterResId, int exitResId, Handler handler, OnAnimationStartedListener listener) {
+ int enterResId, int exitResId, int backgroundColor, Handler handler,
+ OnAnimationStartedListener listener) {
ActivityOptions opts = new ActivityOptions();
opts.mPackageName = context.getPackageName();
opts.mAnimationType = ANIM_CUSTOM;
opts.mCustomEnterResId = enterResId;
opts.mCustomExitResId = exitResId;
+ opts.mCustomBackgroundColor = backgroundColor;
opts.setOnAnimationStartedListener(handler, listener);
return opts;
}
@@ -510,11 +539,11 @@ public class ActivityOptions extends ComponentOptions {
*/
@TestApi
public static @NonNull ActivityOptions makeCustomAnimation(@NonNull Context context,
- int enterResId, int exitResId, @Nullable Handler handler,
+ int enterResId, int exitResId, int backgroundColor, @Nullable Handler handler,
@Nullable OnAnimationStartedListener startedListener,
@Nullable OnAnimationFinishedListener finishedListener) {
- ActivityOptions opts = makeCustomAnimation(context, enterResId, exitResId, handler,
- startedListener);
+ ActivityOptions opts = makeCustomAnimation(context, enterResId, exitResId, backgroundColor,
+ handler, startedListener);
opts.setOnAnimationFinishedListener(handler, finishedListener);
return opts;
}
@@ -547,8 +576,8 @@ public class ActivityOptions extends ComponentOptions {
int enterResId, int exitResId, @Nullable Handler handler,
@Nullable OnAnimationStartedListener startedListener,
@Nullable OnAnimationFinishedListener finishedListener) {
- ActivityOptions opts = makeCustomAnimation(context, enterResId, exitResId, handler,
- startedListener, finishedListener);
+ ActivityOptions opts = makeCustomAnimation(context, enterResId, exitResId, 0,
+ handler, startedListener, finishedListener);
opts.mOverrideTaskTransition = true;
return opts;
}
@@ -1243,6 +1272,11 @@ public class ActivityOptions extends ComponentOptions {
return mCustomInPlaceResId;
}
+ /** @hide */
+ public int getCustomBackgroundColor() {
+ return mCustomBackgroundColor;
+ }
+
/**
* The thumbnail is copied into a hardware bitmap when it is bundled and sent to the system, so
* it should always be backed by a HardwareBuffer on the other end.
@@ -1775,6 +1809,7 @@ public class ActivityOptions extends ComponentOptions {
case ANIM_CUSTOM:
mCustomEnterResId = otherOptions.mCustomEnterResId;
mCustomExitResId = otherOptions.mCustomExitResId;
+ mCustomBackgroundColor = otherOptions.mCustomBackgroundColor;
mThumbnail = null;
if (mAnimationStartedListener != null) {
try {
@@ -1862,6 +1897,7 @@ public class ActivityOptions extends ComponentOptions {
case ANIM_CUSTOM:
b.putInt(KEY_ANIM_ENTER_RES_ID, mCustomEnterResId);
b.putInt(KEY_ANIM_EXIT_RES_ID, mCustomExitResId);
+ b.putInt(KEY_ANIM_BACKGROUND_COLOR, mCustomBackgroundColor);
b.putBinder(KEY_ANIM_START_LISTENER, mAnimationStartedListener
!= null ? mAnimationStartedListener.asBinder() : null);
break;
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index ea6271412289..3289304a0df4 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -175,7 +175,6 @@ import android.util.proto.ProtoOutputStream;
import android.view.Choreographer;
import android.view.Display;
import android.view.DisplayAdjustments;
-import android.view.DisplayAdjustments.FixedRotationAdjustments;
import android.view.SurfaceControl;
import android.view.ThreadedRenderer;
import android.view.View;
@@ -201,6 +200,7 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IVoiceInteractor;
import com.android.internal.content.ReferrerIntent;
+import com.android.internal.os.BinderCallsStats;
import com.android.internal.os.BinderInternal;
import com.android.internal.os.RuntimeInit;
import com.android.internal.os.SomeArgs;
@@ -598,12 +598,6 @@ public final class ActivityThread extends ClientTransactionHandler
/** The options for scene transition. */
ActivityOptions mActivityOptions;
- /**
- * If non-null, the activity is launching with a specified rotation, the adjustments should
- * be consumed before activity creation.
- */
- FixedRotationAdjustments mPendingFixedRotationAdjustments;
-
/** Whether this activiy was launched from a bubble. */
boolean mLaunchedFromBubble;
@@ -625,8 +619,7 @@ public final class ActivityThread extends ClientTransactionHandler
PersistableBundle persistentState, List<ResultInfo> pendingResults,
List<ReferrerIntent> pendingNewIntents, ActivityOptions activityOptions,
boolean isForward, ProfilerInfo profilerInfo, ClientTransactionHandler client,
- IBinder assistToken, FixedRotationAdjustments fixedRotationAdjustments,
- IBinder shareableActivityToken, boolean launchedFromBubble) {
+ IBinder assistToken, IBinder shareableActivityToken, boolean launchedFromBubble) {
this.token = token;
this.assistToken = assistToken;
this.shareableActivityToken = shareableActivityToken;
@@ -646,7 +639,6 @@ public final class ActivityThread extends ClientTransactionHandler
this.packageInfo = client.getPackageInfoNoCheck(activityInfo.applicationInfo,
compatInfo);
mActivityOptions = activityOptions;
- mPendingFixedRotationAdjustments = fixedRotationAdjustments;
mLaunchedFromBubble = launchedFromBubble;
init();
}
@@ -1013,6 +1005,11 @@ public final class ActivityThread extends ClientTransactionHandler
RemoteCallback finishCallback;
}
+ static final class DumpResourcesData {
+ public ParcelFileDescriptor fd;
+ public RemoteCallback finishCallback;
+ }
+
static final class UpdateCompatibilityData {
String pkg;
CompatibilityInfo info;
@@ -1324,6 +1321,20 @@ public final class ActivityThread extends ClientTransactionHandler
sendMessage(H.SCHEDULE_CRASH, args, typeId);
}
+ @Override
+ public void dumpResources(ParcelFileDescriptor fd, RemoteCallback callback) {
+ DumpResourcesData data = new DumpResourcesData();
+ try {
+ data.fd = fd.dup();
+ data.finishCallback = callback;
+ sendMessage(H.DUMP_RESOURCES, data, 0, 0, false /*async*/);
+ } catch (IOException e) {
+ Slog.w(TAG, "dumpResources failed", e);
+ } finally {
+ IoUtils.closeQuietly(fd);
+ }
+ }
+
public void dumpActivity(ParcelFileDescriptor pfd, IBinder activitytoken,
String prefix, String[] args) {
DumpComponentInfo data = new DumpComponentInfo();
@@ -2047,6 +2058,7 @@ public final class ActivityThread extends ClientTransactionHandler
public static final int UPDATE_UI_TRANSLATION_STATE = 163;
public static final int SET_CONTENT_CAPTURE_OPTIONS_CALLBACK = 164;
public static final int DUMP_GFXINFO = 165;
+ public static final int DUMP_RESOURCES = 166;
public static final int INSTRUMENT_WITHOUT_RESTART = 170;
public static final int FINISH_INSTRUMENTATION_WITHOUT_RESTART = 171;
@@ -2100,6 +2112,7 @@ public final class ActivityThread extends ClientTransactionHandler
case INSTRUMENT_WITHOUT_RESTART: return "INSTRUMENT_WITHOUT_RESTART";
case FINISH_INSTRUMENTATION_WITHOUT_RESTART:
return "FINISH_INSTRUMENTATION_WITHOUT_RESTART";
+ case DUMP_RESOURCES: return "DUMP_RESOURCES";
}
}
return Integer.toString(code);
@@ -2215,6 +2228,9 @@ public final class ActivityThread extends ClientTransactionHandler
case DUMP_HEAP:
handleDumpHeap((DumpHeapData) msg.obj);
break;
+ case DUMP_RESOURCES:
+ handleDumpResources((DumpResourcesData) msg.obj);
+ break;
case DUMP_ACTIVITY:
handleDumpActivity((DumpComponentInfo)msg.obj);
break;
@@ -3512,21 +3528,6 @@ public final class ActivityThread extends ClientTransactionHandler
mH.sendMessage(msg);
}
- private void sendMessage(int what, Object obj, int arg1, int arg2, int seq) {
- if (DEBUG_MESSAGES) Slog.v(
- TAG, "SCHEDULE " + mH.codeToString(what) + " arg1=" + arg1 + " arg2=" + arg2 +
- "seq= " + seq);
- Message msg = Message.obtain();
- msg.what = what;
- SomeArgs args = SomeArgs.obtain();
- args.arg1 = obj;
- args.argi1 = arg1;
- args.argi2 = arg2;
- args.argi3 = seq;
- msg.obj = args;
- mH.sendMessage(msg);
- }
-
final void scheduleContextCleanup(ContextImpl context, String who,
String what) {
ContextCleanupInfo cci = new ContextCleanupInfo();
@@ -3536,64 +3537,6 @@ public final class ActivityThread extends ClientTransactionHandler
sendMessage(H.CLEAN_UP_CONTEXT, cci);
}
- /**
- * Applies the rotation adjustments to override display information in resources belong to the
- * provided token. If the token is activity token, the adjustments also apply to application
- * because the appearance of activity is usually more sensitive to the application resources.
- *
- * @param token The token to apply the adjustments.
- * @param fixedRotationAdjustments The information to override the display adjustments of
- * corresponding resources. If it is null, the exiting override
- * will be cleared.
- */
- @Override
- public void handleFixedRotationAdjustments(@NonNull IBinder token,
- @Nullable FixedRotationAdjustments fixedRotationAdjustments) {
- final Consumer<DisplayAdjustments> override = fixedRotationAdjustments != null
- ? displayAdjustments -> displayAdjustments
- .setFixedRotationAdjustments(fixedRotationAdjustments)
- : null;
- if (!mResourcesManager.overrideTokenDisplayAdjustments(token, override)) {
- // No resources are associated with the token.
- return;
- }
- if (mActivities.get(token) == null) {
- // Nothing to do for application if it is not an activity token.
- return;
- }
-
- overrideApplicationDisplayAdjustments(token, override);
- }
-
- /**
- * Applies the last override to application resources for compatibility. Because the Resources
- * of Display can be from application, e.g.
- * applicationContext.getSystemService(DisplayManager.class).getDisplay(displayId)
- * and the deprecated usage:
- * applicationContext.getSystemService(WindowManager.class).getDefaultDisplay();
- *
- * @param token The owner and target of the override.
- * @param override The display adjustments override for application resources. If it is null,
- * the override of the token will be removed and pop the last one to use.
- */
- private void overrideApplicationDisplayAdjustments(@NonNull IBinder token,
- @Nullable Consumer<DisplayAdjustments> override) {
- final Consumer<DisplayAdjustments> appOverride;
- if (mActiveRotationAdjustments == null) {
- mActiveRotationAdjustments = new ArrayList<>(2);
- }
- if (override != null) {
- mActiveRotationAdjustments.add(Pair.create(token, override));
- appOverride = override;
- } else {
- mActiveRotationAdjustments.removeIf(adjustmentsPair -> adjustmentsPair.first == token);
- appOverride = mActiveRotationAdjustments.isEmpty()
- ? null
- : mActiveRotationAdjustments.get(mActiveRotationAdjustments.size() - 1).second;
- }
- mInitialApplication.getResources().overrideDisplayAdjustments(appOverride);
- }
-
/** Core implementation of activity launch. */
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
ActivityInfo aInfo = r.activityInfo;
@@ -3811,19 +3754,6 @@ public final class ActivityThread extends ClientTransactionHandler
ContextImpl appContext = ContextImpl.createActivityContext(
this, r.packageInfo, r.activityInfo, r.token, displayId, r.overrideConfig);
- // The rotation adjustments must be applied before creating the activity, so the activity
- // can get the adjusted display info during creation.
- if (r.mPendingFixedRotationAdjustments != null) {
- // The adjustments should have been set by handleLaunchActivity, so the last one is the
- // override for activity resources.
- if (mActiveRotationAdjustments != null && !mActiveRotationAdjustments.isEmpty()) {
- mResourcesManager.overrideTokenDisplayAdjustments(r.token,
- mActiveRotationAdjustments.get(
- mActiveRotationAdjustments.size() - 1).second);
- }
- r.mPendingFixedRotationAdjustments = null;
- }
-
final DisplayManagerGlobal dm = DisplayManagerGlobal.getInstance();
// For debugging purposes, if the activity's package name contains the value of
// the "debug.use-second-display" system property as a substring, then show
@@ -3859,13 +3789,6 @@ public final class ActivityThread extends ClientTransactionHandler
mProfiler.startProfiling();
}
- if (r.mPendingFixedRotationAdjustments != null) {
- // The rotation adjustments must be applied before handling configuration, so process
- // level display metrics can be adjusted.
- overrideApplicationDisplayAdjustments(r.token, adjustments ->
- adjustments.setFixedRotationAdjustments(r.mPendingFixedRotationAdjustments));
- }
-
// Make sure we are running with the most recent config.
mConfigurationController.handleConfigurationChanged(null, null);
@@ -4686,6 +4609,23 @@ public final class ActivityThread extends ClientTransactionHandler
}
}
+ private void handleDumpResources(DumpResourcesData info) {
+ final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
+ try {
+ PrintWriter pw = new FastPrintWriter(new FileOutputStream(
+ info.fd.getFileDescriptor()));
+
+ Resources.dumpHistory(pw, "");
+ pw.flush();
+ if (info.finishCallback != null) {
+ info.finishCallback.sendResult(null);
+ }
+ } finally {
+ IoUtils.closeQuietly(info.fd);
+ StrictMode.setThreadPolicy(oldPolicy);
+ }
+ }
+
private void handleDumpActivity(DumpComponentInfo info) {
final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
try {
@@ -4693,7 +4633,7 @@ public final class ActivityThread extends ClientTransactionHandler
if (r != null && r.activity != null) {
PrintWriter pw = new FastPrintWriter(new FileOutputStream(
info.fd.getFileDescriptor()));
- r.activity.dump(info.prefix, info.fd.getFileDescriptor(), pw, info.args);
+ r.activity.dumpInternal(info.prefix, info.fd.getFileDescriptor(), pw, info.args);
pw.flush();
}
} finally {
@@ -5974,13 +5914,6 @@ public final class ActivityThread extends ClientTransactionHandler
final Configuration finalOverrideConfig = createNewConfigAndUpdateIfNotNull(
amOverrideConfig, contextThemeWrapperOverrideConfig);
mResourcesManager.updateResourcesForActivity(activityToken, finalOverrideConfig, displayId);
- final Resources res = activity.getResources();
- if (res.hasOverrideDisplayAdjustments()) {
- // If fixed rotation is applied while the activity is visible (e.g. PiP), the rotated
- // configuration of activity may be sent later than the adjustments. In this case, the
- // adjustments need to be updated for the consistency of display info.
- res.getDisplayAdjustments().getConfiguration().updateFrom(finalOverrideConfig);
- }
activity.mConfigChangeFlags = 0;
activity.mCurrentConfig = new Configuration(newConfig);
@@ -7643,8 +7576,7 @@ public final class ActivityThread extends ClientTransactionHandler
// We need to apply this change to the resources immediately, because upon returning
// the view hierarchy will be informed about it.
if (mResourcesManager.applyConfigurationToResources(globalConfig,
- null /* compat */,
- mInitialApplication.getResources().getDisplayAdjustments())) {
+ null /* compat */)) {
mConfigurationController.updateLocaleListFromAppContext(
mInitialApplication.getApplicationContext());
@@ -7922,6 +7854,8 @@ public final class ActivityThread extends ClientTransactionHandler
MediaFrameworkPlatformInitializer.setMediaServiceManager(new MediaServiceManager());
MediaFrameworkInitializer.setMediaServiceManager(new MediaServiceManager());
BluetoothFrameworkInitializer.setBluetoothServiceManager(new BluetoothServiceManager());
+ BluetoothFrameworkInitializer.setBinderCallsStatsInitializer(context -> {
+ BinderCallsStats.startForBluetooth(context); });
}
private void purgePendingResources() {
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 008123407cff..fdf37f6633ee 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -1327,9 +1327,17 @@ public class AppOpsManager {
*/
public static final int OP_ESTABLISH_VPN_MANAGER = AppProtoEnums.APP_OP_ESTABLISH_VPN_MANAGER;
+ /**
+ * Access restricted settings.
+ *
+ * @hide
+ */
+ public static final int OP_ACCESS_RESTRICTED_SETTINGS =
+ AppProtoEnums.APP_OP_ACCESS_RESTRICTED_SETTINGS;
+
/** @hide */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- public static final int _NUM_OP = 119;
+ public static final int _NUM_OP = 120;
/** Access to coarse location information. */
public static final String OPSTR_COARSE_LOCATION = "android:coarse_location";
@@ -1784,6 +1792,14 @@ public class AppOpsManager {
@SystemApi
public static final String OPSTR_ESTABLISH_VPN_MANAGER = "android:establish_vpn_manager";
+ /**
+ * Limit user accessing restricted settings.
+ *
+ * @hide
+ */
+ public static final String OPSTR_ACCESS_RESTRICTED_SETTINGS =
+ "android:access_restricted_settings";
+
/** {@link #sAppOpsToNote} not initialized yet for this op */
private static final byte SHOULD_COLLECT_NOTE_OP_NOT_INITIALIZED = 0;
/** Should not collect noting of this app-op in {@link #sAppOpsToNote} */
@@ -2004,6 +2020,7 @@ public class AppOpsManager {
OP_NEARBY_WIFI_DEVICES, // OP_NEARBY_WIFI_DEVICES
OP_ESTABLISH_VPN_SERVICE, // OP_ESTABLISH_VPN_SERVICE
OP_ESTABLISH_VPN_MANAGER, // OP_ESTABLISH_VPN_MANAGER
+ OP_ACCESS_RESTRICTED_SETTINGS, // OP_ACCESS_RESTRICTED_SETTINGS
};
/**
@@ -2129,6 +2146,7 @@ public class AppOpsManager {
OPSTR_NEARBY_WIFI_DEVICES,
OPSTR_ESTABLISH_VPN_SERVICE,
OPSTR_ESTABLISH_VPN_MANAGER,
+ OPSTR_ACCESS_RESTRICTED_SETTINGS,
};
/**
@@ -2255,6 +2273,7 @@ public class AppOpsManager {
"NEARBY_WIFI_DEVICES",
"ESTABLISH_VPN_SERVICE",
"ESTABLISH_VPN_MANAGER",
+ "ACCESS_RESTRICTED_SETTINGS",
};
/**
@@ -2344,11 +2363,11 @@ public class AppOpsManager {
Manifest.permission.USE_BIOMETRIC,
Manifest.permission.ACTIVITY_RECOGNITION,
Manifest.permission.SMS_FINANCIAL_TRANSACTIONS,
- Manifest.permission.READ_MEDIA_AUDIO,
+ null,
null, // no permission for OP_WRITE_MEDIA_AUDIO
- Manifest.permission.READ_MEDIA_VIDEO,
+ null,
null, // no permission for OP_WRITE_MEDIA_VIDEO
- Manifest.permission.READ_MEDIA_IMAGE,
+ null,
null, // no permission for OP_WRITE_MEDIA_IMAGES
null, // no permission for OP_LEGACY_STORAGE
null, // no permission for OP_ACCESS_ACCESSIBILITY
@@ -2382,6 +2401,7 @@ public class AppOpsManager {
Manifest.permission.NEARBY_WIFI_DEVICES,
null, // no permission for OP_ESTABLISH_VPN_SERVICE
null, // no permission for OP_ESTABLISH_VPN_MANAGER
+ null, // no permission for OP_ACCESS_RESTRICTED_SETTINGS,
};
/**
@@ -2509,6 +2529,7 @@ public class AppOpsManager {
null, // NEARBY_WIFI_DEVICES
null, // ESTABLISH_VPN_SERVICE
null, // ESTABLISH_VPN_MANAGER
+ null, // ACCESS_RESTRICTED_SETTINGS,
};
/**
@@ -2635,6 +2656,7 @@ public class AppOpsManager {
null, // NEARBY_WIFI_DEVICES
null, // ESTABLISH_VPN_SERVICE
null, // ESTABLISH_VPN_MANAGER
+ null, // ACCESS_RESTRICTED_SETTINGS,
};
/**
@@ -2760,6 +2782,7 @@ public class AppOpsManager {
AppOpsManager.MODE_ALLOWED, // NEARBY_WIFI_DEVICES
AppOpsManager.MODE_ALLOWED, // ESTABLISH_VPN_SERVICE
AppOpsManager.MODE_ALLOWED, // ESTABLISH_VPN_MANAGER
+ AppOpsManager.MODE_ALLOWED, // ACCESS_RESTRICTED_SETTINGS,
};
/**
@@ -2889,6 +2912,7 @@ public class AppOpsManager {
false, // NEARBY_WIFI_DEVICES
false, // OP_ESTABLISH_VPN_SERVICE
false, // OP_ESTABLISH_VPN_MANAGER
+ true, // ACCESS_RESTRICTED_SETTINGS
};
/**
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 7b55b6c0e458..b1956effb093 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -16,6 +16,11 @@
package android.app;
+import static android.app.admin.DevicePolicyResources.Drawables.Style.SOLID_COLORED;
+import static android.app.admin.DevicePolicyResources.Drawables.Style.SOLID_NOT_COLORED;
+import static android.app.admin.DevicePolicyResources.Drawables.UNDEFINED;
+import static android.app.admin.DevicePolicyResources.Drawables.WORK_PROFILE_ICON;
+import static android.app.admin.DevicePolicyResources.Drawables.WORK_PROFILE_ICON_BADGE;
import static android.content.pm.Checksum.TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256;
import static android.content.pm.Checksum.TYPE_PARTIAL_MERKLE_ROOT_1M_SHA512;
import static android.content.pm.Checksum.TYPE_WHOLE_MD5;
@@ -31,6 +36,7 @@ import android.annotation.Nullable;
import android.annotation.StringRes;
import android.annotation.UserIdInt;
import android.annotation.XmlRes;
+import android.app.admin.DevicePolicyManager;
import android.app.role.RoleManager;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
@@ -62,7 +68,6 @@ import android.content.pm.PackageInfo;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageItemInfo;
import android.content.pm.PackageManager;
-import android.content.pm.PackageParser;
import android.content.pm.ParceledListSlice;
import android.content.pm.PermissionGroupInfo;
import android.content.pm.PermissionInfo;
@@ -74,7 +79,6 @@ import android.content.pm.SuspendDialogInfo;
import android.content.pm.VerifierDeviceIdentity;
import android.content.pm.VersionedPackage;
import android.content.pm.dex.ArtManager;
-import android.content.pm.pkg.FrameworkPackageUserState;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.XmlResourceParser;
@@ -122,7 +126,6 @@ import dalvik.system.VMRuntime;
import libcore.util.EmptyArray;
-import java.io.File;
import java.lang.ref.WeakReference;
import java.security.cert.Certificate;
import java.security.cert.CertificateEncodingException;
@@ -172,6 +175,8 @@ public class ApplicationPackageManager extends PackageManager {
private PackageInstaller mInstaller;
@GuardedBy("mLock")
private ArtManager mArtManager;
+ @GuardedBy("mLock")
+ private DevicePolicyManager mDevicePolicyManager;
@GuardedBy("mDelegates")
private final ArrayList<MoveCallbackDelegate> mDelegates = new ArrayList<>();
@@ -188,6 +193,15 @@ public class ApplicationPackageManager extends PackageManager {
}
}
+ DevicePolicyManager getDevicePolicyManager() {
+ synchronized (mLock) {
+ if (mDevicePolicyManager == null) {
+ mDevicePolicyManager = mContext.getSystemService(DevicePolicyManager.class);
+ }
+ return mDevicePolicyManager;
+ }
+ }
+
private PermissionManager getPermissionManager() {
synchronized (mLock) {
if (mPermissionManager == null) {
@@ -1876,12 +1890,27 @@ public class ApplicationPackageManager extends PackageManager {
if (!hasUserBadge(user.getIdentifier())) {
return icon;
}
+
+ final Drawable badgeForeground = getDevicePolicyManager().getDrawable(
+ getUpdatableUserIconBadgeId(user),
+ SOLID_COLORED,
+ () -> getDefaultUserIconBadge(user));
+
Drawable badge = new LauncherIcons(mContext).getBadgeDrawable(
- getUserManager().getUserIconBadgeResId(user.getIdentifier()),
+ badgeForeground,
getUserBadgeColor(user, false));
return getBadgedDrawable(icon, badge, null, true);
}
+ private String getUpdatableUserIconBadgeId(UserHandle user) {
+ return getUserManager().isManagedProfile(user.getIdentifier())
+ ? WORK_PROFILE_ICON_BADGE : UNDEFINED;
+ }
+
+ private Drawable getDefaultUserIconBadge(UserHandle user) {
+ return mContext.getDrawable(getUserManager().getUserIconBadgeResId(user.getIdentifier()));
+ }
+
@Override
public Drawable getUserBadgedDrawableForDensity(Drawable drawable, UserHandle user,
Rect badgeLocation, int badgeDensity) {
@@ -1913,13 +1942,28 @@ public class ApplicationPackageManager extends PackageManager {
if (badgeColor == null) {
return null;
}
- Drawable badgeForeground = getDrawableForDensity(
- getUserManager().getUserBadgeResId(user.getIdentifier()), density);
+
+ final Drawable badgeForeground = getDevicePolicyManager().getDrawableForDensity(
+ getUpdatableUserBadgeId(user),
+ SOLID_COLORED,
+ density,
+ () -> getDefaultUserBadgeForDensity(user, density));
+
badgeForeground.setTint(getUserBadgeColor(user, false));
Drawable badge = new LayerDrawable(new Drawable[] {badgeColor, badgeForeground });
return badge;
}
+ private String getUpdatableUserBadgeId(UserHandle user) {
+ return getUserManager().isManagedProfile(user.getIdentifier())
+ ? WORK_PROFILE_ICON : UNDEFINED;
+ }
+
+ private Drawable getDefaultUserBadgeForDensity(UserHandle user, int density) {
+ return getDrawableForDensity(
+ getUserManager().getUserBadgeResId(user.getIdentifier()), density);
+ }
+
/**
* Returns the badge color based on whether device has dark theme enabled or not.
*/
@@ -1928,14 +1972,24 @@ public class ApplicationPackageManager extends PackageManager {
if (!hasUserBadge(user.getIdentifier())) {
return null;
}
- Drawable badge = getDrawableForDensity(
- getUserManager().getUserBadgeNoBackgroundResId(user.getIdentifier()), density);
+
+ final Drawable badge = getDevicePolicyManager().getDrawableForDensity(
+ getUpdatableUserBadgeId(user),
+ SOLID_NOT_COLORED,
+ density,
+ () -> getDefaultUserBadgeNoBackgroundForDensity(user, density));
+
if (badge != null) {
badge.setTint(getUserBadgeColor(user, true));
}
return badge;
}
+ private Drawable getDefaultUserBadgeNoBackgroundForDensity(UserHandle user, int density) {
+ return getDrawableForDensity(
+ getUserManager().getUserBadgeNoBackgroundResId(user.getIdentifier()), density);
+ }
+
private Drawable getDrawableForDensity(int drawableId, int density) {
if (density <= 0) {
density = mContext.getResources().getDisplayMetrics().densityDpi;
diff --git a/core/java/android/app/AsyncNotedAppOp.java b/core/java/android/app/AsyncNotedAppOp.java
index db58c215ffe2..7845b6a6588e 100644
--- a/core/java/android/app/AsyncNotedAppOp.java
+++ b/core/java/android/app/AsyncNotedAppOp.java
@@ -37,7 +37,8 @@ import com.android.internal.util.Preconditions;
@Immutable
@DataClass(genEqualsHashCode = true,
genAidl = true,
- genHiddenConstructor = true)
+ genHiddenConstructor = true,
+ genToString = true)
// - We don't expose the opCode, but rather the public name of the op, hence use a non-standard
// getter
@DataClass.Suppress({"getOpCode"})
@@ -70,9 +71,13 @@ public final class AsyncNotedAppOp implements Parcelable {
Preconditions.checkArgumentInRange(mOpCode, 0, AppOpsManager._NUM_OP - 1, "opCode");
}
+ private String opCodeToString() {
+ return getOp();
+ }
+
- // Code below generated by codegen v1.0.20.
+ // Code below generated by codegen v1.0.23.
//
// DO NOT MODIFY!
// CHECKSTYLE:OFF Generated code
@@ -160,6 +165,21 @@ public final class AsyncNotedAppOp implements Parcelable {
@Override
@DataClass.Generated.Member
+ public String toString() {
+ // You can override field toString logic by defining methods like:
+ // String fieldNameToString() { ... }
+
+ return "AsyncNotedAppOp { " +
+ "opCode = " + opCodeToString() + ", " +
+ "notingUid = " + mNotingUid + ", " +
+ "attributionTag = " + mAttributionTag + ", " +
+ "message = " + mMessage + ", " +
+ "time = " + mTime +
+ " }";
+ }
+
+ @Override
+ @DataClass.Generated.Member
public boolean equals(@Nullable Object o) {
// You can override field equality logic by defining either of the methods like:
// boolean fieldNameEquals(AsyncNotedAppOp other) { ... }
@@ -261,10 +281,10 @@ public final class AsyncNotedAppOp implements Parcelable {
};
@DataClass.Generated(
- time = 1604456255752L,
- codegenVersion = "1.0.20",
+ time = 1643320606160L,
+ codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/app/AsyncNotedAppOp.java",
- inputSignatures = "private final @android.annotation.IntRange int mOpCode\nprivate final @android.annotation.IntRange int mNotingUid\nprivate final @android.annotation.Nullable java.lang.String mAttributionTag\nprivate final @android.annotation.NonNull java.lang.String mMessage\nprivate final @android.annotation.CurrentTimeMillisLong long mTime\npublic @android.annotation.NonNull java.lang.String getOp()\nprivate void onConstructed()\nclass AsyncNotedAppOp extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genAidl=true, genHiddenConstructor=true)")
+ inputSignatures = "private final @android.annotation.IntRange int mOpCode\nprivate final @android.annotation.IntRange int mNotingUid\nprivate final @android.annotation.Nullable java.lang.String mAttributionTag\nprivate final @android.annotation.NonNull java.lang.String mMessage\nprivate final @android.annotation.CurrentTimeMillisLong long mTime\npublic @android.annotation.NonNull java.lang.String getOp()\nprivate void onConstructed()\nprivate java.lang.String opCodeToString()\nclass AsyncNotedAppOp extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genAidl=true, genHiddenConstructor=true, genToString=true)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/app/ClientTransactionHandler.java b/core/java/android/app/ClientTransactionHandler.java
index d365269ed1a6..65e6ab7a2137 100644
--- a/core/java/android/app/ClientTransactionHandler.java
+++ b/core/java/android/app/ClientTransactionHandler.java
@@ -27,7 +27,6 @@ import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.os.IBinder;
import android.util.MergedConfiguration;
-import android.view.DisplayAdjustments.FixedRotationAdjustments;
import android.view.SurfaceControl;
import android.window.SplashScreenView.SplashScreenViewParcelable;
@@ -187,10 +186,6 @@ public abstract class ClientTransactionHandler {
/** Deliver app configuration change notification. */
public abstract void handleConfigurationChanged(Configuration config);
- /** Apply addition adjustments to override display information. */
- public abstract void handleFixedRotationAdjustments(IBinder token,
- FixedRotationAdjustments fixedRotationAdjustments);
-
/**
* Get {@link android.app.ActivityThread.ActivityClientRecord} instance that corresponds to the
* provided token.
diff --git a/core/java/android/app/ConfigurationController.java b/core/java/android/app/ConfigurationController.java
index 58f60a6a59a7..1a77b65c8ef6 100644
--- a/core/java/android/app/ConfigurationController.java
+++ b/core/java/android/app/ConfigurationController.java
@@ -185,14 +185,7 @@ class ConfigurationController {
final Application app = mActivityThread.getApplication();
final Resources appResources = app.getResources();
- if (appResources.hasOverrideDisplayAdjustments()) {
- // The value of Display#getRealSize will be adjusted by FixedRotationAdjustments,
- // but Display#getSize refers to DisplayAdjustments#mConfiguration. So the rotated
- // configuration also needs to set to the adjustments for consistency.
- appResources.getDisplayAdjustments().getConfiguration().updateFrom(config);
- }
- mResourcesManager.applyConfigurationToResources(config, compat,
- appResources.getDisplayAdjustments());
+ mResourcesManager.applyConfigurationToResources(config, compat);
updateLocaleListFromAppContext(app.getApplicationContext());
if (mConfiguration == null) {
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index f3315a8dc089..8181a74e8f95 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -1997,7 +1997,8 @@ class ContextImpl extends Context {
private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags,
String instanceName, Handler handler, Executor executor, UserHandle user) {
- // Keep this in sync with DevicePolicyManager.bindDeviceAdminServiceAsUser.
+ // Keep this in sync with DevicePolicyManager.bindDeviceAdminServiceAsUser and
+ // ActivityManagerService.LocalService.startAndBindSupplementalProcessService
IServiceConnection sd;
if (conn == null) {
throw new IllegalArgumentException("connection is null");
@@ -2023,10 +2024,10 @@ class ContextImpl extends Context {
flags |= BIND_WAIVE_PRIORITY;
}
service.prepareToLeaveProcess(this);
- int res = ActivityManager.getService().bindIsolatedService(
- mMainThread.getApplicationThread(), getActivityToken(), service,
- service.resolveTypeIfNeeded(getContentResolver()),
- sd, flags, instanceName, getOpPackageName(), user.getIdentifier());
+ int res = ActivityManager.getService().bindServiceInstance(
+ mMainThread.getApplicationThread(), getActivityToken(), service,
+ service.resolveTypeIfNeeded(getContentResolver()),
+ sd, flags, instanceName, getOpPackageName(), user.getIdentifier());
if (res < 0) {
throw new SecurityException(
"Not allowed to bind to service " + service);
diff --git a/core/java/android/app/IActivityClientController.aidl b/core/java/android/app/IActivityClientController.aidl
index 83c57c573b82..396e5528ab0c 100644
--- a/core/java/android/app/IActivityClientController.aidl
+++ b/core/java/android/app/IActivityClientController.aidl
@@ -112,7 +112,7 @@ interface IActivityClientController {
* calls, so this method should be the same as them to keep the invocation order.
*/
void overridePendingTransition(in IBinder token, in String packageName,
- int enterAnim, int exitAnim);
+ int enterAnim, int exitAnim, int backgroundColor);
int setVrMode(in IBinder token, boolean enabled, in ComponentName packageName);
/** See {@link android.app.Activity#setDisablePreviewScreenshots}. */
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 82c419affac2..e4ef12c250ab 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -169,7 +169,7 @@ interface IActivityManager {
int bindService(in IApplicationThread caller, in IBinder token, in Intent service,
in String resolvedType, in IServiceConnection connection, int flags,
in String callingPackage, int userId);
- int bindIsolatedService(in IApplicationThread caller, in IBinder token, in Intent service,
+ int bindServiceInstance(in IApplicationThread caller, in IBinder token, in Intent service,
in String resolvedType, in IServiceConnection connection, int flags,
in String instanceName, in String callingPackage, int userId);
void updateServiceGroup(in IServiceConnection connection, int group, int importance);
diff --git a/core/java/android/app/IApplicationThread.aidl b/core/java/android/app/IApplicationThread.aidl
index 1714229486e4..77657d58cc4c 100644
--- a/core/java/android/app/IApplicationThread.aidl
+++ b/core/java/android/app/IApplicationThread.aidl
@@ -113,6 +113,7 @@ oneway interface IApplicationThread {
in ParcelFileDescriptor fd, in RemoteCallback finishCallback);
void dumpActivity(in ParcelFileDescriptor fd, IBinder servicetoken, in String prefix,
in String[] args);
+ void dumpResources(in ParcelFileDescriptor fd, in RemoteCallback finishCallback);
void clearDnsCache();
void updateHttpProxy();
void setCoreSettings(in Bundle coreSettings);
diff --git a/core/java/android/app/KeyguardManager.java b/core/java/android/app/KeyguardManager.java
index 14afd0fcebf8..5d1f4df4359e 100644
--- a/core/java/android/app/KeyguardManager.java
+++ b/core/java/android/app/KeyguardManager.java
@@ -746,7 +746,7 @@ public class KeyguardManager {
if (!hasPermission(Manifest.permission.SET_INITIAL_LOCK)) {
throw new SecurityException("Requires SET_INITIAL_LOCK permission.");
}
- return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
+ return true;
}
private boolean hasPermission(String permission) {
@@ -814,6 +814,8 @@ public class KeyguardManager {
/**
* Set the lockscreen password after validating against its expected complexity level.
*
+ * Below {@link android.os.Build.VERSION_CODES#S_V2}, this API will only work
+ * when {@link PackageManager.FEATURE_AUTOMOTIVE} is present.
* @param lockType - type of lock as specified in {@link LockTypes}
* @param password - password to validate; this has the same encoding
* as the output of String#getBytes
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index 4e32e9a41869..56c725eb81f9 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -746,6 +746,10 @@ public final class LoadedApk {
// default linker namespace.
continue;
}
+ if (info.isSdk()) {
+ // SDKs are not loaded automatically.
+ continue;
+ }
if (libsToLoadAfter.contains(info.getName())) {
if (DEBUG) {
Slog.v(ActivityThread.TAG,
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index d57c288165d5..6a147720f8e9 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -17,6 +17,10 @@
package android.app;
import static android.annotation.Dimension.DP;
+import static android.app.admin.DevicePolicyResources.Drawables.Source.NOTIFICATION;
+import static android.app.admin.DevicePolicyResources.Drawables.Style.SOLID_COLORED;
+import static android.app.admin.DevicePolicyResources.Drawables.UNDEFINED;
+import static android.app.admin.DevicePolicyResources.Drawables.WORK_PROFILE_ICON;
import static android.graphics.drawable.Icon.TYPE_URI;
import static android.graphics.drawable.Icon.TYPE_URI_ADAPTIVE_BITMAP;
@@ -39,6 +43,7 @@ import android.annotation.StyleableRes;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.TestApi;
+import android.app.admin.DevicePolicyManager;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.Intent;
@@ -71,6 +76,7 @@ import android.os.Parcelable;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserHandle;
+import android.os.UserManager;
import android.provider.Settings;
import android.text.BidiFormatter;
import android.text.SpannableStringBuilder;
@@ -5046,6 +5052,18 @@ public class Notification implements Parcelable
}
// Note: This assumes that the current user can read the profile badge of the
// originating user.
+ DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
+ return dpm.getDrawable(
+ getUpdatableProfileBadgeId(), SOLID_COLORED, NOTIFICATION,
+ this::getDefaultProfileBadgeDrawable);
+ }
+
+ private String getUpdatableProfileBadgeId() {
+ return mContext.getSystemService(UserManager.class).isManagedProfile()
+ ? WORK_PROFILE_ICON : UNDEFINED;
+ }
+
+ private Drawable getDefaultProfileBadgeDrawable() {
return mContext.getPackageManager().getUserBadgeForDensityNoBackground(
new UserHandle(mContext.getUserId()), 0);
}
diff --git a/core/java/android/app/PropertyInvalidatedCache.java b/core/java/android/app/PropertyInvalidatedCache.java
index ec8d989c61d4..715de14345f7 100644
--- a/core/java/android/app/PropertyInvalidatedCache.java
+++ b/core/java/android/app/PropertyInvalidatedCache.java
@@ -16,7 +16,11 @@
package android.app;
+import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -27,9 +31,10 @@ import android.text.TextUtils;
import android.util.Log;
import com.android.internal.annotations.GuardedBy;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.FastPrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
@@ -104,17 +109,21 @@ import java.util.concurrent.atomic.AtomicLong;
* <pre>
* public class ActivityThread {
* ...
+ * private final PropertyInvalidatedCache.QueryHandler&lt;Integer, Birthday&gt; mBirthdayQuery =
+ * new PropertyInvalidatedCache.QueryHandler&lt;Integer, Birthday&gt;() {
+ * {@literal @}Override
+ * public Birthday apply(Integer) {
+ * return GetService("birthdayd").getUserBirthday(userId);
+ * }
+ * };
* private static final int BDAY_CACHE_MAX = 8; // Maximum birthdays to cache
* private static final String BDAY_CACHE_KEY = "cache_key.birthdayd";
* private final PropertyInvalidatedCache&lt;Integer, Birthday%&gt; mBirthdayCache = new
- * PropertyInvalidatedCache&lt;Integer, Birthday%&gt;(BDAY_CACHE_MAX, BDAY_CACHE_KEY) {
- * {@literal @}Override
- * protected Birthday recompute(Integer userId) {
- * return GetService("birthdayd").getUserBirthday(userId);
- * }
- * };
+ * PropertyInvalidatedCache&lt;Integer, Birthday%&gt;(
+ * BDAY_CACHE_MAX, MODULE_SYSTEM, "getUserBirthday", mBirthdayQuery);
+ *
* public void disableUserBirthdayCache() {
- * mBirthdayCache.disableLocal();
+ * mBirthdayCache.disableForCurrentProcess();
* }
* public void invalidateUserBirthdayCache() {
* mBirthdayCache.invalidateCache();
@@ -221,10 +230,124 @@ import java.util.concurrent.atomic.AtomicLong;
*
* @param <Query> The class used to index cache entries: must be hashable and comparable
* @param <Result> The class holding cache entries; use a boxed primitive if possible
- *
- * {@hide}
+ * @hide
*/
-public abstract class PropertyInvalidatedCache<Query, Result> {
+@SystemApi(client=SystemApi.Client.MODULE_LIBRARIES)
+@TestApi
+public class PropertyInvalidatedCache<Query, Result> {
+ /**
+ * This is a configuration class that customizes a cache instance.
+ * @hide
+ */
+ @SystemApi(client=SystemApi.Client.MODULE_LIBRARIES)
+ @TestApi
+ public static abstract class QueryHandler<Q,R> {
+ /**
+ * Compute a result given a query. The semantics are those of Functor.
+ */
+ public abstract @Nullable R apply(@NonNull Q query);
+
+ /**
+ * Return true if a query should not use the cache. The default implementation
+ * always uses the cache.
+ */
+ public boolean shouldBypassCache(@NonNull Q query) {
+ return false;
+ }
+ };
+
+ /**
+ * The system properties used by caches should be of the form <prefix>.<module>.<api>,
+ * where the prefix is "cache_key", the module is one of the constants below, and the
+ * api is any string. The ability to write the property (which happens during
+ * invalidation) depends on SELinux rules; these rules are defined against
+ * <prefix>.<module>. Therefore, the module chosen for a cache property must match
+ * the permissions granted to the processes that contain the corresponding caches.
+ * @hide
+ */
+ @IntDef(prefix = { "MODULE_" }, value = {
+ MODULE_TEST,
+ MODULE_SYSTEM,
+ MODULE_BLUETOOTH
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Module {}
+
+ /**
+ * The module used for unit tests and cts tests. It is expected that no process in
+ * the system has permissions to write properties with this module.
+ * @hide
+ */
+ @TestApi
+ public static final int MODULE_TEST = 0;
+
+ /**
+ * The module used for system server/framework caches. This is not visible outside
+ * the system processes.
+ * @hide
+ */
+ @TestApi
+ public static final int MODULE_SYSTEM = 1;
+
+ /**
+ * The module used for bluetooth caches.
+ * @hide
+ */
+ @SystemApi(client=SystemApi.Client.MODULE_LIBRARIES)
+ @TestApi
+ public static final int MODULE_BLUETOOTH = 2;
+
+ // A static array mapping module constants to strings.
+ private final static String[] sModuleNames =
+ { "test", "system_server", "bluetooth" };
+
+ /**
+ * Construct a system property that matches the rules described above. The module is
+ * one of the permitted values above. The API is a string that is a legal Java simple
+ * identifier. The api is modified to conform to the system property style guide by
+ * replacing every upper case letter with an underscore and the lower case equivalent.
+ * There is no requirement that the apiName be the name of an actual API.
+ *
+ * Be aware that SystemProperties has a maximum length which is private to the
+ * implementation. The current maximum is 92 characters. If this method creates a
+ * property name that is too long, SystemProperties.set() will fail without a good
+ * error message.
+ * @hide
+ */
+ @TestApi
+ public static @NonNull String createPropertyName(@Module int module, @NonNull String apiName) {
+ char[] api = apiName.toCharArray();
+ int upper = 0;
+ for (int i = 0; i < api.length; i++) {
+ if (Character.isUpperCase(api[i])) {
+ upper++;
+ }
+ }
+ char[] suffix = new char[api.length + upper];
+ int j = 0;
+ for (int i = 0; i < api.length; i++) {
+ if (Character.isJavaIdentifierPart(api[i])) {
+ if (Character.isUpperCase(api[i])) {
+ suffix[j++] = '_';
+ suffix[j++] = Character.toLowerCase(api[i]);
+ } else {
+ suffix[j++] = api[i];
+ }
+ } else {
+ throw new IllegalArgumentException("invalid api name");
+ }
+ }
+
+ String moduleName = null;
+ try {
+ moduleName = sModuleNames[module];
+ } catch (ArrayIndexOutOfBoundsException e) {
+ throw new IllegalArgumentException("invalid module " + module);
+ }
+
+ return "cache_key." + moduleName + "." + new String(suffix);
+ }
+
/**
* Reserved nonce values. Use isReservedNonce() to test for a reserved value. Note
* that all values cause the cache to be skipped.
@@ -335,6 +458,25 @@ public abstract class PropertyInvalidatedCache<Query, Result> {
*/
private final String mCacheName;
+ /**
+ * The function that computes a Result, given a Query. This function is called on a
+ * cache miss.
+ */
+ private QueryHandler<Query, Result> mComputer;
+
+ /**
+ * A default function that delegates to the deprecated recompute() method.
+ */
+ private static class DefaultComputer<Query, Result> extends QueryHandler<Query, Result> {
+ final PropertyInvalidatedCache<Query, Result> mCache;
+ DefaultComputer(PropertyInvalidatedCache<Query, Result> cache) {
+ mCache = cache;
+ }
+ public Result apply(Query query) {
+ return mCache.recompute(query);
+ }
+ }
+
@GuardedBy("mLock")
private final LinkedHashMap<Query, Result> mCache;
@@ -359,8 +501,13 @@ public abstract class PropertyInvalidatedCache<Query, Result> {
* property name. New clients should prefer the constructor that takes an explicit
* cache name.
*
+ * TODO(216112648): deprecate this as a public interface, in favor of the four-argument
+ * constructor.
+ *
* @param maxEntries Maximum number of entries to cache; LRU discard
* @param propertyName Name of the system property holding the cache invalidation nonce.
+ *
+ * @hide
*/
public PropertyInvalidatedCache(int maxEntries, @NonNull String propertyName) {
this(maxEntries, propertyName, propertyName);
@@ -369,32 +516,73 @@ public abstract class PropertyInvalidatedCache<Query, Result> {
/**
* Make a new property invalidated cache.
*
+ * TODO(216112648): deprecate this as a public interface, in favor of the four-argument
+ * constructor.
+ *
* @param maxEntries Maximum number of entries to cache; LRU discard
* @param propertyName Name of the system property holding the cache invalidation nonce
* @param cacheName Name of this cache in debug and dumpsys
+ * @hide
*/
public PropertyInvalidatedCache(int maxEntries, @NonNull String propertyName,
@NonNull String cacheName) {
mPropertyName = propertyName;
mCacheName = cacheName;
mMaxEntries = maxEntries;
- mCache = new LinkedHashMap<Query, Result>(
+ mComputer = new DefaultComputer<>(this);
+ mCache = createMap();
+ registerCache();
+ }
+
+ /**
+ * Make a new property invalidated cache. The key is computed from the module and api
+ * parameters.
+ *
+ * @param maxEntries Maximum number of entries to cache; LRU discard
+ * @param module The module under which the cache key should be placed.
+ * @param api The api this cache front-ends. The api must be a Java identifier but
+ * need not be an actual api.
+ * @param cacheName Name of this cache in debug and dumpsys
+ * @param computer The code to compute values that are not in the cache.
+ * @hide
+ */
+ @SystemApi(client=SystemApi.Client.MODULE_LIBRARIES)
+ @TestApi
+ public PropertyInvalidatedCache(int maxEntries, @Module int module, @NonNull String api,
+ @NonNull String cacheName, @NonNull QueryHandler<Query, Result> computer) {
+ mPropertyName = createPropertyName(module, api);
+ mCacheName = cacheName;
+ mMaxEntries = maxEntries;
+ mComputer = computer;
+ mCache = createMap();
+ registerCache();
+ }
+
+ // Create a map. This should be called only from the constructor.
+ private LinkedHashMap<Query, Result> createMap() {
+ return new LinkedHashMap<Query, Result>(
2 /* start small */,
0.75f /* default load factor */,
true /* LRU access order */) {
+ @GuardedBy("mLock")
@Override
protected boolean removeEldestEntry(Map.Entry eldest) {
final int size = size();
if (size > mHighWaterMark) {
mHighWaterMark = size;
}
- if (size > maxEntries) {
+ if (size > mMaxEntries) {
mMissOverflow++;
return true;
}
return false;
}
- };
+ };
+ }
+
+ // Register the map in the global list. If the cache is disabled globally, disable it
+ // now.
+ private void registerCache() {
synchronized (sCorkLock) {
sCaches.put(this, null);
if (sDisabledKeys.contains(mCacheName)) {
@@ -418,8 +606,9 @@ public abstract class PropertyInvalidatedCache<Query, Result> {
/**
* Enable or disable testing. The testing property map is cleared every time this
* method is called.
+ * @hide
*/
- @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ @TestApi
public static void setTestMode(boolean mode) {
sTesting = mode;
synchronized (sTestingPropertyMap) {
@@ -431,13 +620,22 @@ public abstract class PropertyInvalidatedCache<Query, Result> {
* Enable testing the specific cache key. Only keys in the map are subject to testing.
* There is no method to stop testing a property name. Just disable the test mode.
*/
- @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
- public static void testPropertyName(@NonNull String name) {
+ private static void testPropertyName(@NonNull String name) {
synchronized (sTestingPropertyMap) {
sTestingPropertyMap.put(name, (long) NONCE_UNSET);
}
}
+ /**
+ * Enable testing the specific cache key. Only keys in the map are subject to testing.
+ * There is no method to stop testing a property name. Just disable the test mode.
+ * @hide
+ */
+ @TestApi
+ public void testPropertyName() {
+ testPropertyName(mPropertyName);
+ }
+
// Read the system property associated with the current cache. This method uses the
// handle for faster reading.
private long getCurrentNonce() {
@@ -490,6 +688,9 @@ public abstract class PropertyInvalidatedCache<Query, Result> {
/**
* Forget all cached values.
+ * TODO(216112648) remove this as a public API. Clients should invalidate caches, not clear
+ * them.
+ * @hide
*/
public final void clear() {
synchronized (mLock) {
@@ -505,22 +706,29 @@ public abstract class PropertyInvalidatedCache<Query, Result> {
* Fetch a result from scratch in case it's not in the cache at all. Called unlocked: may
* block. If this function returns null, the result of the cache query is null. There is no
* "negative cache" in the query: we don't cache null results at all.
+ * TODO(216112648): deprecate this as a public interface, in favor of an instance of
+ * QueryHandler.
+ * @hide
*/
- public abstract @NonNull Result recompute(@NonNull Query query);
+ public Result recompute(@NonNull Query query) {
+ return mComputer.apply(query);
+ }
/**
* Return true if the query should bypass the cache. The default behavior is to
* always use the cache but the method can be overridden for a specific class.
+ * TODO(216112648): deprecate this as a public interface, in favor of an instance of
+ * QueryHandler.
+ * @hide
*/
public boolean bypass(@NonNull Query query) {
- return false;
+ return mComputer.shouldBypassCache(query);
}
/**
- * Determines if a pair of responses are considered equal. Used to determine whether a
- * cache is inadvertently returning stale results when VERIFY is set to true. Some
- * existing clients override this method, but it is now deprecated in favor of a valid
- * equals() method on the Result class.
+ * Determines if a pair of responses are considered equal. Used to determine whether
+ * a cache is inadvertently returning stale results when VERIFY is set to true.
+ * @hide
*/
public boolean resultEquals(Result cachedResult, Result fetchedResult) {
// If a service crashes and returns a null result, the cached value remains valid.
@@ -541,6 +749,7 @@ public abstract class PropertyInvalidatedCache<Query, Result> {
* the meantime (if the nonce has changed in the meantime, we drop the cache and try the
* whole query again), or 3) null, which causes the old value to be removed from the cache
* and null to be returned as the result of the cache query.
+ * @hide
*/
protected Result refresh(Result oldResult, Query query) {
return oldResult;
@@ -551,7 +760,7 @@ public abstract class PropertyInvalidatedCache<Query, Result> {
* testing. To disable a cache in normal code, use disableLocal().
* @hide
*/
- @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ @TestApi
public final void disableInstance() {
synchronized (mLock) {
mDisabled = true;
@@ -580,9 +789,10 @@ public abstract class PropertyInvalidatedCache<Query, Result> {
* disabled remain disabled (the "disabled" setting is sticky). However, new caches
* with this name will not be disabled. It is not an error if the cache name is not
* found in the list of disabled caches.
+ * @hide
*/
- @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
- public final void clearDisableLocal() {
+ @TestApi
+ public final void forgetDisableLocal() {
synchronized (sCorkLock) {
sDisabledKeys.remove(mCacheName);
}
@@ -592,25 +802,43 @@ public abstract class PropertyInvalidatedCache<Query, Result> {
* Disable this cache in the current process, and all other caches that use the same
* name. This does not affect caches that have a different name but use the same
* property.
+ * TODO(216112648) Remove this in favor of disableForCurrentProcess().
+ * @hide
*/
public final void disableLocal() {
+ disableForCurrentProcess();
+ }
+
+ /**
+ * Disable this cache in the current process, and all other caches that use the same
+ * name. This does not affect caches that have a different name but use the same
+ * property.
+ * @hide
+ */
+ @SystemApi(client=SystemApi.Client.MODULE_LIBRARIES)
+ @TestApi
+ public final void disableForCurrentProcess() {
disableLocal(mCacheName);
}
/**
- * Return whether the cache is disabled in this process.
+ * Return whether a cache instance is disabled.
+ * @hide
*/
- @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
- public final boolean isDisabledLocal() {
+ @TestApi
+ public final boolean isDisabled() {
return mDisabled || !sEnabled;
}
/**
* Get a value from the cache or recompute it.
+ * @hide
*/
- public @NonNull Result query(@NonNull Query query) {
+ @SystemApi(client=SystemApi.Client.MODULE_LIBRARIES)
+ @TestApi
+ public final @Nullable Result query(@NonNull Query query) {
// Let access to mDisabled race: it's atomic anyway.
- long currentNonce = (!isDisabledLocal()) ? getCurrentNonce() : NONCE_DISABLED;
+ long currentNonce = (!isDisabled()) ? getCurrentNonce() : NONCE_DISABLED;
if (bypass(query)) {
currentNonce = NONCE_BYPASS;
}
@@ -724,8 +952,9 @@ public abstract class PropertyInvalidatedCache<Query, Result> {
* When multiple caches share a single property value, using an instance method on one of
* the cache objects to invalidate all of the cache objects becomes confusing and you should
* just use the static version of this function.
+ * @hide
*/
- @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ @TestApi
public final void disableSystemWide() {
disableSystemWide(mPropertyName);
}
@@ -746,16 +975,33 @@ public abstract class PropertyInvalidatedCache<Query, Result> {
/**
* Non-static convenience version of invalidateCache() for situations in which only a single
* PropertyInvalidatedCache is keyed on a particular property value.
+ * @hide
*/
+ @SystemApi(client=SystemApi.Client.MODULE_LIBRARIES)
+ @TestApi
public final void invalidateCache() {
invalidateCache(mPropertyName);
}
/**
+ * Invalidate caches in all processes that are keyed for the module and api.
+ * @hide
+ */
+ @SystemApi(client=SystemApi.Client.MODULE_LIBRARIES)
+ @TestApi
+ public static void invalidateCache(@Module int module, @NonNull String api) {
+ invalidateCache(createPropertyName(module, api));
+ }
+
+ /**
* Invalidate PropertyInvalidatedCache caches in all processes that are keyed on
* {@var name}. This function is synchronous: caches are invalidated upon return.
*
+ * TODO(216112648) make this method private in favor of the two-argument (module, api)
+ * override.
+ *
* @param name Name of the cache-key property to invalidate
+ * @hide
*/
public static void invalidateCache(@NonNull String name) {
if (!sEnabled) {
@@ -824,6 +1070,7 @@ public abstract class PropertyInvalidatedCache<Query, Result> {
* corkInvalidations() and uncorkInvalidations() must be called in pairs.
*
* @param name Name of the cache-key property to cork
+ * @hide
*/
public static void corkInvalidations(@NonNull String name) {
if (!sEnabled) {
@@ -871,6 +1118,7 @@ public abstract class PropertyInvalidatedCache<Query, Result> {
* transitioning it to normal operation (unless explicitly disabled system-wide).
*
* @param name Name of the cache-key property to uncork
+ * @hide
*/
public static void uncorkInvalidations(@NonNull String name) {
if (!sEnabled) {
@@ -916,6 +1164,7 @@ public abstract class PropertyInvalidatedCache<Query, Result> {
* The auto-cork delay is configurable but it should not be too long. The purpose of
* the delay is to minimize the number of times a server writes to the system property
* when invalidating the cache. One write every 50ms does not hurt system performance.
+ * @hide
*/
public static final class AutoCorker {
public static final int DEFAULT_AUTO_CORK_DELAY_MS = 50;
@@ -1043,6 +1292,8 @@ public abstract class PropertyInvalidatedCache<Query, Result> {
* Return the query as a string, to be used in debug messages. New clients should not
* override this, but should instead add the necessary toString() method to the Query
* class.
+ * TODO(216112648) add a method in the QueryHandler and deprecate this API.
+ * @hide
*/
protected @NonNull String queryToString(@NonNull Query query) {
return Objects.toString(query);
@@ -1054,8 +1305,9 @@ public abstract class PropertyInvalidatedCache<Query, Result> {
* process does not have privileges to write SystemProperties. Once disabled it is not
* possible to re-enable caching in the current process. If a client wants to
* temporarily disable caching, use the corking mechanism.
+ * @hide
*/
- @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ @TestApi
public static void disableForTestMode() {
Log.d(TAG, "disabling all caches in the process");
sEnabled = false;
@@ -1064,10 +1316,11 @@ public abstract class PropertyInvalidatedCache<Query, Result> {
/**
* Report the disabled status of this cache instance. The return value does not
* reflect status of the property key.
+ * @hide
*/
- @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ @TestApi
public boolean getDisabledState() {
- return isDisabledLocal();
+ return isDisabled();
}
/**
@@ -1133,7 +1386,8 @@ public abstract class PropertyInvalidatedCache<Query, Result> {
}
/**
- * Dumps the contents of every cache in the process to the provided ParcelFileDescriptor.
+ * Dumps contents of every cache in the process to the provided ParcelFileDescriptor.
+ * @hide
*/
public static void dumpCacheInfo(@NonNull ParcelFileDescriptor pfd, @NonNull String[] args) {
ArrayList<PropertyInvalidatedCache> activeCaches;
@@ -1174,6 +1428,7 @@ public abstract class PropertyInvalidatedCache<Query, Result> {
/**
* Trim memory by clearing all the caches.
+ * @hide
*/
public static void onTrimMemory() {
for (PropertyInvalidatedCache pic : getActiveCaches()) {
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index f360bbed0156..2a1883d5e0bb 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -65,7 +65,6 @@ import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.WeakHashMap;
-import java.util.function.Consumer;
import java.util.function.Function;
/** @hide */
@@ -343,6 +342,25 @@ public class ResourcesManager {
return dm;
}
+ /**
+ * Like getDisplayMetrics, but will adjust the result based on the display information in
+ * config. This is used to make sure that the global configuration matches the activity's
+ * apparent display.
+ */
+ private DisplayMetrics getDisplayMetrics(Configuration config) {
+ final DisplayManagerGlobal displayManagerGlobal = DisplayManagerGlobal.getInstance();
+ final DisplayMetrics dm = new DisplayMetrics();
+ final DisplayInfo displayInfo = displayManagerGlobal != null
+ ? displayManagerGlobal.getDisplayInfo(mResDisplayId) : null;
+ if (displayInfo != null) {
+ displayInfo.getAppMetrics(dm,
+ DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS.getCompatibilityInfo(), config);
+ } else {
+ dm.setToDefaults();
+ }
+ return dm;
+ }
+
private static void applyDisplayMetricsToConfiguration(@NonNull DisplayMetrics dm,
@NonNull Configuration config) {
config.touchscreen = Configuration.TOUCHSCREEN_NOTOUCH;
@@ -1311,12 +1329,6 @@ public class ResourcesManager {
public final boolean applyConfigurationToResources(@NonNull Configuration config,
@Nullable CompatibilityInfo compat) {
- return applyConfigurationToResources(config, compat, null /* adjustments */);
- }
-
- /** Applies the global configuration to the managed resources. */
- public final boolean applyConfigurationToResources(@NonNull Configuration config,
- @Nullable CompatibilityInfo compat, @Nullable DisplayAdjustments adjustments) {
synchronized (mLock) {
try {
Trace.traceBegin(Trace.TRACE_TAG_RESOURCES,
@@ -1346,12 +1358,7 @@ public class ResourcesManager {
applyAllPendingAppInfoUpdates();
}
- DisplayMetrics displayMetrics = getDisplayMetrics();
- if (adjustments != null) {
- // Currently the only case where the adjustment takes effect is to simulate
- // placing an app in a rotated display.
- adjustments.adjustGlobalAppMetrics(displayMetrics);
- }
+ final DisplayMetrics displayMetrics = getDisplayMetrics(config);
Resources.updateSystemConfiguration(config, displayMetrics, compat);
ApplicationPackageManager.configurationChanged();
@@ -1591,41 +1598,6 @@ public class ResourcesManager {
}
}
- /**
- * Overrides the display adjustments of all resources which are associated with the given token.
- *
- * @param token The token that owns the resources.
- * @param override The operation to override the existing display adjustments. If it is null,
- * the override adjustments will be cleared.
- * @return {@code true} if the override takes effect.
- */
- public boolean overrideTokenDisplayAdjustments(IBinder token,
- @Nullable Consumer<DisplayAdjustments> override) {
- boolean handled = false;
- synchronized (mLock) {
- final ActivityResources tokenResources = mActivityResourceReferences.get(token);
- if (tokenResources == null) {
- return false;
- }
- final ArrayList<ActivityResource> resourcesRefs = tokenResources.activityResources;
- for (int i = resourcesRefs.size() - 1; i >= 0; i--) {
- final ActivityResource activityResource = resourcesRefs.get(i);
- if (activityResource.overrideDisplayId != null) {
- // This resource overrides the display of the token so we should not be
- // modifying its display adjustments here.
- continue;
- }
-
- final Resources res = activityResource.resources.get();
- if (res != null) {
- res.overrideDisplayAdjustments(override);
- handled = true;
- }
- }
- }
- return handled;
- }
-
private class UpdateHandler implements Resources.UpdateCallbacks {
/**
diff --git a/core/java/android/app/SyncNotedAppOp.java b/core/java/android/app/SyncNotedAppOp.java
index 7c0c08a7fc35..f156b30d5050 100644
--- a/core/java/android/app/SyncNotedAppOp.java
+++ b/core/java/android/app/SyncNotedAppOp.java
@@ -40,7 +40,8 @@ import com.android.internal.util.DataClass;
@DataClass(
genEqualsHashCode = true,
genAidl = true,
- genConstructor = false
+ genConstructor = false,
+ genToString = true
)
@DataClass.Suppress({"getOpCode", "getOpMode"})
public final class SyncNotedAppOp implements Parcelable {
@@ -118,6 +119,10 @@ public final class SyncNotedAppOp implements Parcelable {
return mOpMode;
}
+ private String opCodeToString() {
+ return getOp();
+ }
+
// Code below generated by codegen v1.0.23.
@@ -153,6 +158,20 @@ public final class SyncNotedAppOp implements Parcelable {
@Override
@DataClass.Generated.Member
+ public String toString() {
+ // You can override field toString logic by defining methods like:
+ // String fieldNameToString() { ... }
+
+ return "SyncNotedAppOp { " +
+ "opMode = " + mOpMode + ", " +
+ "opCode = " + opCodeToString() + ", " +
+ "attributionTag = " + mAttributionTag + ", " +
+ "packageName = " + mPackageName +
+ " }";
+ }
+
+ @Override
+ @DataClass.Generated.Member
public boolean equals(@Nullable Object o) {
// You can override field equality logic by defining either of the methods like:
// boolean fieldNameEquals(SyncNotedAppOp other) { ... }
@@ -245,10 +264,10 @@ public final class SyncNotedAppOp implements Parcelable {
};
@DataClass.Generated(
- time = 1619711733947L,
+ time = 1643320427700L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/app/SyncNotedAppOp.java",
- inputSignatures = "private final int mOpMode\nprivate final @android.annotation.IntRange int mOpCode\nprivate final @android.annotation.Nullable java.lang.String mAttributionTag\nprivate final @android.annotation.NonNull java.lang.String mPackageName\npublic @android.annotation.NonNull java.lang.String getOp()\npublic int getOpMode()\nclass SyncNotedAppOp extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genAidl=true, genConstructor=false)")
+ inputSignatures = "private final int mOpMode\nprivate final @android.annotation.IntRange int mOpCode\nprivate final @android.annotation.Nullable java.lang.String mAttributionTag\nprivate final @android.annotation.NonNull java.lang.String mPackageName\npublic @android.annotation.NonNull java.lang.String getOp()\npublic int getOpMode()\nprivate java.lang.String opCodeToString()\nclass SyncNotedAppOp extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genAidl=true, genConstructor=false, genToString=true)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index dd832c81d529..bbdd7050cb07 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -1034,15 +1034,14 @@ public final class SystemServiceRegistry {
}});
registerService(Context.PERSISTENT_DATA_BLOCK_SERVICE, PersistentDataBlockManager.class,
- new CachedServiceFetcher<PersistentDataBlockManager>() {
+ new StaticServiceFetcher<PersistentDataBlockManager>() {
@Override
- public PersistentDataBlockManager createService(ContextImpl ctx)
- throws ServiceNotFoundException {
+ public PersistentDataBlockManager createService() throws ServiceNotFoundException {
IBinder b = ServiceManager.getServiceOrThrow(Context.PERSISTENT_DATA_BLOCK_SERVICE);
IPersistentDataBlockService persistentDataBlockService =
IPersistentDataBlockService.Stub.asInterface(b);
if (persistentDataBlockService != null) {
- return new PersistentDataBlockManager(ctx, persistentDataBlockService);
+ return new PersistentDataBlockManager(persistentDataBlockService);
} else {
// not supported
return null;
diff --git a/core/java/android/app/TaskInfo.java b/core/java/android/app/TaskInfo.java
index 18f9379c4111..3d2c03dc4e24 100644
--- a/core/java/android/app/TaskInfo.java
+++ b/core/java/android/app/TaskInfo.java
@@ -214,6 +214,12 @@ public class TaskInfo {
public boolean topActivityInSizeCompat;
/**
+ * Whether the direct top activity is eligible for letterbox education.
+ * @hide
+ */
+ public boolean topActivityEligibleForLetterboxEducation;
+
+ /**
* Whether this task is resizable. Unlike {@link #resizeMode} (which is what the top activity
* supports), this is what the system actually uses for resizability based on other policy and
* developer options.
@@ -398,7 +404,8 @@ public class TaskInfo {
/** @hide */
public boolean hasCompatUI() {
- return hasCameraCompatControl() || topActivityInSizeCompat;
+ return hasCameraCompatControl() || topActivityInSizeCompat
+ || topActivityEligibleForLetterboxEducation;
}
/**
@@ -460,6 +467,8 @@ public class TaskInfo {
return displayId == that.displayId
&& taskId == that.taskId
&& topActivityInSizeCompat == that.topActivityInSizeCompat
+ && topActivityEligibleForLetterboxEducation
+ == that.topActivityEligibleForLetterboxEducation
&& cameraCompatControlState == that.cameraCompatControlState
// Bounds are important if top activity has compat controls.
&& (!hasCompatUI() || configuration.windowConfiguration.getBounds()
@@ -507,6 +516,7 @@ public class TaskInfo {
isVisible = source.readBoolean();
isSleeping = source.readBoolean();
topActivityInSizeCompat = source.readBoolean();
+ topActivityEligibleForLetterboxEducation = source.readBoolean();
mTopActivityLocusId = source.readTypedObject(LocusId.CREATOR);
displayAreaFeatureId = source.readInt();
cameraCompatControlState = source.readInt();
@@ -551,6 +561,7 @@ public class TaskInfo {
dest.writeBoolean(isVisible);
dest.writeBoolean(isSleeping);
dest.writeBoolean(topActivityInSizeCompat);
+ dest.writeBoolean(topActivityEligibleForLetterboxEducation);
dest.writeTypedObject(mTopActivityLocusId, flags);
dest.writeInt(displayAreaFeatureId);
dest.writeInt(cameraCompatControlState);
@@ -585,6 +596,8 @@ public class TaskInfo {
+ " isVisible=" + isVisible
+ " isSleeping=" + isSleeping
+ " topActivityInSizeCompat=" + topActivityInSizeCompat
+ + " topActivityEligibleForLetterboxEducation= "
+ + topActivityEligibleForLetterboxEducation
+ " locusId=" + mTopActivityLocusId
+ " displayAreaFeatureId=" + displayAreaFeatureId
+ " cameraCompatControlState="
diff --git a/core/java/android/app/WindowConfiguration.java b/core/java/android/app/WindowConfiguration.java
index 4ff7924c4b98..b791f05c1076 100644
--- a/core/java/android/app/WindowConfiguration.java
+++ b/core/java/android/app/WindowConfiguration.java
@@ -37,6 +37,7 @@ import android.util.proto.ProtoInputStream;
import android.util.proto.ProtoOutputStream;
import android.util.proto.WireTypeMismatchException;
import android.view.DisplayInfo;
+import android.view.Surface;
import android.view.WindowManager;
import java.io.IOException;
@@ -74,6 +75,13 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu
private final Rect mMaxBounds = new Rect();
/**
+ * The rotation of this window's apparent display. This can differ from mRotation in some
+ * situations (like letterbox).
+ */
+ @Surface.Rotation
+ private int mDisplayRotation = ROTATION_UNDEFINED;
+
+ /**
* The current rotation of this window container relative to the default
* orientation of the display it is on (regardless of how deep in the hierarchy
* it is). It is used by the configuration hierarchy to apply rotation-dependent
@@ -196,6 +204,9 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu
/** Bit that indicates that the {@link #mDisplayWindowingMode} changed.
* @hide */
public static final int WINDOW_CONFIG_DISPLAY_WINDOWING_MODE = 1 << 7;
+ /** Bit that indicates that the apparent-display changed.
+ * @hide */
+ public static final int WINDOW_CONFIG_DISPLAY_ROTATION = 1 << 8;
/** @hide */
@IntDef(flag = true, prefix = { "WINDOW_CONFIG_" }, value = {
@@ -207,6 +218,7 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu
WINDOW_CONFIG_ALWAYS_ON_TOP,
WINDOW_CONFIG_ROTATION,
WINDOW_CONFIG_DISPLAY_WINDOWING_MODE,
+ WINDOW_CONFIG_DISPLAY_ROTATION,
})
public @interface WindowConfig {}
@@ -237,6 +249,7 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu
dest.writeInt(mAlwaysOnTop);
dest.writeInt(mRotation);
dest.writeInt(mDisplayWindowingMode);
+ dest.writeInt(mDisplayRotation);
}
/** @hide */
@@ -249,6 +262,7 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu
mAlwaysOnTop = source.readInt();
mRotation = source.readInt();
mDisplayWindowingMode = source.readInt();
+ mDisplayRotation = source.readInt();
}
@Override
@@ -318,6 +332,14 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu
}
/**
+ * Sets the apparent display cutout.
+ * @hide
+ */
+ public void setDisplayRotation(@Surface.Rotation int rotation) {
+ mDisplayRotation = rotation;
+ }
+
+ /**
* Sets whether this window should be always on top.
* @param alwaysOnTop {@code true} to set window always on top, otherwise {@code false}
* @hide
@@ -359,6 +381,14 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu
return mMaxBounds;
}
+ /**
+ * @see #setDisplayRotation
+ * @hide
+ */
+ public @Surface.Rotation int getDisplayRotation() {
+ return mDisplayRotation;
+ }
+
public int getRotation() {
return mRotation;
}
@@ -413,6 +443,7 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu
setBounds(other.mBounds);
setAppBounds(other.mAppBounds);
setMaxBounds(other.mMaxBounds);
+ setDisplayRotation(other.mDisplayRotation);
setWindowingMode(other.mWindowingMode);
setActivityType(other.mActivityType);
setAlwaysOnTop(other.mAlwaysOnTop);
@@ -431,6 +462,7 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu
setAppBounds(null);
setBounds(null);
setMaxBounds(null);
+ setDisplayRotation(ROTATION_UNDEFINED);
setWindowingMode(WINDOWING_MODE_UNDEFINED);
setActivityType(ACTIVITY_TYPE_UNDEFINED);
setAlwaysOnTop(ALWAYS_ON_TOP_UNDEFINED);
@@ -485,6 +517,11 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu
changed |= WINDOW_CONFIG_DISPLAY_WINDOWING_MODE;
setDisplayWindowingMode(delta.mDisplayWindowingMode);
}
+ if (delta.mDisplayRotation != ROTATION_UNDEFINED
+ && delta.mDisplayRotation != mDisplayRotation) {
+ changed |= WINDOW_CONFIG_DISPLAY_ROTATION;
+ setDisplayRotation(delta.mDisplayRotation);
+ }
return changed;
}
@@ -517,6 +554,9 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu
if ((mask & WINDOW_CONFIG_DISPLAY_WINDOWING_MODE) != 0) {
setDisplayWindowingMode(delta.mDisplayWindowingMode);
}
+ if ((mask & WINDOW_CONFIG_DISPLAY_ROTATION) != 0) {
+ setDisplayRotation(delta.mDisplayRotation);
+ }
}
/**
@@ -573,6 +613,11 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu
changes |= WINDOW_CONFIG_DISPLAY_WINDOWING_MODE;
}
+ if ((compareUndefined || other.mDisplayRotation != ROTATION_UNDEFINED)
+ && mDisplayRotation != other.mDisplayRotation) {
+ changes |= WINDOW_CONFIG_DISPLAY_ROTATION;
+ }
+
return changes;
}
@@ -620,8 +665,11 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu
if (n != 0) return n;
n = mRotation - that.mRotation;
if (n != 0) return n;
+
n = mDisplayWindowingMode - that.mDisplayWindowingMode;
if (n != 0) return n;
+ n = mDisplayRotation - that.mDisplayRotation;
+ if (n != 0) return n;
// if (n != 0) return n;
return n;
@@ -650,6 +698,7 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu
result = 31 * result + mAlwaysOnTop;
result = 31 * result + mRotation;
result = 31 * result + mDisplayWindowingMode;
+ result = 31 * result + mDisplayRotation;
return result;
}
@@ -659,6 +708,8 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu
return "{ mBounds=" + mBounds
+ " mAppBounds=" + mAppBounds
+ " mMaxBounds=" + mMaxBounds
+ + " mDisplayRotation=" + (mRotation == ROTATION_UNDEFINED
+ ? "undefined" : rotationToString(mDisplayRotation))
+ " mWindowingMode=" + windowingModeToString(mWindowingMode)
+ " mDisplayWindowingMode=" + windowingModeToString(mDisplayWindowingMode)
+ " mActivityType=" + activityTypeToString(mActivityType)
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 3960f4e1518e..a4227a4d3074 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -454,6 +454,52 @@ public class DevicePolicyManager {
* <li>{@link #EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE}, optional</li>
* </ul>
*
+ * <p>Once the device admin app is set as the device owner, the following APIs are available for
+ * managing polices on the device:
+ * <ul>
+ * <li>{@link #isDeviceManaged()}</li>
+ * <li>{@link #isUninstallBlocked(ComponentName, String)}</li>
+ * <li>{@link #setUninstallBlocked(ComponentName, String, boolean)}</li>
+ * <li>{@link #setUserControlDisabledPackages(ComponentName, List)}</li>
+ * <li>{@link #getUserControlDisabledPackages(ComponentName)}</li>
+ * <li>{@link #setOrganizationName(ComponentName, CharSequence)}</li>
+ * <li>{@link #setShortSupportMessage(ComponentName, CharSequence)}</li>
+ * <li>{@link #isBackupServiceEnabled(ComponentName)}</li>
+ * <li>{@link #setBackupServiceEnabled(ComponentName, boolean)}</li>
+ * <li>{@link #isLockTaskPermitted(String)}</li>
+ * <li>{@link #setLockTaskFeatures(ComponentName, int)}, where the following lock task features
+ * can be set (otherwise a {@link SecurityException} will be thrown):</li>
+ * <ul>
+ * <li>{@link #LOCK_TASK_FEATURE_SYSTEM_INFO}</li>
+ * <li>{@link #LOCK_TASK_FEATURE_KEYGUARD}</li>
+ * <li>{@link #LOCK_TASK_FEATURE_HOME}</li>
+ * <li>{@link #LOCK_TASK_FEATURE_GLOBAL_ACTIONS}</li>
+ * <li>{@link #LOCK_TASK_FEATURE_NOTIFICATIONS}</li>
+ * </ul>
+ * <li>{@link #setLockTaskPackages(ComponentName, String[])}</li>
+ * <li>{@link #addPersistentPreferredActivity(ComponentName, IntentFilter, ComponentName)}</li>
+ * <li>{@link #clearPackagePersistentPreferredActivities(ComponentName, String)} </li>
+ * <li>{@link #wipeData(int)}</li>
+ * <li>{@link #isDeviceOwnerApp(String)}</li>
+ * <li>{@link #clearDeviceOwnerApp(String)}</li>
+ * <li>{@link #setPermissionGrantState(ComponentName, String, String, int)}, where
+ * {@link permission#READ_PHONE_STATE} is the <b>only</b> permission that can be
+ * {@link #PERMISSION_GRANT_STATE_GRANTED}, {@link #PERMISSION_GRANT_STATE_DENIED}, or
+ * {@link #PERMISSION_GRANT_STATE_DEFAULT} and can <b>only</b> be applied to the device admin
+ * app (otherwise a {@link SecurityException} will be thrown)</li>
+ * <li>{@link #addUserRestriction(ComponentName, String)}, where the following user restrictions
+ * are permitted (otherwise a {@link SecurityException} will be thrown):</li>
+ * <ul>
+ * <li>{@link UserManager#DISALLOW_ADD_USER}</li>
+ * <li>{@link UserManager#DISALLOW_DEBUGGING_FEATURES}</li>
+ * <li>{@link UserManager#DISALLOW_INSTALL_UNKNOWN_SOURCES}</li>
+ * <li>{@link UserManager#DISALLOW_SAFE_BOOT}</li>
+ * <li>{@link UserManager#DISALLOW_CONFIG_DATE_TIME}</li>
+ * <li>{@link UserManager#DISALLOW_OUTGOING_CALLS}</li>
+ * </ul>
+ * <li>{@link #clearUserRestriction(ComponentName, String)}</li>
+ * </ul>
+ *
* @hide
*/
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
@@ -587,7 +633,7 @@ public class DevicePolicyManager {
* #ACTION_ROLE_HOLDER_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE} to signal that an update
* to the role holder is required.
*
- * <p>This result code must be accompanied by {@link #EXTRA_ROLE_HOLDER_STATE}.
+ * <p>This result code can be accompanied by {@link #EXTRA_ROLE_HOLDER_STATE}.
*
* @hide
*/
@@ -595,15 +641,19 @@ public class DevicePolicyManager {
public static final int RESULT_UPDATE_ROLE_HOLDER = 2;
/**
- * A {@link Bundle} extra which describes the state of the role holder at the time when it
- * returns {@link #RESULT_UPDATE_ROLE_HOLDER}.
+ * A {@link PersistableBundle} extra which the role holder can use to describe its own state
+ * when it returns {@link #RESULT_UPDATE_ROLE_HOLDER}.
*
- * <p>After the update completes, the role holder's {@link
- * #ACTION_ROLE_HOLDER_PROVISION_MANAGED_PROFILE} or {@link
+ * <p>If {@link #RESULT_UPDATE_ROLE_HOLDER} was accompanied by this extra, after the update
+ * completes, the role holder's {@link #ACTION_ROLE_HOLDER_PROVISION_MANAGED_PROFILE} or {@link
* #ACTION_ROLE_HOLDER_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE} intent will be relaunched,
* which will contain this extra. It is the role holder's responsibility to restore its
* state from this extra.
*
+ * <p>The content of this {@link PersistableBundle} is entirely up to the role holder. It
+ * should contain anything the role holder needs to restore its original state when it gets
+ * restarted.
+ *
* @hide
*/
@SystemApi
@@ -14577,6 +14627,7 @@ public class DevicePolicyManager {
*
* @hide
*/
+ @TestApi
public void setDeviceOwnerType(@NonNull ComponentName admin,
@DeviceOwnerType int deviceOwnerType) {
throwIfParentInstance("setDeviceOwnerType");
@@ -14600,6 +14651,7 @@ public class DevicePolicyManager {
*
* @hide
*/
+ @TestApi
@DeviceOwnerType
public int getDeviceOwnerType(@NonNull ComponentName admin) {
throwIfParentInstance("getDeviceOwnerType");
@@ -14767,6 +14819,11 @@ public class DevicePolicyManager {
* The device may not connect to networks that do not meet the minimum security level.
* If the current network does not meet the minimum security level set, it will be disconnected.
*
+ * The following shows the Wi-Fi security levels from the lowest to the highest security level:
+ * {@link #WIFI_SECURITY_OPEN}
+ * {@link #WIFI_SECURITY_PERSONAL}
+ * {@link #WIFI_SECURITY_ENTERPRISE_EAP}
+ * {@link #WIFI_SECURITY_ENTERPRISE_192}
*
* @param level minimum security level
* @throws SecurityException if the caller is not a device owner or a profile owner on
@@ -14943,8 +15000,8 @@ public class DevicePolicyManager {
* <p>Also returns the drawable from {@code defaultDrawableLoader} if
* {@link DevicePolicyResources.Drawables#UNDEFINED} was passed.
*
- * <p>{@code defaultDrawableLoader} must return a non {@code null} {@link Drawable}, otherwise a
- * {@link NullPointerException} is thrown.
+ * <p>Calls to this API will not return {@code null} unless no updated drawable was found
+ * and the call to {@code defaultDrawableLoader} returned {@code null}.
*
* <p>This API uses the screen density returned from {@link Resources#getConfiguration()}, to
* set a different value use
@@ -14961,7 +15018,7 @@ public class DevicePolicyManager {
* @param defaultDrawableLoader To get the default drawable if no updated drawable was set for
* the provided params.
*/
- @NonNull
+ @Nullable
public Drawable getDrawable(
@NonNull @DevicePolicyResources.UpdatableDrawableId String drawableId,
@NonNull @DevicePolicyResources.UpdatableDrawableStyle String drawableStyle,
@@ -14977,8 +15034,8 @@ public class DevicePolicyManager {
* {@link #getDrawable(String, String, Callable)}
* if an override was set for that specific source.
*
- * <p>{@code defaultDrawableLoader} must return a non {@code null} {@link Drawable}, otherwise a
- * {@link NullPointerException} is thrown.
+ * <p>Calls to this API will not return {@code null} unless no updated drawable was found
+ * and the call to {@code defaultDrawableLoader} returned {@code null}.
*
* <p>Callers should register for {@link #ACTION_DEVICE_POLICY_RESOURCE_UPDATED} to get
* notified when a resource has been updated.
@@ -14989,7 +15046,7 @@ public class DevicePolicyManager {
* @param defaultDrawableLoader To get the default drawable if no updated drawable was set for
* the provided params.
*/
- @NonNull
+ @Nullable
public Drawable getDrawable(
@NonNull @DevicePolicyResources.UpdatableDrawableId String drawableId,
@NonNull @DevicePolicyResources.UpdatableDrawableStyle String drawableStyle,
@@ -15032,8 +15089,8 @@ public class DevicePolicyManager {
* Similar to {@link #getDrawable(String, String, Callable)}, but also accepts
* {@code density}. See {@link Resources#getDrawableForDensity(int, int, Resources.Theme)}.
*
- * <p>{@code defaultDrawableLoader} must return a non {@code null} {@link Drawable}, otherwise a
- * {@link NullPointerException} is thrown.
+ * <p>Calls to this API will not return {@code null} unless no updated drawable was found
+ * and the call to {@code defaultDrawableLoader} returned {@code null}.
*
* <p>Callers should register for {@link #ACTION_DEVICE_POLICY_RESOURCE_UPDATED} to get
* notified when a resource has been updated.
@@ -15046,7 +15103,7 @@ public class DevicePolicyManager {
* @param defaultDrawableLoader To get the default drawable if no updated drawable was set for
* the provided params.
*/
- @NonNull
+ @Nullable
public Drawable getDrawableForDensity(
@NonNull @DevicePolicyResources.UpdatableDrawableId String drawableId,
@NonNull @DevicePolicyResources.UpdatableDrawableStyle String drawableStyle,
@@ -15064,8 +15121,8 @@ public class DevicePolicyManager {
* Similar to {@link #getDrawable(String, String, String, Callable)}, but also accepts
* {@code density}. See {@link Resources#getDrawableForDensity(int, int, Resources.Theme)}.
*
- * <p>{@code defaultDrawableLoader} must return a non {@code null} {@link Drawable}, otherwise a
- * {@link NullPointerException} is thrown.
+ * <p>Calls to this API will not return {@code null} unless no updated drawable was found
+ * and the call to {@code defaultDrawableLoader} returned {@code null}.
*
* <p>Callers should register for {@link #ACTION_DEVICE_POLICY_RESOURCE_UPDATED} to get
* notified when a resource has been updated.
@@ -15079,7 +15136,7 @@ public class DevicePolicyManager {
* @param defaultDrawableLoader To get the default drawable if no updated drawable was set for
* the provided params.
*/
- @NonNull
+ @Nullable
public Drawable getDrawableForDensity(
@NonNull @DevicePolicyResources.UpdatableDrawableId String drawableId,
@NonNull @DevicePolicyResources.UpdatableDrawableStyle String drawableStyle,
@@ -15118,7 +15175,7 @@ public class DevicePolicyManager {
/**
* For each {@link DevicePolicyStringResource} item in {@code strings}, it updates the string
* resource for {@link DevicePolicyStringResource#getStringId()} to the string with ID
- * {@code callingPackageResourceId} (see {@link DevicePolicyResources.String}), meaning any
+ * {@code callingPackageResourceId} (see {@link DevicePolicyResources.Strings}), meaning any
* system UI surface calling {@link #getString} with {@code stringId} will get
* the new resource after this API is called.
*
@@ -15154,7 +15211,7 @@ public class DevicePolicyManager {
/**
* Removes the updated strings for the list of {@code stringIds} (see
- * {@link DevicePolicyResources.String}) that was previously set by calling {@link #setStrings},
+ * {@link DevicePolicyResources.Strings}) that was previously set by calling {@link #setStrings},
* meaning any subsequent calls to {@link #getString} for the provided IDs will
* return the default string from {@code defaultStringLoader}.
*
@@ -15179,14 +15236,14 @@ public class DevicePolicyManager {
/**
* Returns the appropriate updated string for the {@code stringId} (see
- * {@link DevicePolicyResources.String}) if one was set using
+ * {@link DevicePolicyResources.Strings}) if one was set using
* {@link #setStrings}, otherwise returns the string from {@code defaultStringLoader}.
*
* <p>Also returns the string from {@code defaultStringLoader} if
- * {@link DevicePolicyResources.String#INVALID_ID} was passed.
+ * {@link DevicePolicyResources.Strings#UNDEFINED} was passed.
*
- * <p>{@code defaultStringLoader} must return a non {@code null} {@link String}, otherwise a
- * {@link NullPointerException} is thrown.
+ * <p>Calls to this API will not return {@code null} unless no updated drawable was found
+ * and the call to {@code defaultStringLoader} returned {@code null}.
*
* <p>Callers should register for {@link #ACTION_DEVICE_POLICY_RESOURCE_UPDATED} to get
* notified when a resource has been updated.
@@ -15201,7 +15258,7 @@ public class DevicePolicyManager {
* @hide
*/
@SystemApi
- @NonNull
+ @Nullable
public String getString(
@NonNull @DevicePolicyResources.UpdatableStringId String stringId,
@NonNull Callable<String> defaultStringLoader) {
@@ -15236,8 +15293,8 @@ public class DevicePolicyManager {
* {@link java.util.Formatter} and {@link java.lang.String#format}, (see
* {@link Resources#getString(int, Object...)}).
*
- * <p>{@code defaultStringLoader} must return a non {@code null} {@link String}, otherwise a
- * {@link NullPointerException} is thrown.
+ * <p>Calls to this API will not return {@code null} unless no updated drawable was found
+ * and the call to {@code defaultStringLoader} returned {@code null}.
*
* @param stringId The IDs to get the updated resource for.
* @param defaultStringLoader To get the default string if no updated string was set for
@@ -15247,7 +15304,7 @@ public class DevicePolicyManager {
* @hide
*/
@SystemApi
- @NonNull
+ @Nullable
@SuppressLint("SamShouldBeLast")
public String getString(
@NonNull @DevicePolicyResources.UpdatableStringId String stringId,
diff --git a/core/java/android/app/admin/DevicePolicyResources.java b/core/java/android/app/admin/DevicePolicyResources.java
index 2ad201093d58..ac39cb4f1e23 100644
--- a/core/java/android/app/admin/DevicePolicyResources.java
+++ b/core/java/android/app/admin/DevicePolicyResources.java
@@ -93,6 +93,173 @@ import static android.app.admin.DevicePolicyResources.Strings.MediaProvider.BLOC
import static android.app.admin.DevicePolicyResources.Strings.MediaProvider.SWITCH_TO_PERSONAL_MESSAGE;
import static android.app.admin.DevicePolicyResources.Strings.MediaProvider.SWITCH_TO_WORK_MESSAGE;
import static android.app.admin.DevicePolicyResources.Strings.MediaProvider.WORK_PROFILE_PAUSED_MESSAGE;
+import static android.app.admin.DevicePolicyResources.Strings.PermissionController.BACKGROUND_ACCESS_DISABLED_BY_ADMIN_MESSAGE;
+import static android.app.admin.DevicePolicyResources.Strings.PermissionController.BACKGROUND_ACCESS_ENABLED_BY_ADMIN_MESSAGE;
+import static android.app.admin.DevicePolicyResources.Strings.PermissionController.FOREGROUND_ACCESS_ENABLED_BY_ADMIN_MESSAGE;
+import static android.app.admin.DevicePolicyResources.Strings.PermissionController.HOME_MISSING_WORK_PROFILE_SUPPORT_MESSAGE;
+import static android.app.admin.DevicePolicyResources.Strings.PermissionController.LOCATION_AUTO_GRANTED_MESSAGE;
+import static android.app.admin.DevicePolicyResources.Strings.PermissionController.WORK_PROFILE_DEFAULT_APPS_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.ACCESSIBILITY_CATEGORY_PERSONAL;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.ACCESSIBILITY_CATEGORY_WORK;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.ACCESSIBILITY_PERSONAL_ACCOUNT_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.ACCESSIBILITY_WORK_ACCOUNT_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.ACCOUNTS_SEARCH_KEYWORDS;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.ACTIVATE_DEVICE_ADMIN_APP;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.ACTIVATE_DEVICE_ADMIN_APP_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.ACTIVATE_THIS_DEVICE_ADMIN_APP;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.ACTIVE_DEVICE_ADMIN_WARNING;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.ADMIN_ACTIONS_APPS_COUNT;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.ADMIN_ACTIONS_APPS_COUNT_MINIMUM;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.ADMIN_ACTION_ACCESS_CAMERA;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.ADMIN_ACTION_ACCESS_LOCATION;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.ADMIN_ACTION_ACCESS_MICROPHONE;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.ADMIN_ACTION_APPS_COUNT_ESTIMATED;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.ADMIN_ACTION_APPS_INSTALLED;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.ADMIN_ACTION_NONE;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.ADMIN_ACTION_SET_CURRENT_INPUT_METHOD;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.ADMIN_ACTION_SET_DEFAULT_APPS;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.ADMIN_ACTION_SET_HTTP_PROXY;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.ADMIN_ACTION_SET_INPUT_METHOD_NAME;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.ADMIN_CAN_LOCK_DEVICE;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.ADMIN_CAN_SEE_APPS_WARNING;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.ADMIN_CAN_SEE_BUG_REPORT_WARNING;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.ADMIN_CAN_SEE_NETWORK_LOGS_WARNING;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.ADMIN_CAN_SEE_SECURITY_LOGS_WARNING;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.ADMIN_CAN_SEE_USAGE_WARNING;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.ADMIN_CAN_SEE_WORK_DATA_WARNING;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.ADMIN_CAN_WIPE_DEVICE;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.ADMIN_CONFIGURED_FAILED_PASSWORD_WIPE_DEVICE;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.ADMIN_CONFIGURED_FAILED_PASSWORD_WIPE_WORK_PROFILE;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.ALWAYS_ON_VPN_DEVICE;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.ALWAYS_ON_VPN_PERSONAL_PROFILE;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.ALWAYS_ON_VPN_WORK_PROFILE;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.APP_CAN_ACCESS_PERSONAL_DATA;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.APP_CAN_ACCESS_PERSONAL_PERMISSIONS;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.CA_CERTS_DEVICE;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.CA_CERTS_PERSONAL_PROFILE;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.CA_CERTS_WORK_PROFILE;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.CHANGES_MADE_BY_YOUR_ORGANIZATION_ADMIN_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.CONFIRM_WORK_PROFILE_PASSWORD_HEADER;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.CONFIRM_WORK_PROFILE_PATTERN_HEADER;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.CONFIRM_WORK_PROFILE_PIN_HEADER;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.CONNECTED_APPS_SEARCH_KEYWORDS;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.CONNECTED_APPS_SHARE_PERMISSIONS_AND_DATA;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.CONNECTED_WORK_AND_PERSONAL_APPS_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.CONNECT_APPS_DIALOG_SUMMARY;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.CONNECT_APPS_DIALOG_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.CONTACT_YOUR_IT_ADMIN;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.CONTROLLED_BY_ADMIN_SUMMARY;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.CROSS_PROFILE_CALENDAR_SUMMARY;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.CROSS_PROFILE_CALENDAR_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.DEVICE_ADMIN_POLICIES_WARNING;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.DEVICE_ADMIN_SETTINGS_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.DEVICE_MANAGED_WITHOUT_NAME;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.DEVICE_MANAGED_WITH_NAME;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.DEVICE_OWNER_INSTALLED_CERTIFICATE_AUTHORITY_WARNING;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.DISABLED_BY_IT_ADMIN_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.ENABLE_WORK_PROFILE_SYNC_WITH_PERSONAL_SOUNDS_DIALOG_MESSAGE;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.ENABLE_WORK_PROFILE_SYNC_WITH_PERSONAL_SOUNDS_DIALOG_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.ENTERPRISE_PRIVACY_HEADER;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.ERROR_MOVE_DEVICE_ADMIN;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.FACE_SETTINGS_FOR_WORK_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.FACE_UNLOCK_DISABLED;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.FINGERPRINT_FOR_WORK;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.FINGERPRINT_UNLOCK_DISABLED;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.FINGERPRINT_UNLOCK_DISABLED_EXPLANATION;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.FORGOT_PASSWORD_TEXT;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.FORGOT_PASSWORD_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.HOW_TO_DISCONNECT_APPS;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.INFORMATION_YOUR_ORGANIZATION_CAN_SEE_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.INSTALL_IN_PERSONAL_PROFILE_TO_CONNECT_PROMPT;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.INSTALL_IN_WORK_PROFILE_TO_CONNECT_PROMPT;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.IT_ADMIN_POLICY_DISABLING_INFO_URL;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.MANAGED_BY;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.MANAGED_DEVICE_INFO;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.MANAGED_DEVICE_INFO_SUMMARY;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.MANAGED_DEVICE_INFO_SUMMARY_WITH_NAME;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.MANAGED_PROFILE_SETTINGS_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.MANAGE_DEVICE_ADMIN_APPS;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.NEW_DEVICE_ADMIN_WARNING;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.NEW_DEVICE_ADMIN_WARNING_SIMPLIFIED;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.NO_DEVICE_ADMINS;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.NUMBER_OF_DEVICE_ADMINS;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.NUMBER_OF_DEVICE_ADMINS_NONE;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.ONLY_CONNECT_TRUSTED_APPS;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.OTHER_OPTIONS_DISABLED_BY_ADMIN;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.PASSWORD_RECENTLY_USED;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.PERSONAL_CATEGORY_HEADER;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.PERSONAL_PROFILE_APP_SUBTEXT;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.PIN_RECENTLY_USED;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.REENTER_WORK_PROFILE_PASSWORD_HEADER;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.REENTER_WORK_PROFILE_PIN_HEADER;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.REMOVE_ACCOUNT_FAILED_ADMIN_RESTRICTION;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.REMOVE_AND_UNINSTALL_DEVICE_ADMIN;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.REMOVE_DEVICE_ADMIN;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.REMOVE_WORK_PROFILE;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.SELECT_DEVICE_ADMIN_APPS;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.SET_PROFILE_OWNER_DIALOG_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.SET_PROFILE_OWNER_POSTSETUP_WARNING;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.SET_PROFILE_OWNER_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.SET_WORK_PROFILE_PASSWORD_HEADER;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.SET_WORK_PROFILE_PATTERN_HEADER;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.SET_WORK_PROFILE_PIN_HEADER;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.SHARE_REMOTE_BUGREPORT_DIALOG_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.SHARE_REMOTE_BUGREPORT_FINISHED_REQUEST_CONSENT;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.SHARE_REMOTE_BUGREPORT_NOT_FINISHED_REQUEST_CONSENT;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.SHARING_REMOTE_BUGREPORT_MESSAGE;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.UNINSTALL_DEVICE_ADMIN;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.USER_ADMIN_POLICIES_WARNING;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_APPS_CANNOT_ACCESS_NOTIFICATION_SETTINGS;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_CATEGORY_HEADER;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_ADMIN_POLICIES_WARNING;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_ALARM_RINGTONE_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_APP_SUBTEXT;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_CONFIRM_PATTERN;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_CONFIRM_PIN;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_CONFIRM_REMOVE_MESSAGE;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_CONFIRM_REMOVE_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_CONTACT_SEARCH_SUMMARY;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_CONTACT_SEARCH_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_DISABLE_USAGE_ACCESS_WARNING;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_FINGERPRINT_LAST_DELETE_MESSAGE;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_INSTALLED_CERTIFICATE_AUTHORITY_WARNING;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_IT_ADMIN_CANT_RESET_SCREEN_LOCK;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_KEYBOARDS_AND_TOOLS;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_LAST_PASSWORD_ATTEMPT_BEFORE_WIPE;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_LAST_PATTERN_ATTEMPT_BEFORE_WIPE;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_LAST_PIN_ATTEMPT_BEFORE_WIPE;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_LOCATION_SWITCH_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_LOCKED_NOTIFICATION_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_LOCK_ATTEMPTS_FAILED;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_LOCK_SCREEN_REDACT_NOTIFICATION_SUMMARY;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_LOCK_SCREEN_REDACT_NOTIFICATION_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_MANAGED_BY;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_NOTIFICATIONS_SECTION_HEADER;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_NOTIFICATION_LISTENER_BLOCKED;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_NOTIFICATION_RINGTONE_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_NOT_AVAILABLE;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_OFF_CONDITION_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_PASSWORD_REQUIRED;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_PRIVACY_POLICY_INFO;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_PRIVACY_POLICY_INFO_SUMMARY;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_RINGTONE_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_SCREEN_LOCK_SETUP_MESSAGE;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_SECURITY_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_SETTING;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_SETTING_OFF_SUMMARY;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_SETTING_ON_SUMMARY;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_SET_UNLOCK_LAUNCH_PICKER_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_SOUND_SETTINGS_SECTION_HEADER;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_SYNC_WITH_PERSONAL_SOUNDS_ACTIVE_SUMMARY;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_UNIFICATION_SEARCH_KEYWORDS;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_UNIFY_LOCKS_DETAIL;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_UNIFY_LOCKS_NONCOMPLIANT;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_UNIFY_LOCKS_SUMMARY;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_UNIFY_LOCKS_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_USER_LABEL;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_USE_PERSONAL_SOUNDS_SUMMARY;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_USE_PERSONAL_SOUNDS_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.YOUR_ACCESS_TO_THIS_DEVICE_TITLE;
import static android.app.admin.DevicePolicyResources.Strings.SystemUi.BIOMETRIC_DIALOG_WORK_LOCK_FAILED_ATTEMPTS;
import static android.app.admin.DevicePolicyResources.Strings.SystemUi.BIOMETRIC_DIALOG_WORK_PASSWORD_LAST_ATTEMPT;
import static android.app.admin.DevicePolicyResources.Strings.SystemUi.BIOMETRIC_DIALOG_WORK_PATTERN_LAST_ATTEMPT;
@@ -159,7 +326,8 @@ public final class DevicePolicyResources {
Drawables.WORK_PROFILE_OFF_ICON,
Drawables.WORK_PROFILE_USER_ICON
})
- public @interface UpdatableDrawableId {}
+ public @interface UpdatableDrawableId {
+ }
/**
* Identifiers to specify the desired style for the updatable device management system
@@ -174,7 +342,8 @@ public final class DevicePolicyResources {
Drawables.Style.OUTLINE,
Drawables.Style.DEFAULT
})
- public @interface UpdatableDrawableStyle {}
+ public @interface UpdatableDrawableStyle {
+ }
/**
* Identifiers to specify the location if the updatable device management system resource.
@@ -191,7 +360,8 @@ public final class DevicePolicyResources {
Drawables.Source.QUICK_SETTINGS,
Drawables.Source.STATUS_BAR
})
- public @interface UpdatableDrawableSource {}
+ public @interface UpdatableDrawableSource {
+ }
/**
* Resource identifiers used to update device management-related string resources.
@@ -231,7 +401,7 @@ public final class DevicePolicyResources {
PERSONAL_APP_SUSPENSION_TITLE, PERSONAL_APP_SUSPENSION_MESSAGE,
PERSONAL_APP_SUSPENSION_SOON_MESSAGE, PERSONAL_APP_SUSPENSION_TURN_ON_PROFILE,
PRINTING_DISABLED_NAMED_ADMIN, LOCATION_CHANGED_TITLE, LOCATION_CHANGED_MESSAGE,
- NETWORK_LOGGING_TITLE, NETWORK_LOGGING_MESSAGE,
+ NETWORK_LOGGING_TITLE, NETWORK_LOGGING_MESSAGE,
NOTIFICATION_WORK_PROFILE_CONTENT_DESCRIPTION, NOTIFICATION_CHANNEL_DEVICE_ADMIN,
SWITCH_TO_WORK_LABEL, SWITCH_TO_PERSONAL_LABEL, FORWARD_INTENT_TO_WORK,
FORWARD_INTENT_TO_PERSONAL, RESOLVER_WORK_PROFILE_NOT_SUPPORTED, RESOLVER_PERSONAL_TAB,
@@ -257,7 +427,92 @@ public final class DevicePolicyResources {
SWITCH_TO_WORK_MESSAGE, SWITCH_TO_PERSONAL_MESSAGE, BLOCKED_BY_ADMIN_TITLE,
BLOCKED_FROM_PERSONAL_MESSAGE, BLOCKED_FROM_PERSONAL_MESSAGE,
BLOCKED_FROM_WORK_MESSAGE, Strings.MediaProvider.WORK_PROFILE_PAUSED_TITLE,
- WORK_PROFILE_PAUSED_MESSAGE
+ WORK_PROFILE_PAUSED_MESSAGE,
+
+ // Settings Strings
+ FACE_SETTINGS_FOR_WORK_TITLE, WORK_PROFILE_FINGERPRINT_LAST_DELETE_MESSAGE,
+ WORK_PROFILE_IT_ADMIN_CANT_RESET_SCREEN_LOCK, WORK_PROFILE_SCREEN_LOCK_SETUP_MESSAGE,
+ WORK_PROFILE_SET_UNLOCK_LAUNCH_PICKER_TITLE,
+ WORK_PROFILE_LAST_PATTERN_ATTEMPT_BEFORE_WIPE,
+ WORK_PROFILE_LAST_PIN_ATTEMPT_BEFORE_WIPE,
+ WORK_PROFILE_LAST_PASSWORD_ATTEMPT_BEFORE_WIPE, WORK_PROFILE_LOCK_ATTEMPTS_FAILED,
+ ACCESSIBILITY_CATEGORY_WORK, ACCESSIBILITY_CATEGORY_PERSONAL,
+ ACCESSIBILITY_WORK_ACCOUNT_TITLE, ACCESSIBILITY_PERSONAL_ACCOUNT_TITLE,
+ WORK_PROFILE_LOCATION_SWITCH_TITLE, SET_WORK_PROFILE_PASSWORD_HEADER,
+ SET_WORK_PROFILE_PIN_HEADER, SET_WORK_PROFILE_PATTERN_HEADER,
+ CONFIRM_WORK_PROFILE_PASSWORD_HEADER, CONFIRM_WORK_PROFILE_PIN_HEADER,
+ CONFIRM_WORK_PROFILE_PATTERN_HEADER, REENTER_WORK_PROFILE_PASSWORD_HEADER,
+ REENTER_WORK_PROFILE_PIN_HEADER, WORK_PROFILE_CONFIRM_PATTERN, WORK_PROFILE_CONFIRM_PIN,
+ WORK_PROFILE_PASSWORD_REQUIRED, WORK_PROFILE_SECURITY_TITLE,
+ WORK_PROFILE_UNIFY_LOCKS_TITLE, WORK_PROFILE_UNIFY_LOCKS_SUMMARY,
+ WORK_PROFILE_UNIFY_LOCKS_DETAIL, WORK_PROFILE_UNIFY_LOCKS_NONCOMPLIANT,
+ WORK_PROFILE_KEYBOARDS_AND_TOOLS, WORK_PROFILE_NOT_AVAILABLE, WORK_PROFILE_SETTING,
+ WORK_PROFILE_SETTING_ON_SUMMARY, WORK_PROFILE_SETTING_OFF_SUMMARY, REMOVE_WORK_PROFILE,
+ DEVICE_OWNER_INSTALLED_CERTIFICATE_AUTHORITY_WARNING,
+ WORK_PROFILE_INSTALLED_CERTIFICATE_AUTHORITY_WARNING, WORK_PROFILE_CONFIRM_REMOVE_TITLE,
+ WORK_PROFILE_CONFIRM_REMOVE_MESSAGE, WORK_APPS_CANNOT_ACCESS_NOTIFICATION_SETTINGS,
+ WORK_PROFILE_SOUND_SETTINGS_SECTION_HEADER, WORK_PROFILE_USE_PERSONAL_SOUNDS_TITLE,
+ WORK_PROFILE_USE_PERSONAL_SOUNDS_SUMMARY, WORK_PROFILE_RINGTONE_TITLE,
+ WORK_PROFILE_NOTIFICATION_RINGTONE_TITLE, WORK_PROFILE_ALARM_RINGTONE_TITLE,
+ WORK_PROFILE_SYNC_WITH_PERSONAL_SOUNDS_ACTIVE_SUMMARY,
+ ENABLE_WORK_PROFILE_SYNC_WITH_PERSONAL_SOUNDS_DIALOG_TITLE,
+ ENABLE_WORK_PROFILE_SYNC_WITH_PERSONAL_SOUNDS_DIALOG_MESSAGE,
+ WORK_PROFILE_NOTIFICATIONS_SECTION_HEADER, WORK_PROFILE_LOCKED_NOTIFICATION_TITLE,
+ WORK_PROFILE_LOCK_SCREEN_REDACT_NOTIFICATION_TITLE,
+ WORK_PROFILE_LOCK_SCREEN_REDACT_NOTIFICATION_SUMMARY,
+ WORK_PROFILE_NOTIFICATION_LISTENER_BLOCKED, CONNECTED_WORK_AND_PERSONAL_APPS_TITLE,
+ CONNECTED_APPS_SHARE_PERMISSIONS_AND_DATA, ONLY_CONNECT_TRUSTED_APPS,
+ HOW_TO_DISCONNECT_APPS, CONNECT_APPS_DIALOG_TITLE, CONNECT_APPS_DIALOG_SUMMARY,
+ APP_CAN_ACCESS_PERSONAL_DATA, APP_CAN_ACCESS_PERSONAL_PERMISSIONS,
+ INSTALL_IN_WORK_PROFILE_TO_CONNECT_PROMPT,
+ INSTALL_IN_PERSONAL_PROFILE_TO_CONNECT_PROMPT, WORK_PROFILE_MANAGED_BY, MANAGED_BY,
+ WORK_PROFILE_DISABLE_USAGE_ACCESS_WARNING, DISABLED_BY_IT_ADMIN_TITLE,
+ CONTACT_YOUR_IT_ADMIN, WORK_PROFILE_ADMIN_POLICIES_WARNING, USER_ADMIN_POLICIES_WARNING,
+ DEVICE_ADMIN_POLICIES_WARNING, WORK_PROFILE_OFF_CONDITION_TITLE,
+ MANAGED_PROFILE_SETTINGS_TITLE, WORK_PROFILE_CONTACT_SEARCH_TITLE,
+ WORK_PROFILE_CONTACT_SEARCH_SUMMARY, CROSS_PROFILE_CALENDAR_TITLE,
+ CROSS_PROFILE_CALENDAR_SUMMARY, ALWAYS_ON_VPN_PERSONAL_PROFILE, ALWAYS_ON_VPN_DEVICE,
+ ALWAYS_ON_VPN_WORK_PROFILE, CA_CERTS_PERSONAL_PROFILE, CA_CERTS_WORK_PROFILE,
+ CA_CERTS_DEVICE, ADMIN_CAN_LOCK_DEVICE, ADMIN_CAN_WIPE_DEVICE,
+ ADMIN_CONFIGURED_FAILED_PASSWORD_WIPE_DEVICE,
+ ADMIN_CONFIGURED_FAILED_PASSWORD_WIPE_WORK_PROFILE, DEVICE_MANAGED_WITHOUT_NAME,
+ DEVICE_MANAGED_WITH_NAME, WORK_PROFILE_APP_SUBTEXT, PERSONAL_PROFILE_APP_SUBTEXT,
+ FINGERPRINT_FOR_WORK, FACE_UNLOCK_DISABLED, FINGERPRINT_UNLOCK_DISABLED,
+ FINGERPRINT_UNLOCK_DISABLED_EXPLANATION, PIN_RECENTLY_USED, PASSWORD_RECENTLY_USED,
+ MANAGE_DEVICE_ADMIN_APPS, NUMBER_OF_DEVICE_ADMINS_NONE, NUMBER_OF_DEVICE_ADMINS,
+ FORGOT_PASSWORD_TITLE, FORGOT_PASSWORD_TEXT, ERROR_MOVE_DEVICE_ADMIN,
+ DEVICE_ADMIN_SETTINGS_TITLE, REMOVE_DEVICE_ADMIN, UNINSTALL_DEVICE_ADMIN,
+ REMOVE_AND_UNINSTALL_DEVICE_ADMIN, SELECT_DEVICE_ADMIN_APPS, NO_DEVICE_ADMINS,
+ ACTIVATE_DEVICE_ADMIN_APP, ACTIVATE_THIS_DEVICE_ADMIN_APP,
+ ACTIVATE_DEVICE_ADMIN_APP_TITLE, NEW_DEVICE_ADMIN_WARNING,
+ NEW_DEVICE_ADMIN_WARNING_SIMPLIFIED, ACTIVE_DEVICE_ADMIN_WARNING,
+ SET_PROFILE_OWNER_TITLE, SET_PROFILE_OWNER_DIALOG_TITLE,
+ SET_PROFILE_OWNER_POSTSETUP_WARNING, OTHER_OPTIONS_DISABLED_BY_ADMIN,
+ REMOVE_ACCOUNT_FAILED_ADMIN_RESTRICTION, IT_ADMIN_POLICY_DISABLING_INFO_URL,
+ SHARE_REMOTE_BUGREPORT_DIALOG_TITLE, SHARE_REMOTE_BUGREPORT_FINISHED_REQUEST_CONSENT,
+ SHARE_REMOTE_BUGREPORT_NOT_FINISHED_REQUEST_CONSENT, SHARING_REMOTE_BUGREPORT_MESSAGE,
+ MANAGED_DEVICE_INFO, MANAGED_DEVICE_INFO_SUMMARY, MANAGED_DEVICE_INFO_SUMMARY_WITH_NAME,
+ ENTERPRISE_PRIVACY_HEADER, INFORMATION_YOUR_ORGANIZATION_CAN_SEE_TITLE,
+ CHANGES_MADE_BY_YOUR_ORGANIZATION_ADMIN_TITLE, YOUR_ACCESS_TO_THIS_DEVICE_TITLE,
+ ADMIN_CAN_SEE_WORK_DATA_WARNING, ADMIN_CAN_SEE_APPS_WARNING,
+ ADMIN_CAN_SEE_USAGE_WARNING, ADMIN_CAN_SEE_NETWORK_LOGS_WARNING,
+ ADMIN_CAN_SEE_BUG_REPORT_WARNING, ADMIN_CAN_SEE_SECURITY_LOGS_WARNING,
+ ADMIN_ACTION_NONE, ADMIN_ACTION_APPS_INSTALLED, ADMIN_ACTION_APPS_COUNT_ESTIMATED,
+ ADMIN_ACTIONS_APPS_COUNT_MINIMUM, ADMIN_ACTION_ACCESS_LOCATION,
+ ADMIN_ACTION_ACCESS_MICROPHONE, ADMIN_ACTION_ACCESS_CAMERA,
+ ADMIN_ACTION_SET_DEFAULT_APPS, ADMIN_ACTIONS_APPS_COUNT,
+ ADMIN_ACTION_SET_CURRENT_INPUT_METHOD, ADMIN_ACTION_SET_INPUT_METHOD_NAME,
+ ADMIN_ACTION_SET_HTTP_PROXY, WORK_PROFILE_PRIVACY_POLICY_INFO_SUMMARY,
+ WORK_PROFILE_PRIVACY_POLICY_INFO, CONNECTED_APPS_SEARCH_KEYWORDS,
+ WORK_PROFILE_UNIFICATION_SEARCH_KEYWORDS, ACCOUNTS_SEARCH_KEYWORDS,
+ CONTROLLED_BY_ADMIN_SUMMARY, WORK_PROFILE_USER_LABEL, WORK_CATEGORY_HEADER,
+ PERSONAL_CATEGORY_HEADER,
+
+ // PermissionController Strings
+ WORK_PROFILE_DEFAULT_APPS_TITLE, HOME_MISSING_WORK_PROFILE_SUPPORT_MESSAGE,
+ BACKGROUND_ACCESS_DISABLED_BY_ADMIN_MESSAGE, BACKGROUND_ACCESS_ENABLED_BY_ADMIN_MESSAGE,
+ BACKGROUND_ACCESS_ENABLED_BY_ADMIN_MESSAGE, FOREGROUND_ACCESS_ENABLED_BY_ADMIN_MESSAGE,
+ LOCATION_AUTO_GRANTED_MESSAGE
})
public @interface UpdatableStringId {
}
@@ -432,7 +687,8 @@ public final class DevicePolicyResources {
@SystemApi
public static final class Strings {
- private Strings() {}
+ private Strings() {
+ }
/**
* An ID for any string that can't be updated.
@@ -446,23 +702,1221 @@ public final class DevicePolicyResources {
private static Set<String> buildStringsSet() {
Set<String> strings = new HashSet<>();
+ strings.addAll(Settings.buildStringsSet());
strings.addAll(Launcher.buildStringsSet());
strings.addAll(SystemUi.buildStringsSet());
strings.addAll(Core.buildStringsSet());
strings.addAll(DocumentsUi.buildStringsSet());
strings.addAll(MediaProvider.buildStringsSet());
+ strings.addAll(PermissionController.buildStringsSet());
return strings;
}
/**
* Class containing the identifiers used to update device management-related system strings
+ * in the Settings package
+ *
+ * @hide
+ */
+ public static final class Settings {
+
+ private Settings() {
+ }
+
+ private static final String PREFIX = "Settings.";
+
+ /**
+ * Title shown for menu item that launches face settings or enrollment, for work profile
+ */
+ public static final String FACE_SETTINGS_FOR_WORK_TITLE =
+ PREFIX + "FACE_SETTINGS_FOR_WORK_TITLE";
+
+ /**
+ * Warning when removing the last fingerprint on a work profile
+ */
+ public static final String WORK_PROFILE_FINGERPRINT_LAST_DELETE_MESSAGE =
+ PREFIX + "WORK_PROFILE_FINGERPRINT_LAST_DELETE_MESSAGE";
+
+ /**
+ * Text letting the user know that their IT admin can't reset their screen lock if they
+ * forget it, and they can choose to set another lock that would be specifically for
+ * their work apps
+ */
+ public static final String WORK_PROFILE_IT_ADMIN_CANT_RESET_SCREEN_LOCK =
+ PREFIX + "WORK_PROFILE_IT_ADMIN_CANT_RESET_SCREEN_LOCK";
+
+ /**
+ * Message shown in screen lock picker for setting up a work profile screen lock
+ */
+ public static final String WORK_PROFILE_SCREEN_LOCK_SETUP_MESSAGE =
+ PREFIX + "WORK_PROFILE_SCREEN_LOCK_SETUP_MESSAGE";
+
+ /**
+ * Title for PreferenceScreen to launch picker for security method for the managed
+ * profile when there is none
+ */
+ public static final String WORK_PROFILE_SET_UNLOCK_LAUNCH_PICKER_TITLE =
+ PREFIX + "WORK_PROFILE_SET_UNLOCK_LAUNCH_PICKER_TITLE";
+
+ /**
+ * Content of the dialog shown when the user only has one attempt left to provide the
+ * work lock pattern before the work profile is removed
+ */
+ public static final String WORK_PROFILE_LAST_PATTERN_ATTEMPT_BEFORE_WIPE =
+ PREFIX + "WORK_PROFILE_LAST_PATTERN_ATTEMPT_BEFORE_WIPE";
+
+ /**
+ * Content of the dialog shown when the user only has one attempt left to provide the
+ * work lock pattern before the work profile is removed
+ */
+ public static final String WORK_PROFILE_LAST_PIN_ATTEMPT_BEFORE_WIPE =
+ PREFIX + "WORK_PROFILE_LAST_PIN_ATTEMPT_BEFORE_WIPE";
+
+ /**
+ * Content of the dialog shown when the user only has one attempt left to provide the
+ * work lock pattern before the work profile is removed
+ */
+ public static final String WORK_PROFILE_LAST_PASSWORD_ATTEMPT_BEFORE_WIPE =
+ PREFIX + "WORK_PROFILE_LAST_PASSWORD_ATTEMPT_BEFORE_WIPE";
+
+ /**
+ * Content of the dialog shown when the user has failed to provide the device lock too
+ * many times and the device is wiped
+ */
+ public static final String WORK_PROFILE_LOCK_ATTEMPTS_FAILED =
+ PREFIX + "WORK_PROFILE_LOCK_ATTEMPTS_FAILED";
+
+ /**
+ * Content description for work profile accounts group
+ */
+ public static final String ACCESSIBILITY_CATEGORY_WORK =
+ PREFIX + "ACCESSIBILITY_CATEGORY_WORK";
+
+ /**
+ * Content description for personal profile accounts group
+ */
+ public static final String ACCESSIBILITY_CATEGORY_PERSONAL =
+ PREFIX + "ACCESSIBILITY_CATEGORY_PERSONAL";
+
+ /**
+ * Content description for work profile details page title
+ */
+ public static final String ACCESSIBILITY_WORK_ACCOUNT_TITLE =
+ PREFIX + "ACCESSIBILITY_WORK_ACCOUNT_TITLE";
+
+ /**
+ * Content description for personal profile details page title
+ */
+ public static final String ACCESSIBILITY_PERSONAL_ACCOUNT_TITLE =
+ PREFIX + "ACCESSIBILITY_PERSONAL_ACCOUNT_TITLE";
+
+ /**
+ * Title for work profile location switch
+ */
+ public static final String WORK_PROFILE_LOCATION_SWITCH_TITLE =
+ PREFIX + "WORK_PROFILE_LOCATION_SWITCH_TITLE";
+
+ /**
+ * Header when setting work profile password
+ */
+ public static final String SET_WORK_PROFILE_PASSWORD_HEADER =
+ PREFIX + "SET_WORK_PROFILE_PASSWORD_HEADER";
+
+ /**
+ * Header when setting work profile PIN
+ */
+ public static final String SET_WORK_PROFILE_PIN_HEADER =
+ PREFIX + "SET_WORK_PROFILE_PIN_HEADER";
+
+ /**
+ * Header when setting work profile pattern
+ */
+ public static final String SET_WORK_PROFILE_PATTERN_HEADER =
+ PREFIX + "SET_WORK_PROFILE_PATTERN_HEADER";
+
+ /**
+ * Header when confirming work profile password
+ */
+ public static final String CONFIRM_WORK_PROFILE_PASSWORD_HEADER =
+ PREFIX + "CONFIRM_WORK_PROFILE_PASSWORD_HEADER";
+
+ /**
+ * Header when confirming work profile pin
+ */
+ public static final String CONFIRM_WORK_PROFILE_PIN_HEADER =
+ PREFIX + "CONFIRM_WORK_PROFILE_PIN_HEADER";
+
+ /**
+ * Header when confirming work profile pattern
+ */
+ public static final String CONFIRM_WORK_PROFILE_PATTERN_HEADER =
+ PREFIX + "CONFIRM_WORK_PROFILE_PATTERN_HEADER";
+
+ /**
+ * Header when re-entering work profile password
+ */
+ public static final String REENTER_WORK_PROFILE_PASSWORD_HEADER =
+ PREFIX + "REENTER_WORK_PROFILE_PASSWORD_HEADER";
+
+ /**
+ * Header when re-entering work profile pin
+ */
+ public static final String REENTER_WORK_PROFILE_PIN_HEADER =
+ PREFIX + "REENTER_WORK_PROFILE_PIN_HEADER";
+
+ /**
+ * Message to be used to explain the users that they need to enter their work pattern to
+ * continue a particular operation
+ */
+ public static final String WORK_PROFILE_CONFIRM_PATTERN =
+ PREFIX + "WORK_PROFILE_CONFIRM_PATTERN";
+
+ /**
+ * Message to be used to explain the users that they need to enter their work pin to
+ * continue a particular operation
+ */
+ public static final String WORK_PROFILE_CONFIRM_PIN =
+ PREFIX + "WORK_PROFILE_CONFIRM_PIN";
+
+ /**
+ * Message to be used to explain the users that they need to enter their work password
+ * to
+ * continue a particular operation
+ */
+ public static final String WORK_PROFILE_CONFIRM_PASSWORD =
+ PREFIX + "WORK_PROFILE_CONFIRM_PASSWORD";
+
+ /**
+ * This string shows = PREFIX + "shows"; up on a screen where a user can enter a pattern
+ * that lets them access
+ * their work profile. This is an extra security measure that's required for them to
+ * continue
+ */
+ public static final String WORK_PROFILE_PATTERN_REQUIRED =
+ PREFIX + "WORK_PROFILE_PATTERN_REQUIRED";
+
+ /**
+ * This string shows = PREFIX + "shows"; up on a screen where a user can enter a pin
+ * that lets them access
+ * their work profile. This is an extra security measure that's required for them to
+ * continue
+ */
+ public static final String WORK_PROFILE_PIN_REQUIRED =
+ PREFIX + "WORK_PROFILE_PIN_REQUIRED";
+
+ /**
+ * This string shows = PREFIX + "shows"; up on a screen where a user can enter a
+ * password that lets them access
+ * their work profile. This is an extra security measure that's required for them to
+ * continue
+ */
+ public static final String WORK_PROFILE_PASSWORD_REQUIRED =
+ PREFIX + "WORK_PROFILE_PASSWORD_REQUIRED";
+
+ /**
+ * Header for Work Profile security settings
+ */
+ public static final String WORK_PROFILE_SECURITY_TITLE =
+ PREFIX + "WORK_PROFILE_SECURITY_TITLE";
+
+ /**
+ * Header for Work Profile unify locks settings
+ */
+ public static final String WORK_PROFILE_UNIFY_LOCKS_TITLE =
+ PREFIX + "WORK_PROFILE_UNIFY_LOCKS_TITLE";
+
+ /**
+ * Setting option explanation to unify work and personal locks
+ */
+ public static final String WORK_PROFILE_UNIFY_LOCKS_SUMMARY =
+ PREFIX + "WORK_PROFILE_UNIFY_LOCKS_SUMMARY";
+
+ /**
+ * Further explanation when the user wants to unify work and personal locks
+ */
+ public static final String WORK_PROFILE_UNIFY_LOCKS_DETAIL =
+ PREFIX + "WORK_PROFILE_UNIFY_LOCKS_DETAIL";
+
+ /**
+ * Ask if the user wants to create a new lock for personal and work as the current work
+ * lock is not enough for the device
+ */
+ public static final String WORK_PROFILE_UNIFY_LOCKS_NONCOMPLIANT =
+ PREFIX + "WORK_PROFILE_UNIFY_LOCKS_NONCOMPLIANT";
+
+ /**
+ * Title of 'Work profile keyboards & tools' preference category
+ */
+ public static final String WORK_PROFILE_KEYBOARDS_AND_TOOLS =
+ PREFIX + "WORK_PROFILE_KEYBOARDS_AND_TOOLS";
+
+ /**
+ * Label for state when work profile is not available
+ */
+ public static final String WORK_PROFILE_NOT_AVAILABLE =
+ PREFIX + "WORK_PROFILE_NOT_AVAILABLE";
+
+ /**
+ * Label for work profile setting (to allow turning work profile on and off)
+ */
+ public static final String WORK_PROFILE_SETTING = PREFIX + "WORK_PROFILE_SETTING";
+
+ /**
+ * Description of the work profile setting when the work profile is on
+ */
+ public static final String WORK_PROFILE_SETTING_ON_SUMMARY =
+ PREFIX + "WORK_PROFILE_SETTING_ON_SUMMARY";
+
+ /**
+ * Description of the work profile setting when the work profile is off
+ */
+ public static final String WORK_PROFILE_SETTING_OFF_SUMMARY =
+ PREFIX + "WORK_PROFILE_SETTING_OFF_SUMMARY";
+
+ /**
+ * Button text to remove work profile
+ */
+ public static final String REMOVE_WORK_PROFILE = PREFIX + "REMOVE_WORK_PROFILE";
+
+ /**
+ * Text of message to show to device owner user whose administrator has installed a SSL
+ * CA Cert
+ */
+ public static final String DEVICE_OWNER_INSTALLED_CERTIFICATE_AUTHORITY_WARNING =
+ PREFIX + "DEVICE_OWNER_INSTALLED_CERTIFICATE_AUTHORITY_WARNING";
+
+ /**
+ * Text of message to show to work profile users whose administrator has installed a SSL
+ * CA Cert
+ */
+ public static final String WORK_PROFILE_INSTALLED_CERTIFICATE_AUTHORITY_WARNING =
+ PREFIX + "WORK_PROFILE_INSTALLED_CERTIFICATE_AUTHORITY_WARNING";
+
+ /**
+ * Work profile removal confirmation title
+ */
+ public static final String WORK_PROFILE_CONFIRM_REMOVE_TITLE =
+ PREFIX + "WORK_PROFILE_CONFIRM_REMOVE_TITLE";
+
+ /**
+ * Work profile removal confirmation message
+ */
+ public static final String WORK_PROFILE_CONFIRM_REMOVE_MESSAGE =
+ PREFIX + "WORK_PROFILE_CONFIRM_REMOVE_MESSAGE";
+
+ /**
+ * Toast shown when an app in the work profile attempts to open notification settings
+ * and apps in the work profile cannot access notification settings
+ */
+ public static final String WORK_APPS_CANNOT_ACCESS_NOTIFICATION_SETTINGS =
+ PREFIX + "WORK_APPS_CANNOT_ACCESS_NOTIFICATION_SETTINGS";
+
+ /**
+ * Work sound settings section header
+ */
+ public static final String WORK_PROFILE_SOUND_SETTINGS_SECTION_HEADER =
+ PREFIX + "WORK_PROFILE_SOUND_SETTINGS_SECTION_HEADER";
+
+ /**
+ * Title for the switch that enables syncing of personal ringtones to work profile
+ */
+ public static final String WORK_PROFILE_USE_PERSONAL_SOUNDS_TITLE =
+ PREFIX + "WORK_PROFILE_USE_PERSONAL_SOUNDS_TITLE";
+
+ /**
+ * Summary for the switch that enables syncing of personal ringtones to work profile
+ */
+ public static final String WORK_PROFILE_USE_PERSONAL_SOUNDS_SUMMARY =
+ PREFIX + "WORK_PROFILE_USE_PERSONAL_SOUNDS_SUMMARY";
+
+ /**
+ * Title for the option defining the work profile phone ringtone
+ */
+ public static final String WORK_PROFILE_RINGTONE_TITLE =
+ PREFIX + "WORK_PROFILE_RINGTONE_TITLE";
+
+ /**
+ * Title for the option defining the default work profile notification ringtone
+ */
+ public static final String WORK_PROFILE_NOTIFICATION_RINGTONE_TITLE =
+ PREFIX + "WORK_PROFILE_NOTIFICATION_RINGTONE_TITLE";
+
+ /**
+ * Title for the option defining the default work alarm ringtone
+ */
+ public static final String WORK_PROFILE_ALARM_RINGTONE_TITLE =
+ PREFIX + "WORK_PROFILE_ALARM_RINGTONE_TITLE";
+
+ /**
+ * Summary for sounds when sync with personal sounds is active
+ */
+ public static final String WORK_PROFILE_SYNC_WITH_PERSONAL_SOUNDS_ACTIVE_SUMMARY =
+ PREFIX + "WORK_PROFILE_SYNC_WITH_PERSONAL_SOUNDS_ACTIVE_SUMMARY";
+
+ /**
+ * Title for dialog shown when enabling sync with personal sounds
+ */
+ public static final String
+ ENABLE_WORK_PROFILE_SYNC_WITH_PERSONAL_SOUNDS_DIALOG_TITLE =
+ PREFIX + "ENABLE_WORK_PROFILE_SYNC_WITH_PERSONAL_SOUNDS_DIALOG_TITLE";
+
+ /**
+ * Message for dialog shown when using the same sounds for work events as for personal
+ * events
+ */
+ public static final String
+ ENABLE_WORK_PROFILE_SYNC_WITH_PERSONAL_SOUNDS_DIALOG_MESSAGE =
+ PREFIX + "ENABLE_WORK_PROFILE_SYNC_WITH_PERSONAL_SOUNDS_DIALOG_MESSAGE";
+
+ /**
+ * Work profile notifications section header
+ */
+ public static final String WORK_PROFILE_NOTIFICATIONS_SECTION_HEADER =
+ PREFIX + "WORK_PROFILE_NOTIFICATIONS_SECTION_HEADER";
+
+ /**
+ * Title for the option controlling notifications for work profile
+ */
+ public static final String WORK_PROFILE_LOCKED_NOTIFICATION_TITLE =
+ PREFIX + "WORK_PROFILE_LOCKED_NOTIFICATION_TITLE";
+
+ /**
+ * Title for redacting sensitive content on lockscreen for work profiles
+ */
+ public static final String WORK_PROFILE_LOCK_SCREEN_REDACT_NOTIFICATION_TITLE =
+ PREFIX + "WORK_PROFILE_LOCK_SCREEN_REDACT_NOTIFICATION_TITLE";
+
+ /**
+ * Summary for redacting sensitive content on lockscreen for work profiles
+ */
+ public static final String WORK_PROFILE_LOCK_SCREEN_REDACT_NOTIFICATION_SUMMARY =
+ PREFIX + "WORK_PROFILE_LOCK_SCREEN_REDACT_NOTIFICATION_SUMMARY";
+
+ /**
+ * Indicates that the work profile admin doesn't allow this notification listener to
+ * access
+ * work profile notifications
+ */
+ public static final String WORK_PROFILE_NOTIFICATION_LISTENER_BLOCKED =
+ PREFIX + "WORK_PROFILE_NOTIFICATION_LISTENER_BLOCKED";
+
+ /**
+ * This setting shows a user's connected work and personal apps.
+ */
+ public static final String CONNECTED_WORK_AND_PERSONAL_APPS_TITLE =
+ PREFIX + "CONNECTED_WORK_AND_PERSONAL_APPS_TITLE";
+
+ /**
+ * This text lets a user know that if they connect work and personal apps,
+ * they will share permissions and can access each other's data
+ */
+ public static final String CONNECTED_APPS_SHARE_PERMISSIONS_AND_DATA =
+ PREFIX + "CONNECTED_APPS_SHARE_PERMISSIONS_AND_DATA";
+
+ /**
+ * This text lets a user know that they should only connect work and personal apps if
+ * they
+ * trust the work app with their personal data
+ */
+ public static final String ONLY_CONNECT_TRUSTED_APPS =
+ PREFIX + "ONLY_CONNECT_TRUSTED_APPS";
+
+ /**
+ * This text lets a user know how to disconnect work and personal apps
+ */
+ public static final String HOW_TO_DISCONNECT_APPS = PREFIX + "HOW_TO_DISCONNECT_APPS";
+
+ /**
+ * Title of confirmation dialog when connecting work and personal apps
+ */
+ public static final String CONNECT_APPS_DIALOG_TITLE =
+ PREFIX + "CONNECT_APPS_DIALOG_TITLE";
+
+ /**
+ * This dialog is shown when a user tries to connect a work app to a personal
+ * app
+ */
+ public static final String CONNECT_APPS_DIALOG_SUMMARY =
+ PREFIX + "CONNECT_APPS_DIALOG_SUMMARY";
+
+ /**
+ * This text lets the user know that their work app will be able to access data in their
+ * personal app
+ */
+ public static final String APP_CAN_ACCESS_PERSONAL_DATA =
+ PREFIX + "APP_CAN_ACCESS_PERSONAL_DATA";
+
+ /**
+ * This text lets the user know that their work app will be able to use permissions in
+ * their personal app
+ */
+ public static final String APP_CAN_ACCESS_PERSONAL_PERMISSIONS =
+ PREFIX + "APP_CAN_ACCESS_PERSONAL_PERMISSIONS";
+
+ /**
+ * lets a user know that they need to install an app in their work profile in order to
+ * connect it to the corresponding personal app
+ */
+ public static final String INSTALL_IN_WORK_PROFILE_TO_CONNECT_PROMPT =
+ PREFIX + "INSTALL_IN_WORK_PROFILE_TO_CONNECT_PROMPT";
+
+ /**
+ * lets a user know that they need to install an app in their personal profile in order
+ * to
+ * connect it to the corresponding work app
+ */
+ public static final String INSTALL_IN_PERSONAL_PROFILE_TO_CONNECT_PROMPT =
+ PREFIX + "INSTALL_IN_PERSONAL_PROFILE_TO_CONNECT_PROMPT";
+
+ /**
+ * Header for showing the organisation managing the work profile
+ */
+ public static final String WORK_PROFILE_MANAGED_BY = PREFIX + "WORK_PROFILE_MANAGED_BY";
+
+ /**
+ * Summary showing the enterprise who manages the device or profile.
+ */
+ public static final String MANAGED_BY = PREFIX + "MANAGED_BY";
+
+ /**
+ * Warning message about disabling usage access on profile owner
+ */
+ public static final String WORK_PROFILE_DISABLE_USAGE_ACCESS_WARNING =
+ PREFIX + "WORK_PROFILE_DISABLE_USAGE_ACCESS_WARNING";
+
+ /**
+ * Title for dialog displayed when user taps a setting on their phone that's blocked by
+ * their IT admin
+ */
+ public static final String DISABLED_BY_IT_ADMIN_TITLE =
+ PREFIX + "DISABLED_BY_IT_ADMIN_TITLE";
+
+ /**
+ * Shown when the user tries to change phone settings that are blocked by their IT admin
+ */
+ public static final String CONTACT_YOUR_IT_ADMIN = PREFIX + "CONTACT_YOUR_IT_ADMIN";
+
+ /**
+ * warn user about policies the admin can set in a work profile
+ */
+ public static final String WORK_PROFILE_ADMIN_POLICIES_WARNING =
+ PREFIX + "WORK_PROFILE_ADMIN_POLICIES_WARNING";
+
+ /**
+ * warn user about policies the admin can set on a user
+ */
+ public static final String USER_ADMIN_POLICIES_WARNING =
+ PREFIX + "USER_ADMIN_POLICIES_WARNING";
+
+ /**
+ * warn user about policies the admin can set on a device
+ */
+ public static final String DEVICE_ADMIN_POLICIES_WARNING =
+ PREFIX + "DEVICE_ADMIN_POLICIES_WARNING";
+
+ /**
+ * Condition that work profile is off
+ */
+ public static final String WORK_PROFILE_OFF_CONDITION_TITLE =
+ PREFIX + "WORK_PROFILE_OFF_CONDITION_TITLE";
+
+ /**
+ * Title of work profile setting page
+ */
+ public static final String MANAGED_PROFILE_SETTINGS_TITLE =
+ PREFIX + "MANAGED_PROFILE_SETTINGS_TITLE";
+
+ /**
+ * Setting that lets a user's personal apps identify contacts using the user's work
+ * directory
+ */
+ public static final String WORK_PROFILE_CONTACT_SEARCH_TITLE =
+ PREFIX + "WORK_PROFILE_CONTACT_SEARCH_TITLE";
+
+ /**
+ * This setting lets a user's personal apps identify contacts using the user's work
+ * directory
+ */
+ public static final String WORK_PROFILE_CONTACT_SEARCH_SUMMARY =
+ PREFIX + "WORK_PROFILE_CONTACT_SEARCH_SUMMARY";
+
+ /**
+ * This setting lets the user show their work events on their personal calendar
+ */
+ public static final String CROSS_PROFILE_CALENDAR_TITLE =
+ PREFIX + "CROSS_PROFILE_CALENDAR_TITLE";
+
+ /**
+ * Setting description. If the user turns on this setting, they can see their work
+ * events on their personal calendar
+ */
+ public static final String CROSS_PROFILE_CALENDAR_SUMMARY =
+ PREFIX + "CROSS_PROFILE_CALENDAR_SUMMARY";
+
+ /**
+ * Label explaining that an always-on VPN was set by the admin in the personal profile
+ */
+ public static final String ALWAYS_ON_VPN_PERSONAL_PROFILE =
+ PREFIX + "ALWAYS_ON_VPN_PERSONAL_PROFILE";
+
+ /**
+ * Label explaining that an always-on VPN was set by the admin for the entire device
+ */
+ public static final String ALWAYS_ON_VPN_DEVICE = PREFIX + "ALWAYS_ON_VPN_DEVICE";
+
+ /**
+ * Label explaining that an always-on VPN was set by the admin in the work profile
+ */
+ public static final String ALWAYS_ON_VPN_WORK_PROFILE =
+ PREFIX + "ALWAYS_ON_VPN_WORK_PROFILE";
+
+ /**
+ * Label explaining that the admin installed trusted CA certificates in personal profile
+ */
+ public static final String CA_CERTS_PERSONAL_PROFILE =
+ PREFIX + "CA_CERTS_PERSONAL_PROFILE";
+
+ /**
+ * Label explaining that the admin installed trusted CA certificates in work profile
+ */
+ public static final String CA_CERTS_WORK_PROFILE = PREFIX + "CA_CERTS_WORK_PROFILE";
+
+ /**
+ * Label explaining that the admin installed trusted CA certificates for the entire
+ * device
+ */
+ public static final String CA_CERTS_DEVICE = PREFIX + "CA_CERTS_DEVICE";
+
+ /**
+ * Label explaining that the admin can lock the device and change the user's password
+ */
+ public static final String ADMIN_CAN_LOCK_DEVICE = PREFIX + "ADMIN_CAN_LOCK_DEVICE";
+
+ /**
+ * Label explaining that the admin can wipe the device remotely
+ */
+ public static final String ADMIN_CAN_WIPE_DEVICE = PREFIX + "ADMIN_CAN_WIPE_DEVICE";
+
+ /**
+ * Label explaining that the admin configured the device to wipe itself when the
+ * password is mistyped too many times
+ */
+ public static final String ADMIN_CONFIGURED_FAILED_PASSWORD_WIPE_DEVICE =
+ PREFIX + "ADMIN_CONFIGURED_FAILED_PASSWORD_WIPE_DEVICE";
+
+ /**
+ * Label explaining that the admin configured the work profile to wipe itself when the
+ * password is mistyped too many times
+ */
+ public static final String ADMIN_CONFIGURED_FAILED_PASSWORD_WIPE_WORK_PROFILE =
+ PREFIX + "ADMIN_CONFIGURED_FAILED_PASSWORD_WIPE_WORK_PROFILE";
+
+ /**
+ * Message indicating that the device is enterprise-managed by a Device Owner
+ */
+ public static final String DEVICE_MANAGED_WITHOUT_NAME =
+ PREFIX + "DEVICE_MANAGED_WITHOUT_NAME";
+
+ /**
+ * Message indicating that the device is enterprise-managed by a Device Owner
+ */
+ public static final String DEVICE_MANAGED_WITH_NAME =
+ PREFIX + "DEVICE_MANAGED_WITH_NAME";
+
+ /**
+ * Subtext of work profile app for current setting
+ */
+ public static final String WORK_PROFILE_APP_SUBTEXT =
+ PREFIX + "WORK_PROFILE_APP_SUBTEXT";
+
+ /**
+ * Subtext of personal profile app for current setting
+ */
+ public static final String PERSONAL_PROFILE_APP_SUBTEXT =
+ PREFIX + "PERSONAL_PROFILE_APP_SUBTEXT";
+
+ /**
+ * Title shown for work menu item that launches fingerprint settings or enrollment
+ */
+ public static final String FINGERPRINT_FOR_WORK = PREFIX + "FINGERPRINT_FOR_WORK";
+
+ /**
+ * Message shown in face enrollment dialog, when face unlock is disabled by device admin
+ */
+ public static final String FACE_UNLOCK_DISABLED = PREFIX + "FACE_UNLOCK_DISABLED";
+
+ /**
+ * message shown in fingerprint enrollment dialog, when fingerprint unlock is disabled
+ * by device admin
+ */
+ public static final String FINGERPRINT_UNLOCK_DISABLED =
+ PREFIX + "FINGERPRINT_UNLOCK_DISABLED";
+
+ /**
+ * Text shown in fingerprint settings explaining what the fingerprint can be used for in
+ * the case unlocking is disabled
+ */
+ public static final String FINGERPRINT_UNLOCK_DISABLED_EXPLANATION =
+ PREFIX + "FINGERPRINT_UNLOCK_DISABLED_EXPLANATION";
+
+ /**
+ * Error shown when in PIN mode and PIN has been used recently
+ */
+ public static final String PIN_RECENTLY_USED = PREFIX + "PIN_RECENTLY_USED";
+
+ /**
+ * Error shown when in PASSWORD mode and password has been used recently
+ */
+ public static final String PASSWORD_RECENTLY_USED = PREFIX + "PASSWORD_RECENTLY_USED";
+
+ /**
+ * Title of preference to manage device admin apps
+ */
+ public static final String MANAGE_DEVICE_ADMIN_APPS =
+ PREFIX + "MANAGE_DEVICE_ADMIN_APPS";
+
+ /**
+ * Inform the user that currently no device admin apps are installed and active
+ */
+ public static final String NUMBER_OF_DEVICE_ADMINS_NONE =
+ PREFIX + "NUMBER_OF_DEVICE_ADMINS_NONE";
+
+ /**
+ * Inform the user how many device admin apps are installed and active
+ */
+ public static final String NUMBER_OF_DEVICE_ADMINS = PREFIX + "NUMBER_OF_DEVICE_ADMINS";
+
+ /**
+ * Title that asks the user to contact the IT admin to reset password
+ */
+ public static final String FORGOT_PASSWORD_TITLE = PREFIX + "FORGOT_PASSWORD_TITLE";
+
+ /**
+ * Content that asks the user to contact the IT admin to reset password
+ */
+ public static final String FORGOT_PASSWORD_TEXT = PREFIX + "FORGOT_PASSWORD_TEXT";
+
+ /**
+ * Error message shown when trying to move device administrators to external disks, such
+ * as SD card
+ */
+ public static final String ERROR_MOVE_DEVICE_ADMIN = PREFIX + "ERROR_MOVE_DEVICE_ADMIN";
+
+ /**
+ * Device admin app settings title
+ */
+ public static final String DEVICE_ADMIN_SETTINGS_TITLE =
+ PREFIX + "DEVICE_ADMIN_SETTINGS_TITLE";
+
+ /**
+ * Button to remove the active device admin app
+ */
+ public static final String REMOVE_DEVICE_ADMIN = PREFIX + "REMOVE_DEVICE_ADMIN";
+
+ /**
+ * Button to uninstall the device admin app
+ */
+ public static final String UNINSTALL_DEVICE_ADMIN = PREFIX + "UNINSTALL_DEVICE_ADMIN";
+
+ /**
+ * Button to deactivate and uninstall the device admin app
+ */
+ public static final String REMOVE_AND_UNINSTALL_DEVICE_ADMIN =
+ PREFIX + "REMOVE_AND_UNINSTALL_DEVICE_ADMIN";
+
+ /**
+ * Title for selecting device admin apps
+ */
+ public static final String SELECT_DEVICE_ADMIN_APPS =
+ PREFIX + "SELECT_DEVICE_ADMIN_APPS";
+
+ /**
+ * Message when there are no available device admin apps to display
+ */
+ public static final String NO_DEVICE_ADMINS = PREFIX + "NO_DEVICE_ADMINS";
+
+ /**
+ * Title for screen to add a device admin app
+ */
+ public static final String ACTIVATE_DEVICE_ADMIN_APP =
+ PREFIX + "ACTIVATE_DEVICE_ADMIN_APP";
+
+ /**
+ * Label for button to set the active device admin
+ */
+ public static final String ACTIVATE_THIS_DEVICE_ADMIN_APP =
+ PREFIX + "ACTIVATE_THIS_DEVICE_ADMIN_APP";
+
+ /**
+ * Activate a specific device admin app title
+ */
+ public static final String ACTIVATE_DEVICE_ADMIN_APP_TITLE =
+ PREFIX + "ACTIVATE_DEVICE_ADMIN_APP_TITLE";
+
+ /**
+ * Device admin warning message about policies a not active admin can use
+ */
+ public static final String NEW_DEVICE_ADMIN_WARNING =
+ PREFIX + "NEW_DEVICE_ADMIN_WARNING";
+
+ /**
+ * Simplified device admin warning message
+ */
+ public static final String NEW_DEVICE_ADMIN_WARNING_SIMPLIFIED =
+ PREFIX + "NEW_DEVICE_ADMIN_WARNING_SIMPLIFIED";
+
+ /**
+ * Device admin warning message about policies the active admin can use
+ */
+ public static final String ACTIVE_DEVICE_ADMIN_WARNING =
+ PREFIX + "ACTIVE_DEVICE_ADMIN_WARNING";
+
+ /**
+ * Title for screen to set a profile owner
+ */
+ public static final String SET_PROFILE_OWNER_TITLE = PREFIX + "SET_PROFILE_OWNER_TITLE";
+
+ /**
+ * Simplified title for dialog to set a profile owner
+ */
+ public static final String SET_PROFILE_OWNER_DIALOG_TITLE =
+ PREFIX + "SET_PROFILE_OWNER_DIALOG_TITLE";
+
+ /**
+ * Warning when trying to add a profile owner admin after setup has completed
+ */
+ public static final String SET_PROFILE_OWNER_POSTSETUP_WARNING =
+ PREFIX + "SET_PROFILE_OWNER_POSTSETUP_WARNING";
+
+ /**
+ * Message displayed to let the user know that some of the options are disabled by admin
+ */
+ public static final String OTHER_OPTIONS_DISABLED_BY_ADMIN =
+ PREFIX + "OTHER_OPTIONS_DISABLED_BY_ADMIN";
+
+ /**
+ * This is shown if the authenticator for a given account fails to remove it due to
+ * admin restrictions
+ */
+ public static final String REMOVE_ACCOUNT_FAILED_ADMIN_RESTRICTION =
+ PREFIX + "REMOVE_ACCOUNT_FAILED_ADMIN_RESTRICTION";
+
+ /**
+ * Url for learning more about IT admin policy disabling
+ */
+ public static final String IT_ADMIN_POLICY_DISABLING_INFO_URL =
+ PREFIX + "IT_ADMIN_POLICY_DISABLING_INFO_URL";
+
+ /**
+ * Title of dialog shown to ask for user consent for sharing a bugreport that was
+ * requested
+ * remotely by the IT administrator
+ */
+ public static final String SHARE_REMOTE_BUGREPORT_DIALOG_TITLE =
+ PREFIX + "SHARE_REMOTE_BUGREPORT_DIALOG_TITLE";
+
+ /**
+ * Message of a dialog shown to ask for user consent for sharing a bugreport that was
+ * requested remotely by the IT administrator
+ */
+ public static final String SHARE_REMOTE_BUGREPORT_FINISHED_REQUEST_CONSENT =
+ PREFIX + "SHARE_REMOTE_BUGREPORT_FINISHED_REQUEST_CONSENT";
+
+ /**
+ * Message of a dialog shown to ask for user consent for sharing a bugreport that was
+ * requested remotely by the IT administrator and it's still being taken
+ */
+ public static final String SHARE_REMOTE_BUGREPORT_NOT_FINISHED_REQUEST_CONSENT =
+ PREFIX + "SHARE_REMOTE_BUGREPORT_NOT_FINISHED_REQUEST_CONSENT";
+
+ /**
+ * Message of a dialog shown to inform that the remote bugreport that was requested
+ * remotely by the IT administrator is still being taken and will be shared when
+ * finished
+ */
+ public static final String SHARING_REMOTE_BUGREPORT_MESSAGE =
+ PREFIX + "SHARING_REMOTE_BUGREPORT_MESSAGE";
+
+ /**
+ * Managed device information screen title
+ */
+ public static final String MANAGED_DEVICE_INFO = PREFIX + "MANAGED_DEVICE_INFO";
+
+ /**
+ * Summary for managed device info section
+ */
+ public static final String MANAGED_DEVICE_INFO_SUMMARY =
+ PREFIX + "MANAGED_DEVICE_INFO_SUMMARY";
+
+ /**
+ * Summary for managed device info section including organization name
+ */
+ public static final String MANAGED_DEVICE_INFO_SUMMARY_WITH_NAME =
+ PREFIX + "MANAGED_DEVICE_INFO_SUMMARY_WITH_NAME";
+
+ /**
+ * Enterprise Privacy settings header, summarizing the powers that the admin has
+ */
+ public static final String ENTERPRISE_PRIVACY_HEADER =
+ PREFIX + "ENTERPRISE_PRIVACY_HEADER";
+
+ /**
+ * Types of information your organization can see section title
+ */
+ public static final String INFORMATION_YOUR_ORGANIZATION_CAN_SEE_TITLE =
+ PREFIX + "INFORMATION_YOUR_ORGANIZATION_CAN_SEE_TITLE";
+
+ /**
+ * Changes made by your organization's admin section title
+ */
+ public static final String CHANGES_MADE_BY_YOUR_ORGANIZATION_ADMIN_TITLE =
+ PREFIX + "CHANGES_MADE_BY_YOUR_ORGANIZATION_ADMIN_TITLE";
+
+ /**
+ * Your access to this device section title
+ */
+ public static final String YOUR_ACCESS_TO_THIS_DEVICE_TITLE =
+ PREFIX + "YOUR_ACCESS_TO_THIS_DEVICE_TITLE";
+
+ /**
+ * Things the admin can see: data associated with the work account
+ */
+ public static final String ADMIN_CAN_SEE_WORK_DATA_WARNING =
+ PREFIX + "ADMIN_CAN_SEE_WORK_DATA_WARNING";
+
+ /**
+ * Things the admin can see: Apps installed on the device
+ */
+ public static final String ADMIN_CAN_SEE_APPS_WARNING =
+ PREFIX + "ADMIN_CAN_SEE_APPS_WARNING";
+
+ /**
+ * Things the admin can see: Amount of time and data spent in each app
+ */
+ public static final String ADMIN_CAN_SEE_USAGE_WARNING =
+ PREFIX + "ADMIN_CAN_SEE_USAGE_WARNING";
+
+ /**
+ * Things the admin can see: Most recent network traffic log
+ */
+ public static final String ADMIN_CAN_SEE_NETWORK_LOGS_WARNING =
+ PREFIX + "ADMIN_CAN_SEE_NETWORK_LOGS_WARNING";
+ /**
+ * Things the admin can see: Most recent bug report
+ */
+ public static final String ADMIN_CAN_SEE_BUG_REPORT_WARNING =
+ PREFIX + "ADMIN_CAN_SEE_BUG_REPORT_WARNING";
+
+ /**
+ * Things the admin can see: Security logs
+ */
+ public static final String ADMIN_CAN_SEE_SECURITY_LOGS_WARNING =
+ PREFIX + "ADMIN_CAN_SEE_SECURITY_LOGS_WARNING";
+
+ /**
+ * Indicate that the admin never took a given action so far (e.g. did not retrieve
+ * security logs or request bug reports).
+ */
+ public static final String ADMIN_ACTION_NONE = PREFIX + "ADMIN_ACTION_NONE";
+
+ /**
+ * Indicate that the admin installed one or more apps on the device
+ */
+ public static final String ADMIN_ACTION_APPS_INSTALLED =
+ PREFIX + "ADMIN_ACTION_APPS_INSTALLED";
+
+ /**
+ * Explaining that the number of apps is an estimation
+ */
+ public static final String ADMIN_ACTION_APPS_COUNT_ESTIMATED =
+ PREFIX + "ADMIN_ACTION_APPS_COUNT_ESTIMATED";
+
+ /**
+ * Indicating the minimum number of apps that a label refers to
+ */
+ public static final String ADMIN_ACTIONS_APPS_COUNT_MINIMUM =
+ PREFIX + "ADMIN_ACTIONS_APPS_COUNT_MINIMUM";
+
+ /**
+ * Indicate that the admin granted one or more apps access to the device's location
+ */
+ public static final String ADMIN_ACTION_ACCESS_LOCATION =
+ PREFIX + "ADMIN_ACTION_ACCESS_LOCATION";
+
+ /**
+ * Indicate that the admin granted one or more apps access to the microphone
+ */
+ public static final String ADMIN_ACTION_ACCESS_MICROPHONE =
+ PREFIX + "ADMIN_ACTION_ACCESS_MICROPHONE";
+
+ /**
+ * Indicate that the admin granted one or more apps access to the camera
+ */
+ public static final String ADMIN_ACTION_ACCESS_CAMERA =
+ PREFIX + "ADMIN_ACTION_ACCESS_CAMERA";
+
+ /**
+ * Indicate that the admin set one or more apps as defaults for common actions
+ */
+ public static final String ADMIN_ACTION_SET_DEFAULT_APPS =
+ PREFIX + "ADMIN_ACTION_SET_DEFAULT_APPS";
+
+ /**
+ * Indicate the number of apps that a label refers to
+ */
+ public static final String ADMIN_ACTIONS_APPS_COUNT =
+ PREFIX + "ADMIN_ACTIONS_APPS_COUNT";
+
+ /**
+ * Indicate that the current input method was set by the admin
+ */
+ public static final String ADMIN_ACTION_SET_CURRENT_INPUT_METHOD =
+ PREFIX + "ADMIN_ACTION_SET_CURRENT_INPUT_METHOD";
+
+ /**
+ * The input method set by the admin
+ */
+ public static final String ADMIN_ACTION_SET_INPUT_METHOD_NAME =
+ PREFIX + "ADMIN_ACTION_SET_INPUT_METHOD_NAME";
+
+ /**
+ * Indicate that a global HTTP proxy was set by the admin
+ */
+ public static final String ADMIN_ACTION_SET_HTTP_PROXY =
+ PREFIX + "ADMIN_ACTION_SET_HTTP_PROXY";
+
+ /**
+ * Summary for Enterprise Privacy settings, explaining what the user can expect to find
+ * under it
+ */
+ public static final String WORK_PROFILE_PRIVACY_POLICY_INFO_SUMMARY =
+ PREFIX + "WORK_PROFILE_PRIVACY_POLICY_INFO_SUMMARY";
+
+ /**
+ * Setting on privacy settings screen that will show work policy info
+ */
+ public static final String WORK_PROFILE_PRIVACY_POLICY_INFO =
+ PREFIX + "WORK_PROFILE_PRIVACY_POLICY_INFO";
+
+ /**
+ * Search keywords for connected work and personal apps
+ */
+ public static final String CONNECTED_APPS_SEARCH_KEYWORDS =
+ PREFIX + "CONNECTED_APPS_SEARCH_KEYWORDS";
+
+ /**
+ * Work profile unification keywords
+ */
+ public static final String WORK_PROFILE_UNIFICATION_SEARCH_KEYWORDS =
+ PREFIX + "WORK_PROFILE_UNIFICATION_SEARCH_KEYWORDS";
+
+ /**
+ * Accounts keywords
+ */
+ public static final String ACCOUNTS_SEARCH_KEYWORDS =
+ PREFIX + "ACCOUNTS_SEARCH_KEYWORDS";
+
+ /**
+ * Summary for settings preference disabled by administrator
+ */
+ public static final String CONTROLLED_BY_ADMIN_SUMMARY =
+ PREFIX + "CONTROLLED_BY_ADMIN_SUMMARY";
+
+ /**
+ * User label for a work profile
+ */
+ public static final String WORK_PROFILE_USER_LABEL = PREFIX + "WORK_PROFILE_USER_LABEL";
+
+ /**
+ * Header for items under the work user
+ */
+ public static final String WORK_CATEGORY_HEADER = PREFIX + "WORK_CATEGORY_HEADER";
+
+ /**
+ * Header for items under the personal user
+ */
+ public static final String PERSONAL_CATEGORY_HEADER = PREFIX + "category_personal";
+
+ /**
+ * @hide
+ */
+ static Set<String> buildStringsSet() {
+ Set<String> strings = new HashSet<>();
+ strings.add(FACE_SETTINGS_FOR_WORK_TITLE);
+ strings.add(WORK_PROFILE_FINGERPRINT_LAST_DELETE_MESSAGE);
+ strings.add(WORK_PROFILE_IT_ADMIN_CANT_RESET_SCREEN_LOCK);
+ strings.add(WORK_PROFILE_SCREEN_LOCK_SETUP_MESSAGE);
+ strings.add(WORK_PROFILE_SET_UNLOCK_LAUNCH_PICKER_TITLE);
+ strings.add(WORK_PROFILE_LAST_PATTERN_ATTEMPT_BEFORE_WIPE);
+ strings.add(WORK_PROFILE_LAST_PIN_ATTEMPT_BEFORE_WIPE);
+ strings.add(WORK_PROFILE_LAST_PASSWORD_ATTEMPT_BEFORE_WIPE);
+ strings.add(WORK_PROFILE_LOCK_ATTEMPTS_FAILED);
+ strings.add(ACCESSIBILITY_CATEGORY_WORK);
+ strings.add(ACCESSIBILITY_CATEGORY_PERSONAL);
+ strings.add(ACCESSIBILITY_WORK_ACCOUNT_TITLE);
+ strings.add(ACCESSIBILITY_PERSONAL_ACCOUNT_TITLE);
+ strings.add(WORK_PROFILE_LOCATION_SWITCH_TITLE);
+ strings.add(SET_WORK_PROFILE_PASSWORD_HEADER);
+ strings.add(SET_WORK_PROFILE_PIN_HEADER);
+ strings.add(SET_WORK_PROFILE_PATTERN_HEADER);
+ strings.add(CONFIRM_WORK_PROFILE_PASSWORD_HEADER);
+ strings.add(CONFIRM_WORK_PROFILE_PIN_HEADER);
+ strings.add(CONFIRM_WORK_PROFILE_PATTERN_HEADER);
+ strings.add(REENTER_WORK_PROFILE_PASSWORD_HEADER);
+ strings.add(REENTER_WORK_PROFILE_PIN_HEADER);
+ strings.add(WORK_PROFILE_CONFIRM_PATTERN);
+ strings.add(WORK_PROFILE_CONFIRM_PIN);
+ strings.add(WORK_PROFILE_PASSWORD_REQUIRED);
+ strings.add(WORK_PROFILE_SECURITY_TITLE);
+ strings.add(WORK_PROFILE_UNIFY_LOCKS_TITLE);
+ strings.add(WORK_PROFILE_UNIFY_LOCKS_SUMMARY);
+ strings.add(WORK_PROFILE_UNIFY_LOCKS_DETAIL);
+ strings.add(WORK_PROFILE_UNIFY_LOCKS_NONCOMPLIANT);
+ strings.add(WORK_PROFILE_KEYBOARDS_AND_TOOLS);
+ strings.add(WORK_PROFILE_NOT_AVAILABLE);
+ strings.add(WORK_PROFILE_SETTING);
+ strings.add(WORK_PROFILE_SETTING_ON_SUMMARY);
+ strings.add(WORK_PROFILE_SETTING_OFF_SUMMARY);
+ strings.add(REMOVE_WORK_PROFILE);
+ strings.add(DEVICE_OWNER_INSTALLED_CERTIFICATE_AUTHORITY_WARNING);
+ strings.add(WORK_PROFILE_INSTALLED_CERTIFICATE_AUTHORITY_WARNING);
+ strings.add(WORK_PROFILE_CONFIRM_REMOVE_TITLE);
+ strings.add(WORK_PROFILE_CONFIRM_REMOVE_MESSAGE);
+ strings.add(WORK_APPS_CANNOT_ACCESS_NOTIFICATION_SETTINGS);
+ strings.add(WORK_PROFILE_SOUND_SETTINGS_SECTION_HEADER);
+ strings.add(WORK_PROFILE_USE_PERSONAL_SOUNDS_TITLE);
+ strings.add(WORK_PROFILE_USE_PERSONAL_SOUNDS_SUMMARY);
+ strings.add(WORK_PROFILE_RINGTONE_TITLE);
+ strings.add(WORK_PROFILE_NOTIFICATION_RINGTONE_TITLE);
+ strings.add(WORK_PROFILE_ALARM_RINGTONE_TITLE);
+ strings.add(WORK_PROFILE_SYNC_WITH_PERSONAL_SOUNDS_ACTIVE_SUMMARY);
+ strings.add(ENABLE_WORK_PROFILE_SYNC_WITH_PERSONAL_SOUNDS_DIALOG_TITLE);
+ strings.add(ENABLE_WORK_PROFILE_SYNC_WITH_PERSONAL_SOUNDS_DIALOG_MESSAGE);
+ strings.add(WORK_PROFILE_NOTIFICATIONS_SECTION_HEADER);
+ strings.add(WORK_PROFILE_LOCKED_NOTIFICATION_TITLE);
+ strings.add(WORK_PROFILE_LOCK_SCREEN_REDACT_NOTIFICATION_TITLE);
+ strings.add(WORK_PROFILE_LOCK_SCREEN_REDACT_NOTIFICATION_SUMMARY);
+ strings.add(WORK_PROFILE_NOTIFICATION_LISTENER_BLOCKED);
+ strings.add(CONNECTED_WORK_AND_PERSONAL_APPS_TITLE);
+ strings.add(CONNECTED_APPS_SHARE_PERMISSIONS_AND_DATA);
+ strings.add(ONLY_CONNECT_TRUSTED_APPS);
+ strings.add(HOW_TO_DISCONNECT_APPS);
+ strings.add(CONNECT_APPS_DIALOG_TITLE);
+ strings.add(CONNECT_APPS_DIALOG_SUMMARY);
+ strings.add(APP_CAN_ACCESS_PERSONAL_DATA);
+ strings.add(APP_CAN_ACCESS_PERSONAL_PERMISSIONS);
+ strings.add(INSTALL_IN_WORK_PROFILE_TO_CONNECT_PROMPT);
+ strings.add(INSTALL_IN_PERSONAL_PROFILE_TO_CONNECT_PROMPT);
+ strings.add(WORK_PROFILE_MANAGED_BY);
+ strings.add(MANAGED_BY);
+ strings.add(WORK_PROFILE_DISABLE_USAGE_ACCESS_WARNING);
+ strings.add(DISABLED_BY_IT_ADMIN_TITLE);
+ strings.add(CONTACT_YOUR_IT_ADMIN);
+ strings.add(WORK_PROFILE_ADMIN_POLICIES_WARNING);
+ strings.add(USER_ADMIN_POLICIES_WARNING);
+ strings.add(DEVICE_ADMIN_POLICIES_WARNING);
+ strings.add(WORK_PROFILE_OFF_CONDITION_TITLE);
+ strings.add(MANAGED_PROFILE_SETTINGS_TITLE);
+ strings.add(WORK_PROFILE_CONTACT_SEARCH_TITLE);
+ strings.add(WORK_PROFILE_CONTACT_SEARCH_SUMMARY);
+ strings.add(CROSS_PROFILE_CALENDAR_TITLE);
+ strings.add(CROSS_PROFILE_CALENDAR_SUMMARY);
+ strings.add(ALWAYS_ON_VPN_PERSONAL_PROFILE);
+ strings.add(ALWAYS_ON_VPN_DEVICE);
+ strings.add(ALWAYS_ON_VPN_WORK_PROFILE);
+ strings.add(CA_CERTS_PERSONAL_PROFILE);
+ strings.add(CA_CERTS_WORK_PROFILE);
+ strings.add(CA_CERTS_DEVICE);
+ strings.add(ADMIN_CAN_LOCK_DEVICE);
+ strings.add(ADMIN_CAN_WIPE_DEVICE);
+ strings.add(ADMIN_CONFIGURED_FAILED_PASSWORD_WIPE_DEVICE);
+ strings.add(ADMIN_CONFIGURED_FAILED_PASSWORD_WIPE_WORK_PROFILE);
+ strings.add(DEVICE_MANAGED_WITHOUT_NAME);
+ strings.add(DEVICE_MANAGED_WITH_NAME);
+ strings.add(WORK_PROFILE_APP_SUBTEXT);
+ strings.add(PERSONAL_PROFILE_APP_SUBTEXT);
+ strings.add(FINGERPRINT_FOR_WORK);
+ strings.add(FACE_UNLOCK_DISABLED);
+ strings.add(FINGERPRINT_UNLOCK_DISABLED);
+ strings.add(FINGERPRINT_UNLOCK_DISABLED_EXPLANATION);
+ strings.add(PIN_RECENTLY_USED);
+ strings.add(PASSWORD_RECENTLY_USED);
+ strings.add(MANAGE_DEVICE_ADMIN_APPS);
+ strings.add(NUMBER_OF_DEVICE_ADMINS_NONE);
+ strings.add(NUMBER_OF_DEVICE_ADMINS);
+ strings.add(FORGOT_PASSWORD_TITLE);
+ strings.add(FORGOT_PASSWORD_TEXT);
+ strings.add(ERROR_MOVE_DEVICE_ADMIN);
+ strings.add(DEVICE_ADMIN_SETTINGS_TITLE);
+ strings.add(REMOVE_DEVICE_ADMIN);
+ strings.add(UNINSTALL_DEVICE_ADMIN);
+ strings.add(REMOVE_AND_UNINSTALL_DEVICE_ADMIN);
+ strings.add(SELECT_DEVICE_ADMIN_APPS);
+ strings.add(NO_DEVICE_ADMINS);
+ strings.add(ACTIVATE_DEVICE_ADMIN_APP);
+ strings.add(ACTIVATE_THIS_DEVICE_ADMIN_APP);
+ strings.add(ACTIVATE_DEVICE_ADMIN_APP_TITLE);
+ strings.add(NEW_DEVICE_ADMIN_WARNING);
+ strings.add(NEW_DEVICE_ADMIN_WARNING_SIMPLIFIED);
+ strings.add(ACTIVE_DEVICE_ADMIN_WARNING);
+ strings.add(SET_PROFILE_OWNER_TITLE);
+ strings.add(SET_PROFILE_OWNER_DIALOG_TITLE);
+ strings.add(SET_PROFILE_OWNER_POSTSETUP_WARNING);
+ strings.add(OTHER_OPTIONS_DISABLED_BY_ADMIN);
+ strings.add(REMOVE_ACCOUNT_FAILED_ADMIN_RESTRICTION);
+ strings.add(IT_ADMIN_POLICY_DISABLING_INFO_URL);
+ strings.add(SHARE_REMOTE_BUGREPORT_DIALOG_TITLE);
+ strings.add(SHARE_REMOTE_BUGREPORT_FINISHED_REQUEST_CONSENT);
+ strings.add(SHARE_REMOTE_BUGREPORT_NOT_FINISHED_REQUEST_CONSENT);
+ strings.add(SHARING_REMOTE_BUGREPORT_MESSAGE);
+ strings.add(MANAGED_DEVICE_INFO);
+ strings.add(MANAGED_DEVICE_INFO_SUMMARY);
+ strings.add(MANAGED_DEVICE_INFO_SUMMARY_WITH_NAME);
+ strings.add(ENTERPRISE_PRIVACY_HEADER);
+ strings.add(INFORMATION_YOUR_ORGANIZATION_CAN_SEE_TITLE);
+ strings.add(CHANGES_MADE_BY_YOUR_ORGANIZATION_ADMIN_TITLE);
+ strings.add(YOUR_ACCESS_TO_THIS_DEVICE_TITLE);
+ strings.add(ADMIN_CAN_SEE_WORK_DATA_WARNING);
+ strings.add(ADMIN_CAN_SEE_APPS_WARNING);
+ strings.add(ADMIN_CAN_SEE_USAGE_WARNING);
+ strings.add(ADMIN_CAN_SEE_NETWORK_LOGS_WARNING);
+ strings.add(ADMIN_CAN_SEE_BUG_REPORT_WARNING);
+ strings.add(ADMIN_CAN_SEE_SECURITY_LOGS_WARNING);
+ strings.add(ADMIN_ACTION_NONE);
+ strings.add(ADMIN_ACTION_APPS_INSTALLED);
+ strings.add(ADMIN_ACTION_APPS_COUNT_ESTIMATED);
+ strings.add(ADMIN_ACTIONS_APPS_COUNT_MINIMUM);
+ strings.add(ADMIN_ACTION_ACCESS_LOCATION);
+ strings.add(ADMIN_ACTION_ACCESS_MICROPHONE);
+ strings.add(ADMIN_ACTION_ACCESS_CAMERA);
+ strings.add(ADMIN_ACTION_SET_DEFAULT_APPS);
+ strings.add(ADMIN_ACTIONS_APPS_COUNT);
+ strings.add(ADMIN_ACTION_SET_CURRENT_INPUT_METHOD);
+ strings.add(ADMIN_ACTION_SET_INPUT_METHOD_NAME);
+ strings.add(ADMIN_ACTION_SET_HTTP_PROXY);
+ strings.add(WORK_PROFILE_PRIVACY_POLICY_INFO_SUMMARY);
+ strings.add(WORK_PROFILE_PRIVACY_POLICY_INFO);
+ strings.add(CONNECTED_APPS_SEARCH_KEYWORDS);
+ strings.add(WORK_PROFILE_UNIFICATION_SEARCH_KEYWORDS);
+ strings.add(ACCOUNTS_SEARCH_KEYWORDS);
+ strings.add(CONTROLLED_BY_ADMIN_SUMMARY);
+ strings.add(WORK_PROFILE_USER_LABEL);
+ strings.add(WORK_CATEGORY_HEADER);
+ strings.add(PERSONAL_CATEGORY_HEADER);
+ return strings;
+ }
+ }
+
+ /**
+ * Class containing the identifiers used to update device management-related system strings
* in the Launcher package.
*
* @hide
*/
public static final class Launcher {
- private Launcher(){}
+ private Launcher() {
+ }
private static final String PREFIX = "Launcher.";
@@ -576,6 +2030,7 @@ public final class DevicePolicyResources {
private SystemUi() {
}
+
private static final String PREFIX = "SystemUi.";
/**
@@ -649,9 +2104,9 @@ public final class DevicePolicyResources {
PREFIX + "QS_MSG_NAMED_WORK_PROFILE_MONITORING";
/**
- * Disclosure at the bottom of Quick Settings to indicate network activity is visible to
+ * Disclosure at the bottom of Quick Settings to indicate network activity is visible to
* admin.
- */
+ */
public static final String QS_MSG_WORK_PROFILE_NETWORK =
PREFIX + "QS_MSG_WORK_PROFILE_NETWORK";
@@ -1413,5 +2868,71 @@ public final class DevicePolicyResources {
return strings;
}
}
+
+ /**
+ * Class containing the identifiers used to update device management-related system strings
+ * in the PermissionController module.
+ */
+ public static final class PermissionController {
+
+ private PermissionController() {
+ }
+
+ private static final String PREFIX = "PermissionController.";
+
+ /**
+ * Title for settings page to show default apps for work.
+ */
+ public static final String WORK_PROFILE_DEFAULT_APPS_TITLE =
+ PREFIX + "WORK_PROFILE_DEFAULT_APPS_TITLE";
+
+ /**
+ * Summary indicating that a home role holder app is missing work profile support.
+ */
+ public static final String HOME_MISSING_WORK_PROFILE_SUPPORT_MESSAGE =
+ PREFIX + "HOME_MISSING_WORK_PROFILE_SUPPORT_MESSAGE";
+
+ /**
+ * Summary of a permission switch in Settings when the background access is denied by an
+ * admin.
+ */
+ public static final String BACKGROUND_ACCESS_DISABLED_BY_ADMIN_MESSAGE =
+ PREFIX + "BACKGROUND_ACCESS_DISABLED_BY_ADMIN_MESSAGE";
+
+ /**
+ * Summary of a permission switch in Settings when the background access is enabled by
+ * an admin.
+ */
+ public static final String BACKGROUND_ACCESS_ENABLED_BY_ADMIN_MESSAGE =
+ PREFIX + "BACKGROUND_ACCESS_ENABLED_BY_ADMIN_MESSAGE";
+
+ /**
+ * Summary of a permission switch in Settings when the foreground access is enabled by
+ * an admin.
+ */
+ public static final String FOREGROUND_ACCESS_ENABLED_BY_ADMIN_MESSAGE =
+ PREFIX + "FOREGROUND_ACCESS_ENABLED_BY_ADMIN_MESSAGE";
+
+ /**
+ * Body of the notification shown to notify the user that the location permission has
+ * been granted to an app, accepts app name as a param.
+ */
+ public static final String LOCATION_AUTO_GRANTED_MESSAGE =
+ PREFIX + "LOCATION_AUTO_GRANTED_MESSAGE";
+
+ /**
+ * @hide
+ */
+ static Set<String> buildStringsSet() {
+ Set<String> strings = new HashSet<>();
+ strings.add(WORK_PROFILE_DEFAULT_APPS_TITLE);
+ strings.add(HOME_MISSING_WORK_PROFILE_SUPPORT_MESSAGE);
+ strings.add(BACKGROUND_ACCESS_DISABLED_BY_ADMIN_MESSAGE);
+ strings.add(BACKGROUND_ACCESS_ENABLED_BY_ADMIN_MESSAGE);
+ strings.add(FOREGROUND_ACCESS_ENABLED_BY_ADMIN_MESSAGE);
+ strings.add(LOCATION_AUTO_GRANTED_MESSAGE);
+ return strings;
+ }
+ }
}
}
diff --git a/core/java/android/app/admin/FullyManagedDeviceProvisioningParams.java b/core/java/android/app/admin/FullyManagedDeviceProvisioningParams.java
index 8c232c012f50..1f7ae4ad35de 100644
--- a/core/java/android/app/admin/FullyManagedDeviceProvisioningParams.java
+++ b/core/java/android/app/admin/FullyManagedDeviceProvisioningParams.java
@@ -25,6 +25,7 @@ import android.annotation.SystemApi;
import android.content.ComponentName;
import android.os.Parcel;
import android.os.Parcelable;
+import android.os.PersistableBundle;
import android.stats.devicepolicy.DevicePolicyEnums;
import java.util.Locale;
@@ -52,6 +53,7 @@ public final class FullyManagedDeviceProvisioningParams implements Parcelable {
@SuppressLint("UseIcu")
@Nullable private final Locale mLocale;
private final boolean mDeviceOwnerCanGrantSensorsPermissions;
+ @NonNull private final PersistableBundle mAdminExtras;
private FullyManagedDeviceProvisioningParams(
@NonNull ComponentName deviceAdminComponentName,
@@ -60,7 +62,8 @@ public final class FullyManagedDeviceProvisioningParams implements Parcelable {
@Nullable String timeZone,
long localTime,
@Nullable @SuppressLint("UseIcu") Locale locale,
- boolean deviceOwnerCanGrantSensorsPermissions) {
+ boolean deviceOwnerCanGrantSensorsPermissions,
+ @NonNull PersistableBundle adminExtras) {
this.mDeviceAdminComponentName = requireNonNull(deviceAdminComponentName);
this.mOwnerName = requireNonNull(ownerName);
this.mLeaveAllSystemAppsEnabled = leaveAllSystemAppsEnabled;
@@ -69,6 +72,7 @@ public final class FullyManagedDeviceProvisioningParams implements Parcelable {
this.mLocale = locale;
this.mDeviceOwnerCanGrantSensorsPermissions =
deviceOwnerCanGrantSensorsPermissions;
+ this.mAdminExtras = adminExtras;
}
private FullyManagedDeviceProvisioningParams(
@@ -78,14 +82,16 @@ public final class FullyManagedDeviceProvisioningParams implements Parcelable {
@Nullable String timeZone,
long localTime,
@Nullable String localeStr,
- boolean deviceOwnerCanGrantSensorsPermissions) {
+ boolean deviceOwnerCanGrantSensorsPermissions,
+ @Nullable PersistableBundle adminExtras) {
this(deviceAdminComponentName,
ownerName,
leaveAllSystemAppsEnabled,
timeZone,
localTime,
getLocale(localeStr),
- deviceOwnerCanGrantSensorsPermissions);
+ deviceOwnerCanGrantSensorsPermissions,
+ adminExtras);
}
@Nullable
@@ -151,6 +157,15 @@ public final class FullyManagedDeviceProvisioningParams implements Parcelable {
}
/**
+ * Returns a copy of the admin extras bundle.
+ *
+ * @see DevicePolicyManager#EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE
+ */
+ public @NonNull PersistableBundle getAdminExtras() {
+ return new PersistableBundle(mAdminExtras);
+ }
+
+ /**
* Logs the provisioning params using {@link DevicePolicyEventLogger}.
*
* @hide
@@ -188,6 +203,7 @@ public final class FullyManagedDeviceProvisioningParams implements Parcelable {
@Nullable private Locale mLocale;
// Default to allowing control over sensor permission grants.
boolean mDeviceOwnerCanGrantSensorsPermissions = true;
+ @NonNull private PersistableBundle mAdminExtras;
/**
* Initialize a new {@link Builder} to construct a
@@ -262,6 +278,17 @@ public final class FullyManagedDeviceProvisioningParams implements Parcelable {
}
/**
+ * Sets a {@link PersistableBundle} that contains admin-specific extras.
+ */
+ @NonNull
+ public Builder setAdminExtras(@NonNull PersistableBundle adminExtras) {
+ mAdminExtras = adminExtras != null
+ ? new PersistableBundle(adminExtras)
+ : new PersistableBundle();
+ return this;
+ }
+
+ /**
* Combines all of the attributes that have been set on this {@code Builder}
*
* @return a new {@link FullyManagedDeviceProvisioningParams} object.
@@ -275,7 +302,8 @@ public final class FullyManagedDeviceProvisioningParams implements Parcelable {
mTimeZone,
mLocalTime,
mLocale,
- mDeviceOwnerCanGrantSensorsPermissions);
+ mDeviceOwnerCanGrantSensorsPermissions,
+ mAdminExtras);
}
}
@@ -298,6 +326,7 @@ public final class FullyManagedDeviceProvisioningParams implements Parcelable {
+ ", mLocale=" + (mLocale == null ? "null" : mLocale)
+ ", mDeviceOwnerCanGrantSensorsPermissions="
+ mDeviceOwnerCanGrantSensorsPermissions
+ + ", mAdminExtras=" + mAdminExtras
+ '}';
}
@@ -310,6 +339,7 @@ public final class FullyManagedDeviceProvisioningParams implements Parcelable {
dest.writeLong(mLocalTime);
dest.writeString(mLocale == null ? null : mLocale.toLanguageTag());
dest.writeBoolean(mDeviceOwnerCanGrantSensorsPermissions);
+ dest.writePersistableBundle(mAdminExtras);
}
@NonNull
@@ -324,6 +354,7 @@ public final class FullyManagedDeviceProvisioningParams implements Parcelable {
long localtime = in.readLong();
String locale = in.readString();
boolean deviceOwnerCanGrantSensorsPermissions = in.readBoolean();
+ PersistableBundle adminExtras = in.readPersistableBundle();
return new FullyManagedDeviceProvisioningParams(
componentName,
@@ -332,7 +363,8 @@ public final class FullyManagedDeviceProvisioningParams implements Parcelable {
timeZone,
localtime,
locale,
- deviceOwnerCanGrantSensorsPermissions);
+ deviceOwnerCanGrantSensorsPermissions,
+ adminExtras);
}
@Override
diff --git a/core/java/android/app/admin/ManagedProfileProvisioningParams.java b/core/java/android/app/admin/ManagedProfileProvisioningParams.java
index ccbef7321be6..f91d60a6a9fa 100644
--- a/core/java/android/app/admin/ManagedProfileProvisioningParams.java
+++ b/core/java/android/app/admin/ManagedProfileProvisioningParams.java
@@ -23,8 +23,10 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.content.ComponentName;
+import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
+import android.os.PersistableBundle;
import android.stats.devicepolicy.DevicePolicyEnums;
/**
@@ -49,7 +51,7 @@ public final class ManagedProfileProvisioningParams implements Parcelable {
private final boolean mLeaveAllSystemAppsEnabled;
private final boolean mOrganizationOwnedProvisioning;
private final boolean mKeepAccountOnMigration;
-
+ @NonNull private final PersistableBundle mAdminExtras;
private ManagedProfileProvisioningParams(
@NonNull ComponentName profileAdminComponentName,
@@ -58,7 +60,8 @@ public final class ManagedProfileProvisioningParams implements Parcelable {
@Nullable Account accountToMigrate,
boolean leaveAllSystemAppsEnabled,
boolean organizationOwnedProvisioning,
- boolean keepAccountOnMigration) {
+ boolean keepAccountOnMigration,
+ @NonNull PersistableBundle adminExtras) {
this.mProfileAdminComponentName = requireNonNull(profileAdminComponentName);
this.mOwnerName = requireNonNull(ownerName);
this.mProfileName = profileName;
@@ -66,6 +69,7 @@ public final class ManagedProfileProvisioningParams implements Parcelable {
this.mLeaveAllSystemAppsEnabled = leaveAllSystemAppsEnabled;
this.mOrganizationOwnedProvisioning = organizationOwnedProvisioning;
this.mKeepAccountOnMigration = keepAccountOnMigration;
+ this.mAdminExtras = adminExtras;
}
/**
@@ -124,6 +128,15 @@ public final class ManagedProfileProvisioningParams implements Parcelable {
}
/**
+ * Returns a copy of the admin extras bundle.
+ *
+ * @see DevicePolicyManager#EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE
+ */
+ public @NonNull PersistableBundle getAdminExtras() {
+ return new PersistableBundle(mAdminExtras);
+ }
+
+ /**
* Logs the provisioning params using {@link DevicePolicyEventLogger}.
*
* @hide
@@ -160,6 +173,7 @@ public final class ManagedProfileProvisioningParams implements Parcelable {
private boolean mLeaveAllSystemAppsEnabled;
private boolean mOrganizationOwnedProvisioning;
private boolean mKeepingAccountOnMigration;
+ @Nullable private PersistableBundle mAdminExtras;
/**
* Initialize a new {@link Builder) to construct a {@link ManagedProfileProvisioningParams}.
@@ -235,6 +249,17 @@ public final class ManagedProfileProvisioningParams implements Parcelable {
}
/**
+ * Sets a {@link Bundle} that contains admin-specific extras.
+ */
+ @NonNull
+ public Builder setAdminExtras(@NonNull PersistableBundle adminExtras) {
+ mAdminExtras = adminExtras != null
+ ? new PersistableBundle(adminExtras)
+ : new PersistableBundle();
+ return this;
+ }
+
+ /**
* Combines all of the attributes that have been set on this {@code Builder}.
*
* @return a new {@link ManagedProfileProvisioningParams} object.
@@ -248,7 +273,8 @@ public final class ManagedProfileProvisioningParams implements Parcelable {
mAccountToMigrate,
mLeaveAllSystemAppsEnabled,
mOrganizationOwnedProvisioning,
- mKeepingAccountOnMigration);
+ mKeepingAccountOnMigration,
+ mAdminExtras);
}
}
@@ -270,6 +296,7 @@ public final class ManagedProfileProvisioningParams implements Parcelable {
+ ", mLeaveAllSystemAppsEnabled=" + mLeaveAllSystemAppsEnabled
+ ", mOrganizationOwnedProvisioning=" + mOrganizationOwnedProvisioning
+ ", mKeepAccountOnMigration=" + mKeepAccountOnMigration
+ + ", mAdminExtras=" + mAdminExtras
+ '}';
}
@@ -282,6 +309,7 @@ public final class ManagedProfileProvisioningParams implements Parcelable {
dest.writeBoolean(mLeaveAllSystemAppsEnabled);
dest.writeBoolean(mOrganizationOwnedProvisioning);
dest.writeBoolean(mKeepAccountOnMigration);
+ dest.writePersistableBundle(mAdminExtras);
}
public static final @NonNull Creator<ManagedProfileProvisioningParams> CREATOR =
@@ -295,6 +323,7 @@ public final class ManagedProfileProvisioningParams implements Parcelable {
boolean leaveAllSystemAppsEnabled = in.readBoolean();
boolean organizationOwnedProvisioning = in.readBoolean();
boolean keepAccountMigrated = in.readBoolean();
+ PersistableBundle adminExtras = in.readPersistableBundle();
return new ManagedProfileProvisioningParams(
componentName,
@@ -303,7 +332,8 @@ public final class ManagedProfileProvisioningParams implements Parcelable {
account,
leaveAllSystemAppsEnabled,
organizationOwnedProvisioning,
- keepAccountMigrated);
+ keepAccountMigrated,
+ adminExtras);
}
@Override
diff --git a/core/java/android/app/admin/ParcelableResource.java b/core/java/android/app/admin/ParcelableResource.java
index dba362820b1d..0b1b166add40 100644
--- a/core/java/android/app/admin/ParcelableResource.java
+++ b/core/java/android/app/admin/ParcelableResource.java
@@ -175,7 +175,7 @@ public final class ParcelableResource implements Parcelable {
* <p>Returns the default drawable by calling the {@code defaultDrawableLoader} if the updated
* drawable was not found or could not be loaded.</p>
*/
- @NonNull
+ @Nullable
public Drawable getDrawable(
Context context,
int density,
@@ -200,7 +200,7 @@ public final class ParcelableResource implements Parcelable {
* <p>Returns the default string by calling {@code defaultStringLoader} if the updated
* string was not found or could not be loaded.</p>
*/
- @NonNull
+ @Nullable
public String getString(
Context context,
@NonNull Callable<String> defaultStringLoader) {
@@ -267,17 +267,11 @@ public final class ParcelableResource implements Parcelable {
/**
* returns the {@link Drawable} loaded from calling {@code defaultDrawableLoader}.
*/
- @NonNull
+ @Nullable
public static Drawable loadDefaultDrawable(@NonNull Callable<Drawable> defaultDrawableLoader) {
try {
Objects.requireNonNull(defaultDrawableLoader, "defaultDrawableLoader can't be null");
-
- Drawable drawable = defaultDrawableLoader.call();
- Objects.requireNonNull(drawable, "defaultDrawable can't be null");
-
- return drawable;
- } catch (NullPointerException rethrown) {
- throw rethrown;
+ return defaultDrawableLoader.call();
} catch (Exception e) {
throw new RuntimeException("Couldn't load default drawable: ", e);
}
@@ -286,17 +280,11 @@ public final class ParcelableResource implements Parcelable {
/**
* returns the {@link String} loaded from calling {@code defaultStringLoader}.
*/
- @NonNull
+ @Nullable
public static String loadDefaultString(@NonNull Callable<String> defaultStringLoader) {
try {
Objects.requireNonNull(defaultStringLoader, "defaultStringLoader can't be null");
-
- String string = defaultStringLoader.call();
- Objects.requireNonNull(string, "defaultString can't be null");
-
- return string;
- } catch (NullPointerException rethrown) {
- throw rethrown;
+ return defaultStringLoader.call();
} catch (Exception e) {
throw new RuntimeException("Couldn't load default string: ", e);
}
diff --git a/core/java/android/app/servertransaction/FixedRotationAdjustmentsItem.java b/core/java/android/app/servertransaction/FixedRotationAdjustmentsItem.java
deleted file mode 100644
index 4c063333fad9..000000000000
--- a/core/java/android/app/servertransaction/FixedRotationAdjustmentsItem.java
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.app.servertransaction;
-
-import android.annotation.Nullable;
-import android.app.ClientTransactionHandler;
-import android.os.IBinder;
-import android.os.Parcel;
-import android.view.DisplayAdjustments.FixedRotationAdjustments;
-
-import java.util.Objects;
-
-/**
- * The request to update display adjustments for a rotated activity or window token.
- * @hide
- */
-public class FixedRotationAdjustmentsItem extends ClientTransactionItem {
-
- /** The token who may have {@link android.content.res.Resources}. */
- private IBinder mToken;
-
- /**
- * The adjustments for the display adjustments of resources. If it is null, the existing
- * rotation adjustments will be dropped to restore natural state.
- */
- private FixedRotationAdjustments mFixedRotationAdjustments;
-
- private FixedRotationAdjustmentsItem() {}
-
- /** Obtain an instance initialized with provided params. */
- public static FixedRotationAdjustmentsItem obtain(IBinder token,
- FixedRotationAdjustments fixedRotationAdjustments) {
- FixedRotationAdjustmentsItem instance =
- ObjectPool.obtain(FixedRotationAdjustmentsItem.class);
- if (instance == null) {
- instance = new FixedRotationAdjustmentsItem();
- }
- instance.mToken = token;
- instance.mFixedRotationAdjustments = fixedRotationAdjustments;
-
- return instance;
- }
-
- @Override
- public void execute(ClientTransactionHandler client, IBinder token,
- PendingTransactionActions pendingActions) {
- client.handleFixedRotationAdjustments(mToken, mFixedRotationAdjustments);
- }
-
- @Override
- public void recycle() {
- mToken = null;
- mFixedRotationAdjustments = null;
- ObjectPool.recycle(this);
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeStrongBinder(mToken);
- dest.writeTypedObject(mFixedRotationAdjustments, flags);
- }
-
- @Override
- public boolean equals(@Nullable Object o) {
- if (this == o) {
- return true;
- }
- if (o == null || getClass() != o.getClass()) {
- return false;
- }
- final FixedRotationAdjustmentsItem other = (FixedRotationAdjustmentsItem) o;
- return Objects.equals(mToken, other.mToken)
- && Objects.equals(mFixedRotationAdjustments, other.mFixedRotationAdjustments);
- }
-
- @Override
- public int hashCode() {
- int result = 17;
- result = 31 * result + Objects.hashCode(mToken);
- result = 31 * result + Objects.hashCode(mFixedRotationAdjustments);
- return result;
- }
-
- private FixedRotationAdjustmentsItem(Parcel in) {
- mToken = in.readStrongBinder();
- mFixedRotationAdjustments = in.readTypedObject(FixedRotationAdjustments.CREATOR);
- }
-
- public static final Creator<FixedRotationAdjustmentsItem> CREATOR =
- new Creator<FixedRotationAdjustmentsItem>() {
- public FixedRotationAdjustmentsItem createFromParcel(Parcel in) {
- return new FixedRotationAdjustmentsItem(in);
- }
-
- public FixedRotationAdjustmentsItem[] newArray(int size) {
- return new FixedRotationAdjustmentsItem[size];
- }
- };
-}
diff --git a/core/java/android/app/servertransaction/LaunchActivityItem.java b/core/java/android/app/servertransaction/LaunchActivityItem.java
index 0a2503c962d6..abf1058f45a2 100644
--- a/core/java/android/app/servertransaction/LaunchActivityItem.java
+++ b/core/java/android/app/servertransaction/LaunchActivityItem.java
@@ -38,7 +38,6 @@ import android.os.IBinder;
import android.os.Parcel;
import android.os.PersistableBundle;
import android.os.Trace;
-import android.view.DisplayAdjustments.FixedRotationAdjustments;
import com.android.internal.app.IVoiceInteractor;
import com.android.internal.content.ReferrerIntent;
@@ -78,7 +77,6 @@ public class LaunchActivityItem extends ClientTransactionItem {
* optimization for quick look up of the interface so the field is ignored for comparison.
*/
private IActivityClientController mActivityClientController;
- private FixedRotationAdjustments mFixedRotationAdjustments;
@Override
public void preExecute(ClientTransactionHandler client, IBinder token) {
@@ -97,8 +95,7 @@ public class LaunchActivityItem extends ClientTransactionItem {
ActivityClientRecord r = new ActivityClientRecord(token, mIntent, mIdent, mInfo,
mOverrideConfig, mCompatInfo, mReferrer, mVoiceInteractor, mState, mPersistentState,
mPendingResults, mPendingNewIntents, mActivityOptions, mIsForward, mProfilerInfo,
- client, mAssistToken, mFixedRotationAdjustments, mShareableActivityToken,
- mLaunchedFromBubble);
+ client, mAssistToken, mShareableActivityToken, mLaunchedFromBubble);
client.handleLaunchActivity(r, pendingActions, null /* customIntent */);
Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}
@@ -121,8 +118,7 @@ public class LaunchActivityItem extends ClientTransactionItem {
PersistableBundle persistentState, List<ResultInfo> pendingResults,
List<ReferrerIntent> pendingNewIntents, ActivityOptions activityOptions,
boolean isForward, ProfilerInfo profilerInfo, IBinder assistToken,
- IActivityClientController activityClientController,
- FixedRotationAdjustments fixedRotationAdjustments, IBinder shareableActivityToken,
+ IActivityClientController activityClientController, IBinder shareableActivityToken,
boolean launchedFromBubble) {
LaunchActivityItem instance = ObjectPool.obtain(LaunchActivityItem.class);
if (instance == null) {
@@ -131,7 +127,7 @@ public class LaunchActivityItem extends ClientTransactionItem {
setValues(instance, intent, ident, info, curConfig, overrideConfig, compatInfo, referrer,
voiceInteractor, procState, state, persistentState, pendingResults,
pendingNewIntents, activityOptions, isForward, profilerInfo, assistToken,
- activityClientController, fixedRotationAdjustments, shareableActivityToken,
+ activityClientController, shareableActivityToken,
launchedFromBubble);
return instance;
@@ -140,7 +136,7 @@ public class LaunchActivityItem extends ClientTransactionItem {
@Override
public void recycle() {
setValues(this, null, 0, null, null, null, null, null, null, 0, null, null, null, null,
- null, false, null, null, null, null, null, false);
+ null, false, null, null, null, null, false);
ObjectPool.recycle(this);
}
@@ -168,7 +164,6 @@ public class LaunchActivityItem extends ClientTransactionItem {
dest.writeTypedObject(mProfilerInfo, flags);
dest.writeStrongBinder(mAssistToken);
dest.writeStrongInterface(mActivityClientController);
- dest.writeTypedObject(mFixedRotationAdjustments, flags);
dest.writeStrongBinder(mShareableActivityToken);
dest.writeBoolean(mLaunchedFromBubble);
}
@@ -188,7 +183,7 @@ public class LaunchActivityItem extends ClientTransactionItem {
in.readTypedObject(ProfilerInfo.CREATOR),
in.readStrongBinder(),
IActivityClientController.Stub.asInterface(in.readStrongBinder()),
- in.readTypedObject(FixedRotationAdjustments.CREATOR), in.readStrongBinder(),
+ in.readStrongBinder(),
in.readBoolean());
}
@@ -232,7 +227,6 @@ public class LaunchActivityItem extends ClientTransactionItem {
&& mIsForward == other.mIsForward
&& Objects.equals(mProfilerInfo, other.mProfilerInfo)
&& Objects.equals(mAssistToken, other.mAssistToken)
- && Objects.equals(mFixedRotationAdjustments, other.mFixedRotationAdjustments)
&& Objects.equals(mShareableActivityToken, other.mShareableActivityToken);
}
@@ -254,7 +248,6 @@ public class LaunchActivityItem extends ClientTransactionItem {
result = 31 * result + (mIsForward ? 1 : 0);
result = 31 * result + Objects.hashCode(mProfilerInfo);
result = 31 * result + Objects.hashCode(mAssistToken);
- result = 31 * result + Objects.hashCode(mFixedRotationAdjustments);
result = 31 * result + Objects.hashCode(mShareableActivityToken);
return result;
}
@@ -292,7 +285,6 @@ public class LaunchActivityItem extends ClientTransactionItem {
+ ",persistentState=" + mPersistentState + ",pendingResults=" + mPendingResults
+ ",pendingNewIntents=" + mPendingNewIntents + ",options=" + mActivityOptions
+ ",profilerInfo=" + mProfilerInfo + ",assistToken=" + mAssistToken
- + ",rotationAdj=" + mFixedRotationAdjustments
+ ",shareableActivityToken=" + mShareableActivityToken + "}";
}
@@ -304,8 +296,7 @@ public class LaunchActivityItem extends ClientTransactionItem {
List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,
ActivityOptions activityOptions, boolean isForward, ProfilerInfo profilerInfo,
IBinder assistToken, IActivityClientController activityClientController,
- FixedRotationAdjustments fixedRotationAdjustments, IBinder shareableActivityToken,
- boolean launchedFromBubble) {
+ IBinder shareableActivityToken, boolean launchedFromBubble) {
instance.mIntent = intent;
instance.mIdent = ident;
instance.mInfo = info;
@@ -324,7 +315,6 @@ public class LaunchActivityItem extends ClientTransactionItem {
instance.mProfilerInfo = profilerInfo;
instance.mAssistToken = assistToken;
instance.mActivityClientController = activityClientController;
- instance.mFixedRotationAdjustments = fixedRotationAdjustments;
instance.mShareableActivityToken = shareableActivityToken;
instance.mLaunchedFromBubble = launchedFromBubble;
}
diff --git a/core/java/android/app/smartspace/SmartspaceTarget.java b/core/java/android/app/smartspace/SmartspaceTarget.java
index 8e9853575c31..78f51be78828 100644
--- a/core/java/android/app/smartspace/SmartspaceTarget.java
+++ b/core/java/android/app/smartspace/SmartspaceTarget.java
@@ -20,6 +20,7 @@ import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
+import android.app.smartspace.uitemplatedata.SmartspaceDefaultUiTemplateData;
import android.appwidget.AppWidgetProviderInfo;
import android.content.ComponentName;
import android.net.Uri;
@@ -131,6 +132,9 @@ public final class SmartspaceTarget implements Parcelable {
@Nullable
private final AppWidgetProviderInfo mWidget;
+ @Nullable
+ private final SmartspaceDefaultUiTemplateData mTemplateData;
+
public static final int FEATURE_UNDEFINED = 0;
public static final int FEATURE_WEATHER = 1;
public static final int FEATURE_CALENDAR = 2;
@@ -189,6 +193,32 @@ public final class SmartspaceTarget implements Parcelable {
public @interface FeatureType {
}
+ public static final int UI_TEMPLATE_UNDEFINED = 0;
+ public static final int UI_TEMPLATE_DEFAULT = 1;
+ public static final int UI_TEMPLATE_SUB_IMAGE = 2;
+ public static final int UI_TEMPLATE_SUB_LIST = 3;
+ public static final int UI_TEMPLATE_CAROUSEL = 4;
+ public static final int UI_TEMPLATE_HEAD_TO_HEAD = 5;
+ public static final int UI_TEMPLATE_COMBINED_CARDS = 6;
+ public static final int UI_TEMPLATE_SUB_CARD = 7;
+
+ /**
+ * @hide
+ */
+ @IntDef(prefix = {"UI_TEMPLATE_"}, value = {
+ UI_TEMPLATE_UNDEFINED,
+ UI_TEMPLATE_DEFAULT,
+ UI_TEMPLATE_SUB_IMAGE,
+ UI_TEMPLATE_SUB_LIST,
+ UI_TEMPLATE_CAROUSEL,
+ UI_TEMPLATE_HEAD_TO_HEAD,
+ UI_TEMPLATE_COMBINED_CARDS,
+ UI_TEMPLATE_SUB_CARD
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface UiTemplateType {
+ }
+
private SmartspaceTarget(Parcel in) {
this.mSmartspaceTargetId = in.readString();
this.mHeaderAction = in.readTypedObject(SmartspaceAction.CREATOR);
@@ -207,6 +237,7 @@ public final class SmartspaceTarget implements Parcelable {
this.mAssociatedSmartspaceTargetId = in.readString();
this.mSliceUri = in.readTypedObject(Uri.CREATOR);
this.mWidget = in.readTypedObject(AppWidgetProviderInfo.CREATOR);
+ this.mTemplateData = in.readTypedObject(SmartspaceDefaultUiTemplateData.CREATOR);
}
private SmartspaceTarget(String smartspaceTargetId,
@@ -217,7 +248,7 @@ public final class SmartspaceTarget implements Parcelable {
boolean shouldShowExpanded, String sourceNotificationKey,
ComponentName componentName, UserHandle userHandle,
String associatedSmartspaceTargetId, Uri sliceUri,
- AppWidgetProviderInfo widget) {
+ AppWidgetProviderInfo widget, SmartspaceDefaultUiTemplateData templateData) {
mSmartspaceTargetId = smartspaceTargetId;
mHeaderAction = headerAction;
mBaseAction = baseAction;
@@ -235,6 +266,7 @@ public final class SmartspaceTarget implements Parcelable {
mAssociatedSmartspaceTargetId = associatedSmartspaceTargetId;
mSliceUri = sliceUri;
mWidget = widget;
+ mTemplateData = templateData;
}
/**
@@ -371,6 +403,14 @@ public final class SmartspaceTarget implements Parcelable {
}
/**
+ * Returns the UI template data.
+ */
+ @Nullable
+ public SmartspaceDefaultUiTemplateData getTemplateData() {
+ return mTemplateData;
+ }
+
+ /**
* @see Parcelable.Creator
*/
@NonNull
@@ -405,6 +445,7 @@ public final class SmartspaceTarget implements Parcelable {
dest.writeString(this.mAssociatedSmartspaceTargetId);
dest.writeTypedObject(this.mSliceUri, flags);
dest.writeTypedObject(this.mWidget, flags);
+ dest.writeTypedObject(this.mTemplateData, flags);
}
@Override
@@ -432,6 +473,7 @@ public final class SmartspaceTarget implements Parcelable {
+ ", mAssociatedSmartspaceTargetId='" + mAssociatedSmartspaceTargetId + '\''
+ ", mSliceUri=" + mSliceUri
+ ", mWidget=" + mWidget
+ + ", mTemplateData=" + mTemplateData
+ '}';
}
@@ -457,7 +499,8 @@ public final class SmartspaceTarget implements Parcelable {
&& Objects.equals(mAssociatedSmartspaceTargetId,
that.mAssociatedSmartspaceTargetId)
&& Objects.equals(mSliceUri, that.mSliceUri)
- && Objects.equals(mWidget, that.mWidget);
+ && Objects.equals(mWidget, that.mWidget)
+ && Objects.equals(mTemplateData, that.mTemplateData);
}
@Override
@@ -465,7 +508,7 @@ public final class SmartspaceTarget implements Parcelable {
return Objects.hash(mSmartspaceTargetId, mHeaderAction, mBaseAction, mCreationTimeMillis,
mExpiryTimeMillis, mScore, mActionChips, mIconGrid, mFeatureType, mSensitive,
mShouldShowExpanded, mSourceNotificationKey, mComponentName, mUserHandle,
- mAssociatedSmartspaceTargetId, mSliceUri, mWidget);
+ mAssociatedSmartspaceTargetId, mSliceUri, mWidget, mTemplateData);
}
/**
@@ -476,6 +519,9 @@ public final class SmartspaceTarget implements Parcelable {
@SystemApi
public static final class Builder {
private final String mSmartspaceTargetId;
+ private final ComponentName mComponentName;
+ private final UserHandle mUserHandle;
+
private SmartspaceAction mHeaderAction;
private SmartspaceAction mBaseAction;
private long mCreationTimeMillis;
@@ -487,11 +533,10 @@ public final class SmartspaceTarget implements Parcelable {
private boolean mSensitive;
private boolean mShouldShowExpanded;
private String mSourceNotificationKey;
- private final ComponentName mComponentName;
- private final UserHandle mUserHandle;
private String mAssociatedSmartspaceTargetId;
private Uri mSliceUri;
private AppWidgetProviderInfo mWidget;
+ private SmartspaceDefaultUiTemplateData mTemplateData;
/**
* A builder for {@link SmartspaceTarget}.
@@ -640,6 +685,16 @@ public final class SmartspaceTarget implements Parcelable {
}
/**
+ * Sets the UI template data.
+ */
+ @NonNull
+ public Builder setTemplateData(
+ @Nullable SmartspaceDefaultUiTemplateData templateData) {
+ mTemplateData = templateData;
+ return this;
+ }
+
+ /**
* Builds a new {@link SmartspaceTarget}.
*
* @throws IllegalStateException when non null fields are set as null.
@@ -655,7 +710,7 @@ public final class SmartspaceTarget implements Parcelable {
mHeaderAction, mBaseAction, mCreationTimeMillis, mExpiryTimeMillis, mScore,
mActionChips, mIconGrid, mFeatureType, mSensitive, mShouldShowExpanded,
mSourceNotificationKey, mComponentName, mUserHandle,
- mAssociatedSmartspaceTargetId, mSliceUri, mWidget);
+ mAssociatedSmartspaceTargetId, mSliceUri, mWidget, mTemplateData);
}
}
}
diff --git a/core/java/android/app/smartspace/SmartspaceUtils.java b/core/java/android/app/smartspace/SmartspaceUtils.java
new file mode 100644
index 000000000000..f058ffa5a04b
--- /dev/null
+++ b/core/java/android/app/smartspace/SmartspaceUtils.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.smartspace;
+
+import android.annotation.Nullable;
+
+/**
+ * Utilities for Smartspace data.
+ *
+ * @hide
+ */
+public final class SmartspaceUtils {
+
+ private SmartspaceUtils() {
+ }
+
+ /** Returns true if the passed-in {@link CharSequence}s are equal. */
+ public static boolean isEqual(@Nullable CharSequence cs1, @Nullable CharSequence cs2) {
+ if ((cs1 == null && cs2 != null) || (cs1 != null && cs2 == null)) return false;
+ if (cs1 == null && cs2 == null) return true;
+ return cs1.toString().contentEquals(cs2);
+ }
+}
diff --git a/core/java/android/app/smartspace/uitemplatedata/SmartspaceCarouselUiTemplateData.java b/core/java/android/app/smartspace/uitemplatedata/SmartspaceCarouselUiTemplateData.java
new file mode 100644
index 000000000000..c4c4fdef67f9
--- /dev/null
+++ b/core/java/android/app/smartspace/uitemplatedata/SmartspaceCarouselUiTemplateData.java
@@ -0,0 +1,360 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.smartspace.uitemplatedata;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.app.smartspace.SmartspaceTarget;
+import android.app.smartspace.SmartspaceUtils;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Holds all the relevant data needed to render a Smartspace card with the carousel Ui Template.
+ *
+ * @hide
+ */
+@SystemApi
+public final class SmartspaceCarouselUiTemplateData extends SmartspaceDefaultUiTemplateData {
+
+ /** Lists of {@link CarouselItem}. */
+ @NonNull
+ private final List<CarouselItem> mCarouselItems;
+
+ /** Tap action for the entire carousel secondary card, including the blank space */
+ @Nullable
+ private final SmartspaceTapAction mCarouselAction;
+
+ SmartspaceCarouselUiTemplateData(@NonNull Parcel in) {
+ super(in);
+ mCarouselItems = in.createTypedArrayList(CarouselItem.CREATOR);
+ mCarouselAction = in.readTypedObject(SmartspaceTapAction.CREATOR);
+ }
+
+ private SmartspaceCarouselUiTemplateData(@SmartspaceTarget.UiTemplateType int templateType,
+ @Nullable CharSequence titleText,
+ @Nullable SmartspaceIcon titleIcon,
+ @Nullable CharSequence subtitleText,
+ @Nullable SmartspaceIcon subTitleIcon,
+ @Nullable SmartspaceTapAction primaryTapAction,
+ @Nullable CharSequence supplementalSubtitleText,
+ @Nullable SmartspaceIcon supplementalSubtitleIcon,
+ @Nullable SmartspaceTapAction supplementalSubtitleTapAction,
+ @Nullable CharSequence supplementalAlarmText,
+ @NonNull List<CarouselItem> carouselItems,
+ @Nullable SmartspaceTapAction carouselAction) {
+ super(templateType, titleText, titleIcon, subtitleText, subTitleIcon, primaryTapAction,
+ supplementalSubtitleText, supplementalSubtitleIcon, supplementalSubtitleTapAction,
+ supplementalAlarmText);
+ mCarouselItems = carouselItems;
+ mCarouselAction = carouselAction;
+ }
+
+ @NonNull
+ public List<CarouselItem> getCarouselItems() {
+ return mCarouselItems;
+ }
+
+ @Nullable
+ public SmartspaceTapAction getCarouselAction() {
+ return mCarouselAction;
+ }
+
+ /**
+ * @see Parcelable.Creator
+ */
+ @NonNull
+ public static final Creator<SmartspaceCarouselUiTemplateData> CREATOR =
+ new Creator<SmartspaceCarouselUiTemplateData>() {
+ @Override
+ public SmartspaceCarouselUiTemplateData createFromParcel(Parcel in) {
+ return new SmartspaceCarouselUiTemplateData(in);
+ }
+
+ @Override
+ public SmartspaceCarouselUiTemplateData[] newArray(int size) {
+ return new SmartspaceCarouselUiTemplateData[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel out, int flags) {
+ super.writeToParcel(out, flags);
+ out.writeTypedList(mCarouselItems);
+ out.writeTypedObject(mCarouselAction, flags);
+ }
+
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof SmartspaceCarouselUiTemplateData)) return false;
+ if (!super.equals(o)) return false;
+ SmartspaceCarouselUiTemplateData that = (SmartspaceCarouselUiTemplateData) o;
+ return mCarouselItems.equals(that.mCarouselItems) && Objects.equals(mCarouselAction,
+ that.mCarouselAction);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(super.hashCode(), mCarouselItems, mCarouselAction);
+ }
+
+ @Override
+ public String toString() {
+ return super.toString() + " + SmartspaceCarouselUiTemplateData{"
+ + "mCarouselItems=" + mCarouselItems
+ + ", mCarouselActions=" + mCarouselAction
+ + '}';
+ }
+
+ /**
+ * A builder for {@link SmartspaceCarouselUiTemplateData} object.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final class Builder extends SmartspaceDefaultUiTemplateData.Builder {
+
+ private final List<CarouselItem> mCarouselItems;
+ private SmartspaceTapAction mCarouselAction;
+
+ /**
+ * A builder for {@link SmartspaceCarouselUiTemplateData}.
+ */
+ public Builder(@NonNull List<CarouselItem> carouselItems) {
+ super(SmartspaceTarget.UI_TEMPLATE_CAROUSEL);
+ mCarouselItems = Objects.requireNonNull(carouselItems);
+ }
+
+ /**
+ * Sets the card tap action.
+ */
+ @NonNull
+ public Builder setCarouselAction(@NonNull SmartspaceTapAction carouselAction) {
+ mCarouselAction = carouselAction;
+ return this;
+ }
+
+ /**
+ * Builds a new SmartspaceCarouselUiTemplateData instance.
+ *
+ * @throws IllegalStateException if the carousel data is invalid.
+ */
+ @NonNull
+ public SmartspaceCarouselUiTemplateData build() {
+ if (mCarouselItems.isEmpty()) {
+ throw new IllegalStateException("Carousel data is empty");
+ }
+ return new SmartspaceCarouselUiTemplateData(getTemplateType(), getTitleText(),
+ getTitleIcon(), getSubtitleText(), getSubTitleIcon(), getPrimaryTapAction(),
+ getSupplementalSubtitleText(), getSupplementalSubtitleIcon(),
+ getSupplementalSubtitleTapAction(), getSupplementalAlarmText(),
+ mCarouselItems,
+ mCarouselAction);
+ }
+ }
+
+ /** Holds all the relevant data needed to render a carousel item. */
+ public static final class CarouselItem implements Parcelable {
+
+ /** Text which is above the image item. */
+ @Nullable
+ private final CharSequence mUpperText;
+
+ /** Image item. Can be empty. */
+ @Nullable
+ private final SmartspaceIcon mImage;
+
+ /** Text which is under the image item. */
+ @Nullable
+ private final CharSequence mLowerText;
+
+ /**
+ * Tap action for this {@link CarouselItem} instance. {@code mCarouselAction} is used if not
+ * being set.
+ */
+ @Nullable
+ private final SmartspaceTapAction mTapAction;
+
+ CarouselItem(@NonNull Parcel in) {
+ mUpperText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+ mImage = in.readTypedObject(SmartspaceIcon.CREATOR);
+ mLowerText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+ mTapAction = in.readTypedObject(SmartspaceTapAction.CREATOR);
+ }
+
+ private CarouselItem(@Nullable CharSequence upperText, @Nullable SmartspaceIcon image,
+ @Nullable CharSequence lowerText, @Nullable SmartspaceTapAction tapAction) {
+ mUpperText = upperText;
+ mImage = image;
+ mLowerText = lowerText;
+ mTapAction = tapAction;
+ }
+
+ @Nullable
+ public CharSequence getUpperText() {
+ return mUpperText;
+ }
+
+ @Nullable
+ public SmartspaceIcon getImage() {
+ return mImage;
+ }
+
+ @Nullable
+ public CharSequence getLowerText() {
+ return mLowerText;
+ }
+
+ @Nullable
+ public SmartspaceTapAction getTapAction() {
+ return mTapAction;
+ }
+
+ /**
+ * @see Parcelable.Creator
+ */
+ @NonNull
+ public static final Creator<CarouselItem> CREATOR =
+ new Creator<CarouselItem>() {
+ @Override
+ public CarouselItem createFromParcel(Parcel in) {
+ return new CarouselItem(in);
+ }
+
+ @Override
+ public CarouselItem[] newArray(int size) {
+ return new CarouselItem[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel out, int flags) {
+ TextUtils.writeToParcel(mUpperText, out, flags);
+ out.writeTypedObject(mImage, flags);
+ TextUtils.writeToParcel(mLowerText, out, flags);
+ out.writeTypedObject(mTapAction, flags);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof CarouselItem)) return false;
+ CarouselItem that = (CarouselItem) o;
+ return SmartspaceUtils.isEqual(mUpperText, that.mUpperText) && Objects.equals(
+ mImage,
+ that.mImage) && SmartspaceUtils.isEqual(mLowerText, that.mLowerText)
+ && Objects.equals(mTapAction, that.mTapAction);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mUpperText, mImage, mLowerText, mTapAction);
+ }
+
+ @Override
+ public String toString() {
+ return "CarouselItem{"
+ + "mUpperText=" + mUpperText
+ + ", mImage=" + mImage
+ + ", mLowerText=" + mLowerText
+ + ", mTapAction=" + mTapAction
+ + '}';
+ }
+
+ /**
+ * A builder for {@link CarouselItem} object.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final class Builder {
+
+ private CharSequence mUpperText;
+ private SmartspaceIcon mImage;
+ private CharSequence mLowerText;
+ private SmartspaceTapAction mTapAction;
+
+ /**
+ * Sets the upper text.
+ */
+ @NonNull
+ public Builder setUpperText(@Nullable CharSequence upperText) {
+ mUpperText = upperText;
+ return this;
+ }
+
+ /**
+ * Sets the image.
+ */
+ @NonNull
+ public Builder setImage(@Nullable SmartspaceIcon image) {
+ mImage = image;
+ return this;
+ }
+
+
+ /**
+ * Sets the lower text.
+ */
+ @NonNull
+ public Builder setLowerText(@Nullable CharSequence lowerText) {
+ mLowerText = lowerText;
+ return this;
+ }
+
+ /**
+ * Sets the tap action.
+ */
+ @NonNull
+ public Builder setTapAction(@Nullable SmartspaceTapAction tapAction) {
+ mTapAction = tapAction;
+ return this;
+ }
+
+ /**
+ * Builds a new CarouselItem instance.
+ *
+ * @throws IllegalStateException if all the rendering data is empty.
+ */
+ @NonNull
+ public CarouselItem build() {
+ if (TextUtils.isEmpty(mUpperText) && mImage == null && TextUtils.isEmpty(
+ mLowerText)) {
+ throw new IllegalStateException("Carousel data is empty");
+ }
+ return new CarouselItem(mUpperText, mImage, mLowerText, mTapAction);
+ }
+ }
+ }
+}
diff --git a/core/java/android/app/smartspace/uitemplatedata/SmartspaceCombinedCardsUiTemplateData.java b/core/java/android/app/smartspace/uitemplatedata/SmartspaceCombinedCardsUiTemplateData.java
new file mode 100644
index 000000000000..7e2f74eac4fe
--- /dev/null
+++ b/core/java/android/app/smartspace/uitemplatedata/SmartspaceCombinedCardsUiTemplateData.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.smartspace.uitemplatedata;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.app.smartspace.SmartspaceTarget;
+import android.os.Parcel;
+
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Holds all the relevant data needed to render a Smartspace card with the combined-card Ui
+ * Template.
+ *
+ * We only support 1 sub-list card combined with 1 carousel card. And we may expand our supported
+ * combinations in the future.
+ *
+ * @hide
+ */
+@SystemApi
+public final class SmartspaceCombinedCardsUiTemplateData extends SmartspaceDefaultUiTemplateData {
+
+ /** A list of secondary cards. */
+ @NonNull
+ private final List<SmartspaceDefaultUiTemplateData> mCombinedCardDataList;
+
+ SmartspaceCombinedCardsUiTemplateData(@NonNull Parcel in) {
+ super(in);
+ mCombinedCardDataList = in.createTypedArrayList(SmartspaceDefaultUiTemplateData.CREATOR);
+ }
+
+ private SmartspaceCombinedCardsUiTemplateData(@SmartspaceTarget.UiTemplateType int templateType,
+ @Nullable CharSequence titleText,
+ @Nullable SmartspaceIcon titleIcon,
+ @Nullable CharSequence subtitleText,
+ @Nullable SmartspaceIcon subTitleIcon,
+ @Nullable SmartspaceTapAction primaryTapAction,
+ @Nullable CharSequence supplementalSubtitleText,
+ @Nullable SmartspaceIcon supplementalSubtitleIcon,
+ @Nullable SmartspaceTapAction supplementalSubtitleTapAction,
+ @Nullable CharSequence supplementalAlarmText,
+ @NonNull List<SmartspaceDefaultUiTemplateData> combinedCardDataList) {
+ super(templateType, titleText, titleIcon, subtitleText, subTitleIcon, primaryTapAction,
+ supplementalSubtitleText, supplementalSubtitleIcon, supplementalSubtitleTapAction,
+ supplementalAlarmText);
+ mCombinedCardDataList = combinedCardDataList;
+ }
+
+ @NonNull
+ public List<SmartspaceDefaultUiTemplateData> getCombinedCardDataList() {
+ return mCombinedCardDataList;
+ }
+
+ /**
+ * @see Parcelable.Creator
+ */
+ @NonNull
+ public static final Creator<SmartspaceCombinedCardsUiTemplateData> CREATOR =
+ new Creator<SmartspaceCombinedCardsUiTemplateData>() {
+ @Override
+ public SmartspaceCombinedCardsUiTemplateData createFromParcel(Parcel in) {
+ return new SmartspaceCombinedCardsUiTemplateData(in);
+ }
+
+ @Override
+ public SmartspaceCombinedCardsUiTemplateData[] newArray(int size) {
+ return new SmartspaceCombinedCardsUiTemplateData[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel out, int flags) {
+ super.writeToParcel(out, flags);
+ out.writeTypedList(mCombinedCardDataList);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof SmartspaceCombinedCardsUiTemplateData)) return false;
+ if (!super.equals(o)) return false;
+ SmartspaceCombinedCardsUiTemplateData that = (SmartspaceCombinedCardsUiTemplateData) o;
+ return mCombinedCardDataList.equals(that.mCombinedCardDataList);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(super.hashCode(), mCombinedCardDataList);
+ }
+
+ @Override
+ public String toString() {
+ return super.toString() + " + SmartspaceCombinedCardsUiTemplateData{"
+ + "mCombinedCardDataList=" + mCombinedCardDataList
+ + '}';
+ }
+
+ /**
+ * A builder for {@link SmartspaceCombinedCardsUiTemplateData} object.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final class Builder extends SmartspaceDefaultUiTemplateData.Builder {
+
+ private final List<SmartspaceDefaultUiTemplateData> mCombinedCardDataList;
+
+ /**
+ * A builder for {@link SmartspaceCombinedCardsUiTemplateData}.
+ */
+ public Builder(@NonNull List<SmartspaceDefaultUiTemplateData> combinedCardDataList) {
+ super(SmartspaceTarget.UI_TEMPLATE_COMBINED_CARDS);
+ mCombinedCardDataList = Objects.requireNonNull(combinedCardDataList);
+ }
+
+ /**
+ * Builds a new SmartspaceCombinedCardsUiTemplateData instance.
+ *
+ * @throws IllegalStateException if any required non-null field is null
+ */
+ @NonNull
+ public SmartspaceCombinedCardsUiTemplateData build() {
+ if (mCombinedCardDataList == null) {
+ throw new IllegalStateException("Please assign a value to all @NonNull args.");
+ }
+ return new SmartspaceCombinedCardsUiTemplateData(getTemplateType(), getTitleText(),
+ getTitleIcon(), getSubtitleText(), getSubTitleIcon(), getPrimaryTapAction(),
+ getSupplementalSubtitleText(), getSupplementalSubtitleIcon(),
+ getSupplementalSubtitleTapAction(), getSupplementalAlarmText(),
+ mCombinedCardDataList);
+ }
+ }
+}
diff --git a/core/java/android/app/smartspace/uitemplatedata/SmartspaceDefaultUiTemplateData.java b/core/java/android/app/smartspace/uitemplatedata/SmartspaceDefaultUiTemplateData.java
new file mode 100644
index 000000000000..742d5c9bdc0e
--- /dev/null
+++ b/core/java/android/app/smartspace/uitemplatedata/SmartspaceDefaultUiTemplateData.java
@@ -0,0 +1,459 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.smartspace.uitemplatedata;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
+import android.app.smartspace.SmartspaceTarget.UiTemplateType;
+import android.app.smartspace.SmartspaceUtils;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+import java.util.Objects;
+
+/**
+ * Holds all the relevant data needed to render a Smartspace card with the default Ui Template.
+ *
+ * @hide
+ */
+@SystemApi
+@SuppressLint("ParcelNotFinal")
+public class SmartspaceDefaultUiTemplateData implements Parcelable {
+
+ /**
+ * {@link UiTemplateType} indicating the template type of this template data.
+ *
+ * @see UiTemplateType
+ */
+ @UiTemplateType
+ private final int mTemplateType;
+
+ /**
+ * Title text and title icon are shown at the first row. When both are absent, the date view
+ * will be used, which has its own tap action applied to the title area.
+ */
+ @Nullable
+ private final CharSequence mTitleText;
+
+ @Nullable
+ private final SmartspaceIcon mTitleIcon;
+
+ /** Subtitle text and icon are shown at the second row. */
+ @Nullable
+ private final CharSequence mSubtitleText;
+
+ @Nullable
+ private final SmartspaceIcon mSubTitleIcon;
+
+ /**
+ * Primary tap action for the entire card, including the blank spaces, except: 1. When title is
+ * absent, the date view's default tap action is used; 2. Supplemental subtitle uses its own tap
+ * action if being set; 3. Secondary card uses its own tap action if being set.
+ */
+ @Nullable
+ private final SmartspaceTapAction mPrimaryTapAction;
+
+ /**
+ * Supplemental subtitle text and icon are shown at the second row following the subtitle text.
+ * Mainly used for weather info on non-weather card.
+ */
+ @Nullable
+ private final CharSequence mSupplementalSubtitleText;
+
+ @Nullable
+ private final SmartspaceIcon mSupplementalSubtitleIcon;
+
+ /**
+ * Tap action for the supplemental subtitle's text and icon. Will use the primary tap action if
+ * not being set.
+ */
+ @Nullable
+ private final SmartspaceTapAction mSupplementalSubtitleTapAction;
+
+ /**
+ * Supplemental alarm text is specifically used for holiday alarm, which is appended to "next
+ * alarm".
+ */
+ @Nullable
+ private final CharSequence mSupplementalAlarmText;
+
+ SmartspaceDefaultUiTemplateData(@NonNull Parcel in) {
+ mTemplateType = in.readInt();
+ mTitleText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+ mTitleIcon = in.readTypedObject(SmartspaceIcon.CREATOR);
+ mSubtitleText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+ mSubTitleIcon = in.readTypedObject(SmartspaceIcon.CREATOR);
+ mPrimaryTapAction = in.readTypedObject(SmartspaceTapAction.CREATOR);
+ mSupplementalSubtitleText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+ mSupplementalSubtitleIcon = in.readTypedObject(SmartspaceIcon.CREATOR);
+ mSupplementalSubtitleTapAction = in.readTypedObject(SmartspaceTapAction.CREATOR);
+ mSupplementalAlarmText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+ }
+
+ /**
+ * Should ONLY used by subclasses. For the general instance creation, please use
+ * SmartspaceDefaultUiTemplateData.Builder.
+ */
+ SmartspaceDefaultUiTemplateData(@UiTemplateType int templateType,
+ @Nullable CharSequence titleText,
+ @Nullable SmartspaceIcon titleIcon,
+ @Nullable CharSequence subtitleText,
+ @Nullable SmartspaceIcon subTitleIcon,
+ @Nullable SmartspaceTapAction primaryTapAction,
+ @Nullable CharSequence supplementalSubtitleText,
+ @Nullable SmartspaceIcon supplementalSubtitleIcon,
+ @Nullable SmartspaceTapAction supplementalSubtitleTapAction,
+ @Nullable CharSequence supplementalAlarmText) {
+ mTemplateType = templateType;
+ mTitleText = titleText;
+ mTitleIcon = titleIcon;
+ mSubtitleText = subtitleText;
+ mSubTitleIcon = subTitleIcon;
+ mPrimaryTapAction = primaryTapAction;
+ mSupplementalSubtitleText = supplementalSubtitleText;
+ mSupplementalSubtitleIcon = supplementalSubtitleIcon;
+ mSupplementalSubtitleTapAction = supplementalSubtitleTapAction;
+ mSupplementalAlarmText = supplementalAlarmText;
+ }
+
+ @UiTemplateType
+ public int getTemplateType() {
+ return mTemplateType;
+ }
+
+ @Nullable
+ public CharSequence getTitleText() {
+ return mTitleText;
+ }
+
+ @Nullable
+ public SmartspaceIcon getTitleIcon() {
+ return mTitleIcon;
+ }
+
+ @Nullable
+ public CharSequence getSubtitleText() {
+ return mSubtitleText;
+ }
+
+ @Nullable
+ public SmartspaceIcon getSubTitleIcon() {
+ return mSubTitleIcon;
+ }
+
+ @Nullable
+ public CharSequence getSupplementalSubtitleText() {
+ return mSupplementalSubtitleText;
+ }
+
+ @Nullable
+ public SmartspaceIcon getSupplementalSubtitleIcon() {
+ return mSupplementalSubtitleIcon;
+ }
+
+ @Nullable
+ public SmartspaceTapAction getPrimaryTapAction() {
+ return mPrimaryTapAction;
+ }
+
+ @Nullable
+ public SmartspaceTapAction getSupplementalSubtitleTapAction() {
+ return mSupplementalSubtitleTapAction;
+ }
+
+ @Nullable
+ public CharSequence getSupplementalAlarmText() {
+ return mSupplementalAlarmText;
+ }
+
+ /**
+ * @see Parcelable.Creator
+ */
+ @NonNull
+ public static final Creator<SmartspaceDefaultUiTemplateData> CREATOR =
+ new Creator<SmartspaceDefaultUiTemplateData>() {
+ @Override
+ public SmartspaceDefaultUiTemplateData createFromParcel(Parcel in) {
+ return new SmartspaceDefaultUiTemplateData(in);
+ }
+
+ @Override
+ public SmartspaceDefaultUiTemplateData[] newArray(int size) {
+ return new SmartspaceDefaultUiTemplateData[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel out, int flags) {
+ out.writeInt(mTemplateType);
+ TextUtils.writeToParcel(mTitleText, out, flags);
+ out.writeTypedObject(mTitleIcon, flags);
+ TextUtils.writeToParcel(mSubtitleText, out, flags);
+ out.writeTypedObject(mSubTitleIcon, flags);
+ out.writeTypedObject(mPrimaryTapAction, flags);
+ TextUtils.writeToParcel(mSupplementalSubtitleText, out, flags);
+ out.writeTypedObject(mSupplementalSubtitleIcon, flags);
+ out.writeTypedObject(mSupplementalSubtitleTapAction, flags);
+ TextUtils.writeToParcel(mSupplementalAlarmText, out, flags);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof SmartspaceDefaultUiTemplateData)) return false;
+ SmartspaceDefaultUiTemplateData that = (SmartspaceDefaultUiTemplateData) o;
+ return mTemplateType == that.mTemplateType && SmartspaceUtils.isEqual(mTitleText,
+ that.mTitleText)
+ && Objects.equals(mTitleIcon, that.mTitleIcon)
+ && SmartspaceUtils.isEqual(mSubtitleText, that.mSubtitleText)
+ && Objects.equals(mSubTitleIcon, that.mSubTitleIcon)
+ && Objects.equals(mPrimaryTapAction, that.mPrimaryTapAction)
+ && SmartspaceUtils.isEqual(mSupplementalSubtitleText,
+ that.mSupplementalSubtitleText)
+ && Objects.equals(mSupplementalSubtitleIcon, that.mSupplementalSubtitleIcon)
+ && Objects.equals(mSupplementalSubtitleTapAction,
+ that.mSupplementalSubtitleTapAction)
+ && SmartspaceUtils.isEqual(mSupplementalAlarmText, that.mSupplementalAlarmText);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mTemplateType, mTitleText, mTitleIcon, mSubtitleText, mSubTitleIcon,
+ mPrimaryTapAction, mSupplementalSubtitleText, mSupplementalSubtitleIcon,
+ mSupplementalSubtitleTapAction, mSupplementalAlarmText);
+ }
+
+ @Override
+ public String toString() {
+ return "SmartspaceDefaultUiTemplateData{"
+ + "mTemplateType=" + mTemplateType
+ + ", mTitleText=" + mTitleText
+ + ", mTitleIcon=" + mTitleIcon
+ + ", mSubtitleText=" + mSubtitleText
+ + ", mSubTitleIcon=" + mSubTitleIcon
+ + ", mPrimaryTapAction=" + mPrimaryTapAction
+ + ", mSupplementalSubtitleText=" + mSupplementalSubtitleText
+ + ", mSupplementalSubtitleIcon=" + mSupplementalSubtitleIcon
+ + ", mSupplementalSubtitleTapAction=" + mSupplementalSubtitleTapAction
+ + ", mSupplementalAlarmText=" + mSupplementalAlarmText
+ + '}';
+ }
+
+ /**
+ * A builder for {@link SmartspaceDefaultUiTemplateData} object.
+ *
+ * @hide
+ */
+ @SystemApi
+ @SuppressLint("StaticFinalBuilder")
+ public static class Builder {
+ @UiTemplateType
+ private final int mTemplateType;
+ private CharSequence mTitleText;
+ private SmartspaceIcon mTitleIcon;
+ private CharSequence mSubtitleText;
+ private SmartspaceIcon mSubTitleIcon;
+ private SmartspaceTapAction mPrimaryTapAction;
+ private CharSequence mSupplementalSubtitleText;
+ private SmartspaceIcon mSupplementalSubtitleIcon;
+ private SmartspaceTapAction mSupplementalSubtitleTapAction;
+ private CharSequence mSupplementalAlarmText;
+
+ /**
+ * A builder for {@link SmartspaceDefaultUiTemplateData}.
+ *
+ * @param templateType the {@link UiTemplateType} of this template data.
+ */
+ public Builder(@UiTemplateType int templateType) {
+ mTemplateType = templateType;
+ }
+
+ /** Should ONLY be used by the subclasses */
+ @UiTemplateType
+ @SuppressLint("GetterOnBuilder")
+ int getTemplateType() {
+ return mTemplateType;
+ }
+
+ /** Should ONLY be used by the subclasses */
+ @Nullable
+ @SuppressLint("GetterOnBuilder")
+ CharSequence getTitleText() {
+ return mTitleText;
+ }
+
+ /** Should ONLY be used by the subclasses */
+ @Nullable
+ @SuppressLint("GetterOnBuilder")
+ SmartspaceIcon getTitleIcon() {
+ return mTitleIcon;
+ }
+
+ /** Should ONLY be used by the subclasses */
+ @Nullable
+ @SuppressLint("GetterOnBuilder")
+ CharSequence getSubtitleText() {
+ return mSubtitleText;
+ }
+
+ /** Should ONLY be used by the subclasses */
+ @Nullable
+ @SuppressLint("GetterOnBuilder")
+ SmartspaceIcon getSubTitleIcon() {
+ return mSubTitleIcon;
+ }
+
+ /** Should ONLY be used by the subclasses */
+ @Nullable
+ @SuppressLint("GetterOnBuilder")
+ SmartspaceTapAction getPrimaryTapAction() {
+ return mPrimaryTapAction;
+ }
+
+ /** Should ONLY be used by the subclasses */
+ @Nullable
+ @SuppressLint("GetterOnBuilder")
+ CharSequence getSupplementalSubtitleText() {
+ return mSupplementalSubtitleText;
+ }
+
+ /** Should ONLY be used by the subclasses */
+ @Nullable
+ @SuppressLint("GetterOnBuilder")
+ SmartspaceIcon getSupplementalSubtitleIcon() {
+ return mSupplementalSubtitleIcon;
+ }
+
+ /** Should ONLY be used by the subclasses */
+ @Nullable
+ @SuppressLint("GetterOnBuilder")
+ SmartspaceTapAction getSupplementalSubtitleTapAction() {
+ return mSupplementalSubtitleTapAction;
+ }
+
+ /** Should ONLY be used by the subclasses */
+ @Nullable
+ @SuppressLint("GetterOnBuilder")
+ CharSequence getSupplementalAlarmText() {
+ return mSupplementalAlarmText;
+ }
+
+ /**
+ * Sets the card title.
+ */
+ @NonNull
+ public Builder setTitleText(@NonNull CharSequence titleText) {
+ mTitleText = titleText;
+ return this;
+ }
+
+ /**
+ * Sets the card title icon.
+ */
+ @NonNull
+ public Builder setTitleIcon(@NonNull SmartspaceIcon titleIcon) {
+ mTitleIcon = titleIcon;
+ return this;
+ }
+
+ /**
+ * Sets the card subtitle.
+ */
+ @NonNull
+ public Builder setSubtitleText(@NonNull CharSequence subtitleText) {
+ mSubtitleText = subtitleText;
+ return this;
+ }
+
+ /**
+ * Sets the card subtitle icon.
+ */
+ @NonNull
+ public Builder setSubTitleIcon(@NonNull SmartspaceIcon subTitleIcon) {
+ mSubTitleIcon = subTitleIcon;
+ return this;
+ }
+
+ /**
+ * Sets the card primary tap action.
+ */
+ @NonNull
+ public Builder setPrimaryTapAction(@NonNull SmartspaceTapAction primaryTapAction) {
+ mPrimaryTapAction = primaryTapAction;
+ return this;
+ }
+
+ /**
+ * Sets the supplemental subtitle text.
+ */
+ @NonNull
+ public Builder setSupplementalSubtitleText(@NonNull CharSequence supplementalSubtitleText) {
+ mSupplementalSubtitleText = supplementalSubtitleText;
+ return this;
+ }
+
+ /**
+ * Sets the supplemental subtitle icon.
+ */
+ @NonNull
+ public Builder setSupplementalSubtitleIcon(
+ @NonNull SmartspaceIcon supplementalSubtitleIcon) {
+ mSupplementalSubtitleIcon = supplementalSubtitleIcon;
+ return this;
+ }
+
+ /**
+ * Sets the supplemental subtitle tap action. {@code mPrimaryTapAction} will be used if not
+ * being
+ * set.
+ */
+ @NonNull
+ public Builder setSupplementalSubtitleTapAction(
+ @NonNull SmartspaceTapAction supplementalSubtitleTapAction) {
+ mSupplementalSubtitleTapAction = supplementalSubtitleTapAction;
+ return this;
+ }
+
+ /**
+ * Sets the supplemental alarm text.
+ */
+ @NonNull
+ public Builder setSupplementalAlarmText(@NonNull CharSequence supplementalAlarmText) {
+ mSupplementalAlarmText = supplementalAlarmText;
+ return this;
+ }
+
+ /**
+ * Builds a new SmartspaceDefaultUiTemplateData instance.
+ */
+ @NonNull
+ public SmartspaceDefaultUiTemplateData build() {
+ return new SmartspaceDefaultUiTemplateData(mTemplateType, mTitleText, mTitleIcon,
+ mSubtitleText, mSubTitleIcon, mPrimaryTapAction, mSupplementalSubtitleText,
+ mSupplementalSubtitleIcon, mSupplementalSubtitleTapAction,
+ mSupplementalAlarmText);
+ }
+ }
+}
diff --git a/core/java/android/app/smartspace/uitemplatedata/SmartspaceHeadToHeadUiTemplateData.java b/core/java/android/app/smartspace/uitemplatedata/SmartspaceHeadToHeadUiTemplateData.java
new file mode 100644
index 000000000000..c76af27e7f16
--- /dev/null
+++ b/core/java/android/app/smartspace/uitemplatedata/SmartspaceHeadToHeadUiTemplateData.java
@@ -0,0 +1,286 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.smartspace.uitemplatedata;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.app.smartspace.SmartspaceTarget;
+import android.app.smartspace.SmartspaceUtils;
+import android.os.Parcel;
+import android.text.TextUtils;
+
+import java.util.Objects;
+
+/**
+ * Holds all the relevant data needed to render a Smartspace card with the head-to-head Ui Template.
+ *
+ * @hide
+ */
+@SystemApi
+public final class SmartspaceHeadToHeadUiTemplateData extends SmartspaceDefaultUiTemplateData {
+
+ @Nullable
+ private final CharSequence mHeadToHeadTitle;
+ @Nullable
+ private final SmartspaceIcon mHeadToHeadFirstCompetitorIcon;
+ @Nullable
+ private final SmartspaceIcon mHeadToHeadSecondCompetitorIcon;
+ @Nullable
+ private final CharSequence mHeadToHeadFirstCompetitorText;
+ @Nullable
+ private final CharSequence mHeadToHeadSecondCompetitorText;
+
+ /** Tap action for the head-to-head secondary card. */
+ @Nullable
+ private final SmartspaceTapAction mHeadToHeadAction;
+
+ SmartspaceHeadToHeadUiTemplateData(@NonNull Parcel in) {
+ super(in);
+ mHeadToHeadTitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+ mHeadToHeadFirstCompetitorIcon = in.readTypedObject(SmartspaceIcon.CREATOR);
+ mHeadToHeadSecondCompetitorIcon = in.readTypedObject(SmartspaceIcon.CREATOR);
+ mHeadToHeadFirstCompetitorText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+ mHeadToHeadSecondCompetitorText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+ mHeadToHeadAction = in.readTypedObject(SmartspaceTapAction.CREATOR);
+ }
+
+ private SmartspaceHeadToHeadUiTemplateData(@SmartspaceTarget.UiTemplateType int templateType,
+ @Nullable CharSequence titleText,
+ @Nullable SmartspaceIcon titleIcon,
+ @Nullable CharSequence subtitleText,
+ @Nullable SmartspaceIcon subTitleIcon,
+ @Nullable SmartspaceTapAction primaryTapAction,
+ @Nullable CharSequence supplementalSubtitleText,
+ @Nullable SmartspaceIcon supplementalSubtitleIcon,
+ @Nullable SmartspaceTapAction supplementalSubtitleTapAction,
+ @Nullable CharSequence supplementalAlarmText,
+ @Nullable CharSequence headToHeadTitle,
+ @Nullable SmartspaceIcon headToHeadFirstCompetitorIcon,
+ @Nullable SmartspaceIcon headToHeadSecondCompetitorIcon,
+ @Nullable CharSequence headToHeadFirstCompetitorText,
+ @Nullable CharSequence headToHeadSecondCompetitorText,
+ @Nullable SmartspaceTapAction headToHeadAction) {
+ super(templateType, titleText, titleIcon, subtitleText, subTitleIcon, primaryTapAction,
+ supplementalSubtitleText, supplementalSubtitleIcon, supplementalSubtitleTapAction,
+ supplementalAlarmText);
+ mHeadToHeadTitle = headToHeadTitle;
+ mHeadToHeadFirstCompetitorIcon = headToHeadFirstCompetitorIcon;
+ mHeadToHeadSecondCompetitorIcon = headToHeadSecondCompetitorIcon;
+ mHeadToHeadFirstCompetitorText = headToHeadFirstCompetitorText;
+ mHeadToHeadSecondCompetitorText = headToHeadSecondCompetitorText;
+ mHeadToHeadAction = headToHeadAction;
+ }
+
+ @Nullable
+ public CharSequence getHeadToHeadTitle() {
+ return mHeadToHeadTitle;
+ }
+
+ @Nullable
+ public SmartspaceIcon getHeadToHeadFirstCompetitorIcon() {
+ return mHeadToHeadFirstCompetitorIcon;
+ }
+
+ @Nullable
+ public SmartspaceIcon getHeadToHeadSecondCompetitorIcon() {
+ return mHeadToHeadSecondCompetitorIcon;
+ }
+
+ @Nullable
+ public CharSequence getHeadToHeadFirstCompetitorText() {
+ return mHeadToHeadFirstCompetitorText;
+ }
+
+ @Nullable
+ public CharSequence getHeadToHeadSecondCompetitorText() {
+ return mHeadToHeadSecondCompetitorText;
+ }
+
+ @Nullable
+ public SmartspaceTapAction getHeadToHeadAction() {
+ return mHeadToHeadAction;
+ }
+
+ /**
+ * @see Parcelable.Creator
+ */
+ @NonNull
+ public static final Creator<SmartspaceHeadToHeadUiTemplateData> CREATOR =
+ new Creator<SmartspaceHeadToHeadUiTemplateData>() {
+ @Override
+ public SmartspaceHeadToHeadUiTemplateData createFromParcel(Parcel in) {
+ return new SmartspaceHeadToHeadUiTemplateData(in);
+ }
+
+ @Override
+ public SmartspaceHeadToHeadUiTemplateData[] newArray(int size) {
+ return new SmartspaceHeadToHeadUiTemplateData[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel out, int flags) {
+ super.writeToParcel(out, flags);
+ TextUtils.writeToParcel(mHeadToHeadTitle, out, flags);
+ out.writeTypedObject(mHeadToHeadFirstCompetitorIcon, flags);
+ out.writeTypedObject(mHeadToHeadSecondCompetitorIcon, flags);
+ TextUtils.writeToParcel(mHeadToHeadFirstCompetitorText, out, flags);
+ TextUtils.writeToParcel(mHeadToHeadSecondCompetitorText, out, flags);
+ out.writeTypedObject(mHeadToHeadAction, flags);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof SmartspaceHeadToHeadUiTemplateData)) return false;
+ if (!super.equals(o)) return false;
+ SmartspaceHeadToHeadUiTemplateData that = (SmartspaceHeadToHeadUiTemplateData) o;
+ return SmartspaceUtils.isEqual(mHeadToHeadTitle, that.mHeadToHeadTitle) && Objects.equals(
+ mHeadToHeadFirstCompetitorIcon, that.mHeadToHeadFirstCompetitorIcon)
+ && Objects.equals(
+ mHeadToHeadSecondCompetitorIcon, that.mHeadToHeadSecondCompetitorIcon)
+ && SmartspaceUtils.isEqual(mHeadToHeadFirstCompetitorText,
+ that.mHeadToHeadFirstCompetitorText)
+ && SmartspaceUtils.isEqual(mHeadToHeadSecondCompetitorText,
+ that.mHeadToHeadSecondCompetitorText)
+ && Objects.equals(
+ mHeadToHeadAction, that.mHeadToHeadAction);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(super.hashCode(), mHeadToHeadTitle, mHeadToHeadFirstCompetitorIcon,
+ mHeadToHeadSecondCompetitorIcon, mHeadToHeadFirstCompetitorText,
+ mHeadToHeadSecondCompetitorText,
+ mHeadToHeadAction);
+ }
+
+ @Override
+ public String toString() {
+ return super.toString() + " + SmartspaceHeadToHeadUiTemplateData{"
+ + "mH2HTitle=" + mHeadToHeadTitle
+ + ", mH2HFirstCompetitorIcon=" + mHeadToHeadFirstCompetitorIcon
+ + ", mH2HSecondCompetitorIcon=" + mHeadToHeadSecondCompetitorIcon
+ + ", mH2HFirstCompetitorText=" + mHeadToHeadFirstCompetitorText
+ + ", mH2HSecondCompetitorText=" + mHeadToHeadSecondCompetitorText
+ + ", mH2HAction=" + mHeadToHeadAction
+ + '}';
+ }
+
+ /**
+ * A builder for {@link SmartspaceHeadToHeadUiTemplateData} object.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final class Builder extends SmartspaceDefaultUiTemplateData.Builder {
+
+ private CharSequence mHeadToHeadTitle;
+ private SmartspaceIcon mHeadToHeadFirstCompetitorIcon;
+ private SmartspaceIcon mHeadToHeadSecondCompetitorIcon;
+ private CharSequence mHeadToHeadFirstCompetitorText;
+ private CharSequence mHeadToHeadSecondCompetitorText;
+ private SmartspaceTapAction mHeadToHeadAction;
+
+ /**
+ * A builder for {@link SmartspaceHeadToHeadUiTemplateData}.
+ */
+ public Builder() {
+ super(SmartspaceTarget.UI_TEMPLATE_HEAD_TO_HEAD);
+ }
+
+ /**
+ * Sets the head-to-head card's title
+ */
+ @NonNull
+ public Builder setHeadToHeadTitle(@Nullable CharSequence headToHeadTitle) {
+ mHeadToHeadTitle = headToHeadTitle;
+ return this;
+ }
+
+ /**
+ * Sets the head-to-head card's first competitor icon
+ */
+ @NonNull
+ public Builder setHeadToHeadFirstCompetitorIcon(
+ @Nullable SmartspaceIcon headToHeadFirstCompetitorIcon) {
+ mHeadToHeadFirstCompetitorIcon = headToHeadFirstCompetitorIcon;
+ return this;
+ }
+
+ /**
+ * Sets the head-to-head card's second competitor icon
+ */
+ @NonNull
+ public Builder setHeadToHeadSecondCompetitorIcon(
+ @Nullable SmartspaceIcon headToHeadSecondCompetitorIcon) {
+ mHeadToHeadSecondCompetitorIcon = headToHeadSecondCompetitorIcon;
+ return this;
+ }
+
+ /**
+ * Sets the head-to-head card's first competitor text
+ */
+ @NonNull
+ public Builder setHeadToHeadFirstCompetitorText(
+ @Nullable CharSequence headToHeadFirstCompetitorText) {
+ mHeadToHeadFirstCompetitorText = headToHeadFirstCompetitorText;
+ return this;
+ }
+
+ /**
+ * Sets the head-to-head card's second competitor text
+ */
+ @NonNull
+ public Builder setHeadToHeadSecondCompetitorText(
+ @Nullable CharSequence headToHeadSecondCompetitorText) {
+ mHeadToHeadSecondCompetitorText = headToHeadSecondCompetitorText;
+ return this;
+ }
+
+ /**
+ * Sets the head-to-head card's tap action
+ */
+ @NonNull
+ public Builder setHeadToHeadAction(@Nullable SmartspaceTapAction headToHeadAction) {
+ mHeadToHeadAction = headToHeadAction;
+ return this;
+ }
+
+ /**
+ * Builds a new SmartspaceHeadToHeadUiTemplateData instance.
+ */
+ @NonNull
+ public SmartspaceHeadToHeadUiTemplateData build() {
+ return new SmartspaceHeadToHeadUiTemplateData(getTemplateType(), getTitleText(),
+ getTitleIcon(), getSubtitleText(), getSubTitleIcon(), getPrimaryTapAction(),
+ getSupplementalSubtitleText(), getSupplementalSubtitleIcon(),
+ getSupplementalSubtitleTapAction(), getSupplementalAlarmText(),
+ mHeadToHeadTitle,
+ mHeadToHeadFirstCompetitorIcon,
+ mHeadToHeadSecondCompetitorIcon, mHeadToHeadFirstCompetitorText,
+ mHeadToHeadSecondCompetitorText,
+ mHeadToHeadAction);
+ }
+ }
+}
diff --git a/core/java/android/app/smartspace/uitemplatedata/SmartspaceIcon.java b/core/java/android/app/smartspace/uitemplatedata/SmartspaceIcon.java
new file mode 100644
index 000000000000..70b30954afa7
--- /dev/null
+++ b/core/java/android/app/smartspace/uitemplatedata/SmartspaceIcon.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.smartspace.uitemplatedata;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.app.smartspace.SmartspaceUtils;
+import android.graphics.drawable.Icon;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+import java.util.Objects;
+
+/**
+ * Holds the information for a Smartspace-card icon. Including the icon image itself, and an
+ * optional content description as the icon's accessibility description.
+ *
+ * @hide
+ */
+@SystemApi
+public final class SmartspaceIcon implements Parcelable {
+
+ @NonNull
+ private final Icon mIcon;
+
+ @Nullable
+ private final CharSequence mContentDescription;
+
+ SmartspaceIcon(@NonNull Parcel in) {
+ mIcon = in.readTypedObject(Icon.CREATOR);
+ mContentDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+ }
+
+ private SmartspaceIcon(@NonNull Icon icon, @Nullable CharSequence contentDescription) {
+ mIcon = icon;
+ mContentDescription = contentDescription;
+ }
+
+ @NonNull
+ public Icon getIcon() {
+ return mIcon;
+ }
+
+ @Nullable
+ public CharSequence getContentDescription() {
+ return mContentDescription;
+ }
+
+ @NonNull
+ public static final Creator<SmartspaceIcon> CREATOR = new Creator<SmartspaceIcon>() {
+ @Override
+ public SmartspaceIcon createFromParcel(Parcel in) {
+ return new SmartspaceIcon(in);
+ }
+
+ @Override
+ public SmartspaceIcon[] newArray(int size) {
+ return new SmartspaceIcon[size];
+ }
+ };
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof SmartspaceIcon)) return false;
+ SmartspaceIcon that = (SmartspaceIcon) o;
+ return mIcon.equals(that.mIcon) && SmartspaceUtils.isEqual(mContentDescription,
+ that.mContentDescription);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mIcon, mContentDescription);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel out, int flags) {
+ out.writeTypedObject(mIcon, flags);
+ TextUtils.writeToParcel(mContentDescription, out, flags);
+ }
+
+ @Override
+ public String toString() {
+ return "SmartspaceIcon{"
+ + "mImage=" + mIcon
+ + ", mContentDescription='" + mContentDescription + '\''
+ + '}';
+ }
+
+ /**
+ * A builder for {@link SmartspaceIcon} object.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final class Builder {
+
+ private Icon mIcon;
+ private CharSequence mContentDescription;
+
+ /**
+ * A builder for {@link SmartspaceIcon}.
+ *
+ * @param icon the icon image of this smartspace icon.
+ */
+ public Builder(@NonNull Icon icon) {
+ mIcon = Objects.requireNonNull(icon);
+ }
+
+ /**
+ * Sets the icon's content description.
+ */
+ @NonNull
+ public Builder setContentDescription(@NonNull CharSequence contentDescription) {
+ mContentDescription = contentDescription;
+ return this;
+ }
+
+ /**
+ * Builds a new SmartspaceIcon instance.
+ */
+ @NonNull
+ public SmartspaceIcon build() {
+ return new SmartspaceIcon(mIcon, mContentDescription);
+ }
+ }
+}
diff --git a/core/java/android/app/smartspace/uitemplatedata/SmartspaceSubCardUiTemplateData.java b/core/java/android/app/smartspace/uitemplatedata/SmartspaceSubCardUiTemplateData.java
new file mode 100644
index 000000000000..287cf8e61bc3
--- /dev/null
+++ b/core/java/android/app/smartspace/uitemplatedata/SmartspaceSubCardUiTemplateData.java
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.smartspace.uitemplatedata;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.app.smartspace.SmartspaceTarget;
+import android.app.smartspace.SmartspaceUtils;
+import android.os.Parcel;
+import android.text.TextUtils;
+
+import java.util.Objects;
+
+/**
+ * Holds all the relevant data needed to render a Smartspace card with the sub-card Ui Template.
+ *
+ * @hide
+ */
+@SystemApi
+public final class SmartspaceSubCardUiTemplateData extends SmartspaceDefaultUiTemplateData {
+
+ /** Icon for the sub-card. */
+ @NonNull
+ private final SmartspaceIcon mSubCardIcon;
+
+ /** Text for the sub-card, which shows below the icon when being set. */
+ @Nullable
+ private final CharSequence mSubCardText;
+
+ /** Tap action for the sub-card secondary card. */
+ @Nullable
+ private final SmartspaceTapAction mSubCardAction;
+
+ SmartspaceSubCardUiTemplateData(@NonNull Parcel in) {
+ super(in);
+ mSubCardIcon = in.readTypedObject(SmartspaceIcon.CREATOR);
+ mSubCardText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+ mSubCardAction = in.readTypedObject(SmartspaceTapAction.CREATOR);
+ }
+
+ private SmartspaceSubCardUiTemplateData(int templateType,
+ @Nullable CharSequence titleText,
+ @Nullable SmartspaceIcon titleIcon,
+ @Nullable CharSequence subtitleText,
+ @Nullable SmartspaceIcon subTitleIcon,
+ @Nullable SmartspaceTapAction primaryTapAction,
+ @Nullable CharSequence supplementalSubtitleText,
+ @Nullable SmartspaceIcon supplementalSubtitleIcon,
+ @Nullable SmartspaceTapAction supplementalSubtitleTapAction,
+ @Nullable CharSequence supplementalAlarmText,
+ @NonNull SmartspaceIcon subCardIcon,
+ @Nullable CharSequence subCardText,
+ @Nullable SmartspaceTapAction subCardAction) {
+ super(templateType, titleText, titleIcon, subtitleText, subTitleIcon, primaryTapAction,
+ supplementalSubtitleText, supplementalSubtitleIcon, supplementalSubtitleTapAction,
+ supplementalAlarmText);
+ mSubCardIcon = subCardIcon;
+ mSubCardText = subCardText;
+ mSubCardAction = subCardAction;
+ }
+
+ @NonNull
+ public SmartspaceIcon getSubCardIcon() {
+ return mSubCardIcon;
+ }
+
+ @Nullable
+ public CharSequence getSubCardText() {
+ return mSubCardText;
+ }
+
+ @Nullable
+ public SmartspaceTapAction getSubCardAction() {
+ return mSubCardAction;
+ }
+
+ /**
+ * @see Parcelable.Creator
+ */
+ @NonNull
+ public static final Creator<SmartspaceSubCardUiTemplateData> CREATOR =
+ new Creator<SmartspaceSubCardUiTemplateData>() {
+ @Override
+ public SmartspaceSubCardUiTemplateData createFromParcel(Parcel in) {
+ return new SmartspaceSubCardUiTemplateData(in);
+ }
+
+ @Override
+ public SmartspaceSubCardUiTemplateData[] newArray(int size) {
+ return new SmartspaceSubCardUiTemplateData[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel out, int flags) {
+ super.writeToParcel(out, flags);
+ out.writeTypedObject(mSubCardIcon, flags);
+ TextUtils.writeToParcel(mSubCardText, out, flags);
+ out.writeTypedObject(mSubCardAction, flags);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof SmartspaceSubCardUiTemplateData)) return false;
+ if (!super.equals(o)) return false;
+ SmartspaceSubCardUiTemplateData that = (SmartspaceSubCardUiTemplateData) o;
+ return mSubCardIcon.equals(that.mSubCardIcon) && SmartspaceUtils.isEqual(mSubCardText,
+ that.mSubCardText) && Objects.equals(mSubCardAction,
+ that.mSubCardAction);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(super.hashCode(), mSubCardIcon, mSubCardText, mSubCardAction);
+ }
+
+ @Override
+ public String toString() {
+ return super.toString() + " + SmartspaceSubCardUiTemplateData{"
+ + "mSubCardIcon=" + mSubCardIcon
+ + ", mSubCardText=" + mSubCardText
+ + ", mSubCardAction=" + mSubCardAction
+ + '}';
+ }
+
+ /**
+ * A builder for {@link SmartspaceSubCardUiTemplateData} object.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final class Builder extends SmartspaceDefaultUiTemplateData.Builder {
+
+ private final SmartspaceIcon mSubCardIcon;
+ private CharSequence mSubCardText;
+ private SmartspaceTapAction mSubCardAction;
+
+ /**
+ * A builder for {@link SmartspaceSubCardUiTemplateData}.
+ */
+ public Builder(@NonNull SmartspaceIcon subCardIcon) {
+ super(SmartspaceTarget.UI_TEMPLATE_SUB_CARD);
+ mSubCardIcon = Objects.requireNonNull(subCardIcon);
+ }
+
+ /**
+ * Sets the card title text.
+ */
+ @NonNull
+ public Builder setSubCardAction(@NonNull CharSequence subCardTitleText) {
+ mSubCardText = subCardTitleText;
+ return this;
+ }
+
+ /**
+ * Sets the card tap action.
+ */
+ @NonNull
+ public Builder setSubCardAction(@NonNull SmartspaceTapAction subCardAction) {
+ mSubCardAction = subCardAction;
+ return this;
+ }
+
+ /**
+ * Builds a new SmartspaceSubCardUiTemplateData instance.
+ */
+ @NonNull
+ public SmartspaceSubCardUiTemplateData build() {
+ return new SmartspaceSubCardUiTemplateData(getTemplateType(), getTitleText(),
+ getTitleIcon(), getSubtitleText(), getSubTitleIcon(), getPrimaryTapAction(),
+ getSupplementalSubtitleText(), getSupplementalSubtitleIcon(),
+ getSupplementalSubtitleTapAction(), getSupplementalAlarmText(), mSubCardIcon,
+ mSubCardText,
+ mSubCardAction);
+ }
+ }
+}
diff --git a/core/java/android/app/smartspace/uitemplatedata/SmartspaceSubImageUiTemplateData.java b/core/java/android/app/smartspace/uitemplatedata/SmartspaceSubImageUiTemplateData.java
new file mode 100644
index 000000000000..c4799936f8c9
--- /dev/null
+++ b/core/java/android/app/smartspace/uitemplatedata/SmartspaceSubImageUiTemplateData.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.smartspace.uitemplatedata;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.app.smartspace.SmartspaceTarget;
+import android.os.Parcel;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Holds all the relevant data needed to render a Smartspace card with the sub-image Ui Template.
+ *
+ * @hide
+ */
+@SystemApi
+public final class SmartspaceSubImageUiTemplateData extends SmartspaceDefaultUiTemplateData {
+
+ /** Texts are shown next to the image as a vertical list */
+ @NonNull
+ private final List<CharSequence> mSubImageTexts;
+
+ /** If multiple images are passed in, they will be rendered as GIF. */
+ @NonNull
+ private final List<SmartspaceIcon> mSubImages;
+
+ /** Tap action for the sub-image secondary card. */
+ @Nullable
+ private final SmartspaceTapAction mSubImageAction;
+
+ SmartspaceSubImageUiTemplateData(@NonNull Parcel in) {
+ super(in);
+ mSubImageTexts = Arrays.asList(in.readCharSequenceArray());
+ mSubImages = in.createTypedArrayList(SmartspaceIcon.CREATOR);
+ mSubImageAction = in.readTypedObject(SmartspaceTapAction.CREATOR);
+ }
+
+ private SmartspaceSubImageUiTemplateData(@SmartspaceTarget.UiTemplateType int templateType,
+ @Nullable CharSequence titleText,
+ @Nullable SmartspaceIcon titleIcon,
+ @Nullable CharSequence subtitleText,
+ @Nullable SmartspaceIcon subTitleIcon,
+ @Nullable SmartspaceTapAction primaryTapAction,
+ @Nullable CharSequence supplementalSubtitleText,
+ @Nullable SmartspaceIcon supplementalSubtitleIcon,
+ @Nullable SmartspaceTapAction supplementalSubtitleTapAction,
+ @Nullable CharSequence supplementalAlarmText,
+ @NonNull List<CharSequence> subImageTexts,
+ @NonNull List<SmartspaceIcon> subImages,
+ @Nullable SmartspaceTapAction subImageAction) {
+ super(templateType, titleText, titleIcon, subtitleText, subTitleIcon, primaryTapAction,
+ supplementalSubtitleText, supplementalSubtitleIcon, supplementalSubtitleTapAction,
+ supplementalAlarmText);
+ mSubImageTexts = subImageTexts;
+ mSubImages = subImages;
+ mSubImageAction = subImageAction;
+ }
+
+ @NonNull
+ public List<CharSequence> getSubImageTexts() {
+ return mSubImageTexts;
+ }
+
+ @NonNull
+ public List<SmartspaceIcon> getSubImages() {
+ return mSubImages;
+ }
+
+ @Nullable
+ public SmartspaceTapAction getSubImageAction() {
+ return mSubImageAction;
+ }
+
+ /**
+ * @see Parcelable.Creator
+ */
+ @NonNull
+ public static final Creator<SmartspaceSubImageUiTemplateData> CREATOR =
+ new Creator<SmartspaceSubImageUiTemplateData>() {
+ @Override
+ public SmartspaceSubImageUiTemplateData createFromParcel(Parcel in) {
+ return new SmartspaceSubImageUiTemplateData(in);
+ }
+
+ @Override
+ public SmartspaceSubImageUiTemplateData[] newArray(int size) {
+ return new SmartspaceSubImageUiTemplateData[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel out, int flags) {
+ super.writeToParcel(out, flags);
+ out.writeCharSequenceList(new ArrayList<>(mSubImageTexts));
+ out.writeTypedList(mSubImages);
+ out.writeTypedObject(mSubImageAction, flags);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof SmartspaceSubImageUiTemplateData)) return false;
+ if (!super.equals(o)) return false;
+ SmartspaceSubImageUiTemplateData that = (SmartspaceSubImageUiTemplateData) o;
+ return Objects.equals(mSubImageTexts, that.mSubImageTexts)
+ && Objects.equals(mSubImages, that.mSubImages) && Objects.equals(
+ mSubImageAction, that.mSubImageAction);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(super.hashCode(), mSubImageTexts, mSubImages, mSubImageAction);
+ }
+
+ @Override
+ public String toString() {
+ return super.toString() + " + SmartspaceSubImageUiTemplateData{"
+ + "mSubImageTexts=" + mSubImageTexts
+ + ", mSubImages=" + mSubImages
+ + ", mSubImageAction=" + mSubImageAction
+ + '}';
+ }
+
+ /**
+ * A builder for {@link SmartspaceSubImageUiTemplateData} object.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final class Builder extends SmartspaceDefaultUiTemplateData.Builder {
+
+ private final List<CharSequence> mSubImageTexts;
+ private final List<SmartspaceIcon> mSubImages;
+ private SmartspaceTapAction mSubImageAction;
+
+ /**
+ * A builder for {@link SmartspaceSubImageUiTemplateData}.
+ */
+ public Builder(@NonNull List<CharSequence> subImageTexts,
+ @NonNull List<SmartspaceIcon> subImages) {
+ super(SmartspaceTarget.UI_TEMPLATE_SUB_IMAGE);
+ mSubImageTexts = Objects.requireNonNull(subImageTexts);
+ mSubImages = Objects.requireNonNull(subImages);
+ }
+
+ /**
+ * Sets the card tap action.
+ */
+ @NonNull
+ public Builder setCarouselAction(@NonNull SmartspaceTapAction subImageAction) {
+ mSubImageAction = subImageAction;
+ return this;
+ }
+
+ /**
+ * Builds a new SmartspaceSubImageUiTemplateData instance.
+ */
+ @NonNull
+ public SmartspaceSubImageUiTemplateData build() {
+ return new SmartspaceSubImageUiTemplateData(getTemplateType(), getTitleText(),
+ getTitleIcon(), getSubtitleText(), getSubTitleIcon(), getPrimaryTapAction(),
+ getSupplementalSubtitleText(), getSupplementalSubtitleIcon(),
+ getSupplementalSubtitleTapAction(), getSupplementalAlarmText(), mSubImageTexts,
+ mSubImages,
+ mSubImageAction);
+ }
+ }
+}
diff --git a/core/java/android/app/smartspace/uitemplatedata/SmartspaceSubListUiTemplateData.java b/core/java/android/app/smartspace/uitemplatedata/SmartspaceSubListUiTemplateData.java
new file mode 100644
index 000000000000..b5d9645027d8
--- /dev/null
+++ b/core/java/android/app/smartspace/uitemplatedata/SmartspaceSubListUiTemplateData.java
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.smartspace.uitemplatedata;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.app.smartspace.SmartspaceTarget;
+import android.os.Parcel;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Holds all the relevant data needed to render a Smartspace card with the sub-list Ui Template.
+ *
+ * @hide
+ */
+@SystemApi
+public final class SmartspaceSubListUiTemplateData extends SmartspaceDefaultUiTemplateData {
+
+ @Nullable
+ private final SmartspaceIcon mSubListIcon;
+ @NonNull
+ private final List<CharSequence> mSubListTexts;
+
+ /** Tap action for the sub-list secondary card. */
+ @Nullable
+ private final SmartspaceTapAction mSubListAction;
+
+ SmartspaceSubListUiTemplateData(@NonNull Parcel in) {
+ super(in);
+ mSubListIcon = in.readTypedObject(SmartspaceIcon.CREATOR);
+ mSubListTexts = Arrays.asList(in.readCharSequenceArray());
+ mSubListAction = in.readTypedObject(SmartspaceTapAction.CREATOR);
+ }
+
+ private SmartspaceSubListUiTemplateData(@SmartspaceTarget.UiTemplateType int templateType,
+ @Nullable CharSequence titleText,
+ @Nullable SmartspaceIcon titleIcon,
+ @Nullable CharSequence subtitleText,
+ @Nullable SmartspaceIcon subTitleIcon,
+ @Nullable SmartspaceTapAction primaryTapAction,
+ @Nullable CharSequence supplementalSubtitleText,
+ @Nullable SmartspaceIcon supplementalSubtitleIcon,
+ @Nullable SmartspaceTapAction supplementalSubtitleTapAction,
+ @Nullable CharSequence supplementalAlarmText,
+ @Nullable SmartspaceIcon subListIcon,
+ @NonNull List<CharSequence> subListTexts,
+ @Nullable SmartspaceTapAction subListAction) {
+ super(templateType, titleText, titleIcon, subtitleText, subTitleIcon, primaryTapAction,
+ supplementalSubtitleText, supplementalSubtitleIcon, supplementalSubtitleTapAction,
+ supplementalAlarmText);
+ mSubListIcon = subListIcon;
+ mSubListTexts = subListTexts;
+ mSubListAction = subListAction;
+ }
+
+ @Nullable
+ public SmartspaceIcon getSubListIcon() {
+ return mSubListIcon;
+ }
+
+ @NonNull
+ public List<CharSequence> getSubListTexts() {
+ return mSubListTexts;
+ }
+
+ @Nullable
+ public SmartspaceTapAction getSubListAction() {
+ return mSubListAction;
+ }
+
+ /**
+ * @see Parcelable.Creator
+ */
+ @NonNull
+ public static final Creator<SmartspaceSubListUiTemplateData> CREATOR =
+ new Creator<SmartspaceSubListUiTemplateData>() {
+ @Override
+ public SmartspaceSubListUiTemplateData createFromParcel(Parcel in) {
+ return new SmartspaceSubListUiTemplateData(in);
+ }
+
+ @Override
+ public SmartspaceSubListUiTemplateData[] newArray(int size) {
+ return new SmartspaceSubListUiTemplateData[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel out, int flags) {
+ super.writeToParcel(out, flags);
+ out.writeTypedObject(mSubListIcon, flags);
+ out.writeCharSequenceList(new ArrayList<>(mSubListTexts));
+ out.writeTypedObject(mSubListAction, flags);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof SmartspaceSubListUiTemplateData)) return false;
+ if (!super.equals(o)) return false;
+ SmartspaceSubListUiTemplateData that = (SmartspaceSubListUiTemplateData) o;
+ return Objects.equals(mSubListIcon, that.mSubListIcon) && Objects.equals(
+ mSubListTexts, that.mSubListTexts) && Objects.equals(mSubListAction,
+ that.mSubListAction);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(super.hashCode(), mSubListIcon, mSubListTexts, mSubListAction);
+ }
+
+ @Override
+ public String toString() {
+ return super.toString() + " + SmartspaceSubListUiTemplateData{"
+ + "mSubListIcon=" + mSubListIcon
+ + ", mSubListTexts=" + mSubListTexts
+ + ", mSubListAction=" + mSubListAction
+ + '}';
+ }
+
+ /**
+ * A builder for {@link SmartspaceSubListUiTemplateData} object.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final class Builder extends SmartspaceDefaultUiTemplateData.Builder {
+
+ private SmartspaceIcon mSubListIcon;
+ private final List<CharSequence> mSubListTexts;
+ private SmartspaceTapAction mSubListAction;
+
+ /**
+ * A builder for {@link SmartspaceSubListUiTemplateData}.
+ */
+ public Builder(@NonNull List<CharSequence> subListTexts) {
+ super(SmartspaceTarget.UI_TEMPLATE_SUB_LIST);
+ mSubListTexts = Objects.requireNonNull(subListTexts);
+ }
+
+ /**
+ * Sets the sub-list card icon.
+ */
+ @NonNull
+ public Builder setSubListIcon(@NonNull SmartspaceIcon subListIcon) {
+ mSubListIcon = subListIcon;
+ return this;
+ }
+
+ /**
+ * Sets the card tap action.
+ */
+ @NonNull
+ public Builder setCarouselAction(@NonNull SmartspaceTapAction subListAction) {
+ mSubListAction = subListAction;
+ return this;
+ }
+
+ /**
+ * Builds a new SmartspaceSubListUiTemplateData instance.
+ */
+ @NonNull
+ public SmartspaceSubListUiTemplateData build() {
+ return new SmartspaceSubListUiTemplateData(getTemplateType(), getTitleText(),
+ getTitleIcon(), getSubtitleText(), getSubTitleIcon(), getPrimaryTapAction(),
+ getSupplementalSubtitleText(), getSupplementalSubtitleIcon(),
+ getSupplementalSubtitleTapAction(), getSupplementalAlarmText(), mSubListIcon,
+ mSubListTexts,
+ mSubListAction);
+ }
+ }
+}
diff --git a/core/java/android/app/smartspace/uitemplatedata/SmartspaceTapAction.java b/core/java/android/app/smartspace/uitemplatedata/SmartspaceTapAction.java
new file mode 100644
index 000000000000..27d8e5fed3b1
--- /dev/null
+++ b/core/java/android/app/smartspace/uitemplatedata/SmartspaceTapAction.java
@@ -0,0 +1,232 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.smartspace.uitemplatedata;
+
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
+import android.app.PendingIntent;
+import android.app.smartspace.SmartspaceUtils;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.UserHandle;
+import android.text.TextUtils;
+
+import java.util.Objects;
+
+/**
+ * A {@link SmartspaceTapAction} represents an action which can be taken by a user by tapping on
+ * either the title, the subtitle or on the icon. Supported instances are Intents and
+ * PendingIntents. These actions can be called from another process or within the client process.
+ *
+ * Clients can also receive ShorcutInfos in the extras bundle.
+ *
+ * @hide
+ */
+@SystemApi
+public final class SmartspaceTapAction implements Parcelable {
+
+ /** A unique Id of this {@link SmartspaceTapAction}. */
+ @Nullable
+ private final CharSequence mId;
+
+ @Nullable
+ private final Intent mIntent;
+
+ @Nullable
+ private final PendingIntent mPendingIntent;
+
+ @Nullable
+ private final UserHandle mUserHandle;
+
+ @Nullable
+ private Bundle mExtras;
+
+ SmartspaceTapAction(@NonNull Parcel in) {
+ mId = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+ mIntent = in.readTypedObject(Intent.CREATOR);
+ mPendingIntent = in.readTypedObject(PendingIntent.CREATOR);
+ mUserHandle = in.readTypedObject(UserHandle.CREATOR);
+ mExtras = in.readBundle();
+ }
+
+ private SmartspaceTapAction(@Nullable CharSequence id, @Nullable Intent intent,
+ @Nullable PendingIntent pendingIntent, @Nullable UserHandle userHandle,
+ @Nullable Bundle extras) {
+ mId = id;
+ mIntent = intent;
+ mPendingIntent = pendingIntent;
+ mUserHandle = userHandle;
+ mExtras = extras;
+ }
+
+ @Nullable
+ public CharSequence getId() {
+ return mId;
+ }
+
+ @SuppressLint("IntentBuilderName")
+ @Nullable
+ public Intent getIntent() {
+ return mIntent;
+ }
+
+ @Nullable
+ public PendingIntent getPendingIntent() {
+ return mPendingIntent;
+ }
+
+ @Nullable
+ public UserHandle getUserHandle() {
+ return mUserHandle;
+ }
+
+ @Nullable
+ @SuppressLint("NullableCollection")
+ public Bundle getExtras() {
+ return mExtras;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel out, int flags) {
+ TextUtils.writeToParcel(mId, out, flags);
+ out.writeTypedObject(mIntent, flags);
+ out.writeTypedObject(mPendingIntent, flags);
+ out.writeTypedObject(mUserHandle, flags);
+ out.writeBundle(mExtras);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @NonNull
+ public static final Creator<SmartspaceTapAction> CREATOR = new Creator<SmartspaceTapAction>() {
+ @Override
+ public SmartspaceTapAction createFromParcel(Parcel in) {
+ return new SmartspaceTapAction(in);
+ }
+
+ @Override
+ public SmartspaceTapAction[] newArray(int size) {
+ return new SmartspaceTapAction[size];
+ }
+ };
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof SmartspaceTapAction)) return false;
+ SmartspaceTapAction that = (SmartspaceTapAction) o;
+ return SmartspaceUtils.isEqual(mId, that.mId);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mId);
+ }
+
+ @Override
+ public String toString() {
+ return "SmartspaceTapAction{"
+ + "mId=" + mId
+ + "mIntent=" + mIntent
+ + ", mPendingIntent=" + mPendingIntent
+ + ", mUserHandle=" + mUserHandle
+ + ", mExtras=" + mExtras
+ + '}';
+ }
+
+ /**
+ * A builder for {@link SmartspaceTapAction} object.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final class Builder {
+
+ private CharSequence mId;
+ private Intent mIntent;
+ private PendingIntent mPendingIntent;
+ private UserHandle mUserHandle;
+ private Bundle mExtras;
+
+ /**
+ * A builder for {@link SmartspaceTapAction}.
+ *
+ * @param id A unique Id of this {@link SmartspaceTapAction}.
+ */
+ public Builder(@NonNull CharSequence id) {
+ mId = Objects.requireNonNull(id);
+ }
+
+ /**
+ * Sets the action intent.
+ */
+ @NonNull
+ public Builder setIntent(@NonNull Intent intent) {
+ mIntent = intent;
+ return this;
+ }
+
+ /**
+ * Sets the pending intent.
+ */
+ @NonNull
+ public Builder setPendingIntent(@NonNull PendingIntent pendingIntent) {
+ mPendingIntent = pendingIntent;
+ return this;
+ }
+
+ /**
+ * Sets the user handle.
+ */
+ @NonNull
+ @SuppressLint("UserHandleName")
+ public Builder setUserHandle(@Nullable UserHandle userHandle) {
+ mUserHandle = userHandle;
+ return this;
+ }
+
+ /**
+ * Sets the extras.
+ */
+ @NonNull
+ public Builder setExtras(@NonNull Bundle extras) {
+ mExtras = extras;
+ return this;
+ }
+
+ /**
+ * Builds a new SmartspaceTapAction instance.
+ *
+ * @throws IllegalStateException if the tap action is empty.
+ */
+ @NonNull
+ public SmartspaceTapAction build() {
+ if (mIntent == null && mPendingIntent == null && mExtras == null) {
+ throw new IllegalStateException("Please assign at least 1 valid tap field");
+ }
+ return new SmartspaceTapAction(mId, mIntent, mPendingIntent, mUserHandle, mExtras);
+ }
+ }
+}
diff --git a/core/java/android/apphibernation/AppHibernationManager.java b/core/java/android/apphibernation/AppHibernationManager.java
index a36da8816d60..6e3bbccdbef0 100644
--- a/core/java/android/apphibernation/AppHibernationManager.java
+++ b/core/java/android/apphibernation/AppHibernationManager.java
@@ -24,7 +24,10 @@ import android.content.Context;
import android.os.RemoteException;
import android.os.ServiceManager;
+import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
+import java.util.Set;
/**
* This class provides an API surface for system apps to manipulate the app hibernation
@@ -129,4 +132,38 @@ public class AppHibernationManager {
throw e.rethrowFromSystemServer();
}
}
+
+ /**
+ * Returns the stats from app hibernation for each package provided.
+ *
+ * @param packageNames the set of packages to return stats for
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(value = android.Manifest.permission.MANAGE_APP_HIBERNATION)
+ public @NonNull Map<String, HibernationStats> getHibernationStatsForUser(
+ @NonNull Set<String> packageNames) {
+ try {
+ return mIAppHibernationService.getHibernationStatsForUser(
+ new ArrayList(packageNames), mContext.getUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns the stats from app hibernation for all packages for the user
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(value = android.Manifest.permission.MANAGE_APP_HIBERNATION)
+ public @NonNull Map<String, HibernationStats> getHibernationStatsForUser() {
+ try {
+ return mIAppHibernationService.getHibernationStatsForUser(
+ null /* packageNames */, mContext.getUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/core/java/android/apphibernation/HibernationStats.aidl b/core/java/android/apphibernation/HibernationStats.aidl
new file mode 100644
index 000000000000..a92b903f56dc
--- /dev/null
+++ b/core/java/android/apphibernation/HibernationStats.aidl
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) 2022, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.apphibernation;
+
+parcelable HibernationStats; \ No newline at end of file
diff --git a/core/java/android/apphibernation/HibernationStats.java b/core/java/android/apphibernation/HibernationStats.java
new file mode 100644
index 000000000000..2c4db8218643
--- /dev/null
+++ b/core/java/android/apphibernation/HibernationStats.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.apphibernation;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Stats for a hibernating package.
+ * @hide
+ */
+@SystemApi
+public final class HibernationStats implements Parcelable {
+ private final long mDiskBytesSaved;
+
+ /** @hide */
+ public HibernationStats(long diskBytesSaved) {
+ mDiskBytesSaved = diskBytesSaved;
+ }
+
+ private HibernationStats(@NonNull Parcel in) {
+ mDiskBytesSaved = in.readLong();
+ }
+
+ /**
+ * Get the disk storage saved from hibernation in bytes.
+ */
+ public long getDiskBytesSaved() {
+ return mDiskBytesSaved;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeLong(mDiskBytesSaved);
+ }
+
+ public static final @NonNull Creator<HibernationStats> CREATOR =
+ new Creator<HibernationStats>() {
+ @Override
+ public HibernationStats createFromParcel(Parcel in) {
+ return new HibernationStats(in);
+ }
+
+ @Override
+ public HibernationStats[] newArray(int size) {
+ return new HibernationStats[size];
+ }
+ };
+}
diff --git a/core/java/android/apphibernation/IAppHibernationService.aidl b/core/java/android/apphibernation/IAppHibernationService.aidl
index afdb3fe03dad..11bb6b505cdf 100644
--- a/core/java/android/apphibernation/IAppHibernationService.aidl
+++ b/core/java/android/apphibernation/IAppHibernationService.aidl
@@ -16,6 +16,8 @@
package android.apphibernation;
+import android.apphibernation.HibernationStats;
+
/**
* Binder interface to communicate with AppHibernationService.
* @hide
@@ -26,4 +28,6 @@ interface IAppHibernationService {
boolean isHibernatingGlobally(String packageName);
void setHibernatingGlobally(String packageName, boolean isHibernating);
List<String> getHibernatingPackagesForUser(int userId);
+ Map<String, HibernationStats> getHibernationStatsForUser(in List<String> packageNames,
+ int userId);
} \ No newline at end of file
diff --git a/core/java/android/companion/virtual/VirtualDeviceParams.java b/core/java/android/companion/virtual/VirtualDeviceParams.java
index 2ddfeb4c8ab5..1d0f7c091807 100644
--- a/core/java/android/companion/virtual/VirtualDeviceParams.java
+++ b/core/java/android/companion/virtual/VirtualDeviceParams.java
@@ -22,6 +22,7 @@ import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.content.ComponentName;
import android.os.Parcel;
@@ -106,11 +107,13 @@ public final class VirtualDeviceParams implements Parcelable {
}
/**
- * Returns the set of activities allowed to be streamed, or {@code null} if this is not set.
+ * Returns the set of activities allowed to be streamed, or {@code null} if all activities are
+ * allowed, except the ones explicitly blocked.
*
* @see Builder#setAllowedActivities(Set)
- * @hide // TODO(b/194949534): Unhide this API
*/
+ // Null and empty have different semantics - Null allows all activities to be streamed
+ @SuppressLint("NullableCollection")
@Nullable
public Set<ComponentName> getAllowedActivities() {
if (mAllowedActivities == null) {
@@ -120,12 +123,13 @@ public final class VirtualDeviceParams implements Parcelable {
}
/**
- * Returns the set of activities that are blocked from streaming, or {@code null} if this is not
- * set.
+ * Returns the set of activities that are blocked from streaming, or {@code null} to indicate
+ * that all activities in {@link #getAllowedActivities} are allowed.
*
* @see Builder#setBlockedActivities(Set)
- * @hide // TODO(b/194949534): Unhide this API
*/
+ // Allowing null to enforce that at most one of allowed / blocked activities can be non-null
+ @SuppressLint("NullableCollection")
@Nullable
public Set<ComponentName> getBlockedActivities() {
if (mBlockedActivities == null) {
@@ -255,8 +259,10 @@ public final class VirtualDeviceParams implements Parcelable {
*
* @param allowedActivities A set of activity {@link ComponentName} allowed to be launched
* in the virtual device.
- * @hide // TODO(b/194949534): Unhide this API
*/
+ // Null and empty have different semantics - Null allows all activities to be streamed
+ @SuppressLint("NullableCollection")
+ @NonNull
public Builder setAllowedActivities(@Nullable Set<ComponentName> allowedActivities) {
if (mBlockedActivities != null && allowedActivities != null) {
throw new IllegalArgumentException(
@@ -279,8 +285,10 @@ public final class VirtualDeviceParams implements Parcelable {
*
* @param blockedActivities A set of {@link ComponentName} to be blocked launching from
* virtual device.
- * @hide // TODO(b/194949534): Unhide this API
*/
+ // Allowing null to enforce that at most one of allowed / blocked activities can be non-null
+ @SuppressLint("NullableCollection")
+ @NonNull
public Builder setBlockedActivities(@Nullable Set<ComponentName> blockedActivities) {
if (mAllowedActivities != null && blockedActivities != null) {
throw new IllegalArgumentException(
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 4b4e00855ac1..a0864d6459d3 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -37,6 +37,7 @@ import android.annotation.TestApi;
import android.annotation.UiContext;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
+import android.app.BroadcastOptions;
import android.app.GameManager;
import android.app.IApplicationThread;
import android.app.IServiceConnection;
@@ -2260,6 +2261,27 @@ public abstract class Context {
}
/**
+ * Version of {@link #sendBroadcastMultiplePermissions(Intent, String[])} that allows you to
+ * specify the {@link android.app.BroadcastOptions}.
+ *
+ * @param intent The Intent to broadcast; all receivers matching this
+ * Intent will receive the broadcast.
+ * @param receiverPermissions Array of names of permissions that a receiver must hold
+ * in order to receive your broadcast.
+ * If empty, no permissions are required.
+ * @param options Additional sending options, generated from a
+ * {@link android.app.BroadcastOptions}.
+ * @see #sendBroadcastMultiplePermissions(Intent, String[])
+ * @see android.app.BroadcastOptions
+ * @hide
+ */
+ @SystemApi
+ public void sendBroadcastMultiplePermissions(@NonNull Intent intent,
+ @NonNull String[] receiverPermissions, @Nullable BroadcastOptions options) {
+ sendBroadcastMultiplePermissions(intent, receiverPermissions, options.toBundle());
+ }
+
+ /**
* Broadcast the given intent to all interested BroadcastReceivers, allowing
* an array of required permissions to be enforced. This call is asynchronous; it returns
* immediately, and you will continue executing while the receivers are run. No results are
@@ -4987,10 +5009,8 @@ public abstract class Context {
* @hide
* @see #getSystemService(String)
*/
- // TODO(216507592): Change cloudsearch_service to cloudsearch.
@SystemApi
- @SuppressLint("ServiceName")
- public static final String CLOUDSEARCH_SERVICE = "cloudsearch_service";
+ public static final String CLOUDSEARCH_SERVICE = "cloudsearch";
/**
* Use with {@link #getSystemService(String)} to access the
@@ -5638,6 +5658,15 @@ public abstract class Context {
public static final String OVERLAY_SERVICE = "overlay";
/**
+ * Use with {@link #getSystemService(String)} to manage resources.
+ *
+ * @see #getSystemService(String)
+ * @see com.android.server.resources.ResourcesManagerService
+ * @hide
+ */
+ public static final String RESOURCES_SERVICE = "resources";
+
+ /**
* Use with {@link #getSystemService(String)} to retrieve a
* {android.os.IIdmap2} for managing idmap files (used by overlay
* packages).
@@ -6677,21 +6706,27 @@ public abstract class Context {
@NonNull Configuration overrideConfiguration);
/**
- * Returns a new <code>Context</code> object from the current context but with resources
- * adjusted to match the metrics of <code>display</code>. Each call to this method
+ * Returns a new {@code Context} object from the current context but with resources
+ * adjusted to match the metrics of {@code display}. Each call to this method
* returns a new instance of a context object. Context objects are not shared; however,
* common state (such as the {@link ClassLoader} and other resources for the same
- * configuration) can be shared, so the <code>Context</code> itself is lightweight.
+ * configuration) can be shared, so the {@code Context} itself is lightweight.
+ *
+ * <p><b>Note:</b>
+ * This {@code Context} is <b>not</b> expected to be updated with new configuration if the
+ * underlying display configuration changes and the cached {@code Resources} it returns
+ * could be stale. It is suggested to use
+ * {@link android.hardware.display.DisplayManager.DisplayListener} to listen for
+ * changes and re-create an instance if necessary. </p>
* <p>
+ * This {@code Context} is <b>not</b> a UI context, do not use it to access UI components
+ * or obtain a {@link WindowManager} instance.
+ * </p><p>
* To obtain an instance of {@link WindowManager} configured to show windows on the given
* display, call {@link #createWindowContext(int, Bundle)} on the returned display context,
* then call {@link #getSystemService(String)} or {@link #getSystemService(Class)} on the
* returned window context.
- * <p>
- * <b>Note:</b> The context returned by <code>createDisplayContext(Display)</code> is not a UI
- * context. Do not access UI components or obtain a {@link WindowManager} from the context
- * created by <code>createDisplayContext(Display)</code>.
- *
+ * </p>
* @param display The display to which the current context's resources are adjusted.
*
* @return A context for the display.
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index fb186fd5dfb3..3e527f8d5215 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -3986,7 +3986,7 @@ public class Intent implements Parcelable, Cloneable {
* {@link android.Manifest.permission#MANAGE_USERS} to receive this broadcast.
* @hide
*/
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @SystemApi
public static final String ACTION_USER_SWITCHED =
"android.intent.action.USER_SWITCHED";
@@ -6196,6 +6196,8 @@ public class Intent implements Parcelable, Cloneable {
*
* @hide
*/
+ @SystemApi
+ @SuppressLint("ActionValue")
public static final String EXTRA_USER_HANDLE =
"android.intent.extra.user_handle";
@@ -7059,6 +7061,7 @@ public class Intent implements Parcelable, Cloneable {
*
* @hide
*/
+ @SystemApi
public static final int FLAG_RECEIVER_INCLUDE_BACKGROUND = 0x01000000;
/**
* If set, the broadcast will never go to manifest receivers in background (cached
diff --git a/core/java/android/content/pm/CrossProfileApps.java b/core/java/android/content/pm/CrossProfileApps.java
index 1e887582e5c3..94f056110bf7 100644
--- a/core/java/android/content/pm/CrossProfileApps.java
+++ b/core/java/android/content/pm/CrossProfileApps.java
@@ -282,7 +282,8 @@ public class CrossProfileApps {
final boolean isManagedProfile =
mUserManager.isManagedProfile(userHandle.getIdentifier());
if (isManagedProfile) {
- return mResources.getDrawable(R.drawable.ic_corp_badge, null);
+ return mContext.getPackageManager().getUserBadgeForDensityNoBackground(
+ userHandle, /* density= */ 0);
} else {
return UserIcons.getDefaultUserIcon(
mResources, UserHandle.USER_SYSTEM, true /* light */);
diff --git a/core/java/android/content/pm/PackageInfoLite.java b/core/java/android/content/pm/PackageInfoLite.java
index 9735f8157a57..410e106ce584 100644
--- a/core/java/android/content/pm/PackageInfoLite.java
+++ b/core/java/android/content/pm/PackageInfoLite.java
@@ -21,7 +21,7 @@ import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
-import com.android.internal.content.PackageHelper;
+import com.android.internal.content.InstallLocationUtils;
/**
* Basic information about a package as specified in its manifest.
@@ -80,10 +80,10 @@ public class PackageInfoLite implements Parcelable {
/**
* Specifies the recommended install location. Can be one of
- * {@link PackageHelper#RECOMMEND_INSTALL_INTERNAL} to install on internal storage,
- * {@link PackageHelper#RECOMMEND_INSTALL_EXTERNAL} to install on external media,
- * {@link PackageHelper#RECOMMEND_FAILED_INSUFFICIENT_STORAGE} for storage errors,
- * or {@link PackageHelper#RECOMMEND_FAILED_INVALID_APK} for parse errors.
+ * {@link InstallLocationUtils#RECOMMEND_INSTALL_INTERNAL} to install on internal storage,
+ * {@link InstallLocationUtils#RECOMMEND_INSTALL_EXTERNAL} to install on external media,
+ * {@link InstallLocationUtils#RECOMMEND_FAILED_INSUFFICIENT_STORAGE} for storage errors,
+ * or {@link InstallLocationUtils#RECOMMEND_FAILED_INVALID_APK} for parse errors.
*/
public int recommendedInstallLocation;
public int installLocation;
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 5b0c2751f57c..e5c31d7c0a8a 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -3154,6 +3154,14 @@ public abstract class PackageManager {
/**
* Feature for {@link #getSystemAvailableFeatures} and
+ * {@link #hasSystemFeature}: The device supports exposing head tracker sensors from peripheral
+ * devices via the dynamic sensors API.
+ */
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_SENSOR_DYNAMIC_HEAD_TRACKER = "android.hardware.sensor.dynamic.head_tracker";
+
+ /**
+ * Feature for {@link #getSystemAvailableFeatures} and
* {@link #hasSystemFeature}: The device supports high fidelity sensor processing
* capabilities.
*/
diff --git a/core/java/android/content/pm/PermissionInfo.java b/core/java/android/content/pm/PermissionInfo.java
index a5d97f958ea5..bb88486a014d 100644
--- a/core/java/android/content/pm/PermissionInfo.java
+++ b/core/java/android/content/pm/PermissionInfo.java
@@ -363,6 +363,9 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable {
/**
* The group this permission is a part of, as per
* {@link android.R.attr#permissionGroup}.
+ * <p>
+ * The actual grouping of platform-defined runtime permissions is subject to change and can be
+ * queried with {@link PackageManager#getGroupOfPlatformPermission}.
*/
public @Nullable String group;
diff --git a/core/java/android/content/pm/SharedLibraryInfo.java b/core/java/android/content/pm/SharedLibraryInfo.java
index 43a4b17e5172..4c0e2e6b36ca 100644
--- a/core/java/android/content/pm/SharedLibraryInfo.java
+++ b/core/java/android/content/pm/SharedLibraryInfo.java
@@ -46,7 +46,7 @@ public final class SharedLibraryInfo implements Parcelable {
TYPE_BUILTIN,
TYPE_DYNAMIC,
TYPE_STATIC,
- TYPE_SDK,
+ TYPE_SDK_PACKAGE,
})
@Retention(RetentionPolicy.SOURCE)
@interface Type{}
@@ -68,15 +68,21 @@ public final class SharedLibraryInfo implements Parcelable {
* Shared library type: this library is <strong>not</strong> backwards
* -compatible, can be updated and updates can be uninstalled. Clients
* link against a specific version of the library.
+ *
+ * Static shared libraries simulate static linking while allowing for
+ * multiple clients to reuse the same instance of the library.
*/
public static final int TYPE_STATIC = 2;
/**
- * SDK library type: this library is <strong>not</strong> backwards
- * -compatible, can be updated and updates can be uninstalled. Clients
- * depend on a specific version of the library.
+ * SDK package shared library type: this library is <strong>not</strong>
+ * compatible between versions, can be updated and updates can be
+ * uninstalled. Clients depend on a specific version of the library.
+ *
+ * SDK packages are not loaded automatically by the OS and rely
+ * e.g. on 3P libraries to make them available for the clients.
*/
- public static final int TYPE_SDK = 3;
+ public static final int TYPE_SDK_PACKAGE = 3;
/**
* Constant for referring to an undefined version.
@@ -301,7 +307,7 @@ public final class SharedLibraryInfo implements Parcelable {
* @hide
*/
public boolean isSdk() {
- return mType == TYPE_SDK;
+ return mType == TYPE_SDK_PACKAGE;
}
/**
@@ -367,7 +373,7 @@ public final class SharedLibraryInfo implements Parcelable {
case TYPE_STATIC: {
return "static";
}
- case TYPE_SDK: {
+ case TYPE_SDK_PACKAGE: {
return "sdk";
}
default: {
diff --git a/core/java/android/content/pm/TEST_MAPPING b/core/java/android/content/pm/TEST_MAPPING
index a503d14b6de4..dea083422612 100644
--- a/core/java/android/content/pm/TEST_MAPPING
+++ b/core/java/android/content/pm/TEST_MAPPING
@@ -162,5 +162,68 @@
{
"name": "CtsInstallHostTestCases"
}
+ ],
+ "staged-platinum-postsubmit": [
+ {
+ "name": "CtsIncrementalInstallHostTestCases"
+ },
+ {
+ "name": "CtsInstallHostTestCases"
+ },
+ {
+ "name": "CtsStagedInstallHostTestCases"
+ },
+ {
+ "name": "CtsExtractNativeLibsHostTestCases"
+ },
+ {
+ "name": "CtsAppSecurityHostTestCases",
+ "options": [
+ {
+ "include-filter": "com.android.cts.splitapp.SplitAppTest"
+ },
+ {
+ "include-filter": "android.appsecurity.cts.EphemeralTest"
+ }
+ ]
+ },
+ {
+ "name": "FrameworksServicesTests",
+ "options": [
+ {
+ "include-filter": "com.android.server.pm.PackageParserTest"
+ }
+ ]
+ },
+ {
+ "name": "CtsRollbackManagerHostTestCases"
+ },
+ {
+ "name": "CtsOsHostTestCases",
+ "options": [
+ {
+ "include-filter": "com.android.server.pm.PackageParserTest"
+ }
+ ]
+ },
+ {
+ "name": "CtsContentTestCases",
+ "options": [
+ {
+ "include-filter": "android.content.cts.IntentFilterTest"
+ }
+ ]
+ },
+ {
+ "name": "CtsAppEnumerationTestCases"
+ },
+ {
+ "name": "PackageManagerServiceUnitTests",
+ "options": [
+ {
+ "include-filter": "com.android.server.pm.test.verify.domain"
+ }
+ ]
+ }
]
}
diff --git a/core/java/android/content/res/ApkAssets.java b/core/java/android/content/res/ApkAssets.java
index 6fd2d05ad135..7a5ac8ede4a4 100644
--- a/core/java/android/content/res/ApkAssets.java
+++ b/core/java/android/content/res/ApkAssets.java
@@ -28,6 +28,7 @@ import com.android.internal.annotations.GuardedBy;
import java.io.FileDescriptor;
import java.io.IOException;
+import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Objects;
@@ -438,6 +439,12 @@ public final class ApkAssets {
}
}
+ void dump(PrintWriter pw, String prefix) {
+ pw.println(prefix + "class=" + getClass());
+ pw.println(prefix + "debugName=" + getDebugName());
+ pw.println(prefix + "assetPath=" + getAssetPath());
+ }
+
private static native long nativeLoad(@FormatType int format, @NonNull String path,
@PropertyFlags int flags, @Nullable AssetsProvider asset) throws IOException;
private static native long nativeLoadEmpty(@PropertyFlags int flags,
diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java
index bfd9fd0a4ef9..a05f5c927b29 100644
--- a/core/java/android/content/res/AssetManager.java
+++ b/core/java/android/content/res/AssetManager.java
@@ -43,6 +43,7 @@ import java.io.FileDescriptor;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
+import java.io.PrintWriter;
import java.lang.ref.Reference;
import java.util.ArrayList;
import java.util.Arrays;
@@ -1531,6 +1532,15 @@ public final class AssetManager implements AutoCloseable {
}
}
+ synchronized void dump(PrintWriter pw, String prefix) {
+ pw.println(prefix + "class=" + getClass());
+ pw.println(prefix + "apkAssets=");
+ for (int i = 0; i < mApkAssets.length; i++) {
+ pw.println(prefix + i);
+ mApkAssets[i].dump(pw, prefix + " ");
+ }
+ }
+
// AssetManager setup native methods.
private static native long nativeCreate();
private static native void nativeDestroy(long ptr);
diff --git a/core/java/android/content/res/IResourcesManager.aidl b/core/java/android/content/res/IResourcesManager.aidl
new file mode 100644
index 000000000000..d1373788f1c5
--- /dev/null
+++ b/core/java/android/content/res/IResourcesManager.aidl
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.res;
+
+import android.os.RemoteCallback;
+
+/**
+ * Api for getting information about resources.
+ *
+ * {@hide}
+ */
+interface IResourcesManager {
+ boolean dumpResources(in String process,
+ in ParcelFileDescriptor fd,
+ in RemoteCallback finishCallback);
+} \ No newline at end of file
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index 1aba961c5ddf..ebef0535f077 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -53,6 +53,7 @@ import android.graphics.drawable.Drawable.ConstantState;
import android.graphics.drawable.DrawableInflater;
import android.os.Build;
import android.os.Bundle;
+import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
@@ -78,12 +79,15 @@ import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
import java.io.InputStream;
+import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
-import java.util.function.Consumer;
+import java.util.Map;
+import java.util.Set;
+import java.util.WeakHashMap;
/**
* Class for accessing an application's resources. This sits on top of the
@@ -143,9 +147,6 @@ public class Resources {
@UnsupportedAppUsage
private DrawableInflater mDrawableInflater;
- /** Used to override the returned adjustments of {@link #getDisplayAdjustments}. */
- private DisplayAdjustments mOverrideDisplayAdjustments;
-
/** Lock object used to protect access to {@link #mTmpValue}. */
private final Object mTmpValueLock = new Object();
@@ -176,6 +177,11 @@ public class Resources {
private int mBaseApkAssetsSize;
+ /** @hide */
+ private static Set<Resources> sResourcesHistory = Collections.synchronizedSet(
+ Collections.newSetFromMap(
+ new WeakHashMap<>()));
+
/**
* Returns the most appropriate default theme for the specified target SDK version.
* <ul>
@@ -322,6 +328,7 @@ public class Resources {
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public Resources(@Nullable ClassLoader classLoader) {
mClassLoader = classLoader == null ? ClassLoader.getSystemClassLoader() : classLoader;
+ sResourcesHistory.add(this);
}
/**
@@ -2174,38 +2181,15 @@ public class Resources {
/** @hide */
@UnsupportedAppUsage(trackingBug = 176190631)
public DisplayAdjustments getDisplayAdjustments() {
- final DisplayAdjustments overrideDisplayAdjustments = mOverrideDisplayAdjustments;
- if (overrideDisplayAdjustments != null) {
- return overrideDisplayAdjustments;
- }
return mResourcesImpl.getDisplayAdjustments();
}
/**
- * Customize the display adjustments based on the current one in {@link #mResourcesImpl}, in
- * order to isolate the effect with other instances of {@link Resource} that may share the same
- * instance of {@link ResourcesImpl}.
- *
- * @param override The operation to override the existing display adjustments. If it is null,
- * the override adjustments will be cleared.
- * @hide
- */
- public void overrideDisplayAdjustments(@Nullable Consumer<DisplayAdjustments> override) {
- if (override != null) {
- mOverrideDisplayAdjustments = new DisplayAdjustments(
- mResourcesImpl.getDisplayAdjustments());
- override.accept(mOverrideDisplayAdjustments);
- } else {
- mOverrideDisplayAdjustments = null;
- }
- }
-
- /**
* Return {@code true} if the override display adjustments have been set.
* @hide
*/
public boolean hasOverrideDisplayAdjustments() {
- return mOverrideDisplayAdjustments != null;
+ return false;
}
/**
@@ -2676,4 +2660,29 @@ public class Resources {
}
}
}
+
+ /** @hide */
+ public void dump(PrintWriter pw, String prefix) {
+ pw.println(prefix + "class=" + getClass());
+ pw.println(prefix + "resourcesImpl");
+ mResourcesImpl.dump(pw, prefix + " ");
+ }
+
+ /** @hide */
+ public static void dumpHistory(PrintWriter pw, String prefix) {
+ pw.println(prefix + "history");
+ // Putting into a map keyed on the apk assets to deduplicate resources that are different
+ // objects but ultimately represent the same assets
+ Map<List<ApkAssets>, Resources> history = new ArrayMap<>();
+ for (Resources r : sResourcesHistory) {
+ history.put(Arrays.asList(r.mResourcesImpl.mAssets.getApkAssets()), r);
+ }
+ int i = 0;
+ for (Resources r : history.values()) {
+ if (r != null) {
+ pw.println(prefix + i++);
+ r.dump(pw, prefix + " ");
+ }
+ }
+ }
}
diff --git a/core/java/android/content/res/ResourcesImpl.java b/core/java/android/content/res/ResourcesImpl.java
index 4d850b0ccfd5..ff072916292b 100644
--- a/core/java/android/content/res/ResourcesImpl.java
+++ b/core/java/android/content/res/ResourcesImpl.java
@@ -61,6 +61,7 @@ import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
import java.io.InputStream;
+import java.io.PrintWriter;
import java.util.Arrays;
import java.util.Locale;
@@ -1271,6 +1272,12 @@ public class ResourcesImpl {
NativeAllocationRegistry.createMalloced(ResourcesImpl.class.getClassLoader(),
AssetManager.getThemeFreeFunction());
+ void dump(PrintWriter pw, String prefix) {
+ pw.println(prefix + "class=" + getClass());
+ pw.println(prefix + "assets");
+ mAssets.dump(pw, prefix + " ");
+ }
+
public class ThemeImpl {
/**
* Unique key for the series of styles applied to this theme.
diff --git a/core/java/android/database/CursorWindow.java b/core/java/android/database/CursorWindow.java
index f13c79587a28..52bba1484f1a 100644
--- a/core/java/android/database/CursorWindow.java
+++ b/core/java/android/database/CursorWindow.java
@@ -131,11 +131,16 @@ public class CursorWindow extends SQLiteClosable implements Parcelable {
*
* @param name The name of the cursor window, or null if none.
* @param windowSizeBytes Size of cursor window in bytes.
+ * @throws IllegalArgumentException if {@code windowSizeBytes} is less than 0
+ * @throws AssertionError if created window pointer is 0
* <p><strong>Note:</strong> Memory is dynamically allocated as data rows are added to the
* window. Depending on the amount of data stored, the actual amount of memory allocated can be
* lower than specified size, but cannot exceed it.
*/
public CursorWindow(String name, @BytesLong long windowSizeBytes) {
+ if (windowSizeBytes < 0) {
+ throw new IllegalArgumentException("Window size cannot be less than 0");
+ }
mStartPos = 0;
mName = name != null && name.length() != 0 ? name : "<unnamed>";
mWindowPtr = nativeCreate(mName, (int) windowSizeBytes);
diff --git a/core/java/android/database/CursorWindowAllocationException.java b/core/java/android/database/CursorWindowAllocationException.java
index 2e3227dc6788..5315c8b591ab 100644
--- a/core/java/android/database/CursorWindowAllocationException.java
+++ b/core/java/android/database/CursorWindowAllocationException.java
@@ -16,14 +16,14 @@
package android.database;
+import android.annotation.NonNull;
+
/**
* This exception is thrown when a CursorWindow couldn't be allocated,
* most probably due to memory not being available.
- *
- * @hide
*/
public class CursorWindowAllocationException extends RuntimeException {
- public CursorWindowAllocationException(String description) {
+ public CursorWindowAllocationException(@NonNull String description) {
super(description);
}
}
diff --git a/core/java/android/hardware/SensorPrivacyManager.java b/core/java/android/hardware/SensorPrivacyManager.java
index 7074a2c8a60d..79153d77a85b 100644
--- a/core/java/android/hardware/SensorPrivacyManager.java
+++ b/core/java/android/hardware/SensorPrivacyManager.java
@@ -31,6 +31,7 @@ import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
import android.service.SensorPrivacyIndividualEnabledSensorProto;
+import android.service.SensorPrivacySensorProto;
import android.service.SensorPrivacyToggleSourceProto;
import android.util.ArrayMap;
import android.util.Log;
@@ -75,7 +76,7 @@ public final class SensorPrivacyManager {
private final SparseArray<Boolean> mToggleSupportCache = new SparseArray<>();
/**
- * Individual sensors not listed in {@link Sensors}
+ * Sensor constants which are used in {@link SensorPrivacyManager}
*/
public static class Sensors {
@@ -84,12 +85,12 @@ public final class SensorPrivacyManager {
/**
* Constant for the microphone
*/
- public static final int MICROPHONE = SensorPrivacyIndividualEnabledSensorProto.MICROPHONE;
+ public static final int MICROPHONE = SensorPrivacySensorProto.MICROPHONE;
/**
* Constant for the camera
*/
- public static final int CAMERA = SensorPrivacyIndividualEnabledSensorProto.CAMERA;
+ public static final int CAMERA = SensorPrivacySensorProto.CAMERA;
/**
* Individual sensors not listed in {@link Sensors}
@@ -161,6 +162,68 @@ public final class SensorPrivacyManager {
}
/**
+ * Types of toggles which can exist for sensor privacy
+ * @hide
+ */
+ public static class ToggleTypes {
+ private ToggleTypes() {}
+
+ /**
+ * Constant for software toggle.
+ */
+ public static final int SOFTWARE = SensorPrivacyIndividualEnabledSensorProto.SOFTWARE;
+
+ /**
+ * Constant for hardware toggle.
+ */
+ public static final int HARDWARE = SensorPrivacyIndividualEnabledSensorProto.HARDWARE;
+
+ /**
+ * Types of toggles which can exist for sensor privacy
+ *
+ * @hide
+ */
+ @IntDef(value = {
+ SOFTWARE,
+ HARDWARE
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ToggleType {}
+
+ }
+
+ /**
+ * Types of state which can exist for the sensor privacy toggle
+ * @hide
+ */
+ public static class StateTypes {
+ private StateTypes() {}
+
+ /**
+ * Constant indicating privacy is enabled.
+ */
+ public static final int ENABLED = SensorPrivacyIndividualEnabledSensorProto.ENABLED;
+
+ /**
+ * Constant indicating privacy is disabled.
+ */
+ public static final int DISABLED = SensorPrivacyIndividualEnabledSensorProto.DISABLED;
+
+ /**
+ * Types of state which can exist for a sensor privacy toggle
+ *
+ * @hide
+ */
+ @IntDef(value = {
+ ENABLED,
+ DISABLED
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface StateType {}
+
+ }
+
+ /**
* A class implementing this interface can register with the {@link
* android.hardware.SensorPrivacyManager} to receive notification when the sensor privacy
* state changes.
@@ -507,7 +570,6 @@ public final class SensorPrivacyManager {
/**
* Don't show dialogs to turn off sensor privacy for this package.
*
- * @param packageName Package name not to show dialogs for
* @param suppress Whether to suppress or re-enable.
*
* @hide
@@ -521,7 +583,6 @@ public final class SensorPrivacyManager {
/**
* Don't show dialogs to turn off sensor privacy for this package.
*
- * @param packageName Package name not to show dialogs for
* @param suppress Whether to suppress or re-enable.
* @param userId the user's id
*
diff --git a/core/java/android/hardware/biometrics/IBiometricContextListener.aidl b/core/java/android/hardware/biometrics/IBiometricContextListener.aidl
new file mode 100644
index 000000000000..55cab52fc4f7
--- /dev/null
+++ b/core/java/android/hardware/biometrics/IBiometricContextListener.aidl
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.biometrics;
+
+/**
+ * A secondary communication channel from AuthController back to BiometricService for
+ * events that are not associated with an autentication session. See
+ * {@link IBiometricSysuiReceiver} for events associated with a session.
+ *
+ * @hide
+ */
+oneway interface IBiometricContextListener {
+ void onDozeChanged(boolean isDozing);
+}
diff --git a/core/java/android/hardware/devicestate/DeviceStateManager.java b/core/java/android/hardware/devicestate/DeviceStateManager.java
index b06d076fe08e..30aa4db938da 100644
--- a/core/java/android/hardware/devicestate/DeviceStateManager.java
+++ b/core/java/android/hardware/devicestate/DeviceStateManager.java
@@ -79,9 +79,9 @@ public final class DeviceStateManager {
* <ul>
* <li>The system deems the request can no longer be honored, for example if the requested
* state becomes unsupported.
- * <li>A call to {@link #cancelRequest(DeviceStateRequest)}.
+ * <li>A call to {@link #cancelStateRequest}.
* <li>Another processes submits a request succeeding this request in which case the request
- * will be suspended until the interrupting request is canceled.
+ * will be canceled.
* </ul>
* However, this behavior can be changed by setting flags on the {@link DeviceStateRequest}.
*
@@ -100,19 +100,18 @@ public final class DeviceStateManager {
}
/**
- * Cancels a {@link DeviceStateRequest request} previously submitted with a call to
+ * Cancels the active {@link DeviceStateRequest} previously submitted with a call to
* {@link #requestState(DeviceStateRequest, Executor, DeviceStateRequest.Callback)}.
* <p>
- * This method is noop if the {@code request} has not been submitted with a call to
- * {@link #requestState(DeviceStateRequest, Executor, DeviceStateRequest.Callback)}.
+ * This method is noop if there is no request currently active.
*
* @throws SecurityException if the caller is neither the current top-focused activity nor if
* the {@link android.Manifest.permission#CONTROL_DEVICE_STATE} permission is held.
*/
@RequiresPermission(value = android.Manifest.permission.CONTROL_DEVICE_STATE,
conditional = true)
- public void cancelRequest(@NonNull DeviceStateRequest request) {
- mGlobal.cancelRequest(request);
+ public void cancelStateRequest() {
+ mGlobal.cancelStateRequest();
}
/**
diff --git a/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java b/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java
index 85e70b0fb3e9..aba538f51043 100644
--- a/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java
+++ b/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java
@@ -151,20 +151,14 @@ public final class DeviceStateManagerGlobal {
* Cancels a {@link DeviceStateRequest request} previously submitted with a call to
* {@link #requestState(DeviceStateRequest, Executor, DeviceStateRequest.Callback)}.
*
- * @see DeviceStateManager#cancelRequest(DeviceStateRequest)
+ * @see DeviceStateManager#cancelStateRequest
*/
- public void cancelRequest(@NonNull DeviceStateRequest request) {
+ public void cancelStateRequest() {
synchronized (mLock) {
registerCallbackIfNeededLocked();
- final IBinder token = findRequestTokenLocked(request);
- if (token == null) {
- // This request has not been submitted.
- return;
- }
-
try {
- mDeviceStateManager.cancelRequest(token);
+ mDeviceStateManager.cancelStateRequest();
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
@@ -299,20 +293,6 @@ public final class DeviceStateManagerGlobal {
/**
* Handles a call from the server that a request for the supplied {@code token} has become
- * suspended.
- */
- private void handleRequestSuspended(IBinder token) {
- DeviceStateRequestWrapper request;
- synchronized (mLock) {
- request = mRequests.get(token);
- }
- if (request != null) {
- request.notifyRequestSuspended();
- }
- }
-
- /**
- * Handles a call from the server that a request for the supplied {@code token} has become
* canceled.
*/
private void handleRequestCanceled(IBinder token) {
@@ -337,11 +317,6 @@ public final class DeviceStateManagerGlobal {
}
@Override
- public void onRequestSuspended(IBinder token) {
- handleRequestSuspended(token);
- }
-
- @Override
public void onRequestCanceled(IBinder token) {
handleRequestCanceled(token);
}
@@ -395,14 +370,6 @@ public final class DeviceStateManagerGlobal {
mExecutor.execute(() -> mCallback.onRequestActivated(mRequest));
}
- void notifyRequestSuspended() {
- if (mCallback == null) {
- return;
- }
-
- mExecutor.execute(() -> mCallback.onRequestSuspended(mRequest));
- }
-
void notifyRequestCanceled() {
if (mCallback == null) {
return;
diff --git a/core/java/android/hardware/devicestate/DeviceStateRequest.java b/core/java/android/hardware/devicestate/DeviceStateRequest.java
index df488d2f6df1..893d765e48da 100644
--- a/core/java/android/hardware/devicestate/DeviceStateRequest.java
+++ b/core/java/android/hardware/devicestate/DeviceStateRequest.java
@@ -32,8 +32,7 @@ import java.util.concurrent.Executor;
* DeviceStateRequest.Callback)}.
* <p>
* By default, the request is kept active until a call to
- * {@link DeviceStateManager#cancelRequest(DeviceStateRequest)} or until one of the following
- * occurs:
+ * {@link DeviceStateManager#cancelStateRequest} or until one of the following occurs:
* <ul>
* <li>Another processes submits a request succeeding this request in which case the request
* will be suspended until the interrupting request is canceled.
diff --git a/core/java/android/hardware/devicestate/IDeviceStateManager.aidl b/core/java/android/hardware/devicestate/IDeviceStateManager.aidl
index 14ed03d09fd0..e450e42497a0 100644
--- a/core/java/android/hardware/devicestate/IDeviceStateManager.aidl
+++ b/core/java/android/hardware/devicestate/IDeviceStateManager.aidl
@@ -41,8 +41,9 @@ interface IDeviceStateManager {
* previously registered with {@link #registerCallback(IDeviceStateManagerCallback)} before a
* call to this method.
*
- * @param token the request token previously registered with
- * {@link #requestState(IBinder, int, int)}
+ * @param token the request token provided
+ * @param state the state of device the request is asking for
+ * @param flags any flags that correspond to the request
*
* @throws IllegalStateException if a callback has not yet been registered for the calling
* process.
@@ -52,14 +53,11 @@ interface IDeviceStateManager {
void requestState(IBinder token, int state, int flags);
/**
- * Cancels a request previously submitted with a call to
+ * Cancels the active request previously submitted with a call to
* {@link #requestState(IBinder, int, int)}.
*
- * @param token the request token previously registered with
- * {@link #requestState(IBinder, int, int)}
- *
- * @throws IllegalStateException if the supplied {@code token} has not been previously
- * requested.
+ * @throws IllegalStateException if a callback has not yet been registered for the calling
+ * process.
*/
- void cancelRequest(IBinder token);
+ void cancelStateRequest();
}
diff --git a/core/java/android/hardware/devicestate/IDeviceStateManagerCallback.aidl b/core/java/android/hardware/devicestate/IDeviceStateManagerCallback.aidl
index efb9888fb6ba..348690f65e78 100644
--- a/core/java/android/hardware/devicestate/IDeviceStateManagerCallback.aidl
+++ b/core/java/android/hardware/devicestate/IDeviceStateManagerCallback.aidl
@@ -41,16 +41,6 @@ interface IDeviceStateManagerCallback {
oneway void onRequestActive(IBinder token);
/**
- * Called to notify the callback that a request has become suspended. Guaranteed to be called
- * before a subsequent call to {@link #onDeviceStateInfoChanged(DeviceStateInfo)} if the request
- * becoming suspended resulted in a change of device state info.
- *
- * @param token the request token previously registered with
- * {@link IDeviceStateManager#requestState(IBinder, int, int)}
- */
- oneway void onRequestSuspended(IBinder token);
-
- /**
* Called to notify the callback that a request has become canceled. No further callbacks will
* be triggered for this request. Guaranteed to be called before a subsequent call to
* {@link #onDeviceStateInfoChanged(DeviceStateInfo)} if the request becoming canceled resulted
diff --git a/core/java/android/hardware/display/BrightnessInfo.java b/core/java/android/hardware/display/BrightnessInfo.java
index 0dc8f92967fb..99f3d156cfa9 100644
--- a/core/java/android/hardware/display/BrightnessInfo.java
+++ b/core/java/android/hardware/display/BrightnessInfo.java
@@ -57,6 +57,23 @@ public final class BrightnessInfo implements Parcelable {
*/
public static final int HIGH_BRIGHTNESS_MODE_HDR = 2;
+ @IntDef(prefix = {"BRIGHTNESS_MAX_REASON_"}, value = {
+ BRIGHTNESS_MAX_REASON_NONE,
+ BRIGHTNESS_MAX_REASON_THERMAL
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface BrightnessMaxReason {}
+
+ /**
+ * Maximum brightness is unrestricted.
+ */
+ public static final int BRIGHTNESS_MAX_REASON_NONE = 0;
+
+ /**
+ * Maximum brightness is restricted due to thermal throttling.
+ */
+ public static final int BRIGHTNESS_MAX_REASON_THERMAL = 1;
+
/** Brightness */
public final float brightness;
@@ -78,21 +95,29 @@ public final class BrightnessInfo implements Parcelable {
*/
public final int highBrightnessMode;
+ /**
+ * The current reason for restricting maximum brightness.
+ * Can be any of BRIGHTNESS_MAX_REASON_* values.
+ */
+ public final int brightnessMaxReason;
+
public BrightnessInfo(float brightness, float brightnessMinimum, float brightnessMaximum,
- @HighBrightnessMode int highBrightnessMode, float highBrightnessTransitionPoint) {
+ @HighBrightnessMode int highBrightnessMode, float highBrightnessTransitionPoint,
+ @BrightnessMaxReason int brightnessMaxReason) {
this(brightness, brightness, brightnessMinimum, brightnessMaximum, highBrightnessMode,
- highBrightnessTransitionPoint);
+ highBrightnessTransitionPoint, brightnessMaxReason);
}
public BrightnessInfo(float brightness, float adjustedBrightness, float brightnessMinimum,
float brightnessMaximum, @HighBrightnessMode int highBrightnessMode,
- float highBrightnessTransitionPoint) {
+ float highBrightnessTransitionPoint, @BrightnessMaxReason int brightnessMaxReason) {
this.brightness = brightness;
this.adjustedBrightness = adjustedBrightness;
this.brightnessMinimum = brightnessMinimum;
this.brightnessMaximum = brightnessMaximum;
this.highBrightnessMode = highBrightnessMode;
this.highBrightnessTransitionPoint = highBrightnessTransitionPoint;
+ this.brightnessMaxReason = brightnessMaxReason;
}
/**
@@ -110,6 +135,19 @@ public final class BrightnessInfo implements Parcelable {
return "invalid";
}
+ /**
+ * @return User-friendly string for specified {@link BrightnessMaxReason} parameter.
+ */
+ public static String briMaxReasonToString(@BrightnessMaxReason int reason) {
+ switch (reason) {
+ case BRIGHTNESS_MAX_REASON_NONE:
+ return "none";
+ case BRIGHTNESS_MAX_REASON_THERMAL:
+ return "thermal";
+ }
+ return "invalid";
+ }
+
@Override
public int describeContents() {
return 0;
@@ -123,6 +161,7 @@ public final class BrightnessInfo implements Parcelable {
dest.writeFloat(brightnessMaximum);
dest.writeInt(highBrightnessMode);
dest.writeFloat(highBrightnessTransitionPoint);
+ dest.writeInt(brightnessMaxReason);
}
public static final @android.annotation.NonNull Creator<BrightnessInfo> CREATOR =
@@ -145,6 +184,7 @@ public final class BrightnessInfo implements Parcelable {
brightnessMaximum = source.readFloat();
highBrightnessMode = source.readInt();
highBrightnessTransitionPoint = source.readFloat();
+ brightnessMaxReason = source.readInt();
}
}
diff --git a/core/java/android/hardware/hdmi/HdmiClient.java b/core/java/android/hardware/hdmi/HdmiClient.java
index cee6a5b16a3c..b96e4f88eb93 100644
--- a/core/java/android/hardware/hdmi/HdmiClient.java
+++ b/core/java/android/hardware/hdmi/HdmiClient.java
@@ -21,6 +21,8 @@ import java.util.concurrent.Executor;
public abstract class HdmiClient {
private static final String TAG = "HdmiClient";
+ private static final int UNKNOWN_VENDOR_ID = 0xFFFFFF;
+
/* package */ final IHdmiControlService mService;
private IHdmiVendorCommandListener mIHdmiVendorCommandListener;
@@ -156,11 +158,25 @@ public abstract class HdmiClient {
}
/**
- * Sets a listener used to receive incoming vendor-specific command.
+ * Sets a listener used to receive incoming vendor-specific command. This listener will only
+ * receive {@code <Vendor Command>} but will not receive any {@code <Vendor Command with ID>}
+ * messages.
*
* @param listener listener object
*/
public void setVendorCommandListener(@NonNull VendorCommandListener listener) {
+ // Set the vendor ID to INVALID_VENDOR_ID.
+ setVendorCommandListener(listener, UNKNOWN_VENDOR_ID);
+ }
+
+ /**
+ * Sets a listener used to receive incoming vendor-specific command.
+ *
+ * @param listener listener object
+ * @param vendorId The listener is interested in {@code <Vendor Command with ID>} received with
+ * this vendorId and all {@code <Vendor Command>} messages.
+ */
+ public void setVendorCommandListener(@NonNull VendorCommandListener listener, int vendorId) {
if (listener == null) {
throw new IllegalArgumentException("listener cannot be null");
}
@@ -169,7 +185,7 @@ public abstract class HdmiClient {
}
try {
IHdmiVendorCommandListener wrappedListener = getListenerWrapper(listener);
- mService.addVendorCommandListener(wrappedListener, getDeviceType());
+ mService.addVendorCommandListener(wrappedListener, vendorId);
mIHdmiVendorCommandListener = wrappedListener;
} catch (RemoteException e) {
Log.e(TAG, "failed to set vendor command listener: ", e);
diff --git a/core/java/android/hardware/hdmi/HdmiControlManager.java b/core/java/android/hardware/hdmi/HdmiControlManager.java
index 5874385f8191..ec55e121bf74 100644
--- a/core/java/android/hardware/hdmi/HdmiControlManager.java
+++ b/core/java/android/hardware/hdmi/HdmiControlManager.java
@@ -105,6 +105,12 @@ public final class HdmiControlManager {
"android.hardware.hdmi.extra.MESSAGE_EXTRA_PARAM1";
/**
+ * Used as an extra field in the Set Menu Language intent. Contains the requested locale.
+ * @hide
+ */
+ public static final String EXTRA_LOCALE = "android.hardware.hdmi.extra.LOCALE";
+
+ /**
* Volume value for mute state.
*/
public static final int AVR_VOLUME_MUTED = 101;
diff --git a/core/java/android/hardware/hdmi/HdmiControlServiceWrapper.java b/core/java/android/hardware/hdmi/HdmiControlServiceWrapper.java
index 16adee9907ed..818554dd8143 100644
--- a/core/java/android/hardware/hdmi/HdmiControlServiceWrapper.java
+++ b/core/java/android/hardware/hdmi/HdmiControlServiceWrapper.java
@@ -221,8 +221,8 @@ public final class HdmiControlServiceWrapper {
}
@Override
- public void addVendorCommandListener(IHdmiVendorCommandListener listener, int deviceType) {
- HdmiControlServiceWrapper.this.addVendorCommandListener(listener, deviceType);
+ public void addVendorCommandListener(IHdmiVendorCommandListener listener, int vendorId) {
+ HdmiControlServiceWrapper.this.addVendorCommandListener(listener, vendorId);
}
@Override
@@ -481,7 +481,7 @@ public final class HdmiControlServiceWrapper {
boolean hasVendorId) {}
/** @hide */
- public void addVendorCommandListener(IHdmiVendorCommandListener listener, int deviceType) {}
+ public void addVendorCommandListener(IHdmiVendorCommandListener listener, int vendorId) {}
/** @hide */
public void sendStandby(int deviceType, int deviceId) {}
diff --git a/core/java/android/hardware/hdmi/IHdmiControlService.aidl b/core/java/android/hardware/hdmi/IHdmiControlService.aidl
index 6613397c8c0f..35dd9ed5bbcc 100644
--- a/core/java/android/hardware/hdmi/IHdmiControlService.aidl
+++ b/core/java/android/hardware/hdmi/IHdmiControlService.aidl
@@ -76,7 +76,7 @@ interface IHdmiControlService {
void askRemoteDeviceToBecomeActiveSource(int physicalAddress);
void sendVendorCommand(int deviceType, int targetAddress, in byte[] params,
boolean hasVendorId);
- void addVendorCommandListener(IHdmiVendorCommandListener listener, int deviceType);
+ void addVendorCommandListener(IHdmiVendorCommandListener listener, int vendorId);
void sendStandby(int deviceType, int deviceId);
void setHdmiRecordListener(IHdmiRecordListener callback);
void startOneTouchRecord(int recorderAddress, in byte[] recordSource);
diff --git a/core/java/android/hardware/input/IInputManager.aidl b/core/java/android/hardware/input/IInputManager.aidl
index 0304815ef8fe..27403ec4fe59 100644
--- a/core/java/android/hardware/input/IInputManager.aidl
+++ b/core/java/android/hardware/input/IInputManager.aidl
@@ -122,9 +122,9 @@ interface IInputManager {
void removePortAssociation(in String inputPort);
// Add a runtime association between the input device and display.
- void addUniqueIdAssociation(in String inputDeviceName, in String displayUniqueId);
+ void addUniqueIdAssociation(in String inputPort, in String displayUniqueId);
// Remove the runtime association between the input device and display.
- void removeUniqueIdAssociation(in String inputDeviceName);
+ void removeUniqueIdAssociation(in String inputPort);
InputSensorInfo[] getSensorList(int deviceId);
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index cbc837393b6b..979e9dd6a1f6 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -1359,19 +1359,18 @@ public final class InputManager {
}
/**
- * Add a runtime association between the input device name and display, by unique id. Input
- * device names are expected to be unique.
- * @param inputDeviceName The name of the input device.
+ * Add a runtime association between the input port and display, by unique id. Input ports are
+ * expected to be unique.
+ * @param inputPort The port of the input device.
* @param displayUniqueId The unique id of the associated display.
* <p>
* Requires {@link android.Manifest.permission.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY}.
* </p>
* @hide
*/
- public void addUniqueIdAssociation(@NonNull String inputDeviceName,
- @NonNull String displayUniqueId) {
+ public void addUniqueIdAssociation(@NonNull String inputPort, @NonNull String displayUniqueId) {
try {
- mIm.addUniqueIdAssociation(inputDeviceName, displayUniqueId);
+ mIm.addUniqueIdAssociation(inputPort, displayUniqueId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1379,15 +1378,15 @@ public final class InputManager {
/**
* Removes a runtime association between the input device and display.
- * @param inputDeviceName The name of the input device.
+ * @param inputPort The port of the input device.
* <p>
* Requires {@link android.Manifest.permission.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY}.
* </p>
* @hide
*/
- public void removeUniqueIdAssociation(@NonNull String inputDeviceName) {
+ public void removeUniqueIdAssociation(@NonNull String inputPort) {
try {
- mIm.removeUniqueIdAssociation(inputDeviceName);
+ mIm.removeUniqueIdAssociation(inputPort);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/hardware/usb/UsbManager.java b/core/java/android/hardware/usb/UsbManager.java
index 60f5135aed6c..7ff74c68fc53 100644
--- a/core/java/android/hardware/usb/UsbManager.java
+++ b/core/java/android/hardware/usb/UsbManager.java
@@ -1341,7 +1341,6 @@ public class UsbManager {
*
* @hide
*/
- @SystemApi
@RequiresPermission(Manifest.permission.MANAGE_USB)
boolean resetUsbPort(@NonNull UsbPort port, int operationId,
IUsbOperationInternal callback) {
diff --git a/core/java/android/inputmethodservice/IInputMethodWrapper.java b/core/java/android/inputmethodservice/IInputMethodWrapper.java
index 41642e7a9fce..af57f793bf73 100644
--- a/core/java/android/inputmethodservice/IInputMethodWrapper.java
+++ b/core/java/android/inputmethodservice/IInputMethodWrapper.java
@@ -70,6 +70,7 @@ class IInputMethodWrapper extends IInputMethod.Stub
private static final int DO_SET_INPUT_CONTEXT = 20;
private static final int DO_UNSET_INPUT_CONTEXT = 30;
private static final int DO_START_INPUT = 32;
+ private static final int DO_ON_SHOULD_SHOW_IME_SWITCHER_WHEN_IME_IS_SHOWN_CHANGED = 35;
private static final int DO_CREATE_SESSION = 40;
private static final int DO_SET_SESSION_ENABLED = 45;
private static final int DO_SHOW_SOFT_INPUT = 60;
@@ -175,7 +176,7 @@ class IInputMethodWrapper extends IInputMethod.Stub
try {
inputMethod.initializeInternal((IBinder) args.arg1,
(IInputMethodPrivilegedOperations) args.arg2, msg.arg1,
- (boolean) args.arg3);
+ (boolean) args.arg3, msg.arg2 != 0);
} finally {
args.recycle();
}
@@ -195,14 +196,22 @@ class IInputMethodWrapper extends IInputMethod.Stub
final EditorInfo info = (EditorInfo) args.arg3;
final CancellationGroup cancellationGroup = (CancellationGroup) args.arg4;
final boolean restarting = args.argi5 == 1;
+ final boolean shouldShowImeSwitcherWhenImeIsShown = args.argi6 != 0;
final InputConnection ic = inputContext != null
? new RemoteInputConnection(mTarget, inputContext, cancellationGroup)
: null;
info.makeCompatible(mTargetSdkVersion);
- inputMethod.dispatchStartInputWithToken(ic, info, restarting, startInputToken);
+ inputMethod.dispatchStartInputWithToken(ic, info, restarting, startInputToken,
+ shouldShowImeSwitcherWhenImeIsShown);
args.recycle();
return;
}
+ case DO_ON_SHOULD_SHOW_IME_SWITCHER_WHEN_IME_IS_SHOWN_CHANGED: {
+ final boolean shouldShowImeSwitcherWhenImeIsShown = msg.arg1 != 0;
+ inputMethod.onShouldShowImeSwitcherWhenImeIsShownChanged(
+ shouldShowImeSwitcherWhenImeIsShown);
+ return;
+ }
case DO_CREATE_SESSION: {
SomeArgs args = (SomeArgs)msg.obj;
inputMethod.createSession(new InputMethodSessionCallbackWrapper(
@@ -291,10 +300,11 @@ class IInputMethodWrapper extends IInputMethod.Stub
@BinderThread
@Override
public void initializeInternal(IBinder token, IInputMethodPrivilegedOperations privOps,
- int configChanges, boolean stylusHwSupported) {
- mCaller.executeOrSendMessage(
- mCaller.obtainMessageIOOO(
- DO_INITIALIZE_INTERNAL, configChanges, token, privOps, stylusHwSupported));
+ int configChanges, boolean stylusHwSupported,
+ boolean shouldShowImeSwitcherWhenImeIsShown) {
+ mCaller.executeOrSendMessage(mCaller.obtainMessageIIOOO(DO_INITIALIZE_INTERNAL,
+ configChanges, shouldShowImeSwitcherWhenImeIsShown ? 1 : 0, token, privOps,
+ stylusHwSupported));
}
@BinderThread
@@ -334,13 +344,23 @@ class IInputMethodWrapper extends IInputMethod.Stub
@BinderThread
@Override
public void startInput(IBinder startInputToken, IInputContext inputContext,
- EditorInfo attribute, boolean restarting) {
+ EditorInfo attribute, boolean restarting, boolean shouldShowImeSwitcherWhenImeIsShown) {
if (mCancellationGroup == null) {
Log.e(TAG, "startInput must be called after bindInput.");
mCancellationGroup = new CancellationGroup();
}
mCaller.executeOrSendMessage(mCaller.obtainMessageOOOOII(DO_START_INPUT, startInputToken,
- inputContext, attribute, mCancellationGroup, restarting ? 1 : 0, 0 /* unused */));
+ inputContext, attribute, mCancellationGroup, restarting ? 1 : 0,
+ shouldShowImeSwitcherWhenImeIsShown ? 1 : 0));
+ }
+
+ @BinderThread
+ @Override
+ public void onShouldShowImeSwitcherWhenImeIsShownChanged(
+ boolean shouldShowImeSwitcherWhenImeIsShown) {
+ mCaller.executeOrSendMessage(mCaller.obtainMessageI(
+ DO_ON_SHOULD_SHOW_IME_SWITCHER_WHEN_IME_IS_SHOWN_CHANGED,
+ shouldShowImeSwitcherWhenImeIsShown ? 1 : 0));
}
@BinderThread
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 4fd375177d6f..fc2fbc39dbeb 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -346,7 +346,7 @@ public class InputMethodService extends AbstractInputMethodService {
*/
@AnyThread
public static boolean canImeRenderGesturalNavButtons() {
- return SystemProperties.getBoolean(PROP_CAN_RENDER_GESTURAL_NAV_BUTTONS, false);
+ return SystemProperties.getBoolean(PROP_CAN_RENDER_GESTURAL_NAV_BUTTONS, true);
}
/**
@@ -658,7 +658,7 @@ public class InputMethodService extends AbstractInputMethodService {
@Override
public final void initializeInternal(@NonNull IBinder token,
IInputMethodPrivilegedOperations privilegedOperations, int configChanges,
- boolean stylusHwSupported) {
+ boolean stylusHwSupported, boolean shouldShowImeSwitcherWhenImeIsShown) {
if (mDestroyed) {
Log.i(TAG, "The InputMethodService has already onDestroyed()."
+ "Ignore the initialization.");
@@ -671,6 +671,8 @@ public class InputMethodService extends AbstractInputMethodService {
if (stylusHwSupported) {
mInkWindow = new InkWindow(mWindow.getContext());
}
+ mNavigationBarController.setShouldShowImeSwitcherWhenImeIsShown(
+ shouldShowImeSwitcherWhenImeIsShown);
attachToken(token);
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
@@ -780,9 +782,10 @@ public class InputMethodService extends AbstractInputMethodService {
@Override
public final void dispatchStartInputWithToken(@Nullable InputConnection inputConnection,
@NonNull EditorInfo editorInfo, boolean restarting,
- @NonNull IBinder startInputToken) {
+ @NonNull IBinder startInputToken, boolean shouldShowImeSwitcherWhenImeIsShown) {
mPrivOps.reportStartInputAsync(startInputToken);
-
+ mNavigationBarController.setShouldShowImeSwitcherWhenImeIsShown(
+ shouldShowImeSwitcherWhenImeIsShown);
if (restarting) {
restartInput(inputConnection, editorInfo);
} else {
@@ -796,6 +799,18 @@ public class InputMethodService extends AbstractInputMethodService {
*/
@MainThread
@Override
+ public void onShouldShowImeSwitcherWhenImeIsShownChanged(
+ boolean shouldShowImeSwitcherWhenImeIsShown) {
+ mNavigationBarController.setShouldShowImeSwitcherWhenImeIsShown(
+ shouldShowImeSwitcherWhenImeIsShown);
+ }
+
+ /**
+ * {@inheritDoc}
+ * @hide
+ */
+ @MainThread
+ @Override
public void hideSoftInputWithToken(int flags, ResultReceiver resultReceiver,
IBinder hideInputToken) {
mSystemCallingHideSoftInput = true;
@@ -1489,7 +1504,7 @@ public class InputMethodService extends AbstractInputMethodService {
Context.LAYOUT_INFLATER_SERVICE);
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.initSoftInputWindow");
mWindow = new SoftInputWindow(this, mTheme, mDispatcherState);
-
+ mNavigationBarController.onSoftInputWindowCreated(mWindow);
{
final Window window = mWindow.getWindow();
{
@@ -2216,10 +2231,12 @@ public class InputMethodService extends AbstractInputMethodService {
}
/**
- * Called by the framework to create the layout for showing extacted text.
+ * Called by the framework to create the layout for showing extracted text.
* Only called when in fullscreen mode. The returned view hierarchy must
* have an {@link ExtractEditText} whose ID is
- * {@link android.R.id#inputExtractEditText}.
+ * {@link android.R.id#inputExtractEditText}, with action ID
+ * {@link android.R.id#inputExtractAction} and accessories ID
+ * {@link android.R.id#inputExtractAccessories}.
*/
public View onCreateExtractTextView() {
return mInflater.inflate(
diff --git a/core/java/android/inputmethodservice/NavigationBarController.java b/core/java/android/inputmethodservice/NavigationBarController.java
index a6e475aeb813..e5c22e4de08e 100644
--- a/core/java/android/inputmethodservice/NavigationBarController.java
+++ b/core/java/android/inputmethodservice/NavigationBarController.java
@@ -17,7 +17,10 @@
package android.inputmethodservice;
import static android.content.Intent.ACTION_OVERLAY_CHANGED;
+import static android.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS;
+import android.animation.ValueAnimator;
+import android.annotation.FloatRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.StatusBarManager;
@@ -26,6 +29,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Resources;
+import android.graphics.Color;
import android.graphics.Insets;
import android.graphics.Rect;
import android.graphics.Region;
@@ -40,7 +44,10 @@ import android.view.ViewParent;
import android.view.ViewTreeObserver;
import android.view.Window;
import android.view.WindowInsets;
+import android.view.WindowInsetsController.Appearance;
import android.view.WindowManagerPolicyConstants;
+import android.view.animation.Interpolator;
+import android.view.animation.PathInterpolator;
import android.widget.FrameLayout;
import java.util.Objects;
@@ -59,6 +66,9 @@ final class NavigationBarController {
@NonNull ViewTreeObserver.InternalInsetsInfo dest) {
}
+ default void onSoftInputWindowCreated(@NonNull SoftInputWindow softInputWindow) {
+ }
+
default void onViewInitialized() {
}
@@ -68,6 +78,10 @@ final class NavigationBarController {
default void onDestroy() {
}
+ default void setShouldShowImeSwitcherWhenImeIsShown(
+ boolean shouldShowImeSwitcherWhenImeIsShown) {
+ }
+
default String toDebugString() {
return "No-op implementation";
}
@@ -88,6 +102,10 @@ final class NavigationBarController {
mImpl.updateTouchableInsets(originalInsets, dest);
}
+ void onSoftInputWindowCreated(@NonNull SoftInputWindow softInputWindow) {
+ mImpl.onSoftInputWindowCreated(softInputWindow);
+ }
+
void onViewInitialized() {
mImpl.onViewInitialized();
}
@@ -100,11 +118,21 @@ final class NavigationBarController {
mImpl.onDestroy();
}
+ void setShouldShowImeSwitcherWhenImeIsShown(boolean shouldShowImeSwitcherWhenImeIsShown) {
+ mImpl.setShouldShowImeSwitcherWhenImeIsShown(shouldShowImeSwitcherWhenImeIsShown);
+ }
+
String toDebugString() {
return mImpl.toDebugString();
}
- private static final class Impl implements Callback {
+ private static final class Impl implements Callback, Window.DecorCallback {
+ private static final int DEFAULT_COLOR_ADAPT_TRANSITION_TIME = 1700;
+
+ // Copied from com.android.systemui.animation.Interpolators#LEGACY_DECELERATE
+ private static final Interpolator LEGACY_DECELERATE =
+ new PathInterpolator(0f, 0f, 0.2f, 1f);
+
@NonNull
private final InputMethodService mService;
@@ -120,6 +148,19 @@ final class NavigationBarController {
@Nullable
private BroadcastReceiver mSystemOverlayChangedReceiver;
+ private boolean mShouldShowImeSwitcherWhenImeIsShown;
+
+ @Appearance
+ private int mAppearance;
+
+ @FloatRange(from = 0.0f, to = 1.0f)
+ private float mDarkIntensity;
+
+ @Nullable
+ private ValueAnimator mTintAnimator;
+
+ private boolean mDrawLegacyNavigationBarBackground;
+
Impl(@NonNull InputMethodService inputMethodService) {
mService = inputMethodService;
}
@@ -177,7 +218,9 @@ final class NavigationBarController {
// TODO(b/213337792): Support InputMethodService#setBackDisposition().
// TODO(b/213337792): Set NAVIGATION_HINT_IME_SHOWN only when necessary.
final int hints = StatusBarManager.NAVIGATION_HINT_BACK_ALT
- | StatusBarManager.NAVIGATION_HINT_IME_SHOWN;
+ | (mShouldShowImeSwitcherWhenImeIsShown
+ ? StatusBarManager.NAVIGATION_HINT_IME_SHOWN
+ : 0);
navigationBarView.setNavigationIconHints(hints);
}
} else {
@@ -186,7 +229,14 @@ final class NavigationBarController {
mLastInsets = systemInsets;
}
- mNavigationBarFrame.setBackground(null);
+ if (mDrawLegacyNavigationBarBackground) {
+ mNavigationBarFrame.setBackgroundColor(Color.BLACK);
+ } else {
+ mNavigationBarFrame.setBackground(null);
+ }
+
+ setIconTintInternal(calculateTargetDarkIntensity(mAppearance,
+ mDrawLegacyNavigationBarBackground));
}
private void uninstallNavigationBarFrameIfNecessary() {
@@ -320,6 +370,13 @@ final class NavigationBarController {
}
@Override
+ public void onSoftInputWindowCreated(@NonNull SoftInputWindow softInputWindow) {
+ final Window window = softInputWindow.getWindow();
+ mAppearance = window.getSystemBarAppearance();
+ window.setDecorCallback(this);
+ }
+
+ @Override
public void onViewInitialized() {
if (mDestroyed) {
return;
@@ -353,6 +410,10 @@ final class NavigationBarController {
if (mDestroyed) {
return;
}
+ if (mTintAnimator != null) {
+ mTintAnimator.cancel();
+ mTintAnimator = null;
+ }
if (mSystemOverlayChangedReceiver != null) {
mService.unregisterReceiver(mSystemOverlayChangedReceiver);
mSystemOverlayChangedReceiver = null;
@@ -389,9 +450,106 @@ final class NavigationBarController {
}
@Override
+ public void setShouldShowImeSwitcherWhenImeIsShown(
+ boolean shouldShowImeSwitcherWhenImeIsShown) {
+ if (mDestroyed) {
+ return;
+ }
+ if (mShouldShowImeSwitcherWhenImeIsShown == shouldShowImeSwitcherWhenImeIsShown) {
+ return;
+ }
+ mShouldShowImeSwitcherWhenImeIsShown = shouldShowImeSwitcherWhenImeIsShown;
+
+ if (mNavigationBarFrame == null) {
+ return;
+ }
+ final NavigationBarView navigationBarView =
+ mNavigationBarFrame.findViewByPredicate(NavigationBarView.class::isInstance);
+ if (navigationBarView == null) {
+ return;
+ }
+ final int hints = StatusBarManager.NAVIGATION_HINT_BACK_ALT
+ | (shouldShowImeSwitcherWhenImeIsShown
+ ? StatusBarManager.NAVIGATION_HINT_IME_SHOWN : 0);
+ navigationBarView.setNavigationIconHints(hints);
+ }
+
+ @Override
+ public void onSystemBarAppearanceChanged(@Appearance int appearance) {
+ if (mDestroyed) {
+ return;
+ }
+
+ mAppearance = appearance;
+
+ if (mNavigationBarFrame == null) {
+ return;
+ }
+
+ final float targetDarkIntensity = calculateTargetDarkIntensity(mAppearance,
+ mDrawLegacyNavigationBarBackground);
+
+ if (mTintAnimator != null) {
+ mTintAnimator.cancel();
+ }
+ mTintAnimator = ValueAnimator.ofFloat(mDarkIntensity, targetDarkIntensity);
+ mTintAnimator.addUpdateListener(
+ animation -> setIconTintInternal((Float) animation.getAnimatedValue()));
+ mTintAnimator.setDuration(DEFAULT_COLOR_ADAPT_TRANSITION_TIME);
+ mTintAnimator.setStartDelay(0);
+ mTintAnimator.setInterpolator(LEGACY_DECELERATE);
+ mTintAnimator.start();
+ }
+
+ private void setIconTintInternal(float darkIntensity) {
+ mDarkIntensity = darkIntensity;
+ if (mNavigationBarFrame == null) {
+ return;
+ }
+ final NavigationBarView navigationBarView =
+ mNavigationBarFrame.findViewByPredicate(NavigationBarView.class::isInstance);
+ if (navigationBarView == null) {
+ return;
+ }
+ navigationBarView.setDarkIntensity(darkIntensity);
+ }
+
+ @FloatRange(from = 0.0f, to = 1.0f)
+ private static float calculateTargetDarkIntensity(@Appearance int appearance,
+ boolean drawLegacyNavigationBarBackground) {
+ final boolean lightNavBar = !drawLegacyNavigationBarBackground
+ && (appearance & APPEARANCE_LIGHT_NAVIGATION_BARS) != 0;
+ return lightNavBar ? 1.0f : 0.0f;
+ }
+
+ @Override
+ public boolean onDrawLegacyNavigationBarBackgroundChanged(
+ boolean drawLegacyNavigationBarBackground) {
+ if (mDestroyed) {
+ return false;
+ }
+
+ if (drawLegacyNavigationBarBackground != mDrawLegacyNavigationBarBackground) {
+ mDrawLegacyNavigationBarBackground = drawLegacyNavigationBarBackground;
+ if (mDrawLegacyNavigationBarBackground) {
+ mNavigationBarFrame.setBackgroundColor(Color.BLACK);
+ } else {
+ mNavigationBarFrame.setBackground(null);
+ }
+ scheduleRelayout();
+ onSystemBarAppearanceChanged(mAppearance);
+ }
+ return drawLegacyNavigationBarBackground;
+ }
+
+ @Override
public String toDebugString() {
return "{mRenderGesturalNavButtons=" + mRenderGesturalNavButtons
+ " mNavigationBarFrame=" + mNavigationBarFrame
+ + " mShouldShowImeSwitcherWhenImeIsShown" + mShouldShowImeSwitcherWhenImeIsShown
+ + " mAppearance=0x" + Integer.toHexString(mAppearance)
+ + " mDarkIntensity=" + mDarkIntensity
+ + " mDrawLegacyNavigationBarBackground=" + mDrawLegacyNavigationBarBackground
+ "}";
}
}
diff --git a/core/java/android/inputmethodservice/SoftInputWindow.java b/core/java/android/inputmethodservice/SoftInputWindow.java
index 6c8eb41d8724..5704dac7a327 100644
--- a/core/java/android/inputmethodservice/SoftInputWindow.java
+++ b/core/java/android/inputmethodservice/SoftInputWindow.java
@@ -23,7 +23,6 @@ import static java.lang.annotation.RetentionPolicy.SOURCE;
import android.annotation.IntDef;
import android.app.Dialog;
-import android.content.Context;
import android.graphics.Rect;
import android.os.Debug;
import android.os.IBinder;
@@ -47,6 +46,7 @@ final class SoftInputWindow extends Dialog {
private final KeyEvent.DispatcherState mDispatcherState;
private final Rect mBounds = new Rect();
+ private final InputMethodService mService;
@Retention(SOURCE)
@IntDef(value = {WindowState.TOKEN_PENDING, WindowState.TOKEN_SET,
@@ -120,7 +120,7 @@ final class SoftInputWindow extends Dialog {
/**
* Create a SoftInputWindow that uses a custom style.
*
- * @param context The Context in which the DockWindow should run. In
+ * @param service The {@link InputMethodService} in which the DockWindow should run. In
* particular, it uses the window manager and theme from this context
* to present its UI.
* @param theme A style resource describing the theme to use for the window.
@@ -129,8 +129,10 @@ final class SoftInputWindow extends Dialog {
* using styles. This theme is applied on top of the current theme in
* <var>context</var>. If 0, the default dialog theme will be used.
*/
- SoftInputWindow(Context context, int theme, KeyEvent.DispatcherState dispatcherState) {
- super(context, theme);
+ SoftInputWindow(InputMethodService service, int theme,
+ KeyEvent.DispatcherState dispatcherState) {
+ super(service, theme);
+ mService = service;
mDispatcherState = dispatcherState;
}
diff --git a/core/java/android/inputmethodservice/navigationbar/DeadZone.java b/core/java/android/inputmethodservice/navigationbar/DeadZone.java
index cd857369bc5a..4adc84bf0b6f 100644
--- a/core/java/android/inputmethodservice/navigationbar/DeadZone.java
+++ b/core/java/android/inputmethodservice/navigationbar/DeadZone.java
@@ -27,6 +27,7 @@ import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.os.SystemClock;
+import android.util.FloatProperty;
import android.util.Log;
import android.view.MotionEvent;
import android.view.Surface;
@@ -46,6 +47,20 @@ final class DeadZone {
public static final int VERTICAL = 1; // Consume taps along the left edge.
private static final boolean CHATTY = true; // print to logcat when we eat a click
+
+ private static final FloatProperty<DeadZone> FLASH_PROPERTY =
+ new FloatProperty<DeadZone>("DeadZoneFlash") {
+ @Override
+ public void setValue(DeadZone object, float value) {
+ object.setFlash(value);
+ }
+
+ @Override
+ public Float get(DeadZone object) {
+ return object.getFlash();
+ }
+ };
+
private final NavigationBarView mNavigationBarView;
private boolean mShouldFlash;
@@ -63,7 +78,7 @@ final class DeadZone {
private final Runnable mDebugFlash = new Runnable() {
@Override
public void run() {
- ObjectAnimator.ofFloat(DeadZone.this, "flash", 1f, 0f).setDuration(150).start();
+ ObjectAnimator.ofFloat(DeadZone.this, FLASH_PROPERTY, 1f, 0f).setDuration(150).start();
}
};
diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java
index c936bfa05118..9122adfece53 100644
--- a/core/java/android/net/NetworkPolicyManager.java
+++ b/core/java/android/net/NetworkPolicyManager.java
@@ -523,9 +523,11 @@ public class NetworkPolicyManager {
*
* @param subId the subscriber to get the subscription plans for.
* @param callingPackage the name of the package making the call.
+ * @return the active {@link SubscriptionPlan}s for the given subscription id, or
+ * {@code null} if not found.
* @hide
*/
- @NonNull
+ @Nullable
public SubscriptionPlan[] getSubscriptionPlans(int subId, @NonNull String callingPackage) {
try {
return mService.getSubscriptionPlans(subId, callingPackage);
@@ -538,7 +540,7 @@ public class NetworkPolicyManager {
* Get subscription plan for the given networkTemplate.
*
* @param template the networkTemplate to get the subscription plan for.
- * @return the active {@link SubscriptionPlan} for the given template, or
+ * @return the active {@link SubscriptionPlan}s for the given template, or
* {@code null} if not found.
* @hide
*/
diff --git a/core/java/android/net/netstats/NetworkStatsDataMigrationUtils.java b/core/java/android/net/netstats/NetworkStatsDataMigrationUtils.java
index 24c22a99b78d..9772bde94ac9 100644
--- a/core/java/android/net/netstats/NetworkStatsDataMigrationUtils.java
+++ b/core/java/android/net/netstats/NetworkStatsDataMigrationUtils.java
@@ -16,9 +16,7 @@
package android.net.netstats;
-import static android.app.usage.NetworkStatsManager.PREFIX_UID;
-import static android.app.usage.NetworkStatsManager.PREFIX_UID_TAG;
-import static android.app.usage.NetworkStatsManager.PREFIX_XT;
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
import static android.net.ConnectivityManager.TYPE_MOBILE;
import static android.net.ConnectivityManager.TYPE_MOBILE_DUN;
import static android.net.ConnectivityManager.TYPE_MOBILE_HIPRI;
@@ -28,6 +26,7 @@ import static android.net.NetworkStats.SET_DEFAULT;
import static android.net.NetworkStats.TAG_NONE;
import android.annotation.NonNull;
+import android.annotation.SystemApi;
import android.net.NetworkIdentity;
import android.net.NetworkStatsCollection;
import android.net.NetworkStatsHistory;
@@ -54,12 +53,27 @@ import java.util.HashSet;
import java.util.Set;
/**
- * Helper class to read old version of persistent network statistics, the implementation is
- * intended to be modified by OEM partners to accommodate their custom changes.
+ * Helper class to read old version of persistent network statistics.
+ *
+ * The implementation is intended to be modified by OEM partners to
+ * accommodate their custom changes.
+ *
* @hide
*/
-// @SystemApi(client = MODULE_LIBRARIES)
+@SystemApi(client = MODULE_LIBRARIES)
public class NetworkStatsDataMigrationUtils {
+ /**
+ * Prefix of the files which are used to store per network interface statistics.
+ */
+ public static final String PREFIX_XT = "xt";
+ /**
+ * Prefix of the files which are used to store per uid statistics.
+ */
+ public static final String PREFIX_UID = "uid";
+ /**
+ * Prefix of the files which are used to store per uid tagged traffic statistics.
+ */
+ public static final String PREFIX_UID_TAG = "uid_tag";
private static final HashMap<String, String> sPrefixLegacyFileNameMap =
new HashMap<String, String>() {{
@@ -146,17 +160,51 @@ public class NetworkStatsDataMigrationUtils {
}
/**
- * Read legacy persisted network stats from disk. This function provides a default
- * implementation to read persisted network stats from two different locations.
- * And this is intended to be modified by OEM to read from custom file format or
- * locations if necessary.
+ * Read legacy persisted network stats from disk.
+ *
+ * This function provides the implementation to read legacy network stats
+ * from disk. It is used for migration of legacy network stats into the
+ * stats provided by the Connectivity module.
+ * This function needs to know about the previous format(s) of the network
+ * stats data that might be stored on this device so it can be read and
+ * conserved upon upgrade to Android 13 or above.
+ *
+ * This function will be called multiple times sequentially, all on the
+ * same thread, and will not be called multiple times concurrently. This
+ * function is expected to do a substantial amount of disk access, and
+ * doesn't need to return particularly fast, but the first boot after
+ * an upgrade to Android 13+ will be held until migration is done. As
+ * migration is only necessary once, after the first boot following the
+ * upgrade, this delay is not incurred.
+ *
+ * If this function fails in any way, it should throw an exception. If this
+ * happens, the system can't know about the data that was stored in the
+ * legacy files, but it will still count data usage happening on this
+ * session. On the next boot, the system will try migration again, and
+ * merge the returned data with the data used with the previous session.
+ * The system will only try the migration up to three (3) times. The remaining
+ * count is stored in the netstats_import_legacy_file_needed device config. The
+ * legacy data is never deleted by the mainline module to avoid any possible
+ * data loss.
+ *
+ * It is possible to set the netstats_import_legacy_file_needed device config
+ * to any positive integer to force the module to perform the migration. This
+ * can be achieved by calling the following command before rebooting :
+ * adb shell device_config put connectivity netstats_import_legacy_file_needed 1
+ *
+ * The AOSP implementation provides code to read persisted network stats as
+ * they were written by AOSP prior to Android 13.
+ * OEMs who have used the AOSP implementation of persisting network stats
+ * to disk don't need to change anything.
+ * OEM that had modifications to this format should modify this function
+ * to read from their custom file format or locations if necessary.
*
* @param prefix Type of data which is being read by the service.
* @param bucketDuration Duration of the buckets of the object, in milliseconds.
* @return {@link NetworkStatsCollection} instance.
*/
@NonNull
- public static NetworkStatsCollection readPlatformCollectionLocked(
+ public static NetworkStatsCollection readPlatformCollection(
@NonNull String prefix, long bucketDuration) throws IOException {
final NetworkStatsCollection.Builder builder =
new NetworkStatsCollection.Builder(bucketDuration);
@@ -397,7 +445,7 @@ public class NetworkStatsDataMigrationUtils {
if (version >= IdentitySetVersion.VERSION_ADD_OEM_MANAGED_NETWORK) {
oemNetCapabilities = in.readInt();
} else {
- oemNetCapabilities = NetworkIdentity.OEM_NONE;
+ oemNetCapabilities = NetworkTemplate.OEM_MANAGED_NO;
}
// Legacy files might contain TYPE_MOBILE_* types which were deprecated in later
diff --git a/core/java/android/os/BatteryConsumer.java b/core/java/android/os/BatteryConsumer.java
index 47a272c2337f..de76c8fab69b 100644
--- a/core/java/android/os/BatteryConsumer.java
+++ b/core/java/android/os/BatteryConsumer.java
@@ -673,6 +673,7 @@ public abstract class BatteryConsumer {
public final int firstCustomConsumedPowerColumn;
public final int firstCustomUsageDurationColumn;
public final int columnCount;
+ public final Key[][] processStateKeys;
private BatteryConsumerDataLayout(int firstColumn, String[] customPowerComponentNames,
boolean powerModelsIncluded, boolean includeProcessStateData) {
@@ -728,6 +729,28 @@ public abstract class BatteryConsumer {
keys[componentId] = perComponentKeys.toArray(KEY_ARRAY);
}
+ if (includeProcessStateData) {
+ processStateKeys = new Key[BatteryConsumer.PROCESS_STATE_COUNT][];
+ ArrayList<Key> perProcStateKeys = new ArrayList<>();
+ for (int processState = 0; processState < PROCESS_STATE_COUNT; processState++) {
+ if (processState == PROCESS_STATE_UNSPECIFIED) {
+ continue;
+ }
+
+ perProcStateKeys.clear();
+ for (int i = 0; i < keys.length; i++) {
+ for (int j = 0; j < keys[i].length; j++) {
+ if (keys[i][j].processState == processState) {
+ perProcStateKeys.add(keys[i][j]);
+ }
+ }
+ }
+ processStateKeys[processState] = perProcStateKeys.toArray(KEY_ARRAY);
+ }
+ } else {
+ processStateKeys = null;
+ }
+
firstCustomConsumedPowerColumn = columnIndex;
columnIndex += customPowerComponentCount;
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 2d338179186e..07a51324404c 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -34,6 +34,7 @@ import android.server.ServerProtoEnums;
import android.service.batterystats.BatteryStatsServiceDumpHistoryProto;
import android.service.batterystats.BatteryStatsServiceDumpProto;
import android.telephony.CellSignalStrength;
+import android.telephony.ServiceState;
import android.telephony.TelephonyManager;
import android.text.format.DateFormat;
import android.util.ArrayMap;
@@ -2654,6 +2655,46 @@ public abstract class BatteryStats implements Parcelable {
*/
public abstract Timer getPhoneDataConnectionTimer(int dataType);
+ /** @hide */
+ public static final int RADIO_ACCESS_TECHNOLOGY_OTHER = 0;
+ /** @hide */
+ public static final int RADIO_ACCESS_TECHNOLOGY_LTE = 1;
+ /** @hide */
+ public static final int RADIO_ACCESS_TECHNOLOGY_NR = 2;
+ /** @hide */
+ public static final int RADIO_ACCESS_TECHNOLOGY_COUNT = 3;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = "RADIO_ACCESS_TECHNOLOGY_",
+ value = {RADIO_ACCESS_TECHNOLOGY_OTHER, RADIO_ACCESS_TECHNOLOGY_LTE,
+ RADIO_ACCESS_TECHNOLOGY_NR})
+ public @interface RadioAccessTechnology {
+ }
+
+ /** @hide */
+ public static final String[] RADIO_ACCESS_TECHNOLOGY_NAMES = {"Other", "LTE", "NR"};
+
+ /**
+ * Returns the time in microseconds that the mobile radio has been active on a
+ * given Radio Access Technology (RAT), at a given frequency (NR RAT only), for a given
+ * transmission power level.
+ *
+ * @param rat Radio Access Technology {@see RadioAccessTechnology}
+ * @param frequencyRange frequency range {@see ServiceState.FrequencyRange}, only needed for
+ * RADIO_ACCESS_TECHNOLOGY_NR. Use
+ * {@link ServiceState.FREQUENCY_RANGE_UNKNOWN} for other Radio Access
+ * Technologies.
+ * @param signalStrength the cellular signal strength. {@see CellSignalStrength#getLevel()}
+ * @param elapsedRealtimeMs current elapsed realtime
+ * @return time (in milliseconds) the mobile radio spent active in the specified state,
+ * while on battery.
+ * @hide
+ */
+ public abstract long getActiveRadioDurationMs(@RadioAccessTechnology int rat,
+ @ServiceState.FrequencyRange int frequencyRange, int signalStrength,
+ long elapsedRealtimeMs);
+
static final String[] WIFI_SUPPL_STATE_NAMES = {
"invalid", "disconn", "disabled", "inactive", "scanning",
"authenticating", "associating", "associated", "4-way-handshake",
@@ -3997,6 +4038,89 @@ public abstract class BatteryStats implements Parcelable {
}
}
+ private void printCellularPerRatBreakdown(PrintWriter pw, StringBuilder sb, String prefix,
+ long rawRealtimeMs) {
+ final String allFrequenciesHeader =
+ " All frequencies:\n";
+ final String[] nrFrequencyRangeDescription = new String[]{
+ " Unknown frequency:\n",
+ " Low frequency (less than 1GHz):\n",
+ " Middle frequency (1GHz to 3GHz):\n",
+ " High frequency (3GHz to 6GHz):\n",
+ " Mmwave frequency (greater than 6GHz):\n"};
+ final String signalStrengthHeader =
+ " Signal Strength Time:\n";
+ final String[] signalStrengthDescription = new String[]{
+ " unknown: ",
+ " poor: ",
+ " moderate: ",
+ " good: ",
+ " great: "};
+
+ final long totalActiveTimesMs = getMobileRadioActiveTime(rawRealtimeMs * 1000,
+ STATS_SINCE_CHARGED) / 1000;
+
+ sb.setLength(0);
+ sb.append(prefix);
+ sb.append("Active Cellular Radio Access Technology Breakdown:");
+ pw.println(sb);
+
+ boolean hasData = false;
+ final int numSignalStrength = CellSignalStrength.getNumSignalStrengthLevels();
+ for (int rat = RADIO_ACCESS_TECHNOLOGY_COUNT - 1; rat >= 0; rat--) {
+ sb.setLength(0);
+ sb.append(prefix);
+ sb.append(" ");
+ sb.append(RADIO_ACCESS_TECHNOLOGY_NAMES[rat]);
+ sb.append(":\n");
+ sb.append(prefix);
+
+ final int numFreqLvl =
+ rat == RADIO_ACCESS_TECHNOLOGY_NR ? nrFrequencyRangeDescription.length : 1;
+ for (int freqLvl = numFreqLvl - 1; freqLvl >= 0; freqLvl--) {
+ final int freqDescriptionStart = sb.length();
+ boolean hasFreqData = false;
+ if (rat == RADIO_ACCESS_TECHNOLOGY_NR) {
+ sb.append(nrFrequencyRangeDescription[freqLvl]);
+ } else {
+ sb.append(allFrequenciesHeader);
+ }
+
+ sb.append(prefix);
+ sb.append(signalStrengthHeader);
+ for (int strength = 0; strength < numSignalStrength; strength++) {
+ final long timeMs = getActiveRadioDurationMs(rat, freqLvl, strength,
+ rawRealtimeMs);
+ if (timeMs <= 0) continue;
+ hasFreqData = true;
+ sb.append(prefix);
+ sb.append(signalStrengthDescription[strength]);
+ formatTimeMs(sb, timeMs);
+ sb.append("(");
+ sb.append(formatRatioLocked(timeMs, totalActiveTimesMs));
+ sb.append(")\n");
+ }
+
+ if (hasFreqData) {
+ hasData = true;
+ pw.print(sb);
+ sb.setLength(0);
+ sb.append(prefix);
+ } else {
+ // No useful data was printed, rewind sb to before the start of this frequency.
+ sb.setLength(freqDescriptionStart);
+ }
+ }
+ }
+
+ if (!hasData) {
+ sb.setLength(0);
+ sb.append(prefix);
+ sb.append(" (no activity)");
+ pw.println(sb);
+ }
+ }
+
/**
* Temporary for settings.
*/
@@ -5269,6 +5393,8 @@ public abstract class BatteryStats implements Parcelable {
printControllerActivity(pw, sb, prefix, CELLULAR_CONTROLLER_NAME,
getModemControllerActivity(), which);
+ printCellularPerRatBreakdown(pw, sb, prefix + " ", rawRealtimeMs);
+
pw.print(" Cellular data received: "); pw.println(formatBytesLocked(mobileRxTotalBytes));
pw.print(" Cellular data sent: "); pw.println(formatBytesLocked(mobileTxTotalBytes));
pw.print(" Cellular packets received: "); pw.println(mobileRxTotalPackets);
diff --git a/core/java/android/os/BatteryStatsManager.java b/core/java/android/os/BatteryStatsManager.java
index 2a609b8f6ae4..f16bbc66e6cd 100644
--- a/core/java/android/os/BatteryStatsManager.java
+++ b/core/java/android/os/BatteryStatsManager.java
@@ -24,6 +24,8 @@ import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.TestApi;
+import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
+import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission;
import android.content.Context;
import android.net.NetworkStack;
import android.os.connectivity.CellularBatteryStats;
@@ -515,6 +517,42 @@ public final class BatteryStatsManager {
}
/**
+ * Indicates that Bluetooth was toggled on.
+ *
+ * @param uid calling package uid
+ * @param reason why Bluetooth has been turned on
+ * @param packageName package responsible for this change
+ */
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
+ public void reportBluetoothOn(int uid, int reason, @NonNull String packageName) {
+ try {
+ mBatteryStats.noteBluetoothOn(uid, reason, packageName);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Indicates that Bluetooth was toggled off.
+ *
+ * @param uid calling package uid
+ * @param reason why Bluetooth has been turned on
+ * @param packageName package responsible for this change
+ */
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
+ public void reportBluetoothOff(int uid, int reason, @NonNull String packageName) {
+ try {
+ mBatteryStats.noteBluetoothOff(uid, reason, packageName);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Indicates that a new Bluetooth LE scan has started.
*
* @param ws worksource (to be used for battery blaming).
diff --git a/core/java/android/os/IPowerManager.aidl b/core/java/android/os/IPowerManager.aidl
index 425e797a9e0e..4fe6524dee27 100644
--- a/core/java/android/os/IPowerManager.aidl
+++ b/core/java/android/os/IPowerManager.aidl
@@ -21,15 +21,16 @@ import android.os.BatterySaverPolicyConfig;
import android.os.ParcelDuration;
import android.os.PowerSaveState;
import android.os.WorkSource;
+import android.os.IWakeLockCallback;
/** @hide */
interface IPowerManager
{
void acquireWakeLock(IBinder lock, int flags, String tag, String packageName, in WorkSource ws,
- String historyTag, int displayId);
+ String historyTag, int displayId, IWakeLockCallback callback);
void acquireWakeLockWithUid(IBinder lock, int flags, String tag, String packageName,
- int uidtoblame, int displayId);
+ int uidtoblame, int displayId, IWakeLockCallback callback);
@UnsupportedAppUsage
void releaseWakeLock(IBinder lock, int flags);
void updateWakeLockUids(IBinder lock, in int[] uids);
@@ -40,6 +41,7 @@ interface IPowerManager
boolean setPowerModeChecked(int mode, boolean enabled);
void updateWakeLockWorkSource(IBinder lock, in WorkSource ws, String historyTag);
+ void updateWakeLockCallback(IBinder lock, IWakeLockCallback callback);
boolean isWakeLockLevelSupported(int level);
void userActivity(int displayId, long time, int event, int flags);
@@ -65,6 +67,11 @@ interface IPowerManager
boolean isBatteryDischargePredictionPersonalized();
boolean isDeviceIdleMode();
boolean isLightDeviceIdleMode();
+ boolean isLowPowerStandbySupported();
+ boolean isLowPowerStandbyEnabled();
+ void setLowPowerStandbyEnabled(boolean enabled);
+ void setLowPowerStandbyActiveDuringMaintenance(boolean activeDuringMaintenance);
+ void forceLowPowerStandbyActive(boolean active);
@UnsupportedAppUsage
void reboot(boolean confirm, String reason, boolean wait);
diff --git a/core/java/android/os/IWakeLockCallback.aidl b/core/java/android/os/IWakeLockCallback.aidl
new file mode 100644
index 000000000000..89615d22421e
--- /dev/null
+++ b/core/java/android/os/IWakeLockCallback.aidl
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+/**
+ * @hide
+ */
+oneway interface IWakeLockCallback {
+ oneway void onStateChanged(boolean enabled);
+}
diff --git a/core/java/android/os/PowerComponents.java b/core/java/android/os/PowerComponents.java
index 590494cd3273..48e111647327 100644
--- a/core/java/android/os/PowerComponents.java
+++ b/core/java/android/os/PowerComponents.java
@@ -16,7 +16,6 @@
package android.os;
import static android.os.BatteryConsumer.POWER_COMPONENT_ANY;
-import static android.os.BatteryConsumer.POWER_COMPONENT_COUNT;
import static android.os.BatteryConsumer.PROCESS_STATE_ANY;
import static android.os.BatteryConsumer.PROCESS_STATE_UNSPECIFIED;
import static android.os.BatteryConsumer.convertMahToDeciCoulombs;
@@ -60,19 +59,16 @@ class PowerComponents {
return mData.getDouble(mData.getKeyOrThrow(dimensions.powerComponent,
dimensions.processState).mPowerColumnIndex);
} else if (dimensions.processState != PROCESS_STATE_ANY) {
- boolean foundSome = false;
- double totalPowerMah = 0;
- for (int componentId = 0; componentId < POWER_COMPONENT_COUNT; componentId++) {
- BatteryConsumer.Key key = mData.getKey(componentId, dimensions.processState);
- if (key != null) {
- foundSome = true;
- totalPowerMah += mData.getDouble(key.mPowerColumnIndex);
- }
- }
- if (!foundSome) {
+ if (!mData.layout.processStateDataIncluded) {
throw new IllegalArgumentException(
"No data included in BatteryUsageStats for " + dimensions);
}
+ final BatteryConsumer.Key[] keys =
+ mData.layout.processStateKeys[dimensions.processState];
+ double totalPowerMah = 0;
+ for (int i = keys.length - 1; i >= 0; i--) {
+ totalPowerMah += mData.getDouble(keys[i].mPowerColumnIndex);
+ }
return totalPowerMah;
} else {
return mData.getDouble(mData.layout.totalConsumedPowerColumnIndex);
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index 881fcedea6f7..315eef78724e 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -216,6 +216,17 @@ public final class PowerManager {
public static final int UNIMPORTANT_FOR_LOGGING = 0x40000000;
/**
+ * Wake lock flag: This wake lock should be held by the system.
+ *
+ * <p>Meant to allow tests to keep the device awake even when power restrictions are active.
+ *
+ * @hide
+ */
+ @TestApi
+ @RequiresPermission(android.Manifest.permission.DEVICE_POWER)
+ public static final int SYSTEM_WAKELOCK = 0x80000000;
+
+ /**
* Flag for {@link WakeLock#release WakeLock.release(int)}: Defer releasing a
* {@link #PROXIMITY_SCREEN_OFF_WAKE_LOCK} wake lock until the proximity sensor
* indicates that an object is not in close proximity.
@@ -2146,6 +2157,105 @@ public final class PowerManager {
}
/**
+ * Returns true if Low Power Standby is supported on this device.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.MANAGE_LOW_POWER_STANDBY,
+ android.Manifest.permission.DEVICE_POWER
+ })
+ public boolean isLowPowerStandbySupported() {
+ try {
+ return mService.isLowPowerStandbySupported();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns true if Low Power Standby is enabled.
+ *
+ * <p>When Low Power Standby is enabled, apps (including apps running foreground services) are
+ * subject to additional restrictions while the device is non-interactive, outside of device
+ * idle maintenance windows: Their network access is disabled, and any wakelocks they hold are
+ * ignored.
+ *
+ * <p>When Low Power Standby is enabled or disabled, a Intent with action
+ * {@link #ACTION_LOW_POWER_STANDBY_ENABLED_CHANGED} is broadcast to registered receivers.
+ */
+ public boolean isLowPowerStandbyEnabled() {
+ try {
+ return mService.isLowPowerStandbyEnabled();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Set whether Low Power Standby is enabled.
+ * Does nothing if Low Power Standby is not supported.
+ *
+ * @see #isLowPowerStandbySupported()
+ * @see #isLowPowerStandbyEnabled()
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.MANAGE_LOW_POWER_STANDBY,
+ android.Manifest.permission.DEVICE_POWER
+ })
+ public void setLowPowerStandbyEnabled(boolean enabled) {
+ try {
+ mService.setLowPowerStandbyEnabled(enabled);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Set whether Low Power Standby should be active during doze maintenance mode.
+ * Does nothing if Low Power Standby is not supported.
+ *
+ * @see #isLowPowerStandbySupported()
+ * @see #isLowPowerStandbyEnabled()
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.MANAGE_LOW_POWER_STANDBY,
+ android.Manifest.permission.DEVICE_POWER
+ })
+ public void setLowPowerStandbyActiveDuringMaintenance(boolean activeDuringMaintenance) {
+ try {
+ mService.setLowPowerStandbyActiveDuringMaintenance(activeDuringMaintenance);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Force Low Power Standby restrictions to be active.
+ * Does nothing if Low Power Standby is not supported.
+ *
+ * @see #isLowPowerStandbySupported()
+ * @hide
+ */
+ @TestApi
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.MANAGE_LOW_POWER_STANDBY,
+ android.Manifest.permission.DEVICE_POWER
+ })
+ public void forceLowPowerStandbyActive(boolean active) {
+ try {
+ mService.forceLowPowerStandbyActive(active);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Return whether the given application package name is on the device's power allowlist.
* Apps can be placed on the allowlist through the settings UI invoked by
* {@link android.provider.Settings#ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS}.
@@ -2631,6 +2741,16 @@ public final class PowerManager {
= "android.os.action.POWER_SAVE_TEMP_WHITELIST_CHANGED";
/**
+ * Intent that is broadcast when Low Power Standby is enabled or disabled.
+ * This broadcast is only sent to registered receivers.
+ *
+ * @see #isLowPowerStandbyEnabled()
+ */
+ @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_LOW_POWER_STANDBY_ENABLED_CHANGED =
+ "android.os.action.LOW_POWER_STANDBY_ENABLED_CHANGED";
+
+ /**
* Constant for PreIdleTimeout normal mode (default mode, not short nor extend timeout) .
* @hide
*/
@@ -2650,6 +2770,23 @@ public final class PowerManager {
public static final int PRE_IDLE_TIMEOUT_MODE_SHORT = 2;
/**
+ * A listener interface to get notified when the wakelock is enabled/disabled.
+ */
+ public interface WakeLockStateListener {
+ /**
+ * Frameworks could disable the wakelock because either device's power allowlist has
+ * changed, or the app's wakelock has exceeded its quota, or the app goes into cached
+ * state.
+ * <p>
+ * This callback is called whenever the wakelock's state has changed.
+ * </p>
+ *
+ * @param enabled true is enabled, false is disabled.
+ */
+ void onStateChanged(boolean enabled);
+ }
+
+ /**
* A wake lock is a mechanism to indicate that your application needs
* to have the device stay on.
* <p>
@@ -2680,6 +2817,8 @@ public final class PowerManager {
private String mHistoryTag;
private final String mTraceName;
private final int mDisplayId;
+ private WakeLockStateListener mListener;
+ private IWakeLockCallback mCallback;
private final Runnable mReleaser = () -> release(RELEASE_FLAG_TIMEOUT);
@@ -2770,7 +2909,7 @@ public final class PowerManager {
Trace.asyncTraceBegin(Trace.TRACE_TAG_POWER, mTraceName, 0);
try {
mService.acquireWakeLock(mToken, mFlags, mTag, mPackageName, mWorkSource,
- mHistoryTag, mDisplayId);
+ mHistoryTag, mDisplayId, mCallback);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -2963,6 +3102,45 @@ public final class PowerManager {
}
};
}
+
+ /**
+ * Set the listener to get notified when the wakelock is enabled/disabled.
+ *
+ * @param executor {@link Executor} to handle listener callback.
+ * @param listener listener to be added, set the listener to null to cancel a listener.
+ */
+ public void setStateListener(@NonNull @CallbackExecutor Executor executor,
+ @Nullable WakeLockStateListener listener) {
+ Preconditions.checkNotNull(executor, "executor cannot be null");
+ synchronized (mToken) {
+ if (listener != mListener) {
+ mListener = listener;
+ if (listener != null) {
+ mCallback = new IWakeLockCallback.Stub() {
+ public void onStateChanged(boolean enabled) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ executor.execute(() -> {
+ listener.onStateChanged(enabled);
+ });
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ };
+ } else {
+ mCallback = null;
+ }
+ if (mHeld) {
+ try {
+ mService.updateWakeLockCallback(mToken, mCallback);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+ }
+ }
}
/**
diff --git a/core/java/android/os/PowerManagerInternal.java b/core/java/android/os/PowerManagerInternal.java
index eb18b96e255b..ec4d3b6a2441 100644
--- a/core/java/android/os/PowerManagerInternal.java
+++ b/core/java/android/os/PowerManagerInternal.java
@@ -184,6 +184,21 @@ public abstract class PowerManagerInternal {
public abstract void setDeviceIdleTempWhitelist(int[] appids);
+ /**
+ * Updates the Low Power Standby allowlist.
+ *
+ * @param uids UIDs that are exempt from Low Power Standby restrictions
+ */
+ public abstract void setLowPowerStandbyAllowlist(int[] uids);
+
+ /**
+ * Used by LowPowerStandbyController to notify the power manager that Low Power Standby's
+ * active state has changed.
+ *
+ * @param active {@code true} to activate Low Power Standby, {@code false} to turn it off.
+ */
+ public abstract void setLowPowerStandbyActive(boolean active);
+
public abstract void startUidChanges();
public abstract void finishUidChanges();
diff --git a/core/java/android/os/VibrationAttributes.java b/core/java/android/os/VibrationAttributes.java
index 8bc219b7dc57..d223a19b4348 100644
--- a/core/java/android/os/VibrationAttributes.java
+++ b/core/java/android/os/VibrationAttributes.java
@@ -37,6 +37,7 @@ public final class VibrationAttributes implements Parcelable {
USAGE_CLASS_UNKNOWN,
USAGE_CLASS_ALARM,
USAGE_CLASS_FEEDBACK,
+ USAGE_CLASS_MEDIA,
})
@Retention(RetentionPolicy.SOURCE)
public @interface UsageClass {}
@@ -105,20 +106,28 @@ public final class VibrationAttributes implements Parcelable {
public static final int USAGE_NOTIFICATION = 0x30 | USAGE_CLASS_ALARM;
/**
* Usage value to use for vibrations which mean a request to enter/end a
- * communication, such as a VoIP communication or video-conference.
+ * communication with the user, such as a voice prompt.
*/
public static final int USAGE_COMMUNICATION_REQUEST = 0x40 | USAGE_CLASS_ALARM;
/**
* Usage value to use for touch vibrations.
+ *
+ * <p>Most typical haptic feedback should be classed as <em>touch</em> feedback. Examples
+ * include vibrations for tap, long press, drag and scroll.
*/
public static final int USAGE_TOUCH = 0x10 | USAGE_CLASS_FEEDBACK;
/**
- * Usage value to use for vibrations which emulate physical effects, such as edge squeeze.
+ * Usage value to use for vibrations which emulate physical hardware reactions,
+ * such as edge squeeze.
+ *
+ * <p>Note that normal screen-touch feedback "click" effects would typically be
+ * classed as {@link #USAGE_TOUCH}, and that on-screen "physical" animations
+ * like bouncing would be {@link #USAGE_MEDIA}.
*/
public static final int USAGE_PHYSICAL_EMULATION = 0x20 | USAGE_CLASS_FEEDBACK;
/**
- * Usage value to use for vibrations which provide a feedback for hardware interaction,
- * such as a fingerprint sensor.
+ * Usage value to use for vibrations which provide a feedback for hardware
+ * component interaction, such as a fingerprint sensor.
*/
public static final int USAGE_HARDWARE_FEEDBACK = 0x30 | USAGE_CLASS_FEEDBACK;
/**
@@ -182,7 +191,6 @@ public final class VibrationAttributes implements Parcelable {
/**
* Return the vibration usage class.
- * @return USAGE_CLASS_ALARM, USAGE_CLASS_FEEDBACK or USAGE_CLASS_UNKNOWN
*/
@UsageClass
public int getUsageClass() {
@@ -191,7 +199,6 @@ public final class VibrationAttributes implements Parcelable {
/**
* Return the vibration usage.
- * @return one of the values that can be set in {@link Builder#setUsage(int)}
*/
@Usage
public int getUsage() {
@@ -428,16 +435,8 @@ public final class VibrationAttributes implements Parcelable {
}
/**
- * Sets the attribute describing the type of corresponding vibration.
- * @param usage one of {@link VibrationAttributes#USAGE_ALARM},
- * {@link VibrationAttributes#USAGE_RINGTONE},
- * {@link VibrationAttributes#USAGE_NOTIFICATION},
- * {@link VibrationAttributes#USAGE_COMMUNICATION_REQUEST},
- * {@link VibrationAttributes#USAGE_TOUCH},
- * {@link VibrationAttributes#USAGE_PHYSICAL_EMULATION},
- * {@link VibrationAttributes#USAGE_HARDWARE_FEEDBACK}.
- * {@link VibrationAttributes#USAGE_ACCESSIBILITY}.
- * {@link VibrationAttributes#USAGE_MEDIA}.
+ * Sets the attribute describing the type of the corresponding vibration.
+ * @param usage The type of usage for the vibration
* @return the same Builder instance.
*/
public @NonNull Builder setUsage(@Usage int usage) {
@@ -459,4 +458,3 @@ public final class VibrationAttributes implements Parcelable {
}
}
}
-
diff --git a/core/java/android/os/VibrationEffect.java b/core/java/android/os/VibrationEffect.java
index f490587ae1e1..21c64876c24c 100644
--- a/core/java/android/os/VibrationEffect.java
+++ b/core/java/android/os/VibrationEffect.java
@@ -194,27 +194,27 @@ public abstract class VibrationEffect implements Parcelable {
}
/**
- * Create a waveform vibration.
+ * Create a waveform vibration, using only off/on transitions at the provided time intervals,
+ * and potentially repeating.
*
- * <p>Waveform vibrations are a potentially repeating series of timing and amplitude pairs. For
- * each pair, the value in the amplitude array determines the strength of the vibration and the
- * value in the timing array determines how long it vibrates for. An amplitude of 0 implies no
- * vibration (i.e. off), and any pairs with a timing value of 0 will be ignored.
+ * <p>In effect, the timings array represents the number of milliseconds <em>before</em> turning
+ * the vibrator on, followed by the number of milliseconds to keep the vibrator on, then
+ * the number of milliseconds turned off, and so on. Consequently, the first timing value will
+ * often be 0, so that the effect will start vibrating immediately.
*
- * <p>The amplitude array of the generated waveform will be the same size as the given
- * timing array with alternating values of 0 (i.e. off) and {@link #DEFAULT_AMPLITUDE},
- * starting with 0. Therefore the first timing value will be the period to wait before turning
- * the vibrator on, the second value will be how long to vibrate at {@link #DEFAULT_AMPLITUDE}
- * strength, etc.
+ * <p>This method is equivalent to calling {@link #createWaveform(long[], int[], int)} with
+ * corresponding amplitude values alternating between 0 and {@link #DEFAULT_AMPLITUDE},
+ * beginning with 0.
*
* <p>To cause the pattern to repeat, pass the index into the timings array at which to start
* the repetition, or -1 to disable repeating. Repeating effects will be played indefinitely
* and should be cancelled via {@link Vibrator#cancel()}.
*
- * @param timings The pattern of alternating on-off timings, starting with off. Timing values
- * of 0 will cause the timing / amplitude pair to be ignored.
- * @param repeat The index into the timings array at which to repeat, or -1 if you you don't
- * want to repeat.
+ * @param timings The pattern of alternating on-off timings, starting with an 'off' timing, and
+ * representing the length of time to sustain the individual item (not
+ * cumulative).
+ * @param repeat The index into the timings array at which to repeat, or -1 if you don't
+ * want to repeat indefinitely.
*
* @return The desired effect.
*/
@@ -229,11 +229,10 @@ public abstract class VibrationEffect implements Parcelable {
/**
* Create a waveform vibration.
*
- * <p>Waveform vibrations are a potentially repeating series of timing and amplitude pairs. For
- * each pair, the value in the amplitude array determines the strength of the vibration and the
- * value in the timing array determines how long it vibrates for, in milliseconds. Amplitude
- * values must be between 0 and 255, and an amplitude of 0 implies no vibration (i.e. off). Any
- * pairs with a timing value of 0 will be ignored.
+ * <p>Waveform vibrations are a potentially repeating series of timing and amplitude pairs,
+ * provided in separate arrays. For each pair, the value in the amplitude array determines
+ * the strength of the vibration and the value in the timing array determines how long it
+ * vibrates for, in milliseconds.
*
* <p>To cause the pattern to repeat, pass the index into the timings array at which to start
* the repetition, or -1 to disable repeating. Repeating effects will be played indefinitely
@@ -244,8 +243,8 @@ public abstract class VibrationEffect implements Parcelable {
* @param amplitudes The amplitude values of the timing / amplitude pairs. Amplitude values
* must be between 0 and 255, or equal to {@link #DEFAULT_AMPLITUDE}. An
* amplitude value of 0 implies the motor is off.
- * @param repeat The index into the timings array at which to repeat, or -1 if you you don't
- * want to repeat.
+ * @param repeat The index into the timings array at which to repeat, or -1 if you don't
+ * want to repeat indefinitely.
*
* @return The desired effect.
*/
@@ -411,9 +410,9 @@ public abstract class VibrationEffect implements Parcelable {
*
* <p>The waveform will start the first transition from the vibrator off state, with the
* resonant frequency by default. To provide an initial state, use
- * {@link #startWaveform(VibrationParameter)}.
+ * {@link #startWaveform(VibrationEffect.VibrationParameter)}.
*
- * @return The {@link VibrationEffect.WaveformBuilder} started with the initial parameters.
+ * @see VibrationEffect.WaveformBuilder
*/
@NonNull
public static WaveformBuilder startWaveform() {
@@ -422,14 +421,16 @@ public abstract class VibrationEffect implements Parcelable {
/**
* Start building a waveform vibration with an initial state specified by a
- * {@link VibrationParameter}.
+ * {@link VibrationEffect.VibrationParameter}.
*
* <p>The waveform builder offers more flexibility for creating waveform vibrations, allowing
* control over vibration amplitude and frequency via smooth transitions between values.
*
- * @param initialParameter The initial {@link VibrationParameter} value to be applied at the
- * beginning of the vibration.
+ * @param initialParameter The initial {@link VibrationEffect.VibrationParameter} value to be
+ * applied at the beginning of the vibration.
* @return The {@link VibrationEffect.WaveformBuilder} started with the initial parameters.
+ *
+ * @see VibrationEffect.WaveformBuilder
*/
@NonNull
public static WaveformBuilder startWaveform(@NonNull VibrationParameter initialParameter) {
@@ -440,17 +441,19 @@ public abstract class VibrationEffect implements Parcelable {
/**
* Start building a waveform vibration with an initial state specified by two
- * {@link VibrationParameter VibrationParameters}.
+ * {@link VibrationEffect.VibrationParameter VibrationParameters}.
*
* <p>The waveform builder offers more flexibility for creating waveform vibrations, allowing
* control over vibration amplitude and frequency via smooth transitions between values.
*
- * @param initialParameter1 The initial {@link VibrationParameter} value to be applied at the
- * beginning of the vibration.
- * @param initialParameter2 The initial {@link VibrationParameter} value to be applied at the
- * beginning of the vibration, must be a different type of parameter
- * than the one specified by the first argument.
+ * @param initialParameter1 The initial {@link VibrationEffect.VibrationParameter} value to be
+ * applied at the beginning of the vibration.
+ * @param initialParameter2 The initial {@link VibrationEffect.VibrationParameter} value to be
+ * applied at the beginning of the vibration, must be a different type
+ * of parameter than the one specified by the first argument.
* @return The {@link VibrationEffect.WaveformBuilder} started with the initial parameters.
+ *
+ * @see VibrationEffect.WaveformBuilder
*/
@NonNull
public static WaveformBuilder startWaveform(@NonNull VibrationParameter initialParameter1,
@@ -806,7 +809,46 @@ public abstract class VibrationEffect implements Parcelable {
}
/**
- * A composition of haptic primitives that, when combined, create a single haptic effect.
+ * A composition of haptic elements that are combined to be playable as a single
+ * {@link VibrationEffect}.
+ *
+ * <p>The haptic primitives are available as {@code Composition.PRIMITIVE_*} constants and
+ * can be added to a composition to create a custom vibration effect. Here is an example of an
+ * effect that grows in intensity and then dies off, with a longer rising portion for emphasis
+ * and an extra tick 100ms after:
+ *
+ * <code>
+ * VibrationEffect effect = VibrationEffect.startComposition()
+ * .addPrimitive(VibrationEffect.Composition.PRIMITIVE_SLOW_RISE, 0.5f)
+ * .addPrimitive(VibrationEffect.Composition.PRIMITIVE_QUICK_FALL, 0.5f)
+ * .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1.0f, 100)
+ * .compose();
+ * </code>
+ *
+ * <p>Composition elements can also be {@link VibrationEffect} instances, including other
+ * compositions, and off durations, which are periods of time when the vibrator will be
+ * turned off. Here is an example of a composition that "warms up" with a light tap,
+ * a stronger double tap, then repeats a vibration pattern indefinitely:
+ *
+ * <code>
+ * VibrationEffect repeatingEffect = VibrationEffect.startComposition()
+ * .addPrimitive(VibrationEffect.Composition.PRIMITIVE_LOW_TICK)
+ * .addOffDuration(Duration.ofMillis(10))
+ * .addEffect(VibrationEffect.createPredefined(VibrationEffect.EFFECT_DOUBLE_CLICK))
+ * .addOffDuration(Duration.ofMillis(50))
+ * .addEffect(VibrationEffect.createWaveform(pattern, repeatIndex))
+ * .compose();
+ * </code>
+ *
+ * <p>When choosing to play a composed effect, you should check that individual components are
+ * supported by the device by using the appropriate vibrator method:
+ *
+ * <ul>
+ * <li>Primitive support can be checked using {@link Vibrator#arePrimitivesSupported}.
+ * <li>Effect support can be checked using {@link Vibrator#areEffectsSupported}.
+ * <li>Amplitude control for one-shot and waveforms with amplitude values can be checked
+ * using {@link Vibrator#hasAmplitudeControl}.
+ * </ul>
*
* @see VibrationEffect#startComposition()
*/
@@ -1092,16 +1134,77 @@ public abstract class VibrationEffect implements Parcelable {
* A builder for waveform haptic effects.
*
* <p>Waveform vibrations constitute of one or more timed transitions to new sets of vibration
- * parameters. These parameters can be the vibration amplitude or frequency, for example.
+ * parameters. These parameters can be the vibration amplitude, frequency, or both.
+ *
+ * <p>The following example ramps a vibrator turned off to full amplitude at 120Hz, over 100ms
+ * starting at 60Hz, then holds that state for 200ms and ramps back down again over 100ms:
+ *
+ * <code>
+ * import static android.os.VibrationEffect.VibrationParameter.targetAmplitude;
+ * import static android.os.VibrationEffect.VibrationParameter.targetFrequency;
+ *
+ * VibrationEffect effect = VibrationEffect.startWaveform(targetFrequency(60))
+ * .addTransition(Duration.ofMillis(100), targetAmplitude(1), targetFrequency(120))
+ * .addSustain(Duration.ofMillis(200))
+ * .addTransition(Duration.ofMillis(100), targetAmplitude(0), targetFrequency(60))
+ * .build();
+ * </code>
+ *
+ * <p>The initial state of the waveform can be set via
+ * {@link VibrationEffect#startWaveform(VibrationParameter)} or
+ * {@link VibrationEffect#startWaveform(VibrationParameter, VibrationParameter)}. If the initial
+ * parameters are not set then the {@link WaveformBuilder} will start with the vibrator off,
+ * represented by zero amplitude, at the vibrator's resonant frequency.
+ *
+ * <p>Repeating waveforms can be created by building the repeating block separately and adding
+ * it to the end of a composition with
+ * {@link Composition#repeatEffectIndefinitely(VibrationEffect)}:
*
* <p>Note that physical vibration actuators have different reaction times for changing
* amplitude and frequency. Durations specified here represent a timeline for the target
* parameters, and quality of effects may be improved if the durations allow time for a
* transition to be smoothly applied.
*
- * <p>Repeating waveforms can be built by constructing the repeating block separately and adding
- * it to the end of a composition using
- * {@link Composition#repeatEffectIndefinitely(VibrationEffect)}.
+ * <p>The following example illustrates both an initial state and a repeating section, using
+ * a {@link VibrationEffect.Composition}. The resulting effect will have a tick followed by a
+ * repeated beating effect with a rise that stretches out and a sharp finish.
+ *
+ * <code>
+ * VibrationEffect patternToBeRepeated = VibrationEffect.startWaveform(targetAmplitude(0.2f))
+ * .addSustain(Duration.ofMillis(10))
+ * .addTransition(Duration.ofMillis(20), targetAmplitude(0.4f))
+ * .addSustain(Duration.ofMillis(30))
+ * .addTransition(Duration.ofMillis(40), targetAmplitude(0.8f))
+ * .addSustain(Duration.ofMillis(50))
+ * .addTransition(Duration.ofMillis(60), targetAmplitude(0.2f))
+ * .build();
+ *
+ * VibrationEffect effect = VibrationEffect.startComposition()
+ * .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK)
+ * .addOffDuration(Duration.ofMillis(20))
+ * .repeatEffectIndefinitely(patternToBeRepeated)
+ * .compose();
+ * </code>
+ *
+ * <p>The amplitude step waveforms that can be created via
+ * {@link VibrationEffect#createWaveform(long[], int[], int)} can also be created with
+ * {@link WaveformBuilder} by adding zero duration transitions:
+ *
+ * <code>
+ * // These two effects are the same
+ * VibrationEffect waveform = VibrationEffect.createWaveform(
+ * new long[] { 10, 20, 30 }, // timings in milliseconds
+ * new int[] { 51, 102, 204 }, // amplitudes in [0,255]
+ * -1); // repeat index
+ *
+ * VibrationEffect sameWaveform = VibrationEffect.startWaveform(targetAmplitude(0.2f))
+ * .addSustain(Duration.ofMillis(10))
+ * .addTransition(Duration.ZERO, targetAmplitude(0.4f))
+ * .addSustain(Duration.ofMillis(20))
+ * .addTransition(Duration.ZERO, targetAmplitude(0.8f))
+ * .addSustain(Duration.ofMillis(30))
+ * .build();
+ * </code>
*
* @see VibrationEffect#startWaveform
*/
diff --git a/core/java/android/os/Vibrator.java b/core/java/android/os/Vibrator.java
index 23baa5d70c66..8f5086021530 100644
--- a/core/java/android/os/Vibrator.java
+++ b/core/java/android/os/Vibrator.java
@@ -98,7 +98,8 @@ public abstract class Vibrator {
/**
* Vibration effect support: unsupported
*
- * This effect is <b>not</b> supported by the underlying hardware.
+ * This effect is <b>not</b> natively supported by the underlying hardware, although
+ * the system may still play a fallback vibration.
*/
public static final int VIBRATION_EFFECT_SUPPORT_NO = 2;
@@ -485,20 +486,25 @@ public abstract class Vibrator {
String reason, @NonNull VibrationAttributes attributes);
/**
- * Query whether the vibrator supports the given effects.
+ * Query whether the vibrator natively supports the given effects.
*
- * Not all hardware reports its effect capabilities, so the system may not necessarily know
- * whether an effect is supported or not.
+ * <p>If an effect is not supported, the system may still automatically fall back to playing
+ * a simpler vibration instead, which is not optimised for the specific device. This includes
+ * the unknown case, which can't be determined in advance, that will dynamically attempt to
+ * fall back if the optimised effect fails to play.
*
- * The returned array will be the same length as the query array and the value at a given index
- * will contain {@link #VIBRATION_EFFECT_SUPPORT_YES} if the effect at that same index in the
- * querying array is supported, {@link #VIBRATION_EFFECT_SUPPORT_NO} if it isn't supported, or
- * {@link #VIBRATION_EFFECT_SUPPORT_UNKNOWN} if the system can't determine whether it's
- * supported or not.
+ * <p>The returned array will be the same length as the query array and the value at a given
+ * index will contain {@link #VIBRATION_EFFECT_SUPPORT_YES} if the effect at that same index
+ * in the querying array is supported, {@link #VIBRATION_EFFECT_SUPPORT_NO} if it isn't
+ * supported, or {@link #VIBRATION_EFFECT_SUPPORT_UNKNOWN} if the system can't determine whether
+ * it's supported or not, as some hardware doesn't report its effect capabilities.
+ *
+ * <p>Use {@link #areAllEffectsSupported(int...)} to get a single combined result,
+ * or for convenience when querying exactly one effect.
*
* @param effectIds Which effects to query for.
* @return An array containing the systems current knowledge about whether the given effects
- * are supported or not.
+ * are natively supported by the device, or not.
*/
@NonNull
@VibrationEffectSupport
@@ -515,23 +521,27 @@ public abstract class Vibrator {
/**
* Query whether the vibrator supports all of the given effects.
*
- * Not all hardware reports its effect capabilities, so the system may not necessarily know
- * whether an effect is supported or not.
+ * <p>If an effect is not supported, the system may still automatically fall back to a simpler
+ * vibration instead, which is not optimised for the specific device, however vibration isn't
+ * guaranteed in this case.
*
- * If the result is {@link #VIBRATION_EFFECT_SUPPORT_YES}, all effects in the query are
+ * <p>If the result is {@link #VIBRATION_EFFECT_SUPPORT_YES}, all effects in the query are
* supported by the hardware.
*
- * If the result is {@link #VIBRATION_EFFECT_SUPPORT_NO}, at least one of the effects in the
- * query is not supported.
+ * <p>If the result is {@link #VIBRATION_EFFECT_SUPPORT_NO}, at least one of the effects in the
+ * query is not supported, and using them may fall back to an un-optimized vibration or no
+ * vibration.
*
- * If the result is {@link #VIBRATION_EFFECT_SUPPORT_UNKNOWN}, the system doesn't know whether
- * all of the effects are supported. It may support any or all of the queried effects,
+ * <p>If the result is {@link #VIBRATION_EFFECT_SUPPORT_UNKNOWN}, the system doesn't know
+ * whether all of the effects are supported. It may support any or all of the queried effects,
* but there's no way to programmatically know whether a {@link #vibrate} call will successfully
* cause a vibration. It's guaranteed, however, that none of the queried effects are
* definitively unsupported by the hardware.
*
+ * <p>Use {@link #areEffectsSupported(int...)} to get individual results for each effect.
+ *
* @param effectIds Which effects to query for.
- * @return Whether all of the effects are supported.
+ * @return Whether all of the effects are natively supported by the device.
*/
@VibrationEffectSupport
public final int areAllEffectsSupported(
@@ -555,6 +565,12 @@ public abstract class Vibrator {
* will contain whether the effect at that same index in the querying array is supported or
* not.
*
+ * <p>If a primitive is not supported by the device, then <em>no vibration</em> will occur if
+ * it is played.
+ *
+ * <p>Use {@link #areAllPrimitivesSupported(int...)} to get a single combined result,
+ * or for convenience when querying exactly one primitive.
+ *
* @param primitiveIds Which primitives to query for.
* @return Whether the primitives are supported.
*/
@@ -572,8 +588,13 @@ public abstract class Vibrator {
/**
* Query whether the vibrator supports all of the given primitives.
*
+ * <p>If a primitive is not supported by the device, then <em>no vibration</em> will occur if
+ * it is played.
+ *
+ * <p>Use {@link #arePrimitivesSupported(int...)} to get individual results for each primitive.
+ *
* @param primitiveIds Which primitives to query for.
- * @return Whether primitives effects are supported.
+ * @return Whether all specified primitives are supported.
*/
public final boolean areAllPrimitivesSupported(
@NonNull @VibrationEffect.Composition.PrimitiveType int... primitiveIds) {
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 8df659de5924..63616da25982 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -291,6 +291,8 @@ public class StorageManager {
public static final int FLAG_INCLUDE_INVISIBLE = 1 << 10;
/** {@hide} */
public static final int FLAG_INCLUDE_RECENT = 1 << 11;
+ /** {@hide} */
+ public static final int FLAG_INCLUDE_SHARED_PROFILE = 1 << 12;
/** {@hide} */
public static final int FSTRIM_FLAG_DEEP = IVold.FSTRIM_FLAG_DEEP_TRIM;
@@ -1328,6 +1330,23 @@ public class StorageManager {
}
/**
+ * Return the list of shared/external storage volumes currently available to
+ * the calling user and the user it shares media with
+ * CDD link : https://source.android.com/compatibility/12/android-12-cdd#95_multi-user_support
+ * <p>
+ * This is similar to {@link StorageManager#getStorageVolumes()} except that the result also
+ * includes the volumes belonging to any user it shares media with
+ */
+ @RequiresPermission(android.Manifest.permission.MANAGE_EXTERNAL_STORAGE)
+ public @NonNull List<StorageVolume> getStorageVolumesIncludingSharedProfiles() {
+ final ArrayList<StorageVolume> res = new ArrayList<>();
+ Collections.addAll(res,
+ getVolumeList(mContext.getUserId(),
+ FLAG_REAL_STATE | FLAG_INCLUDE_INVISIBLE | FLAG_INCLUDE_SHARED_PROFILE));
+ return res;
+ }
+
+ /**
* Return the list of shared/external storage volumes both currently and
* recently available to the calling user.
* <p>
diff --git a/core/java/android/os/storage/StorageVolume.java b/core/java/android/os/storage/StorageVolume.java
index 8ee52c21e869..e1f112af41e7 100644
--- a/core/java/android/os/storage/StorageVolume.java
+++ b/core/java/android/os/storage/StorageVolume.java
@@ -16,8 +16,6 @@
package android.os.storage;
-import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
-
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
@@ -308,11 +306,9 @@ public final class StorageVolume implements Parcelable {
/**
* Returns the user that owns this volume
- *
- * {@hide}
*/
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
- @SystemApi(client = MODULE_LIBRARIES)
+ // TODO(b/193460475) : Android Lint handle API change from systemApi to public Api incorrectly
+ @SuppressLint("NewApi")
public @NonNull UserHandle getOwner() {
return mOwner;
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 2a563ac47294..ee6f9c033271 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -2386,6 +2386,15 @@ public final class Settings {
"android.settings.ENABLE_MMS_DATA_REQUEST";
/**
+ * Shows restrict settings dialog when settings is blocked.
+ *
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_SHOW_RESTRICTED_SETTING_DIALOG =
+ "android.settings.SHOW_RESTRICTED_SETTING_DIALOG";
+
+ /**
* Integer value that specifies the reason triggering enable MMS data notification.
* This must be passed as an extra field to the {@link #ACTION_ENABLE_MMS_DATA_REQUEST}.
* Extra with value of EnableMmsDataReason interface.
@@ -2427,6 +2436,23 @@ public final class Settings {
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
public static final String ACTION_MMS_MESSAGE_SETTING = "android.settings.MMS_MESSAGE_SETTING";
+ /**
+ * Activity Action: Show a screen of bedtime settings, which is provided by the wellbeing app.
+ * <p>
+ * The handler of this intent action may not exist.
+ * <p>
+ * To start an activity with this intent, apps should set the wellbeing package explicitly in
+ * the intent together with this action. The wellbeing package is defined in
+ * {@code com.android.internal.R.string.config_defaultWellbeingPackage}.
+ * <p>
+ * Output: Nothing
+ *
+ * @hide
+ */
+ @SystemApi
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_BEDTIME_SETTINGS = "android.settings.BEDTIME_SETTINGS";
+
// End of Intent actions for Settings
/**
@@ -6068,9 +6094,11 @@ public final class Settings {
}
/** @hide */
- @UnsupportedAppUsage
- public static String getStringForUser(ContentResolver resolver, String name,
- int userHandle) {
+ @SystemApi
+ @Nullable
+ @SuppressLint("VisiblySynchronized")
+ public static String getStringForUser(@NonNull ContentResolver resolver,
+ @NonNull String name, int userHandle) {
if (MOVED_TO_GLOBAL.contains(name)) {
Log.w(TAG, "Setting " + name + " has moved from android.provider.Settings.Secure"
+ " to android.provider.Settings.Global.");
@@ -6302,8 +6330,9 @@ public final class Settings {
}
/** @hide */
- @UnsupportedAppUsage
- public static int getIntForUser(ContentResolver cr, String name, int def, int userHandle) {
+ @SystemApi
+ public static int getIntForUser(@NonNull ContentResolver cr, @NonNull String name,
+ int def, int userHandle) {
String v = getStringForUser(cr, name, userHandle);
return parseIntSettingWithDefault(v, def);
}
@@ -6574,6 +6603,8 @@ public final class Settings {
* @hide
*/
@Readable(maxTargetSdk = Build.VERSION_CODES.S)
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ @SuppressLint("NoSettingsProvider")
public static final String BLUETOOTH_NAME = "bluetooth_name";
/**
@@ -6581,6 +6612,8 @@ public final class Settings {
* @hide
*/
@Readable(maxTargetSdk = Build.VERSION_CODES.S)
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ @SuppressLint("NoSettingsProvider")
public static final String BLUETOOTH_ADDRESS = "bluetooth_address";
/**
@@ -6588,6 +6621,8 @@ public final class Settings {
* @hide
*/
@Readable(maxTargetSdk = Build.VERSION_CODES.S)
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ @SuppressLint("NoSettingsProvider")
public static final String BLUETOOTH_ADDR_VALID = "bluetooth_addr_valid";
/**
@@ -10033,6 +10068,13 @@ public final class Settings {
public static final String QS_AUTO_ADDED_TILES = "qs_auto_tiles";
/**
+ * The duration of timeout, in milliseconds, to switch from a non-primary user to the
+ * primary user when the device is docked.
+ * @hide
+ */
+ public static final String TIMEOUT_TO_USER_ZERO = "timeout_to_user_zero";
+
+ /**
* Backup manager behavioral parameters.
* This is encoded as a key=value list, separated by commas. Ex:
*
@@ -10259,6 +10301,22 @@ public final class Settings {
public static final String NEARBY_SHARING_COMPONENT = "nearby_sharing_component";
/**
+ * Nearby Sharing Slice URI for the SliceProvider to
+ * read Nearby Sharing scan results and then draw the UI.
+ * @hide
+ */
+ public static final String NEARBY_SHARING_SLICE_URI = "nearby_sharing_slice_uri";
+
+ /**
+ * Current provider of Fast Pair saved devices page.
+ * Default value in @string/config_defaultNearbyFastPairSettingsDevicesComponent.
+ * No VALIDATOR as this setting will not be backed up.
+ * @hide
+ */
+ public static final String NEARBY_FAST_PAIR_SETTINGS_DEVICES_COMPONENT =
+ "nearby_fast_pair_settings_devices_component";
+
+ /**
* Controls whether aware is enabled.
* @hide
*/
@@ -10610,6 +10668,15 @@ public final class Settings {
public static final String FAST_PAIR_SCAN_ENABLED = "fast_pair_scan_enabled";
/**
+ * Setting to store denylisted system languages by the CEC {@code <Set Menu Language>}
+ * confirmation dialog.
+ *
+ * @hide
+ */
+ public static final String HDMI_CEC_SET_MENU_LANGUAGE_DENYLIST =
+ "hdmi_cec_set_menu_language_denylist";
+
+ /**
* These entries are considered common between the personal and the managed profile,
* since the managed profile doesn't get to change them.
*/
@@ -10826,6 +10893,8 @@ public final class Settings {
* @hide
*/
@Readable
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ @SuppressLint("NoSettingsProvider")
public static final String BLUETOOTH_CLASS_OF_DEVICE = "bluetooth_class_of_device";
/**
@@ -10834,6 +10903,8 @@ public final class Settings {
* {@hide}
*/
@Readable
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ @SuppressLint("NoSettingsProvider")
public static final String BLUETOOTH_DISABLED_PROFILES = "bluetooth_disabled_profiles";
/**
@@ -12371,6 +12442,8 @@ public final class Settings {
* @hide
*/
@Readable
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ @SuppressLint("NoSettingsProvider")
public static final String BLE_SCAN_ALWAYS_AVAILABLE = "ble_scan_always_enabled";
/**
@@ -12378,6 +12451,8 @@ public final class Settings {
* @hide
*/
@Readable
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ @SuppressLint("NoSettingsProvider")
public static final String BLE_SCAN_LOW_POWER_WINDOW_MS = "ble_scan_low_power_window_ms";
/**
@@ -12385,6 +12460,8 @@ public final class Settings {
* @hide
*/
@Readable
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ @SuppressLint("NoSettingsProvider")
public static final String BLE_SCAN_BALANCED_WINDOW_MS = "ble_scan_balanced_window_ms";
/**
@@ -12392,6 +12469,8 @@ public final class Settings {
* @hide
*/
@Readable
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ @SuppressLint("NoSettingsProvider")
public static final String BLE_SCAN_LOW_LATENCY_WINDOW_MS =
"ble_scan_low_latency_window_ms";
@@ -12400,6 +12479,8 @@ public final class Settings {
* @hide
*/
@Readable
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ @SuppressLint("NoSettingsProvider")
public static final String BLE_SCAN_LOW_POWER_INTERVAL_MS =
"ble_scan_low_power_interval_ms";
@@ -12408,6 +12489,8 @@ public final class Settings {
* @hide
*/
@Readable
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ @SuppressLint("NoSettingsProvider")
public static final String BLE_SCAN_BALANCED_INTERVAL_MS =
"ble_scan_balanced_interval_ms";
@@ -12416,6 +12499,8 @@ public final class Settings {
* @hide
*/
@Readable
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ @SuppressLint("NoSettingsProvider")
public static final String BLE_SCAN_LOW_LATENCY_INTERVAL_MS =
"ble_scan_low_latency_interval_ms";
@@ -12424,6 +12509,8 @@ public final class Settings {
* @hide
*/
@Readable
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ @SuppressLint("NoSettingsProvider")
public static final String BLE_SCAN_BACKGROUND_MODE = "ble_scan_background_mode";
/**
@@ -13156,6 +13243,8 @@ public final class Settings {
/** {@hide} */
@Readable
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ @SuppressLint("NoSettingsProvider")
public static final String
BLUETOOTH_BTSNOOP_DEFAULT_MODE = "bluetooth_btsnoop_default_mode";
/** {@hide} */
@@ -16706,6 +16795,30 @@ public final class Settings {
public static final String RESTRICTED_NETWORKING_MODE = "restricted_networking_mode";
/**
+ * Setting indicating whether Low Power Standby is enabled, if supported.
+ *
+ * Values are:
+ * 0: disabled
+ * 1: enabled
+ *
+ * @hide
+ */
+ public static final String LOW_POWER_STANDBY_ENABLED = "low_power_standby_enabled";
+
+ /**
+ * Setting indicating whether Low Power Standby is allowed to be active during doze
+ * maintenance mode.
+ *
+ * Values are:
+ * 0: Low Power Standby will be disabled during doze maintenance mode
+ * 1: Low Power Standby can be active during doze maintenance mode
+ *
+ * @hide
+ */
+ public static final String LOW_POWER_STANDBY_ACTIVE_DURING_MAINTENANCE =
+ "low_power_standby_active_during_maintenance";
+
+ /**
* Settings migrated from Wear OS settings provider.
* @hide
*/
diff --git a/core/java/android/service/autofill/AutofillService.java b/core/java/android/service/autofill/AutofillService.java
index 29c7796d8660..cb1b5d3d20b8 100644
--- a/core/java/android/service/autofill/AutofillService.java
+++ b/core/java/android/service/autofill/AutofillService.java
@@ -578,6 +578,14 @@ public abstract class AutofillService extends Service {
public static final String SERVICE_META_DATA = "android.autofill";
/**
+ * Name of the {@link FillResponse} extra used to return a delayed fill response.
+ *
+ * <p>Please see {@link FillRequest#getDelayedFillIntentSender()} on how to send a delayed
+ * fill response to framework.</p>
+ */
+ public static final String EXTRA_FILL_RESPONSE = "android.service.autofill.extra.FILL_RESPONSE";
+
+ /**
* Name of the {@link IResultReceiver} extra used to return the primary result of a request.
*
* @hide
diff --git a/core/java/android/service/autofill/Dataset.java b/core/java/android/service/autofill/Dataset.java
index 86341a908ad7..cfb690916ecc 100644
--- a/core/java/android/service/autofill/Dataset.java
+++ b/core/java/android/service/autofill/Dataset.java
@@ -65,6 +65,16 @@ import java.util.regex.Pattern;
* can be shown by the keyboard as a suggestion. To use this feature, the Dataset should contain
* an {@link InlinePresentation} representing how the inline suggestion UI will be rendered.
*
+ * <a name="FillDialogUI"></a>
+ * <h3>Fill Dialog UI</h3>
+ *
+ * <p>The fill dialog UI is a more conspicuous and efficient interface than dropdown UI. If autofill
+ * suggestions are available when the user clicks on a field that supports filling the dialog UI,
+ * Autofill will pop up a fill dialog. The dialog will take up a larger area to display the
+ * datasets, so it is easy for users to pay attention to the datasets and selecting a dataset.
+ * If the user focuses on the view before suggestions are available, will fall back to dropdown UI
+ * or inline suggestions.
+ *
* <a name="Authentication"></a>
* <h3>Dataset authentication</h3>
*
@@ -92,10 +102,9 @@ import java.util.regex.Pattern;
* <ol>
* <li>If the view's {@link android.view.View#getAutofillValue() autofill value} is not
* {@link AutofillValue#isText() text} or is empty, all datasets are shown.
- * <li>Datasets that have a filter regex (set through
- * {@link Dataset.Builder#setValue(AutofillId, AutofillValue, Pattern)} or
- * {@link Dataset.Builder#setValue(AutofillId, AutofillValue, Pattern, RemoteViews)}) and whose
- * regex matches the view's text value converted to lower case are shown.
+ * <li>Datasets that have a filter regex (set through {@link Field.Builder#setFilter(Pattern)}
+ * and {@link Dataset.Builder#setField(AutofillId, Field)}) and whose regex matches the view's
+ * text value converted to lower case are shown.
* <li>Datasets that do not require authentication, have a field value that is
* {@link AutofillValue#isText() text} and whose {@link AutofillValue#getTextValue() value} starts
* with the lower case value of the view's text are shown.
@@ -107,11 +116,13 @@ public final class Dataset implements Parcelable {
private final ArrayList<AutofillId> mFieldIds;
private final ArrayList<AutofillValue> mFieldValues;
private final ArrayList<RemoteViews> mFieldPresentations;
+ private final ArrayList<RemoteViews> mFieldDialogPresentations;
private final ArrayList<InlinePresentation> mFieldInlinePresentations;
private final ArrayList<InlinePresentation> mFieldInlineTooltipPresentations;
private final ArrayList<DatasetFieldFilter> mFieldFilters;
@Nullable private final ClipData mFieldContent;
private final RemoteViews mPresentation;
+ private final RemoteViews mDialogPresentation;
@Nullable private final InlinePresentation mInlinePresentation;
@Nullable private final InlinePresentation mInlineTooltipPresentation;
private final IntentSender mAuthentication;
@@ -121,11 +132,13 @@ public final class Dataset implements Parcelable {
mFieldIds = builder.mFieldIds;
mFieldValues = builder.mFieldValues;
mFieldPresentations = builder.mFieldPresentations;
+ mFieldDialogPresentations = builder.mFieldDialogPresentations;
mFieldInlinePresentations = builder.mFieldInlinePresentations;
mFieldInlineTooltipPresentations = builder.mFieldInlineTooltipPresentations;
mFieldFilters = builder.mFieldFilters;
mFieldContent = builder.mFieldContent;
mPresentation = builder.mPresentation;
+ mDialogPresentation = builder.mDialogPresentation;
mInlinePresentation = builder.mInlinePresentation;
mInlineTooltipPresentation = builder.mInlineTooltipPresentation;
mAuthentication = builder.mAuthentication;
@@ -153,6 +166,12 @@ public final class Dataset implements Parcelable {
}
/** @hide */
+ public RemoteViews getFieldDialogPresentation(int index) {
+ final RemoteViews customPresentation = mFieldDialogPresentations.get(index);
+ return customPresentation != null ? customPresentation : mDialogPresentation;
+ }
+
+ /** @hide */
public @Nullable InlinePresentation getFieldInlinePresentation(int index) {
final InlinePresentation inlinePresentation = mFieldInlinePresentations.get(index);
return inlinePresentation != null ? inlinePresentation : mInlinePresentation;
@@ -219,6 +238,9 @@ public final class Dataset implements Parcelable {
if (mFieldPresentations != null) {
builder.append(", fieldPresentations=").append(mFieldPresentations.size());
}
+ if (mFieldDialogPresentations != null) {
+ builder.append(", fieldDialogPresentations=").append(mFieldDialogPresentations.size());
+ }
if (mFieldInlinePresentations != null) {
builder.append(", fieldInlinePresentations=").append(mFieldInlinePresentations.size());
}
@@ -232,6 +254,9 @@ public final class Dataset implements Parcelable {
if (mPresentation != null) {
builder.append(", hasPresentation");
}
+ if (mDialogPresentation != null) {
+ builder.append(", hasDialogPresentation");
+ }
if (mInlinePresentation != null) {
builder.append(", hasInlinePresentation");
}
@@ -264,11 +289,13 @@ public final class Dataset implements Parcelable {
private ArrayList<AutofillId> mFieldIds;
private ArrayList<AutofillValue> mFieldValues;
private ArrayList<RemoteViews> mFieldPresentations;
+ private ArrayList<RemoteViews> mFieldDialogPresentations;
private ArrayList<InlinePresentation> mFieldInlinePresentations;
private ArrayList<InlinePresentation> mFieldInlineTooltipPresentations;
private ArrayList<DatasetFieldFilter> mFieldFilters;
@Nullable private ClipData mFieldContent;
private RemoteViews mPresentation;
+ private RemoteViews mDialogPresentation;
@Nullable private InlinePresentation mInlinePresentation;
@Nullable private InlinePresentation mInlineTooltipPresentation;
private IntentSender mAuthentication;
@@ -279,7 +306,9 @@ public final class Dataset implements Parcelable {
* Creates a new builder.
*
* @param presentation The presentation used to visualize this dataset.
+ * @deprecated Use {@link #Builder(Presentations)} instead.
*/
+ @Deprecated
public Builder(@NonNull RemoteViews presentation) {
Objects.requireNonNull(presentation, "presentation must be non-null");
mPresentation = presentation;
@@ -294,19 +323,34 @@ public final class Dataset implements Parcelable {
* as inline suggestions. If the dataset supports inline suggestions,
* this should not be null.
* @hide
+ * @deprecated Use {@link #Builder(Presentations)} instead.
*/
@SystemApi
+ @Deprecated
public Builder(@NonNull InlinePresentation inlinePresentation) {
Objects.requireNonNull(inlinePresentation, "inlinePresentation must be non-null");
mInlinePresentation = inlinePresentation;
}
/**
+ * Creates a new builder.
+ *
+ * @param presentations The presentations used to visualize this dataset.
+ */
+ public Builder(@NonNull Presentations presentations) {
+ Objects.requireNonNull(presentations, "presentations must be non-null");
+
+ mPresentation = presentations.getMenuPresentation();
+ mInlinePresentation = presentations.getInlinePresentation();
+ mInlineTooltipPresentation = presentations.getInlineTooltipPresentation();
+ mDialogPresentation = presentations.getDialogPresentation();
+ }
+
+ /**
* Creates a new builder for a dataset where each field will be visualized independently.
*
- * <p>When using this constructor, fields must be set through
- * {@link #setValue(AutofillId, AutofillValue, RemoteViews)} or
- * {@link #setValue(AutofillId, AutofillValue, Pattern, RemoteViews)}.
+ * <p>When using this constructor, a presentation must be provided for each field through
+ * {@link #setField(AutofillId, Field)}.
*/
public Builder() {
}
@@ -318,7 +362,9 @@ public final class Dataset implements Parcelable {
* @throws IllegalStateException if {@link #build()} was already called.
*
* @return this builder.
+ * @deprecated Use {@link #Builder(Presentations)} instead.
*/
+ @Deprecated
public @NonNull Builder setInlinePresentation(
@NonNull InlinePresentation inlinePresentation) {
throwIfDestroyed();
@@ -339,7 +385,9 @@ public final class Dataset implements Parcelable {
* @throws IllegalStateException if {@link #build()} was already called.
*
* @return this builder.
+ * @deprecated Use {@link #Builder(Presentations)} instead.
*/
+ @Deprecated
public @NonNull Builder setInlinePresentation(
@NonNull InlinePresentation inlinePresentation,
@NonNull InlinePresentation inlineTooltipPresentation) {
@@ -479,7 +527,7 @@ public final class Dataset implements Parcelable {
"Content items cannot contain an Intent: content=" + content);
}
}
- setLifeTheUniverseAndEverything(id, null, null, null, null);
+ setLifeTheUniverseAndEverything(id, null, null, null, null, null, null);
mFieldContent = content;
return this;
}
@@ -509,10 +557,12 @@ public final class Dataset implements Parcelable {
* @throws IllegalStateException if {@link #build()} was already called.
*
* @return this builder.
+ * @deprecated Use {@link #setField(AutofillId, Field)} instead.
*/
+ @Deprecated
public @NonNull Builder setValue(@NonNull AutofillId id, @Nullable AutofillValue value) {
throwIfDestroyed();
- setLifeTheUniverseAndEverything(id, value, null, null, null);
+ setLifeTheUniverseAndEverything(id, value, null, null, null, null, null);
return this;
}
@@ -537,12 +587,14 @@ public final class Dataset implements Parcelable {
* @throws IllegalStateException if {@link #build()} was already called.
*
* @return this builder.
+ * @deprecated Use {@link #setField(AutofillId, Field)} instead.
*/
+ @Deprecated
public @NonNull Builder setValue(@NonNull AutofillId id, @Nullable AutofillValue value,
@NonNull RemoteViews presentation) {
throwIfDestroyed();
Objects.requireNonNull(presentation, "presentation cannot be null");
- setLifeTheUniverseAndEverything(id, value, presentation, null, null);
+ setLifeTheUniverseAndEverything(id, value, presentation, null, null, null, null);
return this;
}
@@ -572,13 +624,16 @@ public final class Dataset implements Parcelable {
* @return this builder.
* @throws IllegalStateException if the builder was constructed without a
* {@link RemoteViews presentation} or {@link #build()} was already called.
+ * @deprecated Use {@link #setField(AutofillId, Field)} instead.
*/
+ @Deprecated
public @NonNull Builder setValue(@NonNull AutofillId id, @Nullable AutofillValue value,
@Nullable Pattern filter) {
throwIfDestroyed();
Preconditions.checkState(mPresentation != null,
"Dataset presentation not set on constructor");
- setLifeTheUniverseAndEverything(id, value, null, null, new DatasetFieldFilter(filter));
+ setLifeTheUniverseAndEverything(
+ id, value, null, null, null, new DatasetFieldFilter(filter), null);
return this;
}
@@ -610,13 +665,15 @@ public final class Dataset implements Parcelable {
* @throws IllegalStateException if {@link #build()} was already called.
*
* @return this builder.
+ * @deprecated Use {@link #setField(AutofillId, Field)} instead.
*/
+ @Deprecated
public @NonNull Builder setValue(@NonNull AutofillId id, @Nullable AutofillValue value,
@Nullable Pattern filter, @NonNull RemoteViews presentation) {
throwIfDestroyed();
Objects.requireNonNull(presentation, "presentation cannot be null");
- setLifeTheUniverseAndEverything(id, value, presentation, null,
- new DatasetFieldFilter(filter));
+ setLifeTheUniverseAndEverything(id, value, presentation, null, null,
+ new DatasetFieldFilter(filter), null);
return this;
}
@@ -641,13 +698,16 @@ public final class Dataset implements Parcelable {
* @throws IllegalStateException if {@link #build()} was already called.
*
* @return this builder.
+ * @deprecated Use {@link #setField(AutofillId, Field)} instead.
*/
+ @Deprecated
public @NonNull Builder setValue(@NonNull AutofillId id, @Nullable AutofillValue value,
@NonNull RemoteViews presentation, @NonNull InlinePresentation inlinePresentation) {
throwIfDestroyed();
Objects.requireNonNull(presentation, "presentation cannot be null");
Objects.requireNonNull(inlinePresentation, "inlinePresentation cannot be null");
- setLifeTheUniverseAndEverything(id, value, presentation, inlinePresentation, null);
+ setLifeTheUniverseAndEverything(
+ id, value, presentation, inlinePresentation, null, null, null);
return this;
}
@@ -672,7 +732,9 @@ public final class Dataset implements Parcelable {
* @throws IllegalStateException if {@link #build()} was already called.
*
* @return this builder.
+ * @deprecated Use {@link #setField(AutofillId, Field)} instead.
*/
+ @Deprecated
public @NonNull Builder setValue(@NonNull AutofillId id, @Nullable AutofillValue value,
@NonNull RemoteViews presentation, @NonNull InlinePresentation inlinePresentation,
@NonNull InlinePresentation inlineTooltipPresentation) {
@@ -682,7 +744,7 @@ public final class Dataset implements Parcelable {
Objects.requireNonNull(inlineTooltipPresentation,
"inlineTooltipPresentation cannot be null");
setLifeTheUniverseAndEverything(id, value, presentation, inlinePresentation,
- inlineTooltipPresentation, null);
+ inlineTooltipPresentation, null, null);
return this;
}
@@ -718,15 +780,17 @@ public final class Dataset implements Parcelable {
* @throws IllegalStateException if {@link #build()} was already called.
*
* @return this builder.
+ * @deprecated Use {@link #setField(AutofillId, Field)} instead.
*/
+ @Deprecated
public @NonNull Builder setValue(@NonNull AutofillId id, @Nullable AutofillValue value,
@Nullable Pattern filter, @NonNull RemoteViews presentation,
@NonNull InlinePresentation inlinePresentation) {
throwIfDestroyed();
Objects.requireNonNull(presentation, "presentation cannot be null");
Objects.requireNonNull(inlinePresentation, "inlinePresentation cannot be null");
- setLifeTheUniverseAndEverything(id, value, presentation, inlinePresentation,
- new DatasetFieldFilter(filter));
+ setLifeTheUniverseAndEverything(id, value, presentation, inlinePresentation, null,
+ new DatasetFieldFilter(filter), null);
return this;
}
@@ -756,7 +820,9 @@ public final class Dataset implements Parcelable {
* @throws IllegalStateException if {@link #build()} was already called.
*
* @return this builder.
+ * @deprecated Use {@link #setField(AutofillId, Field)} instead.
*/
+ @Deprecated
public @NonNull Builder setValue(@NonNull AutofillId id, @Nullable AutofillValue value,
@Nullable Pattern filter, @NonNull RemoteViews presentation,
@NonNull InlinePresentation inlinePresentation,
@@ -767,7 +833,91 @@ public final class Dataset implements Parcelable {
Objects.requireNonNull(inlineTooltipPresentation,
"inlineTooltipPresentation cannot be null");
setLifeTheUniverseAndEverything(id, value, presentation, inlinePresentation,
- inlineTooltipPresentation, new DatasetFieldFilter(filter));
+ inlineTooltipPresentation, new DatasetFieldFilter(filter), null);
+ return this;
+ }
+
+ /**
+ * Sets the value of a field.
+ *
+ * Before Android 13, this information could be provided using several overloaded
+ * setValue(...) methods. This method replaces those with a Builder pattern.
+ * For example, in the old workflow, the app sets a field would be:
+ * <pre class="prettyprint">
+ * Dataset.Builder dataset = new Dataset.Builder();
+ * if (filter != null) {
+ * if (presentation != null) {
+ * if (inlinePresentation != null) {
+ * dataset.setValue(id, value, filter, presentation, inlinePresentation)
+ * } else {
+ * dataset.setValue(id, value, filter, presentation);
+ * }
+ * } else {
+ * dataset.setValue(id, value, filter);
+ * }
+ * } else {
+ * if (presentation != null) {
+ * if (inlinePresentation != null) {
+ * dataset.setValue(id, value, presentation, inlinePresentation)
+ * } else {
+ * dataset.setValue(id, value, presentation);
+ * }
+ * } else {
+ * dataset.setValue(id, value);
+ * }
+ * }
+ * </pre>
+ * <p>The new workflow would be:
+ * <pre class="prettyprint">
+ * Field.Builder fieldBuilder = new Field.Builder();
+ * if (value != null) {
+ * fieldBuilder.setValue(value);
+ * }
+ * if (filter != null) {
+ * fieldBuilder.setFilter(filter);
+ * }
+ * Presentations.Builder presentationsBuilder = new Presentations.Builder(id);
+ * if (presentation != null) {
+ * presentationsBuilder.setMenuPresentation(presentation);
+ * }
+ * if (inlinePresentation != null) {
+ * presentationsBuilder.setInlinePresentation(inlinePresentation);
+ * }
+ * if (dialogPresentation != null) {
+ * presentationsBuilder.setDialogPresentation(dialogPresentation);
+ * }
+ * fieldBuilder.setPresentations(presentationsBuilder.build());
+ * dataset.setField(id, fieldBuilder.build());
+ * </pre>
+ *
+ * @see Field
+ *
+ * @param id id returned by {@link
+ * android.app.assist.AssistStructure.ViewNode#getAutofillId()}.
+ * @param field the fill information about the field.
+ *
+ * @throws IllegalStateException if {@link #build()} was already called.
+ *
+ * @return this builder.
+ */
+ public @NonNull Builder setField(@NonNull AutofillId id, @Nullable Field field) {
+ throwIfDestroyed();
+ if (field == null) {
+ setLifeTheUniverseAndEverything(id, null, null, null, null, null, null);
+ } else {
+ final DatasetFieldFilter filter = field.getFilter();
+ final Presentations presentations = field.getPresentations();
+ if (presentations == null) {
+ setLifeTheUniverseAndEverything(id, field.getValue(), null, null, null,
+ filter, null);
+ } else {
+ setLifeTheUniverseAndEverything(id, field.getValue(),
+ presentations.getMenuPresentation(),
+ presentations.getInlinePresentation(),
+ presentations.getInlineTooltipPresentation(), filter,
+ presentations.getDialogPresentation());
+ }
+ }
return this;
}
@@ -793,39 +943,34 @@ public final class Dataset implements Parcelable {
* @throws IllegalStateException if {@link #build()} was already called.
*
* @return this builder.
- *
+ * @deprecated Use {@link #setField(AutofillId, Field)} instead.
* @hide
*/
+ @Deprecated
@SystemApi
public @NonNull Builder setFieldInlinePresentation(@NonNull AutofillId id,
@Nullable AutofillValue value, @Nullable Pattern filter,
@NonNull InlinePresentation inlinePresentation) {
throwIfDestroyed();
Objects.requireNonNull(inlinePresentation, "inlinePresentation cannot be null");
- setLifeTheUniverseAndEverything(id, value, null, inlinePresentation,
- new DatasetFieldFilter(filter));
+ setLifeTheUniverseAndEverything(id, value, null, inlinePresentation, null,
+ new DatasetFieldFilter(filter), null);
return this;
}
private void setLifeTheUniverseAndEverything(@NonNull AutofillId id,
@Nullable AutofillValue value, @Nullable RemoteViews presentation,
@Nullable InlinePresentation inlinePresentation,
- @Nullable DatasetFieldFilter filter) {
- setLifeTheUniverseAndEverything(id, value, presentation, inlinePresentation, null,
- filter);
- }
-
- private void setLifeTheUniverseAndEverything(@NonNull AutofillId id,
- @Nullable AutofillValue value, @Nullable RemoteViews presentation,
- @Nullable InlinePresentation inlinePresentation,
@Nullable InlinePresentation tooltip,
- @Nullable DatasetFieldFilter filter) {
+ @Nullable DatasetFieldFilter filter,
+ @Nullable RemoteViews dialogPresentation) {
Objects.requireNonNull(id, "id cannot be null");
if (mFieldIds != null) {
final int existingIdx = mFieldIds.indexOf(id);
if (existingIdx >= 0) {
mFieldValues.set(existingIdx, value);
mFieldPresentations.set(existingIdx, presentation);
+ mFieldDialogPresentations.set(existingIdx, dialogPresentation);
mFieldInlinePresentations.set(existingIdx, inlinePresentation);
mFieldInlineTooltipPresentations.set(existingIdx, tooltip);
mFieldFilters.set(existingIdx, filter);
@@ -835,6 +980,7 @@ public final class Dataset implements Parcelable {
mFieldIds = new ArrayList<>();
mFieldValues = new ArrayList<>();
mFieldPresentations = new ArrayList<>();
+ mFieldDialogPresentations = new ArrayList<>();
mFieldInlinePresentations = new ArrayList<>();
mFieldInlineTooltipPresentations = new ArrayList<>();
mFieldFilters = new ArrayList<>();
@@ -842,6 +988,7 @@ public final class Dataset implements Parcelable {
mFieldIds.add(id);
mFieldValues.add(value);
mFieldPresentations.add(presentation);
+ mFieldDialogPresentations.add(dialogPresentation);
mFieldInlinePresentations.add(inlinePresentation);
mFieldInlineTooltipPresentations.add(tooltip);
mFieldFilters.add(filter);
@@ -853,10 +1000,7 @@ public final class Dataset implements Parcelable {
* <p>You should not interact with this builder once this method is called.
*
* @throws IllegalStateException if no field was set (through
- * {@link #setValue(AutofillId, AutofillValue)} or
- * {@link #setValue(AutofillId, AutofillValue, RemoteViews)} or
- * {@link #setValue(AutofillId, AutofillValue, RemoteViews, InlinePresentation)}),
- * or if {@link #build()} was already called.
+ * {@link #setField(AutofillId, Field)}), or if {@link #build()} was already called.
*
* @return The built dataset.
*/
@@ -897,11 +1041,13 @@ public final class Dataset implements Parcelable {
@Override
public void writeToParcel(Parcel parcel, int flags) {
parcel.writeParcelable(mPresentation, flags);
+ parcel.writeParcelable(mDialogPresentation, flags);
parcel.writeParcelable(mInlinePresentation, flags);
parcel.writeParcelable(mInlineTooltipPresentation, flags);
parcel.writeTypedList(mFieldIds, flags);
parcel.writeTypedList(mFieldValues, flags);
parcel.writeTypedList(mFieldPresentations, flags);
+ parcel.writeTypedList(mFieldDialogPresentations, flags);
parcel.writeTypedList(mFieldInlinePresentations, flags);
parcel.writeTypedList(mFieldInlineTooltipPresentations, flags);
parcel.writeTypedList(mFieldFilters, flags);
@@ -913,8 +1059,12 @@ public final class Dataset implements Parcelable {
public static final @NonNull Creator<Dataset> CREATOR = new Creator<Dataset>() {
@Override
public Dataset createFromParcel(Parcel parcel) {
- final RemoteViews presentation = parcel.readParcelable(null, android.widget.RemoteViews.class);
- final InlinePresentation inlinePresentation = parcel.readParcelable(null, android.service.autofill.InlinePresentation.class);
+ final RemoteViews presentation = parcel.readParcelable(null,
+ android.widget.RemoteViews.class);
+ final RemoteViews dialogPresentation = parcel.readParcelable(null,
+ android.widget.RemoteViews.class);
+ final InlinePresentation inlinePresentation = parcel.readParcelable(null,
+ android.service.autofill.InlinePresentation.class);
final InlinePresentation inlineTooltipPresentation =
parcel.readParcelable(null, android.service.autofill.InlinePresentation.class);
final ArrayList<AutofillId> ids =
@@ -923,27 +1073,41 @@ public final class Dataset implements Parcelable {
parcel.createTypedArrayList(AutofillValue.CREATOR);
final ArrayList<RemoteViews> presentations =
parcel.createTypedArrayList(RemoteViews.CREATOR);
+ final ArrayList<RemoteViews> dialogPresentations =
+ parcel.createTypedArrayList(RemoteViews.CREATOR);
final ArrayList<InlinePresentation> inlinePresentations =
parcel.createTypedArrayList(InlinePresentation.CREATOR);
final ArrayList<InlinePresentation> inlineTooltipPresentations =
parcel.createTypedArrayList(InlinePresentation.CREATOR);
final ArrayList<DatasetFieldFilter> filters =
parcel.createTypedArrayList(DatasetFieldFilter.CREATOR);
- final ClipData fieldContent = parcel.readParcelable(null, android.content.ClipData.class);
- final IntentSender authentication = parcel.readParcelable(null, android.content.IntentSender.class);
+ final ClipData fieldContent = parcel.readParcelable(null,
+ android.content.ClipData.class);
+ final IntentSender authentication = parcel.readParcelable(null,
+ android.content.IntentSender.class);
final String datasetId = parcel.readString();
// Always go through the builder to ensure the data ingested by
// the system obeys the contract of the builder to avoid attacks
// using specially crafted parcels.
- final Builder builder = (presentation != null) ? new Builder(presentation)
- : new Builder();
- if (inlinePresentation != null) {
+ final Builder builder;
+ if (presentation != null || inlinePresentation != null || dialogPresentation != null) {
+ final Presentations.Builder presentationsBuilder = new Presentations.Builder();
+ if (presentation != null) {
+ presentationsBuilder.setMenuPresentation(presentation);
+ }
+ if (inlinePresentation != null) {
+ presentationsBuilder.setInlinePresentation(inlinePresentation);
+ }
if (inlineTooltipPresentation != null) {
- builder.setInlinePresentation(inlinePresentation, inlineTooltipPresentation);
- } else {
- builder.setInlinePresentation(inlinePresentation);
+ presentationsBuilder.setInlineTooltipPresentation(inlineTooltipPresentation);
+ }
+ if (dialogPresentation != null) {
+ presentationsBuilder.setDialogPresentation(dialogPresentation);
}
+ builder = new Builder(presentationsBuilder.build());
+ } else {
+ builder = new Builder();
}
if (fieldContent != null) {
@@ -954,13 +1118,15 @@ public final class Dataset implements Parcelable {
final AutofillId id = ids.get(i);
final AutofillValue value = values.get(i);
final RemoteViews fieldPresentation = presentations.get(i);
+ final RemoteViews fieldDialogPresentation = dialogPresentations.get(i);
final InlinePresentation fieldInlinePresentation =
i < inlinePresentationsSize ? inlinePresentations.get(i) : null;
final InlinePresentation fieldInlineTooltipPresentation =
i < inlinePresentationsSize ? inlineTooltipPresentations.get(i) : null;
final DatasetFieldFilter filter = filters.get(i);
builder.setLifeTheUniverseAndEverything(id, value, fieldPresentation,
- fieldInlinePresentation, fieldInlineTooltipPresentation, filter);
+ fieldInlinePresentation, fieldInlineTooltipPresentation, filter,
+ fieldDialogPresentation);
}
builder.setAuthentication(authentication);
builder.setId(datasetId);
@@ -986,7 +1152,7 @@ public final class Dataset implements Parcelable {
@Nullable
public final Pattern pattern;
- private DatasetFieldFilter(@Nullable Pattern pattern) {
+ DatasetFieldFilter(@Nullable Pattern pattern) {
this.pattern = pattern;
}
diff --git a/core/java/android/service/autofill/Field.java b/core/java/android/service/autofill/Field.java
new file mode 100644
index 000000000000..b7c0d82f41f5
--- /dev/null
+++ b/core/java/android/service/autofill/Field.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.autofill;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.view.autofill.AutofillId;
+import android.view.autofill.AutofillValue;
+
+import com.android.internal.util.DataClass;
+
+import java.util.regex.Pattern;
+
+/**
+ * This class is used to set all information of a field. Such as the
+ * {@link AutofillId} of the field, the {@link AutofillValue} to be autofilled,
+ * a <a href="#Filtering">explicit filter</a>, and presentations to be visualized,
+ * etc.
+ */
+public final class Field {
+
+ /**
+ * The value to be autofilled. Pass {@code null} if you do not have the value
+ * but the target view is a logical part of the dataset. For example, if the
+ * dataset needs authentication and you have no access to the value.
+ */
+ private @Nullable AutofillValue mValue;
+
+ /**
+ * Regex used to determine if the dataset should be shown in the autofill UI;
+ * when {@code null}, it disables filtering on that dataset (this is the recommended
+ * approach when {@code value} is not {@code null} and field contains sensitive data
+ * such as passwords).
+ *
+ * @see Dataset.DatasetFieldFilter
+ * @hide
+ */
+ private @Nullable Dataset.DatasetFieldFilter mFilter;
+
+ /**
+ * The presentations used to visualize this field in Autofill UI.
+ */
+ private @Nullable Presentations mPresentations;
+
+
+ /* package-private */ Field(
+ @Nullable AutofillValue value,
+ @Nullable Dataset.DatasetFieldFilter filter,
+ @Nullable Presentations presentations) {
+ this.mValue = value;
+ this.mFilter = filter;
+ this.mPresentations = presentations;
+ }
+
+ /**
+ * The value to be autofilled. Pass {@code null} if you do not have the value
+ * but the target view is a logical part of the dataset. For example, if the
+ * dataset needs authentication and you have no access to the value.
+ */
+ @DataClass.Generated.Member
+ public @Nullable AutofillValue getValue() {
+ return mValue;
+ }
+
+ /**
+ * Regex used to determine if the dataset should be shown in the autofill UI;
+ * when {@code null}, it disables filtering on that dataset (this is the recommended
+ * approach when {@code value} is not {@code null} and field contains sensitive data
+ * such as passwords).
+ *
+ * @see Dataset.DatasetFieldFilter
+ * @hide
+ */
+ public @Nullable Dataset.DatasetFieldFilter getFilter() {
+ return mFilter;
+ }
+
+ /**
+ * The presentations used to visualize this field in Autofill UI.
+ */
+ public @Nullable Presentations getPresentations() {
+ return mPresentations;
+ }
+
+ /**
+ * A builder for {@link Field}
+ */
+ public static final class Builder {
+
+ private @Nullable AutofillValue mValue = null;
+ private @Nullable Dataset.DatasetFieldFilter mFilter = null;
+ private @Nullable Presentations mPresentations = null;
+ private boolean mDestroyed = false;
+
+ public Builder() {
+ }
+
+ /**
+ * The value to be autofilled. Pass {@code null} if you do not have the value
+ * but the target view is a logical part of the dataset. For example, if the
+ * dataset needs authentication and you have no access to the value.
+ */
+ public @NonNull Builder setValue(@NonNull AutofillValue value) {
+ checkNotUsed();
+ mValue = value;
+ return this;
+ }
+
+ /**
+ * Regex used to determine if the dataset should be shown in the autofill UI;
+ * when {@code null}, it disables filtering on that dataset (this is the recommended
+ * approach when {@code value} is not {@code null} and field contains sensitive data
+ * such as passwords).
+ */
+ @SuppressLint("MissingGetterMatchingBuilder")
+ public @NonNull Builder setFilter(@Nullable Pattern value) {
+ checkNotUsed();
+ mFilter = new Dataset.DatasetFieldFilter(value);
+ return this;
+ }
+
+ /**
+ * The presentations used to visualize this field in Autofill UI.
+ */
+ public @NonNull Builder setPresentations(@NonNull Presentations value) {
+ checkNotUsed();
+ mPresentations = value;
+ return this;
+ }
+
+ /** Builds the instance. This builder should not be touched after calling this! */
+ public @NonNull Field build() {
+ checkNotUsed();
+ mDestroyed = true; // Mark builder used
+
+ Field o = new Field(
+ mValue,
+ mFilter,
+ mPresentations);
+ return o;
+ }
+
+ private void checkNotUsed() {
+ if (mDestroyed) {
+ throw new IllegalStateException(
+ "This Builder should not be reused. Use a new Builder instance instead");
+ }
+ }
+ }
+}
diff --git a/core/java/android/service/autofill/FillRequest.java b/core/java/android/service/autofill/FillRequest.java
index 43bd4102ffb5..e4d3732361ed 100644
--- a/core/java/android/service/autofill/FillRequest.java
+++ b/core/java/android/service/autofill/FillRequest.java
@@ -19,6 +19,7 @@ package android.service.autofill;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.IntentSender;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
@@ -98,6 +99,12 @@ public final class FillRequest implements Parcelable {
// The flag value 0x20 has been defined in AutofillManager.
+ /**
+ * Indicates the request is coming from the activity just started.
+ * @hide
+ */
+ public static final @RequestFlags int FLAG_ACTIVITY_START = 0x40;
+
/** @hide */
public static final int INVALID_REQUEST_ID = Integer.MIN_VALUE;
@@ -154,19 +161,32 @@ public final class FillRequest implements Parcelable {
*/
private final @Nullable InlineSuggestionsRequest mInlineSuggestionsRequest;
+ /**
+ * Gets the {@link IntentSender} to send a delayed fill response.
+ *
+ * <p>The autofill service must first indicate that it wants to return a delayed
+ * {@link FillResponse} by setting {@link FillResponse#FLAG_DELAY_FILL} in a successful
+ * fill response. Then it can use this IntentSender to send an Intent with extra
+ * {@link AutofillService#EXTRA_FILL_RESPONSE} with the delayed response.</p>
+ *
+ * <p>Note that this may be null if a delayed fill response is not supported for
+ * this fill request.</p>
+ */
+ private final @Nullable IntentSender mDelayedFillIntentSender;
+
private void onConstructed() {
Preconditions.checkCollectionElementsNotNull(mFillContexts, "contexts");
}
- // Code below generated by codegen v1.0.15.
+ // Code below generated by codegen v1.0.23.
//
// DO NOT MODIFY!
// CHECKSTYLE:OFF Generated code
//
// To regenerate run:
- // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/service/autofill/FillRequest.java
+ // $ codegen $ANDROID_BUILD_TOP/./frameworks/base/core/java/android/service/autofill/FillRequest.java
//
// To exclude the generated code from IntelliJ auto-formatting enable (one-time):
// Settings > Editor > Code Style > Formatter Control
@@ -178,7 +198,8 @@ public final class FillRequest implements Parcelable {
FLAG_MANUAL_REQUEST,
FLAG_COMPATIBILITY_MODE_REQUEST,
FLAG_PASSWORD_INPUT_TYPE,
- FLAG_VIEW_NOT_FOCUSED
+ FLAG_VIEW_NOT_FOCUSED,
+ FLAG_ACTIVITY_START
})
@Retention(RetentionPolicy.SOURCE)
@DataClass.Generated.Member
@@ -202,6 +223,8 @@ public final class FillRequest implements Parcelable {
return "FLAG_PASSWORD_INPUT_TYPE";
case FLAG_VIEW_NOT_FOCUSED:
return "FLAG_VIEW_NOT_FOCUSED";
+ case FLAG_ACTIVITY_START:
+ return "FLAG_ACTIVITY_START";
default: return Integer.toHexString(value);
}
}
@@ -243,6 +266,16 @@ public final class FillRequest implements Parcelable {
*
* <p>The Autofill Service must set supportsInlineSuggestions in its XML to enable support
* for inline suggestions.</p>
+ * @param delayedFillIntentSender
+ * Gets the {@link IntentSender} to send a delayed fill response.
+ *
+ * <p>The autofill service must first indicate that it wants to return a delayed
+ * {@link FillResponse} by setting {@link FillResponse#FLAG_DELAY_FILL} in a successful
+ * fill response. Then it can use this IntentSender to send an Intent with extra
+ * {@link AutofillService#EXTRA_FILL_RESPONSE} with the delayed response.</p>
+ *
+ * <p>Note that this may be null if a delayed fill response is not supported for
+ * this fill request.</p>
* @hide
*/
@DataClass.Generated.Member
@@ -251,7 +284,8 @@ public final class FillRequest implements Parcelable {
@NonNull List<FillContext> fillContexts,
@Nullable Bundle clientState,
@RequestFlags int flags,
- @Nullable InlineSuggestionsRequest inlineSuggestionsRequest) {
+ @Nullable InlineSuggestionsRequest inlineSuggestionsRequest,
+ @Nullable IntentSender delayedFillIntentSender) {
this.mId = id;
this.mFillContexts = fillContexts;
com.android.internal.util.AnnotationValidations.validate(
@@ -264,8 +298,10 @@ public final class FillRequest implements Parcelable {
FLAG_MANUAL_REQUEST
| FLAG_COMPATIBILITY_MODE_REQUEST
| FLAG_PASSWORD_INPUT_TYPE
- | FLAG_VIEW_NOT_FOCUSED);
+ | FLAG_VIEW_NOT_FOCUSED
+ | FLAG_ACTIVITY_START);
this.mInlineSuggestionsRequest = inlineSuggestionsRequest;
+ this.mDelayedFillIntentSender = delayedFillIntentSender;
onConstructed();
}
@@ -338,6 +374,22 @@ public final class FillRequest implements Parcelable {
return mInlineSuggestionsRequest;
}
+ /**
+ * Gets the {@link IntentSender} to send a delayed fill response.
+ *
+ * <p>The autofill service must first indicate that it wants to return a delayed
+ * {@link FillResponse} by setting {@link FillResponse#FLAG_DELAY_FILL} in a successful
+ * fill response. Then it can use this IntentSender to send an Intent with extra
+ * {@link AutofillService#EXTRA_FILL_RESPONSE} with the delayed response.</p>
+ *
+ * <p>Note that this may be null if a delayed fill response is not supported for
+ * this fill request.</p>
+ */
+ @DataClass.Generated.Member
+ public @Nullable IntentSender getDelayedFillIntentSender() {
+ return mDelayedFillIntentSender;
+ }
+
@Override
@DataClass.Generated.Member
public String toString() {
@@ -349,7 +401,8 @@ public final class FillRequest implements Parcelable {
"fillContexts = " + mFillContexts + ", " +
"clientState = " + mClientState + ", " +
"flags = " + requestFlagsToString(mFlags) + ", " +
- "inlineSuggestionsRequest = " + mInlineSuggestionsRequest +
+ "inlineSuggestionsRequest = " + mInlineSuggestionsRequest + ", " +
+ "delayedFillIntentSender = " + mDelayedFillIntentSender +
" }";
}
@@ -362,12 +415,14 @@ public final class FillRequest implements Parcelable {
byte flg = 0;
if (mClientState != null) flg |= 0x4;
if (mInlineSuggestionsRequest != null) flg |= 0x10;
+ if (mDelayedFillIntentSender != null) flg |= 0x20;
dest.writeByte(flg);
dest.writeInt(mId);
dest.writeParcelableList(mFillContexts, flags);
if (mClientState != null) dest.writeBundle(mClientState);
dest.writeInt(mFlags);
if (mInlineSuggestionsRequest != null) dest.writeTypedObject(mInlineSuggestionsRequest, flags);
+ if (mDelayedFillIntentSender != null) dest.writeTypedObject(mDelayedFillIntentSender, flags);
}
@Override
@@ -384,10 +439,11 @@ public final class FillRequest implements Parcelable {
byte flg = in.readByte();
int id = in.readInt();
List<FillContext> fillContexts = new ArrayList<>();
- in.readParcelableList(fillContexts, FillContext.class.getClassLoader(), android.service.autofill.FillContext.class);
+ in.readParcelableList(fillContexts, FillContext.class.getClassLoader());
Bundle clientState = (flg & 0x4) == 0 ? null : in.readBundle();
int flags = in.readInt();
InlineSuggestionsRequest inlineSuggestionsRequest = (flg & 0x10) == 0 ? null : (InlineSuggestionsRequest) in.readTypedObject(InlineSuggestionsRequest.CREATOR);
+ IntentSender delayedFillIntentSender = (flg & 0x20) == 0 ? null : (IntentSender) in.readTypedObject(IntentSender.CREATOR);
this.mId = id;
this.mFillContexts = fillContexts;
@@ -401,8 +457,10 @@ public final class FillRequest implements Parcelable {
FLAG_MANUAL_REQUEST
| FLAG_COMPATIBILITY_MODE_REQUEST
| FLAG_PASSWORD_INPUT_TYPE
- | FLAG_VIEW_NOT_FOCUSED);
+ | FLAG_VIEW_NOT_FOCUSED
+ | FLAG_ACTIVITY_START);
this.mInlineSuggestionsRequest = inlineSuggestionsRequest;
+ this.mDelayedFillIntentSender = delayedFillIntentSender;
onConstructed();
}
@@ -422,10 +480,10 @@ public final class FillRequest implements Parcelable {
};
@DataClass.Generated(
- time = 1589280816805L,
- codegenVersion = "1.0.15",
+ time = 1643386870464L,
+ codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/service/autofill/FillRequest.java",
- inputSignatures = "public static final @android.service.autofill.FillRequest.RequestFlags int FLAG_MANUAL_REQUEST\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_COMPATIBILITY_MODE_REQUEST\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_PASSWORD_INPUT_TYPE\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_VIEW_NOT_FOCUSED\npublic static final int INVALID_REQUEST_ID\nprivate final int mId\nprivate final @android.annotation.NonNull java.util.List<android.service.autofill.FillContext> mFillContexts\nprivate final @android.annotation.Nullable android.os.Bundle mClientState\nprivate final @android.service.autofill.FillRequest.RequestFlags int mFlags\nprivate final @android.annotation.Nullable android.view.inputmethod.InlineSuggestionsRequest mInlineSuggestionsRequest\nprivate void onConstructed()\nclass FillRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genHiddenConstructor=true, genHiddenConstDefs=true)")
+ inputSignatures = "public static final @android.service.autofill.FillRequest.RequestFlags int FLAG_MANUAL_REQUEST\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_COMPATIBILITY_MODE_REQUEST\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_PASSWORD_INPUT_TYPE\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_VIEW_NOT_FOCUSED\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_ACTIVITY_START\npublic static final int INVALID_REQUEST_ID\nprivate final int mId\nprivate final @android.annotation.NonNull java.util.List<android.service.autofill.FillContext> mFillContexts\nprivate final @android.annotation.Nullable android.os.Bundle mClientState\nprivate final @android.service.autofill.FillRequest.RequestFlags int mFlags\nprivate final @android.annotation.Nullable android.view.inputmethod.InlineSuggestionsRequest mInlineSuggestionsRequest\nprivate final @android.annotation.Nullable android.content.IntentSender mDelayedFillIntentSender\nprivate void onConstructed()\nclass FillRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genHiddenConstructor=true, genHiddenConstDefs=true)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/service/autofill/FillResponse.java b/core/java/android/service/autofill/FillResponse.java
index d94988ebea66..296877a448ab 100644
--- a/core/java/android/service/autofill/FillResponse.java
+++ b/core/java/android/service/autofill/FillResponse.java
@@ -65,10 +65,22 @@ public final class FillResponse implements Parcelable {
*/
public static final int FLAG_DISABLE_ACTIVITY_ONLY = 0x2;
+ /**
+ * Flag used to request to wait for a delayed fill from the remote Autofill service if it's
+ * passed to {@link Builder#setFlags(int)}.
+ *
+ * <p>Some datasets (i.e. OTP) take time to produce. This flags allows remote service to send
+ * a {@link FillResponse} to the latest {@link FillRequest} via
+ * {@link FillRequest#getDelayedFillIntentSender()} even if the original {@link FillCallback}
+ * has timed out.
+ */
+ public static final int FLAG_DELAY_FILL = 0x4;
+
/** @hide */
@IntDef(flag = true, prefix = { "FLAG_" }, value = {
FLAG_TRACK_CONTEXT_COMMITED,
- FLAG_DISABLE_ACTIVITY_ONLY
+ FLAG_DISABLE_ACTIVITY_ONLY,
+ FLAG_DELAY_FILL
})
@Retention(RetentionPolicy.SOURCE)
@interface FillResponseFlags {}
@@ -79,11 +91,14 @@ public final class FillResponse implements Parcelable {
private final @Nullable RemoteViews mPresentation;
private final @Nullable InlinePresentation mInlinePresentation;
private final @Nullable InlinePresentation mInlineTooltipPresentation;
+ private final @Nullable RemoteViews mDialogPresentation;
+ private final @Nullable RemoteViews mDialogHeader;
private final @Nullable RemoteViews mHeader;
private final @Nullable RemoteViews mFooter;
private final @Nullable IntentSender mAuthentication;
private final @Nullable AutofillId[] mAuthenticationIds;
private final @Nullable AutofillId[] mIgnoredIds;
+ private final @Nullable AutofillId[] mFillDialogTriggerIds;
private final long mDisableDuration;
private final @Nullable AutofillId[] mFieldClassificationIds;
private final int mFlags;
@@ -99,10 +114,13 @@ public final class FillResponse implements Parcelable {
mPresentation = builder.mPresentation;
mInlinePresentation = builder.mInlinePresentation;
mInlineTooltipPresentation = builder.mInlineTooltipPresentation;
+ mDialogPresentation = builder.mDialogPresentation;
+ mDialogHeader = builder.mDialogHeader;
mHeader = builder.mHeader;
mFooter = builder.mFooter;
mAuthentication = builder.mAuthentication;
mAuthenticationIds = builder.mAuthenticationIds;
+ mFillDialogTriggerIds = builder.mFillDialogTriggerIds;
mIgnoredIds = builder.mIgnoredIds;
mDisableDuration = builder.mDisableDuration;
mFieldClassificationIds = builder.mFieldClassificationIds;
@@ -144,6 +162,16 @@ public final class FillResponse implements Parcelable {
}
/** @hide */
+ public @Nullable RemoteViews getDialogPresentation() {
+ return mDialogPresentation;
+ }
+
+ /** @hide */
+ public @Nullable RemoteViews getDialogHeader() {
+ return mDialogHeader;
+ }
+
+ /** @hide */
public @Nullable RemoteViews getHeader() {
return mHeader;
}
@@ -164,6 +192,11 @@ public final class FillResponse implements Parcelable {
}
/** @hide */
+ public @Nullable AutofillId[] getFillDialogTriggerIds() {
+ return mFillDialogTriggerIds;
+ }
+
+ /** @hide */
public @Nullable AutofillId[] getIgnoredIds() {
return mIgnoredIds;
}
@@ -229,6 +262,8 @@ public final class FillResponse implements Parcelable {
private RemoteViews mPresentation;
private InlinePresentation mInlinePresentation;
private InlinePresentation mInlineTooltipPresentation;
+ private RemoteViews mDialogPresentation;
+ private RemoteViews mDialogHeader;
private RemoteViews mHeader;
private RemoteViews mFooter;
private IntentSender mAuthentication;
@@ -236,6 +271,7 @@ public final class FillResponse implements Parcelable {
private AutofillId[] mIgnoredIds;
private long mDisableDuration;
private AutofillId[] mFieldClassificationIds;
+ private AutofillId[] mFillDialogTriggerIds;
private int mFlags;
private boolean mDestroyed;
private UserData mUserData;
@@ -243,7 +279,7 @@ public final class FillResponse implements Parcelable {
private boolean mSupportsInlineSuggestions;
/**
- * Triggers a custom UI before before autofilling the screen with any data set in this
+ * Triggers a custom UI before autofilling the screen with any data set in this
* response.
*
* <p><b>Note:</b> Although the name of this method suggests that it should be used just for
@@ -277,7 +313,7 @@ public final class FillResponse implements Parcelable {
* example a credit card whose CVV needs to be entered.
*
* <p>If you provide an authentication intent you must also provide a presentation
- * which is used to visualize visualize the response for triggering the authentication
+ * which is used to visualize the response for triggering the authentication
* flow.
*
* <p><b>Note:</b> Do not make the provided pending intent
@@ -306,7 +342,11 @@ public final class FillResponse implements Parcelable {
* {@link #setFooter(RemoteViews) footer} are already set for this builder.
*
* @see android.app.PendingIntent#getIntentSender()
+ * @deprecated Use
+ * {@link #setAuthentication(AutofillId[], IntentSender, Presentations)}
+ * instead.
*/
+ @Deprecated
@NonNull
public Builder setAuthentication(@NonNull AutofillId[] ids,
@Nullable IntentSender authentication, @Nullable RemoteViews presentation) {
@@ -327,7 +367,7 @@ public final class FillResponse implements Parcelable {
}
/**
- * Triggers a custom UI before before autofilling the screen with any data set in this
+ * Triggers a custom UI before autofilling the screen with any data set in this
* response.
*
* <p><b>Note:</b> Although the name of this method suggests that it should be used just for
@@ -365,7 +405,11 @@ public final class FillResponse implements Parcelable {
* {@link #setFooter(RemoteViews) footer} are already set for this builder.
*
* @see android.app.PendingIntent#getIntentSender()
+ * @deprecated Use
+ * {@link #setAuthentication(AutofillId[], IntentSender, Presentations)}
+ * instead.
*/
+ @Deprecated
@NonNull
public Builder setAuthentication(@NonNull AutofillId[] ids,
@Nullable IntentSender authentication, @Nullable RemoteViews presentation,
@@ -374,13 +418,18 @@ public final class FillResponse implements Parcelable {
}
/**
- * Triggers a custom UI before before autofilling the screen with any data set in this
+ * Triggers a custom UI before autofilling the screen with any data set in this
* response.
*
* <p>This method like
* {@link #setAuthentication(AutofillId[], IntentSender, RemoteViews, InlinePresentation)}
* but allows setting an {@link InlinePresentation} for the inline suggestion tooltip.
+ *
+ * @deprecated Use
+ * {@link #setAuthentication(AutofillId[], IntentSender, Presentations)}
+ * instead.
*/
+ @Deprecated
@NonNull
public Builder setAuthentication(@SuppressLint("ArrayReturn") @NonNull AutofillId[] ids,
@Nullable IntentSender authentication, @Nullable RemoteViews presentation,
@@ -388,6 +437,105 @@ public final class FillResponse implements Parcelable {
@Nullable InlinePresentation inlineTooltipPresentation) {
throwIfDestroyed();
throwIfDisableAutofillCalled();
+ return setAuthentication(ids, authentication, presentation,
+ inlinePresentation, inlineTooltipPresentation, null);
+ }
+
+ /**
+ * Triggers a custom UI before autofilling the screen with any data set in this
+ * response.
+ *
+ * <p><b>Note:</b> Although the name of this method suggests that it should be used just for
+ * authentication flow, it can be used for other advanced flows; see {@link AutofillService}
+ * for examples.
+ *
+ * <p>This is typically useful when a user interaction is required to unlock their
+ * data vault if you encrypt the data set labels and data set data. It is recommended
+ * to encrypt only the sensitive data and not the data set labels which would allow
+ * auth on the data set level leading to a better user experience. Note that if you
+ * use sensitive data as a label, for example an email address, then it should also
+ * be encrypted. The provided {@link android.app.PendingIntent intent} must be an
+ * {@link Activity} which implements your authentication flow. Also if you provide an auth
+ * intent you also need to specify the presentation view to be shown in the fill UI
+ * for the user to trigger your authentication flow.
+ *
+ * <p>When a user triggers autofill, the system launches the provided intent
+ * whose extras will have the
+ * {@link android.view.autofill.AutofillManager#EXTRA_ASSIST_STRUCTURE screen
+ * content} and your {@link android.view.autofill.AutofillManager#EXTRA_CLIENT_STATE
+ * client state}. Once you complete your authentication flow you should set the
+ * {@link Activity} result to {@link android.app.Activity#RESULT_OK} and set the
+ * {@link android.view.autofill.AutofillManager#EXTRA_AUTHENTICATION_RESULT} extra
+ * with the fully populated {@link FillResponse response} (or {@code null} if the screen
+ * cannot be autofilled).
+ *
+ * <p>For example, if you provided an empty {@link FillResponse response} because the
+ * user's data was locked and marked that the response needs an authentication then
+ * in the response returned if authentication succeeds you need to provide all
+ * available data sets some of which may need to be further authenticated, for
+ * example a credit card whose CVV needs to be entered.
+ *
+ * <p>If you provide an authentication intent you must also provide a presentation
+ * which is used to visualize the response for triggering the authentication
+ * flow.
+ *
+ * <p><b>Note:</b> Do not make the provided pending intent
+ * immutable by using {@link android.app.PendingIntent#FLAG_IMMUTABLE} as the
+ * platform needs to fill in the authentication arguments.
+ *
+ * <p><b>Note:</b> {@link #setHeader(RemoteViews)} or {@link #setFooter(RemoteViews)} does
+ * not work with {@link InlinePresentation}.</p>
+ *
+ * @param ids id of Views that when focused will display the authentication UI.
+ * @param authentication Intent to an activity with your authentication flow.
+ * @param presentations The presentations to visualize the response.
+ *
+ * @throws IllegalArgumentException if any of the following occurs:
+ * <ul>
+ * <li>{@code ids} is {@code null}</li>
+ * <li>{@code ids} is empty</li>
+ * <li>{@code ids} contains a {@code null} element</li>
+ * <li>{@code authentication} is {@code null}, but either or both of
+ * {@code presentations.getPresentation()} and
+ * {@code presentations.getInlinePresentation()} is non-{@code null}</li>
+ * <li>{@code authentication} is non-{{@code null}, but both
+ * {@code presentations.getPresentation()} and
+ * {@code presentations.getInlinePresentation()} are {@code null}</li>
+ * </ul>
+ *
+ * @throws IllegalStateException if a {@link #setHeader(RemoteViews) header} or a
+ * {@link #setFooter(RemoteViews) footer} are already set for this builder.
+ *
+ * @return This builder.
+ */
+ @NonNull
+ public Builder setAuthentication(@SuppressLint("ArrayReturn") @NonNull AutofillId[] ids,
+ @Nullable IntentSender authentication,
+ @Nullable Presentations presentations) {
+ throwIfDestroyed();
+ throwIfDisableAutofillCalled();
+ if (presentations == null) {
+ return setAuthentication(ids, authentication, null, null, null, null);
+ }
+ return setAuthentication(ids, authentication,
+ presentations.getMenuPresentation(),
+ presentations.getInlinePresentation(),
+ presentations.getInlineTooltipPresentation(),
+ presentations.getDialogPresentation());
+ }
+
+ /**
+ * Triggers a custom UI before autofilling the screen with any data set in this
+ * response.
+ */
+ @NonNull
+ private Builder setAuthentication(@SuppressLint("ArrayReturn") @NonNull AutofillId[] ids,
+ @Nullable IntentSender authentication, @Nullable RemoteViews presentation,
+ @Nullable InlinePresentation inlinePresentation,
+ @Nullable InlinePresentation inlineTooltipPresentation,
+ @Nullable RemoteViews dialogPresentation) {
+ throwIfDestroyed();
+ throwIfDisableAutofillCalled();
if (mHeader != null || mFooter != null) {
throw new IllegalStateException("Already called #setHeader() or #setFooter()");
}
@@ -400,6 +548,7 @@ public final class FillResponse implements Parcelable {
mPresentation = presentation;
mInlinePresentation = inlinePresentation;
mInlineTooltipPresentation = inlineTooltipPresentation;
+ mDialogPresentation = dialogPresentation;
mAuthenticationIds = assertValid(ids);
return this;
}
@@ -520,7 +669,7 @@ public final class FillResponse implements Parcelable {
public Builder setFlags(@FillResponseFlags int flags) {
throwIfDestroyed();
mFlags = Preconditions.checkFlagsArgument(flags,
- FLAG_TRACK_CONTEXT_COMMITED | FLAG_DISABLE_ACTIVITY_ONLY);
+ FLAG_TRACK_CONTEXT_COMMITED | FLAG_DISABLE_ACTIVITY_ONLY | FLAG_DELAY_FILL);
return this;
}
@@ -552,7 +701,7 @@ public final class FillResponse implements Parcelable {
*
* @throws IllegalArgumentException if {@code duration} is not a positive number.
* @throws IllegalStateException if either {@link #addDataset(Dataset)},
- * {@link #setAuthentication(AutofillId[], IntentSender, RemoteViews)},
+ * {@link #setAuthentication(AutofillId[], IntentSender, Presentations)},
* {@link #setSaveInfo(SaveInfo)}, {@link #setClientState(Bundle)}, or
* {@link #setFieldClassificationIds(AutofillId...)} was already called.
*/
@@ -591,8 +740,8 @@ public final class FillResponse implements Parcelable {
* @return this builder
*
* @throws IllegalStateException if an
- * {@link #setAuthentication(AutofillId[], IntentSender, RemoteViews) authentication} was
- * already set for this builder.
+ * {@link #setAuthentication(AutofillId[], IntentSender, Presentations)
+ * authentication} was already set for this builder.
*/
// TODO(b/69796626): make it sticky / update javadoc
@NonNull
@@ -623,7 +772,7 @@ public final class FillResponse implements Parcelable {
* @return this builder
*
* @throws IllegalStateException if the FillResponse
- * {@link #setAuthentication(AutofillId[], IntentSender, RemoteViews)
+ * {@link #setAuthentication(AutofillId[], IntentSender, Presentations)
* requires authentication}.
*/
// TODO(b/69796626): make it sticky / update javadoc
@@ -643,7 +792,7 @@ public final class FillResponse implements Parcelable {
*
* @return this builder
* @throws IllegalStateException if the FillResponse
- * {@link #setAuthentication(AutofillId[], IntentSender, RemoteViews)
+ * {@link #setAuthentication(AutofillId[], IntentSender, Presentations)
* requires authentication}.
*/
@NonNull
@@ -674,13 +823,46 @@ public final class FillResponse implements Parcelable {
}
/**
+ * Sets the presentation of header in fill dialog UI. The header should have
+ * a prompt for what datasets are shown in the dialog. If this is not set,
+ * the dialog only shows your application icon.
+ *
+ * More details about the fill dialog, see
+ * <a href="Dataset.html#FillDialogUI">fill dialog UI</a>
+ */
+ @NonNull
+ public Builder setDialogHeader(@NonNull RemoteViews header) {
+ throwIfDestroyed();
+ Objects.requireNonNull(header);
+ mDialogHeader = header;
+ return this;
+ }
+
+ /**
+ * Sets which fields are used for the fill dialog UI.
+ *
+ * More details about the fill dialog, see
+ * <a href="Dataset.html#FillDialogUI">fill dialog UI</a>
+ *
+ * @throws IllegalStateException if {@link #build()} was already called.
+ * @throws NullPointerException if {@code ids} or any element on it is {@code null}.
+ */
+ @NonNull
+ public Builder setFillDialogTriggerIds(@NonNull AutofillId... ids) {
+ throwIfDestroyed();
+ Preconditions.checkArrayElementsNotNull(ids, "ids");
+ mFillDialogTriggerIds = ids;
+ return this;
+ }
+
+ /**
* Builds a new {@link FillResponse} instance.
*
* @throws IllegalStateException if any of the following conditions occur:
* <ol>
* <li>{@link #build()} was already called.
* <li>No call was made to {@link #addDataset(Dataset)},
- * {@link #setAuthentication(AutofillId[], IntentSender, RemoteViews)},
+ * {@link #setAuthentication(AutofillId[], IntentSender, Presentations)},
* {@link #setSaveInfo(SaveInfo)}, {@link #disableAutofill(long)},
* {@link #setClientState(Bundle)},
* or {@link #setFieldClassificationIds(AutofillId...)}.
@@ -767,6 +949,12 @@ public final class FillResponse implements Parcelable {
if (mInlineTooltipPresentation != null) {
builder.append(", hasInlineTooltipPresentation");
}
+ if (mDialogPresentation != null) {
+ builder.append(", hasDialogPresentation");
+ }
+ if (mDialogHeader != null) {
+ builder.append(", hasDialogHeader");
+ }
if (mHeader != null) {
builder.append(", hasHeader");
}
@@ -779,6 +967,10 @@ public final class FillResponse implements Parcelable {
if (mAuthenticationIds != null) {
builder.append(", authenticationIds=").append(Arrays.toString(mAuthenticationIds));
}
+ if (mFillDialogTriggerIds != null) {
+ builder.append(", fillDialogTriggerIds=")
+ .append(Arrays.toString(mFillDialogTriggerIds));
+ }
builder.append(", disableDuration=").append(mDisableDuration);
if (mFlags != 0) {
builder.append(", flags=").append(mFlags);
@@ -815,6 +1007,9 @@ public final class FillResponse implements Parcelable {
parcel.writeParcelable(mPresentation, flags);
parcel.writeParcelable(mInlinePresentation, flags);
parcel.writeParcelable(mInlineTooltipPresentation, flags);
+ parcel.writeParcelable(mDialogPresentation, flags);
+ parcel.writeParcelable(mDialogHeader, flags);
+ parcel.writeParcelableArray(mFillDialogTriggerIds, flags);
parcel.writeParcelable(mHeader, flags);
parcel.writeParcelable(mFooter, flags);
parcel.writeParcelable(mUserData, flags);
@@ -850,9 +1045,18 @@ public final class FillResponse implements Parcelable {
final RemoteViews presentation = parcel.readParcelable(null, android.widget.RemoteViews.class);
final InlinePresentation inlinePresentation = parcel.readParcelable(null, android.service.autofill.InlinePresentation.class);
final InlinePresentation inlineTooltipPresentation = parcel.readParcelable(null, android.service.autofill.InlinePresentation.class);
+ final RemoteViews dialogPresentation = parcel.readParcelable(null, android.widget.RemoteViews.class);
if (authenticationIds != null) {
builder.setAuthentication(authenticationIds, authentication, presentation,
- inlinePresentation, inlineTooltipPresentation);
+ inlinePresentation, inlineTooltipPresentation, dialogPresentation);
+ }
+ final RemoteViews dialogHeader = parcel.readParcelable(null, android.widget.RemoteViews.class);
+ if (dialogHeader != null) {
+ builder.setDialogHeader(dialogHeader);
+ }
+ final AutofillId[] triggerIds = parcel.readParcelableArray(null, AutofillId.class);
+ if (triggerIds != null) {
+ builder.setFillDialogTriggerIds(triggerIds);
}
final RemoteViews header = parcel.readParcelable(null, android.widget.RemoteViews.class);
if (header != null) {
diff --git a/core/java/android/service/autofill/Presentations.java b/core/java/android/service/autofill/Presentations.java
new file mode 100644
index 000000000000..e8ac628ebd12
--- /dev/null
+++ b/core/java/android/service/autofill/Presentations.java
@@ -0,0 +1,278 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.autofill;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.widget.RemoteViews;
+
+import com.android.internal.util.DataClass;
+
+/**
+ * Holds presentations used to visualize autofill suggestions for each available UI type.
+ *
+ * @see Field
+ */
+@DataClass(genBuilder = true)
+public final class Presentations {
+
+ /**
+ * The presentation used to visualize this field in fill UI.
+ *
+ * <p>Note: Before Android 13, this was referred to simply as "presentation" in the SDK.
+ *
+ * <p>Theme does not work with RemoteViews layout. Avoid hardcoded text color
+ * or background color: Autofill on different platforms may have different themes.
+ */
+ private @Nullable RemoteViews mMenuPresentation;
+
+ /**
+ * The {@link InlinePresentation} used to visualize this dataset as inline suggestions.
+ * If the dataset supports inline suggestions, this should not be null.
+ */
+ private @Nullable InlinePresentation mInlinePresentation;
+
+ /**
+ * The presentation used to visualize this field in the
+ * <a href="Dataset.html#FillDialogUI">fill dialog UI</a>
+ *
+ * <p>Theme does not work with RemoteViews layout. Avoid hardcoded text color
+ * or background color: Autofill on different platforms may have different themes.
+ */
+ private @Nullable RemoteViews mDialogPresentation;
+
+ /**
+ * The {@link InlinePresentation} used to show the tooltip for the
+ * {@code mInlinePresentation}. If the set this field, the
+ * {@code mInlinePresentation} should not be null.
+ */
+ private @Nullable InlinePresentation mInlineTooltipPresentation;
+
+ private static RemoteViews defaultMenuPresentation() {
+ return null;
+ }
+
+ private static InlinePresentation defaultInlinePresentation() {
+ return null;
+ }
+
+ private static RemoteViews defaultDialogPresentation() {
+ return null;
+ }
+
+ private static InlinePresentation defaultInlineTooltipPresentation() {
+ return null;
+ }
+
+ private void onConstructed() {
+ if (mMenuPresentation == null
+ && mInlinePresentation == null
+ && mDialogPresentation == null) {
+ throw new IllegalStateException("All presentations are null.");
+ }
+ if (mInlineTooltipPresentation != null && mInlinePresentation == null) {
+ throw new IllegalStateException(
+ "The inline presentation is required for mInlineTooltipPresentation.");
+ }
+ }
+
+
+
+ // Code below generated by codegen v1.0.23.
+ //
+ // DO NOT MODIFY!
+ // CHECKSTYLE:OFF Generated code
+ //
+ // To regenerate run:
+ // $ codegen $ANDROID_BUILD_TOP/./frameworks/base/core/java/android/service/autofill/Presentations.java
+ //
+ // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+ // Settings > Editor > Code Style > Formatter Control
+ //@formatter:off
+
+
+ @DataClass.Generated.Member
+ /* package-private */ Presentations(
+ @Nullable RemoteViews menuPresentation,
+ @Nullable InlinePresentation inlinePresentation,
+ @Nullable RemoteViews dialogPresentation,
+ @Nullable InlinePresentation inlineTooltipPresentation) {
+ this.mMenuPresentation = menuPresentation;
+ this.mInlinePresentation = inlinePresentation;
+ this.mDialogPresentation = dialogPresentation;
+ this.mInlineTooltipPresentation = inlineTooltipPresentation;
+
+ onConstructed();
+ }
+
+ /**
+ * The presentation used to visualize this field in fill UI.
+ *
+ * <p>Theme does not work with RemoteViews layout. Avoid hardcoded text color
+ * or background color: Autofill on different platforms may have different themes.
+ */
+ @DataClass.Generated.Member
+ public @Nullable RemoteViews getMenuPresentation() {
+ return mMenuPresentation;
+ }
+
+ /**
+ * The {@link InlinePresentation} used to visualize this dataset as inline suggestions.
+ * If the dataset supports inline suggestions, this should not be null.
+ */
+ @DataClass.Generated.Member
+ public @Nullable InlinePresentation getInlinePresentation() {
+ return mInlinePresentation;
+ }
+
+ /**
+ * The presentation used to visualize this field in the fill dialog UI.
+ *
+ * <p>Theme does not work with RemoteViews layout. Avoid hardcoded text color
+ * or background color: Autofill on different platforms may have different themes.
+ */
+ @DataClass.Generated.Member
+ public @Nullable RemoteViews getDialogPresentation() {
+ return mDialogPresentation;
+ }
+
+ /**
+ * The {@link InlinePresentation} used to show the tooltip for the
+ * {@code mInlinePresentation}. If the set this field, the
+ * {@code mInlinePresentation} should not be null.
+ */
+ @DataClass.Generated.Member
+ public @Nullable InlinePresentation getInlineTooltipPresentation() {
+ return mInlineTooltipPresentation;
+ }
+
+ /**
+ * A builder for {@link Presentations}
+ */
+ @SuppressWarnings("WeakerAccess")
+ @DataClass.Generated.Member
+ public static final class Builder {
+
+ private @Nullable RemoteViews mMenuPresentation;
+ private @Nullable InlinePresentation mInlinePresentation;
+ private @Nullable RemoteViews mDialogPresentation;
+ private @Nullable InlinePresentation mInlineTooltipPresentation;
+
+ private long mBuilderFieldsSet = 0L;
+
+ public Builder() {
+ }
+
+ /**
+ * The presentation used to visualize this field in fill UI.
+ *
+ * <p>Theme does not work with RemoteViews layout. Avoid hardcoded text color
+ * or background color: Autofill on different platforms may have different themes.
+ */
+ @DataClass.Generated.Member
+ public @NonNull Builder setMenuPresentation(@NonNull RemoteViews value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x1;
+ mMenuPresentation = value;
+ return this;
+ }
+
+ /**
+ * The {@link InlinePresentation} used to visualize this dataset as inline suggestions.
+ * If the dataset supports inline suggestions, this should not be null.
+ */
+ @DataClass.Generated.Member
+ public @NonNull Builder setInlinePresentation(@NonNull InlinePresentation value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x2;
+ mInlinePresentation = value;
+ return this;
+ }
+
+ /**
+ * The presentation used to visualize this field in the fill dialog UI.
+ *
+ * <p>Theme does not work with RemoteViews layout. Avoid hardcoded text color
+ * or background color: Autofill on different platforms may have different themes.
+ */
+ @DataClass.Generated.Member
+ public @NonNull Builder setDialogPresentation(@NonNull RemoteViews value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x4;
+ mDialogPresentation = value;
+ return this;
+ }
+
+ /**
+ * The {@link InlinePresentation} used to show the tooltip for the
+ * {@code mInlinePresentation}. If the set this field, the
+ * {@code mInlinePresentation} should not be null.
+ */
+ @DataClass.Generated.Member
+ public @NonNull Builder setInlineTooltipPresentation(@NonNull InlinePresentation value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x8;
+ mInlineTooltipPresentation = value;
+ return this;
+ }
+
+ /** Builds the instance. This builder should not be touched after calling this! */
+ public @NonNull Presentations build() {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x10; // Mark builder used
+
+ if ((mBuilderFieldsSet & 0x1) == 0) {
+ mMenuPresentation = defaultMenuPresentation();
+ }
+ if ((mBuilderFieldsSet & 0x2) == 0) {
+ mInlinePresentation = defaultInlinePresentation();
+ }
+ if ((mBuilderFieldsSet & 0x4) == 0) {
+ mDialogPresentation = defaultDialogPresentation();
+ }
+ if ((mBuilderFieldsSet & 0x8) == 0) {
+ mInlineTooltipPresentation = defaultInlineTooltipPresentation();
+ }
+ Presentations o = new Presentations(
+ mMenuPresentation,
+ mInlinePresentation,
+ mDialogPresentation,
+ mInlineTooltipPresentation);
+ return o;
+ }
+
+ private void checkNotUsed() {
+ if ((mBuilderFieldsSet & 0x10) != 0) {
+ throw new IllegalStateException(
+ "This Builder should not be reused. Use a new Builder instance instead");
+ }
+ }
+ }
+
+ @DataClass.Generated(
+ time = 1643083242164L,
+ codegenVersion = "1.0.23",
+ sourceFile = "frameworks/base/core/java/android/service/autofill/Presentations.java",
+ inputSignatures = "private @android.annotation.Nullable android.widget.RemoteViews mMenuPresentation\nprivate @android.annotation.Nullable android.service.autofill.InlinePresentation mInlinePresentation\nprivate @android.annotation.Nullable android.widget.RemoteViews mDialogPresentation\nprivate @android.annotation.Nullable android.service.autofill.InlinePresentation mInlineTooltipPresentation\nprivate static android.widget.RemoteViews defaultMenuPresentation()\nprivate static android.service.autofill.InlinePresentation defaultInlinePresentation()\nprivate static android.widget.RemoteViews defaultDialogPresentation()\nprivate static android.service.autofill.InlinePresentation defaultInlineTooltipPresentation()\nprivate void onConstructed()\nclass Presentations extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genBuilder=true)")
+ @Deprecated
+ private void __metadata() {}
+
+
+ //@formatter:on
+ // End of generated code
+
+}
diff --git a/core/java/android/service/games/GameService.java b/core/java/android/service/games/GameService.java
index 870a7e3f2646..9df83587f125 100644
--- a/core/java/android/service/games/GameService.java
+++ b/core/java/android/service/games/GameService.java
@@ -16,9 +16,11 @@
package android.service.games;
+import android.Manifest;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
import android.annotation.SystemApi;
import android.app.IGameManagerService;
@@ -173,6 +175,7 @@ public class GameService extends Service {
*
* @param taskId The taskId of the game.
*/
+ @RequiresPermission(Manifest.permission.MANAGE_GAME_ACTIVITY)
public final void createGameSession(@IntRange(from = 0) int taskId) {
if (mGameServiceController == null) {
throw new IllegalStateException("Can not call before connected()");
diff --git a/core/java/android/service/games/GameSession.java b/core/java/android/service/games/GameSession.java
index f4baedc18acf..9590933cf2da 100644
--- a/core/java/android/service/games/GameSession.java
+++ b/core/java/android/service/games/GameSession.java
@@ -278,7 +278,7 @@ public abstract class GameSession {
*
* @return {@code true} if the game was successfully restarted; otherwise, {@code false}.
*/
- @RequiresPermission(android.Manifest.permission.FORCE_STOP_PACKAGES)
+ @RequiresPermission(android.Manifest.permission.MANAGE_GAME_ACTIVITY)
public final boolean restartGame() {
try {
mGameSessionController.restartGame(mTaskId);
diff --git a/core/java/android/service/persistentdata/IPersistentDataBlockService.aidl b/core/java/android/service/persistentdata/IPersistentDataBlockService.aidl
index 31352f14fe08..11e5ad8789f2 100644
--- a/core/java/android/service/persistentdata/IPersistentDataBlockService.aidl
+++ b/core/java/android/service/persistentdata/IPersistentDataBlockService.aidl
@@ -37,5 +37,6 @@ interface IPersistentDataBlockService {
boolean getOemUnlockEnabled();
int getFlashLockState();
boolean hasFrpCredentialHandle();
+ String getPersistentDataPackageName();
}
diff --git a/core/java/android/service/persistentdata/PersistentDataBlockManager.java b/core/java/android/service/persistentdata/PersistentDataBlockManager.java
index 44a886257d5a..9167153a0ef5 100644
--- a/core/java/android/service/persistentdata/PersistentDataBlockManager.java
+++ b/core/java/android/service/persistentdata/PersistentDataBlockManager.java
@@ -26,8 +26,6 @@ import android.content.Context;
import android.os.RemoteException;
import android.service.oemlock.OemLockManager;
-import com.android.internal.R;
-
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -53,7 +51,6 @@ import java.lang.annotation.RetentionPolicy;
@SystemService(Context.PERSISTENT_DATA_BLOCK_SERVICE)
public class PersistentDataBlockManager {
private static final String TAG = PersistentDataBlockManager.class.getSimpleName();
- private final Context mContext;
private IPersistentDataBlockService sService;
/**
@@ -78,10 +75,7 @@ public class PersistentDataBlockManager {
public @interface FlashLockState {}
/** @hide */
- public PersistentDataBlockManager(
- Context context,
- IPersistentDataBlockService service) {
- mContext = context;
+ public PersistentDataBlockManager(IPersistentDataBlockService service) {
sService = service;
}
@@ -219,7 +213,12 @@ public class PersistentDataBlockManager {
*/
@SystemApi
@NonNull
+ @RequiresPermission(android.Manifest.permission.ACCESS_PDB_STATE)
public String getPersistentDataPackageName() {
- return mContext.getString(R.string.config_persistentDataPackageName);
+ try {
+ return sService.getPersistentDataPackageName();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
}
diff --git a/core/java/android/service/tracing/TraceReportService.java b/core/java/android/service/tracing/TraceReportService.java
new file mode 100644
index 000000000000..3d16a3d41ea3
--- /dev/null
+++ b/core/java/android/service/tracing/TraceReportService.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.tracing;
+
+import static android.annotation.SystemApi.Client.PRIVILEGED_APPS;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.app.Service;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.ParcelFileDescriptor;
+import android.os.Process;
+import android.tracing.TraceReportParams;
+import android.util.Log;
+
+import java.io.IOException;
+import java.util.UUID;
+
+/**
+ * Service to be sub-classed and exposed by (privileged) apps which want to report
+ * system traces.
+ * <p>
+ * Subclasses should implement the onReportTrace method to handle traces reported
+ * to them.
+ * </p>
+ * <pre>
+ * public class SampleReportService extends TraceReportService {
+ * public void onReportTrace(TraceParams args) {
+ * // --- Implementation goes here ---
+ * }
+ * }
+ * </pre>
+ * <p>
+ * The service declaration in the application manifest must specify
+ * BIND_TRACE_REPORT_SERVICE in the permission attribute.
+ * </p>
+ * <pre>
+ * &lt;application>
+ * &lt;service android:name=".SampleReportService"
+ * android:permission="android.permission.BIND_TRACE_REPORT_SERVICE">
+ * &lt;/service>
+ * &lt;/application>
+ * </pre>
+ *
+ * Moreover, the package containing this service must hold the DUMP and PACKAGE_USAGE_STATS
+ * permissions.
+ *
+ * @hide
+ */
+@SystemApi(client = PRIVILEGED_APPS)
+public class TraceReportService extends Service {
+ private static final String TAG = "TraceReportService";
+ private Messenger mMessenger = null;
+
+ /**
+ * Public to allow this to be used by TracingServiceProxy in system_server.
+ *
+ * @hide
+ */
+ public static final int MSG_REPORT_TRACE = 1;
+
+ /**
+ * Contains information about the trace which is being reported.
+ *
+ * @hide
+ */
+ @SystemApi(client = PRIVILEGED_APPS)
+ public static final class TraceParams {
+ private final ParcelFileDescriptor mFd;
+ private final UUID mUuid;
+
+ private TraceParams(TraceReportParams params) {
+ mFd = params.fd;
+ mUuid = new UUID(params.uuidMsb, params.uuidLsb);
+ }
+
+ /**
+ * Returns the ParcelFileDescriptor for the collected trace.
+ */
+ @NonNull
+ public ParcelFileDescriptor getFd() {
+ return mFd;
+ }
+
+ /**
+ * Returns the UUID of the trace; this is exactly the UUID created by the tracing system
+ * (i.e. Perfetto) and is also present inside the trace file.
+ */
+ @NonNull
+ public UUID getUuid() {
+ return mUuid;
+ }
+ }
+
+ // Methods to override.
+ /**
+ * Called when a trace is reported and sent to this class.
+ *
+ * Note: the trace file descriptor should not be persisted beyond the lifetime of this
+ * function as it is owned by the framework and will be closed immediately after this function
+ * returns: if future use of the fd is needed, it should be duped.
+ */
+ public void onReportTrace(@NonNull TraceParams args) {
+ }
+
+ // Optional methods to override.
+ // Realistically, these methods are internal implementation details but since this class is
+ // a SystemApi, it's better to err on the side of flexibility just in-case we need to override
+ // these methods down the line.
+
+ /**
+ * Handles binder calls from system_server.
+ */
+ public boolean onMessage(@NonNull Message msg) {
+ if (msg.what == MSG_REPORT_TRACE) {
+ if (!(msg.obj instanceof TraceReportParams)) {
+ Log.e(TAG, "Received invalid type for report trace message.");
+ return false;
+ }
+ TraceParams params = new TraceParams((TraceReportParams) msg.obj);
+ try {
+ onReportTrace(params);
+ } finally {
+ try {
+ params.getFd().close();
+ } catch (IOException ignored) {
+ }
+ }
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Returns an IBinder for handling binder calls from system_server.
+ */
+ @Nullable
+ @Override
+ public IBinder onBind(@NonNull Intent intent) {
+ if (mMessenger == null) {
+ mMessenger = new Messenger(new Handler(Looper.getMainLooper(), this::onMessage));
+ }
+ return mMessenger.getBinder();
+ }
+} \ No newline at end of file
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index f2a03558663e..c91851a8896d 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -905,11 +905,12 @@ public abstract class WallpaperService extends Service {
if (!ENABLE_WALLPAPER_DIMMING || mBbqSurfaceControl == null) {
return;
}
+
+ SurfaceControl.Transaction surfaceControlTransaction = new SurfaceControl.Transaction();
// TODO: apply the dimming to preview as well once surface transparency works in
// preview mode.
if (!isPreview() && mShouldDim) {
Log.v(TAG, "Setting wallpaper dimming: " + mWallpaperDimAmount);
- SurfaceControl.Transaction surfaceControl = new SurfaceControl.Transaction();
// Animate dimming to gradually change the wallpaper alpha from the previous
// dim amount to the new amount only if the dim amount changed.
@@ -919,16 +920,15 @@ public abstract class WallpaperService extends Service {
? 0 : DIMMING_ANIMATION_DURATION_MS);
animator.addUpdateListener((ValueAnimator va) -> {
final float dimValue = (float) va.getAnimatedValue();
- surfaceControl
- .setAlpha(mBbqSurfaceControl, 1 - dimValue)
- .apply();
+ if (mBbqSurfaceControl != null) {
+ surfaceControlTransaction
+ .setAlpha(mBbqSurfaceControl, 1 - dimValue).apply();
+ }
});
animator.start();
} else {
Log.v(TAG, "Setting wallpaper dimming: " + 0);
- new SurfaceControl.Transaction()
- .setAlpha(mBbqSurfaceControl, 1.0f)
- .apply();
+ surfaceControlTransaction.setAlpha(mBbqSurfaceControl, 1.0f).apply();
}
}
diff --git a/core/java/android/tracing/ITracingServiceProxy.aidl b/core/java/android/tracing/ITracingServiceProxy.aidl
index 4520db3915a2..8029b88226b0 100644
--- a/core/java/android/tracing/ITracingServiceProxy.aidl
+++ b/core/java/android/tracing/ITracingServiceProxy.aidl
@@ -16,17 +16,25 @@
package android.tracing;
+import android.tracing.TraceReportParams;
+
/**
* Binder interface for the TracingServiceProxy running in system_server.
*
* {@hide}
*/
-interface ITracingServiceProxy
-{
+interface ITracingServiceProxy {
/**
* Notifies system tracing app that a tracing session has ended. If a session is repurposed
* for use in a bugreport, sessionStolen can be set to indicate that tracing has ended but
* there is no buffer available to dump.
*/
oneway void notifyTraceSessionEnded(boolean sessionStolen);
+
+ /**
+ * Notifies the specified service that a trace has been captured. The contents of |params|
+ * contains the intended recipient (package and class) of this trace as well as a file
+ * descriptor to an unlinked trace |fd| (i.e. an fd opened using O_TMPFILE).
+ */
+ oneway void reportTrace(in TraceReportParams params);
}
diff --git a/core/java/android/tracing/TraceReportParams.aidl b/core/java/android/tracing/TraceReportParams.aidl
new file mode 100644
index 000000000000..f57386c087ea
--- /dev/null
+++ b/core/java/android/tracing/TraceReportParams.aidl
@@ -0,0 +1,59 @@
+/**
+ * Copyright (c) 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.tracing;
+
+import android.os.ParcelFileDescriptor;
+
+/*
+ * Parameters for a trace report.
+ *
+ * See ITracingServiceProxy::reportTrace for more details.
+ *
+ * @hide
+ */
+parcelable TraceReportParams {
+ // The package name containing the reporter service (see |reporterClassName|).
+ String reporterPackageName;
+
+ // The class name of the reporter service. The framework will bind to this service and pass the
+ // trace fd and metadata to this class.
+ // This class should be "trusted" (in practice this means being a priv_app + having DUMP and
+ // USAGE_STATS permissions).
+ String reporterClassName;
+
+ // The file descriptor for the trace file. This will be an unlinked file fd (i.e. created
+ // with O_TMPFILE); the intention is that reporter classes link this fd into a app-private
+ // folder for reporting when conditions are right (e.g. charging, on unmetered networks etc).
+ ParcelFileDescriptor fd;
+
+ // The least-significant-bytes of the UUID of this trace.
+ long uuidLsb;
+
+ // The most-significant-bytes of the UUID of this trace.
+ long uuidMsb;
+
+ // Flag indicating whether, instead of passing the fd from the trace collector, to pass a
+ // pipe fd from system_server and send the file over it.
+ //
+ // This flag is necessary because there is no good way to write a CTS test where a helper
+ // priv_app (in terms of SELinux) is needed (this is because priv_apps are supposed to be
+ // preinstalled on the system partition). By creating a pipe in system_server we work around
+ // this restriction. Note that there is a maximum allowed file size if this flag is set
+ // (see TracingServiceProxy). Further note that, even though SELinux may be worked around,
+ // manifest (i.e. framework) permissions are still checked even if this flag is set.
+ boolean usePipeForTesting;
+} \ No newline at end of file
diff --git a/core/java/android/util/IconDrawableFactory.java b/core/java/android/util/IconDrawableFactory.java
index b5e8dd7ed0af..5bb263a1f463 100644
--- a/core/java/android/util/IconDrawableFactory.java
+++ b/core/java/android/util/IconDrawableFactory.java
@@ -15,7 +15,12 @@
*/
package android.util;
+import static android.app.admin.DevicePolicyResources.Drawables.Style.SOLID_COLORED;
+import static android.app.admin.DevicePolicyResources.Drawables.UNDEFINED;
+import static android.app.admin.DevicePolicyResources.Drawables.WORK_PROFILE_ICON_BADGE;
+
import android.annotation.UserIdInt;
+import android.app.admin.DevicePolicyManager;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.pm.ApplicationInfo;
@@ -37,6 +42,7 @@ public class IconDrawableFactory {
protected final Context mContext;
protected final PackageManager mPm;
protected final UserManager mUm;
+ protected final DevicePolicyManager mDpm;
protected final LauncherIcons mLauncherIcons;
protected final boolean mEmbedShadow;
@@ -44,6 +50,7 @@ public class IconDrawableFactory {
mContext = context;
mPm = context.getPackageManager();
mUm = context.getSystemService(UserManager.class);
+ mDpm = context.getSystemService(DevicePolicyManager.class);
mLauncherIcons = new LauncherIcons(context);
mEmbedShadow = embedShadow;
}
@@ -73,18 +80,32 @@ public class IconDrawableFactory {
if (appInfo.isInstantApp()) {
int badgeColor = Resources.getSystem().getColor(
com.android.internal.R.color.instant_app_badge, null);
+ Drawable badge = mContext.getDrawable(
+ com.android.internal.R.drawable.ic_instant_icon_badge_bolt);
icon = mLauncherIcons.getBadgedDrawable(icon,
- com.android.internal.R.drawable.ic_instant_icon_badge_bolt,
+ badge,
badgeColor);
}
if (mUm.hasBadge(userId)) {
- icon = mLauncherIcons.getBadgedDrawable(icon,
- mUm.getUserIconBadgeResId(userId),
- mUm.getUserBadgeColor(userId));
+
+ Drawable badge = mDpm.getDrawable(
+ getUpdatableUserIconBadgeId(userId),
+ SOLID_COLORED,
+ () -> getDefaultUserIconBadge(userId));
+
+ icon = mLauncherIcons.getBadgedDrawable(icon, badge, mUm.getUserBadgeColor(userId));
}
return icon;
}
+ private String getUpdatableUserIconBadgeId(int userId) {
+ return mUm.isManagedProfile(userId) ? WORK_PROFILE_ICON_BADGE : UNDEFINED;
+ }
+
+ private Drawable getDefaultUserIconBadge(int userId) {
+ return mContext.getResources().getDrawable(mUm.getUserIconBadgeResId(userId));
+ }
+
/**
* Add shadow to the icon if {@link AdaptiveIconDrawable}
*/
diff --git a/core/java/android/util/LauncherIcons.java b/core/java/android/util/LauncherIcons.java
index e652e17bceb3..355b2e9934fd 100644
--- a/core/java/android/util/LauncherIcons.java
+++ b/core/java/android/util/LauncherIcons.java
@@ -45,10 +45,12 @@ public final class LauncherIcons {
private final SparseArray<Bitmap> mShadowCache = new SparseArray<>();
private final int mIconSize;
private final Resources mRes;
+ private final Context mContext;
public LauncherIcons(Context context) {
mRes = context.getResources();
mIconSize = mRes.getDimensionPixelSize(android.R.dimen.app_icon_size);
+ mContext = context;
}
public Drawable wrapIconDrawableWithShadow(Drawable drawable) {
@@ -98,14 +100,14 @@ public final class LauncherIcons {
return shadow;
}
- public Drawable getBadgeDrawable(int foregroundRes, int backgroundColor) {
- return getBadgedDrawable(null, foregroundRes, backgroundColor);
+ public Drawable getBadgeDrawable(Drawable badgeForeground, int backgroundColor) {
+ return getBadgedDrawable(null, badgeForeground, backgroundColor);
}
- public Drawable getBadgedDrawable(Drawable base, int foregroundRes, int backgroundColor) {
+ public Drawable getBadgedDrawable(
+ Drawable base, Drawable badgeForeground, int backgroundColor) {
Resources overlayableRes =
ActivityThread.currentActivityThread().getApplication().getResources();
-
// ic_corp_icon_badge_shadow is not work-profile-specific.
Drawable badgeShadow = overlayableRes.getDrawable(
com.android.internal.R.drawable.ic_corp_icon_badge_shadow);
@@ -115,7 +117,6 @@ public final class LauncherIcons {
com.android.internal.R.drawable.ic_corp_icon_badge_color)
.getConstantState().newDrawable().mutate();
- Drawable badgeForeground = overlayableRes.getDrawable(foregroundRes);
badgeForeground.setTint(backgroundColor);
Drawable[] drawables = base == null
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index d6e074fbe178..fa39380cdcc1 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -115,14 +115,6 @@ public final class Display {
private int mCachedAppHeightCompat;
/**
- * Indicates that the application is started in a different rotation than the real display, so
- * the display information may be adjusted. That ensures the methods {@link #getRotation},
- * {@link #getRealSize}, {@link #getRealMetrics}, and {@link #getCutout} are consistent with how
- * the application window is laid out.
- */
- private boolean mMayAdjustByFixedRotation;
-
- /**
* Cache if the application is the recents component.
* TODO(b/179308296) Remove once Launcher addresses issue
*/
@@ -916,14 +908,15 @@ public final class Display {
* degrees counter-clockwise, to compensate rendering will be rotated by
* 90 degrees clockwise and thus the returned value here will be
* {@link Surface#ROTATION_90 Surface.ROTATION_90}.
+ *
+ * This rotation value will match the results of {@link #getMetrics}: this means that the
+ * rotation value will correspond to the activity if accessed through the activity.
*/
@Surface.Rotation
public int getRotation() {
synchronized (mLock) {
updateDisplayInfoLocked();
- return mMayAdjustByFixedRotation
- ? getDisplayAdjustments().getRotation(mDisplayInfo.rotation)
- : mDisplayInfo.rotation;
+ return getLocalRotation();
}
}
@@ -959,9 +952,15 @@ public final class Display {
public DisplayCutout getCutout() {
synchronized (mLock) {
updateDisplayInfoLocked();
- return mMayAdjustByFixedRotation
- ? getDisplayAdjustments().getDisplayCutout(mDisplayInfo.displayCutout)
- : mDisplayInfo.displayCutout;
+ if (mResources == null) return mDisplayInfo.displayCutout;
+ final DisplayCutout localCutout = mDisplayInfo.displayCutout;
+ if (localCutout == null) return null;
+ int rotation = getLocalRotation();
+ if (rotation != mDisplayInfo.rotation) {
+ return localCutout.getRotated(mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight,
+ mDisplayInfo.rotation, rotation);
+ }
+ return localCutout;
}
}
@@ -977,15 +976,11 @@ public final class Display {
public RoundedCorner getRoundedCorner(@RoundedCorner.Position int position) {
synchronized (mLock) {
updateDisplayInfoLocked();
- RoundedCorners roundedCorners;
- if (mMayAdjustByFixedRotation) {
- roundedCorners = getDisplayAdjustments().adjustRoundedCorner(
- mDisplayInfo.roundedCorners,
- mDisplayInfo.rotation,
- mDisplayInfo.logicalWidth,
- mDisplayInfo.logicalHeight);
- } else {
- roundedCorners = mDisplayInfo.roundedCorners;
+ final RoundedCorners roundedCorners = mDisplayInfo.roundedCorners;
+ final @Surface.Rotation int rotation = getLocalRotation();
+ if (roundedCorners != null && rotation != mDisplayInfo.rotation) {
+ roundedCorners.rotate(rotation,
+ mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight);
}
return roundedCorners == null ? null : roundedCorners.getRoundedCorner(position);
}
@@ -1468,8 +1463,9 @@ public final class Display {
}
outSize.x = mDisplayInfo.logicalWidth;
outSize.y = mDisplayInfo.logicalHeight;
- if (mMayAdjustByFixedRotation) {
- getDisplayAdjustments().adjustSize(outSize, mDisplayInfo.rotation);
+ final @Surface.Rotation int rotation = getLocalRotation();
+ if (rotation != mDisplayInfo.rotation) {
+ adjustSize(outSize, mDisplayInfo.rotation, rotation);
}
}
}
@@ -1537,8 +1533,9 @@ public final class Display {
}
mDisplayInfo.getLogicalMetrics(outMetrics,
CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null);
- if (mMayAdjustByFixedRotation) {
- getDisplayAdjustments().adjustMetrics(outMetrics, mDisplayInfo.rotation);
+ final @Surface.Rotation int rotation = getLocalRotation();
+ if (rotation != mDisplayInfo.rotation) {
+ adjustMetrics(outMetrics, mDisplayInfo.rotation, rotation);
}
}
}
@@ -1663,9 +1660,6 @@ public final class Display {
}
}
}
-
- mMayAdjustByFixedRotation = mResources != null
- && mResources.hasOverrideDisplayAdjustments();
}
private void updateCachedAppSizeIfNeededLocked() {
@@ -1679,6 +1673,49 @@ public final class Display {
}
}
+ /** Returns {@code false} if the width and height of display should swap. */
+ private static boolean noFlip(@Surface.Rotation int realRotation,
+ @Surface.Rotation int localRotation) {
+ // Check if the delta is rotated by 90 degrees.
+ return (realRotation - localRotation + 4) % 2 == 0;
+ }
+
+ /**
+ * Adjusts the given size by a rotation offset if necessary.
+ * @hide
+ */
+ private void adjustSize(@NonNull Point size, @Surface.Rotation int realRotation,
+ @Surface.Rotation int localRotation) {
+ if (noFlip(realRotation, localRotation)) return;
+ final int w = size.x;
+ size.x = size.y;
+ size.y = w;
+ }
+
+ /**
+ * Adjusts the given metrics by a rotation offset if necessary.
+ * @hide
+ */
+ private void adjustMetrics(@NonNull DisplayMetrics metrics,
+ @Surface.Rotation int realRotation, @Surface.Rotation int localRotation) {
+ if (noFlip(realRotation, localRotation)) return;
+ int w = metrics.widthPixels;
+ metrics.widthPixels = metrics.heightPixels;
+ metrics.heightPixels = w;
+
+ w = metrics.noncompatWidthPixels;
+ metrics.noncompatWidthPixels = metrics.noncompatHeightPixels;
+ metrics.noncompatHeightPixels = w;
+ }
+
+ private @Surface.Rotation int getLocalRotation() {
+ if (mResources == null) return mDisplayInfo.rotation;
+ final @Surface.Rotation int localRotation =
+ mResources.getConfiguration().windowConfiguration.getDisplayRotation();
+ if (localRotation != WindowConfiguration.ROTATION_UNDEFINED) return localRotation;
+ return mDisplayInfo.rotation;
+ }
+
// For debugging purposes
@Override
public String toString() {
@@ -1686,9 +1723,7 @@ public final class Display {
updateDisplayInfoLocked();
final DisplayAdjustments adjustments = getDisplayAdjustments();
mDisplayInfo.getAppMetrics(mTempMetrics, adjustments);
- return "Display id " + mDisplayId + ": " + mDisplayInfo
- + (mMayAdjustByFixedRotation
- ? (", " + adjustments.getFixedRotationAdjustments() + ", ") : ", ")
+ return "Display id " + mDisplayId + ": " + mDisplayInfo + ", "
+ mTempMetrics + ", isValid=" + mIsValid;
}
}
diff --git a/core/java/android/view/DisplayAdjustments.java b/core/java/android/view/DisplayAdjustments.java
index e307eff7a2dd..bb508493226d 100644
--- a/core/java/android/view/DisplayAdjustments.java
+++ b/core/java/android/view/DisplayAdjustments.java
@@ -21,10 +21,6 @@ import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
-import android.graphics.Point;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.util.DisplayMetrics;
import java.util.Objects;
@@ -34,7 +30,6 @@ public class DisplayAdjustments {
private volatile CompatibilityInfo mCompatInfo = CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO;
private final Configuration mConfiguration = new Configuration(Configuration.EMPTY);
- private FixedRotationAdjustments mFixedRotationAdjustments;
@UnsupportedAppUsage
public DisplayAdjustments() {
@@ -49,7 +44,6 @@ public class DisplayAdjustments {
public DisplayAdjustments(@NonNull DisplayAdjustments daj) {
setCompatibilityInfo(daj.mCompatInfo);
mConfiguration.setTo(daj.getConfiguration());
- mFixedRotationAdjustments = daj.mFixedRotationAdjustments;
}
@UnsupportedAppUsage
@@ -90,97 +84,11 @@ public class DisplayAdjustments {
return mConfiguration;
}
- public void setFixedRotationAdjustments(FixedRotationAdjustments fixedRotationAdjustments) {
- mFixedRotationAdjustments = fixedRotationAdjustments;
- }
-
- public FixedRotationAdjustments getFixedRotationAdjustments() {
- return mFixedRotationAdjustments;
- }
-
- /** Returns {@code false} if the width and height of display should swap. */
- private boolean noFlip(@Surface.Rotation int realRotation) {
- final FixedRotationAdjustments rotationAdjustments = mFixedRotationAdjustments;
- if (rotationAdjustments == null) {
- return true;
- }
- // Check if the delta is rotated by 90 degrees.
- return (realRotation - rotationAdjustments.mRotation + 4) % 2 == 0;
- }
-
- /** Adjusts the given size if possible. */
- public void adjustSize(@NonNull Point size, @Surface.Rotation int realRotation) {
- if (noFlip(realRotation)) {
- return;
- }
- final int w = size.x;
- size.x = size.y;
- size.y = w;
- }
-
- /** Adjusts the given metrics if possible. */
- public void adjustMetrics(@NonNull DisplayMetrics metrics, @Surface.Rotation int realRotation) {
- if (noFlip(realRotation)) {
- return;
- }
- int w = metrics.widthPixels;
- metrics.widthPixels = metrics.heightPixels;
- metrics.heightPixels = w;
-
- w = metrics.noncompatWidthPixels;
- metrics.noncompatWidthPixels = metrics.noncompatHeightPixels;
- metrics.noncompatHeightPixels = w;
- }
-
- /** Adjusts global display metrics that is available to applications. */
- public void adjustGlobalAppMetrics(@NonNull DisplayMetrics metrics) {
- final FixedRotationAdjustments rotationAdjustments = mFixedRotationAdjustments;
- if (rotationAdjustments == null) {
- return;
- }
- metrics.noncompatWidthPixels = metrics.widthPixels = rotationAdjustments.mAppWidth;
- metrics.noncompatHeightPixels = metrics.heightPixels = rotationAdjustments.mAppHeight;
- }
-
- /** Returns the adjusted cutout if available. Otherwise the original cutout is returned. */
- @Nullable
- public DisplayCutout getDisplayCutout(@Nullable DisplayCutout realCutout) {
- final FixedRotationAdjustments rotationAdjustments = mFixedRotationAdjustments;
- return rotationAdjustments != null && rotationAdjustments.mRotatedDisplayCutout != null
- ? rotationAdjustments.mRotatedDisplayCutout
- : realCutout;
- }
-
- /**
- * Returns the adjusted {@link RoundedCorners} if available. Otherwise the original
- * {@link RoundedCorners} is returned.
- */
- @Nullable
- public RoundedCorners adjustRoundedCorner(@Nullable RoundedCorners realRoundedCorners,
- @Surface.Rotation int realRotation, int displayWidth, int displayHeight) {
- final FixedRotationAdjustments rotationAdjustments = mFixedRotationAdjustments;
- if (realRoundedCorners == null || rotationAdjustments == null
- || rotationAdjustments.mRotation == realRotation) {
- return realRoundedCorners;
- }
-
- return realRoundedCorners.rotate(
- rotationAdjustments.mRotation, displayWidth, displayHeight);
- }
-
- /** Returns the adjusted rotation if available. Otherwise the original rotation is returned. */
- @Surface.Rotation
- public int getRotation(@Surface.Rotation int realRotation) {
- final FixedRotationAdjustments rotationAdjustments = mFixedRotationAdjustments;
- return rotationAdjustments != null ? rotationAdjustments.mRotation : realRotation;
- }
-
@Override
public int hashCode() {
int hash = 17;
hash = hash * 31 + Objects.hashCode(mCompatInfo);
hash = hash * 31 + Objects.hashCode(mConfiguration);
- hash = hash * 31 + Objects.hashCode(mFixedRotationAdjustments);
return hash;
}
@@ -191,101 +99,6 @@ public class DisplayAdjustments {
}
DisplayAdjustments daj = (DisplayAdjustments)o;
return Objects.equals(daj.mCompatInfo, mCompatInfo)
- && Objects.equals(daj.mConfiguration, mConfiguration)
- && Objects.equals(daj.mFixedRotationAdjustments, mFixedRotationAdjustments);
- }
-
- /**
- * An application can be launched in different rotation than the real display. This class
- * provides the information to adjust the values returned by {@link Display}.
- * @hide
- */
- public static class FixedRotationAdjustments implements Parcelable {
- /** The application-based rotation. */
- @Surface.Rotation
- final int mRotation;
-
- /**
- * The rotated {@link DisplayInfo#appWidth}. The value cannot be simply swapped according
- * to rotation because it minus the region of screen decorations.
- */
- final int mAppWidth;
-
- /** The rotated {@link DisplayInfo#appHeight}. */
- final int mAppHeight;
-
- /** Non-null if the device has cutout. */
- @Nullable
- final DisplayCutout mRotatedDisplayCutout;
-
- public FixedRotationAdjustments(@Surface.Rotation int rotation, int appWidth, int appHeight,
- DisplayCutout cutout) {
- mRotation = rotation;
- mAppWidth = appWidth;
- mAppHeight = appHeight;
- mRotatedDisplayCutout = cutout;
- }
-
- @Override
- public int hashCode() {
- int hash = 17;
- hash = hash * 31 + mRotation;
- hash = hash * 31 + mAppWidth;
- hash = hash * 31 + mAppHeight;
- hash = hash * 31 + Objects.hashCode(mRotatedDisplayCutout);
- return hash;
- }
-
- @Override
- public boolean equals(@Nullable Object o) {
- if (!(o instanceof FixedRotationAdjustments)) {
- return false;
- }
- final FixedRotationAdjustments other = (FixedRotationAdjustments) o;
- return mRotation == other.mRotation
- && mAppWidth == other.mAppWidth && mAppHeight == other.mAppHeight
- && Objects.equals(mRotatedDisplayCutout, other.mRotatedDisplayCutout);
- }
-
- @Override
- public String toString() {
- return "FixedRotationAdjustments{rotation=" + Surface.rotationToString(mRotation)
- + " appWidth=" + mAppWidth + " appHeight=" + mAppHeight
- + " cutout=" + mRotatedDisplayCutout + "}";
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeInt(mRotation);
- dest.writeInt(mAppWidth);
- dest.writeInt(mAppHeight);
- dest.writeTypedObject(
- new DisplayCutout.ParcelableWrapper(mRotatedDisplayCutout), flags);
- }
-
- private FixedRotationAdjustments(Parcel in) {
- mRotation = in.readInt();
- mAppWidth = in.readInt();
- mAppHeight = in.readInt();
- final DisplayCutout.ParcelableWrapper cutoutWrapper =
- in.readTypedObject(DisplayCutout.ParcelableWrapper.CREATOR);
- mRotatedDisplayCutout = cutoutWrapper != null ? cutoutWrapper.get() : null;
- }
-
- public static final Creator<FixedRotationAdjustments> CREATOR =
- new Creator<FixedRotationAdjustments>() {
- public FixedRotationAdjustments createFromParcel(Parcel in) {
- return new FixedRotationAdjustments(in);
- }
-
- public FixedRotationAdjustments[] newArray(int size) {
- return new FixedRotationAdjustments[size];
- }
- };
+ && Objects.equals(daj.mConfiguration, mConfiguration);
}
}
diff --git a/core/java/android/view/IDisplayWindowListener.aidl b/core/java/android/view/IDisplayWindowListener.aidl
index 449e9b325904..67ae7430e0b7 100644
--- a/core/java/android/view/IDisplayWindowListener.aidl
+++ b/core/java/android/view/IDisplayWindowListener.aidl
@@ -63,5 +63,5 @@ oneway interface IDisplayWindowListener {
/**
* Called when the keep clear ares on a display have changed.
*/
- void onKeepClearAreasChanged(int displayId, in List<Rect> keepClearAreas);
+ void onKeepClearAreasChanged(int displayId, in List<Rect> restricted, in List<Rect> unrestricted);
}
diff --git a/core/java/android/view/SurfaceControlViewHost.java b/core/java/android/view/SurfaceControlViewHost.java
index b461faf70296..7e0d887a8f79 100644
--- a/core/java/android/view/SurfaceControlViewHost.java
+++ b/core/java/android/view/SurfaceControlViewHost.java
@@ -26,6 +26,7 @@ import android.graphics.Rect;
import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
+import android.os.RemoteException;
import android.view.accessibility.IAccessibilityEmbeddedConnection;
import android.view.InsetsState;
@@ -180,6 +181,36 @@ public class SurfaceControlViewHost {
return mRemoteInterface;
}
+ /**
+ * Forward a configuration to the remote SurfaceControlViewHost.
+ * This will cause View#onConfigurationChanged to be invoked on the remote
+ * end. This does not automatically cause the SurfaceControlViewHost
+ * to be resized. The root View of a SurfaceControlViewHost
+ * is more akin to a PopupWindow in that the size is user specified
+ * independent of configuration width and height.
+ *
+ * @param c The configuration to forward
+ */
+ public void notifyConfigurationChanged(@NonNull Configuration c) {
+ try {
+ getRemoteInterface().onConfigurationChanged(c);
+ } catch (RemoteException e) {
+ e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * Tear down the remote SurfaceControlViewHost and cause
+ * View#onDetachedFromWindow to be invoked on the other side.
+ */
+ public void notifyDetachedFromWindow() {
+ try {
+ getRemoteInterface().onDispatchDetachedFromWindow();
+ } catch (RemoteException e) {
+ e.rethrowAsRuntimeException();
+ }
+ }
+
@Override
public int describeContents() {
return 0;
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 75592730067a..4ff7e2297ea0 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -8178,9 +8178,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
// to User. Ideally View should handle the event when isVisibleToUser()
// becomes true where it should issue notifyViewEntered().
afm.notifyViewEntered(this);
+ } else {
+ afm.enableFillRequestActivityStarted();
}
} else if (!enter && !isFocused()) {
afm.notifyViewExited(this);
+ } else if (enter) {
+ afm.enableFillRequestActivityStarted();
}
}
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index eaa12e53c321..386b277156e3 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -563,6 +563,8 @@ public final class ViewRootImpl implements ViewParent,
@Nullable
int mContentCaptureEnabled = CONTENT_CAPTURE_ENABLED_NOT_CHECKED;
boolean mPerformContentCapture;
+ boolean mPerformAutoFill;
+
boolean mReportNextDraw;
/**
@@ -842,6 +844,7 @@ public final class ViewRootImpl implements ViewParent,
mPreviousTransparentRegion = new Region();
mFirst = true; // true for the first time the view is added
mPerformContentCapture = true; // also true for the first time the view is added
+ mPerformAutoFill = true;
mAdded = false;
mAttachInfo = new View.AttachInfo(mWindowSession, mWindow, display, this, mHandler, this,
context);
@@ -4353,6 +4356,18 @@ public final class ViewRootImpl implements ViewParent,
if (mPerformContentCapture) {
performContentCaptureInitialReport();
}
+
+ if (mPerformAutoFill) {
+ notifyEnterForAutoFillIfNeeded();
+ }
+ }
+
+ private void notifyEnterForAutoFillIfNeeded() {
+ mPerformAutoFill = false;
+ final AutofillManager afm = getAutofillManager();
+ if (afm != null) {
+ afm.notifyViewEnteredForActivityStarted(mView);
+ }
}
/**
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index aa9ea19b025e..8401b7cdb243 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -295,6 +295,9 @@ public abstract class Window {
private OnWindowDismissedCallback mOnWindowDismissedCallback;
private OnWindowSwipeDismissedCallback mOnWindowSwipeDismissedCallback;
private WindowControllerCallback mWindowControllerCallback;
+ @WindowInsetsController.Appearance
+ private int mSystemBarAppearance;
+ private DecorCallback mDecorCallback;
private OnRestrictedCaptionAreaChangedListener mOnRestrictedCaptionAreaChangedListener;
private Rect mRestrictedCaptionAreaRect;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
@@ -661,6 +664,35 @@ public abstract class Window {
void updateNavigationBarColor(int color);
}
+ /** @hide */
+ public interface DecorCallback {
+ /**
+ * Called from
+ * {@link com.android.internal.policy.DecorView#onSystemBarAppearanceChanged(int)}.
+ *
+ * @param appearance The newly applied appearance.
+ */
+ void onSystemBarAppearanceChanged(@WindowInsetsController.Appearance int appearance);
+
+ /**
+ * Called from
+ * {@link com.android.internal.policy.DecorView#updateColorViews(WindowInsets, boolean)}
+ * when {@link com.android.internal.policy.DecorView#mDrawLegacyNavigationBarBackground} is
+ * being updated.
+ *
+ * @param drawLegacyNavigationBarBackground the new value that is being set to
+ * {@link com.android.internal.policy.DecorView#mDrawLegacyNavigationBarBackground}.
+ * @return The value to be set to
+ * {@link com.android.internal.policy.DecorView#mDrawLegacyNavigationBarBackgroundHandled}
+ * on behalf of the {@link com.android.internal.policy.DecorView}.
+ * {@code true} to tell that the Window can render the legacy navigation bar
+ * background on behalf of the {@link com.android.internal.policy.DecorView}.
+ * {@code false} to let {@link com.android.internal.policy.DecorView} handle it.
+ */
+ boolean onDrawLegacyNavigationBarBackgroundChanged(
+ boolean drawLegacyNavigationBarBackground);
+ }
+
/**
* Callback for clients that want to be aware of where caption draws content.
*/
@@ -985,6 +1017,36 @@ public abstract class Window {
return mWindowControllerCallback;
}
+ /** @hide */
+ public final void setDecorCallback(DecorCallback decorCallback) {
+ mDecorCallback = decorCallback;
+ }
+
+ /** @hide */
+ @WindowInsetsController.Appearance
+ public final int getSystemBarAppearance() {
+ return mSystemBarAppearance;
+ }
+
+ /** @hide */
+ public final void dispatchOnSystemBarAppearanceChanged(
+ @WindowInsetsController.Appearance int appearance) {
+ mSystemBarAppearance = appearance;
+ if (mDecorCallback != null) {
+ mDecorCallback.onSystemBarAppearanceChanged(appearance);
+ }
+ }
+
+ /** @hide */
+ public final boolean onDrawLegacyNavigationBarBackgroundChanged(
+ boolean drawLegacyNavigationBarBackground) {
+ if (mDecorCallback == null) {
+ return false;
+ }
+ return mDecorCallback.onDrawLegacyNavigationBarBackgroundChanged(
+ drawLegacyNavigationBarBackground);
+ }
+
/**
* Set a callback for changes of area where caption will draw its content.
*
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index 0a33d6cd82ea..a31cacfdfd2b 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -710,6 +710,8 @@ public class AccessibilityNodeInfo implements Parcelable {
private static final int BOOLEAN_PROPERTY_IS_TEXT_ENTRY_KEY = 0x0400000;
+ private static final int BOOLEAN_PROPERTY_IS_TEXT_SELECTABLE = 0x0800000;
+
/**
* Bits that provide the id of a virtual descendant of a view.
*/
@@ -2276,6 +2278,38 @@ public class AccessibilityNodeInfo implements Parcelable {
}
/**
+ * Gets if the node has selectable text.
+ *
+ * <p>
+ * Services should use {@link #ACTION_SET_SELECTION} for selection. Editable text nodes must
+ * also be selectable. But not all UIs will populate this field, so services should consider
+ * 'isTextSelectable | isEditable' to ensure they don't miss nodes with selectable text.
+ * </p>
+ *
+ * @see #isEditable
+ * @return True if the node has selectable text.
+ */
+ public boolean isTextSelectable() {
+ return getBooleanProperty(BOOLEAN_PROPERTY_IS_TEXT_SELECTABLE);
+ }
+
+ /**
+ * Sets if the node has selectable text.
+ * <p>
+ * <strong>Note:</strong> Cannot be called from an
+ * {@link android.accessibilityservice.AccessibilityService}.
+ * This class is made immutable before being delivered to an AccessibilityService.
+ * </p>
+ *
+ * @param selectableText True if the node has selectable text, false otherwise.
+ *
+ * @throws IllegalStateException If called from an AccessibilityService.
+ */
+ public void setTextSelectable(boolean selectableText) {
+ setBooleanProperty(BOOLEAN_PROPERTY_IS_TEXT_SELECTABLE, selectableText);
+ }
+
+ /**
* Gets if the node is editable.
*
* @return True if the node is editable, false otherwise.
@@ -4327,8 +4361,12 @@ public class AccessibilityNodeInfo implements Parcelable {
return "ACTION_CANCEL_DRAG";
case R.id.accessibilityActionDragDrop:
return "ACTION_DROP";
- default:
+ default: {
+ if (action == R.id.accessibilityActionShowSuggestions) {
+ return "ACTION_SHOW_SUGGESTIONS";
+ }
return "ACTION_UNKNOWN";
+ }
}
}
@@ -4462,6 +4500,7 @@ public class AccessibilityNodeInfo implements Parcelable {
builder.append("; importantForAccessibility: ").append(isImportantForAccessibility());
builder.append("; visible: ").append(isVisibleToUser());
builder.append("; actions: ").append(mActions);
+ builder.append("; isTextSelectable: ").append(isTextSelectable());
return builder.toString();
}
diff --git a/core/java/android/view/animation/Animation.java b/core/java/android/view/animation/Animation.java
index ab749ee284a8..3914a3c963b6 100644
--- a/core/java/android/view/animation/Animation.java
+++ b/core/java/android/view/animation/Animation.java
@@ -865,6 +865,15 @@ public abstract class Animation implements Cloneable {
}
/**
+ * @return if a window animation has outsets applied to it.
+ *
+ * @hide
+ */
+ public boolean hasExtension() {
+ return false;
+ }
+
+ /**
* If showBackground is {@code true} and this animation is applied on a window, then the windows
* in the animation will animate with the background associated with this window behind them.
*
@@ -942,6 +951,21 @@ public abstract class Animation implements Cloneable {
}
/**
+ * Gets the transformation to apply a specific point in time. Implementations of this method
+ * should always be kept in sync with getTransformation.
+ *
+ * @param normalizedTime time between 0 and 1 where 0 is the start of the animation and 1 the
+ * end.
+ * @param outTransformation A transformation object that is provided by the
+ * caller and will be filled in by the animation.
+ * @hide
+ */
+ public void getTransformationAt(float normalizedTime, Transformation outTransformation) {
+ final float interpolatedTime = mInterpolator.getInterpolation(normalizedTime);
+ applyTransformation(interpolatedTime, outTransformation);
+ }
+
+ /**
* Gets the transformation to apply at a specified point in time. Implementations of this
* method should always replace the specified Transformation or document they are doing
* otherwise.
@@ -987,8 +1011,7 @@ public abstract class Animation implements Cloneable {
normalizedTime = 1.0f - normalizedTime;
}
- final float interpolatedTime = mInterpolator.getInterpolation(normalizedTime);
- applyTransformation(interpolatedTime, outTransformation);
+ getTransformationAt(normalizedTime, outTransformation);
}
if (expired) {
diff --git a/core/java/android/view/animation/AnimationSet.java b/core/java/android/view/animation/AnimationSet.java
index 03c6ca69a592..a2f3544c70ab 100644
--- a/core/java/android/view/animation/AnimationSet.java
+++ b/core/java/android/view/animation/AnimationSet.java
@@ -363,6 +363,26 @@ public class AnimationSet extends Animation {
* The transformation of an animation set is the concatenation of all of its
* component animations.
*
+ * @see android.view.animation.Animation#getTransformationAt
+ * @hide
+ */
+ @Override
+ public void getTransformationAt(float interpolatedTime, Transformation t) {
+ final Transformation temp = mTempTransformation;
+
+ for (int i = mAnimations.size() - 1; i >= 0; --i) {
+ final Animation a = mAnimations.get(i);
+
+ temp.clear();
+ a.getTransformationAt(interpolatedTime, t);
+ t.compose(temp);
+ }
+ }
+
+ /**
+ * The transformation of an animation set is the concatenation of all of its
+ * component animations.
+ *
* @see android.view.animation.Animation#getTransformation
*/
@Override
@@ -517,4 +537,15 @@ public class AnimationSet extends Animation {
public boolean willChangeBounds() {
return (mFlags & PROPERTY_CHANGE_BOUNDS_MASK) == PROPERTY_CHANGE_BOUNDS_MASK;
}
+
+ /** @hide */
+ @Override
+ public boolean hasExtension() {
+ for (Animation animation : mAnimations) {
+ if (animation.hasExtension()) {
+ return true;
+ }
+ }
+ return false;
+ }
}
diff --git a/core/java/android/view/animation/AnimationUtils.java b/core/java/android/view/animation/AnimationUtils.java
index 7ce0f4571f76..7d1dc7660871 100644
--- a/core/java/android/view/animation/AnimationUtils.java
+++ b/core/java/android/view/animation/AnimationUtils.java
@@ -190,6 +190,8 @@ public class AnimationUtils {
anim = new TranslateAnimation(c, attrs);
} else if (name.equals("cliprect")) {
anim = new ClipRectAnimation(c, attrs);
+ } else if (name.equals("extend")) {
+ anim = new ExtendAnimation(c, attrs);
} else {
throw new RuntimeException("Unknown animation name: " + parser.getName());
}
diff --git a/core/java/android/view/animation/ExtendAnimation.java b/core/java/android/view/animation/ExtendAnimation.java
new file mode 100644
index 000000000000..fd627e50ab0e
--- /dev/null
+++ b/core/java/android/view/animation/ExtendAnimation.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.animation;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Insets;
+import android.util.AttributeSet;
+
+/**
+ * An animation that controls the outset of an object.
+ *
+ * @hide
+ */
+public class ExtendAnimation extends Animation {
+ protected Insets mFromInsets = Insets.NONE;
+ protected Insets mToInsets = Insets.NONE;
+
+ private int mFromLeftType = ABSOLUTE;
+ private int mFromTopType = ABSOLUTE;
+ private int mFromRightType = ABSOLUTE;
+ private int mFromBottomType = ABSOLUTE;
+
+ private int mToLeftType = ABSOLUTE;
+ private int mToTopType = ABSOLUTE;
+ private int mToRightType = ABSOLUTE;
+ private int mToBottomType = ABSOLUTE;
+
+ private float mFromLeftValue;
+ private float mFromTopValue;
+ private float mFromRightValue;
+ private float mFromBottomValue;
+
+ private float mToLeftValue;
+ private float mToTopValue;
+ private float mToRightValue;
+ private float mToBottomValue;
+
+ /**
+ * Constructor used when an ExtendAnimation is loaded from a resource.
+ *
+ * @param context Application context to use
+ * @param attrs Attribute set from which to read values
+ */
+ public ExtendAnimation(Context context, AttributeSet attrs) {
+ super(context, attrs);
+
+ TypedArray a = context.obtainStyledAttributes(attrs,
+ com.android.internal.R.styleable.ExtendAnimation);
+
+ Description d = Description.parseValue(a.peekValue(
+ com.android.internal.R.styleable.ExtendAnimation_fromExtendLeft));
+ mFromLeftType = d.type;
+ mFromLeftValue = d.value;
+
+ d = Description.parseValue(a.peekValue(
+ com.android.internal.R.styleable.ExtendAnimation_fromExtendTop));
+ mFromTopType = d.type;
+ mFromTopValue = d.value;
+
+ d = Description.parseValue(a.peekValue(
+ com.android.internal.R.styleable.ExtendAnimation_fromExtendRight));
+ mFromRightType = d.type;
+ mFromRightValue = d.value;
+
+ d = Description.parseValue(a.peekValue(
+ com.android.internal.R.styleable.ExtendAnimation_fromExtendBottom));
+ mFromBottomType = d.type;
+ mFromBottomValue = d.value;
+
+
+ d = Description.parseValue(a.peekValue(
+ com.android.internal.R.styleable.ExtendAnimation_toExtendLeft));
+ mToLeftType = d.type;
+ mToLeftValue = d.value;
+
+ d = Description.parseValue(a.peekValue(
+ com.android.internal.R.styleable.ExtendAnimation_toExtendTop));
+ mToTopType = d.type;
+ mToTopValue = d.value;
+
+ d = Description.parseValue(a.peekValue(
+ com.android.internal.R.styleable.ExtendAnimation_toExtendRight));
+ mToRightType = d.type;
+ mToRightValue = d.value;
+
+ d = Description.parseValue(a.peekValue(
+ com.android.internal.R.styleable.ExtendAnimation_toExtendBottom));
+ mToBottomType = d.type;
+ mToBottomValue = d.value;
+
+ a.recycle();
+ }
+
+ /**
+ * Constructor to use when building an ExtendAnimation from code
+ *
+ * @param fromInsets the insets to animate from
+ * @param toInsets the insets to animate to
+ */
+ public ExtendAnimation(Insets fromInsets, Insets toInsets) {
+ if (fromInsets == null || toInsets == null) {
+ throw new RuntimeException("Expected non-null animation outsets");
+ }
+ mFromLeftValue = -fromInsets.left;
+ mFromTopValue = -fromInsets.top;
+ mFromRightValue = -fromInsets.right;
+ mFromBottomValue = -fromInsets.bottom;
+
+ mToLeftValue = -toInsets.left;
+ mToTopValue = -toInsets.top;
+ mToRightValue = -toInsets.right;
+ mToBottomValue = -toInsets.bottom;
+ }
+
+ /**
+ * Constructor to use when building an ExtendAnimation from code
+ */
+ public ExtendAnimation(int fromL, int fromT, int fromR, int fromB,
+ int toL, int toT, int toR, int toB) {
+ this(Insets.of(-fromL, -fromT, -fromR, -fromB), Insets.of(-toL, -toT, -toR, -toB));
+ }
+
+ @Override
+ protected void applyTransformation(float it, Transformation tr) {
+ int l = mFromInsets.left + (int) ((mToInsets.left - mFromInsets.left) * it);
+ int t = mFromInsets.top + (int) ((mToInsets.top - mFromInsets.top) * it);
+ int r = mFromInsets.right + (int) ((mToInsets.right - mFromInsets.right) * it);
+ int b = mFromInsets.bottom + (int) ((mToInsets.bottom - mFromInsets.bottom) * it);
+ tr.setInsets(l, t, r, b);
+ }
+
+ @Override
+ public boolean willChangeTransformationMatrix() {
+ return false;
+ }
+
+ /** @hide */
+ @Override
+ public boolean hasExtension() {
+ return mFromInsets.left < 0 || mFromInsets.top < 0 || mFromInsets.right < 0
+ || mFromInsets.bottom < 0;
+ }
+
+ @Override
+ public void initialize(int width, int height, int parentWidth, int parentHeight) {
+ super.initialize(width, height, parentWidth, parentHeight);
+ // We remove any negative extension (i.e. positive insets) and set those to 0
+ mFromInsets = Insets.min(Insets.of(
+ -(int) resolveSize(mFromLeftType, mFromLeftValue, width, parentWidth),
+ -(int) resolveSize(mFromTopType, mFromTopValue, height, parentHeight),
+ -(int) resolveSize(mFromRightType, mFromRightValue, width, parentWidth),
+ -(int) resolveSize(mFromBottomType, mFromBottomValue, height, parentHeight)
+ ), Insets.NONE);
+ mToInsets = Insets.min(Insets.of(
+ -(int) resolveSize(mToLeftType, mToLeftValue, width, parentWidth),
+ -(int) resolveSize(mToTopType, mToTopValue, height, parentHeight),
+ -(int) resolveSize(mToRightType, mToRightValue, width, parentWidth),
+ -(int) resolveSize(mToBottomType, mToBottomValue, height, parentHeight)
+ ), Insets.NONE);
+ }
+}
diff --git a/core/java/android/view/animation/Transformation.java b/core/java/android/view/animation/Transformation.java
index b35a66e6eb26..bd623088017a 100644
--- a/core/java/android/view/animation/Transformation.java
+++ b/core/java/android/view/animation/Transformation.java
@@ -18,6 +18,7 @@ package android.view.animation;
import android.annotation.FloatRange;
import android.compat.annotation.UnsupportedAppUsage;
+import android.graphics.Insets;
import android.graphics.Matrix;
import android.graphics.Rect;
@@ -53,6 +54,8 @@ public class Transformation {
private boolean mHasClipRect;
private Rect mClipRect = new Rect();
+ private Insets mInsets = Insets.NONE;
+
/**
* Creates a new transformation with alpha = 1 and the identity matrix.
*/
@@ -132,8 +135,9 @@ public class Transformation {
setClipRect(bounds);
}
}
+ setInsets(Insets.add(getInsets(), t.getInsets()));
}
-
+
/**
* Like {@link #compose(Transformation)} but does this.postConcat(t) of
* the transformation matrix.
@@ -160,7 +164,7 @@ public class Transformation {
public Matrix getMatrix() {
return mMatrix;
}
-
+
/**
* Sets the degree of transparency
* @param alpha 1.0 means fully opaqe and 0.0 means fully transparent
@@ -170,6 +174,13 @@ public class Transformation {
}
/**
+ * @return The degree of transparency
+ */
+ public float getAlpha() {
+ return mAlpha;
+ }
+
+ /**
* Sets the current Transform's clip rect
* @hide
*/
@@ -203,12 +214,29 @@ public class Transformation {
}
/**
- * @return The degree of transparency
+ * Sets the current Transform's insets
+ * @hide
*/
- public float getAlpha() {
- return mAlpha;
+ public void setInsets(Insets insets) {
+ mInsets = insets;
}
-
+
+ /**
+ * Sets the current Transform's insets
+ * @hide
+ */
+ public void setInsets(int left, int top, int right, int bottom) {
+ mInsets = Insets.of(left, top, right, bottom);
+ }
+
+ /**
+ * Returns the current Transform's outset rect
+ * @hide
+ */
+ public Insets getInsets() {
+ return mInsets;
+ }
+
@Override
public String toString() {
StringBuilder sb = new StringBuilder(64);
@@ -216,7 +244,7 @@ public class Transformation {
toShortString(sb);
return sb.toString();
}
-
+
/**
* Return a string representation of the transformation in a compact form.
*/
@@ -225,7 +253,7 @@ public class Transformation {
toShortString(sb);
return sb.toString();
}
-
+
/**
* @hide
*/
@@ -234,7 +262,7 @@ public class Transformation {
sb.append(" matrix="); sb.append(mMatrix.toShortString());
sb.append('}');
}
-
+
/**
* Print short string, to optimize dumping.
* @hide
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index bb13c1e78964..60ccf67249b7 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -16,6 +16,7 @@
package android.view.autofill;
+import static android.service.autofill.FillRequest.FLAG_ACTIVITY_START;
import static android.service.autofill.FillRequest.FLAG_MANUAL_REQUEST;
import static android.service.autofill.FillRequest.FLAG_PASSWORD_INPUT_TYPE;
import static android.service.autofill.FillRequest.FLAG_VIEW_NOT_FOCUSED;
@@ -538,6 +539,8 @@ public final class AutofillManager {
*/
public static final int NO_SESSION = Integer.MAX_VALUE;
+ private static final boolean HAS_FILL_DIALOG_UI_FEATURE = false;
+
private final IAutoFillManager mService;
private final Object mLock = new Object();
@@ -629,6 +632,29 @@ public final class AutofillManager {
@GuardedBy("mLock")
@Nullable private Executor mRequestCallbackExecutor;
+ /**
+ * Indicates whether there are any fields that need to do a fill request
+ * after the activity starts.
+ *
+ * Note: This field will be set to true multiple times if there are many
+ * autofillable views. So needs to check mIsFillRequested at the same time to
+ * avoid re-trigger autofill.
+ */
+ private boolean mRequireAutofill;
+
+ /**
+ * Indicates whether there is already a field to do a fill request after
+ * the activity started.
+ *
+ * Autofill will automatically trigger a fill request after activity
+ * start if there is any field is autofillable. But if there is a field that
+ * triggered autofill, it is unnecessary to trigger again through
+ * AutofillManager#notifyViewEnteredForActivityStarted.
+ */
+ private boolean mIsFillRequested;
+
+ @Nullable private List<AutofillId> mFillDialogTriggerIds;
+
/** @hide */
public interface AutofillClient {
/**
@@ -766,6 +792,8 @@ public final class AutofillManager {
mContext = Objects.requireNonNull(context, "context cannot be null");
mService = service;
mOptions = context.getAutofillOptions();
+ mIsFillRequested = false;
+ mRequireAutofill = false;
if (mOptions != null) {
sDebug = (mOptions.loggingLevel & FLAG_ADD_CLIENT_DEBUG) != 0;
@@ -1042,6 +1070,39 @@ public final class AutofillManager {
notifyViewEntered(view, 0);
}
+ /**
+ * The view is autofillable, marked to perform a fill request after layout if
+ * the field does not trigger a fill request.
+ *
+ * @hide
+ */
+ public void enableFillRequestActivityStarted() {
+ mRequireAutofill = true;
+ }
+
+ private boolean hasFillDialogUiFeature() {
+ return HAS_FILL_DIALOG_UI_FEATURE;
+ }
+
+ /**
+ * Notify autofill to do a fill request while the activity started.
+ *
+ * @hide
+ */
+ public void notifyViewEnteredForActivityStarted(@NonNull View view) {
+ if (!hasAutofillFeature() || !hasFillDialogUiFeature()) {
+ return;
+ }
+
+ if (!mRequireAutofill || mIsFillRequested) {
+ return;
+ }
+
+ int flags = FLAG_ACTIVITY_START;
+ flags |= FLAG_VIEW_NOT_FOCUSED;
+ notifyViewEntered(view, flags);
+ }
+
@GuardedBy("mLock")
private boolean shouldIgnoreViewEnteredLocked(@NonNull AutofillId id, int flags) {
if (isDisabledByServiceLocked()) {
@@ -1082,6 +1143,7 @@ public final class AutofillManager {
}
AutofillCallback callback;
synchronized (mLock) {
+ mIsFillRequested = true;
callback = notifyViewEnteredLocked(view, flags);
}
@@ -2026,6 +2088,8 @@ public final class AutofillManager {
mFillableIds = null;
mSaveTriggerId = null;
mIdShownFillUi = null;
+ mIsFillRequested = false;
+ mRequireAutofill = false;
if (resetEnteredIds) {
mEnteredIds = null;
}
@@ -3031,6 +3095,29 @@ public final class AutofillManager {
client.autofillClientRunOnUiThread(runnable);
}
+ private void setFillDialogTriggerIds(@Nullable List<AutofillId> ids) {
+ mFillDialogTriggerIds = ids;
+ }
+
+ /**
+ * Checks the id of autofill whether supported the fill dialog.
+ *
+ * @hide
+ */
+ public boolean isShowFillDialog(AutofillId id) {
+ if (!hasFillDialogUiFeature() || mFillDialogTriggerIds == null) {
+ return false;
+ }
+ final int size = mFillDialogTriggerIds.size();
+ for (int i = 0; i < size; i++) {
+ AutofillId fillId = mFillDialogTriggerIds.get(i);
+ if (fillId.equalsIgnoreSession(id)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
/**
* Implementation of the accessibility based compatibility.
*/
@@ -3736,6 +3823,13 @@ public final class AutofillManager {
new FillCallback(callback, id));
}
}
+
+ public void notifyFillDialogTriggerIds(List<AutofillId> ids) {
+ final AutofillManager afm = mAfm.get();
+ if (afm != null) {
+ afm.post(() -> afm.setFillDialogTriggerIds(ids));
+ }
+ }
}
private static final class AugmentedAutofillManagerClient
diff --git a/core/java/android/view/autofill/IAutoFillManagerClient.aidl b/core/java/android/view/autofill/IAutoFillManagerClient.aidl
index 64507aac54cb..2e5967cc32d1 100644
--- a/core/java/android/view/autofill/IAutoFillManagerClient.aidl
+++ b/core/java/android/view/autofill/IAutoFillManagerClient.aidl
@@ -148,4 +148,9 @@ oneway interface IAutoFillManagerClient {
*/
void requestFillFromClient(int id, in InlineSuggestionsRequest request,
in IFillCallback callback);
+
+ /**
+ * Notifies autofill ids that require to show the fill dialog.
+ */
+ void notifyFillDialogTriggerIds(in List<AutofillId> ids);
}
diff --git a/core/java/android/view/contentcapture/ContentCaptureSession.java b/core/java/android/view/contentcapture/ContentCaptureSession.java
index 955269160e49..2134d819943e 100644
--- a/core/java/android/view/contentcapture/ContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/ContentCaptureSession.java
@@ -291,6 +291,8 @@ public abstract class ContentCaptureSession implements AutoCloseable {
* <p>Typically used to change the context associated with the default session from an activity.
*/
public final void setContentCaptureContext(@Nullable ContentCaptureContext context) {
+ if (!isContentCaptureEnabled()) return;
+
mClientContext = context;
updateContentCaptureContext(context);
}
diff --git a/core/java/android/view/inputmethod/InputMethod.java b/core/java/android/view/inputmethod/InputMethod.java
index ff6903e814b7..08cc31c8534f 100644
--- a/core/java/android/view/inputmethod/InputMethod.java
+++ b/core/java/android/view/inputmethod/InputMethod.java
@@ -105,12 +105,14 @@ public interface InputMethod {
* current IME.
* @param configChanges {@link InputMethodInfo#getConfigChanges()} declared by IME.
* @param stylusHwSupported {@link InputMethodInfo#supportsStylusHandwriting()} declared by IME.
+ * @param shouldShowImeSwitcherWhenImeIsShown {@code true} If the IME switcher is expected to be
+ * shown while the IME is shown.
* @hide
*/
@MainThread
default void initializeInternal(IBinder token,
IInputMethodPrivilegedOperations privilegedOperations, int configChanges,
- boolean stylusHwSupported) {
+ boolean stylusHwSupported, boolean shouldShowImeSwitcherWhenImeIsShown) {
attachToken(token);
}
@@ -229,6 +231,8 @@ public interface InputMethod {
* the next {@link #startInput(InputConnection, EditorInfo, IBinder)} as
* long as your implementation of {@link InputMethod} relies on such
* IPCs
+ * @param shouldShowImeSwitcherWhenImeIsShown {@code true} If the IME switcher is expected to be
+ * shown while the IME is shown.
* @see #startInput(InputConnection, EditorInfo)
* @see #restartInput(InputConnection, EditorInfo)
* @see EditorInfo
@@ -237,7 +241,7 @@ public interface InputMethod {
@MainThread
default void dispatchStartInputWithToken(@Nullable InputConnection inputConnection,
@NonNull EditorInfo editorInfo, boolean restarting,
- @NonNull IBinder startInputToken) {
+ @NonNull IBinder startInputToken, boolean shouldShowImeSwitcherWhenImeIsShown) {
if (restarting) {
restartInput(inputConnection, editorInfo);
} else {
@@ -246,6 +250,18 @@ public interface InputMethod {
}
/**
+ * Notifies that whether the IME should show the IME switcher or not is being changed.
+ *
+ * @param shouldShowImeSwitcherWhenImeIsShown {@code true} If the IME switcher is expected to be
+ * shown while the IME is shown.
+ * @hide
+ */
+ @MainThread
+ default void onShouldShowImeSwitcherWhenImeIsShownChanged(
+ boolean shouldShowImeSwitcherWhenImeIsShown) {
+ }
+
+ /**
* Create a new {@link InputMethodSession} that can be handed to client
* applications for interacting with the input method. You can later
* use {@link #revokeSession(InputMethodSession)} to destroy the session
diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java
index f14c25140d10..280e7dfeac9a 100644
--- a/core/java/android/webkit/WebSettings.java
+++ b/core/java/android/webkit/WebSettings.java
@@ -141,7 +141,7 @@ public abstract class WebSettings {
* and WebView to attempt to darken web content by algorithmic darkening when
* appropriate.
*
- * Refer to {@link #setAllowAlgorithmicDarkening} for detail.
+ * Refer to {@link #setAlgorithmicDarkeningAllowed} for detail.
*
* @hide
*/
@@ -1558,7 +1558,7 @@ public abstract class WebSettings {
* {@code targetSdkVersion} &ge; {@link android.os.Build.VERSION_CODES#TIRAMISU}
* this API is a no-op and WebView will always use the dark style defined by web content
* authors if the app's theme is dark. To customize the behavior, refer to
- * {@link #setAllowAlgorithmicDarkening}.
+ * {@link #setAlgorithmicDarkeningAllowed}.
*/
public void setForceDark(@ForceDark int forceDark) {
// Stub implementation to satisfy Roboelectrc shadows that don't override this yet.
@@ -1604,7 +1604,7 @@ public abstract class WebSettings {
*
* @param allow allow algorithmic darkening or not.
*/
- public void setAllowAlgorithmicDarkening(boolean allow) {
+ public void setAlgorithmicDarkeningAllowed(boolean allow) {
// Stub implementation to satisfy Roboelectrc shadows that don't override this yet.
}
@@ -1613,9 +1613,9 @@ public abstract class WebSettings {
* The default is false.
*
* @return if the algorithmic darkening is allowed or not.
- * @see #setAllowAlgorithmicDarkening
+ * @see #setAlgorithmicDarkeningAllowed
*/
- public boolean getAllowAlgorithmicDarkening() {
+ public boolean isAlgorithmicDarkeningAllowed() {
// Stub implementation to satisfy Roboelectrc shadows that don't override this yet.
return false;
}
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index c6f64f4ad633..b00a3829f468 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -301,6 +301,13 @@ public class RemoteViews implements Parcelable, Filter {
public static final int FLAG_USE_LIGHT_BACKGROUND_LAYOUT = 4;
/**
+ * This mask determines which flags are propagated to nested RemoteViews (either added by
+ * addView, or set as portrait/landscape/sized RemoteViews).
+ */
+ static final int FLAG_MASK_TO_PROPAGATE =
+ FLAG_WIDGET_IS_COLLECTION_CHILD | FLAG_USE_LIGHT_BACKGROUND_LAYOUT;
+
+ /**
* A ReadWriteHelper which has the same behavior as ReadWriteHelper.DEFAULT, but which is
* intentionally a different instance in order to trick Bundle reader so that it doesn't allow
* lazy initialization.
@@ -467,6 +474,18 @@ public class RemoteViews implements Parcelable, Filter {
*/
public void addFlags(@ApplyFlags int flags) {
mApplyFlags = mApplyFlags | flags;
+
+ int flagsToPropagate = flags & FLAG_MASK_TO_PROPAGATE;
+ if (flagsToPropagate != 0) {
+ if (hasSizedRemoteViews()) {
+ for (RemoteViews remoteView : mSizedRemoteViews) {
+ remoteView.addFlags(flagsToPropagate);
+ }
+ } else if (hasLandscapeAndPortraitLayouts()) {
+ mLandscape.addFlags(flagsToPropagate);
+ mPortrait.addFlags(flagsToPropagate);
+ }
+ }
}
/**
@@ -2407,6 +2426,10 @@ public class RemoteViews implements Parcelable, Filter {
// will return -1.
final int nextChild = getNextRecyclableChild(target);
RemoteViews rvToApply = mNestedViews.getRemoteViewsToApply(context);
+
+ int flagsToPropagate = mApplyFlags & FLAG_MASK_TO_PROPAGATE;
+ if (flagsToPropagate != 0) rvToApply.addFlags(flagsToPropagate);
+
if (nextChild >= 0 && mStableId != NO_ID) {
// At that point, the views starting at index nextChild are the ones recyclable but
// not yet recycled. All views added on that round of application are placed before.
@@ -2419,8 +2442,8 @@ public class RemoteViews implements Parcelable, Filter {
target.removeViews(nextChild, recycledViewIndex - nextChild);
}
setNextRecyclableChild(target, nextChild + 1, target.getChildCount());
- rvToApply.reapply(context, child, handler, null /* size */, colorResources,
- false /* topLevel */);
+ rvToApply.reapplyNestedViews(context, child, rootParent, handler,
+ null /* size */, colorResources);
return;
}
// If we cannot recycle the views, we still remove all views in between to
@@ -2431,8 +2454,8 @@ public class RemoteViews implements Parcelable, Filter {
// If we cannot recycle, insert the new view before the next recyclable child.
// Inflate nested views and add as children
- View nestedView = rvToApply.apply(context, target, handler, null /* size */,
- colorResources);
+ View nestedView = rvToApply.applyNestedViews(context, target, rootParent, handler,
+ null /* size */, colorResources);
if (mStableId != NO_ID) {
setStableId(nestedView, mStableId);
}
@@ -3780,7 +3803,7 @@ public class RemoteViews implements Parcelable, Filter {
* @param parcel
*/
public RemoteViews(Parcel parcel) {
- this(parcel, /* rootParent= */ null, /* info= */ null, /* depth= */ 0);
+ this(parcel, /* rootData= */ null, /* info= */ null, /* depth= */ 0);
}
private RemoteViews(@NonNull Parcel parcel, @Nullable HierarchyRootData rootData,
@@ -5580,6 +5603,16 @@ public class RemoteViews implements Parcelable, Filter {
return result;
}
+ private View applyNestedViews(Context context, ViewGroup directParent,
+ ViewGroup rootParent, InteractionHandler handler, SizeF size,
+ ColorResources colorResources) {
+ RemoteViews rvToApply = getRemoteViewsToApply(context, size);
+
+ View result = inflateView(context, rvToApply, directParent, 0, colorResources);
+ rvToApply.performApply(result, rootParent, handler, colorResources);
+ return result;
+ }
+
private View inflateView(Context context, RemoteViews rv, ViewGroup parent) {
return inflateView(context, rv, parent, 0, null);
}
@@ -5895,6 +5928,12 @@ public class RemoteViews implements Parcelable, Filter {
}
}
+ private void reapplyNestedViews(Context context, View v, ViewGroup rootParent,
+ InteractionHandler handler, SizeF size, ColorResources colorResources) {
+ RemoteViews rvToApply = getRemoteViewsToReapply(context, v, size);
+ rvToApply.performApply(v, rootParent, handler, colorResources);
+ }
+
/**
* Applies all the actions to the provided view, moving as much of the task on the background
* thread as possible.
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 41c540116928..0fe2ed51beb6 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -4461,7 +4461,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
* pixel" units. This size is adjusted based on the current density and
* user font size preference.
*
- * <p>Note: if this TextView has the auto-size feature enabled than this function is no-op.
+ * <p>Note: if this TextView has the auto-size feature enabled, then this function is no-op.
*
* @param size The scaled pixel size.
*
@@ -4476,7 +4476,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
* Set the default text size to a given unit and value. See {@link
* TypedValue} for the possible dimension units.
*
- * <p>Note: if this TextView has the auto-size feature enabled than this function is no-op.
+ * <p>Note: if this TextView has the auto-size feature enabled, then this function is no-op.
*
* @param unit The desired dimension unit.
* @param size The desired size in the given units.
@@ -12289,6 +12289,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
EXTRA_DATA_RENDERING_INFO_KEY,
EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY
));
+ info.setTextSelectable(isTextSelectable());
} else {
info.setAvailableExtraData(Arrays.asList(
EXTRA_DATA_RENDERING_INFO_KEY
diff --git a/core/java/android/window/ConfigurationHelper.java b/core/java/android/window/ConfigurationHelper.java
index 9a079751553f..3a3eb74bf2e3 100644
--- a/core/java/android/window/ConfigurationHelper.java
+++ b/core/java/android/window/ConfigurationHelper.java
@@ -21,6 +21,7 @@ import static android.view.Display.INVALID_DISPLAY;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ResourcesManager;
+import android.app.WindowConfiguration;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
@@ -99,6 +100,10 @@ public class ConfigurationHelper {
if (shouldUpdateWindowMetricsBounds(config, newConfig)) {
return true;
}
+ // If the display rotation has changed, we also need to update resources.
+ if (isDisplayRotationChanged(config, newConfig)) {
+ return true;
+ }
return configChanged == null ? config.diff(newConfig) != 0 : configChanged;
}
@@ -129,4 +134,15 @@ public class ConfigurationHelper {
return !currentBounds.equals(newBounds) || !currentMaxBounds.equals(newMaxBounds);
}
+
+ private static boolean isDisplayRotationChanged(@NonNull Configuration config,
+ @NonNull Configuration newConfig) {
+ final int origRot = config.windowConfiguration.getDisplayRotation();
+ final int newRot = newConfig.windowConfiguration.getDisplayRotation();
+ if (newRot == WindowConfiguration.ROTATION_UNDEFINED
+ || origRot == WindowConfiguration.ROTATION_UNDEFINED) {
+ return false;
+ }
+ return origRot != newRot;
+ }
}
diff --git a/core/java/android/window/TransitionInfo.java b/core/java/android/window/TransitionInfo.java
index fd1e84822193..3fa62e017976 100644
--- a/core/java/android/window/TransitionInfo.java
+++ b/core/java/android/window/TransitionInfo.java
@@ -599,6 +599,7 @@ public final class TransitionInfo implements Parcelable {
private final Rect mTransitionBounds = new Rect();
private HardwareBuffer mThumbnail;
private int mAnimations;
+ private @ColorInt int mBackgroundColor;
private AnimationOptions(int type) {
mType = type;
@@ -608,6 +609,7 @@ public final class TransitionInfo implements Parcelable {
mType = in.readInt();
mEnterResId = in.readInt();
mExitResId = in.readInt();
+ mBackgroundColor = in.readInt();
mOverrideTaskTransition = in.readBoolean();
mPackageName = in.readString();
mTransitionBounds.readFromParcel(in);
@@ -624,11 +626,12 @@ public final class TransitionInfo implements Parcelable {
}
public static AnimationOptions makeCustomAnimOptions(String packageName, int enterResId,
- int exitResId, boolean overrideTaskTransition) {
+ int exitResId, @ColorInt int backgroundColor, boolean overrideTaskTransition) {
AnimationOptions options = new AnimationOptions(ANIM_CUSTOM);
options.mPackageName = packageName;
options.mEnterResId = enterResId;
options.mExitResId = exitResId;
+ options.mBackgroundColor = backgroundColor;
options.mOverrideTaskTransition = overrideTaskTransition;
return options;
}
@@ -673,6 +676,10 @@ public final class TransitionInfo implements Parcelable {
return mExitResId;
}
+ public @ColorInt int getBackgroundColor() {
+ return mBackgroundColor;
+ }
+
public boolean getOverrideTaskTransition() {
return mOverrideTaskTransition;
}
@@ -698,6 +705,7 @@ public final class TransitionInfo implements Parcelable {
dest.writeInt(mType);
dest.writeInt(mEnterResId);
dest.writeInt(mExitResId);
+ dest.writeInt(mBackgroundColor);
dest.writeBoolean(mOverrideTaskTransition);
dest.writeString(mPackageName);
mTransitionBounds.writeToParcel(dest, flags);
@@ -740,7 +748,7 @@ public final class TransitionInfo implements Parcelable {
@Override
public String toString() {
- return "{ AnimationOtions type= " + typeToString(mType) + " package=" + mPackageName
+ return "{ AnimationOptions type= " + typeToString(mType) + " package=" + mPackageName
+ " override=" + mOverrideTaskTransition + " b=" + mTransitionBounds + "}";
}
}
diff --git a/core/java/com/android/internal/app/BlockedAppActivity.java b/core/java/com/android/internal/app/BlockedAppActivity.java
index fbdbbfb06b78..65526eba3e54 100644
--- a/core/java/com/android/internal/app/BlockedAppActivity.java
+++ b/core/java/com/android/internal/app/BlockedAppActivity.java
@@ -17,6 +17,7 @@
package com.android.internal.app;
import android.content.Intent;
+import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.os.Bundle;
@@ -35,6 +36,9 @@ public class BlockedAppActivity extends AlertActivity {
private static final String TAG = "BlockedAppActivity";
private static final String PACKAGE_NAME = "com.android.internal.app";
private static final String EXTRA_BLOCKED_PACKAGE = PACKAGE_NAME + ".extra.BLOCKED_PACKAGE";
+ private static final String EXTRA_BLOCKED_ACTIVITY_INFO =
+ PACKAGE_NAME + ".extra.BLOCKED_ACTIVITY_INFO";
+ private static final String EXTRA_STREAMED_DEVICE = PACKAGE_NAME + ".extra.STREAMED_DEVICE";
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -48,17 +52,30 @@ public class BlockedAppActivity extends AlertActivity {
return;
}
+ CharSequence appLabel = null;
String packageName = intent.getStringExtra(EXTRA_BLOCKED_PACKAGE);
- if (TextUtils.isEmpty(packageName)) {
- Slog.wtf(TAG, "Invalid package: " + packageName);
+ ActivityInfo activityInfo = intent.getParcelableExtra(EXTRA_BLOCKED_ACTIVITY_INFO);
+ if (activityInfo != null) {
+ appLabel = activityInfo.loadLabel(getPackageManager());
+ } else if (!TextUtils.isEmpty(packageName)) {
+ appLabel = getAppLabel(userId, packageName);
+ }
+
+ if (TextUtils.isEmpty(appLabel)) {
+ Slog.wtf(TAG, "Invalid package: " + packageName + " or activity info: " + activityInfo);
finish();
return;
}
- CharSequence appLabel = getAppLabel(userId, packageName);
-
- mAlertParams.mTitle = getString(R.string.app_blocked_title);
- mAlertParams.mMessage = getString(R.string.app_blocked_message, appLabel);
+ CharSequence streamedDeviceName = intent.getCharSequenceExtra(EXTRA_STREAMED_DEVICE);
+ if (!TextUtils.isEmpty(streamedDeviceName)) {
+ mAlertParams.mTitle = getString(R.string.app_streaming_blocked_title, appLabel);
+ mAlertParams.mMessage =
+ getString(R.string.app_streaming_blocked_message, streamedDeviceName);
+ } else {
+ mAlertParams.mTitle = getString(R.string.app_blocked_title);
+ mAlertParams.mMessage = getString(R.string.app_blocked_message, appLabel);
+ }
mAlertParams.mPositiveButtonText = getString(android.R.string.ok);
setupAlert();
}
@@ -83,4 +100,19 @@ public class BlockedAppActivity extends AlertActivity {
.putExtra(Intent.EXTRA_USER_ID, userId)
.putExtra(EXTRA_BLOCKED_PACKAGE, packageName);
}
+
+ /**
+ * Creates an intent that launches {@link BlockedAppActivity} when app streaming is blocked.
+ *
+ * Using this method and providing a non-empty {@code streamedDeviceName} will cause the dialog
+ * to use streaming-specific error messages.
+ */
+ public static Intent createStreamingBlockedIntent(int userId, ActivityInfo activityInfo,
+ CharSequence streamedDeviceName) {
+ return new Intent()
+ .setClassName("android", BlockedAppActivity.class.getName())
+ .putExtra(Intent.EXTRA_USER_ID, userId)
+ .putExtra(EXTRA_BLOCKED_ACTIVITY_INFO, activityInfo)
+ .putExtra(EXTRA_STREAMED_DEVICE, streamedDeviceName);
+ }
}
diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl
index 9648008274ef..629a1b36b9e6 100644
--- a/core/java/com/android/internal/app/IBatteryStats.aidl
+++ b/core/java/com/android/internal/app/IBatteryStats.aidl
@@ -113,7 +113,7 @@ interface IBatteryStats {
void notePhoneOn();
void notePhoneOff();
void notePhoneSignalStrength(in SignalStrength signalStrength);
- void notePhoneDataConnectionState(int dataType, boolean hasData, int serviceType);
+ void notePhoneDataConnectionState(int dataType, boolean hasData, int serviceType, int nrFrequency);
void notePhoneState(int phoneState);
void noteWifiOn();
void noteWifiOff();
@@ -145,6 +145,8 @@ interface IBatteryStats {
long getAwakeTimeBattery();
long getAwakeTimePlugged();
+ void noteBluetoothOn(int uid, int reason, String packageName);
+ void noteBluetoothOff(int uid, int reason, String packageName);
void noteBleScanStarted(in WorkSource ws, boolean isUnoptimized);
void noteBleScanStopped(in WorkSource ws, boolean isUnoptimized);
void noteBleScanReset();
diff --git a/core/java/com/android/internal/content/PackageHelper.java b/core/java/com/android/internal/content/InstallLocationUtils.java
index c2f20526f8fd..c456cf3a4bb6 100644
--- a/core/java/com/android/internal/content/PackageHelper.java
+++ b/core/java/com/android/internal/content/InstallLocationUtils.java
@@ -16,6 +16,7 @@
package com.android.internal.content;
+import static android.content.pm.PackageManager.INSTALL_SUCCEEDED;
import static android.os.storage.VolumeInfo.ID_PRIVATE_INTERNAL;
import android.content.Context;
@@ -53,7 +54,7 @@ import java.util.UUID;
* and media container service transports.
* Some utility methods to invoke StorageManagerService api.
*/
-public class PackageHelper {
+public class InstallLocationUtils {
public static final int RECOMMEND_INSTALL_INTERNAL = 1;
public static final int RECOMMEND_INSTALL_EXTERNAL = 2;
public static final int RECOMMEND_INSTALL_EPHEMERAL = 3;
@@ -89,9 +90,13 @@ public class PackageHelper {
*/
public static abstract class TestableInterface {
abstract public StorageManager getStorageManager(Context context);
+
abstract public boolean getForceAllowOnExternalSetting(Context context);
+
abstract public boolean getAllow3rdPartyOnInternalConfig(Context context);
+
abstract public ApplicationInfo getExistingAppInfo(Context context, String packageName);
+
abstract public File getDataDirectory();
}
@@ -150,11 +155,11 @@ public class PackageHelper {
/**
* Given a requested {@link PackageInfo#installLocation} and calculated
* install size, pick the actual volume to install the app. Only considers
- * internal and private volumes, and prefers to keep an existing package on
+ * internal and private volumes, and prefers to keep an existing package onocation
* its current volume.
*
* @return the {@link VolumeInfo#fsUuid} to install onto, or {@code null}
- * for internal storage.
+ * for internal storage.
*/
public static String resolveInstallVolume(Context context, SessionParams params)
throws IOException {
@@ -316,21 +321,6 @@ public class PackageHelper {
&& params.sizeBytes <= storage.getStorageBytesUntilLow(primary.getPathFile());
}
- @Deprecated
- public static int resolveInstallLocation(Context context, String packageName,
- int installLocation, long sizeBytes, int installFlags) {
- final SessionParams params = new SessionParams(SessionParams.MODE_INVALID);
- params.appPackageName = packageName;
- params.installLocation = installLocation;
- params.sizeBytes = sizeBytes;
- params.installFlags = installFlags;
- try {
- return resolveInstallLocation(context, params);
- } catch (IOException e) {
- throw new IllegalStateException(e);
- }
- }
-
/**
* Given a requested {@link PackageInfo#installLocation} and calculated
* install size, pick the actual location to install the app.
@@ -393,24 +383,24 @@ public class PackageHelper {
// and will fall through to return INSUFFICIENT_STORAGE
if (fitsOnInternal) {
return (ephemeral)
- ? PackageHelper.RECOMMEND_INSTALL_EPHEMERAL
- : PackageHelper.RECOMMEND_INSTALL_INTERNAL;
+ ? InstallLocationUtils.RECOMMEND_INSTALL_EPHEMERAL
+ : InstallLocationUtils.RECOMMEND_INSTALL_INTERNAL;
}
} else if (prefer == RECOMMEND_INSTALL_EXTERNAL) {
if (fitsOnExternal) {
- return PackageHelper.RECOMMEND_INSTALL_EXTERNAL;
+ return InstallLocationUtils.RECOMMEND_INSTALL_EXTERNAL;
}
}
if (checkBoth) {
if (fitsOnInternal) {
- return PackageHelper.RECOMMEND_INSTALL_INTERNAL;
+ return InstallLocationUtils.RECOMMEND_INSTALL_INTERNAL;
} else if (fitsOnExternal) {
- return PackageHelper.RECOMMEND_INSTALL_EXTERNAL;
+ return InstallLocationUtils.RECOMMEND_INSTALL_EXTERNAL;
}
}
- return PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE;
+ return InstallLocationUtils.RECOMMEND_FAILED_INSUFFICIENT_STORAGE;
}
@Deprecated
@@ -476,4 +466,48 @@ public class PackageHelper {
return 0;
}
}
+
+ public static int installLocationPolicy(int installLocation, int recommendedInstallLocation,
+ int installFlags, boolean installedPkgIsSystem, boolean installedPackageOnExternal) {
+ if ((installFlags & PackageManager.INSTALL_REPLACE_EXISTING) == 0) {
+ // Invalid install. Return error code
+ return RECOMMEND_FAILED_ALREADY_EXISTS;
+ }
+ // Check for updated system application.
+ if (installedPkgIsSystem) {
+ return RECOMMEND_INSTALL_INTERNAL;
+ }
+ // If current upgrade specifies particular preference
+ if (installLocation == PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY) {
+ // Application explicitly specified internal.
+ return RECOMMEND_INSTALL_INTERNAL;
+ } else if (installLocation == PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL) {
+ // App explicitly prefers external. Let policy decide
+ return recommendedInstallLocation;
+ } else {
+ // Prefer previous location
+ if (installedPackageOnExternal) {
+ return RECOMMEND_INSTALL_EXTERNAL;
+ }
+ return RECOMMEND_INSTALL_INTERNAL;
+ }
+ }
+
+ public static int getInstallationErrorCode(int loc) {
+ if (loc == RECOMMEND_FAILED_INVALID_LOCATION) {
+ return PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
+ } else if (loc == RECOMMEND_FAILED_ALREADY_EXISTS) {
+ return PackageManager.INSTALL_FAILED_ALREADY_EXISTS;
+ } else if (loc == RECOMMEND_FAILED_INSUFFICIENT_STORAGE) {
+ return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
+ } else if (loc == RECOMMEND_FAILED_INVALID_APK) {
+ return PackageManager.INSTALL_FAILED_INVALID_APK;
+ } else if (loc == RECOMMEND_FAILED_INVALID_URI) {
+ return PackageManager.INSTALL_FAILED_INVALID_URI;
+ } else if (loc == RECOMMEND_MEDIA_UNAVAILABLE) {
+ return PackageManager.INSTALL_FAILED_MEDIA_UNAVAILABLE;
+ } else {
+ return INSTALL_SUCCEEDED;
+ }
+ }
}
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 25ee2d0fa019..c0fec62bdd94 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -69,10 +69,14 @@ import android.os.connectivity.GpsBatteryStats;
import android.os.connectivity.WifiActivityEnergyInfo;
import android.os.connectivity.WifiBatteryStats;
import android.provider.Settings;
+import android.telephony.Annotation.NetworkType;
import android.telephony.CellSignalStrength;
+import android.telephony.CellSignalStrengthLte;
+import android.telephony.CellSignalStrengthNr;
import android.telephony.DataConnectionRealTimeInfo;
import android.telephony.ModemActivityInfo;
import android.telephony.ServiceState;
+import android.telephony.ServiceState.RegState;
import android.telephony.SignalStrength;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
@@ -935,6 +939,119 @@ public class BatteryStatsImpl extends BatteryStats {
final StopwatchTimer[] mPhoneDataConnectionsTimer =
new StopwatchTimer[NUM_DATA_CONNECTION_TYPES];
+ @RadioAccessTechnology
+ int mActiveRat = RADIO_ACCESS_TECHNOLOGY_OTHER;
+
+ private static class RadioAccessTechnologyBatteryStats {
+ /**
+ * This RAT is currently being used.
+ */
+ private boolean mActive = false;
+ /**
+ * Current active frequency range for this RAT.
+ */
+ @ServiceState.FrequencyRange
+ private int mFrequencyRange = ServiceState.FREQUENCY_RANGE_UNKNOWN;
+ /**
+ * Current signal strength for this RAT.
+ */
+ private int mSignalStrength = CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+ /**
+ * Timers for each combination of frequency range and signal strength.
+ */
+ public final StopwatchTimer[][] perStateTimers;
+
+ RadioAccessTechnologyBatteryStats(int freqCount, Clock clock, TimeBase timeBase) {
+ perStateTimers =
+ new StopwatchTimer[freqCount][CellSignalStrength.NUM_SIGNAL_STRENGTH_BINS];
+ for (int i = 0; i < freqCount; i++) {
+ for (int j = 0; j < CellSignalStrength.NUM_SIGNAL_STRENGTH_BINS; j++) {
+ perStateTimers[i][j] = new StopwatchTimer(clock, null, -1, null, timeBase);
+ }
+ }
+ }
+
+ /**
+ * Note this RAT is currently being used.
+ */
+ public void noteActive(boolean active, long elapsedRealtimeMs) {
+ if (mActive == active) return;
+ mActive = active;
+ if (mActive) {
+ perStateTimers[mFrequencyRange][mSignalStrength].startRunningLocked(
+ elapsedRealtimeMs);
+ } else {
+ perStateTimers[mFrequencyRange][mSignalStrength].stopRunningLocked(
+ elapsedRealtimeMs);
+ }
+ }
+
+ /**
+ * Note current frequency range has changed.
+ */
+ public void noteFrequencyRange(@ServiceState.FrequencyRange int frequencyRange,
+ long elapsedRealtimeMs) {
+ if (mFrequencyRange == frequencyRange) return;
+
+ if (!mActive) {
+ // RAT not in use, note the frequency change and move on.
+ mFrequencyRange = frequencyRange;
+ return;
+ }
+ perStateTimers[mFrequencyRange][mSignalStrength].stopRunningLocked(elapsedRealtimeMs);
+ perStateTimers[frequencyRange][mSignalStrength].startRunningLocked(elapsedRealtimeMs);
+ mFrequencyRange = frequencyRange;
+ }
+
+ /**
+ * Note current signal strength has changed.
+ */
+ public void noteSignalStrength(int signalStrength, long elapsedRealtimeMs) {
+ if (mSignalStrength == signalStrength) return;
+
+ if (!mActive) {
+ // RAT not in use, note the signal strength change and move on.
+ mSignalStrength = signalStrength;
+ return;
+ }
+ perStateTimers[mFrequencyRange][mSignalStrength].stopRunningLocked(elapsedRealtimeMs);
+ perStateTimers[mFrequencyRange][signalStrength].startRunningLocked(elapsedRealtimeMs);
+ mSignalStrength = signalStrength;
+ }
+
+ /**
+ * Reset display timers.
+ */
+ public void reset(long elapsedRealtimeUs) {
+ final int size = perStateTimers.length;
+ for (int i = 0; i < size; i++) {
+ for (int j = 0; j < CellSignalStrength.NUM_SIGNAL_STRENGTH_BINS; j++) {
+ perStateTimers[i][j].reset(false, elapsedRealtimeUs);
+ }
+ }
+ }
+ }
+
+ /**
+ * Number of frequency ranges, keep in sync with {@link ServiceState.FrequencyRange}
+ */
+ private static final int NR_FREQUENCY_COUNT = 5;
+
+ RadioAccessTechnologyBatteryStats[] mPerRatBatteryStats =
+ new RadioAccessTechnologyBatteryStats[RADIO_ACCESS_TECHNOLOGY_COUNT];
+
+ @GuardedBy("this")
+ private RadioAccessTechnologyBatteryStats getRatBatteryStatsLocked(
+ @RadioAccessTechnology int rat) {
+ RadioAccessTechnologyBatteryStats stats = mPerRatBatteryStats[rat];
+ if (stats == null) {
+ final int freqCount = rat == RADIO_ACCESS_TECHNOLOGY_NR ? NR_FREQUENCY_COUNT : 1;
+ stats = new RadioAccessTechnologyBatteryStats(freqCount, mClock, mOnBatteryTimeBase);
+ mPerRatBatteryStats[rat] = stats;
+ }
+ return stats;
+ }
+
final LongSamplingCounter[] mNetworkByteActivityCounters =
new LongSamplingCounter[NUM_NETWORK_ACTIVITY_TYPES];
@@ -1896,17 +2013,15 @@ public class BatteryStatsImpl extends BatteryStats {
private final TimeBase mTimeBase;
private final LongArrayMultiStateCounter mCounter;
- private TimeInFreqMultiStateCounter(TimeBase timeBase, Parcel in, long timestampMs) {
- mTimeBase = timeBase;
- mCounter = LongArrayMultiStateCounter.CREATOR.createFromParcel(in);
- mCounter.setEnabled(mTimeBase.isRunning(), timestampMs);
- timeBase.add(this);
+ private TimeInFreqMultiStateCounter(TimeBase timeBase, int stateCount, int cpuFreqCount,
+ long timestampMs) {
+ this(timeBase, new LongArrayMultiStateCounter(stateCount, cpuFreqCount), timestampMs);
}
- private TimeInFreqMultiStateCounter(TimeBase timeBase, int stateCount, int cpuFreqCount,
+ private TimeInFreqMultiStateCounter(TimeBase timeBase, LongArrayMultiStateCounter counter,
long timestampMs) {
mTimeBase = timeBase;
- mCounter = new LongArrayMultiStateCounter(stateCount, cpuFreqCount);
+ mCounter = counter;
mCounter.setEnabled(mTimeBase.isRunning(), timestampMs);
timeBase.add(this);
}
@@ -1915,6 +2030,19 @@ public class BatteryStatsImpl extends BatteryStats {
mCounter.writeToParcel(out, 0);
}
+ @Nullable
+ private static TimeInFreqMultiStateCounter readFromParcel(Parcel in, TimeBase timeBase,
+ int stateCount, int cpuFreqCount, long timestampMs) {
+ // Read the object from the Parcel, whether it's usable or not
+ LongArrayMultiStateCounter counter =
+ LongArrayMultiStateCounter.CREATOR.createFromParcel(in);
+ if (counter.getStateCount() != stateCount
+ || counter.getArrayLength() != cpuFreqCount) {
+ return null;
+ }
+ return new TimeInFreqMultiStateCounter(timeBase, counter, timestampMs);
+ }
+
@Override
public void onTimeStarted(long elapsedRealtimeUs, long baseUptimeUs, long baseRealtimeUs) {
mCounter.setEnabled(true, elapsedRealtimeUs / 1000);
@@ -5886,6 +6014,10 @@ public class BatteryStatsImpl extends BatteryStats {
+ Integer.toHexString(mHistoryCur.states));
addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
mMobileRadioPowerState = powerState;
+
+ // Inform current RatBatteryStats that the modem active state might have changed.
+ getRatBatteryStatsLocked(mActiveRat).noteActive(active, elapsedRealtimeMs);
+
if (active) {
mMobileRadioActiveTimer.startRunningLocked(elapsedRealtimeMs);
mMobileRadioActivePerAppTimer.startRunningLocked(elapsedRealtimeMs);
@@ -6307,21 +6439,86 @@ public class BatteryStatsImpl extends BatteryStats {
@GuardedBy("this")
public void notePhoneSignalStrengthLocked(SignalStrength signalStrength,
long elapsedRealtimeMs, long uptimeMs) {
- // Bin the strength.
- int bin = signalStrength.getLevel();
- updateAllPhoneStateLocked(mPhoneServiceStateRaw, mPhoneSimStateRaw, bin,
+ final int overallSignalStrength = signalStrength.getLevel();
+ final SparseIntArray perRatSignalStrength = new SparseIntArray(
+ BatteryStats.RADIO_ACCESS_TECHNOLOGY_COUNT);
+
+ // Extract signal strength level for each RAT.
+ final List<CellSignalStrength> cellSignalStrengths =
+ signalStrength.getCellSignalStrengths();
+ final int size = cellSignalStrengths.size();
+ for (int i = 0; i < size; i++) {
+ CellSignalStrength cellSignalStrength = cellSignalStrengths.get(i);
+ // Map each CellSignalStrength to a BatteryStats.RadioAccessTechnology
+ final int ratType;
+ final int level;
+ if (cellSignalStrength instanceof CellSignalStrengthNr) {
+ ratType = RADIO_ACCESS_TECHNOLOGY_NR;
+ level = cellSignalStrength.getLevel();
+ } else if (cellSignalStrength instanceof CellSignalStrengthLte) {
+ ratType = RADIO_ACCESS_TECHNOLOGY_LTE;
+ level = cellSignalStrength.getLevel();
+ } else {
+ ratType = RADIO_ACCESS_TECHNOLOGY_OTHER;
+ level = cellSignalStrength.getLevel();
+ }
+
+ // According to SignalStrength#getCellSignalStrengths(), multiple of the same
+ // cellSignalStrength can be present. Just take the highest level one for each RAT.
+ if (perRatSignalStrength.get(ratType, -1) < level) {
+ perRatSignalStrength.put(ratType, level);
+ }
+ }
+
+ notePhoneSignalStrengthLocked(overallSignalStrength, perRatSignalStrength,
+ elapsedRealtimeMs, uptimeMs);
+ }
+
+ /**
+ * Note phone signal strength change, including per RAT signal strength.
+ *
+ * @param signalStrength overall signal strength {@see SignalStrength#getLevel()}
+ * @param perRatSignalStrength signal strength of available RATs
+ */
+ @GuardedBy("this")
+ public void notePhoneSignalStrengthLocked(int signalStrength,
+ SparseIntArray perRatSignalStrength) {
+ notePhoneSignalStrengthLocked(signalStrength, perRatSignalStrength,
+ mClock.elapsedRealtime(), mClock.uptimeMillis());
+ }
+
+ /**
+ * Note phone signal strength change, including per RAT signal strength.
+ *
+ * @param signalStrength overall signal strength {@see SignalStrength#getLevel()}
+ * @param perRatSignalStrength signal strength of available RATs
+ */
+ @GuardedBy("this")
+ public void notePhoneSignalStrengthLocked(int signalStrength,
+ SparseIntArray perRatSignalStrength,
+ long elapsedRealtimeMs, long uptimeMs) {
+ // Note each RAT's signal strength.
+ final int size = perRatSignalStrength.size();
+ for (int i = 0; i < size; i++) {
+ final int rat = perRatSignalStrength.keyAt(i);
+ final int ratSignalStrength = perRatSignalStrength.valueAt(i);
+ getRatBatteryStatsLocked(rat).noteSignalStrength(ratSignalStrength, elapsedRealtimeMs);
+ }
+ updateAllPhoneStateLocked(mPhoneServiceStateRaw, mPhoneSimStateRaw, signalStrength,
elapsedRealtimeMs, uptimeMs);
}
@UnsupportedAppUsage
@GuardedBy("this")
- public void notePhoneDataConnectionStateLocked(int dataType, boolean hasData, int serviceType) {
- notePhoneDataConnectionStateLocked(dataType, hasData, serviceType,
+ public void notePhoneDataConnectionStateLocked(@NetworkType int dataType, boolean hasData,
+ @RegState int serviceType, @ServiceState.FrequencyRange int nrFrequency) {
+ notePhoneDataConnectionStateLocked(dataType, hasData, serviceType, nrFrequency,
mClock.elapsedRealtime(), mClock.uptimeMillis());
}
@GuardedBy("this")
- public void notePhoneDataConnectionStateLocked(int dataType, boolean hasData, int serviceType,
+ public void notePhoneDataConnectionStateLocked(@NetworkType int dataType, boolean hasData,
+ @RegState int serviceType, @ServiceState.FrequencyRange int nrFrequency,
long elapsedRealtimeMs, long uptimeMs) {
// BatteryStats uses 0 to represent no network type.
// Telephony does not have a concept of no network type, and uses 0 to represent unknown.
@@ -6344,6 +6541,13 @@ public class BatteryStatsImpl extends BatteryStats {
}
}
}
+
+ final int newRat = mapNetworkTypeToRadioAccessTechnology(bin);
+ if (newRat == RADIO_ACCESS_TECHNOLOGY_NR) {
+ // Note possible frequency change for the NR RAT.
+ getRatBatteryStatsLocked(newRat).noteFrequencyRange(nrFrequency, elapsedRealtimeMs);
+ }
+
if (DEBUG) Log.i(TAG, "Phone Data Connection -> " + dataType + " = " + hasData);
if (mPhoneDataConnectionType != bin) {
mHistoryCur.states = (mHistoryCur.states&~HistoryItem.STATE_DATA_CONNECTION_MASK)
@@ -6357,6 +6561,45 @@ public class BatteryStatsImpl extends BatteryStats {
}
mPhoneDataConnectionType = bin;
mPhoneDataConnectionsTimer[bin].startRunningLocked(elapsedRealtimeMs);
+
+ if (mActiveRat != newRat) {
+ getRatBatteryStatsLocked(mActiveRat).noteActive(false, elapsedRealtimeMs);
+ mActiveRat = newRat;
+ }
+ final boolean modemActive = mMobileRadioActiveTimer.isRunningLocked();
+ getRatBatteryStatsLocked(newRat).noteActive(modemActive, elapsedRealtimeMs);
+ }
+ }
+
+ @RadioAccessTechnology
+ private static int mapNetworkTypeToRadioAccessTechnology(@NetworkType int dataType) {
+ switch (dataType) {
+ case TelephonyManager.NETWORK_TYPE_NR:
+ return RADIO_ACCESS_TECHNOLOGY_NR;
+ case TelephonyManager.NETWORK_TYPE_LTE:
+ return RADIO_ACCESS_TECHNOLOGY_LTE;
+ case TelephonyManager.NETWORK_TYPE_UNKNOWN: //fallthrough
+ case TelephonyManager.NETWORK_TYPE_GPRS: //fallthrough
+ case TelephonyManager.NETWORK_TYPE_EDGE: //fallthrough
+ case TelephonyManager.NETWORK_TYPE_UMTS: //fallthrough
+ case TelephonyManager.NETWORK_TYPE_CDMA: //fallthrough
+ case TelephonyManager.NETWORK_TYPE_EVDO_0: //fallthrough
+ case TelephonyManager.NETWORK_TYPE_EVDO_A: //fallthrough
+ case TelephonyManager.NETWORK_TYPE_1xRTT: //fallthrough
+ case TelephonyManager.NETWORK_TYPE_HSDPA: //fallthrough
+ case TelephonyManager.NETWORK_TYPE_HSUPA: //fallthrough
+ case TelephonyManager.NETWORK_TYPE_HSPA: //fallthrough
+ case TelephonyManager.NETWORK_TYPE_IDEN: //fallthrough
+ case TelephonyManager.NETWORK_TYPE_EVDO_B: //fallthrough
+ case TelephonyManager.NETWORK_TYPE_EHRPD: //fallthrough
+ case TelephonyManager.NETWORK_TYPE_HSPAP: //fallthrough
+ case TelephonyManager.NETWORK_TYPE_GSM: //fallthrough
+ case TelephonyManager.NETWORK_TYPE_TD_SCDMA: //fallthrough
+ case TelephonyManager.NETWORK_TYPE_IWLAN: //fallthrough
+ return RADIO_ACCESS_TECHNOLOGY_OTHER;
+ default:
+ Slog.w(TAG, "Unhandled NetworkType (" + dataType + "), mapping to OTHER");
+ return RADIO_ACCESS_TECHNOLOGY_OTHER;
}
}
@@ -7731,6 +7974,23 @@ public class BatteryStatsImpl extends BatteryStats {
return mPhoneDataConnectionsTimer[dataType];
}
+ @Override public long getActiveRadioDurationMs(@RadioAccessTechnology int rat,
+ @ServiceState.FrequencyRange int frequencyRange, int signalStrength,
+ long elapsedRealtimeMs) {
+ final RadioAccessTechnologyBatteryStats stats = mPerRatBatteryStats[rat];
+ if (stats == null) return 0L;
+
+ final int freqCount = stats.perStateTimers.length;
+ if (frequencyRange < 0 || frequencyRange >= freqCount) return 0L;
+
+ final StopwatchTimer[] strengthTimers = stats.perStateTimers[frequencyRange];
+ final int strengthCount = strengthTimers.length;
+ if (signalStrength < 0 || signalStrength >= strengthCount) return 0L;
+
+ return stats.perStateTimers[frequencyRange][signalStrength].getTotalTimeLocked(
+ elapsedRealtimeMs * 1000, STATS_SINCE_CHARGED) / 1000;
+ }
+
@UnsupportedAppUsage
@Override public long getMobileRadioActiveTime(long elapsedRealtimeUs, int which) {
return mMobileRadioActiveTimer.getTotalTimeLocked(elapsedRealtimeUs, which);
@@ -10535,25 +10795,18 @@ public class BatteryStatsImpl extends BatteryStats {
stateCount = in.readInt();
if (stateCount != 0) {
- // Read the object from the Parcel, whether it's usable or not
- TimeInFreqMultiStateCounter counter = new TimeInFreqMultiStateCounter(
- mBsi.mOnBatteryTimeBase, in, timestampMs);
- if (stateCount == PROC_STATE_TIME_COUNTER_STATE_COUNT) {
- mProcStateTimeMs = counter;
- }
+ mProcStateTimeMs = TimeInFreqMultiStateCounter.readFromParcel(in,
+ mBsi.mOnBatteryTimeBase, PROC_STATE_TIME_COUNTER_STATE_COUNT,
+ mBsi.getCpuFreqCount(), mBsi.mClock.elapsedRealtime());
} else {
mProcStateTimeMs = null;
}
stateCount = in.readInt();
if (stateCount != 0) {
- // Read the object from the Parcel, whether it's usable or not
- TimeInFreqMultiStateCounter counter =
- new TimeInFreqMultiStateCounter(
- mBsi.mOnBatteryScreenOffTimeBase, in, timestampMs);
- if (stateCount == PROC_STATE_TIME_COUNTER_STATE_COUNT) {
- mProcStateScreenOffTimeMs = counter;
- }
+ mProcStateScreenOffTimeMs = TimeInFreqMultiStateCounter.readFromParcel(in,
+ mBsi.mOnBatteryScreenOffTimeBase, PROC_STATE_TIME_COUNTER_STATE_COUNT,
+ mBsi.getCpuFreqCount(), mBsi.mClock.elapsedRealtime());
} else {
mProcStateScreenOffTimeMs = null;
}
@@ -12553,6 +12806,11 @@ public class BatteryStatsImpl extends BatteryStats {
mNetworkByteActivityCounters[i].reset(false, elapsedRealtimeUs);
mNetworkPacketActivityCounters[i].reset(false, elapsedRealtimeUs);
}
+ for (int i = 0; i < RADIO_ACCESS_TECHNOLOGY_COUNT; i++) {
+ final RadioAccessTechnologyBatteryStats stats = mPerRatBatteryStats[i];
+ if (stats == null) continue;
+ stats.reset(elapsedRealtimeUs);
+ }
mMobileRadioActiveTimer.reset(false, elapsedRealtimeUs);
mMobileRadioActivePerAppTimer.reset(false, elapsedRealtimeUs);
mMobileRadioActiveAdjustedTime.reset(false, elapsedRealtimeUs);
@@ -16107,6 +16365,11 @@ public class BatteryStatsImpl extends BatteryStats {
BATTERY_CHARGED_DELAY_MS = delay >= 0 ? delay : mParser.getInt(
KEY_BATTERY_CHARGED_DELAY_MS,
DEFAULT_BATTERY_CHARGED_DELAY_MS);
+
+ if (mHandler.hasCallbacks(mDeferSetCharging)) {
+ mHandler.removeCallbacks(mDeferSetCharging);
+ mHandler.postDelayed(mDeferSetCharging, BATTERY_CHARGED_DELAY_MS);
+ }
}
private void updateKernelUidReadersThrottleTime(long oldTimeMs, long newTimeMs) {
@@ -16906,12 +17169,10 @@ public class BatteryStatsImpl extends BatteryStats {
stateCount = in.readInt();
if (stateCount != 0) {
- // Read the object from the Parcel, whether it's usable or not
- TimeInFreqMultiStateCounter counter = new TimeInFreqMultiStateCounter(
- mOnBatteryTimeBase, in, mClock.elapsedRealtime());
- if (stateCount == PROC_STATE_TIME_COUNTER_STATE_COUNT) {
- u.mProcStateTimeMs = counter;
- }
+ detachIfNotNull(u.mProcStateTimeMs);
+ u.mProcStateTimeMs = TimeInFreqMultiStateCounter.readFromParcel(in,
+ mOnBatteryTimeBase, PROC_STATE_TIME_COUNTER_STATE_COUNT,
+ getCpuFreqCount(), mClock.elapsedRealtime());
}
detachIfNotNull(u.mProcStateScreenOffTimeMs);
@@ -16920,13 +17181,9 @@ public class BatteryStatsImpl extends BatteryStats {
stateCount = in.readInt();
if (stateCount != 0) {
detachIfNotNull(u.mProcStateScreenOffTimeMs);
- // Read the object from the Parcel, whether it's usable or not
- TimeInFreqMultiStateCounter counter =
- new TimeInFreqMultiStateCounter(
- mOnBatteryScreenOffTimeBase, in, mClock.elapsedRealtime());
- if (stateCount == PROC_STATE_TIME_COUNTER_STATE_COUNT) {
- u.mProcStateScreenOffTimeMs = counter;
- }
+ u.mProcStateScreenOffTimeMs = TimeInFreqMultiStateCounter.readFromParcel(in,
+ mOnBatteryScreenOffTimeBase, PROC_STATE_TIME_COUNTER_STATE_COUNT,
+ getCpuFreqCount(), mClock.elapsedRealtime());
}
if (in.readInt() != 0) {
diff --git a/core/java/com/android/internal/os/BinderCallsStats.java b/core/java/com/android/internal/os/BinderCallsStats.java
index be91aaca5d39..0a29fc5285a5 100644
--- a/core/java/com/android/internal/os/BinderCallsStats.java
+++ b/core/java/com/android/internal/os/BinderCallsStats.java
@@ -1159,6 +1159,17 @@ public class BinderCallsStats implements BinderInternal.Observer {
: Integer.compare(a.transactionCode, b.transactionCode);
}
+ /** @hide */
+ public static void startForBluetooth(Context context) {
+ new BinderCallsStats.SettingsObserver(
+ context,
+ new BinderCallsStats(
+ new BinderCallsStats.Injector(),
+ com.android.internal.os.BinderLatencyProto.Dims.BLUETOOTH));
+
+ }
+
+
/**
* Settings observer for other processes (not system_server).
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index d7eeb7b8dec0..2925341cd948 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -285,6 +285,7 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
private Insets mBackgroundInsets = Insets.NONE;
private Insets mLastBackgroundInsets = Insets.NONE;
private boolean mDrawLegacyNavigationBarBackground;
+ private boolean mDrawLegacyNavigationBarBackgroundHandled;
private PendingInsetsController mPendingInsetsController = new PendingInsetsController();
@@ -1034,6 +1035,9 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
@Override
public void onSystemBarAppearanceChanged(@WindowInsetsController.Appearance int appearance) {
updateColorViews(null /* insets */, true /* animate */);
+ if (mWindow != null) {
+ mWindow.dispatchOnSystemBarAppearanceChanged(appearance);
+ }
}
@Override
@@ -1168,6 +1172,9 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
mDrawLegacyNavigationBarBackground = mNavigationColorViewState.visible
&& (mWindow.getAttributes().flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0;
if (oldDrawLegacy != mDrawLegacyNavigationBarBackground) {
+ mDrawLegacyNavigationBarBackgroundHandled =
+ mWindow.onDrawLegacyNavigationBarBackgroundChanged(
+ mDrawLegacyNavigationBarBackground);
if (viewRoot != null) {
viewRoot.requestInvalidateRootRenderNode();
}
@@ -1260,7 +1267,7 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
}
}
- if (forceConsumingNavBar) {
+ if (forceConsumingNavBar && !mDrawLegacyNavigationBarBackgroundHandled) {
mBackgroundInsets = Insets.of(mLastLeftInset, 0, mLastRightInset, mLastBottomInset);
} else {
mBackgroundInsets = Insets.NONE;
@@ -2485,7 +2492,7 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
}
private void drawLegacyNavigationBarBackground(RecordingCanvas canvas) {
- if (!mDrawLegacyNavigationBarBackground) {
+ if (!mDrawLegacyNavigationBarBackground || mDrawLegacyNavigationBarBackgroundHandled) {
return;
}
View v = mNavigationColorViewState.view;
diff --git a/core/java/com/android/internal/policy/TransitionAnimation.java b/core/java/com/android/internal/policy/TransitionAnimation.java
index faea7706ee14..ece6f2f3571b 100644
--- a/core/java/com/android/internal/policy/TransitionAnimation.java
+++ b/core/java/com/android/internal/policy/TransitionAnimation.java
@@ -29,7 +29,6 @@ import static android.view.WindowManager.TRANSIT_OLD_WALLPAPER_INTRA_CLOSE;
import static android.view.WindowManager.TRANSIT_OLD_WALLPAPER_INTRA_OPEN;
import static android.view.WindowManager.TRANSIT_OPEN;
-import android.annotation.DrawableRes;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
@@ -915,7 +914,7 @@ public class TransitionAnimation {
* animation.
*/
public HardwareBuffer createCrossProfileAppsThumbnail(
- @DrawableRes int thumbnailDrawableRes, Rect frame) {
+ Drawable thumbnailDrawable, Rect frame) {
final int width = frame.width();
final int height = frame.height();
@@ -924,14 +923,13 @@ public class TransitionAnimation {
canvas.drawColor(Color.argb(0.6f, 0, 0, 0));
final int thumbnailSize = mContext.getResources().getDimensionPixelSize(
com.android.internal.R.dimen.cross_profile_apps_thumbnail_size);
- final Drawable drawable = mContext.getDrawable(thumbnailDrawableRes);
- drawable.setBounds(
+ thumbnailDrawable.setBounds(
(width - thumbnailSize) / 2,
(height - thumbnailSize) / 2,
(width + thumbnailSize) / 2,
(height + thumbnailSize) / 2);
- drawable.setTint(mContext.getColor(android.R.color.white));
- drawable.draw(canvas);
+ thumbnailDrawable.setTint(mContext.getColor(android.R.color.white));
+ thumbnailDrawable.draw(canvas);
picture.endRecording();
return Bitmap.createBitmap(picture).getHardwareBuffer();
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index a5cf7ce1d37c..23ebc9f94915 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -20,6 +20,7 @@ import android.app.ITransientNotificationCallback;
import android.content.ComponentName;
import android.graphics.drawable.Icon;
import android.graphics.Rect;
+import android.hardware.biometrics.IBiometricContextListener;
import android.hardware.biometrics.IBiometricSysuiReceiver;
import android.hardware.biometrics.PromptInfo;
import android.hardware.fingerprint.IUdfpsHbmListener;
@@ -166,6 +167,8 @@ oneway interface IStatusBar
* Used to hide the authentication dialog, e.g. when the application cancels authentication.
*/
void hideAuthenticationDialog();
+ /* Used to notify the biometric service of events that occur outside of an operation. */
+ void setBiometicContextListener(in IBiometricContextListener listener);
/**
* Sets an instance of IUdfpsHbmListener for UdfpsController.
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index accb98645599..f28325e3cc51 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -20,6 +20,7 @@ import android.app.Notification;
import android.content.ComponentName;
import android.graphics.drawable.Icon;
import android.graphics.Rect;
+import android.hardware.biometrics.IBiometricContextListener;
import android.hardware.biometrics.IBiometricSysuiReceiver;
import android.hardware.biometrics.PromptInfo;
import android.hardware.fingerprint.IUdfpsHbmListener;
@@ -125,6 +126,8 @@ interface IStatusBarService
void onBiometricError(int modality, int error, int vendorCode);
// Used to hide the authentication dialog, e.g. when the application cancels authentication
void hideAuthenticationDialog();
+ // Used to notify the biometric service of events that occur outside of an operation.
+ void setBiometicContextListener(in IBiometricContextListener listener);
/**
* Sets an instance of IUdfpsHbmListener for UdfpsController.
diff --git a/core/java/com/android/internal/util/UserIcons.java b/core/java/com/android/internal/util/UserIcons.java
index bfe43237da58..17b84ffc2f3f 100644
--- a/core/java/com/android/internal/util/UserIcons.java
+++ b/core/java/com/android/internal/util/UserIcons.java
@@ -16,6 +16,7 @@
package com.android.internal.util;
+import android.annotation.ColorInt;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Canvas;
@@ -72,9 +73,30 @@ public class UserIcons {
// Return colored icon instead
colorResId = USER_ICON_COLORS[userId % USER_ICON_COLORS.length];
}
+ return getDefaultUserIconInColor(resources, resources.getColor(colorResId, null));
+ }
+
+ /**
+ * Returns a default user icon in a particular color.
+ *
+ * @param resources resources object to fetch the user icon
+ * @param color the color used for the icon
+ */
+ public static Drawable getDefaultUserIconInColor(Resources resources, @ColorInt int color) {
Drawable icon = resources.getDrawable(R.drawable.ic_account_circle, null).mutate();
- icon.setColorFilter(resources.getColor(colorResId, null), Mode.SRC_IN);
+ icon.setColorFilter(color, Mode.SRC_IN);
icon.setBounds(0, 0, icon.getIntrinsicWidth(), icon.getIntrinsicHeight());
return icon;
}
+
+ /**
+ * Returns an array containing colors to be used for default user icons.
+ */
+ public static int[] getUserIconColors(Resources resources) {
+ int[] result = new int[USER_ICON_COLORS.length];
+ for (int i = 0; i < result.length; i++) {
+ result[i] = resources.getColor(USER_ICON_COLORS[i], null);
+ }
+ return result;
+ }
}
diff --git a/core/java/com/android/internal/view/IInputMethod.aidl b/core/java/com/android/internal/view/IInputMethod.aidl
index da24832f9d84..d2bc3442ed36 100644
--- a/core/java/com/android/internal/view/IInputMethod.aidl
+++ b/core/java/com/android/internal/view/IInputMethod.aidl
@@ -37,7 +37,8 @@ import com.android.internal.view.InlineSuggestionsRequestInfo;
*/
oneway interface IInputMethod {
void initializeInternal(IBinder token, IInputMethodPrivilegedOperations privOps,
- int configChanges, boolean stylusHwSupported);
+ int configChanges, boolean stylusHwSupported,
+ boolean shouldShowImeSwitcherWhenImeIsShown);
void onCreateInlineSuggestionsRequest(in InlineSuggestionsRequestInfo requestInfo,
in IInlineSuggestionsRequestCallback cb);
@@ -47,7 +48,10 @@ oneway interface IInputMethod {
void unbindInput();
void startInput(in IBinder startInputToken, in IInputContext inputContext,
- in EditorInfo attribute, boolean restarting);
+ in EditorInfo attribute, boolean restarting,
+ boolean shouldShowImeSwitcherWhenImeIsShown);
+
+ void onShouldShowImeSwitcherWhenImeIsShownChanged(boolean shouldShowImeSwitcherWhenImeIsShown);
void createSession(in InputChannel channel, IInputSessionCallback callback);
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 430d84ebb3e1..8bb9a0a0d6ff 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -195,7 +195,6 @@ cc_library_shared {
"android_util_FileObserver.cpp",
"android/opengl/poly_clip.cpp", // TODO: .arm
"android/opengl/util.cpp",
- "android_server_NetworkManagementSocketTagger.cpp",
"android_ddm_DdmHandleNativeHeap.cpp",
"android_backup_BackupDataInput.cpp",
"android_backup_BackupDataOutput.cpp",
@@ -310,6 +309,8 @@ cc_library_shared {
"libdl_android",
"libtimeinstate",
"server_configurable_flags",
+ // TODO: delete when ConnectivityT moves to APEX.
+ "libframework-connectivity-tiramisu-jni",
],
export_shared_lib_headers: [
// our headers include libnativewindow's public headers
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index f4296becf484..cde71cf6b30f 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -164,7 +164,6 @@ extern int register_android_text_AndroidCharacter(JNIEnv *env);
extern int register_android_text_Hyphenator(JNIEnv *env);
extern int register_android_opengl_classes(JNIEnv *env);
extern int register_android_ddm_DdmHandleNativeHeap(JNIEnv *env);
-extern int register_android_server_NetworkManagementSocketTagger(JNIEnv* env);
extern int register_android_backup_BackupDataInput(JNIEnv *env);
extern int register_android_backup_BackupDataOutput(JNIEnv *env);
extern int register_android_backup_FileBackupHelperBase(JNIEnv *env);
@@ -1623,7 +1622,6 @@ static const RegJNIRec gRegJNI[] = {
REG_JNI(register_android_media_midi),
REG_JNI(register_android_opengl_classes),
- REG_JNI(register_android_server_NetworkManagementSocketTagger),
REG_JNI(register_android_ddm_DdmHandleNativeHeap),
REG_JNI(register_android_backup_BackupDataInput),
REG_JNI(register_android_backup_BackupDataOutput),
diff --git a/core/jni/OWNERS b/core/jni/OWNERS
index 9a460f58effe..24c0d2a9795e 100644
--- a/core/jni/OWNERS
+++ b/core/jni/OWNERS
@@ -66,6 +66,7 @@ per-file com_android_internal_net_* = file:/services/core/java/com/android/serve
### Graphics ###
per-file android_graphics_* = file:/graphics/java/android/graphics/OWNERS
+per-file android_hardware_HardwareBuffer.cpp = file:/graphics/java/android/graphics/OWNERS
### Text ###
per-file android_text_* = file:/core/java/android/text/OWNERS
diff --git a/core/jni/android/opengl/OWNERS b/core/jni/android/opengl/OWNERS
new file mode 100644
index 000000000000..ce4b9075d59c
--- /dev/null
+++ b/core/jni/android/opengl/OWNERS
@@ -0,0 +1 @@
+file:/graphics/java/android/graphics/OWNERS
diff --git a/core/jni/android/opengl/util.cpp b/core/jni/android/opengl/util.cpp
index 82601baee914..d8522658d747 100644
--- a/core/jni/android/opengl/util.cpp
+++ b/core/jni/android/opengl/util.cpp
@@ -643,6 +643,8 @@ static bool checkInternalFormat(int32_t bitmapFormat, int internalformat, int ty
return (type == GL_UNSIGNED_SHORT_5_6_5 && internalformat == GL_RGB);
case ANDROID_BITMAP_FORMAT_RGBA_F16:
return (type == GL_HALF_FLOAT && internalformat == GL_RGBA16F);
+ case ANDROID_BITMAP_FORMAT_RGBA_1010102:
+ return (type == GL_UNSIGNED_INT_2_10_10_10_REV && internalformat == GL_RGB10_A2);
default:
break;
}
@@ -676,6 +678,8 @@ static int getInternalFormat(int32_t bitmapFormat) {
return GL_RGB;
case ANDROID_BITMAP_FORMAT_RGBA_F16:
return GL_RGBA16F;
+ case ANDROID_BITMAP_FORMAT_RGBA_1010102:
+ return GL_RGB10_A2;
default:
return -1;
}
@@ -693,6 +697,8 @@ static int getType(int32_t bitmapFormat) {
return GL_UNSIGNED_SHORT_5_6_5;
case ANDROID_BITMAP_FORMAT_RGBA_F16:
return GL_HALF_FLOAT;
+ case ANDROID_BITMAP_FORMAT_RGBA_1010102:
+ return GL_UNSIGNED_INT_2_10_10_10_REV;
default:
return -1;
}
diff --git a/core/jni/android_hardware_HardwareBuffer.cpp b/core/jni/android_hardware_HardwareBuffer.cpp
index b25b4f766b89..f462523c6ce4 100644
--- a/core/jni/android_hardware_HardwareBuffer.cpp
+++ b/core/jni/android_hardware_HardwareBuffer.cpp
@@ -85,7 +85,7 @@ static jlong android_hardware_HardwareBuffer_create(JNIEnv* env, jobject clazz,
sp<GraphicBuffer> buffer = new GraphicBuffer(width, height, pixelFormat, layers,
grallocUsage, std::string("HardwareBuffer pid [") + std::to_string(getpid()) +"]");
status_t error = buffer->initCheck();
- if (error < 0) {
+ if (error != OK) {
if (kDebugGraphicBuffer) {
ALOGW("createGraphicBuffer() failed in HardwareBuffer.create()");
}
diff --git a/core/jni/android_hardware_SensorManager.cpp b/core/jni/android_hardware_SensorManager.cpp
index d039bcff3b26..78b403c39a17 100644
--- a/core/jni/android_hardware_SensorManager.cpp
+++ b/core/jni/android_hardware_SensorManager.cpp
@@ -133,6 +133,18 @@ nativeClassInit (JNIEnv *_env, jclass _this)
MakeGlobalRefOrDie(_env, _env->CallObjectMethod(empty.get(), stringOffsets.intern));
}
+uint64_t htonll(uint64_t ll) {
+ constexpr uint32_t kBytesToTest = 0x12345678;
+ constexpr uint8_t kFirstByte = (const uint8_t &)kBytesToTest;
+ constexpr bool kIsLittleEndian = kFirstByte == 0x78;
+
+ if constexpr (kIsLittleEndian) {
+ return static_cast<uint64_t>(htonl(ll & 0xffffffff)) << 32 | htonl(ll >> 32);
+ } else {
+ return ll;
+ }
+}
+
static jstring getJavaInternedString(JNIEnv *env, const String8 &string) {
if (string == "") {
return gStringOffsets.emptyString;
@@ -193,7 +205,8 @@ translateNativeSensorToJavaSensor(JNIEnv *env, jobject sensor, const Sensor& nat
int32_t id = nativeSensor.getId();
env->CallVoidMethod(sensor, sensorOffsets.setId, id);
Sensor::uuid_t uuid = nativeSensor.getUuid();
- env->CallVoidMethod(sensor, sensorOffsets.setUuid, id, uuid.i64[0], uuid.i64[1]);
+ env->CallVoidMethod(sensor, sensorOffsets.setUuid, htonll(uuid.i64[0]),
+ htonll(uuid.i64[1]));
}
return sensor;
}
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index e13b78868a2c..edc8c5b99ebe 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -204,6 +204,12 @@ jclass gClsAudioRecordRoutingProxy;
jclass gAudioProfileClass;
jmethodID gAudioProfileCstor;
+static struct {
+ jfieldID mSamplingRates;
+ jfieldID mChannelMasks;
+ jfieldID mChannelIndexMasks;
+ jfieldID mEncapsulationType;
+} gAudioProfileFields;
jclass gVibratorClass;
static struct {
@@ -1288,7 +1294,7 @@ static jint convertAudioProfileFromNative(JNIEnv *env, jobject *jAudioProfile,
audioFormatFromNative(nAudioProfile->format), jSamplingRates.get(),
jChannelMasks.get(), jChannelIndexMasks.get(), encapsulationType);
- if (jAudioProfile == nullptr) {
+ if (*jAudioProfile == nullptr) {
return AUDIO_JAVA_ERROR;
}
@@ -1340,81 +1346,50 @@ static jint convertAudioPortFromNative(JNIEnv *env, jobject *jAudioPort,
jStatus = (jint)AUDIO_JAVA_ERROR;
goto exit;
}
- for (size_t i = 0; i < nAudioPort->num_audio_profiles; ++i) {
- size_t numPositionMasks = 0;
- size_t numIndexMasks = 0;
- // count up how many masks are positional and indexed
- for (size_t index = 0; index < nAudioPort->audio_profiles[i].num_channel_masks; index++) {
- const audio_channel_mask_t mask = nAudioPort->audio_profiles[i].channel_masks[index];
- if (audio_channel_mask_get_representation(mask) == AUDIO_CHANNEL_REPRESENTATION_INDEX) {
- numIndexMasks++;
- } else {
- numPositionMasks++;
- }
- }
- ScopedLocalRef<jintArray> jSamplingRates(env,
- env->NewIntArray(nAudioPort->audio_profiles[i]
- .num_sample_rates));
- ScopedLocalRef<jintArray> jChannelMasks(env, env->NewIntArray(numPositionMasks));
- ScopedLocalRef<jintArray> jChannelIndexMasks(env, env->NewIntArray(numIndexMasks));
- if (!jSamplingRates.get() || !jChannelMasks.get() || !jChannelIndexMasks.get()) {
+ for (size_t i = 0; i < nAudioPort->num_audio_profiles; ++i) {
+ jobject jAudioProfile = nullptr;
+ jStatus = convertAudioProfileFromNative(env, &jAudioProfile, &nAudioPort->audio_profiles[i],
+ useInMask);
+ if (jStatus != NO_ERROR) {
jStatus = (jint)AUDIO_JAVA_ERROR;
goto exit;
}
+ env->CallBooleanMethod(jAudioProfiles, gArrayListMethods.add, jAudioProfile);
- if (nAudioPort->audio_profiles[i].num_sample_rates) {
- env->SetIntArrayRegion(jSamplingRates.get(), 0 /*start*/,
- nAudioPort->audio_profiles[i].num_sample_rates,
- (jint *)nAudioPort->audio_profiles[i].sample_rates);
- }
-
- // put the masks in the output arrays
- for (size_t maskIndex = 0, posMaskIndex = 0, indexedMaskIndex = 0;
- maskIndex < nAudioPort->audio_profiles[i].num_channel_masks; maskIndex++) {
- const audio_channel_mask_t mask =
- nAudioPort->audio_profiles[i].channel_masks[maskIndex];
- if (audio_channel_mask_get_representation(mask) == AUDIO_CHANNEL_REPRESENTATION_INDEX) {
- jint jMask = audio_channel_mask_get_bits(mask);
- env->SetIntArrayRegion(jChannelIndexMasks.get(), indexedMaskIndex++, 1, &jMask);
- } else {
- jint jMask =
- useInMask ? inChannelMaskFromNative(mask) : outChannelMaskFromNative(mask);
- env->SetIntArrayRegion(jChannelMasks.get(), posMaskIndex++, 1, &jMask);
- }
- }
-
- int encapsulationType;
- if (audioEncapsulationTypeFromNative(nAudioPort->audio_profiles[i].encapsulation_type,
- &encapsulationType) != NO_ERROR) {
- ALOGW("Unknown encapsualtion type for JAVA API: %u",
- nAudioPort->audio_profiles[i].encapsulation_type);
- continue;
- }
-
- ScopedLocalRef<jobject>
- jAudioProfile(env,
- env->NewObject(gAudioProfileClass, gAudioProfileCstor,
- audioFormatFromNative(
- nAudioPort->audio_profiles[i].format),
- jSamplingRates.get(), jChannelMasks.get(),
- jChannelIndexMasks.get(), encapsulationType));
- if (jAudioProfile == nullptr) {
- jStatus = (jint)AUDIO_JAVA_ERROR;
- goto exit;
- }
- env->CallBooleanMethod(jAudioProfiles, gArrayListMethods.add, jAudioProfile.get());
if (nAudioPort->audio_profiles[i].format == AUDIO_FORMAT_PCM_FLOAT) {
hasFloat = true;
} else if (jPcmFloatProfileFromExtendedInteger.get() == nullptr &&
audio_is_linear_pcm(nAudioPort->audio_profiles[i].format) &&
audio_bytes_per_sample(nAudioPort->audio_profiles[i].format) > 2) {
+ ScopedLocalRef<jintArray>
+ jSamplingRates(env,
+ (jintArray)
+ env->GetObjectField(jAudioProfile,
+ gAudioProfileFields.mSamplingRates));
+ ScopedLocalRef<jintArray>
+ jChannelMasks(env,
+ (jintArray)
+ env->GetObjectField(jAudioProfile,
+ gAudioProfileFields.mChannelMasks));
+ ScopedLocalRef<jintArray>
+ jChannelIndexMasks(env,
+ (jintArray)env->GetObjectField(jAudioProfile,
+ gAudioProfileFields
+ .mChannelIndexMasks));
+ int encapsulationType =
+ env->GetIntField(jAudioProfile, gAudioProfileFields.mEncapsulationType);
+
jPcmFloatProfileFromExtendedInteger.reset(
env->NewObject(gAudioProfileClass, gAudioProfileCstor,
audioFormatFromNative(AUDIO_FORMAT_PCM_FLOAT),
jSamplingRates.get(), jChannelMasks.get(),
jChannelIndexMasks.get(), encapsulationType));
}
+
+ if (jAudioProfile != nullptr) {
+ env->DeleteLocalRef(jAudioProfile);
+ }
}
if (!hasFloat && jPcmFloatProfileFromExtendedInteger.get() != nullptr) {
// R and earlier compatibility - add ENCODING_PCM_FLOAT to the end
@@ -3285,6 +3260,14 @@ int register_android_media_AudioSystem(JNIEnv *env)
jclass audioProfileClass = FindClassOrDie(env, "android/media/AudioProfile");
gAudioProfileClass = MakeGlobalRefOrDie(env, audioProfileClass);
gAudioProfileCstor = GetMethodIDOrDie(env, audioProfileClass, "<init>", "(I[I[I[II)V");
+ gAudioProfileFields.mSamplingRates =
+ GetFieldIDOrDie(env, audioProfileClass, "mSamplingRates", "[I");
+ gAudioProfileFields.mChannelMasks =
+ GetFieldIDOrDie(env, audioProfileClass, "mChannelMasks", "[I");
+ gAudioProfileFields.mChannelIndexMasks =
+ GetFieldIDOrDie(env, audioProfileClass, "mChannelIndexMasks", "[I");
+ gAudioProfileFields.mEncapsulationType =
+ GetFieldIDOrDie(env, audioProfileClass, "mEncapsulationType", "I");
jclass audioDescriptorClass = FindClassOrDie(env, "android/media/AudioDescriptor");
gAudioDescriptorClass = MakeGlobalRefOrDie(env, audioDescriptorClass);
diff --git a/core/jni/android_server_NetworkManagementSocketTagger.cpp b/core/jni/android_server_NetworkManagementSocketTagger.cpp
deleted file mode 100644
index 1be18733e97d..000000000000
--- a/core/jni/android_server_NetworkManagementSocketTagger.cpp
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright 2011, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "NMST_QTagUidNative"
-
-#include <android/multinetwork.h>
-#include <cutils/qtaguid.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <nativehelper/JNIPlatformHelp.h>
-#include <sys/socket.h>
-#include <sys/types.h>
-#include <utils/Log.h>
-#include <utils/misc.h>
-
-#include "jni.h"
-
-namespace android {
-
-static jint tagSocketFd(JNIEnv* env, jclass, jobject fileDescriptor,
- jint tagNum, jint uid) {
- int userFd = jniGetFDFromFileDescriptor(env, fileDescriptor);
-
- if (env->ExceptionCheck()) {
- ALOGE("Can't get FileDescriptor num");
- return (jint)-1;
- }
-
- int res = android_tag_socket_with_uid(userFd, tagNum, uid);
- if (res < 0) {
- return (jint)-errno;
- }
- return (jint)res;
-}
-
-static jint untagSocketFd(JNIEnv* env, jclass, jobject fileDescriptor) {
- int userFd = jniGetFDFromFileDescriptor(env, fileDescriptor);
-
- if (env->ExceptionCheck()) {
- ALOGE("Can't get FileDescriptor num");
- return (jint)-1;
- }
-
- int res = android_untag_socket(userFd);
- if (res < 0) {
- return (jint)-errno;
- }
- return (jint)res;
-}
-
-static jint setCounterSet(JNIEnv* env, jclass, jint setNum, jint uid) {
- int res = qtaguid_setCounterSet(setNum, uid);
- if (res < 0) {
- return (jint)-errno;
- }
- return (jint)res;
-}
-
-static jint deleteTagData(JNIEnv* env, jclass, jint tagNum, jint uid) {
- int res = qtaguid_deleteTagData(tagNum, uid);
- if (res < 0) {
- return (jint)-errno;
- }
- return (jint)res;
-}
-
-static const JNINativeMethod gQTagUidMethods[] = {
- { "native_tagSocketFd", "(Ljava/io/FileDescriptor;II)I", (void*)tagSocketFd},
- { "native_untagSocketFd", "(Ljava/io/FileDescriptor;)I", (void*)untagSocketFd},
- { "native_setCounterSet", "(II)I", (void*)setCounterSet},
- { "native_deleteTagData", "(II)I", (void*)deleteTagData},
-};
-
-int register_android_server_NetworkManagementSocketTagger(JNIEnv* env) {
- return jniRegisterNativeMethods(env, "com/android/server/NetworkManagementSocketTagger", gQTagUidMethods, NELEM(gQTagUidMethods));
-}
-
-};
diff --git a/core/proto/android/hardware/sensorprivacy.proto b/core/proto/android/hardware/sensorprivacy.proto
index 81d849e7c6e8..97870a16da2b 100644
--- a/core/proto/android/hardware/sensorprivacy.proto
+++ b/core/proto/android/hardware/sensorprivacy.proto
@@ -22,6 +22,13 @@ option java_outer_classname = "SensorPrivacyServiceProto";
import "frameworks/base/core/proto/android/privacy.proto";
+message AllSensorPrivacyServiceDumpProto {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ // Is global sensor privacy enabled
+ optional bool is_enabled = 1;
+}
+
message SensorPrivacyServiceDumpProto {
option (android.msg_privacy).dest = DEST_AUTOMATIC;
@@ -35,6 +42,9 @@ message SensorPrivacyServiceDumpProto {
// Per user settings for sensor privacy
repeated SensorPrivacyUserProto user = 3;
+
+ // Implementation
+ optional string storage_implementation = 4;
}
message SensorPrivacyUserProto {
@@ -43,16 +53,47 @@ message SensorPrivacyUserProto {
// User id
optional int32 user_id = 1;
+ // DEPRECATED
// Is global sensor privacy enabled
optional bool is_enabled = 2;
// Per sensor privacy enabled
+ // DEPRECATED
repeated SensorPrivacyIndividualEnabledSensorProto individual_enabled_sensor = 3;
+
+ // Per toggle type sensor privacy
+ repeated SensorPrivacySensorProto sensors = 4;
+}
+
+message SensorPrivacySensorProto {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ enum Sensor {
+ UNKNOWN = 0;
+
+ MICROPHONE = 1;
+ CAMERA = 2;
+ }
+
+ optional int32 sensor = 1;
+
+ repeated SensorPrivacyIndividualEnabledSensorProto toggles = 2;
}
message SensorPrivacyIndividualEnabledSensorProto {
option (android.msg_privacy).dest = DEST_AUTOMATIC;
+ enum ToggleType {
+ SOFTWARE = 1;
+ HARDWARE = 2;
+ }
+
+ enum StateType {
+ ENABLED = 1;
+ DISABLED = 2;
+ }
+
+ // DEPRECATED
enum Sensor {
UNKNOWN = 0;
@@ -63,8 +104,17 @@ message SensorPrivacyIndividualEnabledSensorProto {
// Sensor for which privacy might be enabled
optional Sensor sensor = 1;
- // If sensor privacy is enabled for this sensor
+ // DEPRECATED
optional bool is_enabled = 2;
+
+ // Timestamp of the last time the sensor was changed
+ optional int64 last_change = 3;
+
+ // The toggle type for this state
+ optional ToggleType toggle_type = 4;
+
+ // If sensor privacy state for this sensor
+ optional StateType state_type = 5;
}
message SensorPrivacyToggleSourceProto {
diff --git a/core/proto/android/server/Android.bp b/core/proto/android/server/Android.bp
new file mode 100644
index 000000000000..362daa73ff14
--- /dev/null
+++ b/core/proto/android/server/Android.bp
@@ -0,0 +1,28 @@
+//
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+filegroup {
+ name: "srcs_bluetooth_manager_service_proto",
+ srcs: [
+ "bluetooth_manager_service.proto",
+ ],
+ visibility: ["//packages/modules/Bluetooth:__subpackages__"],
+}
+
diff --git a/core/proto/android/server/powermanagerservice.proto b/core/proto/android/server/powermanagerservice.proto
index acb7429df5c3..d48ea3b8785c 100644
--- a/core/proto/android/server/powermanagerservice.proto
+++ b/core/proto/android/server/powermanagerservice.proto
@@ -127,7 +127,7 @@ message PowerManagerServiceDumpProto {
optional bool is_light_device_idle_mode = 25;
// True if we are currently in device idle mode.
optional bool is_device_idle_mode = 26;
- // Set of app ids that we will always respect the wake locks for.
+ // Set of app ids that we will respect the wake locks for while in device idle mode.
repeated int32 device_idle_whitelist = 27;
// Set of app ids that are temporarily allowed to acquire wakelocks due to
// high-pri message
@@ -187,6 +187,8 @@ message PowerManagerServiceDumpProto {
// Whether or not the current enhanced discharge prediction is personalized based on device
// usage or not.
optional bool is_enhanced_discharge_prediction_personalized = 54;
+ optional bool is_low_power_standby_active = 55;
+ optional LowPowerStandbyControllerDumpProto low_power_standby_controller = 56;
}
// A com.android.server.power.PowerManagerService.SuspendBlockerImpl object.
@@ -209,6 +211,8 @@ message WakeLockProto {
// When this wake lock is released, poke the user activity timer
// so the screen stays on for a little longer.
optional bool is_on_after_release = 2;
+ // The wakelock is held by the system server on request by another app.
+ optional bool system_wakelock = 3;
}
optional .android.os.WakeLockLevelEnum lock_level = 1;
@@ -428,3 +432,39 @@ message BatterySaverStateMachineProto {
// Next tag: 23
}
+
+message LowPowerStandbyControllerDumpProto {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ // True if Low Power Standby is active
+ optional bool is_active = 1;
+
+ // True if Low Power Standby is enabled
+ optional bool is_enabled = 2;
+
+ // True if Low Power Standby is supported
+ optional bool is_supported_config = 3;
+
+ // True if Low Power Standby is enabled by default
+ optional bool is_enabled_by_default_config = 4;
+
+ // True if the device is currently interactive
+ optional bool is_interactive = 5;
+
+ // Time (in elapsedRealtime) when the device was last interactive
+ optional bool last_interactive_time = 6;
+
+ // Time (in milliseconds) after becoming non-interactive that Low Power Standby can activate
+ optional int32 standby_timeout_config = 7;
+
+ // True if the device has entered idle mode since becoming non-interactive
+ optional int32 idle_since_non_interactive = 8;
+
+ // True if the device is currently in idle mode
+ optional int32 is_device_idle = 9;
+
+ // Set of app ids that are exempt form low power standby
+ repeated int32 allowlist = 10;
+
+ // Next tag: 11
+}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index ff04339658fa..66d5e88423e6 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -107,6 +107,7 @@
<protected-broadcast android:name="android.os.action.POWER_SAVE_WHITELIST_CHANGED" />
<protected-broadcast android:name="android.os.action.POWER_SAVE_TEMP_WHITELIST_CHANGED" />
<protected-broadcast android:name="android.os.action.POWER_SAVE_MODE_CHANGED_INTERNAL" />
+ <protected-broadcast android:name="android.os.action.LOW_POWER_STANDBY_ENABLED_CHANGED" />
<protected-broadcast android:name="android.os.action.ENHANCED_DISCHARGE_PREDICTION_CHANGED" />
<!-- @deprecated This is rarely used and will be phased out soon. -->
@@ -718,6 +719,7 @@
<protected-broadcast android:name="android.safetycenter.action.REFRESH_SAFETY_SOURCES" />
<protected-broadcast android:name="android.app.action.DEVICE_POLICY_RESOURCE_UPDATED" />
<protected-broadcast android:name="android.intent.action.SHOW_FOREGROUND_SERVICE_MANAGER" />
+ <protected-broadcast android:name="android.service.autofill.action.DELAYED_FILL" />
<!-- ====================================================================== -->
<!-- RUNTIME PERMISSIONS -->
@@ -975,61 +977,6 @@
android:permissionFlags="softRestricted|immutablyRestricted"
android:protectionLevel="dangerous" />
- <!-- Required to be able to read audio files from shared storage.
- <p>Protection level: dangerous -->
- <permission-group android:name="android.permission-group.READ_MEDIA_AURAL"
- android:icon="@drawable/perm_group_read_media_aural"
- android:label="@string/permgrouplab_readMediaAural"
- android:description="@string/permgroupdesc_readMediaAural"
- android:priority="950" />
-
- <!-- Allows an application to read audio files from external storage.
- <p>This permission is enforced starting in API level
- {@link android.os.Build.VERSION_CODES#TIRAMISU}.
- For apps with a <a href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#target">{@code
- targetSdkVersion}</a> of {@link android.os.Build.VERSION_CODES#S} or lower, this permission
- must not be used and the READ_EXTERNAL_STORAGE permission must be used instead.
- <p>Protection level: dangerous -->
- <permission android:name="android.permission.READ_MEDIA_AUDIO"
- android:permissionGroup="android.permission-group.UNDEFINED"
- android:label="@string/permlab_readMediaAudio"
- android:description="@string/permdesc_readMediaAudio"
- android:protectionLevel="dangerous" />
-
- <!-- Required to be able to read image and video files from shared storage.
- <p>Protection level: dangerous -->
- <permission-group android:name="android.permission-group.READ_MEDIA_VISUAL"
- android:icon="@drawable/perm_group_read_media_visual"
- android:label="@string/permgrouplab_readMediaVisual"
- android:description="@string/permgroupdesc_readMediaVisual"
- android:priority="1000" />
-
- <!-- Allows an application to read audio files from external storage.
- <p>This permission is enforced starting in API level
- {@link android.os.Build.VERSION_CODES#TIRAMISU}.
- For apps with a <a href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#target">{@code
- targetSdkVersion}</a> of {@link android.os.Build.VERSION_CODES#S} or lower, this permission
- must not be used and the READ_EXTERNAL_STORAGE permission must be used instead.
- <p>Protection level: dangerous -->
- <permission android:name="android.permission.READ_MEDIA_VIDEO"
- android:permissionGroup="android.permission-group.UNDEFINED"
- android:label="@string/permlab_readMediaVideo"
- android:description="@string/permdesc_readMediaVideo"
- android:protectionLevel="dangerous" />
-
- <!-- Allows an application to read image files from external storage.
- <p>This permission is enforced starting in API level
- {@link android.os.Build.VERSION_CODES#TIRAMISU}.
- For apps with a <a href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#target">{@code
- targetSdkVersion}</a> of {@link android.os.Build.VERSION_CODES#S} or lower, this permission
- must not be used and the READ_EXTERNAL_STORAGE permission must be used instead.
- <p>Protection level: dangerous -->
- <permission android:name="android.permission.READ_MEDIA_IMAGE"
- android:permissionGroup="android.permission-group.UNDEFINED"
- android:label="@string/permlab_readMediaImage"
- android:description="@string/permdesc_readMediaImage"
- android:protectionLevel="dangerous" />
-
<!-- Allows an application to write to external storage.
<p class="note"><strong>Note:</strong> If <em>both</em> your <a
href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#min">{@code
@@ -1143,6 +1090,15 @@
android:description="@string/permdesc_accessBackgroundLocation"
android:protectionLevel="dangerous|instant" />
+ <!-- Allows an application (emergency or advanced driver-assistance app) to bypass
+ location settings.
+ <p>Not for use by third-party applications.
+ @SystemApi
+ @hide
+ -->
+ <permission android:name="android.permission.LOCATION_BYPASS"
+ android:protectionLevel="signature|privileged"/>
+
<!-- ====================================================================== -->
<!-- Permissions for accessing the call log -->
<!-- ====================================================================== -->
@@ -1646,7 +1602,7 @@
android:protectionLevel="normal"
android:permissionFlags="removed"/>
- <!-- @hide We need to keep this around for backwards compatibility -->
+ <!-- @SystemApi @hide We need to keep this around for backwards compatibility -->
<permission android:name="android.permission.WRITE_SMS"
android:protectionLevel="normal"
android:permissionFlags="removed"/>
@@ -1724,7 +1680,7 @@
<permission android:name="android.permission.RECEIVE_EMERGENCY_BROADCAST"
android:protectionLevel="signature|privileged" />
- <!-- Allows an application to monitor incoming Bluetooth MAP messages, to record
+ <!-- @SystemApi Allows an application to monitor incoming Bluetooth MAP messages, to record
or perform processing on them. -->
<!-- @hide -->
<permission android:name="android.permission.RECEIVE_BLUETOOTH_MAP"
@@ -1838,11 +1794,12 @@
<permission android:name="android.permission.ACCESS_MOCK_LOCATION"
android:protectionLevel="signature" />
- <!-- @SystemApi @hide Allows automotive applications to control location
+ <!-- @hide @SystemApi(client=android.annotation.SystemApi.Client.MODULE_LIBRARIES)
+ Allows automotive applications to control location
suspend state for power management use cases.
<p>Not for use by third-party applications.
-->
- <permission android:name="android.permission.AUTOMOTIVE_GNSS_CONTROLS"
+ <permission android:name="android.permission.CONTROL_AUTOMOTIVE_GNSS"
android:protectionLevel="signature|privileged" />
<!-- ======================================= -->
@@ -3661,6 +3618,13 @@
<permission android:name="android.permission.SIGNAL_PERSISTENT_PROCESSES"
android:protectionLevel="signature|privileged|development" />
+ <!-- @hide @SystemApi Must be required by a
+ {@link com.android.service.tracing.TraceReportService}, to ensure that only the system
+ can bind to it.
+ <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.BIND_TRACE_REPORT_SERVICE"
+ android:protectionLevel="signature" />
+
<!-- @hide @SystemApi @TestApi
Allow an application to approve incident and bug reports to be
shared off-device. There can be only one application installed on the
@@ -4663,6 +4627,13 @@
<permission android:name="android.permission.READ_FRAME_BUFFER"
android:protectionLevel="signature|recents" />
+ <!-- @SystemApi Allows an application to change the touch mode state.
+ Without this permission, an app can only change the touch mode
+ if it currently has focus.
+ @hide -->
+ <permission android:name="android.permission.MODIFY_TOUCH_MODE_STATE"
+ android:protectionLevel="signature" />
+
<!-- Allows an application to use InputFlinger's low level features.
@hide -->
<permission android:name="android.permission.ACCESS_INPUT_FLINGER"
@@ -4955,6 +4926,11 @@
<permission android:name="android.permission.USER_ACTIVITY"
android:protectionLevel="signature|privileged" />
+ <!-- @hide @SystemApi Allows an application to manage Low Power Standby settings.
+ <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.MANAGE_LOW_POWER_STANDBY"
+ android:protectionLevel="signature|privileged" />
+
<!-- @hide Allows low-level access to tun tap driver -->
<permission android:name="android.permission.NET_TUNNELING"
android:protectionLevel="signature" />
@@ -6150,6 +6126,12 @@
<permission android:name="android.permission.ACCESS_FPS_COUNTER"
android:protectionLevel="signature|privileged" />
+ <!-- @SystemApi Allows the GameService provider to create GameSession and call GameSession
+ APIs and overlay a view on top of the game's Activity.
+ @hide -->
+ <permission android:name="android.permission.MANAGE_GAME_ACTIVITY"
+ android:protectionLevel="signature|privileged" />
+
<!-- @SystemApi Allows the holder to register callbacks to inform the RebootReadinessManager
when they are performing reboot-blocking work.
@hide -->
@@ -6276,6 +6258,15 @@
<permission android:name="android.permission.BIND_AMBIENT_CONTEXT_DETECTION_SERVICE"
android:protectionLevel="signature" />
+ <!-- @SystemApi Allows an app to set keep-clear areas without restrictions on the size or
+ number of keep-clear areas (see {@link android.view.View#setPreferKeepClearRects}).
+ When the system arranges floating windows onscreen, it might decide to ignore keep-clear
+ areas from windows, whose owner does not have this permission.
+ @hide
+ -->
+ <permission android:name="android.permission.SET_UNRESTRICTED_KEEP_CLEAR_AREAS"
+ android:protectionLevel="signature|privileged" />
+
<!-- Attribution for Geofencing service. -->
<attribution android:tag="GeofencingService" android:label="@string/geofencing_service"/>
<!-- Attribution for Country Detector. -->
diff --git a/core/res/res/drawable/perm_group_read_media_aural.xml b/core/res/res/drawable/perm_group_read_media_aural.xml
deleted file mode 100644
index 6fc9c69254cc..000000000000
--- a/core/res/res/drawable/perm_group_read_media_aural.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2015 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
- android:viewportWidth="24"
- android:viewportHeight="24"
- android:tint="?attr/colorControlNormal">
- <path
- android:fillColor="@android:color/white"
- android:pathData="M10,21q-1.65,0 -2.825,-1.175Q6,18.65 6,17q0,-1.65 1.175,-2.825Q8.35,13 10,13q0.575,0 1.063,0.137 0.487,0.138 0.937,0.413V3h6v4h-4v10q0,1.65 -1.175,2.825Q11.65,21 10,21z"/>
-</vector> \ No newline at end of file
diff --git a/core/res/res/drawable/perm_group_read_media_visual.xml b/core/res/res/drawable/perm_group_read_media_visual.xml
deleted file mode 100644
index a5db2718c983..000000000000
--- a/core/res/res/drawable/perm_group_read_media_visual.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2015 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
- android:viewportWidth="24"
- android:viewportHeight="24"
- android:tint="?attr/colorControlNormal">
- <path
- android:fillColor="@android:color/white"
- android:pathData="M9,14h10l-3.45,-4.5 -2.3,3 -1.55,-2zM8,18q-0.825,0 -1.412,-0.587Q6,16.825 6,16L6,4q0,-0.825 0.588,-1.413Q7.175,2 8,2h12q0.825,0 1.413,0.587Q22,3.175 22,4v12q0,0.825 -0.587,1.413Q20.825,18 20,18zM8,16h12L20,4L8,4v12zM4,22q-0.825,0 -1.413,-0.587Q2,20.825 2,20L2,6h2v14h14v2zM8,4v12L8,4z"/>
-</vector> \ No newline at end of file
diff --git a/core/res/res/layout/autofill_fill_dialog.xml b/core/res/res/layout/autofill_fill_dialog.xml
new file mode 100644
index 000000000000..cce9593f1f2a
--- /dev/null
+++ b/core/res/res/layout/autofill_fill_dialog.xml
@@ -0,0 +1,115 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!-- NOTE: outer layout is required to provide proper shadow. -->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/autofill_dialog_picker"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/autofill_save_outer_top_margin"
+ android:padding="@dimen/autofill_save_outer_top_padding"
+ android:elevation="@dimen/autofill_elevation"
+ android:background="?android:attr/colorBackground"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
+ android:paddingStart="@dimen/autofill_save_inner_padding"
+ android:paddingEnd="@dimen/autofill_save_inner_padding"
+ android:orientation="vertical">
+
+ <ImageView
+ android:id="@+id/autofill_service_icon"
+ android:scaleType="fitStart"
+ android:visibility="gone"
+ android:layout_width="@dimen/autofill_dialog_icon_size"
+ android:layout_height="@dimen/autofill_dialog_icon_size"/>
+
+ <LinearLayout
+ android:id="@+id/autofill_dialog_header"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
+ android:paddingStart="@dimen/autofill_save_inner_padding"
+ android:paddingEnd="@dimen/autofill_save_inner_padding"
+ android:visibility="gone"
+ android:foreground="?attr/listChoiceBackgroundIndicator"
+ style="@style/AutofillDatasetPicker" />
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/autofill_dialog_container"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
+ android:paddingStart="@dimen/autofill_save_inner_padding"
+ android:paddingEnd="@dimen/autofill_save_inner_padding"
+ android:visibility="gone"
+ android:foreground="?attr/listChoiceBackgroundIndicator"
+ style="@style/AutofillDatasetPicker" />
+
+ <ListView
+ android:id="@+id/autofill_dialog_list"
+ android:layout_weight="1"
+ android:layout_width="fill_parent"
+ android:layout_height="0dp"
+ android:drawSelectorOnTop="true"
+ android:clickable="true"
+ android:divider="@null"
+ android:visibility="gone"
+ android:paddingStart="@dimen/autofill_save_inner_padding"
+ android:paddingEnd="@dimen/autofill_save_inner_padding"
+ android:foreground="?attr/listChoiceBackgroundIndicator"
+ style="@style/AutofillDatasetPicker" />
+
+ <com.android.internal.widget.ButtonBarLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="end"
+ android:padding="@dimen/autofill_save_button_bar_padding"
+ android:clipToPadding="false"
+ android:orientation="horizontal">
+
+ <Button
+ android:id="@+id/autofill_dialog_no"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ style="?android:attr/buttonBarButtonStyle"
+ android:text="@string/dismiss_action">
+ </Button>
+
+ <Space
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:visibility="invisible">
+ </Space>
+
+ <Button
+ android:id="@+id/autofill_dialog_yes"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ style="@style/Widget.DeviceDefault.Button.Colored"
+ android:text="@string/autofill_save_yes"
+ android:visibility="gone" >
+ </Button>
+
+ </com.android.internal.widget.ButtonBarLayout>
+
+</LinearLayout>
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index 3bc02830b0b9..408054d8a985 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -1187,6 +1187,10 @@
<string name="deleteText" msgid="4200807474529938112">"Vee uit"</string>
<string name="inputMethod" msgid="1784759500516314751">"Invoermetode"</string>
<string name="editTextMenuTitle" msgid="857666911134482176">"Teksaksies"</string>
+ <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+ <skip />
+ <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="9024241779284783414">"Bergingspasie word min"</string>
<string name="low_internal_storage_view_text" msgid="8172166728369697835">"Sommige stelselfunksies werk moontlik nie"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Nie genoeg berging vir die stelsel nie. Maak seker jy het 250 MB spasie beskikbaar en herbegin."</string>
@@ -1491,6 +1495,8 @@
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Laat \'n program toe om toestemming te vra om batteryoptimerings vir daardie program ignoreer."</string>
<string name="permlab_queryAllPackages" msgid="2928450604653281650">"navraag oor alle pakkette"</string>
<string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Laat \'n program toe om alle geïnstalleerde pakette te sien."</string>
+ <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"verkry toegang tot SupplementalApis"</string>
+ <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"Gee \'n program toegang tot SupplementalApis."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Klop twee keer vir zoembeheer"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Kon nie legstuk byvoeg nie."</string>
<string name="ime_action_go" msgid="5536744546326495436">"Gaan"</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index fb52e6ad965e..3ac6c60fca94 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -1187,6 +1187,10 @@
<string name="deleteText" msgid="4200807474529938112">"ሰርዝ"</string>
<string name="inputMethod" msgid="1784759500516314751">"ግቤት ስልት"</string>
<string name="editTextMenuTitle" msgid="857666911134482176">"የፅሁፍ እርምጃዎች"</string>
+ <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+ <skip />
+ <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="9024241779284783414">"የማከማቻ ቦታ እያለቀ ነው"</string>
<string name="low_internal_storage_view_text" msgid="8172166728369697835">"አንዳንድ የስርዓት ተግባራት ላይሰሩ ይችላሉ"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"ለስርዓቱ የሚሆን በቂ ቦታ የለም። 250 ሜባ ነጻ ቦታ እንዳለዎት ያረጋግጡና ዳግም ያስጀምሩ።"</string>
@@ -1491,6 +1495,8 @@
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"አንድ መተግበሪያ ለዚያ መተግበሪያ የባትሪ ማትባቶችን ችላ ለማለት እንዲጠይቅ ይፈቅድለታል።"</string>
<string name="permlab_queryAllPackages" msgid="2928450604653281650">"ሁሉንም ጥቅሎች ይጠይቁ"</string>
<string name="permdesc_queryAllPackages" msgid="5339069855520996010">"አንድ መተግበሪያ ሁሉንም የተጫኑ ጥቅሎችን እንዲያይ ይፈቅድለታል።"</string>
+ <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"SupplementalApisን ይድረሱ"</string>
+ <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"መተግበሪያ SupplementalApisን እንዲደርስ ይፈቅዳል።"</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"ለአጉላ መቆጣጠሪያ ሁለት ጊዜ ነካ አድርግ"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"ምግብር ማከል አልተቻለም።"</string>
<string name="ime_action_go" msgid="5536744546326495436">"ሂድ"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index 12d1b83ea1df..018595f82684 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -1267,6 +1267,10 @@
<string name="deleteText" msgid="4200807474529938112">"حذف"</string>
<string name="inputMethod" msgid="1784759500516314751">"طريقة الإرسال"</string>
<string name="editTextMenuTitle" msgid="857666911134482176">"إجراءات النص"</string>
+ <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+ <skip />
+ <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="9024241779284783414">"مساحة التخزين منخفضة"</string>
<string name="low_internal_storage_view_text" msgid="8172166728369697835">"قد لا تعمل بعض وظائف النظام"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"ليست هناك مساحة تخزين كافية للنظام. تأكد من أنه لديك مساحة خالية تبلغ ٢٥٠ ميغابايت وأعد التشغيل."</string>
@@ -1571,6 +1575,8 @@
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"للسماح للتطبيق بطلب الإذن لتجاهل تحسينات البطارية في هذا التطبيق."</string>
<string name="permlab_queryAllPackages" msgid="2928450604653281650">"طلب البحث في كل الحِزم"</string>
<string name="permdesc_queryAllPackages" msgid="5339069855520996010">"يسمح هذا الإذن للتطبيق بعرض كل الحِزم المثبّتة."</string>
+ <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"‏الوصول إلى SupplementalApis"</string>
+ <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"‏السماح لأحد التطبيقات بالوصول إلى SupplementalApis"</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"اضغط مرتين للتحكم في التكبير أو التصغير"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"تعذرت إضافة أداة."</string>
<string name="ime_action_go" msgid="5536744546326495436">"تنفيذ"</string>
diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml
index f57e42bf7664..6db3b18e56ad 100644
--- a/core/res/res/values-as/strings.xml
+++ b/core/res/res/values-as/strings.xml
@@ -1187,6 +1187,10 @@
<string name="deleteText" msgid="4200807474529938112">"মচক"</string>
<string name="inputMethod" msgid="1784759500516314751">"ইনপুট পদ্ধতি"</string>
<string name="editTextMenuTitle" msgid="857666911134482176">"পাঠ বিষয়ক কাৰ্য"</string>
+ <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+ <skip />
+ <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="9024241779284783414">"ষ্ট’ৰেজৰ খালী ঠাই শেষ হৈ আছে"</string>
<string name="low_internal_storage_view_text" msgid="8172166728369697835">"ছিষ্টেমৰ কিছুমান কাৰ্যকলাপে কাম নকৰিবও পাৰে"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"ছিষ্টেমৰ বাবে পৰ্যাপ্ত খালী ঠাই নাই। আপোনাৰ ২৫০এমবি খালী ঠাই থকাটো নিশ্চিত কৰক আৰু ৰিষ্টাৰ্ট কৰক।"</string>
@@ -1491,6 +1495,8 @@
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"কোনো এপক সেই এপ্‌টোৰ বাবে বেটাৰী অপ্টিমাইজেশ্বন উপেক্ষা কৰিবলৈ অনুমতি বিচাৰিবলৈ দিয়ে।"</string>
<string name="permlab_queryAllPackages" msgid="2928450604653281650">"আটাইবোৰ পেকেজত প্ৰশ্ন সোধক"</string>
<string name="permdesc_queryAllPackages" msgid="5339069855520996010">"এপক আটাইবোৰ ইনষ্টল কৰি থোৱা পেকেজ চাবলৈ দিয়ে।"</string>
+ <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"SupplementalApis এক্সেছ কৰক"</string>
+ <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"কোনো এপ্লিকেশ্বনক SupplementalApis এক্সেছ কৰিবলৈ অনুমতি দিয়ে।"</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"জুম নিয়ন্ত্ৰণ কৰিবলৈ দুবাৰ টিপক"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"ৱিজেট যোগ কৰিব পৰা নগ\'ল।"</string>
<string name="ime_action_go" msgid="5536744546326495436">"যাওক"</string>
diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml
index 76384f9ce6b7..a6e2aab202cf 100644
--- a/core/res/res/values-az/strings.xml
+++ b/core/res/res/values-az/strings.xml
@@ -1187,6 +1187,10 @@
<string name="deleteText" msgid="4200807474529938112">"Sil"</string>
<string name="inputMethod" msgid="1784759500516314751">"Daxiletmə metodu"</string>
<string name="editTextMenuTitle" msgid="857666911134482176">"Mətn əməliyyatları"</string>
+ <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+ <skip />
+ <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="9024241779284783414">"Yaddaş yeri bitir"</string>
<string name="low_internal_storage_view_text" msgid="8172166728369697835">"Bəzi sistem funksiyaları işləməyə bilər"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Sistem üçün yetərincə yaddaş ehtiyatı yoxdur. 250 MB yaddaş ehtiyatının olmasına əmin olun və yenidən başladın."</string>
@@ -1491,6 +1495,8 @@
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Tatareya optimallaşdırılmasını o tətbiq üçün iqnor edilməsinə icazə vermək məqsədilə soruşmağa tətbiqə icazə verilir."</string>
<string name="permlab_queryAllPackages" msgid="2928450604653281650">"bütün paketlər üçün sorğu göndərin"</string>
<string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Tətbiqə bütün quraşdırılmış paketləri görmək icazəsi verir."</string>
+ <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"SupplementalApis\'ə giriş"</string>
+ <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"Tətbiqə SupplementalApis\'ə giriş icazəsi verir."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Zoom kontrolu üçün iki dəfə toxunun"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Widget əlavə edilə bilmədi."</string>
<string name="ime_action_go" msgid="5536744546326495436">"Get"</string>
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index 15e3aa09cec5..0c06851a5197 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -1207,6 +1207,10 @@
<string name="deleteText" msgid="4200807474529938112">"Izbriši"</string>
<string name="inputMethod" msgid="1784759500516314751">"Metod unosa"</string>
<string name="editTextMenuTitle" msgid="857666911134482176">"Radnje u vezi sa tekstom"</string>
+ <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+ <skip />
+ <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="9024241779284783414">"Memorijski prostor je na izmaku"</string>
<string name="low_internal_storage_view_text" msgid="8172166728369697835">"Neke sistemske funkcije možda ne funkcionišu"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Nema dovoljno memorijskog prostora za sistem. Uverite se da imate 250 MB slobodnog prostora i ponovo pokrenite."</string>
@@ -1511,6 +1515,8 @@
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Dozvoljava aplikaciji da traži dozvolu za ignorisanje optimizacija baterije za tu aplikaciju."</string>
<string name="permlab_queryAllPackages" msgid="2928450604653281650">"slanje upita za sve pakete"</string>
<string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Dozvoljava aplikaciji da vidi sve instalirane pakete."</string>
+ <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"pristup stavci SupplementalApis"</string>
+ <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"Dozvoljava aplikaciji da pristupa stavci SupplementalApis."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Dodirnite dvaput za kontrolu zumiranja"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Nije moguće dodati vidžet."</string>
<string name="ime_action_go" msgid="5536744546326495436">"Idi"</string>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index e182c286ce40..ebcc13d0af9f 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -1227,6 +1227,10 @@
<string name="deleteText" msgid="4200807474529938112">"Выдалiць"</string>
<string name="inputMethod" msgid="1784759500516314751">"Метад уводу"</string>
<string name="editTextMenuTitle" msgid="857666911134482176">"Дзеянні з тэкстам"</string>
+ <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+ <skip />
+ <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="9024241779284783414">"Месца для захавання на зыходзе"</string>
<string name="low_internal_storage_view_text" msgid="8172166728369697835">"Некаторыя сістэмныя функцыі могуць не працаваць"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Не хапае сховішча для сістэмы. Пераканайцеся, што ў вас ёсць 250 МБ свабоднага месца, і перазапусціце."</string>
@@ -1531,6 +1535,8 @@
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Дазваляе праграме запытваць дазвол на ігнараванне аптымізацыі акумулятара для гэтай праграмы."</string>
<string name="permlab_queryAllPackages" msgid="2928450604653281650">"запыт усіх пакетаў"</string>
<string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Дазваляе праграме выяўляць усе ўсталяваныя пакеты."</string>
+ <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"доступ да SupplementalApis"</string>
+ <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"Дазваляе праграме мець доступ да SupplementalApis."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Націсніце двойчы, каб кіраваць маштабаваннем"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Немагчыма дадаць віджэт."</string>
<string name="ime_action_go" msgid="5536744546326495436">"Пачаць"</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index 41aa9f7ed95e..8cd3ef13aeeb 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -1187,6 +1187,10 @@
<string name="deleteText" msgid="4200807474529938112">"Изтриване"</string>
<string name="inputMethod" msgid="1784759500516314751">"Метод на въвеждане"</string>
<string name="editTextMenuTitle" msgid="857666911134482176">"Действия с текста"</string>
+ <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+ <skip />
+ <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="9024241779284783414">"Мястото в хранилището е на изчерпване"</string>
<string name="low_internal_storage_view_text" msgid="8172166728369697835">"Възможно е някои функции на системата да не работят"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"За системата няма достатъчно място в хранилището. Уверете се, че имате свободни 250 МБ, и рестартирайте."</string>
@@ -1491,6 +1495,8 @@
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Разрешава на дадено приложение да иска разрешение за пренебрегване на свързаните с него оптимизации на батерията."</string>
<string name="permlab_queryAllPackages" msgid="2928450604653281650">"заявка за всички пакети"</string>
<string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Разрешава на приложението да вижда всички инсталирани пакети."</string>
+ <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"достъп до SupplementalApis"</string>
+ <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"Позволява на приложението достъп до SupplementalApis."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Докоснете двукратно за управление на промяната на мащаба"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Приспособлението не можа да бъде добавено."</string>
<string name="ime_action_go" msgid="5536744546326495436">"Старт"</string>
diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml
index 7dac240dd079..13673f4e3bbe 100644
--- a/core/res/res/values-bn/strings.xml
+++ b/core/res/res/values-bn/strings.xml
@@ -1187,6 +1187,10 @@
<string name="deleteText" msgid="4200807474529938112">"মুছুন"</string>
<string name="inputMethod" msgid="1784759500516314751">"ইনপুট পদ্ধতি"</string>
<string name="editTextMenuTitle" msgid="857666911134482176">"পাঠ্য ক্রিয়াগুলি"</string>
+ <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+ <skip />
+ <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="9024241779284783414">"স্টোরেজ পূর্ণ হতে চলেছে"</string>
<string name="low_internal_storage_view_text" msgid="8172166728369697835">"কিছু কিছু সিস্টেম ক্রিয়াকলাপ কাজ নাও করতে পারে"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"সিস্টেমের জন্য যথেষ্ট স্টোরেজ নেই৷ আপনার কাছে ২৫০এমবি ফাঁকা স্থান রয়েছে কিনা সে বিষয়ে নিশ্চিত হন এবং সিস্টেম চালু করুন৷"</string>
@@ -1491,6 +1495,8 @@
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"কোনো অ্যাপের জন্য ব্যাটারি অপ্টিমাইজেশন উপেক্ষা করতে সেটিকে অনুমতির চাওয়ার মঞ্জুরি দেয়৷"</string>
<string name="permlab_queryAllPackages" msgid="2928450604653281650">"অন্যান্য সব প্যাকেজের তথ্য সম্পর্কিত কোয়েরি দেখুন"</string>
<string name="permdesc_queryAllPackages" msgid="5339069855520996010">"এটি কোনও অ্যাপকে সর্বদা ইনস্টল করা সব প্যাকেজ দেখতে অনুমতি দেয়।"</string>
+ <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"SupplementalApis অ্যাক্সেস করুন"</string>
+ <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"SupplementalApis অ্যাক্সেস করতে অ্যাপে অনুমতি দেয়।"</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"জুম নিয়ন্ত্রণের জন্য দুবার ট্যাপ করুন"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"উইজেট যোগ করা যায়নি৷"</string>
<string name="ime_action_go" msgid="5536744546326495436">"যান"</string>
diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml
index 28ca86d2e534..6b1afb2ca1ed 100644
--- a/core/res/res/values-bs/strings.xml
+++ b/core/res/res/values-bs/strings.xml
@@ -1207,6 +1207,10 @@
<string name="deleteText" msgid="4200807474529938112">"Izbriši"</string>
<string name="inputMethod" msgid="1784759500516314751">"Način unosa"</string>
<string name="editTextMenuTitle" msgid="857666911134482176">"Akcije za tekst"</string>
+ <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+ <skip />
+ <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="9024241779284783414">"Ponestaje prostora za pohranu"</string>
<string name="low_internal_storage_view_text" msgid="8172166728369697835">"Neke funkcije sistema možda neće raditi"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Nema dovoljno prostora za sistem. Obezbijedite 250MB slobodnog prostora i ponovo pokrenite uređaj."</string>
@@ -1511,6 +1515,8 @@
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Omogućava aplikaciji da traži odobrenje za zanemarivanje optimizacije baterije za tu aplikaciju."</string>
<string name="permlab_queryAllPackages" msgid="2928450604653281650">"upit za sve pakete"</string>
<string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Omogućava aplikaciji da pregleda sve instalirane pakete."</string>
+ <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"pristup za SupplementalApis"</string>
+ <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"Dozvoljava aplikaciji pristup za SupplementalApis."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Dodirnite dvaput za kontrolu uvećanja"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Dodavanje vidžeta nije uspjelo."</string>
<string name="ime_action_go" msgid="5536744546326495436">"Započni"</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index be1b43c69670..3a1baf92dc51 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -1187,6 +1187,10 @@
<string name="deleteText" msgid="4200807474529938112">"Suprimeix"</string>
<string name="inputMethod" msgid="1784759500516314751">"Mètode d\'introducció de text"</string>
<string name="editTextMenuTitle" msgid="857666911134482176">"Accions de text"</string>
+ <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+ <skip />
+ <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="9024241779284783414">"L\'espai d\'emmagatzematge s\'està esgotant"</string>
<string name="low_internal_storage_view_text" msgid="8172166728369697835">"És possible que algunes funcions del sistema no funcionin"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"No hi ha prou espai d\'emmagatzematge per al sistema. Comprova que tinguis 250 MB d\'espai lliure i reinicia."</string>
@@ -1491,6 +1495,8 @@
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Permet que una aplicació demani permís per ignorar les optimitzacions de bateria per a l\'aplicació."</string>
<string name="permlab_queryAllPackages" msgid="2928450604653281650">"consultar tots els paquets"</string>
<string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Permet que una aplicació vegi els paquets instal·lats."</string>
+ <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"accedir a SupplementalApis"</string>
+ <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"Permet que una aplicació accedeixi a SupplementalApis."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Piqueu dos cops per controlar el zoom"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"No s\'ha pogut afegir el widget."</string>
<string name="ime_action_go" msgid="5536744546326495436">"Ves"</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index d038dfcac045..4936836d2e41 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -1227,6 +1227,10 @@
<string name="deleteText" msgid="4200807474529938112">"Smazat"</string>
<string name="inputMethod" msgid="1784759500516314751">"Metoda zadávání dat"</string>
<string name="editTextMenuTitle" msgid="857666911134482176">"Operace s textem"</string>
+ <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+ <skip />
+ <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="9024241779284783414">"V úložišti je málo místa"</string>
<string name="low_internal_storage_view_text" msgid="8172166728369697835">"Některé systémové funkce nemusí fungovat"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Pro systém není dostatek místa v úložišti. Uvolněte alespoň 250 MB místa a restartujte zařízení."</string>
@@ -1531,6 +1535,8 @@
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Povoluje aplikaci požádat o oprávnění ignorovat optimalizaci využití baterie, která pro ni je nastavena."</string>
<string name="permlab_queryAllPackages" msgid="2928450604653281650">"zjistit všechny balíčky"</string>
<string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Umožňuje aplikaci načíst všechny nainstalované balíčky."</string>
+ <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"přístup k rozhraním SupplementalApi"</string>
+ <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"Umožňuje aplikaci přístup k rozhraním SupplementalApi."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Poklepáním můžete ovládat přiblížení"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Widget nelze přidat."</string>
<string name="ime_action_go" msgid="5536744546326495436">"Přejít"</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index afe1175aca56..eed13d4152be 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -1187,6 +1187,10 @@
<string name="deleteText" msgid="4200807474529938112">"Slet"</string>
<string name="inputMethod" msgid="1784759500516314751">"Inputmetode"</string>
<string name="editTextMenuTitle" msgid="857666911134482176">"Teksthandlinger"</string>
+ <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+ <skip />
+ <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="9024241779284783414">"Der er snart ikke mere lagerplads"</string>
<string name="low_internal_storage_view_text" msgid="8172166728369697835">"Nogle systemfunktioner virker måske ikke"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Der er ikke nok ledig lagerplads til systemet. Sørg for, at du har 250 MB ledig plads, og genstart."</string>
@@ -1491,6 +1495,8 @@
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Gør det muligt for en app at bede om tilladelse til at ignorere batterioptimeringer for den pågældende app."</string>
<string name="permlab_queryAllPackages" msgid="2928450604653281650">"forespørg om alle pakker"</string>
<string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Giver en app tilladelse til at se alle installerede pakker."</string>
+ <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"adgang til SupplementalApis"</string>
+ <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"Giver en app adgang til SupplementalApis."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Tryk to gange for zoomkontrol"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Widget kunne ikke tilføjes."</string>
<string name="ime_action_go" msgid="5536744546326495436">"Gå"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index ecaa63c1a337..1bd20bd62cf9 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -1187,6 +1187,10 @@
<string name="deleteText" msgid="4200807474529938112">"Löschen"</string>
<string name="inputMethod" msgid="1784759500516314751">"Eingabemethode"</string>
<string name="editTextMenuTitle" msgid="857666911134482176">"Textaktionen"</string>
+ <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+ <skip />
+ <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="9024241779284783414">"Der Speicherplatz wird knapp"</string>
<string name="low_internal_storage_view_text" msgid="8172166728369697835">"Einige Systemfunktionen funktionieren eventuell nicht."</string>
<string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Der Speicherplatz reicht nicht für das System aus. Stelle sicher, dass 250 MB freier Speicherplatz vorhanden sind, und starte das Gerät dann neu."</string>
@@ -1491,6 +1495,10 @@
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Erlaubt einer App, nach der Berechtigung zum Ignorieren der Akku-Leistungsoptimierungen zu fragen."</string>
<string name="permlab_queryAllPackages" msgid="2928450604653281650">"Alle Pakete abfragen"</string>
<string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Ermöglicht der App, alle installierten Pakete zu sehen."</string>
+ <!-- no translation found for permlab_accessSupplementalApi (3544659160536960275) -->
+ <skip />
+ <!-- no translation found for permdesc_accessSupplementalApi (8974758769370951074) -->
+ <skip />
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Für Zoomeinstellung zweimal berühren"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Widget konnte nicht hinzugefügt werden."</string>
<string name="ime_action_go" msgid="5536744546326495436">"Los"</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 44fe4261526b..52ab901ee245 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -1187,6 +1187,10 @@
<string name="deleteText" msgid="4200807474529938112">"Διαγραφή"</string>
<string name="inputMethod" msgid="1784759500516314751">"Μέθοδος εισόδου"</string>
<string name="editTextMenuTitle" msgid="857666911134482176">"Ενέργειες κειμένου"</string>
+ <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+ <skip />
+ <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="9024241779284783414">"Ο αποθηκευτικός χώρος εξαντλείται"</string>
<string name="low_internal_storage_view_text" msgid="8172166728369697835">"Ορισμένες λειτουργίες συστήματος ενδέχεται να μην λειτουργούν"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Δεν υπάρχει αρκετός αποθηκευτικός χώρος για το σύστημα. Βεβαιωθείτε ότι διαθέτετε 250 MB ελεύθερου χώρου και κάντε επανεκκίνηση."</string>
@@ -1491,6 +1495,8 @@
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Επιτρέπει σε μια εφαρμογή να ζητήσει άδεια για την αγνόηση βελτιστοποιήσεων της μπαταρίας για τη συγκεκριμένη εφαρμογή."</string>
<string name="permlab_queryAllPackages" msgid="2928450604653281650">"υποβολή ερωτήματος σε όλα τα πακέτα"</string>
<string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Επιτρέπει σε μια εφαρμογή να βλέπει όλα τα εγκατεστημένα πακέτα."</string>
+ <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"πρόσβαση στο SupplementalApis"</string>
+ <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"Επιτρέπει σε μια εφαρμογή να έχει πρόσβαση στο SupplementalApis."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Πατήστε δύο φορές για έλεγχο εστίασης"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Δεν ήταν δυνατή η προσθήκη του γραφικού στοιχείου."</string>
<string name="ime_action_go" msgid="5536744546326495436">"Μετάβαση"</string>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index a23eda269524..9a40fa61dac4 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -1187,6 +1187,10 @@
<string name="deleteText" msgid="4200807474529938112">"Delete"</string>
<string name="inputMethod" msgid="1784759500516314751">"Input method"</string>
<string name="editTextMenuTitle" msgid="857666911134482176">"Text actions"</string>
+ <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+ <skip />
+ <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="9024241779284783414">"Storage space running out"</string>
<string name="low_internal_storage_view_text" msgid="8172166728369697835">"Some system functions may not work"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Not enough storage for the system. Make sure that you have 250 MB of free space and restart."</string>
@@ -1491,6 +1495,8 @@
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Allows an app to ask for permission to ignore battery optimisations for that app."</string>
<string name="permlab_queryAllPackages" msgid="2928450604653281650">"query all packages"</string>
<string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Allows an app to see all installed packages."</string>
+ <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"access SupplementalApis"</string>
+ <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"Allows an application to access SupplementalApis."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Tap twice for zoom control"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Couldn\'t add widget."</string>
<string name="ime_action_go" msgid="5536744546326495436">"Go"</string>
diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml
index 281ecb1fc954..1a2e8d97d76d 100644
--- a/core/res/res/values-en-rCA/strings.xml
+++ b/core/res/res/values-en-rCA/strings.xml
@@ -1187,6 +1187,10 @@
<string name="deleteText" msgid="4200807474529938112">"Delete"</string>
<string name="inputMethod" msgid="1784759500516314751">"Input method"</string>
<string name="editTextMenuTitle" msgid="857666911134482176">"Text actions"</string>
+ <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+ <skip />
+ <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="9024241779284783414">"Storage space running out"</string>
<string name="low_internal_storage_view_text" msgid="8172166728369697835">"Some system functions may not work"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Not enough storage for the system. Make sure that you have 250 MB of free space and restart."</string>
@@ -1491,6 +1495,8 @@
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Allows an app to ask for permission to ignore battery optimisations for that app."</string>
<string name="permlab_queryAllPackages" msgid="2928450604653281650">"query all packages"</string>
<string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Allows an app to see all installed packages."</string>
+ <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"access SupplementalApis"</string>
+ <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"Allows an application to access SupplementalApis."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Tap twice for zoom control"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Couldn\'t add widget."</string>
<string name="ime_action_go" msgid="5536744546326495436">"Go"</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index 8d8b33c81235..3039233a29ac 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -1187,6 +1187,10 @@
<string name="deleteText" msgid="4200807474529938112">"Delete"</string>
<string name="inputMethod" msgid="1784759500516314751">"Input method"</string>
<string name="editTextMenuTitle" msgid="857666911134482176">"Text actions"</string>
+ <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+ <skip />
+ <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="9024241779284783414">"Storage space running out"</string>
<string name="low_internal_storage_view_text" msgid="8172166728369697835">"Some system functions may not work"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Not enough storage for the system. Make sure that you have 250 MB of free space and restart."</string>
@@ -1491,6 +1495,8 @@
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Allows an app to ask for permission to ignore battery optimisations for that app."</string>
<string name="permlab_queryAllPackages" msgid="2928450604653281650">"query all packages"</string>
<string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Allows an app to see all installed packages."</string>
+ <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"access SupplementalApis"</string>
+ <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"Allows an application to access SupplementalApis."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Tap twice for zoom control"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Couldn\'t add widget."</string>
<string name="ime_action_go" msgid="5536744546326495436">"Go"</string>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index e4116553d795..0e93f3192407 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -1187,6 +1187,10 @@
<string name="deleteText" msgid="4200807474529938112">"Delete"</string>
<string name="inputMethod" msgid="1784759500516314751">"Input method"</string>
<string name="editTextMenuTitle" msgid="857666911134482176">"Text actions"</string>
+ <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+ <skip />
+ <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="9024241779284783414">"Storage space running out"</string>
<string name="low_internal_storage_view_text" msgid="8172166728369697835">"Some system functions may not work"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Not enough storage for the system. Make sure that you have 250 MB of free space and restart."</string>
@@ -1491,6 +1495,8 @@
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Allows an app to ask for permission to ignore battery optimisations for that app."</string>
<string name="permlab_queryAllPackages" msgid="2928450604653281650">"query all packages"</string>
<string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Allows an app to see all installed packages."</string>
+ <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"access SupplementalApis"</string>
+ <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"Allows an application to access SupplementalApis."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Tap twice for zoom control"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Couldn\'t add widget."</string>
<string name="ime_action_go" msgid="5536744546326495436">"Go"</string>
diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml
index c2e747207356..db0f55990885 100644
--- a/core/res/res/values-en-rXC/strings.xml
+++ b/core/res/res/values-en-rXC/strings.xml
@@ -1187,6 +1187,8 @@
<string name="deleteText" msgid="4200807474529938112">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‏‎‎‏‎‎‏‏‎‎‎‏‎‎‎‏‎‎‎‎‏‎‎‏‎‎‎‎‏‎‏‎‎‏‎‎‏‎‏‎‎‏‏‎‏‎‏‎‏‎‏‏‎‎‎‎‎‎‎Delete‎‏‎‎‏‎"</string>
<string name="inputMethod" msgid="1784759500516314751">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‎‎‏‏‎‎‎‏‎‎‏‎‏‏‏‏‎‏‎‏‎‎‏‏‏‏‎‏‎‎‎‏‎‏‏‏‎‎‎‏‏‏‎‏‎‎‏‏‏‎‎‏‏‏‏‏‏‏‎Input method‎‏‎‎‏‎"</string>
<string name="editTextMenuTitle" msgid="857666911134482176">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‏‏‏‏‏‎‎‏‏‏‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‏‏‏‏‎‏‎‏‏‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‎‎‎‎‎‎‎‎‎Text actions‎‏‎‎‏‎"</string>
+ <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‏‎‏‎‏‏‏‏‎‎‎‎‏‎‎‏‏‎‎‎‎‎‎‎‏‏‎‏‏‏‎‎‏‎‎‏‏‏‏‏‏‏‎‎‏‎‎‎‎‏‏‎‎‎‏‎‏‏‎Back‎‏‎‎‏‎"</string>
+ <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‎‏‏‏‏‏‏‎‏‎‎‎‏‎‎‏‏‎‏‎‏‎‏‎‏‏‎‎‏‎‏‏‏‏‎‎‎‏‎‏‎‏‎‏‏‏‏‏‏‎‎‏‏‎‎‏‎‏‎Switch input method‎‏‎‎‏‎"</string>
<string name="low_internal_storage_view_title" msgid="9024241779284783414">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‎‏‎‎‏‏‏‏‎‎‏‎‎‎‏‏‎‎‎‎‎‏‎‏‎‏‏‏‏‏‎‏‎‏‏‎‎‎‎‎‏‎‎‎‏‏‎‎‎‏‎‎‏‏‎‏‏‎‎Storage space running out‎‏‎‎‏‎"</string>
<string name="low_internal_storage_view_text" msgid="8172166728369697835">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‎‏‎‏‏‎‏‎‎‏‎‏‎‏‏‏‏‎‎‏‎‏‎‏‏‏‎‏‏‎‏‎‎‏‏‎‏‏‏‏‎‏‏‏‏‏‎‏‎‎‎‎‏‎‏‎‏‏‎Some system functions may not work‎‏‎‎‏‎"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‏‎‎‏‎‎‎‎‏‏‏‏‎‏‎‏‎‏‏‎‎‎‎‎‎‎‏‎‎‎‏‏‎‏‎‏‎‎‏‎‏‎‎‏‎‏‎‎‏‎‎‏‎‎‏‏‎‎‎Not enough storage for the system. Make sure you have 250MB of free space and restart.‎‏‎‎‏‎"</string>
@@ -1491,6 +1493,8 @@
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‎‎‏‏‎‎‏‏‎‏‎‏‎‏‏‎‎‎‏‎‏‏‎‏‎‎‏‏‏‏‏‏‏‎‏‎‎‏‏‎‎‎‏‎‎‎‎‏‏‎‏‎‏‏‎‏‎‎‎Allows an app to ask for permission to ignore battery optimizations for that app.‎‏‎‎‏‎"</string>
<string name="permlab_queryAllPackages" msgid="2928450604653281650">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‎‎‏‎‏‎‎‎‏‏‏‏‏‏‎‎‏‎‎‏‎‎‏‏‏‏‎‎‏‏‎‎‎‎‏‎‏‎‎‎‏‏‎‎‎‎‎‎‎‏‎‏‏‏‎‎‏‎‎query all packages‎‏‎‎‏‎"</string>
<string name="permdesc_queryAllPackages" msgid="5339069855520996010">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‏‎‎‎‎‏‏‎‎‎‎‎‏‎‏‏‏‏‏‎‏‏‏‏‏‎‏‏‏‏‏‎‎‏‎‏‎‏‎‎‏‏‎‎‏‏‏‎‏‎‏‎‏‎‏‎‏‎‎Allows an app to see all installed packages.‎‏‎‎‏‎"</string>
+ <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‎‏‎‎‏‏‎‎‎‏‎‎‏‎‏‎‎‎‏‎‏‏‏‎‎‏‏‏‏‎‎‎‎‏‏‎‏‎‎‎‏‏‏‏‎‎‏‏‎‏‎‎‎‏‎‎‏‏‎access SupplementalApis‎‏‎‎‏‎"</string>
+ <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‎‎‏‎‎‎‏‏‎‎‏‎‏‏‏‏‏‏‏‎‎‎‏‏‎‎‏‏‏‎‏‎‎‏‎‎‏‎‏‏‎‏‎‏‏‎‏‏‎‏‏‎‏‎‎‎‏‎‎Allows an application to access SupplementalApis.‎‏‎‎‏‎"</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‎‏‏‎‎‏‎‎‏‏‎‎‏‏‎‎‏‎‏‎‏‏‏‏‎‏‏‎‎‎‎‏‏‏‎‏‎‎‏‎‏‎‎‎‎‎‏‎‎‎‏‏‏‎‎‏‏‎‎Tap twice for zoom control‎‏‎‎‏‎"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‏‎‎‎‎‎‎‎‎‎‎‎‎‎‎‎‏‏‎‎‎‏‎‏‏‎‎‎‎‎‏‎‏‎‏‏‎‏‎‎‎‏‎‎‏‏‏‏‏‎‎‎‎‎‎‎‎‎‎Couldn\'t add widget.‎‏‎‎‏‎"</string>
<string name="ime_action_go" msgid="5536744546326495436">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‎‎‏‏‎‏‎‏‏‎‎‏‏‏‎‏‏‏‏‏‎‏‎‏‎‎‎‏‏‏‎‏‏‏‏‏‎‏‏‏‏‏‎‏‎‎‎‎‎‎‏‏‎‎‏‏‎‎‎Go‎‏‎‎‏‎"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index fad71eb7d67c..dc8f35718b3d 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -1187,6 +1187,10 @@
<string name="deleteText" msgid="4200807474529938112">"Eliminar"</string>
<string name="inputMethod" msgid="1784759500516314751">"Método de entrada"</string>
<string name="editTextMenuTitle" msgid="857666911134482176">"Acciones de texto"</string>
+ <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+ <skip />
+ <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="9024241779284783414">"Queda poco espacio de almacenamiento"</string>
<string name="low_internal_storage_view_text" msgid="8172166728369697835">"Es posible que algunas funciones del sistema no estén disponibles."</string>
<string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"No hay espacio suficiente para el sistema. Asegúrate de que haya 250 MB libres y reinicia el dispositivo."</string>
@@ -1491,6 +1495,8 @@
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Permite que una app solicite permiso para ignorar las optimizaciones de la batería."</string>
<string name="permlab_queryAllPackages" msgid="2928450604653281650">"Búsqueda de todos los paquetes"</string>
<string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Permite que una app vea todos los paquetes que se instalaron."</string>
+ <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"Acceder a SupplementalApis"</string>
+ <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"Permite que una aplicación acceda a SupplementalApis."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Presiona dos veces para obtener el control del zoom"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"No se pudo agregar el widget."</string>
<string name="ime_action_go" msgid="5536744546326495436">"Ir"</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 669e914ef8fb..ec17d22a0b8b 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -1187,6 +1187,10 @@
<string name="deleteText" msgid="4200807474529938112">"Eliminar"</string>
<string name="inputMethod" msgid="1784759500516314751">"Método de introducción de texto"</string>
<string name="editTextMenuTitle" msgid="857666911134482176">"Acciones de texto"</string>
+ <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+ <skip />
+ <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="9024241779284783414">"Queda poco espacio"</string>
<string name="low_internal_storage_view_text" msgid="8172166728369697835">"Es posible que algunas funciones del sistema no funcionen."</string>
<string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"No hay espacio suficiente para el sistema. Comprueba que haya 250 MB libres y reinicia el dispositivo."</string>
@@ -1264,7 +1268,7 @@
<string name="screen_compat_mode_hint" msgid="4032272159093750908">"Para volver a habilitar esta opción, accede a Ajustes &gt; Aplicaciones &gt; Descargadas."</string>
<string name="unsupported_display_size_message" msgid="7265211375269394699">"<xliff:g id="APP_NAME">%1$s</xliff:g> no admite el tamaño de pantalla actual y es posible que funcione de forma inesperada."</string>
<string name="unsupported_display_size_show" msgid="980129850974919375">"Mostrar siempre"</string>
- <string name="unsupported_compile_sdk_message" msgid="7326293500707890537">"<xliff:g id="APP_NAME">%1$s</xliff:g> se diseñó para una versión incompatible de Android OS y puede que funcione de forma inesperada. Es posible que haya una versión actualizada de la aplicación."</string>
+ <string name="unsupported_compile_sdk_message" msgid="7326293500707890537">"<xliff:g id="APP_NAME">%1$s</xliff:g> se diseñó para una versión incompatible del SO Android y puede que funcione de forma inesperada. Es posible que haya una versión actualizada de la aplicación."</string>
<string name="unsupported_compile_sdk_show" msgid="1601210057960312248">"Mostrar siempre"</string>
<string name="unsupported_compile_sdk_check_update" msgid="1103639989147664456">"Buscar actualizaciones"</string>
<string name="smv_application" msgid="3775183542777792638">"La aplicación <xliff:g id="APPLICATION">%1$s</xliff:g> (proceso <xliff:g id="PROCESS">%2$s</xliff:g>) ha infringido su política StrictMode autoaplicable."</string>
@@ -1491,6 +1495,8 @@
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Permite que una aplicación solicite permiso para ignorar las optimizaciones de la batería."</string>
<string name="permlab_queryAllPackages" msgid="2928450604653281650">"consultar todos los paquetes"</string>
<string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Permite que una aplicación vea todos los paquetes instalados."</string>
+ <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"acceder a SupplementalApis"</string>
+ <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"Permite que una aplicación acceda a SupplementalApis."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Da dos toques para acceder al control de zoom."</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"No se ha podido añadir el widget."</string>
<string name="ime_action_go" msgid="5536744546326495436">"Ir"</string>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index e4d33a7a1e6e..30e78c151356 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -1187,6 +1187,10 @@
<string name="deleteText" msgid="4200807474529938112">"Kustuta"</string>
<string name="inputMethod" msgid="1784759500516314751">"Sisestusmeetod"</string>
<string name="editTextMenuTitle" msgid="857666911134482176">"Tekstitoimingud"</string>
+ <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+ <skip />
+ <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="9024241779284783414">"Talletusruum saab täis"</string>
<string name="low_internal_storage_view_text" msgid="8172166728369697835">"Mõned süsteemifunktsioonid ei pruugi töötada"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Süsteemis pole piisavalt talletusruumi. Veenduge, et seadmes oleks 250 MB vaba ruumi, ja käivitage seade uuesti."</string>
@@ -1491,6 +1495,8 @@
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Lubab rakendusel küsida luba rakenduse aku optimeerimise eiramiseks."</string>
<string name="permlab_queryAllPackages" msgid="2928450604653281650">"päringute esitamine kõikide pakettide kohta"</string>
<string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Võimaldab rakendusel näha kõiki installitud pakette."</string>
+ <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"juurdepääs üksusele SupplementalApis"</string>
+ <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"Annab rakendusele juurdepääsu üksusele SupplementalApis."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Suumi kasutamiseks koputage kaks korda"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Vidinat ei saanud lisada."</string>
<string name="ime_action_go" msgid="5536744546326495436">"Mine"</string>
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index a3766b767f7d..1c301e6ae9bd 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -1187,6 +1187,10 @@
<string name="deleteText" msgid="4200807474529938112">"Ezabatu"</string>
<string name="inputMethod" msgid="1784759500516314751">"Idazketa-metodoa"</string>
<string name="editTextMenuTitle" msgid="857666911134482176">"Testu-ekintzak"</string>
+ <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+ <skip />
+ <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="9024241779284783414">"Memoria betetzen ari da"</string>
<string name="low_internal_storage_view_text" msgid="8172166728369697835">"Sistemaren funtzio batzuek ez dute agian funtzionatuko"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Sisteman ez dago behar adina memoria. Ziurtatu gutxienez 250 MB erabilgarri dituzula eta, ondoren, berrabiarazi gailua."</string>
@@ -1491,6 +1495,8 @@
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Bateriaren optimizazioei ez ikusi egiteko baimena eskatzea baimentzen die aplikazioei."</string>
<string name="permlab_queryAllPackages" msgid="2928450604653281650">"Kontsultatu pakete guztiak"</string>
<string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Instalatutako pakete guztiak ikusteko baimena ematen dio aplikazioari."</string>
+ <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"atzitu SupplementalApis"</string>
+ <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"SupplementalApis direlakoak (API osagarriak) atzitzeko baimena ematen die aplikazioei."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Sakatu birritan zooma kontrolatzeko"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Ezin izan da widgeta gehitu."</string>
<string name="ime_action_go" msgid="5536744546326495436">"Joan"</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index c9dedf919195..ca06cf9a8baa 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -1187,6 +1187,10 @@
<string name="deleteText" msgid="4200807474529938112">"حذف"</string>
<string name="inputMethod" msgid="1784759500516314751">"روش ورودی"</string>
<string name="editTextMenuTitle" msgid="857666911134482176">"کنش‌های متنی"</string>
+ <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+ <skip />
+ <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="9024241779284783414">"فضای ذخیره‌سازی درحال پر شدن است"</string>
<string name="low_internal_storage_view_text" msgid="8172166728369697835">"برخی از عملکردهای سیستم ممکن است کار نکنند"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"فضای ذخیره‌سازی سیستم کافی نیست. اطمینان حاصل کنید که دارای ۲۵۰ مگابایت فضای خالی هستید و سیستم را راه‌اندازی مجدد کنید."</string>
@@ -1491,6 +1495,8 @@
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"به یک برنامه اجازه می‌دهد جهت نادیده گرفتن بهینه‌سازی باتری برای خود مجوز درخواست کند."</string>
<string name="permlab_queryAllPackages" msgid="2928450604653281650">"پُرسمان همه بسته‌ها"</string>
<string name="permdesc_queryAllPackages" msgid="5339069855520996010">"به برنامه اجازه می‌دهد همه بسته‌های نصب‌شده را ببیند."</string>
+ <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"‏دسترسی به SupplementalApis"</string>
+ <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"‏به برنامه اجازه می‌دهد به SupplementalApis دسترسی داشته باشد."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"برای کنترل بزرگ‌نمایی، دو بار ضربه بزنید"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"افزودن ابزارک انجام نشد."</string>
<string name="ime_action_go" msgid="5536744546326495436">"برو"</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index b7234beabb6c..6bac5ac01e3a 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -1187,6 +1187,10 @@
<string name="deleteText" msgid="4200807474529938112">"Poista"</string>
<string name="inputMethod" msgid="1784759500516314751">"Syöttötapa"</string>
<string name="editTextMenuTitle" msgid="857666911134482176">"Tekstitoiminnot"</string>
+ <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+ <skip />
+ <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="9024241779284783414">"Tallennustila loppumassa"</string>
<string name="low_internal_storage_view_text" msgid="8172166728369697835">"Kaikki järjestelmätoiminnot eivät välttämättä toimi"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Tallennustila ei riitä. Varmista, että vapaata tilaa on 250 Mt, ja käynnistä uudelleen."</string>
@@ -1491,6 +1495,8 @@
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Sallii sovelluksen pyytää lupaa ohittaa tietyn sovelluksen akun optimoinnit."</string>
<string name="permlab_queryAllPackages" msgid="2928450604653281650">"kaikkien pakettien näkeminen"</string>
<string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Antaa sovelluksen nähdä kaikki asennetut paketit."</string>
+ <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"pääsy SupplementalApi-rajapintoihin"</string>
+ <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"Sovellus saa pääsyn SupplementalApi-rajapintoihin."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Hallitse zoomausta napauttamalla kahdesti"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Widgetin lisääminen epäonnistui."</string>
<string name="ime_action_go" msgid="5536744546326495436">"Siirry"</string>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index 6a08237d41ef..2db9e97ce0e7 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -1187,6 +1187,10 @@
<string name="deleteText" msgid="4200807474529938112">"Supprimer"</string>
<string name="inputMethod" msgid="1784759500516314751">"Mode de saisie"</string>
<string name="editTextMenuTitle" msgid="857666911134482176">"Actions sur le texte"</string>
+ <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+ <skip />
+ <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="9024241779284783414">"Espace de stockage bientôt saturé"</string>
<string name="low_internal_storage_view_text" msgid="8172166728369697835">"Il est possible que certaines fonctionnalités du système ne soient pas opérationnelles."</string>
<string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Espace de stockage insuffisant pour le système. Assurez-vous de disposer de 250 Mo d\'espace libre, puis redémarrez."</string>
@@ -1491,6 +1495,8 @@
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Permet à une application de demander la permission d\'ignorer les optimisations de la pile."</string>
<string name="permlab_queryAllPackages" msgid="2928450604653281650">"envoyer une requête à propos de tous les paquets"</string>
<string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Permet à une application de voir tous les paquets installés."</string>
+ <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"accès à SupplementalApis"</string>
+ <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"Autorise une application à accéder à SupplementalApis."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Appuyer deux fois pour régler le zoom"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Impossible d\'ajouter le widget."</string>
<string name="ime_action_go" msgid="5536744546326495436">"Aller"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index c606ed3048e6..bb28b024f57a 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -1187,6 +1187,10 @@
<string name="deleteText" msgid="4200807474529938112">"Supprimer"</string>
<string name="inputMethod" msgid="1784759500516314751">"Mode de saisie"</string>
<string name="editTextMenuTitle" msgid="857666911134482176">"Actions sur le texte"</string>
+ <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+ <skip />
+ <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="9024241779284783414">"Espace de stockage bientôt saturé"</string>
<string name="low_internal_storage_view_text" msgid="8172166728369697835">"Il est possible que certaines fonctionnalités du système ne soient pas opérationnelles."</string>
<string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Espace de stockage insuffisant pour le système. Assurez-vous de disposer de 250 Mo d\'espace libre, puis redémarrez."</string>
@@ -1491,6 +1495,8 @@
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Autorise une application à demander l\'autorisation d\'ignorer les optimisations de batterie pour cette application."</string>
<string name="permlab_queryAllPackages" msgid="2928450604653281650">"interroger tous les packages"</string>
<string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Autorise une appli à voir tous les packages installés."</string>
+ <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"accéder à SupplementalApis"</string>
+ <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"Autorise une application à accéder à SupplementalApis."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Appuyer deux fois pour régler le zoom"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Impossible d\'ajouter le widget."</string>
<string name="ime_action_go" msgid="5536744546326495436">"OK"</string>
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index 24287d92c5c2..47247bff6e0e 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -1187,6 +1187,10 @@
<string name="deleteText" msgid="4200807474529938112">"Eliminar"</string>
<string name="inputMethod" msgid="1784759500516314751">"Método de introdución de texto"</string>
<string name="editTextMenuTitle" msgid="857666911134482176">"Accións de texto"</string>
+ <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+ <skip />
+ <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="9024241779284783414">"Estase esgotando o espazo de almacenamento"</string>
<string name="low_internal_storage_view_text" msgid="8172166728369697835">"É posible que algunhas funcións do sistema non funcionen"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Non hai almacenamento suficiente para o sistema. Asegúrate de ter un espazo libre de 250 MB e reinicia o dispositivo."</string>
@@ -1491,6 +1495,8 @@
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Fai que unha aplicación poida solicitar permiso para ignorar as optimizacións da batería."</string>
<string name="permlab_queryAllPackages" msgid="2928450604653281650">"consultar todos os paquetes"</string>
<string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Permite que unha aplicación consulte todos os paquetes instalados."</string>
+ <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"acceso a SupplementalApis"</string>
+ <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"Permite que unha aplicación acceda a SupplementalApis."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Toca dúas veces para controlar o zoom"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Non se puido engadir o widget."</string>
<string name="ime_action_go" msgid="5536744546326495436">"Ir"</string>
diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml
index 5ca2c39e2f99..b0efeabd8498 100644
--- a/core/res/res/values-gu/strings.xml
+++ b/core/res/res/values-gu/strings.xml
@@ -1187,6 +1187,10 @@
<string name="deleteText" msgid="4200807474529938112">"ડિલીટ કરો"</string>
<string name="inputMethod" msgid="1784759500516314751">"ઇનપુટ પદ્ધતિ"</string>
<string name="editTextMenuTitle" msgid="857666911134482176">"ટેક્સ્ટ ક્રિયાઓ"</string>
+ <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+ <skip />
+ <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="9024241779284783414">"સ્ટોરેજ સ્થાન સમાપ્ત થયું"</string>
<string name="low_internal_storage_view_text" msgid="8172166728369697835">"કેટલાક સિસ્ટમ Tasks કામ કરી શકશે નહીં"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"સિસ્ટમ માટે પર્યાપ્ત સ્ટોરેજ નથી. ખાતરી કરો કે તમારી પાસે 250MB ખાલી સ્થાન છે અને ફરીથી પ્રારંભ કરો."</string>
@@ -1491,6 +1495,8 @@
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"ઍપ્લિકેશનને તે ઍપ્લિકેશન માટે બૅટરી ઓપ્ટિમાઇઝેશન્સને અવગણવાની પરવાનગી આપવા માટે પૂછવાની મંજૂરી આપે છે."</string>
<string name="permlab_queryAllPackages" msgid="2928450604653281650">"બધા પૅકેજ જુઓ"</string>
<string name="permdesc_queryAllPackages" msgid="5339069855520996010">"કોઈ ઍપને ઇન્સ્ટૉલ કરેલા બધા પૅકેજ જોવાની મંજૂરી આપે છે."</string>
+ <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"SupplementalApisને ઍક્સેસ કરો"</string>
+ <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"કોઈ ઍપ્લિકેશનને SupplementalApis ઍક્સેસ કરવાની મંજૂરી આપે છે."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"ઝૂમ નિયંત્રણ માટે બેવાર ટૅપ કરો"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"વિજેટ ઉમેરી શકાયું નથી."</string>
<string name="ime_action_go" msgid="5536744546326495436">"જાઓ"</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index b846ff8024c3..70d0270f8713 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -1187,6 +1187,10 @@
<string name="deleteText" msgid="4200807474529938112">"मिटाएं"</string>
<string name="inputMethod" msgid="1784759500516314751">"इनपुट विधि"</string>
<string name="editTextMenuTitle" msgid="857666911134482176">"लेख क्रियाएं"</string>
+ <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+ <skip />
+ <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="9024241779284783414">"मेमोरी में जगह नहीं बची है"</string>
<string name="low_internal_storage_view_text" msgid="8172166728369697835">"हो सकता है कुछ सिस्टम फ़ंक्शन काम नहीं करें"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"सिस्टम के लिए ज़रूरी मेमोरी नहीं है. पक्का करें कि आपके पास 250एमबी की खाली जगह है और फिर से शुरू करें."</string>
@@ -1491,6 +1495,8 @@
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"किसी ऐप्लिकेशन को उस ऐप्लिकेशन के लिए बैटरी ऑप्टिमाइज़ेशन पर ध्यान ना देने की अनुमति के लिए पूछने देता है."</string>
<string name="permlab_queryAllPackages" msgid="2928450604653281650">"डिवाइस पर इंस्टॉल किए गए सभी पैकेज देखें"</string>
<string name="permdesc_queryAllPackages" msgid="5339069855520996010">"यह किसी ऐप्लिकेशन को, डिवाइस पर इंस्टॉल किए गए सभी पैकेज देखने की अनुमति देता है."</string>
+ <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"SupplementalApis का ऐक्सेस पाना"</string>
+ <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"इससे, किसी ऐप्लिकेशन को SupplementalApis को ऐक्सेस करने की अनुमति मिलती है."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"ज़ूम नियंत्रण के लिए दो बार टैप करें"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"विजेट नहीं जोड़ा जा सका."</string>
<string name="ime_action_go" msgid="5536744546326495436">"जाएं"</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index e88b331c7ff3..d48713ff17b6 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -1207,6 +1207,10 @@
<string name="deleteText" msgid="4200807474529938112">"Izbriši"</string>
<string name="inputMethod" msgid="1784759500516314751">"Način unosa"</string>
<string name="editTextMenuTitle" msgid="857666911134482176">"Radnje s tekstom"</string>
+ <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+ <skip />
+ <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="9024241779284783414">"Ponestaje prostora za pohranu"</string>
<string name="low_internal_storage_view_text" msgid="8172166728369697835">"Neke sistemske funkcije možda neće raditi"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Nema dovoljno pohrane za sustav. Oslobodite 250 MB prostora i pokrenite uređaj ponovo."</string>
@@ -1511,6 +1515,8 @@
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Aplikaciji omogućuje da traži dopuštenje za zanemarivanje optimizacija baterije za tu aplikaciju."</string>
<string name="permlab_queryAllPackages" msgid="2928450604653281650">"slanje upita za sve pakete"</string>
<string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Aplikaciji omogućuje pregled instaliranih paketa."</string>
+ <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"pristupiti SupplementalApijima"</string>
+ <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"Omogućuje aplikaciji pristupanje SupplementalApijima."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Dvaput dotaknite za upravljanje zumiranjem"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Widget nije moguće dodati."</string>
<string name="ime_action_go" msgid="5536744546326495436">"Idi"</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index 46fcc2a40677..5d3fa9f86878 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -1187,6 +1187,10 @@
<string name="deleteText" msgid="4200807474529938112">"Törlés"</string>
<string name="inputMethod" msgid="1784759500516314751">"Beviteli mód"</string>
<string name="editTextMenuTitle" msgid="857666911134482176">"Műveletek szöveggel"</string>
+ <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+ <skip />
+ <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="9024241779284783414">"Kevés a szabad terület"</string>
<string name="low_internal_storage_view_text" msgid="8172166728369697835">"Előfordulhat, hogy néhány rendszerfunkció nem működik."</string>
<string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Nincs elegendő tárhely a rendszerhez. Győződjön meg arról, hogy rendelkezik 250 MB szabad területtel, majd kezdje elölről."</string>
@@ -1491,6 +1495,8 @@
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Az alkalmazás engedélyt kérhet az akkumulátoroptimalizálási beállítások mellőzésére."</string>
<string name="permlab_queryAllPackages" msgid="2928450604653281650">"az összes csomag lekérdezése"</string>
<string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Engedélyezi az adott alkalmazás számára, hogy lássa az összes telepített csomagot."</string>
+ <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"hozzáférés ehhez: SupplementalApis"</string>
+ <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"Lehetővé teszi egy alkalmazás számára, hogy hozzáférjen a következőhöz: SupplementalApis."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Érintse meg kétszer a nagyítás beállításához"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Nem sikerült hozzáadni a modult."</string>
<string name="ime_action_go" msgid="5536744546326495436">"Ugrás"</string>
diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml
index b60f08b82374..8b6ef0920392 100644
--- a/core/res/res/values-hy/strings.xml
+++ b/core/res/res/values-hy/strings.xml
@@ -1187,6 +1187,10 @@
<string name="deleteText" msgid="4200807474529938112">"Ջնջել"</string>
<string name="inputMethod" msgid="1784759500516314751">"Մուտքագրման եղանակը"</string>
<string name="editTextMenuTitle" msgid="857666911134482176">"Տեքստի գործողությունները"</string>
+ <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+ <skip />
+ <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="9024241779284783414">"Հիշողությունը սպառվում է"</string>
<string name="low_internal_storage_view_text" msgid="8172166728369697835">"Որոշ գործառույթներ կարող են չաշխատել"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Համակարգի համար բավարար հիշողություն չկա: Համոզվեք, որ ունեք 250ՄԲ ազատ տարածություն և վերագործարկեք:"</string>
@@ -1491,6 +1495,8 @@
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Հավելվածին հնարավորություն է տալիս հայցելու թույլտվություն՝ տվյալ հավելվածի համար մարտկոցի օպտիմալացումն անտեսելու համար:"</string>
<string name="permlab_queryAllPackages" msgid="2928450604653281650">"հարցում բոլոր փաթեթների համար"</string>
<string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Թույլ է տալիս հավելվածին տեսնել բոլոր տեղադրված փաթեթները։"</string>
+ <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"օգտվել SupplementalApis-ից"</string>
+ <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"Թույլ է տալիս հավելվածին օգտվել SupplementalApis-ից։"</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Հպեք երկու անգամ` խոշորացման վերահսկման համար"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Չհաջողվեց վիջեթ ավելացնել:"</string>
<string name="ime_action_go" msgid="5536744546326495436">"Առաջ"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index 7925b3f1ce6b..10bfa5e03daa 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -1187,6 +1187,10 @@
<string name="deleteText" msgid="4200807474529938112">"Hapus"</string>
<string name="inputMethod" msgid="1784759500516314751">"Metode masukan"</string>
<string name="editTextMenuTitle" msgid="857666911134482176">"Tindakan teks"</string>
+ <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+ <skip />
+ <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="9024241779284783414">"Ruang penyimpanan hampir habis"</string>
<string name="low_internal_storage_view_text" msgid="8172166728369697835">"Beberapa fungsi sistem mungkin tidak dapat bekerja"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Penyimpanan tidak cukup untuk sistem. Pastikan Anda memiliki 250 MB ruang kosong, lalu mulai ulang."</string>
@@ -1491,6 +1495,8 @@
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Mengizinkan aplikasi meminta izin untuk mengabaikan pengoptimalan baterai bagi aplikasi tersebut."</string>
<string name="permlab_queryAllPackages" msgid="2928450604653281650">"mengkueri semua paket"</string>
<string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Mengizinkan aplikasi melihat semua paket yang diinstal."</string>
+ <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"akses SupplementalApis"</string>
+ <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"Mengizinkan aplikasi mengakses SupplementalApis."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Ketuk dua kali untuk kontrol perbesar/perkecil"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Tidak dapat menambahkan widget."</string>
<string name="ime_action_go" msgid="5536744546326495436">"Buka"</string>
diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml
index c758de5f406b..79c2c6804a39 100644
--- a/core/res/res/values-is/strings.xml
+++ b/core/res/res/values-is/strings.xml
@@ -1187,6 +1187,10 @@
<string name="deleteText" msgid="4200807474529938112">"Eyða"</string>
<string name="inputMethod" msgid="1784759500516314751">"Innsláttaraðferð"</string>
<string name="editTextMenuTitle" msgid="857666911134482176">"Textaaðgerðir"</string>
+ <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+ <skip />
+ <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="9024241779284783414">"Geymslurýmið er senn á þrotum"</string>
<string name="low_internal_storage_view_text" msgid="8172166728369697835">"Sumir kerfiseiginleikar kunna að vera óvirkir"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Ekki nægt geymslurými fyrir kerfið. Gakktu úr skugga um að 250 MB séu laus og endurræstu."</string>
@@ -1491,6 +1495,8 @@
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Gerir forriti kleift að biðja um heimild til að hunsa rafhlöðusparnað fyrir forritið."</string>
<string name="permlab_queryAllPackages" msgid="2928450604653281650">"spyrja fyrir alla pakka"</string>
<string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Leyfir forriti að sjá alla uppsetta pakka."</string>
+ <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"aðgangur að SupplementalApis"</string>
+ <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"Veitir forriti aðgang að SupplementalApis."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Ýttu tvisvar til að opna aðdráttarstýringar"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Ekki tókst að bæta græju við."</string>
<string name="ime_action_go" msgid="5536744546326495436">"Áfram"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index c5683faa45b4..a27a0923e122 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -1187,6 +1187,10 @@
<string name="deleteText" msgid="4200807474529938112">"Elimina"</string>
<string name="inputMethod" msgid="1784759500516314751">"Metodo inserimento"</string>
<string name="editTextMenuTitle" msgid="857666911134482176">"Azioni testo"</string>
+ <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+ <skip />
+ <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="9024241779284783414">"Spazio di archiviazione in esaurimento"</string>
<string name="low_internal_storage_view_text" msgid="8172166728369697835">"Alcune funzioni di sistema potrebbero non funzionare"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Memoria insufficiente per il sistema. Assicurati di avere 250 MB di spazio libero e riavvia."</string>
@@ -1491,6 +1495,8 @@
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Consente a un\'app di chiedere l\'autorizzazione a ignorare le ottimizzazioni della batteria per quell\'app."</string>
<string name="permlab_queryAllPackages" msgid="2928450604653281650">"Invio di query per tutti i pacchetti"</string>
<string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Consente a un\'app di visualizzare tutti i pacchetti installati."</string>
+ <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"Accesso a SupplementalApis"</string>
+ <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"Consente a un\'applicazione di accedere a SupplementalApis."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Tocca due volte per il comando dello zoom"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Aggiunta del widget non riuscita."</string>
<string name="ime_action_go" msgid="5536744546326495436">"Vai"</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 9da03ea1538e..c234ea145d63 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -1227,6 +1227,10 @@
<string name="deleteText" msgid="4200807474529938112">"מחיקה"</string>
<string name="inputMethod" msgid="1784759500516314751">"שיטת קלט"</string>
<string name="editTextMenuTitle" msgid="857666911134482176">"פעולות טקסט"</string>
+ <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+ <skip />
+ <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="9024241779284783414">"מקום האחסון עומד להיגמר"</string>
<string name="low_internal_storage_view_text" msgid="8172166728369697835">"ייתכן שפונקציות מערכת מסוימות לא יפעלו"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"‏אין מספיק מקום אחסון עבור המערכת. עליך לוודא שיש לך מקום פנוי בנפח של 250MB ולהתחיל שוב."</string>
@@ -1531,6 +1535,8 @@
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"מאפשרת לאפליקציה לבקש רשות להתעלם מאופטימיזציות של הסוללה לאפליקציה הזו."</string>
<string name="permlab_queryAllPackages" msgid="2928450604653281650">"שליחת שאילתות לכל החבילות"</string>
<string name="permdesc_queryAllPackages" msgid="5339069855520996010">"מאפשרת לאפליקציה לראות את כל החבילות המותקנות."</string>
+ <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"‏גישה אל SupplementalApis"</string>
+ <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"‏מאפשרת לאפליקציה לגשת אל SupplementalApis."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"יש להקיש פעמיים לשינוי המרחק מהתצוגה"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"‏לא ניתן להוסיף widget."</string>
<string name="ime_action_go" msgid="5536744546326495436">"התחלה"</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 76d248c4467a..736fca671884 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -1187,6 +1187,10 @@
<string name="deleteText" msgid="4200807474529938112">"削除"</string>
<string name="inputMethod" msgid="1784759500516314751">"入力方法"</string>
<string name="editTextMenuTitle" msgid="857666911134482176">"テキスト操作"</string>
+ <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+ <skip />
+ <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="9024241779284783414">"空き容量わずか"</string>
<string name="low_internal_storage_view_text" msgid="8172166728369697835">"一部のシステム機能が動作しない可能性があります"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"システムに十分な容量がありません。250MBの空き容量を確保して再起動してください。"</string>
@@ -1491,6 +1495,8 @@
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"電池の最適化の無視についてアプリが確認することを許可します。"</string>
<string name="permlab_queryAllPackages" msgid="2928450604653281650">"すべてのパッケージを照会する"</string>
<string name="permdesc_queryAllPackages" msgid="5339069855520996010">"すべてのインストール済みパッケージを参照することをアプリに許可します。"</string>
+ <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"SupplementalApis へのアクセス"</string>
+ <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"SupplementalApis へのアクセスをアプリに許可します。"</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"ダブルタップでズームします"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"ウィジェットを追加できませんでした。"</string>
<string name="ime_action_go" msgid="5536744546326495436">"移動"</string>
diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml
index 91144c65832f..e0b14a9c8426 100644
--- a/core/res/res/values-ka/strings.xml
+++ b/core/res/res/values-ka/strings.xml
@@ -1187,6 +1187,10 @@
<string name="deleteText" msgid="4200807474529938112">"წაშლა"</string>
<string name="inputMethod" msgid="1784759500516314751">"შეყვანის მეთოდი"</string>
<string name="editTextMenuTitle" msgid="857666911134482176">"ქმედებები ტექსტზე"</string>
+ <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+ <skip />
+ <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="9024241779284783414">"თავისუფალი ადგილი იწურება"</string>
<string name="low_internal_storage_view_text" msgid="8172166728369697835">"სისტემის ზოგიერთმა ფუნქციამ შესაძლოა არ იმუშავოს"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"სისტემისათვის საკმარისი საცავი არ არის. დარწმუნდით, რომ იქონიოთ სულ მცირე 250 მბაიტი თავისუფალი სივრცე და დაიწყეთ ხელახლა."</string>
@@ -1491,6 +1495,8 @@
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"საშუალებას მისცემს აპს, მოითხოვოს მასთან დაკავშირებული ბატარეის ოპტიმიზაციის იგნორირების ნებართვა."</string>
<string name="permlab_queryAllPackages" msgid="2928450604653281650">"ყველა პაკეტის მოთხოვნა"</string>
<string name="permdesc_queryAllPackages" msgid="5339069855520996010">"საშუალებას აძლევს აპს, ნახოს ყველა ინსტალირებული პაკეტი."</string>
+ <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"SupplementalApis-ზე წვდომა"</string>
+ <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"აპლიკაციას SupplementalApis-ზე წვდომის საშუალებას აძლევს."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"მასშტაბის ცვლილებისთვის შეეხეთ ორჯერ"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"ვერ დაემატა ვიჯეტი."</string>
<string name="ime_action_go" msgid="5536744546326495436">"გადასვლა"</string>
diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml
index 3f75495920b7..3a130bdbe1e6 100644
--- a/core/res/res/values-kk/strings.xml
+++ b/core/res/res/values-kk/strings.xml
@@ -1187,6 +1187,10 @@
<string name="deleteText" msgid="4200807474529938112">"Жою"</string>
<string name="inputMethod" msgid="1784759500516314751">"Енгізу әдісі"</string>
<string name="editTextMenuTitle" msgid="857666911134482176">"Мәтін әрекеттері"</string>
+ <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+ <skip />
+ <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="9024241779284783414">"Жадта орын азайып барады"</string>
<string name="low_internal_storage_view_text" msgid="8172166728369697835">"Жүйенің кейбір функциялары жұмыс істемеуі мүмкін"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Жүйе үшін жад жеткіліксіз. 250 МБ бос орын бар екенін тексеріп, қайта іске қосыңыз."</string>
@@ -1491,6 +1495,8 @@
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Қолданба батареяны оңтайландыру әрекетін елемеуді сұрай алады."</string>
<string name="permlab_queryAllPackages" msgid="2928450604653281650">"барлық бумаға сұрау жасау"</string>
<string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Қолданба барлық орнатылған буманы көре алады."</string>
+ <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"SupplementalApi интерфейстерін пайдалану"</string>
+ <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"Қолданбаға SupplementalApi интерфейстерін пайдалануға мүмкіндік береді."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Масштабтау параметрін басқару үшін екі рет түртіңіз"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Виджетті қосу."</string>
<string name="ime_action_go" msgid="5536744546326495436">"Өту"</string>
diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml
index ecae41a98eca..bb25bae03219 100644
--- a/core/res/res/values-km/strings.xml
+++ b/core/res/res/values-km/strings.xml
@@ -1187,6 +1187,10 @@
<string name="deleteText" msgid="4200807474529938112">"លុប"</string>
<string name="inputMethod" msgid="1784759500516314751">"វិធីសាស្ត្រ​បញ្ចូល"</string>
<string name="editTextMenuTitle" msgid="857666911134482176">"សកម្មភាព​អត្ថបទ"</string>
+ <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+ <skip />
+ <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="9024241779284783414">"អស់​ទំហំ​ផ្ទុក"</string>
<string name="low_internal_storage_view_text" msgid="8172166728369697835">"មុខងារ​ប្រព័ន្ធ​មួយ​ចំនួន​អាច​មិន​ដំណើរការ​"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"មិន​មាន​ទំហំ​ផ្ទុក​​គ្រប់​គ្រាន់​សម្រាប់​ប្រព័ន្ធ​។ សូម​ប្រាកដ​ថា​អ្នក​មាន​ទំហំ​ទំនេរ​ 250MB ហើយ​ចាប់ផ្ដើម​ឡើង​វិញ។"</string>
@@ -1491,6 +1495,8 @@
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"អនុញ្ញាតឲ្យកម្មវិធីស្នើសុំការអនុញ្ញាត ដើម្បីមិនអើពើចំពោះការបង្កើនប្រសិទ្ធភាពថ្ម។"</string>
<string name="permlab_queryAllPackages" msgid="2928450604653281650">"សួរសំណួរអំពីកញ្ចប់ទាំងអស់"</string>
<string name="permdesc_queryAllPackages" msgid="5339069855520996010">"អនុញ្ញាតឱ្យកម្មវិធីមើលកញ្ចប់ដែលបានដំឡើងទាំងអស់។"</string>
+ <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"ចូលប្រើ SupplementalApis"</string>
+ <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"អនុញ្ញាតឱ្យកម្មវិធីចូលប្រើ SupplementalApis។"</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"ប៉ះ ពីរ​ដង​ដើម្បី​ពិនិត្យ​ការ​ពង្រីក"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"មិន​អាច​បន្ថែម​ធាតុ​ក្រាហ្វិក។"</string>
<string name="ime_action_go" msgid="5536744546326495436">"ទៅ"</string>
diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml
index a24166cc0faf..06075acc6e8d 100644
--- a/core/res/res/values-kn/strings.xml
+++ b/core/res/res/values-kn/strings.xml
@@ -1187,6 +1187,10 @@
<string name="deleteText" msgid="4200807474529938112">"ಅಳಿಸಿ"</string>
<string name="inputMethod" msgid="1784759500516314751">"ಇನ್‌ಪುಟ್ ವಿಧಾನ"</string>
<string name="editTextMenuTitle" msgid="857666911134482176">"ಪಠ್ಯದ ಕ್ರಮಗಳು"</string>
+ <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+ <skip />
+ <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="9024241779284783414">"ಸಂಗ್ರಹಣೆ ಸ್ಥಳವು ತುಂಬಿದೆ"</string>
<string name="low_internal_storage_view_text" msgid="8172166728369697835">"ಕೆಲವು ಸಿಸ್ಟಂ ಕಾರ್ಯವಿಧಾನಗಳು ಕಾರ್ಯನಿರ್ವಹಿಸದೇ ಇರಬಹುದು"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"ಸಿಸ್ಟಂನಲ್ಲಿ ಸಾಕಷ್ಟು ಸಂಗ್ರಹಣೆಯಿಲ್ಲ. ನೀವು 250MB ನಷ್ಟು ಖಾಲಿ ಸ್ಥಳವನ್ನು ಹೊಂದಿರುವಿರಾ ಎಂಬುದನ್ನು ಖಚಿತಪಡಿಸಿಕೊಳ್ಳಿ ಹಾಗೂ ಮರುಪ್ರಾರಂಭಿಸಿ."</string>
@@ -1491,6 +1495,8 @@
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"ಈ ಅಪ್ಲಿಕೇಶನ್‌ಗೆ ಬ್ಯಾಟರಿ ಆಪ್ಟಿಮೈಸೇಶನ್‌ಗಳನ್ನು ಕಡೆಗಣಿಸುವುದಕ್ಕೆ ಅನುಮತಿಯನ್ನು ಕೇಳಲು ಅಪ್ಲಿಕೇಶನ್‌ ಅನ್ನು ಅನುಮತಿಸುತ್ತದೆ."</string>
<string name="permlab_queryAllPackages" msgid="2928450604653281650">"ಎಲ್ಲಾ ಪ್ಯಾಕೇಜ್‌ಗಳ ಕುರಿತಾದ ಮಾಹಿತಿಯನ್ನು ಕೇಳಿ"</string>
<string name="permdesc_queryAllPackages" msgid="5339069855520996010">"ಇನ್‌ಸ್ಟಾಲ್ ಮಾಡಿದ ಎಲ್ಲಾ ಪ್ಯಾಕೇಜ್‌ಗಳನ್ನು ವೀಕ್ಷಿಸಲು ಆ್ಯಪ್‌ಗೆ ಅನುಮತಿಸುತ್ತದೆ."</string>
+ <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"SupplementalApis ಅನ್ನು ಪ್ರವೇಶಿಸಿ"</string>
+ <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"SupplementalApis ಅನ್ನು ಪ್ರವೇಶಿಸಲು ಆ್ಯಪ್‌ಗೆ ಅನುಮತಿಸುತ್ತದೆ."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"ಝೂಮ್‌ ನಿಯಂತ್ರಿಸಲು ಎರಡು ಬಾರಿ ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"ವಿಜೆಟ್ ಸೇರಿಸಲು ಸಾಧ್ಯವಾಗುತ್ತಿಲ್ಲ."</string>
<string name="ime_action_go" msgid="5536744546326495436">"ಹೋಗು"</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 8920be2481d4..bbfae68390d2 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -1187,6 +1187,10 @@
<string name="deleteText" msgid="4200807474529938112">"삭제"</string>
<string name="inputMethod" msgid="1784759500516314751">"입력 방법"</string>
<string name="editTextMenuTitle" msgid="857666911134482176">"텍스트 작업"</string>
+ <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+ <skip />
+ <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="9024241779284783414">"저장 공간이 부족함"</string>
<string name="low_internal_storage_view_text" msgid="8172166728369697835">"일부 시스템 기능이 작동하지 않을 수 있습니다."</string>
<string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"시스템의 저장 공간이 부족합니다. 250MB의 여유 공간이 확보한 후 다시 시작하세요."</string>
@@ -1491,6 +1495,8 @@
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"앱에서 배터리 최적화를 무시할 수 있는 권한을 요청할 수 있도록 허용합니다."</string>
<string name="permlab_queryAllPackages" msgid="2928450604653281650">"모든 패키지 쿼리"</string>
<string name="permdesc_queryAllPackages" msgid="5339069855520996010">"앱이 설치된 패키지를 모두 볼 수 있도록 허용합니다."</string>
+ <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"SupplementalApis 액세스"</string>
+ <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"애플리케이션에서 SupplementalApis에 액세스하도록 허용합니다."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"확대/축소하려면 두 번 탭하세요."</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"위젯을 추가할 수 없습니다."</string>
<string name="ime_action_go" msgid="5536744546326495436">"이동"</string>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index 50c939361ec9..aa7f5aaf3fa7 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -1187,6 +1187,10 @@
<string name="deleteText" msgid="4200807474529938112">"Жок кылуу"</string>
<string name="inputMethod" msgid="1784759500516314751">"Киргизүү ыкмасы"</string>
<string name="editTextMenuTitle" msgid="857666911134482176">"Текст боюнча иштер"</string>
+ <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+ <skip />
+ <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="9024241779284783414">"Сактагычта орун калбай баратат"</string>
<string name="low_internal_storage_view_text" msgid="8172166728369697835">"Айрым функциялар иштебеши мүмкүн"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Тутумда сактагыч жетишсиз. 250МБ бош орун бар экенин текшерип туруп, өчүрүп күйгүзүңүз."</string>
@@ -1491,6 +1495,8 @@
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Колдонмо батареянын кубатын керектегенден мурун уруксат суралсын."</string>
<string name="permlab_queryAllPackages" msgid="2928450604653281650">"бардык топтомдор боюнча сурам жөнөтүү"</string>
<string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Колдонмо бардык орнотулган топтомдорду көрөт."</string>
+ <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"SupplementalApis\'ке кирүү"</string>
+ <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"Колдонмого SupplementalApis\'ке кирүү уруксатын берет."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Масштабдын параметрлерин өзгөртүү үчүн бул жерди эки жолу басыңыз."</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Виджетти кошуу мүмкүн болбоду."</string>
<string name="ime_action_go" msgid="5536744546326495436">"Өтүү"</string>
diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml
index 398b6b3b3e4f..fc6821137b6a 100644
--- a/core/res/res/values-lo/strings.xml
+++ b/core/res/res/values-lo/strings.xml
@@ -1187,6 +1187,10 @@
<string name="deleteText" msgid="4200807474529938112">"ລຶບ"</string>
<string name="inputMethod" msgid="1784759500516314751">"ຮູບແບບການປ້ອນຂໍ້ມູນ"</string>
<string name="editTextMenuTitle" msgid="857666911134482176">"ການເຮັດວຽກຂອງຂໍ້ຄວາມ"</string>
+ <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+ <skip />
+ <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="9024241779284783414">"ພື້ນທີ່ຈັດເກັບຂໍ້ມູນກຳລັງຈະເຕັມ"</string>
<string name="low_internal_storage_view_text" msgid="8172166728369697835">"ການເຮັດວຽກບາງຢ່າງຂອງລະບົບບາງອາດຈະໃຊ້ບໍ່ໄດ້"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"​ບໍ່​ມີ​ບ່ອນ​ເກັບ​ຂໍ້​ມູນ​ພຽງ​ພໍ​ສຳ​ລັບ​ລະ​ບົບ. ກວດ​ສອບ​ໃຫ້​ແນ່​ໃຈ​ວ່າ​ທ່ານ​ມີ​ພື້ນ​ທີ່​ຫວ່າງ​ຢ່າງ​ໜ້ອຍ 250MB ​ແລ້ວລອງ​ໃໝ່."</string>
@@ -1491,6 +1495,8 @@
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"ອະນຸຍາດໃຫ້ແອັບຖາມສິດອະນຸຍາດເພື່ອເພີກເສີຍຕໍ່ການປັບແຕ່ງແບັດເຕີຣີສຳລັບແອັບນັ້ນ."</string>
<string name="permlab_queryAllPackages" msgid="2928450604653281650">"ຊອກຫາແພັກເກດທັງໝົດ"</string>
<string name="permdesc_queryAllPackages" msgid="5339069855520996010">"ອະນຸຍາດໃຫ້ແອັບເບິ່ງເຫັນແພັກເກດທີ່ຕິດຕັ້ງແລ້ວທັງໝົດໄດ້."</string>
+ <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"ເຂົ້າເຖິງ SupplementalApis"</string>
+ <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"ອະນຸຍາດໃຫ້ແອັບພລິເຄຊັນເຂົ້າເຖິງ SupplementalApis ໄດ້."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"ແຕະສອງເທື່ອເພື່ອຄວບຄຸມການຊູມ"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"ບໍ່ສາມາດເພີ່ມວິດເຈັດໄດ້."</string>
<string name="ime_action_go" msgid="5536744546326495436">"ໄປ"</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index de36cb68442b..ef7b0f111c60 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -1227,6 +1227,10 @@
<string name="deleteText" msgid="4200807474529938112">"Ištrinti"</string>
<string name="inputMethod" msgid="1784759500516314751">"Įvesties būdas"</string>
<string name="editTextMenuTitle" msgid="857666911134482176">"Teksto veiksmai"</string>
+ <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+ <skip />
+ <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="9024241779284783414">"Mažėja laisvos saugyklos vietos"</string>
<string name="low_internal_storage_view_text" msgid="8172166728369697835">"Kai kurios sistemos funkcijos gali neveikti"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Sistemos saugykloje nepakanka vietos. Įsitikinkite, kad yra 250 MB laisvos vietos, ir paleiskite iš naujo."</string>
@@ -1531,6 +1535,8 @@
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Programai leidžiama prašyti leidimo nepaisyti tai programai skirto akumuliatoriaus optimizavimo nustatymų."</string>
<string name="permlab_queryAllPackages" msgid="2928450604653281650">"Teikti visų paketų užklausą"</string>
<string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Programai leidžiama peržiūrėti visus įdiegtus paketus."</string>
+ <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"pasiekti papildomas API"</string>
+ <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"Leidžiama programai pasiekti papildomas API."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Bakstelėkite du kartus, kad valdytumėte mastelio keitimą"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Nepavyko pridėti."</string>
<string name="ime_action_go" msgid="5536744546326495436">"Pradėti"</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index 93414e0a49f2..68725cbe3a73 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -982,7 +982,7 @@
<string name="keyguard_accessibility_widget_deleted" msgid="1509738950119878705">"Logrīks <xliff:g id="WIDGET_INDEX">%1$s</xliff:g> ir izdzēsts."</string>
<string name="keyguard_accessibility_expand_lock_area" msgid="4215280881346033434">"Izvērst atbloķēšanas apgabalu."</string>
<string name="keyguard_accessibility_slide_unlock" msgid="2968195219692413046">"Autorizācija, velkot ar pirkstu."</string>
- <string name="keyguard_accessibility_pattern_unlock" msgid="8669128146589233293">"Autorizācija ar kombināciju."</string>
+ <string name="keyguard_accessibility_pattern_unlock" msgid="8669128146589233293">"Atbloķēšanas kombinācija."</string>
<string name="keyguard_accessibility_face_unlock" msgid="4533832120787386728">"Autorizācija pēc sejas."</string>
<string name="keyguard_accessibility_pin_unlock" msgid="4020864007967340068">"Autorizācija ar PIN kodu."</string>
<string name="keyguard_accessibility_sim_pin_unlock" msgid="4895939120871890557">"SIM kartes atbloķēšanas PIN"</string>
@@ -1207,6 +1207,10 @@
<string name="deleteText" msgid="4200807474529938112">"Dzēst"</string>
<string name="inputMethod" msgid="1784759500516314751">"Ievades metode"</string>
<string name="editTextMenuTitle" msgid="857666911134482176">"Teksta darbības"</string>
+ <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+ <skip />
+ <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="9024241779284783414">"Paliek maz brīvas vietas"</string>
<string name="low_internal_storage_view_text" msgid="8172166728369697835">"Dažas sistēmas funkcijas var nedarboties."</string>
<string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Sistēmai pietrūkst vietas. Atbrīvojiet vismaz 250 MB vietas un restartējiet ierīci."</string>
@@ -1511,6 +1515,8 @@
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Ļauj lietotnei lūgt atļauju ignorēt akumulatora optimizāciju šai lietotnei."</string>
<string name="permlab_queryAllPackages" msgid="2928450604653281650">"pieprasīt atļauju skatīt visas pakotnes"</string>
<string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Ļauj lietotnei skatīt visas instalētās pakotnes."</string>
+ <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"piekļuve SupplementalApis"</string>
+ <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"Ļauj lietojumprogrammai piekļūt SupplementalApis."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Pieskarieties divreiz, lai kontrolētu tālummaiņu."</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Nevarēja pievienot logrīku."</string>
<string name="ime_action_go" msgid="5536744546326495436">"Doties uz"</string>
@@ -1917,7 +1923,7 @@
<string name="managed_profile_label_badge_2" msgid="5673187309555352550">"2. darba profils: <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="managed_profile_label_badge_3" msgid="6882151970556391957">"3. darba profils: <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="lock_to_app_unlock_pin" msgid="3890940811866290782">"Prasīt PIN kodu pirms atspraušanas"</string>
- <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"Pirms atspraušanas pieprasīt grafisko atsl."</string>
+ <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"Pirms atspraušanas pieprasīt atbloķēšanas kombināciju"</string>
<string name="lock_to_app_unlock_password" msgid="9126722403506560473">"Pirms atspraušanas pieprasīt paroli"</string>
<string name="package_installed_device_owner" msgid="7035926868974878525">"Instalēja administrators"</string>
<string name="package_updated_device_owner" msgid="7560272363805506941">"Atjaunināja administrators"</string>
diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml
index d4f099f20068..1ccfc7e26079 100644
--- a/core/res/res/values-mk/strings.xml
+++ b/core/res/res/values-mk/strings.xml
@@ -1187,6 +1187,10 @@
<string name="deleteText" msgid="4200807474529938112">"Избриши"</string>
<string name="inputMethod" msgid="1784759500516314751">"Метод на внес"</string>
<string name="editTextMenuTitle" msgid="857666911134482176">"Дејства со текст"</string>
+ <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+ <skip />
+ <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="9024241779284783414">"Капацитетот е речиси полн"</string>
<string name="low_internal_storage_view_text" msgid="8172166728369697835">"Некои системски функции може да не работат"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Нема доволно меморија во системот. Проверете дали има слободен простор од 250 MB и рестартирајте."</string>
@@ -1491,6 +1495,8 @@
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Овозможува апликацијата да побара дозвола за игнорирање на оптимизациите на батеријата за таа апликација."</string>
<string name="permlab_queryAllPackages" msgid="2928450604653281650">"пребарување на сите пакети"</string>
<string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Дозволува апликацијата да ги гледа сите инсталирани пакети."</string>
+ <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"пристап до SupplementalApis"</string>
+ <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"Дозволува апликацијата да пристапува до SupplementalApis."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Допрете двапати за контрола на зумот"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Не може да се додаде виџет."</string>
<string name="ime_action_go" msgid="5536744546326495436">"Оди"</string>
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index 1c98f25cd95a..77540e1a1c1a 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -1187,6 +1187,10 @@
<string name="deleteText" msgid="4200807474529938112">"ഇല്ലാതാക്കുക"</string>
<string name="inputMethod" msgid="1784759500516314751">"ടൈപ്പുചെയ്യൽ രീതി"</string>
<string name="editTextMenuTitle" msgid="857666911134482176">"ടെക്‌സ്‌റ്റ് പ്രവർത്തനങ്ങൾ"</string>
+ <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+ <skip />
+ <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="9024241779284783414">"സംഭരണയിടം കഴിഞ്ഞു"</string>
<string name="low_internal_storage_view_text" msgid="8172166728369697835">"ചില സിസ്റ്റം പ്രവർത്തനങ്ങൾ പ്രവർത്തിക്കണമെന്നില്ല."</string>
<string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"സിസ്‌റ്റത്തിനായി മതിയായ സംഭരണമില്ല. 250MB സൗജന്യ സംഭരണമുണ്ടെന്ന് ഉറപ്പുവരുത്തി പുനരാരംഭിക്കുക."</string>
@@ -1491,6 +1495,8 @@
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"ആപ്പിന് വേണ്ടിയുള്ള ബാറ്ററി ഒപ്റ്റിമൈസേഷനുകളെ അവഗണിക്കാനുള്ള അനുമതി ചോദിക്കുന്നതിന് ആപ്പിനെ അനുവദിക്കുന്നു."</string>
<string name="permlab_queryAllPackages" msgid="2928450604653281650">"എല്ലാ പാക്കേജുകളും നോക്കുക"</string>
<string name="permdesc_queryAllPackages" msgid="5339069855520996010">"ഇൻസ്‌റ്റാൾ ചെയ്‌ത എല്ലാ പാക്കേജുകളും കാണാൻ ഒരു ആപ്പിനെ അനുവദിക്കുന്നു."</string>
+ <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"SupplementalApis ആക്‌സസ് ചെയ്യുക"</string>
+ <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"SupplementalApis ആക്‌സസ് ചെയ്യാൻ ഒരു ആപ്പിനെ അനുവദിക്കുന്നു."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"സൂം നിയന്ത്രണം ലഭിക്കാൻ രണ്ടുതവണ ടാപ്പുചെയ്യുക"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"വിജറ്റ് ചേർക്കാനായില്ല."</string>
<string name="ime_action_go" msgid="5536744546326495436">"പോവുക"</string>
diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml
index db67e904d29d..dcb48593e895 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -1187,6 +1187,10 @@
<string name="deleteText" msgid="4200807474529938112">"Устгах"</string>
<string name="inputMethod" msgid="1784759500516314751">"Оруулах арга"</string>
<string name="editTextMenuTitle" msgid="857666911134482176">"Текст үйлдэл"</string>
+ <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+ <skip />
+ <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="9024241779284783414">"Сангийн хэмжээ дутагдаж байна"</string>
<string name="low_internal_storage_view_text" msgid="8172166728369697835">"Зарим систем функц ажиллахгүй байна"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Системд хангалттай сан байхгүй байна. 250MБ чөлөөтэй зай байгаа эсэхийг шалгаад дахин эхлүүлнэ үү."</string>
@@ -1491,6 +1495,8 @@
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Тухайн аппaaс батерейны оновчлол алгасах зөвшөөрөл асуухыг зөвшөөрдөг."</string>
<string name="permlab_queryAllPackages" msgid="2928450604653281650">"бүх багцыг лавлах"</string>
<string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Аппап бүх суулгасан багцыг харахыг зөвшөөрнө."</string>
+ <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"SupplementalApis-д хандах"</string>
+ <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"Аппликэйшнд SupplementalApis-д хандах зөвшөөрлийг олгодог."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Өсгөх контрол дээр хоёр удаа товшино уу"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Виджет нэмж чадсангүй."</string>
<string name="ime_action_go" msgid="5536744546326495436">"Очих"</string>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index 1385dcd51878..9cac6c860982 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -1187,6 +1187,10 @@
<string name="deleteText" msgid="4200807474529938112">"हटवा"</string>
<string name="inputMethod" msgid="1784759500516314751">"इनपुट पद्धत"</string>
<string name="editTextMenuTitle" msgid="857666911134482176">"मजकूर क्रिया"</string>
+ <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+ <skip />
+ <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="9024241779284783414">"संचयन स्थान संपत आहे"</string>
<string name="low_internal_storage_view_text" msgid="8172166728369697835">"काही सिस्टम कार्ये कार्य करू शकत नाहीत"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"सिस्टीमसाठी पुरेसे संचयन नाही. आपल्याकडे 250MB मोकळे स्थान असल्याचे सुनिश्चित करा आणि रीस्टार्ट करा."</string>
@@ -1491,6 +1495,8 @@
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"त्या ॲपसाठी बॅटरी ऑप्टिमायझेशन दुर्लक्षित करण्‍यासाठी ॲपला परवानगी मागण्याची अनुमती देते."</string>
<string name="permlab_queryAllPackages" msgid="2928450604653281650">"सर्व पॅकेजविषयी क्वेरी करा"</string>
<string name="permdesc_queryAllPackages" msgid="5339069855520996010">"ॲपला इंस्टॉल केलेले सर्व पॅकेज पाहण्याची अनुमती देते."</string>
+ <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"SupplementalApis अ‍ॅक्सेस करा"</string>
+ <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"अ‍ॅप्लिकेशनला SupplementalApis अ‍ॅक्सेस करण्याची अनुमती देते."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"झूम नियंत्रणासाठी दोनदा टॅप करा"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"विजेट जोडू शकलो नाही."</string>
<string name="ime_action_go" msgid="5536744546326495436">"जा"</string>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index 43847feefea9..1f5b81878f74 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -1187,6 +1187,10 @@
<string name="deleteText" msgid="4200807474529938112">"Padam"</string>
<string name="inputMethod" msgid="1784759500516314751">"Kaedah input"</string>
<string name="editTextMenuTitle" msgid="857666911134482176">"Tindakan teks"</string>
+ <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+ <skip />
+ <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="9024241779284783414">"Ruang storan semakin berkurangan"</string>
<string name="low_internal_storage_view_text" msgid="8172166728369697835">"Beberapa fungsi sistem mungkin tidak berfungsi"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Tidak cukup storan untuk sistem. Pastikan anda mempunyai 250MB ruang kosong dan mulakan semula."</string>
@@ -1491,6 +1495,8 @@
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Membenarkan apl meminta kebenaran untuk mengabaikan pengoptimuman bateri untuk apl itu."</string>
<string name="permlab_queryAllPackages" msgid="2928450604653281650">"buat pertanyaan untuk semua pakej"</string>
<string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Membenarkan apl melihat semua pakej yang dipasang."</string>
+ <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"akses SupplementalApis"</string>
+ <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"Membenarkan aplikasi mengakses SupplementalApis."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Ketik dua kali untuk mendapatkan kawalan zum"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Tidak dapat menambahkan widget."</string>
<string name="ime_action_go" msgid="5536744546326495436">"Pergi"</string>
diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml
index f509906480a2..b244af5c6b87 100644
--- a/core/res/res/values-my/strings.xml
+++ b/core/res/res/values-my/strings.xml
@@ -1187,6 +1187,10 @@
<string name="deleteText" msgid="4200807474529938112">"ဖျက်ရန်"</string>
<string name="inputMethod" msgid="1784759500516314751">"ထည့်သွင်းရန်နည်းလမ်း"</string>
<string name="editTextMenuTitle" msgid="857666911134482176">"စာတို လုပ်ဆောင်ချက်"</string>
+ <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+ <skip />
+ <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="9024241779284783414">"သိမ်းဆည်သော နေရာ နည်းနေပါသည်"</string>
<string name="low_internal_storage_view_text" msgid="8172166728369697835">"တချို့ စနစ်လုပ်ငန်းများ အလုပ် မလုပ်ခြင်း ဖြစ်နိုင်ပါသည်"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"စနစ်အတွက် သိုလှောင်ခန်း မလုံလောက်ပါ။ သင့်ဆီမှာ နေရာလွတ် ၂၅၀ MB ရှိတာ စစ်ကြည့်ပြီး စတင်ပါ။"</string>
@@ -1491,6 +1495,8 @@
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"ဘက်ထရီ ပိုမိုကောင်းမွန်အောင် ပြုလုပ်ခြင်းကို လျစ်လျူရှုရန်အတွက် ခွင့်ပြုချက်တောင်းရန် အက်ပ်ကို ခွင့်ပြုပါ။"</string>
<string name="permlab_queryAllPackages" msgid="2928450604653281650">"ပက်ကေ့ဂျ်အားလုံးကို မေးမြန်းခြင်း"</string>
<string name="permdesc_queryAllPackages" msgid="5339069855520996010">"ထည့်သွင်းထားသော ပက်ကေ့ဂျ်အားလုံး ကြည့်ရန် အက်ပ်ကို ခွင့်ပြုပါ။"</string>
+ <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"SupplementalApis သုံးခွင့်"</string>
+ <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"အပလီကေးရှင်းအား SupplementalApis သုံးခွင့်ပေးနိုင်သည်။"</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"ဇူးမ်အသုံးပြုရန် နှစ်ချက်တို့ပါ"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"ဝဒ်ဂျက်ထည့်လို့ မရပါ"</string>
<string name="ime_action_go" msgid="5536744546326495436">"သွားပါ"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index 2ddd45da07be..a792d37d2011 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -1187,6 +1187,10 @@
<string name="deleteText" msgid="4200807474529938112">"Slett"</string>
<string name="inputMethod" msgid="1784759500516314751">"Inndatametode"</string>
<string name="editTextMenuTitle" msgid="857666911134482176">"Teksthandlinger"</string>
+ <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+ <skip />
+ <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="9024241779284783414">"Lite ledig lagringsplass"</string>
<string name="low_internal_storage_view_text" msgid="8172166728369697835">"Enkelte systemfunksjoner fungerer muligens ikke slik de skal"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Det er ikke nok lagringsplass for systemet. Kontrollér at du har 250 MB ledig plass, og start på nytt."</string>
@@ -1491,6 +1495,8 @@
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Gjør det mulig for apper å be om tillatelse til å ignorere batterioptimaliseringer for disse appene."</string>
<string name="permlab_queryAllPackages" msgid="2928450604653281650">"søk i alle pakker"</string>
<string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Lar en app se alle installerte pakker."</string>
+ <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"tilgang til SupplementalApis"</string>
+ <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"Gir appen tilgang til SupplementalApis."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Trykk to ganger for zoomkontroll"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Kunne ikke legge til modulen."</string>
<string name="ime_action_go" msgid="5536744546326495436">"Utfør"</string>
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index d4828b6ce942..fe9c52b1c489 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -1187,6 +1187,10 @@
<string name="deleteText" msgid="4200807474529938112">"मेट्नुहोस्"</string>
<string name="inputMethod" msgid="1784759500516314751">"निवेश विधि"</string>
<string name="editTextMenuTitle" msgid="857666911134482176">"पाठ कार्यहरू"</string>
+ <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+ <skip />
+ <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="9024241779284783414">"भण्डारण ठाउँ सकिँदै छ"</string>
<string name="low_internal_storage_view_text" msgid="8172166728369697835">"सायद केही प्रणाली कार्यक्रमहरूले काम गर्दैनन्"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"प्रणालीको लागि पर्याप्त भण्डारण छैन। तपाईँसँग २५० मेगा बाइट ठाउँ खाली भएको निश्चित गर्नुहोस् र फेरि सुरु गर्नुहोस्।"</string>
@@ -1491,6 +1495,8 @@
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"कुनै एपलाई त्यसका ब्याट्री सम्बन्धी अनुकूलनहरूलाई बेवास्ता गर्नाका लागि अनुमति माग्न दिन्छ।"</string>
<string name="permlab_queryAllPackages" msgid="2928450604653281650">"सबै प्याकेजहरू खोज्नुहोस्"</string>
<string name="permdesc_queryAllPackages" msgid="5339069855520996010">"यसले यस एपलाई इन्स्टल गरिएका सबै प्याकेजहरू हेर्ने अनुमति दिन्छ।"</string>
+ <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"SupplementalApis प्रयोग गर्ने अनुमति"</string>
+ <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"एपलाई SupplementalApis प्रयोग गर्ने अनुमति दिन्छ।"</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"जुम नियन्त्रणको लागि दुई चोटि ट्याप गर्नुहोस्"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"विजेट थप गर्न सकिँदैन।"</string>
<string name="ime_action_go" msgid="5536744546326495436">"जानुहोस्"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index 3b6db066eb8f..686c951ec037 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -1187,6 +1187,10 @@
<string name="deleteText" msgid="4200807474529938112">"Verwijderen"</string>
<string name="inputMethod" msgid="1784759500516314751">"Invoermethode"</string>
<string name="editTextMenuTitle" msgid="857666911134482176">"Tekstacties"</string>
+ <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+ <skip />
+ <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="9024241779284783414">"Opslagruimte is bijna vol"</string>
<string name="low_internal_storage_view_text" msgid="8172166728369697835">"Bepaalde systeemfuncties werken mogelijk niet"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Onvoldoende opslagruimte voor het systeem. Zorg ervoor dat je 250 MB vrije ruimte hebt en start opnieuw."</string>
@@ -1491,6 +1495,8 @@
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Hiermee kan een app rechten vragen om batterijoptimalisatie voor die app te negeren."</string>
<string name="permlab_queryAllPackages" msgid="2928450604653281650">"alle pakketten opvragen"</string>
<string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Hiermee kan een app alle geïnstalleerde pakketten zien."</string>
+ <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"toegang tot SupplementalApis"</string>
+ <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"Geeft een app toegang tot SupplementalApis."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Tik twee keer voor zoomregeling"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Kan widget niet toevoegen."</string>
<string name="ime_action_go" msgid="5536744546326495436">"Ga"</string>
diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml
index 0220ce3c3540..69ca39c9c1d4 100644
--- a/core/res/res/values-or/strings.xml
+++ b/core/res/res/values-or/strings.xml
@@ -1187,6 +1187,10 @@
<string name="deleteText" msgid="4200807474529938112">"ଡିଲିଟ୍‍ କରନ୍ତୁ"</string>
<string name="inputMethod" msgid="1784759500516314751">"ଇନପୁଟ୍ ପଦ୍ଧତି"</string>
<string name="editTextMenuTitle" msgid="857666911134482176">"ଟେକ୍ସଟ୍‌ କାର୍ଯ୍ୟ"</string>
+ <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+ <skip />
+ <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="9024241779284783414">"ଷ୍ଟୋରେଜ୍‌ ସ୍ପେସ୍‌ ଶେଷ ହେବାରେ ଲାଗିଛି"</string>
<string name="low_internal_storage_view_text" msgid="8172166728369697835">"କିଛି ସିଷ୍ଟମ ପ୍ରକାର୍ଯ୍ୟ କାମ କରିନପାରେ"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"ସିଷ୍ଟମ୍ ପାଇଁ ପ୍ରର୍ଯ୍ୟାପ୍ତ ଷ୍ଟୋରେଜ୍‌ ନାହିଁ। ସୁନିଶ୍ଚିତ କରନ୍ତୁ ଯେ, ଆପଣଙ୍କ ପାଖରେ 250MB ଖାଲି ଜାଗା ଅଛି ଏବଂ ପୁନଃ ଆରମ୍ଭ କରନ୍ତୁ।"</string>
@@ -1491,6 +1495,8 @@
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"ଆପ୍‍ ପାଇଁ ବ୍ୟାଟେରୀ ଅନୁକୂଳନ ଏଡ଼ାଇବାର ଅନୁମତି ମାଗିବା ନିମନ୍ତେ ଆପ୍‍କୁ ଅନୁମତି ଦେଇଥାଏ।"</string>
<string name="permlab_queryAllPackages" msgid="2928450604653281650">"ସବୁ ପ୍ୟାକେଜ୍ ବିଷୟରେ କ୍ୱେରୀ କରନ୍ତୁ"</string>
<string name="permdesc_queryAllPackages" msgid="5339069855520996010">"ଇନଷ୍ଟଲ୍ କରାଯାଇଥିବା ସମସ୍ତ ପ୍ୟାକେଜକୁ ଦେଖିବା ପାଇଁ ଏକ ଆପକୁ ଅନୁମତି ଦିଏ।"</string>
+ <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"SupplementalApiଗୁଡ଼ିକୁ ଆକ୍ସେସ କରନ୍ତୁ"</string>
+ <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"SupplementalApiଗୁଡ଼ିକୁ ଆକ୍ସେସ କରିବା ପାଇଁ ଏକ ଆପ୍ଲିକେସନକୁ ଅନୁମତି ଦିଏ।"</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"ଜୁମ୍ ନିୟନ୍ତ୍ରଣ ପାଇଁ ଦୁଇଥର ଟାପ୍‌ କରନ୍ତୁ"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"ୱିଜେଟ୍‍ ଯୋଡ଼ିପାରିବ ନାହିଁ।"</string>
<string name="ime_action_go" msgid="5536744546326495436">"ଯାଆନ୍ତୁ"</string>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index 7a3bc337bb82..864dda2a53e9 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -1187,6 +1187,10 @@
<string name="deleteText" msgid="4200807474529938112">"ਮਿਟਾਓ"</string>
<string name="inputMethod" msgid="1784759500516314751">"ਇਨਪੁੱਟ ਵਿਧੀ"</string>
<string name="editTextMenuTitle" msgid="857666911134482176">"ਟੈਕਸਟ ਕਿਰਿਆਵਾਂ"</string>
+ <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+ <skip />
+ <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="9024241779284783414">"ਸਟੋਰੇਜ ਦੀ ਜਗ੍ਹਾ ਖਤਮ ਹੋ ਰਹੀ ਹੈ"</string>
<string name="low_internal_storage_view_text" msgid="8172166728369697835">"ਕੁਝ ਸਿਸਟਮ ਫੰਕਸ਼ਨ ਕੰਮ ਨਹੀਂ ਵੀ ਕਰ ਸਕਦੇ"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"ਸਿਸਟਮ ਲਈ ਲੋੜੀਂਦੀ ਸਟੋਰੇਜ ਨਹੀਂ ਹੈ। ਯਕੀਨੀ ਬਣਾਓ ਕਿ ਤੁਹਾਡੇ ਕੋਲ 250MB ਖਾਲੀ ਜਗ੍ਹਾ ਹੈ ਅਤੇ ਮੁੜ-ਚਾਲੂ ਕਰੋ।"</string>
@@ -1491,6 +1495,8 @@
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"ਕਿਸੇ ਐਪ ਨੂੰ ਉਸ ਵਾਸਤੇ ਬੈਟਰੀ ਸੁਯੋਗਤਾਵਾਂ ਨੂੰ ਅਣਡਿੱਠ ਕਰਨ ਲਈ ਇਜਾਜ਼ਤ ਵਾਸਤੇ ਪੁੱਛਣ ਲਈ ਆਗਿਆ ਦਿੰਦੀ ਹੈ।"</string>
<string name="permlab_queryAllPackages" msgid="2928450604653281650">"ਸਾਰੇ ਪੈਕੇਜਾਂ ਬਾਰੇ ਪੁੱਛਗਿੱਛ"</string>
<string name="permdesc_queryAllPackages" msgid="5339069855520996010">"ਇਸ ਨਾਲ ਐਪ ਨੂੰ ਸਥਾਪਤ ਕੀਤੇ ਸਾਰੇ ਪੈਕੇਜ ਦੇਖਣ ਦੀ ਇਜਾਜ਼ਤ ਮਿਲਦੀ ਹੈ।"</string>
+ <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"SupplementalApis ਤੱਕ ਪਹੁੰਚ"</string>
+ <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"ਕਿਸੇ ਐਪਲੀਕੇਸ਼ਨ ਨੂੰ SupplementalApis ਤੱਕ ਪਹੁੰਚ ਦੇ ਯੋਗ ਬਣਾਉਂਦਾ ਹੈ।"</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"ਜ਼ੂਮ ਕੰਟਰੋਲ ਲਈ ਦੋ ਵਾਰ ਟੈਪ ਕਰੋ"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"ਵਿਜੇਟ ਸ਼ਾਮਲ ਨਹੀਂ ਹੋ ਸਕਿਆ।"</string>
<string name="ime_action_go" msgid="5536744546326495436">"ਜਾਓ"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index bbc18679f089..4d9a07c1cd0f 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -1227,6 +1227,10 @@
<string name="deleteText" msgid="4200807474529938112">"Usuń"</string>
<string name="inputMethod" msgid="1784759500516314751">"Sposób wprowadzania tekstu"</string>
<string name="editTextMenuTitle" msgid="857666911134482176">"Działania na tekście"</string>
+ <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+ <skip />
+ <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="9024241779284783414">"Kończy się miejsce"</string>
<string name="low_internal_storage_view_text" msgid="8172166728369697835">"Niektóre funkcje systemu mogą nie działać"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Za mało pamięci w systemie. Upewnij się, że masz 250 MB wolnego miejsca i uruchom urządzenie ponownie."</string>
@@ -1531,6 +1535,8 @@
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Zezwala aplikacji na proszenie o uprawnienia do ignorowania optymalizacji wykorzystania baterii w przypadku danej aplikacji."</string>
<string name="permlab_queryAllPackages" msgid="2928450604653281650">"zapytanie o wszystkie pakiety"</string>
<string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Pozwala aplikacji wyświetlać wszystkie zainstalowane pakiety."</string>
+ <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"dostęp do SupplementalApis"</string>
+ <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"Zezwala na dostęp aplikacji do SupplementalApis."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Dotknij dwukrotnie, aby sterować powiększeniem"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Nie można dodać widżetu."</string>
<string name="ime_action_go" msgid="5536744546326495436">"OK"</string>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index 383531023f07..7b7fd55e2f23 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -1187,6 +1187,10 @@
<string name="deleteText" msgid="4200807474529938112">"Excluir"</string>
<string name="inputMethod" msgid="1784759500516314751">"Método de entrada"</string>
<string name="editTextMenuTitle" msgid="857666911134482176">"Ações de texto"</string>
+ <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+ <skip />
+ <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="9024241779284783414">"Pouco espaço de armazenamento"</string>
<string name="low_internal_storage_view_text" msgid="8172166728369697835">"Algumas funções do sistema podem não funcionar"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Não há armazenamento suficiente para o sistema. Certifique-se de ter 250 MB de espaço livre e reinicie."</string>
@@ -1491,6 +1495,8 @@
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Permite que um app peça permissão para ignorar as otimizações de bateria para esse app."</string>
<string name="permlab_queryAllPackages" msgid="2928450604653281650">"consultar todos os pacotes"</string>
<string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Permite que um app veja todos os pacotes instalados."</string>
+ <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"acessar SupplementalApis"</string>
+ <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"Permite que um aplicativo tenha acesso a SupplementalApis."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Toque duas vezes para ter controle do zoom"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Não foi possível adicionar widget."</string>
<string name="ime_action_go" msgid="5536744546326495436">"Ir"</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index c2df19aea46b..b4c191dfeb40 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -1187,6 +1187,10 @@
<string name="deleteText" msgid="4200807474529938112">"Eliminar"</string>
<string name="inputMethod" msgid="1784759500516314751">"Método de entrada"</string>
<string name="editTextMenuTitle" msgid="857666911134482176">"Acções de texto"</string>
+ <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+ <skip />
+ <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="9024241779284783414">"Está quase sem espaço de armazenamento"</string>
<string name="low_internal_storage_view_text" msgid="8172166728369697835">"Algumas funções do sistema poderão não funcionar"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Não existe armazenamento suficiente para o sistema. Certifique-se de que tem 250 MB de espaço livre e reinicie."</string>
@@ -1491,6 +1495,8 @@
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Permite que uma app solicite autorização para ignorar as otimizações da bateria para a mesma."</string>
<string name="permlab_queryAllPackages" msgid="2928450604653281650">"consultar todos os pacotes"</string>
<string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Permite a uma app ver todos os pacotes instalados."</string>
+ <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"aceder a SupplementalApis"</string>
+ <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"Permite a uma aplicação aceder a SupplementalApis."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Tocar duas vezes para controlar o zoom"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Não foi possível adicionar widget."</string>
<string name="ime_action_go" msgid="5536744546326495436">"Ir"</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index 383531023f07..7b7fd55e2f23 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -1187,6 +1187,10 @@
<string name="deleteText" msgid="4200807474529938112">"Excluir"</string>
<string name="inputMethod" msgid="1784759500516314751">"Método de entrada"</string>
<string name="editTextMenuTitle" msgid="857666911134482176">"Ações de texto"</string>
+ <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+ <skip />
+ <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="9024241779284783414">"Pouco espaço de armazenamento"</string>
<string name="low_internal_storage_view_text" msgid="8172166728369697835">"Algumas funções do sistema podem não funcionar"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Não há armazenamento suficiente para o sistema. Certifique-se de ter 250 MB de espaço livre e reinicie."</string>
@@ -1491,6 +1495,8 @@
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Permite que um app peça permissão para ignorar as otimizações de bateria para esse app."</string>
<string name="permlab_queryAllPackages" msgid="2928450604653281650">"consultar todos os pacotes"</string>
<string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Permite que um app veja todos os pacotes instalados."</string>
+ <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"acessar SupplementalApis"</string>
+ <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"Permite que um aplicativo tenha acesso a SupplementalApis."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Toque duas vezes para ter controle do zoom"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Não foi possível adicionar widget."</string>
<string name="ime_action_go" msgid="5536744546326495436">"Ir"</string>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index 4469a67d3caa..ffa84e32470f 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -1207,6 +1207,10 @@
<string name="deleteText" msgid="4200807474529938112">"Ștergeți"</string>
<string name="inputMethod" msgid="1784759500516314751">"Metodă de intrare"</string>
<string name="editTextMenuTitle" msgid="857666911134482176">"Acțiuni pentru text"</string>
+ <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+ <skip />
+ <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="9024241779284783414">"Spațiul de stocare aproape ocupat"</string>
<string name="low_internal_storage_view_text" msgid="8172166728369697835">"Este posibil ca unele funcții de sistem să nu funcționeze"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Spațiu de stocare insuficient pentru sistem. Asigurați-vă că aveți 250 MB de spațiu liber și reporniți."</string>
@@ -1511,6 +1515,8 @@
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Permite unei aplicații să solicite permisiunea de a ignora optimizările bateriei pentru aplicația respectivă."</string>
<string name="permlab_queryAllPackages" msgid="2928450604653281650">"să interogheze toate pachetele"</string>
<string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Permite unei aplicații să vadă toate pachetele instalate."</string>
+ <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"să acceseze SupplementalApis"</string>
+ <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"Permite unei aplicații să acceseze SupplementalApis."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Apăsați de două ori pentru a controla mărirea/micșorarea"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Nu s-a putut adăuga widgetul."</string>
<string name="ime_action_go" msgid="5536744546326495436">"Accesați"</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 6b874b9e4e40..2993b48288e1 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -1227,6 +1227,10 @@
<string name="deleteText" msgid="4200807474529938112">"Удалить"</string>
<string name="inputMethod" msgid="1784759500516314751">"Способ ввода"</string>
<string name="editTextMenuTitle" msgid="857666911134482176">"Операции с текстом"</string>
+ <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+ <skip />
+ <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="9024241779284783414">"Недостаточно памяти"</string>
<string name="low_internal_storage_view_text" msgid="8172166728369697835">"Некоторые функции могут не работать"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Недостаточно свободного места для системы. Освободите не менее 250 МБ дискового пространства и перезапустите устройство."</string>
@@ -1531,6 +1535,8 @@
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Разрешает приложению игнорировать ограничение на расход заряда батареи."</string>
<string name="permlab_queryAllPackages" msgid="2928450604653281650">"Запрос информации обо всех пакетах"</string>
<string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Приложение сможет просматривать все установленные пакеты."</string>
+ <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"Доступ к SupplementalApis"</string>
+ <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"Приложение сможет получать доступ к SupplementalApis."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Нажмите дважды для изменения масштаба"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Не удалось добавить виджет."</string>
<string name="ime_action_go" msgid="5536744546326495436">"Выбрать"</string>
diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml
index 34436810b101..f243dab34563 100644
--- a/core/res/res/values-si/strings.xml
+++ b/core/res/res/values-si/strings.xml
@@ -1187,6 +1187,10 @@
<string name="deleteText" msgid="4200807474529938112">"මකන්න"</string>
<string name="inputMethod" msgid="1784759500516314751">"ආදාන ක්‍රමය"</string>
<string name="editTextMenuTitle" msgid="857666911134482176">"පෙළ ක්‍රියාවන්"</string>
+ <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+ <skip />
+ <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="9024241779284783414">"ආචයනය ඉඩ ප්‍රමාණය අඩු වී ඇත"</string>
<string name="low_internal_storage_view_text" msgid="8172166728369697835">"සමහර පද්ධති කාර්යයන් ක්‍රියා නොකරනු ඇත"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"පද්ධතිය සඳහා ප්‍රමාණවත් ඉඩ නොමැත. ඔබට 250MB නිදහස් ඉඩක් තිබෙන ඔබට තිබෙන බව සහතික කරගෙන නැවත උත්සාහ කරන්න."</string>
@@ -1491,6 +1495,8 @@
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"යෙදුමකට එම යෙදුම සඳහා බැටරි ප්‍රශස්තකරණ නොසලකා හැරීමට අවසර ඉල්ලීමට ඉඩ දෙයි."</string>
<string name="permlab_queryAllPackages" msgid="2928450604653281650">"සියලු පැකේජ විමසන්න"</string>
<string name="permdesc_queryAllPackages" msgid="5339069855520996010">"ස්ථාපනය කර ඇති සියලු පැකේජ බැලීමට යෙදුමකට ඉඩ දෙයි."</string>
+ <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"SupplementalApis වෙත ප්‍රවේශ වන්න"</string>
+ <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"SupplementalApis වෙත ප්‍රවේශ වීමට යෙදුමකට ඉඩ දෙයි."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"විශාලන පාලක සඳහා දෙවතාවක් තට්ටු කරන්න"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"විජටය එකතු කිරීමට නොහැකි විය."</string>
<string name="ime_action_go" msgid="5536744546326495436">"යන්න"</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index 517ce6c28a6e..f2b3ae18e117 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -1227,6 +1227,10 @@
<string name="deleteText" msgid="4200807474529938112">"Odstrániť"</string>
<string name="inputMethod" msgid="1784759500516314751">"Metóda vstupu"</string>
<string name="editTextMenuTitle" msgid="857666911134482176">"Operácie s textom"</string>
+ <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+ <skip />
+ <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="9024241779284783414">"Nedostatok ukladacieho priestoru"</string>
<string name="low_internal_storage_view_text" msgid="8172166728369697835">"Niektoré systémové funkcie nemusia fungovať"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"V úložisku nie je dostatok voľného miesta pre systém. Zaistite, aby ste mali 250 MB voľného miesta a zariadenie reštartujte."</string>
@@ -1531,6 +1535,8 @@
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Umožňuje aplikácii požiadať o povolenie ignorovať optimalizácie výdrže batérie pre danú aplikáciu."</string>
<string name="permlab_queryAllPackages" msgid="2928450604653281650">"dopytovať všetky balíky"</string>
<string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Povoľuje aplikácii čítať všetky nainštalované balíky."</string>
+ <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"prístup k rozhraniam SupplementalApi"</string>
+ <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"Umožňuje aplikácii získať prístup k rozhraniam SupplementalApi."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Dvojitým klepnutím môžete ovládať priblíženie"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Miniaplikáciu sa nepodarilo pridať."</string>
<string name="ime_action_go" msgid="5536744546326495436">"Hľadať"</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 7bcc257b4abb..0d71d3bd41f5 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -1227,6 +1227,10 @@
<string name="deleteText" msgid="4200807474529938112">"Izbriši"</string>
<string name="inputMethod" msgid="1784759500516314751">"Način vnosa"</string>
<string name="editTextMenuTitle" msgid="857666911134482176">"Besedilna dejanja"</string>
+ <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+ <skip />
+ <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="9024241779284783414">"Prostor za shranjevanje bo pošel"</string>
<string name="low_internal_storage_view_text" msgid="8172166728369697835">"Nekatere sistemske funkcije morda ne delujejo"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"V shrambi ni dovolj prostora za sistem. Sprostite 250 MB prostora in znova zaženite napravo."</string>
@@ -1531,6 +1535,8 @@
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Aplikaciji dovoljuje, da vpraša za dovoljenje, ali naj prezre optimizacije baterije."</string>
<string name="permlab_queryAllPackages" msgid="2928450604653281650">"poizvedovanje po vseh paketih"</string>
<string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Aplikaciji dovoli, da vidi vse nameščene pakete."</string>
+ <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"dostop do SupplementalApis"</string>
+ <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"Aplikaciji omogoča dostop do SupplementalApis."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Tapnite dvakrat za nadzor povečave/pomanjšave"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Pripomočka ni bilo mogoče dodati."</string>
<string name="ime_action_go" msgid="5536744546326495436">"Pojdi"</string>
diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml
index 07371009414e..3050c84a7b20 100644
--- a/core/res/res/values-sq/strings.xml
+++ b/core/res/res/values-sq/strings.xml
@@ -1187,6 +1187,10 @@
<string name="deleteText" msgid="4200807474529938112">"Fshi"</string>
<string name="inputMethod" msgid="1784759500516314751">"Metoda e hyrjes"</string>
<string name="editTextMenuTitle" msgid="857666911134482176">"Veprimet e tekstit"</string>
+ <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+ <skip />
+ <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="9024241779284783414">"Hapësira ruajtëse po mbaron"</string>
<string name="low_internal_storage_view_text" msgid="8172166728369697835">"Disa funksione të sistemit mund të mos punojnë"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Nuk ka hapësirë të mjaftueshme ruajtjeje për sistemin. Sigurohu që të kesh 250 MB hapësirë të lirë dhe pastaj të rifillosh."</string>
@@ -1491,6 +1495,8 @@
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Lejon që një aplikacion të kërkojë leje për të shpërfillur optimizimet e baterisë për atë aplikacion."</string>
<string name="permlab_queryAllPackages" msgid="2928450604653281650">"kërko të gjitha paketat"</string>
<string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Lejon një aplikacion të shikojë të gjitha paketat e instaluara."</string>
+ <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"qasje te SupplementalApis"</string>
+ <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"Lejon një aplikacion të ketë qasje në SupplementalApis."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Trokit dy herë për të kontrolluar zmadhimin"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Nuk mundi të shtonte miniaplikacion."</string>
<string name="ime_action_go" msgid="5536744546326495436">"Shko"</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index e117bdaaefa9..c66bb8c2bd11 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -1207,6 +1207,10 @@
<string name="deleteText" msgid="4200807474529938112">"Избриши"</string>
<string name="inputMethod" msgid="1784759500516314751">"Метод уноса"</string>
<string name="editTextMenuTitle" msgid="857666911134482176">"Радње у вези са текстом"</string>
+ <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+ <skip />
+ <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="9024241779284783414">"Меморијски простор је на измаку"</string>
<string name="low_internal_storage_view_text" msgid="8172166728369697835">"Неке системске функције можда не функционишу"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Нема довољно меморијског простора за систем. Уверите се да имате 250 MB слободног простора и поново покрените."</string>
@@ -1511,6 +1515,8 @@
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Дозвољава апликацији да тражи дозволу за игнорисање оптимизација батерије за ту апликацију."</string>
<string name="permlab_queryAllPackages" msgid="2928450604653281650">"слање упита за све пакете"</string>
<string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Дозвољава апликацији да види све инсталиране пакете."</string>
+ <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"приступ ставци SupplementalApis"</string>
+ <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"Дозвољава апликацији да приступа ставци SupplementalApis."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Додирните двапут за контролу зумирања"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Није могуће додати виџет."</string>
<string name="ime_action_go" msgid="5536744546326495436">"Иди"</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 90de24523802..bb40f8777641 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -1187,6 +1187,10 @@
<string name="deleteText" msgid="4200807474529938112">"Ta bort"</string>
<string name="inputMethod" msgid="1784759500516314751">"Indatametod"</string>
<string name="editTextMenuTitle" msgid="857666911134482176">"Textåtgärder"</string>
+ <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+ <skip />
+ <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="9024241779284783414">"Lagringsutrymmet börjar ta slut"</string>
<string name="low_internal_storage_view_text" msgid="8172166728369697835">"Det kan hända att vissa systemfunktioner inte fungerar"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Det finns inte tillräckligt med utrymme för systemet. Kontrollera att du har ett lagringsutrymme på minst 250 MB och starta om."</string>
@@ -1491,6 +1495,8 @@
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Appen får be om tillstånd att ignorera batterioptimering."</string>
<string name="permlab_queryAllPackages" msgid="2928450604653281650">"fråga alla paket"</string>
<string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Tillåter att en app ser alla installerade paket."</string>
+ <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"åtkomst till SupplementalApis"</string>
+ <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"Tillåter att en app får åtkomst till SupplementalApis."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Peka två gånger för zoomkontroll"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Det gick inte att lägga till widgeten."</string>
<string name="ime_action_go" msgid="5536744546326495436">"Kör"</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index ec241023ee35..58e73caab1ac 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -1187,6 +1187,10 @@
<string name="deleteText" msgid="4200807474529938112">"Futa"</string>
<string name="inputMethod" msgid="1784759500516314751">"Mbinu ya uingizaji"</string>
<string name="editTextMenuTitle" msgid="857666911134482176">"Vitendo vya maandishi"</string>
+ <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+ <skip />
+ <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="9024241779284783414">"Nafasi ya kuhifadhi inakaribia kujaa"</string>
<string name="low_internal_storage_view_text" msgid="8172166728369697835">"Baadhi ya vipengee vya mfumo huenda visifanye kazi"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Hifadhi haitoshi kwa ajili ya mfumo. Hakikisha una MB 250 za nafasi ya hifadhi isiyotumika na uanzishe upya."</string>
@@ -1491,6 +1495,8 @@
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Huruhusu programu kuomba ruhusa ya kupuuza uimarishaji wa betri katika programu yako."</string>
<string name="permlab_queryAllPackages" msgid="2928450604653281650">"kutuma hoja kwa vifurushi vyote"</string>
<string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Huruhusu programu kuona vifurushi vyote vilivyosakinishwa."</string>
+ <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"kufikia SupplementalApis"</string>
+ <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"Huruhusu programu kufikia SupplementalApis."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Gusa mara mbili kwa udhibiti wa kuza"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Haikuweza kuongeza wijeti."</string>
<string name="ime_action_go" msgid="5536744546326495436">"Nenda"</string>
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index e7ea59d36387..f4f37a6c03b4 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -1187,6 +1187,10 @@
<string name="deleteText" msgid="4200807474529938112">"நீக்கு"</string>
<string name="inputMethod" msgid="1784759500516314751">"உள்ளீட்டு முறை"</string>
<string name="editTextMenuTitle" msgid="857666911134482176">"உரை நடவடிக்கைகள்"</string>
+ <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+ <skip />
+ <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="9024241779284783414">"சேமிப்பிடம் குறைகிறது"</string>
<string name="low_internal_storage_view_text" msgid="8172166728369697835">"சில அமைப்பு செயல்பாடுகள் வேலை செய்யாமல் போகலாம்"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"முறைமையில் போதுமான சேமிப்பகம் இல்லை. 250மெ.பை. அளவு காலி இடவசதி இருப்பதை உறுதிசெய்து மீண்டும் தொடங்கவும்."</string>
@@ -1491,6 +1495,8 @@
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"பயன்பாட்டிற்கான பேட்டரி மேம்படுத்தல்களைப் புறக்கணிப்பதற்கான அனுமதியைக் கோர, ஆப்ஸை அனுமதிக்கும்."</string>
<string name="permlab_queryAllPackages" msgid="2928450604653281650">"அனைத்துப் பேக்கேஜ்களையும் பார்க்க அனுமதித்தல்"</string>
<string name="permdesc_queryAllPackages" msgid="5339069855520996010">"நிறுவப்பட்டுள்ள அனைத்துப் பேக்கேஜ்களையும் பார்ப்பதற்கு ஆப்ஸை அனுமதிக்கும்."</string>
+ <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"SupplementalApiகளை அணுகுதல்"</string>
+ <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"SupplementalApiகளை அணுக ஆப்ஸை அனுமதிக்கும்."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"அளவை மாற்றுவதற்கான கட்டுப்பாட்டிற்கு, இருமுறை தட்டவும்"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"விட்ஜெட்டைச் சேர்க்க முடியவில்லை."</string>
<string name="ime_action_go" msgid="5536744546326495436">"செல்"</string>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index 176d845f21dd..46fe3759f61d 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -174,7 +174,7 @@
<string name="httpErrorTooManyRequests" msgid="2149677715552037198">"చాలా ఎక్కువ రిక్వెస్ట్‌లు ప్రాసెస్ చేయబడుతున్నాయి. తర్వాత మళ్లీ ప్రయత్నించండి."</string>
<string name="notification_title" msgid="5783748077084481121">"<xliff:g id="ACCOUNT">%1$s</xliff:g>కు సైన్‌ఇన్ ఎర్రర్"</string>
<string name="contentServiceSync" msgid="2341041749565687871">"సింక్‌"</string>
- <string name="contentServiceSyncNotificationTitle" msgid="5766411446676388623">"సమకాలీకరించడం సాధ్యపడదు"</string>
+ <string name="contentServiceSyncNotificationTitle" msgid="5766411446676388623">"సింక్ చేయడం సాధ్యపడదు"</string>
<string name="contentServiceTooManyDeletesNotificationDesc" msgid="4562226280528716090">"చాలా ఎక్కువ <xliff:g id="CONTENT_TYPE">%s</xliff:g> తొలగించడానికి ప్రయత్నించారు."</string>
<string name="low_memory" product="tablet" msgid="5557552311566179924">"టాబ్లెట్ నిల్వ నిండింది. స్థలాన్ని ఖాళీ చేయడానికి కొన్ని ఫైళ్లను తొలగించండి."</string>
<string name="low_memory" product="watch" msgid="3479447988234030194">"వాచ్ నిల్వ నిండింది. స్థలాన్ని ఖాళీ చేయడానికి కొన్ని ఫైళ్లను తొలగించండి."</string>
@@ -224,7 +224,7 @@
<string name="silent_mode_vibrate" msgid="8821830448369552678">"రింగర్ వైబ్రేట్‌లో ఉంది"</string>
<string name="silent_mode_ring" msgid="6039011004781526678">"రింగర్ ఆన్‌లో ఉంది"</string>
<string name="reboot_to_update_title" msgid="2125818841916373708">"Android సిస్టమ్ అప్‌డేట్"</string>
- <string name="reboot_to_update_prepare" msgid="6978842143587422365">"నవీకరించడానికి సిద్ధం చేస్తోంది…"</string>
+ <string name="reboot_to_update_prepare" msgid="6978842143587422365">"అప్‌డేట్ చేయడానికి సిద్ధం చేస్తోంది…"</string>
<string name="reboot_to_update_package" msgid="4644104795527534811">"అప్‌డేట్ ప్యాకేజీని ప్రాసెస్ చేస్తోంది…"</string>
<string name="reboot_to_update_reboot" msgid="4474726009984452312">"పునఃప్రారంభించబడుతోంది…"</string>
<string name="reboot_to_reset_title" msgid="2226229680017882787">"ఫ్యాక్టరీ డేటా రీసెట్ చేయండి"</string>
@@ -369,7 +369,7 @@
<string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"సెల్ ప్రసార మెసేజ్‌లను చదవడం"</string>
<string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"మీ పరికరం స్వీకరించిన సెల్ ప్రసార మెసేజ్‌లను చదవడానికి యాప్‌ను అనుమతిస్తుంది. ఎమర్జెన్సీ పరిస్థితుల గురించి మిమ్మల్ని హెచ్చరించడానికి కొన్ని లొకేషన్లలో సెల్ ప్రసార అలర్ట్‌లు డెలివరీ చేయబడతాయి. ఎమర్జెన్సీ సెల్ ప్రసార అలర్ట్‌ను స్వీకరించినప్పుడు హానికరమైన యాప్‌లు మీ పరికరం పనితీరుకు లేదా నిర్వహణకు ఆటంకం కలిగించే అవకాశం ఉంది."</string>
<string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"చందా చేయబడిన ఫీడ్‌లను చదవడం"</string>
- <string name="permdesc_subscribedFeedsRead" msgid="6911349196661811865">"ప్రస్తుతం సమకాలీకరించిన ఫీడ్‌ల గురించి వివరాలను పొందడానికి యాప్‌ను అనుమతిస్తుంది."</string>
+ <string name="permdesc_subscribedFeedsRead" msgid="6911349196661811865">"ప్రస్తుతం సింక్ చేసిన ఫీడ్‌ల గురించి వివరాలను పొందడానికి యాప్‌ను అనుమతిస్తుంది."</string>
<string name="permlab_sendSms" msgid="7757368721742014252">"SMS మెసేజ్‌లను పంపడం, వీక్షించడం"</string>
<string name="permdesc_sendSms" msgid="6757089798435130769">"SMS మెసేజ్‌లు పంపడానికి యాప్‌ను అనుమతిస్తుంది. దీని వలన ఊహించని ఛార్జీలు విధించబడవచ్చు. హానికరమైన యాప్‌లు మీ నిర్ధారణ లేకుండానే మెసేజ్‌లను పంపడం ద్వారా మీకు డబ్బు ఖర్చయ్యేలా చేయవచ్చు."</string>
<string name="permlab_readSms" msgid="5164176626258800297">"మీ టెక్స్ట్ మెసేజ్‌లు (SMS లేదా MMS) చదవడం"</string>
@@ -403,7 +403,7 @@
<string name="permlab_getPackageSize" msgid="375391550792886641">"యాప్ నిల్వ స్థలాన్ని అంచనా వేయడం"</string>
<string name="permdesc_getPackageSize" msgid="742743530909966782">"యాప్‌ కోడ్, డేటా మరియు కాష్ పరిమాణాలను తిరిగి పొందడానికి దాన్ని అనుమతిస్తుంది"</string>
<string name="permlab_writeSettings" msgid="8057285063719277394">"సిస్టమ్ సెట్టింగ్‌లను మార్చడం"</string>
- <string name="permdesc_writeSettings" msgid="8293047411196067188">"సిస్టమ్ యొక్క సెట్టింగ్‌ల డేటాను సవరించడానికి యాప్‌ను అనుమతిస్తుంది. హానికరమైన యాప్‌లు మీ సిస్టమ్ యొక్క కాన్ఫిగరేషన్‌ను నాశనం చేయవచ్చు."</string>
+ <string name="permdesc_writeSettings" msgid="8293047411196067188">"సిస్టమ్ యొక్క సెట్టింగ్‌ల డేటాను ఎడిట్ చేయడానికి యాప్‌ను అనుమతిస్తుంది. హానికరమైన యాప్‌లు మీ సిస్టమ్ యొక్క కాన్ఫిగరేషన్‌ను నాశనం చేయవచ్చు."</string>
<string name="permlab_receiveBootCompleted" msgid="6643339400247325379">"ప్రారంభంలో అమలు చేయడం"</string>
<string name="permdesc_receiveBootCompleted" product="tablet" msgid="5565659082718177484">"సిస్టమ్ బూటింగ్‌ను పూర్తి చేసిన వెంటనే దానికదే ప్రారంభించబడటానికి యాప్‌ను అనుమతిస్తుంది. ఇది టాబ్లెట్‌ను ప్రారంభించడానికి ఎక్కువ సమయం పట్టేలా చేయవచ్చు మరియు ఎల్లప్పుడూ అమలు చేయడం ద్వారా మొత్తం టాబ్లెట్‌ను నెమ్మదిగా పని చేయడానికి యాప్‌ను అనుమతించేలా చేయవచ్చు."</string>
<string name="permdesc_receiveBootCompleted" product="tv" msgid="4900842256047614307">"సిస్టమ్ బూటింగ్‌ను పూర్తి చేసిన వెంటనే యాప్ దానికదే ప్రారంభం కావడానికి అనుమతిస్తుంది. ఇది మీ Android TV పరికరం ప్రారంభం కావడానికి ఎక్కువ సమయం పట్టేలా చేయవచ్చు మరియు ఎల్లప్పుడూ అమలు కావడం ద్వారా మొత్తం పరికరం పనితీరును నెమ్మది చేయడానికి యాప్‌ను అనుమతించవచ్చు."</string>
@@ -417,15 +417,15 @@
<string name="permdesc_readContacts" product="tv" msgid="8400138591135554789">"మీ Android TV పరికరంలో నిల్వ చేసిన కాంటాక్ట్‌లకు సంబంధించిన డేటాను చదవడానికి యాప్‌ను అనుమతిస్తుంది. కాంటాక్ట్‌లను సృష్టించిన మీ Android TV పరికరంలోని ఖాతాలకు కూడా యాప్‌లకు యాక్సెస్ ఉంటుంది. ఇందులో మీరు ఇన్‌స్టాల్ చేసిన యాప్‌ల ద్వారా సృష్టించబడిన ఖాతాలు ఉండవచ్చు. ఈ అనుమతి, మీ కాంటాక్ట్ డేటాను సేవ్ చేయడానికి యాప్‌లను అనుమతిస్తుంది, హానికరమైన యాప్‌లు మీకు తెలియకుండానే కాంటాక్ట్ డేటాను షేర్ చేయవచ్చు."</string>
<string name="permdesc_readContacts" product="default" msgid="4911989776203207644">"ఫోన్‌లో నిల్వ చేసిన మీ కాంటాక్ట్‌లకు సంబంధించిన డేటాను చదవడానికి యాప్‌ను అనుమతిస్తుంది. కాంటాక్ట్‌లను సృష్టించిన మీ ఫోన్‌లోని ఖాతాలను కూడా యాప్‌లు యాక్సెస్ చేయగలవు. ఇందులో మీరు ఇన్‌స్టాల్ చేసిన యాప్‌ల ద్వారా సృష్టించబడిన ఖాతాలు ఉండవచ్చు. ఈ అనుమతి, మీ కాంటాక్ట్ డేటాను సేవ్ చేయడానికి యాప్‌లను అనుమతిస్తుంది, హానికరమైన యాప్‌లు మీకు తెలియకుండానే కాంటాక్ట్ డేటాను షేర్ చేయవచ్చు."</string>
<string name="permlab_writeContacts" msgid="8919430536404830430">"మీ కాంటాక్ట్‌లను సవరించడం"</string>
- <string name="permdesc_writeContacts" product="tablet" msgid="6422419281427826181">"మీ టాబ్లెట్‌లో నిల్వ చేసి ఉన్న కాంటాక్ట్‌లకు సంబంధించిన డేటాను సవరించడానికి యాప్‌ను అనుమతిస్తుంది. ఈ అనుమతి, కాంటాక్ట్ డేటాను తొలగించడానికి యాప్‌లను అనుమతిస్తుంది."</string>
- <string name="permdesc_writeContacts" product="tv" msgid="6488872735379978935">"మీ Android TV పరికరంలో నిల్వ చేసి ఉన్న కాంటాక్ట్‌లకు సంబంధించిన డేటాను సవరించడానికి యాప్‌ను అనుమతిస్తుంది. ఈ అనుమతి, కాంటాక్ట్ డేటాను తొలగించడానికి యాప్‌లను అనుమతిస్తుంది."</string>
- <string name="permdesc_writeContacts" product="default" msgid="8304795696237065281">"మీ ఫోన్‌లో నిల్వ చేసి ఉన్న కాంటాక్ట్‌లకు సంబంధించిన డేటాను సవరించడానికి యాప్‌ను అనుమతిస్తుంది. ఈ అనుమతి, కాంటాక్ట్ డేటాను తొలగించడానికి యాప్‌లను అనుమతిస్తుంది."</string>
+ <string name="permdesc_writeContacts" product="tablet" msgid="6422419281427826181">"మీ టాబ్లెట్‌లో నిల్వ చేసి ఉన్న కాంటాక్ట్‌లకు సంబంధించిన డేటాను ఎడిట్ చేయడానికి యాప్‌ను అనుమతిస్తుంది. ఈ అనుమతి, కాంటాక్ట్ డేటాను తొలగించడానికి యాప్‌లను అనుమతిస్తుంది."</string>
+ <string name="permdesc_writeContacts" product="tv" msgid="6488872735379978935">"మీ Android TV పరికరంలో నిల్వ చేసి ఉన్న కాంటాక్ట్‌లకు సంబంధించిన డేటాను ఎడిట్ చేయడానికి యాప్‌ను అనుమతిస్తుంది. ఈ అనుమతి, కాంటాక్ట్ డేటాను తొలగించడానికి యాప్‌లను అనుమతిస్తుంది."</string>
+ <string name="permdesc_writeContacts" product="default" msgid="8304795696237065281">"మీ ఫోన్‌లో నిల్వ చేసి ఉన్న కాంటాక్ట్‌లకు సంబంధించిన డేటాను ఎడిట్ చేయడానికి యాప్‌ను అనుమతిస్తుంది. ఈ అనుమతి, కాంటాక్ట్ డేటాను తొలగించడానికి యాప్‌లను అనుమతిస్తుంది."</string>
<string name="permlab_readCallLog" msgid="1739990210293505948">"కాల్ లాగ్‌ను చదవడం"</string>
<string name="permdesc_readCallLog" msgid="8964770895425873433">"ఈ యాప్‌ మీ కాల్ చరిత్రను చదవగలదు."</string>
<string name="permlab_writeCallLog" msgid="670292975137658895">"కాల్ లాగ్‌ను రాయడం"</string>
- <string name="permdesc_writeCallLog" product="tablet" msgid="2657525794731690397">"ఇన్‌కమింగ్ మరియు అవుట్‌గోయింగ్ కాల్స్‌ల గురించిన డేటాతో సహా మీ టాబ్లెట్ యొక్క కాల్ లాగ్‌ను సవరించడానికి యాప్‌ను అనుమతిస్తుంది. హానికరమైన యాప్‌లు మీ కాల్ లాగ్‌ను ఎరేజ్ చేయడానికి లేదా సవరించడానికి దీన్ని ఉపయోగించవచ్చు."</string>
- <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"ఇన్‌కమింగ్ మరియు అవుట్‌గోయింగ్ కాల్స్‌కు సంబంధించిన డేటాతో సహా మీ Android TV పరికరం కాల్ లాగ్‌ను సవరించడానికి యాప్‌ని అనుమతిస్తుంది. హానికరమైన యాప్‌లు మీ కాల్ లాగ్‌ను తీసివేయడానికి లేదా సవరించడానికి దీన్ని ఉపయోగించవచ్చు."</string>
- <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"ఇన్‌కమింగ్ మరియు అవుట్‌గోయింగ్ కాల్స్‌ల గురించిన డేటాతో సహా మీ ఫోన్ యొక్క కాల్ లాగ్‌ను సవరించడానికి యాప్‌ను అనుమతిస్తుంది. హానికరమైన యాప్‌లు మీ కాల్ లాగ్‌ను ఎరేజ్ చేయడానికి లేదా సవరించడానికి దీన్ని ఉపయోగించవచ్చు."</string>
+ <string name="permdesc_writeCallLog" product="tablet" msgid="2657525794731690397">"ఇన్‌కమింగ్ మరియు అవుట్‌గోయింగ్ కాల్స్‌ల గురించిన డేటాతో సహా మీ టాబ్లెట్ యొక్క కాల్ లాగ్‌ను ఎడిట్ చేయడానికి యాప్‌ను అనుమతిస్తుంది. హానికరమైన యాప్‌లు మీ కాల్ లాగ్‌ను ఎరేజ్ చేయడానికి లేదా ఎడిట్ చేయడానికి దీన్ని ఉపయోగించవచ్చు."</string>
+ <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"ఇన్‌కమింగ్ మరియు అవుట్‌గోయింగ్ కాల్స్‌కు సంబంధించిన డేటాతో సహా మీ Android TV పరికరం కాల్ లాగ్‌ను ఎడిట్ చేయడానికి యాప్‌ని అనుమతిస్తుంది. హానికరమైన యాప్‌లు మీ కాల్ లాగ్‌ను తీసివేయడానికి లేదా ఎడిట్ చేయడానికి దీన్ని ఉపయోగించవచ్చు."</string>
+ <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"ఇన్‌కమింగ్ మరియు అవుట్‌గోయింగ్ కాల్స్‌ల గురించిన డేటాతో సహా మీ ఫోన్ యొక్క కాల్ లాగ్‌ను ఎడిట్ చేయడానికి యాప్‌ను అనుమతిస్తుంది. హానికరమైన యాప్‌లు మీ కాల్ లాగ్‌ను ఎరేజ్ చేయడానికి లేదా ఎడిట్ చేయడానికి దీన్ని ఉపయోగించవచ్చు."</string>
<string name="permlab_bodySensors" msgid="3411035315357380862">"శరీర సెన్సార్‌లను (గుండె స్పందన రేటు మానిటర్‌ల వంటివి) యాక్సెస్ చేయండి"</string>
<string name="permdesc_bodySensors" product="default" msgid="3208940894182188063">"గుండె స్పందన రేటు, ఉష్ణోగ్రత, రక్తంలో ఆక్సిజన్ శాతం మొదలైన శరీర సెన్సార్‌ల నుండి డేటాను యాక్సెస్ చేయండి."</string>
<string name="permlab_bodySensors_background" msgid="4352831883331744370">"బ్యాక్‌గ్రౌండ్‌లో శరీర సెన్సార్‌లను (గుండె రేటు మానిటర్స్) యాక్సెస్ చేయండి"</string>
@@ -447,7 +447,7 @@
<string name="permlab_accessBackgroundLocation" msgid="1721164702777366138">"బ్యాక్‌గ్రౌండ్‌లో లొకేషన్‌ను యాక్సెస్ చేయి"</string>
<string name="permdesc_accessBackgroundLocation" msgid="8264885066095638105">"యాప్ ఉపయోగంలో లేనప్పటికీ కూడా, ఈ యాప్, లొకేషన్‌ను ఎప్పుడైనా యాక్సెస్ చేయగలదు."</string>
<string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"మీ ఆడియో సెట్టింగ్‌లను మార్చడం"</string>
- <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"వాల్యూమ్ మరియు అవుట్‌పుట్ కోసం ఉపయోగించాల్సిన స్పీకర్ వంటి సార్వజనీన ఆడియో సెట్టింగ్‌లను సవరించడానికి యాప్‌ను అనుమతిస్తుంది."</string>
+ <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"వాల్యూమ్ మరియు అవుట్‌పుట్ కోసం ఉపయోగించాల్సిన స్పీకర్ వంటి సార్వజనీన ఆడియో సెట్టింగ్‌లను ఎడిట్ చేయడానికి యాప్‌ను అనుమతిస్తుంది."</string>
<string name="permlab_recordAudio" msgid="1208457423054219147">"ఆడియోను రికార్డ్ చేయడం"</string>
<string name="permdesc_recordAudio" msgid="5857246765327514062">"యాప్ ఉపయోగంలో ఉన్నపుడు మైక్రోఫోన్‌ను ఉపయోగించి ఈ యాప్, ఆడియోను రికార్డ్ చేయగలదు."</string>
<string name="permlab_recordBackgroundAudio" msgid="5891032812308878254">"బ్యాక్‌గ్రౌండ్‌లో ఆడియోను రికార్డ్ చేయగలదు"</string>
@@ -566,11 +566,11 @@
<string name="permlab_useFingerprint" msgid="1001421069766751922">"వేలిముద్ర హార్డ్‌వేర్‌ని ఉపయోగించడానికి అనుమతి"</string>
<string name="permdesc_useFingerprint" msgid="412463055059323742">"ప్రామాణీకరణ కోసం వేలిముద్ర హార్డ్‌వేర్‌ను ఉపయోగించడానికి యాప్‌ను అనుమతిస్తుంది"</string>
<string name="permlab_audioWrite" msgid="8501705294265669405">"మీ సంగీత సేకరణను ఎడిట్ చేయండి"</string>
- <string name="permdesc_audioWrite" msgid="8057399517013412431">"మీ సంగీత సేకరణని సవరించడానికి యాప్‌ను అనుమతిస్తుంది."</string>
+ <string name="permdesc_audioWrite" msgid="8057399517013412431">"మీ సంగీత సేకరణని ఎడిట్ చేయడానికి యాప్‌ను అనుమతిస్తుంది."</string>
<string name="permlab_videoWrite" msgid="5940738769586451318">"మీ వీడియో సేకరణను ఎడిట్ చేయండి"</string>
- <string name="permdesc_videoWrite" msgid="6124731210613317051">"మీ వీడియో సేకరణను సవరించడానికి యాప్‌ని అనుమతిస్తుంది."</string>
+ <string name="permdesc_videoWrite" msgid="6124731210613317051">"మీ వీడియో సేకరణను ఎడిట్ చేయడానికి యాప్‌ని అనుమతిస్తుంది."</string>
<string name="permlab_imagesWrite" msgid="1774555086984985578">"మీ ఫోటో సేకరణను ఎడిట్ చేయండి"</string>
- <string name="permdesc_imagesWrite" msgid="5195054463269193317">"మీ ఫోటో సేకరణను సవరించడానికి యాప్‌ను అనుమతిస్తుంది."</string>
+ <string name="permdesc_imagesWrite" msgid="5195054463269193317">"మీ ఫోటో సేకరణను ఎడిట్ చేయడానికి యాప్‌ను అనుమతిస్తుంది."</string>
<string name="permlab_mediaLocation" msgid="7368098373378598066">"మీ మీడియా సేకరణ నుండి లొకేషన్లను చదవండి"</string>
<string name="permdesc_mediaLocation" msgid="597912899423578138">"మీ మీడియా సేకరణ నుండి లొకేషన్లను చదవడానికి యాప్‌ను అనుమతిస్తుంది."</string>
<string name="biometric_app_setting_name" msgid="3339209978734534457">"బయోమెట్రిక్స్‌ను ఉపయోగించండి"</string>
@@ -678,7 +678,7 @@
<string name="permlab_readSyncSettings" msgid="6250532864893156277">"సింక్ సెట్టింగ్‌లను చదవగలగడం"</string>
<string name="permdesc_readSyncSettings" msgid="1325658466358779298">"ఖాతా యొక్క సింక్‌ సెట్టింగ్‌లను చదవడానికి యాప్‌ను అనుమతిస్తుంది. ఉదాహరణకు, వ్యక్తుల యాప్‌ ఖాతాతో సమకాలీకరించబడాలా లేదా అనే విషయాన్ని ఇది నిశ్చయించవచ్చు."</string>
<string name="permlab_writeSyncSettings" msgid="6583154300780427399">"\'సింక్\'ను ఆన్, ఆఫ్‌ల మధ్య టోగుల్ చేయడం"</string>
- <string name="permdesc_writeSyncSettings" msgid="6029151549667182687">"ఖాతా యొక్క సింక్‌ సెట్టింగ్‌లను సవరించడానికి యాప్‌ను అనుమతిస్తుంది. ఉదాహరణకు, ఇది ఒక ఖాతాతో వ్యక్తుల యాప్ యొక్క సింక్‌ను ప్రారంభించడానికి ఉపయోగించబడవచ్చు."</string>
+ <string name="permdesc_writeSyncSettings" msgid="6029151549667182687">"ఖాతా యొక్క సింక్‌ సెట్టింగ్‌లను ఎడిట్ చేయడానికి యాప్‌ను అనుమతిస్తుంది. ఉదాహరణకు, ఇది ఒక ఖాతాతో వ్యక్తుల యాప్ యొక్క సింక్‌ను ప్రారంభించడానికి ఉపయోగించబడవచ్చు."</string>
<string name="permlab_readSyncStats" msgid="3747407238320105332">"సింక్ గణాంకాలను చదవగలగడం"</string>
<string name="permdesc_readSyncStats" msgid="3867809926567379434">"ఖాతా యొక్క సింక్‌ గణాంకాలను అలాగే సింక్‌ ఈవెంట్‌ల చరిత్రను మరియు ఎంత డేటా సమకాలీకరించబడింది అనేవాటిని చదవడానికి యాప్‌ను అనుమతిస్తుంది."</string>
<string name="permlab_sdcardRead" msgid="5791467020950064920">"మీ షేర్ చేసిన నిల్వ యొక్క కంటెంట్‌లను చదువుతుంది"</string>
@@ -704,7 +704,7 @@
<string name="permlab_manageNetworkPolicy" msgid="6872549423152175378">"నెట్‌వర్క్ విధానాన్ని నిర్వహించడం"</string>
<string name="permdesc_manageNetworkPolicy" msgid="1865663268764673296">"నెట్‌వర్క్ విధానాలను నిర్వహించడానికి మరియు యాప్-నిర్దిష్ట నిబంధనలను నిర్వచించడానికి యాప్‌ను అనుమతిస్తుంది."</string>
<string name="permlab_modifyNetworkAccounting" msgid="7448790834938749041">"నెట్‌వర్క్ వినియోగ అకౌంటింగ్‌ను సవరించడం"</string>
- <string name="permdesc_modifyNetworkAccounting" msgid="5076042642247205390">"యాప్‌లలో నెట్‌వర్క్ వినియోగం ఎలా గణించాలనే దాన్ని సవరించడానికి యాప్‌ను అనుమతిస్తుంది. సాధారణ యాప్‌ల ద్వారా ఉపయోగించడానికి ఉద్దేశించినది కాదు."</string>
+ <string name="permdesc_modifyNetworkAccounting" msgid="5076042642247205390">"యాప్‌లలో నెట్‌వర్క్ వినియోగం ఎలా గణించాలనే దాన్ని ఎడిట్ చేయడానికి యాప్‌ను అనుమతిస్తుంది. సాధారణ యాప్‌ల ద్వారా ఉపయోగించడానికి ఉద్దేశించినది కాదు."</string>
<string name="permlab_accessNotifications" msgid="7130360248191984741">"నోటిఫికేషన్‌లను యాక్సెస్ చేయడం"</string>
<string name="permdesc_accessNotifications" msgid="761730149268789668">"నోటిఫికేషన్‌లను, ఇతర యాప్‌ల ద్వారా పోస్ట్ చేయబడిన వాటిని తిరిగి పొందడానికి, పరిశీలించడానికి మరియు క్లియర్ చేయడానికి యాప్‌ను అనుమతిస్తుంది."</string>
<string name="permlab_bindNotificationListenerService" msgid="5848096702733262458">"నోటిఫికేషన్ పరిశీలన సేవకు అనుబంధించడం"</string>
@@ -718,7 +718,7 @@
<string name="permlab_accessNetworkConditions" msgid="1270732533356286514">"నెట్‌వర్క్ పరిస్థితులపై పరిశీలనల గురించి తెలుసుకోవడం"</string>
<string name="permdesc_accessNetworkConditions" msgid="2959269186741956109">"నెట్‌వర్క్ పరిస్థితులపై పరిశీలనల గురించి తెలుసుకోవడానికి యాప్‌ను అనుమతిస్తుంది. సాధారణ యాప్‌లకు ఎప్పటికీ అవసరం ఉండకూడదు."</string>
<string name="permlab_setInputCalibration" msgid="932069700285223434">"ఇన్‌పుట్ పరికరం క్రమాంకనాన్ని మార్చండి"</string>
- <string name="permdesc_setInputCalibration" msgid="2937872391426631726">"టచ్ స్క్రీన్ యొక్క క్రమాంకన పరామితులను సవరించడానికి యాప్‌ను అనుమతిస్తుంది. సాధారణ యాప్‌లకు ఎప్పటికీ అవసరం ఉండదు."</string>
+ <string name="permdesc_setInputCalibration" msgid="2937872391426631726">"టచ్ స్క్రీన్ యొక్క క్రమాంకన పరామితులను ఎడిట్ చేయడానికి యాప్‌ను అనుమతిస్తుంది. సాధారణ యాప్‌లకు ఎప్పటికీ అవసరం ఉండదు."</string>
<string name="permlab_accessDrmCertificates" msgid="6473765454472436597">"DRM ప్రమాణపత్రాలను యాక్సెస్ చేయడం"</string>
<string name="permdesc_accessDrmCertificates" msgid="6983139753493781941">"DRM ప్రమాణపత్రాలను కేటాయించడానికి మరియు ఉపయోగించడానికి యాప్‌ను అనుమతిస్తుంది. సాధారణ యాప్‌లకు ఎప్పటికీ అవసరం ఉండదు."</string>
<string name="permlab_handoverStatus" msgid="7620438488137057281">"Android Beam బదిలీ స్టేటస్‌ని స్వీకరించడం"</string>
@@ -1029,14 +1029,14 @@
<string name="permdesc_readHistoryBookmarks" msgid="2323799501008967852">"బ్రౌజర్ సందర్శించిన అన్ని URLల చరిత్ర గురించి మరియు అన్ని బ్రౌజర్ బుక్‌మార్క్‌ల గురించి చదవడానికి యాప్‌ను అనుమతిస్తుంది. గమనిక: ఈ అనుమతి మూడవ పక్షం బ్రౌజర్‌లు లేదా వెబ్ బ్రౌజింగ్ సామర్థ్యాలు గల ఇతర యాప్‌ల ద్వారా అమలు చేయబడకపోవచ్చు."</string>
<string name="permlab_writeHistoryBookmarks" msgid="6090259925187986937">"వెబ్ బుక్‌మార్క్‌లు మరియు చరిత్రను రాయడం"</string>
<string name="permdesc_writeHistoryBookmarks" product="tablet" msgid="573341025292489065">"మీ టాబ్లెట్‌లో నిల్వ చేయబడిన బ్రౌజర్ హిస్టరీని, బుక్‌మార్క్‌లను ఎడిట్ చేయడానికి యాప్‌ను అనుమతిస్తుంది. ఇది బ్రౌజర్ డేటాను ఎరేజ్ చేయడానికి లేదా ఎడిట్ చేయడానికి యాప్‌ను అనుమతించవచ్చు. గమనిక: ఈ అనుమతిని థర్డ్ పార్టీ బ్రౌజర్‌లు లేదా వెబ్ బ్రౌజింగ్ సామర్థ్యాలు గల ఇతర యాప్‌లు అమలు చేయకపోవచ్చు."</string>
- <string name="permdesc_writeHistoryBookmarks" product="tv" msgid="88642768580408561">"మీ Android TV పరికరంలో నిల్వ చేసిన బ్రౌజర్ చరిత్ర లేదా బుక్‌మార్క్‌లను సవరించడానికి యాప్‌ని అనుమతిస్తుంది. ఇది బ్రౌజర్ డేటాను తీసివేయడానికి లేదా సవరించడానికి యాప్‌ని అనుమతించవచ్చు. గమనిక: ఈ అనుమతి మూడవ-పక్ష బ్రౌజర్‌లు లేదా వెబ్ బ్రౌజింగ్ సామర్థ్యాలు గల ఇతర యాప్‌ల ద్వారా అమలు కాకపోవచ్చు."</string>
- <string name="permdesc_writeHistoryBookmarks" product="default" msgid="2245203087160913652">"మీ ఫోన్‌లో నిల్వ చేయబడిన బ్రౌజర్ చరిత్రను లేదా బుక్‌మార్క్‌లను సవరించడానికి యాప్‌ను అనుమతిస్తుంది. ఇది బ్రౌజర్ డేటాను ఎరేజ్ చేయడానికి లేదా సవరించడానికి యాప్‌ను అనుమతించవచ్చు. గమనిక: ఈ అనుమతి మూడవ పక్షం బ్రౌజర్‌లు లేదా వెబ్ బ్రౌజింగ్ సామర్థ్యాలు గల ఇతర యాప్‌ల ద్వారా అమలు చేయబడకపోవచ్చు."</string>
+ <string name="permdesc_writeHistoryBookmarks" product="tv" msgid="88642768580408561">"మీ Android TV పరికరంలో నిల్వ చేసిన బ్రౌజర్ చరిత్ర లేదా బుక్‌మార్క్‌లను ఎడిట్ చేయడానికి యాప్‌ని అనుమతిస్తుంది. ఇది బ్రౌజర్ డేటాను తీసివేయడానికి లేదా ఎడిట్ చేయడానికి యాప్‌ని అనుమతించవచ్చు. గమనిక: ఈ అనుమతి మూడవ-పక్ష బ్రౌజర్‌లు లేదా వెబ్ బ్రౌజింగ్ సామర్థ్యాలు గల ఇతర యాప్‌ల ద్వారా అమలు కాకపోవచ్చు."</string>
+ <string name="permdesc_writeHistoryBookmarks" product="default" msgid="2245203087160913652">"మీ ఫోన్‌లో నిల్వ చేయబడిన బ్రౌజర్ చరిత్రను లేదా బుక్‌మార్క్‌లను ఎడిట్ చేయడానికి యాప్‌ను అనుమతిస్తుంది. ఇది బ్రౌజర్ డేటాను ఎరేజ్ చేయడానికి లేదా ఎడిట్ చేయడానికి యాప్‌ను అనుమతించవచ్చు. గమనిక: ఈ అనుమతి మూడవ పక్షం బ్రౌజర్‌లు లేదా వెబ్ బ్రౌజింగ్ సామర్థ్యాలు గల ఇతర యాప్‌ల ద్వారా అమలు చేయబడకపోవచ్చు."</string>
<string name="permlab_setAlarm" msgid="1158001610254173567">"అలారం సెట్ చేయడం"</string>
<string name="permdesc_setAlarm" msgid="2185033720060109640">"ఇన్‌స్టాల్ చేయబడిన అలారం గడియారం యాప్‌లో అలారంను సెట్ చేయడానికి యాప్‌ను అనుమతిస్తుంది. కొన్ని అలారం గల గడియారం యాప్‌లు ఈ ఫీచర్‌ను అమలు చేయకపోవచ్చు."</string>
<string name="permlab_addVoicemail" msgid="4770245808840814471">"వాయిస్ మెయిల్‌ను జోడించడం"</string>
<string name="permdesc_addVoicemail" msgid="5470312139820074324">"మీ వాయిస్ మెయిల్ ఇన్‌బాక్స్‌కు మెసేజ్‌లను జోడించడానికి యాప్‌ను అనుమతిస్తుంది."</string>
<string name="permlab_writeGeolocationPermissions" msgid="8605631647492879449">"బ్రౌజర్ భౌగోళిక స్థానం అనుమతులను సవరించడం"</string>
- <string name="permdesc_writeGeolocationPermissions" msgid="5817346421222227772">"బ్రౌజర్ యొక్క భౌగోళిక లొకేషన్ అనుమతులను సవరించడానికి యాప్‌ను అనుమతిస్తుంది. హానికరమైన యాప్‌లు ఏకపక్ష వెబ్ సైట్‌లకు లొకేషన్ సమాచారాన్ని అనుమతించడానికి దీన్ని ఉపయోగించవచ్చు."</string>
+ <string name="permdesc_writeGeolocationPermissions" msgid="5817346421222227772">"బ్రౌజర్ యొక్క భౌగోళిక లొకేషన్ అనుమతులను ఎడిట్ చేయడానికి యాప్‌ను అనుమతిస్తుంది. హానికరమైన యాప్‌లు ఏకపక్ష వెబ్ సైట్‌లకు లొకేషన్ సమాచారాన్ని అనుమతించడానికి దీన్ని ఉపయోగించవచ్చు."</string>
<string name="save_password_message" msgid="2146409467245462965">"మీరు బ్రౌజర్ ఈ పాస్‌వర్డ్‌ను గుర్తుపెట్టుకోవాలని కోరుకుంటున్నారా?"</string>
<string name="save_password_notnow" msgid="2878327088951240061">"ఇప్పుడు కాదు"</string>
<string name="save_password_remember" msgid="6490888932657708341">"గుర్తుంచుకో"</string>
@@ -1187,6 +1187,10 @@
<string name="deleteText" msgid="4200807474529938112">"తొలగించు"</string>
<string name="inputMethod" msgid="1784759500516314751">"ఇన్‌పుట్ పద్ధతి"</string>
<string name="editTextMenuTitle" msgid="857666911134482176">"వచనానికి సంబంధించిన చర్యలు"</string>
+ <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+ <skip />
+ <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="9024241779284783414">"నిల్వ ఖాళీ అయిపోతోంది"</string>
<string name="low_internal_storage_view_text" msgid="8172166728369697835">"కొన్ని సిస్టమ్ కార్యాచరణలు పని చేయకపోవచ్చు"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"సిస్టమ్ కోసం తగినంత నిల్వ లేదు. మీకు 250MB ఖాళీ స్థలం ఉందని నిర్ధారించుకుని, పునఃప్రారంభించండి."</string>
@@ -1491,6 +1495,8 @@
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"ఆ యాప్ కోసం బ్యాటరీ అనుకూలీకరణలు విస్మరించేలా అనుమతి కోరడానికి యాప్‌ను అనుమతిస్తుంది."</string>
<string name="permlab_queryAllPackages" msgid="2928450604653281650">"అన్ని ప్యాకేజీలను క్వెరీ చేయండి"</string>
<string name="permdesc_queryAllPackages" msgid="5339069855520996010">"ఇన్‌స్టాల్ చేసిన అన్ని ప్యాకేజీలను చూడటానికి యాప్‌ను అనుమతించండి."</string>
+ <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"SupplementalApisని యాక్సెస్ చేయండి"</string>
+ <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"SupplementalApisని యాక్సెస్ చేయడానికి యాప్‌ను అనుమతిస్తుంది."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"జూమ్ నియంత్రణ కోసం రెండుసార్లు నొక్కండి"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"విడ్జెట్‌ను జోడించడం సాధ్యపడలేదు."</string>
<string name="ime_action_go" msgid="5536744546326495436">"వెళ్లు"</string>
@@ -1871,7 +1877,7 @@
<string name="restr_pin_enter_old_pin" msgid="7537079094090650967">"ప్రస్తుత పిన్‌"</string>
<string name="restr_pin_enter_new_pin" msgid="3267614461844565431">"కొత్త పిన్‌"</string>
<string name="restr_pin_confirm_pin" msgid="7143161971614944989">"కొత్త పిన్‌ను నిర్ధారించండి"</string>
- <string name="restr_pin_create_pin" msgid="917067613896366033">"నియంత్రణలను సవరించడానికి పిన్‌ను రూపొందించండి"</string>
+ <string name="restr_pin_create_pin" msgid="917067613896366033">"నియంత్రణలను ఎడిట్ చేయడానికి పిన్‌ను రూపొందించండి"</string>
<string name="restr_pin_error_doesnt_match" msgid="7063392698489280556">"పిన్‌లు సరిపోలలేదు. మళ్లీ ప్రయత్నించండి."</string>
<string name="restr_pin_error_too_short" msgid="1547007808237941065">"పిన్‌ చాలా చిన్నదిగా ఉంది. తప్పనిసరిగా కనీసం 4 అంకెలు ఉండాలి."</string>
<plurals name="restr_pin_countdown" formatted="false" msgid="4427486903285216153">
@@ -1879,7 +1885,7 @@
<item quantity="one">1 సెకనులో మళ్లీ ప్రయత్నించండి</item>
</plurals>
<string name="restr_pin_try_later" msgid="5897719962541636727">"తర్వాత మళ్లీ ప్రయత్నించండి"</string>
- <string name="immersive_cling_title" msgid="2307034298721541791">"పూర్తి స్క్రీన్‌లో వీక్షిస్తున్నారు"</string>
+ <string name="immersive_cling_title" msgid="2307034298721541791">"ఫుల్-స్క్రీన్‌లో వీక్షిస్తున్నారు"</string>
<string name="immersive_cling_description" msgid="7092737175345204832">"నిష్క్రమించడానికి, పై నుండి క్రిందికి స్వైప్ చేయండి."</string>
<string name="immersive_cling_positive" msgid="7047498036346489883">"అర్థమైంది"</string>
<string name="done_label" msgid="7283767013231718521">"పూర్తయింది"</string>
@@ -2148,8 +2154,8 @@
<string name="mime_type_image_ext" msgid="5743552697560999471">"<xliff:g id="EXTENSION">%1$s</xliff:g> చిత్రం"</string>
<string name="mime_type_compressed" msgid="8737300936080662063">"ఆర్కైవ్"</string>
<string name="mime_type_compressed_ext" msgid="4775627287994475737">"<xliff:g id="EXTENSION">%1$s</xliff:g> ఆర్కైవ్"</string>
- <string name="mime_type_document" msgid="3737256839487088554">"పత్రం"</string>
- <string name="mime_type_document_ext" msgid="2398002765046677311">"<xliff:g id="EXTENSION">%1$s</xliff:g> పత్రం"</string>
+ <string name="mime_type_document" msgid="3737256839487088554">"డాక్యుమెంట్‌"</string>
+ <string name="mime_type_document_ext" msgid="2398002765046677311">"<xliff:g id="EXTENSION">%1$s</xliff:g> డాక్యుమెంట్‌"</string>
<string name="mime_type_spreadsheet" msgid="8188407519131275838">"స్ప్రెడ్‌షీట్"</string>
<string name="mime_type_spreadsheet_ext" msgid="8720173181137254414">"<xliff:g id="EXTENSION">%1$s</xliff:g> స్ప్రెడ్‌షీట్"</string>
<string name="mime_type_presentation" msgid="1145384236788242075">"ప్రదర్శన"</string>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 30b3d0b3c127..f485e8513f6d 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -1187,6 +1187,10 @@
<string name="deleteText" msgid="4200807474529938112">"ลบ"</string>
<string name="inputMethod" msgid="1784759500516314751">"วิธีป้อนข้อมูล"</string>
<string name="editTextMenuTitle" msgid="857666911134482176">"การทำงานของข้อความ"</string>
+ <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+ <skip />
+ <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="9024241779284783414">"พื้นที่จัดเก็บเหลือน้อย"</string>
<string name="low_internal_storage_view_text" msgid="8172166728369697835">"บางฟังก์ชันระบบอาจไม่ทำงาน"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"พื้นที่เก็บข้อมูลไม่เพียงพอสำหรับระบบ โปรดตรวจสอบว่าคุณมีพื้นที่ว่าง 250 MB แล้วรีสตาร์ท"</string>
@@ -1491,6 +1495,8 @@
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"อนุญาตให้แอปขอสิทธิ์เพิกเฉยต่อการเพิ่มประสิทธิภาพแบตเตอรี่สำหรับแอปนั้น"</string>
<string name="permlab_queryAllPackages" msgid="2928450604653281650">"ค้นหาแพ็กเกจทั้งหมด"</string>
<string name="permdesc_queryAllPackages" msgid="5339069855520996010">"อนุญาตให้แอปดูแพ็กเกจที่ติดตั้งไว้ทั้งหมด"</string>
+ <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"เข้าถึง SupplementalApis"</string>
+ <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"อนุญาตให้แอปพลิเคชันเข้าถึง SupplementalApis"</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"แตะสองครั้งเพื่อควบคุมการซูม"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"ไม่สามารถเพิ่มวิดเจ็ต"</string>
<string name="ime_action_go" msgid="5536744546326495436">"ไป"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index c0bbc6958482..64487558d2af 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -1187,6 +1187,10 @@
<string name="deleteText" msgid="4200807474529938112">"I-delete"</string>
<string name="inputMethod" msgid="1784759500516314751">"Pamamaraan ng pag-input"</string>
<string name="editTextMenuTitle" msgid="857666911134482176">"Pagkilos ng teksto"</string>
+ <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+ <skip />
+ <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="9024241779284783414">"Nauubusan na ang puwang ng storage"</string>
<string name="low_internal_storage_view_text" msgid="8172166728369697835">"Maaaring hindi gumana nang tama ang ilang paggana ng system"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Walang sapat na storage para sa system. Tiyaking mayroon kang 250MB na libreng espasyo at i-restart."</string>
@@ -1491,6 +1495,8 @@
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Pinapayagang humingi ng pahintulot ang isang app na balewalain ang mga pag-optimize ng baterya para sa app na iyon."</string>
<string name="permlab_queryAllPackages" msgid="2928450604653281650">"i-query ang lahat ng package"</string>
<string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Nagbibigay-daan sa isang app na makita ang lahat ng naka-install na package."</string>
+ <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"access sa SupplementalApis"</string>
+ <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"Nagbibigay-daan sa isang application na i-access ang SupplementalApis."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Tapikin ng dalawang beses para sa pagkontrol ng zoom"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Hindi maidagdag ang widget."</string>
<string name="ime_action_go" msgid="5536744546326495436">"Pumunta"</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index 8a8622715b2e..e3c20baf014d 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -1187,6 +1187,10 @@
<string name="deleteText" msgid="4200807474529938112">"Sil"</string>
<string name="inputMethod" msgid="1784759500516314751">"Giriş yöntemi"</string>
<string name="editTextMenuTitle" msgid="857666911134482176">"Metin eylemleri"</string>
+ <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+ <skip />
+ <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="9024241779284783414">"Depolama alanı bitiyor"</string>
<string name="low_internal_storage_view_text" msgid="8172166728369697835">"Bazı sistem işlevleri çalışmayabilir"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Sistem için yeterli depolama alanı yok. 250 MB boş alanınızın bulunduğundan emin olun ve yeniden başlatın."</string>
@@ -1491,6 +1495,8 @@
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Bir uygulamanın, kendisi için pil optimizasyonlarını göz ardı etme izni istemesine olanak sağlar."</string>
<string name="permlab_queryAllPackages" msgid="2928450604653281650">"tüm paketleri sorgulama"</string>
<string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Uygulamaya tüm yüklü paketleri görme izni verir."</string>
+ <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"SupplementalApis\'e erişim"</string>
+ <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"Bir uygulamanın SupplementalApis\'e erişimine izin verir."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Zum denetimi için iki kez dokun"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Widget eklenemedi."</string>
<string name="ime_action_go" msgid="5536744546326495436">"Git"</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index f4fb575f836e..c920e6471547 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -1227,6 +1227,10 @@
<string name="deleteText" msgid="4200807474529938112">"Видалити"</string>
<string name="inputMethod" msgid="1784759500516314751">"Метод введення"</string>
<string name="editTextMenuTitle" msgid="857666911134482176">"Дії з текстом"</string>
+ <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+ <skip />
+ <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="9024241779284783414">"Закінчується пам’ять"</string>
<string name="low_internal_storage_view_text" msgid="8172166728369697835">"Деякі системні функції можуть не працювати"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Недостатньо місця для системи. Переконайтесь, що на пристрої є 250 МБ вільного місця, і повторіть спробу."</string>
@@ -1531,6 +1535,8 @@
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Додаток зможе запитувати дозвіл ігнорувати оптимізацію використання заряду акумулятора."</string>
<string name="permlab_queryAllPackages" msgid="2928450604653281650">"подавати запити на всі пакети"</string>
<string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Дозволяє додатку переглядати всі встановлені пакети."</string>
+ <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"Доступ до SupplementalApis"</string>
+ <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"Надає додатку доступ до SupplementalApis."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Двічі натис. для кер. масшт."</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Не вдалося додати віджет."</string>
<string name="ime_action_go" msgid="5536744546326495436">"Йти"</string>
diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml
index 2c712f4018b2..17da4e4fd27a 100644
--- a/core/res/res/values-ur/strings.xml
+++ b/core/res/res/values-ur/strings.xml
@@ -1187,6 +1187,10 @@
<string name="deleteText" msgid="4200807474529938112">"حذف کریں"</string>
<string name="inputMethod" msgid="1784759500516314751">"اندراج کا طریقہ"</string>
<string name="editTextMenuTitle" msgid="857666911134482176">"متن کی کارروائیاں"</string>
+ <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+ <skip />
+ <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="9024241779284783414">"اسٹوریج کی جگہ ختم ہو رہی ہے"</string>
<string name="low_internal_storage_view_text" msgid="8172166728369697835">"ممکن ہے سسٹم کے کچھ فنکشنز کام نہ کریں"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"‏سسٹم کیلئے کافی اسٹوریج نہیں ہے۔ اس بات کو یقینی بنائیں کہ آپ کے پاس 250MB خالی جگہ ہے اور دوبارہ شروع کریں۔"</string>
@@ -1491,6 +1495,8 @@
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"اس ایپ کیلئے ایک ایپ کو بیٹری کی کارکردگی بہتر بنانے کو نظر انداز کرنے کی اجازت دیں۔"</string>
<string name="permlab_queryAllPackages" msgid="2928450604653281650">"سبھی پیکیجز سے متعلق استفسار کریں"</string>
<string name="permdesc_queryAllPackages" msgid="5339069855520996010">"ایپ کو سبھی انسٹال کردہ پیکیجز دیکھنے کی اجازت دیتا ہے۔"</string>
+ <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"‏SupplementalApis تک رسائی حاصل کریں"</string>
+ <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"‏ایپلیکیشن کو SupplementalApis تک رسائی حاصل کرنے کی اجازت دیتا ہے۔"</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"زوم کنٹرول کیلئے دوبار تھپتھپائیں"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"ویجٹس کو شامل نہیں کرسکا۔"</string>
<string name="ime_action_go" msgid="5536744546326495436">"جائیں"</string>
diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml
index 42a88d743d70..880a22de4ee3 100644
--- a/core/res/res/values-uz/strings.xml
+++ b/core/res/res/values-uz/strings.xml
@@ -1187,6 +1187,10 @@
<string name="deleteText" msgid="4200807474529938112">"O‘chirish"</string>
<string name="inputMethod" msgid="1784759500516314751">"Kiritish uslubi"</string>
<string name="editTextMenuTitle" msgid="857666911134482176">"Matn yozish"</string>
+ <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+ <skip />
+ <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="9024241779284783414">"Xotirada joy yetarli emas"</string>
<string name="low_internal_storage_view_text" msgid="8172166728369697835">"Ayrim funksiyalar ishlamasligi mumkin"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Tizim uchun xotirada joy yetarli emas. Avval 250 megabayt joy bo‘shatib, keyin qurilmani o‘chirib yoqing."</string>
@@ -1491,6 +1495,8 @@
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Ilovaga batareya quvvatidan xohlagancha foydalanish uchun ruxsat so‘rashga imkon beradi."</string>
<string name="permlab_queryAllPackages" msgid="2928450604653281650">"barcha paketlarni chiqarish"</string>
<string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Ilova oʻrnatilgan barcha paketlarni koʻrishiga ruxsat beradi"</string>
+ <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"SupplementalApis omboriga ruxsat"</string>
+ <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"Ilovaga SupplementalApis omboriga ruxsat beradi."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Ko‘lamini o‘zgartirish uchun ikki marta bosing"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Vidjet qo‘shilmadi."</string>
<string name="ime_action_go" msgid="5536744546326495436">"Tanlash"</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 05899edf1e8c..448207eb3d7b 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -1187,6 +1187,10 @@
<string name="deleteText" msgid="4200807474529938112">"Xóa"</string>
<string name="inputMethod" msgid="1784759500516314751">"Phương thức nhập"</string>
<string name="editTextMenuTitle" msgid="857666911134482176">"Tác vụ văn bản"</string>
+ <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+ <skip />
+ <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="9024241779284783414">"Sắp hết dung lượng lưu trữ"</string>
<string name="low_internal_storage_view_text" msgid="8172166728369697835">"Một số chức năng hệ thống có thể không hoạt động"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Không đủ bộ nhớ cho hệ thống. Đảm bảo bạn có 250 MB dung lượng trống và khởi động lại."</string>
@@ -1491,6 +1495,8 @@
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Cho phép ứng dụng hỏi quyền để bỏ qua tối ưu hóa pin cho ứng dụng đó."</string>
<string name="permlab_queryAllPackages" msgid="2928450604653281650">"truy vấn tất cả các gói"</string>
<string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Cho phép một ứng dụng xem tất cả các gói đã cài đặt."</string>
+ <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"quyền truy cập vào SupplementalApis"</string>
+ <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"Cho phép ứng dụng truy cập vào SupplementalApis."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Nhấn hai lần để kiểm soát thu phóng"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Không thể thêm tiện ích."</string>
<string name="ime_action_go" msgid="5536744546326495436">"Đến"</string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 6acd3e01d20f..6c330f247327 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -1187,6 +1187,10 @@
<string name="deleteText" msgid="4200807474529938112">"删除"</string>
<string name="inputMethod" msgid="1784759500516314751">"输入法"</string>
<string name="editTextMenuTitle" msgid="857666911134482176">"文字操作"</string>
+ <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+ <skip />
+ <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="9024241779284783414">"存储空间不足"</string>
<string name="low_internal_storage_view_text" msgid="8172166728369697835">"某些系统功能可能无法正常使用"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"系统存储空间不足。请确保您有250MB的可用空间,然后重新启动。"</string>
@@ -1491,6 +1495,8 @@
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"允许应用请求相应的权限,以便忽略针对该应用的电池优化。"</string>
<string name="permlab_queryAllPackages" msgid="2928450604653281650">"查询所有软件包"</string>
<string name="permdesc_queryAllPackages" msgid="5339069855520996010">"允许应用查看所有已安装的软件包。"</string>
+ <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"访问 SupplementalApis"</string>
+ <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"允许应用访问 SupplementalApis。"</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"双击可以进行缩放控制"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"无法添加微件。"</string>
<string name="ime_action_go" msgid="5536744546326495436">"开始"</string>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index bf51c1aae753..7a468f763c79 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -1187,6 +1187,10 @@
<string name="deleteText" msgid="4200807474529938112">"刪除"</string>
<string name="inputMethod" msgid="1784759500516314751">"輸入法"</string>
<string name="editTextMenuTitle" msgid="857666911134482176">"文字操作"</string>
+ <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+ <skip />
+ <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="9024241779284783414">"儲存空間即將用盡"</string>
<string name="low_internal_storage_view_text" msgid="8172166728369697835">"部分系統功能可能無法運作"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"系統儲存空間不足。請確認裝置有 250 MB 的可用空間,然後重新啟動。"</string>
@@ -1491,6 +1495,8 @@
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"允許應用程式要求就該應用程式忽略電池優化。"</string>
<string name="permlab_queryAllPackages" msgid="2928450604653281650">"查詢所有套件"</string>
<string name="permdesc_queryAllPackages" msgid="5339069855520996010">"允許應用程式查看所有已安裝的套件。"</string>
+ <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"存取 SupplementalApis"</string>
+ <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"允許應用程式存取 SupplementalApis。"</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"輕觸兩下控制縮放"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"無法新增小工具。"</string>
<string name="ime_action_go" msgid="5536744546326495436">"開始"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index bd0b9a242d1a..e570d65c2c7e 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -1187,6 +1187,10 @@
<string name="deleteText" msgid="4200807474529938112">"刪除"</string>
<string name="inputMethod" msgid="1784759500516314751">"輸入法"</string>
<string name="editTextMenuTitle" msgid="857666911134482176">"文字動作"</string>
+ <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+ <skip />
+ <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="9024241779284783414">"儲存空間即將用盡"</string>
<string name="low_internal_storage_view_text" msgid="8172166728369697835">"部分系統功能可能無法運作"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"系統儲存空間不足。請確定你已釋出 250MB 的可用空間,然後重新啟動。"</string>
@@ -1491,6 +1495,8 @@
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"允許應用程式要求權限,以便忽略針對該應用程式的電池效能最佳化設定。"</string>
<string name="permlab_queryAllPackages" msgid="2928450604653281650">"查詢所有套件"</string>
<string name="permdesc_queryAllPackages" msgid="5339069855520996010">"允許應用程式查看所有已安裝的套件。"</string>
+ <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"存取 SupplementalApis"</string>
+ <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"允許應用程式存取 SupplementalApis。"</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"點兩下以進行縮放控制"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"無法新增小工具。"</string>
<string name="ime_action_go" msgid="5536744546326495436">"開始"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index bf024528f3e3..301dced9f678 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -1187,6 +1187,10 @@
<string name="deleteText" msgid="4200807474529938112">"Susa"</string>
<string name="inputMethod" msgid="1784759500516314751">"Indlela yokufakwayo"</string>
<string name="editTextMenuTitle" msgid="857666911134482176">"Izenzo zombhalo"</string>
+ <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+ <skip />
+ <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="9024241779284783414">"Isikhala sokulondoloza siyaphela"</string>
<string name="low_internal_storage_view_text" msgid="8172166728369697835">"Eminye imisebenzi yohlelo ingahle ingasebenzi"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Akusona isitoreji esanele sesistimu. Qiniseka ukuthi unesikhala esikhululekile esingu-250MB uphinde uqalise kabusha."</string>
@@ -1491,6 +1495,8 @@
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Ivumela uhlelo lokusebenza ukuthi licele imvume yokuziba ukulungiselela ibhethri yalolo hlelo lokusebenza."</string>
<string name="permlab_queryAllPackages" msgid="2928450604653281650">"buza wonke amaphakheji"</string>
<string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Ivumela i-app ibone wonke amaphakheji afakiwe."</string>
+ <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"finyelela i-SupplementalApis"</string>
+ <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"Ivumela i-application ukuba ifinyelele i-SupplementalApis."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Thepha kabili ukuthola ukulawula ukusondeza"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Yehlulekile ukwengeza i-widget."</string>
<string name="ime_action_go" msgid="5536744546326495436">"Iya"</string>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 7916ef43da50..04e29890568a 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -2885,7 +2885,7 @@
<code>public void sayHello(View v)</code> method of your context
(typically, your Activity).
{@deprecated View actually traverses the Context
- hierarchy looking for the relevant method, which is fragile (an intermediate
+ hierarchy looking for the relevant method, which is fragile (an intermediate
ContextWrapper adding a same-named method would change behavior) and restricts
bytecode optimizers such as R8. Instead, use View.setOnClickListener.}-->
<attr name="onClick" format="string" />
@@ -6975,6 +6975,34 @@
<attr name="toBottom" format="fraction" />
</declare-styleable>
+ <!-- Defines the ExtendAnimation used to extend windows during animations -->
+ <declare-styleable name="ExtendAnimation">
+ <!-- Defines the amount a window should be extended outward from the left at
+ the start of the animation. -->
+ <attr name="fromExtendLeft" format="float|fraction" />
+ <!-- Defines the amount a window should be extended outward from the top at
+ the start of the animation. -->
+ <attr name="fromExtendTop" format="float|fraction" />
+ <!-- Defines the amount a window should be extended outward from the right at
+ the start of the animation. -->
+ <attr name="fromExtendRight" format="float|fraction" />
+ <!-- Defines the amount a window should be extended outward from the bottom at
+ the start of the animation. -->
+ <attr name="fromExtendBottom" format="float|fraction" />
+ <!-- Defines the amount a window should be extended outward from the left by
+ the end of the animation by transitioning from the fromExtendLeft amount. -->
+ <attr name="toExtendLeft" format="float|fraction" />
+ <!-- Defines the amount a window should be extended outward from the top by
+ the end of the animation by transitioning from the fromExtendTop amount. -->
+ <attr name="toExtendTop" format="float|fraction" />
+ <!-- Defines the amount a window should be extended outward from the right by
+ the end of the animation by transitioning from the fromExtendRight amount. -->
+ <attr name="toExtendRight" format="float|fraction" />
+ <!-- Defines the amount a window should be extended outward from the bottom by
+ the end of the animation by transitioning from the fromExtendBottom amount. -->
+ <attr name="toExtendBottom" format="float|fraction" />
+ </declare-styleable>
+
<declare-styleable name="LayoutAnimation">
<!-- Fraction of the animation duration used to delay the beginning of
the animation of each child. -->
@@ -7827,7 +7855,7 @@
<!-- Name of a method on the Context used to inflate the menu that will be
called when the item is clicked.
- {@deprecated Menu actually traverses the Context hierarchy looking for the
+ {@deprecated Menu actually traverses the Context hierarchy looking for the
relevant method, which is fragile (an intermediate ContextWrapper adding a
same-named method would change behavior) and restricts bytecode optimizers
such as R8. Instead, use MenuItem.setOnMenuItemClickListener.} -->
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 74cd519db169..a06b2cb28037 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2612,6 +2612,10 @@
will be locked. -->
<bool name="config_multiuserDelayUserDataLocking">false</bool>
+ <!-- Whether to automatically switch a non-primary user back to the primary user after a
+ timeout when the device is docked. -->
+ <bool name="config_enableTimeoutToUserZeroWhenDocked">false</bool>
+
<!-- Whether to only install system packages on a user if they're allowlisted for that user
type. These are flags and can be freely combined.
0 - disable allowlist (install all system packages; no logging)
@@ -2707,6 +2711,9 @@
Values are bandwidth_estimator, carrier_config and modem. -->
<string name="config_bandwidthEstimateSource">bandwidth_estimator</string>
+ <!-- Whether force to enable telephony new data stack or not -->
+ <bool name="config_force_enable_telephony_new_data_stack">false</bool>
+
<!-- Whether WiFi display is supported by this device.
There are many prerequisites for this feature to work correctly.
Here are a few of them:
@@ -2884,6 +2891,11 @@
<string name="config_sensorUseStartedActivity" translatable="false"
>com.android.systemui/com.android.systemui.sensorprivacy.SensorUseStartedActivity</string>
+ <!-- Component name of the activity used to ask a user to confirm system language change after
+ receiving <Set Menu Language> CEC message. -->
+ <string name="config_hdmiCecSetMenuLanguageActivity"
+ >com.android.systemui/com.android.systemui.hdmi.HdmiCecSetMenuLanguageActivity</string>
+
<!-- Name of the dialog that is used to request the user's consent for a Platform VPN -->
<string name="config_platformVpnConfirmDialogComponent" translatable="false"
>com.android.vpndialogs/com.android.vpndialogs.PlatformVpnConfirmDialog</string>
@@ -4971,6 +4983,10 @@
<!-- URI used for Nearby Share SliceProvider scanning. -->
<string translatable="false" name="config_defaultNearbySharingSliceUri"></string>
+ <!-- Component name that accepts settings intents for saved devices.
+ Used by FastPairSettingsFragment. -->
+ <string translatable="false" name="config_defaultNearbyFastPairSettingsDevicesComponent"></string>
+
<!-- Boolean indicating whether frameworks needs to reset cell broadcast geo-fencing
check after reboot or airplane mode toggling -->
<bool translatable="false" name="reset_geo_fencing_check_after_boot_or_apm">false</bool>
@@ -5133,6 +5149,9 @@
If given value is outside of this range, the option 1 (center) is assummed. -->
<integer name="config_letterboxDefaultPositionForReachability">1</integer>
+ <!-- Whether displaying letterbox education is enabled for letterboxed fullscreen apps. -->
+ <bool name="config_letterboxIsEducationEnabled">false</bool>
+
<!-- Whether a camera compat controller is enabled to allow the user to apply or revert
treatment for stretched issues in camera viewfinder. -->
<bool name="config_isCameraCompatControlForStretchedIssuesEnabled">false</bool>
@@ -5624,4 +5643,23 @@
<!-- Whether or not to enable the lock screen entry point for the QR code scanner. -->
<bool name="config_enableQrCodeScannerOnLockScreen">false</bool>
+
+ <!-- Whether Low Power Standby is supported and can be enabled. -->
+ <bool name="config_lowPowerStandbySupported">false</bool>
+
+ <!-- If supported, whether Low Power Standby is enabled by default. -->
+ <bool name="config_lowPowerStandbyEnabledByDefault">false</bool>
+
+ <!-- The amount of time after becoming non-interactive (in ms) after which
+ Low Power Standby can activate. -->
+ <integer name="config_lowPowerStandbyNonInteractiveTimeout">5000</integer>
+
+
+ <!-- Mapping to select an Intent.EXTRA_DOCK_STATE value from extcon state
+ key-value pairs. Each entry is evaluated in order and is of the form:
+ "[EXTRA_DOCK_STATE value],key1=value1,key2=value2[,...]"
+ An entry with no key-value pairs is valid and can be used as a wildcard.
+ -->
+ <string-array name="config_dockExtconStateMapping">
+ </string-array>
</resources>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 771c0724fb01..4874e6529620 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -877,6 +877,9 @@
<!-- Maximum number of datasets that are visible in the UX picker without scrolling -->
<integer name="autofill_max_visible_datasets">3</integer>
+ <!-- Size of an icon in the Autolfill fill dialog -->
+ <dimen name="autofill_dialog_icon_size">56dp</dimen>
+
<!-- Size of a slice shortcut view -->
<dimen name="slice_shortcut_size">56dp</dimen>
<!-- Size of action icons in a slice -->
diff --git a/core/res/res/values/ids.xml b/core/res/res/values/ids.xml
index db348ed7dc0b..bccd2b6529ac 100644
--- a/core/res/res/values/ids.xml
+++ b/core/res/res/values/ids.xml
@@ -59,6 +59,12 @@
<item type="id" name="candidatesArea" />
<item type="id" name="inputArea" />
<item type="id" name="inputExtractEditText" />
+ <!-- View id for the action of text editor inside of an extracted text
+ {@link InputMethodService#onCreateExtractTextView IME extract view}. -->
+ <item type="id" name="inputExtractAction" />
+ <!-- View id for the accessories of text editor inside of an extracted text
+ {@link InputMethodService#onCreateExtractTextView IME extract view}. -->
+ <item type="id" name="inputExtractAccessories" />
<item type="id" name="selectAll" />
<item type="id" name="cut" />
<item type="id" name="copy" />
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 655deac5a19e..047c04b3596c 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3260,6 +3260,14 @@
<public name="inheritKeyStoreKeys" />
<public name="preferKeepClear" />
<public name="autoHandwritingEnabled" />
+ <public name="fromExtendLeft" />
+ <public name="fromExtendTop" />
+ <public name="fromExtendRight" />
+ <public name="fromExtendBottom" />
+ <public name="toExtendLeft" />
+ <public name="toExtendTop" />
+ <public name="toExtendRight" />
+ <public name="toExtendBottom" />
</staging-public-group>
<staging-public-group type="id" first-id="0x01de0000">
@@ -3268,6 +3276,8 @@
<public name="accessibilityActionSwipeUp" />
<public name="accessibilityActionSwipeDown" />
<public name="accessibilityActionShowSuggestions" />
+ <public name="inputExtractAction" />
+ <public name="inputExtractAccessories" />
</staging-public-group>
<staging-public-group type="style" first-id="0x01dd0000">
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 44ede49af15b..610c6a69822c 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -880,16 +880,6 @@
<!-- Description of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permgroupdesc_storage">access photos, media, and files on your device</string>
- <!-- Title of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=40]-->
- <string name="permgrouplab_readMediaAural">Music &amp; other audio</string>
- <!-- Description of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=NONE]-->
- <string name="permgroupdesc_readMediaAural">access audio files on your device</string>
-
- <!-- Title of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=40]-->
- <string name="permgrouplab_readMediaVisual">Photos &amp; videos</string>
- <!-- Description of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=NONE]-->
- <string name="permgroupdesc_readMediaVisual">access images and video files on your device</string>
-
<!-- Title of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permgrouplab_microphone">Microphone</string>
<!-- Description of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. -->
@@ -1903,21 +1893,6 @@
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. "shared storage" refers to a storage space on the device that all apps with this permission can read from. [CHAR LIMIT=none] -->
<string name="permdesc_sdcardRead">Allows the app to read the contents of your shared storage.</string>
- <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. "shared storage" refers to a storage space on the device that all apps with this permission can read from. [CHAR LIMIT=none] -->
- <string name="permlab_readMediaAudio">read audio files from shared storage</string>
- <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. "shared storage" refers to a storage space on the device that all apps with this permission can read from. [CHAR LIMIT=none] -->
- <string name="permdesc_readMediaAudio">Allows the app to read audio files from your shared storage.</string>
-
- <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. "shared storage" refers to a storage space on the device that all apps with this permission can read from. [CHAR LIMIT=none] -->
- <string name="permlab_readMediaVideo">read video files from shared storage</string>
- <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. "shared storage" refers to a storage space on the device that all apps with this permission can read from. [CHAR LIMIT=none] -->
- <string name="permdesc_readMediaVideo">Allows the app to read video files from your shared storage.</string>
-
- <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. "shared storage" refers to a storage space on the device that all apps with this permission can read from. [CHAR LIMIT=none] -->
- <string name="permlab_readMediaImage">read image files from shared storage</string>
- <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. "shared storage" refers to a storage space on the device that all apps with this permission can read from. [CHAR LIMIT=none] -->
- <string name="permdesc_readMediaImage">Allows the app to read image files from your shared storage.</string>
-
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. "shared storage" refers to a storage space on the device that all apps with this permission can write to. [CHAR LIMIT=none] -->
<string name="permlab_sdcardWrite">modify or delete the contents of your shared storage</string>
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. "shared storage" refers to a storage space on the device that all apps with this permission can write to. [CHAR LIMIT=none] -->
@@ -3872,6 +3847,11 @@
<!-- Message of notification shown when serial console is enabled. [CHAR LIMIT=NONE] -->
<string name="console_running_notification_message">Performance is impacted. To disable, check bootloader.</string>
+ <!-- Title of notification shown when MTE status override is enabled. [CHAR LIMIT=NONE] -->
+ <string name="mte_override_notification_title">Experimental MTE enabled</string>
+ <!-- Message of notification shown when MTE status override is enabled. [CHAR LIMIT=NONE] -->
+ <string name="mte_override_notification_message">Performance and stability might be impacted. Reboot to disable. If enabled using arm64.memtag.bootctl, set it to "none" beforehand.</string>
+
<!-- Title of notification shown when contaminant is detected on the USB port. [CHAR LIMIT=NONE] -->
<string name="usb_contaminant_detected_title">Liquid or debris in USB port</string>
<!-- Message of notification shown when contaminant is detected on the USB port. [CHAR LIMIT=NONE] -->
@@ -5459,6 +5439,15 @@
<xliff:g id="app_name" example="Gmail">%1$s</xliff:g> is not available right now.
</string>
+ <!-- Title of the dialog shown when an app is blocked from being streamed to a remote device. [CHAR LIMIT=NONE] -->
+ <string name="app_streaming_blocked_title"><xliff:g id="activity" example="Permission dialog">%1$s</xliff:g> unavailable</string>
+ <!-- Message shown when an app is blocked from being streamed to a remote device. [CHAR LIMIT=NONE] -->
+ <string name="app_streaming_blocked_message" product="tv">This can’t be accessed on your <xliff:g id="device" example="Chromebook">%1$s</xliff:g> at this time. Try on your Android TV device instead.</string>
+ <!-- Message shown when an app is blocked from being streamed to a remote device. [CHAR LIMIT=NONE] -->
+ <string name="app_streaming_blocked_message" product="tablet">This can’t be accessed on your <xliff:g id="device" example="Chromebook">%1$s</xliff:g> at this time. Try on your tablet instead.</string>
+ <!-- Message shown when an app is blocked from being streamed to a remote device. [CHAR LIMIT=NONE] -->
+ <string name="app_streaming_blocked_message" product="default">This can’t be accessed on your <xliff:g id="device" example="Chromebook">%1$s</xliff:g> at this time. Try on your phone instead.</string>
+
<!-- Message displayed in dialog when app is too old to run on this verison of android. [CHAR LIMIT=NONE] -->
<string name="deprecated_target_sdk_message">This app was built for an older version of Android and may not work properly. Try checking for updates, or contact the developer.</string>
<!-- Title for button to see application detail in app store which it came from - it may allow user to update to newer version. [CHAR LIMIT=50] -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 6cc93fcd7a78..4c1cc4dc8f70 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -375,6 +375,7 @@
<java-symbol type="string" name="config_usbConfirmActivity" />
<java-symbol type="string" name="config_usbResolverActivity" />
<java-symbol type="string" name="config_sensorUseStartedActivity" />
+ <java-symbol type="string" name="config_hdmiCecSetMenuLanguageActivity" />
<java-symbol type="integer" name="config_minNumVisibleRecentTasks_lowRam" />
<java-symbol type="integer" name="config_maxNumVisibleRecentTasks_lowRam" />
<java-symbol type="integer" name="config_minNumVisibleRecentTasks_grid" />
@@ -465,6 +466,7 @@
<java-symbol type="integer" name="config_multiuserMaximumUsers" />
<java-symbol type="integer" name="config_multiuserMaxRunningUsers" />
<java-symbol type="bool" name="config_multiuserDelayUserDataLocking" />
+ <java-symbol type="bool" name="config_enableTimeoutToUserZeroWhenDocked" />
<java-symbol type="integer" name="config_userTypePackageWhitelistMode"/>
<java-symbol type="xml" name="config_user_types" />
<java-symbol type="integer" name="config_safe_media_volume_index" />
@@ -472,6 +474,7 @@
<java-symbol type="integer" name="config_mobile_mtu" />
<java-symbol type="array" name="config_mobile_tcp_buffers" />
<java-symbol type="string" name="config_tcp_buffers" />
+ <java-symbol type="bool" name="config_force_enable_telephony_new_data_stack" />
<java-symbol type="integer" name="config_volte_replacement_rat"/>
<java-symbol type="integer" name="config_valid_wappush_index" />
<java-symbol type="integer" name="config_overrideHasPermanentMenuKey" />
@@ -2080,6 +2083,8 @@
<java-symbol type="string" name="test_harness_mode_notification_message" />
<java-symbol type="string" name="console_running_notification_title" />
<java-symbol type="string" name="console_running_notification_message" />
+ <java-symbol type="string" name="mte_override_notification_title" />
+ <java-symbol type="string" name="mte_override_notification_message" />
<java-symbol type="string" name="taking_remote_bugreport_notification_title" />
<java-symbol type="string" name="share_remote_bugreport_notification_title" />
<java-symbol type="string" name="sharing_remote_bugreport_notification_title" />
@@ -3264,6 +3269,9 @@
<java-symbol type="string" name="app_blocked_title" />
<java-symbol type="string" name="app_blocked_message" />
+ <java-symbol type="string" name="app_streaming_blocked_title" />
+ <java-symbol type="string" name="app_streaming_blocked_message" />
+
<!-- Used internally for assistant to launch activity transitions -->
<java-symbol type="id" name="cross_task_transition" />
@@ -3505,6 +3513,7 @@
<java-symbol type="layout" name="autofill_dataset_picker"/>
<java-symbol type="layout" name="autofill_dataset_picker_fullscreen"/>
<java-symbol type="layout" name="autofill_dataset_picker_header_footer"/>
+ <java-symbol type="layout" name="autofill_fill_dialog"/>
<java-symbol type="id" name="autofill" />
<java-symbol type="id" name="autofill_dataset_footer"/>
<java-symbol type="id" name="autofill_dataset_header"/>
@@ -3517,6 +3526,13 @@
<java-symbol type="id" name="autofill_save_no" />
<java-symbol type="id" name="autofill_save_title" />
<java-symbol type="id" name="autofill_save_yes" />
+ <java-symbol type="id" name="autofill_service_icon" />
+ <java-symbol type="id" name="autofill_dialog_picker"/>
+ <java-symbol type="id" name="autofill_dialog_header"/>
+ <java-symbol type="id" name="autofill_dialog_container"/>
+ <java-symbol type="id" name="autofill_dialog_list"/>
+ <java-symbol type="id" name="autofill_dialog_no" />
+ <java-symbol type="id" name="autofill_dialog_yes" />
<java-symbol type="string" name="autofill_error_cannot_autofill" />
<java-symbol type="string" name="autofill_picker_no_suggestions" />
<java-symbol type="string" name="autofill_picker_some_suggestions" />
@@ -4085,6 +4101,7 @@
<java-symbol type="layout" name="chooser_action_button" />
<java-symbol type="dimen" name="chooser_action_button_icon_size" />
<java-symbol type="string" name="config_defaultNearbySharingComponent" />
+ <java-symbol type="string" name="config_defaultNearbyFastPairSettingsDevicesComponent" />
<java-symbol type="bool" name="config_disable_all_cb_messages" />
<java-symbol type="drawable" name="ic_close" />
@@ -4315,6 +4332,7 @@
<java-symbol type="dimen" name="config_letterboxHorizontalPositionMultiplier" />
<java-symbol type="bool" name="config_letterboxIsReachabilityEnabled" />
<java-symbol type="integer" name="config_letterboxDefaultPositionForReachability" />
+ <java-symbol type="bool" name="config_letterboxIsEducationEnabled" />
<java-symbol type="bool" name="config_isCameraCompatControlForStretchedIssuesEnabled" />
<java-symbol type="bool" name="config_hideDisplayCutoutWithDisplayArea" />
@@ -4662,9 +4680,15 @@
<java-symbol type="string" name="config_deviceSpecificDeviceStatePolicyProvider" />
+ <java-symbol type="array" name="config_dockExtconStateMapping" />
+
<java-symbol type="string" name="notification_channel_abusive_bg_apps"/>
<java-symbol type="string" name="notification_title_abusive_bg_apps"/>
<java-symbol type="string" name="notification_content_abusive_bg_apps"/>
<java-symbol type="string" name="notification_content_long_running_fgs"/>
<java-symbol type="string" name="notification_action_check_bg_apps"/>
+
+ <java-symbol type="bool" name="config_lowPowerStandbySupported" />
+ <java-symbol type="bool" name="config_lowPowerStandbyEnabledByDefault" />
+ <java-symbol type="integer" name="config_lowPowerStandbyNonInteractiveTimeout" />
</resources>
diff --git a/core/tests/coretests/res/layout/remote_view_relative_layout.xml b/core/tests/coretests/res/layout/remote_view_relative_layout.xml
new file mode 100644
index 000000000000..713a4c89ea4d
--- /dev/null
+++ b/core/tests/coretests/res/layout/remote_view_relative_layout.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2008, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
diff --git a/core/tests/coretests/res/layout/remote_view_relative_layout_with_theme.xml b/core/tests/coretests/res/layout/remote_view_relative_layout_with_theme.xml
new file mode 100644
index 000000000000..74c939b7eaa3
--- /dev/null
+++ b/core/tests/coretests/res/layout/remote_view_relative_layout_with_theme.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2008, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/themed_layout"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:theme="@style/RelativeLayoutAlignTop25Alpha"/>
diff --git a/core/tests/coretests/res/layout/remote_views_light_background_text.xml b/core/tests/coretests/res/layout/remote_views_light_background_text.xml
new file mode 100644
index 000000000000..f300f0991a97
--- /dev/null
+++ b/core/tests/coretests/res/layout/remote_views_light_background_text.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2016 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/light_background_text"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
diff --git a/core/tests/coretests/res/layout/remote_views_list.xml b/core/tests/coretests/res/layout/remote_views_list.xml
new file mode 100644
index 000000000000..ca43bc8986e7
--- /dev/null
+++ b/core/tests/coretests/res/layout/remote_views_list.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2016 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<ListView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/list"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"/>
+
diff --git a/core/tests/coretests/res/values/styles.xml b/core/tests/coretests/res/values/styles.xml
index 352b4dceb3cc..32eebb35e0c3 100644
--- a/core/tests/coretests/res/values/styles.xml
+++ b/core/tests/coretests/res/values/styles.xml
@@ -34,6 +34,16 @@
<style name="LayoutInDisplayCutoutModeAlways">
<item name="android:windowLayoutInDisplayCutoutMode">always</item>
</style>
+ <style name="RelativeLayoutAlignBottom50Alpha">
+ <item name="android:layout_alignParentTop">false</item>
+ <item name="android:layout_alignParentBottom">true</item>
+ <item name="android:alpha">0.5</item>
+ </style>
+ <style name="RelativeLayoutAlignTop25Alpha">
+ <item name="android:layout_alignParentTop">true</item>
+ <item name="android:layout_alignParentBottom">false</item>
+ <item name="android:alpha">0.25</item>
+ </style>
<style name="WindowBackgroundColorLiteral">
<item name="android:windowBackground">#00FF00</item>
</style>
diff --git a/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java b/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java
index d3e8bb0ec317..5338d046596a 100644
--- a/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java
+++ b/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java
@@ -37,9 +37,9 @@ import org.junit.Test;
@SmallTest
public class PropertyInvalidatedCacheTests {
- // This property is never set. The test process does not have permission to set any
- // properties.
- static final String CACHE_PROPERTY = "cache_key.cache_test_a";
+ // Configuration for creating caches
+ private static final int MODULE = PropertyInvalidatedCache.MODULE_TEST;
+ private static final String API = "testApi";
// This class is a proxy for binder calls. It contains a counter that increments
// every time the class is queried.
@@ -64,6 +64,25 @@ public class PropertyInvalidatedCacheTests {
}
}
+ // The functions for querying the server.
+ private static class ServerQuery
+ extends PropertyInvalidatedCache.QueryHandler<Integer, Boolean> {
+ private final ServerProxy mServer;
+
+ ServerQuery(ServerProxy server) {
+ mServer = server;
+ }
+
+ @Override
+ public Boolean apply(Integer x) {
+ return mServer.query(x);
+ }
+ @Override
+ public boolean shouldBypassCache(Integer x) {
+ return x % 13 == 0;
+ }
+ }
+
// Clear the test mode after every test, in case this process is used for other
// tests. This also resets the test property map.
@After
@@ -82,19 +101,11 @@ public class PropertyInvalidatedCacheTests {
// Create a cache that uses simple arithmetic to computer its values.
PropertyInvalidatedCache<Integer, Boolean> testCache =
- new PropertyInvalidatedCache<>(4, CACHE_PROPERTY) {
- @Override
- public Boolean recompute(Integer x) {
- return tester.query(x);
- }
- @Override
- public boolean bypass(Integer x) {
- return x % 13 == 0;
- }
- };
+ new PropertyInvalidatedCache<>(4, MODULE, API, "cache1",
+ new ServerQuery(tester));
PropertyInvalidatedCache.setTestMode(true);
- PropertyInvalidatedCache.testPropertyName(CACHE_PROPERTY);
+ testCache.testPropertyName();
tester.verify(0);
assertEquals(tester.value(3), testCache.query(3));
@@ -136,26 +147,14 @@ public class PropertyInvalidatedCacheTests {
// Three caches, all using the same system property but one uses a different name.
PropertyInvalidatedCache<Integer, Boolean> cache1 =
- new PropertyInvalidatedCache<>(4, CACHE_PROPERTY) {
- @Override
- public Boolean recompute(Integer x) {
- return tester.query(x);
- }
- };
+ new PropertyInvalidatedCache<>(4, MODULE, API, "cache1",
+ new ServerQuery(tester));
PropertyInvalidatedCache<Integer, Boolean> cache2 =
- new PropertyInvalidatedCache<>(4, CACHE_PROPERTY) {
- @Override
- public Boolean recompute(Integer x) {
- return tester.query(x);
- }
- };
+ new PropertyInvalidatedCache<>(4, MODULE, API, "cache1",
+ new ServerQuery(tester));
PropertyInvalidatedCache<Integer, Boolean> cache3 =
- new PropertyInvalidatedCache<>(4, CACHE_PROPERTY, "cache3") {
- @Override
- public Boolean recompute(Integer x) {
- return tester.query(x);
- }
- };
+ new PropertyInvalidatedCache<>(4, MODULE, API, "cache3",
+ new ServerQuery(tester));
// Caches are enabled upon creation.
assertEquals(false, cache1.getDisabledState());
@@ -176,61 +175,70 @@ public class PropertyInvalidatedCacheTests {
assertEquals(false, cache3.getDisabledState());
// Create a new cache1. Verify that the new instance is disabled.
- cache1 = new PropertyInvalidatedCache<>(4, CACHE_PROPERTY) {
- @Override
- public Boolean recompute(Integer x) {
- return tester.query(x);
- }
- };
+ cache1 = new PropertyInvalidatedCache<>(4, MODULE, API, "cache1",
+ new ServerQuery(tester));
assertEquals(true, cache1.getDisabledState());
// Remove the record of caches being locally disabled. This is a clean-up step.
- cache1.clearDisableLocal();
+ cache1.forgetDisableLocal();
assertEquals(true, cache1.getDisabledState());
assertEquals(true, cache2.getDisabledState());
assertEquals(false, cache3.getDisabledState());
// Create a new cache1. Verify that the new instance is not disabled.
- cache1 = new PropertyInvalidatedCache<>(4, CACHE_PROPERTY) {
- @Override
- public Boolean recompute(Integer x) {
- return tester.query(x);
- }
- };
+ cache1 = new PropertyInvalidatedCache<>(4, MODULE, API, "cache1",
+ new ServerQuery(tester));
assertEquals(false, cache1.getDisabledState());
}
- private static final String UNSET_KEY = "Aiw7woh6ie4toh7W";
+ private static class TestQuery
+ extends PropertyInvalidatedCache.QueryHandler<Integer, String> {
+
+ private int mRecomputeCount = 0;
+
+ @Override
+ public String apply(Integer qv) {
+ mRecomputeCount += 1;
+ return "foo" + qv.toString();
+ }
+
+ int getRecomputeCount() {
+ return mRecomputeCount;
+ }
+ }
private static class TestCache extends PropertyInvalidatedCache<Integer, String> {
+ private final TestQuery mQuery;
+
TestCache() {
- this(CACHE_PROPERTY);
+ this(MODULE, API);
}
- TestCache(String key) {
- super(4, key);
+ TestCache(int module, String api) {
+ this(module, api, new TestQuery());
setTestMode(true);
- testPropertyName(key);
+ testPropertyName();
}
- @Override
- public String recompute(Integer qv) {
- mRecomputeCount += 1;
- return "foo" + qv.toString();
+ TestCache(int module, String api, TestQuery query) {
+ super(4, module, api, api, query);
+ mQuery = query;
+ setTestMode(true);
+ testPropertyName();
}
- int getRecomputeCount() {
- return mRecomputeCount;
+ public int getRecomputeCount() {
+ return mQuery.getRecomputeCount();
}
- private int mRecomputeCount = 0;
+
}
@Test
public void testCacheRecompute() {
TestCache cache = new TestCache();
cache.invalidateCache();
- assertEquals(cache.isDisabledLocal(), false);
+ assertEquals(cache.isDisabled(), false);
assertEquals("foo5", cache.query(5));
assertEquals(1, cache.getRecomputeCount());
assertEquals("foo5", cache.query(5));
@@ -241,6 +249,11 @@ public class PropertyInvalidatedCacheTests {
assertEquals("foo5", cache.query(5));
assertEquals("foo5", cache.query(5));
assertEquals(3, cache.getRecomputeCount());
+ // Invalidate the cache with a direct call to the property.
+ PropertyInvalidatedCache.invalidateCache(MODULE, API);
+ assertEquals("foo5", cache.query(5));
+ assertEquals("foo5", cache.query(5));
+ assertEquals(4, cache.getRecomputeCount());
}
@Test
@@ -257,7 +270,8 @@ public class PropertyInvalidatedCacheTests {
@Test
public void testCachePropertyUnset() {
- TestCache cache = new TestCache(UNSET_KEY);
+ final String UNSET_API = "otherApi";
+ TestCache cache = new TestCache(MODULE, UNSET_API);
assertEquals("foo5", cache.query(5));
assertEquals("foo5", cache.query(5));
assertEquals(2, cache.getRecomputeCount());
@@ -327,17 +341,40 @@ public class PropertyInvalidatedCacheTests {
@Test
public void testLocalProcessDisable() {
TestCache cache = new TestCache();
- assertEquals(cache.isDisabledLocal(), false);
+ assertEquals(cache.isDisabled(), false);
cache.invalidateCache();
assertEquals("foo5", cache.query(5));
assertEquals(1, cache.getRecomputeCount());
assertEquals("foo5", cache.query(5));
assertEquals(1, cache.getRecomputeCount());
- assertEquals(cache.isDisabledLocal(), false);
+ assertEquals(cache.isDisabled(), false);
cache.disableLocal();
- assertEquals(cache.isDisabledLocal(), true);
+ assertEquals(cache.isDisabled(), true);
assertEquals("foo5", cache.query(5));
assertEquals("foo5", cache.query(5));
assertEquals(3, cache.getRecomputeCount());
}
+
+ @Test
+ public void testPropertyNames() {
+ String n1;
+ n1 = PropertyInvalidatedCache.createPropertyName(
+ PropertyInvalidatedCache.MODULE_SYSTEM, "getPackageInfo");
+ assertEquals(n1, "cache_key.system_server.get_package_info");
+ n1 = PropertyInvalidatedCache.createPropertyName(
+ PropertyInvalidatedCache.MODULE_SYSTEM, "get_package_info");
+ assertEquals(n1, "cache_key.system_server.get_package_info");
+ try {
+ n1 = PropertyInvalidatedCache.createPropertyName(
+ PropertyInvalidatedCache.MODULE_SYSTEM - 1, "get package_info");
+ // n1 is an invalid api name.
+ assertEquals(false, true);
+ } catch (IllegalArgumentException e) {
+ // An exception is expected here.
+ }
+
+ n1 = PropertyInvalidatedCache.createPropertyName(
+ PropertyInvalidatedCache.MODULE_BLUETOOTH, "getState");
+ assertEquals(n1, "cache_key.bluetooth.get_state");
+ }
}
diff --git a/core/tests/coretests/src/android/app/servertransaction/TestUtils.java b/core/tests/coretests/src/android/app/servertransaction/TestUtils.java
index 75da0bfba581..1467fed898c4 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TestUtils.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TestUtils.java
@@ -29,7 +29,6 @@ import android.os.Bundle;
import android.os.IBinder;
import android.os.PersistableBundle;
import android.util.MergedConfiguration;
-import android.view.DisplayAdjustments.FixedRotationAdjustments;
import com.android.internal.app.IVoiceInteractor;
import com.android.internal.content.ReferrerIntent;
@@ -110,7 +109,6 @@ class TestUtils {
private ProfilerInfo mProfilerInfo;
private IBinder mAssistToken;
private IBinder mShareableActivityToken;
- private FixedRotationAdjustments mFixedRotationAdjustments;
private boolean mLaunchedFromBubble;
LaunchActivityItemBuilder setIntent(Intent intent) {
@@ -203,11 +201,6 @@ class TestUtils {
return this;
}
- LaunchActivityItemBuilder setFixedRotationAdjustments(FixedRotationAdjustments fra) {
- mFixedRotationAdjustments = fra;
- return this;
- }
-
LaunchActivityItemBuilder setLaunchedFromBubble(boolean launchedFromBubble) {
mLaunchedFromBubble = launchedFromBubble;
return this;
@@ -218,8 +211,8 @@ class TestUtils {
mCurConfig, mOverrideConfig, mCompatInfo, mReferrer, mVoiceInteractor,
mProcState, mState, mPersistentState, mPendingResults, mPendingNewIntents,
mActivityOptions, mIsForward, mProfilerInfo, mAssistToken,
- null /* activityClientController */, mFixedRotationAdjustments,
- mShareableActivityToken, mLaunchedFromBubble);
+ null /* activityClientController */, mShareableActivityToken,
+ mLaunchedFromBubble);
}
}
}
diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
index 60d48b226754..5c9044c56f95 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
@@ -56,9 +56,6 @@ import android.os.RemoteCallback;
import android.os.RemoteException;
import android.os.SharedMemory;
import android.platform.test.annotations.Presubmit;
-import android.view.DisplayAdjustments.FixedRotationAdjustments;
-import android.view.DisplayCutout;
-import android.view.Surface;
import android.view.autofill.AutofillId;
import android.view.translation.TranslationSpec;
import android.view.translation.UiTranslationSpec;
@@ -198,8 +195,6 @@ public class TransactionParcelTests {
bundle.putParcelable("data", new ParcelableData(1));
PersistableBundle persistableBundle = new PersistableBundle();
persistableBundle.putInt("k", 4);
- FixedRotationAdjustments fixedRotationAdjustments = new FixedRotationAdjustments(
- Surface.ROTATION_90, 1920, 1080, DisplayCutout.NO_CUTOUT);
LaunchActivityItem item = new LaunchActivityItemBuilder()
.setIntent(intent).setIdent(ident).setInfo(activityInfo).setCurConfig(config())
@@ -207,8 +202,7 @@ public class TransactionParcelTests {
.setProcState(procState).setState(bundle).setPersistentState(persistableBundle)
.setPendingResults(resultInfoList()).setActivityOptions(ActivityOptions.makeBasic())
.setPendingNewIntents(referrerIntentList()).setIsForward(true)
- .setAssistToken(new Binder()).setFixedRotationAdjustments(fixedRotationAdjustments)
- .setShareableActivityToken(new Binder())
+ .setAssistToken(new Binder()).setShareableActivityToken(new Binder())
.build();
writeAndPrepareForReading(item);
@@ -359,23 +353,6 @@ public class TransactionParcelTests {
assertTrue(transaction.equals(result));
}
- @Test
- public void testFixedRotationAdjustments() {
- ClientTransaction transaction = ClientTransaction.obtain(new StubAppThread(),
- null /* activityToken */);
- transaction.addCallback(FixedRotationAdjustmentsItem.obtain(new Binder(),
- new FixedRotationAdjustments(Surface.ROTATION_270, 1920, 1080,
- DisplayCutout.NO_CUTOUT)));
-
- writeAndPrepareForReading(transaction);
-
- // Read from parcel and assert
- ClientTransaction result = ClientTransaction.CREATOR.createFromParcel(mParcel);
-
- assertEquals(transaction.hashCode(), result.hashCode());
- assertTrue(transaction.equals(result));
- }
-
/** Write to {@link #mParcel} and reset its position to prepare for reading from the start. */
private void writeAndPrepareForReading(Parcelable parcelable) {
parcelable.writeToParcel(mParcel, 0 /* flags */);
@@ -669,6 +646,10 @@ public class TransactionParcelTests {
}
@Override
+ public void dumpResources(ParcelFileDescriptor fd, RemoteCallback finishCallback) {
+ }
+
+ @Override
public final void runIsolatedEntryPoint(String entryPoint, String[] entryPointArgs) {
}
diff --git a/core/tests/coretests/src/android/content/pm/CrossProfileAppsTest.java b/core/tests/coretests/src/android/content/pm/CrossProfileAppsTest.java
index b66642c20808..fa657f7a8928 100644
--- a/core/tests/coretests/src/android/content/pm/CrossProfileAppsTest.java
+++ b/core/tests/coretests/src/android/content/pm/CrossProfileAppsTest.java
@@ -71,6 +71,8 @@ public class CrossProfileAppsTest {
private Resources mResources;
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private Drawable mDrawable;
+ @Mock
+ private PackageManager mPackageManager;
private CrossProfileApps mCrossProfileApps;
@Before
@@ -87,6 +89,7 @@ public class CrossProfileAppsTest {
Context.DEVICE_POLICY_SERVICE);
when(mContext.getSystemService(Context.DEVICE_POLICY_SERVICE)).thenReturn(
mDevicePolicyManager);
+ when(mContext.getPackageManager()).thenReturn(mPackageManager);
}
@Before
@@ -131,7 +134,8 @@ public class CrossProfileAppsTest {
setValidTargetProfile(MANAGED_PROFILE);
mCrossProfileApps.getProfileSwitchingIconDrawable(MANAGED_PROFILE);
- verify(mResources).getDrawable(R.drawable.ic_corp_badge, null);
+ verify(mPackageManager).getUserBadgeForDensityNoBackground(
+ MANAGED_PROFILE, /* density= */0);
}
@Test
diff --git a/core/tests/coretests/src/android/content/pm/PackageHelperTests.java b/core/tests/coretests/src/android/content/pm/InstallLocationUtilsTests.java
index 947da0b07234..0629a999e6bf 100644
--- a/core/tests/coretests/src/android/content/pm/PackageHelperTests.java
+++ b/core/tests/coretests/src/android/content/pm/InstallLocationUtilsTests.java
@@ -25,7 +25,7 @@ import android.platform.test.annotations.Presubmit;
import android.test.AndroidTestCase;
import android.util.Log;
-import com.android.internal.content.PackageHelper;
+import com.android.internal.content.InstallLocationUtils;
import org.mockito.Mockito;
@@ -36,10 +36,9 @@ import java.util.List;
import java.util.UUID;
@Presubmit
-public class PackageHelperTests extends AndroidTestCase {
+public class InstallLocationUtilsTests extends AndroidTestCase {
private static final boolean localLOGV = true;
public static final String TAG = "PackageHelperTests";
- protected final String PREFIX = "android.content.pm";
private static final String sInternalVolPath = "/data";
private static final String sAdoptedVolPath = "/mnt/expand/123";
@@ -88,11 +87,14 @@ public class PackageHelperTests extends AndroidTestCase {
UUID internalUuid = UUID.randomUUID();
UUID adoptedUuid = UUID.randomUUID();
UUID publicUuid = UUID.randomUUID();
- Mockito.when(storageManager.getStorageBytesUntilLow(internalFile)).thenReturn(sInternalSize);
+ Mockito.when(storageManager.getStorageBytesUntilLow(internalFile))
+ .thenReturn(sInternalSize);
Mockito.when(storageManager.getStorageBytesUntilLow(adoptedFile)).thenReturn(sAdoptedSize);
Mockito.when(storageManager.getStorageBytesUntilLow(publicFile)).thenReturn(sPublicSize);
- Mockito.when(storageManager.getUuidForPath(Mockito.eq(internalFile))).thenReturn(internalUuid);
- Mockito.when(storageManager.getUuidForPath(Mockito.eq(adoptedFile))).thenReturn(adoptedUuid);
+ Mockito.when(storageManager.getUuidForPath(Mockito.eq(internalFile)))
+ .thenReturn(internalUuid);
+ Mockito.when(storageManager.getUuidForPath(Mockito.eq(adoptedFile)))
+ .thenReturn(adoptedUuid);
Mockito.when(storageManager.getUuidForPath(Mockito.eq(publicFile))).thenReturn(publicUuid);
Mockito.when(storageManager.getAllocatableBytes(Mockito.eq(internalUuid), Mockito.anyInt()))
.thenReturn(sInternalSize);
@@ -103,7 +105,7 @@ public class PackageHelperTests extends AndroidTestCase {
return storageManager;
}
- private static final class MockedInterface extends PackageHelper.TestableInterface {
+ private static final class MockedInterface extends InstallLocationUtils.TestableInterface {
private boolean mForceAllowOnExternal = false;
private boolean mAllow3rdPartyOnInternal = true;
private ApplicationInfo mApplicationInfo = null;
@@ -164,25 +166,25 @@ public class PackageHelperTests extends AndroidTestCase {
mockedInterface.setMockValues(systemAppInfo, false /*force allow on external*/,
true /*allow 3rd party on internal*/);
String volume = null;
- volume = PackageHelper.resolveInstallVolume(getContext(), "package.name",
+ volume = InstallLocationUtils.resolveInstallVolume(getContext(), "package.name",
1 /*install location*/, 1000 /*size bytes*/, mockedInterface);
assertEquals(StorageManager.UUID_PRIVATE_INTERNAL, volume);
mockedInterface.setMockValues(systemAppInfo, true /*force allow on external*/,
true /*allow 3rd party on internal*/);
- volume = PackageHelper.resolveInstallVolume(getContext(), "package.name",
+ volume = InstallLocationUtils.resolveInstallVolume(getContext(), "package.name",
1 /*install location*/, 1000 /*size bytes*/, mockedInterface);
assertEquals(StorageManager.UUID_PRIVATE_INTERNAL, volume);
mockedInterface.setMockValues(systemAppInfo, false /*force allow on external*/,
false /*allow 3rd party on internal*/);
- volume = PackageHelper.resolveInstallVolume(getContext(), "package.name",
+ volume = InstallLocationUtils.resolveInstallVolume(getContext(), "package.name",
1 /*install location*/, 1000 /*size bytes*/, mockedInterface);
assertEquals(StorageManager.UUID_PRIVATE_INTERNAL, volume);
mockedInterface.setMockValues(systemAppInfo, true /*force allow on external*/,
false /*allow 3rd party on internal*/);
- volume = PackageHelper.resolveInstallVolume(getContext(), "package.name",
+ volume = InstallLocationUtils.resolveInstallVolume(getContext(), "package.name",
1 /*install location*/, 1000 /*size bytes*/, mockedInterface);
assertEquals(StorageManager.UUID_PRIVATE_INTERNAL, volume);
@@ -192,7 +194,7 @@ public class PackageHelperTests extends AndroidTestCase {
mockedInterface.setMockValues(systemAppInfo, true /*force allow on external*/,
true /*allow 3rd party on internal*/);
try {
- PackageHelper.resolveInstallVolume(getContext(), "package.name",
+ InstallLocationUtils.resolveInstallVolume(getContext(), "package.name",
1 /*install location*/, 1000000 /*size bytes*/, mockedInterface);
fail("Expected exception in resolveInstallVolume was not thrown");
} catch(IOException e) {
@@ -202,7 +204,7 @@ public class PackageHelperTests extends AndroidTestCase {
mockedInterface.setMockValues(systemAppInfo, false /*force allow on external*/,
true /*allow 3rd party on internal*/);
try {
- PackageHelper.resolveInstallVolume(getContext(), "package.name",
+ InstallLocationUtils.resolveInstallVolume(getContext(), "package.name",
1 /*install location*/, 1000000 /*size bytes*/, mockedInterface);
fail("Expected exception in resolveInstallVolume was not thrown");
} catch(IOException e) {
@@ -212,7 +214,7 @@ public class PackageHelperTests extends AndroidTestCase {
mockedInterface.setMockValues(systemAppInfo, false /*force allow on external*/,
false /*allow 3rd party on internal*/);
try {
- PackageHelper.resolveInstallVolume(getContext(), "package.name",
+ InstallLocationUtils.resolveInstallVolume(getContext(), "package.name",
1 /*install location*/, 1000000 /*size bytes*/, mockedInterface);
fail("Expected exception in resolveInstallVolume was not thrown");
} catch(IOException e) {
@@ -222,7 +224,7 @@ public class PackageHelperTests extends AndroidTestCase {
mockedInterface.setMockValues(systemAppInfo, true /*force allow on external*/,
false /*allow 3rd party on internal*/);
try {
- PackageHelper.resolveInstallVolume(getContext(), "package.name",
+ InstallLocationUtils.resolveInstallVolume(getContext(), "package.name",
1 /*install location*/, 1000000 /*size bytes*/, mockedInterface);
fail("Expected exception in resolveInstallVolume was not thrown");
} catch(IOException e) {
@@ -240,13 +242,13 @@ public class PackageHelperTests extends AndroidTestCase {
mockedInterface.setMockValues(appInfo, false /*force allow on external*/,
true /*allow 3rd party on internal*/);
String volume = null;
- volume = PackageHelper.resolveInstallVolume(getContext(), "package.name",
+ volume = InstallLocationUtils.resolveInstallVolume(getContext(), "package.name",
0 /*install location*/, 1000 /*size bytes*/, mockedInterface);
assertEquals(sInternalVolUuid, volume);
mockedInterface.setMockValues(appInfo, true /*force allow on external*/,
true /*allow 3rd party on internal*/);
- volume = PackageHelper.resolveInstallVolume(getContext(), "package.name",
+ volume = InstallLocationUtils.resolveInstallVolume(getContext(), "package.name",
0 /*install location*/, 1000 /*size bytes*/, mockedInterface);
assertEquals(sInternalVolUuid, volume);
}
@@ -260,25 +262,25 @@ public class PackageHelperTests extends AndroidTestCase {
appInfo.volumeUuid = sAdoptedVolUuid;
mockedInterface.setMockValues(appInfo, false /*force allow on external*/,
true /*allow 3rd party on internal*/);
- volume = PackageHelper.resolveInstallVolume(getContext(), "package.name",
+ volume = InstallLocationUtils.resolveInstallVolume(getContext(), "package.name",
0 /*install location*/, 1000 /*size bytes*/, mockedInterface);
assertEquals(sAdoptedVolUuid, volume);
mockedInterface.setMockValues(appInfo, true /*force allow on external*/,
true /*allow 3rd party on internal*/);
- volume = PackageHelper.resolveInstallVolume(getContext(), "package.name",
+ volume = InstallLocationUtils.resolveInstallVolume(getContext(), "package.name",
0 /*install location*/, 1000 /*size bytes*/, mockedInterface);
assertEquals(sAdoptedVolUuid, volume);
mockedInterface.setMockValues(appInfo, false /*force allow on external*/,
false /*allow 3rd party on internal*/);
- volume = PackageHelper.resolveInstallVolume(getContext(), "package.name",
+ volume = InstallLocationUtils.resolveInstallVolume(getContext(), "package.name",
0 /*install location*/, 1000 /*size bytes*/, mockedInterface);
assertEquals(sAdoptedVolUuid, volume);
mockedInterface.setMockValues(appInfo, true /*force allow on external*/,
false /*allow 3rd party on internal*/);
- volume = PackageHelper.resolveInstallVolume(getContext(), "package.name",
+ volume = InstallLocationUtils.resolveInstallVolume(getContext(), "package.name",
0 /*install location*/, 1000 /*size bytes*/, mockedInterface);
assertEquals(sAdoptedVolUuid, volume);
}
@@ -292,7 +294,7 @@ public class PackageHelperTests extends AndroidTestCase {
mockedInterface.setMockValues(appInfo, false /*force allow on external*/,
true /*allow 3rd party on internal*/);
try {
- PackageHelper.resolveInstallVolume(getContext(), "package.name",
+ InstallLocationUtils.resolveInstallVolume(getContext(), "package.name",
0 /*install location*/, 10000001 /*BIG size, won't fit*/, mockedInterface);
fail("Expected exception was not thrown " + appInfo.volumeUuid);
} catch (IOException e) {
@@ -302,7 +304,7 @@ public class PackageHelperTests extends AndroidTestCase {
mockedInterface.setMockValues(appInfo, false /*force allow on external*/,
false /*allow 3rd party on internal*/);
try {
- PackageHelper.resolveInstallVolume(getContext(), "package.name",
+ InstallLocationUtils.resolveInstallVolume(getContext(), "package.name",
0 /*install location*/, 10000001 /*BIG size, won't fit*/, mockedInterface);
fail("Expected exception was not thrown " + appInfo.volumeUuid);
} catch (IOException e) {
@@ -312,7 +314,7 @@ public class PackageHelperTests extends AndroidTestCase {
mockedInterface.setMockValues(appInfo, true /*force allow on external*/,
false /*allow 3rd party on internal*/);
try {
- PackageHelper.resolveInstallVolume(getContext(), "package.name",
+ InstallLocationUtils.resolveInstallVolume(getContext(), "package.name",
0 /*install location*/, 10000001 /*BIG size, won't fit*/, mockedInterface);
fail("Expected exception was not thrown " + appInfo.volumeUuid);
} catch (IOException e) {
@@ -322,7 +324,7 @@ public class PackageHelperTests extends AndroidTestCase {
mockedInterface.setMockValues(appInfo, true /*force allow on external*/,
true /*allow 3rd party on internal*/);
try {
- PackageHelper.resolveInstallVolume(getContext(), "package.name",
+ InstallLocationUtils.resolveInstallVolume(getContext(), "package.name",
0 /*install location*/, 10000001 /*BIG size, won't fit*/, mockedInterface);
fail("Expected exception was not thrown " + appInfo.volumeUuid);
} catch (IOException e) {
@@ -336,28 +338,28 @@ public class PackageHelperTests extends AndroidTestCase {
mockedInterface.setMockValues(appInfo, false /*force allow on external*/,
true /*allow 3rd party on internal*/);
String volume = null;
- volume = PackageHelper.resolveInstallVolume(getContext(), "package.name",
+ volume = InstallLocationUtils.resolveInstallVolume(getContext(), "package.name",
0 /*install location auto*/, 1000 /*size bytes*/, mockedInterface);
// Should return the volume with bigger available space.
assertEquals(sInternalVolUuid, volume);
mockedInterface.setMockValues(appInfo, true /*force allow on external*/,
true /*allow 3rd party on internal*/);
- volume = PackageHelper.resolveInstallVolume(getContext(), "package.name",
+ volume = InstallLocationUtils.resolveInstallVolume(getContext(), "package.name",
0 /*install location auto*/, 1000 /*size bytes*/, mockedInterface);
// Should return the volume with bigger available space.
assertEquals(sInternalVolUuid, volume);
mockedInterface.setMockValues(appInfo, true /*force allow on external*/,
false /*allow 3rd party on internal*/);
- volume = PackageHelper.resolveInstallVolume(getContext(), "package.name",
+ volume = InstallLocationUtils.resolveInstallVolume(getContext(), "package.name",
0 /*install location auto*/, 1000 /*size bytes*/, mockedInterface);
// Should return the volume with bigger available space.
assertEquals(sAdoptedVolUuid, volume);
mockedInterface.setMockValues(appInfo, false /*force allow on external*/,
false /*allow 3rd party on internal*/);
- volume = PackageHelper.resolveInstallVolume(getContext(), "package.name",
+ volume = InstallLocationUtils.resolveInstallVolume(getContext(), "package.name",
0 /*install location auto*/, 1000 /*size bytes*/, mockedInterface);
// Should return the volume with bigger available space.
assertEquals(sAdoptedVolUuid, volume);
@@ -371,20 +373,20 @@ public class PackageHelperTests extends AndroidTestCase {
mockedInterface.setMockValues(appInfo, false /*force allow on external*/,
true /*allow 3rd party on internal*/);
String volume = null;
- volume = PackageHelper.resolveInstallVolume(getContext(), "package.name",
+ volume = InstallLocationUtils.resolveInstallVolume(getContext(), "package.name",
1 /*install location internal ONLY*/, 1000 /*size bytes*/, mockedInterface);
assertEquals(sInternalVolUuid, volume);
mockedInterface.setMockValues(appInfo, true /*force allow on external*/,
true /*allow 3rd party on internal*/);
- volume = PackageHelper.resolveInstallVolume(getContext(), "package.name",
+ volume = InstallLocationUtils.resolveInstallVolume(getContext(), "package.name",
1 /*install location internal ONLY*/, 1000 /*size bytes*/, mockedInterface);
assertEquals(sInternalVolUuid, volume);
mockedInterface.setMockValues(appInfo, false /*force allow on external*/,
false /*allow 3rd party on internal*/);
try {
- volume = PackageHelper.resolveInstallVolume(getContext(), "package.name",
+ volume = InstallLocationUtils.resolveInstallVolume(getContext(), "package.name",
1 /*install location internal only*/, 1000 /*size bytes*/, mockedInterface);
fail("Expected exception in resolveInstallVolume was not thrown");
} catch (IOException e) {
@@ -395,7 +397,7 @@ public class PackageHelperTests extends AndroidTestCase {
mockedInterface.setMockValues(appInfo, true /*force allow on external*/,
false /*allow 3rd party on internal*/);
volume = null;
- volume = PackageHelper.resolveInstallVolume(getContext(), "package.name",
+ volume = InstallLocationUtils.resolveInstallVolume(getContext(), "package.name",
1 /*install location internal only*/, 1000 /*size bytes*/, mockedInterface);
assertEquals(sAdoptedVolUuid, volume);
}
@@ -407,7 +409,7 @@ public class PackageHelperTests extends AndroidTestCase {
mockedInterface.setMockValues(appInfo, false /*force allow on external*/,
false /*allow 3rd party on internal*/);
String volume = null;
- volume = PackageHelper.resolveInstallVolume(getContext(), "package.name",
+ volume = InstallLocationUtils.resolveInstallVolume(getContext(), "package.name",
0 /*install location auto*/, 1000 /*size bytes*/, mockedInterface);
// Should return the non-internal volume.
assertEquals(sAdoptedVolUuid, volume);
@@ -415,7 +417,7 @@ public class PackageHelperTests extends AndroidTestCase {
appInfo = null;
mockedInterface.setMockValues(appInfo, true /*force allow on external*/,
false /*allow 3rd party on internal*/);
- volume = PackageHelper.resolveInstallVolume(getContext(), "package.name",
+ volume = InstallLocationUtils.resolveInstallVolume(getContext(), "package.name",
0 /*install location auto*/, 1000 /*size bytes*/, mockedInterface);
// Should return the non-internal volume.
assertEquals(sAdoptedVolUuid, volume);
@@ -428,7 +430,7 @@ public class PackageHelperTests extends AndroidTestCase {
true /*allow 3rd party on internal*/);
String volume = null;
try {
- volume = PackageHelper.resolveInstallVolume(getContext(), "package.name",
+ volume = InstallLocationUtils.resolveInstallVolume(getContext(), "package.name",
1 /*install location internal ONLY*/,
1000000 /*size too big*/, mockedInterface);
fail("Expected exception in resolveInstallVolume was not thrown");
@@ -445,7 +447,7 @@ public class PackageHelperTests extends AndroidTestCase {
false /*allow 3rd party on internal*/);
String volume = null;
try {
- volume = PackageHelper.resolveInstallVolume(getContext(), "package.name",
+ volume = InstallLocationUtils.resolveInstallVolume(getContext(), "package.name",
1 /*install location internal only*/, 1000 /*size bytes*/, mockedInterface);
fail("Expected exception in resolveInstallVolume was not thrown");
} catch (IOException e) {
@@ -456,7 +458,7 @@ public class PackageHelperTests extends AndroidTestCase {
mockedInterface.setMockValues(appInfo, true /*force allow on external*/,
false /*allow 3rd party on internal*/);
volume = null;
- volume = PackageHelper.resolveInstallVolume(getContext(), "package.name",
+ volume = InstallLocationUtils.resolveInstallVolume(getContext(), "package.name",
1 /*install location internal only*/, 1000 /*size bytes*/, mockedInterface);
assertEquals(sAdoptedVolUuid, volume);
@@ -474,7 +476,7 @@ public class PackageHelperTests extends AndroidTestCase {
mockedInterface.setMockValues(appInfo, true /*force allow on external*/,
false /*allow 3rd party on internal*/);
String volume = null;
- volume = PackageHelper.resolveInstallVolume(getContext(), "package.name",
+ volume = InstallLocationUtils.resolveInstallVolume(getContext(), "package.name",
1 /*install location internal only*/, 1000 /*size bytes*/, mockedInterface);
assertEquals(sAdoptedVolUuid, volume);
}
diff --git a/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java b/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java
index 34a8bdea5e8a..87c167c0df07 100644
--- a/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java
+++ b/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java
@@ -281,37 +281,6 @@ public class ResourcesManagerTest extends TestCase {
}
@SmallTest
- public void testOverrideDisplayAdjustments() {
- final int originalOverrideDensity = 200;
- final int overrideDisplayDensity = 400;
- final Binder token = new Binder();
- final Configuration overrideConfig = new Configuration();
- overrideConfig.densityDpi = originalOverrideDensity;
- final Resources resources = mResourcesManager.createBaseTokenResources(
- token, APP_ONE_RES_DIR, null /* splitResDirs */, null /* legacyOverlayDirs */,
- null /* overlayDirs */,null /* libDirs */, Display.DEFAULT_DISPLAY, overrideConfig,
- CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null /* classLoader */,
- null /* loaders */);
-
- // Update the override.
- boolean handled = mResourcesManager.overrideTokenDisplayAdjustments(token,
- adjustments -> adjustments.getConfiguration().densityDpi = overrideDisplayDensity);
-
- assertTrue(handled);
- assertTrue(resources.hasOverrideDisplayAdjustments());
- assertEquals(overrideDisplayDensity,
- resources.getDisplayAdjustments().getConfiguration().densityDpi);
-
- // Clear the override.
- handled = mResourcesManager.overrideTokenDisplayAdjustments(token, null /* override */);
-
- assertTrue(handled);
- assertFalse(resources.hasOverrideDisplayAdjustments());
- assertEquals(originalOverrideDensity,
- resources.getDisplayAdjustments().getConfiguration().densityDpi);
- }
-
- @SmallTest
public void testChangingActivityDisplayDoesntOverrideDisplayRequestedByResources() {
Binder activity = new Binder();
diff --git a/core/tests/coretests/src/android/os/VibrationEffectTest.java b/core/tests/coretests/src/android/os/VibrationEffectTest.java
index 104f077e5ad2..f7ca822c36e2 100644
--- a/core/tests/coretests/src/android/os/VibrationEffectTest.java
+++ b/core/tests/coretests/src/android/os/VibrationEffectTest.java
@@ -146,6 +146,7 @@ public class VibrationEffectTest {
@Test
public void testValidateWaveformBuilder() {
+ // Cover builder methods
VibrationEffect.startWaveform(targetAmplitude(1))
.addTransition(Duration.ofSeconds(1), targetAmplitude(0.5f), targetFrequency(100))
.addTransition(Duration.ZERO, targetAmplitude(0f), targetFrequency(200))
@@ -158,6 +159,39 @@ public class VibrationEffectTest {
.build()
.validate();
+ // Make sure class summary javadoc examples compile and are valid.
+ // NOTE: IF THIS IS UPDATED, PLEASE ALSO UPDATE WaveformBuilder javadocs.
+ VibrationEffect.startWaveform(targetFrequency(60))
+ .addTransition(Duration.ofMillis(100), targetAmplitude(1), targetFrequency(120))
+ .addSustain(Duration.ofMillis(200))
+ .addTransition(Duration.ofMillis(100), targetAmplitude(0), targetFrequency(60))
+ .build()
+ .validate();
+ VibrationEffect.startComposition()
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK)
+ .addOffDuration(Duration.ofMillis(20))
+ .repeatEffectIndefinitely(
+ VibrationEffect.startWaveform(targetAmplitude(0.2f))
+ .addSustain(Duration.ofMillis(10))
+ .addTransition(Duration.ofMillis(20), targetAmplitude(0.4f))
+ .addSustain(Duration.ofMillis(30))
+ .addTransition(Duration.ofMillis(40), targetAmplitude(0.8f))
+ .addSustain(Duration.ofMillis(50))
+ .addTransition(Duration.ofMillis(60), targetAmplitude(0.2f))
+ .build())
+ .compose()
+ .validate();
+ VibrationEffect.createWaveform(new long[]{10, 20, 30}, new int[]{51, 102, 204}, -1)
+ .validate();
+ VibrationEffect.startWaveform(targetAmplitude(0.2f))
+ .addSustain(Duration.ofMillis(10))
+ .addTransition(Duration.ZERO, targetAmplitude(0.4f))
+ .addSustain(Duration.ofMillis(20))
+ .addTransition(Duration.ZERO, targetAmplitude(0.8f))
+ .addSustain(Duration.ofMillis(30))
+ .build()
+ .validate();
+
assertThrows(IllegalStateException.class,
() -> VibrationEffect.startWaveform().build().validate());
assertThrows(IllegalArgumentException.class, () -> targetAmplitude(-2));
@@ -171,6 +205,7 @@ public class VibrationEffectTest {
@Test
public void testValidateComposed() {
+ // Cover builder methods
VibrationEffect.startComposition()
.addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
.addEffect(TEST_ONE_SHOT)
@@ -178,14 +213,31 @@ public class VibrationEffectTest {
.addOffDuration(Duration.ofMillis(100))
.addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 0.5f, 10)
.addEffect(VibrationEffect.get(VibrationEffect.EFFECT_CLICK))
+ .addEffect(VibrationEffect.createWaveform(new long[]{10, 20}, /* repeat= */ 0))
.compose()
.validate();
-
VibrationEffect.startComposition()
.repeatEffectIndefinitely(TEST_ONE_SHOT)
.compose()
.validate();
+ // Make sure class summary javadoc examples compile and are valid.
+ // NOTE: IF THIS IS UPDATED, PLEASE ALSO UPDATE Composition javadocs.
+ VibrationEffect.startComposition()
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_SLOW_RISE, 0.5f)
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_QUICK_FALL, 0.5f)
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1.0f, 100)
+ .compose()
+ .validate();
+ VibrationEffect.startComposition()
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_LOW_TICK)
+ .addOffDuration(Duration.ofMillis(10))
+ .addEffect(VibrationEffect.createPredefined(VibrationEffect.EFFECT_DOUBLE_CLICK))
+ .addOffDuration(Duration.ofMillis(50))
+ .addEffect(VibrationEffect.createWaveform(new long[]{10, 20}, /* repeat= */ 0))
+ .compose()
+ .validate();
+
assertThrows(IllegalStateException.class,
() -> VibrationEffect.startComposition().compose().validate());
assertThrows(IllegalStateException.class,
diff --git a/core/tests/coretests/src/android/view/DisplayAdjustmentsTests.java b/core/tests/coretests/src/android/view/DisplayAdjustmentsTests.java
index 3cf1722d49d3..afbf8db3cd2d 100644
--- a/core/tests/coretests/src/android/view/DisplayAdjustmentsTests.java
+++ b/core/tests/coretests/src/android/view/DisplayAdjustmentsTests.java
@@ -19,9 +19,6 @@ package android.view;
import static org.junit.Assert.assertEquals;
import android.content.res.Configuration;
-import android.graphics.Point;
-import android.util.DisplayMetrics;
-import android.view.DisplayAdjustments.FixedRotationAdjustments;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -70,48 +67,4 @@ public class DisplayAdjustmentsTests {
assertEquals(configuration, newAdjustments.getConfiguration());
}
-
- @Test
- public void testFixedRotationAdjustments() {
- final DisplayAdjustments mDisplayAdjustments = new DisplayAdjustments();
- final int realRotation = Surface.ROTATION_0;
- final int fixedRotation = Surface.ROTATION_90;
-
- final int appWidth = 1080;
- final int appHeight = 1920;
- mDisplayAdjustments.setFixedRotationAdjustments(new FixedRotationAdjustments(
- fixedRotation, appWidth, appHeight, null /* cutout */));
-
- final int w = 1000;
- final int h = 2000;
- final Point size = new Point(w, h);
- mDisplayAdjustments.adjustSize(size, realRotation);
-
- assertEquals(fixedRotation, mDisplayAdjustments.getRotation(realRotation));
- assertEquals(new Point(h, w), size);
-
- final DisplayMetrics metrics = new DisplayMetrics();
- metrics.xdpi = metrics.noncompatXdpi = w;
- metrics.widthPixels = metrics.noncompatWidthPixels = w;
- metrics.ydpi = metrics.noncompatYdpi = h;
- metrics.heightPixels = metrics.noncompatHeightPixels = h;
-
- final DisplayMetrics flippedMetrics = new DisplayMetrics();
- // The physical dpi should not be adjusted.
- flippedMetrics.xdpi = flippedMetrics.noncompatXdpi = w;
- flippedMetrics.widthPixels = flippedMetrics.noncompatWidthPixels = h;
- flippedMetrics.ydpi = flippedMetrics.noncompatYdpi = h;
- flippedMetrics.heightPixels = flippedMetrics.noncompatHeightPixels = w;
-
- mDisplayAdjustments.adjustMetrics(metrics, realRotation);
-
- assertEquals(flippedMetrics, metrics);
-
- mDisplayAdjustments.adjustGlobalAppMetrics(metrics);
-
- assertEquals(appWidth, metrics.widthPixels);
- assertEquals(appWidth, metrics.noncompatWidthPixels);
- assertEquals(appHeight, metrics.heightPixels);
- assertEquals(appHeight, metrics.noncompatHeightPixels);
- }
}
diff --git a/core/tests/coretests/src/android/view/OWNERS b/core/tests/coretests/src/android/view/OWNERS
index 74cdd2146937..10f6f1fd22f1 100644
--- a/core/tests/coretests/src/android/view/OWNERS
+++ b/core/tests/coretests/src/android/view/OWNERS
@@ -16,3 +16,6 @@ per-file *Window* = file:/services/core/java/com/android/server/wm/OWNERS
# Scroll Capture
per-file *ScrollCapture*.java = file:/packages/SystemUI/src/com/android/systemui/screenshot/OWNERS
+
+# Stylus
+per-file stylus/* = file:/core/java/android/text/OWNERS \ No newline at end of file
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java
index 201883689546..99670d98bb84 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java
@@ -58,7 +58,7 @@ public class AccessibilityNodeInfoTest {
// The number of flags held in boolean properties. Their values should also be double-checked
// in the methods above.
- private static final int NUM_BOOLEAN_PROPERTIES = 23;
+ private static final int NUM_BOOLEAN_PROPERTIES = 24;
@Test
public void testStandardActions_serializationFlagIsValid() {
diff --git a/core/tests/coretests/src/android/view/HandwritingInitiatorTest.java b/core/tests/coretests/src/android/view/stylus/HandwritingInitiatorTest.java
index 632a1a9384e3..e11fe17bac9f 100644
--- a/core/tests/coretests/src/android/view/HandwritingInitiatorTest.java
+++ b/core/tests/coretests/src/android/view/stylus/HandwritingInitiatorTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.view;
+package android.view.stylus;
import static android.view.MotionEvent.ACTION_DOWN;
import static android.view.MotionEvent.ACTION_MOVE;
@@ -33,6 +33,12 @@ import android.app.Instrumentation;
import android.content.Context;
import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
+import android.view.HandwritingInitiator;
+import android.view.InputDevice;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.ViewGroup;
import android.view.inputmethod.InputMethodManager;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -47,7 +53,7 @@ import org.junit.runner.RunWith;
* Tests for {@link HandwritingInitiator}
*
* Build/Install/Run:
- * atest FrameworksCoreTests:HandwritingInitiatorTest
+ * atest FrameworksCoreTests:android.view.stylus.HandwritingInitiatorTest
*/
@Presubmit
@SmallTest
diff --git a/core/tests/coretests/src/android/widget/RemoteViewsTest.java b/core/tests/coretests/src/android/widget/RemoteViewsTest.java
index 059c764213bc..00b3693c902b 100644
--- a/core/tests/coretests/src/android/widget/RemoteViewsTest.java
+++ b/core/tests/coretests/src/android/widget/RemoteViewsTest.java
@@ -16,8 +16,12 @@
package android.widget;
+import static com.android.internal.R.id.pending_intent_tag;
+
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
@@ -31,7 +35,10 @@ import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.os.Binder;
+import android.os.Looper;
import android.os.Parcel;
+import android.util.SizeF;
+import android.view.ContextThemeWrapper;
import android.view.View;
import android.view.ViewGroup;
@@ -49,6 +56,7 @@ import org.junit.runner.RunWith;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Map;
import java.util.concurrent.CountDownLatch;
/**
@@ -261,6 +269,148 @@ public class RemoteViewsTest {
verifyViewTree(syncView, asyncView, "row1-c1", "row1-c2", "row1-c3", "row2-c1", "row2-c2");
}
+ @Test
+ public void nestedViews_setRemoteAdapter_intent() {
+ Looper.prepare();
+
+ AppWidgetHostView widget = new AppWidgetHostView(mContext);
+ RemoteViews top = new RemoteViews(mPackage, R.layout.remote_view_host);
+ RemoteViews inner1 = new RemoteViews(mPackage, R.layout.remote_view_host);
+ RemoteViews inner2 = new RemoteViews(mPackage, R.layout.remote_views_list);
+ inner2.setRemoteAdapter(R.id.list, new Intent());
+ inner1.addView(R.id.container, inner2);
+ top.addView(R.id.container, inner1);
+
+ View view = top.apply(mContext, widget);
+ widget.addView(view);
+
+ ListView listView = (ListView) view.findViewById(R.id.list);
+ listView.onRemoteAdapterConnected();
+ assertNotNull(listView.getAdapter());
+
+ top.reapply(mContext, view);
+ listView = (ListView) view.findViewById(R.id.list);
+ assertNotNull(listView.getAdapter());
+ }
+
+ @Test
+ public void nestedViews_setRemoteAdapter_remoteCollectionItems() {
+ AppWidgetHostView widget = new AppWidgetHostView(mContext);
+ RemoteViews top = new RemoteViews(mPackage, R.layout.remote_view_host);
+ RemoteViews inner1 = new RemoteViews(mPackage, R.layout.remote_view_host);
+ RemoteViews inner2 = new RemoteViews(mPackage, R.layout.remote_views_list);
+ inner2.setRemoteAdapter(
+ R.id.list,
+ new RemoteViews.RemoteCollectionItems.Builder()
+ .addItem(0, new RemoteViews(mPackage, R.layout.remote_view_host))
+ .build());
+ inner1.addView(R.id.container, inner2);
+ top.addView(R.id.container, inner1);
+
+ View view = top.apply(mContext, widget);
+ widget.addView(view);
+
+ ListView listView = (ListView) view.findViewById(R.id.list);
+ assertNotNull(listView.getAdapter());
+
+ top.reapply(mContext, view);
+ listView = (ListView) view.findViewById(R.id.list);
+ assertNotNull(listView.getAdapter());
+ }
+
+ @Test
+ public void nestedViews_collectionChildFlag() throws Exception {
+ RemoteViews nested = new RemoteViews(mPackage, R.layout.remote_views_text);
+ nested.setOnClickPendingIntent(
+ R.id.text,
+ PendingIntent.getActivity(mContext, 0, new Intent(), PendingIntent.FLAG_MUTABLE)
+ );
+
+ RemoteViews listItem = new RemoteViews(mPackage, R.layout.remote_view_host);
+ listItem.addView(R.id.container, nested);
+ listItem.addFlags(RemoteViews.FLAG_WIDGET_IS_COLLECTION_CHILD);
+
+ View view = listItem.apply(mContext, mContainer);
+ TextView text = (TextView) view.findViewById(R.id.text);
+ assertNull(text.getTag(pending_intent_tag));
+ }
+
+ @Test
+ public void landscapePortraitViews_collectionChildFlag() throws Exception {
+ RemoteViews inner = new RemoteViews(mPackage, R.layout.remote_views_text);
+ inner.setOnClickPendingIntent(
+ R.id.text,
+ PendingIntent.getActivity(mContext, 0, new Intent(), PendingIntent.FLAG_MUTABLE)
+ );
+
+ RemoteViews listItem = new RemoteViews(inner, inner);
+ listItem.addFlags(RemoteViews.FLAG_WIDGET_IS_COLLECTION_CHILD);
+
+ View view = listItem.apply(mContext, mContainer);
+ TextView text = (TextView) view.findViewById(R.id.text);
+ assertNull(text.getTag(pending_intent_tag));
+ }
+
+ @Test
+ public void sizedViews_collectionChildFlag() throws Exception {
+ RemoteViews inner = new RemoteViews(mPackage, R.layout.remote_views_text);
+ inner.setOnClickPendingIntent(
+ R.id.text,
+ PendingIntent.getActivity(mContext, 0, new Intent(), PendingIntent.FLAG_MUTABLE)
+ );
+
+ RemoteViews listItem = new RemoteViews(
+ Map.of(new SizeF(0, 0), inner, new SizeF(100, 100), inner));
+ listItem.addFlags(RemoteViews.FLAG_WIDGET_IS_COLLECTION_CHILD);
+
+ View view = listItem.apply(mContext, mContainer);
+ TextView text = (TextView) view.findViewById(R.id.text);
+ assertNull(text.getTag(pending_intent_tag));
+ }
+
+ @Test
+ public void nestedViews_lightBackgroundLayoutFlag() {
+ RemoteViews nested = new RemoteViews(mPackage, R.layout.remote_views_text);
+ nested.setLightBackgroundLayoutId(R.layout.remote_views_light_background_text);
+
+ RemoteViews parent = new RemoteViews(mPackage, R.layout.remote_view_host);
+ parent.addView(R.id.container, nested);
+ parent.setLightBackgroundLayoutId(R.layout.remote_view_host);
+ parent.addFlags(RemoteViews.FLAG_USE_LIGHT_BACKGROUND_LAYOUT);
+
+ View view = parent.apply(mContext, mContainer);
+ assertNull(view.findViewById(R.id.text));
+ assertNotNull(view.findViewById(R.id.light_background_text));
+ }
+
+
+ @Test
+ public void landscapePortraitViews_lightBackgroundLayoutFlag() {
+ RemoteViews inner = new RemoteViews(mPackage, R.layout.remote_views_text);
+ inner.setLightBackgroundLayoutId(R.layout.remote_views_light_background_text);
+
+ RemoteViews parent = new RemoteViews(inner, inner);
+ parent.addFlags(RemoteViews.FLAG_USE_LIGHT_BACKGROUND_LAYOUT);
+
+ View view = parent.apply(mContext, mContainer);
+ assertNull(view.findViewById(R.id.text));
+ assertNotNull(view.findViewById(R.id.light_background_text));
+ }
+
+ @Test
+ public void sizedViews_lightBackgroundLayoutFlag() {
+ RemoteViews inner = new RemoteViews(mPackage, R.layout.remote_views_text);
+ inner.setLightBackgroundLayoutId(R.layout.remote_views_light_background_text);
+
+ RemoteViews parent = new RemoteViews(
+ Map.of(new SizeF(0, 0), inner, new SizeF(100, 100), inner));
+ parent.addFlags(RemoteViews.FLAG_USE_LIGHT_BACKGROUND_LAYOUT);
+
+ View view = parent.apply(mContext, mContainer);
+ assertNull(view.findViewById(R.id.text));
+ assertNotNull(view.findViewById(R.id.light_background_text));
+ }
+
private RemoteViews createViewChained(int depth, String... texts) {
RemoteViews result = new RemoteViews(mPackage, R.layout.remote_view_host);
@@ -483,6 +633,47 @@ public class RemoteViewsTest {
index, inflated.getTag(com.android.internal.R.id.notification_action_index_tag));
}
+ @Test
+ public void nestedViews_themesPropagateCorrectly() {
+ Context themedContext =
+ new ContextThemeWrapper(mContext, R.style.RelativeLayoutAlignBottom50Alpha);
+ RelativeLayout rootParent = new RelativeLayout(themedContext);
+
+ RemoteViews top = new RemoteViews(mPackage, R.layout.remote_view_relative_layout);
+ RemoteViews inner1 =
+ new RemoteViews(mPackage, R.layout.remote_view_relative_layout_with_theme);
+ RemoteViews inner2 =
+ new RemoteViews(mPackage, R.layout.remote_view_relative_layout);
+
+ inner1.addView(R.id.themed_layout, inner2);
+ top.addView(R.id.container, inner1);
+
+ RelativeLayout root = (RelativeLayout) top.apply(themedContext, rootParent);
+ assertEquals(0.5, root.getAlpha(), 0.);
+ RelativeLayout.LayoutParams rootParams =
+ (RelativeLayout.LayoutParams) root.getLayoutParams();
+ assertEquals(RelativeLayout.TRUE,
+ rootParams.getRule(RelativeLayout.ALIGN_PARENT_BOTTOM));
+
+ // The theme is set on inner1View and its descendants. However, inner1View does
+ // not get its layout params from its theme (though its descendants do), but other
+ // attributes such as alpha are set.
+ RelativeLayout inner1View = (RelativeLayout) root.getChildAt(0);
+ assertEquals(R.id.themed_layout, inner1View.getId());
+ assertEquals(0.25, inner1View.getAlpha(), 0.);
+ RelativeLayout.LayoutParams inner1Params =
+ (RelativeLayout.LayoutParams) inner1View.getLayoutParams();
+ assertEquals(RelativeLayout.TRUE,
+ inner1Params.getRule(RelativeLayout.ALIGN_PARENT_BOTTOM));
+
+ RelativeLayout inner2View = (RelativeLayout) inner1View.getChildAt(0);
+ assertEquals(0.25, inner2View.getAlpha(), 0.);
+ RelativeLayout.LayoutParams inner2Params =
+ (RelativeLayout.LayoutParams) inner2View.getLayoutParams();
+ assertEquals(RelativeLayout.TRUE,
+ inner2Params.getRule(RelativeLayout.ALIGN_PARENT_TOP));
+ }
+
private class WidgetContainer extends AppWidgetHostView {
int[] mSharedViewIds;
String[] mSharedViewNames;
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java
index b655369d7e60..f5cbffb64bb5 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java
@@ -17,6 +17,7 @@
package com.android.internal.os;
import static android.os.BatteryStats.NUM_SCREEN_BRIGHTNESS_BINS;
+import static android.os.BatteryStats.RADIO_ACCESS_TECHNOLOGY_NR;
import static android.os.BatteryStats.STATS_SINCE_CHARGED;
import static android.os.BatteryStats.WAKE_TYPE_PARTIAL;
@@ -30,6 +31,12 @@ import android.os.BatteryStats.Uid.Sensor;
import android.os.Process;
import android.os.UserHandle;
import android.os.WorkSource;
+import android.telephony.Annotation;
+import android.telephony.CellSignalStrength;
+import android.telephony.DataConnectionRealTimeInfo;
+import android.telephony.ServiceState;
+import android.telephony.TelephonyManager;
+import android.util.SparseIntArray;
import android.util.SparseLongArray;
import android.view.Display;
@@ -1165,6 +1172,185 @@ public class BatteryStatsNoteTest extends TestCase {
"D", totalBlameA, totalBlameB, uid1, blame1A, blame1B, uid2, blame2A, blame2B, bi);
}
+ @SmallTest
+ public void testGetPerStateActiveRadioDurationMs() {
+ final MockClock clock = new MockClock(); // holds realtime and uptime in ms
+ final MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clock);
+ final int ratCount = BatteryStats.RADIO_ACCESS_TECHNOLOGY_COUNT;
+ final int frequencyCount = ServiceState.FREQUENCY_RANGE_MMWAVE + 1;
+ final int txLevelCount = CellSignalStrength.getNumSignalStrengthLevels();
+
+ final long[][][] expectedDurationsMs = new long[ratCount][frequencyCount][txLevelCount];
+ for (int rat = 0; rat < ratCount; rat++) {
+ for (int freq = 0; freq < frequencyCount; freq++) {
+ for (int txLvl = 0; txLvl < txLevelCount; txLvl++) {
+ expectedDurationsMs[rat][freq][txLvl] = 0;
+ }
+ }
+ }
+
+ class ModemAndBatteryState {
+ public long currentTimeMs = 100;
+ public boolean onBattery = false;
+ public boolean modemActive = false;
+ @Annotation.NetworkType
+ public int currentNetworkDataType = TelephonyManager.NETWORK_TYPE_UNKNOWN;
+ @BatteryStats.RadioAccessTechnology
+ public int currentRat = BatteryStats.RADIO_ACCESS_TECHNOLOGY_OTHER;
+ @ServiceState.FrequencyRange
+ public int currentFrequencyRange = ServiceState.FREQUENCY_RANGE_UNKNOWN;
+ public SparseIntArray currentSignalStrengths = new SparseIntArray();
+
+ void setOnBattery(boolean onBattery) {
+ this.onBattery = onBattery;
+ bi.updateTimeBasesLocked(onBattery, Display.STATE_OFF, currentTimeMs * 1000,
+ currentTimeMs * 1000);
+ }
+
+ void setModemActive(boolean active) {
+ modemActive = active;
+ final int state = active ? DataConnectionRealTimeInfo.DC_POWER_STATE_HIGH
+ : DataConnectionRealTimeInfo.DC_POWER_STATE_LOW;
+ bi.noteMobileRadioPowerStateLocked(state, currentTimeMs * 1000_000L, UID);
+ }
+
+ void setRatType(@Annotation.NetworkType int dataType,
+ @BatteryStats.RadioAccessTechnology int rat) {
+ currentNetworkDataType = dataType;
+ currentRat = rat;
+ bi.notePhoneDataConnectionStateLocked(dataType, true, ServiceState.STATE_IN_SERVICE,
+ currentFrequencyRange);
+ }
+
+ void setFrequencyRange(@ServiceState.FrequencyRange int frequency) {
+ currentFrequencyRange = frequency;
+ bi.notePhoneDataConnectionStateLocked(currentNetworkDataType, true,
+ ServiceState.STATE_IN_SERVICE, frequency);
+ }
+
+ void setSignalStrength(@BatteryStats.RadioAccessTechnology int rat, int strength) {
+ currentSignalStrengths.put(rat, strength);
+ final int size = currentSignalStrengths.size();
+ final int newestGenSignalStrength = currentSignalStrengths.valueAt(size - 1);
+ bi.notePhoneSignalStrengthLocked(newestGenSignalStrength, currentSignalStrengths);
+ }
+ }
+ final ModemAndBatteryState state = new ModemAndBatteryState();
+
+ IntConsumer incrementTime = inc -> {
+ state.currentTimeMs += inc;
+ clock.realtime = clock.uptime = state.currentTimeMs;
+
+ // If the device is not on battery, no timers should increment.
+ if (!state.onBattery) return;
+ // If the modem is not active, no timers should increment.
+ if (!state.modemActive) return;
+
+ final int currentRat = state.currentRat;
+ final int currentFrequencyRange =
+ currentRat == RADIO_ACCESS_TECHNOLOGY_NR ? state.currentFrequencyRange : 0;
+ int currentSignalStrength = state.currentSignalStrengths.get(currentRat);
+ expectedDurationsMs[currentRat][currentFrequencyRange][currentSignalStrength] += inc;
+ };
+
+ state.setOnBattery(false);
+ state.setModemActive(false);
+ state.setRatType(TelephonyManager.NETWORK_TYPE_UNKNOWN,
+ BatteryStats.RADIO_ACCESS_TECHNOLOGY_OTHER);
+ state.setFrequencyRange(ServiceState.FREQUENCY_RANGE_UNKNOWN);
+ state.setSignalStrength(BatteryStats.RADIO_ACCESS_TECHNOLOGY_OTHER,
+ CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN);
+ checkPerStateActiveRadioDurations(expectedDurationsMs, bi, state.currentTimeMs);
+
+ // While not on battery, the timers should not increase.
+ state.setModemActive(true);
+ incrementTime.accept(100);
+ checkPerStateActiveRadioDurations(expectedDurationsMs, bi, state.currentTimeMs);
+
+ state.setRatType(TelephonyManager.NETWORK_TYPE_NR, BatteryStats.RADIO_ACCESS_TECHNOLOGY_NR);
+ incrementTime.accept(200);
+ checkPerStateActiveRadioDurations(expectedDurationsMs, bi, state.currentTimeMs);
+
+ state.setSignalStrength(BatteryStats.RADIO_ACCESS_TECHNOLOGY_NR,
+ CellSignalStrength.SIGNAL_STRENGTH_GOOD);
+ incrementTime.accept(500);
+ checkPerStateActiveRadioDurations(expectedDurationsMs, bi, state.currentTimeMs);
+
+ state.setFrequencyRange(ServiceState.FREQUENCY_RANGE_MMWAVE);
+ incrementTime.accept(300);
+ checkPerStateActiveRadioDurations(expectedDurationsMs, bi, state.currentTimeMs);
+
+ state.setRatType(TelephonyManager.NETWORK_TYPE_LTE,
+ BatteryStats.RADIO_ACCESS_TECHNOLOGY_LTE);
+ incrementTime.accept(400);
+ checkPerStateActiveRadioDurations(expectedDurationsMs, bi, state.currentTimeMs);
+
+ state.setSignalStrength(BatteryStats.RADIO_ACCESS_TECHNOLOGY_LTE,
+ CellSignalStrength.SIGNAL_STRENGTH_MODERATE);
+ incrementTime.accept(500);
+ checkPerStateActiveRadioDurations(expectedDurationsMs, bi, state.currentTimeMs);
+
+ // When set on battery, currently active state (RAT:LTE, Signal Strength:Moderate) should
+ // start counting up.
+ state.setOnBattery(true);
+ incrementTime.accept(600);
+ checkPerStateActiveRadioDurations(expectedDurationsMs, bi, state.currentTimeMs);
+
+ // Changing LTE signal strength should be tracked.
+ state.setSignalStrength(BatteryStats.RADIO_ACCESS_TECHNOLOGY_LTE,
+ CellSignalStrength.SIGNAL_STRENGTH_POOR);
+ incrementTime.accept(700);
+ checkPerStateActiveRadioDurations(expectedDurationsMs, bi, state.currentTimeMs);
+
+ state.setSignalStrength(BatteryStats.RADIO_ACCESS_TECHNOLOGY_LTE,
+ CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN);
+ incrementTime.accept(800);
+ checkPerStateActiveRadioDurations(expectedDurationsMs, bi, state.currentTimeMs);
+
+ state.setSignalStrength(BatteryStats.RADIO_ACCESS_TECHNOLOGY_LTE,
+ CellSignalStrength.SIGNAL_STRENGTH_GOOD);
+ incrementTime.accept(900);
+ checkPerStateActiveRadioDurations(expectedDurationsMs, bi, state.currentTimeMs);
+
+ state.setSignalStrength(BatteryStats.RADIO_ACCESS_TECHNOLOGY_LTE,
+ CellSignalStrength.SIGNAL_STRENGTH_GREAT);
+ incrementTime.accept(1000);
+ checkPerStateActiveRadioDurations(expectedDurationsMs, bi, state.currentTimeMs);
+
+ // Change in the signal strength of nonactive RAT should not affect anything.
+ state.setSignalStrength(BatteryStats.RADIO_ACCESS_TECHNOLOGY_OTHER,
+ CellSignalStrength.SIGNAL_STRENGTH_POOR);
+ incrementTime.accept(1100);
+ checkPerStateActiveRadioDurations(expectedDurationsMs, bi, state.currentTimeMs);
+
+ // Changing to OTHER Rat should start tracking the poor signal strength.
+ state.setRatType(TelephonyManager.NETWORK_TYPE_CDMA,
+ BatteryStats.RADIO_ACCESS_TECHNOLOGY_OTHER);
+ incrementTime.accept(1200);
+ checkPerStateActiveRadioDurations(expectedDurationsMs, bi, state.currentTimeMs);
+
+ // Noting frequency change should not affect non NR Rat.
+ state.setFrequencyRange(ServiceState.FREQUENCY_RANGE_HIGH);
+ incrementTime.accept(1300);
+ checkPerStateActiveRadioDurations(expectedDurationsMs, bi, state.currentTimeMs);
+
+ // Now the NR Rat, HIGH frequency range, good signal strength should start counting.
+ state.setRatType(TelephonyManager.NETWORK_TYPE_NR, BatteryStats.RADIO_ACCESS_TECHNOLOGY_NR);
+ incrementTime.accept(1400);
+ checkPerStateActiveRadioDurations(expectedDurationsMs, bi, state.currentTimeMs);
+
+ // Noting frequency change should not affect non NR Rat.
+ state.setFrequencyRange(ServiceState.FREQUENCY_RANGE_LOW);
+ incrementTime.accept(1500);
+ checkPerStateActiveRadioDurations(expectedDurationsMs, bi, state.currentTimeMs);
+
+ // Modem no longer active, should not be tracking any more.
+ state.setModemActive(false);
+ incrementTime.accept(1500);
+ checkPerStateActiveRadioDurations(expectedDurationsMs, bi, state.currentTimeMs);
+
+ }
+
private void setFgState(int uid, boolean fgOn, MockBatteryStatsImpl bi) {
// Note that noteUidProcessStateLocked uses ActivityManager process states.
if (fgOn) {
@@ -1238,4 +1424,30 @@ public class BatteryStatsNoteTest extends TestCase {
bi.getScreenBrightnessTime(bin, currentTimeMs * 1000, STATS_SINCE_CHARGED));
}
}
+
+ private void checkPerStateActiveRadioDurations(long[][][] expectedDurationsMs,
+ BatteryStatsImpl bi, long currentTimeMs) {
+ for (int rat = 0; rat < expectedDurationsMs.length; rat++) {
+ final long[][] expectedRatDurationsMs = expectedDurationsMs[rat];
+ for (int freq = 0; freq < expectedRatDurationsMs.length; freq++) {
+ final long[] expectedFreqDurationsMs = expectedRatDurationsMs[freq];
+ for (int strength = 0; strength < expectedFreqDurationsMs.length; strength++) {
+ final long expectedSignalStrengthDurationMs = expectedFreqDurationsMs[strength];
+ final long actualDurationMs = bi.getActiveRadioDurationMs(rat, freq,
+ strength, currentTimeMs);
+
+ // Build a verbose fail message, just in case.
+ final StringBuilder sb = new StringBuilder();
+ sb.append("Wrong time in state for RAT:");
+ sb.append(BatteryStats.RADIO_ACCESS_TECHNOLOGY_NAMES[rat]);
+ sb.append(", frequency:");
+ sb.append(ServiceState.frequencyRangeToString(freq));
+ sb.append(", strength:");
+ sb.append(strength);
+
+ assertEquals(sb.toString(), expectedSignalStrengthDurationMs, actualDurationMs);
+ }
+ }
+ }
+ }
}
diff --git a/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java b/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java
index db63e6e0b187..4c247427ef8f 100644
--- a/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java
+++ b/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java
@@ -38,7 +38,6 @@ import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import org.mockito.Mockito;
-import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;
@@ -155,7 +154,7 @@ public final class DeviceStateManagerGlobalTest {
verify(callback).onStateChanged(eq(OTHER_DEVICE_STATE));
Mockito.reset(callback);
- mDeviceStateManagerGlobal.cancelRequest(request);
+ mDeviceStateManagerGlobal.cancelStateRequest();
verify(callback).onStateChanged(eq(mService.getBaseState()));
}
@@ -172,7 +171,7 @@ public final class DeviceStateManagerGlobalTest {
verify(callback).onRequestActivated(eq(request));
Mockito.reset(callback);
- mDeviceStateManagerGlobal.cancelRequest(request);
+ mDeviceStateManagerGlobal.cancelStateRequest();
verify(callback).onRequestCanceled(eq(request));
}
@@ -203,13 +202,13 @@ public final class DeviceStateManagerGlobalTest {
private int[] mSupportedStates = new int[] { DEFAULT_DEVICE_STATE, OTHER_DEVICE_STATE };
private int mBaseState = DEFAULT_DEVICE_STATE;
- private ArrayList<Request> mRequests = new ArrayList<>();
+ private Request mRequest;
private Set<IDeviceStateManagerCallback> mCallbacks = new HashSet<>();
private DeviceStateInfo getInfo() {
- final int mergedState = mRequests.isEmpty()
- ? mBaseState : mRequests.get(mRequests.size() - 1).state;
+ final int mergedState = mRequest == null
+ ? mBaseState : mRequest.state;
return new DeviceStateInfo(mSupportedStates, mBaseState, mergedState);
}
@@ -245,11 +244,10 @@ public final class DeviceStateManagerGlobalTest {
@Override
public void requestState(IBinder token, int state, int flags) {
- if (!mRequests.isEmpty()) {
- final Request topRequest = mRequests.get(mRequests.size() - 1);
+ if (mRequest != null) {
for (IDeviceStateManagerCallback callback : mCallbacks) {
try {
- callback.onRequestSuspended(topRequest.token);
+ callback.onRequestCanceled(mRequest.token);
} catch (RemoteException e) {
// Do nothing. Should never happen.
}
@@ -257,7 +255,7 @@ public final class DeviceStateManagerGlobalTest {
}
final Request request = new Request(token, state, flags);
- mRequests.add(request);
+ mRequest = request;
notifyDeviceStateInfoChanged();
for (IDeviceStateManagerCallback callback : mCallbacks) {
@@ -270,20 +268,9 @@ public final class DeviceStateManagerGlobalTest {
}
@Override
- public void cancelRequest(IBinder token) {
- int index = -1;
- for (int i = 0; i < mRequests.size(); i++) {
- if (mRequests.get(i).token.equals(token)) {
- index = i;
- break;
- }
- }
-
- if (index == -1) {
- throw new IllegalArgumentException("Unknown request: " + token);
- }
-
- mRequests.remove(index);
+ public void cancelStateRequest() {
+ IBinder token = mRequest.token;
+ mRequest = null;
for (IDeviceStateManagerCallback callback : mCallbacks) {
try {
callback.onRequestCanceled(token);
diff --git a/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java b/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java
index f04a9f735881..e16a2f8e8560 100644
--- a/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java
+++ b/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java
@@ -288,9 +288,8 @@ public class HdmiAudioSystemClientTest {
}
@Override
- public void addVendorCommandListener(final IHdmiVendorCommandListener listener,
- final int deviceType) {
- }
+ public void addVendorCommandListener(
+ final IHdmiVendorCommandListener listener, final int vendorId) {}
@Override
public void sendVendorCommand(final int deviceType, final int targetAddress,
diff --git a/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java b/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java
index 269d8424a78f..f8db069b99ce 100644
--- a/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java
+++ b/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java
@@ -297,8 +297,7 @@ public class ActivityThreadClientTest {
null /* voiceInteractor */, null /* state */, null /* persistentState */,
null /* pendingResults */, null /* pendingNewIntents */,
null /* activityOptions */, true /* isForward */, null /* profilerInfo */,
- mThread /* client */, null /* asssitToken */,
- null /* fixedRotationAdjustments */, null /* shareableActivityToken */,
+ mThread /* client */, null /* asssitToken */, null /* shareableActivityToken */,
false /* launchedFromBubble */);
}
diff --git a/core/tests/mockingcoretests/src/android/view/DisplayTest.java b/core/tests/mockingcoretests/src/android/view/DisplayTest.java
index f8dd1538882b..0c939ec24643 100644
--- a/core/tests/mockingcoretests/src/android/view/DisplayTest.java
+++ b/core/tests/mockingcoretests/src/android/view/DisplayTest.java
@@ -27,6 +27,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSess
import static com.google.common.truth.Truth.assertThat;
+import android.app.WindowConfiguration;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Point;
@@ -34,7 +35,6 @@ import android.graphics.Rect;
import android.hardware.display.DisplayManagerGlobal;
import android.platform.test.annotations.Presubmit;
import android.util.DisplayMetrics;
-import android.view.DisplayAdjustments.FixedRotationAdjustments;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -49,8 +49,6 @@ import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.mockito.quality.Strictness;
-import java.util.function.Consumer;
-
/**
* Tests for {@link Display}.
*
@@ -96,14 +94,12 @@ public class DisplayTest {
// Ensure no adjustments are set before each test.
mApplicationContext = ApplicationProvider.getApplicationContext();
- DisplayAdjustments displayAdjustments =
- mApplicationContext.getResources().getDisplayAdjustments();
- displayAdjustments.setFixedRotationAdjustments(null);
- mApplicationContext.getResources().overrideDisplayAdjustments(null);
mApplicationContext.getResources().getConfiguration().windowConfiguration.setAppBounds(
null);
mApplicationContext.getResources().getConfiguration().windowConfiguration.setMaxBounds(
null);
+ mApplicationContext.getResources().getConfiguration().windowConfiguration
+ .setDisplayRotation(WindowConfiguration.ROTATION_UNDEFINED);
mDisplayInfo.rotation = ROTATION_0;
mDisplayManagerGlobal = mock(DisplayManagerGlobal.class);
@@ -151,41 +147,11 @@ public class DisplayTest {
}
@Test
- public void testGetRotation_displayAdjustmentsWithoutOverride_rotationNotAdjusted() {
- // GIVEN display is not rotated.
- setDisplayInfoPortrait(mDisplayInfo);
- // GIVEN fixed rotation adjustments are rotated, but no override is set.
- DisplayAdjustments displayAdjustments = DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS;
- final FixedRotationAdjustments fixedRotationAdjustments =
- new FixedRotationAdjustments(ROTATION_90, APP_WIDTH, APP_HEIGHT,
- DisplayCutout.NO_CUTOUT);
- displayAdjustments.setFixedRotationAdjustments(fixedRotationAdjustments);
- // GIVEN display is constructed with display adjustments.
- final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
- displayAdjustments);
- // THEN rotation is not adjusted since no override was set.
- assertThat(display.getRotation()).isEqualTo(ROTATION_0);
- }
-
- @Test
- public void testGetRotation_resourcesWithoutOverride_rotationNotAdjusted() {
- // GIVEN display is not rotated.
- setDisplayInfoPortrait(mDisplayInfo);
- // GIVEN fixed rotation adjustments are rotated, but no override is set.
- setFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_90);
- // GIVEN display is constructed with default resources.
- final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
- mApplicationContext.getResources());
- // THEN rotation is not adjusted since no override is set.
- assertThat(display.getRotation()).isEqualTo(ROTATION_0);
- }
-
- @Test
public void testGetRotation_resourcesWithOverrideDisplayAdjustments_rotationAdjusted() {
// GIVEN display is not rotated.
setDisplayInfoPortrait(mDisplayInfo);
// GIVEN fixed rotation adjustments are rotated, and an override is set.
- setOverrideFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_90);
+ setLocalDisplayInConfig(mApplicationContext.getResources(), ROTATION_90);
// GIVEN display is constructed with default resources.
final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
mApplicationContext.getResources());
@@ -234,37 +200,11 @@ public class DisplayTest {
}
@Test
- public void testGetRealSize_resourcesPortraitWithFixedRotation_notRotatedLogicalSize() {
- // GIVEN display is rotated.
- setDisplayInfoLandscape(mDisplayInfo);
- // GIVEN fixed rotation adjustments are rotated.
- setFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_0);
- // GIVEN display is constructed with default resources.
- final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
- mApplicationContext.getResources());
- // THEN real size matches display orientation.
- verifyRealSizeIsLandscape(display);
- }
-
- @Test
- public void testGetRealSize_resourcesWithLandscapeFixedRotation_notRotatedLogicalSize() {
- // GIVEN display is not rotated.
- setDisplayInfoPortrait(mDisplayInfo);
- // GIVEN fixed rotation adjustments are rotated.
- setFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_90);
- // GIVEN display is constructed with default resources.
- final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
- mApplicationContext.getResources());
- // THEN real size matches display orientation.
- verifyRealSizeIsPortrait(display);
- }
-
- @Test
public void testGetRealSize_resourcesWithPortraitOverrideRotation_rotatedLogicalSize() {
// GIVEN display is rotated.
setDisplayInfoLandscape(mDisplayInfo);
// GIVEN fixed rotation adjustments are rotated, and an override is set.
- setOverrideFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_0);
+ setLocalDisplayInConfig(mApplicationContext.getResources(), ROTATION_0);
// GIVEN display is constructed with default resources.
final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
mApplicationContext.getResources());
@@ -277,7 +217,7 @@ public class DisplayTest {
// GIVEN display is not rotated.
setDisplayInfoPortrait(mDisplayInfo);
// GIVEN fixed rotation adjustments are rotated, and an override is set.
- setOverrideFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_90);
+ setLocalDisplayInConfig(mApplicationContext.getResources(), ROTATION_90);
// GIVEN display is constructed with default resources.
final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
mApplicationContext.getResources());
@@ -380,37 +320,11 @@ public class DisplayTest {
}
@Test
- public void testGetRealMetrics_resourcesPortraitWithFixedRotation_notRotatedLogicalSize() {
- // GIVEN display is rotated.
- setDisplayInfoLandscape(mDisplayInfo);
- // GIVEN fixed rotation adjustments are rotated.
- setFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_0);
- // GIVEN display is constructed with default resources.
- final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
- mApplicationContext.getResources());
- // THEN real metrics matches display orientation.
- verifyRealMetricsIsLandscape(display);
- }
-
- @Test
- public void testGetRealMetrics_resourcesWithLandscapeFixedRotation_notRotatedLogicalSize() {
- // GIVEN display is not rotated.
- setDisplayInfoPortrait(mDisplayInfo);
- // GIVEN fixed rotation adjustments are rotated.
- setFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_90);
- // GIVEN display is constructed with default resources.
- final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
- mApplicationContext.getResources());
- // THEN real metrics matches display orientation.
- verifyRealMetricsIsPortrait(display);
- }
-
- @Test
public void testGetRealMetrics_resourcesWithPortraitOverrideRotation_rotatedLogicalSize() {
// GIVEN display is rotated.
setDisplayInfoLandscape(mDisplayInfo);
// GIVEN fixed rotation adjustments are rotated with an override.
- setOverrideFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_0);
+ setLocalDisplayInConfig(mApplicationContext.getResources(), ROTATION_0);
// GIVEN display is constructed with default resources.
final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
mApplicationContext.getResources());
@@ -423,7 +337,7 @@ public class DisplayTest {
// GIVEN display is not rotated.
setDisplayInfoPortrait(mDisplayInfo);
// GIVEN fixed rotation adjustments are rotated.
- setOverrideFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_90);
+ setLocalDisplayInConfig(mApplicationContext.getResources(), ROTATION_90);
// GIVEN display is constructed with default resources.
final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
mApplicationContext.getResources());
@@ -569,27 +483,8 @@ public class DisplayTest {
assertThat(metrics.heightPixels).isEqualTo(bounds.height());
}
- private static FixedRotationAdjustments setOverrideFixedRotationAdjustments(
- Resources resources, @Surface.Rotation int rotation) {
- FixedRotationAdjustments fixedRotationAdjustments =
- setFixedRotationAdjustments(resources, rotation);
- resources.overrideDisplayAdjustments(
- buildOverrideRotationAdjustments(fixedRotationAdjustments));
- return fixedRotationAdjustments;
- }
-
- private static FixedRotationAdjustments setFixedRotationAdjustments(Resources resources,
+ private static void setLocalDisplayInConfig(Resources resources,
@Surface.Rotation int rotation) {
- final FixedRotationAdjustments fixedRotationAdjustments =
- new FixedRotationAdjustments(rotation, APP_WIDTH, APP_HEIGHT,
- DisplayCutout.NO_CUTOUT);
- resources.getDisplayAdjustments().setFixedRotationAdjustments(fixedRotationAdjustments);
- return fixedRotationAdjustments;
- }
-
- private static Consumer<DisplayAdjustments> buildOverrideRotationAdjustments(
- FixedRotationAdjustments fixedRotationAdjustments) {
- return consumedDisplayAdjustments
- -> consumedDisplayAdjustments.setFixedRotationAdjustments(fixedRotationAdjustments);
+ resources.getConfiguration().windowConfiguration.setDisplayRotation(rotation);
}
}
diff --git a/data/etc/com.android.systemui.xml b/data/etc/com.android.systemui.xml
index d95644a02e69..ae350ec547c3 100644
--- a/data/etc/com.android.systemui.xml
+++ b/data/etc/com.android.systemui.xml
@@ -74,5 +74,6 @@
<permission name="android.permission.BROADCAST_CLOSE_SYSTEM_DIALOGS" />
<permission name="android.permission.FORCE_STOP_PACKAGES" />
<permission name="android.permission.ACCESS_FPS_COUNTER" />
+ <permission name="android.permission.CHANGE_CONFIGURATION" />
</privapp-permissions>
</permissions>
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index 88920c865511..92fca3661fbc 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -231,18 +231,6 @@
targetSdk="29">
<new-permission name="android.permission.ACCESS_MEDIA_LOCATION" />
</split-permission>
- <split-permission name="android.permission.READ_EXTERNAL_STORAGE"
- targetSdk="33">
- <new-permission name="android.permission.READ_MEDIA_AUDIO" />
- </split-permission>
- <split-permission name="android.permission.READ_EXTERNAL_STORAGE"
- targetSdk="33">
- <new-permission name="android.permission.READ_MEDIA_VIDEO" />
- </split-permission>
- <split-permission name="android.permission.READ_EXTERNAL_STORAGE"
- targetSdk="33">
- <new-permission name="android.permission.READ_MEDIA_IMAGE" />
- </split-permission>
<split-permission name="android.permission.BLUETOOTH"
targetSdk="31">
<new-permission name="android.permission.BLUETOOTH_SCAN" />
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index e68b1ace4c2a..b3dcc34d6e93 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -334,6 +334,7 @@ applications that come with the platform
<permission name="android.permission.MANAGE_ACCESSIBILITY"/>
<permission name="android.permission.MANAGE_DEVICE_ADMINS"/>
<permission name="android.permission.MANAGE_GAME_MODE"/>
+ <permission name="android.permission.MANAGE_LOW_POWER_STANDBY" />
<permission name="android.permission.MANAGE_ROLLBACKS"/>
<permission name="android.permission.MANAGE_USB"/>
<permission name="android.permission.MODIFY_APPWIDGET_BIND_PERMISSIONS"/>
diff --git a/data/fonts/fonts.xml b/data/fonts/fonts.xml
index 4ae0fc4ae6ed..5a3a033c1cc8 100644
--- a/data/fonts/fonts.xml
+++ b/data/fonts/fonts.xml
@@ -272,13 +272,13 @@
<!-- fallback fonts -->
<family lang="und-Arab" variant="elegant">
- <font weight="400" style="normal" postScriptName="NotoNaskhArabic">
+ <font weight="400" style="normal">
NotoNaskhArabic-Regular.ttf
</font>
<font weight="700" style="normal">NotoNaskhArabic-Bold.ttf</font>
</family>
<family lang="und-Arab" variant="compact">
- <font weight="400" style="normal" postScriptName="NotoNaskhArabicUI">
+ <font weight="400" style="normal">
NotoNaskhArabicUI-Regular.ttf
</font>
<font weight="700" style="normal">NotoNaskhArabicUI-Bold.ttf</font>
@@ -329,7 +329,7 @@
<font weight="400" style="normal" postScriptName="NotoSansThai">NotoSansThai-Regular.ttf
</font>
<font weight="700" style="normal">NotoSansThai-Bold.ttf</font>
- <font weight="400" style="normal" fallbackFor="serif" postScriptName="NotoSerifThai">
+ <font weight="400" style="normal" fallbackFor="serif">
NotoSerifThai-Regular.ttf
</font>
<font weight="700" style="normal" fallbackFor="serif">NotoSerifThai-Bold.ttf</font>
@@ -923,16 +923,16 @@
<font weight="700" style="normal">NotoSansKhmerUI-Bold.ttf</font>
</family>
<family lang="und-Laoo" variant="elegant">
- <font weight="400" style="normal" postScriptName="NotoSansLao">NotoSansLao-Regular.ttf
+ <font weight="400" style="normal">NotoSansLao-Regular.ttf
</font>
<font weight="700" style="normal">NotoSansLao-Bold.ttf</font>
- <font weight="400" style="normal" fallbackFor="serif" postScriptName="NotoSerifLao">
+ <font weight="400" style="normal" fallbackFor="serif">
NotoSerifLao-Regular.ttf
</font>
<font weight="700" style="normal" fallbackFor="serif">NotoSerifLao-Bold.ttf</font>
</family>
<family lang="und-Laoo" variant="compact">
- <font weight="400" style="normal" postScriptName="NotoSansLaoUI">NotoSansLaoUI-Regular.ttf
+ <font weight="400" style="normal">NotoSansLaoUI-Regular.ttf
</font>
<font weight="700" style="normal">NotoSansLaoUI-Bold.ttf</font>
</family>
@@ -1013,7 +1013,7 @@
</font>
</family>
<family lang="und-Cans">
- <font weight="400" style="normal" postScriptName="NotoSansCanadianAboriginal">
+ <font weight="400" style="normal">
NotoSansCanadianAboriginal-Regular.ttf
</font>
</family>
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index 055e5ad17def..857af11e4ca3 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -458,7 +458,7 @@ public final class Bitmap implements Parcelable {
* No color information is stored.
* With this configuration, each pixel requires 1 byte of memory.
*/
- ALPHA_8 (1),
+ ALPHA_8(1),
/**
* Each pixel is stored on 2 bytes and only the RGB channels are
@@ -479,7 +479,7 @@ public final class Bitmap implements Parcelable {
* short color = (R & 0x1f) << 11 | (G & 0x3f) << 5 | (B & 0x1f);
* </pre>
*/
- RGB_565 (3),
+ RGB_565(3),
/**
* Each pixel is stored on 2 bytes. The three RGB color channels
@@ -501,7 +501,7 @@ public final class Bitmap implements Parcelable {
* it is advised to use {@link #ARGB_8888} instead.
*/
@Deprecated
- ARGB_4444 (4),
+ ARGB_4444(4),
/**
* Each pixel is stored on 4 bytes. Each channel (RGB and alpha
@@ -516,10 +516,10 @@ public final class Bitmap implements Parcelable {
* int color = (A & 0xff) << 24 | (B & 0xff) << 16 | (G & 0xff) << 8 | (R & 0xff);
* </pre>
*/
- ARGB_8888 (5),
+ ARGB_8888(5),
/**
- * Each pixels is stored on 8 bytes. Each channel (RGB and alpha
+ * Each pixel is stored on 8 bytes. Each channel (RGB and alpha
* for translucency) is stored as a
* {@link android.util.Half half-precision floating point value}.
*
@@ -531,7 +531,7 @@ public final class Bitmap implements Parcelable {
* long color = (A & 0xffff) << 48 | (B & 0xffff) << 32 | (G & 0xffff) << 16 | (R & 0xffff);
* </pre>
*/
- RGBA_F16 (6),
+ RGBA_F16(6),
/**
* Special configuration, when bitmap is stored only in graphic memory.
@@ -540,13 +540,29 @@ public final class Bitmap implements Parcelable {
* It is optimal for cases, when the only operation with the bitmap is to draw it on a
* screen.
*/
- HARDWARE (7);
+ HARDWARE(7),
+
+ /**
+ * Each pixel is stored on 4 bytes. Each RGB channel is stored with 10 bits of precision
+ * (1024 possible values). There is an additional alpha channel that is stored with 2 bits
+ * of precision (4 possible values).
+ *
+ * This configuration is suited for wide-gamut and HDR content which does not require alpha
+ * blending, such that the memory cost is the same as ARGB_8888 while enabling higher color
+ * precision.
+ *
+ * <p>Use this formula to pack into 32 bits:</p>
+ * <pre class="prettyprint">
+ * int color = (A & 0x3) << 30 | (B & 0x3ff) << 20 | (G & 0x3ff) << 10 | (R & 0x3ff);
+ * </pre>
+ */
+ RGBA_1010102(8);
@UnsupportedAppUsage
final int nativeInt;
private static Config sConfigs[] = {
- null, ALPHA_8, null, RGB_565, ARGB_4444, ARGB_8888, RGBA_F16, HARDWARE
+ null, ALPHA_8, null, RGB_565, ARGB_4444, ARGB_8888, RGBA_F16, HARDWARE, RGBA_1010102
};
Config(int ni) {
@@ -1000,8 +1016,8 @@ public final class Bitmap implements Parcelable {
* @param width The width of the bitmap
* @param height The height of the bitmap
* @param config The bitmap config to create.
- * @param hasAlpha If the bitmap is ARGB_8888 or RGBA_16F this flag can be used to
- * mark the bitmap as opaque. Doing so will clear the bitmap in black
+ * @param hasAlpha If the bitmap is ARGB_8888, RGBA_16F, or RGBA_1010102 this flag can be
+ * used to mark the bitmap as opaque. Doing so will clear the bitmap in black
* instead of transparent.
*
* @throws IllegalArgumentException if the width or height are <= 0, or if
@@ -1019,8 +1035,8 @@ public final class Bitmap implements Parcelable {
* @param width The width of the bitmap
* @param height The height of the bitmap
* @param config The bitmap config to create.
- * @param hasAlpha If the bitmap is ARGB_8888 or RGBA_16F this flag can be used to
- * mark the bitmap as opaque. Doing so will clear the bitmap in black
+ * @param hasAlpha If the bitmap is ARGB_8888, RGBA_16F, or RGBA_1010102 this flag can be
+ * used to mark the bitmap as opaque. Doing so will clear the bitmap in black
* instead of transparent.
* @param colorSpace The color space of the bitmap. If the config is {@link Config#RGBA_F16}
* and {@link ColorSpace.Named#SRGB sRGB} or
@@ -1050,8 +1066,8 @@ public final class Bitmap implements Parcelable {
* @param width The width of the bitmap
* @param height The height of the bitmap
* @param config The bitmap config to create.
- * @param hasAlpha If the bitmap is ARGB_8888 or RGBA_16F this flag can be used to
- * mark the bitmap as opaque. Doing so will clear the bitmap in black
+ * @param hasAlpha If the bitmap is ARGB_8888, RGBA_16F, or RGBA_1010102 this flag can be
+ * used to mark the bitmap as opaque. Doing so will clear the bitmap in black
* instead of transparent.
*
* @throws IllegalArgumentException if the width or height are <= 0, or if
@@ -1074,8 +1090,8 @@ public final class Bitmap implements Parcelable {
* @param width The width of the bitmap
* @param height The height of the bitmap
* @param config The bitmap config to create.
- * @param hasAlpha If the bitmap is ARGB_8888 or RGBA_16F this flag can be used to
- * mark the bitmap as opaque. Doing so will clear the bitmap in black
+ * @param hasAlpha If the bitmap is ARGB_8888, RGBA_16F, or RGBA_1010102 this flag can be
+ * used to mark the bitmap as opaque. Doing so will clear the bitmap in black
* instead of transparent.
* @param colorSpace The color space of the bitmap. If the config is {@link Config#RGBA_F16}
* and {@link ColorSpace.Named#SRGB sRGB} or
diff --git a/identity/java/android/security/identity/IdentityCredential.java b/identity/java/android/security/identity/IdentityCredential.java
index cdf746fc9900..f440b693a5b3 100644
--- a/identity/java/android/security/identity/IdentityCredential.java
+++ b/identity/java/android/security/identity/IdentityCredential.java
@@ -454,7 +454,8 @@ public abstract class IdentityCredential {
* @param challenge is a non-empty byte array whose contents should be unique, fresh and
* provided by the issuing authority. The value provided is embedded in the
* generated CBOR and enables the issuing authority to verify that the
- * returned proof is fresh.
+ * returned proof is fresh. Implementations are required to support
+ * challenges at least 32 bytes of length.
* @return the COSE_Sign1 data structure above
*/
public @NonNull byte[] proveOwnership(@NonNull byte[] challenge) {
@@ -485,7 +486,8 @@ public abstract class IdentityCredential {
* @param challenge is a non-empty byte array whose contents should be unique, fresh and
* provided by the issuing authority. The value provided is embedded in the
* generated CBOR and enables the issuing authority to verify that the
- * returned proof is fresh.
+ * returned proof is fresh. Implementations are required to support
+ * challenges at least 32 bytes of length.
* @return the COSE_Sign1 data structure above
*/
public @NonNull byte[] delete(@NonNull byte[] challenge) {
diff --git a/identity/java/android/security/identity/WritableIdentityCredential.java b/identity/java/android/security/identity/WritableIdentityCredential.java
index 305d0ead0652..6d569648f2c6 100644
--- a/identity/java/android/security/identity/WritableIdentityCredential.java
+++ b/identity/java/android/security/identity/WritableIdentityCredential.java
@@ -59,7 +59,8 @@ public abstract class WritableIdentityCredential {
* @param challenge is a non-empty byte array whose contents should be unique, fresh and
* provided by the issuing authority. The value provided is embedded in the
* attestation extension and enables the issuing authority to verify that the
- * attestation certificate is fresh.
+ * attestation certificate is fresh. Implementations are required to support
+ * challenges at least 32 bytes of length.
* @return the X.509 certificate for this credential's CredentialKey.
*/
public abstract @NonNull Collection<X509Certificate> getCredentialKeyCertificateChain(
diff --git a/libs/WindowManager/Shell/res/values-te/strings.xml b/libs/WindowManager/Shell/res/values-te/strings.xml
index 84cf285a4b32..76b40361545e 100644
--- a/libs/WindowManager/Shell/res/values-te/strings.xml
+++ b/libs/WindowManager/Shell/res/values-te/strings.xml
@@ -36,16 +36,16 @@
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"ప్రత్యామ్నాయ డిస్‌ప్లేలో యాప్ పని చేయకపోవచ్చు."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"ప్రత్యామ్నాయ డిస్‌ప్లేల్లో ప్రారంభానికి యాప్ మద్దతు లేదు."</string>
<string name="accessibility_divider" msgid="703810061635792791">"విభజన స్క్రీన్ విభాగిని"</string>
- <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"ఎడమవైపు పూర్తి స్క్రీన్"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"ఎడమవైపు ఫుల్-స్క్రీన్‌"</string>
<string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"ఎడమవైపు 70%"</string>
<string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"ఎడమవైపు 50%"</string>
<string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"ఎడమవైపు 30%"</string>
- <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"కుడివైపు పూర్తి స్క్రీన్"</string>
- <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"ఎగువ పూర్తి స్క్రీన్"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"కుడివైపు ఫుల్-స్క్రీన్‌"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"ఎగువ ఫుల్-స్క్రీన్‌"</string>
<string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"ఎగువ 70%"</string>
<string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"ఎగువ 50%"</string>
<string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"ఎగువ 30%"</string>
- <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"దిగువ పూర్తి స్క్రీన్"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"దిగువ ఫుల్-స్క్రీన్‌"</string>
<string name="one_handed_tutorial_title" msgid="4583241688067426350">"వన్-హ్యాండెడ్ మోడ్‌ను ఉపయోగించడం"</string>
<string name="one_handed_tutorial_description" msgid="3486582858591353067">"నిష్క్రమించడానికి, స్క్రీన్ కింది భాగం నుండి పైకి స్వైప్ చేయండి లేదా యాప్ పైన ఎక్కడైనా ట్యాప్ చేయండి"</string>
<string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"వన్-హ్యాండెడ్ మోడ్‌ను ప్రారంభిస్తుంది"</string>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java
index e2bc36028405..9384e2b4dfdf 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java
@@ -245,7 +245,8 @@ public class DisplayController {
}
}
- private void onKeepClearAreasChanged(int displayId, List<Rect> keepClearAreas) {
+ private void onKeepClearAreasChanged(int displayId, List<Rect> restricted,
+ List<Rect> unrestricted) {
synchronized (mDisplays) {
if (mDisplays.get(displayId) == null || getDisplay(displayId) == null) {
Slog.w(TAG, "Skipping onKeepClearAreasChanged on unknown"
@@ -253,7 +254,8 @@ public class DisplayController {
return;
}
for (int i = mDisplayChangedListeners.size() - 1; i >= 0; --i) {
- mDisplayChangedListeners.get(i).onKeepClearAreasChanged(displayId, keepClearAreas);
+ mDisplayChangedListeners.get(i)
+ .onKeepClearAreasChanged(displayId, restricted, unrestricted);
}
}
}
@@ -318,9 +320,10 @@ public class DisplayController {
}
@Override
- public void onKeepClearAreasChanged(int displayId, List<Rect> keepClearAreas) {
+ public void onKeepClearAreasChanged(int displayId, List<Rect> restricted,
+ List<Rect> unrestricted) {
mMainExecutor.execute(() -> {
- DisplayController.this.onKeepClearAreasChanged(displayId, keepClearAreas);
+ DisplayController.this.onKeepClearAreasChanged(displayId, restricted, unrestricted);
});
}
}
@@ -361,6 +364,7 @@ public class DisplayController {
/**
* Called when keep-clear areas on a display have changed.
*/
- default void onKeepClearAreasChanged(int displayId, List<Rect> keepClearAreas) {}
+ default void onKeepClearAreasChanged(int displayId, List<Rect> restricted,
+ List<Rect> unrestricted) {}
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
index f61e62444366..9f4ff7c8dc06 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
@@ -471,9 +471,10 @@ public abstract class WMShellBaseModule {
static Transitions provideTransitions(ShellTaskOrganizer organizer, TransactionPool pool,
DisplayController displayController, Context context,
@ShellMainThread ShellExecutor mainExecutor,
+ @ShellMainThread Handler mainHandler,
@ShellAnimationThread ShellExecutor animExecutor) {
return new Transitions(organizer, pool, displayController, context, mainExecutor,
- animExecutor);
+ mainHandler, animExecutor);
}
@WMSingleton
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java
index eda09e3ce0b0..5ebdceba135b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java
@@ -150,43 +150,45 @@ public class DragAndDropPolicy {
if (inLandscape) {
final Rect leftHitRegion = new Rect();
- final Rect leftDrawRegion = topOrLeftBounds;
final Rect rightHitRegion = new Rect();
- final Rect rightDrawRegion = bottomOrRightBounds;
// If we have existing split regions use those bounds, otherwise split it 50/50
if (inSplitScreen) {
- // Add the divider bounds to each side since that counts for the hit region.
- leftHitRegion.set(topOrLeftBounds);
- leftHitRegion.right += dividerWidth / 2;
- rightHitRegion.set(bottomOrRightBounds);
- rightHitRegion.left -= dividerWidth / 2;
+ // The bounds of the existing split will have a divider bar, the hit region
+ // should include that space. Find the center of the divider bar:
+ float centerX = topOrLeftBounds.right + (dividerWidth / 2);
+ // Now set the hit regions using that center.
+ leftHitRegion.set(displayRegion);
+ leftHitRegion.right = (int) centerX;
+ rightHitRegion.set(displayRegion);
+ rightHitRegion.left = (int) centerX;
} else {
displayRegion.splitVertically(leftHitRegion, rightHitRegion);
}
- mTargets.add(new Target(TYPE_SPLIT_LEFT, leftHitRegion, leftDrawRegion));
- mTargets.add(new Target(TYPE_SPLIT_RIGHT, rightHitRegion, rightDrawRegion));
+ mTargets.add(new Target(TYPE_SPLIT_LEFT, leftHitRegion, topOrLeftBounds));
+ mTargets.add(new Target(TYPE_SPLIT_RIGHT, rightHitRegion, bottomOrRightBounds));
} else {
final Rect topHitRegion = new Rect();
- final Rect topDrawRegion = topOrLeftBounds;
final Rect bottomHitRegion = new Rect();
- final Rect bottomDrawRegion = bottomOrRightBounds;
// If we have existing split regions use those bounds, otherwise split it 50/50
if (inSplitScreen) {
- // Add the divider bounds to each side since that counts for the hit region.
- topHitRegion.set(topOrLeftBounds);
- topHitRegion.bottom += dividerWidth / 2;
- bottomHitRegion.set(bottomOrRightBounds);
- bottomHitRegion.top -= dividerWidth / 2;
+ // The bounds of the existing split will have a divider bar, the hit region
+ // should include that space. Find the center of the divider bar:
+ float centerX = topOrLeftBounds.bottom + (dividerWidth / 2);
+ // Now set the hit regions using that center.
+ topHitRegion.set(displayRegion);
+ topHitRegion.bottom = (int) centerX;
+ bottomHitRegion.set(displayRegion);
+ bottomHitRegion.top = (int) centerX;
} else {
displayRegion.splitHorizontally(topHitRegion, bottomHitRegion);
}
- mTargets.add(new Target(TYPE_SPLIT_TOP, topHitRegion, topDrawRegion));
- mTargets.add(new Target(TYPE_SPLIT_BOTTOM, bottomHitRegion, bottomDrawRegion));
+ mTargets.add(new Target(TYPE_SPLIT_TOP, topHitRegion, topOrLeftBounds));
+ mTargets.add(new Target(TYPE_SPLIT_BOTTOM, bottomHitRegion, bottomOrRightBounds));
}
} else {
// Split-screen not allowed, so only show the fullscreen target
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java
index fd3be2b11c15..7307ba30fd67 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java
@@ -24,7 +24,6 @@ import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSIT
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
-import android.animation.ObjectAnimator;
import android.annotation.SuppressLint;
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
@@ -103,7 +102,7 @@ public class DragLayout extends LinearLayout {
MATCH_PARENT));
((LayoutParams) mDropZoneView1.getLayoutParams()).weight = 1;
((LayoutParams) mDropZoneView2.getLayoutParams()).weight = 1;
- updateContainerMargins();
+ updateContainerMargins(getResources().getConfiguration().orientation);
}
@Override
@@ -128,20 +127,18 @@ public class DragLayout extends LinearLayout {
}
public void onConfigChanged(Configuration newConfig) {
- final int orientation = getResources().getConfiguration().orientation;
- if (orientation == Configuration.ORIENTATION_LANDSCAPE
+ if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE
&& getOrientation() != HORIZONTAL) {
setOrientation(LinearLayout.HORIZONTAL);
- updateContainerMargins();
- } else if (orientation == Configuration.ORIENTATION_PORTRAIT
+ updateContainerMargins(newConfig.orientation);
+ } else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT
&& getOrientation() != VERTICAL) {
setOrientation(LinearLayout.VERTICAL);
- updateContainerMargins();
+ updateContainerMargins(newConfig.orientation);
}
}
- private void updateContainerMargins() {
- final int orientation = getResources().getConfiguration().orientation;
+ private void updateContainerMargins(int orientation) {
final float halfMargin = mDisplayMargin / 2f;
if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
mDropZoneView1.setContainerMargin(
@@ -156,10 +153,6 @@ public class DragLayout extends LinearLayout {
}
}
- public boolean hasDropTarget() {
- return mCurrentTarget != null;
- }
-
public boolean hasDropped() {
return mHasDropped;
}
@@ -271,6 +264,9 @@ public class DragLayout extends LinearLayout {
* Updates the visible drop target as the user drags.
*/
public void update(DragEvent event) {
+ if (mHasDropped) {
+ return;
+ }
// Find containing region, if the same as mCurrentRegion, then skip, otherwise, animate the
// visibility of the current region
DragAndDropPolicy.Target target = mPolicy.getTargetAtLocation(
@@ -286,7 +282,8 @@ public class DragLayout extends LinearLayout {
animateHighlight(target);
} else {
// Switching between targets
- animateHighlight(target);
+ mDropZoneView1.animateSwitch();
+ mDropZoneView2.animateSwitch();
}
mCurrentTarget = target;
}
@@ -323,7 +320,7 @@ public class DragLayout extends LinearLayout {
: DISABLE_NONE);
mDropZoneView1.setShowingMargin(visible);
mDropZoneView2.setShowingMargin(visible);
- ObjectAnimator animator = mDropZoneView1.getAnimator();
+ Animator animator = mDropZoneView1.getAnimator();
if (animCompleteCallback != null) {
if (animator != null) {
animator.addListener(new AnimatorListenerAdapter() {
@@ -343,17 +340,11 @@ public class DragLayout extends LinearLayout {
if (target.type == DragAndDropPolicy.Target.TYPE_SPLIT_LEFT
|| target.type == DragAndDropPolicy.Target.TYPE_SPLIT_TOP) {
mDropZoneView1.setShowingHighlight(true);
- mDropZoneView1.setShowingSplash(false);
-
mDropZoneView2.setShowingHighlight(false);
- mDropZoneView2.setShowingSplash(true);
} else if (target.type == DragAndDropPolicy.Target.TYPE_SPLIT_RIGHT
|| target.type == DragAndDropPolicy.Target.TYPE_SPLIT_BOTTOM) {
mDropZoneView1.setShowingHighlight(false);
- mDropZoneView1.setShowingSplash(true);
-
mDropZoneView2.setShowingHighlight(true);
- mDropZoneView2.setShowingSplash(false);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DropZoneView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DropZoneView.java
index 2f47af57d496..a3ee8aed204d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DropZoneView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DropZoneView.java
@@ -18,6 +18,7 @@ package com.android.wm.shell.draganddrop;
import static com.android.wm.shell.animation.Interpolators.FAST_OUT_SLOW_IN;
+import android.animation.Animator;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.graphics.Canvas;
@@ -27,7 +28,6 @@ import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.FloatProperty;
-import android.util.IntProperty;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
@@ -43,8 +43,8 @@ import com.android.wm.shell.R;
*/
public class DropZoneView extends FrameLayout {
- private static final int SPLASHSCREEN_ALPHA_INT = (int) (255 * 0.90f);
- private static final int HIGHLIGHT_ALPHA_INT = 255;
+ private static final float SPLASHSCREEN_ALPHA = 0.90f;
+ private static final float HIGHLIGHT_ALPHA = 1f;
private static final int MARGIN_ANIMATION_ENTER_DURATION = 400;
private static final int MARGIN_ANIMATION_EXIT_DURATION = 250;
@@ -61,54 +61,27 @@ public class DropZoneView extends FrameLayout {
}
};
- private static final IntProperty<ColorDrawable> SPLASHSCREEN_ALPHA =
- new IntProperty<ColorDrawable>("splashscreen") {
- @Override
- public void setValue(ColorDrawable d, int alpha) {
- d.setAlpha(alpha);
- }
-
- @Override
- public Integer get(ColorDrawable d) {
- return d.getAlpha();
- }
- };
-
- private static final IntProperty<ColorDrawable> HIGHLIGHT_ALPHA =
- new IntProperty<ColorDrawable>("highlight") {
- @Override
- public void setValue(ColorDrawable d, int alpha) {
- d.setAlpha(alpha);
- }
-
- @Override
- public Integer get(ColorDrawable d) {
- return d.getAlpha();
- }
- };
-
private final Path mPath = new Path();
private final float[] mContainerMargin = new float[4];
private float mCornerRadius;
private float mBottomInset;
private int mMarginColor; // i.e. color used for negative space like the container insets
- private int mHighlightColor;
private boolean mShowingHighlight;
private boolean mShowingSplash;
private boolean mShowingMargin;
- // TODO: might be more seamless to animate between splash/highlight color instead of 2 separate
- private ObjectAnimator mSplashAnimator;
- private ObjectAnimator mHighlightAnimator;
+ private int mSplashScreenColor;
+ private int mHighlightColor;
+
+ private ObjectAnimator mBackgroundAnimator;
private ObjectAnimator mMarginAnimator;
private float mMarginPercent;
// Renders a highlight or neutral transparent color
- private ColorDrawable mDropZoneDrawable;
+ private ColorDrawable mColorDrawable;
// Renders the translucent splashscreen with the app icon in the middle
private ImageView mSplashScreenView;
- private ColorDrawable mSplashBackgroundDrawable;
// Renders the margin / insets around the dropzone container
private MarginView mMarginView;
@@ -130,19 +103,14 @@ public class DropZoneView extends FrameLayout {
mCornerRadius = ScreenDecorationsUtils.getWindowCornerRadius(context);
mMarginColor = getResources().getColor(R.color.taskbar_background);
- mHighlightColor = getResources().getColor(android.R.color.system_accent1_500);
-
- mDropZoneDrawable = new ColorDrawable();
- mDropZoneDrawable.setColor(mHighlightColor);
- mDropZoneDrawable.setAlpha(0);
- setBackgroundDrawable(mDropZoneDrawable);
+ int c = getResources().getColor(android.R.color.system_accent1_500);
+ mHighlightColor = Color.argb(HIGHLIGHT_ALPHA, Color.red(c), Color.green(c), Color.blue(c));
+ mSplashScreenColor = Color.argb(SPLASHSCREEN_ALPHA, 0, 0, 0);
+ mColorDrawable = new ColorDrawable();
+ setBackgroundDrawable(mColorDrawable);
mSplashScreenView = new ImageView(context);
mSplashScreenView.setScaleType(ImageView.ScaleType.CENTER);
- mSplashBackgroundDrawable = new ColorDrawable();
- mSplashBackgroundDrawable.setColor(Color.WHITE);
- mSplashBackgroundDrawable.setAlpha(SPLASHSCREEN_ALPHA_INT);
- mSplashScreenView.setBackgroundDrawable(mSplashBackgroundDrawable);
addView(mSplashScreenView, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT));
mSplashScreenView.setAlpha(0f);
@@ -157,10 +125,6 @@ public class DropZoneView extends FrameLayout {
mMarginColor = getResources().getColor(R.color.taskbar_background);
mHighlightColor = getResources().getColor(android.R.color.system_accent1_500);
- final int alpha = mDropZoneDrawable.getAlpha();
- mDropZoneDrawable.setColor(mHighlightColor);
- mDropZoneDrawable.setAlpha(alpha);
-
if (mMarginPercent > 0) {
mMarginView.invalidate();
}
@@ -187,38 +151,39 @@ public class DropZoneView extends FrameLayout {
}
/** Sets the color and icon to use for the splashscreen when shown. */
- public void setAppInfo(int splashScreenColor, Drawable appIcon) {
- mSplashBackgroundDrawable.setColor(splashScreenColor);
+ public void setAppInfo(int color, Drawable appIcon) {
+ Color c = Color.valueOf(color);
+ mSplashScreenColor = Color.argb(SPLASHSCREEN_ALPHA, c.red(), c.green(), c.blue());
mSplashScreenView.setImageDrawable(appIcon);
}
/** @return an active animator for this view if one exists. */
@Nullable
- public ObjectAnimator getAnimator() {
+ public Animator getAnimator() {
if (mMarginAnimator != null && mMarginAnimator.isRunning()) {
return mMarginAnimator;
- } else if (mHighlightAnimator != null && mHighlightAnimator.isRunning()) {
- return mHighlightAnimator;
- } else if (mSplashAnimator != null && mSplashAnimator.isRunning()) {
- return mSplashAnimator;
+ } else if (mBackgroundAnimator != null && mBackgroundAnimator.isRunning()) {
+ return mBackgroundAnimator;
}
return null;
}
- /** Animates the splashscreen to show or hide. */
- public void setShowingSplash(boolean showingSplash) {
- if (mShowingSplash != showingSplash) {
- mShowingSplash = showingSplash;
- animateSplashToState();
- }
+ /** Animates between highlight and splashscreen depending on current state. */
+ public void animateSwitch() {
+ mShowingHighlight = !mShowingHighlight;
+ mShowingSplash = !mShowingHighlight;
+ final int newColor = mShowingHighlight ? mHighlightColor : mSplashScreenColor;
+ animateBackground(mColorDrawable.getColor(), newColor);
+ animateSplashScreenIcon();
}
/** Animates the highlight indicating the zone is hovered on or not. */
public void setShowingHighlight(boolean showingHighlight) {
- if (mShowingHighlight != showingHighlight) {
- mShowingHighlight = showingHighlight;
- animateHighlightToState();
- }
+ mShowingHighlight = showingHighlight;
+ mShowingSplash = !mShowingHighlight;
+ final int newColor = mShowingHighlight ? mHighlightColor : mSplashScreenColor;
+ animateBackground(Color.TRANSPARENT, newColor);
+ animateSplashScreenIcon();
}
/** Animates the margins around the drop zone to show or hide. */
@@ -228,38 +193,29 @@ public class DropZoneView extends FrameLayout {
animateMarginToState();
}
if (!mShowingMargin) {
- setShowingHighlight(false);
- setShowingSplash(false);
+ mShowingHighlight = false;
+ mShowingSplash = false;
+ animateBackground(mColorDrawable.getColor(), Color.TRANSPARENT);
+ animateSplashScreenIcon();
}
}
- private void animateSplashToState() {
- if (mSplashAnimator != null) {
- mSplashAnimator.cancel();
+ private void animateBackground(int startColor, int endColor) {
+ if (mBackgroundAnimator != null) {
+ mBackgroundAnimator.cancel();
}
- mSplashAnimator = ObjectAnimator.ofInt(mSplashBackgroundDrawable,
- SPLASHSCREEN_ALPHA,
- mSplashBackgroundDrawable.getAlpha(),
- mShowingSplash ? SPLASHSCREEN_ALPHA_INT : 0);
- if (!mShowingSplash) {
- mSplashAnimator.setInterpolator(FAST_OUT_SLOW_IN);
+ mBackgroundAnimator = ObjectAnimator.ofArgb(mColorDrawable,
+ "color",
+ startColor,
+ endColor);
+ if (!mShowingSplash && !mShowingHighlight) {
+ mBackgroundAnimator.setInterpolator(FAST_OUT_SLOW_IN);
}
- mSplashAnimator.start();
- mSplashScreenView.animate().alpha(mShowingSplash ? 1f : 0f).start();
+ mBackgroundAnimator.start();
}
- private void animateHighlightToState() {
- if (mHighlightAnimator != null) {
- mHighlightAnimator.cancel();
- }
- mHighlightAnimator = ObjectAnimator.ofInt(mDropZoneDrawable,
- HIGHLIGHT_ALPHA,
- mDropZoneDrawable.getAlpha(),
- mShowingHighlight ? HIGHLIGHT_ALPHA_INT : 0);
- if (!mShowingHighlight) {
- mHighlightAnimator.setInterpolator(FAST_OUT_SLOW_IN);
- }
- mHighlightAnimator.start();
+ private void animateSplashScreenIcon() {
+ mSplashScreenView.animate().alpha(mShowingSplash ? 1f : 0f).start();
}
private void animateMarginToState() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index e592101d2b20..a2c2f591cde0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -879,6 +879,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
if (mMainUnfoldController != null && mSideUnfoldController != null) {
mMainUnfoldController.onSplitVisibilityChanged(mDividerVisible);
mSideUnfoldController.onSplitVisibilityChanged(mDividerVisible);
+ updateUnfoldBounds();
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
index 13e81bdb3c0b..ddf01a8c5ee9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
@@ -25,6 +25,11 @@ import static android.app.ActivityOptions.ANIM_SCALE_UP;
import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_DOWN;
import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_UP;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_RESOURCE_UPDATED;
+import static android.app.admin.DevicePolicyManager.EXTRA_RESOURCE_TYPE_DRAWABLE;
+import static android.app.admin.DevicePolicyResources.Drawables.Source.PROFILE_SWITCH_ANIMATION;
+import static android.app.admin.DevicePolicyResources.Drawables.Style.OUTLINE;
+import static android.app.admin.DevicePolicyResources.Drawables.WORK_PROFILE_ICON;
import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_JUMPCUT;
import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_ROTATE;
import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS;
@@ -57,16 +62,27 @@ import android.annotation.ColorInt;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityThread;
+import android.app.admin.DevicePolicyManager;
+import android.content.BroadcastReceiver;
import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.graphics.Canvas;
import android.graphics.Color;
+import android.graphics.Insets;
+import android.graphics.Paint;
+import android.graphics.PixelFormat;
import android.graphics.Point;
import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
import android.hardware.HardwareBuffer;
+import android.os.Handler;
import android.os.IBinder;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.util.ArrayMap;
import android.view.Choreographer;
+import android.view.Surface;
import android.view.SurfaceControl;
import android.view.SurfaceSession;
import android.view.WindowManager;
@@ -92,6 +108,8 @@ import com.android.wm.shell.common.TransactionPool;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Consumer;
/** The default handler that handles anything not already handled. */
public class DefaultTransitionHandler implements Transitions.TransitionHandler {
@@ -118,6 +136,7 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
private final ShellExecutor mMainExecutor;
private final ShellExecutor mAnimExecutor;
private final TransitionAnimation mTransitionAnimation;
+ private final DevicePolicyManager mDevicePolicyManager;
private final SurfaceSession mSurfaceSession = new SurfaceSession();
@@ -132,9 +151,24 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
private ScreenRotationAnimation mRotationAnimation;
+ private Drawable mEnterpriseThumbnailDrawable;
+
+ private BroadcastReceiver mEnterpriseResourceUpdatedReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ boolean isDrawable = intent.getBooleanExtra(
+ EXTRA_RESOURCE_TYPE_DRAWABLE, /* default= */ false);
+ if (!isDrawable) {
+ return;
+ }
+ updateEnterpriseThumbnailDrawable();
+ }
+ };
+
DefaultTransitionHandler(@NonNull DisplayController displayController,
@NonNull TransactionPool transactionPool, Context context,
- @NonNull ShellExecutor mainExecutor, @NonNull ShellExecutor animExecutor) {
+ @NonNull ShellExecutor mainExecutor, @NonNull Handler mainHandler,
+ @NonNull ShellExecutor animExecutor) {
mDisplayController = displayController;
mTransactionPool = transactionPool;
mContext = context;
@@ -143,9 +177,23 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
mTransitionAnimation = new TransitionAnimation(context, false /* debug */, Transitions.TAG);
mCurrentUserId = UserHandle.myUserId();
+ mDevicePolicyManager = context.getSystemService(DevicePolicyManager.class);
+ updateEnterpriseThumbnailDrawable();
+ mContext.registerReceiver(
+ mEnterpriseResourceUpdatedReceiver,
+ new IntentFilter(ACTION_DEVICE_POLICY_RESOURCE_UPDATED),
+ /* broadcastPermission = */ null,
+ mainHandler);
+
AttributeCache.init(context);
}
+ private void updateEnterpriseThumbnailDrawable() {
+ mEnterpriseThumbnailDrawable = mDevicePolicyManager.getDrawable(
+ WORK_PROFILE_ICON, OUTLINE, PROFILE_SWITCH_ANIMATION,
+ () -> mContext.getDrawable(R.drawable.ic_corp_badge));
+ }
+
@VisibleForTesting
static boolean isRotationSeamless(@NonNull TransitionInfo info,
DisplayController displayController) {
@@ -290,6 +338,9 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
finishCallback.onTransitionFinished(null /* wct */, null /* wctCB */);
};
+ final List<Consumer<SurfaceControl.Transaction>> postStartTransactionCallbacks =
+ new ArrayList<>();
+
@ColorInt int backgroundColorForTransition = 0;
final int wallpaperTransit = getWallpaperTransitType(info);
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
@@ -361,30 +412,61 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
}
}
- float cornerRadius = 0;
+ final float cornerRadius;
if (a.hasRoundedCorners() && isTask) {
// hasRoundedCorners is currently only enabled for tasks
final Context displayContext =
mDisplayController.getDisplayContext(change.getTaskInfo().displayId);
cornerRadius =
ScreenDecorationsUtils.getWindowCornerRadius(displayContext);
+ } else {
+ cornerRadius = 0;
}
if (a.getShowBackground()) {
- // use the window's background color if provided as the background color for the
- // animation - the top most window with a valid background color and
- // showBackground set takes precedence.
- if (change.getBackgroundColor() != 0) {
+ if (info.getAnimationOptions().getBackgroundColor() != 0) {
+ // If available use the background color provided through AnimationOptions
+ backgroundColorForTransition =
+ info.getAnimationOptions().getBackgroundColor();
+ } else if (change.getBackgroundColor() != 0) {
+ // Otherwise default to the window's background color if provided through
+ // the theme as the background color for the animation - the top most window
+ // with a valid background color and showBackground set takes precedence.
backgroundColorForTransition = change.getBackgroundColor();
}
}
+ boolean delayedEdgeExtension = false;
+ if (!isTask && a.hasExtension()) {
+ if (!Transitions.isOpeningType(change.getMode())) {
+ // Can screenshot now (before startTransaction is applied)
+ edgeExtendWindow(change, a, startTransaction, finishTransaction);
+ } else {
+ // Need to screenshot after startTransaction is applied otherwise activity
+ // may not be visible or ready yet.
+ postStartTransactionCallbacks
+ .add(t -> edgeExtendWindow(change, a, t, finishTransaction));
+ delayedEdgeExtension = true;
+ }
+ }
+
final Rect clipRect = Transitions.isClosingType(change.getMode())
? mRotator.getEndBoundsInStartRotation(change)
: change.getEndAbsBounds();
- startSurfaceAnimation(animations, a, change.getLeash(), onAnimFinish,
- mTransactionPool, mMainExecutor, mAnimExecutor, null /* position */,
- cornerRadius, clipRect);
+
+ if (delayedEdgeExtension) {
+ // If the edge extension needs to happen after the startTransition has been
+ // applied, then we want to only start the animation after the edge extension
+ // postStartTransaction callback has been run
+ postStartTransactionCallbacks.add(t ->
+ startSurfaceAnimation(animations, a, change.getLeash(), onAnimFinish,
+ mTransactionPool, mMainExecutor, mAnimExecutor,
+ null /* position */, cornerRadius, clipRect));
+ } else {
+ startSurfaceAnimation(animations, a, change.getLeash(), onAnimFinish,
+ mTransactionPool, mMainExecutor, mAnimExecutor, null /* position */,
+ cornerRadius, clipRect);
+ }
if (info.getAnimationOptions() != null) {
attachThumbnail(animations, onAnimFinish, change, info.getAnimationOptions(),
@@ -398,7 +480,20 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
startTransaction, finishTransaction);
}
- startTransaction.apply();
+ // postStartTransactionCallbacks require that the start transaction is already
+ // applied to run otherwise they may result in flickers and UI inconsistencies.
+ boolean waitForStartTransactionApply = postStartTransactionCallbacks.size() > 0;
+ startTransaction.apply(waitForStartTransactionApply);
+
+ // Run tasks that require startTransaction to already be applied
+ for (Consumer<SurfaceControl.Transaction> postStartTransactionCallback :
+ postStartTransactionCallbacks) {
+ final SurfaceControl.Transaction t = mTransactionPool.acquire();
+ postStartTransactionCallback.accept(t);
+ t.apply();
+ mTransactionPool.release(t);
+ }
+
mRotator.cleanUp(finishTransaction);
TransitionMetrics.getInstance().reportAnimationStart(transition);
// run finish now in-case there are no animations
@@ -406,6 +501,117 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
return true;
}
+ private void edgeExtendWindow(TransitionInfo.Change change,
+ Animation a, SurfaceControl.Transaction startTransaction,
+ SurfaceControl.Transaction finishTransaction) {
+ final Transformation transformationAtStart = new Transformation();
+ a.getTransformationAt(0, transformationAtStart);
+ final Transformation transformationAtEnd = new Transformation();
+ a.getTransformationAt(1, transformationAtEnd);
+
+ // We want to create an extension surface that is the maximal size and the animation will
+ // take care of cropping any part that overflows.
+ final Insets maxExtensionInsets = Insets.min(
+ transformationAtStart.getInsets(), transformationAtEnd.getInsets());
+
+ final int targetSurfaceHeight = Math.max(change.getStartAbsBounds().height(),
+ change.getEndAbsBounds().height());
+ final int targetSurfaceWidth = Math.max(change.getStartAbsBounds().width(),
+ change.getEndAbsBounds().width());
+ if (maxExtensionInsets.left < 0) {
+ final Rect edgeBounds = new Rect(0, 0, 1, targetSurfaceHeight);
+ final Rect extensionRect = new Rect(0, 0,
+ -maxExtensionInsets.left, targetSurfaceHeight);
+ final int xPos = maxExtensionInsets.left;
+ final int yPos = 0;
+ createExtensionSurface(change.getLeash(), edgeBounds, extensionRect, xPos, yPos,
+ "Left Edge Extension", startTransaction, finishTransaction);
+ }
+
+ if (maxExtensionInsets.top < 0) {
+ final Rect edgeBounds = new Rect(0, 0, targetSurfaceWidth, 1);
+ final Rect extensionRect = new Rect(0, 0,
+ targetSurfaceWidth, -maxExtensionInsets.top);
+ final int xPos = 0;
+ final int yPos = maxExtensionInsets.top;
+ createExtensionSurface(change.getLeash(), edgeBounds, extensionRect, xPos, yPos,
+ "Top Edge Extension", startTransaction, finishTransaction);
+ }
+
+ if (maxExtensionInsets.right < 0) {
+ final Rect edgeBounds = new Rect(targetSurfaceWidth - 1, 0,
+ targetSurfaceWidth, targetSurfaceHeight);
+ final Rect extensionRect = new Rect(0, 0,
+ -maxExtensionInsets.right, targetSurfaceHeight);
+ final int xPos = targetSurfaceWidth;
+ final int yPos = 0;
+ createExtensionSurface(change.getLeash(), edgeBounds, extensionRect, xPos, yPos,
+ "Right Edge Extension", startTransaction, finishTransaction);
+ }
+
+ if (maxExtensionInsets.bottom < 0) {
+ final Rect edgeBounds = new Rect(0, targetSurfaceHeight - 1,
+ targetSurfaceWidth, targetSurfaceHeight);
+ final Rect extensionRect = new Rect(0, 0,
+ targetSurfaceWidth, -maxExtensionInsets.bottom);
+ final int xPos = maxExtensionInsets.left;
+ final int yPos = targetSurfaceHeight;
+ createExtensionSurface(change.getLeash(), edgeBounds, extensionRect, xPos, yPos,
+ "Bottom Edge Extension", startTransaction, finishTransaction);
+ }
+ }
+
+ private SurfaceControl createExtensionSurface(SurfaceControl surfaceToExtend, Rect edgeBounds,
+ Rect extensionRect, int xPos, int yPos, String layerName,
+ SurfaceControl.Transaction startTransaction,
+ SurfaceControl.Transaction finishTransaction) {
+ final SurfaceControl edgeExtensionLayer = new SurfaceControl.Builder()
+ .setName(layerName)
+ .setParent(surfaceToExtend)
+ .setHidden(true)
+ .setCallsite("DefaultTransitionHandler#startAnimation")
+ .setOpaque(true)
+ .setBufferSize(extensionRect.width(), extensionRect.height())
+ .build();
+
+ SurfaceControl.LayerCaptureArgs captureArgs =
+ new SurfaceControl.LayerCaptureArgs.Builder(surfaceToExtend)
+ .setSourceCrop(edgeBounds)
+ .setFrameScale(1)
+ .setPixelFormat(PixelFormat.RGBA_8888)
+ .setChildrenOnly(true)
+ .setAllowProtected(true)
+ .build();
+ final SurfaceControl.ScreenshotHardwareBuffer edgeBuffer =
+ SurfaceControl.captureLayers(captureArgs);
+
+ if (edgeBuffer == null) {
+ ProtoLog.e(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
+ "Failed to capture edge of window.");
+ return null;
+ }
+
+ android.graphics.BitmapShader shader =
+ new android.graphics.BitmapShader(edgeBuffer.asBitmap(),
+ android.graphics.Shader.TileMode.CLAMP,
+ android.graphics.Shader.TileMode.CLAMP);
+ final Paint paint = new Paint();
+ paint.setShader(shader);
+
+ final Surface surface = new Surface(edgeExtensionLayer);
+ Canvas c = surface.lockHardwareCanvas();
+ c.drawRect(extensionRect, paint);
+ surface.unlockCanvasAndPost(c);
+ surface.release();
+
+ startTransaction.setLayer(edgeExtensionLayer, Integer.MIN_VALUE);
+ startTransaction.setPosition(edgeExtensionLayer, xPos, yPos);
+ startTransaction.setVisibility(edgeExtensionLayer, true);
+ finishTransaction.remove(edgeExtensionLayer);
+
+ return edgeExtensionLayer;
+ }
+
private void addBackgroundToTransition(
@NonNull SurfaceControl rootLeash,
@ColorInt int color,
@@ -628,7 +834,7 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
final boolean isClose = Transitions.isClosingType(change.getMode());
if (isOpen) {
if (options.getType() == ANIM_OPEN_CROSS_PROFILE_APPS && isTask) {
- attachCrossProfileThunmbnailAnimation(animations, finishCallback, change,
+ attachCrossProfileThumbnailAnimation(animations, finishCallback, change,
cornerRadius);
} else if (options.getType() == ANIM_THUMBNAIL_SCALE_UP) {
attachThumbnailAnimation(animations, finishCallback, change, options, cornerRadius);
@@ -638,13 +844,14 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
}
}
- private void attachCrossProfileThunmbnailAnimation(@NonNull ArrayList<Animator> animations,
+ private void attachCrossProfileThumbnailAnimation(@NonNull ArrayList<Animator> animations,
@NonNull Runnable finishCallback, TransitionInfo.Change change, float cornerRadius) {
- final int thumbnailDrawableRes = change.getTaskInfo().userId == mCurrentUserId
- ? R.drawable.ic_account_circle : R.drawable.ic_corp_badge;
final Rect bounds = change.getEndAbsBounds();
+ // Show the right drawable depending on the user we're transitioning to.
+ final Drawable thumbnailDrawable = change.getTaskInfo().userId == mCurrentUserId
+ ? mContext.getDrawable(R.drawable.ic_account_circle) : mEnterpriseThumbnailDrawable;
final HardwareBuffer thumbnail = mTransitionAnimation.createCrossProfileAppsThumbnail(
- thumbnailDrawableRes, bounds);
+ thumbnailDrawable, bounds);
if (thumbnail == null) {
return;
}
@@ -732,9 +939,17 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
}
t.setMatrix(leash, transformation.getMatrix(), matrix);
t.setAlpha(leash, transformation.getAlpha());
+
+ Insets extensionInsets = Insets.min(transformation.getInsets(), Insets.NONE);
+ if (!extensionInsets.equals(Insets.NONE) && clipRect != null && !clipRect.isEmpty()) {
+ // Clip out any overflowing edge extension
+ clipRect.inset(extensionInsets);
+ t.setCrop(leash, clipRect);
+ }
+
if (anim.hasRoundedCorners() && cornerRadius > 0 && clipRect != null) {
// We can only apply rounded corner if a crop is set
- t.setWindowCrop(leash, clipRect);
+ t.setCrop(leash, clipRect);
t.setCornerRadius(leash, cornerRadius);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
index 33a98b2fd80e..86b73fc30ca8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
@@ -33,6 +33,7 @@ import android.annotation.Nullable;
import android.content.ContentResolver;
import android.content.Context;
import android.database.ContentObserver;
+import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.SystemProperties;
@@ -123,7 +124,8 @@ public class Transitions implements RemoteCallable<Transitions> {
public Transitions(@NonNull WindowOrganizer organizer, @NonNull TransactionPool pool,
@NonNull DisplayController displayController, @NonNull Context context,
- @NonNull ShellExecutor mainExecutor, @NonNull ShellExecutor animExecutor) {
+ @NonNull ShellExecutor mainExecutor, @NonNull Handler mainHandler,
+ @NonNull ShellExecutor animExecutor) {
mOrganizer = organizer;
mContext = context;
mMainExecutor = mainExecutor;
@@ -132,7 +134,7 @@ public class Transitions implements RemoteCallable<Transitions> {
mPlayerImpl = new TransitionPlayerImpl();
// The very last handler (0 in the list) should be the default one.
mHandlers.add(new DefaultTransitionHandler(displayController, pool, context, mainExecutor,
- animExecutor));
+ mainHandler, animExecutor));
// Next lowest priority is remote transitions.
mRemoteTransitionHandler = new RemoteTransitionHandler(mainExecutor);
mHandlers.add(mRemoteTransitionHandler);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
index 825320b4e784..a6caefe6d3e7 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
@@ -363,6 +363,45 @@ public class ShellTaskOrganizerTests {
}
@Test
+ public void testOnEligibleForLetterboxEducationActivityChanged() {
+ final RunningTaskInfo taskInfo1 = createTaskInfo(12, WINDOWING_MODE_FULLSCREEN);
+ taskInfo1.displayId = DEFAULT_DISPLAY;
+ taskInfo1.topActivityEligibleForLetterboxEducation = false;
+ final TrackingTaskListener taskListener = new TrackingTaskListener();
+ mOrganizer.addListenerForType(taskListener, TASK_LISTENER_TYPE_FULLSCREEN);
+ mOrganizer.onTaskAppeared(taskInfo1, null);
+
+ // Task listener sent to compat UI is null if top activity isn't eligible for letterbox
+ // education.
+ verify(mCompatUI).onCompatInfoChanged(taskInfo1, null /* taskListener */);
+
+ // Task listener is non-null if top activity is eligible for letterbox education and task
+ // is visible.
+ clearInvocations(mCompatUI);
+ final RunningTaskInfo taskInfo2 =
+ createTaskInfo(taskInfo1.taskId, WINDOWING_MODE_FULLSCREEN);
+ taskInfo2.displayId = taskInfo1.displayId;
+ taskInfo2.topActivityEligibleForLetterboxEducation = true;
+ taskInfo2.isVisible = true;
+ mOrganizer.onTaskInfoChanged(taskInfo2);
+ verify(mCompatUI).onCompatInfoChanged(taskInfo2, taskListener);
+
+ // Task listener is null if task is invisible.
+ clearInvocations(mCompatUI);
+ final RunningTaskInfo taskInfo3 =
+ createTaskInfo(taskInfo1.taskId, WINDOWING_MODE_FULLSCREEN);
+ taskInfo3.displayId = taskInfo1.displayId;
+ taskInfo3.topActivityEligibleForLetterboxEducation = true;
+ taskInfo3.isVisible = false;
+ mOrganizer.onTaskInfoChanged(taskInfo3);
+ verify(mCompatUI).onCompatInfoChanged(taskInfo3, null /* taskListener */);
+
+ clearInvocations(mCompatUI);
+ mOrganizer.onTaskVanished(taskInfo1);
+ verify(mCompatUI).onCompatInfoChanged(taskInfo1, null /* taskListener */);
+ }
+
+ @Test
public void testOnCameraCompatActivityChanged() {
final RunningTaskInfo taskInfo1 = createTaskInfo(1, WINDOWING_MODE_FULLSCREEN);
taskInfo1.displayId = DEFAULT_DISPLAY;
@@ -375,7 +414,7 @@ public class ShellTaskOrganizerTests {
// compat control.
verify(mCompatUI).onCompatInfoChanged(taskInfo1, null /* taskListener */);
- // Task linster is non-null when request a camera compat control for a visible task.
+ // Task listener is non-null when request a camera compat control for a visible task.
clearInvocations(mCompatUI);
final RunningTaskInfo taskInfo2 =
createTaskInfo(taskInfo1.taskId, taskInfo1.getWindowingMode());
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 e39171343bb9..0f4a06f22986 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
@@ -54,7 +54,9 @@ import static org.mockito.Mockito.verify;
import android.app.ActivityManager.RunningTaskInfo;
import android.content.Context;
import android.os.Binder;
+import android.os.Handler;
import android.os.IBinder;
+import android.os.Looper;
import android.os.RemoteException;
import android.view.IDisplayWindowListener;
import android.view.IWindowManager;
@@ -84,8 +86,6 @@ import com.android.wm.shell.common.TransactionPool;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.invocation.InvocationOnMock;
-import org.mockito.stubbing.Answer;
import java.util.ArrayList;
@@ -106,6 +106,7 @@ public class ShellTransitionTests {
private final TestShellExecutor mMainExecutor = new TestShellExecutor();
private final ShellExecutor mAnimExecutor = new TestShellExecutor();
private final TestTransitionHandler mDefaultHandler = new TestTransitionHandler();
+ private final Handler mMainHandler = new Handler(Looper.getMainLooper());
@Before
public void setUp() {
@@ -752,7 +753,7 @@ public class ShellTransitionTests {
private Transitions createTestTransitions() {
return new Transitions(mOrganizer, mTransactionPool, createTestDisplayController(),
- mContext, mMainExecutor, mAnimExecutor);
+ mContext, mMainExecutor, mMainHandler, mAnimExecutor);
}
//
// private class TestDisplayController extends DisplayController {
diff --git a/libs/hwui/HardwareBitmapUploader.cpp b/libs/hwui/HardwareBitmapUploader.cpp
index db3a1081e32c..dd272cd5ff7d 100644
--- a/libs/hwui/HardwareBitmapUploader.cpp
+++ b/libs/hwui/HardwareBitmapUploader.cpp
@@ -287,29 +287,29 @@ private:
std::mutex mVkLock;
};
-bool HardwareBitmapUploader::hasFP16Support() {
- static std::once_flag sOnce;
- static bool hasFP16Support = false;
-
- // Gralloc shouldn't let us create a USAGE_HW_TEXTURE if GLES is unable to consume it, so
- // we don't need to double-check the GLES version/extension.
- std::call_once(sOnce, []() {
- AHardwareBuffer_Desc desc = {
- .width = 1,
- .height = 1,
- .layers = 1,
- .format = AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT,
- .usage = AHARDWAREBUFFER_USAGE_CPU_READ_NEVER |
- AHARDWAREBUFFER_USAGE_CPU_WRITE_NEVER |
- AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE,
- };
- UniqueAHardwareBuffer buffer = allocateAHardwareBuffer(desc);
- hasFP16Support = buffer != nullptr;
- });
+static bool checkSupport(AHardwareBuffer_Format format) {
+ AHardwareBuffer_Desc desc = {
+ .width = 1,
+ .height = 1,
+ .layers = 1,
+ .format = format,
+ .usage = AHARDWAREBUFFER_USAGE_CPU_READ_NEVER | AHARDWAREBUFFER_USAGE_CPU_WRITE_NEVER |
+ AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE,
+ };
+ UniqueAHardwareBuffer buffer = allocateAHardwareBuffer(desc);
+ return buffer != nullptr;
+}
+bool HardwareBitmapUploader::hasFP16Support() {
+ static bool hasFP16Support = checkSupport(AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT);
return hasFP16Support;
}
+bool HardwareBitmapUploader::has1010102Support() {
+ static bool has101012Support = checkSupport(AHARDWAREBUFFER_FORMAT_R10G10B10A2_UNORM);
+ return has101012Support;
+}
+
static FormatInfo determineFormat(const SkBitmap& skBitmap, bool usingGL) {
FormatInfo formatInfo;
switch (skBitmap.info().colorType()) {
@@ -350,6 +350,19 @@ static FormatInfo determineFormat(const SkBitmap& skBitmap, bool usingGL) {
formatInfo.type = GL_UNSIGNED_BYTE;
formatInfo.vkFormat = VK_FORMAT_R8G8B8A8_UNORM;
break;
+ case kRGBA_1010102_SkColorType:
+ formatInfo.isSupported = HardwareBitmapUploader::has1010102Support();
+ if (formatInfo.isSupported) {
+ formatInfo.type = GL_UNSIGNED_INT_2_10_10_10_REV;
+ formatInfo.bufferFormat = AHARDWAREBUFFER_FORMAT_R10G10B10A2_UNORM;
+ formatInfo.vkFormat = VK_FORMAT_A2B10G10R10_UNORM_PACK32;
+ } else {
+ formatInfo.type = GL_UNSIGNED_BYTE;
+ formatInfo.bufferFormat = AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM;
+ formatInfo.vkFormat = VK_FORMAT_R8G8B8A8_UNORM;
+ }
+ formatInfo.format = GL_RGBA;
+ break;
default:
ALOGW("unable to create hardware bitmap of colortype: %d", skBitmap.info().colorType());
formatInfo.valid = false;
diff --git a/libs/hwui/HardwareBitmapUploader.h b/libs/hwui/HardwareBitmapUploader.h
index ad7a95a4fa03..34f43cd8a198 100644
--- a/libs/hwui/HardwareBitmapUploader.h
+++ b/libs/hwui/HardwareBitmapUploader.h
@@ -29,10 +29,12 @@ public:
#ifdef __ANDROID__
static bool hasFP16Support();
+ static bool has1010102Support();
#else
static bool hasFP16Support() {
return true;
}
+ static bool has1010102Support() { return true; }
#endif
};
diff --git a/libs/hwui/apex/android_bitmap.cpp b/libs/hwui/apex/android_bitmap.cpp
index 3780ba072308..bc6bc456ba5a 100644
--- a/libs/hwui/apex/android_bitmap.cpp
+++ b/libs/hwui/apex/android_bitmap.cpp
@@ -57,6 +57,8 @@ static AndroidBitmapFormat getFormat(const SkImageInfo& info) {
return ANDROID_BITMAP_FORMAT_A_8;
case kRGBA_F16_SkColorType:
return ANDROID_BITMAP_FORMAT_RGBA_F16;
+ case kRGBA_1010102_SkColorType:
+ return ANDROID_BITMAP_FORMAT_RGBA_1010102;
default:
return ANDROID_BITMAP_FORMAT_NONE;
}
@@ -74,6 +76,8 @@ static SkColorType getColorType(AndroidBitmapFormat format) {
return kAlpha_8_SkColorType;
case ANDROID_BITMAP_FORMAT_RGBA_F16:
return kRGBA_F16_SkColorType;
+ case ANDROID_BITMAP_FORMAT_RGBA_1010102:
+ return kRGBA_1010102_SkColorType;
default:
return kUnknown_SkColorType;
}
@@ -249,6 +253,9 @@ int ABitmap_compress(const AndroidBitmapInfo* info, ADataSpace dataSpace, const
case ANDROID_BITMAP_FORMAT_RGBA_F16:
colorType = kRGBA_F16_SkColorType;
break;
+ case ANDROID_BITMAP_FORMAT_RGBA_1010102:
+ colorType = kRGBA_1010102_SkColorType;
+ break;
default:
return ANDROID_BITMAP_RESULT_BAD_PARAMETER;
}
diff --git a/libs/hwui/hwui/ImageDecoder.cpp b/libs/hwui/hwui/ImageDecoder.cpp
index fc542c81a597..dd68f825b61d 100644
--- a/libs/hwui/hwui/ImageDecoder.cpp
+++ b/libs/hwui/hwui/ImageDecoder.cpp
@@ -159,6 +159,8 @@ bool ImageDecoder::setOutColorType(SkColorType colorType) {
break;
case kRGBA_F16_SkColorType:
break;
+ case kRGBA_1010102_SkColorType:
+ break;
default:
return false;
}
diff --git a/libs/hwui/jni/BitmapRegionDecoder.cpp b/libs/hwui/jni/BitmapRegionDecoder.cpp
index 4cc05ef6f13b..1c20415dcc8f 100644
--- a/libs/hwui/jni/BitmapRegionDecoder.cpp
+++ b/libs/hwui/jni/BitmapRegionDecoder.cpp
@@ -137,9 +137,16 @@ static jobject nativeDecodeRegion(JNIEnv* env, jobject, jlong brdHandle, jint in
auto* brd = reinterpret_cast<skia::BitmapRegionDecoder*>(brdHandle);
SkColorType decodeColorType = brd->computeOutputColorType(colorType);
- if (decodeColorType == kRGBA_F16_SkColorType && isHardware &&
+
+ if (isHardware) {
+ if (decodeColorType == kRGBA_F16_SkColorType &&
!uirenderer::HardwareBitmapUploader::hasFP16Support()) {
- decodeColorType = kN32_SkColorType;
+ decodeColorType = kN32_SkColorType;
+ }
+ if (decodeColorType == kRGBA_1010102_SkColorType &&
+ !uirenderer::HardwareBitmapUploader::has1010102Support()) {
+ decodeColorType = kN32_SkColorType;
+ }
}
// Set up the pixel allocator
diff --git a/libs/hwui/jni/Graphics.cpp b/libs/hwui/jni/Graphics.cpp
index 77f46beb2100..33669ac0a34e 100644
--- a/libs/hwui/jni/Graphics.cpp
+++ b/libs/hwui/jni/Graphics.cpp
@@ -365,6 +365,8 @@ jint GraphicsJNI::colorTypeToLegacyBitmapConfig(SkColorType colorType) {
return kRGB_565_LegacyBitmapConfig;
case kAlpha_8_SkColorType:
return kA8_LegacyBitmapConfig;
+ case kRGBA_1010102_SkColorType:
+ return kRGBA_1010102_LegacyBitmapConfig;
case kUnknown_SkColorType:
default:
break;
@@ -374,14 +376,10 @@ jint GraphicsJNI::colorTypeToLegacyBitmapConfig(SkColorType colorType) {
SkColorType GraphicsJNI::legacyBitmapConfigToColorType(jint legacyConfig) {
const uint8_t gConfig2ColorType[] = {
- kUnknown_SkColorType,
- kAlpha_8_SkColorType,
- kUnknown_SkColorType, // Previously kIndex_8_SkColorType,
- kRGB_565_SkColorType,
- kARGB_4444_SkColorType,
- kN32_SkColorType,
- kRGBA_F16_SkColorType,
- kN32_SkColorType
+ kUnknown_SkColorType, kAlpha_8_SkColorType,
+ kUnknown_SkColorType, // Previously kIndex_8_SkColorType,
+ kRGB_565_SkColorType, kARGB_4444_SkColorType, kN32_SkColorType,
+ kRGBA_F16_SkColorType, kN32_SkColorType, kRGBA_1010102_SkColorType,
};
if (legacyConfig < 0 || legacyConfig > kLastEnum_LegacyBitmapConfig) {
@@ -399,15 +397,12 @@ AndroidBitmapFormat GraphicsJNI::getFormatFromConfig(JNIEnv* env, jobject jconfi
jint javaConfigId = env->GetIntField(jconfig, gBitmapConfig_nativeInstanceID);
const AndroidBitmapFormat config2BitmapFormat[] = {
- ANDROID_BITMAP_FORMAT_NONE,
- ANDROID_BITMAP_FORMAT_A_8,
- ANDROID_BITMAP_FORMAT_NONE, // Previously Config.Index_8
- ANDROID_BITMAP_FORMAT_RGB_565,
- ANDROID_BITMAP_FORMAT_RGBA_4444,
- ANDROID_BITMAP_FORMAT_RGBA_8888,
- ANDROID_BITMAP_FORMAT_RGBA_F16,
- ANDROID_BITMAP_FORMAT_NONE // Congfig.HARDWARE
- };
+ ANDROID_BITMAP_FORMAT_NONE, ANDROID_BITMAP_FORMAT_A_8,
+ ANDROID_BITMAP_FORMAT_NONE, // Previously Config.Index_8
+ ANDROID_BITMAP_FORMAT_RGB_565, ANDROID_BITMAP_FORMAT_RGBA_4444,
+ ANDROID_BITMAP_FORMAT_RGBA_8888, ANDROID_BITMAP_FORMAT_RGBA_F16,
+ ANDROID_BITMAP_FORMAT_NONE, // Congfig.HARDWARE
+ ANDROID_BITMAP_FORMAT_RGBA_1010102};
return config2BitmapFormat[javaConfigId];
}
@@ -430,6 +425,9 @@ jobject GraphicsJNI::getConfigFromFormat(JNIEnv* env, AndroidBitmapFormat format
case ANDROID_BITMAP_FORMAT_RGBA_F16:
configId = kRGBA_16F_LegacyBitmapConfig;
break;
+ case ANDROID_BITMAP_FORMAT_RGBA_1010102:
+ configId = kRGBA_1010102_LegacyBitmapConfig;
+ break;
default:
break;
}
diff --git a/libs/hwui/jni/GraphicsJNI.h b/libs/hwui/jni/GraphicsJNI.h
index ba407f2164de..085a905abaf8 100644
--- a/libs/hwui/jni/GraphicsJNI.h
+++ b/libs/hwui/jni/GraphicsJNI.h
@@ -34,16 +34,17 @@ public:
// This enum must keep these int values, to match the int values
// in the java Bitmap.Config enum.
enum LegacyBitmapConfig {
- kNo_LegacyBitmapConfig = 0,
- kA8_LegacyBitmapConfig = 1,
- kIndex8_LegacyBitmapConfig = 2,
- kRGB_565_LegacyBitmapConfig = 3,
- kARGB_4444_LegacyBitmapConfig = 4,
- kARGB_8888_LegacyBitmapConfig = 5,
- kRGBA_16F_LegacyBitmapConfig = 6,
- kHardware_LegacyBitmapConfig = 7,
-
- kLastEnum_LegacyBitmapConfig = kHardware_LegacyBitmapConfig
+ kNo_LegacyBitmapConfig = 0,
+ kA8_LegacyBitmapConfig = 1,
+ kIndex8_LegacyBitmapConfig = 2,
+ kRGB_565_LegacyBitmapConfig = 3,
+ kARGB_4444_LegacyBitmapConfig = 4,
+ kARGB_8888_LegacyBitmapConfig = 5,
+ kRGBA_16F_LegacyBitmapConfig = 6,
+ kHardware_LegacyBitmapConfig = 7,
+ kRGBA_1010102_LegacyBitmapConfig = 8,
+
+ kLastEnum_LegacyBitmapConfig = kRGBA_1010102_LegacyBitmapConfig
};
static void setJavaVM(JavaVM* javaVM);
diff --git a/libs/hwui/renderthread/DrawFrameTask.h b/libs/hwui/renderthread/DrawFrameTask.h
index 8ad8abcff2ed..25ed935b7a76 100644
--- a/libs/hwui/renderthread/DrawFrameTask.h
+++ b/libs/hwui/renderthread/DrawFrameTask.h
@@ -16,19 +16,18 @@
#ifndef DRAWFRAMETASK_H
#define DRAWFRAMETASK_H
-#include <optional>
-#include <vector>
-
-#include <performance_hint_private.h>
+#include <android/performance_hint.h>
#include <utils/Condition.h>
#include <utils/Mutex.h>
#include <utils/StrongPointer.h>
-#include "RenderTask.h"
+#include <optional>
+#include <vector>
#include "../FrameInfo.h"
#include "../Rect.h"
#include "../TreeInfo.h"
+#include "RenderTask.h"
namespace android {
namespace uirenderer {
diff --git a/libs/tracingproxy/Android.bp b/libs/tracingproxy/Android.bp
index 7126bfac773d..23d107b56340 100644
--- a/libs/tracingproxy/Android.bp
+++ b/libs/tracingproxy/Android.bp
@@ -37,6 +37,7 @@ cc_library_shared {
srcs: [
":ITracingServiceProxy.aidl",
+ ":TraceReportParams.aidl",
],
shared_libs: [
diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl
index 8054fd452022..f4e965f422c0 100644
--- a/location/java/android/location/ILocationManager.aidl
+++ b/location/java/android/location/ILocationManager.aidl
@@ -125,8 +125,8 @@ interface ILocationManager
boolean isAdasGnssLocationEnabledForUser(int userId);
void setAdasGnssLocationEnabledForUser(boolean enabled, int userId);
- boolean isAutoGnssSuspended();
- void setAutoGnssSuspended(boolean suspended);
+ boolean isAutomotiveGnssSuspended();
+ void setAutomotiveGnssSuspended(boolean suspended);
void addTestProvider(String name, in ProviderProperties properties,
in List<String> locationTags, String packageName, @nullable String attributionTag);
diff --git a/location/java/android/location/LastLocationRequest.java b/location/java/android/location/LastLocationRequest.java
index 73c5c826584f..fe0a14f37cb6 100644
--- a/location/java/android/location/LastLocationRequest.java
+++ b/location/java/android/location/LastLocationRequest.java
@@ -16,6 +16,9 @@
package android.location;
+import static android.Manifest.permission.LOCATION_BYPASS;
+import static android.Manifest.permission.WRITE_SECURE_SETTINGS;
+
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
@@ -220,8 +223,9 @@ public final class LastLocationRequest implements Parcelable {
*
* @hide
*/
+ // TODO: remove WRITE_SECURE_SETTINGS.
@SystemApi
- @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
+ @RequiresPermission(anyOf = {WRITE_SECURE_SETTINGS, LOCATION_BYPASS})
public @NonNull LastLocationRequest.Builder setAdasGnssBypass(boolean adasGnssBypass) {
mAdasGnssBypass = adasGnssBypass;
return this;
@@ -238,8 +242,9 @@ public final class LastLocationRequest implements Parcelable {
*
* @hide
*/
+ // TODO: remove WRITE_SECURE_SETTINGS.
@SystemApi
- @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
+ @RequiresPermission(anyOf = {WRITE_SECURE_SETTINGS, LOCATION_BYPASS})
public @NonNull Builder setLocationSettingsIgnored(boolean locationSettingsIgnored) {
mLocationSettingsIgnored = locationSettingsIgnored;
return this;
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index 9109a18f120e..59c989b7f01e 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -18,6 +18,7 @@ package android.location;
import static android.Manifest.permission.ACCESS_COARSE_LOCATION;
import static android.Manifest.permission.ACCESS_FINE_LOCATION;
+import static android.Manifest.permission.LOCATION_BYPASS;
import static android.Manifest.permission.LOCATION_HARDWARE;
import static android.Manifest.permission.WRITE_SECURE_SETTINGS;
import static android.location.LocationRequest.createFromDeprecatedCriteria;
@@ -678,8 +679,9 @@ public class LocationManager {
*
* @hide
*/
+ // TODO: remove WRITE_SECURE_SETTINGS.
@SystemApi
- @RequiresPermission(WRITE_SECURE_SETTINGS)
+ @RequiresPermission(anyOf = {WRITE_SECURE_SETTINGS, LOCATION_BYPASS})
public void setAdasGnssLocationEnabled(boolean enabled) {
try {
mService.setAdasGnssLocationEnabledForUser(enabled, mContext.getUser().getIdentifier());
@@ -758,45 +760,41 @@ public class LocationManager {
}
/**
- * Set whether GNSS requests are suspended on the device.
+ * Set whether GNSS requests are suspended on the automotive device.
*
- * This method was added to help support power management use cases on automotive devices. More
- * specifically, it is being added to fix a suspend to RAM issue where the SoC can't go into
- * a lower power state when applications are actively requesting GNSS updates.
+ * For devices where GNSS prevents the system from going into a low power state, GNSS should
+ * be suspended right before going into the lower power state and resumed right after the device
+ * wakes up.
*
- * Ideally, the issue should be fixed at a lower layer in the stack, but this API introduces a
- * workaround in the platform layer. This API allows car specific services to halt GNSS requests
- * based on changes to the car power policy, which will in turn enable the device to go into
- * suspend.
+ * This method disables GNSS and should only be used for power management use cases such as
+ * suspend-to-RAM or suspend-to-disk.
*
* @hide
*/
- @SystemApi
- @RequiresPermission(android.Manifest.permission.AUTOMOTIVE_GNSS_CONTROLS)
- public void setAutoGnssSuspended(boolean suspended) {
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ @RequiresFeature(PackageManager.FEATURE_AUTOMOTIVE)
+ @RequiresPermission(android.Manifest.permission.CONTROL_AUTOMOTIVE_GNSS)
+ public void setAutomotiveGnssSuspended(boolean suspended) {
try {
- mService.setAutoGnssSuspended(suspended);
+ mService.setAutomotiveGnssSuspended(suspended);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
- * Return whether GNSS requests are suspended or not.
- *
- * This method was added to help support power management use cases on automotive devices. More
- * specifically, it is being added as part of the fix for a suspend to RAM issue where the SoC
- * can't go into a lower power state when applications are actively requesting GNSS updates.
+ * Return whether GNSS requests are suspended on the automotive device.
*
* @return true if GNSS requests are suspended and false if they aren't.
*
* @hide
*/
- @SystemApi
- @RequiresPermission(android.Manifest.permission.AUTOMOTIVE_GNSS_CONTROLS)
- public boolean isAutoGnssSuspended() {
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ @RequiresFeature(PackageManager.FEATURE_AUTOMOTIVE)
+ @RequiresPermission(android.Manifest.permission.CONTROL_AUTOMOTIVE_GNSS)
+ public boolean isAutomotiveGnssSuspended() {
try {
- return mService.isAutoGnssSuspended();
+ return mService.isAutomotiveGnssSuspended();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/location/java/android/location/LocationRequest.java b/location/java/android/location/LocationRequest.java
index 587222a4df43..59f4f5e8c19e 100644
--- a/location/java/android/location/LocationRequest.java
+++ b/location/java/android/location/LocationRequest.java
@@ -16,6 +16,9 @@
package android.location;
+import static android.Manifest.permission.LOCATION_BYPASS;
+import static android.Manifest.permission.WRITE_SECURE_SETTINGS;
+
import static java.lang.Math.max;
import static java.lang.Math.min;
@@ -662,9 +665,10 @@ public final class LocationRequest implements Parcelable {
* @hide
* @deprecated LocationRequests should be treated as immutable.
*/
+ // TODO: remove WRITE_SECURE_SETTINGS.
@SystemApi
@Deprecated
- @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
+ @RequiresPermission(anyOf = {WRITE_SECURE_SETTINGS, LOCATION_BYPASS})
public @NonNull LocationRequest setLocationSettingsIgnored(boolean locationSettingsIgnored) {
mBypass = locationSettingsIgnored;
return this;
@@ -1132,8 +1136,9 @@ public final class LocationRequest implements Parcelable {
*
* @hide
*/
+ // TODO: remove WRITE_SECURE_SETTINGS
@SystemApi
- @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
+ @RequiresPermission(anyOf = {WRITE_SECURE_SETTINGS, LOCATION_BYPASS})
public @NonNull Builder setAdasGnssBypass(boolean adasGnssBypass) {
mAdasGnssBypass = adasGnssBypass;
return this;
@@ -1150,8 +1155,9 @@ public final class LocationRequest implements Parcelable {
*
* @hide
*/
+ // TODO: remove WRITE_SECURE_SETTINGS
@SystemApi
- @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
+ @RequiresPermission(anyOf = {WRITE_SECURE_SETTINGS, LOCATION_BYPASS})
public @NonNull Builder setLocationSettingsIgnored(boolean locationSettingsIgnored) {
mBypass = locationSettingsIgnored;
return this;
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index a2704f9a67fd..15a398de9021 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -181,6 +181,22 @@ public class AudioManager {
public static final String VOLUME_CHANGED_ACTION = "android.media.VOLUME_CHANGED_ACTION";
/**
+ * @hide Broadcast intent when the volume for a particular stream type changes.
+ * Includes the stream, the new volume and previous volumes.
+ * Notes:
+ * - for internal platform use only, do not make public,
+ * - never used for "remote" volume changes
+ *
+ * @see #EXTRA_VOLUME_STREAM_TYPE
+ * @see #EXTRA_VOLUME_STREAM_VALUE
+ * @see #EXTRA_PREV_VOLUME_STREAM_VALUE
+ */
+ @SystemApi
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ @SuppressLint("ActionValue")
+ public static final String ACTION_VOLUME_CHANGED = "android.media.VOLUME_CHANGED_ACTION";
+
+ /**
* @hide Broadcast intent when the devices for a particular stream type changes.
* Includes the stream, the new devices and previous devices.
* Notes:
@@ -244,7 +260,8 @@ public class AudioManager {
/**
* @hide The stream type for the volume changed intent.
*/
- @UnsupportedAppUsage
+ @SystemApi
+ @SuppressLint("ActionValue")
public static final String EXTRA_VOLUME_STREAM_TYPE = "android.media.EXTRA_VOLUME_STREAM_TYPE";
/**
@@ -261,7 +278,8 @@ public class AudioManager {
/**
* @hide The volume associated with the stream for the volume changed intent.
*/
- @UnsupportedAppUsage
+ @SystemApi
+ @SuppressLint("ActionValue")
public static final String EXTRA_VOLUME_STREAM_VALUE =
"android.media.EXTRA_VOLUME_STREAM_VALUE";
@@ -368,7 +386,7 @@ public class AudioManager {
public static final int STREAM_NOTIFICATION = AudioSystem.STREAM_NOTIFICATION;
/** @hide Used to identify the volume of audio streams for phone calls when connected
* to bluetooth */
- @UnsupportedAppUsage
+ @SystemApi
public static final int STREAM_BLUETOOTH_SCO = AudioSystem.STREAM_BLUETOOTH_SCO;
/** @hide Used to identify the volume of audio streams for enforced system sounds
* in certain countries (e.g camera in Japan) */
@@ -544,6 +562,7 @@ public class AudioManager {
* Indicates the volume set/adjust call is for Bluetooth absolute volume
* @hide
*/
+ @SystemApi
public static final int FLAG_BLUETOOTH_ABS_VOLUME = 1 << 6;
/**
diff --git a/media/java/android/media/BtProfileConnectionInfo.java b/media/java/android/media/BtProfileConnectionInfo.java
index d1bb41e70b54..88b9777e911d 100644
--- a/media/java/android/media/BtProfileConnectionInfo.java
+++ b/media/java/android/media/BtProfileConnectionInfo.java
@@ -15,39 +15,25 @@
*/
package android.media;
-import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.bluetooth.BluetoothProfile;
import android.os.Parcel;
import android.os.Parcelable;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
/**
* Contains information about Bluetooth profile connection state changed
* {@hide}
*/
@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
public final class BtProfileConnectionInfo implements Parcelable {
- /** @hide */
- @IntDef({
- BluetoothProfile.A2DP,
- BluetoothProfile.A2DP_SINK,
- BluetoothProfile.HEADSET, // Can only be set by BtHelper
- BluetoothProfile.HEARING_AID,
- BluetoothProfile.LE_AUDIO,
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface BtProfile {}
-
- private final @BtProfile int mProfile;
+
+ private final int mProfile;
private final boolean mSupprNoisy;
private final int mVolume;
private final boolean mIsLeOutput;
- private BtProfileConnectionInfo(@BtProfile int profile, boolean suppressNoisyIntent, int volume,
+ private BtProfileConnectionInfo(int profile, boolean suppressNoisyIntent, int volume,
boolean isLeOutput) {
mProfile = profile;
mSupprNoisy = suppressNoisyIntent;
@@ -59,7 +45,7 @@ public final class BtProfileConnectionInfo implements Parcelable {
* Constructor used by BtHelper when a profile is connected
* {@hide}
*/
- public BtProfileConnectionInfo(@BtProfile int profile) {
+ public BtProfileConnectionInfo(int profile) {
this(profile, false, -1, false);
}
@@ -142,7 +128,7 @@ public final class BtProfileConnectionInfo implements Parcelable {
/**
* @return The profile connection
*/
- public @BtProfile int getProfile() {
+ public int getProfile() {
return mProfile;
}
diff --git a/media/java/android/media/IMediaRouter2Manager.aidl b/media/java/android/media/IMediaRouter2Manager.aidl
index 5113dc2058e0..71dc2a781ba9 100644
--- a/media/java/android/media/IMediaRouter2Manager.aidl
+++ b/media/java/android/media/IMediaRouter2Manager.aidl
@@ -18,6 +18,7 @@ package android.media;
import android.media.MediaRoute2ProviderInfo;
import android.media.MediaRoute2Info;
+import android.media.RouteDiscoveryPreference;
import android.media.RoutingSessionInfo;
/**
@@ -27,7 +28,8 @@ oneway interface IMediaRouter2Manager {
void notifySessionCreated(int requestId, in RoutingSessionInfo session);
void notifySessionUpdated(in RoutingSessionInfo session);
void notifySessionReleased(in RoutingSessionInfo session);
- void notifyPreferredFeaturesChanged(String packageName, in List<String> preferredFeatures);
+ void notifyDiscoveryPreferenceChanged(String packageName,
+ in RouteDiscoveryPreference discoveryPreference);
void notifyRoutesAdded(in List<MediaRoute2Info> routes);
void notifyRoutesRemoved(in List<MediaRoute2Info> routes);
void notifyRoutesChanged(in List<MediaRoute2Info> routes);
diff --git a/media/java/android/media/MediaRoute2Info.java b/media/java/android/media/MediaRoute2Info.java
index 2427fa64562d..ee0293d629b1 100644
--- a/media/java/android/media/MediaRoute2Info.java
+++ b/media/java/android/media/MediaRoute2Info.java
@@ -34,6 +34,7 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
+import java.util.Set;
/**
* Describes the properties of a route.
@@ -340,10 +341,12 @@ public final class MediaRoute2Info implements Parcelable {
@ConnectionState
final int mConnectionState;
final String mClientPackageName;
+ final String mPackageName;
final int mVolumeHandling;
final int mVolumeMax;
final int mVolume;
final String mAddress;
+ final Set<String> mDeduplicationIds;
final Bundle mExtras;
final String mProviderId;
@@ -357,10 +360,12 @@ public final class MediaRoute2Info implements Parcelable {
mDescription = builder.mDescription;
mConnectionState = builder.mConnectionState;
mClientPackageName = builder.mClientPackageName;
+ mPackageName = builder.mPackageName;
mVolumeHandling = builder.mVolumeHandling;
mVolumeMax = builder.mVolumeMax;
mVolume = builder.mVolume;
mAddress = builder.mAddress;
+ mDeduplicationIds = builder.mDeduplicationIds;
mExtras = builder.mExtras;
mProviderId = builder.mProviderId;
}
@@ -375,10 +380,12 @@ public final class MediaRoute2Info implements Parcelable {
mDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
mConnectionState = in.readInt();
mClientPackageName = in.readString();
+ mPackageName = in.readString();
mVolumeHandling = in.readInt();
mVolumeMax = in.readInt();
mVolume = in.readInt();
mAddress = in.readString();
+ mDeduplicationIds = Set.of(in.readStringArray());
mExtras = in.readBundle();
mProviderId = in.readString();
}
@@ -486,6 +493,17 @@ public final class MediaRoute2Info implements Parcelable {
}
/**
+ * Gets the package name of the provider that published the route.
+ * <p>
+ * It is set by the system service.
+ * @hide
+ */
+ @Nullable
+ public String getPackageName() {
+ return mPackageName;
+ }
+
+ /**
* Gets information about how volume is handled on the route.
*
* @return {@link #PLAYBACK_VOLUME_FIXED} or {@link #PLAYBACK_VOLUME_VARIABLE}
@@ -518,6 +536,18 @@ public final class MediaRoute2Info implements Parcelable {
return mAddress;
}
+ /**
+ * Gets the Deduplication ID of the route if available.
+ * @see RouteDiscoveryPreference#shouldRemoveDuplicates()
+ */
+ @NonNull
+ public Set<String> getDeduplicationIds() {
+ return mDeduplicationIds;
+ }
+
+ /**
+ * Gets an optional bundle with extra data.
+ */
@Nullable
public Bundle getExtras() {
return mExtras == null ? null : new Bundle(mExtras);
@@ -549,7 +579,7 @@ public final class MediaRoute2Info implements Parcelable {
* Returns if the route has at least one of the specified route features.
*
* @param features the list of route features to consider
- * @return true if the route has at least one feature in the list
+ * @return {@code true} if the route has at least one feature in the list
* @hide
*/
public boolean hasAnyFeatures(@NonNull Collection<String> features) {
@@ -563,6 +593,21 @@ public final class MediaRoute2Info implements Parcelable {
}
/**
+ * Returns if the route has all the specified route features.
+ *
+ * @hide
+ */
+ public boolean hasAllFeatures(@NonNull Collection<String> features) {
+ Objects.requireNonNull(features, "features must not be null");
+ for (String feature : features) {
+ if (!getFeatures().contains(feature)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
* Returns true if the route info has all of the required field.
* A route is valid if and only if it is obtained from
* {@link com.android.server.media.MediaRouterService}.
@@ -596,10 +641,12 @@ public final class MediaRoute2Info implements Parcelable {
&& Objects.equals(mDescription, other.mDescription)
&& (mConnectionState == other.mConnectionState)
&& Objects.equals(mClientPackageName, other.mClientPackageName)
+ && Objects.equals(mPackageName, other.mPackageName)
&& (mVolumeHandling == other.mVolumeHandling)
&& (mVolumeMax == other.mVolumeMax)
&& (mVolume == other.mVolume)
&& Objects.equals(mAddress, other.mAddress)
+ && Objects.equals(mDeduplicationIds, other.mDeduplicationIds)
&& Objects.equals(mProviderId, other.mProviderId);
}
@@ -607,8 +654,8 @@ public final class MediaRoute2Info implements Parcelable {
public int hashCode() {
// Note: mExtras is not included.
return Objects.hash(mId, mName, mFeatures, mType, mIsSystem, mIconUri, mDescription,
- mConnectionState, mClientPackageName, mVolumeHandling, mVolumeMax, mVolume,
- mAddress, mProviderId);
+ mConnectionState, mClientPackageName, mPackageName, mVolumeHandling, mVolumeMax,
+ mVolume, mAddress, mDeduplicationIds, mProviderId);
}
@Override
@@ -626,6 +673,7 @@ public final class MediaRoute2Info implements Parcelable {
.append(", volumeHandling=").append(getVolumeHandling())
.append(", volumeMax=").append(getVolumeMax())
.append(", volume=").append(getVolume())
+ .append(", deduplicationIds=").append(String.join(",", getDeduplicationIds()))
.append(", providerId=").append(getProviderId())
.append(" }");
return result.toString();
@@ -647,10 +695,12 @@ public final class MediaRoute2Info implements Parcelable {
TextUtils.writeToParcel(mDescription, dest, flags);
dest.writeInt(mConnectionState);
dest.writeString(mClientPackageName);
+ dest.writeString(mPackageName);
dest.writeInt(mVolumeHandling);
dest.writeInt(mVolumeMax);
dest.writeInt(mVolume);
dest.writeString(mAddress);
+ dest.writeStringArray(mDeduplicationIds.toArray(new String[mDeduplicationIds.size()]));
dest.writeBundle(mExtras);
dest.writeString(mProviderId);
}
@@ -671,10 +721,12 @@ public final class MediaRoute2Info implements Parcelable {
@ConnectionState
int mConnectionState;
String mClientPackageName;
+ String mPackageName;
int mVolumeHandling = PLAYBACK_VOLUME_FIXED;
int mVolumeMax;
int mVolume;
String mAddress;
+ Set<String> mDeduplicationIds;
Bundle mExtras;
String mProviderId;
@@ -698,6 +750,7 @@ public final class MediaRoute2Info implements Parcelable {
mId = id;
mName = name;
mFeatures = new ArrayList<>();
+ mDeduplicationIds = Set.of();
}
/**
@@ -733,10 +786,12 @@ public final class MediaRoute2Info implements Parcelable {
mDescription = routeInfo.mDescription;
mConnectionState = routeInfo.mConnectionState;
mClientPackageName = routeInfo.mClientPackageName;
+ mPackageName = routeInfo.mPackageName;
mVolumeHandling = routeInfo.mVolumeHandling;
mVolumeMax = routeInfo.mVolumeMax;
mVolume = routeInfo.mVolume;
mAddress = routeInfo.mAddress;
+ mDeduplicationIds = Set.copyOf(routeInfo.mDeduplicationIds);
if (routeInfo.mExtras != null) {
mExtras = new Bundle(routeInfo.mExtras);
}
@@ -860,6 +915,16 @@ public final class MediaRoute2Info implements Parcelable {
}
/**
+ * Sets the package name of the route.
+ * @hide
+ */
+ @NonNull
+ public Builder setPackageName(@NonNull String packageName) {
+ mPackageName = packageName;
+ return this;
+ }
+
+ /**
* Sets the route's volume handling.
*/
@NonNull
@@ -897,6 +962,20 @@ public final class MediaRoute2Info implements Parcelable {
}
/**
+ * Sets the deduplication ID of the route.
+ * Routes have the same ID could be removed even when
+ * they are from different providers.
+ * <p>
+ * If it's {@code null}, the route will not be removed.
+ * @see RouteDiscoveryPreference#shouldRemoveDuplicates()
+ */
+ @NonNull
+ public Builder setDeduplicationIds(@NonNull Set<String> id) {
+ mDeduplicationIds = Set.copyOf(id);
+ return this;
+ }
+
+ /**
* Sets a bundle of extras for the route.
* <p>
* Note: The extras will not affect the result of {@link MediaRoute2Info#equals(Object)}.
diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java
index 4b32dbfc42f5..b485eb51380d 100644
--- a/media/java/android/media/MediaRouter2.java
+++ b/media/java/android/media/MediaRouter2.java
@@ -34,15 +34,18 @@ import android.os.RemoteException;
import android.os.ServiceManager;
import android.text.TextUtils;
import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.Log;
import com.android.internal.annotations.GuardedBy;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
+import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicInteger;
@@ -302,8 +305,7 @@ public final class MediaRouter2 {
mSystemController = new SystemRoutingController(
ensureClientPackageNameForSystemSession(
sManager.getSystemRoutingSession(clientPackageName)));
- mDiscoveryPreference = new RouteDiscoveryPreference.Builder(
- sManager.getPreferredFeatures(clientPackageName), true).build();
+ mDiscoveryPreference = sManager.getDiscoveryPreference(clientPackageName);
updateAllRoutesFromManager();
// Only used by non-system MediaRouter2.
@@ -1060,11 +1062,48 @@ public final class MediaRouter2 {
.build();
}
+ private List<MediaRoute2Info> getSortedRoutes(List<MediaRoute2Info> routes,
+ RouteDiscoveryPreference preference) {
+ if (!preference.shouldRemoveDuplicates()) {
+ return routes;
+ }
+ Map<String, Integer> packagePriority = new ArrayMap<>();
+ int count = preference.getDeduplicationPackageOrder().size();
+ for (int i = 0; i < count; i++) {
+ // the last package will have 1 as the priority
+ packagePriority.put(preference.getDeduplicationPackageOrder().get(i), count - i);
+ }
+ ArrayList<MediaRoute2Info> sortedRoutes = new ArrayList<>(routes);
+ // take the negative for descending order
+ sortedRoutes.sort(Comparator.comparingInt(
+ r -> -packagePriority.getOrDefault(r.getPackageName(), 0)));
+ return sortedRoutes;
+ }
+
private List<MediaRoute2Info> filterRoutes(List<MediaRoute2Info> routes,
- RouteDiscoveryPreference discoveryRequest) {
- return routes.stream()
- .filter(route -> route.hasAnyFeatures(discoveryRequest.getPreferredFeatures()))
- .collect(Collectors.toList());
+ RouteDiscoveryPreference discoveryPreference) {
+
+ Set<String> deduplicationIdSet = new ArraySet<>();
+
+ List<MediaRoute2Info> filteredRoutes = new ArrayList<>();
+ for (MediaRoute2Info route : getSortedRoutes(routes, discoveryPreference)) {
+ if (!route.hasAllFeatures(discoveryPreference.getRequiredFeatures())
+ || !route.hasAnyFeatures(discoveryPreference.getPreferredFeatures())) {
+ continue;
+ }
+ if (!discoveryPreference.getAllowedPackages().isEmpty()
+ && !discoveryPreference.getAllowedPackages().contains(route.getPackageName())) {
+ continue;
+ }
+ if (discoveryPreference.shouldRemoveDuplicates()) {
+ if (Collections.disjoint(deduplicationIdSet, route.getDeduplicationIds())) {
+ continue;
+ }
+ deduplicationIdSet.addAll(route.getDeduplicationIds());
+ }
+ filteredRoutes.add(route);
+ }
+ return filteredRoutes;
}
private void updateAllRoutesFromManager() {
diff --git a/media/java/android/media/MediaRouter2Manager.java b/media/java/android/media/MediaRouter2Manager.java
index 83fa7c255f9f..8635c0ea762c 100644
--- a/media/java/android/media/MediaRouter2Manager.java
+++ b/media/java/android/media/MediaRouter2Manager.java
@@ -29,6 +29,8 @@ import android.os.Message;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.Log;
import com.android.internal.annotations.GuardedBy;
@@ -36,15 +38,18 @@ import com.android.internal.annotations.VisibleForTesting;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
+import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Predicate;
import java.util.stream.Collectors;
/**
@@ -84,7 +89,8 @@ public final class MediaRouter2Manager {
@GuardedBy("mRoutesLock")
private final Map<String, MediaRoute2Info> mRoutes = new HashMap<>();
@NonNull
- final ConcurrentMap<String, List<String>> mPreferredFeaturesMap = new ConcurrentHashMap<>();
+ final ConcurrentMap<String, RouteDiscoveryPreference> mDiscoveryPreferenceMap =
+ new ConcurrentHashMap<>();
private final AtomicInteger mNextRequestId = new AtomicInteger(1);
private final CopyOnWriteArrayList<TransferRequest> mTransferRequests =
@@ -247,25 +253,8 @@ public final class MediaRouter2Manager {
*/
@NonNull
public List<MediaRoute2Info> getAvailableRoutes(@NonNull RoutingSessionInfo sessionInfo) {
- Objects.requireNonNull(sessionInfo, "sessionInfo must not be null");
-
- List<MediaRoute2Info> routes = new ArrayList<>();
-
- String packageName = sessionInfo.getClientPackageName();
- List<String> preferredFeatures = mPreferredFeaturesMap.get(packageName);
- if (preferredFeatures == null) {
- preferredFeatures = Collections.emptyList();
- }
- synchronized (mRoutesLock) {
- for (MediaRoute2Info route : mRoutes.values()) {
- if (route.hasAnyFeatures(preferredFeatures)
- || sessionInfo.getSelectedRoutes().contains(route.getId())
- || sessionInfo.getTransferableRoutes().contains(route.getId())) {
- routes.add(route);
- }
- }
- }
- return routes;
+ return getFilteredRoutes(sessionInfo, /*includeSelectedRoutes=*/true,
+ null);
}
/**
@@ -281,27 +270,70 @@ public final class MediaRouter2Manager {
*/
@NonNull
public List<MediaRoute2Info> getTransferableRoutes(@NonNull RoutingSessionInfo sessionInfo) {
+ return getFilteredRoutes(sessionInfo, /*includeSelectedRoutes=*/false,
+ (route) -> sessionInfo.isSystemSession() ^ route.isSystemRoute());
+ }
+
+ private List<MediaRoute2Info> getSortedRoutes(RouteDiscoveryPreference preference) {
+ if (!preference.shouldRemoveDuplicates()) {
+ synchronized (mRoutesLock) {
+ return List.copyOf(mRoutes.values());
+ }
+ }
+ Map<String, Integer> packagePriority = new ArrayMap<>();
+ int count = preference.getDeduplicationPackageOrder().size();
+ for (int i = 0; i < count; i++) {
+ // the last package will have 1 as the priority
+ packagePriority.put(preference.getDeduplicationPackageOrder().get(i), count - i);
+ }
+ ArrayList<MediaRoute2Info> routes;
+ synchronized (mRoutesLock) {
+ routes = new ArrayList<>(mRoutes.values());
+ }
+ // take the negative for descending order
+ routes.sort(Comparator.comparingInt(
+ r -> -packagePriority.getOrDefault(r.getPackageName(), 0)));
+ return routes;
+ }
+
+ private List<MediaRoute2Info> getFilteredRoutes(@NonNull RoutingSessionInfo sessionInfo,
+ boolean includeSelectedRoutes,
+ @Nullable Predicate<MediaRoute2Info> additionalFilter) {
Objects.requireNonNull(sessionInfo, "sessionInfo must not be null");
List<MediaRoute2Info> routes = new ArrayList<>();
+ Set<String> deduplicationIdSet = new ArraySet<>();
String packageName = sessionInfo.getClientPackageName();
- List<String> preferredFeatures = mPreferredFeaturesMap.get(packageName);
- if (preferredFeatures == null) {
- preferredFeatures = Collections.emptyList();
- }
- synchronized (mRoutesLock) {
- for (MediaRoute2Info route : mRoutes.values()) {
- if (sessionInfo.getTransferableRoutes().contains(route.getId())) {
- routes.add(route);
+ RouteDiscoveryPreference discoveryPreference =
+ mDiscoveryPreferenceMap.getOrDefault(packageName, RouteDiscoveryPreference.EMPTY);
+
+ for (MediaRoute2Info route : getSortedRoutes(discoveryPreference)) {
+ if (sessionInfo.getTransferableRoutes().contains(route.getId())
+ || (includeSelectedRoutes
+ && sessionInfo.getSelectedRoutes().contains(route.getId()))) {
+ routes.add(route);
+ continue;
+ }
+ if (!route.hasAllFeatures(discoveryPreference.getRequiredFeatures())
+ || !route.hasAnyFeatures(discoveryPreference.getPreferredFeatures())) {
+ continue;
+ }
+ if (!discoveryPreference.getAllowedPackages().isEmpty()
+ && !discoveryPreference.getAllowedPackages()
+ .contains(route.getPackageName())) {
+ continue;
+ }
+ if (additionalFilter != null && !additionalFilter.test(route)) {
+ continue;
+ }
+ if (discoveryPreference.shouldRemoveDuplicates()) {
+ if (Collections.disjoint(deduplicationIdSet, route.getDeduplicationIds())) {
continue;
}
- // Add Phone -> Cast and Cast -> Phone
- if (route.hasAnyFeatures(preferredFeatures)
- && (sessionInfo.isSystemSession() ^ route.isSystemRoute())) {
- routes.add(route);
- }
+ deduplicationIdSet.addAll(route.getDeduplicationIds());
}
+ routes.add(route);
}
return routes;
}
@@ -310,44 +342,10 @@ public final class MediaRouter2Manager {
* Returns the preferred features of the specified package name.
*/
@NonNull
- public List<String> getPreferredFeatures(@NonNull String packageName) {
- Objects.requireNonNull(packageName, "packageName must not be null");
-
- List<String> preferredFeatures = mPreferredFeaturesMap.get(packageName);
- if (preferredFeatures == null) {
- preferredFeatures = Collections.emptyList();
- }
- return preferredFeatures;
- }
-
- /**
- * Returns a list of routes which are related to the given package name in the given route list.
- */
- @NonNull
- public List<MediaRoute2Info> filterRoutesForPackage(@NonNull List<MediaRoute2Info> routes,
- @NonNull String packageName) {
- Objects.requireNonNull(routes, "routes must not be null");
+ public RouteDiscoveryPreference getDiscoveryPreference(@NonNull String packageName) {
Objects.requireNonNull(packageName, "packageName must not be null");
- List<RoutingSessionInfo> sessions = getRoutingSessions(packageName);
- RoutingSessionInfo sessionInfo = sessions.get(sessions.size() - 1);
-
- List<MediaRoute2Info> result = new ArrayList<>();
- List<String> preferredFeatures = mPreferredFeaturesMap.get(packageName);
- if (preferredFeatures == null) {
- preferredFeatures = Collections.emptyList();
- }
-
- synchronized (mRoutesLock) {
- for (MediaRoute2Info route : routes) {
- if (route.hasAnyFeatures(preferredFeatures)
- || sessionInfo.getSelectedRoutes().contains(route.getId())
- || sessionInfo.getTransferableRoutes().contains(route.getId())) {
- result.add(route);
- }
- }
- }
- return result;
+ return mDiscoveryPreferenceMap.getOrDefault(packageName, RouteDiscoveryPreference.EMPTY);
}
/**
@@ -713,19 +711,19 @@ public final class MediaRouter2Manager {
}
}
- void updatePreferredFeatures(String packageName, List<String> preferredFeatures) {
- if (preferredFeatures == null) {
- mPreferredFeaturesMap.remove(packageName);
+ void updateDiscoveryPreference(String packageName, RouteDiscoveryPreference preference) {
+ if (preference == null) {
+ mDiscoveryPreferenceMap.remove(packageName);
return;
}
- List<String> prevFeatures = mPreferredFeaturesMap.put(packageName, preferredFeatures);
- if ((prevFeatures == null && preferredFeatures.size() == 0)
- || Objects.equals(preferredFeatures, prevFeatures)) {
+ RouteDiscoveryPreference prevPreference =
+ mDiscoveryPreferenceMap.put(packageName, preference);
+ if (Objects.equals(preference, prevPreference)) {
return;
}
for (CallbackRecord record : mCallbackRecords) {
record.mExecutor.execute(() -> record.mCallback
- .onPreferredFeaturesChanged(packageName, preferredFeatures));
+ .onDiscoveryPreferenceChanged(packageName, preference));
}
}
@@ -1047,6 +1045,17 @@ public final class MediaRouter2Manager {
@NonNull List<String> preferredFeatures) {}
/**
+ * Called when the preferred route features of an app is changed.
+ *
+ * @param packageName the package name of the application
+ * @param discoveryPreference the new discovery preference set by the application.
+ */
+ default void onDiscoveryPreferenceChanged(@NonNull String packageName,
+ @NonNull RouteDiscoveryPreference discoveryPreference) {
+ onPreferredFeaturesChanged(packageName, discoveryPreference.getPreferredFeatures());
+ }
+
+ /**
* Called when a previous request has failed.
*
* @param reason the reason that the request has failed. Can be one of followings:
@@ -1125,9 +1134,10 @@ public final class MediaRouter2Manager {
}
@Override
- public void notifyPreferredFeaturesChanged(String packageName, List<String> features) {
- mHandler.sendMessage(obtainMessage(MediaRouter2Manager::updatePreferredFeatures,
- MediaRouter2Manager.this, packageName, features));
+ public void notifyDiscoveryPreferenceChanged(String packageName,
+ RouteDiscoveryPreference discoveryPreference) {
+ mHandler.sendMessage(obtainMessage(MediaRouter2Manager::updateDiscoveryPreference,
+ MediaRouter2Manager.this, packageName, discoveryPreference));
}
@Override
diff --git a/media/java/android/media/RouteDiscoveryPreference.java b/media/java/android/media/RouteDiscoveryPreference.java
index 37fee8466859..004501812ff6 100644
--- a/media/java/android/media/RouteDiscoveryPreference.java
+++ b/media/java/android/media/RouteDiscoveryPreference.java
@@ -64,6 +64,13 @@ public final class RouteDiscoveryPreference implements Parcelable {
@NonNull
private final List<String> mPreferredFeatures;
+ @NonNull
+ private final List<String> mRequiredFeatures;
+ @NonNull
+ private final List<String> mPackagesOrder;
+ @NonNull
+ private final List<String> mAllowedPackages;
+
private final boolean mShouldPerformActiveScan;
@Nullable
private final Bundle mExtras;
@@ -78,12 +85,18 @@ public final class RouteDiscoveryPreference implements Parcelable {
RouteDiscoveryPreference(@NonNull Builder builder) {
mPreferredFeatures = builder.mPreferredFeatures;
+ mRequiredFeatures = builder.mRequiredFeatures;
+ mPackagesOrder = builder.mPackageOrder;
+ mAllowedPackages = builder.mAllowedPackages;
mShouldPerformActiveScan = builder.mActiveScan;
mExtras = builder.mExtras;
}
RouteDiscoveryPreference(@NonNull Parcel in) {
mPreferredFeatures = in.createStringArrayList();
+ mRequiredFeatures = in.createStringArrayList();
+ mPackagesOrder = in.createStringArrayList();
+ mAllowedPackages = in.createStringArrayList();
mShouldPerformActiveScan = in.readBoolean();
mExtras = in.readBundle();
}
@@ -96,6 +109,8 @@ public final class RouteDiscoveryPreference implements Parcelable {
* {@link MediaRoute2Info#FEATURE_LIVE_AUDIO}, {@link MediaRoute2Info#FEATURE_LIVE_VIDEO},
* or {@link MediaRoute2Info#FEATURE_REMOTE_PLAYBACK} or custom features defined by a provider.
* </p>
+ *
+ * @see #getRequiredFeatures()
*/
@NonNull
public List<String> getPreferredFeatures() {
@@ -103,6 +118,47 @@ public final class RouteDiscoveryPreference implements Parcelable {
}
/**
+ * Gets the required features of routes that media router would like to discover.
+ * <p>
+ * Routes that have all the required features will be discovered.
+ * They may include predefined features such as
+ * {@link MediaRoute2Info#FEATURE_LIVE_AUDIO}, {@link MediaRoute2Info#FEATURE_LIVE_VIDEO},
+ * or {@link MediaRoute2Info#FEATURE_REMOTE_PLAYBACK} or custom features defined by a provider.
+ *
+ * @see #getPreferredFeatures()
+ */
+ @NonNull
+ public List<String> getRequiredFeatures() {
+ return mRequiredFeatures;
+ }
+
+ /**
+ * Gets the ordered list of package names used to remove duplicate routes.
+ * <p>
+ * Duplicate route removal is enabled if the returned list is non-empty. Routes are deduplicated
+ * based on their {@link MediaRoute2Info#getDeduplicationIds() deduplication IDs}. If two routes
+ * have a deduplication ID in common, only the route from the provider whose package name is
+ * first in the provided list will remain.
+ *
+ * @see #shouldRemoveDuplicates()
+ */
+ @NonNull
+ public List<String> getDeduplicationPackageOrder() {
+ return mPackagesOrder;
+ }
+
+ /**
+ * Gets the list of allowed packages.
+ * <p>
+ * If it's not empty, it will only discover routes from the provider whose package name
+ * belongs to the list.
+ */
+ @NonNull
+ public List<String> getAllowedPackages() {
+ return mAllowedPackages;
+ }
+
+ /**
* Gets whether active scanning should be performed.
* <p>
* If any of discovery preferences sets this as {@code true}, active scanning will
@@ -114,6 +170,15 @@ public final class RouteDiscoveryPreference implements Parcelable {
}
/**
+ * Gets whether duplicate routes removal is enabled.
+ *
+ * @see #getDeduplicationPackageOrder()
+ */
+ public boolean shouldRemoveDuplicates() {
+ return !mPackagesOrder.isEmpty();
+ }
+
+ /**
* @hide
*/
public Bundle getExtras() {
@@ -128,6 +193,9 @@ public final class RouteDiscoveryPreference implements Parcelable {
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeStringList(mPreferredFeatures);
+ dest.writeStringList(mRequiredFeatures);
+ dest.writeStringList(mPackagesOrder);
+ dest.writeStringList(mAllowedPackages);
dest.writeBoolean(mShouldPerformActiveScan);
dest.writeBundle(mExtras);
}
@@ -155,14 +223,17 @@ public final class RouteDiscoveryPreference implements Parcelable {
return false;
}
RouteDiscoveryPreference other = (RouteDiscoveryPreference) o;
- //TODO: Make this order-free
return Objects.equals(mPreferredFeatures, other.mPreferredFeatures)
+ && Objects.equals(mRequiredFeatures, other.mRequiredFeatures)
+ && Objects.equals(mPackagesOrder, other.mPackagesOrder)
+ && Objects.equals(mAllowedPackages, other.mAllowedPackages)
&& mShouldPerformActiveScan == other.mShouldPerformActiveScan;
}
@Override
public int hashCode() {
- return Objects.hash(mPreferredFeatures, mShouldPerformActiveScan);
+ return Objects.hash(mPreferredFeatures, mRequiredFeatures, mPackagesOrder, mAllowedPackages,
+ mShouldPerformActiveScan);
}
/**
@@ -170,13 +241,21 @@ public final class RouteDiscoveryPreference implements Parcelable {
*/
public static final class Builder {
List<String> mPreferredFeatures;
+ List<String> mRequiredFeatures;
+ List<String> mPackageOrder;
+ List<String> mAllowedPackages;
+
boolean mActiveScan;
+
Bundle mExtras;
public Builder(@NonNull List<String> preferredFeatures, boolean activeScan) {
Objects.requireNonNull(preferredFeatures, "preferredFeatures must not be null");
mPreferredFeatures = preferredFeatures.stream().filter(str -> !TextUtils.isEmpty(str))
.collect(Collectors.toList());
+ mRequiredFeatures = List.of();
+ mPackageOrder = List.of();
+ mAllowedPackages = List.of();
mActiveScan = activeScan;
}
@@ -184,12 +263,15 @@ public final class RouteDiscoveryPreference implements Parcelable {
Objects.requireNonNull(preference, "preference must not be null");
mPreferredFeatures = preference.getPreferredFeatures();
+ mRequiredFeatures = preference.getRequiredFeatures();
+ mPackageOrder = preference.getDeduplicationPackageOrder();
+ mAllowedPackages = preference.getAllowedPackages();
mActiveScan = preference.shouldPerformActiveScan();
mExtras = preference.getExtras();
}
/**
- * A constructor to combine all of the preferences into a single preference.
+ * A constructor to combine all the preferences into a single preference.
* It ignores extras of preferences.
*
* @hide
@@ -224,6 +306,30 @@ public final class RouteDiscoveryPreference implements Parcelable {
}
/**
+ * Sets the required route features to discover.
+ */
+ @NonNull
+ public Builder setRequiredFeatures(@NonNull List<String> requiredFeatures) {
+ Objects.requireNonNull(requiredFeatures, "preferredFeatures must not be null");
+ mRequiredFeatures = requiredFeatures.stream().filter(str -> !TextUtils.isEmpty(str))
+ .collect(Collectors.toList());
+ return this;
+ }
+
+ /**
+ * Sets the list of package names of providers that media router would like to discover.
+ * <p>
+ * If it's non-empty, media router only discovers route from the provider in the list.
+ * The default value is empty, which discovers routes from all providers.
+ */
+ @NonNull
+ public Builder setAllowedPackages(@NonNull List<String> allowedPackages) {
+ Objects.requireNonNull(allowedPackages, "allowedPackages must not be null");
+ mAllowedPackages = List.copyOf(allowedPackages);
+ return this;
+ }
+
+ /**
* Sets if active scanning should be performed.
* <p>
* Since active scanning uses more system resources, set this as {@code true} only
@@ -237,6 +343,24 @@ public final class RouteDiscoveryPreference implements Parcelable {
}
/**
+ * Sets the order of packages to use when removing duplicate routes.
+ * <p>
+ * Routes are deduplicated based on their
+ * {@link MediaRoute2Info#getDeduplicationIds() deduplication IDs}.
+ * If two routes have a deduplication ID in common, only the route from the provider whose
+ * package name is first in the provided list will remain.
+ *
+ * @param packageOrder ordered list of package names used to remove duplicate routes, or an
+ * empty list if deduplication should not be enabled.
+ */
+ @NonNull
+ public Builder setDeduplicationPackageOrder(@NonNull List<String> packageOrder) {
+ Objects.requireNonNull(packageOrder, "packageOrder must not be null");
+ mPackageOrder = List.copyOf(packageOrder);
+ return this;
+ }
+
+ /**
* Sets the extras of the route.
* @hide
*/
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index 41666c7225d5..75236f455057 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -1851,6 +1851,7 @@ public final class TvInputManager {
*
* @hide
*/
+ @SystemApi
@RequiresPermission(android.Manifest.permission.TUNER_RESOURCE_ACCESS)
public int getClientPid(@NonNull String sessionId) {
return getClientPidInternal(sessionId);
diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java
index ffc443306df5..1e32cadc2748 100644
--- a/media/java/android/media/tv/tuner/Tuner.java
+++ b/media/java/android/media/tv/tuner/Tuner.java
@@ -68,6 +68,7 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
@@ -1602,25 +1603,27 @@ public class Tuner implements AutoCloseable {
*
* @param statusTypes an array of status types.
*
- * @return an array of current readiness states. {@code null} if the operation failed or
- * unsupported versions.
+ * @return a list of current readiness states. It is empty if the operation fails or unsupported
+ * versions.
* @throws IllegalStateException if there is no active frontend currently.
*/
- @Nullable
- @SuppressLint("ArrayReturn")
- @SuppressWarnings("NullableCollection")
- public FrontendStatusReadiness[] getFrontendStatusReadiness(
+ @NonNull
+ public List<FrontendStatusReadiness> getFrontendStatusReadiness(
@NonNull @FrontendStatusType int[] statusTypes) {
mFrontendLock.lock();
try {
if (!TunerVersionChecker.checkHigherOrEqualVersionTo(
- TunerVersionChecker.TUNER_VERSION_2_0, "Remove output PID")) {
- return null;
+ TunerVersionChecker.TUNER_VERSION_2_0, "Get fronted status readiness")) {
+ return Collections.EMPTY_LIST;
}
if (mFrontend == null) {
throw new IllegalStateException("frontend is not initialized");
}
- return nativeGetFrontendStatusReadiness(statusTypes);
+ FrontendStatusReadiness[] readiness = nativeGetFrontendStatusReadiness(statusTypes);
+ if (readiness == null) {
+ return Collections.EMPTY_LIST;
+ }
+ return Arrays.asList(readiness);
} finally {
mFrontendLock.unlock();
}
diff --git a/media/java/android/media/tv/tuner/frontend/FrontendStatusReadiness.java b/media/java/android/media/tv/tuner/frontend/FrontendStatusReadiness.java
index 52527b3fb065..c30753ccc3a0 100644
--- a/media/java/android/media/tv/tuner/frontend/FrontendStatusReadiness.java
+++ b/media/java/android/media/tv/tuner/frontend/FrontendStatusReadiness.java
@@ -30,7 +30,7 @@ import java.lang.annotation.RetentionPolicy;
* @hide
*/
@SystemApi
-public class FrontendStatusReadiness {
+public final class FrontendStatusReadiness {
/** @hide */
@IntDef({FRONTEND_STATUS_READINESS_UNDEFINED, FRONTEND_STATUS_READINESS_UNAVAILABLE,
FRONTEND_STATUS_READINESS_UNSTABLE, FRONTEND_STATUS_READINESS_STABLE,
diff --git a/media/jni/Android.bp b/media/jni/Android.bp
index feae6065c680..18b779fa7c57 100644
--- a/media/jni/Android.bp
+++ b/media/jni/Android.bp
@@ -87,6 +87,7 @@ cc_library_shared {
"android.hardware.drm@1.4",
"android.hidl.memory@1.0",
"android.hidl.token@1.0-utils",
+ "android.hardware.drm-V1-ndk",
],
header_libs: [
diff --git a/media/native/midi/amidi.cpp b/media/native/midi/amidi.cpp
index aa076e85e30d..fd8a06da5c6d 100644
--- a/media/native/midi/amidi.cpp
+++ b/media/native/midi/amidi.cpp
@@ -401,10 +401,14 @@ ssize_t AMIDI_API AMidiInputPort_send(const AMidiInputPort *inputPort, const uin
ssize_t AMIDI_API AMidiInputPort_sendWithTimestamp(const AMidiInputPort *inputPort,
const uint8_t *data, size_t numBytes, int64_t timestamp) {
- if (inputPort == nullptr || data == nullptr) {
+ if (inputPort == nullptr || data == nullptr || numBytes < 0 || timestamp < 0) {
return AMEDIA_ERROR_INVALID_PARAMETER;
}
+ if (numBytes == 0) {
+ return 0;
+ }
+
// AMIDI_logBuffer(data, numBytes);
uint8_t writeBuffer[AMIDI_BUFFER_SIZE + AMIDI_PACKET_OVERHEAD];
diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt
index 35c794e82695..f9a17746892e 100644
--- a/native/android/libandroid.map.txt
+++ b/native/android/libandroid.map.txt
@@ -315,18 +315,18 @@ LIBANDROID {
AThermal_registerThermalStatusListener; # introduced=30
AThermal_unregisterThermalStatusListener; # introduced=30
AThermal_getThermalHeadroom; # introduced=31
+ APerformanceHint_getManager; # introduced=Tiramisu
+ APerformanceHint_createSession; # introduced=Tiramisu
+ APerformanceHint_getPreferredUpdateRateNanos; # introduced=Tiramisu
+ APerformanceHint_updateTargetWorkDuration; # introduced=Tiramisu
+ APerformanceHint_reportActualWorkDuration; # introduced=Tiramisu
+ APerformanceHint_closeSession; # introduced=Tiramisu
local:
*;
};
LIBANDROID_PLATFORM {
global:
- APerformanceHint_getManager;
- APerformanceHint_createSession;
- APerformanceHint_getPreferredUpdateRateNanos;
- APerformanceHint_updateTargetWorkDuration;
- APerformanceHint_reportActualWorkDuration;
- APerformanceHint_closeSession;
APerformanceHint_setIHintManagerForTesting;
extern "C++" {
ASurfaceControl_registerSurfaceStatsListener*;
diff --git a/native/android/performance_hint.cpp b/native/android/performance_hint.cpp
index 51a0c99af66e..0c360519ceb2 100644
--- a/native/android/performance_hint.cpp
+++ b/native/android/performance_hint.cpp
@@ -16,17 +16,18 @@
#define LOG_TAG "perf_hint"
-#include <utility>
-#include <vector>
-
#include <android/os/IHintManager.h>
#include <android/os/IHintSession.h>
+#include <android/performance_hint.h>
#include <binder/Binder.h>
#include <binder/IBinder.h>
#include <binder/IServiceManager.h>
#include <performance_hint_private.h>
#include <utils/SystemClock.h>
+#include <utility>
+#include <vector>
+
using namespace android;
using namespace android::os;
diff --git a/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp b/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp
index 284e9ee909ee..b17850e5d1e4 100644
--- a/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp
+++ b/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp
@@ -18,10 +18,12 @@
#include <android/os/IHintManager.h>
#include <android/os/IHintSession.h>
+#include <android/performance_hint.h>
#include <binder/IBinder.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <performance_hint_private.h>
+
#include <memory>
#include <vector>
diff --git a/native/graphics/jni/imagedecoder.cpp b/native/graphics/jni/imagedecoder.cpp
index a0f3098ad347..bb25274e3136 100644
--- a/native/graphics/jni/imagedecoder.cpp
+++ b/native/graphics/jni/imagedecoder.cpp
@@ -198,14 +198,16 @@ static SkColorType getColorType(AndroidBitmapFormat format) {
return kGray_8_SkColorType;
case ANDROID_BITMAP_FORMAT_RGBA_F16:
return kRGBA_F16_SkColorType;
+ case ANDROID_BITMAP_FORMAT_RGBA_1010102:
+ return kRGBA_1010102_SkColorType;
default:
return kUnknown_SkColorType;
}
}
int AImageDecoder_setAndroidBitmapFormat(AImageDecoder* decoder, int32_t format) {
- if (!decoder || format < ANDROID_BITMAP_FORMAT_NONE
- || format > ANDROID_BITMAP_FORMAT_RGBA_F16) {
+ if (!decoder || format < ANDROID_BITMAP_FORMAT_NONE ||
+ format > ANDROID_BITMAP_FORMAT_RGBA_1010102) {
return ANDROID_IMAGE_DECODER_BAD_PARAMETER;
}
@@ -290,6 +292,8 @@ static AndroidBitmapFormat getFormat(SkColorType colorType) {
return ANDROID_BITMAP_FORMAT_A_8;
case kRGBA_F16_SkColorType:
return ANDROID_BITMAP_FORMAT_RGBA_F16;
+ case kRGBA_1010102_SkColorType:
+ return ANDROID_BITMAP_FORMAT_RGBA_1010102;
default:
return ANDROID_BITMAP_FORMAT_NONE;
}
diff --git a/omapi/OWNERS b/omapi/OWNERS
new file mode 100644
index 000000000000..5682fd3281f4
--- /dev/null
+++ b/omapi/OWNERS
@@ -0,0 +1,5 @@
+# Bug component: 456592
+
+zachoverflow@google.com
+alisher@google.com
+jackcwyu@google.com
diff --git a/packages/ConnectivityT/framework-t/Android.bp b/packages/ConnectivityT/framework-t/Android.bp
index 223bdcdd9c95..327b1fb2f5d8 100644
--- a/packages/ConnectivityT/framework-t/Android.bp
+++ b/packages/ConnectivityT/framework-t/Android.bp
@@ -39,7 +39,6 @@ filegroup {
"src/android/net/TrafficStats.java",
"src/android/net/UnderlyingNetworkInfo.*",
"src/android/net/netstats/**/*.*",
- "src/com/android/server/NetworkManagementSocketTagger.java",
],
path: "src",
visibility: [
@@ -176,3 +175,34 @@ filegroup {
"//packages/modules/Connectivity:__subpackages__",
],
}
+
+cc_library_shared {
+ name: "libframework-connectivity-tiramisu-jni",
+ min_sdk_version: "30",
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wno-unused-parameter",
+ // Don't warn about S API usage even with
+ // min_sdk 30: the library is only loaded
+ // on S+ devices
+ "-Wno-unguarded-availability",
+ "-Wthread-safety",
+ ],
+ srcs: [
+ "jni/android_net_TrafficStats.cpp",
+ "jni/onload.cpp",
+ ],
+ shared_libs: [
+ "liblog",
+ ],
+ static_libs: [
+ "libnativehelper_compat_libc++",
+ ],
+ stl: "none",
+ apex_available: [
+ "com.android.tethering",
+ // TODO: remove when ConnectivityT moves to APEX.
+ "//apex_available:platform",
+ ],
+}
diff --git a/packages/ConnectivityT/framework-t/jni/android_net_TrafficStats.cpp b/packages/ConnectivityT/framework-t/jni/android_net_TrafficStats.cpp
new file mode 100644
index 000000000000..f3c58b112f0d
--- /dev/null
+++ b/packages/ConnectivityT/framework-t/jni/android_net_TrafficStats.cpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android/file_descriptor_jni.h>
+#include <android/multinetwork.h>
+#include <nativehelper/JNIHelp.h>
+
+namespace android {
+
+static jint tagSocketFd(JNIEnv* env, jclass, jobject fileDescriptor, jint tag, jint uid) {
+ int fd = AFileDescriptor_getFd(env, fileDescriptor);
+ if (fd == -1) return -EBADF;
+ return android_tag_socket_with_uid(fd, tag, uid);
+}
+
+static jint untagSocketFd(JNIEnv* env, jclass, jobject fileDescriptor) {
+ int fd = AFileDescriptor_getFd(env, fileDescriptor);
+ if (fd == -1) return -EBADF;
+ return android_untag_socket(fd);
+}
+
+static const JNINativeMethod gMethods[] = {
+ /* name, signature, funcPtr */
+ { "native_tagSocketFd", "(Ljava/io/FileDescriptor;II)I", (void*) tagSocketFd },
+ { "native_untagSocketFd", "(Ljava/io/FileDescriptor;)I", (void*) untagSocketFd },
+};
+
+int register_android_net_TrafficStats(JNIEnv* env) {
+ return jniRegisterNativeMethods(env, "android/net/TrafficStats", gMethods, NELEM(gMethods));
+}
+
+}; // namespace android
+
diff --git a/packages/ConnectivityT/framework-t/jni/onload.cpp b/packages/ConnectivityT/framework-t/jni/onload.cpp
new file mode 100644
index 000000000000..1fb42c63477e
--- /dev/null
+++ b/packages/ConnectivityT/framework-t/jni/onload.cpp
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "FrameworkConnectivityJNI"
+
+#include <log/log.h>
+#include <nativehelper/JNIHelp.h>
+
+namespace android {
+
+int register_android_net_TrafficStats(JNIEnv* env);
+
+extern "C" jint JNI_OnLoad(JavaVM* vm, void*) {
+ JNIEnv *env;
+ if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
+ __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "ERROR: GetEnv failed");
+ return JNI_ERR;
+ }
+
+ if (register_android_net_TrafficStats(env) < 0) return JNI_ERR;
+
+ return JNI_VERSION_1_6;
+}
+
+}; // namespace android
+
diff --git a/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStatsManager.java b/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStatsManager.java
index 84adef53e488..5ce7e59b38ff 100644
--- a/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStatsManager.java
+++ b/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStatsManager.java
@@ -41,6 +41,7 @@ import android.net.NetworkStateSnapshot;
import android.net.NetworkTemplate;
import android.net.UnderlyingNetworkInfo;
import android.net.netstats.IUsageCallback;
+import android.net.netstats.NetworkStatsDataMigrationUtils;
import android.net.netstats.provider.INetworkStatsProviderCallback;
import android.net.netstats.provider.NetworkStatsProvider;
import android.os.Build;
@@ -126,17 +127,12 @@ public class NetworkStatsManager {
private final INetworkStatsService mService;
/**
- * Type constants for reading different types of Data Usage.
+ * @deprecated Use {@link NetworkStatsDataMigrationUtils#PREFIX_XT}
+ * instead.
* @hide
*/
- // @SystemApi(client = MODULE_LIBRARIES)
+ @Deprecated
public static final String PREFIX_DEV = "dev";
- /** @hide */
- public static final String PREFIX_XT = "xt";
- /** @hide */
- public static final String PREFIX_UID = "uid";
- /** @hide */
- public static final String PREFIX_UID_TAG = "uid_tag";
/** @hide */
public static final int FLAG_POLL_ON_OPEN = 1 << 0;
diff --git a/packages/ConnectivityT/framework-t/src/android/net/NetworkIdentity.java b/packages/ConnectivityT/framework-t/src/android/net/NetworkIdentity.java
index 73b9c726996f..56faa52e82df 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/NetworkIdentity.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/NetworkIdentity.java
@@ -30,7 +30,6 @@ import android.app.usage.NetworkStatsManager;
import android.content.Context;
import android.net.wifi.WifiInfo;
import android.service.NetworkIdentityProto;
-import android.telephony.Annotation;
import android.telephony.TelephonyManager;
import android.util.proto.ProtoOutputStream;
@@ -275,8 +274,7 @@ public class NetworkIdentity {
@Deprecated
@NonNull
public static NetworkIdentity buildNetworkIdentity(Context context,
- @NonNull NetworkStateSnapshot snapshot,
- boolean defaultNetwork, @Annotation.NetworkType int ratType) {
+ @NonNull NetworkStateSnapshot snapshot, boolean defaultNetwork, int ratType) {
final NetworkIdentity.Builder builder = new NetworkIdentity.Builder()
.setNetworkStateSnapshot(snapshot).setDefaultNetwork(defaultNetwork);
if (snapshot.getLegacyType() == TYPE_MOBILE && ratType != NETWORK_TYPE_ALL) {
@@ -433,7 +431,7 @@ public class NetworkIdentity {
* @return this builder.
*/
@NonNull
- public Builder setRatType(@Annotation.NetworkType int ratType) {
+ public Builder setRatType(int ratType) {
if (!CollectionUtils.contains(TelephonyManager.getAllNetworkTypes(), ratType)
&& ratType != TelephonyManager.NETWORK_TYPE_UNKNOWN
&& ratType != NetworkStatsManager.NETWORK_TYPE_5G_NSA) {
diff --git a/packages/ConnectivityT/framework-t/src/android/net/NetworkTemplate.java b/packages/ConnectivityT/framework-t/src/android/net/NetworkTemplate.java
index 27e717fb59de..9b58b016bbf3 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/NetworkTemplate.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/NetworkTemplate.java
@@ -47,7 +47,6 @@ import android.net.wifi.WifiInfo;
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
-import android.telephony.Annotation.NetworkType;
import android.text.TextUtils;
import android.util.ArraySet;
@@ -203,7 +202,7 @@ public final class NetworkTemplate implements Parcelable {
* @hide
*/
public static NetworkTemplate buildTemplateMobileWithRatType(@Nullable String subscriberId,
- @NetworkType int ratType, int metered) {
+ int ratType, int metered) {
if (TextUtils.isEmpty(subscriberId)) {
return new NetworkTemplate(MATCH_MOBILE_WILDCARD, null /* subscriberId */,
null /* matchSubscriberIds */,
@@ -1039,7 +1038,7 @@ public final class NetworkTemplate implements Parcelable {
* @return this builder.
*/
@NonNull
- public Builder setRatType(@NetworkType int ratType) {
+ public Builder setRatType(int ratType) {
// Input will be validated with the match rule when building the template.
mRatType = ratType;
return this;
diff --git a/packages/ConnectivityT/framework-t/src/android/net/TrafficStats.java b/packages/ConnectivityT/framework-t/src/android/net/TrafficStats.java
index c2f0cdfb048c..bc836d857e3e 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/TrafficStats.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/TrafficStats.java
@@ -31,12 +31,9 @@ import android.media.MediaPlayer;
import android.os.Binder;
import android.os.Build;
import android.os.RemoteException;
+import android.os.StrictMode;
import android.util.Log;
-import com.android.server.NetworkManagementSocketTagger;
-
-import dalvik.system.SocketTagger;
-
import java.io.FileDescriptor;
import java.io.IOException;
import java.net.DatagramSocket;
@@ -56,6 +53,10 @@ import java.net.SocketException;
* use {@link NetworkStatsManager} instead.
*/
public class TrafficStats {
+ static {
+ System.loadLibrary("framework-connectivity-tiramisu-jni");
+ }
+
private static final String TAG = TrafficStats.class.getSimpleName();
/**
* The return value to indicate that the device does not support the statistic.
@@ -232,9 +233,68 @@ public class TrafficStats {
*/
@SystemApi(client = MODULE_LIBRARIES)
public static void attachSocketTagger() {
- NetworkManagementSocketTagger.install();
+ dalvik.system.SocketTagger.set(new SocketTagger());
+ }
+
+ private static class SocketTagger extends dalvik.system.SocketTagger {
+
+ // TODO: set to false
+ private static final boolean LOGD = true;
+
+ SocketTagger() {
+ }
+
+ @Override
+ public void tag(FileDescriptor fd) throws SocketException {
+ final UidTag tagInfo = sThreadUidTag.get();
+ if (LOGD) {
+ Log.d(TAG, "tagSocket(" + fd.getInt$() + ") with statsTag=0x"
+ + Integer.toHexString(tagInfo.tag) + ", statsUid=" + tagInfo.uid);
+ }
+ if (tagInfo.tag == -1) {
+ StrictMode.noteUntaggedSocket();
+ }
+
+ if (tagInfo.tag == -1 && tagInfo.uid == -1) return;
+ final int errno = native_tagSocketFd(fd, tagInfo.tag, tagInfo.uid);
+ if (errno < 0) {
+ Log.i(TAG, "tagSocketFd(" + fd.getInt$() + ", "
+ + tagInfo.tag + ", "
+ + tagInfo.uid + ") failed with errno" + errno);
+ }
+ }
+
+ @Override
+ public void untag(FileDescriptor fd) throws SocketException {
+ if (LOGD) {
+ Log.i(TAG, "untagSocket(" + fd.getInt$() + ")");
+ }
+
+ final UidTag tagInfo = sThreadUidTag.get();
+ if (tagInfo.tag == -1 && tagInfo.uid == -1) return;
+
+ final int errno = native_untagSocketFd(fd);
+ if (errno < 0) {
+ Log.w(TAG, "untagSocket(" + fd.getInt$() + ") failed with errno " + errno);
+ }
+ }
+ }
+
+ private static native int native_tagSocketFd(FileDescriptor fd, int tag, int uid);
+ private static native int native_untagSocketFd(FileDescriptor fd);
+
+ private static class UidTag {
+ public int tag = -1;
+ public int uid = -1;
}
+ private static ThreadLocal<UidTag> sThreadUidTag = new ThreadLocal<UidTag>() {
+ @Override
+ protected UidTag initialValue() {
+ return new UidTag();
+ }
+ };
+
/**
* Set active tag to use when accounting {@link Socket} traffic originating
* from the current thread. Only one active tag per thread is supported.
@@ -249,7 +309,7 @@ public class TrafficStats {
* @see #clearThreadStatsTag()
*/
public static void setThreadStatsTag(int tag) {
- NetworkManagementSocketTagger.setThreadSocketStatsTag(tag);
+ getAndSetThreadStatsTag(tag);
}
/**
@@ -267,7 +327,9 @@ public class TrafficStats {
* restore any existing values after a nested operation is finished
*/
public static int getAndSetThreadStatsTag(int tag) {
- return NetworkManagementSocketTagger.setThreadSocketStatsTag(tag);
+ final int old = sThreadUidTag.get().tag;
+ sThreadUidTag.get().tag = tag;
+ return old;
}
/**
@@ -327,7 +389,7 @@ public class TrafficStats {
* @see #setThreadStatsTag(int)
*/
public static int getThreadStatsTag() {
- return NetworkManagementSocketTagger.getThreadSocketStatsTag();
+ return sThreadUidTag.get().tag;
}
/**
@@ -337,7 +399,7 @@ public class TrafficStats {
* @see #setThreadStatsTag(int)
*/
public static void clearThreadStatsTag() {
- NetworkManagementSocketTagger.setThreadSocketStatsTag(-1);
+ sThreadUidTag.get().tag = -1;
}
/**
@@ -357,7 +419,7 @@ public class TrafficStats {
*/
@SuppressLint("RequiresPermission")
public static void setThreadStatsUid(int uid) {
- NetworkManagementSocketTagger.setThreadSocketStatsUid(uid);
+ sThreadUidTag.get().uid = uid;
}
/**
@@ -368,7 +430,7 @@ public class TrafficStats {
* @see #setThreadStatsUid(int)
*/
public static int getThreadStatsUid() {
- return NetworkManagementSocketTagger.getThreadSocketStatsUid();
+ return sThreadUidTag.get().uid;
}
/**
@@ -395,7 +457,7 @@ public class TrafficStats {
*/
@SuppressLint("RequiresPermission")
public static void clearThreadStatsUid() {
- NetworkManagementSocketTagger.setThreadSocketStatsUid(-1);
+ setThreadStatsUid(-1);
}
/**
diff --git a/packages/ConnectivityT/framework-t/src/com/android/server/NetworkManagementSocketTagger.java b/packages/ConnectivityT/framework-t/src/com/android/server/NetworkManagementSocketTagger.java
deleted file mode 100644
index 1eb52fb44629..000000000000
--- a/packages/ConnectivityT/framework-t/src/com/android/server/NetworkManagementSocketTagger.java
+++ /dev/null
@@ -1,146 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server;
-
-import android.os.StrictMode;
-import android.util.Log;
-
-import dalvik.system.SocketTagger;
-
-import java.io.FileDescriptor;
-import java.net.SocketException;
-
-/**
- * Assigns tags to sockets for traffic stats.
- * @hide
- */
-public final class NetworkManagementSocketTagger extends SocketTagger {
- private static final String TAG = "NetworkManagementSocketTagger";
- private static final boolean LOGD = false;
-
- private static ThreadLocal<SocketTags> threadSocketTags = new ThreadLocal<SocketTags>() {
- @Override
- protected SocketTags initialValue() {
- return new SocketTags();
- }
- };
-
- public static void install() {
- SocketTagger.set(new NetworkManagementSocketTagger());
- }
-
- public static int setThreadSocketStatsTag(int tag) {
- final int old = threadSocketTags.get().statsTag;
- threadSocketTags.get().statsTag = tag;
- return old;
- }
-
- public static int getThreadSocketStatsTag() {
- return threadSocketTags.get().statsTag;
- }
-
- public static int setThreadSocketStatsUid(int uid) {
- final int old = threadSocketTags.get().statsUid;
- threadSocketTags.get().statsUid = uid;
- return old;
- }
-
- public static int getThreadSocketStatsUid() {
- return threadSocketTags.get().statsUid;
- }
-
- @Override
- public void tag(FileDescriptor fd) throws SocketException {
- final SocketTags options = threadSocketTags.get();
- if (LOGD) {
- Log.d(TAG, "tagSocket(" + fd.getInt$() + ") with statsTag=0x"
- + Integer.toHexString(options.statsTag) + ", statsUid=" + options.statsUid);
- }
- if (options.statsTag == -1) {
- StrictMode.noteUntaggedSocket();
- }
- // TODO: skip tagging when options would be no-op
- tagSocketFd(fd, options.statsTag, options.statsUid);
- }
-
- private void tagSocketFd(FileDescriptor fd, int tag, int uid) {
- if (tag == -1 && uid == -1) return;
-
- final int errno = native_tagSocketFd(fd, tag, uid);
- if (errno < 0) {
- Log.i(TAG, "tagSocketFd(" + fd.getInt$() + ", "
- + tag + ", "
- + uid + ") failed with errno" + errno);
- }
- }
-
- @Override
- public void untag(FileDescriptor fd) throws SocketException {
- if (LOGD) {
- Log.i(TAG, "untagSocket(" + fd.getInt$() + ")");
- }
- unTagSocketFd(fd);
- }
-
- private void unTagSocketFd(FileDescriptor fd) {
- final SocketTags options = threadSocketTags.get();
- if (options.statsTag == -1 && options.statsUid == -1) return;
-
- final int errno = native_untagSocketFd(fd);
- if (errno < 0) {
- Log.w(TAG, "untagSocket(" + fd.getInt$() + ") failed with errno " + errno);
- }
- }
-
- public static class SocketTags {
- public int statsTag = -1;
- public int statsUid = -1;
- }
-
- public static void setKernelCounterSet(int uid, int counterSet) {
- final int errno = native_setCounterSet(counterSet, uid);
- if (errno < 0) {
- Log.w(TAG, "setKernelCountSet(" + uid + ", " + counterSet + ") failed with errno "
- + errno);
- }
- }
-
- public static void resetKernelUidStats(int uid) {
- int errno = native_deleteTagData(0, uid);
- if (errno < 0) {
- Log.w(TAG, "problem clearing counters for uid " + uid + " : errno " + errno);
- }
- }
-
- /**
- * Convert {@code /proc/} tag format to {@link Integer}. Assumes incoming
- * format like {@code 0x7fffffff00000000}.
- */
- public static int kernelToTag(String string) {
- int length = string.length();
- if (length > 10) {
- return Long.decode(string.substring(0, length - 8)).intValue();
- } else {
- return 0;
- }
- }
-
- private static native int native_tagSocketFd(FileDescriptor fd, int tag, int uid);
- private static native int native_untagSocketFd(FileDescriptor fd);
- private static native int native_setCounterSet(int uid, int counterSetNum);
- private static native int native_deleteTagData(int tag, int uid);
-}
diff --git a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsFactory.java b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsFactory.java
index 17f3455d20a2..668d1cba921b 100644
--- a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsFactory.java
+++ b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsFactory.java
@@ -22,8 +22,6 @@ import static android.net.NetworkStats.TAG_ALL;
import static android.net.NetworkStats.TAG_NONE;
import static android.net.NetworkStats.UID_ALL;
-import static com.android.server.NetworkManagementSocketTagger.kernelToTag;
-
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
@@ -470,6 +468,19 @@ public class NetworkStatsFactory {
}
/**
+ * Convert {@code /proc/} tag format to {@link Integer}. Assumes incoming
+ * format like {@code 0x7fffffff00000000}.
+ */
+ public static int kernelToTag(String string) {
+ int length = string.length();
+ if (length > 10) {
+ return Long.decode(string.substring(0, length - 8)).intValue();
+ } else {
+ return 0;
+ }
+ }
+
+ /**
* Parse statistics from file into given {@link NetworkStats} object. Values
* are expected to monotonically increase since device boot.
*/
diff --git a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java
index 8e584d084fb9..9f3371b724cf 100644
--- a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java
+++ b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java
@@ -20,9 +20,6 @@ import static android.Manifest.permission.NETWORK_STATS_PROVIDER;
import static android.Manifest.permission.READ_NETWORK_USAGE_HISTORY;
import static android.Manifest.permission.UPDATE_DEVICE_STATS;
import static android.app.usage.NetworkStatsManager.PREFIX_DEV;
-import static android.app.usage.NetworkStatsManager.PREFIX_UID;
-import static android.app.usage.NetworkStatsManager.PREFIX_UID_TAG;
-import static android.app.usage.NetworkStatsManager.PREFIX_XT;
import static android.content.Intent.ACTION_SHUTDOWN;
import static android.content.Intent.ACTION_UID_REMOVED;
import static android.content.Intent.ACTION_USER_REMOVED;
@@ -50,6 +47,9 @@ import static android.net.TrafficStats.KB_IN_BYTES;
import static android.net.TrafficStats.MB_IN_BYTES;
import static android.net.TrafficStats.UID_TETHERING;
import static android.net.TrafficStats.UNSUPPORTED;
+import static android.net.netstats.NetworkStatsDataMigrationUtils.PREFIX_UID;
+import static android.net.netstats.NetworkStatsDataMigrationUtils.PREFIX_UID_TAG;
+import static android.net.netstats.NetworkStatsDataMigrationUtils.PREFIX_XT;
import static android.os.Trace.TRACE_TAG_NETWORK;
import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
import static android.text.format.DateUtils.DAY_IN_MILLIS;
@@ -59,7 +59,6 @@ import static android.text.format.DateUtils.SECOND_IN_MILLIS;
import static com.android.net.module.util.NetworkCapabilitiesUtils.getDisplayTransport;
import static com.android.net.module.util.NetworkStatsUtils.LIMIT_GLOBAL_ALERT;
-import static com.android.server.NetworkManagementSocketTagger.resetKernelUidStats;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -121,6 +120,7 @@ import android.provider.Settings.Global;
import android.service.NetworkInterfaceProto;
import android.service.NetworkStatsServiceDumpProto;
import android.system.ErrnoException;
+import android.system.Os;
import android.telephony.PhoneStateListener;
import android.telephony.SubscriptionPlan;
import android.text.TextUtils;
@@ -546,6 +546,10 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
return null;
}
}
+
+ public TagStatsDeleter getTagStatsDeleter() {
+ return NetworkStatsService::nativeDeleteTagData;
+ }
}
/**
@@ -1801,7 +1805,10 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
// Clear kernel stats associated with UID
for (int uid : uids) {
- resetKernelUidStats(uid);
+ final int ret = mDeps.getTagStatsDeleter().deleteTagData(uid);
+ if (ret < 0) {
+ Log.w(TAG, "problem clearing counters for uid " + uid + ": " + Os.strerror(-ret));
+ }
}
}
@@ -2380,4 +2387,12 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
private static native long nativeGetTotalStat(int type);
private static native long nativeGetIfaceStat(String iface, int type);
private static native long nativeGetUidStat(int uid, int type);
+
+ // TODO: use BpfNetMaps to delete tag data and remove this.
+ @VisibleForTesting
+ interface TagStatsDeleter {
+ int deleteTagData(int uid);
+ }
+
+ private static native int nativeDeleteTagData(int uid);
}
diff --git a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsSubscriptionsMonitor.java b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsSubscriptionsMonitor.java
index 3e35e603e87e..5bba0b17aa42 100644
--- a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsSubscriptionsMonitor.java
+++ b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsSubscriptionsMonitor.java
@@ -24,7 +24,6 @@ import static android.telephony.TelephonyManager.NETWORK_TYPE_LTE;
import android.annotation.NonNull;
import android.content.Context;
-import android.telephony.Annotation;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyCallback;
import android.telephony.TelephonyDisplayInfo;
@@ -59,8 +58,7 @@ public class NetworkStatsSubscriptionsMonitor extends
* @param collapsedRatType collapsed RAT type.
* @see android.app.usage.NetworkStatsManager#getCollapsedRatType(int).
*/
- void onCollapsedRatTypeChanged(@NonNull String subscriberId,
- @Annotation.NetworkType int collapsedRatType);
+ void onCollapsedRatTypeChanged(@NonNull String subscriberId, int collapsedRatType);
}
private final Delegate mDelegate;
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java b/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java
index 1d0ae9912d97..b65e976c4829 100755
--- a/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java
@@ -37,7 +37,7 @@ import android.view.View;
import android.widget.Button;
import com.android.internal.app.AlertActivity;
-import com.android.internal.content.PackageHelper;
+import com.android.internal.content.InstallLocationUtils;
import java.io.File;
import java.io.FileInputStream;
@@ -154,8 +154,8 @@ public class InstallInstalling extends AlertActivity {
final PackageLite pkg = result.getResult();
params.setAppPackageName(pkg.getPackageName());
params.setInstallLocation(pkg.getInstallLocation());
- params.setSize(
- PackageHelper.calculateInstalledSize(pkg, params.abiOverride));
+ params.setSize(InstallLocationUtils.calculateInstalledSize(pkg,
+ params.abiOverride));
}
} catch (IOException e) {
Log.e(LOG_TAG,
diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp
index fcf2282160a7..684f4dee9ac2 100644
--- a/packages/SettingsLib/Android.bp
+++ b/packages/SettingsLib/Android.bp
@@ -50,6 +50,7 @@ android_library {
"SettingsLibSettingsTransition",
"SettingsLibActivityEmbedding",
"SettingsLibButtonPreference",
+ "setupdesign",
],
// ANDROIDMK TRANSLATION ERROR: unsupported assignment to LOCAL_SHARED_JAVA_LIBRARIES
diff --git a/packages/SettingsLib/AndroidManifest.xml b/packages/SettingsLib/AndroidManifest.xml
index a3473459948d..13f8a372c9b5 100644
--- a/packages/SettingsLib/AndroidManifest.xml
+++ b/packages/SettingsLib/AndroidManifest.xml
@@ -18,4 +18,10 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.settingslib">
+ <application>
+ <activity
+ android:name="com.android.settingslib.users.AvatarPickerActivity"
+ android:theme="@style/SudThemeGlifV2.DayNight"/>
+ </application>
+
</manifest>
diff --git a/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java b/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java
index e51bb45ebca2..3eb6ea98f118 100644
--- a/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java
+++ b/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java
@@ -42,6 +42,7 @@ public class FooterPreference extends Preference {
@VisibleForTesting
View.OnClickListener mLearnMoreListener;
private CharSequence mContentDescription;
+ private CharSequence mLearnMoreText;
private CharSequence mLearnMoreContentDescription;
private FooterLearnMoreSpan mLearnMoreSpan;
@@ -69,7 +70,12 @@ public class FooterPreference extends Preference {
TextView learnMore = holder.itemView.findViewById(R.id.settingslib_learn_more);
if (learnMore != null && mLearnMoreListener != null) {
learnMore.setVisibility(View.VISIBLE);
- SpannableString learnMoreText = new SpannableString(learnMore.getText());
+ if (TextUtils.isEmpty(mLearnMoreText)) {
+ mLearnMoreText = learnMore.getText();
+ } else {
+ learnMore.setText(mLearnMoreText);
+ }
+ SpannableString learnMoreText = new SpannableString(mLearnMoreText);
if (mLearnMoreSpan != null) {
learnMoreText.removeSpan(mLearnMoreSpan);
}
@@ -123,6 +129,18 @@ public class FooterPreference extends Preference {
}
/**
+ * Sets the learn more text.
+ *
+ * @param learnMoreText The string of the learn more text.
+ */
+ public void setLearnMoreText(CharSequence learnMoreText) {
+ if (!TextUtils.equals(mLearnMoreText, learnMoreText)) {
+ mLearnMoreText = learnMoreText;
+ notifyChanged();
+ }
+ }
+
+ /**
* To set content description of the learn more text. This can use for talkback
* environment if developer wants to have a customization content.
*
diff --git a/packages/SettingsLib/res/drawable/add_a_photo_circled.xml b/packages/SettingsLib/res/drawable/add_a_photo_circled.xml
new file mode 100644
index 000000000000..bcfd221e94c5
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/add_a_photo_circled.xml
@@ -0,0 +1,28 @@
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item>
+ <shape android:shape="oval">
+ <solid android:color="?android:attr/colorAccent"/>
+ </shape>
+ </item>
+ <item
+ android:left="@dimen/add_a_photo_circled_padding"
+ android:right="@dimen/add_a_photo_circled_padding"
+ android:top="@dimen/add_a_photo_circled_padding"
+ android:bottom="@dimen/add_a_photo_circled_padding"
+ android:drawable="@drawable/ic_add_a_photo"/>
+</layer-list>
diff --git a/packages/SettingsLib/res/drawable/avatar_choose_photo_circled.xml b/packages/SettingsLib/res/drawable/avatar_choose_photo_circled.xml
new file mode 100644
index 000000000000..97aec740ea43
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/avatar_choose_photo_circled.xml
@@ -0,0 +1,30 @@
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item>
+ <shape android:shape="oval">
+ <stroke
+ android:width="2dp"
+ android:color="?android:attr/colorPrimary"/>
+ </shape>
+ </item>
+ <item
+ android:left="@dimen/avatar_picker_icon_inset"
+ android:right="@dimen/avatar_picker_icon_inset"
+ android:top="@dimen/avatar_picker_icon_inset"
+ android:bottom="@dimen/avatar_picker_icon_inset"
+ android:drawable="@drawable/ic_avatar_choose_photo"/>
+</layer-list>
diff --git a/core/res/res/drawable-car/car_checkbox_background.xml b/packages/SettingsLib/res/drawable/avatar_selector.xml
index 69dcdbb0e94c..ccde59763a4a 100644
--- a/core/res/res/drawable-car/car_checkbox_background.xml
+++ b/packages/SettingsLib/res/drawable/avatar_selector.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- Copyright 2021 The Android Open Source Project
+ Copyright (C) 2022 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -13,19 +13,13 @@
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_focused="true" android:state_pressed="true">
- <shape android:shape="rectangle">
- <solid android:color="#8A0041BE" />
- <stroke android:width="4dp" android:color="#0041BE" />
- </shape>
- </item>
- <item android:state_focused="true">
- <shape android:shape="rectangle">
- <solid android:color="#3D0059B3" />
- <stroke android:width="8dp" android:color="#0059B3" />
+ <item android:state_selected="true">
+ <shape android:shape="oval">
+ <stroke
+ android:color="?android:attr/colorPrimary"
+ android:width="@dimen/avatar_picker_padding"/>
</shape>
</item>
-</selector>
+</selector> \ No newline at end of file
diff --git a/packages/SettingsLib/res/drawable/avatar_take_photo_circled.xml b/packages/SettingsLib/res/drawable/avatar_take_photo_circled.xml
new file mode 100644
index 000000000000..7033aaeec911
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/avatar_take_photo_circled.xml
@@ -0,0 +1,30 @@
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item>
+ <shape android:shape="oval">
+ <stroke
+ android:width="2dp"
+ android:color="?android:attr/colorPrimary"/>
+ </shape>
+ </item>
+ <item
+ android:left="@dimen/avatar_picker_icon_inset"
+ android:right="@dimen/avatar_picker_icon_inset"
+ android:top="@dimen/avatar_picker_icon_inset"
+ android:bottom="@dimen/avatar_picker_icon_inset"
+ android:drawable="@drawable/ic_avatar_take_photo"/>
+</layer-list>
diff --git a/packages/SettingsLib/res/drawable/ic_account_circle_outline.xml b/packages/SettingsLib/res/drawable/ic_account_circle_outline.xml
new file mode 100644
index 000000000000..0cc54b6b4972
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_account_circle_outline.xml
@@ -0,0 +1,25 @@
+<!--
+ Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="?android:attr/colorPrimary"
+ android:pathData="M5.85,17.1q1.275,-0.975 2.85,-1.538Q10.275,15 12,15q1.725,0 3.3,0.563 1.575,0.562 2.85,1.537 0.875,-1.025 1.363,-2.325Q20,13.475 20,12q0,-3.325 -2.337,-5.662Q15.325,4 12,4T6.338,6.338Q4,8.675 4,12q0,1.475 0.487,2.775 0.488,1.3 1.363,2.325zM12,13q-1.475,0 -2.488,-1.012Q8.5,10.975 8.5,9.5t1.012,-2.487Q10.525,6 12,6t2.488,1.013Q15.5,8.024 15.5,9.5t-1.012,2.488Q13.475,13 12,13zM12,22q-2.075,0 -3.9,-0.788 -1.825,-0.787 -3.175,-2.137 -1.35,-1.35 -2.137,-3.175Q2,14.075 2,12t0.788,-3.9q0.787,-1.825 2.137,-3.175 1.35,-1.35 3.175,-2.137Q9.925,2 12,2t3.9,0.788q1.825,0.787 3.175,2.137 1.35,1.35 2.137,3.175Q22,9.925 22,12t-0.788,3.9q-0.787,1.825 -2.137,3.175 -1.35,1.35 -3.175,2.137Q14.075,22 12,22zM12,20q1.325,0 2.5,-0.387 1.175,-0.388 2.15,-1.113 -0.975,-0.725 -2.15,-1.113Q13.325,17 12,17t-2.5,0.387q-1.175,0.388 -2.15,1.113 0.975,0.725 2.15,1.113Q10.675,20 12,20zM12,11q0.65,0 1.075,-0.425 0.425,-0.425 0.425,-1.075 0,-0.65 -0.425,-1.075Q12.65,8 12,8q-0.65,0 -1.075,0.425Q10.5,8.85 10.5,9.5q0,0.65 0.425,1.075Q11.35,11 12,11zM12,9.5zM12,18.5z"/>
+</vector>
+
diff --git a/packages/SettingsLib/res/drawable/ic_add_a_photo.xml b/packages/SettingsLib/res/drawable/ic_add_a_photo.xml
new file mode 100644
index 000000000000..4e355031befe
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_add_a_photo.xml
@@ -0,0 +1,24 @@
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="?android:attr/colorPrimary"
+ android:pathData="M11.5,17.5q1.875,0 3.188,-1.313Q16,14.876 16,13q0,-1.875 -1.313,-3.188Q13.376,8.5 11.5,8.5q-1.875,0 -3.188,1.313Q7,11.124 7,13q0,1.875 1.313,3.188Q9.624,17.5 11.5,17.5zM3.5,21q-0.825,0 -1.413,-0.587Q1.5,19.825 1.5,19L1.5,7q0,-0.825 0.587,-1.412Q2.675,5 3.5,5h3.15L8.5,3h6v4h-11v12h16v-9h2v9q0,0.825 -0.587,1.413Q20.325,21 19.5,21zM18.5,8L18.5,6h-2L16.5,4h2L18.5,2h2v2h2v2h-2v2zM11.5,13z"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_avatar_choose_photo.xml b/packages/SettingsLib/res/drawable/ic_avatar_choose_photo.xml
new file mode 100644
index 000000000000..b85fdc2de597
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_avatar_choose_photo.xml
@@ -0,0 +1,24 @@
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="?android:attr/colorPrimary"
+ android:pathData="M9,14h10l-3.45,-4.5 -2.3,3 -1.55,-2zM8,18q-0.825,0 -1.412,-0.587Q6,16.825 6,16L6,4q0,-0.825 0.588,-1.413Q7.175,2 8,2h12q0.825,0 1.413,0.587Q22,3.175 22,4v12q0,0.825 -0.587,1.413Q20.825,18 20,18zM8,16h12L20,4L8,4v12zM4,22q-0.825,0 -1.413,-0.587Q2,20.825 2,20L2,6h2v14h14v2zM8,4v12L8,4z"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_avatar_take_photo.xml b/packages/SettingsLib/res/drawable/ic_avatar_take_photo.xml
new file mode 100644
index 000000000000..5c56276ec4f3
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_avatar_take_photo.xml
@@ -0,0 +1,24 @@
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="?android:attr/colorPrimary"
+ android:pathData="M12,17.5q1.875,0 3.188,-1.313Q16.5,14.876 16.5,13q0,-1.875 -1.313,-3.188Q13.876,8.5 12,8.5q-1.875,0 -3.188,1.313Q7.5,11.124 7.5,13q0,1.875 1.313,3.188Q10.124,17.5 12,17.5zM4,21q-0.825,0 -1.413,-0.587Q2,19.825 2,19L2,7q0,-0.825 0.587,-1.412Q3.175,5 4,5h3.15L9,3h6l1.85,2L20,5q0.825,0 1.413,0.588Q22,6.175 22,7v12q0,0.825 -0.587,1.413Q20.825,21 20,21zM20,19L20,7L4,7v12zM4,19L4,7v12z"/>
+</vector>
diff --git a/core/res/res/drawable-car/car_checkbox.xml b/packages/SettingsLib/res/layout/avatar_item.xml
index 083a7aa193aa..c52f6648cb7e 100644
--- a/core/res/res/drawable-car/car_checkbox.xml
+++ b/packages/SettingsLib/res/layout/avatar_item.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- Copyright 2019 The Android Open Source Project
+ Copyright (C) 2022 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -13,15 +13,12 @@
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.
--->
-
-<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
- <item
- android:width="@*android:dimen/car_primary_icon_size"
- android:height="@*android:dimen/car_primary_icon_size"
- android:drawable="@drawable/btn_check_material_anim"/>
- <item
- android:width="@*android:dimen/car_primary_icon_size"
- android:height="@*android:dimen/car_primary_icon_size"
- android:drawable="@drawable/car_checkbox_background"/>
-</layer-list>
+ -->
+<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/avatar_image"
+ android:layout_height="@dimen/avatar_size_in_picker"
+ android:layout_width="@dimen/avatar_size_in_picker"
+ android:layout_margin="@dimen/avatar_picker_margin"
+ android:layout_gravity="center"
+ android:padding="@dimen/avatar_picker_padding"
+ android:background="@drawable/avatar_selector"/>
diff --git a/packages/SettingsLib/res/layout/avatar_picker.xml b/packages/SettingsLib/res/layout/avatar_picker.xml
new file mode 100644
index 000000000000..2d40bd0de7ea
--- /dev/null
+++ b/packages/SettingsLib/res/layout/avatar_picker.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+<com.google.android.setupdesign.GlifLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/glif_layout"
+ android:layout_height="match_parent"
+ android:layout_width="match_parent"
+ android:icon="@drawable/ic_account_circle_outline"
+ app:sucUsePartnerResource="true"
+ app:sucHeaderText="@string/avatar_picker_title">
+
+ <LinearLayout
+ android:layout_height="match_parent"
+ android:layout_width="match_parent"
+ android:gravity="center_horizontal"
+ style="@style/SudContentFrame">
+ <androidx.recyclerview.widget.RecyclerView
+ android:id="@+id/avatar_grid"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+ </LinearLayout>
+
+</com.google.android.setupdesign.GlifLayout> \ No newline at end of file
diff --git a/packages/SettingsLib/res/layout/edit_user_info_dialog_content.xml b/packages/SettingsLib/res/layout/edit_user_info_dialog_content.xml
index f66ff007fb90..c8ddcc870e46 100644
--- a/packages/SettingsLib/res/layout/edit_user_info_dialog_content.xml
+++ b/packages/SettingsLib/res/layout/edit_user_info_dialog_content.xml
@@ -18,24 +18,32 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:baselineAligned="false"
- android:orientation="horizontal"
+ android:orientation="vertical"
android:padding="16dp">
- <ImageView
- android:id="@+id/user_photo"
- android:layout_width="56dp"
- android:layout_height="56dp"
- android:layout_gravity="bottom"
- android:contentDescription="@string/user_image_photo_selector"
- android:background="@*android:drawable/spinner_background_holo_dark"
- android:scaleType="fitCenter"/>
+ <FrameLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center">
+ <ImageView
+ android:id="@+id/user_photo"
+ android:layout_width="@dimen/user_photo_size_in_profile_info_dialog"
+ android:layout_height="@dimen/user_photo_size_in_profile_info_dialog"
+ android:contentDescription="@string/user_image_photo_selector"
+ android:scaleType="fitCenter"/>
+ <ImageView
+ android:id="@+id/add_a_photo_icon"
+ android:layout_width="@dimen/add_a_photo_icon_size_in_profile_info_dialog"
+ android:layout_height="@dimen/add_a_photo_icon_size_in_profile_info_dialog"
+ android:src="@drawable/add_a_photo_circled"
+ android:layout_gravity="bottom|right" />
+ </FrameLayout>
<EditText
android:id="@+id/user_name"
- android:layout_width="0dp"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_gravity="bottom"
- android:layout_weight="1"
+ android:layout_gravity="center"
android:minWidth="200dp"
android:layout_marginStart="6dp"
android:minHeight="@dimen/min_tap_target_size"
diff --git a/packages/SettingsLib/res/values-w1280dp-land/dimens.xml b/packages/SettingsLib/res/values-w1280dp-land/dimens.xml
new file mode 100644
index 000000000000..4097d0564251
--- /dev/null
+++ b/packages/SettingsLib/res/values-w1280dp-land/dimens.xml
@@ -0,0 +1,21 @@
+<!--
+ Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+<resources>
+ <integer name="avatar_picker_columns">4</integer>
+ <dimen name="avatar_size_in_picker">104dp</dimen>
+ <dimen name="avatar_picker_padding">8dp</dimen>
+ <dimen name="avatar_picker_margin">3dp</dimen>
+</resources> \ No newline at end of file
diff --git a/packages/SettingsLib/res/values-w1440dp-land/dimens.xml b/packages/SettingsLib/res/values-w1440dp-land/dimens.xml
new file mode 100644
index 000000000000..764870efe222
--- /dev/null
+++ b/packages/SettingsLib/res/values-w1440dp-land/dimens.xml
@@ -0,0 +1,21 @@
+<!--
+ Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+<resources>
+ <integer name="avatar_picker_columns">4</integer>
+ <dimen name="avatar_size_in_picker">128dp</dimen>
+ <dimen name="avatar_picker_padding">8dp</dimen>
+ <dimen name="avatar_picker_margin">4dp</dimen>
+</resources> \ No newline at end of file
diff --git a/packages/SettingsLib/res/values-w1600dp-land/dimens.xml b/packages/SettingsLib/res/values-w1600dp-land/dimens.xml
new file mode 100644
index 000000000000..872b88a3776d
--- /dev/null
+++ b/packages/SettingsLib/res/values-w1600dp-land/dimens.xml
@@ -0,0 +1,21 @@
+<!--
+ Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+<resources>
+ <integer name="avatar_picker_columns">4</integer>
+ <dimen name="avatar_size_in_picker">148dp</dimen>
+ <dimen name="avatar_picker_padding">8dp</dimen>
+ <dimen name="avatar_picker_margin">6dp</dimen>
+</resources> \ No newline at end of file
diff --git a/packages/SettingsLib/res/values-w480dp-port/dimens.xml b/packages/SettingsLib/res/values-w480dp-port/dimens.xml
new file mode 100644
index 000000000000..cab78d64ce8d
--- /dev/null
+++ b/packages/SettingsLib/res/values-w480dp-port/dimens.xml
@@ -0,0 +1,21 @@
+<!--
+ Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+<resources>
+ <integer name="avatar_picker_columns">3</integer>
+ <dimen name="avatar_size_in_picker">112dp</dimen>
+ <dimen name="avatar_picker_padding">8dp</dimen>
+ <dimen name="avatar_picker_margin">3dp</dimen>
+</resources>
diff --git a/packages/SettingsLib/res/values-w600dp-port/dimens.xml b/packages/SettingsLib/res/values-w600dp-port/dimens.xml
new file mode 100644
index 000000000000..4097d0564251
--- /dev/null
+++ b/packages/SettingsLib/res/values-w600dp-port/dimens.xml
@@ -0,0 +1,21 @@
+<!--
+ Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+<resources>
+ <integer name="avatar_picker_columns">4</integer>
+ <dimen name="avatar_size_in_picker">104dp</dimen>
+ <dimen name="avatar_picker_padding">8dp</dimen>
+ <dimen name="avatar_picker_margin">3dp</dimen>
+</resources> \ No newline at end of file
diff --git a/packages/SettingsLib/res/values-w720dp-port/dimens.xml b/packages/SettingsLib/res/values-w720dp-port/dimens.xml
new file mode 100644
index 000000000000..764870efe222
--- /dev/null
+++ b/packages/SettingsLib/res/values-w720dp-port/dimens.xml
@@ -0,0 +1,21 @@
+<!--
+ Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+<resources>
+ <integer name="avatar_picker_columns">4</integer>
+ <dimen name="avatar_size_in_picker">128dp</dimen>
+ <dimen name="avatar_picker_padding">8dp</dimen>
+ <dimen name="avatar_picker_margin">4dp</dimen>
+</resources> \ No newline at end of file
diff --git a/packages/SettingsLib/res/values-w840dp-port/dimens.xml b/packages/SettingsLib/res/values-w840dp-port/dimens.xml
new file mode 100644
index 000000000000..872b88a3776d
--- /dev/null
+++ b/packages/SettingsLib/res/values-w840dp-port/dimens.xml
@@ -0,0 +1,21 @@
+<!--
+ Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+<resources>
+ <integer name="avatar_picker_columns">4</integer>
+ <dimen name="avatar_size_in_picker">148dp</dimen>
+ <dimen name="avatar_picker_padding">8dp</dimen>
+ <dimen name="avatar_picker_margin">6dp</dimen>
+</resources> \ No newline at end of file
diff --git a/packages/SettingsLib/res/values-w960dp-land/dimens.xml b/packages/SettingsLib/res/values-w960dp-land/dimens.xml
new file mode 100644
index 000000000000..8403dba54329
--- /dev/null
+++ b/packages/SettingsLib/res/values-w960dp-land/dimens.xml
@@ -0,0 +1,21 @@
+<!--
+ Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+<resources>
+ <integer name="avatar_picker_columns">4</integer>
+ <dimen name="avatar_size_in_picker">96dp</dimen>
+ <dimen name="avatar_picker_padding">6dp</dimen>
+ <dimen name="avatar_picker_margin">2dp</dimen>
+</resources>
diff --git a/packages/SettingsLib/res/values/arrays.xml b/packages/SettingsLib/res/values/arrays.xml
index 2b5e9cdc017d..93e3deef7aa5 100644
--- a/packages/SettingsLib/res/values/arrays.xml
+++ b/packages/SettingsLib/res/values/arrays.xml
@@ -647,4 +647,6 @@
<item>disabled</item>
</array>
+ <array name="avatar_images"/>
+
</resources>
diff --git a/packages/SettingsLib/res/values/dimens.xml b/packages/SettingsLib/res/values/dimens.xml
index 9bccc3f1d864..120df76218b3 100644
--- a/packages/SettingsLib/res/values/dimens.xml
+++ b/packages/SettingsLib/res/values/dimens.xml
@@ -98,4 +98,15 @@
<!-- Minimum width for the popup for updating a user's photo. -->
<dimen name="update_user_photo_popup_min_width">300dp</dimen>
+ <dimen name="add_a_photo_circled_padding">6dp</dimen>
+ <dimen name="user_photo_size_in_profile_info_dialog">112dp</dimen>
+ <dimen name="add_a_photo_icon_size_in_profile_info_dialog">32dp</dimen>
+
+ <integer name="avatar_picker_columns">3</integer>
+ <dimen name="avatar_size_in_picker">96dp</dimen>
+ <dimen name="avatar_picker_padding">6dp</dimen>
+ <dimen name="avatar_picker_margin">2dp</dimen>
+
+ <dimen name="avatar_picker_icon_inset">25dp</dimen>
+
</resources>
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index e4eab4b20109..af6a658f362a 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -1164,6 +1164,9 @@
<!-- Summary for settings preference disabled by administrator [CHAR LIMIT=50] -->
<string name="disabled_by_admin_summary_text">Controlled by admin</string>
+ <!-- Summary for settings preference disabled by app ops [CHAR LIMIT=50] -->
+ <string name="disabled_by_app_ops_text">Controlled by Restricted Setting</string>
+
<!-- [CHAR LIMIT=25] Manage applications, text telling using an application is disabled. -->
<string name="disabled">Disabled</string>
<!-- Summary of app trusted to install apps [CHAR LIMIT=45] -->
@@ -1545,4 +1548,18 @@
<!-- Content description of the no calling for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_no_calling">No calling.</string>
+
+ <!-- Screensaver overlay which displays the time. [CHAR LIMIT=20] -->
+ <string name="dream_complication_title_time">Time</string>
+ <!-- Screensaver overlay which displays the date. [CHAR LIMIT=20] -->
+ <string name="dream_complication_title_date">Date</string>
+ <!-- Screensaver overlay which displays the weather. [CHAR LIMIT=20] -->
+ <string name="dream_complication_title_weather">Weather</string>
+ <!-- Screensaver overlay which displays air quality. [CHAR LIMIT=20] -->
+ <string name="dream_complication_title_aqi">Air Quality</string>
+ <!-- Screensaver overlay which displays cast info. [CHAR LIMIT=20] -->
+ <string name="dream_complication_title_cast_info">Cast Info</string>
+
+ <!-- Title for a screen allowing the user to choose a profile picture. [CHAR LIMIT=NONE] -->
+ <string name="avatar_picker_title">Choose a profile picture</string>
</resources>
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java
index 1e8cb9fc4622..1573edbbfae9 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java
@@ -26,14 +26,17 @@ import android.app.AppGlobals;
import android.app.admin.DevicePolicyManager;
import android.content.ComponentName;
import android.content.Context;
+import android.content.Intent;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
+import android.os.Build;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
+import android.provider.Settings;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.style.ForegroundColorSpan;
@@ -42,6 +45,7 @@ import android.util.Log;
import android.view.MenuItem;
import android.widget.TextView;
+import androidx.annotation.RequiresApi;
import androidx.annotation.VisibleForTesting;
import com.android.internal.widget.LockPatternUtils;
@@ -729,6 +733,26 @@ public class RestrictedLockUtilsInternal extends RestrictedLockUtils {
}
/**
+ * Show restricted setting dialog.
+ */
+ @RequiresApi(Build.VERSION_CODES.TIRAMISU)
+ public static void sendShowRestrictedSettingDialogIntent(Context context,
+ String packageName, int uid) {
+ final Intent intent = getShowRestrictedSettingsIntent(packageName, uid);
+ context.startActivity(intent);
+ }
+
+ /**
+ * Get restricted settings dialog intent.
+ */
+ private static Intent getShowRestrictedSettingsIntent(String packageName, int uid) {
+ final Intent intent = new Intent(Settings.ACTION_SHOW_RESTRICTED_SETTING_DIALOG);
+ intent.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName);
+ intent.putExtra(Intent.EXTRA_UID, uid);
+ return intent;
+ }
+
+ /**
* Static {@link LockPatternUtils} and {@link DevicePolicyManager} wrapper for testing purposes.
* {@link LockPatternUtils} is an internal API not supported by robolectric.
* {@link DevicePolicyManager} has a {@code getProfileParent} not yet suppored by robolectric.
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java
index fc8b5879c5fa..81146fa9ffd1 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java
@@ -19,6 +19,7 @@ package com.android.settingslib;
import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
import android.content.Context;
+import android.os.Process;
import android.os.UserHandle;
import android.util.AttributeSet;
import android.view.View;
@@ -37,9 +38,14 @@ public class RestrictedPreference extends TwoTargetPreference {
RestrictedPreferenceHelper mHelper;
public RestrictedPreference(Context context, AttributeSet attrs,
- int defStyleAttr, int defStyleRes) {
+ int defStyleAttr, int defStyleRes, String packageName, int uid) {
super(context, attrs, defStyleAttr, defStyleRes);
- mHelper = new RestrictedPreferenceHelper(context, this, attrs);
+ mHelper = new RestrictedPreferenceHelper(context, this, attrs, packageName, uid);
+ }
+
+ public RestrictedPreference(Context context, AttributeSet attrs,
+ int defStyleAttr, int defStyleRes) {
+ this(context, attrs, defStyleAttr, defStyleRes, null, Process.INVALID_UID);
}
public RestrictedPreference(Context context, AttributeSet attrs, int defStyleAttr) {
@@ -55,6 +61,11 @@ public class RestrictedPreference extends TwoTargetPreference {
this(context, null);
}
+ public RestrictedPreference(Context context, String packageName, int uid) {
+ this(context, null, TypedArrayUtils.getAttr(context, R.attr.preferenceStyle,
+ android.R.attr.preferenceStyle), 0, packageName, uid);
+ }
+
@Override
protected int getSecondTargetResId() {
return R.layout.restricted_icon;
@@ -115,7 +126,21 @@ public class RestrictedPreference extends TwoTargetPreference {
}
}
+ public void setDisabledByAppOps(boolean disabled) {
+ if (mHelper.setDisabledByAppOps(disabled)) {
+ notifyChanged();
+ }
+ }
+
public boolean isDisabledByAdmin() {
return mHelper.isDisabledByAdmin();
}
+
+ public int getUid() {
+ return mHelper != null ? mHelper.uid : Process.INVALID_UID;
+ }
+
+ public String getPackageName() {
+ return mHelper != null ? mHelper.packageName : null;
+ }
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java
index 83a6973ffec6..f7b297461f15 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java
@@ -16,10 +16,14 @@
package com.android.settingslib;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.CONTROLLED_BY_ADMIN_SUMMARY;
+
import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
+import android.app.admin.DevicePolicyManager;
import android.content.Context;
import android.content.res.TypedArray;
+import android.os.Build;
import android.os.UserHandle;
import android.text.TextUtils;
import android.util.AttributeSet;
@@ -29,6 +33,8 @@ import android.widget.TextView;
import androidx.preference.Preference;
import androidx.preference.PreferenceViewHolder;
+import com.android.internal.util.Preconditions;
+
/**
* Helper class for managing settings preferences that can be disabled
* by device admins via user restrictions.
@@ -36,16 +42,22 @@ import androidx.preference.PreferenceViewHolder;
public class RestrictedPreferenceHelper {
private final Context mContext;
private final Preference mPreference;
+ final String packageName;
+ final int uid;
private boolean mDisabledByAdmin;
private EnforcedAdmin mEnforcedAdmin;
private String mAttrUserRestriction = null;
- private boolean mUseAdminDisabledSummary = false;
+ private boolean mDisabledSummary = false;
+
+ private boolean mDisabledByAppOps;
public RestrictedPreferenceHelper(Context context, Preference preference,
- AttributeSet attrs) {
+ AttributeSet attrs, String packageName, int uid) {
mContext = context;
mPreference = preference;
+ this.packageName = packageName;
+ this.uid = uid;
if (attrs != null) {
final TypedArray attributes = context.obtainStyledAttributes(attrs,
@@ -71,27 +83,37 @@ public class RestrictedPreferenceHelper {
final TypedValue useAdminDisabledSummary =
attributes.peekValue(R.styleable.RestrictedPreference_useAdminDisabledSummary);
if (useAdminDisabledSummary != null) {
- mUseAdminDisabledSummary =
+ mDisabledSummary =
(useAdminDisabledSummary.type == TypedValue.TYPE_INT_BOOLEAN
&& useAdminDisabledSummary.data != 0);
}
}
}
+ public RestrictedPreferenceHelper(Context context, Preference preference,
+ AttributeSet attrs) {
+ this(context, preference, attrs, null, android.os.Process.INVALID_UID);
+ }
+
/**
* Modify PreferenceViewHolder to add padlock if restriction is disabled.
*/
public void onBindViewHolder(PreferenceViewHolder holder) {
- if (mDisabledByAdmin) {
+ if (mDisabledByAdmin || mDisabledByAppOps) {
holder.itemView.setEnabled(true);
}
- if (mUseAdminDisabledSummary) {
+ if (mDisabledSummary) {
final TextView summaryView = (TextView) holder.findViewById(android.R.id.summary);
if (summaryView != null) {
- final CharSequence disabledText = summaryView.getContext().getText(
- R.string.disabled_by_admin_summary_text);
+ final CharSequence disabledText = mContext
+ .getSystemService(DevicePolicyManager.class)
+ .getString(CONTROLLED_BY_ADMIN_SUMMARY,
+ () -> summaryView.getContext().getString(
+ R.string.disabled_by_admin_summary_text));
if (mDisabledByAdmin) {
summaryView.setText(disabledText);
+ } else if (mDisabledByAppOps) {
+ summaryView.setText(R.string.disabled_by_app_ops_text);
} else if (TextUtils.equals(disabledText, summaryView.getText())) {
// It's previously set to disabled text, clear it.
summaryView.setText(null);
@@ -101,7 +123,7 @@ public class RestrictedPreferenceHelper {
}
public void useAdminDisabledSummary(boolean useSummary) {
- mUseAdminDisabledSummary = useSummary;
+ mDisabledSummary = useSummary;
}
/**
@@ -109,11 +131,19 @@ public class RestrictedPreferenceHelper {
*
* @return true if the method handled the click.
*/
+ @SuppressWarnings("NewApi")
public boolean performClick() {
if (mDisabledByAdmin) {
RestrictedLockUtils.sendShowAdminSupportDetailsIntent(mContext, mEnforcedAdmin);
return true;
}
+ if (mDisabledByAppOps) {
+ Preconditions.checkState(Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU,
+ "Build SDK version needs >= T");
+ RestrictedLockUtilsInternal.sendShowRestrictedSettingDialogIntent(mContext, packageName,
+ uid);
+ return true;
+ }
return false;
}
@@ -166,14 +196,34 @@ public class RestrictedPreferenceHelper {
changed = true;
}
- if (!(mPreference instanceof RestrictedTopLevelPreference)) {
- mPreference.setEnabled(!disabled);
+ updateDisabledState();
+
+ return changed;
+ }
+
+ public boolean setDisabledByAppOps(boolean disabled) {
+ boolean changed = false;
+ if (mDisabledByAppOps != disabled) {
+ mDisabledByAppOps = disabled;
+ changed = true;
}
+ updateDisabledState();
+
return changed;
}
public boolean isDisabledByAdmin() {
return mDisabledByAdmin;
}
+
+ public boolean isDisabledByAppOps() {
+ return mDisabledByAppOps;
+ }
+
+ private void updateDisabledState() {
+ if (!(mPreference instanceof RestrictedTopLevelPreference)) {
+ mPreference.setEnabled(!(mDisabledByAdmin || mDisabledByAppOps));
+ }
+ }
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/Utils.java b/packages/SettingsLib/src/com/android/settingslib/Utils.java
index d73e45eba5dc..883e0806f5f5 100644
--- a/packages/SettingsLib/src/com/android/settingslib/Utils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/Utils.java
@@ -1,7 +1,10 @@
package com.android.settingslib;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_USER_LABEL;
+
import android.annotation.ColorInt;
import android.annotation.Nullable;
+import android.app.admin.DevicePolicyManager;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
@@ -124,7 +127,8 @@ public class Utils {
String name = info != null ? info.name : null;
if (info.isManagedProfile()) {
// We use predefined values for managed profiles
- return context.getString(R.string.managed_user_title);
+ return context.getSystemService(DevicePolicyManager.class).getString(
+ WORK_PROFILE_USER_LABEL, () -> context.getString(R.string.managed_user_title));
} else if (info.isGuest()) {
name = context.getString(R.string.user_guest);
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/AppIconCacheManager.java b/packages/SettingsLib/src/com/android/settingslib/applications/AppIconCacheManager.java
new file mode 100644
index 000000000000..9dfc8eaac024
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/AppIconCacheManager.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.applications;
+
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.os.UserHandle;
+import android.util.Log;
+import android.util.LruCache;
+
+/**
+ * Cache app icon for management.
+ */
+public class AppIconCacheManager {
+ private static final String TAG = "AppIconCacheManager";
+ private static final float CACHE_RATIO = 0.1f;
+ private static final int MAX_CACHE_SIZE_IN_KB = getMaxCacheInKb();
+ private static final String DELIMITER = ":";
+ private static AppIconCacheManager sAppIconCacheManager;
+ private final LruCache<String, Drawable> mDrawableCache;
+
+ private AppIconCacheManager() {
+ mDrawableCache = new LruCache<String, Drawable>(MAX_CACHE_SIZE_IN_KB) {
+ @Override
+ protected int sizeOf(String key, Drawable drawable) {
+ if (drawable instanceof BitmapDrawable) {
+ return ((BitmapDrawable) drawable).getBitmap().getByteCount() / 1024;
+ }
+ // Rough estimate each pixel will use 4 bytes by default.
+ return drawable.getIntrinsicHeight() * drawable.getIntrinsicWidth() * 4 / 1024;
+ }
+ };
+ }
+
+ /**
+ * Get an {@link AppIconCacheManager} instance.
+ */
+ public static synchronized AppIconCacheManager getInstance() {
+ if (sAppIconCacheManager == null) {
+ sAppIconCacheManager = new AppIconCacheManager();
+ }
+ return sAppIconCacheManager;
+ }
+
+ /**
+ * Put app icon to cache
+ *
+ * @param packageName of icon
+ * @param uid of packageName
+ * @param drawable app icon
+ */
+ public void put(String packageName, int uid, Drawable drawable) {
+ final String key = getKey(packageName, uid);
+ if (key == null || drawable == null || drawable.getIntrinsicHeight() < 0
+ || drawable.getIntrinsicWidth() < 0) {
+ Log.w(TAG, "Invalid key or drawable.");
+ return;
+ }
+ mDrawableCache.put(key, drawable);
+ }
+
+ /**
+ * Get app icon from cache.
+ *
+ * @param packageName of icon
+ * @param uid of packageName
+ * @return app icon
+ */
+ public Drawable get(String packageName, int uid) {
+ final String key = getKey(packageName, uid);
+ if (key == null) {
+ Log.w(TAG, "Invalid key with package or uid.");
+ return null;
+ }
+ final Drawable cachedDrawable = mDrawableCache.get(key);
+ return cachedDrawable != null ? cachedDrawable.mutate() : null;
+ }
+
+ /**
+ * Release cache.
+ */
+ public static void release() {
+ if (sAppIconCacheManager != null) {
+ sAppIconCacheManager.mDrawableCache.evictAll();
+ }
+ }
+
+ private static String getKey(String packageName, int uid) {
+ if (packageName == null || uid < 0) {
+ return null;
+ }
+ return packageName + DELIMITER + UserHandle.getUserId(uid);
+ }
+
+ private static int getMaxCacheInKb() {
+ return Math.round(CACHE_RATIO * Runtime.getRuntime().maxMemory() / 1024);
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/AppUtils.java b/packages/SettingsLib/src/com/android/settingslib/applications/AppUtils.java
index a5da8b6bd15e..cc4fef8399c3 100644
--- a/packages/SettingsLib/src/com/android/settingslib/applications/AppUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/AppUtils.java
@@ -25,6 +25,7 @@ import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
+import android.graphics.drawable.Drawable;
import android.hardware.usb.IUsbManager;
import android.net.Uri;
import android.os.Environment;
@@ -35,7 +36,9 @@ import android.text.TextUtils;
import android.util.Log;
import com.android.settingslib.R;
+import com.android.settingslib.Utils;
import com.android.settingslib.applications.instantapps.InstantAppDataProvider;
+import com.android.settingslib.utils.ThreadUtils;
import java.util.ArrayList;
import java.util.List;
@@ -212,4 +215,82 @@ public class AppUtils {
UserHandle.myUserId());
return TextUtils.equals(packageName, defaultBrowserPackage);
}
+
+ /**
+ * Get the app icon by app entry.
+ *
+ * @param context caller's context
+ * @param appEntry AppEntry of ApplicationsState
+ * @return app icon of the app entry
+ */
+ public static Drawable getIcon(Context context, ApplicationsState.AppEntry appEntry) {
+ if (appEntry == null || appEntry.info == null) {
+ return null;
+ }
+
+ final AppIconCacheManager appIconCacheManager = AppIconCacheManager.getInstance();
+ final String packageName = appEntry.info.packageName;
+ final int uid = appEntry.info.uid;
+
+ Drawable icon = appIconCacheManager.get(packageName, uid);
+ if (icon == null) {
+ if (appEntry.apkFile != null && appEntry.apkFile.exists()) {
+ icon = Utils.getBadgedIcon(context, appEntry.info);
+ appIconCacheManager.put(packageName, uid, icon);
+ } else {
+ setAppEntryMounted(appEntry, /* mounted= */ false);
+ icon = context.getDrawable(
+ com.android.internal.R.drawable.sym_app_on_sd_unavailable_icon);
+ }
+ } else if (!appEntry.mounted && appEntry.apkFile != null && appEntry.apkFile.exists()) {
+ // If the app wasn't mounted but is now mounted, reload its icon.
+ setAppEntryMounted(appEntry, /* mounted= */ true);
+ icon = Utils.getBadgedIcon(context, appEntry.info);
+ appIconCacheManager.put(packageName, uid, icon);
+ }
+
+ return icon;
+ }
+
+ /**
+ * Get the app icon from cache by app entry.
+ *
+ * @param appEntry AppEntry of ApplicationsState
+ * @return app icon of the app entry
+ */
+ public static Drawable getIconFromCache(ApplicationsState.AppEntry appEntry) {
+ return appEntry == null || appEntry.info == null ? null
+ : AppIconCacheManager.getInstance().get(
+ appEntry.info.packageName,
+ appEntry.info.uid);
+ }
+
+ /**
+ * Preload the top N icons of app entry list.
+ *
+ * @param context caller's context
+ * @param appEntries AppEntry list of ApplicationsState
+ * @param number the number of Top N icons of the appEntries
+ */
+ public static void preloadTopIcons(Context context,
+ ArrayList<ApplicationsState.AppEntry> appEntries, int number) {
+ if (appEntries == null || appEntries.isEmpty() || number <= 0) {
+ return;
+ }
+
+ for (int i = 0; i < Math.min(appEntries.size(), number); i++) {
+ final ApplicationsState.AppEntry entry = appEntries.get(i);
+ ThreadUtils.postOnBackgroundThread(() -> {
+ getIcon(context, entry);
+ });
+ }
+ }
+
+ private static void setAppEntryMounted(ApplicationsState.AppEntry appEntry, boolean mounted) {
+ if (appEntry.mounted != mounted) {
+ synchronized (appEntry) {
+ appEntry.mounted = mounted;
+ }
+ }
+ }
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
index f046f06cc691..fdb06072bbd1 100644
--- a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
@@ -95,6 +95,7 @@ public class ApplicationsState {
private static final Object sLock = new Object();
private static final Pattern REMOVE_DIACRITICALS_PATTERN
= Pattern.compile("\\p{InCombiningDiacriticalMarks}+");
+ private static final String SETTING_PKG = "com.android.settings";
@VisibleForTesting
static ApplicationsState sInstance;
@@ -492,6 +493,9 @@ public class ApplicationsState {
return null;
}
+ /**
+ * Starting Android T, this method will not be used if {@link AppIconCacheManager} is applied.
+ */
public void ensureIcon(AppEntry entry) {
if (entry.icon != null) {
return;
@@ -758,6 +762,10 @@ public class ApplicationsState {
return null;
}
+ private static boolean isAppIconCacheEnabled(Context context) {
+ return SETTING_PKG.equals(context.getPackageName());
+ }
+
void rebuildActiveSessions() {
synchronized (mEntriesMap) {
if (!mSessionsChanged) {
@@ -806,6 +814,11 @@ public class ApplicationsState {
} else {
mHasLifecycle = false;
}
+
+ if (isAppIconCacheEnabled(mContext)) {
+ // Skip the preloading all icons step to save memory usage.
+ mFlags = mFlags & ~FLAG_SESSION_REQUEST_ICONS;
+ }
}
@SessionFlags
@@ -814,7 +827,12 @@ public class ApplicationsState {
}
public void setSessionFlags(@SessionFlags int flags) {
- mFlags = flags;
+ if (isAppIconCacheEnabled(mContext)) {
+ // Skip the preloading all icons step to save memory usage.
+ mFlags = flags & ~FLAG_SESSION_REQUEST_ICONS;
+ } else {
+ mFlags = flags;
+ }
}
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
@@ -1576,6 +1594,10 @@ public class ApplicationsState {
// Need to synchronize on 'this' for the following.
public ApplicationInfo info;
+ /**
+ * Starting Android T, this field will not be used if {@link AppIconCacheManager} is
+ * applied.
+ */
public Drawable icon;
public String sizeStr;
public String internalSizeStr;
@@ -1596,15 +1618,11 @@ public class ApplicationsState {
this.size = SIZE_UNKNOWN;
this.sizeStale = true;
ensureLabel(context);
- // Speed up the cache of the icon and label description if they haven't been created.
- ThreadUtils.postOnBackgroundThread(() -> {
- if (this.icon == null) {
- this.ensureIconLocked(context);
- }
- if (this.labelDescription == null) {
- this.ensureLabelDescriptionLocked(context);
- }
- });
+ // Speed up the cache of the label description if they haven't been created.
+ if (this.labelDescription == null) {
+ ThreadUtils.postOnBackgroundThread(
+ () -> this.ensureLabelDescriptionLocked(context));
+ }
}
public void ensureLabel(Context context) {
@@ -1620,7 +1638,15 @@ public class ApplicationsState {
}
}
+ /**
+ * Starting Android T, this method will not be used if {@link AppIconCacheManager} is
+ * applied.
+ */
boolean ensureIconLocked(Context context) {
+ if (isAppIconCacheEnabled(context)) {
+ return false;
+ }
+
if (this.icon == null) {
if (this.apkFile.exists()) {
this.icon = Utils.getBadgedIcon(context, info);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingsManager.java b/packages/SettingsLib/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManager.java
index bec5fc8e7b9f..afd3626ab889 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingsManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManager.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.policy;
+package com.android.settingslib.devicestate;
import static android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_IGNORED;
import static android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_LOCKED;
diff --git a/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java b/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java
index 46e31ceb7485..6bf43e528009 100644
--- a/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java
+++ b/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java
@@ -34,6 +34,7 @@ import android.os.ServiceManager;
import android.provider.Settings;
import android.service.dreams.DreamService;
import android.service.dreams.IDreamManager;
+import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
import android.util.Xml;
@@ -50,6 +51,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
+import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
@@ -292,6 +294,11 @@ public class DreamBackend {
}
}
+ /** Returns whether a particular complication is enabled */
+ public boolean isComplicationEnabled(@ComplicationType int complication) {
+ return getEnabledComplications().contains(complication);
+ }
+
/** Gets all complications which have been enabled by the user. */
public Set<Integer> getEnabledComplications() {
final String enabledComplications = Settings.Secure.getString(
@@ -331,6 +338,35 @@ public class DreamBackend {
convertToString(enabledComplications));
}
+ /**
+ * Gets the title of a particular complication type to be displayed to the user. If there
+ * is no title, null is returned.
+ */
+ @Nullable
+ public CharSequence getComplicationTitle(@ComplicationType int complicationType) {
+ int res = 0;
+ switch (complicationType) {
+ case COMPLICATION_TYPE_TIME:
+ res = R.string.dream_complication_title_time;
+ break;
+ case COMPLICATION_TYPE_DATE:
+ res = R.string.dream_complication_title_date;
+ break;
+ case COMPLICATION_TYPE_WEATHER:
+ res = R.string.dream_complication_title_weather;
+ break;
+ case COMPLICATION_TYPE_AIR_QUALITY:
+ res = R.string.dream_complication_title_aqi;
+ break;
+ case COMPLICATION_TYPE_CAST_INFO:
+ res = R.string.dream_complication_title_cast_info;
+ break;
+ default:
+ return null;
+ }
+ return mContext.getString(res);
+ }
+
private static String convertToString(Set<Integer> set) {
return set.stream()
.map(String::valueOf)
@@ -338,6 +374,9 @@ public class DreamBackend {
}
private static Set<Integer> parseFromString(String string) {
+ if (TextUtils.isEmpty(string)) {
+ return new HashSet<>();
+ }
return Arrays.stream(string.split(","))
.map(Integer::parseInt)
.collect(Collectors.toSet());
diff --git a/packages/SettingsLib/src/com/android/settingslib/users/AvatarPhotoController.java b/packages/SettingsLib/src/com/android/settingslib/users/AvatarPhotoController.java
new file mode 100644
index 000000000000..61b8911acea4
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/users/AvatarPhotoController.java
@@ -0,0 +1,297 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.users;
+
+import android.app.Activity;
+import android.content.ClipData;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.database.Cursor;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.RectF;
+import android.media.ExifInterface;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.StrictMode;
+import android.provider.ContactsContract;
+import android.provider.MediaStore;
+import android.util.EventLog;
+import android.util.Log;
+
+import androidx.core.content.FileProvider;
+
+import libcore.io.Streams;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+class AvatarPhotoController {
+ private static final String TAG = "AvatarPhotoController";
+
+ private static final int REQUEST_CODE_CHOOSE_PHOTO = 1001;
+ private static final int REQUEST_CODE_TAKE_PHOTO = 1002;
+ private static final int REQUEST_CODE_CROP_PHOTO = 1003;
+ // in rare cases we get a null Cursor when querying for DisplayPhoto.CONTENT_MAX_DIMENSIONS_URI
+ // so we need a default photo size
+ private static final int DEFAULT_PHOTO_SIZE = 500;
+
+ private static final String IMAGES_DIR = "multi_user";
+ private static final String CROP_PICTURE_FILE_NAME = "CropEditUserPhoto.jpg";
+ private static final String TAKE_PICTURE_FILE_NAME = "TakeEditUserPhoto.jpg";
+
+ private final int mPhotoSize;
+
+ private final AvatarPickerActivity mActivity;
+ private final String mFileAuthority;
+
+ private final File mImagesDir;
+ private final Uri mCropPictureUri;
+ private final Uri mTakePictureUri;
+
+ AvatarPhotoController(AvatarPickerActivity activity, boolean waiting, String fileAuthority) {
+ mActivity = activity;
+ mFileAuthority = fileAuthority;
+
+ mImagesDir = new File(activity.getCacheDir(), IMAGES_DIR);
+ mImagesDir.mkdir();
+ mCropPictureUri = createTempImageUri(activity, CROP_PICTURE_FILE_NAME, !waiting);
+ mTakePictureUri = createTempImageUri(activity, TAKE_PICTURE_FILE_NAME, !waiting);
+ mPhotoSize = getPhotoSize(activity);
+ }
+
+ /**
+ * Handles activity result from containing activity/fragment after a take/choose/crop photo
+ * action result is received.
+ */
+ public boolean onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (resultCode != Activity.RESULT_OK) {
+ return false;
+ }
+ final Uri pictureUri = data != null && data.getData() != null
+ ? data.getData() : mTakePictureUri;
+
+ // Check if the result is a content uri
+ if (!ContentResolver.SCHEME_CONTENT.equals(pictureUri.getScheme())) {
+ Log.e(TAG, "Invalid pictureUri scheme: " + pictureUri.getScheme());
+ EventLog.writeEvent(0x534e4554, "172939189", -1, pictureUri.getPath());
+ return false;
+ }
+
+ switch (requestCode) {
+ case REQUEST_CODE_CROP_PHOTO:
+ mActivity.returnUriResult(pictureUri);
+ return true;
+ case REQUEST_CODE_TAKE_PHOTO:
+ case REQUEST_CODE_CHOOSE_PHOTO:
+ if (mTakePictureUri.equals(pictureUri)) {
+ if (PhotoCapabilityUtils.canCropPhoto(mActivity)) {
+ cropPhoto();
+ } else {
+ onPhotoNotCropped(pictureUri);
+ }
+ } else {
+ copyAndCropPhoto(pictureUri);
+ }
+ return true;
+ }
+ return false;
+ }
+
+ void takePhoto() {
+ Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE_SECURE);
+ appendOutputExtra(intent, mTakePictureUri);
+ mActivity.startActivityForResult(intent, REQUEST_CODE_TAKE_PHOTO);
+ }
+
+ void choosePhoto() {
+ Intent intent = new Intent(MediaStore.ACTION_PICK_IMAGES, null);
+ intent.setType("image/*");
+ mActivity.startActivityForResult(intent, REQUEST_CODE_CHOOSE_PHOTO);
+ }
+
+ private void copyAndCropPhoto(final Uri pictureUri) {
+ // TODO: Replace AsyncTask
+ new AsyncTask<Void, Void, Void>() {
+ @Override
+ protected Void doInBackground(Void... params) {
+ final ContentResolver cr = mActivity.getContentResolver();
+ try (InputStream in = cr.openInputStream(pictureUri);
+ OutputStream out = cr.openOutputStream(mTakePictureUri)) {
+ Streams.copy(in, out);
+ } catch (IOException e) {
+ Log.w(TAG, "Failed to copy photo", e);
+ }
+ return null;
+ }
+
+ @Override
+ protected void onPostExecute(Void result) {
+ if (!mActivity.isFinishing() && !mActivity.isDestroyed()) {
+ cropPhoto();
+ }
+ }
+ }.execute();
+ }
+
+ private void cropPhoto() {
+ // TODO: Use a public intent, when there is one.
+ Intent intent = new Intent("com.android.camera.action.CROP");
+ intent.setDataAndType(mTakePictureUri, "image/*");
+ appendOutputExtra(intent, mCropPictureUri);
+ appendCropExtras(intent);
+ if (intent.resolveActivity(mActivity.getPackageManager()) != null) {
+ try {
+ StrictMode.disableDeathOnFileUriExposure();
+ mActivity.startActivityForResult(intent, REQUEST_CODE_CROP_PHOTO);
+ } finally {
+ StrictMode.enableDeathOnFileUriExposure();
+ }
+ } else {
+ onPhotoNotCropped(mTakePictureUri);
+ }
+ }
+
+ private void appendOutputExtra(Intent intent, Uri pictureUri) {
+ intent.putExtra(MediaStore.EXTRA_OUTPUT, pictureUri);
+ intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION
+ | Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ intent.setClipData(ClipData.newRawUri(MediaStore.EXTRA_OUTPUT, pictureUri));
+ }
+
+ private void appendCropExtras(Intent intent) {
+ intent.putExtra("crop", "true");
+ intent.putExtra("scale", true);
+ intent.putExtra("scaleUpIfNeeded", true);
+ intent.putExtra("aspectX", 1);
+ intent.putExtra("aspectY", 1);
+ intent.putExtra("outputX", mPhotoSize);
+ intent.putExtra("outputY", mPhotoSize);
+ }
+
+ private void onPhotoNotCropped(final Uri data) {
+ // TODO: Replace AsyncTask to avoid possible memory leaks and handle configuration change
+ new AsyncTask<Void, Void, Bitmap>() {
+ @Override
+ protected Bitmap doInBackground(Void... params) {
+ // Scale and crop to a square aspect ratio
+ Bitmap croppedImage = Bitmap.createBitmap(mPhotoSize, mPhotoSize,
+ Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(croppedImage);
+ Bitmap fullImage;
+ try {
+ InputStream imageStream = mActivity.getContentResolver()
+ .openInputStream(data);
+ fullImage = BitmapFactory.decodeStream(imageStream);
+ } catch (FileNotFoundException fe) {
+ return null;
+ }
+ if (fullImage != null) {
+ int rotation = getRotation(mActivity, data);
+ final int squareSize = Math.min(fullImage.getWidth(),
+ fullImage.getHeight());
+ final int left = (fullImage.getWidth() - squareSize) / 2;
+ final int top = (fullImage.getHeight() - squareSize) / 2;
+
+ Matrix matrix = new Matrix();
+ RectF rectSource = new RectF(left, top,
+ left + squareSize, top + squareSize);
+ RectF rectDest = new RectF(0, 0, mPhotoSize, mPhotoSize);
+ matrix.setRectToRect(rectSource, rectDest, Matrix.ScaleToFit.CENTER);
+ matrix.postRotate(rotation, mPhotoSize / 2f, mPhotoSize / 2f);
+ canvas.drawBitmap(fullImage, matrix, new Paint());
+ return croppedImage;
+ } else {
+ // Bah! Got nothin.
+ return null;
+ }
+ }
+
+ @Override
+ protected void onPostExecute(Bitmap bitmap) {
+ saveBitmapToFile(bitmap, new File(mImagesDir, CROP_PICTURE_FILE_NAME));
+ mActivity.returnUriResult(mCropPictureUri);
+ }
+ }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[]) null);
+ }
+
+ /**
+ * Reads the image's exif data and determines the rotation degree needed to display the image
+ * in portrait mode.
+ */
+ private int getRotation(Context context, Uri selectedImage) {
+ int rotation = -1;
+ try {
+ InputStream imageStream = context.getContentResolver().openInputStream(selectedImage);
+ ExifInterface exif = new ExifInterface(imageStream);
+ rotation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, -1);
+ } catch (IOException exception) {
+ Log.e(TAG, "Error while getting rotation", exception);
+ }
+
+ switch (rotation) {
+ case ExifInterface.ORIENTATION_ROTATE_90:
+ return 90;
+ case ExifInterface.ORIENTATION_ROTATE_180:
+ return 180;
+ case ExifInterface.ORIENTATION_ROTATE_270:
+ return 270;
+ default:
+ return 0;
+ }
+ }
+
+ private void saveBitmapToFile(Bitmap bitmap, File file) {
+ try {
+ OutputStream os = new FileOutputStream(file);
+ bitmap.compress(Bitmap.CompressFormat.PNG, 100, os);
+ os.flush();
+ os.close();
+ } catch (IOException e) {
+ Log.e(TAG, "Cannot create temp file", e);
+ }
+ }
+
+ private static int getPhotoSize(Context context) {
+ try (Cursor cursor = context.getContentResolver().query(
+ ContactsContract.DisplayPhoto.CONTENT_MAX_DIMENSIONS_URI,
+ new String[]{ContactsContract.DisplayPhoto.DISPLAY_MAX_DIM}, null, null, null)) {
+ if (cursor != null) {
+ cursor.moveToFirst();
+ return cursor.getInt(0);
+ } else {
+ return DEFAULT_PHOTO_SIZE;
+ }
+ }
+ }
+
+ private Uri createTempImageUri(Context context, String fileName, boolean purge) {
+ final File fullPath = new File(mImagesDir, fileName);
+ if (purge) {
+ fullPath.delete();
+ }
+ return FileProvider.getUriForFile(context, mFileAuthority, fullPath);
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/users/AvatarPickerActivity.java b/packages/SettingsLib/src/com/android/settingslib/users/AvatarPickerActivity.java
new file mode 100644
index 000000000000..50015e653399
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/users/AvatarPickerActivity.java
@@ -0,0 +1,334 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.users;
+
+import android.app.Activity;
+import android.content.ContentResolver;
+import android.content.Intent;
+import android.content.res.TypedArray;
+import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+
+import androidx.annotation.NonNull;
+import androidx.core.graphics.drawable.RoundedBitmapDrawable;
+import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory;
+import androidx.recyclerview.widget.GridLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.android.internal.util.UserIcons;
+import com.android.settingslib.R;
+
+import com.google.android.setupcompat.template.FooterBarMixin;
+import com.google.android.setupcompat.template.FooterButton;
+import com.google.android.setupdesign.GlifLayout;
+import com.google.android.setupdesign.util.ThemeHelper;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Activity to allow the user to choose a user profile picture.
+ *
+ * <p>Options are provided to take a photo or choose a photo using the photo picker. In addition,
+ * preselected avatar images may be provided in the resource array {@code avatar_images}. If
+ * provided, every element of that array must be a bitmap drawable.
+ *
+ * <p>If preselected images are not provided, the default avatar will be shown instead, in a range
+ * of colors.
+ *
+ * <p>This activity should be started with startActivityForResult. If a photo or a preselected image
+ * is selected, a Uri will be returned in the data field of the result intent. If a colored default
+ * avatar is selected, the chosen color will be returned as {@code EXTRA_DEFAULT_ICON_TINT_COLOR}
+ * and the data field will be empty.
+ */
+public class AvatarPickerActivity extends Activity {
+
+ static final String EXTRA_FILE_AUTHORITY = "file_authority";
+ static final String EXTRA_DEFAULT_ICON_TINT_COLOR = "default_icon_tint_color";
+
+ private static final String KEY_AWAITING_RESULT = "awaiting_result";
+ private static final String KEY_SELECTED_POSITION = "selected_position";
+
+ private boolean mWaitingForActivityResult;
+
+ private FooterButton mDoneButton;
+ private AvatarAdapter mAdapter;
+
+ private AvatarPhotoController mAvatarPhotoController;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ ThemeHelper.trySetDynamicColor(this);
+ setContentView(R.layout.avatar_picker);
+ setUpButtons();
+
+ RecyclerView recyclerView = findViewById(R.id.avatar_grid);
+ mAdapter = new AvatarAdapter();
+ recyclerView.setAdapter(mAdapter);
+ recyclerView.setLayoutManager(new GridLayoutManager(this,
+ getResources().getInteger(R.integer.avatar_picker_columns)));
+
+ restoreState(savedInstanceState);
+
+ mAvatarPhotoController = new AvatarPhotoController(
+ this, mWaitingForActivityResult, getFileAuthority());
+ }
+
+ private void setUpButtons() {
+ GlifLayout glifLayout = findViewById(R.id.glif_layout);
+ FooterBarMixin mixin = glifLayout.getMixin(FooterBarMixin.class);
+
+ FooterButton secondaryButton =
+ new FooterButton.Builder(this)
+ .setText("Cancel")
+ .setListener(view -> cancel())
+ .build();
+
+ mDoneButton =
+ new FooterButton.Builder(this)
+ .setText("Done")
+ .setListener(view -> mAdapter.returnSelectionResult())
+ .build();
+ mDoneButton.setEnabled(false);
+
+ mixin.setSecondaryButton(secondaryButton);
+ mixin.setPrimaryButton(mDoneButton);
+ }
+
+ private String getFileAuthority() {
+ String authority = getIntent().getStringExtra(EXTRA_FILE_AUTHORITY);
+ if (authority == null) {
+ throw new IllegalStateException("File authority must be provided");
+ }
+ return authority;
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ mWaitingForActivityResult = false;
+ mAvatarPhotoController.onActivityResult(requestCode, resultCode, data);
+ }
+
+ @Override
+ protected void onSaveInstanceState(@NonNull Bundle outState) {
+ outState.putBoolean(KEY_AWAITING_RESULT, mWaitingForActivityResult);
+ outState.putInt(KEY_SELECTED_POSITION, mAdapter.mSelectedPosition);
+ super.onSaveInstanceState(outState);
+ }
+
+ private void restoreState(Bundle savedInstanceState) {
+ if (savedInstanceState != null) {
+ mWaitingForActivityResult = savedInstanceState.getBoolean(KEY_AWAITING_RESULT, false);
+ mAdapter.mSelectedPosition =
+ savedInstanceState.getInt(KEY_SELECTED_POSITION, AvatarAdapter.NONE);
+ }
+ }
+
+ @Override
+ public void startActivityForResult(Intent intent, int requestCode) {
+ mWaitingForActivityResult = true;
+ super.startActivityForResult(intent, requestCode);
+ }
+
+ void returnUriResult(Uri uri) {
+ Intent resultData = new Intent();
+ resultData.setData(uri);
+ setResult(RESULT_OK, resultData);
+ finish();
+ }
+
+ void returnColorResult(int color) {
+ Intent resultData = new Intent();
+ resultData.putExtra(EXTRA_DEFAULT_ICON_TINT_COLOR, color);
+ setResult(RESULT_OK, resultData);
+ finish();
+ }
+
+ private void cancel() {
+ setResult(RESULT_CANCELED);
+ finish();
+ }
+
+ private class AvatarAdapter extends RecyclerView.Adapter<AvatarViewHolder> {
+
+ private static final int NONE = -1;
+
+ private final int mTakePhotoPosition;
+ private final int mChoosePhotoPosition;
+ private final int mPreselectedImageStartPosition;
+
+ private final List<Drawable> mImageDrawables;
+ private final TypedArray mPreselectedImages;
+ private final int[] mUserIconColors;
+ private int mSelectedPosition = NONE;
+
+ AvatarAdapter() {
+ final boolean canTakePhoto =
+ PhotoCapabilityUtils.canTakePhoto(AvatarPickerActivity.this);
+ final boolean canChoosePhoto =
+ PhotoCapabilityUtils.canChoosePhoto(AvatarPickerActivity.this);
+ mTakePhotoPosition = (canTakePhoto ? 0 : NONE);
+ mChoosePhotoPosition = (canChoosePhoto ? (canTakePhoto ? 1 : 0) : NONE);
+ mPreselectedImageStartPosition = (canTakePhoto ? 1 : 0) + (canChoosePhoto ? 1 : 0);
+
+ mPreselectedImages = getResources().obtainTypedArray(R.array.avatar_images);
+ mUserIconColors = UserIcons.getUserIconColors(getResources());
+ mImageDrawables = buildDrawableList();
+ }
+
+ @NonNull
+ @Override
+ public AvatarViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int position) {
+ LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
+ View itemView = layoutInflater.inflate(R.layout.avatar_item, parent, false);
+ return new AvatarViewHolder(itemView);
+ }
+
+ @Override
+ public void onBindViewHolder(@NonNull AvatarViewHolder viewHolder, int position) {
+ if (position == mTakePhotoPosition) {
+ viewHolder.setDrawable(getDrawable(R.drawable.avatar_take_photo_circled));
+ viewHolder.setClickListener(view -> mAvatarPhotoController.takePhoto());
+
+ } else if (position == mChoosePhotoPosition) {
+ viewHolder.setDrawable(getDrawable(R.drawable.avatar_choose_photo_circled));
+ viewHolder.setClickListener(view -> mAvatarPhotoController.choosePhoto());
+
+ } else if (position >= mPreselectedImageStartPosition) {
+ viewHolder.setSelected(position == mSelectedPosition);
+ viewHolder.setDrawable(mImageDrawables.get(indexFromPosition(position)));
+ viewHolder.setClickListener(view -> {
+ if (mSelectedPosition == position) {
+ deselect(position);
+ } else {
+ select(position);
+ }
+ });
+ }
+ }
+
+ @Override
+ public int getItemCount() {
+ return mPreselectedImageStartPosition + mImageDrawables.size();
+ }
+
+ private List<Drawable> buildDrawableList() {
+ List<Drawable> result = new ArrayList<>();
+
+ for (int i = 0; i < mPreselectedImages.length(); i++) {
+ Drawable drawable = mPreselectedImages.getDrawable(i);
+ if (drawable instanceof BitmapDrawable) {
+ result.add(circularDrawableFrom((BitmapDrawable) drawable));
+ } else {
+ throw new IllegalStateException("Avatar drawables must be bitmaps");
+ }
+ }
+ if (!result.isEmpty()) {
+ return result;
+ }
+
+ // No preselected images. Use tinted default icon.
+ for (int i = 0; i < mUserIconColors.length; i++) {
+ result.add(UserIcons.getDefaultUserIconInColor(getResources(), mUserIconColors[i]));
+ }
+ return result;
+ }
+
+ private Drawable circularDrawableFrom(BitmapDrawable drawable) {
+ Bitmap bitmap = drawable.getBitmap();
+
+ RoundedBitmapDrawable roundedBitmapDrawable =
+ RoundedBitmapDrawableFactory.create(getResources(), bitmap);
+ roundedBitmapDrawable.setCircular(true);
+
+ return roundedBitmapDrawable;
+ }
+
+ private int indexFromPosition(int position) {
+ return position - mPreselectedImageStartPosition;
+ }
+
+ private void select(int position) {
+ final int oldSelection = mSelectedPosition;
+ mSelectedPosition = position;
+ notifyItemChanged(position);
+ if (oldSelection != NONE) {
+ notifyItemChanged(oldSelection);
+ } else {
+ mDoneButton.setEnabled(true);
+ }
+ }
+
+ private void deselect(int position) {
+ mSelectedPosition = NONE;
+ notifyItemChanged(position);
+ mDoneButton.setEnabled(false);
+ }
+
+ private void returnSelectionResult() {
+ int index = indexFromPosition(mSelectedPosition);
+ if (mPreselectedImages.length() > 0) {
+ int resourceId = mPreselectedImages.getResourceId(index, -1);
+ if (resourceId == -1) {
+ throw new IllegalStateException("Preselected avatar images must be resources.");
+ }
+ returnUriResult(uriForResourceId(resourceId));
+ } else {
+ returnColorResult(
+ mUserIconColors[index]);
+ }
+ }
+
+ private Uri uriForResourceId(int resourceId) {
+ return new Uri.Builder()
+ .scheme(ContentResolver.SCHEME_ANDROID_RESOURCE)
+ .authority(getResources().getResourcePackageName(resourceId))
+ .appendPath(getResources().getResourceTypeName(resourceId))
+ .appendPath(getResources().getResourceEntryName(resourceId))
+ .build();
+ }
+ }
+
+ private static class AvatarViewHolder extends RecyclerView.ViewHolder {
+ private final ImageView mImageView;
+
+ AvatarViewHolder(View view) {
+ super(view);
+ mImageView = view.findViewById(R.id.avatar_image);
+ }
+
+ public void setDrawable(Drawable drawable) {
+ mImageView.setImageDrawable(drawable);
+ }
+
+ public void setClickListener(View.OnClickListener listener) {
+ mImageView.setOnClickListener(listener);
+ }
+
+ public void setSelected(boolean selected) {
+ mImageView.setSelected(selected);
+ }
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/users/EditUserInfoController.java b/packages/SettingsLib/src/com/android/settingslib/users/EditUserInfoController.java
index 58599532d9cb..80ee86f5e489 100644
--- a/packages/SettingsLib/src/com/android/settingslib/users/EditUserInfoController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/users/EditUserInfoController.java
@@ -25,6 +25,7 @@ import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.UserHandle;
+import android.os.UserManager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.WindowManager;
@@ -36,6 +37,8 @@ import androidx.annotation.VisibleForTesting;
import com.android.internal.util.UserIcons;
import com.android.settingslib.R;
+import com.android.settingslib.RestrictedLockUtils;
+import com.android.settingslib.RestrictedLockUtilsInternal;
import com.android.settingslib.drawable.CircleFramedDrawable;
import java.io.File;
@@ -139,13 +142,20 @@ public class EditUserInfoController {
Drawable userIcon = getUserIcon(activity, defaultUserIcon);
userPhotoView.setImageDrawable(userIcon);
- if (canChangePhoto(activity)) {
- mEditUserPhotoController = createEditUserPhotoController(activity, activityStarter,
- userPhotoView);
+ if (isChangePhotoRestrictedByBase(activity)) {
+ // some users can't change their photos so we need to remove the suggestive icon
+ content.findViewById(R.id.add_a_photo_icon).setVisibility(View.GONE);
} else {
- // some users can't change their photos so we need to remove suggestive
- // background from the photoView
- userPhotoView.setBackground(null);
+ RestrictedLockUtils.EnforcedAdmin adminRestriction =
+ getChangePhotoAdminRestriction(activity);
+ if (adminRestriction != null) {
+ userPhotoView.setOnClickListener(view ->
+ RestrictedLockUtils.sendShowAdminSupportDetailsIntent(
+ activity, adminRestriction));
+ } else {
+ mEditUserPhotoController = createEditUserPhotoController(activity, activityStarter,
+ userPhotoView);
+ }
}
mEditUserInfoDialog = buildDialog(activity, content, userNameView, oldUserIcon,
@@ -204,16 +214,21 @@ public class EditUserInfoController {
}
@VisibleForTesting
- boolean canChangePhoto(Context context) {
- return (PhotoCapabilityUtils.canCropPhoto(context)
- && PhotoCapabilityUtils.canChoosePhoto(context))
- || PhotoCapabilityUtils.canTakePhoto(context);
+ boolean isChangePhotoRestrictedByBase(Context context) {
+ return RestrictedLockUtilsInternal.hasBaseUserRestriction(
+ context, UserManager.DISALLOW_SET_USER_ICON, UserHandle.myUserId());
+ }
+
+ @VisibleForTesting
+ RestrictedLockUtils.EnforcedAdmin getChangePhotoAdminRestriction(Context context) {
+ return RestrictedLockUtilsInternal.checkIfRestrictionEnforced(
+ context, UserManager.DISALLOW_SET_USER_ICON, UserHandle.myUserId());
}
@VisibleForTesting
EditUserPhotoController createEditUserPhotoController(Activity activity,
ActivityStarter activityStarter, ImageView userPhotoView) {
return new EditUserPhotoController(activity, activityStarter, userPhotoView,
- mSavedPhoto, mWaitingForActivityResult, mFileAuthority);
+ mSavedPhoto, mFileAuthority);
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/users/EditUserPhotoController.java b/packages/SettingsLib/src/com/android/settingslib/users/EditUserPhotoController.java
index f9584a3e15e9..f8bb38b5978e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/users/EditUserPhotoController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/users/EditUserPhotoController.java
@@ -16,46 +16,21 @@
package com.android.settingslib.users;
+import android.annotation.NonNull;
import android.app.Activity;
-import android.content.ClipData;
-import android.content.ContentResolver;
-import android.content.Context;
import android.content.Intent;
-import android.database.Cursor;
import android.graphics.Bitmap;
-import android.graphics.Bitmap.Config;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
-import android.graphics.Matrix;
-import android.graphics.Paint;
-import android.graphics.RectF;
import android.graphics.drawable.Drawable;
-import android.media.ExifInterface;
import android.net.Uri;
-import android.os.AsyncTask;
-import android.os.StrictMode;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.provider.ContactsContract.DisplayPhoto;
-import android.provider.MediaStore;
-import android.util.EventLog;
import android.util.Log;
-import android.view.Gravity;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ArrayAdapter;
import android.widget.ImageView;
-import android.widget.ListPopupWindow;
-import android.widget.TextView;
-
-import androidx.core.content.FileProvider;
+import com.android.internal.util.UserIcons;
import com.android.settingslib.R;
-import com.android.settingslib.RestrictedLockUtils;
-import com.android.settingslib.RestrictedLockUtilsInternal;
import com.android.settingslib.drawable.CircleFramedDrawable;
-
-import libcore.io.Streams;
+import com.android.settingslib.utils.ThreadUtils;
import java.io.File;
import java.io.FileNotFoundException;
@@ -63,8 +38,7 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
-import java.util.ArrayList;
-import java.util.List;
+import java.util.concurrent.ExecutionException;
/**
* This class contains logic for starting activities to take/choose/crop photo, reads and transforms
@@ -75,45 +49,30 @@ public class EditUserPhotoController {
// It seems that this class generates custom request codes and they may
// collide with ours, these values are very unlikely to have a conflict.
- private static final int REQUEST_CODE_CHOOSE_PHOTO = 1001;
- private static final int REQUEST_CODE_TAKE_PHOTO = 1002;
- private static final int REQUEST_CODE_CROP_PHOTO = 1003;
- // in rare cases we get a null Cursor when querying for DisplayPhoto.CONTENT_MAX_DIMENSIONS_URI
- // so we need a default photo size
- private static final int DEFAULT_PHOTO_SIZE = 500;
+ private static final int REQUEST_CODE_PICK_AVATAR = 1004;
private static final String IMAGES_DIR = "multi_user";
- private static final String CROP_PICTURE_FILE_NAME = "CropEditUserPhoto.jpg";
- private static final String TAKE_PICTURE_FILE_NAME = "TakeEditUserPhoto.jpg";
private static final String NEW_USER_PHOTO_FILE_NAME = "NewUserPhoto.png";
- private final int mPhotoSize;
-
private final Activity mActivity;
private final ActivityStarter mActivityStarter;
private final ImageView mImageView;
private final String mFileAuthority;
private final File mImagesDir;
- private final Uri mCropPictureUri;
- private final Uri mTakePictureUri;
-
private Bitmap mNewUserPhotoBitmap;
private Drawable mNewUserPhotoDrawable;
public EditUserPhotoController(Activity activity, ActivityStarter activityStarter,
- ImageView view, Bitmap bitmap, boolean waiting, String fileAuthority) {
+ ImageView view, Bitmap bitmap, String fileAuthority) {
mActivity = activity;
mActivityStarter = activityStarter;
- mImageView = view;
mFileAuthority = fileAuthority;
mImagesDir = new File(activity.getCacheDir(), IMAGES_DIR);
mImagesDir.mkdir();
- mCropPictureUri = createTempImageUri(activity, CROP_PICTURE_FILE_NAME, !waiting);
- mTakePictureUri = createTempImageUri(activity, TAKE_PICTURE_FILE_NAME, !waiting);
- mPhotoSize = getPhotoSize(activity);
- mImageView.setOnClickListener(v -> showUpdatePhotoPopup());
+ mImageView = view;
+ mImageView.setOnClickListener(v -> showAvatarPicker());
mNewUserPhotoBitmap = bitmap;
}
@@ -125,32 +84,19 @@ public class EditUserPhotoController {
if (resultCode != Activity.RESULT_OK) {
return false;
}
- final Uri pictureUri = data != null && data.getData() != null
- ? data.getData() : mTakePictureUri;
- // Check if the result is a content uri
- if (!ContentResolver.SCHEME_CONTENT.equals(pictureUri.getScheme())) {
- Log.e(TAG, "Invalid pictureUri scheme: " + pictureUri.getScheme());
- EventLog.writeEvent(0x534e4554, "172939189", -1, pictureUri.getPath());
- return false;
- }
-
- switch (requestCode) {
- case REQUEST_CODE_CROP_PHOTO:
- onPhotoCropped(pictureUri);
+ if (requestCode == REQUEST_CODE_PICK_AVATAR) {
+ if (data.hasExtra(AvatarPickerActivity.EXTRA_DEFAULT_ICON_TINT_COLOR)) {
+ int tintColor =
+ data.getIntExtra(AvatarPickerActivity.EXTRA_DEFAULT_ICON_TINT_COLOR, -1);
+ onDefaultIconSelected(tintColor);
return true;
- case REQUEST_CODE_TAKE_PHOTO:
- case REQUEST_CODE_CHOOSE_PHOTO:
- if (mTakePictureUri.equals(pictureUri)) {
- if (PhotoCapabilityUtils.canCropPhoto(mActivity)) {
- cropPhoto();
- } else {
- onPhotoNotCropped(pictureUri);
- }
- } else {
- copyAndCropPhoto(pictureUri);
- }
+ }
+ if (data.getData() != null) {
+ onPhotoCropped(data.getData());
return true;
+ }
+
}
return false;
}
@@ -159,224 +105,60 @@ public class EditUserPhotoController {
return mNewUserPhotoDrawable;
}
- private void showUpdatePhotoPopup() {
- final Context context = mImageView.getContext();
- final boolean canTakePhoto = PhotoCapabilityUtils.canTakePhoto(context);
- final boolean canChoosePhoto = PhotoCapabilityUtils.canChoosePhoto(context);
-
- if (!canTakePhoto && !canChoosePhoto) {
- return;
- }
-
- final List<EditUserPhotoController.RestrictedMenuItem> items = new ArrayList<>();
-
- if (canTakePhoto) {
- final String title = context.getString(R.string.user_image_take_photo);
- items.add(new RestrictedMenuItem(context, title, UserManager.DISALLOW_SET_USER_ICON,
- this::takePhoto));
- }
-
- if (canChoosePhoto) {
- final String title = context.getString(R.string.user_image_choose_photo);
- items.add(new RestrictedMenuItem(context, title, UserManager.DISALLOW_SET_USER_ICON,
- this::choosePhoto));
- }
-
- final ListPopupWindow listPopupWindow = new ListPopupWindow(context);
-
- listPopupWindow.setAnchorView(mImageView);
- listPopupWindow.setModal(true);
- listPopupWindow.setInputMethodMode(ListPopupWindow.INPUT_METHOD_NOT_NEEDED);
- listPopupWindow.setAdapter(new RestrictedPopupMenuAdapter(context, items));
-
- final int width = Math.max(mImageView.getWidth(), context.getResources()
- .getDimensionPixelSize(R.dimen.update_user_photo_popup_min_width));
- listPopupWindow.setWidth(width);
- listPopupWindow.setDropDownGravity(Gravity.START);
-
- listPopupWindow.setOnItemClickListener((parent, view, position, id) -> {
- listPopupWindow.dismiss();
- final RestrictedMenuItem item =
- (RestrictedMenuItem) parent.getAdapter().getItem(position);
- item.doAction();
- });
-
- listPopupWindow.show();
- }
-
- private void takePhoto() {
- Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE_SECURE);
- appendOutputExtra(intent, mTakePictureUri);
- mActivityStarter.startActivityForResult(intent, REQUEST_CODE_TAKE_PHOTO);
- }
-
- private void choosePhoto() {
- Intent intent = new Intent(Intent.ACTION_GET_CONTENT, null);
- intent.setType("image/*");
- appendOutputExtra(intent, mTakePictureUri);
- mActivityStarter.startActivityForResult(intent, REQUEST_CODE_CHOOSE_PHOTO);
+ private void showAvatarPicker() {
+ Intent intent = new Intent(mImageView.getContext(), AvatarPickerActivity.class);
+ intent.putExtra(AvatarPickerActivity.EXTRA_FILE_AUTHORITY, mFileAuthority);
+ mActivityStarter.startActivityForResult(intent, REQUEST_CODE_PICK_AVATAR);
}
- private void copyAndCropPhoto(final Uri pictureUri) {
- // TODO: Replace AsyncTask
- new AsyncTask<Void, Void, Void>() {
- @Override
- protected Void doInBackground(Void... params) {
- final ContentResolver cr = mActivity.getContentResolver();
- try (InputStream in = cr.openInputStream(pictureUri);
- OutputStream out = cr.openOutputStream(mTakePictureUri)) {
- Streams.copy(in, out);
- } catch (IOException e) {
- Log.w(TAG, "Failed to copy photo", e);
- }
- return null;
- }
-
- @Override
- protected void onPostExecute(Void result) {
- if (!mActivity.isFinishing() && !mActivity.isDestroyed()) {
- cropPhoto();
- }
- }
- }.execute();
- }
+ private void onDefaultIconSelected(int tintColor) {
+ try {
+ ThreadUtils.postOnBackgroundThread(() -> {
+ Drawable drawable =
+ UserIcons.getDefaultUserIconInColor(mActivity.getResources(), tintColor);
+ Bitmap bitmap = convertToBitmap(drawable,
+ (int) mActivity.getResources().getDimension(R.dimen.circle_avatar_size));
- private void cropPhoto() {
- // TODO: Use a public intent, when there is one.
- Intent intent = new Intent("com.android.camera.action.CROP");
- intent.setDataAndType(mTakePictureUri, "image/*");
- appendOutputExtra(intent, mCropPictureUri);
- appendCropExtras(intent);
- if (intent.resolveActivity(mActivity.getPackageManager()) != null) {
- try {
- StrictMode.disableDeathOnFileUriExposure();
- mActivityStarter.startActivityForResult(intent, REQUEST_CODE_CROP_PHOTO);
- } finally {
- StrictMode.enableDeathOnFileUriExposure();
- }
- } else {
- onPhotoNotCropped(mTakePictureUri);
+ ThreadUtils.postOnMainThread(() -> onPhotoProcessed(bitmap));
+ }).get();
+ } catch (InterruptedException | ExecutionException e) {
+ Log.e(TAG, "Error processing default icon", e);
}
}
- private void appendOutputExtra(Intent intent, Uri pictureUri) {
- intent.putExtra(MediaStore.EXTRA_OUTPUT, pictureUri);
- intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION
- | Intent.FLAG_GRANT_READ_URI_PERMISSION);
- intent.setClipData(ClipData.newRawUri(MediaStore.EXTRA_OUTPUT, pictureUri));
- }
-
- private void appendCropExtras(Intent intent) {
- intent.putExtra("crop", "true");
- intent.putExtra("scale", true);
- intent.putExtra("scaleUpIfNeeded", true);
- intent.putExtra("aspectX", 1);
- intent.putExtra("aspectY", 1);
- intent.putExtra("outputX", mPhotoSize);
- intent.putExtra("outputY", mPhotoSize);
+ private static Bitmap convertToBitmap(@NonNull Drawable icon, int size) {
+ Bitmap bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(bitmap);
+ icon.setBounds(0, 0, size, size);
+ icon.draw(canvas);
+ return bitmap;
}
private void onPhotoCropped(final Uri data) {
- // TODO: Replace AsyncTask to avoid possible memory leaks and handle configuration change
- new AsyncTask<Void, Void, Bitmap>() {
- @Override
- protected Bitmap doInBackground(Void... params) {
- InputStream imageStream = null;
- try {
- imageStream = mActivity.getContentResolver()
- .openInputStream(data);
- return BitmapFactory.decodeStream(imageStream);
- } catch (FileNotFoundException fe) {
- Log.w(TAG, "Cannot find image file", fe);
- return null;
- } finally {
- if (imageStream != null) {
- try {
- imageStream.close();
- } catch (IOException ioe) {
- Log.w(TAG, "Cannot close image stream", ioe);
- }
+ ThreadUtils.postOnBackgroundThread(() -> {
+ InputStream imageStream = null;
+ Bitmap bitmap = null;
+ try {
+ imageStream = mActivity.getContentResolver()
+ .openInputStream(data);
+ bitmap = BitmapFactory.decodeStream(imageStream);
+ } catch (FileNotFoundException fe) {
+ Log.w(TAG, "Cannot find image file", fe);
+ } finally {
+ if (imageStream != null) {
+ try {
+ imageStream.close();
+ } catch (IOException ioe) {
+ Log.w(TAG, "Cannot close image stream", ioe);
}
}
}
- @Override
- protected void onPostExecute(Bitmap bitmap) {
- onPhotoProcessed(bitmap);
-
- }
- }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[]) null);
- }
-
- private void onPhotoNotCropped(final Uri data) {
- // TODO: Replace AsyncTask to avoid possible memory leaks and handle configuration change
- new AsyncTask<Void, Void, Bitmap>() {
- @Override
- protected Bitmap doInBackground(Void... params) {
- // Scale and crop to a square aspect ratio
- Bitmap croppedImage = Bitmap.createBitmap(mPhotoSize, mPhotoSize,
- Config.ARGB_8888);
- Canvas canvas = new Canvas(croppedImage);
- Bitmap fullImage;
- try {
- InputStream imageStream = mActivity.getContentResolver()
- .openInputStream(data);
- fullImage = BitmapFactory.decodeStream(imageStream);
- } catch (FileNotFoundException fe) {
- return null;
- }
- if (fullImage != null) {
- int rotation = getRotation(mActivity, data);
- final int squareSize = Math.min(fullImage.getWidth(),
- fullImage.getHeight());
- final int left = (fullImage.getWidth() - squareSize) / 2;
- final int top = (fullImage.getHeight() - squareSize) / 2;
-
- Matrix matrix = new Matrix();
- RectF rectSource = new RectF(left, top,
- left + squareSize, top + squareSize);
- RectF rectDest = new RectF(0, 0, mPhotoSize, mPhotoSize);
- matrix.setRectToRect(rectSource, rectDest, Matrix.ScaleToFit.CENTER);
- matrix.postRotate(rotation, mPhotoSize / 2f, mPhotoSize / 2f);
- canvas.drawBitmap(fullImage, matrix, new Paint());
- return croppedImage;
- } else {
- // Bah! Got nothin.
- return null;
- }
- }
-
- @Override
- protected void onPostExecute(Bitmap bitmap) {
- onPhotoProcessed(bitmap);
+ if (bitmap != null) {
+ Bitmap finalBitmap = bitmap;
+ ThreadUtils.postOnMainThread(() -> onPhotoProcessed(finalBitmap));
}
- }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[]) null);
- }
-
- /**
- * Reads the image's exif data and determines the rotation degree needed to display the image
- * in portrait mode.
- */
- private int getRotation(Context context, Uri selectedImage) {
- int rotation = -1;
- try {
- InputStream imageStream = context.getContentResolver().openInputStream(selectedImage);
- ExifInterface exif = new ExifInterface(imageStream);
- rotation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, -1);
- } catch (IOException exception) {
- Log.e(TAG, "Error while getting rotation", exception);
- }
-
- switch (rotation) {
- case ExifInterface.ORIENTATION_ROTATE_90:
- return 90;
- case ExifInterface.ORIENTATION_ROTATE_180:
- return 180;
- case ExifInterface.ORIENTATION_ROTATE_270:
- return 270;
- default:
- return 0;
- }
+ });
}
private void onPhotoProcessed(Bitmap bitmap) {
@@ -386,29 +168,6 @@ public class EditUserPhotoController {
.getInstance(mImageView.getContext(), mNewUserPhotoBitmap);
mImageView.setImageDrawable(mNewUserPhotoDrawable);
}
- new File(mImagesDir, TAKE_PICTURE_FILE_NAME).delete();
- new File(mImagesDir, CROP_PICTURE_FILE_NAME).delete();
- }
-
- private static int getPhotoSize(Context context) {
- try (Cursor cursor = context.getContentResolver().query(
- DisplayPhoto.CONTENT_MAX_DIMENSIONS_URI,
- new String[]{DisplayPhoto.DISPLAY_MAX_DIM}, null, null, null)) {
- if (cursor != null) {
- cursor.moveToFirst();
- return cursor.getInt(0);
- } else {
- return DEFAULT_PHOTO_SIZE;
- }
- }
- }
-
- private Uri createTempImageUri(Context context, String fileName, boolean purge) {
- final File fullPath = new File(mImagesDir, fileName);
- if (purge) {
- fullPath.delete();
- }
- return FileProvider.getUriForFile(context, mFileAuthority, fullPath);
}
File saveNewUserPhotoBitmap() {
@@ -435,84 +194,4 @@ public class EditUserPhotoController {
void removeNewUserPhotoBitmapFile() {
new File(mImagesDir, NEW_USER_PHOTO_FILE_NAME).delete();
}
-
- private static final class RestrictedMenuItem {
- private final Context mContext;
- private final String mTitle;
- private final Runnable mAction;
- private final RestrictedLockUtils.EnforcedAdmin mAdmin;
- // Restriction may be set by system or something else via UserManager.setUserRestriction().
- private final boolean mIsRestrictedByBase;
-
- /**
- * The menu item, used for popup menu. Any element of such a menu can be disabled by admin.
- *
- * @param context A context.
- * @param title The title of the menu item.
- * @param restriction The restriction, that if is set, blocks the menu item.
- * @param action The action on menu item click.
- */
- RestrictedMenuItem(Context context, String title, String restriction,
- Runnable action) {
- mContext = context;
- mTitle = title;
- mAction = action;
-
- final int myUserId = UserHandle.myUserId();
- mAdmin = RestrictedLockUtilsInternal.checkIfRestrictionEnforced(context,
- restriction, myUserId);
- mIsRestrictedByBase = RestrictedLockUtilsInternal.hasBaseUserRestriction(mContext,
- restriction, myUserId);
- }
-
- @Override
- public String toString() {
- return mTitle;
- }
-
- void doAction() {
- if (isRestrictedByBase()) {
- return;
- }
-
- if (isRestrictedByAdmin()) {
- RestrictedLockUtils.sendShowAdminSupportDetailsIntent(mContext, mAdmin);
- return;
- }
-
- mAction.run();
- }
-
- boolean isRestrictedByAdmin() {
- return mAdmin != null;
- }
-
- boolean isRestrictedByBase() {
- return mIsRestrictedByBase;
- }
- }
-
- /**
- * Provide this adapter to ListPopupWindow.setAdapter() to have a popup window menu, where
- * any element can be restricted by admin (profile owner or device owner).
- */
- private static final class RestrictedPopupMenuAdapter extends ArrayAdapter<RestrictedMenuItem> {
- RestrictedPopupMenuAdapter(Context context, List<RestrictedMenuItem> items) {
- super(context, R.layout.restricted_popup_menu_item, R.id.text, items);
- }
-
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- final View view = super.getView(position, convertView, parent);
- final RestrictedMenuItem item = getItem(position);
- final TextView text = (TextView) view.findViewById(R.id.text);
- final ImageView image = (ImageView) view.findViewById(R.id.restricted_icon);
-
- text.setEnabled(!item.isRestrictedByAdmin() && !item.isRestrictedByBase());
- image.setVisibility(item.isRestrictedByAdmin() && !item.isRestrictedByBase()
- ? ImageView.VISIBLE : ImageView.GONE);
-
- return view;
- }
- }
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/users/PhotoCapabilityUtils.java b/packages/SettingsLib/src/com/android/settingslib/users/PhotoCapabilityUtils.java
index 165c2808f16d..b8615a7e5aa9 100644
--- a/packages/SettingsLib/src/com/android/settingslib/users/PhotoCapabilityUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/users/PhotoCapabilityUtils.java
@@ -40,12 +40,12 @@ public class PhotoCapabilityUtils {
/**
* Check if the current user can perform any activity for
- * android.intent.action.GET_CONTENT action for images.
+ * ACTION_PICK_IMAGES action for images.
* Returns false if the device is currently locked and
* requires a PIN, pattern or password to unlock.
*/
public static boolean canChoosePhoto(Context context) {
- Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
+ Intent intent = new Intent(MediaStore.ACTION_PICK_IMAGES);
intent.setType("image/*");
boolean canPerformActivityForGetImage =
context.getPackageManager().queryIntentActivities(intent, 0).size() > 0;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/AppIconCacheManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/AppIconCacheManagerTest.java
new file mode 100644
index 000000000000..64f8bef1ecf3
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/AppIconCacheManagerTest.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.applications;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.doReturn;
+
+import android.graphics.drawable.Drawable;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+
+@RunWith(RobolectricTestRunner.class)
+public class AppIconCacheManagerTest {
+
+ private static final String APP_PACKAGE_NAME = "com.test.app";
+ private static final int APP_UID = 9999;
+
+ @Mock
+ private Drawable mIcon;
+
+ private AppIconCacheManager mAppIconCacheManager;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mAppIconCacheManager = AppIconCacheManager.getInstance();
+ doReturn(10).when(mIcon).getIntrinsicHeight();
+ doReturn(10).when(mIcon).getIntrinsicWidth();
+ doReturn(mIcon).when(mIcon).mutate();
+ }
+
+ @After
+ public void tearDown() {
+ AppIconCacheManager.release();
+ }
+
+ @Test
+ public void get_invalidPackageOrUid_shouldReturnNull() {
+ assertThat(mAppIconCacheManager.get(/* packageName= */ null, /* uid= */ -1)).isNull();
+ }
+
+ @Test
+ public void put_invalidPackageOrUid_shouldNotCrash() {
+ mAppIconCacheManager.put(/* packageName= */ null, /* uid= */ 0, mIcon);
+ // no crash
+ }
+
+ @Test
+ public void put_invalidIcon_shouldNotCacheIcon() {
+ mAppIconCacheManager.put(APP_PACKAGE_NAME, APP_UID, /* drawable= */ null);
+
+ assertThat(mAppIconCacheManager.get(APP_PACKAGE_NAME, APP_UID)).isNull();
+ }
+
+ @Test
+ public void put_invalidIconSize_shouldNotCacheIcon() {
+ doReturn(-1).when(mIcon).getIntrinsicHeight();
+ doReturn(-1).when(mIcon).getIntrinsicWidth();
+
+ mAppIconCacheManager.put(APP_PACKAGE_NAME, APP_UID, mIcon);
+
+ assertThat(mAppIconCacheManager.get(APP_PACKAGE_NAME, APP_UID)).isNull();
+ }
+
+ @Test
+ public void put_shouldCacheIcon() {
+ mAppIconCacheManager.put(APP_PACKAGE_NAME, APP_UID, mIcon);
+
+ assertThat(mAppIconCacheManager.get(APP_PACKAGE_NAME, APP_UID)).isEqualTo(mIcon);
+ }
+
+ @Test
+ public void release_noInstance_shouldNotCrash() {
+ mAppIconCacheManager = null;
+
+ AppIconCacheManager.release();
+ // no crash
+ }
+
+ @Test
+ public void release_existInstance_shouldClearCache() {
+ mAppIconCacheManager.put(APP_PACKAGE_NAME, APP_UID, mIcon);
+
+ AppIconCacheManager.release();
+
+ assertThat(mAppIconCacheManager.get(APP_PACKAGE_NAME, APP_UID)).isNull();
+ }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/AppUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/AppUtilsTest.java
new file mode 100644
index 000000000000..8e448aa0eace
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/AppUtilsTest.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.applications;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.graphics.drawable.Drawable;
+
+import com.android.settingslib.Utils;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+
+import java.io.File;
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.concurrent.TimeUnit;
+
+@RunWith(RobolectricTestRunner.class)
+public class AppUtilsTest {
+
+ private static final String APP_PACKAGE_NAME = "com.test.app";
+ private static final int APP_UID = 9999;
+
+ @Mock
+ private Drawable mIcon;
+
+ private Context mContext;
+ private AppIconCacheManager mAppIconCacheManager;
+ private ApplicationInfo mAppInfo;
+ private ApplicationsState.AppEntry mAppEntry;
+ private ArrayList<ApplicationsState.AppEntry> mAppEntries;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mContext = RuntimeEnvironment.application;
+ mAppIconCacheManager = AppIconCacheManager.getInstance();
+ mAppInfo = createApplicationInfo(APP_PACKAGE_NAME, APP_UID);
+ mAppEntry = createAppEntry(mAppInfo, /* id= */ 1);
+ mAppEntries = new ArrayList<>(Arrays.asList(mAppEntry));
+ doReturn(mIcon).when(mIcon).mutate();
+ }
+
+ @After
+ public void tearDown() {
+ AppIconCacheManager.release();
+ }
+
+ @Test
+ public void getIcon_nullAppEntry_shouldReturnNull() {
+ assertThat(AppUtils.getIcon(mContext, /* appEntry= */ null)).isNull();
+ }
+
+ @Test
+ @Config(shadows = ShadowUtils.class)
+ public void getIcon_noCachedIcon_shouldNotReturnNull() {
+ assertThat(AppUtils.getIcon(mContext, mAppEntry)).isNotNull();
+ }
+
+ @Test
+ public void getIcon_existCachedIcon_shouldReturnCachedIcon() {
+ mAppIconCacheManager.put(APP_PACKAGE_NAME, APP_UID, mIcon);
+
+ assertThat(AppUtils.getIcon(mContext, mAppEntry)).isEqualTo(mIcon);
+ }
+
+ @Test
+ public void getIconFromCache_nullAppEntry_shouldReturnNull() {
+ assertThat(AppUtils.getIconFromCache(/* appEntry= */ null)).isNull();
+ }
+
+ @Test
+ public void getIconFromCache_shouldReturnCachedIcon() {
+ mAppIconCacheManager.put(APP_PACKAGE_NAME, APP_UID, mIcon);
+
+ assertThat(AppUtils.getIconFromCache(mAppEntry)).isEqualTo(mIcon);
+ }
+
+ @Test
+ public void preloadTopIcons_nullAppEntries_shouldNotCrash() {
+ AppUtils.preloadTopIcons(mContext, /* appEntries= */ null, /* number= */ 1);
+ // no crash
+ }
+
+ @Test
+ public void preloadTopIcons_zeroPreloadIcons_shouldNotCacheIcons() {
+ AppUtils.preloadTopIcons(mContext, mAppEntries, /* number= */ 0);
+
+ assertThat(mAppIconCacheManager.get(APP_PACKAGE_NAME, APP_UID)).isNull();
+ }
+
+ @Test
+ @Config(shadows = ShadowUtils.class)
+ public void preloadTopIcons_shouldCheckIconFromCache() throws InterruptedException {
+ AppUtils.preloadTopIcons(mContext, mAppEntries, /* number= */ 1);
+
+ TimeUnit.SECONDS.sleep(1);
+ assertThat(mAppIconCacheManager.get(APP_PACKAGE_NAME, APP_UID)).isNotNull();
+ }
+
+ private ApplicationsState.AppEntry createAppEntry(ApplicationInfo appInfo, int id) {
+ ApplicationsState.AppEntry appEntry = new ApplicationsState.AppEntry(mContext, appInfo, id);
+ appEntry.label = "label";
+ appEntry.mounted = true;
+ final File apkFile = mock(File.class);
+ doReturn(true).when(apkFile).exists();
+ try {
+ Field field = ApplicationsState.AppEntry.class.getDeclaredField("apkFile");
+ field.setAccessible(true);
+ field.set(appEntry, apkFile);
+ } catch (NoSuchFieldException | IllegalAccessException e) {
+ fail("Not able to mock apkFile: " + e);
+ }
+ return appEntry;
+ }
+
+ private ApplicationInfo createApplicationInfo(String packageName, int uid) {
+ ApplicationInfo appInfo = new ApplicationInfo();
+ appInfo.sourceDir = "appPath";
+ appInfo.packageName = packageName;
+ appInfo.uid = uid;
+ return appInfo;
+ }
+
+ @Implements(Utils.class)
+ private static class ShadowUtils {
+ @Implementation
+ public static Drawable getBadgedIcon(Context context, ApplicationInfo appInfo) {
+ final Drawable icon = mock(Drawable.class);
+ doReturn(10).when(icon).getIntrinsicHeight();
+ doReturn(10).when(icon).getIntrinsicWidth();
+ doReturn(icon).when(icon).mutate();
+ return icon;
+ }
+ }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java
index 10ccd22eca83..1f2297ba3a0c 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java
@@ -33,6 +33,7 @@ import static org.mockito.Mockito.when;
import static org.robolectric.shadow.api.Shadow.extract;
import android.annotation.UserIdInt;
+import android.app.Application;
import android.app.ApplicationPackageManager;
import android.app.usage.StorageStats;
import android.app.usage.StorageStatsManager;
@@ -110,6 +111,7 @@ public class ApplicationsStateRoboTest {
private ApplicationsState mApplicationsState;
private Session mSession;
+ private Application mApplication;
@Mock
private Callbacks mCallbacks;
@@ -190,6 +192,7 @@ public class ApplicationsStateRoboTest {
ShadowContextImpl shadowContext = Shadow.extract(
RuntimeEnvironment.application.getBaseContext());
shadowContext.setSystemService(Context.STORAGE_STATS_SERVICE, mStorageStatsManager);
+ mApplication = spy(RuntimeEnvironment.application);
StorageStats storageStats = new StorageStats();
storageStats.codeBytes = 10;
storageStats.cacheBytes = 30;
@@ -207,8 +210,7 @@ public class ApplicationsStateRoboTest {
anyLong() /* flags */, anyInt() /* userId */)).thenReturn(new ParceledListSlice(infos));
ApplicationsState.sInstance = null;
- mApplicationsState =
- ApplicationsState.getInstance(RuntimeEnvironment.application, mPackageManagerService);
+ mApplicationsState = ApplicationsState.getInstance(mApplication, mPackageManagerService);
mApplicationsState.clearEntries();
mSession = mApplicationsState.newSession(mCallbacks);
@@ -703,6 +705,23 @@ public class ApplicationsStateRoboTest {
verify(mApplicationsState, never()).clearEntries();
}
+ @Test
+ public void testDefaultSession_enabledAppIconCache_shouldSkipPreloadIcon() {
+ when(mApplication.getPackageName()).thenReturn("com.android.settings");
+ mSession.onResume();
+
+ addApp(HOME_PACKAGE_NAME, 1);
+ addApp(LAUNCHABLE_PACKAGE_NAME, 2);
+ mSession.rebuild(ApplicationsState.FILTER_EVERYTHING, ApplicationsState.SIZE_COMPARATOR);
+ processAllMessages();
+ verify(mCallbacks).onRebuildComplete(mAppEntriesCaptor.capture());
+
+ List<AppEntry> appEntries = mAppEntriesCaptor.getValue();
+ for (AppEntry appEntry : appEntries) {
+ assertThat(appEntry.icon).isNull();
+ }
+ }
+
private void setupDoResumeIfNeededLocked(ArrayList<ApplicationInfo> ownerApps,
ArrayList<ApplicationInfo> profileApps)
throws RemoteException {
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/ConnectivityPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/ConnectivityPreferenceControllerTest.java
index c1cc3ae9778a..445701fa7244 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/ConnectivityPreferenceControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/ConnectivityPreferenceControllerTest.java
@@ -18,6 +18,7 @@ package com.android.settingslib.deviceinfo;
import static com.google.common.truth.Truth.assertWithMessage;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.doReturn;
@@ -33,7 +34,6 @@ import android.os.Handler;
import com.android.settingslib.core.lifecycle.Lifecycle;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
@@ -55,7 +55,6 @@ public class ConnectivityPreferenceControllerTest {
}
@Test
- @Ignore
public void testBroadcastReceiver() {
final AbstractConnectivityPreferenceController preferenceController =
spy(new ConcreteConnectivityPreferenceController(mContext, mLifecycle));
@@ -73,7 +72,7 @@ public class ConnectivityPreferenceControllerTest {
verify(mContext, times(1))
.registerReceiver(receiverArgumentCaptor.capture(),
filterArgumentCaptor.capture(),
- anyString(), nullable(Handler.class));
+ anyString(), nullable(Handler.class), anyInt());
final BroadcastReceiver receiver = receiverArgumentCaptor.getValue();
final IntentFilter filter = filterArgumentCaptor.getValue();
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/EditUserInfoControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/EditUserInfoControllerTest.java
index d6c8816ecc58..a5ee4c35f724 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/EditUserInfoControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/EditUserInfoControllerTest.java
@@ -62,7 +62,7 @@ public class EditUserInfoControllerTest {
@Mock
private ActivityStarter mActivityStarter;
- private boolean mCanChangePhoto;
+ private boolean mPhotoRestrictedByBase;
private Activity mActivity;
private TestEditUserInfoController mController;
@@ -85,8 +85,8 @@ public class EditUserInfoControllerTest {
}
@Override
- boolean canChangePhoto(Context context) {
- return mCanChangePhoto;
+ boolean isChangePhotoRestrictedByBase(Context context) {
+ return mPhotoRestrictedByBase;
}
}
@@ -96,7 +96,7 @@ public class EditUserInfoControllerTest {
mActivity = spy(ActivityController.of(new FragmentActivity()).get());
mActivity.setTheme(R.style.Theme_AppCompat_DayNight);
mController = new TestEditUserInfoController();
- mCanChangePhoto = true;
+ mPhotoRestrictedByBase = true;
}
@Test
@@ -260,7 +260,7 @@ public class EditUserInfoControllerTest {
@Test
public void createDialog_canNotChangePhoto_nullPhotoController() {
- mCanChangePhoto = false;
+ mPhotoRestrictedByBase = false;
mController.createDialog(mActivity, mActivityStarter, mCurrentIcon,
"test", "title", null, null);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceTest.java
index 10ac829186d4..3b18c57e28fa 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceTest.java
@@ -64,6 +64,20 @@ public class FooterPreferenceTest {
}
@Test
+ public void setLearnMoreText_shouldSetAsTextInLearnMore() {
+ final PreferenceViewHolder holder = PreferenceViewHolder.createInstanceForTests(
+ LayoutInflater.from(mContext).inflate(R.layout.preference_footer, null));
+ mFooterPreference.setLearnMoreText("Custom learn more");
+ mFooterPreference.setLearnMoreAction(view -> { /* do nothing */ } /* listener */);
+
+ mFooterPreference.onBindViewHolder(holder);
+
+ assertThat(((TextView) holder.findViewById(
+ R.id.settingslib_learn_more)).getText().toString())
+ .isEqualTo("Custom learn more");
+ }
+
+ @Test
public void setContentDescription_contentSet_shouldGetSameContentDescription() {
mFooterPreference.setContentDescription("test");
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
index dc7632dbc4a9..b851232ace82 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
@@ -46,7 +46,7 @@ import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.Log;
-import com.android.internal.content.PackageHelper;
+import com.android.internal.content.InstallLocationUtils;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.RILConstants;
import com.android.internal.util.XmlUtils;
@@ -783,7 +783,7 @@ class DatabaseHelper extends SQLiteOpenHelper {
+ " VALUES(?,?);");
loadSetting(stmt, Global.SET_INSTALL_LOCATION, 0);
loadSetting(stmt, Global.DEFAULT_INSTALL_LOCATION,
- PackageHelper.APP_INSTALL_AUTO);
+ InstallLocationUtils.APP_INSTALL_AUTO);
db.setTransactionSuccessful();
} finally {
db.endTransaction();
@@ -2534,7 +2534,7 @@ class DatabaseHelper extends SQLiteOpenHelper {
loadSetting(stmt, Settings.Global.SET_INSTALL_LOCATION, 0);
loadSetting(stmt, Settings.Global.DEFAULT_INSTALL_LOCATION,
- PackageHelper.APP_INSTALL_AUTO);
+ InstallLocationUtils.APP_INSTALL_AUTO);
// Set default cdma emergency tone
loadSetting(stmt, Settings.Global.EMERGENCY_TONE, 0);
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 52a708d85bba..13ae87015fa4 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -326,6 +326,8 @@ public class SettingsBackupTest {
Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL_MAX,
Settings.Global.LOW_POWER_MODE_STICKY,
Settings.Global.LOW_POWER_MODE_SUGGESTION_PARAMS,
+ Settings.Global.LOW_POWER_STANDBY_ACTIVE_DURING_MAINTENANCE,
+ Settings.Global.LOW_POWER_STANDBY_ENABLED,
Settings.Global.LTE_SERVICE_FORCED,
Settings.Global.LID_BEHAVIOR,
Settings.Global.MAX_ERROR_BYTES_PREFIX,
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index ebff00c59732..ca90fbedd4e0 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -192,6 +192,9 @@
<uses-permission android:name="android.permission.REVOKE_RUNTIME_PERMISSIONS" />
<uses-permission android:name="android.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS" />
<uses-permission android:name="android.permission.WHITELIST_RESTRICTED_PERMISSIONS" />
+ <!-- Permission required for processes that don't own the focused window to switch
+ touch mode state -->
+ <uses-permission android:name="android.permission.MODIFY_TOUCH_MODE_STATE" />
<!-- Permission required to test onPermissionsChangedListener -->
<uses-permission android:name="android.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS" />
<uses-permission android:name="android.permission.SET_KEYBOARD_LAYOUT" />
@@ -248,6 +251,7 @@
<uses-permission android:name="android.permission.MANAGE_CONTENT_CAPTURE" />
<uses-permission android:name="android.permission.MANAGE_CONTENT_SUGGESTIONS" />
<uses-permission android:name="android.permission.MANAGE_APP_PREDICTIONS" />
+ <uses-permission android:name="android.permission.MANAGE_LOW_POWER_STANDBY" />
<uses-permission android:name="android.permission.MANAGE_SEARCH_UI" />
<uses-permission android:name="android.permission.MANAGE_SMARTSPACE" />
<uses-permission android:name="android.permission.MANAGE_WALLPAPER_EFFECTS_GENERATION" />
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index 137a1fd4f299..b1cfb1118bb6 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -107,6 +107,7 @@ android_library {
"androidx.slice_slice-view",
"androidx.slice_slice-builders",
"androidx.arch.core_core-runtime",
+ "androidx.lifecycle_lifecycle-common-java8",
"androidx.lifecycle_lifecycle-extensions",
"androidx.dynamicanimation_dynamicanimation",
"androidx-constraintlayout_constraintlayout",
@@ -209,6 +210,7 @@ android_library {
"androidx.slice_slice-view",
"androidx.slice_slice-builders",
"androidx.arch.core_core-runtime",
+ "androidx.lifecycle_lifecycle-common-java8",
"androidx.lifecycle_lifecycle-extensions",
"androidx.dynamicanimation_dynamicanimation",
"androidx-constraintlayout_constraintlayout",
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index f35f5dd0c3ae..59893817da6f 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -213,6 +213,9 @@
<!-- DevicePolicyManager get user restrictions -->
<uses-permission android:name="android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS" />
+ <!-- DevicePolicyManager get admin policy -->
+ <uses-permission android:name="android.permission.QUERY_ADMIN_POLICY" />
+
<!-- TV picture-in-picture -->
<uses-permission android:name="android.permission.RECEIVE_MEDIA_RESOURCE_USAGE" />
@@ -301,6 +304,10 @@
<!-- For clipboard overlay -->
<uses-permission android:name="android.permission.READ_CLIPBOARD_IN_BACKGROUND" />
+ <uses-permission android:name="android.permission.SET_CLIP_SOURCE" />
+
+ <!-- To change system language (HDMI CEC) -->
+ <uses-permission android:name="android.permission.CHANGE_CONFIGURATION" />
<protected-broadcast android:name="com.android.settingslib.action.REGISTER_SLICE_RECEIVER" />
<protected-broadcast android:name="com.android.settingslib.action.UNREGISTER_SLICE_RECEIVER" />
@@ -485,6 +492,16 @@
android:excludeFromRecents="true">
</activity>
+ <!-- started from HdmiCecLocalDevicePlayback -->
+ <activity android:name=".hdmi.HdmiCecSetMenuLanguageActivity"
+ android:exported="true"
+ android:launchMode="singleTop"
+ android:permission="android.permission.CHANGE_CONFIGURATION"
+ android:theme="@style/BottomSheet"
+ android:finishOnCloseSystemDialogs="true"
+ android:excludeFromRecents="true">
+ </activity>
+
<!-- started from SensoryPrivacyService -->
<activity android:name=".sensorprivacy.SensorUseStartedActivity"
android:exported="true"
diff --git a/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java b/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java
index 68c8c3e02a42..ffee8946e016 100644
--- a/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java
+++ b/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java
@@ -20,6 +20,7 @@ import android.app.PendingIntent;
import android.app.smartspace.SmartspaceAction;
import android.app.smartspace.SmartspaceTarget;
import android.app.smartspace.SmartspaceTargetEvent;
+import android.app.smartspace.uitemplatedata.SmartspaceTapAction;
import android.content.ActivityNotFoundException;
import android.content.Intent;
import android.graphics.drawable.Drawable;
@@ -143,6 +144,18 @@ public interface BcSmartspaceDataPlugin extends Plugin {
}
}
+ default void startFromAction(SmartspaceTapAction action, View v, boolean showOnLockscreen) {
+ try {
+ if (action.getIntent() != null) {
+ startIntent(v, action.getIntent(), showOnLockscreen);
+ } else if (action.getPendingIntent() != null) {
+ startPendingIntent(action.getPendingIntent(), showOnLockscreen);
+ }
+ } catch (ActivityNotFoundException e) {
+ Log.w(TAG, "Could not launch intent for action: " + action, e);
+ }
+ }
+
/** Start the intent */
void startIntent(View v, Intent i, boolean showOnLockscreen);
diff --git a/packages/SystemUI/res/layout/clipboard_overlay.xml b/packages/SystemUI/res/layout/clipboard_overlay.xml
index 2a3761e4ca05..7e31909613ee 100644
--- a/packages/SystemUI/res/layout/clipboard_overlay.xml
+++ b/packages/SystemUI/res/layout/clipboard_overlay.xml
@@ -17,6 +17,7 @@
<com.android.systemui.clipboardoverlay.DraggableConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:theme="@style/Screenshot"
android:alpha="0"
android:layout_width="match_parent"
android:layout_height="match_parent">
@@ -50,7 +51,8 @@
<LinearLayout
android:id="@+id/actions"
android:layout_width="wrap_content"
- android:layout_height="wrap_content">
+ android:layout_height="wrap_content"
+ android:animateLayoutChanges="true">
<include layout="@layout/screenshot_action_chip"
android:id="@+id/remote_copy_chip"/>
<include layout="@layout/screenshot_action_chip"
@@ -64,7 +66,7 @@
android:layout_marginStart="@dimen/overlay_offset_x"
android:layout_marginBottom="@dimen/overlay_offset_y"
app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintBottom_toBottomOf="@id/actions_container_background"
android:elevation="@dimen/overlay_preview_elevation"
app:layout_constraintEnd_toEndOf="@id/clipboard_preview_end"
app:layout_constraintTop_toTopOf="@id/clipboard_preview_top"
diff --git a/packages/SystemUI/res/layout/controls_fullscreen.xml b/packages/SystemUI/res/layout/controls_fullscreen.xml
index 7fd029cb2c1e..11a566588738 100644
--- a/packages/SystemUI/res/layout/controls_fullscreen.xml
+++ b/packages/SystemUI/res/layout/controls_fullscreen.xml
@@ -35,7 +35,8 @@
android:layout_height="wrap_content"
android:clipChildren="false"
android:orientation="vertical"
- android:clipToPadding="false" />
+ android:clipToPadding="false"
+ android:paddingHorizontal="@dimen/controls_padding_horizontal" />
</com.android.systemui.globalactions.MinHeightScrollView>
</LinearLayout>
diff --git a/packages/SystemUI/res/layout/dream_overlay_complication_clock_date.xml b/packages/SystemUI/res/layout/dream_overlay_complication_clock_date.xml
new file mode 100644
index 000000000000..b6f516fd2042
--- /dev/null
+++ b/packages/SystemUI/res/layout/dream_overlay_complication_clock_date.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+-->
+<TextClock
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/date_view"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingLeft="@dimen/dream_overlay_complication_clock_date_padding_left"
+ android:paddingBottom="@dimen/dream_overlay_complication_clock_date_padding_bottom"
+ android:gravity="center_horizontal"
+ android:textColor="@android:color/white"
+ android:shadowColor="@color/keyguard_shadow_color"
+ android:shadowRadius="?attr/shadowRadius"
+ android:format12Hour="EEE, MMM d"
+ android:format24Hour="EEE, MMM d"
+ android:textSize="@dimen/dream_overlay_complication_clock_date_text_size"/>
diff --git a/packages/SystemUI/res/layout/dream_overlay_complication_clock_time.xml b/packages/SystemUI/res/layout/dream_overlay_complication_clock_time.xml
new file mode 100644
index 000000000000..a41d34f8ab41
--- /dev/null
+++ b/packages/SystemUI/res/layout/dream_overlay_complication_clock_time.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+-->
+<TextClock
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/time_view"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingLeft="@dimen/dream_overlay_complication_clock_time_padding_left"
+ android:fontFamily="sans-serif-thin"
+ android:textColor="@android:color/white"
+ android:format12Hour="h:mm"
+ android:format24Hour="kk:mm"
+ android:shadowColor="@color/keyguard_shadow_color"
+ android:shadowRadius="?attr/shadowRadius"
+ android:textSize="@dimen/dream_overlay_complication_clock_time_text_size"/>
diff --git a/packages/SystemUI/res/layout/dream_overlay_complication_weather.xml b/packages/SystemUI/res/layout/dream_overlay_complication_weather.xml
new file mode 100644
index 000000000000..08f0d6781b04
--- /dev/null
+++ b/packages/SystemUI/res/layout/dream_overlay_complication_weather.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+-->
+<TextView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/weather_view"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingLeft="@dimen/dream_overlay_complication_weather_padding_left"
+ android:paddingBottom="@dimen/dream_overlay_complication_weather_padding_bottom"
+ android:textColor="@android:color/white"
+ android:shadowColor="@color/keyguard_shadow_color"
+ android:shadowRadius="?attr/shadowRadius"
+ android:textSize="@dimen/dream_overlay_complication_weather_text_size"/>
diff --git a/packages/SystemUI/res/layout/dream_overlay_complications_layer.xml b/packages/SystemUI/res/layout/dream_overlay_complications_layer.xml
index 51359471ff98..f4eb32f7cf22 100644
--- a/packages/SystemUI/res/layout/dream_overlay_complications_layer.xml
+++ b/packages/SystemUI/res/layout/dream_overlay_complications_layer.xml
@@ -20,30 +20,28 @@
android:id="@+id/dream_overlay_complications_layer"
android:layout_width="match_parent"
android:layout_height="match_parent">
- <TextClock
- android:id="@+id/time_view"
+ <androidx.constraintlayout.widget.Guideline
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:fontFamily="sans-serif-thin"
- android:format12Hour="h:mm"
- android:format24Hour="kk:mm"
- android:shadowColor="#B2000000"
- android:shadowRadius="2.0"
- android:singleLine="true"
- android:textSize="72sp"
- app:layout_constraintBottom_toTopOf="@+id/date_view"
- app:layout_constraintStart_toStartOf="parent" />
- <TextClock
- android:id="@+id/date_view"
+ android:id="@+id/complication_top_guide"
+ app:layout_constraintGuide_percent="@dimen/dream_overlay_complication_guide_top_percent"
+ android:orientation="horizontal"/>
+ <androidx.constraintlayout.widget.Guideline
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:shadowColor="#B2000000"
- android:shadowRadius="2.0"
- android:format12Hour="EEE, MMM d"
- android:format24Hour="EEE, MMM d"
- android:singleLine="true"
- android:textSize="18sp"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintEnd_toEndOf="@+id/time_view"
- app:layout_constraintStart_toStartOf="@+id/time_view" />
+ android:id="@+id/complication_end_guide"
+ app:layout_constraintGuide_percent="@dimen/dream_overlay_complication_guide_end_percent"
+ android:orientation="vertical"/>
+ <androidx.constraintlayout.widget.Guideline
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/complication_bottom_guide"
+ app:layout_constraintGuide_percent="@dimen/dream_overlay_complication_guide_bottom_percent"
+ android:orientation="horizontal"/>
+ <androidx.constraintlayout.widget.Guideline
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/complication_start_guide"
+ app:layout_constraintGuide_percent="@dimen/dream_overlay_complication_guide_start_percent"
+ android:orientation="vertical"/>
</androidx.constraintlayout.widget.ConstraintLayout> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/dream_overlay_container.xml b/packages/SystemUI/res/layout/dream_overlay_container.xml
index 4929f502fef0..3c2183d0ca61 100644
--- a/packages/SystemUI/res/layout/dream_overlay_container.xml
+++ b/packages/SystemUI/res/layout/dream_overlay_container.xml
@@ -25,7 +25,7 @@
android:id="@+id/dream_overlay_content"
android:layout_width="match_parent"
android:layout_height="0dp"
- app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/dream_overlay_status_bar"
app:layout_constraintBottom_toBottomOf="parent" />
<com.android.systemui.dreams.DreamOverlayStatusBarView
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index 2290964eccd0..39d7f4f5db4e 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -122,7 +122,7 @@
android:layout_marginTop="@dimen/notification_panel_margin_top"
android:layout_width="@dimen/notification_panel_width"
android:layout_height="match_parent"
- android:layout_marginBottom="@dimen/close_handle_underlap"
+ android:layout_marginBottom="@dimen/notification_panel_margin_bottom"
android:importantForAccessibility="no"
systemui:layout_constraintStart_toStartOf="parent"
systemui:layout_constraintEnd_toEndOf="parent"
diff --git a/packages/SystemUI/res/values-land/config.xml b/packages/SystemUI/res/values-land/config.xml
index ac4dfd212bb8..8c5006de577e 100644
--- a/packages/SystemUI/res/values-land/config.xml
+++ b/packages/SystemUI/res/values-land/config.xml
@@ -31,9 +31,6 @@
<!-- orientation of the dead zone when touches have recently occurred elsewhere on screen -->
<integer name="navigation_bar_deadzone_orientation">1</integer>
- <!-- Max number of columns for quick controls area -->
- <integer name="controls_max_columns">4</integer>
-
<!-- Max number of columns for power menu -->
<integer name="power_menu_max_columns">4</integer>
</resources>
diff --git a/packages/SystemUI/res/values-land/dimens.xml b/packages/SystemUI/res/values-land/dimens.xml
index fc5edf3ade8f..9d24e9b97da3 100644
--- a/packages/SystemUI/res/values-land/dimens.xml
+++ b/packages/SystemUI/res/values-land/dimens.xml
@@ -66,4 +66,6 @@
<dimen name="controls_management_favorites_top_margin">8dp</dimen>
<dimen name="wallet_card_carousel_container_top_margin">24dp</dimen>
+
+ <dimen name="large_dialog_width">348dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values-sw600dp-land/config.xml b/packages/SystemUI/res/values-sw600dp-land/config.xml
index dabc3108458f..fe546f65bb13 100644
--- a/packages/SystemUI/res/values-sw600dp-land/config.xml
+++ b/packages/SystemUI/res/values-sw600dp-land/config.xml
@@ -15,9 +15,6 @@
~ limitations under the License
-->
<resources>
- <!-- Max number of columns for quick controls area -->
- <integer name="controls_max_columns">2</integer>
-
<!-- The maximum number of rows in the QSPanel -->
<integer name="quick_settings_max_rows">3</integer>
diff --git a/packages/SystemUI/res/values-sw600dp-land/dimens.xml b/packages/SystemUI/res/values-sw600dp-land/dimens.xml
index 89d046b83d97..c2cec5231243 100644
--- a/packages/SystemUI/res/values-sw600dp-land/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp-land/dimens.xml
@@ -26,6 +26,10 @@
keyguard_split_shade_top_margin - status_bar_header_height_keyguard = 8dp -->
<dimen name="keyguard_clock_top_margin">8dp</dimen>
+ <dimen name="split_shade_notifications_scrim_margin_bottom">16dp</dimen>
+
+ <dimen name="notification_panel_margin_bottom">48dp</dimen>
+
<!-- Limit the TaskView to this percentage of the overall screen width (0.0 - 1.0) -->
<item name="controls_task_view_width_percentage" translatable="false" format="float" type="dimen">0.45</item>
<dimen name="controls_task_view_right_margin">8dp</dimen>
diff --git a/packages/SystemUI/res/values-sw600dp-port/config.xml b/packages/SystemUI/res/values-sw600dp-port/config.xml
index 02fd25bd077c..3c6a81e7c617 100644
--- a/packages/SystemUI/res/values-sw600dp-port/config.xml
+++ b/packages/SystemUI/res/values-sw600dp-port/config.xml
@@ -15,7 +15,6 @@
~ limitations under the License
-->
<resources>
-
<!-- The maximum number of tiles in the QuickQSPanel -->
<integer name="quick_qs_panel_max_tiles">6</integer>
diff --git a/packages/SystemUI/res/values-sw600dp/config.xml b/packages/SystemUI/res/values-sw600dp/config.xml
index f5dc7e3e16da..1b8453ae824d 100644
--- a/packages/SystemUI/res/values-sw600dp/config.xml
+++ b/packages/SystemUI/res/values-sw600dp/config.xml
@@ -29,9 +29,6 @@
<!-- orientation of the dead zone when touches have recently occurred elsewhere on screen -->
<integer name="navigation_bar_deadzone_orientation">0</integer>
- <!-- Max number of columns for quick controls area -->
- <integer name="controls_max_columns">4</integer>
-
<!-- How many lines to show in the security footer -->
<integer name="qs_security_footer_maxLines">1</integer>
diff --git a/packages/SystemUI/res/values-sw600dp/dimens.xml b/packages/SystemUI/res/values-sw600dp/dimens.xml
index 7d033018c27f..a66ed15c9d84 100644
--- a/packages/SystemUI/res/values-sw600dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp/dimens.xml
@@ -69,5 +69,5 @@
<dimen name="qs_detail_margin_top">0dp</dimen>
<!-- The width of large/content heavy dialogs (e.g. Internet, Media output, etc) -->
- <dimen name="large_dialog_width">504dp</dimen>
+ <dimen name="large_dialog_width">472dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values-sw720dp-land/config.xml b/packages/SystemUI/res/values-sw720dp-land/config.xml
index ae89ef4ccc86..be34a48d1cd6 100644
--- a/packages/SystemUI/res/values-sw720dp-land/config.xml
+++ b/packages/SystemUI/res/values-sw720dp-land/config.xml
@@ -15,9 +15,6 @@
~ limitations under the License
-->
<resources>
- <!-- Max number of columns for quick controls area -->
- <integer name="controls_max_columns">2</integer>
-
<!-- The maximum number of rows in the QSPanel -->
<integer name="quick_settings_max_rows">3</integer>
diff --git a/packages/SystemUI/res/values-sw720dp-land/dimens.xml b/packages/SystemUI/res/values-sw720dp-land/dimens.xml
new file mode 100644
index 000000000000..219fd43c0e83
--- /dev/null
+++ b/packages/SystemUI/res/values-sw720dp-land/dimens.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (c) 2022, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+*/
+-->
+<resources>
+ <dimen name="split_shade_notifications_scrim_margin_bottom">16dp</dimen>
+ <dimen name="notification_panel_margin_bottom">56dp</dimen>
+</resources>
diff --git a/packages/SystemUI/res/values-w500dp/config.xml b/packages/SystemUI/res/values-w500dp/config.xml
new file mode 100644
index 000000000000..ef499ff5cdb7
--- /dev/null
+++ b/packages/SystemUI/res/values-w500dp/config.xml
@@ -0,0 +1,20 @@
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources>
+ <!-- Max number of columns for quick controls area -->
+ <integer name="controls_max_columns">3</integer>
+</resources> \ No newline at end of file
diff --git a/packages/SystemUI/res/values-w500dp/dimens.xml b/packages/SystemUI/res/values-w500dp/dimens.xml
new file mode 100644
index 000000000000..5ce5ceee6dc9
--- /dev/null
+++ b/packages/SystemUI/res/values-w500dp/dimens.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources>
+ <dimen name="controls_padding_horizontal">75dp</dimen>
+</resources>
diff --git a/packages/SystemUI/res/values-w850dp/config.xml b/packages/SystemUI/res/values-w850dp/config.xml
new file mode 100644
index 000000000000..337ebe19c748
--- /dev/null
+++ b/packages/SystemUI/res/values-w850dp/config.xml
@@ -0,0 +1,20 @@
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources>
+ <!-- Max number of columns for quick controls area -->
+ <integer name="controls_max_columns">4</integer>
+</resources> \ No newline at end of file
diff --git a/packages/SystemUI/res/values-w850dp/dimens.xml b/packages/SystemUI/res/values-w850dp/dimens.xml
new file mode 100644
index 000000000000..bb6ba8fb07b6
--- /dev/null
+++ b/packages/SystemUI/res/values-w850dp/dimens.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources>
+ <dimen name="controls_padding_horizontal">205dp</dimen>
+</resources>
diff --git a/packages/SystemUI/res/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml
index db699242a061..de136de9dd5f 100644
--- a/packages/SystemUI/res/values/attrs.xml
+++ b/packages/SystemUI/res/values/attrs.xml
@@ -85,7 +85,7 @@
Contract: Pixel with fillColor blended over backgroundColor blended over translucent should
equal to singleToneColor blended over translucent. -->
<declare-styleable name="TonedIcon">
- <attr name="backgroundColor" format="integer" />
+ <attr name="iconBackgroundColor" format="integer" />
<attr name="fillColor" format="integer" />
<attr name="singleToneColor" format="integer" />
<attr name="homeHandleColor" format="integer" />
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index f94031c73e3c..bba616fe24a2 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -311,9 +311,6 @@
<!-- Move the back button drawable for 3 button layout upwards in ime mode and in portrait -->
<dimen name="navbar_back_button_ime_offset">2dp</dimen>
- <!-- Amount of close_handle that will NOT overlap the notification list -->
- <dimen name="close_handle_underlap">32dp</dimen>
-
<!-- Height of the status bar header bar in the car setting. -->
<dimen name="car_status_bar_header_height">128dp</dimen>
@@ -369,8 +366,19 @@
<!-- The top margin of the panel that holds the list of notifications. -->
<dimen name="notification_panel_margin_top">0dp</dimen>
- <!-- The bottom margin of the panel that holds the list of notifications. -->
- <dimen name="notification_panel_margin_bottom">0dp</dimen>
+ <!-- The minimum content height for the split shade NSSL.
+ It is used because if the height is too small, the expansion motion is too fast.
+ Note that the value of 256dp is more or less a random value and can be changed to tweak
+ the expansion motion.
+ -->
+ <dimen name="nssl_split_shade_min_content_height">256dp</dimen>
+
+ <dimen name="notification_panel_margin_bottom">32dp</dimen>
+
+ <!-- The bottom padding of the panel that holds the list of notifications. -->
+ <dimen name="notification_panel_padding_bottom">0dp</dimen>
+
+ <dimen name="split_shade_notifications_scrim_margin_bottom">0dp</dimen>
<dimen name="notification_panel_width">@dimen/match_parent</dimen>
@@ -1028,6 +1036,7 @@
<dimen name="controls_header_bottom_margin">24dp</dimen>
<dimen name="controls_header_app_icon_size">24dp</dimen>
<dimen name="controls_top_margin">48dp</dimen>
+ <dimen name="controls_padding_horizontal">0dp</dimen>
<dimen name="control_header_text_size">20sp</dimen>
<dimen name="control_item_text_size">16sp</dimen>
<dimen name="control_menu_item_text_size">16sp</dimen>
@@ -1336,4 +1345,47 @@
<!-- Height of the area at the top of the dream overlay to allow dragging down the notifications
shade. -->
<dimen name="dream_overlay_notifications_drag_area_height">100dp</dimen>
+
+ <!-- Dream overlay complications related dimensions -->
+ <dimen name="dream_overlay_complication_clock_time_padding_left">50dp</dimen>
+ <dimen name="dream_overlay_complication_clock_time_text_size">72sp</dimen>
+ <dimen name="dream_overlay_complication_clock_date_padding_left">60dp</dimen>
+ <dimen name="dream_overlay_complication_clock_date_padding_bottom">50dp</dimen>
+ <dimen name="dream_overlay_complication_clock_date_text_size">18sp</dimen>
+ <dimen name="dream_overlay_complication_weather_padding_left">20dp</dimen>
+ <dimen name="dream_overlay_complication_weather_padding_bottom">50dp</dimen>
+ <dimen name="dream_overlay_complication_weather_text_size">18sp</dimen>
+
+ <!-- The position of the end guide, which dream overlay complications can align their start with
+ if their end is aligned with the parent end. Represented as the percentage over from the
+ start of the parent container. -->
+ <item name="dream_overlay_complication_guide_end_percent" format="float" type="dimen">
+ 0.75
+ </item>
+
+ <!-- The position of the start guide, which dream overlay complications can align their end to
+ if their start is aligned with the parent start. Represented as the percentage over from
+ the start of the parent container. -->
+ <item name="dream_overlay_complication_guide_start_percent" format="float" type="dimen">
+ 0.25
+ </item>
+
+ <!-- The position of the bottom guide, which dream overlay complications can align their top to
+ if their bottom is aligned with the parent bottom. Represented as the percentage over from
+ the top of the parent container. -->
+ <item name="dream_overlay_complication_guide_bottom_percent" format="float" type="dimen">
+ 0.90
+ </item>
+
+ <!-- The position of the top guide, which dream overlay complications can align their bottom to
+ if their top is aligned with the parent top. Represented as the percentage over from
+ the top of the parent container. -->
+ <item name="dream_overlay_complication_guide_top_percent" format="float" type="dimen">
+ 0.10
+ </item>
+
+ <!-- The percentage of the screen from which a swipe can start to reveal the bouncer. -->
+ <item name="dream_overlay_bouncer_start_region_screen_percentage" format="float" type="dimen">
+ .2
+ </item>
</resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 75ae52c6e6f4..4dca0b0076d9 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -123,6 +123,18 @@
<!-- Message of notification shown when trying to enable USB debugging but a secondary user is the current foreground user. -->
<string name="usb_debugging_secondary_user_message">The user currently signed in to this device can\'t turn on USB debugging. To use this feature, switch to the primary user.</string>
+ <!-- Title of confirmation dialog for wireless debugging [CHAR LIMIT=80] -->
+ <string name="hdmi_cec_set_menu_language_title">Do you want to change the system language to <xliff:g id="language" example="German">%1$s</xliff:g>?</string>
+
+ <!-- Description for the <Set Menu Language> confirmation dialog [CHAR LIMIT=NONE] -->
+ <string name="hdmi_cec_set_menu_language_description">System language change requested by another device</string>
+
+ <!-- Button label for accepting language change [CHAR LIMIT=25] -->
+ <string name="hdmi_cec_set_menu_language_accept">Change language</string>
+
+ <!-- Button label for declining language change [CHAR LIMIT=25] -->
+ <string name="hdmi_cec_set_menu_language_decline">Keep current language</string>
+
<!-- Title of confirmation dialog for wireless debugging [CHAR LIMIT=NONE] -->
<string name="wifi_debugging_title">Allow wireless debugging on this network?</string>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index ac9873938a29..57f1f3f1606c 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -404,19 +404,19 @@
</style>
<style name="DualToneLightTheme">
- <item name="backgroundColor">@color/light_mode_icon_color_dual_tone_background</item>
+ <item name="iconBackgroundColor">@color/light_mode_icon_color_dual_tone_background</item>
<item name="fillColor">@color/light_mode_icon_color_dual_tone_fill</item>
<item name="singleToneColor">@color/light_mode_icon_color_single_tone</item>
<item name="homeHandleColor">@color/navigation_bar_home_handle_light_color</item>
</style>
<style name="DualToneDarkTheme">
- <item name="backgroundColor">@color/dark_mode_icon_color_dual_tone_background</item>
+ <item name="iconBackgroundColor">@color/dark_mode_icon_color_dual_tone_background</item>
<item name="fillColor">@color/dark_mode_icon_color_dual_tone_fill</item>
<item name="singleToneColor">@color/dark_mode_icon_color_single_tone</item>
<item name="homeHandleColor">@color/navigation_bar_home_handle_dark_color</item>
</style>
<style name="QSHeaderDarkTheme">
- <item name="backgroundColor">@color/dark_mode_qs_icon_color_dual_tone_background</item>
+ <item name="iconBackgroundColor">@color/dark_mode_qs_icon_color_dual_tone_background</item>
<item name="fillColor">@color/dark_mode_qs_icon_color_dual_tone_fill</item>
<item name="singleToneColor">@color/dark_mode_qs_icon_color_single_tone</item>
</style>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimator.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimator.kt
new file mode 100644
index 000000000000..ffab3cd79d7f
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimator.kt
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.shared.animation
+
+import android.view.View
+import android.view.ViewGroup
+import com.android.systemui.shared.animation.UnfoldConstantTranslateAnimator.ViewIdToTranslate
+import com.android.systemui.unfold.UnfoldTransitionProgressProvider
+import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
+import java.lang.ref.WeakReference
+
+/**
+ * Translates items away/towards the hinge when the device is opened/closed, according to the
+ * direction specified in [ViewIdToTranslate.direction], for a maximum of [translationMax] when
+ * progresses are 0.
+ */
+class UnfoldConstantTranslateAnimator(
+ private val viewsIdToTranslate: Set<ViewIdToTranslate>,
+ private val progressProvider: UnfoldTransitionProgressProvider
+) : TransitionProgressListener {
+
+ private var viewsToTranslate = listOf<ViewToTranslate>()
+ private lateinit var rootView: ViewGroup
+ private var translationMax = 0f
+
+ fun init(rootView: ViewGroup, translationMax: Float) {
+ this.rootView = rootView
+ this.translationMax = translationMax
+ progressProvider.addCallback(this)
+ }
+
+ override fun onTransitionStarted() {
+ registerViewsForAnimation(rootView, viewsIdToTranslate)
+ }
+
+ override fun onTransitionProgress(progress: Float) {
+ translateViews(progress)
+ }
+
+ override fun onTransitionFinished() {
+ translateViews(progress = 1f)
+ }
+
+ private fun translateViews(progress: Float) {
+ // progress == 0 -> -translationMax
+ // progress == 1 -> 0
+ val xTrans = (progress - 1f) * translationMax
+ viewsToTranslate.forEach { (view, direction, shouldBeAnimated) ->
+ if (shouldBeAnimated()) {
+ view.get()?.translationX = xTrans * direction.multiplier
+ }
+ }
+ }
+
+ /** Finds in [parent] all views specified by [ids] and register them for the animation. */
+ private fun registerViewsForAnimation(parent: ViewGroup, ids: Set<ViewIdToTranslate>) {
+ viewsToTranslate =
+ ids.mapNotNull { (id, dir, pred) ->
+ parent.findViewById<View>(id)?.let { view ->
+ ViewToTranslate(WeakReference(view), dir, pred)
+ }
+ }
+ }
+
+ /** Represents a view to animate. [rootView] should contain a view with [viewId] inside. */
+ data class ViewIdToTranslate(
+ val viewId: Int,
+ val direction: Direction,
+ val shouldBeAnimated: () -> Boolean = { true }
+ )
+
+ private data class ViewToTranslate(
+ val view: WeakReference<View>,
+ val direction: Direction,
+ val shouldBeAnimated: () -> Boolean
+ )
+
+ /** Direction of the animation. */
+ enum class Direction(val multiplier: Float) {
+ LEFT(-1f),
+ RIGHT(1f),
+ }
+}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/communal/ICommunalHost.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/communal/ICommunalHost.aidl
deleted file mode 100644
index b76be4fc719a..000000000000
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/communal/ICommunalHost.aidl
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.shared.communal;
-
-import com.android.systemui.shared.communal.ICommunalSource;
-
-/**
-* An interface, implemented by SystemUI, for hosting a shared, communal surface on the lock
-* screen. Clients declare themselves sources (as defined by ICommunalSource). ICommunalHost is
-* meant only for the input of said sources. The lifetime scope and interactions that follow after
-* are bound to source.
-*/
-oneway interface ICommunalHost {
- /**
- * Invoked to specify the CommunalSource that should be consulted for communal surfaces to be
- * displayed.
- */
- void setSource(in ICommunalSource source) = 1;
-} \ No newline at end of file
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/communal/ICommunalSource.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/communal/ICommunalSource.aidl
deleted file mode 100644
index 7ef403b414be..000000000000
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/communal/ICommunalSource.aidl
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.shared.communal;
-
-import com.android.systemui.shared.communal.ICommunalSurfaceCallback;
-
-/**
- * An interface, implemented by clients of CommunalHost, to provide communal surfaces for SystemUI.
- * The associated binder proxy will be retained by SystemUI and called on-demand when a communal
- * surface is needed (either new instantiation or update).
- */
-oneway interface ICommunalSource {
- /**
- * Called by the CommunalHost when a new communal surface is needed. The provided arguments
- * match the arguments necessary to construct a SurfaceControlViewHost for producing a
- * SurfacePackage to return.
- */
- void getCommunalSurface(in IBinder hostToken, in int width, in int height, in int displayId,
- in ICommunalSurfaceCallback callback) = 1;
-} \ No newline at end of file
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
index 5d092d02a835..eebc7918c72c 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
@@ -120,6 +120,8 @@ public class QuickStepContract {
public static final int SYSUI_STATE_BACK_DISABLED = 1 << 22;
// The bubble stack is expanded AND the mange menu for bubbles is expanded on top of it.
public static final int SYSUI_STATE_BUBBLES_MANAGE_MENU_EXPANDED = 1 << 23;
+ // The current app is in immersive mode
+ public static final int SYSUI_STATE_IMMERSIVE_MODE = 1 << 24;
@Retention(RetentionPolicy.SOURCE)
@IntDef({SYSUI_STATE_SCREEN_PINNING,
@@ -145,7 +147,8 @@ public class QuickStepContract {
SYSUI_STATE_IME_SWITCHER_SHOWING,
SYSUI_STATE_DEVICE_DOZING,
SYSUI_STATE_BACK_DISABLED,
- SYSUI_STATE_BUBBLES_MANAGE_MENU_EXPANDED
+ SYSUI_STATE_BUBBLES_MANAGE_MENU_EXPANDED,
+ SYSUI_STATE_IMMERSIVE_MODE
})
public @interface SystemUiStateFlags {}
@@ -179,6 +182,7 @@ public class QuickStepContract {
str.add((flags & SYSUI_STATE_BACK_DISABLED) != 0 ? "back_disabled" : "");
str.add((flags & SYSUI_STATE_BUBBLES_MANAGE_MENU_EXPANDED) != 0
? "bubbles_mange_menu_expanded" : "");
+ str.add((flags & SYSUI_STATE_IMMERSIVE_MODE) != 0 ? "immersive_mode" : "");
return str.toString();
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardBiometricLockoutLogger.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardBiometricLockoutLogger.kt
index 214b284ac4b9..43cd764f0110 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardBiometricLockoutLogger.kt
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardBiometricLockoutLogger.kt
@@ -16,6 +16,7 @@
package com.android.keyguard
+import android.app.StatusBarManager.SESSION_KEYGUARD
import android.content.Context
import android.hardware.biometrics.BiometricSourceType
import com.android.internal.annotations.VisibleForTesting
@@ -28,7 +29,7 @@ import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUT
import com.android.keyguard.KeyguardBiometricLockoutLogger.PrimaryAuthRequiredEvent
import com.android.systemui.CoreStartable
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dump.DumpManager
+import com.android.systemui.log.SessionTracker
import java.io.FileDescriptor
import java.io.PrintWriter
import javax.inject.Inject
@@ -44,7 +45,7 @@ class KeyguardBiometricLockoutLogger @Inject constructor(
context: Context?,
private val uiEventLogger: UiEventLogger,
private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
- private val dumpManager: DumpManager
+ private val sessionTracker: SessionTracker
) : CoreStartable(context) {
private var fingerprintLockedOut = false
private var faceLockedOut = false
@@ -53,7 +54,6 @@ class KeyguardBiometricLockoutLogger @Inject constructor(
private var timeout = false
override fun start() {
- dumpManager.registerDumpable(this)
mKeyguardUpdateMonitorCallback.onStrongAuthStateChanged(
KeyguardUpdateMonitor.getCurrentUser())
keyguardUpdateMonitor.registerCallback(mKeyguardUpdateMonitorCallback)
@@ -65,22 +65,17 @@ class KeyguardBiometricLockoutLogger @Inject constructor(
if (biometricSourceType == BiometricSourceType.FINGERPRINT) {
val lockedOut = keyguardUpdateMonitor.isFingerprintLockedOut
if (lockedOut && !fingerprintLockedOut) {
- uiEventLogger.log(
- PrimaryAuthRequiredEvent.PRIMARY_AUTH_REQUIRED_FINGERPRINT_LOCKED_OUT)
+ log(PrimaryAuthRequiredEvent.PRIMARY_AUTH_REQUIRED_FINGERPRINT_LOCKED_OUT)
} else if (!lockedOut && fingerprintLockedOut) {
- uiEventLogger.log(
- PrimaryAuthRequiredEvent
- .PRIMARY_AUTH_REQUIRED_FINGERPRINT_LOCKED_OUT_RESET)
+ log(PrimaryAuthRequiredEvent.PRIMARY_AUTH_REQUIRED_FINGERPRINT_LOCKED_OUT_RESET)
}
fingerprintLockedOut = lockedOut
} else if (biometricSourceType == BiometricSourceType.FACE) {
val lockedOut = keyguardUpdateMonitor.isFaceLockedOut
if (lockedOut && !faceLockedOut) {
- uiEventLogger.log(
- PrimaryAuthRequiredEvent.PRIMARY_AUTH_REQUIRED_FACE_LOCKED_OUT)
+ log(PrimaryAuthRequiredEvent.PRIMARY_AUTH_REQUIRED_FACE_LOCKED_OUT)
} else if (!lockedOut && faceLockedOut) {
- uiEventLogger.log(
- PrimaryAuthRequiredEvent.PRIMARY_AUTH_REQUIRED_FACE_LOCKED_OUT_RESET)
+ log(PrimaryAuthRequiredEvent.PRIMARY_AUTH_REQUIRED_FACE_LOCKED_OUT_RESET)
}
faceLockedOut = lockedOut
}
@@ -95,20 +90,19 @@ class KeyguardBiometricLockoutLogger @Inject constructor(
val newEncryptedOrLockdown = keyguardUpdateMonitor.isEncryptedOrLockdown(userId)
if (newEncryptedOrLockdown && !encryptedOrLockdown) {
- uiEventLogger.log(
- PrimaryAuthRequiredEvent.PRIMARY_AUTH_REQUIRED_ENCRYPTED_OR_LOCKDOWN)
+ log(PrimaryAuthRequiredEvent.PRIMARY_AUTH_REQUIRED_ENCRYPTED_OR_LOCKDOWN)
}
encryptedOrLockdown = newEncryptedOrLockdown
val newUnattendedUpdate = isUnattendedUpdate(strongAuthFlags)
if (newUnattendedUpdate && !unattendedUpdate) {
- uiEventLogger.log(PrimaryAuthRequiredEvent.PRIMARY_AUTH_REQUIRED_UNATTENDED_UPDATE)
+ log(PrimaryAuthRequiredEvent.PRIMARY_AUTH_REQUIRED_UNATTENDED_UPDATE)
}
unattendedUpdate = newUnattendedUpdate
val newTimeout = isStrongAuthTimeout(strongAuthFlags)
if (newTimeout && !timeout) {
- uiEventLogger.log(PrimaryAuthRequiredEvent.PRIMARY_AUTH_REQUIRED_TIMEOUT)
+ log(PrimaryAuthRequiredEvent.PRIMARY_AUTH_REQUIRED_TIMEOUT)
}
timeout = newTimeout
}
@@ -123,6 +117,9 @@ class KeyguardBiometricLockoutLogger @Inject constructor(
) = containsFlag(flags, STRONG_AUTH_REQUIRED_AFTER_TIMEOUT) ||
containsFlag(flags, STRONG_AUTH_REQUIRED_AFTER_NON_STRONG_BIOMETRICS_TIMEOUT)
+ private fun log(event: PrimaryAuthRequiredEvent) =
+ uiEventLogger.log(event, sessionTracker.getSessionId(SESSION_KEYGUARD))
+
override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<String>) {
pw.println(" mFingerprintLockedOut=$fingerprintLockedOut")
pw.println(" mFaceLockedOut=$faceLockedOut")
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index 49a802235619..57997d8efd6f 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -16,6 +16,8 @@
package com.android.keyguard;
+import static android.app.StatusBarManager.SESSION_KEYGUARD;
+
import static com.android.keyguard.KeyguardSecurityContainer.BOUNCER_DISMISS_BIOMETRIC;
import static com.android.keyguard.KeyguardSecurityContainer.BOUNCER_DISMISS_EXTENDED_ACCESS;
import static com.android.keyguard.KeyguardSecurityContainer.BOUNCER_DISMISS_NONE_SECURITY;
@@ -36,7 +38,10 @@ import android.util.Log;
import android.util.Slog;
import android.view.MotionEvent;
+import androidx.annotation.Nullable;
+
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.logging.InstanceId;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.nano.MetricsProto;
@@ -53,6 +58,7 @@ import com.android.systemui.R;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
+import com.android.systemui.log.SessionTracker;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.shared.system.SysUiStatsLog;
import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -86,6 +92,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
private final UserSwitcherController mUserSwitcherController;
private final GlobalSettings mGlobalSettings;
private final FeatureFlags mFeatureFlags;
+ private final SessionTracker mSessionTracker;
private int mLastOrientation = Configuration.ORIENTATION_UNDEFINED;
@@ -191,7 +198,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
mMetricsLogger.write(new LogMaker(MetricsEvent.BOUNCER)
.setType(success ? MetricsEvent.TYPE_SUCCESS : MetricsEvent.TYPE_FAILURE));
mUiEventLogger.log(success ? BouncerUiEvent.BOUNCER_PASSWORD_SUCCESS
- : BouncerUiEvent.BOUNCER_PASSWORD_FAILURE);
+ : BouncerUiEvent.BOUNCER_PASSWORD_FAILURE, getSessionId());
}
public void reset() {
@@ -242,7 +249,8 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
FalsingManager falsingManager,
UserSwitcherController userSwitcherController,
FeatureFlags featureFlags,
- GlobalSettings globalSettings) {
+ GlobalSettings globalSettings,
+ SessionTracker sessionTracker) {
super(view);
mLockPatternUtils = lockPatternUtils;
mUpdateMonitor = keyguardUpdateMonitor;
@@ -261,6 +269,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
mUserSwitcherController = userSwitcherController;
mFeatureFlags = featureFlags;
mGlobalSettings = globalSettings;
+ mSessionTracker = sessionTracker;
}
@Override
@@ -456,7 +465,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
.setType(MetricsProto.MetricsEvent.TYPE_DISMISS).setSubtype(eventSubtype));
}
if (uiEvent != BouncerUiEvent.UNKNOWN) {
- mUiEventLogger.log(uiEvent);
+ mUiEventLogger.log(uiEvent, getSessionId());
}
if (finish) {
mSecurityCallback.finish(strongAuth, targetUserId);
@@ -599,6 +608,10 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
}
}
+ private @Nullable InstanceId getSessionId() {
+ return mSessionTracker.getSessionId(SESSION_KEYGUARD);
+ }
+
/** Update keyguard position based on a tapped X coordinate. */
public void updateKeyguardPosition(float x) {
mView.updatePositionByTouchX(x);
@@ -622,6 +635,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
private final GlobalSettings mGlobalSettings;
private final FeatureFlags mFeatureFlags;
private final UserSwitcherController mUserSwitcherController;
+ private final SessionTracker mSessionTracker;
@Inject
Factory(KeyguardSecurityContainer view,
@@ -639,7 +653,8 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
FalsingManager falsingManager,
UserSwitcherController userSwitcherController,
FeatureFlags featureFlags,
- GlobalSettings globalSettings) {
+ GlobalSettings globalSettings,
+ SessionTracker sessionTracker) {
mView = view;
mAdminSecondaryLockScreenControllerFactory = adminSecondaryLockScreenControllerFactory;
mLockPatternUtils = lockPatternUtils;
@@ -655,6 +670,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
mFeatureFlags = featureFlags;
mGlobalSettings = globalSettings;
mUserSwitcherController = userSwitcherController;
+ mSessionTracker = sessionTracker;
}
public KeyguardSecurityContainerController create(
@@ -664,7 +680,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
mKeyguardUpdateMonitor, mKeyguardSecurityModel, mMetricsLogger, mUiEventLogger,
mKeyguardStateController, securityCallback, mSecurityViewFlipperController,
mConfigurationController, mFalsingCollector, mFalsingManager,
- mUserSwitcherController, mFeatureFlags, mGlobalSettings);
+ mUserSwitcherController, mFeatureFlags, mGlobalSettings, mSessionTracker);
}
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt
index cb25e1a2a40e..89d6fb5f062f 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt
@@ -17,11 +17,13 @@
package com.android.keyguard
import android.content.Context
-import android.view.View
import android.view.ViewGroup
import com.android.systemui.R
+import com.android.systemui.shared.animation.UnfoldConstantTranslateAnimator
+import com.android.systemui.shared.animation.UnfoldConstantTranslateAnimator.Direction.LEFT
+import com.android.systemui.shared.animation.UnfoldConstantTranslateAnimator.Direction.RIGHT
+import com.android.systemui.shared.animation.UnfoldConstantTranslateAnimator.ViewIdToTranslate
import com.android.systemui.unfold.SysUIUnfoldScope
-import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
import com.android.systemui.unfold.util.NaturalRotationUnfoldProgressProvider
import javax.inject.Inject
@@ -30,84 +32,37 @@ import javax.inject.Inject
* the set of ids, which also dictact which direction to move and when, via a filter function.
*/
@SysUIUnfoldScope
-class KeyguardUnfoldTransition @Inject constructor(
- val context: Context,
- val unfoldProgressProvider: NaturalRotationUnfoldProgressProvider
+class KeyguardUnfoldTransition
+@Inject
+constructor(
+ private val context: Context,
+ unfoldProgressProvider: NaturalRotationUnfoldProgressProvider
) {
- companion object {
- final val LEFT = -1
- final val RIGHT = 1
- }
+ /** Certain views only need to move if they are not currently centered */
+ var statusViewCentered = false
private val filterSplitShadeOnly = { !statusViewCentered }
private val filterNever = { true }
- private val ids = setOf(
- Triple(R.id.keyguard_status_area, LEFT, filterNever),
- Triple(R.id.controls_button, LEFT, filterNever),
- Triple(R.id.lockscreen_clock_view_large, LEFT, filterSplitShadeOnly),
- Triple(R.id.lockscreen_clock_view, LEFT, filterNever),
- Triple(R.id.notification_stack_scroller, RIGHT, filterSplitShadeOnly),
- Triple(R.id.wallet_button, RIGHT, filterNever)
- )
- private var parent: ViewGroup? = null
- private var views = listOf<Triple<View, Int, () -> Boolean>>()
- private var xTranslationMax = 0f
-
- /**
- * Certain views only need to move if they are not currently centered
- */
- var statusViewCentered = false
-
- init {
- unfoldProgressProvider.addCallback(
- object : TransitionProgressListener {
- override fun onTransitionStarted() {
- findViews()
- }
-
- override fun onTransitionProgress(progress: Float) {
- translateViews(progress)
- }
-
- override fun onTransitionFinished() {
- translateViews(1f)
- }
- }
- )
+ private val translateAnimator by lazy {
+ UnfoldConstantTranslateAnimator(
+ viewsIdToTranslate =
+ setOf(
+ ViewIdToTranslate(R.id.keyguard_status_area, LEFT, filterNever),
+ ViewIdToTranslate(R.id.controls_button, LEFT, filterNever),
+ ViewIdToTranslate(R.id.lockscreen_clock_view_large, LEFT, filterSplitShadeOnly),
+ ViewIdToTranslate(R.id.lockscreen_clock_view, LEFT, filterNever),
+ ViewIdToTranslate(
+ R.id.notification_stack_scroller, RIGHT, filterSplitShadeOnly),
+ ViewIdToTranslate(R.id.wallet_button, RIGHT, filterNever)),
+ progressProvider = unfoldProgressProvider)
}
- /**
- * Relies on the [parent] to locate views to translate
- */
+ /** Relies on the [parent] to locate views to translate. */
fun setup(parent: ViewGroup) {
- this.parent = parent
- xTranslationMax = context.resources.getDimensionPixelSize(
- R.dimen.keyguard_unfold_translation_x).toFloat()
- }
-
- /**
- * Manually translate views based on set direction. At the moment
- * [UnfoldMoveFromCenterAnimator] exists but moves all views a dynamic distance
- * from their mid-point. This code instead will only ever translate by a fixed amount.
- */
- private fun translateViews(progress: Float) {
- val xTrans = progress * xTranslationMax - xTranslationMax
- views.forEach {
- (view, direction, pred) -> if (pred()) {
- view.setTranslationX(xTrans * direction)
- }
- }
- }
-
- private fun findViews() {
- parent?.let { p ->
- views = ids.mapNotNull {
- (id, direction, pred) -> p.findViewById<View>(id)?.let {
- Triple(it, direction, pred)
- }
- }
- }
+ val translationMax =
+ context.resources.getDimensionPixelSize(R.dimen.keyguard_unfold_translation_x).toFloat()
+ translateAnimator.init(parent, translationMax)
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index f2d0427c39d3..cc10b02a8bb6 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -1196,6 +1196,21 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
return fingerprintAllowed || faceAllowed;
}
+ /**
+ * Returns whether the user is unlocked with a biometric that is currently bypassing
+ * the lock screen.
+ */
+ public boolean getUserUnlockedWithBiometricAndIsBypassing(int userId) {
+ BiometricAuthenticated fingerprint = mUserFingerprintAuthenticated.get(userId);
+ BiometricAuthenticated face = mUserFaceAuthenticated.get(userId);
+ // fingerprint always bypasses
+ boolean fingerprintAllowed = fingerprint != null && fingerprint.mAuthenticated
+ && isUnlockingWithBiometricAllowed(fingerprint.mIsStrongBiometric);
+ boolean faceAllowed = face != null && face.mAuthenticated
+ && isUnlockingWithBiometricAllowed(face.mIsStrongBiometric);
+ return fingerprintAllowed || faceAllowed && mKeyguardBypassController.canBypass();
+ }
+
public boolean getUserTrustIsManaged(int userId) {
return mUserTrustIsManaged.get(userId) && !isTrustDisabled(userId);
}
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
index 6626f59aae8c..80a3a0ebb250 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
@@ -35,7 +35,6 @@ import android.hardware.biometrics.SensorLocationInternal;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.os.Process;
import android.os.VibrationAttributes;
-import android.os.Vibrator;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.MathUtils;
@@ -60,6 +59,7 @@ import com.android.systemui.dump.DumpManager;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -103,7 +103,7 @@ public class LockIconViewController extends ViewController<LockIconView> impleme
@NonNull private CharSequence mUnlockedLabel;
@NonNull private CharSequence mLockedLabel;
- @Nullable private final Vibrator mVibrator;
+ @NonNull private final VibratorHelper mVibrator;
@Nullable private final AuthRippleController mAuthRippleController;
// Tracks the velocity of a touch to help filter out the touches that move too fast.
@@ -154,7 +154,7 @@ public class LockIconViewController extends ViewController<LockIconView> impleme
@NonNull AccessibilityManager accessibilityManager,
@NonNull ConfigurationController configurationController,
@NonNull @Main DelayableExecutor executor,
- @Nullable Vibrator vibrator,
+ @NonNull VibratorHelper vibrator,
@Nullable AuthRippleController authRippleController,
@NonNull @Main Resources resources
) {
@@ -560,7 +560,7 @@ public class LockIconViewController extends ViewController<LockIconView> impleme
switch(event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_HOVER_ENTER:
- if (mVibrator != null && !mDownDetected) {
+ if (!mDownDetected) {
mVibrator.vibrate(
Process.myUid(),
getContext().getOpPackageName(),
@@ -647,15 +647,13 @@ public class LockIconViewController extends ViewController<LockIconView> impleme
mOnGestureDetectedRunnable.run();
}
- if (mVibrator != null) {
- // play device entry haptic (same as biometric success haptic)
- mVibrator.vibrate(
- Process.myUid(),
- getContext().getOpPackageName(),
- UdfpsController.EFFECT_CLICK,
- "lock-icon-device-entry",
- TOUCH_VIBRATION_ATTRIBUTES);
- }
+ // play device entry haptic (same as biometric success haptic)
+ mVibrator.vibrate(
+ Process.myUid(),
+ getContext().getOpPackageName(),
+ UdfpsController.EFFECT_CLICK,
+ "lock-icon-device-entry",
+ TOUCH_VIBRATION_ATTRIBUTES);
mKeyguardViewController.showBouncer(/* scrim */ true);
}
@@ -670,12 +668,9 @@ public class LockIconViewController extends ViewController<LockIconView> impleme
mVelocityTracker.recycle();
mVelocityTracker = null;
}
- if (mVibrator != null) {
- mVibrator.cancel();
- }
+ mVibrator.cancel();
}
-
private boolean inLockIconArea(MotionEvent event) {
return mSensorTouchLocation.contains((int) event.getX(), (int) event.getY())
&& mView.getVisibility() == View.VISIBLE;
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index 08ed24caec44..b32c2b639f16 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -49,6 +49,7 @@ import com.android.systemui.dock.DockManager;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.fragments.FragmentService;
+import com.android.systemui.hdmi.HdmiCecSetMenuLanguageHelper;
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.media.dialog.MediaOutputDialogFactory;
@@ -250,6 +251,7 @@ public class Dependency {
@Inject Lazy<LocationController> mLocationController;
@Inject Lazy<RotationLockController> mRotationLockController;
@Inject Lazy<ZenModeController> mZenModeController;
+ @Inject Lazy<HdmiCecSetMenuLanguageHelper> mHdmiCecSetMenuLanguageHelper;
@Inject Lazy<HotspotController> mHotspotController;
@Inject Lazy<CastController> mCastController;
@Inject Lazy<FlashlightController> mFlashlightController;
diff --git a/packages/SystemUI/src/com/android/systemui/DualToneHandler.kt b/packages/SystemUI/src/com/android/systemui/DualToneHandler.kt
index fdc3229ab8d7..2b8d3e046ffa 100644
--- a/packages/SystemUI/src/com/android/systemui/DualToneHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/DualToneHandler.kt
@@ -54,11 +54,11 @@ class DualToneHandler(context: Context) {
Utils.getThemeAttr(context, R.attr.lightIconTheme))
darkColor = Color(
Utils.getColorAttrDefaultColor(dualToneDarkTheme, R.attr.singleToneColor),
- Utils.getColorAttrDefaultColor(dualToneDarkTheme, R.attr.backgroundColor),
+ Utils.getColorAttrDefaultColor(dualToneDarkTheme, R.attr.iconBackgroundColor),
Utils.getColorAttrDefaultColor(dualToneDarkTheme, R.attr.fillColor))
lightColor = Color(
Utils.getColorAttrDefaultColor(dualToneLightTheme, R.attr.singleToneColor),
- Utils.getColorAttrDefaultColor(dualToneLightTheme, R.attr.backgroundColor),
+ Utils.getColorAttrDefaultColor(dualToneLightTheme, R.attr.iconBackgroundColor),
Utils.getColorAttrDefaultColor(dualToneLightTheme, R.attr.fillColor))
}
diff --git a/packages/SystemUI/src/com/android/systemui/ExpandHelper.java b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
index 0b967b7c5e67..f00615bc0fe6 100644
--- a/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
@@ -23,6 +23,7 @@ import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.content.Context;
+import android.util.FloatProperty;
import android.util.Log;
import android.view.Gravity;
import android.view.HapticFeedbackConstants;
@@ -69,6 +70,19 @@ public class ExpandHelper implements Gefingerpoken {
// 2f: maximum brightness is stretching a 1U to 3U, or a 4U to 6U
private static final float STRETCH_INTERVAL = 2f;
+ private static final FloatProperty<ViewScaler> VIEW_SCALER_HEIGHT_PROPERTY =
+ new FloatProperty<ViewScaler>("ViewScalerHeight") {
+ @Override
+ public void setValue(ViewScaler object, float value) {
+ object.setHeight(value);
+ }
+
+ @Override
+ public Float get(ViewScaler object) {
+ return object.getHeight();
+ }
+ };
+
@SuppressWarnings("unused")
private Context mContext;
@@ -147,6 +161,7 @@ public class ExpandHelper implements Gefingerpoken {
public void setView(ExpandableView v) {
mView = v;
}
+
public void setHeight(float h) {
if (DEBUG_SCALE) Log.v(TAG, "SetHeight: setting to " + h);
mView.setActualHeight((int) h);
@@ -176,7 +191,7 @@ public class ExpandHelper implements Gefingerpoken {
mCallback = callback;
mScaler = new ViewScaler();
mGravity = Gravity.TOP;
- mScaleAnimation = ObjectAnimator.ofFloat(mScaler, "height", 0f);
+ mScaleAnimation = ObjectAnimator.ofFloat(mScaler, VIEW_SCALER_HEIGHT_PROPERTY, 0f);
mPullGestureMinXSpan = mContext.getResources().getDimension(R.dimen.pull_span_min);
final ViewConfiguration configuration = ViewConfiguration.get(mContext);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index b0f7e55112af..fe5e36ef23e6 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -37,6 +37,7 @@ import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricManager.Authenticators;
import android.hardware.biometrics.BiometricManager.BiometricMultiSensorMode;
import android.hardware.biometrics.BiometricPrompt;
+import android.hardware.biometrics.IBiometricContextListener;
import android.hardware.biometrics.IBiometricSysuiReceiver;
import android.hardware.biometrics.PromptInfo;
import android.hardware.display.DisplayManager;
@@ -64,6 +65,7 @@ import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.doze.DozeReceiver;
import com.android.systemui.keyguard.WakefulnessLifecycle;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.util.concurrency.Execution;
@@ -96,6 +98,7 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
private final Handler mHandler;
private final Execution mExecution;
private final CommandQueue mCommandQueue;
+ private final StatusBarStateController mStatusBarStateController;
private final ActivityTaskManager mActivityTaskManager;
@Nullable
private final FingerprintManager mFingerprintManager;
@@ -118,6 +121,7 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
@Nullable private UdfpsController mUdfpsController;
@Nullable private IUdfpsHbmListener mUdfpsHbmListener;
@Nullable private SidefpsController mSidefpsController;
+ @Nullable private IBiometricContextListener mBiometricContextListener;
@VisibleForTesting
TaskStackListener mTaskStackListener;
@VisibleForTesting
@@ -130,7 +134,7 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
@Nullable private List<FingerprintSensorPropertiesInternal> mSidefpsProps;
@NonNull private final SparseBooleanArray mUdfpsEnrolledForUser;
- private SensorPrivacyManager mSensorPrivacyManager;
+ @NonNull private final SensorPrivacyManager mSensorPrivacyManager;
private final WakefulnessLifecycle mWakefulnessLifecycle;
private class BiometricTaskStackListener extends TaskStackListener {
@@ -491,6 +495,7 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
Provider<SidefpsController> sidefpsControllerFactory,
@NonNull DisplayManager displayManager,
WakefulnessLifecycle wakefulnessLifecycle,
+ @NonNull StatusBarStateController statusBarStateController,
@Main Handler handler) {
super(context);
mExecution = execution;
@@ -504,6 +509,7 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
mSidefpsControllerFactory = sidefpsControllerFactory;
mWindowManager = windowManager;
mUdfpsEnrolledForUser = new SparseBooleanArray();
+
mOrientationListener = new BiometricDisplayListener(
context,
displayManager,
@@ -514,6 +520,14 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
return Unit.INSTANCE;
});
+ mStatusBarStateController = statusBarStateController;
+ mStatusBarStateController.addCallback(new StatusBarStateController.StateListener() {
+ @Override
+ public void onDozingChanged(boolean isDozing) {
+ notifyDozeChanged(isDozing);
+ }
+ });
+
mFaceProps = mFaceManager != null ? mFaceManager.getSensorPropertiesInternal() : null;
int[] faceAuthLocation = context.getResources().getIntArray(
@@ -564,6 +578,22 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
mActivityTaskManager.registerTaskStackListener(mTaskStackListener);
}
+ @Override
+ public void setBiometicContextListener(IBiometricContextListener listener) {
+ mBiometricContextListener = listener;
+ notifyDozeChanged(mStatusBarStateController.isDozing());
+ }
+
+ private void notifyDozeChanged(boolean isDozing) {
+ if (mBiometricContextListener != null) {
+ try {
+ mBiometricContextListener.onDozeChanged(isDozing);
+ } catch (RemoteException e) {
+ Log.w(TAG, "failed to notify initial doze state");
+ }
+ }
+ }
+
/**
* Stores the listener received from {@link com.android.server.display.DisplayModeDirector}.
*
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index 5ddfd7554ac9..8052c2071d86 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -42,7 +42,6 @@ import android.os.Process;
import android.os.Trace;
import android.os.VibrationAttributes;
import android.os.VibrationEffect;
-import android.os.Vibrator;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MotionEvent;
@@ -64,6 +63,7 @@ import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.LockscreenShadeTransitionController;
+import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.phone.SystemUIDialogManager;
@@ -117,7 +117,7 @@ public class UdfpsController implements DozeReceiver {
@NonNull private final DumpManager mDumpManager;
@NonNull private final SystemUIDialogManager mDialogManager;
@NonNull private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
- @Nullable private final Vibrator mVibrator;
+ @NonNull private final VibratorHelper mVibrator;
@NonNull private final FalsingManager mFalsingManager;
@NonNull private final PowerManager mPowerManager;
@NonNull private final AccessibilityManager mAccessibilityManager;
@@ -506,7 +506,7 @@ public class UdfpsController implements DozeReceiver {
@NonNull AccessibilityManager accessibilityManager,
@NonNull LockscreenShadeTransitionController lockscreenShadeTransitionController,
@NonNull ScreenLifecycle screenLifecycle,
- @Nullable Vibrator vibrator,
+ @NonNull VibratorHelper vibrator,
@NonNull UdfpsHapticsSimulator udfpsHapticsSimulator,
@NonNull Optional<UdfpsHbmProvider> hbmProvider,
@NonNull KeyguardStateController keyguardStateController,
@@ -577,14 +577,12 @@ public class UdfpsController implements DozeReceiver {
*/
@VisibleForTesting
public void playStartHaptic() {
- if (mVibrator != null) {
- mVibrator.vibrate(
- Process.myUid(),
- mContext.getOpPackageName(),
- EFFECT_CLICK,
- "udfps-onStart-click",
- VIBRATION_ATTRIBUTES);
- }
+ mVibrator.vibrate(
+ Process.myUid(),
+ mContext.getOpPackageName(),
+ EFFECT_CLICK,
+ "udfps-onStart-click",
+ VIBRATION_ATTRIBUTES);
}
@Nullable
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsHapticsSimulator.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsHapticsSimulator.kt
index e23131069eab..eaee19aa5dca 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsHapticsSimulator.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsHapticsSimulator.kt
@@ -18,16 +18,12 @@ package com.android.systemui.biometrics
import android.media.AudioAttributes
import android.os.VibrationEffect
-import android.os.Vibrator
-
import com.android.keyguard.KeyguardUpdateMonitor
-
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.VibratorHelper
import com.android.systemui.statusbar.commandline.Command
import com.android.systemui.statusbar.commandline.CommandRegistry
-
import java.io.PrintWriter
-
import javax.inject.Inject
/**
@@ -36,7 +32,7 @@ import javax.inject.Inject
@SysUISingleton
class UdfpsHapticsSimulator @Inject constructor(
commandRegistry: CommandRegistry,
- val vibrator: Vibrator?,
+ val vibrator: VibratorHelper,
val keyguardUpdateMonitor: KeyguardUpdateMonitor
) : Command {
val sonificationEffects =
@@ -60,13 +56,13 @@ class UdfpsHapticsSimulator @Inject constructor(
}
"success" -> {
// needs to be kept up to date with AcquisitionClient#SUCCESS_VIBRATION_EFFECT
- vibrator?.vibrate(
+ vibrator.vibrate(
VibrationEffect.get(VibrationEffect.EFFECT_CLICK),
sonificationEffects)
}
"error" -> {
// needs to be kept up to date with AcquisitionClient#ERROR_VIBRATION_EFFECT
- vibrator?.vibrate(
+ vibrator.vibrate(
VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK),
sonificationEffects)
}
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardListener.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardListener.java
index 0e1cd51ce42c..72b40d42b7b8 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardListener.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardListener.java
@@ -63,7 +63,8 @@ public class ClipboardListener extends CoreStartable
mClipboardOverlayController =
new ClipboardOverlayController(mContext, new TimeoutHandler(mContext));
}
- mClipboardOverlayController.setClipData(mClipboardManager.getPrimaryClip());
+ mClipboardOverlayController.setClipData(
+ mClipboardManager.getPrimaryClip(), mClipboardManager.getPrimaryClipSource());
mClipboardOverlayController.setOnSessionCompleteListener(() -> {
// Session is complete, free memory until it's needed again.
mClipboardOverlayController = null;
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
index b6bcb871ff18..12759f489a26 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
@@ -182,6 +182,7 @@ public class ClipboardOverlayController {
withWindowAttached(() -> {
mWindow.setContentView(mView);
updateInsets(mWindowManager.getCurrentWindowMetrics().getWindowInsets());
+ mView.requestLayout();
mView.post(this::animateIn);
});
@@ -213,7 +214,7 @@ public class ClipboardOverlayController {
mContext.sendBroadcast(new Intent(COPY_OVERLAY_ACTION), SELF_PERMISSION);
}
- void setClipData(ClipData clipData) {
+ void setClipData(ClipData clipData, String clipSource) {
reset();
if (clipData == null || clipData.getItemCount() == 0) {
showTextPreview(mContext.getResources().getString(
@@ -221,7 +222,7 @@ public class ClipboardOverlayController {
} else if (!TextUtils.isEmpty(clipData.getItemAt(0).getText())) {
ClipData.Item item = clipData.getItemAt(0);
if (item.getTextLinks() != null) {
- AsyncTask.execute(() -> classifyText(clipData.getItemAt(0)));
+ AsyncTask.execute(() -> classifyText(clipData.getItemAt(0), clipSource));
}
showEditableText(item.getText());
} else if (clipData.getItemAt(0).getUri() != null) {
@@ -238,7 +239,7 @@ public class ClipboardOverlayController {
mOnSessionCompleteListener = runnable;
}
- private void classifyText(ClipData.Item item) {
+ private void classifyText(ClipData.Item item, String source) {
ArrayList<RemoteAction> actions = new ArrayList<>();
for (TextLinks.TextLink link : item.getTextLinks().getLinks()) {
TextClassification classification = mTextClassifier.classifyText(
@@ -246,14 +247,14 @@ public class ClipboardOverlayController {
actions.addAll(classification.getActions());
}
mView.post(() -> {
- for (ScreenshotActionChip chip : mActionChips) {
- mActionContainer.removeView(chip);
- }
- mActionChips.clear();
+ resetActionChips();
for (RemoteAction action : actions) {
- ScreenshotActionChip chip = constructActionChip(action);
- mActionContainer.addView(chip);
- mActionChips.add(chip);
+ Intent targetIntent = action.getActionIntent().getIntent();
+ if (!TextUtils.equals(source, targetIntent.getComponent().getPackageName())) {
+ ScreenshotActionChip chip = constructActionChip(action);
+ mActionContainer.addView(chip);
+ mActionChips.add(chip);
+ }
}
});
}
@@ -451,13 +452,17 @@ public class ClipboardOverlayController {
}
}
- private void reset() {
- mView.setTranslationX(0);
- mView.setAlpha(0);
+ private void resetActionChips() {
for (ScreenshotActionChip chip : mActionChips) {
mActionContainer.removeView(chip);
}
mActionChips.clear();
+ }
+
+ private void reset() {
+ mView.setTranslationX(0);
+ mView.setAlpha(0);
+ resetActionChips();
mTimeoutHandler.cancelTimeout();
}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt
index a29f3e91f227..f87fa96dea65 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt
@@ -24,7 +24,6 @@ import android.content.Intent
import android.content.pm.PackageManager
import android.content.pm.ResolveInfo
import android.os.VibrationEffect
-import android.os.Vibrator
import android.service.controls.Control
import android.service.controls.actions.BooleanAction
import android.service.controls.actions.CommandAction
@@ -32,16 +31,14 @@ import android.service.controls.actions.FloatAction
import android.util.Log
import android.view.HapticFeedbackConstants
import com.android.internal.annotations.VisibleForTesting
-import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.controls.ControlsMetricsLogger
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.globalactions.GlobalActionsComponent
import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.statusbar.VibratorHelper
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.concurrency.DelayableExecutor
import com.android.wm.shell.TaskViewFactory
-import dagger.Lazy
import java.util.Optional
import javax.inject.Inject
@@ -52,14 +49,11 @@ class ControlActionCoordinatorImpl @Inject constructor(
@Main private val uiExecutor: DelayableExecutor,
private val activityStarter: ActivityStarter,
private val keyguardStateController: KeyguardStateController,
- private val globalActionsComponent: GlobalActionsComponent,
private val taskViewFactory: Optional<TaskViewFactory>,
- private val broadcastDispatcher: BroadcastDispatcher,
- private val lazyUiController: Lazy<ControlsUiController>,
- private val controlsMetricsLogger: ControlsMetricsLogger
+ private val controlsMetricsLogger: ControlsMetricsLogger,
+ private val vibrator: VibratorHelper
) : ControlActionCoordinator {
private var dialog: Dialog? = null
- private val vibrator = context.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
private var pendingAction: Action? = null
private var actionsInProgress = mutableSetOf<String>()
private val isLocked: Boolean
@@ -194,7 +188,7 @@ class ControlActionCoordinatorImpl @Inject constructor(
}
private fun vibrate(effect: VibrationEffect) {
- bgExecutor.execute { vibrator.vibrate(effect) }
+ vibrator.vibrate(effect)
}
private fun showDetail(cvh: ControlViewHolder, pendingIntent: PendingIntent) {
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
index bce878434aa1..1653e0adadb5 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
@@ -19,6 +19,7 @@ package com.android.systemui.dagger;
import android.app.Activity;
import com.android.systemui.ForegroundServicesDialog;
+import com.android.systemui.hdmi.HdmiCecSetMenuLanguageActivity;
import com.android.systemui.keyguard.WorkLockActivity;
import com.android.systemui.people.PeopleSpaceActivity;
import com.android.systemui.people.widget.LaunchConversationActivity;
@@ -120,4 +121,11 @@ public abstract class DefaultActivityBinder {
@IntoMap
@ClassKey(TvUnblockSensorActivity.class)
public abstract Activity bindTvUnblockSensorActivity(TvUnblockSensorActivity activity);
+
+ /** Inject into HdmiCecSetMenuLanguageActivity. */
+ @Binds
+ @IntoMap
+ @ClassKey(HdmiCecSetMenuLanguageActivity.class)
+ public abstract Activity bindHdmiCecSetMenuLanguageActivity(
+ HdmiCecSetMenuLanguageActivity activity);
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
index 96e2302f937c..ec2beb15959e 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
@@ -26,10 +26,15 @@ import com.android.systemui.accessibility.WindowMagnification;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.clipboardoverlay.ClipboardListener;
import com.android.systemui.dreams.DreamOverlayRegistrant;
+import com.android.systemui.dreams.SmartSpaceComplication;
+import com.android.systemui.dreams.complication.DreamClockDateComplication;
+import com.android.systemui.dreams.complication.DreamClockTimeComplication;
+import com.android.systemui.dreams.complication.DreamWeatherComplication;
import com.android.systemui.globalactions.GlobalActionsComponent;
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.keyguard.dagger.KeyguardModule;
import com.android.systemui.log.SessionTracker;
+import com.android.systemui.media.dream.MediaDreamSentinel;
import com.android.systemui.media.systemsounds.HomeSoundEffectController;
import com.android.systemui.power.PowerUI;
import com.android.systemui.privacy.television.TvOngoingPrivacyChip;
@@ -218,4 +223,39 @@ public abstract class SystemUIBinder {
@ClassKey(DreamOverlayRegistrant.class)
public abstract CoreStartable bindDreamOverlayRegistrant(
DreamOverlayRegistrant dreamOverlayRegistrant);
+
+ /** Inject into SmartSpaceComplication.Registrant */
+ @Binds
+ @IntoMap
+ @ClassKey(SmartSpaceComplication.Registrant.class)
+ public abstract CoreStartable bindSmartSpaceComplicationRegistrant(
+ SmartSpaceComplication.Registrant registrant);
+
+ /** Inject into MediaDreamSentinel. */
+ @Binds
+ @IntoMap
+ @ClassKey(MediaDreamSentinel.class)
+ public abstract CoreStartable bindMediaDreamSentinel(
+ MediaDreamSentinel sentinel);
+
+ /** Inject into DreamClockTimeComplication.Registrant */
+ @Binds
+ @IntoMap
+ @ClassKey(DreamClockTimeComplication.Registrant.class)
+ public abstract CoreStartable bindDreamClockTimeComplicationRegistrant(
+ DreamClockTimeComplication.Registrant registrant);
+
+ /** Inject into DreamClockDateComplication.Registrant */
+ @Binds
+ @IntoMap
+ @ClassKey(DreamClockDateComplication.Registrant.class)
+ public abstract CoreStartable bindDreamClockDateComplicationRegistrant(
+ DreamClockDateComplication.Registrant registrant);
+
+ /** Inject into DreamWeatherComplication.Registrant */
+ @Binds
+ @IntoMap
+ @ClassKey(DreamWeatherComplication.Registrant.class)
+ public abstract CoreStartable bindDreamWeatherComplicationRegistrant(
+ DreamWeatherComplication.Registrant registrant);
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
index 16ed1fbba75f..2160744c6803 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
@@ -29,9 +29,12 @@ import androidx.lifecycle.LifecycleRegistry;
import androidx.lifecycle.ViewModelStore;
import com.android.internal.policy.PhoneWindow;
+import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dreams.complication.Complication;
import com.android.systemui.dreams.dagger.DreamOverlayComponent;
+import com.android.systemui.dreams.touch.DreamOverlayTouchMonitor;
import java.util.concurrent.Executor;
@@ -53,6 +56,7 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ
// A controller for the dream overlay container view (which contains both the status bar and the
// content area).
private final DreamOverlayContainerViewController mDreamOverlayContainerViewController;
+ private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
// A reference to the {@link Window} used to hold the dream overlay.
private Window mWindow;
@@ -68,19 +72,44 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ
private ViewModelStore mViewModelStore = new ViewModelStore();
+ private DreamOverlayTouchMonitor mDreamOverlayTouchMonitor;
+
+ private final KeyguardUpdateMonitorCallback mKeyguardCallback =
+ new KeyguardUpdateMonitorCallback() {
+ @Override
+ public void onShadeExpandedChanged(boolean expanded) {
+ if (mLifecycleRegistry.getCurrentState() != Lifecycle.State.RESUMED
+ && mLifecycleRegistry.getCurrentState() != Lifecycle.State.STARTED) {
+ return;
+ }
+
+ mLifecycleRegistry.setCurrentState(
+ expanded ? Lifecycle.State.STARTED : Lifecycle.State.RESUMED);
+ }
+ };
+
+ private DreamOverlayStateController mStateController;
+
@Inject
public DreamOverlayService(
Context context,
@Main Executor executor,
- DreamOverlayComponent.Factory dreamOverlayComponentFactory) {
+ DreamOverlayComponent.Factory dreamOverlayComponentFactory,
+ DreamOverlayStateController stateController,
+ KeyguardUpdateMonitor keyguardUpdateMonitor) {
mContext = context;
mExecutor = executor;
+ mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+ mKeyguardUpdateMonitor.registerCallback(mKeyguardCallback);
+ mStateController = stateController;
final DreamOverlayComponent component =
dreamOverlayComponentFactory.create(mViewModelStore, mHost);
mDreamOverlayContainerViewController = component.getDreamOverlayContainerViewController();
setCurrentState(Lifecycle.State.CREATED);
mLifecycleRegistry = component.getLifecycleRegistry();
+ mDreamOverlayTouchMonitor = component.getDreamOverlayTouchMonitor();
+ mDreamOverlayTouchMonitor.init();
}
private void setCurrentState(Lifecycle.State state) {
@@ -89,9 +118,11 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ
@Override
public void onDestroy() {
+ mKeyguardUpdateMonitor.registerCallback(mKeyguardCallback);
setCurrentState(Lifecycle.State.DESTROYED);
final WindowManager windowManager = mContext.getSystemService(WindowManager.class);
windowManager.removeView(mWindow.getDecorView());
+ mStateController.setOverlayActive(false);
super.onDestroy();
}
@@ -101,6 +132,7 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ
mExecutor.execute(() -> {
addOverlayWindowLocked(layoutParams);
setCurrentState(Lifecycle.State.RESUMED);
+ mStateController.setOverlayActive(true);
});
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java
index e83884819f70..ac7457d90e49 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java
@@ -16,6 +16,8 @@
package com.android.systemui.dreams;
+import android.util.Log;
+
import androidx.annotation.NonNull;
import com.android.internal.annotations.VisibleForTesting;
@@ -30,6 +32,8 @@ import java.util.Collections;
import java.util.HashSet;
import java.util.Objects;
import java.util.concurrent.Executor;
+import java.util.function.Consumer;
+import java.util.stream.Collectors;
import javax.inject.Inject;
@@ -41,6 +45,16 @@ import javax.inject.Inject;
@SysUISingleton
public class DreamOverlayStateController implements
CallbackController<DreamOverlayStateController.Callback> {
+ private static final String TAG = "DreamOverlayStateCtlr";
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+ public static final int STATE_DREAM_OVERLAY_ACTIVE = 1 << 0;
+
+ private static final int OP_CLEAR_STATE = 1;
+ private static final int OP_SET_STATE = 2;
+
+ private int mState;
+
/**
* Callback for dream overlay events.
*/
@@ -50,11 +64,26 @@ public class DreamOverlayStateController implements
*/
default void onComplicationsChanged() {
}
+
+ /**
+ * Called when the dream overlay state changes.
+ */
+ default void onStateChanged() {
+ }
+
+ /**
+ * Called when the available complication types changes.
+ */
+ default void onAvailableComplicationTypesChanged() {
+ }
}
private final Executor mExecutor;
private final ArrayList<Callback> mCallbacks = new ArrayList<>();
+ @Complication.ComplicationType
+ private int mAvailableComplicationTypes = Complication.COMPLICATION_TYPE_NONE;
+
private final Collection<Complication> mComplications = new HashSet();
@VisibleForTesting
@@ -89,7 +118,33 @@ public class DreamOverlayStateController implements
* Returns collection of present {@link Complication}.
*/
public Collection<Complication> getComplications() {
- return Collections.unmodifiableCollection(mComplications);
+ return getComplications(true);
+ }
+
+ /**
+ * Returns collection of present {@link Complication}.
+ */
+ public Collection<Complication> getComplications(boolean filterByAvailability) {
+ return Collections.unmodifiableCollection(filterByAvailability
+ ? mComplications
+ .stream()
+ .filter(complication -> {
+ @Complication.ComplicationType
+ final int requiredTypes = complication.getRequiredTypeAvailability();
+
+ return requiredTypes == Complication.COMPLICATION_TYPE_NONE
+ || (requiredTypes & getAvailableComplicationTypes()) == requiredTypes;
+ })
+ .collect(Collectors.toCollection(HashSet::new))
+ : mComplications);
+ }
+
+ private void notifyCallbacks(Consumer<Callback> callbackConsumer) {
+ mExecutor.execute(() -> {
+ for (Callback callback : mCallbacks) {
+ callbackConsumer.accept(callback);
+ }
+ });
}
@Override
@@ -117,4 +172,58 @@ public class DreamOverlayStateController implements
mCallbacks.remove(callback);
});
}
+
+ /**
+ * Returns whether the overlay is active.
+ * @return {@code true} if overlay is active, {@code false} otherwise.
+ */
+ public boolean isOverlayActive() {
+ return containsState(STATE_DREAM_OVERLAY_ACTIVE);
+ }
+
+ private boolean containsState(int state) {
+ return (mState & state) != 0;
+ }
+
+ private void modifyState(int op, int state) {
+ final int existingState = mState;
+ switch (op) {
+ case OP_CLEAR_STATE:
+ mState &= ~state;
+ break;
+ case OP_SET_STATE:
+ mState |= state;
+ break;
+ }
+
+ if (existingState != mState) {
+ notifyCallbacks(callback -> callback.onStateChanged());
+ }
+ }
+
+ /**
+ * Sets whether the overlay is active.
+ * @param active {@code true} if overlay is active, {@code false} otherwise.
+ */
+ public void setOverlayActive(boolean active) {
+ modifyState(active ? OP_SET_STATE : OP_CLEAR_STATE, STATE_DREAM_OVERLAY_ACTIVE);
+ }
+
+ /**
+ * Returns the available complication types.
+ */
+ @Complication.ComplicationType
+ public int getAvailableComplicationTypes() {
+ return mAvailableComplicationTypes;
+ }
+
+ /**
+ * Sets the available complication types for the dream overlay.
+ */
+ public void setAvailableComplicationTypes(@Complication.ComplicationType int types) {
+ mExecutor.execute(() -> {
+ mAvailableComplicationTypes = types;
+ mCallbacks.forEach(callback -> callback.onAvailableComplicationTypesChanged());
+ });
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/SmartSpaceComplication.java b/packages/SystemUI/src/com/android/systemui/dreams/SmartSpaceComplication.java
new file mode 100644
index 000000000000..09221b4f810a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/SmartSpaceComplication.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.dreams;
+
+import android.content.Context;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+
+import com.android.systemui.CoreStartable;
+import com.android.systemui.dreams.complication.Complication;
+import com.android.systemui.dreams.complication.ComplicationLayoutParams;
+import com.android.systemui.dreams.complication.ComplicationViewModel;
+import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController;
+
+import javax.inject.Inject;
+
+/**
+ * {@link SmartSpaceComplication} embodies the SmartSpace view found on the lockscreen as a
+ * {@link Complication}
+ */
+public class SmartSpaceComplication implements Complication {
+ /**
+ * {@link CoreStartable} responsbile for registering {@link SmartSpaceComplication} with
+ * SystemUI.
+ */
+ public static class Registrant extends CoreStartable {
+ private final LockscreenSmartspaceController mSmartSpaceController;
+ private final DreamOverlayStateController mDreamOverlayStateController;
+ private final SmartSpaceComplication mComplication;
+
+ /**
+ * Default constructor for {@link SmartSpaceComplication}.
+ */
+ @Inject
+ public Registrant(Context context,
+ DreamOverlayStateController dreamOverlayStateController,
+ SmartSpaceComplication smartSpaceComplication,
+ LockscreenSmartspaceController smartSpaceController) {
+ super(context);
+ mDreamOverlayStateController = dreamOverlayStateController;
+ mComplication = smartSpaceComplication;
+ mSmartSpaceController = smartSpaceController;
+ }
+
+ @Override
+ public void start() {
+ if (mSmartSpaceController.isEnabled()) {
+ mDreamOverlayStateController.addComplication(mComplication);
+ }
+ }
+ }
+
+ private static class SmartSpaceComplicationViewHolder implements ViewHolder {
+ private final LockscreenSmartspaceController mSmartSpaceController;
+ private final Context mContext;
+
+ protected SmartSpaceComplicationViewHolder(
+ Context context,
+ LockscreenSmartspaceController smartSpaceController) {
+ mSmartSpaceController = smartSpaceController;
+ mContext = context;
+ }
+
+ @Override
+ public View getView() {
+ final FrameLayout smartSpaceContainer = new FrameLayout(mContext);
+ smartSpaceContainer.addView(
+ mSmartSpaceController.buildAndConnectView(smartSpaceContainer),
+ new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT));
+
+ return smartSpaceContainer;
+ }
+
+ @Override
+ public ComplicationLayoutParams getLayoutParams() {
+ return new ComplicationLayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT,
+ ComplicationLayoutParams.POSITION_TOP | ComplicationLayoutParams.POSITION_START,
+ ComplicationLayoutParams.DIRECTION_DOWN,
+ 0, true);
+ }
+ }
+
+ private final LockscreenSmartspaceController mSmartSpaceController;
+ private final Context mContext;
+
+ @Inject
+ public SmartSpaceComplication(Context context,
+ LockscreenSmartspaceController smartSpaceController) {
+ mContext = context;
+ mSmartSpaceController = smartSpaceController;
+ }
+
+ @Override
+ public ViewHolder createView(ComplicationViewModel model) {
+ return new SmartSpaceComplicationViewHolder(mContext, mSmartSpaceController);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/Complication.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/Complication.java
index 96cf50d58d10..fe458f4c5318 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/Complication.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/Complication.java
@@ -154,6 +154,27 @@ public interface Complication {
int CATEGORY_SYSTEM = 1 << 1;
/**
+ * The type of dream complications which can be provided by a {@link Complication}.
+ */
+ @IntDef(prefix = {"COMPLICATION_TYPE_"}, flag = true, value = {
+ COMPLICATION_TYPE_NONE,
+ COMPLICATION_TYPE_TIME,
+ COMPLICATION_TYPE_DATE,
+ COMPLICATION_TYPE_WEATHER,
+ COMPLICATION_TYPE_AIR_QUALITY,
+ COMPLICATION_TYPE_CAST_INFO
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface ComplicationType {}
+
+ int COMPLICATION_TYPE_NONE = 0;
+ int COMPLICATION_TYPE_TIME = 1;
+ int COMPLICATION_TYPE_DATE = 1 << 1;
+ int COMPLICATION_TYPE_WEATHER = 1 << 2;
+ int COMPLICATION_TYPE_AIR_QUALITY = 1 << 3;
+ int COMPLICATION_TYPE_CAST_INFO = 1 << 4;
+
+ /**
* The {@link Host} interface specifies a way a {@link Complication} to communicate with its
* parent entity for information and actions.
*/
@@ -207,4 +228,15 @@ public interface Complication {
* @return a {@link ViewHolder} for this {@link Complication} instance.
*/
ViewHolder createView(ComplicationViewModel model);
+
+ /**
+ * Returns the types that must be present in order for this complication to participate on
+ * the dream overlay. By default, this method returns
+ * {@code Complication.COMPLICATION_TYPE_NONE} to indicate no types are required.
+ * @return
+ */
+ @Complication.ComplicationType
+ default int getRequiredTypeAvailability() {
+ return Complication.COMPLICATION_TYPE_NONE;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationCollectionLiveData.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationCollectionLiveData.java
index 76818fa0c42e..f6fe8d2e579c 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationCollectionLiveData.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationCollectionLiveData.java
@@ -42,6 +42,10 @@ public class ComplicationCollectionLiveData extends LiveData<Collection<Complica
setValue(mDreamOverlayStateController.getComplications());
}
+ @Override
+ public void onAvailableComplicationTypesChanged() {
+ setValue(mDreamOverlayStateController.getComplications());
+ }
};
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutEngine.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutEngine.java
index cb24ae609eeb..5223f379508f 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutEngine.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutEngine.java
@@ -25,10 +25,13 @@ import android.view.ViewGroup;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.constraintlayout.widget.Constraints;
+import com.android.systemui.R;
+
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
+import java.util.function.Consumer;
import javax.inject.Inject;
import javax.inject.Named;
@@ -102,6 +105,8 @@ public class ComplicationLayoutEngine {
final int direction = getLayoutParams().getDirection();
+ final boolean snapsToGuide = getLayoutParams().snapsToGuide();
+
// If no parent, view is the anchor. In this case, it is given the highest priority for
// alignment. All alignment preferences are done in relation to the parent container.
final boolean isRoot = head == mView;
@@ -125,6 +130,11 @@ public class ComplicationLayoutEngine {
} else {
params.startToEnd = head.getId();
}
+ if (snapsToGuide
+ && (direction == ComplicationLayoutParams.DIRECTION_DOWN
+ || direction == ComplicationLayoutParams.DIRECTION_UP)) {
+ params.endToStart = R.id.complication_start_guide;
+ }
break;
case ComplicationLayoutParams.POSITION_TOP:
if (isRoot || direction != ComplicationLayoutParams.DIRECTION_DOWN) {
@@ -132,6 +142,11 @@ public class ComplicationLayoutEngine {
} else {
params.topToBottom = head.getId();
}
+ if (snapsToGuide
+ && (direction == ComplicationLayoutParams.DIRECTION_END
+ || direction == ComplicationLayoutParams.DIRECTION_START)) {
+ params.endToStart = R.id.complication_top_guide;
+ }
break;
case ComplicationLayoutParams.POSITION_BOTTOM:
if (isRoot || direction != ComplicationLayoutParams.DIRECTION_UP) {
@@ -139,6 +154,11 @@ public class ComplicationLayoutEngine {
} else {
params.bottomToTop = head.getId();
}
+ if (snapsToGuide
+ && (direction == ComplicationLayoutParams.DIRECTION_END
+ || direction == ComplicationLayoutParams.DIRECTION_START)) {
+ params.topToBottom = R.id.complication_bottom_guide;
+ }
break;
case ComplicationLayoutParams.POSITION_END:
if (isRoot || direction != ComplicationLayoutParams.DIRECTION_START) {
@@ -146,6 +166,11 @@ public class ComplicationLayoutEngine {
} else {
params.endToStart = head.getId();
}
+ if (snapsToGuide
+ && (direction == ComplicationLayoutParams.DIRECTION_UP
+ || direction == ComplicationLayoutParams.DIRECTION_DOWN)) {
+ params.startToEnd = R.id.complication_end_guide;
+ }
break;
}
});
@@ -153,6 +178,16 @@ public class ComplicationLayoutEngine {
mView.setLayoutParams(params);
}
+ private void setGuide(ConstraintLayout.LayoutParams lp, int validDirections,
+ Consumer<ConstraintLayout.LayoutParams> consumer) {
+ final ComplicationLayoutParams layoutParams = getLayoutParams();
+ if (!layoutParams.snapsToGuide()) {
+ return;
+ }
+
+ consumer.accept(lp);
+ }
+
/**
* Informs the {@link ViewEntry}'s parent entity to remove the {@link ViewEntry} from
* being shown further.
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutParams.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutParams.java
index f9a69fadfedc..8e8cb72d6ad0 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutParams.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutParams.java
@@ -40,13 +40,13 @@ public class ComplicationLayoutParams extends ViewGroup.LayoutParams {
@interface Position {}
/** Align view with the top of parent or bottom of preceding {@link Complication}. */
- static final int POSITION_TOP = 1 << 0;
+ public static final int POSITION_TOP = 1 << 0;
/** Align view with the bottom of parent or top of preceding {@link Complication}. */
- static final int POSITION_BOTTOM = 1 << 1;
+ public static final int POSITION_BOTTOM = 1 << 1;
/** Align view with the start of parent or end of preceding {@link Complication}. */
- static final int POSITION_START = 1 << 2;
+ public static final int POSITION_START = 1 << 2;
/** Align view with the end of parent or start of preceding {@link Complication}. */
- static final int POSITION_END = 1 << 3;
+ public static final int POSITION_END = 1 << 3;
private static final int FIRST_POSITION = POSITION_TOP;
private static final int LAST_POSITION = POSITION_END;
@@ -61,13 +61,13 @@ public class ComplicationLayoutParams extends ViewGroup.LayoutParams {
@interface Direction {}
/** Position view upward from position. */
- static final int DIRECTION_UP = 1 << 0;
+ public static final int DIRECTION_UP = 1 << 0;
/** Position view downward from position. */
- static final int DIRECTION_DOWN = 1 << 1;
+ public static final int DIRECTION_DOWN = 1 << 1;
/** Position view towards the start of the parent. */
- static final int DIRECTION_START = 1 << 2;
+ public static final int DIRECTION_START = 1 << 2;
/** Position view towards the end of parent. */
- static final int DIRECTION_END = 1 << 3;
+ public static final int DIRECTION_END = 1 << 3;
@Position
private final int mPosition;
@@ -77,6 +77,8 @@ public class ComplicationLayoutParams extends ViewGroup.LayoutParams {
private final int mWeight;
+ private final boolean mSnapToGuide;
+
// Do not allow specifying opposite positions
private static final int[] INVALID_POSITIONS =
{ POSITION_BOTTOM | POSITION_TOP, POSITION_END | POSITION_START };
@@ -104,6 +106,27 @@ public class ComplicationLayoutParams extends ViewGroup.LayoutParams {
*/
public ComplicationLayoutParams(int width, int height, @Position int position,
@Direction int direction, int weight) {
+ this(width, height, position, direction, weight, false);
+ }
+
+ /**
+ * Constructs a {@link ComplicationLayoutParams}.
+ * @param width The width {@link android.view.View.MeasureSpec} for the view.
+ * @param height The height {@link android.view.View.MeasureSpec} for the view.
+ * @param position The place within the parent container where the view should be positioned.
+ * @param direction The direction the view should be laid out from either the parent container
+ * or preceding view.
+ * @param weight The weight that should be considered for this view when compared to other
+ * views. This has an impact on the placement of the view but not the rendering of
+ * the view.
+ * @param snapToGuide When set to {@code true}, the dimension perpendicular to the direction
+ * will be automatically set to align with a predetermined guide for that
+ * side. For example, if the complication is aligned to the top end and
+ * direction is down, then the width of the complication will be set to span
+ * from the end of the parent to the guide.
+ */
+ public ComplicationLayoutParams(int width, int height, @Position int position,
+ @Direction int direction, int weight, boolean snapToGuide) {
super(width, height);
if (!validatePosition(position)) {
@@ -118,6 +141,8 @@ public class ComplicationLayoutParams extends ViewGroup.LayoutParams {
mDirection = direction;
mWeight = weight;
+
+ mSnapToGuide = snapToGuide;
}
/**
@@ -128,6 +153,7 @@ public class ComplicationLayoutParams extends ViewGroup.LayoutParams {
mPosition = source.mPosition;
mDirection = source.mDirection;
mWeight = source.mWeight;
+ mSnapToGuide = source.mSnapToGuide;
}
private static boolean validateDirection(@Position int position, @Direction int direction) {
@@ -180,7 +206,19 @@ public class ComplicationLayoutParams extends ViewGroup.LayoutParams {
return mPosition;
}
+ /**
+ * Returns the set weight for the complication. The weight determines ordering a complication
+ * given the same position/direction.
+ */
public int getWeight() {
return mWeight;
}
+
+ /**
+ * Returns whether the complication's dimension perpendicular to direction should be
+ * automatically set.
+ */
+ public boolean snapsToGuide() {
+ return mSnapToGuide;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationUtils.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationUtils.java
new file mode 100644
index 000000000000..3a2a6ef60f03
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationUtils.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.dreams.complication;
+
+import static com.android.systemui.dreams.complication.Complication.COMPLICATION_TYPE_AIR_QUALITY;
+import static com.android.systemui.dreams.complication.Complication.COMPLICATION_TYPE_CAST_INFO;
+import static com.android.systemui.dreams.complication.Complication.COMPLICATION_TYPE_DATE;
+import static com.android.systemui.dreams.complication.Complication.COMPLICATION_TYPE_NONE;
+import static com.android.systemui.dreams.complication.Complication.COMPLICATION_TYPE_TIME;
+import static com.android.systemui.dreams.complication.Complication.COMPLICATION_TYPE_WEATHER;
+
+import com.android.settingslib.dream.DreamBackend;
+
+/**
+ * A collection of utility methods for working with {@link Complication}.
+ */
+public class ComplicationUtils {
+ /**
+ * Converts a {@link com.android.settingslib.dream.DreamBackend.ComplicationType} to
+ * {@link ComplicationType}.
+ */
+ @Complication.ComplicationType
+ public static int convertComplicationType(@DreamBackend.ComplicationType int type) {
+ switch (type) {
+ case DreamBackend.COMPLICATION_TYPE_TIME:
+ return COMPLICATION_TYPE_TIME;
+ case DreamBackend.COMPLICATION_TYPE_DATE:
+ return COMPLICATION_TYPE_DATE;
+ case DreamBackend.COMPLICATION_TYPE_WEATHER:
+ return COMPLICATION_TYPE_WEATHER;
+ case DreamBackend.COMPLICATION_TYPE_AIR_QUALITY:
+ return COMPLICATION_TYPE_AIR_QUALITY;
+ case DreamBackend.COMPLICATION_TYPE_CAST_INFO:
+ return COMPLICATION_TYPE_CAST_INFO;
+ default:
+ return COMPLICATION_TYPE_NONE;
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamClockDateComplication.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamClockDateComplication.java
new file mode 100644
index 000000000000..59c52b9e402b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamClockDateComplication.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.dreams.complication;
+
+import static com.android.systemui.dreams.complication.dagger.DreamClockDateComplicationComponent.DreamClockDateComplicationModule.DREAM_CLOCK_DATE_COMPLICATION_LAYOUT_PARAMS;
+import static com.android.systemui.dreams.complication.dagger.DreamClockDateComplicationComponent.DreamClockDateComplicationModule.DREAM_CLOCK_DATE_COMPLICATION_VIEW;
+
+import android.content.Context;
+import android.view.View;
+
+import com.android.systemui.CoreStartable;
+import com.android.systemui.dreams.DreamOverlayStateController;
+import com.android.systemui.dreams.complication.dagger.DreamClockDateComplicationComponent;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+
+/**
+ * Clock Date Complication that produce Clock Date view holder.
+ */
+public class DreamClockDateComplication implements Complication {
+ DreamClockDateComplicationComponent.Factory mComponentFactory;
+
+ /**
+ * Default constructor for {@link DreamClockDateComplication}.
+ */
+ @Inject
+ public DreamClockDateComplication(
+ DreamClockDateComplicationComponent.Factory componentFactory) {
+ mComponentFactory = componentFactory;
+ }
+
+ /**
+ * Create {@link DreamClockDateViewHolder}.
+ */
+ @Override
+ public ViewHolder createView(ComplicationViewModel model) {
+ return mComponentFactory.create().getViewHolder();
+ }
+
+ /**
+ * {@link CoreStartable} responsbile for registering {@link DreamClockDateComplication} with
+ * SystemUI.
+ */
+ public static class Registrant extends CoreStartable {
+ private final DreamOverlayStateController mDreamOverlayStateController;
+ private final DreamClockDateComplication mComplication;
+
+ /**
+ * Default constructor to register {@link DreamClockDateComplication}.
+ */
+ @Inject
+ public Registrant(Context context,
+ DreamOverlayStateController dreamOverlayStateController,
+ DreamClockDateComplication dreamClockDateComplication) {
+ super(context);
+ mDreamOverlayStateController = dreamOverlayStateController;
+ mComplication = dreamClockDateComplication;
+ }
+
+ @Override
+ public void start() {
+ mDreamOverlayStateController.addComplication(mComplication);
+ }
+ }
+
+ /**
+ * ViewHolder to contain value/logic associated with a Clock Date Complication View.
+ */
+ public static class DreamClockDateViewHolder implements ViewHolder {
+ private final View mView;
+ private final ComplicationLayoutParams mLayoutParams;
+
+ @Inject
+ DreamClockDateViewHolder(@Named(DREAM_CLOCK_DATE_COMPLICATION_VIEW) View view,
+ @Named(DREAM_CLOCK_DATE_COMPLICATION_LAYOUT_PARAMS)
+ ComplicationLayoutParams layoutParams) {
+ mView = view;
+ mLayoutParams = layoutParams;
+ }
+
+ @Override
+ public View getView() {
+ return mView;
+ }
+
+ @Override
+ public ComplicationLayoutParams getLayoutParams() {
+ return mLayoutParams;
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamClockTimeComplication.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamClockTimeComplication.java
new file mode 100644
index 000000000000..b0c3a424cc92
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamClockTimeComplication.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.dreams.complication;
+
+import static com.android.systemui.dreams.complication.dagger.DreamClockTimeComplicationComponent.DreamClockTimeComplicationModule.DREAM_CLOCK_TIME_COMPLICATION_LAYOUT_PARAMS;
+import static com.android.systemui.dreams.complication.dagger.DreamClockTimeComplicationComponent.DreamClockTimeComplicationModule.DREAM_CLOCK_TIME_COMPLICATION_VIEW;
+
+import android.content.Context;
+import android.view.View;
+
+import com.android.systemui.CoreStartable;
+import com.android.systemui.dreams.DreamOverlayStateController;
+import com.android.systemui.dreams.complication.dagger.DreamClockTimeComplicationComponent;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+
+/**
+ * Clock Time Complication that produce Clock Time view holder.
+ */
+public class DreamClockTimeComplication implements Complication {
+ DreamClockTimeComplicationComponent.Factory mComponentFactory;
+
+ /**
+ * Default constructor for {@link DreamClockTimeComplication}.
+ */
+ @Inject
+ public DreamClockTimeComplication(
+ DreamClockTimeComplicationComponent.Factory componentFactory) {
+ mComponentFactory = componentFactory;
+ }
+
+ /**
+ * Create {@link DreamClockTimeViewHolder}.
+ */
+ @Override
+ public ViewHolder createView(ComplicationViewModel model) {
+ return mComponentFactory.create().getViewHolder();
+ }
+
+ /**
+ * {@link CoreStartable} responsbile for registering {@link DreamClockTimeComplication} with
+ * SystemUI.
+ */
+ public static class Registrant extends CoreStartable {
+ private final DreamOverlayStateController mDreamOverlayStateController;
+ private final DreamClockTimeComplication mComplication;
+
+ /**
+ * Default constructor to register {@link DreamClockTimeComplication}.
+ */
+ @Inject
+ public Registrant(Context context,
+ DreamOverlayStateController dreamOverlayStateController,
+ DreamClockTimeComplication dreamClockTimeComplication) {
+ super(context);
+ mDreamOverlayStateController = dreamOverlayStateController;
+ mComplication = dreamClockTimeComplication;
+ }
+
+ @Override
+ public void start() {
+ mDreamOverlayStateController.addComplication(mComplication);
+ }
+ }
+
+ /**
+ * ViewHolder to contain value/logic associated with a Clock Time Complication View.
+ */
+ public static class DreamClockTimeViewHolder implements ViewHolder {
+ private final View mView;
+ private final ComplicationLayoutParams mLayoutParams;
+
+ @Inject
+ DreamClockTimeViewHolder(@Named(DREAM_CLOCK_TIME_COMPLICATION_VIEW) View view,
+ @Named(DREAM_CLOCK_TIME_COMPLICATION_LAYOUT_PARAMS)
+ ComplicationLayoutParams layoutParams) {
+ mView = view;
+ mLayoutParams = layoutParams;
+ }
+
+ @Override
+ public View getView() {
+ return mView;
+ }
+
+ @Override
+ public ComplicationLayoutParams getLayoutParams() {
+ return mLayoutParams;
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamWeatherComplication.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamWeatherComplication.java
new file mode 100644
index 000000000000..cbdbef3ae57e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamWeatherComplication.java
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.dreams.complication;
+
+import static com.android.systemui.dreams.complication.dagger.DreamWeatherComplicationComponent.DreamWeatherComplicationModule.DREAM_WEATHER_COMPLICATION_LAYOUT_PARAMS;
+import static com.android.systemui.dreams.complication.dagger.DreamWeatherComplicationComponent.DreamWeatherComplicationModule.DREAM_WEATHER_COMPLICATION_VIEW;
+
+import android.app.smartspace.SmartspaceAction;
+import android.app.smartspace.SmartspaceTarget;
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
+import android.text.TextUtils;
+import android.widget.TextView;
+
+import com.android.systemui.CoreStartable;
+import com.android.systemui.R;
+import com.android.systemui.dreams.DreamOverlayStateController;
+import com.android.systemui.dreams.complication.dagger.DreamWeatherComplicationComponent;
+import com.android.systemui.plugins.BcSmartspaceDataPlugin.SmartspaceTargetListener;
+import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController;
+import com.android.systemui.util.ViewController;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+
+/**
+ * Weather Complication that produce Weather view holder.
+ */
+public class DreamWeatherComplication implements Complication {
+ DreamWeatherComplicationComponent.Factory mComponentFactory;
+
+ /**
+ * Default constructor for {@link DreamWeatherComplication}.
+ */
+ @Inject
+ public DreamWeatherComplication(
+ DreamWeatherComplicationComponent.Factory componentFactory) {
+ mComponentFactory = componentFactory;
+ }
+
+ /**
+ * Create {@link DreamWeatherViewHolder}.
+ */
+ @Override
+ public ViewHolder createView(ComplicationViewModel model) {
+ return mComponentFactory.create().getViewHolder();
+ }
+
+ /**
+ * {@link CoreStartable} for registering {@link DreamWeatherComplication} with SystemUI.
+ */
+ public static class Registrant extends CoreStartable {
+ private final LockscreenSmartspaceController mSmartSpaceController;
+ private final DreamOverlayStateController mDreamOverlayStateController;
+ private final DreamWeatherComplication mComplication;
+
+ /**
+ * Default constructor to register {@link DreamWeatherComplication}.
+ */
+ @Inject
+ public Registrant(Context context,
+ LockscreenSmartspaceController smartspaceController,
+ DreamOverlayStateController dreamOverlayStateController,
+ DreamWeatherComplication dreamWeatherComplication) {
+ super(context);
+ mSmartSpaceController = smartspaceController;
+ mDreamOverlayStateController = dreamOverlayStateController;
+ mComplication = dreamWeatherComplication;
+ }
+
+ @Override
+ public void start() {
+ if (mSmartSpaceController.isEnabled()) {
+ mDreamOverlayStateController.addComplication(mComplication);
+ }
+ }
+ }
+
+ /**
+ * ViewHolder to contain value/logic associated with a Weather Complication View.
+ */
+ public static class DreamWeatherViewHolder implements ViewHolder {
+ private final TextView mView;
+ private final ComplicationLayoutParams mLayoutParams;
+ private final DreamWeatherViewController mViewController;
+
+ @Inject
+ DreamWeatherViewHolder(
+ @Named(DREAM_WEATHER_COMPLICATION_VIEW) TextView view,
+ DreamWeatherViewController controller,
+ @Named(DREAM_WEATHER_COMPLICATION_LAYOUT_PARAMS)
+ ComplicationLayoutParams layoutParams) {
+ mView = view;
+ mLayoutParams = layoutParams;
+ mViewController = controller;
+ mViewController.init();
+ }
+
+ @Override
+ public TextView getView() {
+ return mView;
+ }
+
+ @Override
+ public ComplicationLayoutParams getLayoutParams() {
+ return mLayoutParams;
+ }
+ }
+
+ /**
+ * ViewController to contain value/logic associated with a Weather Complication View.
+ */
+ static class DreamWeatherViewController extends ViewController<TextView> {
+ private final LockscreenSmartspaceController mSmartSpaceController;
+ private SmartspaceTargetListener mSmartspaceTargetListener;
+
+ @Inject
+ DreamWeatherViewController(
+ @Named(DREAM_WEATHER_COMPLICATION_VIEW) TextView view,
+ LockscreenSmartspaceController smartspaceController
+ ) {
+ super(view);
+ mSmartSpaceController = smartspaceController;
+ }
+
+ @Override
+ protected void onViewAttached() {
+ mSmartspaceTargetListener = targets -> targets.forEach(
+ t -> {
+ if (t instanceof SmartspaceTarget
+ && ((SmartspaceTarget) t).getFeatureType()
+ == SmartspaceTarget.FEATURE_WEATHER) {
+ final SmartspaceTarget target = (SmartspaceTarget) t;
+ final SmartspaceAction headerAction = target.getHeaderAction();
+ if (headerAction == null || TextUtils.isEmpty(
+ headerAction.getTitle())) {
+ return;
+ }
+
+ String temperature = headerAction.getTitle().toString();
+ mView.setText(temperature);
+ final Icon icon = headerAction.getIcon();
+ if (icon != null) {
+ final int iconSize =
+ getResources().getDimensionPixelSize(
+ R.dimen.smart_action_button_icon_size);
+ final Drawable iconDrawable = icon.loadDrawable(getContext());
+ iconDrawable.setBounds(0, 0, iconSize, iconSize);
+ mView.setCompoundDrawables(iconDrawable, null, null, null);
+ mView.setCompoundDrawablePadding(
+ getResources().getDimensionPixelSize(
+ R.dimen.smart_action_button_icon_padding));
+
+ }
+ }
+ });
+ mSmartSpaceController.addListener(mSmartspaceTargetListener);
+ }
+
+ @Override
+ protected void onViewDetached() {
+ mSmartSpaceController.removeListener(mSmartspaceTargetListener);
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamClockDateComplicationComponent.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamClockDateComplicationComponent.java
new file mode 100644
index 000000000000..eaffb1ca5b3e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamClockDateComplicationComponent.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.dreams.complication.dagger;
+
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.android.internal.util.Preconditions;
+import com.android.systemui.R;
+import com.android.systemui.dreams.complication.ComplicationLayoutParams;
+import com.android.systemui.dreams.complication.DreamClockDateComplication.DreamClockDateViewHolder;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+
+import javax.inject.Named;
+import javax.inject.Scope;
+
+import dagger.Module;
+import dagger.Provides;
+import dagger.Subcomponent;
+
+/**
+ * {@link DreamClockDateComplicationComponent} is responsible for generating dependencies
+ * surrounding the
+ * Clock Date {@link com.android.systemui.dreams.complication.Complication}, such as the layout
+ * details.
+ */
+@Subcomponent(modules = {
+ DreamClockDateComplicationComponent.DreamClockDateComplicationModule.class,
+})
+@DreamClockDateComplicationComponent.DreamClockDateComplicationScope
+public interface DreamClockDateComplicationComponent {
+ /**
+ * Creates {@link DreamClockDateViewHolder}.
+ */
+ DreamClockDateViewHolder getViewHolder();
+
+ @Documented
+ @Retention(RUNTIME)
+ @Scope
+ @interface DreamClockDateComplicationScope {
+ }
+
+ /**
+ * Generates {@link DreamClockDateComplicationComponent}.
+ */
+ @Subcomponent.Factory
+ interface Factory {
+ DreamClockDateComplicationComponent create();
+ }
+
+ /**
+ * Scoped values for {@link DreamClockDateComplicationComponent}.
+ */
+ @Module
+ interface DreamClockDateComplicationModule {
+ String DREAM_CLOCK_DATE_COMPLICATION_VIEW = "clock_date_complication_view";
+ String DREAM_CLOCK_DATE_COMPLICATION_LAYOUT_PARAMS =
+ "clock_date_complication_layout_params";
+ // Order weight of insert into parent container
+ int INSERT_ORDER_WEIGHT = 2;
+
+ /**
+ * Provides the complication view.
+ */
+ @Provides
+ @DreamClockDateComplicationScope
+ @Named(DREAM_CLOCK_DATE_COMPLICATION_VIEW)
+ static View provideComplicationView(LayoutInflater layoutInflater) {
+ return Preconditions.checkNotNull(
+ layoutInflater.inflate(R.layout.dream_overlay_complication_clock_date,
+ null, false),
+ "R.layout.dream_overlay_complication_clock_date did not properly inflated");
+ }
+
+ /**
+ * Provides the layout parameters for the complication view.
+ */
+ @Provides
+ @DreamClockDateComplicationScope
+ @Named(DREAM_CLOCK_DATE_COMPLICATION_LAYOUT_PARAMS)
+ static ComplicationLayoutParams provideLayoutParams() {
+ return new ComplicationLayoutParams(0,
+ ViewGroup.LayoutParams.WRAP_CONTENT,
+ ComplicationLayoutParams.POSITION_BOTTOM
+ | ComplicationLayoutParams.POSITION_START,
+ ComplicationLayoutParams.DIRECTION_END,
+ INSERT_ORDER_WEIGHT,
+ true);
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamClockTimeComplicationComponent.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamClockTimeComplicationComponent.java
new file mode 100644
index 000000000000..053c5b345760
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamClockTimeComplicationComponent.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.dreams.complication.dagger;
+
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.android.internal.util.Preconditions;
+import com.android.systemui.R;
+import com.android.systemui.dreams.complication.ComplicationLayoutParams;
+import com.android.systemui.dreams.complication.DreamClockTimeComplication.DreamClockTimeViewHolder;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+
+import javax.inject.Named;
+import javax.inject.Scope;
+
+import dagger.Module;
+import dagger.Provides;
+import dagger.Subcomponent;
+
+/**
+ * {@link DreamClockTimeComplicationComponent} is responsible for generating dependencies
+ * surrounding the
+ * Clock Time {@link com.android.systemui.dreams.complication.Complication}, such as the layout
+ * details.
+ */
+@Subcomponent(modules = {
+ DreamClockTimeComplicationComponent.DreamClockTimeComplicationModule.class,
+})
+@DreamClockTimeComplicationComponent.DreamClockTimeComplicationScope
+public interface DreamClockTimeComplicationComponent {
+ /**
+ * Creates {@link DreamClockTimeViewHolder}.
+ */
+ DreamClockTimeViewHolder getViewHolder();
+
+ @Documented
+ @Retention(RUNTIME)
+ @Scope
+ @interface DreamClockTimeComplicationScope {
+ }
+
+ /**
+ * Generates {@link DreamClockTimeComplicationComponent}.
+ */
+ @Subcomponent.Factory
+ interface Factory {
+ DreamClockTimeComplicationComponent create();
+ }
+
+ /**
+ * Scoped values for {@link DreamClockTimeComplicationComponent}.
+ */
+ @Module
+ interface DreamClockTimeComplicationModule {
+ String DREAM_CLOCK_TIME_COMPLICATION_VIEW = "clock_time_complication_view";
+ String DREAM_CLOCK_TIME_COMPLICATION_LAYOUT_PARAMS =
+ "clock_time_complication_layout_params";
+ // Order weight of insert into parent container
+ int INSERT_ORDER_WEIGHT = 0;
+
+ /**
+ * Provides the complication view.
+ */
+ @Provides
+ @DreamClockTimeComplicationScope
+ @Named(DREAM_CLOCK_TIME_COMPLICATION_VIEW)
+ static View provideComplicationView(LayoutInflater layoutInflater) {
+ return Preconditions.checkNotNull(
+ layoutInflater.inflate(R.layout.dream_overlay_complication_clock_time,
+ null, false),
+ "R.layout.dream_overlay_complication_clock_time did not properly inflated");
+ }
+
+ /**
+ * Provides the layout parameters for the complication view.
+ */
+ @Provides
+ @DreamClockTimeComplicationScope
+ @Named(DREAM_CLOCK_TIME_COMPLICATION_LAYOUT_PARAMS)
+ static ComplicationLayoutParams provideLayoutParams() {
+ return new ComplicationLayoutParams(0,
+ ViewGroup.LayoutParams.WRAP_CONTENT,
+ ComplicationLayoutParams.POSITION_BOTTOM
+ | ComplicationLayoutParams.POSITION_START,
+ ComplicationLayoutParams.DIRECTION_UP,
+ INSERT_ORDER_WEIGHT,
+ true);
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamWeatherComplicationComponent.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamWeatherComplicationComponent.java
new file mode 100644
index 000000000000..a282594ff282
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamWeatherComplicationComponent.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.dreams.complication.dagger;
+
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import android.view.LayoutInflater;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import com.android.internal.util.Preconditions;
+import com.android.systemui.R;
+import com.android.systemui.dreams.complication.ComplicationLayoutParams;
+import com.android.systemui.dreams.complication.DreamWeatherComplication.DreamWeatherViewHolder;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+
+import javax.inject.Named;
+import javax.inject.Scope;
+
+import dagger.Module;
+import dagger.Provides;
+import dagger.Subcomponent;
+
+/**
+ * {@link DreamWeatherComplicationComponent} is responsible for generating dependencies surrounding
+ * the
+ * Clock Date {@link com.android.systemui.dreams.complication.Complication}, such as the layout
+ * details.
+ */
+@Subcomponent(modules = {
+ DreamWeatherComplicationComponent.DreamWeatherComplicationModule.class,
+})
+@DreamWeatherComplicationComponent.DreamWeatherComplicationScope
+public interface DreamWeatherComplicationComponent {
+ /**
+ * Creates {@link DreamWeatherViewHolder}.
+ */
+ DreamWeatherViewHolder getViewHolder();
+
+ @Documented
+ @Retention(RUNTIME)
+ @Scope
+ @interface DreamWeatherComplicationScope {
+ }
+
+ /**
+ * Generates {@link DreamWeatherComplicationComponent}.
+ */
+ @Subcomponent.Factory
+ interface Factory {
+ DreamWeatherComplicationComponent create();
+ }
+
+ /**
+ * Scoped values for {@link DreamWeatherComplicationComponent}.
+ */
+ @Module
+ interface DreamWeatherComplicationModule {
+ String DREAM_WEATHER_COMPLICATION_VIEW = "weather_complication_view";
+ String DREAM_WEATHER_COMPLICATION_LAYOUT_PARAMS =
+ "weather_complication_layout_params";
+ // Order weight of insert into parent container
+ int INSERT_ORDER_WEIGHT = 1;
+
+ /**
+ * Provides the complication view.
+ */
+ @Provides
+ @DreamWeatherComplicationScope
+ @Named(DREAM_WEATHER_COMPLICATION_VIEW)
+ static TextView provideComplicationView(LayoutInflater layoutInflater) {
+ return Preconditions.checkNotNull((TextView)
+ layoutInflater.inflate(R.layout.dream_overlay_complication_weather,
+ null, false),
+ "R.layout.dream_overlay_complication_weather did not properly inflated");
+ }
+
+ /**
+ * Provides the layout parameters for the complication view.
+ */
+ @Provides
+ @DreamWeatherComplicationScope
+ @Named(DREAM_WEATHER_COMPLICATION_LAYOUT_PARAMS)
+ static ComplicationLayoutParams provideLayoutParams() {
+ return new ComplicationLayoutParams(0,
+ ViewGroup.LayoutParams.WRAP_CONTENT,
+ ComplicationLayoutParams.POSITION_BOTTOM
+ | ComplicationLayoutParams.POSITION_START,
+ ComplicationLayoutParams.DIRECTION_END,
+ INSERT_ORDER_WEIGHT,
+ true);
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/RegisteredComplicationsModule.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/RegisteredComplicationsModule.java
new file mode 100644
index 000000000000..8e4fb3783f4a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/RegisteredComplicationsModule.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.dreams.complication.dagger;
+
+import com.android.systemui.dagger.SystemUIBinder;
+
+import dagger.Module;
+
+/**
+ * Module for all components with corresponding dream layer complications registered in
+ * {@link SystemUIBinder}.
+ */
+@Module(subcomponents = {
+ DreamClockTimeComplicationComponent.class,
+ DreamClockDateComplicationComponent.class,
+ DreamWeatherComplicationComponent.class,
+})
+public interface RegisteredComplicationsModule {
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java
index d5053a03fec6..d8af9e5f1f4a 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java
@@ -16,13 +16,20 @@
package com.android.systemui.dreams.dagger;
+import com.android.systemui.dreams.complication.dagger.RegisteredComplicationsModule;
+import com.android.systemui.dreams.touch.dagger.DreamTouchModule;
+
import dagger.Module;
/**
* Dagger Module providing Communal-related functionality.
*/
-@Module(subcomponents = {
- DreamOverlayComponent.class,
-})
+@Module(includes = {
+ DreamTouchModule.class,
+ RegisteredComplicationsModule.class,
+ },
+ subcomponents = {
+ DreamOverlayComponent.class,
+ })
public interface DreamModule {
} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayComponent.java b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayComponent.java
index f0ab6964faa1..05ab9015fecc 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayComponent.java
@@ -25,6 +25,7 @@ import androidx.lifecycle.ViewModelStore;
import com.android.systemui.dreams.DreamOverlayContainerViewController;
import com.android.systemui.dreams.complication.Complication;
import com.android.systemui.dreams.complication.dagger.ComplicationModule;
+import com.android.systemui.dreams.touch.DreamOverlayTouchMonitor;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
@@ -64,4 +65,7 @@ public interface DreamOverlayComponent {
/** Builds a {@link androidx.lifecycle.LifecycleOwner} */
LifecycleOwner getLifecycleOwner();
+
+ /** Builds a {@link DreamOverlayTouchMonitor} */
+ DreamOverlayTouchMonitor getDreamOverlayTouchMonitor();
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java
index b56aa2c92a27..4eb5cb97607a 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java
@@ -22,6 +22,7 @@ import android.os.Handler;
import android.view.LayoutInflater;
import android.view.ViewGroup;
+import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.LifecycleRegistry;
@@ -140,4 +141,10 @@ public abstract class DreamOverlayModule {
static LifecycleRegistry providesLifecycleRegistry(LifecycleOwner lifecycleOwner) {
return new LifecycleRegistry(lifecycleOwner);
}
+
+ @Provides
+ @DreamOverlayComponent.DreamOverlayScope
+ static Lifecycle providesLifecycle(LifecycleOwner lifecycleOwner) {
+ return lifecycleOwner.getLifecycle();
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java b/packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java
new file mode 100644
index 000000000000..d16c8c8c59d6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java
@@ -0,0 +1,273 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.dreams.touch;
+
+import static com.android.systemui.dreams.touch.dagger.BouncerSwipeModule.SWIPE_TO_BOUNCER_FLING_ANIMATION_UTILS_CLOSING;
+import static com.android.systemui.dreams.touch.dagger.BouncerSwipeModule.SWIPE_TO_BOUNCER_FLING_ANIMATION_UTILS_OPENING;
+import static com.android.systemui.dreams.touch.dagger.BouncerSwipeModule.SWIPE_TO_BOUNCER_START_REGION;
+
+import android.animation.ValueAnimator;
+import android.util.Log;
+import android.view.GestureDetector;
+import android.view.InputEvent;
+import android.view.MotionEvent;
+import android.view.VelocityTracker;
+
+import com.android.systemui.statusbar.NotificationShadeWindowController;
+import com.android.systemui.statusbar.phone.KeyguardBouncer;
+import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
+import com.android.wm.shell.animation.FlingAnimationUtils;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+
+/**
+ * Monitor for tracking touches on the DreamOverlay to bring up the bouncer.
+ */
+public class BouncerSwipeTouchHandler implements DreamTouchHandler {
+ /**
+ * An interface for creating ValueAnimators.
+ */
+ public interface ValueAnimatorCreator {
+ /**
+ * Creates {@link ValueAnimator}.
+ */
+ ValueAnimator create(float start, float finish);
+ }
+
+ /**
+ * An interface for obtaining VelocityTrackers.
+ */
+ public interface VelocityTrackerFactory {
+ /**
+ * Obtains {@link VelocityTracker}.
+ */
+ VelocityTracker obtain();
+ }
+
+ public static final float FLING_PERCENTAGE_THRESHOLD = 0.5f;
+
+ private static final String TAG = "BouncerSwipeTouchHandler";
+ private final NotificationShadeWindowController mNotificationShadeWindowController;
+ private final float mBouncerZoneScreenPercentage;
+
+ private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
+ private float mCurrentExpansion;
+ private final StatusBar mStatusBar;
+
+ private VelocityTracker mVelocityTracker;
+
+ private final FlingAnimationUtils mFlingAnimationUtils;
+ private final FlingAnimationUtils mFlingAnimationUtilsClosing;
+
+ private Boolean mCapture;
+
+ private TouchSession mTouchSession;
+
+ private ValueAnimatorCreator mValueAnimatorCreator;
+
+ private VelocityTrackerFactory mVelocityTrackerFactory;
+
+ private final GestureDetector.OnGestureListener mOnGestureListener =
+ new GestureDetector.SimpleOnGestureListener() {
+ boolean mTrack;
+ boolean mBouncerPresent;
+
+ @Override
+ public boolean onDown(MotionEvent e) {
+ // We only consider gestures that originate from the lower portion of the
+ // screen.
+ final float displayHeight = mStatusBar.getDisplayHeight();
+
+ mBouncerPresent = mStatusBar.isBouncerShowing();
+
+ // The target zone is either at the top or bottom of the screen, dependent on
+ // whether the bouncer is present.
+ final float zonePercentage =
+ Math.abs(e.getY() - (mBouncerPresent ? 0 : displayHeight))
+ / displayHeight;
+
+ mTrack = zonePercentage < mBouncerZoneScreenPercentage;
+
+ // Never capture onDown. While this might lead to some false positive touches
+ // being sent to other windows/layers, this is necessary to make sure the
+ // proper touch event sequence is received by others in the event we do not
+ // consume the sequence here.
+ return false;
+ }
+
+ @Override
+ public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
+ float distanceY) {
+ // Do not handle scroll gestures if not tracking touch events.
+ if (!mTrack) {
+ return false;
+ }
+
+ if (mCapture == null) {
+ // If the user scrolling favors a vertical direction, begin capturing
+ // scrolls.
+ mCapture = Math.abs(distanceY) > Math.abs(distanceX);
+
+ if (mCapture) {
+ // Since the user is dragging the bouncer up, set scrimmed to false.
+ mStatusBarKeyguardViewManager.showBouncer(false);
+ }
+ }
+
+ if (!mCapture) {
+ return false;
+ }
+
+ // For consistency, we adopt the expansion definition found in the
+ // PanelViewController. In this case, expansion refers to the view above the
+ // bouncer. As that view's expansion shrinks, the bouncer appears. The bouncer
+ // is fully hidden at full expansion (1) and fully visible when fully collapsed
+ // (0).
+ final float screenTravelPercentage =
+ Math.abs((e1.getY() - e2.getY()) / mStatusBar.getDisplayHeight());
+ setPanelExpansion(
+ mBouncerPresent ? screenTravelPercentage : 1 - screenTravelPercentage);
+
+ return true;
+ }
+ };
+
+ private void setPanelExpansion(float expansion) {
+ mCurrentExpansion = expansion;
+ mStatusBarKeyguardViewManager.onPanelExpansionChanged(mCurrentExpansion, false, true);
+ }
+
+ @Inject
+ public BouncerSwipeTouchHandler(
+ StatusBarKeyguardViewManager statusBarKeyguardViewManager,
+ StatusBar statusBar,
+ NotificationShadeWindowController notificationShadeWindowController,
+ ValueAnimatorCreator valueAnimatorCreator,
+ VelocityTrackerFactory velocityTrackerFactory,
+ @Named(SWIPE_TO_BOUNCER_FLING_ANIMATION_UTILS_CLOSING)
+ FlingAnimationUtils flingAnimationUtils,
+ @Named(SWIPE_TO_BOUNCER_FLING_ANIMATION_UTILS_OPENING)
+ FlingAnimationUtils flingAnimationUtilsClosing,
+ @Named(SWIPE_TO_BOUNCER_START_REGION) float swipeRegionPercentage) {
+ mStatusBar = statusBar;
+ mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
+ mNotificationShadeWindowController = notificationShadeWindowController;
+ mBouncerZoneScreenPercentage = swipeRegionPercentage;
+ mFlingAnimationUtils = flingAnimationUtils;
+ mFlingAnimationUtilsClosing = flingAnimationUtilsClosing;
+ mValueAnimatorCreator = valueAnimatorCreator;
+ mVelocityTrackerFactory = velocityTrackerFactory;
+ }
+
+ @Override
+ public void onSessionStart(TouchSession session) {
+ mVelocityTracker = mVelocityTrackerFactory.obtain();
+ mTouchSession = session;
+ mVelocityTracker.clear();
+ mNotificationShadeWindowController.setForcePluginOpen(true, this);
+ session.registerGestureListener(mOnGestureListener);
+ session.registerInputListener(ev -> onMotionEvent(ev));
+
+ }
+
+ @Override
+ public void onSessionEnd(TouchSession session) {
+ mVelocityTracker.recycle();
+ mCapture = null;
+ mNotificationShadeWindowController.setForcePluginOpen(false, this);
+ }
+
+ private void onMotionEvent(InputEvent event) {
+ if (!(event instanceof MotionEvent)) {
+ Log.e(TAG, "non MotionEvent received:" + event);
+ return;
+ }
+
+ final MotionEvent motionEvent = (MotionEvent) event;
+
+ switch(motionEvent.getAction()) {
+ case MotionEvent.ACTION_UP:
+ // If we are not capturing any input, there is no need to consider animating to
+ // finish transition.
+ if (mCapture == null || !mCapture) {
+ break;
+ }
+
+ // We must capture the resulting velocities as resetMonitor() will clear these
+ // values.
+ mVelocityTracker.computeCurrentVelocity(1000);
+ final float verticalVelocity = mVelocityTracker.getYVelocity();
+ final float horizontalVelocity = mVelocityTracker.getXVelocity();
+
+ final float velocityVector =
+ (float) Math.hypot(horizontalVelocity, verticalVelocity);
+
+
+ final float expansion = flingRevealsOverlay(verticalVelocity, velocityVector)
+ ? KeyguardBouncer.EXPANSION_HIDDEN : KeyguardBouncer.EXPANSION_VISIBLE;
+ flingToExpansion(verticalVelocity, expansion);
+
+ if (expansion == KeyguardBouncer.EXPANSION_HIDDEN) {
+ mStatusBarKeyguardViewManager.reset(false);
+ }
+ mTouchSession.pop();
+ break;
+ default:
+ mVelocityTracker.addMovement(motionEvent);
+ break;
+ }
+ }
+
+ private ValueAnimator createExpansionAnimator(float targetExpansion) {
+ final ValueAnimator animator =
+ mValueAnimatorCreator.create(mCurrentExpansion, targetExpansion);
+ animator.addUpdateListener(
+ animation -> {
+ setPanelExpansion((float) animation.getAnimatedValue());
+ });
+ return animator;
+ }
+
+ protected boolean flingRevealsOverlay(float velocity, float velocityVector) {
+ // Fully expand if the user has expanded the bouncer less than halfway or final velocity was
+ // positive, indicating an downward direction.
+ if (Math.abs(velocityVector) < mFlingAnimationUtils.getMinVelocityPxPerSecond()) {
+ return mCurrentExpansion > FLING_PERCENTAGE_THRESHOLD;
+ } else {
+ return velocity > 0;
+ }
+ }
+
+ protected void flingToExpansion(float velocity, float expansion) {
+ final float viewHeight = mStatusBar.getDisplayHeight();
+ final float currentHeight = viewHeight * mCurrentExpansion;
+ final float targetHeight = viewHeight * expansion;
+
+ final ValueAnimator animator = createExpansionAnimator(expansion);
+ if (expansion == KeyguardBouncer.EXPANSION_HIDDEN) {
+ // The animation utils deal in pixel units, rather than expansion height.
+ mFlingAnimationUtils.apply(animator, currentHeight, targetHeight, velocity, viewHeight);
+ } else {
+ mFlingAnimationUtilsClosing.apply(
+ animator, mCurrentExpansion, currentHeight, targetHeight, viewHeight);
+ }
+
+ animator.start();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitor.java b/packages/SystemUI/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitor.java
new file mode 100644
index 000000000000..3e5efb2115a0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitor.java
@@ -0,0 +1,368 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.dreams.touch;
+
+import android.view.GestureDetector;
+import android.view.InputEvent;
+import android.view.MotionEvent;
+
+import androidx.annotation.NonNull;
+import androidx.concurrent.futures.CallbackToFutureAdapter;
+import androidx.lifecycle.DefaultLifecycleObserver;
+import androidx.lifecycle.Lifecycle;
+import androidx.lifecycle.LifecycleObserver;
+import androidx.lifecycle.LifecycleOwner;
+
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.dreams.touch.dagger.InputSessionComponent;
+import com.android.systemui.shared.system.InputChannelCompat;
+
+import com.google.common.util.concurrent.ListenableFuture;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.Executor;
+import java.util.function.Consumer;
+import java.util.stream.Collectors;
+
+import javax.inject.Inject;
+
+/**
+ * {@link DreamOverlayTouchMonitor} is responsible for monitoring touches and gestures over the
+ * dream overlay and redirecting them to a set of listeners. This monitor is in charge of figuring
+ * out when listeners are eligible for receiving touches and filtering the listener pool if
+ * touches are consumed.
+ */
+public class DreamOverlayTouchMonitor {
+ // This executor is used to protect {@code mActiveTouchSessions} from being modified
+ // concurrently. Any operation that adds or removes values should use this executor.
+ private final Executor mExecutor;
+ private final Lifecycle mLifecycle;
+
+ /**
+ * Adds a new {@link TouchSessionImpl} to participate in receiving future touches and gestures.
+ */
+ private ListenableFuture<DreamTouchHandler.TouchSession> push(
+ TouchSessionImpl touchSessionImpl) {
+ return CallbackToFutureAdapter.getFuture(completer -> {
+ mExecutor.execute(() -> {
+ if (!mActiveTouchSessions.remove(touchSessionImpl)) {
+ completer.set(null);
+ return;
+ }
+
+ final TouchSessionImpl touchSession =
+ new TouchSessionImpl(this, touchSessionImpl);
+ mActiveTouchSessions.add(touchSession);
+ completer.set(touchSession);
+ });
+
+ return "DreamOverlayTouchMonitor::push";
+ });
+ }
+
+ /**
+ * Removes a {@link TouchSessionImpl} from receiving further updates.
+ */
+ private ListenableFuture<DreamTouchHandler.TouchSession> pop(
+ TouchSessionImpl touchSessionImpl) {
+ return CallbackToFutureAdapter.getFuture(completer -> {
+ mExecutor.execute(() -> {
+ if (mActiveTouchSessions.remove(touchSessionImpl)) {
+ touchSessionImpl.onRemoved();
+
+ final TouchSessionImpl predecessor = touchSessionImpl.getPredecessor();
+
+ if (predecessor != null) {
+ mActiveTouchSessions.add(predecessor);
+ }
+
+ completer.set(predecessor);
+ }
+ });
+
+ return "DreamOverlayTouchMonitor::pop";
+ });
+ }
+
+ /**
+ * {@link TouchSessionImpl} implements {@link DreamTouchHandler.TouchSession} for
+ * {@link DreamOverlayTouchMonitor}. It enables the monitor to access the associated listeners
+ * and provides the associated client with access to the monitor.
+ */
+ private static class TouchSessionImpl implements DreamTouchHandler.TouchSession {
+ private final HashSet<InputChannelCompat.InputEventListener> mEventListeners =
+ new HashSet<>();
+ private final HashSet<GestureDetector.OnGestureListener> mGestureListeners =
+ new HashSet<>();
+ private final HashSet<Callback> mCallbacks = new HashSet<>();
+
+ private final TouchSessionImpl mPredecessor;
+ private final DreamOverlayTouchMonitor mTouchMonitor;
+
+ TouchSessionImpl(DreamOverlayTouchMonitor touchMonitor, TouchSessionImpl predecessor) {
+ mPredecessor = predecessor;
+ mTouchMonitor = touchMonitor;
+ }
+
+ @Override
+ public void registerCallback(Callback callback) {
+ mCallbacks.add(callback);
+ }
+
+ @Override
+ public boolean registerInputListener(
+ InputChannelCompat.InputEventListener inputEventListener) {
+ return mEventListeners.add(inputEventListener);
+ }
+
+ @Override
+ public boolean registerGestureListener(GestureDetector.OnGestureListener gestureListener) {
+ return mGestureListeners.add(gestureListener);
+ }
+
+ @Override
+ public ListenableFuture<DreamTouchHandler.TouchSession> push() {
+ return mTouchMonitor.push(this);
+ }
+
+ @Override
+ public ListenableFuture<DreamTouchHandler.TouchSession> pop() {
+ return mTouchMonitor.pop(this);
+ }
+
+ /**
+ * Returns the active listeners to receive touch events.
+ */
+ public Collection<InputChannelCompat.InputEventListener> getEventListeners() {
+ return mEventListeners;
+ }
+
+ /**
+ * Returns the active listeners to receive gesture events.
+ */
+ public Collection<GestureDetector.OnGestureListener> getGestureListeners() {
+ return mGestureListeners;
+ }
+
+ /**
+ * Returns the {@link TouchSessionImpl} that preceded this current session. This will
+ * become the new active session when this session is popped.
+ */
+ private TouchSessionImpl getPredecessor() {
+ return mPredecessor;
+ }
+
+ /**
+ * Called by the monitor when this session is removed.
+ */
+ private void onRemoved() {
+ mCallbacks.forEach(callback -> callback.onRemoved());
+ }
+ }
+
+ /**
+ * This lifecycle observer ensures touch monitoring only occurs while the overlay is "resumed".
+ * This concept is mapped over from the equivalent view definition: The {@link LifecycleOwner}
+ * will report the dream is not resumed when it is obscured (from the notification shade being
+ * expanded for example) or not active (such as when it is destroyed).
+ */
+ private final LifecycleObserver mLifecycleObserver = new DefaultLifecycleObserver() {
+ @Override
+ public void onResume(@NonNull LifecycleOwner owner) {
+ startMonitoring();
+ }
+
+ @Override
+ public void onPause(@NonNull LifecycleOwner owner) {
+ stopMonitoring();
+ }
+ };
+
+ /**
+ * When invoked, instantiates a new {@link InputSession} to monitor touch events.
+ */
+ private void startMonitoring() {
+ stopMonitoring();
+ mCurrentInputSession = mInputSessionFactory.create(
+ "dreamOverlay",
+ mInputEventListener,
+ mOnGestureListener,
+ true)
+ .getInputSession();
+ }
+
+ /**
+ * Destroys any active {@link InputSession}.
+ */
+ private void stopMonitoring() {
+ if (mCurrentInputSession == null) {
+ return;
+ }
+
+ mCurrentInputSession.dispose();
+ mCurrentInputSession = null;
+ }
+
+
+ private final HashSet<TouchSessionImpl> mActiveTouchSessions = new HashSet<>();
+ private final Collection<DreamTouchHandler> mHandlers;
+
+ private InputChannelCompat.InputEventListener mInputEventListener =
+ new InputChannelCompat.InputEventListener() {
+ @Override
+ public void onInputEvent(InputEvent ev) {
+ // No Active sessions are receiving touches. Create sessions for each listener
+ if (mActiveTouchSessions.isEmpty()) {
+ for (DreamTouchHandler handler : mHandlers) {
+ final TouchSessionImpl sessionStack =
+ new TouchSessionImpl(DreamOverlayTouchMonitor.this, null);
+ mActiveTouchSessions.add(sessionStack);
+ handler.onSessionStart(sessionStack);
+ }
+ }
+
+ // Find active sessions and invoke on InputEvent.
+ mActiveTouchSessions.stream()
+ .map(touchSessionStack -> touchSessionStack.getEventListeners())
+ .flatMap(Collection::stream)
+ .forEach(inputEventListener -> inputEventListener.onInputEvent(ev));
+ }
+ };
+
+ /**
+ * The {@link Evaluator} interface allows for callers to inspect a listener from the
+ * {@link android.view.GestureDetector.OnGestureListener} set. This helps reduce duplicated
+ * iteration loops over this set.
+ */
+ private interface Evaluator {
+ boolean evaluate(GestureDetector.OnGestureListener listener);
+ }
+
+ private GestureDetector.OnGestureListener mOnGestureListener =
+ new GestureDetector.OnGestureListener() {
+ private boolean evaluate(Evaluator evaluator) {
+ final Set<TouchSessionImpl> consumingSessions = new HashSet<>();
+
+ // When a gesture is consumed, it is assumed that all touches for the current session
+ // should be directed only to those TouchSessions until those sessions are popped. All
+ // non-participating sessions are removed from receiving further updates with
+ // {@link DreamOverlayTouchMonitor#isolate}.
+ final boolean eventConsumed = mActiveTouchSessions.stream()
+ .map(touchSession -> {
+ boolean consume = touchSession.getGestureListeners()
+ .stream()
+ .map(listener -> evaluator.evaluate(listener))
+ .anyMatch(consumed -> consumed);
+
+ if (consume) {
+ consumingSessions.add(touchSession);
+ }
+ return consume;
+ }).anyMatch(consumed -> consumed);
+
+ if (eventConsumed) {
+ DreamOverlayTouchMonitor.this.isolate(consumingSessions);
+ }
+
+ return eventConsumed;
+ }
+
+ // This method is called for gesture events that cannot be consumed.
+ private void observe(Consumer<GestureDetector.OnGestureListener> consumer) {
+ mActiveTouchSessions.stream()
+ .map(touchSession -> touchSession.getGestureListeners())
+ .flatMap(Collection::stream)
+ .forEach(listener -> consumer.accept(listener));
+ }
+
+ @Override
+ public boolean onDown(MotionEvent e) {
+ return evaluate(listener -> listener.onDown(e));
+ }
+
+ @Override
+ public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
+ return evaluate(listener -> listener.onFling(e1, e2, velocityX, velocityY));
+ }
+
+ @Override
+ public void onLongPress(MotionEvent e) {
+ observe(listener -> listener.onLongPress(e));
+ }
+
+ @Override
+ public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
+ return evaluate(listener -> listener.onScroll(e1, e2, distanceX, distanceY));
+ }
+
+ @Override
+ public void onShowPress(MotionEvent e) {
+ observe(listener -> listener.onShowPress(e));
+ }
+
+ @Override
+ public boolean onSingleTapUp(MotionEvent e) {
+ return evaluate(listener -> listener.onSingleTapUp(e));
+ }
+ };
+
+ private InputSessionComponent.Factory mInputSessionFactory;
+ private InputSession mCurrentInputSession;
+
+ /**
+ * Designated constructor for {@link DreamOverlayTouchMonitor}
+ * @param executor This executor will be used for maintaining the active listener list to avoid
+ * concurrent modification.
+ * @param lifecycle {@link DreamOverlayTouchMonitor} will listen to this lifecycle to determine
+ * whether touch monitoring should be active.
+ * @param inputSessionFactory This factory will generate the {@link InputSession} requested by
+ * the monitor. Each session should be unique and valid when
+ * returned.
+ * @param handlers This set represents the {@link DreamTouchHandler} instances that will
+ * participate in touch handling.
+ */
+ @Inject
+ public DreamOverlayTouchMonitor(
+ @Main Executor executor,
+ Lifecycle lifecycle,
+ InputSessionComponent.Factory inputSessionFactory,
+ Set<DreamTouchHandler> handlers) {
+ mHandlers = handlers;
+ mInputSessionFactory = inputSessionFactory;
+ mExecutor = executor;
+ mLifecycle = lifecycle;
+ }
+
+ /**
+ * Initializes the monitor. should only be called once after creation.
+ */
+ public void init() {
+ mLifecycle.addObserver(mLifecycleObserver);
+ }
+
+ private void isolate(Set<TouchSessionImpl> sessions) {
+ Collection<TouchSessionImpl> removedSessions = mActiveTouchSessions.stream()
+ .filter(touchSession -> !sessions.contains(touchSession))
+ .collect(Collectors.toCollection(HashSet::new));
+
+ removedSessions.forEach(touchSession -> touchSession.onRemoved());
+
+ mActiveTouchSessions.removeAll(removedSessions);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/DreamTouchHandler.java b/packages/SystemUI/src/com/android/systemui/dreams/touch/DreamTouchHandler.java
new file mode 100644
index 000000000000..c73ff733854d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/touch/DreamTouchHandler.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.dreams.touch;
+
+import android.view.GestureDetector;
+
+import com.android.systemui.shared.system.InputChannelCompat;
+
+import com.google.common.util.concurrent.ListenableFuture;
+
+/**
+ * The {@link DreamTouchHandler} interface provides a way for dream overlay components to observe
+ * touch events and gestures with the ability to intercept the latter. Touch interaction sequences
+ * are abstracted as sessions. A session represents the time of first
+ * {@code android.view.MotionEvent.ACTION_DOWN} event to the last {@link DreamTouchHandler}
+ * stopping interception of gestures. If no gesture is intercepted, the session continues
+ * indefinitely. {@link DreamTouchHandler} have the ability to create a stack of sessions, which
+ * allows for motion logic to be captured in modal states.
+ */
+public interface DreamTouchHandler {
+ /**
+ * A touch session captures the interaction surface of a {@link DreamTouchHandler}. Clients
+ * register listeners as desired to participate in motion/gesture callbacks.
+ */
+ interface TouchSession {
+ interface Callback {
+ void onRemoved();
+ }
+
+ void registerCallback(Callback callback);
+
+ /**
+ * Adds a input event listener for the given session.
+ * @param inputEventListener
+ */
+ boolean registerInputListener(InputChannelCompat.InputEventListener inputEventListener);
+
+ /**
+ * Adds a gesture listener for the given session.
+ * @param gestureListener
+ */
+ boolean registerGestureListener(GestureDetector.OnGestureListener gestureListener);
+
+ /**
+ * Creates a new {@link TouchSession} that will receive any updates that would have been
+ * directed to this {@link TouchSession}.
+ * @return The future which will return a new {@link TouchSession} that will receive
+ * subsequent events. If the operation fails, {@code null} will be returned.
+ */
+ ListenableFuture<TouchSession> push();
+
+ /**
+ * Explicitly releases this {@link TouchSession}. The registered listeners will no longer
+ * receive any further updates.
+ * @return The future containing the {@link TouchSession} that will receive subsequent
+ * events. This session will be the direct predecessor of the popped session. {@code null}
+ * if the popped {@link TouchSession} was the initial session or has already been popped.
+ */
+ ListenableFuture<TouchSession> pop();
+ }
+
+ /**
+ * Informed a new touch session has begun. The first touch event will be delivered to any
+ * listener registered through
+ * {@link TouchSession#registerInputListener(InputChannelCompat.InputEventListener)} during this
+ * call. If there are no interactions with this touch session after this method returns, it will
+ * be dropped.
+ * @param session
+ */
+ void onSessionStart(TouchSession session);
+
+ /**
+ * Invoked when a session has ended. This will be invoked for every session completion, even
+ * those that are removed through {@link TouchSession#pop()}.
+ * @param session
+ */
+ default void onSessionEnd(TouchSession session) { }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/InputSession.java b/packages/SystemUI/src/com/android/systemui/dreams/touch/InputSession.java
new file mode 100644
index 000000000000..43827573470f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/touch/InputSession.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.dreams.touch;
+
+import static com.android.systemui.dreams.touch.dagger.DreamTouchModule.INPUT_SESSION_NAME;
+import static com.android.systemui.dreams.touch.dagger.DreamTouchModule.PILFER_ON_GESTURE_CONSUME;
+
+import android.os.Looper;
+import android.view.Choreographer;
+import android.view.Display;
+import android.view.GestureDetector;
+import android.view.MotionEvent;
+
+import com.android.systemui.shared.system.InputChannelCompat;
+import com.android.systemui.shared.system.InputMonitorCompat;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+
+/**
+ * {@link InputSession} encapsulates components behind input monitoring and handles their lifecycle.
+ * Sessions are meant to be disposable; actions such as exclusively capturing touch events is modal
+ * and destroying the sessions allows a reset. Additionally, {@link InputSession} is meant to have
+ * a single listener for input and gesture. Any broadcasting must be accomplished elsewhere.
+ */
+public class InputSession {
+ private final InputMonitorCompat mInputMonitor;
+ private final InputChannelCompat.InputEventReceiver mInputEventReceiver;
+ private final GestureDetector mGestureDetector;
+
+ /**
+ * Default session constructor.
+ * @param sessionName The session name that will be applied to the underlying
+ * {@link InputMonitorCompat}.
+ * @param inputEventListener A listener to receive input events.
+ * @param gestureListener A listener to receive gesture events.
+ * @param pilferOnGestureConsume Whether touch events should be pilfered after a gesture has
+ * been consumed.
+ */
+ @Inject
+ public InputSession(@Named(INPUT_SESSION_NAME) String sessionName,
+ InputChannelCompat.InputEventListener inputEventListener,
+ GestureDetector.OnGestureListener gestureListener,
+ @Named(PILFER_ON_GESTURE_CONSUME) boolean pilferOnGestureConsume) {
+ mInputMonitor = new InputMonitorCompat(sessionName, Display.DEFAULT_DISPLAY);
+ mGestureDetector = new GestureDetector(gestureListener);
+
+ mInputEventReceiver = mInputMonitor.getInputReceiver(Looper.getMainLooper(),
+ Choreographer.getInstance(),
+ ev -> {
+ // Process event. Since sometimes input may be a prerequisite for some
+ // gesture logic, process input first.
+ inputEventListener.onInputEvent(ev);
+
+ if (ev instanceof MotionEvent
+ && mGestureDetector.onTouchEvent((MotionEvent) ev)
+ && pilferOnGestureConsume) {
+ mInputMonitor.pilferPointers();
+ }
+ });
+ }
+
+ /**
+ * Destroys the {@link InputSession}, removing any component from listening to future touch
+ * events.
+ */
+ public void dispose() {
+ if (mInputEventReceiver != null) {
+ mInputEventReceiver.dispose();
+ }
+
+ if (mInputMonitor != null) {
+ mInputMonitor.dispose();
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/dagger/BouncerSwipeModule.java b/packages/SystemUI/src/com/android/systemui/dreams/touch/dagger/BouncerSwipeModule.java
new file mode 100644
index 000000000000..b9436f96c74f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/touch/dagger/BouncerSwipeModule.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.dreams.touch.dagger;
+
+import android.animation.ValueAnimator;
+import android.content.res.Resources;
+import android.util.TypedValue;
+import android.view.VelocityTracker;
+
+import com.android.systemui.R;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.dreams.touch.BouncerSwipeTouchHandler;
+import com.android.systemui.dreams.touch.DreamTouchHandler;
+import com.android.systemui.statusbar.phone.PanelViewController;
+import com.android.wm.shell.animation.FlingAnimationUtils;
+
+import javax.inject.Named;
+import javax.inject.Provider;
+
+import dagger.Module;
+import dagger.Provides;
+import dagger.multibindings.IntoSet;
+
+/**
+ * This module captures the components associated with {@link BouncerSwipeTouchHandler}.
+ */
+@Module
+public class BouncerSwipeModule {
+ /**
+ * The region, defined as the percentage of the screen, from which a touch gesture to start
+ * swiping up to the bouncer can occur.
+ */
+ public static final String SWIPE_TO_BOUNCER_START_REGION = "swipe_to_bouncer_start_region";
+
+ /**
+ * The {@link android.view.animation.AnimationUtils} for animating the bouncer closing.
+ */
+ public static final String SWIPE_TO_BOUNCER_FLING_ANIMATION_UTILS_CLOSING =
+ "swipe_to_bouncer_fling_animation_utils_closing";
+
+ /**
+ * The {@link android.view.animation.AnimationUtils} for animating the bouncer opening.
+ */
+ public static final String SWIPE_TO_BOUNCER_FLING_ANIMATION_UTILS_OPENING =
+ "swipe_to_bouncer_fling_animation_utils_opening";
+
+ /**
+ * Provides {@link BouncerSwipeTouchHandler} for inclusion in touch handling over the dream.
+ */
+ @Provides
+ @IntoSet
+ public static DreamTouchHandler providesBouncerSwipeTouchHandler(
+ BouncerSwipeTouchHandler touchHandler) {
+ return touchHandler;
+ }
+
+ /**
+ * Provides {@link android.view.animation.AnimationUtils} for closing.
+ */
+ @Provides
+ @Named(SWIPE_TO_BOUNCER_FLING_ANIMATION_UTILS_CLOSING)
+ public static FlingAnimationUtils providesSwipeToBouncerFlingAnimationUtilsClosing(
+ Provider<FlingAnimationUtils.Builder> flingAnimationUtilsBuilderProvider) {
+ return flingAnimationUtilsBuilderProvider.get()
+ .reset()
+ .setMaxLengthSeconds(PanelViewController.FLING_CLOSING_MAX_LENGTH_SECONDS)
+ .setSpeedUpFactor(PanelViewController.FLING_SPEED_UP_FACTOR)
+ .build();
+ }
+
+ /**
+ * Provides {@link android.view.animation.AnimationUtils} for opening.
+ */
+ @Provides
+ @Named(SWIPE_TO_BOUNCER_FLING_ANIMATION_UTILS_OPENING)
+ public static FlingAnimationUtils providesSwipeToBouncerFlingAnimationUtilsOpening(
+ Provider<FlingAnimationUtils.Builder> flingAnimationUtilsBuilderProvider) {
+ return flingAnimationUtilsBuilderProvider.get()
+ .reset()
+ .setMaxLengthSeconds(PanelViewController.FLING_MAX_LENGTH_SECONDS)
+ .setSpeedUpFactor(PanelViewController.FLING_SPEED_UP_FACTOR)
+ .build();
+ }
+
+ /**
+ * Provides the region to start swipe gestures from.
+ */
+ @Provides
+ @Named(SWIPE_TO_BOUNCER_START_REGION)
+ public static float providesSwipeToBouncerStartRegion(@Main Resources resources) {
+ TypedValue typedValue = new TypedValue();
+ resources.getValue(R.dimen.dream_overlay_bouncer_start_region_screen_percentage,
+ typedValue, true);
+ return typedValue.getFloat();
+ }
+
+ /**
+ * Provides the default {@link BouncerSwipeTouchHandler.ValueAnimatorCreator}, which is simply
+ * a wrapper around {@link ValueAnimator}.
+ */
+ @Provides
+ public static BouncerSwipeTouchHandler.ValueAnimatorCreator providesValueAnimatorCreator() {
+ return (start, finish) -> ValueAnimator.ofFloat(start, finish);
+ }
+
+ /**
+ * Provides the default {@link BouncerSwipeTouchHandler.VelocityTrackerFactory}. which is a
+ * passthrough to {@link android.view.VelocityTracker}.
+ */
+ @Provides
+ public static BouncerSwipeTouchHandler.VelocityTrackerFactory providesVelocityTrackerFactory() {
+ return () -> VelocityTracker.obtain();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/dagger/DreamTouchModule.java b/packages/SystemUI/src/com/android/systemui/dreams/touch/dagger/DreamTouchModule.java
new file mode 100644
index 000000000000..dad0004613f6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/touch/dagger/DreamTouchModule.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.dreams.touch.dagger;
+
+import dagger.Module;
+
+/**
+ * {@link DreamTouchModule} encapsulates dream touch-related components.
+ */
+@Module(includes = {
+ BouncerSwipeModule.class,
+ }, subcomponents = {
+ InputSessionComponent.class,
+})
+public interface DreamTouchModule {
+ String INPUT_SESSION_NAME = "INPUT_SESSION_NAME";
+ String PILFER_ON_GESTURE_CONSUME = "PILFER_ON_GESTURE_CONSUME";
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/dagger/InputSessionComponent.java b/packages/SystemUI/src/com/android/systemui/dreams/touch/dagger/InputSessionComponent.java
new file mode 100644
index 000000000000..ad59a2e2b5c3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/touch/dagger/InputSessionComponent.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.dreams.touch.dagger;
+
+import static com.android.systemui.dreams.touch.dagger.DreamTouchModule.INPUT_SESSION_NAME;
+import static com.android.systemui.dreams.touch.dagger.DreamTouchModule.PILFER_ON_GESTURE_CONSUME;
+
+import android.view.GestureDetector;
+
+import com.android.systemui.dreams.touch.InputSession;
+import com.android.systemui.shared.system.InputChannelCompat;
+
+import javax.inject.Named;
+
+import dagger.BindsInstance;
+import dagger.Subcomponent;
+
+/**
+ * {@link InputSessionComponent} generates {@link InputSession} with specific instances bound for
+ * the session name and whether touches should be pilfered when consumed.
+ */
+@Subcomponent
+public interface InputSessionComponent {
+ /**
+ * Generates {@link InputSessionComponent}.
+ */
+ @Subcomponent.Factory
+ interface Factory {
+ InputSessionComponent create(@Named(INPUT_SESSION_NAME) @BindsInstance String name,
+ @BindsInstance InputChannelCompat.InputEventListener inputEventListener,
+ @BindsInstance GestureDetector.OnGestureListener gestureListener,
+ @Named(PILFER_ON_GESTURE_CONSUME) @BindsInstance boolean pilferOnGestureConsume);
+ }
+
+ /** */
+ InputSession getInputSession();
+}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.java b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
index 97edf3bac4a8..c894b7023d75 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
@@ -112,6 +112,9 @@ public class Flags {
public static final BooleanFlag COMBINED_STATUS_BAR_SIGNAL_ICONS =
new BooleanFlag(601, false);
+ public static final BooleanFlag STATUS_BAR_USER_SWITCHER =
+ new BooleanFlag(602, false);
+
/***************************************/
// 700 - dialer/calls
public static final BooleanFlag ONGOING_CALL_STATUS_BAR_CHIP =
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
index 2ebcd8531128..f0371fc1f0cd 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
@@ -62,7 +62,6 @@ import android.os.RemoteException;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
-import android.os.Vibrator;
import android.provider.Settings;
import android.service.dreams.IDreamManager;
import android.sysprop.TelephonyProperties;
@@ -119,6 +118,7 @@ import com.android.systemui.plugins.GlobalActions.GlobalActionsManager;
import com.android.systemui.plugins.GlobalActionsPanelPlugin;
import com.android.systemui.scrim.ScrimDrawable;
import com.android.systemui.statusbar.NotificationShadeWindowController;
+import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.SystemUIDialog;
import com.android.systemui.statusbar.phone.SystemUIDialogManager;
@@ -327,7 +327,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
TelephonyListenerManager telephonyListenerManager,
GlobalSettings globalSettings,
SecureSettings secureSettings,
- @Nullable Vibrator vibrator,
+ @NonNull VibratorHelper vibrator,
@Main Resources resources,
ConfigurationController configurationController,
KeyguardStateController keyguardStateController,
@@ -397,7 +397,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
mGlobalSettings.registerContentObserver(
Settings.Global.getUriFor(Settings.Global.AIRPLANE_MODE_ON), true,
mAirplaneModeObserver);
- mHasVibrator = vibrator != null && vibrator.hasVibrator();
+ mHasVibrator = vibrator.hasVibrator();
mShowSilentToggle = SHOW_SILENT_TOGGLE && !resources.getBoolean(
R.bool.config_useFixedVolume);
diff --git a/packages/SystemUI/src/com/android/systemui/hdmi/HdmiCecSetMenuLanguageActivity.java b/packages/SystemUI/src/com/android/systemui/hdmi/HdmiCecSetMenuLanguageActivity.java
new file mode 100644
index 000000000000..b304c3ca737a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/hdmi/HdmiCecSetMenuLanguageActivity.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.hdmi;
+
+import android.hardware.hdmi.HdmiControlManager;
+import android.os.Bundle;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.Button;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.android.systemui.R;
+import com.android.systemui.tv.TvBottomSheetActivity;
+
+import javax.inject.Inject;
+
+/**
+ * Confirmation dialog shown when Set Menu Language CEC message was received.
+ */
+public class HdmiCecSetMenuLanguageActivity extends TvBottomSheetActivity
+ implements View.OnClickListener {
+ private static final String TAG = HdmiCecSetMenuLanguageActivity.class.getSimpleName();
+
+ private final HdmiCecSetMenuLanguageHelper mHdmiCecSetMenuLanguageHelper;
+
+ @Inject
+ public HdmiCecSetMenuLanguageActivity(
+ HdmiCecSetMenuLanguageHelper hdmiCecSetMenuLanguageHelper) {
+ mHdmiCecSetMenuLanguageHelper = hdmiCecSetMenuLanguageHelper;
+ }
+
+ @Override
+ public final void onCreate(Bundle b) {
+ super.onCreate(b);
+ getWindow().addPrivateFlags(
+ WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
+ String languageTag = getIntent().getStringExtra(HdmiControlManager.EXTRA_LOCALE);
+ mHdmiCecSetMenuLanguageHelper.setLocale(languageTag);
+ if (mHdmiCecSetMenuLanguageHelper.isLocaleDenylisted()) {
+ finish();
+ }
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ CharSequence title = getString(R.string.hdmi_cec_set_menu_language_title,
+ mHdmiCecSetMenuLanguageHelper.getLocale().getDisplayLanguage());
+ CharSequence text = getString(R.string.hdmi_cec_set_menu_language_description);
+ initUI(title, text);
+ }
+
+ @Override
+ public void onClick(View v) {
+ if (v.getId() == R.id.bottom_sheet_positive_button) {
+ mHdmiCecSetMenuLanguageHelper.acceptLocale();
+ } else {
+ mHdmiCecSetMenuLanguageHelper.declineLocale();
+ }
+ finish();
+ }
+
+ void initUI(CharSequence title, CharSequence text) {
+ TextView titleTextView = findViewById(R.id.bottom_sheet_title);
+ TextView contentTextView = findViewById(R.id.bottom_sheet_body);
+ ImageView icon = findViewById(R.id.bottom_sheet_icon);
+ ImageView secondIcon = findViewById(R.id.bottom_sheet_second_icon);
+ Button okButton = findViewById(R.id.bottom_sheet_positive_button);
+ Button cancelButton = findViewById(R.id.bottom_sheet_negative_button);
+
+ titleTextView.setText(title);
+ contentTextView.setText(text);
+ icon.setImageResource(com.android.internal.R.drawable.ic_settings_language);
+ secondIcon.setVisibility(View.GONE);
+
+ okButton.setText(R.string.hdmi_cec_set_menu_language_accept);
+ okButton.setOnClickListener(this);
+
+ cancelButton.setText(R.string.hdmi_cec_set_menu_language_decline);
+ cancelButton.setOnClickListener(this);
+ cancelButton.requestFocus();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/hdmi/HdmiCecSetMenuLanguageHelper.java b/packages/SystemUI/src/com/android/systemui/hdmi/HdmiCecSetMenuLanguageHelper.java
new file mode 100644
index 000000000000..1f58112c5dc6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/hdmi/HdmiCecSetMenuLanguageHelper.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.hdmi;
+
+import android.provider.Settings;
+
+import com.android.internal.app.LocalePicker;
+import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.util.settings.SecureSettings;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Locale;
+import java.util.concurrent.Executor;
+
+import javax.inject.Inject;
+
+/**
+ * Helper class to separate model and view for system language change initiated by HDMI CEC.
+ */
+@SysUISingleton
+public class HdmiCecSetMenuLanguageHelper {
+ private static final String TAG = HdmiCecSetMenuLanguageHelper.class.getSimpleName();
+ private static final String SEPARATOR = ",";
+
+ private final Executor mBackgroundExecutor;
+ private final SecureSettings mSecureSettings;
+
+ private Locale mLocale;
+ private HashSet<String> mDenylist;
+
+ @Inject
+ public HdmiCecSetMenuLanguageHelper(@Background Executor executor,
+ SecureSettings secureSettings) {
+ mBackgroundExecutor = executor;
+ mSecureSettings = secureSettings;
+ String denylist = mSecureSettings.getString(
+ Settings.Secure.HDMI_CEC_SET_MENU_LANGUAGE_DENYLIST);
+ mDenylist = new HashSet<>(denylist == null
+ ? Collections.EMPTY_SET
+ : Arrays.asList(denylist.split(SEPARATOR)));
+ }
+
+ /**
+ * Set internal locale based on given language tag.
+ */
+ public void setLocale(String languageTag) {
+ mLocale = Locale.forLanguageTag(languageTag);
+ }
+
+ /**
+ * Returns the locale from {@code <Set Menu Language>} CEC message.
+ */
+ public Locale getLocale() {
+ return mLocale;
+ }
+
+ /**
+ * Returns whether the locale from {@code <Set Menu Language>} CEC message was already
+ * denylisted.
+ */
+ public boolean isLocaleDenylisted() {
+ return mDenylist.contains(mLocale.toLanguageTag());
+ }
+
+ /**
+ * Accepts the new locale and updates system language.
+ */
+ public void acceptLocale() {
+ mBackgroundExecutor.execute(() -> LocalePicker.updateLocale(mLocale));
+ }
+
+ /**
+ * Declines the locale and puts it on the denylist.
+ */
+ public void declineLocale() {
+ mDenylist.add(mLocale.toLanguageTag());
+ mSecureSettings.putString(Settings.Secure.HDMI_CEC_SET_MENU_LANGUAGE_DENYLIST,
+ String.join(SEPARATOR, mDenylist));
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 701d1391d271..fd2c6dd3ca36 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -106,6 +106,7 @@ import com.android.systemui.animation.Interpolators;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.dagger.qualifiers.UiBackground;
+import com.android.systemui.dreams.DreamOverlayStateController;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.keyguard.dagger.KeyguardModule;
import com.android.systemui.navigationbar.NavigationModeController;
@@ -138,7 +139,7 @@ import dagger.Lazy;
* state of the keyguard, power management events that effect whether the keyguard
* should be shown or reset, callbacks to the phone window manager to notify
* it of when the keyguard is showing, and events from the keyguard view itself
- * stating that the keyguard was succesfully unlocked.
+ * stating that the keyguard was successfully unlocked.
*
* Note that the keyguard view is shown when the screen is off (as appropriate)
* so that once the screen comes on, it will be ready immediately.
@@ -152,15 +153,15 @@ import dagger.Lazy;
* - the keyguard is showing
*
* Example external events that translate to keyguard view changes:
- * - screen turned off -> reset the keyguard, and show it so it will be ready
+ * - screen turned off -> reset the keyguard, and show it, so it will be ready
* next time the screen turns on
* - keyboard is slid open -> if the keyguard is not secure, hide it
*
* Events from the keyguard view:
- * - user succesfully unlocked keyguard -> hide keyguard view, and no longer
+ * - user successfully unlocked keyguard -> hide keyguard view, and no longer
* restrict input events.
*
- * Note: in addition to normal power managment events that effect the state of
+ * Note: in addition to normal power management events that effect the state of
* whether the keyguard should be showing, external apps and services may request
* that the keyguard be disabled via {@link #setKeyguardEnabled(boolean)}. When
* false, this will override all other conditions for turning on the keyguard.
@@ -224,7 +225,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
/**
* How long we'll wait for the {@link ViewMediatorCallback#keyguardDoneDrawing()}
* callback before unblocking a call to {@link #setKeyguardEnabled(boolean)}
- * that is reenabling the keyguard.
+ * that is re-enabling the keyguard.
*/
private static final int KEYGUARD_DONE_DRAWING_TIMEOUT_MS = 2000;
@@ -233,6 +234,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
* keyguard to show even if it is disabled for the current user.
*/
public static final String OPTION_FORCE_SHOW = "force_show";
+ private final DreamOverlayStateController mDreamOverlayStateController;
/** The stream type that the lock sounds are tied to. */
private int mUiSoundsStreamType;
@@ -273,14 +275,14 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
// these are protected by synchronized (this)
/**
- * External apps (like the phone app) can tell us to disable the keygaurd.
+ * External apps (like the phone app) can tell us to disable the keyguard.
*/
private boolean mExternallyEnabled = true;
/**
* Remember if an external call to {@link #setKeyguardEnabled} with value
* false caused us to hide the keyguard, so that we need to reshow it once
- * the keygaurd is reenabled with another call with value true.
+ * the keyguard is re-enabled with another call with value true.
*/
private boolean mNeedToReshowWhenReenabled = false;
@@ -291,6 +293,9 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
// AOD is enabled and status bar is in AOD state.
private boolean mAodShowing;
+ // Dream overlay is visible.
+ private boolean mDreamOverlayShowing;
+
/** Cached value of #isInputRestricted */
private boolean mInputRestricted;
@@ -304,7 +309,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
private int mDelayedShowingSequence;
/**
- * Simiar to {@link #mDelayedProfileShowingSequence}, but it is for profile case.
+ * Similar to {@link #mDelayedProfileShowingSequence}, but it is for profile case.
*/
private int mDelayedProfileShowingSequence;
@@ -341,7 +346,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
private String mPhoneState = TelephonyManager.EXTRA_STATE_IDLE;
/**
- * Whether a hide is pending an we are just waiting for #startKeyguardExitAnimation to be
+ * Whether a hide is pending and we are just waiting for #startKeyguardExitAnimation to be
* called.
* */
private boolean mHiding;
@@ -355,7 +360,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
| Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);
/**
- * {@link #setKeyguardEnabled} waits on this condition when it reenables
+ * {@link #setKeyguardEnabled} waits on this condition when it re-enables
* the keyguard.
*/
private boolean mWaitingUntilKeyguardVisible = false;
@@ -470,6 +475,14 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
}
};
+ private final DreamOverlayStateController.Callback mDreamOverlayStateCallback =
+ new DreamOverlayStateController.Callback() {
+ @Override
+ public void onStateChanged() {
+ mDreamOverlayShowing = mDreamOverlayStateController.isOverlayActive();
+ }
+ };
+
KeyguardUpdateMonitorCallback mUpdateCallback = new KeyguardUpdateMonitorCallback() {
@Override
@@ -494,7 +507,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
synchronized (KeyguardViewMediator.this) {
resetKeyguardDonePendingLocked();
if (mLockPatternUtils.isLockScreenDisabled(userId)) {
- // If we switching to a user that has keyguard disabled, dismiss keyguard.
+ // If we are switching to a user that has keyguard disabled, dismiss keyguard.
dismiss(null /* callback */, null /* message */);
} else {
resetStateLocked();
@@ -508,7 +521,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
if (DEBUG) Log.d(TAG, String.format("onUserSwitchComplete %d", userId));
if (userId != UserHandle.USER_SYSTEM) {
UserInfo info = UserManager.get(mContext).getUserInfo(userId);
- // Don't try to dismiss if the user has Pin/Patter/Password set
+ // Don't try to dismiss if the user has Pin/Pattern/Password set
if (info == null || mLockPatternUtils.isSecure(userId)) {
return;
} else if (info.isGuest() || info.isDemo()) {
@@ -836,6 +849,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
Lazy<NotificationShadeDepthController> notificationShadeDepthController,
ScreenOnCoordinator screenOnCoordinator,
InteractionJankMonitor interactionJankMonitor,
+ DreamOverlayStateController dreamOverlayStateController,
Lazy<NotificationShadeWindowController> notificationShadeWindowControllerLazy) {
super(context);
mFalsingCollector = falsingCollector;
@@ -875,6 +889,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
mKeyguardUnlockAnimationControllerLazy = keyguardUnlockAnimationControllerLazy;
mScreenOffAnimationController = screenOffAnimationController;
mInteractionJankMonitor = interactionJankMonitor;
+ mDreamOverlayStateController = dreamOverlayStateController;
}
public void userActivity() {
@@ -980,6 +995,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
mSystemReady = true;
doKeyguardLocked(null);
mUpdateMonitor.registerCallback(mUpdateCallback);
+ mDreamOverlayStateController.addCallback(mDreamOverlayStateCallback);
}
// Most services aren't available until the system reaches the ready state, so we
// send it here when the device first boots.
@@ -1041,7 +1057,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
mUpdateMonitor.dispatchStartedGoingToSleep(offReason);
- // Reset keyguard going away state so we can start listening for fingerprint. We
+ // Reset keyguard going away state, so we can start listening for fingerprint. We
// explicitly DO NOT want to call
// mKeyguardViewControllerLazy.get().setKeyguardGoingAwayState(false)
// here, since that will mess with the device lock state.
@@ -1121,9 +1137,9 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
}
private long getLockTimeout(int userId) {
- // if the screen turned off because of timeout or the user hit the power button
+ // if the screen turned off because of timeout or the user hit the power button,
// and we don't need to lock immediately, set an alarm
- // to enable it a little bit later (i.e, give the user a chance
+ // to enable it a bit later (i.e, give the user a chance
// to turn the screen back on within a certain window without
// having to unlock the screen)
final ContentResolver cr = mContext.getContentResolver();
@@ -1217,7 +1233,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
}
/**
- * Let's us know when the device is waking up.
+ * It will let us know when the device is waking up.
*/
public void onStartedWakingUp(boolean cameraGestureTriggered) {
Trace.beginSection("KeyguardViewMediator#onStartedWakingUp");
@@ -1299,7 +1315,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
if (mExitSecureCallback != null) {
if (DEBUG) Log.d(TAG, "in process of verifyUnlock request, ignoring");
// we're in the process of handling a request to verify the user
- // can get past the keyguard. ignore extraneous requests to disable / reenable
+ // can get past the keyguard. ignore extraneous requests to disable / re-enable
return;
}
@@ -1310,7 +1326,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
updateInputRestrictedLocked();
hideLocked();
} else if (enabled && mNeedToReshowWhenReenabled) {
- // reenabled after previously hidden, reshow
+ // re-enabled after previously hidden, reshow
if (DEBUG) Log.d(TAG, "previously hidden, reshowing, reenabling "
+ "status bar expansion");
mNeedToReshowWhenReenabled = false;
@@ -1328,8 +1344,8 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
} else {
showLocked(null);
- // block until we know the keygaurd is done drawing (and post a message
- // to unblock us after a timeout so we don't risk blocking too long
+ // block until we know the keyguard is done drawing (and post a message
+ // to unblock us after a timeout, so we don't risk blocking too long
// and causing an ANR).
mWaitingUntilKeyguardVisible = true;
mHandler.sendEmptyMessageDelayed(KEYGUARD_DONE_DRAWING, KEYGUARD_DONE_DRAWING_TIMEOUT_MS);
@@ -1917,7 +1933,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
mExitSecureCallback = null;
- // after succesfully exiting securely, no need to reshow
+ // after successfully exiting securely, no need to reshow
// the keyguard when they've released the lock
mExternallyEnabled = true;
mNeedToReshowWhenReenabled = false;
@@ -1992,7 +2008,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
if (mAudioManager.isStreamMute(mUiSoundsStreamType)) return;
int id = mLockSounds.play(soundId,
- mLockSoundVolume, mLockSoundVolume, 1/*priortiy*/, 0/*loop*/, 1.0f/*rate*/);
+ mLockSoundVolume, mLockSoundVolume, 1/*priority*/, 0/*loop*/, 1.0f/*rate*/);
synchronized (this) {
mLockSoundStreamId = id;
}
@@ -2078,7 +2094,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
|| mScreenOnCoordinator.getWakeAndUnlocking()
&& mWallpaperSupportsAmbientMode) {
// When the wallpaper supports ambient mode, the scrim isn't fully opaque during
- // wake and unlock and we should fade in the app on top of the wallpaper
+ // wake and unlock, and we should fade in the app on top of the wallpaper
flags |= WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_TO_SHADE;
}
if (mKeyguardViewControllerLazy.get().isUnlockWithWallpaper()) {
@@ -2123,7 +2139,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
Trace.beginSection("KeyguardViewMediator#handleHide");
// It's possible that the device was unlocked in a dream state. It's time to wake up.
- if (mAodShowing) {
+ if (mAodShowing || mDreamOverlayShowing) {
PowerManager pm = mContext.getSystemService(PowerManager.class);
pm.wakeUp(SystemClock.uptimeMillis(), PowerManager.WAKE_REASON_GESTURE,
"com.android.systemui:BOUNCER_DOZING");
@@ -2167,15 +2183,15 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
synchronized (KeyguardViewMediator.this) {
// Tell ActivityManager that we canceled the keyguard animation if
- // handleStartKeyguardExitAnimation was called but we're not hiding the keyguard, unless
- // we're animating the surface behind the keyguard and will be hiding the keyguard
- // shortly.
+ // handleStartKeyguardExitAnimation was called, but we're not hiding the keyguard,
+ // unless we're animating the surface behind the keyguard and will be hiding the
+ // keyguard shortly.
if (!mHiding
&& !mSurfaceBehindRemoteAnimationRequested
&& !mKeyguardStateController.isFlingingToDismissKeyguardDuringSwipeGesture()) {
if (finishedCallback != null) {
// There will not execute animation, send a finish callback to ensure the remote
- // animation won't hanging there.
+ // animation won't hang there.
try {
finishedCallback.onAnimationFinished();
} catch (RemoteException e) {
@@ -2192,7 +2208,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
if (mScreenOnCoordinator.getWakeAndUnlocking()) {
// Hack level over 9000: To speed up wake-and-unlock sequence, force it to report
- // the next draw from here so we don't have to wait for window manager to signal
+ // the next draw from here, so we don't have to wait for window manager to signal
// this to our ViewRootImpl.
mKeyguardViewControllerLazy.get().getViewRootImpl().setReportNextDraw();
mScreenOnCoordinator.setWakeAndUnlocking(false);
@@ -2344,7 +2360,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
}
/**
- * Called if the keyguard exit animation has been cancelled and we should dismiss to the
+ * Called if the keyguard exit animation has been cancelled, and we should dismiss to the
* keyguard.
*
* This can happen due to the system cancelling the RemoteAnimation (due to a timeout, a new
@@ -2595,7 +2611,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
}
/**
- * Notifies to System UI that the activity behind has now been drawn and it's safe to remove
+ * Notifies to System UI that the activity behind has now been drawn, and it's safe to remove
* the wallpaper and keyguard flag, and WindowManager has started running keyguard exit
* animation.
*
@@ -2609,7 +2625,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
}
/**
- * Notifies to System UI that the activity behind has now been drawn and it's safe to remove
+ * Notifies to System UI that the activity behind has now been drawn, and it's safe to remove
* the wallpaper and keyguard flag, and System UI should start running keyguard exit animation.
*
* @param apps The list of apps to animate.
@@ -2625,7 +2641,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
}
/**
- * Notifies to System UI that the activity behind has now been drawn and it's safe to remove
+ * Notifies to System UI that the activity behind has now been drawn, and it's safe to remove
* the wallpaper and keyguard flag, and start running keyguard exit animation.
*
* @param startTime the start time of the animation in uptime milliseconds. Deprecated.
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
index f14d13093620..b49b49cbbb6d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
@@ -37,6 +37,7 @@ import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.classifier.FalsingModule;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.UiBackground;
+import com.android.systemui.dreams.DreamOverlayStateController;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.keyguard.DismissCallbackRegistry;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
@@ -100,6 +101,7 @@ public class KeyguardModule {
Lazy<NotificationShadeDepthController> notificationShadeDepthController,
ScreenOnCoordinator screenOnCoordinator,
InteractionJankMonitor interactionJankMonitor,
+ DreamOverlayStateController dreamOverlayStateController,
Lazy<NotificationShadeWindowController> notificationShadeWindowController) {
return new KeyguardViewMediator(
context,
@@ -125,6 +127,7 @@ public class KeyguardModule {
notificationShadeDepthController,
screenOnCoordinator,
interactionJankMonitor,
+ dreamOverlayStateController,
notificationShadeWindowController
);
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
index c8cd43287c99..64ebe568c790 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
@@ -31,6 +31,7 @@ import androidx.annotation.VisibleForTesting
import com.android.systemui.R
import com.android.systemui.animation.Interpolators
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dreams.DreamOverlayStateController
import com.android.systemui.keyguard.WakefulnessLifecycle
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.CrossFadeHelper
@@ -82,7 +83,8 @@ class MediaHierarchyManager @Inject constructor(
private val notifLockscreenUserManager: NotificationLockscreenUserManager,
configurationController: ConfigurationController,
wakefulnessLifecycle: WakefulnessLifecycle,
- private val statusBarKeyguardViewManager: StatusBarKeyguardViewManager
+ private val statusBarKeyguardViewManager: StatusBarKeyguardViewManager,
+ private val dreamOverlayStateController: DreamOverlayStateController
) {
/**
@@ -167,7 +169,7 @@ class MediaHierarchyManager @Inject constructor(
})
}
- private val mediaHosts = arrayOfNulls<MediaHost>(LOCATION_LOCKSCREEN + 1)
+ private val mediaHosts = arrayOfNulls<MediaHost>(LOCATION_DREAM_OVERLAY + 1)
/**
* The last location where this view was at before going to the desired location. This is
* useful for guided transitions.
@@ -349,6 +351,17 @@ class MediaHierarchyManager @Inject constructor(
}
/**
+ * Is the doze animation currently Running
+ */
+ private var dreamOverlayActive: Boolean = false
+ private set(value) {
+ if (field != value) {
+ field = value
+ updateDesiredLocation(forceNoAnimation = true)
+ }
+ }
+
+ /**
* The current cross fade progress. 0.5f means it's just switching
* between the start and the end location and the content is fully faded, while 0.75f means
* that we're halfway faded in again in the target state.
@@ -444,6 +457,12 @@ class MediaHierarchyManager @Inject constructor(
}
})
+ dreamOverlayStateController.addCallback(object : DreamOverlayStateController.Callback {
+ override fun onStateChanged() {
+ dreamOverlayStateController.isOverlayActive.also { dreamOverlayActive = it }
+ }
+ })
+
wakefulnessLifecycle.addObserver(object : WakefulnessLifecycle.Observer {
override fun onFinishedGoingToSleep() {
goingToSleep = false
@@ -940,6 +959,7 @@ class MediaHierarchyManager @Inject constructor(
statusbarState == StatusBarState.FULLSCREEN_USER_SWITCHER))
val allowedOnLockscreen = notifLockscreenUserManager.shouldShowLockscreenNotifications()
val location = when {
+ dreamOverlayActive -> LOCATION_DREAM_OVERLAY
(qsExpansion > 0.0f || inSplitShade) && !onLockscreen -> LOCATION_QS
qsExpansion > 0.4f && onLockscreen -> LOCATION_QS
!hasActiveMedia -> LOCATION_QS
@@ -1035,6 +1055,11 @@ class MediaHierarchyManager @Inject constructor(
const val LOCATION_LOCKSCREEN = 2
/**
+ * Attached on the dream overlay
+ */
+ const val LOCATION_DREAM_OVERLAY = 3
+
+ /**
* Attached at the root of the hierarchy in an overlay
*/
const val IN_OVERLAY = -1000
diff --git a/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java b/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java
index 4baef3aef309..2bc910e4a21a 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java
@@ -25,6 +25,7 @@ import com.android.systemui.media.MediaDataManager;
import com.android.systemui.media.MediaHierarchyManager;
import com.android.systemui.media.MediaHost;
import com.android.systemui.media.MediaHostStatesManager;
+import com.android.systemui.media.dream.dagger.MediaComplicationComponent;
import com.android.systemui.media.taptotransfer.MediaTttCommandLineHelper;
import com.android.systemui.media.taptotransfer.MediaTttFlags;
import com.android.systemui.media.taptotransfer.receiver.MediaTttChipControllerReceiver;
@@ -43,11 +44,14 @@ import dagger.multibindings.ClassKey;
import dagger.multibindings.IntoMap;
/** Dagger module for the media package. */
-@Module
+@Module(subcomponents = {
+ MediaComplicationComponent.class,
+})
public interface MediaModule {
String QS_PANEL = "media_qs_panel";
String QUICK_QS_PANEL = "media_quick_qs_panel";
String KEYGUARD = "media_keyguard";
+ String DREAM = "dream";
/** */
@Provides
@@ -82,6 +86,16 @@ public interface MediaModule {
/** */
@Provides
@SysUISingleton
+ @Named(DREAM)
+ static MediaHost providesDreamMediaHost(MediaHost.MediaHostStateHolder stateHolder,
+ MediaHierarchyManager hierarchyManager, MediaDataManager dataManager,
+ MediaHostStatesManager statesManager) {
+ return new MediaHost(stateHolder, hierarchyManager, dataManager, statesManager);
+ }
+
+ /** */
+ @Provides
+ @SysUISingleton
static Optional<MediaTttChipControllerSender> providesMediaTttChipControllerSender(
MediaTttFlags mediaTttFlags,
Context context,
diff --git a/packages/SystemUI/src/com/android/systemui/media/dream/MediaComplicationViewController.java b/packages/SystemUI/src/com/android/systemui/media/dream/MediaComplicationViewController.java
new file mode 100644
index 000000000000..65c5bc76f3c5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/dream/MediaComplicationViewController.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.media.dream;
+
+import static com.android.systemui.media.dagger.MediaModule.DREAM;
+import static com.android.systemui.media.dream.dagger.MediaComplicationComponent.MediaComplicationModule.MEDIA_COMPLICATION_CONTAINER;
+
+import android.widget.FrameLayout;
+
+import com.android.systemui.media.MediaHierarchyManager;
+import com.android.systemui.media.MediaHost;
+import com.android.systemui.media.MediaHostState;
+import com.android.systemui.util.ViewController;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+
+/**
+ * {@link MediaComplicationViewController} handles connecting the
+ * {@link com.android.systemui.dreams.complication.Complication} view to the {@link MediaHost}.
+ */
+public class MediaComplicationViewController extends ViewController<FrameLayout> {
+ private final MediaHost mMediaHost;
+
+ @Inject
+ public MediaComplicationViewController(
+ @Named(MEDIA_COMPLICATION_CONTAINER) FrameLayout view,
+ @Named(DREAM) MediaHost mediaHost) {
+ super(view);
+ mMediaHost = mediaHost;
+ }
+
+ @Override
+ protected void onInit() {
+ super.onInit();
+ mMediaHost.setExpansion(MediaHostState.COLLAPSED);
+ mMediaHost.setShowsOnlyActiveMedia(true);
+ mMediaHost.setFalsingProtectionNeeded(true);
+ mMediaHost.init(MediaHierarchyManager.LOCATION_DREAM_OVERLAY);
+ }
+
+ @Override
+ protected void onViewAttached() {
+ mMediaHost.hostView.setLayoutParams(new FrameLayout.LayoutParams(
+ FrameLayout.LayoutParams.MATCH_PARENT,
+ FrameLayout.LayoutParams.WRAP_CONTENT));
+ mView.addView(mMediaHost.hostView);
+ }
+
+ @Override
+ protected void onViewDetached() {
+ mView.removeView(mMediaHost.hostView);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/dream/MediaDreamComplication.java b/packages/SystemUI/src/com/android/systemui/media/dream/MediaDreamComplication.java
new file mode 100644
index 000000000000..2c35db337cda
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/dream/MediaDreamComplication.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.media.dream;
+
+import com.android.systemui.dreams.complication.Complication;
+import com.android.systemui.dreams.complication.ComplicationViewModel;
+import com.android.systemui.media.dream.dagger.MediaComplicationComponent;
+
+import javax.inject.Inject;
+
+/**
+ * Media control complication for dream overlay.
+ */
+public class MediaDreamComplication implements Complication {
+ MediaComplicationComponent.Factory mComponentFactory;
+
+ /**
+ * Default constructor for {@link MediaDreamComplication}.
+ */
+ @Inject
+ public MediaDreamComplication(MediaComplicationComponent.Factory componentFactory) {
+ mComponentFactory = componentFactory;
+ }
+
+ @Override
+ public ViewHolder createView(ComplicationViewModel model) {
+ return mComponentFactory.create().getViewHolder();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/dream/MediaDreamSentinel.java b/packages/SystemUI/src/com/android/systemui/media/dream/MediaDreamSentinel.java
new file mode 100644
index 000000000000..8934cd1085b8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/dream/MediaDreamSentinel.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.media.dream;
+
+import android.content.Context;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.systemui.CoreStartable;
+import com.android.systemui.dreams.DreamOverlayStateController;
+import com.android.systemui.media.MediaData;
+import com.android.systemui.media.MediaDataManager;
+import com.android.systemui.media.SmartspaceMediaData;
+
+import javax.inject.Inject;
+
+/**
+ * {@link MediaDreamSentinel} is responsible for tracking media state and registering/unregistering
+ * the media complication as appropriate
+ */
+public class MediaDreamSentinel extends CoreStartable {
+ private MediaDataManager.Listener mListener = new MediaDataManager.Listener() {
+ private boolean mAdded;
+ @Override
+ public void onSmartspaceMediaDataRemoved(@NonNull String key, boolean immediately) {
+ }
+
+ @Override
+ public void onMediaDataRemoved(@NonNull String key) {
+ if (!mAdded) {
+ return;
+ }
+
+ if (mMediaDataManager.hasActiveMedia()) {
+ return;
+ }
+
+ mAdded = false;
+ mDreamOverlayStateController.removeComplication(mComplication);
+ }
+
+ @Override
+ public void onSmartspaceMediaDataLoaded(@NonNull String key,
+ @NonNull SmartspaceMediaData data, boolean shouldPrioritize,
+ boolean isSsReactivated) {
+ }
+
+ @Override
+ public void onMediaDataLoaded(@NonNull String key, @Nullable String oldKey,
+ @NonNull MediaData data, boolean immediately, int receivedSmartspaceCardLatency) {
+ if (mAdded) {
+ return;
+ }
+
+ if (!mMediaDataManager.hasActiveMedia()) {
+ return;
+ }
+
+ mAdded = true;
+ mDreamOverlayStateController.addComplication(mComplication);
+ }
+ };
+
+ private final MediaDataManager mMediaDataManager;
+ private final DreamOverlayStateController mDreamOverlayStateController;
+ private final MediaDreamComplication mComplication;
+
+ @Inject
+ public MediaDreamSentinel(Context context, MediaDataManager mediaDataManager,
+ DreamOverlayStateController dreamOverlayStateController,
+ MediaDreamComplication complication) {
+ super(context);
+ mMediaDataManager = mediaDataManager;
+ mDreamOverlayStateController = dreamOverlayStateController;
+ mComplication = complication;
+ }
+
+ @Override
+ public void start() {
+ mMediaDataManager.addListener(mListener);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/dream/MediaViewHolder.java b/packages/SystemUI/src/com/android/systemui/media/dream/MediaViewHolder.java
new file mode 100644
index 000000000000..128a38c639be
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/dream/MediaViewHolder.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.media.dream;
+
+import static com.android.systemui.media.dream.dagger.MediaComplicationComponent.MediaComplicationModule.MEDIA_COMPLICATION_CONTAINER;
+import static com.android.systemui.media.dream.dagger.MediaComplicationComponent.MediaComplicationModule.MEDIA_COMPLICATION_LAYOUT_PARAMS;
+
+import android.view.View;
+import android.widget.FrameLayout;
+
+import com.android.systemui.dreams.complication.Complication;
+import com.android.systemui.dreams.complication.ComplicationLayoutParams;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+
+/**
+ * {@link Complication.ViewHolder} implementation for media control.
+ */
+public class MediaViewHolder implements Complication.ViewHolder {
+ private final FrameLayout mContainer;
+ private final MediaComplicationViewController mViewController;
+ private final ComplicationLayoutParams mLayoutParams;
+
+ @Inject
+ MediaViewHolder(@Named(MEDIA_COMPLICATION_CONTAINER) FrameLayout container,
+ MediaComplicationViewController controller,
+ @Named(MEDIA_COMPLICATION_LAYOUT_PARAMS) ComplicationLayoutParams layoutParams) {
+ mContainer = container;
+ mViewController = controller;
+ mViewController.init();
+ mLayoutParams = layoutParams;
+ }
+
+ @Override
+ public View getView() {
+ return mContainer;
+ }
+
+ @Override
+ public ComplicationLayoutParams getLayoutParams() {
+ return mLayoutParams;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/dream/dagger/MediaComplicationComponent.java b/packages/SystemUI/src/com/android/systemui/media/dream/dagger/MediaComplicationComponent.java
new file mode 100644
index 000000000000..3372899b8fd7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/dream/dagger/MediaComplicationComponent.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.media.dream.dagger;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import android.content.Context;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+
+import com.android.systemui.dreams.complication.ComplicationLayoutParams;
+import com.android.systemui.media.dream.MediaViewHolder;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+
+import javax.inject.Named;
+import javax.inject.Scope;
+
+import dagger.Module;
+import dagger.Provides;
+import dagger.Subcomponent;
+
+/**
+ * {@link MediaComplicationComponent} is responsible for generating dependencies surrounding the
+ * media {@link com.android.systemui.dreams.complication.Complication}, such as view controllers
+ * and layout details.
+ */
+@Subcomponent(modules = {
+ MediaComplicationComponent.MediaComplicationModule.class,
+})
+@MediaComplicationComponent.MediaComplicationScope
+public interface MediaComplicationComponent {
+ @Documented
+ @Retention(RUNTIME)
+ @Scope
+ @interface MediaComplicationScope {}
+
+ /**
+ * Generates {@link MediaComplicationComponent}.
+ */
+ @Subcomponent.Factory
+ interface Factory {
+ MediaComplicationComponent create();
+ }
+
+ /**
+ * Creates {@link MediaViewHolder}.
+ */
+ MediaViewHolder getViewHolder();
+
+ /**
+ * Scoped values for {@link MediaComplicationComponent}.
+ */
+ @Module
+ interface MediaComplicationModule {
+ String MEDIA_COMPLICATION_CONTAINER = "media_complication_container";
+ String MEDIA_COMPLICATION_LAYOUT_PARAMS = "media_complication_layout_params";
+
+ /**
+ * Provides the complication view.
+ */
+ @Provides
+ @MediaComplicationScope
+ @Named(MEDIA_COMPLICATION_CONTAINER)
+ static FrameLayout provideComplicationContainer(Context context) {
+ return new FrameLayout(context);
+ }
+
+ /**
+ * Provides the layout parameters for the complication view.
+ */
+ @Provides
+ @MediaComplicationScope
+ @Named(MEDIA_COMPLICATION_LAYOUT_PARAMS)
+ static ComplicationLayoutParams provideLayoutParams() {
+ return new ComplicationLayoutParams(0,
+ ViewGroup.LayoutParams.WRAP_CONTENT,
+ ComplicationLayoutParams.POSITION_BOTTOM
+ | ComplicationLayoutParams.POSITION_END,
+ ComplicationLayoutParams.DIRECTION_UP,
+ 0,
+ true);
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
index 441e79a97521..ec15b2469358 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
@@ -31,6 +31,7 @@ import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_B
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_HOME_DISABLED;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SHOWING;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SWITCHER_SHOWING;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IMMERSIVE_MODE;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NAV_BAR_HIDDEN;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING;
@@ -304,6 +305,7 @@ public class TaskbarDelegate implements CommandQueue.Callbacks,
allowSystemGestureIgnoringBarVisibility())
.setFlag(SYSUI_STATE_SCREEN_PINNING,
ActivityManagerWrapper.getInstance().isScreenPinningActive())
+ .setFlag(SYSUI_STATE_IMMERSIVE_MODE, isImmersiveMode())
.commitUpdate(mDisplayId);
}
@@ -445,6 +447,10 @@ public class TaskbarDelegate implements CommandQueue.Callbacks,
return mBehavior != BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
}
+ private boolean isImmersiveMode() {
+ return mBehavior == BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
+ }
+
@Override
public void onConfigurationChanged(Configuration configuration) {
mEdgeBackGestureHandler.onConfigurationChanged(configuration);
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/DeadZone.java b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/DeadZone.java
index 7e5b5548237b..7fb58f0d8fc6 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/DeadZone.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/DeadZone.java
@@ -20,6 +20,7 @@ import android.animation.ObjectAnimator;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.os.SystemClock;
+import android.util.FloatProperty;
import android.util.Slog;
import android.view.MotionEvent;
import android.view.Surface;
@@ -44,6 +45,20 @@ public class DeadZone {
public static final int VERTICAL = 1; // Consume taps along the left edge.
private static final boolean CHATTY = true; // print to logcat when we eat a click
+
+ private static final FloatProperty<DeadZone> FLASH_PROPERTY =
+ new FloatProperty<DeadZone>("DeadZoneFlash") {
+ @Override
+ public void setValue(DeadZone object, float value) {
+ object.setFlash(value);
+ }
+
+ @Override
+ public Float get(DeadZone object) {
+ return object.getFlash();
+ }
+ };
+
private final NavigationBarController mNavBarController;
private final NavigationBarView mNavigationBarView;
@@ -63,7 +78,7 @@ public class DeadZone {
private final Runnable mDebugFlash = new Runnable() {
@Override
public void run() {
- ObjectAnimator.ofFloat(DeadZone.this, "flash", 1f, 0f).setDuration(150).start();
+ ObjectAnimator.ofFloat(DeadZone.this, FLASH_PROPERTY, 1f, 0f).setDuration(150).start();
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
index 4f4bd1e86e7c..be45a62eeba6 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
@@ -104,7 +104,8 @@ public class EdgeBackGestureHandler extends CurrentUserTracker
private static final int MAX_NUM_LOGGED_PREDICTIONS = 10;
private static final int MAX_NUM_LOGGED_GESTURES = 10;
- static final boolean DEBUG_MISSING_GESTURE = false;
+ // Temporary log until b/202433017 is resolved
+ static final boolean DEBUG_MISSING_GESTURE = true;
static final String DEBUG_MISSING_GESTURE_TAG = "NoBackGesture";
private ISystemGestureExclusionListener mGestureExclusionListener =
@@ -318,7 +319,11 @@ public class EdgeBackGestureHandler extends CurrentUserTracker
String recentsPackageName = recentsComponentName.getPackageName();
PackageManager manager = context.getPackageManager();
try {
- Resources resources = manager.getResourcesForApplication(recentsPackageName);
+ Resources resources = manager.getResourcesForApplication(
+ manager.getApplicationInfo(recentsPackageName,
+ PackageManager.MATCH_UNINSTALLED_PACKAGES
+ | PackageManager.MATCH_DISABLED_COMPONENTS
+ | PackageManager.GET_SHARED_LIBRARY_FILES));
int resId = resources.getIdentifier(
"gesture_blocking_activities", "array", recentsPackageName);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 597e4242af66..9d43d303b834 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -37,6 +37,7 @@ import android.content.Context;
import android.graphics.drawable.Icon;
import android.hardware.biometrics.BiometricAuthenticator.Modality;
import android.hardware.biometrics.BiometricManager.BiometricMultiSensorMode;
+import android.hardware.biometrics.IBiometricContextListener;
import android.hardware.biometrics.IBiometricSysuiReceiver;
import android.hardware.biometrics.PromptInfo;
import android.hardware.display.DisplayManager;
@@ -154,6 +155,7 @@ public class CommandQueue extends IStatusBar.Stub implements
private static final int MSG_SET_UDFPS_HBM_LISTENER = 60 << MSG_SHIFT;
private static final int MSG_TILE_SERVICE_REQUEST_ADD = 61 << MSG_SHIFT;
private static final int MSG_TILE_SERVICE_REQUEST_CANCEL = 62 << MSG_SHIFT;
+ private static final int MSG_SET_BIOMETRICS_LISTENER = 63 << MSG_SHIFT;
public static final int FLAG_EXCLUDE_NONE = 0;
public static final int FLAG_EXCLUDE_SEARCH_PANEL = 1 << 0;
@@ -317,6 +319,12 @@ public class CommandQueue extends IStatusBar.Stub implements
}
/**
+ * @see IStatusBar#setBiometicContextListener(IBiometricContextListener)
+ */
+ default void setBiometicContextListener(IBiometricContextListener listener) {
+ }
+
+ /**
* @see IStatusBar#setUdfpsHbmListener(IUdfpsHbmListener)
*/
default void setUdfpsHbmListener(IUdfpsHbmListener listener) {
@@ -958,6 +966,13 @@ public class CommandQueue extends IStatusBar.Stub implements
}
@Override
+ public void setBiometicContextListener(IBiometricContextListener listener) {
+ synchronized (mLock) {
+ mHandler.obtainMessage(MSG_SET_BIOMETRICS_LISTENER, listener).sendToTarget();
+ }
+ }
+
+ @Override
public void setUdfpsHbmListener(IUdfpsHbmListener listener) {
synchronized (mLock) {
mHandler.obtainMessage(MSG_SET_UDFPS_HBM_LISTENER, listener).sendToTarget();
@@ -1411,6 +1426,12 @@ public class CommandQueue extends IStatusBar.Stub implements
mCallbacks.get(i).hideAuthenticationDialog();
}
break;
+ case MSG_SET_BIOMETRICS_LISTENER:
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ mCallbacks.get(i).setBiometicContextListener(
+ (IBiometricContextListener) msg.obj);
+ }
+ break;
case MSG_SET_UDFPS_HBM_LISTENER:
for (int i = 0; i < mCallbacks.size(); i++) {
mCallbacks.get(i).setUdfpsHbmListener((IUdfpsHbmListener) msg.obj);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
index c136d9cc7272..b312ce20b313 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
@@ -337,11 +337,11 @@ class LockscreenShadeTransitionController @Inject constructor(
if (field != value || forceApplyAmount) {
field = value
if (!nsslController.isInLockedDownShade() || field == 0f || forceApplyAmount) {
- nsslController.setTransitionToFullShadeAmount(field)
- notificationPanelController.setTransitionToFullShadeAmount(field,
- false /* animate */, 0 /* delay */)
qSDragProgress = MathUtils.saturate(dragDownAmount / scrimTransitionDistance)
+ nsslController.setTransitionToFullShadeAmount(field, qSDragProgress)
qS.setTransitionToFullShadeAmount(field, qSDragProgress)
+ notificationPanelController.setTransitionToFullShadeAmount(field,
+ false /* animate */, 0 /* delay */)
// TODO: appear media also in split shade
val mediaAmount = if (useSplitShade) 0f else field
mediaHierarchyManager.setTransitionToFullShadeAmount(mediaAmount)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index 51a66aad39fb..3411eab23d63 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar;
+import static com.android.systemui.statusbar.phone.NotificationIconContainer.MAX_ICONS_ON_LOCKSCREEN;
+
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
@@ -32,6 +34,7 @@ import android.view.animation.PathInterpolator;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.policy.SystemBarUtils;
import com.android.systemui.R;
+import com.android.systemui.animation.Interpolators;
import com.android.systemui.animation.ShadeInterpolation;
import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
import com.android.systemui.statusbar.notification.NotificationUtils;
@@ -45,6 +48,7 @@ import com.android.systemui.statusbar.notification.stack.NotificationStackScroll
import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm;
import com.android.systemui.statusbar.notification.stack.ViewState;
import com.android.systemui.statusbar.phone.NotificationIconContainer;
+import com.android.systemui.util.Utils;
/**
* A notification shelf view that is placed inside the notification scroller. It manages the
@@ -81,6 +85,11 @@ public class NotificationShelf extends ActivatableNotificationView implements
private int mIndexOfFirstViewInShelf = -1;
private float mCornerAnimationDistance;
private NotificationShelfController mController;
+ private int mActualWidth = -1;
+ private boolean mUseSplitShade;
+
+ /** Fraction of lockscreen to shade animation (on lockscreen swipe down). */
+ private float mFractionToShade;
public NotificationShelf(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -122,13 +131,16 @@ public class NotificationShelf extends ActivatableNotificationView implements
layoutParams.height = res.getDimensionPixelOffset(R.dimen.notification_shelf_height);
setLayoutParams(layoutParams);
- int padding = res.getDimensionPixelOffset(R.dimen.shelf_icon_container_padding);
+ final int padding = res.getDimensionPixelOffset(R.dimen.shelf_icon_container_padding);
mShelfIcons.setPadding(padding, 0, padding, 0);
mScrollFastThreshold = res.getDimensionPixelOffset(R.dimen.scroll_fast_threshold);
mShowNotificationShelf = res.getBoolean(R.bool.config_showNotificationShelf);
mCornerAnimationDistance = res.getDimensionPixelSize(
R.dimen.notification_corner_animation_distance);
+ // TODO(b/213480466) enable short shelf on split shade
+ mUseSplitShade = Utils.shouldUseSplitNotificationShade(mContext.getResources());
+
mShelfIcons.setInNotificationIconShelf(true);
if (!mShowNotificationShelf) {
setVisibility(GONE);
@@ -203,6 +215,10 @@ public class NotificationShelf extends ActivatableNotificationView implements
final float stackEnd = ambientState.getStackY() + ambientState.getStackHeight();
viewState.yTranslation = stackEnd - viewState.height;
+
+ final int shortestWidth = mShelfIcons.calculateWidthFor(MAX_ICONS_ON_LOCKSCREEN);
+ final float fraction = Interpolators.STANDARD.getInterpolation(mFractionToShade);
+ updateStateWidth(viewState, fraction, shortestWidth);
} else {
viewState.hidden = true;
viewState.location = ExpandableViewState.LOCATION_GONE;
@@ -211,6 +227,77 @@ public class NotificationShelf extends ActivatableNotificationView implements
}
/**
+ * @param shelfState View state for NotificationShelf
+ * @param fraction Fraction of lockscreen to shade transition
+ * @param shortestWidth Shortest width to use for lockscreen shelf
+ */
+ @VisibleForTesting
+ public void updateStateWidth(ShelfState shelfState, float fraction, int shortestWidth) {
+ shelfState.actualWidth = !mUseSplitShade && mAmbientState.isOnKeyguard()
+ ? (int) MathUtils.lerp(shortestWidth, getWidth(), fraction)
+ : getWidth();
+ }
+
+ /**
+ * @param fractionToShade Fraction of lockscreen to shade transition
+ */
+ public void setFractionToShade(float fractionToShade) {
+ mFractionToShade = fractionToShade;
+ }
+
+ /**
+ * @return Actual width of shelf, accounting for possible ongoing width animation
+ */
+ public int getActualWidth() {
+ return mActualWidth > -1 ? mActualWidth : getWidth();
+ }
+
+ /**
+ * @param localX Click x from left of screen
+ * @param slop Margin of error within which we count x for valid click
+ * @param left Left of shelf, from left of screen
+ * @param right Right of shelf, from left of screen
+ * @return Whether click x was in view
+ */
+ @VisibleForTesting
+ public boolean isXInView(float localX, float slop, float left, float right) {
+ return (left - slop) <= localX && localX < (right + slop);
+ }
+
+ /**
+ * @param localY Click y from top of shelf
+ * @param slop Margin of error within which we count y for valid click
+ * @param top Top of shelf
+ * @param bottom Height of shelf
+ * @return Whether click y was in view
+ */
+ @VisibleForTesting
+ public boolean isYInView(float localY, float slop, float top, float bottom) {
+ return (top - slop) <= localY && localY < (bottom + slop);
+ }
+
+ /**
+ * @param localX Click x
+ * @param localY Click y
+ * @param slop Margin of error for valid click
+ * @return Whether this click was on the visible (non-clipped) part of the shelf
+ */
+ @Override
+ public boolean pointInView(float localX, float localY, float slop) {
+ final float containerWidth = getWidth();
+ final float shelfWidth = getActualWidth();
+
+ final float left = isLayoutRtl() ? containerWidth - shelfWidth : 0;
+ final float right = isLayoutRtl() ? containerWidth : shelfWidth;
+
+ final float top = mClipTopAmount;
+ final float bottom = getActualHeight();
+
+ return isXInView(localX, slop, left, right)
+ && isYInView(localY, slop, top, bottom);
+ }
+
+ /**
* Update the shelf appearance based on the other notifications around it. This transforms
* the icons from the notification area into the shelf.
*/
@@ -732,11 +819,15 @@ public class NotificationShelf extends ActivatableNotificationView implements
// we always want to clip to our sides, such that nothing can draw outside of these bounds
int height = getResources().getDisplayMetrics().heightPixels;
mClipRect.set(0, -height, getWidth(), height);
- mShelfIcons.setClipBounds(mClipRect);
+ if (mShelfIcons != null) {
+ mShelfIcons.setClipBounds(mClipRect);
+ }
}
private void updateRelativeOffset() {
- mCollapsedIcons.getLocationOnScreen(mTmp);
+ if (mCollapsedIcons != null) {
+ mCollapsedIcons.getLocationOnScreen(mTmp);
+ }
getLocationOnScreen(mTmp);
}
@@ -831,9 +922,20 @@ public class NotificationShelf extends ActivatableNotificationView implements
mIndexOfFirstViewInShelf = mHostLayoutController.indexOfChild(firstViewInShelf);
}
- private class ShelfState extends ExpandableViewState {
+ public class ShelfState extends ExpandableViewState {
private boolean hasItemsInStableShelf;
private ExpandableView firstViewInShelf;
+ public int actualWidth = -1;
+
+ private void updateShelfWidth(View view) {
+ if (actualWidth < 0) {
+ return;
+ }
+ mActualWidth = actualWidth;
+ ActivatableNotificationView anv = (ActivatableNotificationView) view;
+ anv.getBackgroundNormal().setActualWidth(actualWidth);
+ mShelfIcons.setActualLayoutWidth(actualWidth);
+ }
@Override
public void applyToView(View view) {
@@ -846,19 +948,21 @@ public class NotificationShelf extends ActivatableNotificationView implements
updateAppearance();
setHasItemsInStableShelf(hasItemsInStableShelf);
mShelfIcons.setAnimationsEnabled(mAnimationsEnabled);
+ updateShelfWidth(view);
}
@Override
- public void animateTo(View child, AnimationProperties properties) {
+ public void animateTo(View view, AnimationProperties properties) {
if (!mShowNotificationShelf) {
return;
}
- super.animateTo(child, properties);
+ super.animateTo(view, properties);
setIndexOfFirstViewInShelf(firstViewInShelf);
updateAppearance();
setHasItemsInStableShelf(hasItemsInStableShelf);
mShelfIcons.setAnimationsEnabled(mAnimationsEnabled);
+ updateShelfWidth(view);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
index abfdfaf2115f..391525e11866 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
@@ -27,6 +27,7 @@ import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
+import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.R;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.flags.FeatureFlags;
@@ -47,6 +48,7 @@ import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.stack.ForegroundServiceSectionController;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.Assert;
import com.android.wm.shell.bubbles.Bubbles;
@@ -98,6 +100,8 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle
private final ForegroundServiceSectionController mFgsSectionController;
private final NotifPipelineFlags mNotifPipelineFlags;
private AssistantFeedbackController mAssistantFeedbackController;
+ private final KeyguardStateController mKeyguardStateController;
+ private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private final Context mContext;
private NotificationPresenter mPresenter;
@@ -129,7 +133,9 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle
DynamicChildBindController dynamicChildBindController,
LowPriorityInflationHelper lowPriorityInflationHelper,
AssistantFeedbackController assistantFeedbackController,
- NotifPipelineFlags notifPipelineFlags) {
+ NotifPipelineFlags notifPipelineFlags,
+ KeyguardUpdateMonitor keyguardUpdateMonitor,
+ KeyguardStateController keyguardStateController) {
mContext = context;
mHandler = mainHandler;
mFeatureFlags = featureFlags;
@@ -149,6 +155,8 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle
mDynamicChildBindController = dynamicChildBindController;
mLowPriorityInflationHelper = lowPriorityInflationHelper;
mAssistantFeedbackController = assistantFeedbackController;
+ mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+ mKeyguardStateController = keyguardStateController;
}
public void setUpWithPresenter(NotificationPresenter presenter,
@@ -174,6 +182,11 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle
beginUpdate();
+ boolean dynamicallyUnlocked = mDynamicPrivacyController.isDynamicallyUnlocked()
+ && !(mStatusBarStateController.getState() == StatusBarState.KEYGUARD
+ && mKeyguardUpdateMonitor.getUserUnlockedWithBiometricAndIsBypassing(
+ KeyguardUpdateMonitor.getCurrentUser()))
+ && !mKeyguardStateController.isKeyguardGoingAway();
List<NotificationEntry> activeNotifications = mEntryManager.getVisibleNotifications();
ArrayList<ExpandableNotificationRow> toShow = new ArrayList<>(activeNotifications.size());
final int N = activeNotifications.size();
@@ -192,7 +205,7 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle
boolean devicePublic = mLockscreenUserManager.isLockscreenPublicMode(currentUserId);
boolean userPublic = devicePublic
|| mLockscreenUserManager.isLockscreenPublicMode(userId);
- if (userPublic && mDynamicPrivacyController.isDynamicallyUnlocked()
+ if (userPublic && dynamicallyUnlocked
&& (userId == currentUserId || userId == UserHandle.USER_ALL
|| !mLockscreenUserManager.needsSeparateWorkChallenge(userId))) {
userPublic = false;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
index bd948eceab9c..f56602ee2bcd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
@@ -518,6 +518,7 @@ public class StatusBarStateControllerImpl implements
}
private void recordHistoricalState(int newState, int lastState, boolean upcoming) {
+ Trace.traceCounter(Trace.TRACE_TAG_APP, "statusBarState", newState);
mHistoryIndex = (mHistoryIndex + 1) % HISTORY_SIZE;
HistoricalState state = mHistoricalRecords[mHistoryIndex];
state.mNewState = newState;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/VibratorHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/VibratorHelper.java
index 6c3a9093fa98..c74621df94c9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/VibratorHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/VibratorHelper.java
@@ -16,13 +16,19 @@
package com.android.systemui.statusbar;
-import android.content.Context;
-import android.os.AsyncTask;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.media.AudioAttributes;
import android.os.VibrationAttributes;
import android.os.VibrationEffect;
import android.os.Vibrator;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Background;
+
+import org.jetbrains.annotations.NotNull;
+
+import java.util.concurrent.Executor;
import javax.inject.Inject;
@@ -32,21 +38,75 @@ import javax.inject.Inject;
public class VibratorHelper {
private final Vibrator mVibrator;
- private final Context mContext;
private static final VibrationAttributes TOUCH_VIBRATION_ATTRIBUTES =
VibrationAttributes.createForUsage(VibrationAttributes.USAGE_TOUCH);
+ private final Executor mExecutor;
/**
*/
@Inject
- public VibratorHelper(Context context) {
- mContext = context;
- mVibrator = context.getSystemService(Vibrator.class);
+ public VibratorHelper(@Nullable Vibrator vibrator, @Background Executor executor) {
+ mExecutor = executor;
+ mVibrator = vibrator;
}
+ /**
+ * @see Vibrator#vibrate(long)
+ */
public void vibrate(final int effectId) {
- AsyncTask.execute(() ->
+ if (!hasVibrator()) {
+ return;
+ }
+ mExecutor.execute(() ->
mVibrator.vibrate(VibrationEffect.get(effectId, false /* fallback */),
TOUCH_VIBRATION_ATTRIBUTES));
}
+
+ /**
+ * @see Vibrator#vibrate(int, String, VibrationEffect, String, VibrationAttributes)
+ */
+ public void vibrate(int uid, String opPkg, @NonNull VibrationEffect vibe,
+ String reason, @NonNull VibrationAttributes attributes) {
+ if (!hasVibrator()) {
+ return;
+ }
+ mExecutor.execute(() -> mVibrator.vibrate(uid, opPkg, vibe, reason, attributes));
+ }
+
+ /**
+ * @see Vibrator#vibrate(VibrationEffect, AudioAttributes)
+ */
+ public void vibrate(@NonNull VibrationEffect effect, @NonNull AudioAttributes attributes) {
+ if (!hasVibrator()) {
+ return;
+ }
+ mExecutor.execute(() -> mVibrator.vibrate(effect, attributes));
+ }
+
+ /**
+ * @see Vibrator#vibrate(VibrationEffect)
+ */
+ public void vibrate(@NotNull VibrationEffect effect) {
+ if (!hasVibrator()) {
+ return;
+ }
+ mExecutor.execute(() -> mVibrator.vibrate(effect));
+ }
+
+ /**
+ * @see Vibrator#hasVibrator()
+ */
+ public boolean hasVibrator() {
+ return mVibrator != null && mVibrator.hasVibrator();
+ }
+
+ /**
+ * @see Vibrator#cancel()
+ */
+ public void cancel() {
+ if (!hasVibrator()) {
+ return;
+ }
+ mExecutor.execute(mVibrator::cancel);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
index d574cdabced6..e3d0d9802b8d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
@@ -23,6 +23,7 @@ import android.os.Handler;
import android.service.dreams.IDreamManager;
import com.android.internal.statusbar.IStatusBarService;
+import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.dagger.SysUISingleton;
@@ -72,6 +73,7 @@ import com.android.systemui.statusbar.phone.StatusBarRemoteInputCallback;
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallFlags;
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallLogger;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.RemoteInputUriController;
import com.android.systemui.statusbar.window.StatusBarWindowController;
import com.android.systemui.tracing.ProtoTracer;
@@ -214,7 +216,9 @@ public interface StatusBarDependenciesModule {
DynamicChildBindController dynamicChildBindController,
LowPriorityInflationHelper lowPriorityInflationHelper,
AssistantFeedbackController assistantFeedbackController,
- NotifPipelineFlags notifPipelineFlags) {
+ NotifPipelineFlags notifPipelineFlags,
+ KeyguardUpdateMonitor keyguardUpdateMonitor,
+ KeyguardStateController keyguardStateController) {
return new NotificationViewHierarchyManager(
context,
mainHandler,
@@ -231,7 +235,9 @@ public interface StatusBarDependenciesModule {
dynamicChildBindController,
lowPriorityInflationHelper,
assistantFeedbackController,
- notifPipelineFlags);
+ notifPipelineFlags,
+ keyguardUpdateMonitor,
+ keyguardStateController);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
index 120c7228e372..74c97fdbddca 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
@@ -359,6 +359,13 @@ public class ShadeListBuilder implements Dumpable {
private void buildList() {
Trace.beginSection("ShadeListBuilder.buildList");
mPipelineState.requireIsBefore(STATE_BUILD_STARTED);
+
+ if (!mNotifStabilityManager.isPipelineRunAllowed()) {
+ mLogger.logPipelineRunSuppressed();
+ Trace.endSection();
+ return;
+ }
+
mPipelineState.setState(STATE_BUILD_STARTED);
// Step 1: Reset notification states
@@ -667,6 +674,12 @@ public class ShadeListBuilder implements Dumpable {
// having its summary promoted, regardless of how many children it has
Set<String> groupsWithChildrenLostToStability =
getGroupsWithChildrenLostToStability(shadeList);
+ // Like groups which lost a child to stability, any group which lost a child to promotion
+ // is exempt from having its summary promoted when it has no attached children.
+ Set<String> groupsWithChildrenLostToPromotionOrStability =
+ getGroupsWithChildrenLostToPromotion(shadeList);
+ groupsWithChildrenLostToPromotionOrStability.addAll(groupsWithChildrenLostToStability);
+
for (int i = 0; i < shadeList.size(); i++) {
final ListEntry tle = shadeList.get(i);
@@ -676,9 +689,9 @@ public class ShadeListBuilder implements Dumpable {
final boolean hasSummary = group.getSummary() != null;
if (hasSummary && children.size() == 0) {
- if (groupsWithChildrenLostToStability.contains(group.getKey())) {
- // This group lost a child on this run to stability, so it is exempt from
- // having its summary promoted to the top level, so prune it.
+ if (groupsWithChildrenLostToPromotionOrStability.contains(group.getKey())) {
+ // This group lost a child on this run to promotion or stability, so it is
+ // exempt from having its summary promoted to the top level, so prune it.
// It has no children, so it will just vanish.
pruneGroupAtIndexAndPromoteAnyChildren(shadeList, group, i);
} else {
@@ -819,6 +832,26 @@ public class ShadeListBuilder implements Dumpable {
}
/**
+ * Collect the keys of any groups which have already lost a child to a {@link NotifPromoter}
+ * this run.
+ *
+ * These groups will be exempt from appearing without any children.
+ */
+ @NonNull
+ private Set<String> getGroupsWithChildrenLostToPromotion(List<ListEntry> shadeList) {
+ ArraySet<String> groupsWithChildrenLostToPromotion = new ArraySet<>();
+ for (int i = 0; i < shadeList.size(); i++) {
+ final ListEntry tle = shadeList.get(i);
+ if (tle.getAttachState().getPromoter() != null) {
+ // This top-level-entry was part of a group, but was promoted out of it.
+ final String groupKey = tle.getRepresentativeEntry().getSbn().getGroupKey();
+ groupsWithChildrenLostToPromotion.add(groupKey);
+ }
+ }
+ return groupsWithChildrenLostToPromotion;
+ }
+
+ /**
* If a ListEntry was added to the shade list and then later removed (e.g. because it was a
* group that was broken up), this method will erase any bookkeeping traces of that addition
* and/or check that they were already erased.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt
index 0bf21af7026b..031132424115 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt
@@ -15,9 +15,13 @@
*/
package com.android.systemui.statusbar.notification.collection.coordinator
+import android.app.Notification
+import android.app.Notification.GROUP_ALERT_SUMMARY
+import android.util.ArrayMap
import android.util.ArraySet
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.statusbar.NotificationRemoteInputManager
+import com.android.systemui.statusbar.notification.collection.GroupEntry
import com.android.systemui.statusbar.notification.collection.ListEntry
import com.android.systemui.statusbar.notification.collection.NotifPipeline
import com.android.systemui.statusbar.notification.collection.NotificationEntry
@@ -29,13 +33,13 @@ import com.android.systemui.statusbar.notification.collection.notifcollection.No
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender.OnEndLifetimeExtensionCallback
import com.android.systemui.statusbar.notification.collection.render.NodeController
import com.android.systemui.statusbar.notification.dagger.IncomingHeader
-import com.android.systemui.statusbar.notification.interruption.HeadsUpController
import com.android.systemui.statusbar.notification.interruption.HeadsUpViewBinder
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider
import com.android.systemui.statusbar.notification.stack.BUCKET_HEADS_UP
import com.android.systemui.statusbar.policy.HeadsUpManager
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener
import com.android.systemui.util.concurrency.DelayableExecutor
+import com.android.systemui.util.time.SystemClock
import javax.inject.Inject
/**
@@ -54,27 +58,283 @@ import javax.inject.Inject
*/
@CoordinatorScope
class HeadsUpCoordinator @Inject constructor(
+ private val mLogger: HeadsUpCoordinatorLogger,
+ private val mSystemClock: SystemClock,
private val mHeadsUpManager: HeadsUpManager,
private val mHeadsUpViewBinder: HeadsUpViewBinder,
private val mNotificationInterruptStateProvider: NotificationInterruptStateProvider,
private val mRemoteInputManager: NotificationRemoteInputManager,
@IncomingHeader private val mIncomingHeaderController: NodeController,
- @Main private val mExecutor: DelayableExecutor
+ @Main private val mExecutor: DelayableExecutor,
) : Coordinator {
+ private val mEntriesBindingUntil = ArrayMap<String, Long>()
private var mEndLifetimeExtension: OnEndLifetimeExtensionCallback? = null
+ private lateinit var mNotifPipeline: NotifPipeline
+ private var mNow: Long = -1
// notifs we've extended the lifetime for
private val mNotifsExtendingLifetime = ArraySet<NotificationEntry>()
override fun attach(pipeline: NotifPipeline) {
+ mNotifPipeline = pipeline
mHeadsUpManager.addListener(mOnHeadsUpChangedListener)
pipeline.addCollectionListener(mNotifCollectionListener)
+ pipeline.addOnBeforeTransformGroupsListener(::onBeforeTransformGroups)
+ pipeline.addOnBeforeFinalizeFilterListener(::onBeforeFinalizeFilter)
pipeline.addPromoter(mNotifPromoter)
pipeline.addNotificationLifetimeExtender(mLifetimeExtender)
}
private fun onHeadsUpViewBound(entry: NotificationEntry) {
mHeadsUpManager.showNotification(entry)
+ mEntriesBindingUntil.remove(entry.key)
+ }
+
+ /**
+ * Once the pipeline starts running, we can look through posted entries and quickly process
+ * any that don't have groups, and thus will never gave a group alert edge case.
+ */
+ fun onBeforeTransformGroups(list: List<ListEntry>) {
+ mNow = mSystemClock.currentTimeMillis()
+ if (mPostedEntries.isEmpty()) {
+ return
+ }
+ // Process all non-group adds/updates
+ mPostedEntries.values.toList().forEach { posted ->
+ if (!posted.entry.sbn.isGroup) {
+ handlePostedEntry(posted, "non-group")
+ mPostedEntries.remove(posted.key)
+ }
+ }
+ }
+
+ /**
+ * Once we have a nearly final shade list (not including what's pruned for inflation reasons),
+ * we know that stability and [NotifPromoter]s have been applied, so we can use the location of
+ * notifications in this list to determine what kind of group alert behavior should happen.
+ */
+ fun onBeforeFinalizeFilter(list: List<ListEntry>) {
+ // Nothing to do if there are no other adds/updates
+ if (mPostedEntries.isEmpty()) {
+ return
+ }
+ // Calculate a bunch of information about the logical group and the locations of group
+ // entries in the nearly-finalized shade list. These may be used in the per-group loop.
+ val postedEntriesByGroup = mPostedEntries.values.groupBy { it.entry.sbn.groupKey }
+ val logicalMembersByGroup = mNotifPipeline.allNotifs.asSequence()
+ .filter { postedEntriesByGroup.contains(it.sbn.groupKey) }
+ .groupBy { it.sbn.groupKey }
+ val groupLocationsByKey: Map<String, GroupLocation> by lazy { getGroupLocationsByKey(list) }
+ mLogger.logEvaluatingGroups(postedEntriesByGroup.size)
+ // For each group, determine which notification(s) for a group should alert.
+ postedEntriesByGroup.forEach { (groupKey, postedEntries) ->
+ // get and classify the logical members
+ val logicalMembers = logicalMembersByGroup[groupKey] ?: emptyList()
+ val logicalSummary = logicalMembers.find { it.sbn.notification.isGroupSummary }
+
+ // Report the start of this group's evaluation
+ mLogger.logEvaluatingGroup(groupKey, postedEntries.size, logicalMembers.size)
+
+ // If there is no logical summary, then there is no alert to transfer
+ if (logicalSummary == null) {
+ postedEntries.forEach { handlePostedEntry(it, "logical-summary-missing") }
+ return@forEach
+ }
+
+ // If summary isn't wanted to be heads up, then there is no alert to transfer
+ if (!isGoingToShowHunStrict(logicalSummary)) {
+ postedEntries.forEach { handlePostedEntry(it, "logical-summary-not-alerting") }
+ return@forEach
+ }
+
+ // The group is alerting! Overall goals:
+ // - Maybe transfer its alert to a child
+ // - Also let any/all newly alerting children still alert
+ var childToReceiveParentAlert: NotificationEntry?
+ var targetType = "undefined"
+
+ // If the parent is alerting, always look at the posted notification with the newest
+ // 'when', and if it is isolated with GROUP_ALERT_SUMMARY, then it should receive the
+ // parent's alert.
+ childToReceiveParentAlert =
+ findAlertOverride(postedEntries, groupLocationsByKey::getLocation)
+ if (childToReceiveParentAlert != null) {
+ targetType = "alertOverride"
+ }
+
+ // If the summary is Detached and we have not picked a receiver of the alert, then we
+ // need to look for the best child to alert in place of the summary.
+ val isSummaryAttached = groupLocationsByKey.contains(logicalSummary.key)
+ if (!isSummaryAttached && childToReceiveParentAlert == null) {
+ childToReceiveParentAlert =
+ findBestTransferChild(logicalMembers, groupLocationsByKey::getLocation)
+ if (childToReceiveParentAlert != null) {
+ targetType = "bestChild"
+ }
+ }
+
+ // If there is no child to receive the parent alert, then just handle the posted entries
+ // and return.
+ if (childToReceiveParentAlert == null) {
+ postedEntries.forEach { handlePostedEntry(it, "no-transfer-target") }
+ return@forEach
+ }
+
+ // At this point we just need to initiate the transfer
+ val summaryUpdate = mPostedEntries[logicalSummary.key]
+
+ // If the summary was not attached, then remove the alert from the detached summary.
+ // Otherwise we can simply ignore its posted update.
+ if (!isSummaryAttached) {
+ val summaryUpdateForRemoval = summaryUpdate?.also {
+ it.shouldHeadsUpEver = false
+ } ?: PostedEntry(logicalSummary,
+ wasAdded = false,
+ wasUpdated = false,
+ shouldHeadsUpEver = false,
+ shouldHeadsUpAgain = false,
+ isAlerting = mHeadsUpManager.isAlerting(logicalSummary.key),
+ isBinding = isEntryBinding(logicalSummary),
+ )
+ // If we transfer the alert and the summary isn't even attached, that means we
+ // should ensure the summary is no longer alerting, so we remove it here.
+ handlePostedEntry(summaryUpdateForRemoval, "detached-summary-remove-alert")
+ } else if (summaryUpdate!=null) {
+ mLogger.logPostedEntryWillNotEvaluate(summaryUpdate, "attached-summary-transferred")
+ }
+
+ // Handle all posted entries -- if the child receiving the parent's alert is in the
+ // list, then set its flags to ensure it alerts.
+ var didAlertChildToReceiveParentAlert = false
+ postedEntries.asSequence()
+ .filter { it.key != logicalSummary.key }
+ .forEach { postedEntry ->
+ if (childToReceiveParentAlert.key == postedEntry.key) {
+ // Update the child's posted update so that it
+ postedEntry.shouldHeadsUpEver = true
+ postedEntry.shouldHeadsUpAgain = true
+ handlePostedEntry(postedEntry, "child-alert-transfer-target-$targetType")
+ didAlertChildToReceiveParentAlert = true
+ } else {
+ handlePostedEntry(postedEntry, "child-alert-non-target")
+ }
+ }
+
+ // If the child receiving the alert was not updated on this tick (which can happen in a
+ // standard alert transfer scenario), then construct an update so that we can apply it.
+ if (!didAlertChildToReceiveParentAlert) {
+ val posted = PostedEntry(
+ childToReceiveParentAlert,
+ wasAdded = false,
+ wasUpdated = false,
+ shouldHeadsUpEver = true,
+ shouldHeadsUpAgain = true,
+ isAlerting = mHeadsUpManager.isAlerting(childToReceiveParentAlert.key),
+ isBinding = isEntryBinding(childToReceiveParentAlert),
+ )
+ handlePostedEntry(posted, "non-posted-child-alert-transfer-target-$targetType")
+ }
+ }
+ // After this method runs, all posted entries should have been handled (or skipped).
+ mPostedEntries.clear()
+ }
+
+ /**
+ * Find the posted child with the newest when, and return it if it is isolated and has
+ * GROUP_ALERT_SUMMARY so that it can be alerted.
+ */
+ private fun findAlertOverride(
+ postedEntries: List<PostedEntry>,
+ locationLookupByKey: (String) -> GroupLocation,
+ ): NotificationEntry? = postedEntries.asSequence()
+ .filter { posted -> !posted.entry.sbn.notification.isGroupSummary }
+ .sortedBy { posted -> -posted.entry.sbn.notification.`when` }
+ .firstOrNull()
+ ?.let { posted ->
+ posted.entry.takeIf { entry ->
+ locationLookupByKey(entry.key) == GroupLocation.Isolated
+ && entry.sbn.notification.groupAlertBehavior == GROUP_ALERT_SUMMARY
+ }
+ }
+
+ /**
+ * Of children which are attached, look for the child to receive the notification:
+ * First prefer children which were updated, then looking for the ones with the newest 'when'
+ */
+ private fun findBestTransferChild(
+ logicalMembers: List<NotificationEntry>,
+ locationLookupByKey: (String) -> GroupLocation,
+ ): NotificationEntry? = logicalMembers.asSequence()
+ .filter { !it.sbn.notification.isGroupSummary }
+ .filter { locationLookupByKey(it.key) != GroupLocation.Detached }
+ .sortedWith(compareBy(
+ { !mPostedEntries.contains(it.key) },
+ { -it.sbn.notification.`when` },
+ ))
+ .firstOrNull()
+
+ private fun getGroupLocationsByKey(list: List<ListEntry>): Map<String, GroupLocation> =
+ mutableMapOf<String, GroupLocation>().also { map ->
+ list.forEach { topLevelEntry ->
+ when (topLevelEntry) {
+ is NotificationEntry -> map[topLevelEntry.key] = GroupLocation.Isolated
+ is GroupEntry -> {
+ topLevelEntry.summary?.let { summary ->
+ map[summary.key] = GroupLocation.Summary
+ }
+ topLevelEntry.children.forEach { child ->
+ map[child.key] = GroupLocation.Child
+ }
+ }
+ else -> error("unhandled type $topLevelEntry")
+ }
+ }
+ }
+
+ private val mPostedEntries = LinkedHashMap<String, PostedEntry>()
+
+ fun handlePostedEntry(posted: PostedEntry, scenario: String) {
+ mLogger.logPostedEntryWillEvaluate(posted, scenario)
+ if (posted.wasAdded) {
+ if (posted.shouldHeadsUpEver) {
+ bindForAsyncHeadsUp(posted)
+ }
+ } else {
+ if (posted.isHeadsUpAlready) {
+ // NOTE: This might be because we're alerting (i.e. tracked by HeadsUpManager) OR
+ // it could be because we're binding, and that will affect the next step.
+ if (posted.shouldHeadsUpEver) {
+ // If alerting, we need to post an update. Otherwise we're still binding,
+ // and we can just let that finish.
+ if (posted.isAlerting) {
+ mHeadsUpManager.updateNotification(posted.key, posted.shouldHeadsUpAgain)
+ }
+ } else {
+ if (posted.isAlerting) {
+ // We don't want this to be interrupting anymore, let's remove it
+ mHeadsUpManager.removeNotification(posted.key, false /*removeImmediately*/)
+ } else {
+ // Don't let the bind finish
+ cancelHeadsUpBind(posted.entry)
+ }
+ }
+ } else if (posted.shouldHeadsUpEver && posted.shouldHeadsUpAgain) {
+ // This notification was updated to be heads up, show it!
+ bindForAsyncHeadsUp(posted)
+ }
+ }
+ }
+
+ private fun cancelHeadsUpBind(entry: NotificationEntry) {
+ mEntriesBindingUntil.remove(entry.key)
+ mHeadsUpViewBinder.abortBindCallback(entry)
+ }
+
+ private fun bindForAsyncHeadsUp(posted: PostedEntry) {
+ // TODO: Add a guarantee to bindHeadsUpView of some kind of callback if the bind is
+ // cancelled so that we don't need to have this sad timeout hack.
+ mEntriesBindingUntil[posted.key] = mNow + BIND_TIMEOUT
+ mHeadsUpViewBinder.bindHeadsUpView(posted.entry, this::onHeadsUpViewBound)
}
private val mNotifCollectionListener = object : NotifCollectionListener {
@@ -82,9 +342,17 @@ class HeadsUpCoordinator @Inject constructor(
* Notification was just added and if it should heads up, bind the view and then show it.
*/
override fun onEntryAdded(entry: NotificationEntry) {
- if (mNotificationInterruptStateProvider.shouldHeadsUp(entry)) {
- mHeadsUpViewBinder.bindHeadsUpView(entry) { entry -> onHeadsUpViewBound(entry) }
- }
+ // shouldHeadsUp includes check for whether this notification should be filtered
+ val shouldHeadsUpEver = mNotificationInterruptStateProvider.shouldHeadsUp(entry)
+ mPostedEntries[entry.key] = PostedEntry(
+ entry,
+ wasAdded = true,
+ wasUpdated = false,
+ shouldHeadsUpEver = shouldHeadsUpEver,
+ shouldHeadsUpAgain = true,
+ isAlerting = false,
+ isBinding = false,
+ )
}
/**
@@ -93,22 +361,26 @@ class HeadsUpCoordinator @Inject constructor(
* up again.
*/
override fun onEntryUpdated(entry: NotificationEntry) {
- val hunAgain = HeadsUpController.alertAgain(entry, entry.sbn.notification)
- // includes check for whether this notification should be filtered:
- val shouldHeadsUp = mNotificationInterruptStateProvider.shouldHeadsUp(entry)
- val wasHeadsUp = mHeadsUpManager.isAlerting(entry.key)
- if (wasHeadsUp) {
- if (shouldHeadsUp) {
- mHeadsUpManager.updateNotification(entry.key, hunAgain)
- } else {
- // We don't want this to be interrupting anymore, let's remove it
- mHeadsUpManager.removeNotification(
- entry.key, false /* removeImmediately */
- )
- }
- } else if (shouldHeadsUp && hunAgain) {
- // This notification was updated to be heads up, show it!
- mHeadsUpViewBinder.bindHeadsUpView(entry) { entry -> onHeadsUpViewBound(entry) }
+ val shouldHeadsUpEver = mNotificationInterruptStateProvider.shouldHeadsUp(entry)
+ val shouldHeadsUpAgain = shouldHunAgain(entry)
+ val isAlerting = mHeadsUpManager.isAlerting(entry.key)
+ val isBinding = isEntryBinding(entry)
+ mPostedEntries.compute(entry.key) { _, value ->
+ value?.also { update ->
+ update.wasUpdated = true
+ update.shouldHeadsUpEver = update.shouldHeadsUpEver || shouldHeadsUpEver
+ update.shouldHeadsUpAgain = update.shouldHeadsUpAgain || shouldHeadsUpAgain
+ update.isAlerting = isAlerting
+ update.isBinding = isBinding
+ } ?: PostedEntry(
+ entry,
+ wasAdded = false,
+ wasUpdated = true,
+ shouldHeadsUpEver = shouldHeadsUpEver,
+ shouldHeadsUpAgain = shouldHeadsUpAgain,
+ isAlerting = isAlerting,
+ isBinding = isBinding,
+ )
}
}
@@ -116,8 +388,12 @@ class HeadsUpCoordinator @Inject constructor(
* Stop alerting HUNs that are removed from the notification collection
*/
override fun onEntryRemoved(entry: NotificationEntry, reason: Int) {
+ mPostedEntries.remove(entry.key)
+ cancelHeadsUpBind(entry)
val entryKey = entry.key
if (mHeadsUpManager.isAlerting(entryKey)) {
+ // TODO: This should probably know the RemoteInputCoordinator's conditions,
+ // or otherwise reference that coordinator's state, rather than replicate its logic
val removeImmediatelyForRemoteInput = (mRemoteInputManager.isSpinning(entryKey) &&
!NotificationRemoteInputManager.FORCE_REMOTE_INPUT_HISTORY)
mHeadsUpManager.removeNotification(entry.key, removeImmediatelyForRemoteInput)
@@ -129,6 +405,14 @@ class HeadsUpCoordinator @Inject constructor(
}
}
+ /**
+ * Checks whether an update for a notification warrants an alert for the user.
+ */
+ private fun shouldHunAgain(entry: NotificationEntry): Boolean {
+ return (!entry.hasInterrupted() ||
+ (entry.sbn.notification.flags and Notification.FLAG_ONLY_ALERT_ONCE) == 0)
+ }
+
private val mLifetimeExtender = object : NotifLifetimeExtender {
override fun getName() = TAG
@@ -164,11 +448,13 @@ class HeadsUpCoordinator @Inject constructor(
private val mNotifPromoter = object : NotifPromoter(TAG) {
override fun shouldPromoteToTopLevel(entry: NotificationEntry): Boolean =
- isCurrentlyShowingHun(entry)
+ isGoingToShowHunNoRetract(entry)
}
val sectioner = object : NotifSectioner("HeadsUp", BUCKET_HEADS_UP) {
- override fun isInSection(entry: ListEntry): Boolean = isCurrentlyShowingHun(entry)
+ override fun isInSection(entry: ListEntry): Boolean =
+ // TODO: This check won't notice if a child of the group is going to HUN...
+ isGoingToShowHunNoRetract(entry)
override fun getHeaderNodeController(): NodeController? =
// TODO: remove SHOW_ALL_SECTIONS, this redundant method, and mIncomingHeaderController
@@ -186,7 +472,34 @@ class HeadsUpCoordinator @Inject constructor(
private fun isSticky(entry: NotificationEntry) = mHeadsUpManager.isSticky(entry.key)
- private fun isCurrentlyShowingHun(entry: ListEntry) = mHeadsUpManager.isAlerting(entry.key)
+ private fun isEntryBinding(entry: ListEntry): Boolean {
+ val bindingUntil = mEntriesBindingUntil[entry.key]
+ return bindingUntil != null && bindingUntil >= mNow
+ }
+
+ /**
+ * Whether the notification is already alerting or binding so that it can imminently alert
+ */
+ private fun isAttemptingToShowHun(entry: ListEntry) =
+ mHeadsUpManager.isAlerting(entry.key) || isEntryBinding(entry)
+
+ /**
+ * Whether the notification is already alerting/binding per [isAttemptingToShowHun] OR if it
+ * has been updated so that it should alert this update. This method is permissive because it
+ * returns `true` even if the update would (in isolation of its group) cause the alert to be
+ * retracted. This is important for not retracting transferred group alerts.
+ */
+ private fun isGoingToShowHunNoRetract(entry: ListEntry) =
+ mPostedEntries[entry.key]?.calculateShouldBeHeadsUpNoRetract ?: isAttemptingToShowHun(entry)
+
+ /**
+ * If the notification has been updated, then whether it should HUN in isolation, otherwise
+ * defers to the already alerting/binding state of [isAttemptingToShowHun]. This method is
+ * strict because any update which would revoke the alert supersedes the current
+ * alerting/binding state.
+ */
+ private fun isGoingToShowHunStrict(entry: ListEntry) =
+ mPostedEntries[entry.key]?.calculateShouldBeHeadsUpStrict ?: isAttemptingToShowHun(entry)
private fun endNotifLifetimeExtensionIfExtended(entry: NotificationEntry) {
if (mNotifsExtendingLifetime.remove(entry)) {
@@ -196,5 +509,29 @@ class HeadsUpCoordinator @Inject constructor(
companion object {
private const val TAG = "HeadsUpCoordinator"
+ private const val BIND_TIMEOUT = 1000L
}
-} \ No newline at end of file
+
+ data class PostedEntry(
+ val entry: NotificationEntry,
+ val wasAdded: Boolean,
+ var wasUpdated: Boolean,
+ var shouldHeadsUpEver: Boolean,
+ var shouldHeadsUpAgain: Boolean,
+ var isAlerting: Boolean,
+ var isBinding: Boolean,
+ ) {
+ val key = entry.key
+ val isHeadsUpAlready: Boolean
+ get() = isAlerting || isBinding
+ val calculateShouldBeHeadsUpStrict: Boolean
+ get() = shouldHeadsUpEver && (wasAdded || shouldHeadsUpAgain || isHeadsUpAlready)
+ val calculateShouldBeHeadsUpNoRetract: Boolean
+ get() = isHeadsUpAlready || (shouldHeadsUpEver && (wasAdded || shouldHeadsUpAgain))
+ }
+}
+
+private enum class GroupLocation { Detached, Isolated, Summary, Child }
+
+private fun Map<String, GroupLocation>.getLocation(key: String): GroupLocation =
+ getOrDefault(key, GroupLocation.Detached)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorLogger.kt
new file mode 100644
index 000000000000..204a494c32e8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorLogger.kt
@@ -0,0 +1,62 @@
+package com.android.systemui.statusbar.notification.collection.coordinator
+
+import android.util.Log
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.LogLevel
+import com.android.systemui.log.dagger.NotificationHeadsUpLog
+import javax.inject.Inject
+
+private const val TAG = "HeadsUpCoordinator"
+
+class HeadsUpCoordinatorLogger constructor(
+ private val buffer: LogBuffer,
+ private val verbose: Boolean,
+) {
+ @Inject
+ constructor(@NotificationHeadsUpLog buffer: LogBuffer) :
+ this(buffer, Log.isLoggable(TAG, Log.VERBOSE))
+
+ fun logPostedEntryWillEvaluate(posted: HeadsUpCoordinator.PostedEntry, reason: String) {
+ if (!verbose) return
+ buffer.log(TAG, LogLevel.VERBOSE, {
+ str1 = posted.key
+ str2 = reason
+ bool1 = posted.shouldHeadsUpEver
+ bool2 = posted.shouldHeadsUpAgain
+ }, {
+ "will evaluate posted entry $str1:" +
+ " reason=$str2 shouldHeadsUpEver=$bool1 shouldHeadsUpAgain=$bool2"
+ })
+ }
+
+ fun logPostedEntryWillNotEvaluate(posted: HeadsUpCoordinator.PostedEntry, reason: String) {
+ if (!verbose) return
+ buffer.log(TAG, LogLevel.VERBOSE, {
+ str1 = posted.key
+ str2 = reason
+ }, {
+ "will not evaluate posted entry $str1: reason=$str2"
+ })
+ }
+
+ fun logEvaluatingGroups(numGroups: Int) {
+ if (!verbose) return
+ buffer.log(TAG, LogLevel.VERBOSE, {
+ int1 = numGroups
+ }, {
+ "evaluating groups for alert transfer: $int1"
+ })
+ }
+
+ fun logEvaluatingGroup(groupKey: String, numPostedEntries: Int, logicalGroupSize: Int) {
+ if (!verbose) return
+ buffer.log(TAG, LogLevel.VERBOSE, {
+ str1 = groupKey
+ int1 = numPostedEntries
+ int2 = logicalGroupSize
+ }, {
+ "evaluating group for alert transfer: $str1" +
+ " numPostedEntries=$int1 logicalGroupSize=$int2"
+ })
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java
index 0ce07cb99d52..22300d8c180d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java
@@ -98,6 +98,8 @@ public class KeyguardCoordinator implements Coordinator {
setupInvalidateNotifListCallbacks();
// Filter at the "finalize" stage so that views remain bound by PreparationCoordinator
pipeline.addFinalizeFilter(mNotifFilter);
+
+ updateSectionHeadersVisibility();
}
private final NotifFilter mNotifFilter = new NotifFilter(TAG) {
@@ -164,6 +166,8 @@ public class KeyguardCoordinator implements Coordinator {
}
}
+ // TODO(b/206118999): merge this class with SensitiveContentCoordinator which also depends on
+ // these same updates
private void setupInvalidateNotifListCallbacks() {
// register onKeyguardShowing callback
mKeyguardStateController.addCallback(mKeyguardCallback);
@@ -220,10 +224,7 @@ public class KeyguardCoordinator implements Coordinator {
}
private void invalidateListFromFilter(String reason) {
- boolean onKeyguard = mStatusBarStateController.getState() == StatusBarState.KEYGUARD;
- boolean neverShowSections = mSectionHeaderVisibilityProvider.getNeverShowSectionHeaders();
- boolean showSections = !onKeyguard && !neverShowSections;
- mSectionHeaderVisibilityProvider.setSectionHeadersVisible(showSections);
+ updateSectionHeadersVisibility();
mNotifFilter.invalidateList();
}
@@ -235,6 +236,13 @@ public class KeyguardCoordinator implements Coordinator {
1) == 0;
}
+ private void updateSectionHeadersVisibility() {
+ boolean onKeyguard = mStatusBarStateController.getState() == StatusBarState.KEYGUARD;
+ boolean neverShowSections = mSectionHeaderVisibilityProvider.getNeverShowSectionHeaders();
+ boolean showSections = !onKeyguard && !neverShowSections;
+ mSectionHeaderVisibilityProvider.setSectionHeadersVisible(showSections);
+ }
+
private final KeyguardStateController.Callback mKeyguardCallback =
new KeyguardStateController.Callback() {
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinator.kt
index a115e0400de3..9c82cb64a0d7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinator.kt
@@ -17,7 +17,10 @@
package com.android.systemui.statusbar.notification.collection.coordinator
import android.os.UserHandle
+import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.NotificationLockscreenUserManager
+import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.notification.DynamicPrivacyController
import com.android.systemui.statusbar.notification.collection.GroupEntry
import com.android.systemui.statusbar.notification.collection.ListEntry
@@ -26,6 +29,7 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope
import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeRenderListListener
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Invalidator
+import com.android.systemui.statusbar.policy.KeyguardStateController
import dagger.Module
import dagger.Provides
@@ -36,9 +40,13 @@ object SensitiveContentCoordinatorModule {
@CoordinatorScope
fun provideCoordinator(
dynamicPrivacyController: DynamicPrivacyController,
- lockscreenUserManager: NotificationLockscreenUserManager
+ lockscreenUserManager: NotificationLockscreenUserManager,
+ keyguardUpdateMonitor: KeyguardUpdateMonitor,
+ statusBarStateController: StatusBarStateController,
+ keyguardStateController: KeyguardStateController
): SensitiveContentCoordinator =
- SensitiveContentCoordinatorImpl(dynamicPrivacyController, lockscreenUserManager)
+ SensitiveContentCoordinatorImpl(dynamicPrivacyController, lockscreenUserManager,
+ keyguardUpdateMonitor, statusBarStateController, keyguardStateController)
}
/** Coordinates re-inflation and post-processing of sensitive notification content. */
@@ -46,7 +54,10 @@ interface SensitiveContentCoordinator : Coordinator
private class SensitiveContentCoordinatorImpl(
private val dynamicPrivacyController: DynamicPrivacyController,
- private val lockscreenUserManager: NotificationLockscreenUserManager
+ private val lockscreenUserManager: NotificationLockscreenUserManager,
+ private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
+ private val statusBarStateController: StatusBarStateController,
+ private val keyguardStateController: KeyguardStateController
) : Invalidator("SensitiveContentInvalidator"),
SensitiveContentCoordinator,
DynamicPrivacyController.Listener,
@@ -61,6 +72,19 @@ private class SensitiveContentCoordinatorImpl(
override fun onDynamicPrivacyChanged(): Unit = invalidateList()
override fun onBeforeRenderList(entries: List<ListEntry>) {
+ if (keyguardStateController.isKeyguardGoingAway() ||
+ statusBarStateController.getState() == StatusBarState.KEYGUARD &&
+ keyguardUpdateMonitor.getUserUnlockedWithBiometricAndIsBypassing(
+ KeyguardUpdateMonitor.getCurrentUser())) {
+ // don't update yet if:
+ // - the keyguard is currently going away
+ // - LS is about to be dismissed by a biometric that bypasses LS (avoid notif flash)
+
+ // TODO(b/206118999): merge this class with KeyguardCoordinator which ensures the
+ // dependent state changes invalidate the pipeline
+ return
+ }
+
val currentUserId = lockscreenUserManager.currentUserId
val devicePublic = lockscreenUserManager.isLockscreenPublicMode(currentUserId)
val deviceSensitive = devicePublic &&
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java
index 327876c95b7f..fcc2b266dce2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java
@@ -32,6 +32,7 @@ import com.android.systemui.statusbar.notification.collection.ListEntry;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifStabilityManager;
+import com.android.systemui.statusbar.notification.collection.render.NotifPanelEventSource;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.util.concurrency.DelayableExecutor;
@@ -56,17 +57,23 @@ import javax.inject.Inject;
*/
// TODO(b/204468557): Move to @CoordinatorScope
@SysUISingleton
-public class VisualStabilityCoordinator implements Coordinator, Dumpable {
+public class VisualStabilityCoordinator implements Coordinator, Dumpable,
+ NotifPanelEventSource.Callbacks {
private final DelayableExecutor mDelayableExecutor;
- private final WakefulnessLifecycle mWakefulnessLifecycle;
- private final StatusBarStateController mStatusBarStateController;
private final HeadsUpManager mHeadsUpManager;
+ private final NotifPanelEventSource mNotifPanelEventSource;
+ private final StatusBarStateController mStatusBarStateController;
+ private final WakefulnessLifecycle mWakefulnessLifecycle;
private boolean mScreenOn;
private boolean mPanelExpanded;
private boolean mPulsing;
+ private boolean mNotifPanelCollapsing;
+ private boolean mNotifPanelLaunchingActivity;
+ private boolean mPipelineRunAllowed;
private boolean mReorderingAllowed;
+ private boolean mIsSuppressingPipelineRun = false;
private boolean mIsSuppressingGroupChange = false;
private final Set<String> mEntriesWithSuppressedSectionChange = new HashSet<>();
private boolean mIsSuppressingEntryReorder = false;
@@ -81,16 +88,17 @@ public class VisualStabilityCoordinator implements Coordinator, Dumpable {
@Inject
public VisualStabilityCoordinator(
+ DelayableExecutor delayableExecutor,
DumpManager dumpManager,
HeadsUpManager headsUpManager,
- WakefulnessLifecycle wakefulnessLifecycle,
+ NotifPanelEventSource notifPanelEventSource,
StatusBarStateController statusBarStateController,
- DelayableExecutor delayableExecutor
- ) {
+ WakefulnessLifecycle wakefulnessLifecycle) {
mHeadsUpManager = headsUpManager;
mWakefulnessLifecycle = wakefulnessLifecycle;
mStatusBarStateController = statusBarStateController;
mDelayableExecutor = delayableExecutor;
+ mNotifPanelEventSource = notifPanelEventSource;
dumpManager.registerDumpable(this);
}
@@ -103,6 +111,7 @@ public class VisualStabilityCoordinator implements Coordinator, Dumpable {
mStatusBarStateController.addCallback(mStatusBarStateControllerListener);
mPulsing = mStatusBarStateController.isPulsing();
+ mNotifPanelEventSource.registerCallbacks(this);
pipeline.setVisualStabilityManager(mNotifStabilityManager);
}
@@ -112,12 +121,19 @@ public class VisualStabilityCoordinator implements Coordinator, Dumpable {
new NotifStabilityManager("VisualStabilityCoordinator") {
@Override
public void onBeginRun() {
+ mIsSuppressingPipelineRun = false;
mIsSuppressingGroupChange = false;
mEntriesWithSuppressedSectionChange.clear();
mIsSuppressingEntryReorder = false;
}
@Override
+ public boolean isPipelineRunAllowed() {
+ mIsSuppressingPipelineRun |= !mPipelineRunAllowed;
+ return mPipelineRunAllowed;
+ }
+
+ @Override
public boolean isGroupChangeAllowed(@NonNull NotificationEntry entry) {
final boolean isGroupChangeAllowedForEntry =
mReorderingAllowed || mHeadsUpManager.isAlerting(entry.getKey());
@@ -154,9 +170,12 @@ public class VisualStabilityCoordinator implements Coordinator, Dumpable {
};
private void updateAllowedStates() {
+ mPipelineRunAllowed = !isPanelCollapsingOrLaunchingActivity();
mReorderingAllowed = isReorderingAllowed();
- if (mReorderingAllowed && (mIsSuppressingGroupChange || isSuppressingSectionChange()
- || mIsSuppressingEntryReorder)) {
+ if ((mPipelineRunAllowed && mIsSuppressingPipelineRun)
+ || (mReorderingAllowed && (mIsSuppressingGroupChange
+ || isSuppressingSectionChange()
+ || mIsSuppressingEntryReorder))) {
mNotifStabilityManager.invalidateList();
}
}
@@ -165,6 +184,10 @@ public class VisualStabilityCoordinator implements Coordinator, Dumpable {
return !mEntriesWithSuppressedSectionChange.isEmpty();
}
+ private boolean isPanelCollapsingOrLaunchingActivity() {
+ return mNotifPanelCollapsing || mNotifPanelLaunchingActivity;
+ }
+
private boolean isReorderingAllowed() {
return (!mScreenOn || !mPanelExpanded) && !mPulsing;
}
@@ -248,4 +271,16 @@ public class VisualStabilityCoordinator implements Coordinator, Dumpable {
pw.println(" " + key);
}
}
+
+ @Override
+ public void onPanelCollapsingChanged(boolean isCollapsing) {
+ mNotifPanelCollapsing = isCollapsing;
+ updateAllowedStates();
+ }
+
+ @Override
+ public void onLaunchingActivityChanged(boolean isLaunchingActivity) {
+ mNotifPanelLaunchingActivity = isLaunchingActivity;
+ updateAllowedStates();
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt
index ba3e8554c16e..f8bf85f92eae 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt
@@ -306,6 +306,9 @@ class ShadeListBuilderLogger @Inject constructor(
}
}
}
+
+ fun logPipelineRunSuppressed() =
+ buffer.log(TAG, INFO, {}) { "Suppressing pipeline run during animation." }
}
private const val TAG = "ShadeListBuilder" \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifStabilityManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifStabilityManager.kt
index 60f557c9bf7e..446449864606 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifStabilityManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifStabilityManager.kt
@@ -27,6 +27,16 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry
*/
abstract class NotifStabilityManager protected constructor(name: String) :
Pluggable<NotifStabilityManager>(name) {
+
+ /**
+ * Called prior to running the pipeline to suppress any visual changes. Ex: collapse animation
+ * is playing, moving stuff around simultaneously will look janky.
+ *
+ * Note: this is invoked *before* [onBeginRun], so that implementors can reference state
+ * maintained from a previous run.
+ */
+ abstract fun isPipelineRunAllowed(): Boolean
+
/**
* Called at the beginning of every pipeline run to perform any necessary cleanup from the
* previous run.
@@ -76,6 +86,7 @@ abstract class NotifStabilityManager protected constructor(name: String) :
/** The default, no-op instance of the stability manager which always allows all changes */
object DefaultNotifStabilityManager : NotifStabilityManager("DefaultNotifStabilityManager") {
+ override fun isPipelineRunAllowed(): Boolean = true
override fun onBeginRun() {}
override fun isGroupChangeAllowed(entry: NotificationEntry): Boolean = true
override fun isSectionChangeAllowed(entry: NotificationEntry): Boolean = true
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifPanelEventSource.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifPanelEventSource.kt
new file mode 100644
index 000000000000..920d3c4cbe55
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifPanelEventSource.kt
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.statusbar.notification.collection.render
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.phone.NotificationPanelViewController
+import com.android.systemui.statusbar.phone.dagger.StatusBarComponent
+import com.android.systemui.statusbar.phone.dagger.StatusBarComponent.StatusBarScope
+import com.android.systemui.util.ListenerSet
+import dagger.Binds
+import dagger.Module
+import dagger.Provides
+import dagger.multibindings.IntoSet
+
+/** Provides certain notification panel events. */
+interface NotifPanelEventSource {
+
+ /** Registers callbacks to be invoked when notification panel events occur. */
+ fun registerCallbacks(callbacks: Callbacks)
+
+ /** Unregisters callbacks previously registered via [.registerCallbacks] */
+ fun unregisterCallbacks(callbacks: Callbacks)
+
+ /** Callbacks for certain notification panel events. */
+ interface Callbacks {
+
+ /** Invoked when the notification panel starts or stops collapsing. */
+ fun onPanelCollapsingChanged(isCollapsing: Boolean)
+
+ /**
+ * Invoked when the notification panel starts or stops launching an [android.app.Activity].
+ */
+ fun onLaunchingActivityChanged(isLaunchingActivity: Boolean)
+ }
+}
+
+@Module
+abstract class NotifPanelEventSourceModule {
+
+ @Binds
+ @SysUISingleton
+ abstract fun bindEventSource(manager: NotifPanelEventSourceManager): NotifPanelEventSource
+
+ @Module
+ companion object {
+ @JvmStatic
+ @Provides
+ fun provideManager(): NotifPanelEventSourceManager = NotifPanelEventSourceManagerImpl()
+ }
+}
+
+@Module
+object StatusBarNotifPanelEventSourceModule {
+ @JvmStatic
+ @Provides
+ @IntoSet
+ @StatusBarScope
+ fun bindStartable(
+ manager: NotifPanelEventSourceManager,
+ notifPanelController: NotificationPanelViewController
+ ): StatusBarComponent.Startable =
+ EventSourceStatusBarStartableImpl(manager, notifPanelController)
+}
+
+/**
+ * Management layer that bridges [SysUiSingleton] and [StatusBarScope]. Necessary because code that
+ * wants to listen to [NotifPanelEventSource] lives in [SysUiSingleton], but the events themselves
+ * come from [NotificationPanelViewController] in [StatusBarScope].
+ */
+interface NotifPanelEventSourceManager : NotifPanelEventSource {
+ var eventSource: NotifPanelEventSource?
+}
+
+private class NotifPanelEventSourceManagerImpl
+ : NotifPanelEventSourceManager, NotifPanelEventSource.Callbacks {
+
+ private val callbackSet = ListenerSet<NotifPanelEventSource.Callbacks>()
+
+ override var eventSource: NotifPanelEventSource? = null
+ set(value) {
+ field?.unregisterCallbacks(this)
+ value?.registerCallbacks(this)
+ field = value
+ }
+
+ override fun registerCallbacks(callbacks: NotifPanelEventSource.Callbacks) {
+ callbackSet.addIfAbsent(callbacks)
+ }
+
+ override fun unregisterCallbacks(callbacks: NotifPanelEventSource.Callbacks) {
+ callbackSet.remove(callbacks)
+ }
+
+ override fun onPanelCollapsingChanged(isCollapsing: Boolean) {
+ callbackSet.forEach { it.onPanelCollapsingChanged(isCollapsing) }
+ }
+
+ override fun onLaunchingActivityChanged(isLaunchingActivity: Boolean) {
+ callbackSet.forEach { it.onLaunchingActivityChanged(isLaunchingActivity) }
+ }
+}
+
+private class EventSourceStatusBarStartableImpl(
+ private val manager: NotifPanelEventSourceManager,
+ private val notifPanelController: NotificationPanelViewController
+) : StatusBarComponent.Startable {
+
+ override fun start() {
+ manager.eventSource = notifPanelController
+ }
+
+ override fun stop() {
+ manager.eventSource = null
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDiffer.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDiffer.kt
index 28cd28594c3e..386e2d31380c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDiffer.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDiffer.kt
@@ -104,19 +104,14 @@ class ShadeViewDiffer(
views.remove(childNode.controller.view)
}
- if (childCompletelyRemoved && parentSpec == null) {
- // If both the child and the parent are being removed at the same time, then
- // keep the child attached to the parent for animation purposes
- logger.logSkippingDetach(childNode.label, parentNode.label)
- } else {
- logger.logDetachingChild(
- childNode.label,
- !childCompletelyRemoved,
- parentNode.label,
- newParentNode?.label)
- parentNode.removeChild(childNode, !childCompletelyRemoved)
- childNode.parent = null
- }
+ logger.logDetachingChild(
+ key = childNode.label,
+ isTransfer = !childCompletelyRemoved,
+ isParentRemoved = parentSpec == null,
+ oldParent = parentNode.label,
+ newParent = newParentNode?.label)
+ parentNode.removeChild(childNode, isTransfer = !childCompletelyRemoved)
+ childNode.parent = null
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDifferLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDifferLogger.kt
index d27455004c01..4c0357243d48 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDifferLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDifferLogger.kt
@@ -28,25 +28,18 @@ class ShadeViewDifferLogger @Inject constructor(
fun logDetachingChild(
key: String,
isTransfer: Boolean,
+ isParentRemoved: Boolean,
oldParent: String?,
newParent: String?
) {
buffer.log(TAG, LogLevel.DEBUG, {
str1 = key
bool1 = isTransfer
+ bool2 = isParentRemoved
str2 = oldParent
str3 = newParent
}, {
- "Detach $str1 isTransfer=$bool1 oldParent=$str2 newParent=$str3"
- })
- }
-
- fun logSkippingDetach(key: String, parent: String?) {
- buffer.log(TAG, LogLevel.DEBUG, {
- str1 = key
- str2 = parent
- }, {
- "Skipping detach of $str1 because its parent $str2 is also being detached"
+ "Detach $str1 isTransfer=$bool1 isParentRemoved=$bool2 oldParent=$str2 newParent=$str3"
})
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
index 05c40b204c86..45a9092068e4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
@@ -67,6 +67,7 @@ import com.android.systemui.statusbar.notification.collection.render.GroupExpans
import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManagerImpl;
import com.android.systemui.statusbar.notification.collection.render.NotifGutsViewManager;
+import com.android.systemui.statusbar.notification.collection.render.NotifPanelEventSourceModule;
import com.android.systemui.statusbar.notification.collection.render.NotifShadeEventSource;
import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
import com.android.systemui.statusbar.notification.init.NotificationsController;
@@ -101,8 +102,9 @@ import dagger.Provides;
* Dagger Module for classes found within the com.android.systemui.statusbar.notification package.
*/
@Module(includes = {
+ CoordinatorsModule.class,
+ NotifPanelEventSourceModule.class,
NotificationSectionHeadersModule.class,
- CoordinatorsModule.class
})
public interface NotificationsModule {
@Binds
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
index 5d6d0f701f12..fca2aa167e37 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
@@ -162,6 +162,13 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
updateBackgroundTint();
}
+ /**
+ * @return The background of this view.
+ */
+ public NotificationBackgroundView getBackgroundNormal() {
+ return mBackgroundNormal;
+ }
+
@Override
protected void onFinishInflate() {
super.onFinishInflate();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index dbd22db333ff..1f7d93012e39 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -49,6 +49,7 @@ import android.os.Build;
import android.os.Bundle;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.Trace;
import android.provider.Settings;
import android.service.notification.StatusBarNotification;
import android.util.ArraySet;
@@ -1246,6 +1247,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
}
private void reInflateViews() {
+ Trace.beginSection("ExpandableNotificationRow#reInflateViews");
// Let's update our childrencontainer. This is intentionally not guarded with
// mIsSummaryWithChildren since we might have had children but not anymore.
if (mChildrenContainer != null) {
@@ -1277,6 +1279,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
RowContentBindParams params = mRowContentBindStage.getStageParams(mEntry);
params.setNeedsReinflation(true);
mRowContentBindStage.requestRebind(mEntry, null /* callback */);
+ Trace.endSection();
}
@Override
@@ -1737,6 +1740,29 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
}
@Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ Trace.beginSection(appendTraceStyleTag("ExpNotRow#onMeasure"));
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ Trace.endSection();
+ }
+
+ /** Generates and appends "(MessagingStyle)" type tag to passed string for tracing. */
+ @NonNull
+ private String appendTraceStyleTag(@NonNull String traceTag) {
+ if (!Trace.isEnabled()) {
+ return traceTag;
+ }
+
+ Class<? extends Notification.Style> style =
+ getEntry().getSbn().getNotification().getNotificationStyle();
+ if (style == null) {
+ return traceTag + "(nostyle)";
+ } else {
+ return traceTag + "(" + style.getSimpleName() + ")";
+ }
+ }
+
+ @Override
protected void onFinishInflate() {
super.onFinishInflate();
mPublicLayout = findViewById(R.id.expandedPublic);
@@ -2542,6 +2568,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ Trace.beginSection(appendTraceStyleTag("ExpNotRow#onLayout"));
int intrinsicBefore = getIntrinsicHeight();
super.onLayout(changed, left, top, right, bottom);
if (intrinsicBefore != getIntrinsicHeight()
@@ -2555,6 +2582,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
if (mLayoutListener != null) {
mLayoutListener.onLayout();
}
+ Trace.endSection();
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java
index 0f615aa9356f..c640ab6c3a90 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java
@@ -29,6 +29,7 @@ import android.view.View;
import com.android.internal.util.ArrayUtils;
import com.android.systemui.R;
+import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.notification.ExpandAnimationParameters;
/**
@@ -39,15 +40,17 @@ public class NotificationBackgroundView extends View {
private final boolean mDontModifyCorners;
private Drawable mBackground;
private int mClipTopAmount;
- private int mActualHeight;
private int mClipBottomAmount;
private int mTintColor;
private final float[] mCornerRadii = new float[8];
private boolean mBottomIsRounded;
private int mBackgroundTop;
private boolean mBottomAmountClips = true;
+ private int mActualHeight = -1;
+ private int mActualWidth = -1;
private boolean mExpandAnimationRunning;
- private float mActualWidth;
+ private int mExpandAnimationWidth = -1;
+ private int mExpandAnimationHeight = -1;
private int mDrawableAlpha = 255;
private boolean mIsPressedAllowed;
@@ -59,11 +62,12 @@ public class NotificationBackgroundView extends View {
@Override
protected void onDraw(Canvas canvas) {
- if (mClipTopAmount + mClipBottomAmount < mActualHeight - mBackgroundTop
+ if (mClipTopAmount + mClipBottomAmount < getActualHeight() - mBackgroundTop
|| mExpandAnimationRunning) {
canvas.save();
if (!mExpandAnimationRunning) {
- canvas.clipRect(0, mClipTopAmount, getWidth(), mActualHeight - mClipBottomAmount);
+ canvas.clipRect(0, mClipTopAmount, getWidth(),
+ getActualHeight() - mClipBottomAmount);
}
draw(canvas, mBackground);
canvas.restore();
@@ -73,17 +77,23 @@ public class NotificationBackgroundView extends View {
private void draw(Canvas canvas, Drawable drawable) {
if (drawable != null) {
int top = mBackgroundTop;
- int bottom = mActualHeight;
+ int bottom = getActualHeight();
if (mBottomIsRounded
&& mBottomAmountClips
&& !mExpandAnimationRunning) {
bottom -= mClipBottomAmount;
}
- int left = 0;
- int right = getWidth();
+ final boolean isRtl = isLayoutRtl();
+ final int width = getWidth();
+ final int actualWidth = getActualWidth();
+
+ int left = isRtl ? width - actualWidth : 0;
+ int right = isRtl ? width : actualWidth;
+
if (mExpandAnimationRunning) {
- left = (int) ((getWidth() - mActualWidth) / 2.0f);
- right = (int) (left + mActualWidth);
+ // Horizontally center this background view inside of the container
+ left = (int) ((width - actualWidth) / 2.0f);
+ right = (int) (left + actualWidth);
}
drawable.setBounds(left, top, right, bottom);
drawable.draw(canvas);
@@ -152,8 +162,26 @@ public class NotificationBackgroundView extends View {
invalidate();
}
- public int getActualHeight() {
- return mActualHeight;
+ private int getActualHeight() {
+ if (mExpandAnimationRunning && mExpandAnimationHeight > -1) {
+ return mExpandAnimationHeight;
+ } else if (mActualHeight > -1) {
+ return mActualHeight;
+ }
+ return getHeight();
+ }
+
+ public void setActualWidth(int actualWidth) {
+ mActualWidth = actualWidth;
+ }
+
+ private int getActualWidth() {
+ if (mExpandAnimationRunning && mExpandAnimationWidth > -1) {
+ return mExpandAnimationWidth;
+ } else if (mActualWidth > -1) {
+ return mActualWidth;
+ }
+ return getWidth();
}
public void setClipTopAmount(int clipTopAmount) {
@@ -241,9 +269,9 @@ public class NotificationBackgroundView extends View {
}
/** Set the current expand animation size. */
- public void setExpandAnimationSize(int actualWidth, int actualHeight) {
- mActualHeight = actualHeight;
- mActualWidth = actualWidth;
+ public void setExpandAnimationSize(int width, int height) {
+ mExpandAnimationHeight = width;
+ mExpandAnimationWidth = height;
invalidate();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
index 7dc2e1949274..ce3e27c55f3e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
@@ -565,6 +565,10 @@ public class AmbientState {
}
}
+ public float getDozeAmount() {
+ return mDozeAmount;
+ }
+
/**
* Is the device fully awake, which is different from not tark at all when there are pulsing
* notifications.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaContainerView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaContainerView.java
deleted file mode 100644
index bd5b7d7df5b6..000000000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaContainerView.java
+++ /dev/null
@@ -1,46 +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.systemui.statusbar.notification.stack;
-
-import android.animation.AnimatorListenerAdapter;
-import android.content.Context;
-import android.util.AttributeSet;
-
-import com.android.systemui.statusbar.notification.row.ExpandableView;
-
-/**
- * Root view to insert Lock screen media controls into the notification stack.
- */
-public class MediaContainerView extends ExpandableView {
-
- public MediaContainerView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- @Override
- public long performRemoveAnimation(long duration, long delay, float translationDirection,
- boolean isHeadsUpAnimation, float endLocation, Runnable onFinishedRunnable,
- AnimatorListenerAdapter animationListener) {
- return 0;
- }
-
- @Override
- public void performAddAnimation(long delay, long duration, boolean isHeadsUpAppear,
- Runnable onEnd) {
- // No animation, it doesn't need it, this would be local
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaContainerView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaContainerView.kt
new file mode 100644
index 000000000000..b8f28b5a60ea
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaContainerView.kt
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.statusbar.notification.stack
+
+import android.animation.AnimatorListenerAdapter
+import android.content.Context
+import android.content.res.Configuration
+import android.graphics.Canvas
+import android.graphics.Path
+import android.graphics.RectF
+import android.util.AttributeSet
+import com.android.systemui.R
+import com.android.systemui.statusbar.notification.row.ExpandableView
+
+/**
+ * Root view to insert Lock screen media controls into the notification stack.
+ */
+class MediaContainerView(context: Context, attrs: AttributeSet?) : ExpandableView(context, attrs) {
+
+ var cornerRadius = 0f
+ var clipHeight = 0
+ var clipRect = RectF()
+ var clipPath = Path()
+
+ init {
+ setWillNotDraw(false) // Run onDraw after invalidate.
+ updateResources()
+ }
+
+ override fun onConfigurationChanged(newConfig: Configuration?) {
+ super.onConfigurationChanged(newConfig)
+ updateResources()
+ }
+
+ private fun updateResources() {
+ cornerRadius = context.resources
+ .getDimensionPixelSize(R.dimen.notification_corner_radius).toFloat()
+ }
+
+ public override fun updateClipping() {
+ if (clipHeight != actualHeight) {
+ clipHeight = actualHeight
+ }
+ invalidate()
+ }
+
+ override fun onDraw(canvas: Canvas) {
+ super.onDraw(canvas)
+
+ val bounds = canvas.clipBounds
+ bounds.bottom = clipHeight
+ clipRect.set(bounds)
+
+ clipPath.reset()
+ clipPath.addRoundRect(clipRect, cornerRadius, cornerRadius, Path.Direction.CW)
+ canvas.clipPath(clipPath)
+ }
+
+
+ override fun performRemoveAnimation(duration: Long, delay: Long, translationDirection: Float,
+ isHeadsUpAnimation: Boolean, endLocation: Float,
+ onFinishedRunnable: Runnable?,
+ animationListener: AnimatorListenerAdapter?): Long {
+ return 0
+ }
+
+ override fun performAddAnimation(delay: Long, duration: Long, isHeadsUpAppear: Boolean,
+ onEnd: Runnable?) {
+ // No animation, it doesn't need it, this would be local
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index eff8af0fb4c3..dd72615768b0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -199,9 +199,10 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
private int mMaxTopPadding;
private int mTopPadding;
private boolean mAnimateNextTopPaddingChange;
- private int mBottomMargin;
+ private int mBottomPadding;
private int mBottomInset = 0;
private float mQsExpansionFraction;
+ private final int mSplitShadeMinContentHeight;
/**
* The algorithm which calculates the properties for our children
@@ -583,6 +584,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
.getDefaultColor();
int minHeight = res.getDimensionPixelSize(R.dimen.notification_min_height);
int maxHeight = res.getDimensionPixelSize(R.dimen.notification_max_height);
+ mSplitShadeMinContentHeight = res.getDimensionPixelSize(
+ R.dimen.nssl_split_shade_min_content_height);
mExpandHelper = new ExpandHelper(getContext(), mExpandHelperCallback,
minHeight, maxHeight);
mExpandHelper.setEventSource(this);
@@ -978,7 +981,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
mMinTopOverScrollToEscape = res.getDimensionPixelSize(
R.dimen.min_top_overscroll_to_qs);
mStatusBarHeight = SystemBarUtils.getStatusBarHeight(mContext);
- mBottomMargin = res.getDimensionPixelSize(R.dimen.notification_panel_margin_bottom);
+ mBottomPadding = res.getDimensionPixelSize(R.dimen.notification_panel_padding_bottom);
mMinimumPaddings = res.getDimensionPixelSize(R.dimen.notification_side_paddings);
mQsTilePadding = res.getDimensionPixelOffset(R.dimen.qs_tile_margin_horizontal);
mSkinnyNotifsInLandscape = res.getBoolean(R.bool.config_skinnyNotifsInLandscape);
@@ -1273,8 +1276,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
* @param listenerNeedsAnimation does the listener need to animate?
*/
private void updateStackPosition(boolean listenerNeedsAnimation) {
- // Consider interpolating from an mExpansionStartY for use on lockscreen and AOD
- float endTopPosition = mTopPadding + mExtraTopInsetForFullShadeTransition
+ final float endTopPosition = mTopPadding + mExtraTopInsetForFullShadeTransition
+ mAmbientState.getOverExpansion()
- getCurrentOverScrollAmount(false /* top */);
final float fraction = mAmbientState.getExpansionFraction();
@@ -1284,15 +1286,31 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
mOnStackYChanged.accept(listenerNeedsAnimation);
}
if (mQsExpansionFraction <= 0) {
- final float stackEndHeight = Math.max(0f,
- getHeight() - getEmptyBottomMargin() - mTopPadding);
- mAmbientState.setStackEndHeight(stackEndHeight);
- mAmbientState.setStackHeight(
- MathUtils.lerp(stackEndHeight * StackScrollAlgorithm.START_FRACTION,
- stackEndHeight, fraction));
+ final float endHeight = updateStackEndHeight(
+ getHeight(), getEmptyBottomMargin(), mTopPadding);
+ updateStackHeight(endHeight, fraction);
}
}
+ public float updateStackEndHeight(float height, float bottomMargin, float topPadding) {
+ final float stackEndHeight = Math.max(0f, height - bottomMargin - topPadding);
+ mAmbientState.setStackEndHeight(stackEndHeight);
+ return stackEndHeight;
+ }
+
+ public void updateStackHeight(float endHeight, float fraction) {
+ // During the (AOD<=>LS) transition where dozeAmount is changing,
+ // apply dozeAmount to stack height instead of expansionFraction
+ // to unfurl notifications on AOD=>LS wakeup (and furl up on LS=>AOD sleep)
+ final float dozeAmount = mAmbientState.getDozeAmount();
+ if (0f < dozeAmount && dozeAmount < 1f) {
+ fraction = 1f - dozeAmount;
+ }
+ mAmbientState.setStackHeight(
+ MathUtils.lerp(endHeight * StackScrollAlgorithm.START_FRACTION,
+ endHeight, fraction));
+ }
+
/**
* Add a listener when the StackY changes. The argument signifies whether an animation is
* needed.
@@ -2262,7 +2280,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
// The topPadding can be bigger than the regular padding when qs is expanded, in that
// state the maxPanelHeight and the contentHeight should be bigger
- mContentHeight = height + Math.max(mIntrinsicPadding, mTopPadding) + mBottomMargin;
+ mContentHeight = height + Math.max(mIntrinsicPadding, mTopPadding) + mBottomPadding;
updateScrollability();
clampScrollPosition();
updateStackPosition();
@@ -3910,7 +3928,17 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
@ShadeViewRefactor(RefactorComponent.COORDINATOR)
int getEmptyBottomMargin() {
- return Math.max(mMaxLayoutHeight - mContentHeight, 0);
+ int contentHeight;
+ if (mShouldUseSplitNotificationShade) {
+ // When in split shade and there are no notifications, the height can be too low, as
+ // it is based on notifications bottom, which is lower on split shade.
+ // Here we prefer to use at least a minimum height defined for split shade.
+ // Otherwise the expansion motion is too fast.
+ contentHeight = Math.max(mSplitShadeMinContentHeight, mContentHeight);
+ } else {
+ contentHeight = mContentHeight;
+ }
+ return Math.max(mMaxLayoutHeight - contentHeight, 0);
}
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
@@ -5469,6 +5497,17 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
}
/**
+ * @param fraction Fraction of the lockscreen to shade transition. 0f for all other states.
+ * Once the lockscreen to shade transition completes and the shade is 100% open
+ * LockscreenShadeTransitionController resets fraction to 0
+ * where it remains until the next lockscreen-to-shade transition.
+ */
+ public void setFractionToShade(float fraction) {
+ mShelf.setFractionToShade(fraction);
+ requestChildrenUpdate();
+ }
+
+ /**
* Set a listener to when scrolling changes.
*/
public void setOnScrollListener(Consumer<Integer> listener) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index 0d0e5e850523..334128a2b4ca 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -1515,10 +1515,18 @@ public class NotificationStackScrollLayoutController {
}
/**
- * Set the amount of pixels we have currently dragged down if we're transitioning to the full
- * shade. 0.0f means we're not transitioning yet.
+ * @param amount The amount of pixels we have currently dragged down
+ * for the lockscreen to shade transition. 0f for all other states.
+ * @param fraction The fraction of lockscreen to shade transition.
+ * 0f for all other states.
+ *
+ * Once the lockscreen to shade transition completes and the shade is 100% open,
+ * LockscreenShadeTransitionController resets amount and fraction to 0, where they remain
+ * until the next lockscreen-to-shade transition.
*/
- public void setTransitionToFullShadeAmount(float amount) {
+ public void setTransitionToFullShadeAmount(float amount, float fraction) {
+ mView.setFractionToShade(fraction);
+
float extraTopInset = 0.0f;
if (mStatusBarStateController.getState() == KEYGUARD) {
float overallProgress = MathUtils.saturate(amount / mView.getHeight());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
index 8f0579cc4693..e24cd3e9b016 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
@@ -46,7 +46,7 @@ import java.util.List;
*/
public class StackScrollAlgorithm {
- public static final float START_FRACTION = 0.3f;
+ public static final float START_FRACTION = 0.5f;
private static final String LOG_TAG = "StackScrollAlgorithm";
private final ViewGroup mHostView;
@@ -61,7 +61,7 @@ public class StackScrollAlgorithm {
@VisibleForTesting float mHeadsUpInset;
private int mPinnedZTranslationExtra;
private float mNotificationScrimPadding;
- private int mCloseHandleUnderlapHeight;
+ private int mMarginBottom;
public StackScrollAlgorithm(
Context context,
@@ -87,7 +87,7 @@ public class StackScrollAlgorithm {
R.dimen.heads_up_pinned_elevation);
mGapHeight = res.getDimensionPixelSize(R.dimen.notification_section_divider_height);
mNotificationScrimPadding = res.getDimensionPixelSize(R.dimen.notification_side_paddings);
- mCloseHandleUnderlapHeight = res.getDimensionPixelSize(R.dimen.close_handle_underlap);
+ mMarginBottom = res.getDimensionPixelSize(R.dimen.notification_panel_margin_bottom);
}
/**
@@ -463,7 +463,7 @@ public class StackScrollAlgorithm {
}
} else {
if (view instanceof EmptyShadeView) {
- float fullHeight = ambientState.getLayoutMaxHeight() + mCloseHandleUnderlapHeight
+ float fullHeight = ambientState.getLayoutMaxHeight() + mMarginBottom
- ambientState.getStackY();
viewState.yTranslation = (fullHeight - getMaxAllowedChildHeight(view)) / 2f;
} else if (view != ambientState.getTrackedHeadsUpRow()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
index 0d2bddcc8b77..7c9df426aba6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
@@ -44,6 +44,7 @@ public class StackStateAnimator {
public static final int ANIMATION_DURATION_STANDARD = 360;
public static final int ANIMATION_DURATION_CORNER_RADIUS = 200;
public static final int ANIMATION_DURATION_WAKEUP = 500;
+ public static final int ANIMATION_DURATION_WAKEUP_SCRIM = 667;
public static final int ANIMATION_DURATION_GO_TO_FULL_SHADE = 448;
public static final int ANIMATION_DURATION_APPEAR_DISAPPEAR = 464;
public static final int ANIMATION_DURATION_SWIPE = 200;
@@ -343,9 +344,11 @@ public class StackStateAnimator {
for (NotificationStackScrollLayout.AnimationEvent event : animationEvents) {
final ExpandableView changingView = (ExpandableView) event.mChangingView;
boolean loggable = false;
+ boolean isHeadsUp = false;
String key = null;
if (changingView instanceof ExpandableNotificationRow && mLogger != null) {
loggable = true;
+ isHeadsUp = ((ExpandableNotificationRow) changingView).isHeadsUp();
key = ((ExpandableNotificationRow) changingView).getEntry().getKey();
}
if (event.animationType ==
@@ -357,6 +360,9 @@ public class StackStateAnimator {
// The position for this child was never generated, let's continue.
continue;
}
+ if (loggable && isHeadsUp) {
+ mLogger.logHUNViewAppearingWithAddEvent(key);
+ }
viewState.applyToView(changingView);
mNewAddChildren.add(changingView);
@@ -398,9 +404,18 @@ public class StackStateAnimator {
translationDirection = Math.max(Math.min(translationDirection, 1.0f),-1.0f);
}
+ Runnable postAnimation = changingView::removeFromTransientContainer;
+ if (loggable && isHeadsUp) {
+ mLogger.logHUNViewDisappearingWithRemoveEvent(key);
+ String finalKey = key;
+ postAnimation = () -> {
+ mLogger.disappearAnimationEnded(finalKey);
+ changingView.removeFromTransientContainer();
+ };
+ }
changingView.performRemoveAnimation(ANIMATION_DURATION_APPEAR_DISAPPEAR,
0 /* delay */, translationDirection, false /* isHeadsUpAppear */,
- 0, changingView::removeFromTransientContainer, null);
+ 0, postAnimation, null);
} else if (event.animationType ==
NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_REMOVE_SWIPED_OUT) {
if (mHostLayout.isFullySwipedOut(changingView)) {
@@ -430,8 +445,7 @@ public class StackStateAnimator {
// this only captures HEADS_UP_APPEAR animations, but HUNs can appear with normal
// ADD animations, which would not be logged here.
if (loggable) {
- mLogger.logHUNViewAppearing(
- ((ExpandableNotificationRow) changingView).getEntry().getKey());
+ mLogger.logHUNViewAppearing(key);
}
mTmpState.applyToView(changingView);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateLogger.kt
index 4315265e79cc..77377af9ddfb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateLogger.kt
@@ -24,6 +24,22 @@ class StackStateLogger @Inject constructor(
})
}
+ fun logHUNViewDisappearingWithRemoveEvent(key: String) {
+ buffer.log(TAG, LogLevel.ERROR, {
+ str1 = key
+ }, {
+ "Heads up view disappearing $str1 for ANIMATION_TYPE_REMOVE"
+ })
+ }
+
+ fun logHUNViewAppearingWithAddEvent(key: String) {
+ buffer.log(TAG, LogLevel.ERROR, {
+ str1 = key
+ }, {
+ "Heads up view disappearing $str1 for ANIMATION_TYPE_ADD"
+ })
+ }
+
fun disappearAnimationEnded(key: String) {
buffer.log(TAG, LogLevel.INFO, {
str1 = key
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
index 8d500fa4e8b0..04d3e9a01d86 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.phone;
+import static android.app.StatusBarManager.SESSION_KEYGUARD;
+
import android.annotation.IntDef;
import android.content.Context;
import android.content.res.Resources;
@@ -28,7 +30,10 @@ import android.os.SystemClock;
import android.os.Trace;
import android.util.Log;
+import androidx.annotation.Nullable;
+
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.logging.InstanceId;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEvent;
import com.android.internal.logging.UiEventLogger;
@@ -48,6 +53,7 @@ import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.keyguard.WakefulnessLifecycle;
+import com.android.systemui.log.SessionTracker;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationMediaManager;
@@ -156,6 +162,7 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp
private final DozeParameters mDozeParameters;
private final KeyguardStateController mKeyguardStateController;
private final NotificationShadeWindowController mNotificationShadeWindowController;
+ private final SessionTracker mSessionTracker;
private final Context mContext;
private final int mWakeUpDelay;
private int mMode;
@@ -273,7 +280,8 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp
ScreenLifecycle screenLifecycle,
AuthController authController,
StatusBarStateController statusBarStateController,
- KeyguardUnlockAnimationController keyguardUnlockAnimationController) {
+ KeyguardUnlockAnimationController keyguardUnlockAnimationController,
+ SessionTracker sessionTracker) {
mContext = context;
mPowerManager = powerManager;
mShadeController = shadeController;
@@ -297,6 +305,7 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp
mAuthController = authController;
mStatusBarStateController = statusBarStateController;
mKeyguardUnlockAnimationController = keyguardUnlockAnimationController;
+ mSessionTracker = sessionTracker;
dumpManager.registerDumpable(getClass().getName(), this);
}
@@ -376,7 +385,7 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp
mMetricsLogger.write(new LogMaker(MetricsEvent.BIOMETRIC_AUTH)
.setType(MetricsEvent.TYPE_SUCCESS).setSubtype(toSubtype(biometricSourceType)));
Optional.ofNullable(BiometricUiEvent.SUCCESS_EVENT_BY_SOURCE_TYPE.get(biometricSourceType))
- .ifPresent(UI_EVENT_LOGGER::log);
+ .ifPresent(event -> UI_EVENT_LOGGER.log(event, getSessionId()));
boolean unlockAllowed =
mKeyguardStateController.isOccluded()
@@ -641,7 +650,7 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp
mMetricsLogger.write(new LogMaker(MetricsEvent.BIOMETRIC_AUTH)
.setType(MetricsEvent.TYPE_FAILURE).setSubtype(toSubtype(biometricSourceType)));
Optional.ofNullable(BiometricUiEvent.FAILURE_EVENT_BY_SOURCE_TYPE.get(biometricSourceType))
- .ifPresent(UI_EVENT_LOGGER::log);
+ .ifPresent(event -> UI_EVENT_LOGGER.log(event, getSessionId()));
if (biometricSourceType == BiometricSourceType.FINGERPRINT
&& mUpdateMonitor.isUdfpsSupported()) {
@@ -656,7 +665,7 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp
if (mNumConsecutiveFpFailures >= FP_ATTEMPTS_BEFORE_SHOW_BOUNCER) {
startWakeAndUnlock(MODE_SHOW_BOUNCER);
- UI_EVENT_LOGGER.log(BiometricUiEvent.BIOMETRIC_BOUNCER_SHOWN);
+ UI_EVENT_LOGGER.log(BiometricUiEvent.BIOMETRIC_BOUNCER_SHOWN, getSessionId());
mNumConsecutiveFpFailures = 0;
}
}
@@ -670,7 +679,7 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp
.setType(MetricsEvent.TYPE_ERROR).setSubtype(toSubtype(biometricSourceType))
.addTaggedData(MetricsEvent.FIELD_BIOMETRIC_AUTH_ERROR, msgId));
Optional.ofNullable(BiometricUiEvent.ERROR_EVENT_BY_SOURCE_TYPE.get(biometricSourceType))
- .ifPresent(UI_EVENT_LOGGER::log);
+ .ifPresent(event -> UI_EVENT_LOGGER.log(event, getSessionId()));
// if we're on the shade and we're locked out, immediately show the bouncer
if (biometricSourceType == BiometricSourceType.FINGERPRINT
@@ -680,7 +689,7 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp
&& (mStatusBarStateController.getState() == StatusBarState.SHADE
|| mStatusBarStateController.getState() == StatusBarState.SHADE_LOCKED)) {
startWakeAndUnlock(MODE_SHOW_BOUNCER);
- UI_EVENT_LOGGER.log(BiometricUiEvent.BIOMETRIC_BOUNCER_SHOWN);
+ UI_EVENT_LOGGER.log(BiometricUiEvent.BIOMETRIC_BOUNCER_SHOWN, getSessionId());
}
cleanup();
}
@@ -786,6 +795,9 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp
return mBiometricType;
}
+ private @Nullable InstanceId getSessionId() {
+ return mSessionTracker.getSessionId(SESSION_KEYGUARD);
+ }
/**
* Translates biometric source type for logging purpose.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
index 00b54e9e042a..2ec5f250eb48 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
@@ -16,7 +16,6 @@
package com.android.systemui.statusbar.phone;
-import static com.android.systemui.DejankUtils.whitelistIpcs;
import static com.android.systemui.ScreenDecorations.DisplayCutoutView.boundsFromDirection;
import static com.android.systemui.util.Utils.getStatusBarHeaderHeightKeyguard;
@@ -27,7 +26,6 @@ import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
-import android.os.UserManager;
import android.util.AttributeSet;
import android.util.Pair;
import android.util.TypedValue;
@@ -47,7 +45,6 @@ import com.android.systemui.R;
import com.android.systemui.animation.Interpolators;
import com.android.systemui.battery.BatteryMeterView;
import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
-import com.android.systemui.statusbar.window.StatusBarWindowView;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -72,7 +69,6 @@ public class KeyguardStatusBarView extends RelativeLayout {
private StatusIconContainer mStatusIconContainer;
private boolean mKeyguardUserSwitcherEnabled;
- private final UserManager mUserManager;
private boolean mIsPrivacyDotEnabled;
private int mSystemIconsSwitcherHiddenExpandedMargin;
@@ -99,10 +95,10 @@ public class KeyguardStatusBarView extends RelativeLayout {
*/
private int mTopClipping;
private final Rect mClipRect = new Rect(0, 0, 0, 0);
+ private boolean mIsUserSwitcherEnabled;
public KeyguardStatusBarView(Context context, AttributeSet attrs) {
super(context, attrs);
- mUserManager = UserManager.get(getContext());
}
@Override
@@ -163,6 +159,10 @@ public class KeyguardStatusBarView extends RelativeLayout {
updateKeyguardStatusBarHeight();
}
+ public void setUserSwitcherEnabled(boolean enabled) {
+ mIsUserSwitcherEnabled = enabled;
+ }
+
private void updateKeyguardStatusBarHeight() {
MarginLayoutParams lp = (MarginLayoutParams) getLayoutParams();
lp.height = getStatusBarHeaderHeightKeyguard(mContext);
@@ -200,11 +200,7 @@ public class KeyguardStatusBarView extends RelativeLayout {
// If we have no keyguard switcher, the screen width is under 600dp. In this case,
// we only show the multi-user switch if it's enabled through UserManager as well as
// by the user.
- // TODO(b/138661450) Move IPC calls to background
- boolean isMultiUserEnabled = whitelistIpcs(() -> mUserManager.isUserSwitcherEnabled(
- mContext.getResources().getBoolean(
- R.bool.qs_show_user_switcher_for_single_user)));
- if (isMultiUserEnabled) {
+ if (mIsUserSwitcherEnabled) {
mMultiUserAvatar.setVisibility(View.VISIBLE);
} else {
mMultiUserAvatar.setVisibility(View.GONE);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
index 81871634fbaf..ee97fd631818 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
@@ -23,8 +23,10 @@ import static com.android.systemui.statusbar.events.SystemStatusAnimationSchedul
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
+import android.content.res.Configuration;
import android.content.res.Resources;
import android.hardware.biometrics.BiometricSourceType;
+import android.os.UserManager;
import android.util.MathUtils;
import android.view.View;
@@ -92,6 +94,7 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat
private final BiometricUnlockController mBiometricUnlockController;
private final SysuiStatusBarStateController mStatusBarStateController;
private final StatusBarContentInsetsProvider mInsetsProvider;
+ private final UserManager mUserManager;
private final ConfigurationController.ConfigurationListener mConfigurationListener =
new ConfigurationController.ConfigurationListener() {
@@ -105,6 +108,11 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat
mView.onOverlayChanged();
KeyguardStatusBarViewController.this.onThemeChanged();
}
+
+ @Override
+ public void onConfigChanged(Configuration newConfig) {
+ updateUserSwitcher();
+ }
};
private final SystemStatusAnimationCallback mAnimationCallback =
@@ -159,6 +167,13 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat
}
@Override
+ public void onKeyguardVisibilityChanged(boolean showing) {
+ if (showing) {
+ updateUserSwitcher();
+ }
+ }
+
+ @Override
public void onBiometricRunningStateChanged(
boolean running,
BiometricSourceType biometricSourceType) {
@@ -230,7 +245,8 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat
KeyguardUpdateMonitor keyguardUpdateMonitor,
BiometricUnlockController biometricUnlockController,
SysuiStatusBarStateController statusBarStateController,
- StatusBarContentInsetsProvider statusBarContentInsetsProvider
+ StatusBarContentInsetsProvider statusBarContentInsetsProvider,
+ UserManager userManager
) {
super(view);
mCarrierTextController = carrierTextController;
@@ -248,6 +264,7 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat
mBiometricUnlockController = biometricUnlockController;
mStatusBarStateController = statusBarStateController;
mInsetsProvider = statusBarContentInsetsProvider;
+ mUserManager = userManager;
mFirstBypassAttempt = mKeyguardBypassController.getBypassEnabled();
mKeyguardStateController.addCallback(
@@ -293,7 +310,7 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat
}
mView.setOnApplyWindowInsetsListener(
(view, windowInsets) -> mView.updateWindowInsets(windowInsets, mInsetsProvider));
-
+ updateUserSwitcher();
onThemeChanged();
}
@@ -437,6 +454,14 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat
}
/**
+ * Updates visibility of the user switcher button based on {@link android.os.UserManager} state.
+ */
+ private void updateUserSwitcher() {
+ mView.setUserSwitcherEnabled(mUserManager.isUserSwitcherEnabled(getResources().getBoolean(
+ R.bool.qs_show_user_switcher_for_single_user)));
+ }
+
+ /**
* Update {@link KeyguardStatusBarView}'s visibility based on whether keyguard is showing and
* whether heads up is visible.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenGestureLogger.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenGestureLogger.java
index 5f222afd77e5..b4e07f724b28 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenGestureLogger.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenGestureLogger.java
@@ -72,7 +72,10 @@ public class LockscreenGestureLogger {
LOCKSCREEN_NOTIFICATION_FALSE_TOUCH(548),
@UiEvent(doc = "Expand the notification panel while unlocked")
- LOCKSCREEN_UNLOCKED_NOTIFICATION_PANEL_EXPAND(549);
+ LOCKSCREEN_UNLOCKED_NOTIFICATION_PANEL_EXPAND(549),
+
+ @UiEvent(doc = "Lockscreen > Tap on switch user icon")
+ LOCKSCREEN_SWITCH_USER_TAP(934);
private final int mId;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
index c09c48540901..ebfed1a689cf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
@@ -135,7 +135,8 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout {
}
}.setDuration(CONTENT_FADE_DURATION);
- private static final int MAX_VISIBLE_ICONS_ON_LOCK = 5;
+ private static final int MAX_ICONS_ON_AOD = 3;
+ public static final int MAX_ICONS_ON_LOCKSCREEN = 3;
public static final int MAX_STATIC_ICONS = 4;
private static final int MAX_DOTS = 1;
@@ -386,6 +387,19 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout {
}
/**
+ * @return Width of shelf for the given number of icons and overflow dot
+ */
+ public int calculateWidthFor(int numMaxIcons) {
+ if (getChildCount() == 0) {
+ return 0;
+ }
+ return (int) (getActualPaddingStart()
+ + numMaxIcons * mIconSize
+ + mOverflowWidth
+ + getActualPaddingEnd());
+ }
+
+ /**
* Calculate the horizontal translations for each notification based on how much the icons
* are inserted into the notification container.
* If this is not a whole number, the fraction means by how much the icon is appearing.
@@ -394,7 +408,7 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout {
float translationX = getActualPaddingStart();
int firstOverflowIndex = -1;
int childCount = getChildCount();
- int maxVisibleIcons = mOnLockScreen ? MAX_VISIBLE_ICONS_ON_LOCK :
+ int maxVisibleIcons = mOnLockScreen ? MAX_ICONS_ON_AOD :
mIsStaticLayout ? MAX_STATIC_ICONS : childCount;
float layoutEnd = getLayoutEnd();
float overflowStart = getMaxOverflowStart();
@@ -414,7 +428,7 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout {
}
boolean forceOverflow = mSpeedBumpIndex != -1 && i >= mSpeedBumpIndex
&& iconState.iconAppearAmount > 0.0f || i >= maxVisibleIcons;
- boolean noOverflowAfter = i == childCount - 1;
+ boolean isLastChild = i == childCount - 1;
float drawingScale = mOnLockScreen && view instanceof StatusBarIconView
? ((StatusBarIconView) view).getIconScaleIncreased()
: 1f;
@@ -423,10 +437,10 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout {
: StatusBarIconView.STATE_ICON;
boolean isOverflowing =
- (translationX > (noOverflowAfter ? layoutEnd - mIconSize
+ (translationX > (isLastChild ? layoutEnd - mIconSize
: overflowStart - mIconSize));
if (firstOverflowIndex == -1 && (forceOverflow || isOverflowing)) {
- firstOverflowIndex = noOverflowAfter && !forceOverflow ? i - 1 : i;
+ firstOverflowIndex = isLastChild && !forceOverflow ? i - 1 : i;
mVisualOverflowStart = layoutEnd - mOverflowWidth;
if (forceOverflow || mIsStaticLayout) {
mVisualOverflowStart = Math.min(translationX, mVisualOverflowStart);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelUnfoldAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelUnfoldAnimationController.kt
new file mode 100644
index 000000000000..ff48755f750a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelUnfoldAnimationController.kt
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone
+
+import android.content.Context
+import android.view.ViewGroup
+import com.android.systemui.R
+import com.android.systemui.shared.animation.UnfoldConstantTranslateAnimator
+import com.android.systemui.shared.animation.UnfoldConstantTranslateAnimator.Direction.LEFT
+import com.android.systemui.shared.animation.UnfoldConstantTranslateAnimator.Direction.RIGHT
+import com.android.systemui.shared.animation.UnfoldConstantTranslateAnimator.ViewIdToTranslate
+import com.android.systemui.unfold.SysUIUnfoldScope
+import com.android.systemui.unfold.util.NaturalRotationUnfoldProgressProvider
+import javax.inject.Inject
+
+@SysUIUnfoldScope
+class NotificationPanelUnfoldAnimationController
+@Inject
+constructor(private val context: Context, progressProvider: NaturalRotationUnfoldProgressProvider) {
+
+ private val translateAnimator by lazy {
+ UnfoldConstantTranslateAnimator(
+ viewsIdToTranslate =
+ setOf(
+ ViewIdToTranslate(R.id.quick_settings_panel, LEFT),
+ ViewIdToTranslate(R.id.notification_stack_scroller, RIGHT),
+ ViewIdToTranslate(R.id.rightLayout, RIGHT),
+ ViewIdToTranslate(R.id.clock, LEFT),
+ ViewIdToTranslate(R.id.date, LEFT)),
+ progressProvider = progressProvider)
+ }
+
+ fun setup(root: ViewGroup) {
+ val translationMax =
+ context.resources.getDimensionPixelSize(R.dimen.notification_side_paddings).toFloat()
+ translateAnimator.init(root, translationMax)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index de9933e0db6a..3d3a1da1cf82 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -20,6 +20,7 @@ import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
import static android.view.View.GONE;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
+import static androidx.constraintlayout.widget.ConstraintSet.BOTTOM;
import static androidx.constraintlayout.widget.ConstraintSet.END;
import static androidx.constraintlayout.widget.ConstraintSet.PARENT_ID;
import static androidx.constraintlayout.widget.ConstraintSet.START;
@@ -181,6 +182,7 @@ import com.android.systemui.statusbar.notification.ViewGroupFadeHelper;
import com.android.systemui.statusbar.notification.collection.ListEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
+import com.android.systemui.statusbar.notification.collection.render.NotifPanelEventSource;
import com.android.systemui.statusbar.notification.collection.render.ShadeViewManager;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -204,6 +206,7 @@ import com.android.systemui.statusbar.policy.KeyguardUserSwitcherView;
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
import com.android.systemui.statusbar.window.StatusBarWindowStateController;
import com.android.systemui.unfold.SysUIUnfoldComponent;
+import com.android.systemui.util.ListenerSet;
import com.android.systemui.util.Utils;
import com.android.systemui.util.settings.SecureSettings;
import com.android.systemui.wallet.controller.QuickAccessWalletController;
@@ -225,7 +228,8 @@ import javax.inject.Inject;
import javax.inject.Provider;
@StatusBarComponent.StatusBarScope
-public class NotificationPanelViewController extends PanelViewController {
+public class NotificationPanelViewController extends PanelViewController
+ implements NotifPanelEventSource {
private static final boolean DEBUG = false;
@@ -411,6 +415,7 @@ public class NotificationPanelViewController extends PanelViewController {
private int mDisplayTopInset = 0; // in pixels
private int mDisplayRightInset = 0; // in pixels
private int mSplitShadeStatusBarHeight;
+ private int mSplitShadeNotificationsScrimMarginBottom;
private final KeyguardClockPositionAlgorithm
mClockPositionAlgorithm =
@@ -666,6 +671,10 @@ public class NotificationPanelViewController extends PanelViewController {
private boolean mStatusViewCentered = true;
private Optional<KeyguardUnfoldTransition> mKeyguardUnfoldTransition;
+ private Optional<NotificationPanelUnfoldAnimationController>
+ mNotificationPanelUnfoldAnimationController;
+
+ private final ListenerSet<Callbacks> mNotifEventSourceCallbacks = new ListenerSet<>();
private View.AccessibilityDelegate mAccessibilityDelegate = new View.AccessibilityDelegate() {
@Override
@@ -924,6 +933,8 @@ public class NotificationPanelViewController extends PanelViewController {
mMaxKeyguardNotifications = resources.getInteger(R.integer.keyguard_max_notification_count);
mKeyguardUnfoldTransition = unfoldComponent.map(c -> c.getKeyguardUnfoldTransition());
+ mNotificationPanelUnfoldAnimationController = unfoldComponent.map(
+ SysUIUnfoldComponent::getNotificationPanelUnfoldAnimationController);
mCommunalSourceMonitorCallback = (source) -> {
mUiExecutor.execute(() -> setCommunalSource(source));
@@ -1059,6 +1070,8 @@ public class NotificationPanelViewController extends PanelViewController {
mTapAgainViewController.init();
mKeyguardUnfoldTransition.ifPresent(u -> u.setup(mView));
+ mNotificationPanelUnfoldAnimationController.ifPresent(controller ->
+ controller.setup(mNotificationContainerParent));
}
@Override
@@ -1157,6 +1170,9 @@ public class NotificationPanelViewController extends PanelViewController {
public void updateResources() {
mQuickQsOffsetHeight = SystemBarUtils.getQuickQsOffsetHeight(mView.getContext());
mSplitShadeStatusBarHeight = Utils.getSplitShadeStatusBarHeight(mView.getContext());
+ mSplitShadeNotificationsScrimMarginBottom =
+ mResources.getDimensionPixelSize(
+ R.dimen.split_shade_notifications_scrim_margin_bottom);
int qsWidth = mResources.getDimensionPixelSize(R.dimen.qs_panel_width);
int panelWidth = mResources.getDimensionPixelSize(R.dimen.notification_panel_width);
mShouldUseSplitNotificationShade =
@@ -1166,6 +1182,8 @@ public class NotificationPanelViewController extends PanelViewController {
mQs.setInSplitShade(mShouldUseSplitNotificationShade);
}
+ int notificationsBottomMargin = mResources.getDimensionPixelSize(
+ R.dimen.notification_panel_margin_bottom);
int topMargin = mShouldUseSplitNotificationShade ? mSplitShadeStatusBarHeight :
mResources.getDimensionPixelSize(R.dimen.notification_panel_margin_top);
mSplitShadeHeaderController.setSplitShadeMode(mShouldUseSplitNotificationShade);
@@ -1194,9 +1212,12 @@ public class NotificationPanelViewController extends PanelViewController {
constraintSet.getConstraint(R.id.notification_stack_scroller).layout.mWidth = panelWidth;
constraintSet.getConstraint(R.id.qs_frame).layout.mWidth = qsWidth;
constraintSet.setMargin(R.id.notification_stack_scroller, TOP, topMargin);
+ constraintSet.setMargin(R.id.notification_stack_scroller, BOTTOM,
+ notificationsBottomMargin);
constraintSet.setMargin(R.id.qs_frame, TOP, topMargin);
constraintSet.applyTo(mNotificationContainerParent);
mAmbientState.setStackTopMargin(topMargin);
+ mNotificationsQSContainerController.updateMargins();
mNotificationsQSContainerController.setSplitShadeEnabled(mShouldUseSplitNotificationShade);
updateKeyguardStatusViewAlignment(/* animate= */false);
@@ -1314,6 +1335,7 @@ public class NotificationPanelViewController extends PanelViewController {
setKeyguardBottomAreaVisibility(mBarState, false);
mKeyguardUnfoldTransition.ifPresent(u -> u.setup(mView));
+ mNotificationPanelUnfoldAnimationController.ifPresent(u -> u.setup(mView));
}
private void attachSplitShadeMediaPlayerContainer(FrameLayout container) {
@@ -2479,7 +2501,6 @@ public class NotificationPanelViewController extends PanelViewController {
mSplitShadeHeaderController.setShadeExpandedFraction(shadeExpandedFraction);
mSplitShadeHeaderController.setQsExpandedFraction(qsExpansionFraction);
mSplitShadeHeaderController.setShadeExpanded(mQsVisible);
- mKeyguardStatusBarViewController.updateViewState();
if (mCommunalViewController != null) {
mCommunalViewController.updateQsExpansion(qsExpansionFraction);
@@ -2554,7 +2575,8 @@ public class NotificationPanelViewController extends PanelViewController {
right = getView().getRight() + mDisplayRightInset;
} else {
top = Math.min(qsPanelBottomY, mSplitShadeStatusBarHeight);
- bottom = top + mNotificationStackScrollLayoutController.getHeight();
+ bottom = top + mNotificationStackScrollLayoutController.getHeight()
+ - mSplitShadeNotificationsScrimMarginBottom;
left = mNotificationStackScrollLayoutController.getLeft();
right = mNotificationStackScrollLayoutController.getRight();
}
@@ -3435,6 +3457,28 @@ public class NotificationPanelViewController extends PanelViewController {
return mIsLaunchTransitionRunning;
}
+ @Override
+ public void setIsLaunchAnimationRunning(boolean running) {
+ boolean wasRunning = isLaunchTransitionRunning();
+ super.setIsLaunchAnimationRunning(running);
+ if (wasRunning != isLaunchTransitionRunning()) {
+ for (Callbacks cb : mNotifEventSourceCallbacks) {
+ cb.onLaunchingActivityChanged(running);
+ }
+ }
+ }
+
+ @Override
+ protected void setIsClosing(boolean isClosing) {
+ boolean wasClosing = isClosing();
+ super.setIsClosing(isClosing);
+ if (wasClosing != isClosing) {
+ for (Callbacks cb : mNotifEventSourceCallbacks) {
+ cb.onPanelCollapsingChanged(isClosing);
+ }
+ }
+ }
+
public void setLaunchTransitionEndRunnable(Runnable r) {
mLaunchAnimationEndRunnable = r;
}
@@ -4162,9 +4206,10 @@ public class NotificationPanelViewController extends PanelViewController {
return false;
}
- // Do not allow panel expansion if bouncer is scrimmed, otherwise user would be able
- // to pull down QS or expand the shade.
- if (mStatusBar.isBouncerShowingScrimmed()) {
+ // Do not allow panel expansion if bouncer is scrimmed or showing over a dream,
+ // otherwise user would be able to pull down QS or expand the shade.
+ if (mStatusBar.isBouncerShowingScrimmed()
+ || mStatusBar.isBouncerShowingOverDream()) {
return false;
}
@@ -4333,6 +4378,16 @@ public class NotificationPanelViewController extends PanelViewController {
.commitUpdate(mDisplayId);
}
+ @Override
+ public void registerCallbacks(Callbacks callbacks) {
+ mNotifEventSourceCallbacks.addIfAbsent(callbacks);
+ }
+
+ @Override
+ public void unregisterCallbacks(Callbacks callbacks) {
+ mNotifEventSourceCallbacks.remove(callbacks);
+ }
+
private class OnHeightChangedListener implements ExpandableView.OnHeightChangedListener {
@Override
public void onHeightChanged(ExpandableView view, boolean needsAnimation) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQSContainerController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQSContainerController.kt
index 34bb6d3e1a27..1a885336be5e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQSContainerController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQSContainerController.kt
@@ -63,7 +63,7 @@ class NotificationsQSContainerController @Inject constructor(
}
public override fun onViewAttached() {
- notificationsBottomMargin = mView.defaultNotificationsMarginBottom
+ updateMargins()
overviewProxyService.addCallback(taskbarVisibilityListener)
mView.setInsetsChangedListener(windowInsetsListener)
mView.setQSFragmentAttachedListener { qs: QS -> qs.setContainerController(this) }
@@ -75,6 +75,10 @@ class NotificationsQSContainerController @Inject constructor(
mView.removeQSFragmentAttachedListener()
}
+ fun updateMargins() {
+ notificationsBottomMargin = mView.defaultNotificationsMarginBottom
+ }
+
override fun setCustomizerAnimating(animating: Boolean) {
if (isQSCustomizerAnimating != animating) {
isQSCustomizerAnimating = animating
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java
index 9210a8b5db80..cecbcdb829c6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java
@@ -45,7 +45,6 @@ public class NotificationsQuickSettingsContainer extends ConstraintLayout
private View mStackScroller;
private View mKeyguardStatusBar;
- private int mStackScrollerMargin;
private ArrayList<View> mDrawingOrderedChildren = new ArrayList<>();
private ArrayList<View> mLayoutDrawingOrder = new ArrayList<>();
private final Comparator<View> mIndexComparator = Comparator.comparingInt(this::indexOfChild);
@@ -63,7 +62,6 @@ public class NotificationsQuickSettingsContainer extends ConstraintLayout
super.onFinishInflate();
mQsFrame = findViewById(R.id.qs_frame);
mStackScroller = findViewById(R.id.notification_stack_scroller);
- mStackScrollerMargin = ((LayoutParams) mStackScroller.getLayoutParams()).bottomMargin;
mKeyguardStatusBar = findViewById(R.id.keyguard_header);
}
@@ -97,7 +95,7 @@ public class NotificationsQuickSettingsContainer extends ConstraintLayout
}
public int getDefaultNotificationsMarginBottom() {
- return mStackScrollerMargin;
+ return ((LayoutParams) mStackScroller.getLayoutParams()).bottomMargin;
}
public void setInsetsChangedListener(Consumer<WindowInsets> onInsetsChangedListener) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
index 54d0b0351a8f..c466a8ce6d3f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
@@ -73,6 +73,10 @@ import java.io.PrintWriter;
public abstract class PanelViewController {
public static final boolean DEBUG = PanelView.DEBUG;
public static final String TAG = PanelView.class.getSimpleName();
+ public static final float FLING_MAX_LENGTH_SECONDS = 0.6f;
+ public static final float FLING_SPEED_UP_FACTOR = 0.6f;
+ public static final float FLING_CLOSING_MAX_LENGTH_SECONDS = 0.6f;
+ public static final float FLING_CLOSING_SPEED_UP_FACTOR = 0.6f;
private static final int NO_FIXED_DURATION = -1;
private static final long SHADE_OPEN_SPRING_OUT_DURATION = 350L;
private static final long SHADE_OPEN_SPRING_BACK_DURATION = 400L;
@@ -269,13 +273,13 @@ public abstract class PanelViewController {
mNotificationShadeWindowController = notificationShadeWindowController;
mFlingAnimationUtils = flingAnimationUtilsBuilder
.reset()
- .setMaxLengthSeconds(0.6f)
- .setSpeedUpFactor(0.6f)
+ .setMaxLengthSeconds(FLING_MAX_LENGTH_SECONDS)
+ .setSpeedUpFactor(FLING_SPEED_UP_FACTOR)
.build();
mFlingAnimationUtilsClosing = flingAnimationUtilsBuilder
.reset()
- .setMaxLengthSeconds(0.5f)
- .setSpeedUpFactor(0.6f)
+ .setMaxLengthSeconds(FLING_CLOSING_MAX_LENGTH_SECONDS)
+ .setSpeedUpFactor(FLING_CLOSING_SPEED_UP_FACTOR)
.build();
mFlingAnimationUtilsDismissing = flingAnimationUtilsBuilder
.reset()
@@ -506,7 +510,7 @@ public abstract class PanelViewController {
private void endClosing() {
if (mClosing) {
- mClosing = false;
+ setIsClosing(false);
onClosingFinished();
}
}
@@ -581,7 +585,7 @@ public abstract class PanelViewController {
boolean expandBecauseOfFalsing) {
float target = expand ? getMaxPanelHeight() : 0;
if (!expand) {
- mClosing = true;
+ setIsClosing(true);
}
flingToHeight(vel, expand, target, collapseSpeedUpFactor, expandBecauseOfFalsing);
}
@@ -870,7 +874,7 @@ public abstract class PanelViewController {
notifyExpandingStarted();
// Set after notifyExpandingStarted, as notifyExpandingStarted resets the closing state.
- mClosing = true;
+ setIsClosing(true);
if (delayed) {
mNextCollapseSpeedUpFactor = speedUpFactor;
mView.postDelayed(mFlingCollapseRunnable, 120);
@@ -1151,6 +1155,14 @@ public abstract class PanelViewController {
mIsLaunchAnimationRunning = running;
}
+ protected void setIsClosing(boolean isClosing) {
+ mClosing = isClosing;
+ }
+
+ protected boolean isClosing() {
+ return mClosing;
+ }
+
public void collapseWithDuration(int animationDuration) {
mFixedDuration = animationDuration;
collapse(false /* delayed */, 1.0f /* speedUpFactor */);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScreenOffAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScreenOffAnimationController.kt
index 091831f36022..ea61a8b94e7e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScreenOffAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScreenOffAnimationController.kt
@@ -113,14 +113,16 @@ class ScreenOffAnimationController @Inject constructor(
* the animation ends
*/
fun shouldDelayKeyguardShow(): Boolean =
- animations.any { it.shouldPlayAnimation() }
+ animations.any { it.shouldDelayKeyguardShow() }
/**
* Return true while we want to ignore requests to show keyguard, we need to handle pending
* keyguard lock requests manually
+ *
+ * @see [com.android.systemui.keyguard.KeyguardViewMediator.maybeHandlePendingLock]
*/
fun isKeyguardShowDelayed(): Boolean =
- animations.any { it.isAnimationPlaying() }
+ animations.any { it.isKeyguardShowDelayed() }
/**
* Return true to ignore requests to hide keyguard
@@ -211,6 +213,8 @@ interface ScreenOffAnimation {
fun shouldAnimateInKeyguard(): Boolean = false
fun animateInKeyguard(keyguardView: View, after: Runnable) = after.run()
+ fun shouldDelayKeyguardShow(): Boolean = false
+ fun isKeyguardShowDelayed(): Boolean = false
fun isKeyguardHideDelayed(): Boolean = false
fun shouldHideScrimOnWakeUp(): Boolean = false
fun overrideNotificationsDozeAmount(): Boolean = false
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
index d2e1650056ac..ef5f21658d83 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
@@ -62,7 +62,7 @@ public enum ScrimState {
public void prepare(ScrimState previousState) {
mBlankScreen = false;
if (previousState == ScrimState.AOD) {
- mAnimationDuration = StackStateAnimator.ANIMATION_DURATION_WAKEUP;
+ mAnimationDuration = StackStateAnimator.ANIMATION_DURATION_WAKEUP_SCRIM;
if (mDisplayRequiresBlanking) {
// DisplayPowerManager will blank the screen, we'll just
// set our scrim to black in this frame to avoid flickering and
@@ -70,7 +70,7 @@ public enum ScrimState {
mBlankScreen = true;
}
} else if (previousState == ScrimState.KEYGUARD) {
- mAnimationDuration = StackStateAnimator.ANIMATION_DURATION_WAKEUP;
+ mAnimationDuration = StackStateAnimator.ANIMATION_DURATION_WAKEUP_SCRIM;
} else {
mAnimationDuration = ScrimController.ANIMATION_DURATION;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 80ae070f7478..2f3300a53ad6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -150,6 +150,7 @@ import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dagger.qualifiers.UiBackground;
import com.android.systemui.demomode.DemoMode;
import com.android.systemui.demomode.DemoModeController;
+import com.android.systemui.dreams.DreamOverlayStateController;
import com.android.systemui.emergency.EmergencyGesture;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
@@ -338,6 +339,7 @@ public class StatusBar extends CoreStartable implements
}
private final LockscreenShadeTransitionController mLockscreenShadeTransitionController;
+ private final DreamOverlayStateController mDreamOverlayStateController;
private StatusBarCommandQueueCallbacks mCommandQueueCallbacks;
void onStatusBarWindowStateChanged(@WindowVisibleState int state) {
@@ -781,7 +783,8 @@ public class StatusBar extends CoreStartable implements
ActivityLaunchAnimator activityLaunchAnimator,
NotifPipelineFlags notifPipelineFlags,
InteractionJankMonitor jankMonitor,
- DeviceStateManager deviceStateManager) {
+ DeviceStateManager deviceStateManager,
+ DreamOverlayStateController dreamOverlayStateController) {
super(context);
mNotificationsController = notificationsController;
mFragmentService = fragmentService;
@@ -869,6 +872,7 @@ public class StatusBar extends CoreStartable implements
mMessageRouter = messageRouter;
mWallpaperManager = wallpaperManager;
mJankMonitor = jankMonitor;
+ mDreamOverlayStateController = dreamOverlayStateController;
mLockscreenShadeTransitionController = lockscreenShadeTransitionController;
mStartingSurfaceOptional = startingSurfaceOptional;
@@ -1544,6 +1548,12 @@ public class StatusBar extends CoreStartable implements
}
private void inflateStatusBarWindow() {
+ if (mStatusBarComponent != null) {
+ // Tear down
+ for (StatusBarComponent.Startable startable : mStatusBarComponent.getStartables()) {
+ startable.stop();
+ }
+ }
mStatusBarComponent = mStatusBarComponentFactory.create();
mFragmentService.addFragmentInstantiationProvider(mStatusBarComponent);
@@ -1572,6 +1582,11 @@ public class StatusBar extends CoreStartable implements
mCommandQueueCallbacks = mStatusBarComponent.getStatusBarCommandQueueCallbacks();
// Connect in to the status bar manager service
mCommandQueue.addCallback(mCommandQueueCallbacks);
+
+ // Perform all other initialization for StatusBarScope
+ for (StatusBarComponent.Startable startable : mStatusBarComponent.getStartables()) {
+ startable.start();
+ }
}
protected void startKeyguard() {
@@ -2972,6 +2987,7 @@ public class StatusBar extends CoreStartable implements
}
public void showKeyguardImpl() {
+ Trace.beginSection("StatusBar#showKeyguard");
mIsKeyguard = true;
// In case we're locking while a smartspace transition is in progress, reset it.
mKeyguardUnlockAnimationController.resetSmartspaceTransition();
@@ -2986,6 +3002,7 @@ public class StatusBar extends CoreStartable implements
mStatusBarStateController.setState(StatusBarState.KEYGUARD);
}
updatePanelExpansionForKeyguard();
+ Trace.endSection();
}
private void updatePanelExpansionForKeyguard() {
@@ -4133,6 +4150,10 @@ public class StatusBar extends CoreStartable implements
return isBouncerShowing() && mStatusBarKeyguardViewManager.bouncerNeedsScrimming();
}
+ public boolean isBouncerShowingOverDream() {
+ return isBouncerShowing() && mDreamOverlayStateController.isOverlayActive();
+ }
+
/**
* When {@link KeyguardBouncer} starts to be dismissed, playing its animation.
*/
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 316e68227e0c..b833c894c43f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -29,6 +29,7 @@ import android.content.res.ColorStateList;
import android.hardware.biometrics.BiometricSourceType;
import android.os.Bundle;
import android.os.SystemClock;
+import android.os.Trace;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
@@ -51,6 +52,7 @@ import com.android.keyguard.ViewMediatorCallback;
import com.android.systemui.DejankUtils;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dock.DockManager;
+import com.android.systemui.dreams.DreamOverlayStateController;
import com.android.systemui.navigationbar.NavigationBarView;
import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -111,6 +113,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
private final NotificationShadeWindowController mNotificationShadeWindowController;
private final KeyguardBouncer.Factory mKeyguardBouncerFactory;
private final KeyguardMessageAreaController.Factory mKeyguardMessageAreaFactory;
+ private final DreamOverlayStateController mDreamOverlayStateController;
private KeyguardMessageAreaController mKeyguardMessageAreaController;
private final Lazy<ShadeController> mShadeController;
private final BouncerExpansionCallback mExpansionCallback = new BouncerExpansionCallback() {
@@ -235,6 +238,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
SysuiStatusBarStateController sysuiStatusBarStateController,
ConfigurationController configurationController,
KeyguardUpdateMonitor keyguardUpdateMonitor,
+ DreamOverlayStateController dreamOverlayStateController,
NavigationModeController navigationModeController,
DockManager dockManager,
NotificationShadeWindowController notificationShadeWindowController,
@@ -249,6 +253,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
mConfigurationController = configurationController;
mNavigationModeController = navigationModeController;
mNotificationShadeWindowController = notificationShadeWindowController;
+ mDreamOverlayStateController = dreamOverlayStateController;
mKeyguardStateController = keyguardStateController;
mMediaManager = notificationMediaManager;
mKeyguardUpdateManager = keyguardUpdateMonitor;
@@ -370,6 +375,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
*/
@Override
public void show(Bundle options) {
+ Trace.beginSection("StatusBarKeyguardViewManager#show");
mShowing = true;
mNotificationShadeWindowController.setKeyguardShowing(true);
mKeyguardStateController.notifyKeyguardState(mShowing,
@@ -377,6 +383,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
reset(true /* hideBouncerWhenShowing */);
SysUiStatsLog.write(SysUiStatsLog.KEYGUARD_STATE_CHANGED,
SysUiStatsLog.KEYGUARD_STATE_CHANGED__STATE__SHOWN);
+ Trace.endSection();
}
/**
@@ -711,6 +718,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
@Override
public void hide(long startTime, long fadeoutDuration) {
+ Trace.beginSection("StatusBarKeyguardViewManager#hide");
mShowing = false;
mKeyguardStateController.notifyKeyguardState(mShowing,
mKeyguardStateController.isOccluded());
@@ -810,6 +818,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
}
SysUiStatsLog.write(SysUiStatsLog.KEYGUARD_STATE_CHANGED,
SysUiStatsLog.KEYGUARD_STATE_CHANGED__STATE__HIDDEN);
+ Trace.endSection();
}
private boolean needsBypassFading() {
@@ -1174,7 +1183,9 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
}
public boolean bouncerNeedsScrimming() {
- return mOccluded || mBouncer.willDismissWithAction()
+ // When a dream overlay is active, scrimming will cause any expansion to immediately expand.
+ return (mOccluded && !mDreamOverlayStateController.isOverlayActive())
+ || mBouncer.willDismissWithAction()
|| mStatusBar.isFullScreenUserSwitcherState()
|| (mBouncer.isShowing() && mBouncer.isScrimmed())
|| mBouncer.isFullscreenBouncer();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
index e3b4caabb134..d6fc0a426590 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
@@ -23,6 +23,8 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Configuration;
+import android.graphics.Insets;
+import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
@@ -87,11 +89,8 @@ public class SystemUIDialog extends AlertDialog implements ViewRootImpl.ConfigCh
this(context, theme, dismissOnDeviceLock, null);
}
- /**
- * @param udfpsDialogManager If set, UDFPS will hide if this dialog is showing.
- */
public SystemUIDialog(Context context, int theme, boolean dismissOnDeviceLock,
- SystemUIDialogManager dialogManager) {
+ @Nullable SystemUIDialogManager dialogManager) {
super(context, theme);
mContext = context;
@@ -148,7 +147,7 @@ public class SystemUIDialog extends AlertDialog implements ViewRootImpl.ConfigCh
* the device configuration changes, and the result will be used to resize this dialog window.
*/
protected int getWidth() {
- return getDefaultDialogWidth(mContext);
+ return getDefaultDialogWidth(this);
}
/**
@@ -279,36 +278,53 @@ public class SystemUIDialog extends AlertDialog implements ViewRootImpl.ConfigCh
// We need to create the dialog first, otherwise the size will be overridden when it is
// created.
dialog.create();
- dialog.getWindow().setLayout(getDefaultDialogWidth(dialog.getContext()),
- getDefaultDialogHeight());
+ dialog.getWindow().setLayout(getDefaultDialogWidth(dialog), getDefaultDialogHeight());
}
- private static int getDefaultDialogWidth(Context context) {
- boolean isOnTablet = context.getResources().getConfiguration().smallestScreenWidthDp >= 600;
- if (!isOnTablet) {
- return ViewGroup.LayoutParams.MATCH_PARENT;
- }
-
+ private static int getDefaultDialogWidth(Dialog dialog) {
+ Context context = dialog.getContext();
int flagValue = SystemProperties.getInt(FLAG_TABLET_DIALOG_WIDTH, 0);
if (flagValue == -1) {
// The width of bottom sheets (624dp).
- return Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 624,
- context.getResources().getDisplayMetrics()));
+ return calculateDialogWidthWithInsets(dialog, 624);
} else if (flagValue == -2) {
// The suggested small width for all dialogs (348dp)
- return Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 348,
- context.getResources().getDisplayMetrics()));
+ return calculateDialogWidthWithInsets(dialog, 348);
} else if (flagValue > 0) {
// Any given width.
- return Math.round(
- TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, flagValue,
- context.getResources().getDisplayMetrics()));
+ return calculateDialogWidthWithInsets(dialog, flagValue);
} else {
- // By default we use the same width as the notification shade in portrait mode (504dp).
- return context.getResources().getDimensionPixelSize(R.dimen.large_dialog_width);
+ // By default we use the same width as the notification shade in portrait mode.
+ int width = context.getResources().getDimensionPixelSize(R.dimen.large_dialog_width);
+ if (width > 0) {
+ // If we are neither WRAP_CONTENT or MATCH_PARENT, add the background insets so that
+ // the dialog is the desired width.
+ width += getHorizontalInsets(dialog);
+ }
+ return width;
}
}
+ /**
+ * Return the pixel width {@param dialog} should be so that it is {@param widthInDp} wide,
+ * taking its background insets into consideration.
+ */
+ private static int calculateDialogWidthWithInsets(Dialog dialog, int widthInDp) {
+ float widthInPixels = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, widthInDp,
+ dialog.getContext().getResources().getDisplayMetrics());
+ return Math.round(widthInPixels + getHorizontalInsets(dialog));
+ }
+
+ private static int getHorizontalInsets(Dialog dialog) {
+ if (dialog.getWindow().getDecorView() == null) {
+ return 0;
+ }
+
+ Drawable background = dialog.getWindow().getDecorView().getBackground();
+ Insets insets = background != null ? background.getOpticalInsets() : Insets.NONE;
+ return insets.left + insets.right;
+ }
+
private static int getDefaultDialogHeight() {
return ViewGroup.LayoutParams.WRAP_CONTENT;
}
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 cc65ca025139..0abe8e489638 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
@@ -136,6 +136,12 @@ class UnlockedScreenOffAnimationController @Inject constructor(
globalSettings.getFloat(Settings.Global.ANIMATOR_DURATION_SCALE, 1f)
}
+ override fun shouldDelayKeyguardShow(): Boolean =
+ shouldPlayAnimation()
+
+ override fun isKeyguardShowDelayed(): Boolean =
+ isAnimationPlaying()
+
/**
* Animates in the provided keyguard view, ending in the same position that it will be in on
* AOD.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarComponent.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarComponent.java
index 61dba927927a..ad8e79e36661 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarComponent.java
@@ -24,6 +24,7 @@ import com.android.keyguard.LockIconViewController;
import com.android.systemui.biometrics.AuthRippleController;
import com.android.systemui.statusbar.NotificationShelfController;
import com.android.systemui.statusbar.core.StatusBarInitializer;
+import com.android.systemui.statusbar.notification.collection.render.StatusBarNotifPanelEventSourceModule;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
import com.android.systemui.statusbar.phone.NotificationPanelViewController;
import com.android.systemui.statusbar.phone.NotificationShadeWindowView;
@@ -35,6 +36,7 @@ import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
+import java.util.Set;
import javax.inject.Named;
import javax.inject.Scope;
@@ -50,7 +52,10 @@ import dagger.Subcomponent;
* that it has many getter methods indicates that we need to access many of these classes from
* outside the component. Should more items be moved *into* this component to avoid so many getters?
*/
-@Subcomponent(modules = {StatusBarViewModule.class})
+@Subcomponent(modules = {
+ StatusBarNotifPanelEventSourceModule.class,
+ StatusBarViewModule.class
+})
@StatusBarComponent.StatusBarScope
public interface StatusBarComponent {
/**
@@ -70,8 +75,7 @@ public interface StatusBarComponent {
@interface StatusBarScope {}
/**
- * Creates a {@link NotificationShadeWindowView}/
- * @return
+ * Creates a {@link NotificationShadeWindowView}.
*/
@StatusBarScope
NotificationShadeWindowView getNotificationShadeWindowView();
@@ -138,4 +142,18 @@ public interface StatusBarComponent {
*/
@StatusBarScope
StatusBarInitializer getStatusBarInitializer();
+
+ /**
+ * Set of startables to be run after a StatusBarComponent has been constructed.
+ */
+ @StatusBarScope
+ Set<Startable> getStartables();
+
+ /**
+ * Performs initialization logic after {@link StatusBarComponent} has been constructed.
+ */
+ interface Startable {
+ void start();
+ void stop();
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
index f5364b9363b9..d3ff4a78c893 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
@@ -40,6 +40,7 @@ import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dagger.qualifiers.UiBackground;
import com.android.systemui.demomode.DemoModeController;
+import com.android.systemui.dreams.DreamOverlayStateController;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.fragments.FragmentService;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
@@ -231,7 +232,8 @@ public interface StatusBarPhoneModule {
ActivityLaunchAnimator activityLaunchAnimator,
NotifPipelineFlags notifPipelineFlags,
InteractionJankMonitor jankMonitor,
- DeviceStateManager deviceStateManager) {
+ DeviceStateManager deviceStateManager,
+ DreamOverlayStateController dreamOverlayStateController) {
return new StatusBar(
context,
notificationsController,
@@ -327,7 +329,8 @@ public interface StatusBarPhoneModule {
activityLaunchAnimator,
notifPipelineFlags,
jankMonitor,
- deviceStateManager
+ deviceStateManager,
+ dreamOverlayStateController
);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java
index 33f2140b150e..d90363957aa9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java
@@ -23,6 +23,7 @@ import android.annotation.Nullable;
import android.hardware.devicestate.DeviceStateManager;
import android.util.Log;
+import com.android.settingslib.devicestate.DeviceStateRotationLockSettingsManager;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.util.wrapper.RotationPolicyWrapper;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java
index 76615af0ba49..b591545ecb77 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java
@@ -29,6 +29,7 @@ import android.view.ViewGroup;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.FrameLayout;
+import com.android.internal.logging.UiEventLogger;
import com.android.keyguard.KeyguardConstants;
import com.android.keyguard.KeyguardVisibilityHelper;
import com.android.keyguard.dagger.KeyguardUserSwitcherScope;
@@ -49,6 +50,7 @@ import com.android.systemui.statusbar.notification.PropertyAnimator;
import com.android.systemui.statusbar.notification.stack.AnimationProperties;
import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
import com.android.systemui.statusbar.phone.DozeParameters;
+import com.android.systemui.statusbar.phone.LockscreenGestureLogger;
import com.android.systemui.statusbar.phone.NotificationPanelViewController;
import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
import com.android.systemui.statusbar.phone.UserAvatarView;
@@ -82,6 +84,7 @@ public class KeyguardQsUserSwitchController extends ViewController<FrameLayout>
private final KeyguardUserDetailAdapter mUserDetailAdapter;
private final FeatureFlags mFeatureFlags;
private final UserSwitchDialogController mUserSwitchDialogController;
+ private final UiEventLogger mUiEventLogger;
private NotificationPanelViewController mNotificationPanelViewController;
private UserAvatarView mUserAvatarView;
UserSwitcherController.UserRecord mCurrentUser;
@@ -133,7 +136,8 @@ public class KeyguardQsUserSwitchController extends ViewController<FrameLayout>
Provider<UserDetailView.Adapter> userDetailViewAdapterProvider,
ScreenOffAnimationController screenOffAnimationController,
FeatureFlags featureFlags,
- UserSwitchDialogController userSwitchDialogController) {
+ UserSwitchDialogController userSwitchDialogController,
+ UiEventLogger uiEventLogger) {
super(view);
if (DEBUG) Log.d(TAG, "New KeyguardQsUserSwitchController");
mContext = context;
@@ -151,6 +155,7 @@ public class KeyguardQsUserSwitchController extends ViewController<FrameLayout>
mUserDetailAdapter = new KeyguardUserDetailAdapter(context, userDetailViewAdapterProvider);
mFeatureFlags = featureFlags;
mUserSwitchDialogController = userSwitchDialogController;
+ mUiEventLogger = uiEventLogger;
}
@Override
@@ -173,7 +178,10 @@ public class KeyguardQsUserSwitchController extends ViewController<FrameLayout>
return;
}
- // Tapping anywhere in the view will open QS user panel
+ // Tapping anywhere in the view will open the user switcher
+ mUiEventLogger.log(
+ LockscreenGestureLogger.LockscreenUiEvent.LOCKSCREEN_SWITCH_USER_TAP);
+
if (mFeatureFlags.isEnabled(Flags.NEW_USER_SWITCHER)) {
mUserSwitchDialogController.showDialog(mView);
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
index 48949f92413d..3205e097c03b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
@@ -118,6 +118,7 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
private boolean mColorized;
private int mTint;
private boolean mResetting;
+ private boolean mWasSpinning;
// TODO(b/193539698): move these to a Controller
private RemoteInputController mController;
@@ -439,6 +440,10 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
mEditText.requestFocus();
}
}
+ if (mWasSpinning) {
+ mController.addSpinning(mEntry.getKey(), mToken);
+ mWasSpinning = false;
+ }
}
@Override
@@ -447,6 +452,7 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
mEditText.removeTextChangedListener(mTextWatcher);
mEditText.setOnEditorActionListener(null);
mEditText.mRemoteInputView = null;
+ mWasSpinning = mController.isSpinning(mEntry.getKey(), mToken);
if (mEntry.getRow().isChangingPosition() || isTemporarilyDetached()) {
return;
}
@@ -533,6 +539,8 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
if (isActive() && mWrapper != null) {
mWrapper.setRemoteInputVisible(true);
}
+
+ mWasSpinning = false;
}
private void reset() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
index 9f20bc55ebc9..49e712d386e5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
@@ -455,6 +455,13 @@ public class UserSwitcherController implements Dumpable {
}
}
+ /**
+ * Returns whether the current user is a system user.
+ */
+ public boolean isSystemUser() {
+ return mUserTracker.getUserId() == UserHandle.USER_SYSTEM;
+ }
+
public void removeUserId(int userId) {
if (userId == UserHandle.USER_SYSTEM) {
Log.w(TAG, "User " + userId + " could not removed.");
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java
index 60938fb2feb4..c3268354a539 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java
@@ -21,6 +21,7 @@ import android.content.res.Resources;
import android.os.UserManager;
import com.android.internal.R;
+import com.android.settingslib.devicestate.DeviceStateRotationLockSettingsManager;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.settings.UserTracker;
@@ -36,7 +37,6 @@ import com.android.systemui.statusbar.policy.DeviceControlsController;
import com.android.systemui.statusbar.policy.DeviceControlsControllerImpl;
import com.android.systemui.statusbar.policy.DevicePostureController;
import com.android.systemui.statusbar.policy.DevicePostureControllerImpl;
-import com.android.systemui.statusbar.policy.DeviceStateRotationLockSettingsManager;
import com.android.systemui.statusbar.policy.ExtensionController;
import com.android.systemui.statusbar.policy.ExtensionControllerImpl;
import com.android.systemui.statusbar.policy.FlashlightController;
diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIBinder.java b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIBinder.java
index e59d2f233804..d0fb91c9342a 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIBinder.java
@@ -18,6 +18,7 @@ package com.android.systemui.tv;
import com.android.systemui.CoreStartable;
import com.android.systemui.dagger.GlobalRootComponent;
+import com.android.systemui.statusbar.tv.VpnStatusObserver;
import com.android.systemui.statusbar.tv.notifications.TvNotificationHandler;
import dagger.Binds;
@@ -34,4 +35,9 @@ interface TvSystemUIBinder {
@IntoMap
@ClassKey(TvNotificationHandler.class)
CoreStartable bindTvNotificationHandler(TvNotificationHandler systemui);
+
+ @Binds
+ @IntoMap
+ @ClassKey(VpnStatusObserver.class)
+ CoreStartable bindVpnStatusObserver(VpnStatusObserver systemui);
}
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt b/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt
index c481fc94c526..2e627a872c24 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt
@@ -20,7 +20,6 @@ import android.os.Handler
import android.os.PowerManager
import android.provider.Settings
import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.keyguard.KeyguardViewMediator
import com.android.systemui.keyguard.WakefulnessLifecycle
import com.android.systemui.statusbar.LightRevealScrim
import com.android.systemui.statusbar.phone.ScreenOffAnimation
@@ -28,7 +27,6 @@ import com.android.systemui.statusbar.phone.StatusBar
import com.android.systemui.statusbar.policy.CallbackController
import com.android.systemui.unfold.FoldAodAnimationController.FoldAodAnimationStatus
import com.android.systemui.util.settings.GlobalSettings
-import dagger.Lazy
import javax.inject.Inject
/**
@@ -40,7 +38,6 @@ class FoldAodAnimationController
@Inject
constructor(
@Main private val handler: Handler,
- private val keyguardViewMediatorLazy: Lazy<KeyguardViewMediator>,
private val wakefulnessLifecycle: WakefulnessLifecycle,
private val globalSettings: GlobalSettings
) : CallbackController<FoldAodAnimationStatus>, ScreenOffAnimation, WakefulnessLifecycle.Observer {
@@ -57,7 +54,6 @@ constructor(
statusBar.notificationPanelViewController.startFoldToAodAnimation {
// End action
isAnimationPlaying = false
- keyguardViewMediatorLazy.get().maybeHandlePendingLock()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt b/packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt
index 07f9c5487c41..7350b37e4b66 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt
@@ -18,6 +18,7 @@ package com.android.systemui.unfold
import com.android.keyguard.KeyguardUnfoldTransition
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.phone.NotificationPanelUnfoldAnimationController
import com.android.systemui.statusbar.phone.StatusBarMoveFromCenterAnimationController
import com.android.systemui.unfold.util.NaturalRotationUnfoldProgressProvider
import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider
@@ -85,6 +86,8 @@ interface SysUIUnfoldComponent {
fun getStatusBarMoveFromCenterAnimationController(): StatusBarMoveFromCenterAnimationController
+ fun getNotificationPanelUnfoldAnimationController(): NotificationPanelUnfoldAnimationController
+
fun getFoldAodAnimationController(): FoldAodAnimationController
fun getUnfoldTransitionWallpaperController(): UnfoldTransitionWallpaperController
diff --git a/packages/SystemUI/src/com/android/systemui/util/service/Observer.java b/packages/SystemUI/src/com/android/systemui/util/service/Observer.java
new file mode 100644
index 000000000000..768743217cc7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/service/Observer.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util.service;
+
+/**
+ * The {@link Observer} interface specifies an entity which listeners
+ * can be informed of changes to the source, which will require updating. Note that this deals
+ * with changes to the source itself, not content which will be updated through the interface.
+ */
+public interface Observer {
+ /**
+ * Callback for receiving updates from the {@link Observer}.
+ */
+ interface Callback {
+ /**
+ * Invoked when the source has changed.
+ */
+ void onSourceChanged();
+ }
+
+ /**
+ * Adds a callback to receive future updates from the {@link Observer}.
+ */
+ void addCallback(Callback callback);
+
+ /**
+ * Removes a callback from receiving further updates.
+ * @param callback
+ */
+ void removeCallback(Callback callback);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/service/PackageObserver.java b/packages/SystemUI/src/com/android/systemui/util/service/PackageObserver.java
new file mode 100644
index 000000000000..2ee7b20c1f93
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/service/PackageObserver.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util.service;
+
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.PatternMatcher;
+import android.util.Log;
+
+import com.android.systemui.communal.CommunalSource;
+
+import com.google.android.collect.Lists;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Iterator;
+
+import javax.inject.Inject;
+
+/**
+ * {@link PackageObserver} allows for monitoring the system for changes relating to a particular
+ * package. This can be used by {@link CommunalSource} clients to detect when a related package
+ * has changed and reloading is necessary.
+ */
+public class PackageObserver implements Observer {
+ private static final String TAG = "PackageObserver";
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+ private final ArrayList<WeakReference<Callback>> mCallbacks = Lists.newArrayList();
+
+ private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (DEBUG) {
+ Log.d(TAG, "package added receiver - onReceive");
+ }
+
+ final Iterator<WeakReference<Callback>> iter = mCallbacks.iterator();
+ while (iter.hasNext()) {
+ final Callback callback = iter.next().get();
+ if (callback != null) {
+ callback.onSourceChanged();
+ } else {
+ iter.remove();
+ }
+ }
+ }
+ };
+
+ private final String mPackageName;
+ private final Context mContext;
+
+ @Inject
+ public PackageObserver(Context context, ComponentName component) {
+ mContext = context;
+ mPackageName = component.getPackageName();
+ }
+
+ @Override
+ public void addCallback(Callback callback) {
+ if (DEBUG) {
+ Log.d(TAG, "addCallback:" + callback);
+ }
+ mCallbacks.add(new WeakReference<>(callback));
+
+ // Only register for listening to package additions on first callback.
+ if (mCallbacks.size() > 1) {
+ return;
+ }
+
+ final IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
+ filter.addDataScheme("package");
+ filter.addDataSchemeSpecificPart(mPackageName, PatternMatcher.PATTERN_LITERAL);
+ // Note that we directly register the receiver here as data schemes are not supported by
+ // BroadcastDispatcher.
+ mContext.registerReceiver(mReceiver, filter, Context.RECEIVER_EXPORTED);
+ }
+
+ @Override
+ public void removeCallback(Callback callback) {
+ if (DEBUG) {
+ Log.d(TAG, "removeCallback:" + callback);
+ }
+ final boolean removed = mCallbacks.removeIf(el -> el.get() == callback);
+
+ if (removed && mCallbacks.isEmpty()) {
+ mContext.unregisterReceiver(mReceiver);
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/service/PersistentConnectionManager.java b/packages/SystemUI/src/com/android/systemui/util/service/PersistentConnectionManager.java
new file mode 100644
index 000000000000..292c062369c1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/service/PersistentConnectionManager.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util.service;
+
+import static com.android.systemui.util.service.dagger.ObservableServiceModule.BASE_RECONNECT_DELAY_MS;
+import static com.android.systemui.util.service.dagger.ObservableServiceModule.MAX_RECONNECT_ATTEMPTS;
+import static com.android.systemui.util.service.dagger.ObservableServiceModule.MIN_CONNECTION_DURATION_MS;
+import static com.android.systemui.util.service.dagger.ObservableServiceModule.OBSERVER;
+import static com.android.systemui.util.service.dagger.ObservableServiceModule.SERVICE_CONNECTION;
+
+import android.util.Log;
+
+import com.android.systemui.util.concurrency.DelayableExecutor;
+import com.android.systemui.util.time.SystemClock;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+
+/**
+ * The {@link PersistentConnectionManager} is responsible for maintaining a connection to a
+ * {@link ObservableServiceConnection}.
+ * @param <T> The transformed connection type handled by the service.
+ */
+public class PersistentConnectionManager<T> {
+ private static final String TAG = "PersistentConnManager";
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+ private final SystemClock mSystemClock;
+ private final DelayableExecutor mMainExecutor;
+ private final int mBaseReconnectDelayMs;
+ private final int mMaxReconnectAttempts;
+ private final int mMinConnectionDuration;
+ private final Observer mObserver;
+
+ private int mReconnectAttempts = 0;
+ private Runnable mCurrentReconnectCancelable;
+
+ private final ObservableServiceConnection<T> mConnection;
+
+ private final Runnable mConnectRunnable = new Runnable() {
+ @Override
+ public void run() {
+ mCurrentReconnectCancelable = null;
+ mConnection.bind();
+ }
+ };
+
+ private final Observer.Callback mObserverCallback = () -> initiateConnectionAttempt();
+
+ private final ObservableServiceConnection.Callback mConnectionCallback =
+ new ObservableServiceConnection.Callback() {
+ private long mStartTime;
+
+ @Override
+ public void onConnected(ObservableServiceConnection connection, Object proxy) {
+ mStartTime = mSystemClock.currentTimeMillis();
+ }
+
+ @Override
+ public void onDisconnected(ObservableServiceConnection connection, int reason) {
+ if (mSystemClock.currentTimeMillis() - mStartTime > mMinConnectionDuration) {
+ initiateConnectionAttempt();
+ } else {
+ scheduleConnectionAttempt();
+ }
+ }
+ };
+
+ @Inject
+ public PersistentConnectionManager(
+ SystemClock clock,
+ DelayableExecutor mainExecutor,
+ @Named(SERVICE_CONNECTION) ObservableServiceConnection<T> serviceConnection,
+ @Named(MAX_RECONNECT_ATTEMPTS) int maxReconnectAttempts,
+ @Named(BASE_RECONNECT_DELAY_MS) int baseReconnectDelayMs,
+ @Named(MIN_CONNECTION_DURATION_MS) int minConnectionDurationMs,
+ @Named(OBSERVER) Observer observer) {
+ mSystemClock = clock;
+ mMainExecutor = mainExecutor;
+ mConnection = serviceConnection;
+ mObserver = observer;
+
+ mMaxReconnectAttempts = maxReconnectAttempts;
+ mBaseReconnectDelayMs = baseReconnectDelayMs;
+ mMinConnectionDuration = minConnectionDurationMs;
+ }
+
+ /**
+ * Begins the {@link PersistentConnectionManager} by connecting to the associated service.
+ */
+ public void start() {
+ mConnection.addCallback(mConnectionCallback);
+ mObserver.addCallback(mObserverCallback);
+ initiateConnectionAttempt();
+ }
+
+ /**
+ * Brings down the {@link PersistentConnectionManager}, disconnecting from the service.
+ */
+ public void stop() {
+ mConnection.removeCallback(mConnectionCallback);
+ mObserver.removeCallback(mObserverCallback);
+ mConnection.unbind();
+ }
+
+ private void initiateConnectionAttempt() {
+ // Reset attempts
+ mReconnectAttempts = 0;
+
+ // The first attempt is always a direct invocation rather than delayed.
+ mConnection.bind();
+ }
+
+ private void scheduleConnectionAttempt() {
+ // always clear cancelable if present.
+ if (mCurrentReconnectCancelable != null) {
+ mCurrentReconnectCancelable.run();
+ mCurrentReconnectCancelable = null;
+ }
+
+ if (mReconnectAttempts >= mMaxReconnectAttempts) {
+ if (DEBUG) {
+ Log.d(TAG, "exceeded max connection attempts.");
+ }
+ return;
+ }
+
+ final long reconnectDelayMs =
+ (long) Math.scalb(mBaseReconnectDelayMs, mReconnectAttempts);
+
+ if (DEBUG) {
+ Log.d(TAG,
+ "scheduling connection attempt in " + reconnectDelayMs + "milliseconds");
+ }
+
+ mCurrentReconnectCancelable = mMainExecutor.executeDelayed(mConnectRunnable,
+ reconnectDelayMs);
+
+ mReconnectAttempts++;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/service/dagger/ObservableServiceModule.java b/packages/SystemUI/src/com/android/systemui/util/service/dagger/ObservableServiceModule.java
new file mode 100644
index 000000000000..c62c95755ce8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/service/dagger/ObservableServiceModule.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package com.android.systemui.util.service.dagger;
+
+import android.content.res.Resources;
+
+import com.android.systemui.R;
+import com.android.systemui.dagger.qualifiers.Main;
+
+import javax.inject.Named;
+
+import dagger.Module;
+import dagger.Provides;
+
+/**
+ * Module containing components and parameters for
+ * {@link com.android.systemui.util.service.ObservableServiceConnection}
+ * and {@link com.android.systemui.util.service.PersistentConnectionManager}.
+ */
+@Module(subcomponents = {
+ PackageObserverComponent.class,
+})
+public class ObservableServiceModule {
+ public static final String MAX_RECONNECT_ATTEMPTS = "max_reconnect_attempts";
+ public static final String BASE_RECONNECT_DELAY_MS = "base_reconnect_attempts";
+ public static final String MIN_CONNECTION_DURATION_MS = "min_connection_duration_ms";
+ public static final String SERVICE_CONNECTION = "service_connection";
+ public static final String OBSERVER = "observer";
+
+ @Provides
+ @Named(MAX_RECONNECT_ATTEMPTS)
+ static int providesMaxReconnectAttempts(@Main Resources resources) {
+ return resources.getInteger(
+ R.integer.config_communalSourceMaxReconnectAttempts);
+ }
+
+ @Provides
+ @Named(BASE_RECONNECT_DELAY_MS)
+ static int provideBaseReconnectDelayMs(@Main Resources resources) {
+ return resources.getInteger(
+ R.integer.config_communalSourceReconnectBaseDelay);
+ }
+
+ @Provides
+ @Named(MIN_CONNECTION_DURATION_MS)
+ static int providesMinConnectionDuration(@Main Resources resources) {
+ return resources.getInteger(
+ R.integer.config_connectionMinDuration);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/service/dagger/PackageObserverComponent.java b/packages/SystemUI/src/com/android/systemui/util/service/dagger/PackageObserverComponent.java
new file mode 100644
index 000000000000..8ee39b34cdf5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/service/dagger/PackageObserverComponent.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.util.service.dagger;
+
+import android.content.ComponentName;
+
+import com.android.systemui.util.service.PackageObserver;
+
+import dagger.BindsInstance;
+import dagger.Subcomponent;
+
+/**
+ * Generates a scoped {@link PackageObserver}.
+ */
+@Subcomponent
+public interface PackageObserverComponent {
+ /**
+ * Generates a {@link PackageObserverComponent} instance.
+ */
+ @Subcomponent.Factory
+ interface Factory {
+ PackageObserverComponent create(@BindsInstance ComponentName component);
+ }
+
+ /**
+ * Creates a {@link PackageObserver}.
+ */
+ PackageObserver getPackageObserver();
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
index 11725ef4867e..57c7f11b752d 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
@@ -33,7 +33,6 @@ import android.media.AudioManager;
import android.media.AudioSystem;
import android.media.IAudioService;
import android.media.IVolumeController;
-import android.media.MediaRoute2Info;
import android.media.MediaRouter2Manager;
import android.media.RoutingSessionInfo;
import android.media.VolumePolicy;
@@ -47,7 +46,6 @@ import android.os.Message;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.VibrationEffect;
-import android.os.Vibrator;
import android.provider.Settings;
import android.service.notification.Condition;
import android.service.notification.ZenModeConfig;
@@ -68,6 +66,7 @@ import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.plugins.VolumeDialogController;
import com.android.systemui.qs.tiles.DndTile;
+import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.util.RingerModeLiveData;
import com.android.systemui.util.RingerModeTracker;
import com.android.systemui.util.concurrency.ThreadFactory;
@@ -78,7 +77,6 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
-import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import javax.inject.Inject;
@@ -135,7 +133,7 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa
protected C mCallbacks = new C();
private final State mState = new State();
protected final MediaSessionsCallbacks mMediaSessionsCallbacksW;
- private final Optional<Vibrator> mVibrator;
+ private final VibratorHelper mVibrator;
private final boolean mHasVibrator;
private boolean mShowA11yStream;
private boolean mShowVolumeDialog;
@@ -173,7 +171,7 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa
ThreadFactory theadFactory,
AudioManager audioManager,
NotificationManager notificationManager,
- Optional<Vibrator> optionalVibrator,
+ VibratorHelper vibrator,
IAudioService iAudioService,
AccessibilityManager accessibilityManager,
PackageManager packageManager,
@@ -199,8 +197,8 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa
mBroadcastDispatcher = broadcastDispatcher;
mObserver.init();
mReceiver.init();
- mVibrator = optionalVibrator;
- mHasVibrator = mVibrator.isPresent() && mVibrator.get().hasVibrator();
+ mVibrator = vibrator;
+ mHasVibrator = mVibrator.hasVibrator();
mAudioService = iAudioService;
boolean accessibilityVolumeStreamActive = accessibilityManager
@@ -393,8 +391,7 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa
}
public void vibrate(VibrationEffect effect) {
- mVibrator.ifPresent(
- vibrator -> vibrator.vibrate(effect, SONIFICIATION_VIBRATION_ATTRIBUTES));
+ mVibrator.vibrate(effect, SONIFICIATION_VIBRATION_ATTRIBUTES);
}
public boolean hasVibrator() {
@@ -402,7 +399,7 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa
}
private void onNotifyVisibleW(boolean visible) {
- if (mDestroyed) return;
+ if (mDestroyed) return;
mAudio.notifyVolumeControllerVisible(mVolumeController, visible);
if (!visible) {
if (updateActiveStreamW(-1)) {
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardBiometricLockoutLoggerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardBiometricLockoutLoggerTest.kt
index 6bc65054d830..aa671d1e3790 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardBiometricLockoutLoggerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardBiometricLockoutLoggerTest.kt
@@ -20,11 +20,12 @@ import android.hardware.biometrics.BiometricSourceType
import org.mockito.Mockito.verify
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
+import com.android.internal.logging.InstanceId
import com.android.internal.logging.UiEventLogger
import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_TIMEOUT
import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE
import com.android.systemui.SysuiTestCase
-import com.android.systemui.dump.DumpManager
+import com.android.systemui.log.SessionTracker
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -44,9 +45,11 @@ class KeyguardBiometricLockoutLoggerTest : SysuiTestCase() {
@Mock
lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
@Mock
- lateinit var dumpManager: DumpManager
- @Mock
lateinit var strongAuthTracker: KeyguardUpdateMonitor.StrongAuthTracker
+ @Mock
+ lateinit var sessionTracker: SessionTracker
+ @Mock
+ lateinit var sessionId: InstanceId
@Captor
lateinit var updateMonitorCallbackCaptor: ArgumentCaptor<KeyguardUpdateMonitorCallback>
@@ -58,11 +61,12 @@ class KeyguardBiometricLockoutLoggerTest : SysuiTestCase() {
fun setUp() {
MockitoAnnotations.initMocks(this)
whenever(keyguardUpdateMonitor.strongAuthTracker).thenReturn(strongAuthTracker)
+ whenever(sessionTracker.getSessionId(anyInt())).thenReturn(sessionId)
keyguardBiometricLockoutLogger = KeyguardBiometricLockoutLogger(
mContext,
uiEventLogger,
keyguardUpdateMonitor,
- dumpManager)
+ sessionTracker)
}
@Test
@@ -76,7 +80,7 @@ class KeyguardBiometricLockoutLoggerTest : SysuiTestCase() {
// THEN encrypted / lockdown state is logged
verify(uiEventLogger).log(KeyguardBiometricLockoutLogger.PrimaryAuthRequiredEvent
- .PRIMARY_AUTH_REQUIRED_ENCRYPTED_OR_LOCKDOWN)
+ .PRIMARY_AUTH_REQUIRED_ENCRYPTED_OR_LOCKDOWN, sessionId)
}
@Test
@@ -93,7 +97,7 @@ class KeyguardBiometricLockoutLoggerTest : SysuiTestCase() {
// THEN primary auth required state is logged
verify(uiEventLogger).log(KeyguardBiometricLockoutLogger.PrimaryAuthRequiredEvent
- .PRIMARY_AUTH_REQUIRED_TIMEOUT)
+ .PRIMARY_AUTH_REQUIRED_TIMEOUT, sessionId)
}
@Test
@@ -110,7 +114,7 @@ class KeyguardBiometricLockoutLoggerTest : SysuiTestCase() {
// THEN primary auth required state is logged
verify(uiEventLogger).log(KeyguardBiometricLockoutLogger.PrimaryAuthRequiredEvent
- .PRIMARY_AUTH_REQUIRED_UNATTENDED_UPDATE)
+ .PRIMARY_AUTH_REQUIRED_UNATTENDED_UPDATE, sessionId)
}
@Test
@@ -128,9 +132,9 @@ class KeyguardBiometricLockoutLoggerTest : SysuiTestCase() {
// THEN primary auth required state is logged with all the reasons
verify(uiEventLogger).log(KeyguardBiometricLockoutLogger.PrimaryAuthRequiredEvent
- .PRIMARY_AUTH_REQUIRED_TIMEOUT)
+ .PRIMARY_AUTH_REQUIRED_TIMEOUT, sessionId)
verify(uiEventLogger).log(KeyguardBiometricLockoutLogger.PrimaryAuthRequiredEvent
- .PRIMARY_AUTH_REQUIRED_UNATTENDED_UPDATE)
+ .PRIMARY_AUTH_REQUIRED_UNATTENDED_UPDATE, sessionId)
// WHEN onStrongAuthStateChanged is called again
updateMonitorCallback.onStrongAuthStateChanged(0)
@@ -152,7 +156,7 @@ class KeyguardBiometricLockoutLoggerTest : SysuiTestCase() {
// THEN primary auth required state is logged
verify(uiEventLogger).log(KeyguardBiometricLockoutLogger.PrimaryAuthRequiredEvent
- .PRIMARY_AUTH_REQUIRED_FACE_LOCKED_OUT)
+ .PRIMARY_AUTH_REQUIRED_FACE_LOCKED_OUT, sessionId)
// WHEN face lockout is reset
whenever(keyguardUpdateMonitor.isFaceLockedOut).thenReturn(false)
@@ -160,7 +164,7 @@ class KeyguardBiometricLockoutLoggerTest : SysuiTestCase() {
// THEN primary auth required state is logged
verify(uiEventLogger).log(KeyguardBiometricLockoutLogger.PrimaryAuthRequiredEvent
- .PRIMARY_AUTH_REQUIRED_FACE_LOCKED_OUT_RESET)
+ .PRIMARY_AUTH_REQUIRED_FACE_LOCKED_OUT_RESET, sessionId)
}
@Test
@@ -176,7 +180,7 @@ class KeyguardBiometricLockoutLoggerTest : SysuiTestCase() {
// THEN primary auth required state is logged
verify(uiEventLogger).log(KeyguardBiometricLockoutLogger.PrimaryAuthRequiredEvent
- .PRIMARY_AUTH_REQUIRED_FINGERPRINT_LOCKED_OUT)
+ .PRIMARY_AUTH_REQUIRED_FINGERPRINT_LOCKED_OUT, sessionId)
// WHEN fingerprint lockout is reset
whenever(keyguardUpdateMonitor.isFingerprintLockedOut).thenReturn(false)
@@ -184,7 +188,7 @@ class KeyguardBiometricLockoutLoggerTest : SysuiTestCase() {
// THEN primary auth required state is logged
verify(uiEventLogger).log(KeyguardBiometricLockoutLogger.PrimaryAuthRequiredEvent
- .PRIMARY_AUTH_REQUIRED_FINGERPRINT_LOCKED_OUT_RESET)
+ .PRIMARY_AUTH_REQUIRED_FINGERPRINT_LOCKED_OUT_RESET, sessionId)
}
fun captureUpdateMonitorCallback() {
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
index 599e5474c564..a819a7a0f815 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
@@ -49,6 +49,7 @@ import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.log.SessionTracker;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -120,6 +121,8 @@ public class KeyguardSecurityContainerControllerTest extends SysuiTestCase {
private FeatureFlags mFeatureFlags;
@Mock
private UserSwitcherController mUserSwitcherController;
+ @Mock
+ private SessionTracker mSessionTracker;
private Configuration mConfiguration;
private KeyguardSecurityContainerController mKeyguardSecurityContainerController;
@@ -154,7 +157,8 @@ public class KeyguardSecurityContainerControllerTest extends SysuiTestCase {
mKeyguardUpdateMonitor, mKeyguardSecurityModel, mMetricsLogger, mUiEventLogger,
mKeyguardStateController, mKeyguardSecurityViewFlipperController,
mConfigurationController, mFalsingCollector, mFalsingManager,
- mUserSwitcherController, mFeatureFlags, mGlobalSettings).create(mSecurityCallback);
+ mUserSwitcherController, mFeatureFlags, mGlobalSettings,
+ mSessionTracker).create(mSecurityCallback);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUnfoldTransitionTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUnfoldTransitionTest.kt
index 164f83dda9b7..6c1f008e9337 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUnfoldTransitionTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUnfoldTransitionTest.kt
@@ -20,23 +20,21 @@ import android.testing.AndroidTestingRunner
import android.view.View
import android.view.ViewGroup
import androidx.test.filters.SmallTest
-import com.android.keyguard.KeyguardUnfoldTransition.Companion.LEFT
-import com.android.keyguard.KeyguardUnfoldTransition.Companion.RIGHT
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
import com.android.systemui.unfold.util.NaturalRotationUnfoldProgressProvider
import com.android.systemui.util.mockito.capture
-import org.junit.Assert.assertEquals
+import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.Captor
import org.mockito.Mock
-import org.mockito.MockitoAnnotations
-import org.mockito.Mockito.`when`
import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
+import org.mockito.MockitoAnnotations
/**
* Translates items away/towards the hinge when the device is opened/closed. This is controlled by
@@ -46,14 +44,11 @@ import org.mockito.Mockito.verify
@RunWith(AndroidTestingRunner::class)
class KeyguardUnfoldTransitionTest : SysuiTestCase() {
- @Mock
- private lateinit var progressProvider: NaturalRotationUnfoldProgressProvider
+ @Mock private lateinit var progressProvider: NaturalRotationUnfoldProgressProvider
- @Captor
- private lateinit var progressListenerCaptor: ArgumentCaptor<TransitionProgressListener>
+ @Captor private lateinit var progressListenerCaptor: ArgumentCaptor<TransitionProgressListener>
- @Mock
- private lateinit var parent: ViewGroup
+ @Mock private lateinit var parent: ViewGroup
private lateinit var keyguardUnfoldTransition: KeyguardUnfoldTransition
private lateinit var progressListener: TransitionProgressListener
@@ -63,87 +58,35 @@ class KeyguardUnfoldTransitionTest : SysuiTestCase() {
fun setup() {
MockitoAnnotations.initMocks(this)
- xTranslationMax = context.resources.getDimensionPixelSize(
- R.dimen.keyguard_unfold_translation_x).toFloat()
+ xTranslationMax =
+ context.resources.getDimensionPixelSize(R.dimen.keyguard_unfold_translation_x).toFloat()
- keyguardUnfoldTransition = KeyguardUnfoldTransition(
- getContext(),
- progressProvider
- )
-
- verify(progressProvider).addCallback(capture(progressListenerCaptor))
- progressListener = progressListenerCaptor.value
+ keyguardUnfoldTransition = KeyguardUnfoldTransition(context, progressProvider)
keyguardUnfoldTransition.setup(parent)
keyguardUnfoldTransition.statusViewCentered = false
- }
-
- @Test
- fun onTransition_noMatchingIds() {
- // GIVEN no views matching any ids
- // WHEN the transition starts
- progressListener.onTransitionStarted()
- progressListener.onTransitionProgress(.1f)
-
- // THEN nothing... no exceptions
- }
- @Test
- fun onTransition_oneMovesLeft() {
- // GIVEN one view with a matching id
- val view = View(getContext())
- `when`(parent.findViewById<View>(R.id.keyguard_status_area)).thenReturn(view)
-
- moveAndValidate(listOf(view to LEFT))
- }
-
- @Test
- fun onTransition_oneMovesLeftAndOneMovesRightMultipleTimes() {
- // GIVEN two views with a matching id
- val leftView = View(getContext())
- val rightView = View(getContext())
- `when`(parent.findViewById<View>(R.id.keyguard_status_area)).thenReturn(leftView)
- `when`(parent.findViewById<View>(R.id.notification_stack_scroller)).thenReturn(rightView)
-
- moveAndValidate(listOf(leftView to LEFT, rightView to RIGHT))
- moveAndValidate(listOf(leftView to LEFT, rightView to RIGHT))
+ verify(progressProvider).addCallback(capture(progressListenerCaptor))
+ progressListener = progressListenerCaptor.value
}
@Test
fun onTransition_centeredViewDoesNotMove() {
keyguardUnfoldTransition.statusViewCentered = true
- val view = View(getContext())
+ val view = View(context)
`when`(parent.findViewById<View>(R.id.lockscreen_clock_view_large)).thenReturn(view)
- moveAndValidate(listOf(view to 0))
- }
-
- private fun moveAndValidate(list: List<Pair<View, Int>>) {
- // Compare values as ints because -0f != 0f
-
- // WHEN the transition starts
progressListener.onTransitionStarted()
+ assertThat(view.translationX).isZero()
+
progressListener.onTransitionProgress(0f)
+ assertThat(view.translationX).isZero()
+
+ progressListener.onTransitionProgress(0.5f)
+ assertThat(view.translationX).isZero()
- list.forEach { (view, direction) ->
- assertEquals((-xTranslationMax * direction).toInt(), view.getTranslationX().toInt())
- }
-
- // WHEN the transition progresses, translation is updated
- progressListener.onTransitionProgress(.5f)
- list.forEach { (view, direction) ->
- assertEquals(
- (-xTranslationMax / 2f * direction).toInt(),
- view.getTranslationX().toInt()
- )
- }
-
- // WHEN the transition ends, translation is completed
- progressListener.onTransitionProgress(1f)
progressListener.onTransitionFinished()
- list.forEach { (view, _) ->
- assertEquals(0, view.getTranslationX().toInt())
- }
+ assertThat(view.translationX).isZero()
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
index 5d39eef999d7..c37e966f3540 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
@@ -27,10 +27,12 @@ import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -46,6 +48,7 @@ import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricManager;
import android.hardware.biometrics.BiometricPrompt;
import android.hardware.biometrics.ComponentInfoInternal;
+import android.hardware.biometrics.IBiometricContextListener;
import android.hardware.biometrics.IBiometricSysuiReceiver;
import android.hardware.biometrics.PromptInfo;
import android.hardware.biometrics.SensorProperties;
@@ -70,6 +73,7 @@ import androidx.test.filters.SmallTest;
import com.android.internal.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.keyguard.WakefulnessLifecycle;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.util.concurrency.Execution;
import com.android.systemui.util.concurrency.FakeExecution;
@@ -80,6 +84,7 @@ import org.junit.runner.RunWith;
import org.mockito.AdditionalMatchers;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
+import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -99,6 +104,8 @@ public class AuthControllerTest extends SysuiTestCase {
@Mock
private IBiometricSysuiReceiver mReceiver;
@Mock
+ private IBiometricContextListener mContextListener;
+ @Mock
private AuthDialog mDialog1;
@Mock
private AuthDialog mDialog2;
@@ -120,10 +127,14 @@ public class AuthControllerTest extends SysuiTestCase {
private DisplayManager mDisplayManager;
@Mock
private WakefulnessLifecycle mWakefulnessLifecycle;
+ @Mock
+ private StatusBarStateController mStatusBarStateController;
@Captor
ArgumentCaptor<IFingerprintAuthenticatorsRegisteredCallback> mAuthenticatorsRegisteredCaptor;
@Captor
ArgumentCaptor<FingerprintStateListener> mFingerprintStateCaptor;
+ @Captor
+ ArgumentCaptor<StatusBarStateController.StateListener> mStatusBarStateListenerCaptor;
private TestableContext mContextSpy;
private Execution mExecution;
@@ -175,12 +186,15 @@ public class AuthControllerTest extends SysuiTestCase {
mAuthController = new TestableAuthController(mContextSpy, mExecution, mCommandQueue,
mActivityTaskManager, mWindowManager, mFingerprintManager, mFaceManager,
- () -> mUdfpsController, () -> mSidefpsController);
+ () -> mUdfpsController, () -> mSidefpsController, mStatusBarStateController);
mAuthController.start();
verify(mFingerprintManager).addAuthenticatorsRegisteredCallback(
mAuthenticatorsRegisteredCaptor.capture());
+ when(mStatusBarStateController.isDozing()).thenReturn(false);
+ verify(mStatusBarStateController).addCallback(mStatusBarStateListenerCaptor.capture());
+
mAuthenticatorsRegisteredCaptor.getValue().onAllAuthenticatorsRegistered(props);
// Ensures that the operations posted on the handler get executed.
@@ -198,7 +212,8 @@ public class AuthControllerTest extends SysuiTestCase {
// This test requires an uninitialized AuthController.
AuthController authController = new TestableAuthController(mContextSpy, mExecution,
mCommandQueue, mActivityTaskManager, mWindowManager, mFingerprintManager,
- mFaceManager, () -> mUdfpsController, () -> mSidefpsController);
+ mFaceManager, () -> mUdfpsController, () -> mSidefpsController,
+ mStatusBarStateController);
authController.start();
verify(mFingerprintManager).addAuthenticatorsRegisteredCallback(
@@ -221,7 +236,8 @@ public class AuthControllerTest extends SysuiTestCase {
// This test requires an uninitialized AuthController.
AuthController authController = new TestableAuthController(mContextSpy, mExecution,
mCommandQueue, mActivityTaskManager, mWindowManager, mFingerprintManager,
- mFaceManager, () -> mUdfpsController, () -> mSidefpsController);
+ mFaceManager, () -> mUdfpsController, () -> mSidefpsController,
+ mStatusBarStateController);
authController.start();
verify(mFingerprintManager).addAuthenticatorsRegisteredCallback(
@@ -656,6 +672,19 @@ public class AuthControllerTest extends SysuiTestCase {
verify(callback).onBiometricPromptDismissed();
}
+ @Test
+ public void testForwardsDozeEvent() throws RemoteException {
+ mAuthController.setBiometicContextListener(mContextListener);
+
+ mStatusBarStateListenerCaptor.getValue().onDozingChanged(false);
+ mStatusBarStateListenerCaptor.getValue().onDozingChanged(true);
+
+ InOrder order = inOrder(mContextListener);
+ // invoked twice since the initial state is false
+ order.verify(mContextListener, times(2)).onDozeChanged(eq(false));
+ order.verify(mContextListener).onDozeChanged(eq(true));
+ }
+
// Helpers
private void showDialog(int[] sensorIds, boolean credentialAllowed) {
@@ -705,10 +734,12 @@ public class AuthControllerTest extends SysuiTestCase {
FingerprintManager fingerprintManager,
FaceManager faceManager,
Provider<UdfpsController> udfpsControllerFactory,
- Provider<SidefpsController> sidefpsControllerFactory) {
+ Provider<SidefpsController> sidefpsControllerFactory,
+ StatusBarStateController statusBarStateController) {
super(context, execution, commandQueue, activityTaskManager, windowManager,
fingerprintManager, faceManager, udfpsControllerFactory,
- sidefpsControllerFactory, mDisplayManager, mWakefulnessLifecycle, mHandler);
+ sidefpsControllerFactory, mDisplayManager, mWakefulnessLifecycle,
+ statusBarStateController, mHandler);
}
@Override
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
index 159bdbab6d8d..35e838bfca9a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -44,7 +44,6 @@ import android.os.Handler;
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.VibrationAttributes;
-import android.os.Vibrator;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper.RunWithLooper;
import android.view.LayoutInflater;
@@ -64,6 +63,7 @@ import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.LockscreenShadeTransitionController;
+import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
@@ -141,7 +141,7 @@ public class UdfpsControllerTest extends SysuiTestCase {
@Mock
private ScreenLifecycle mScreenLifecycle;
@Mock
- private Vibrator mVibrator;
+ private VibratorHelper mVibrator;
@Mock
private UdfpsHapticsSimulator mUdfpsHapticsSimulator;
@Mock
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlActionCoordinatorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlActionCoordinatorImplTest.kt
index 55509d1ae0bd..9908d44507cd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlActionCoordinatorImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlActionCoordinatorImplTest.kt
@@ -20,13 +20,11 @@ import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.controls.ControlsMetricsLogger
-import com.android.systemui.globalactions.GlobalActionsComponent
import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.statusbar.VibratorHelper
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.concurrency.DelayableExecutor
import com.android.wm.shell.TaskViewFactory
-import dagger.Lazy
-import java.util.Optional
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -41,15 +39,14 @@ import org.mockito.Mockito.reset
import org.mockito.Mockito.spy
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
+import java.util.Optional
@SmallTest
@RunWith(AndroidTestingRunner::class)
class ControlActionCoordinatorImplTest : SysuiTestCase() {
@Mock
- private lateinit var uiController: ControlsUiController
- @Mock
- private lateinit var lazyUiController: Lazy<ControlsUiController>
+ private lateinit var vibratorHelper: VibratorHelper
@Mock
private lateinit var keyguardStateController: KeyguardStateController
@Mock
@@ -59,8 +56,6 @@ class ControlActionCoordinatorImplTest : SysuiTestCase() {
@Mock
private lateinit var activityStarter: ActivityStarter
@Mock
- private lateinit var globalActionsComponent: GlobalActionsComponent
- @Mock
private lateinit var taskViewFactory: Optional<TaskViewFactory>
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private lateinit var cvh: ControlViewHolder
@@ -86,11 +81,9 @@ class ControlActionCoordinatorImplTest : SysuiTestCase() {
uiExecutor,
activityStarter,
keyguardStateController,
- globalActionsComponent,
taskViewFactory,
- getFakeBroadcastDispatcher(),
- lazyUiController,
- metricsLogger
+ metricsLogger,
+ vibratorHelper
))
`when`(cvh.cws.ci.controlId).thenReturn(ID)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java
index 6b156a42dcc8..d5bd67adcf09 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java
@@ -33,12 +33,12 @@ import android.view.WindowManagerImpl;
import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.LifecycleRegistry;
-import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
+import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.SysuiTestableContext;
import com.android.systemui.dreams.dagger.DreamOverlayComponent;
+import com.android.systemui.dreams.touch.DreamOverlayTouchMonitor;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.time.FakeSystemClock;
import com.android.systemui.utils.leaks.LeakCheckedTest;
@@ -65,10 +65,6 @@ public class DreamOverlayServiceTest extends SysuiTestCase {
@Rule
public final LeakCheckedTest.SysuiLeakCheck mLeakCheck = new LeakCheckedTest.SysuiLeakCheck();
- @Rule
- public SysuiTestableContext mContext = new SysuiTestableContext(
- InstrumentationRegistry.getContext(), mLeakCheck);
-
WindowManager.LayoutParams mWindowParams = new WindowManager.LayoutParams();
@Mock
@@ -89,6 +85,16 @@ public class DreamOverlayServiceTest extends SysuiTestCase {
@Mock
DreamOverlayContainerViewController mDreamOverlayContainerViewController;
+ @Mock
+ KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+
+ @Mock
+ DreamOverlayTouchMonitor mDreamOverlayTouchMonitor;
+
+ @Mock
+ DreamOverlayStateController mStateController;
+
+
DreamOverlayService mService;
@Before
@@ -102,6 +108,8 @@ public class DreamOverlayServiceTest extends SysuiTestCase {
.thenReturn(mLifecycleOwner);
when(mDreamOverlayComponent.getLifecycleRegistry())
.thenReturn(mLifecycleRegistry);
+ when(mDreamOverlayComponent.getDreamOverlayTouchMonitor())
+ .thenReturn(mDreamOverlayTouchMonitor);
when(mDreamOverlayComponentFactory
.create(any(), any()))
.thenReturn(mDreamOverlayComponent);
@@ -109,7 +117,9 @@ public class DreamOverlayServiceTest extends SysuiTestCase {
.thenReturn(mDreamOverlayContainerView);
mService = new DreamOverlayService(mContext, mMainExecutor,
- mDreamOverlayComponentFactory);
+ mDreamOverlayComponentFactory,
+ mStateController,
+ mKeyguardUpdateMonitor);
final IBinder proxy = mService.onBind(new Intent());
final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(proxy);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java
index 7d0833db7ae4..627da3c5ec77 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java
@@ -16,11 +16,15 @@
package com.android.systemui.dreams;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import android.testing.AndroidTestingRunner;
@@ -35,6 +39,7 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
+import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import java.util.Collection;
@@ -56,7 +61,29 @@ public class DreamOverlayStateControllerTest extends SysuiTestCase {
}
@Test
- public void testCallback() throws Exception {
+ public void testStateChange() {
+ final DreamOverlayStateController stateController = new DreamOverlayStateController(
+ mExecutor);
+ stateController.addCallback(mCallback);
+ stateController.setOverlayActive(true);
+ mExecutor.runAllReady();
+
+ verify(mCallback).onStateChanged();
+ assertThat(stateController.isOverlayActive()).isTrue();
+
+ Mockito.clearInvocations(mCallback);
+ stateController.setOverlayActive(true);
+ mExecutor.runAllReady();
+ verify(mCallback, never()).onStateChanged();
+
+ stateController.setOverlayActive(false);
+ mExecutor.runAllReady();
+ verify(mCallback).onStateChanged();
+ assertThat(stateController.isOverlayActive()).isFalse();
+ }
+
+ @Test
+ public void testCallback() {
final DreamOverlayStateController stateController = new DreamOverlayStateController(
mExecutor);
stateController.addCallback(mCallback);
@@ -94,4 +121,43 @@ public class DreamOverlayStateControllerTest extends SysuiTestCase {
mExecutor.runAllReady();
verify(mCallback, times(1)).onComplicationsChanged();
}
+
+ @Test
+ public void testComplicationFiltering() {
+ final DreamOverlayStateController stateController =
+ new DreamOverlayStateController(mExecutor);
+
+ final Complication alwaysAvailableComplication = Mockito.mock(Complication.class);
+ final Complication weatherComplication = Mockito.mock(Complication.class);
+ when(alwaysAvailableComplication.getRequiredTypeAvailability())
+ .thenReturn(Complication.COMPLICATION_TYPE_NONE);
+ when(weatherComplication.getRequiredTypeAvailability())
+ .thenReturn(Complication.COMPLICATION_TYPE_WEATHER);
+
+ stateController.addComplication(alwaysAvailableComplication);
+ stateController.addComplication(weatherComplication);
+
+ final DreamOverlayStateController.Callback callback =
+ Mockito.mock(DreamOverlayStateController.Callback.class);
+
+ stateController.addCallback(callback);
+ mExecutor.runAllReady();
+
+ {
+ final Collection<Complication> complications = stateController.getComplications();
+ assertThat(complications.contains(alwaysAvailableComplication)).isTrue();
+ assertThat(complications.contains(weatherComplication)).isFalse();
+ }
+
+ stateController.setAvailableComplicationTypes(Complication.COMPLICATION_TYPE_WEATHER);
+ mExecutor.runAllReady();
+ verify(callback).onAvailableComplicationTypesChanged();
+
+ {
+ final Collection<Complication> complications = stateController.getComplications();
+ assertThat(complications.contains(alwaysAvailableComplication)).isTrue();
+ assertThat(complications.contains(weatherComplication)).isTrue();
+ }
+
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/SmartSpaceComplicationTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/SmartSpaceComplicationTest.java
new file mode 100644
index 000000000000..3b17a8071cb2
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/SmartSpaceComplicationTest.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.dreams;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class SmartSpaceComplicationTest extends SysuiTestCase {
+ @Mock
+ private Context mContext;
+
+ @Mock
+ private LockscreenSmartspaceController mSmartspaceController;
+
+ @Mock
+ private DreamOverlayStateController mDreamOverlayStateController;
+
+ @Mock
+ private SmartSpaceComplication mComplication;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ /**
+ * Ensures {@link SmartSpaceComplication} is only registered when it is available.
+ */
+ @Test
+ public void testAvailability() {
+ when(mSmartspaceController.isEnabled()).thenReturn(false);
+
+ final SmartSpaceComplication.Registrant registrant = new SmartSpaceComplication.Registrant(
+ mContext,
+ mDreamOverlayStateController,
+ mComplication,
+ mSmartspaceController);
+ registrant.start();
+ verify(mDreamOverlayStateController, never()).addComplication(any());
+
+ when(mSmartspaceController.isEnabled()).thenReturn(true);
+ registrant.start();
+ verify(mDreamOverlayStateController).addComplication(eq(mComplication));
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationCollectionLiveDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationCollectionLiveDataTest.java
index afc0309dd195..5fcf414f0251 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationCollectionLiveDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationCollectionLiveDataTest.java
@@ -75,6 +75,10 @@ public class ComplicationCollectionLiveDataTest extends SysuiTestCase {
callbackCaptor.getValue().onComplicationsChanged();
verifyUpdate(observer, complications);
+
+ callbackCaptor.getValue().onAvailableComplicationTypesChanged();
+
+ verifyUpdate(observer, complications);
});
}
@@ -85,7 +89,9 @@ public class ComplicationCollectionLiveDataTest extends SysuiTestCase {
verify(observer).onChanged(collectionCaptor.capture());
- assertThat(collectionCaptor.getValue().equals(targetCollection)).isTrue();
+ final Collection collection = collectionCaptor.getValue();
+ assertThat(collection.containsAll(targetCollection)
+ && targetCollection.containsAll(collection)).isTrue();
Mockito.clearInvocations(observer);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationLayoutEngineTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationLayoutEngineTest.java
index f227a9b78c39..d5ab708f893b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationLayoutEngineTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationLayoutEngineTest.java
@@ -27,6 +27,7 @@ import android.view.View;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.test.filters.SmallTest;
+import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import org.junit.Before;
@@ -122,6 +123,34 @@ public class ComplicationLayoutEngineTest extends SysuiTestCase {
}
/**
+ * Makes sure the engine properly places a view within the {@link ConstraintLayout}.
+ */
+ @Test
+ public void testSnapToGuide() {
+ final ViewInfo firstViewInfo = new ViewInfo(
+ new ComplicationLayoutParams(
+ 100,
+ 100,
+ ComplicationLayoutParams.POSITION_TOP
+ | ComplicationLayoutParams.POSITION_END,
+ ComplicationLayoutParams.DIRECTION_DOWN,
+ 0,
+ true),
+ Complication.CATEGORY_STANDARD,
+ mLayout);
+
+ final ComplicationLayoutEngine engine = new ComplicationLayoutEngine(mLayout);
+ addComplication(engine, firstViewInfo);
+
+ // Ensure the view is added to the top end corner
+ verifyChange(firstViewInfo, true, lp -> {
+ assertThat(lp.topToTop == ConstraintLayout.LayoutParams.PARENT_ID).isTrue();
+ assertThat(lp.endToEnd == ConstraintLayout.LayoutParams.PARENT_ID).isTrue();
+ assertThat(lp.startToEnd == R.id.complication_end_guide).isTrue();
+ });
+ }
+
+ /**
* Ensures layout in a particular direction updates.
*/
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationLayoutParamsTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationLayoutParamsTest.java
index d080bbca0616..967b30d07e63 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationLayoutParamsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationLayoutParamsTest.java
@@ -81,7 +81,7 @@ public class ComplicationLayoutParamsTest extends SysuiTestCase {
final boolean properDirection = (invalidPosition & position) != invalidPosition;
try {
- final ComplicationLayoutParams params = new ComplicationLayoutParams(
+ new ComplicationLayoutParams(
100,
100,
position,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationUtilsTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationUtilsTest.java
new file mode 100644
index 000000000000..f1978b214594
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationUtilsTest.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.dreams.complication;
+
+import static com.android.systemui.dreams.complication.Complication.COMPLICATION_TYPE_AIR_QUALITY;
+import static com.android.systemui.dreams.complication.Complication.COMPLICATION_TYPE_CAST_INFO;
+import static com.android.systemui.dreams.complication.Complication.COMPLICATION_TYPE_DATE;
+import static com.android.systemui.dreams.complication.Complication.COMPLICATION_TYPE_TIME;
+import static com.android.systemui.dreams.complication.Complication.COMPLICATION_TYPE_WEATHER;
+import static com.android.systemui.dreams.complication.ComplicationUtils.convertComplicationType;
+
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.settingslib.dream.DreamBackend;
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class ComplicationUtilsTest extends SysuiTestCase {
+ @Test
+ public void testConvertComplicationType() {
+ assertThat(convertComplicationType(DreamBackend.COMPLICATION_TYPE_TIME))
+ .isEqualTo(COMPLICATION_TYPE_TIME);
+ assertThat(convertComplicationType(DreamBackend.COMPLICATION_TYPE_DATE))
+ .isEqualTo(COMPLICATION_TYPE_DATE);
+ assertThat(convertComplicationType(DreamBackend.COMPLICATION_TYPE_WEATHER))
+ .isEqualTo(COMPLICATION_TYPE_WEATHER);
+ assertThat(convertComplicationType(DreamBackend.COMPLICATION_TYPE_AIR_QUALITY))
+ .isEqualTo(COMPLICATION_TYPE_AIR_QUALITY);
+ assertThat(convertComplicationType(DreamBackend.COMPLICATION_TYPE_CAST_INFO))
+ .isEqualTo(COMPLICATION_TYPE_CAST_INFO);
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamClockDateComplicationTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamClockDateComplicationTest.java
new file mode 100644
index 000000000000..b02c506be8be
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamClockDateComplicationTest.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.dreams.complication;
+
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.dreams.DreamOverlayStateController;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class DreamClockDateComplicationTest extends SysuiTestCase {
+ @SuppressWarnings("HidingField")
+ @Mock
+ private Context mContext;
+
+ @Mock
+ private DreamOverlayStateController mDreamOverlayStateController;
+
+ @Mock
+ private DreamClockDateComplication mComplication;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ /**
+ * Ensures {@link DreamClockDateComplication} is registered.
+ */
+ @Test
+ public void testComplicationAdded() {
+ final DreamClockDateComplication.Registrant registrant =
+ new DreamClockDateComplication.Registrant(
+ mContext,
+ mDreamOverlayStateController,
+ mComplication);
+ registrant.start();
+ verify(mDreamOverlayStateController).addComplication(eq(mComplication));
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamClockTimeComplicationTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamClockTimeComplicationTest.java
new file mode 100644
index 000000000000..088b4d5136ff
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamClockTimeComplicationTest.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.dreams.complication;
+
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.dreams.DreamOverlayStateController;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class DreamClockTimeComplicationTest extends SysuiTestCase {
+ @SuppressWarnings("HidingField")
+ @Mock
+ private Context mContext;
+
+ @Mock
+ private DreamOverlayStateController mDreamOverlayStateController;
+
+ @Mock
+ private DreamClockTimeComplication mComplication;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ /**
+ * Ensures {@link DreamClockTimeComplication} is registered.
+ */
+ @Test
+ public void testComplicationAdded() {
+ final DreamClockTimeComplication.Registrant registrant =
+ new DreamClockTimeComplication.Registrant(
+ mContext,
+ mDreamOverlayStateController,
+ mComplication);
+ registrant.start();
+ verify(mDreamOverlayStateController).addComplication(eq(mComplication));
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamWeatherComplicationTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamWeatherComplicationTest.java
new file mode 100644
index 000000000000..151742af7e1a
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamWeatherComplicationTest.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.dreams.complication;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.dreams.DreamOverlayStateController;
+import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class DreamWeatherComplicationTest extends SysuiTestCase {
+ @SuppressWarnings("HidingField")
+ @Mock
+ private Context mContext;
+
+ @Mock
+ private LockscreenSmartspaceController mSmartspaceController;
+
+ @Mock
+ private DreamOverlayStateController mDreamOverlayStateController;
+
+ @Mock
+ private DreamWeatherComplication mComplication;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ /**
+ * Ensures {@link DreamWeatherComplication} is only registered when it is available.
+ */
+ @Test
+ public void testComplicationAvailability() {
+ when(mSmartspaceController.isEnabled()).thenReturn(false);
+ final DreamWeatherComplication.Registrant registrant =
+ new DreamWeatherComplication.Registrant(
+ mContext,
+ mSmartspaceController,
+ mDreamOverlayStateController,
+ mComplication);
+ registrant.start();
+ verify(mDreamOverlayStateController, never()).addComplication(any());
+
+ when(mSmartspaceController.isEnabled()).thenReturn(true);
+ registrant.start();
+ verify(mDreamOverlayStateController).addComplication(eq(mComplication));
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java
new file mode 100644
index 000000000000..1a8326fd5bd1
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java
@@ -0,0 +1,252 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.dreams.touch;
+
+import static com.google.common.truth.Truth.assertThat;
+
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyFloat;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.animation.ValueAnimator;
+import android.testing.AndroidTestingRunner;
+import android.view.GestureDetector;
+import android.view.MotionEvent;
+import android.view.VelocityTracker;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.shared.system.InputChannelCompat;
+import com.android.systemui.statusbar.NotificationShadeWindowController;
+import com.android.systemui.statusbar.phone.KeyguardBouncer;
+import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
+import com.android.wm.shell.animation.FlingAnimationUtils;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Random;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class BouncerSwipeTouchHandlerTest extends SysuiTestCase {
+ @Mock
+ StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
+
+ @Mock
+ StatusBar mStatusBar;
+
+ @Mock
+ NotificationShadeWindowController mNotificationShadeWindowController;
+
+ @Mock
+ FlingAnimationUtils mFlingAnimationUtils;
+
+
+ @Mock
+ FlingAnimationUtils mFlingAnimationUtilsClosing;
+
+ @Mock
+ DreamTouchHandler.TouchSession mTouchSession;
+
+ BouncerSwipeTouchHandler mTouchHandler;
+
+ @Mock
+ BouncerSwipeTouchHandler.ValueAnimatorCreator mValueAnimatorCreator;
+
+ @Mock
+ ValueAnimator mValueAnimator;
+
+ @Mock
+ BouncerSwipeTouchHandler.VelocityTrackerFactory mVelocityTrackerFactory;
+
+ @Mock
+ VelocityTracker mVelocityTracker;
+
+ private static final float TOUCH_REGION = .3f;
+ private static final float SCREEN_HEIGHT_PX = 100;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ mTouchHandler = new BouncerSwipeTouchHandler(
+ mStatusBarKeyguardViewManager,
+ mStatusBar,
+ mNotificationShadeWindowController,
+ mValueAnimatorCreator,
+ mVelocityTrackerFactory,
+ mFlingAnimationUtils,
+ mFlingAnimationUtilsClosing,
+ TOUCH_REGION);
+ when(mStatusBar.getDisplayHeight()).thenReturn(SCREEN_HEIGHT_PX);
+ when(mValueAnimatorCreator.create(anyFloat(), anyFloat())).thenReturn(mValueAnimator);
+ when(mVelocityTrackerFactory.obtain()).thenReturn(mVelocityTracker);
+ }
+
+ private static void beginValidSwipe(GestureDetector.OnGestureListener listener) {
+ listener.onDown(MotionEvent.obtain(0, 0,
+ MotionEvent.ACTION_DOWN, 0,
+ SCREEN_HEIGHT_PX - (.5f * TOUCH_REGION * SCREEN_HEIGHT_PX), 0));
+ }
+
+ /**
+ * Ensures expansion only happens when touch down happens in valid part of the screen.
+ */
+ @Test
+ public void testSessionStart() {
+ mTouchHandler.onSessionStart(mTouchSession);
+ verify(mNotificationShadeWindowController).setForcePluginOpen(eq(true), any());
+ ArgumentCaptor<InputChannelCompat.InputEventListener> eventListenerCaptor =
+ ArgumentCaptor.forClass(InputChannelCompat.InputEventListener.class);
+ ArgumentCaptor<GestureDetector.OnGestureListener> gestureListenerCaptor =
+ ArgumentCaptor.forClass(GestureDetector.OnGestureListener.class);
+ verify(mTouchSession).registerGestureListener(gestureListenerCaptor.capture());
+ verify(mTouchSession).registerInputListener(eventListenerCaptor.capture());
+
+ final Random random = new Random(System.currentTimeMillis());
+
+ // If an initial touch down meeting criteria has been met, scroll behavior should be
+ // ignored.
+ assertThat(gestureListenerCaptor.getValue()
+ .onScroll(Mockito.mock(MotionEvent.class),
+ Mockito.mock(MotionEvent.class),
+ random.nextFloat(),
+ random.nextFloat())).isFalse();
+
+ // A touch at the top of the screen should also not trigger listening.
+ final MotionEvent touchDownEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN,
+ 0, 0, 0);
+
+ gestureListenerCaptor.getValue().onDown(touchDownEvent);
+ assertThat(gestureListenerCaptor.getValue()
+ .onScroll(Mockito.mock(MotionEvent.class),
+ Mockito.mock(MotionEvent.class),
+ random.nextFloat(),
+ random.nextFloat())).isFalse();
+
+ // A touch within range at the bottom of the screen should trigger listening
+ beginValidSwipe(gestureListenerCaptor.getValue());
+ assertThat(gestureListenerCaptor.getValue()
+ .onScroll(Mockito.mock(MotionEvent.class),
+ Mockito.mock(MotionEvent.class),
+ random.nextFloat(),
+ random.nextFloat())).isTrue();
+ }
+
+ /**
+ * Makes sure expansion amount is proportional to scroll.
+ */
+ @Test
+ public void testExpansionAmount() {
+ mTouchHandler.onSessionStart(mTouchSession);
+ ArgumentCaptor<GestureDetector.OnGestureListener> gestureListenerCaptor =
+ ArgumentCaptor.forClass(GestureDetector.OnGestureListener.class);
+ verify(mTouchSession).registerGestureListener(gestureListenerCaptor.capture());
+
+ beginValidSwipe(gestureListenerCaptor.getValue());
+
+ final float scrollAmount = .3f;
+ final float distanceY = SCREEN_HEIGHT_PX * scrollAmount;
+
+ final MotionEvent event1 = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE,
+ 0, SCREEN_HEIGHT_PX, 0);
+ final MotionEvent event2 = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE,
+ 0, SCREEN_HEIGHT_PX - distanceY, 0);
+
+ assertThat(gestureListenerCaptor.getValue().onScroll(event1, event2, 0 , distanceY))
+ .isTrue();
+
+ // Ensure only called once
+ verify(mStatusBarKeyguardViewManager)
+ .onPanelExpansionChanged(anyFloat(), anyBoolean(), anyBoolean());
+
+ // Ensure correct expansion passed in.
+ verify(mStatusBarKeyguardViewManager)
+ .onPanelExpansionChanged(eq(1 - scrollAmount), eq(false), eq(true));
+ }
+
+ private void swipeToPosition(float position, float velocityY) {
+ mTouchHandler.onSessionStart(mTouchSession);
+ ArgumentCaptor<GestureDetector.OnGestureListener> gestureListenerCaptor =
+ ArgumentCaptor.forClass(GestureDetector.OnGestureListener.class);
+ ArgumentCaptor<InputChannelCompat.InputEventListener> inputEventListenerCaptor =
+ ArgumentCaptor.forClass(InputChannelCompat.InputEventListener.class);
+ verify(mTouchSession).registerGestureListener(gestureListenerCaptor.capture());
+ verify(mTouchSession).registerInputListener(inputEventListenerCaptor.capture());
+
+ when(mVelocityTracker.getYVelocity()).thenReturn(velocityY);
+
+ beginValidSwipe(gestureListenerCaptor.getValue());
+
+ final float distanceY = SCREEN_HEIGHT_PX * position;
+
+ final MotionEvent event1 = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE,
+ 0, SCREEN_HEIGHT_PX, 0);
+ final MotionEvent event2 = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE,
+ 0, SCREEN_HEIGHT_PX - distanceY, 0);
+
+ assertThat(gestureListenerCaptor.getValue().onScroll(event1, event2, 0 , distanceY))
+ .isTrue();
+
+ final MotionEvent upEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_UP,
+ 0, 0, 0);
+
+ inputEventListenerCaptor.getValue().onInputEvent(upEvent);
+ }
+
+ /**
+ * Tests that ending a swipe before the set expansion threshold leads to bouncer collapsing
+ * down.
+ */
+ @Test
+ public void testCollapseOnThreshold() {
+ final float swipeUpPercentage = .3f;
+ swipeToPosition(swipeUpPercentage, -1);
+
+ verify(mValueAnimatorCreator).create(eq(1 - swipeUpPercentage),
+ eq(KeyguardBouncer.EXPANSION_VISIBLE));
+ verify(mFlingAnimationUtilsClosing).apply(eq(mValueAnimator), anyFloat(), anyFloat(),
+ anyFloat(), anyFloat());
+ verify(mValueAnimator).start();
+ }
+
+ /**
+ * Tests that ending a swipe above the set expansion threshold will continue the expansion.
+ */
+ @Test
+ public void testExpandOnThreshold() {
+ final float swipeUpPercentage = .7f;
+ swipeToPosition(swipeUpPercentage, 1);
+
+ verify(mValueAnimatorCreator).create(eq(1 - swipeUpPercentage),
+ eq(KeyguardBouncer.EXPANSION_HIDDEN));
+ verify(mFlingAnimationUtils).apply(eq(mValueAnimator), anyFloat(), anyFloat(),
+ anyFloat(), anyFloat());
+ verify(mValueAnimator).start();
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitorTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitorTest.java
new file mode 100644
index 000000000000..74b217b2fdbf
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitorTest.java
@@ -0,0 +1,369 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.dreams.touch;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.testing.AndroidTestingRunner;
+import android.util.Pair;
+import android.view.GestureDetector;
+import android.view.InputEvent;
+import android.view.MotionEvent;
+
+import androidx.lifecycle.DefaultLifecycleObserver;
+import androidx.lifecycle.Lifecycle;
+import androidx.lifecycle.LifecycleObserver;
+import androidx.lifecycle.LifecycleOwner;
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.dreams.touch.dagger.InputSessionComponent;
+import com.android.systemui.shared.system.InputChannelCompat;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.time.FakeSystemClock;
+
+import com.google.common.util.concurrent.ListenableFuture;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import java.util.function.Consumer;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class DreamOverlayTouchMonitorTest extends SysuiTestCase {
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ private static class Environment {
+ private final InputSessionComponent.Factory mInputFactory;
+ private final InputSession mInputSession;
+ private final Lifecycle mLifecycle;
+ private final LifecycleOwner mLifecycleOwner;
+ private final DreamOverlayTouchMonitor mMonitor;
+ private final DefaultLifecycleObserver mLifecycleObserver;
+ private final InputChannelCompat.InputEventListener mEventListener;
+ private final GestureDetector.OnGestureListener mGestureListener;
+ private final FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock());
+
+ Environment(Set<DreamTouchHandler> handlers) {
+ mLifecycle = Mockito.mock(Lifecycle.class);
+ mLifecycleOwner = Mockito.mock(LifecycleOwner.class);
+
+ mInputFactory = Mockito.mock(InputSessionComponent.Factory.class);
+ final InputSessionComponent inputComponent = Mockito.mock(InputSessionComponent.class);
+ mInputSession = Mockito.mock(InputSession.class);
+
+ when(mInputFactory.create(any(), any(), any(), anyBoolean()))
+ .thenReturn(inputComponent);
+ when(inputComponent.getInputSession()).thenReturn(mInputSession);
+
+ mMonitor = new DreamOverlayTouchMonitor(mExecutor, mLifecycle, mInputFactory, handlers);
+ mMonitor.init();
+
+ final ArgumentCaptor<LifecycleObserver> lifecycleObserverCaptor =
+ ArgumentCaptor.forClass(LifecycleObserver.class);
+ verify(mLifecycle).addObserver(lifecycleObserverCaptor.capture());
+ assertThat(lifecycleObserverCaptor.getValue() instanceof DefaultLifecycleObserver)
+ .isTrue();
+ mLifecycleObserver = (DefaultLifecycleObserver) lifecycleObserverCaptor.getValue();
+
+ updateLifecycle(observer -> observer.first.onResume(observer.second));
+
+ // Capture creation request.
+ final ArgumentCaptor<InputChannelCompat.InputEventListener> inputEventListenerCaptor =
+ ArgumentCaptor.forClass(InputChannelCompat.InputEventListener.class);
+ final ArgumentCaptor<GestureDetector.OnGestureListener> gestureListenerCaptor =
+ ArgumentCaptor.forClass(GestureDetector.OnGestureListener.class);
+ verify(mInputFactory).create(any(), inputEventListenerCaptor.capture(),
+ gestureListenerCaptor.capture(),
+ eq(true));
+ mEventListener = inputEventListenerCaptor.getValue();
+ mGestureListener = gestureListenerCaptor.getValue();
+ }
+
+ void executeAll() {
+ mExecutor.runAllReady();
+ }
+
+ void publishInputEvent(InputEvent event) {
+ mEventListener.onInputEvent(event);
+ }
+
+ void publishGestureEvent(Consumer<GestureDetector.OnGestureListener> listenerConsumer) {
+ listenerConsumer.accept(mGestureListener);
+ }
+
+ void updateLifecycle(Consumer<Pair<DefaultLifecycleObserver, LifecycleOwner>> consumer) {
+ consumer.accept(Pair.create(mLifecycleObserver, mLifecycleOwner));
+ }
+
+ void verifyInputSessionDispose() {
+ verify(mInputSession).dispose();
+ Mockito.clearInvocations(mInputSession);
+ }
+ }
+
+ @Test
+ public void testInputEventPropagation() {
+ final DreamTouchHandler touchHandler = Mockito.mock(DreamTouchHandler.class);
+
+ final Environment environment = new Environment(Stream.of(touchHandler)
+ .collect(Collectors.toCollection(HashSet::new)));
+
+ final InputEvent initialEvent = Mockito.mock(InputEvent.class);
+ environment.publishInputEvent(initialEvent);
+
+ // Ensure session started
+ final InputChannelCompat.InputEventListener eventListener =
+ registerInputEventListener(touchHandler);
+
+ // First event will be missed since we register after the execution loop,
+ final InputEvent event = Mockito.mock(InputEvent.class);
+ environment.publishInputEvent(event);
+ verify(eventListener).onInputEvent(eq(event));
+ }
+
+ @Test
+ public void testInputGesturePropagation() {
+ final DreamTouchHandler touchHandler = Mockito.mock(DreamTouchHandler.class);
+
+ final Environment environment = new Environment(Stream.of(touchHandler)
+ .collect(Collectors.toCollection(HashSet::new)));
+
+ final InputEvent initialEvent = Mockito.mock(InputEvent.class);
+ environment.publishInputEvent(initialEvent);
+
+ // Ensure session started
+ final GestureDetector.OnGestureListener gestureListener =
+ registerGestureListener(touchHandler);
+
+ final MotionEvent event = Mockito.mock(MotionEvent.class);
+ environment.publishGestureEvent(onGestureListener -> onGestureListener.onShowPress(event));
+ verify(gestureListener).onShowPress(eq(event));
+ }
+
+ @Test
+ public void testGestureConsumption() {
+ final DreamTouchHandler touchHandler = Mockito.mock(DreamTouchHandler.class);
+
+ final Environment environment = new Environment(Stream.of(touchHandler)
+ .collect(Collectors.toCollection(HashSet::new)));
+
+ final InputEvent initialEvent = Mockito.mock(InputEvent.class);
+ environment.publishInputEvent(initialEvent);
+
+ // Ensure session started
+ final GestureDetector.OnGestureListener gestureListener =
+ registerGestureListener(touchHandler);
+
+ when(gestureListener.onDown(any())).thenReturn(true);
+ final MotionEvent event = Mockito.mock(MotionEvent.class);
+ environment.publishGestureEvent(onGestureListener -> {
+ assertThat(onGestureListener.onDown(event)).isTrue();
+ });
+
+ verify(gestureListener).onDown(eq(event));
+ }
+
+ @Test
+ public void testBroadcast() {
+ final DreamTouchHandler touchHandler = Mockito.mock(DreamTouchHandler.class);
+ final DreamTouchHandler touchHandler2 = Mockito.mock(DreamTouchHandler.class);
+
+ final Environment environment = new Environment(Stream.of(touchHandler, touchHandler2)
+ .collect(Collectors.toCollection(HashSet::new)));
+
+ final InputEvent initialEvent = Mockito.mock(InputEvent.class);
+ environment.publishInputEvent(initialEvent);
+
+ final HashSet<InputChannelCompat.InputEventListener> inputListeners = new HashSet<>();
+
+ inputListeners.add(registerInputEventListener(touchHandler));
+ inputListeners.add(registerInputEventListener(touchHandler));
+ inputListeners.add(registerInputEventListener(touchHandler2));
+
+ final MotionEvent event = Mockito.mock(MotionEvent.class);
+ environment.publishInputEvent(event);
+
+ inputListeners
+ .stream()
+ .forEach(inputEventListener -> verify(inputEventListener).onInputEvent(event));
+ }
+
+ @Test
+ public void testPush() throws InterruptedException, ExecutionException {
+ final DreamTouchHandler touchHandler = Mockito.mock(DreamTouchHandler.class);
+
+ final Environment environment = new Environment(Stream.of(touchHandler)
+ .collect(Collectors.toCollection(HashSet::new)));
+
+ final InputEvent initialEvent = Mockito.mock(InputEvent.class);
+ environment.publishInputEvent(initialEvent);
+
+ final DreamTouchHandler.TouchSession session = captureSession(touchHandler);
+ final InputChannelCompat.InputEventListener eventListener =
+ registerInputEventListener(session);
+
+ final ListenableFuture<DreamTouchHandler.TouchSession> frontSessionFuture = session.push();
+ environment.executeAll();
+ final DreamTouchHandler.TouchSession frontSession = frontSessionFuture.get();
+ final InputChannelCompat.InputEventListener frontEventListener =
+ registerInputEventListener(frontSession);
+
+ final MotionEvent event = Mockito.mock(MotionEvent.class);
+ environment.publishInputEvent(event);
+
+ verify(frontEventListener).onInputEvent(eq(event));
+ verify(eventListener, never()).onInputEvent(any());
+
+ Mockito.clearInvocations(eventListener, frontEventListener);
+
+ ListenableFuture<DreamTouchHandler.TouchSession> sessionFuture = frontSession.pop();
+ environment.executeAll();
+
+ DreamTouchHandler.TouchSession returnedSession = sessionFuture.get();
+ assertThat(session == returnedSession).isTrue();
+
+ environment.executeAll();
+
+ final MotionEvent followupEvent = Mockito.mock(MotionEvent.class);
+ environment.publishInputEvent(followupEvent);
+
+ verify(eventListener).onInputEvent(eq(followupEvent));
+ verify(frontEventListener, never()).onInputEvent(any());
+ }
+
+ @Test
+ public void testPause() {
+ final DreamTouchHandler touchHandler = Mockito.mock(DreamTouchHandler.class);
+
+ final Environment environment = new Environment(Stream.of(touchHandler)
+ .collect(Collectors.toCollection(HashSet::new)));
+
+ final InputEvent initialEvent = Mockito.mock(InputEvent.class);
+ environment.publishInputEvent(initialEvent);
+
+ // Ensure session started
+ final InputChannelCompat.InputEventListener eventListener =
+ registerInputEventListener(touchHandler);
+
+ // First event will be missed since we register after the execution loop,
+ final InputEvent event = Mockito.mock(InputEvent.class);
+ environment.publishInputEvent(event);
+ verify(eventListener).onInputEvent(eq(event));
+
+ environment.updateLifecycle(observerOwnerPair -> {
+ observerOwnerPair.first.onPause(observerOwnerPair.second);
+ });
+
+ environment.verifyInputSessionDispose();
+ }
+
+ @Test
+ public void testPilfering() {
+ final DreamTouchHandler touchHandler1 = Mockito.mock(DreamTouchHandler.class);
+ final DreamTouchHandler touchHandler2 = Mockito.mock(DreamTouchHandler.class);
+
+ final Environment environment = new Environment(Stream.of(touchHandler1, touchHandler2)
+ .collect(Collectors.toCollection(HashSet::new)));
+
+ final InputEvent initialEvent = Mockito.mock(InputEvent.class);
+ environment.publishInputEvent(initialEvent);
+
+ final DreamTouchHandler.TouchSession session1 = captureSession(touchHandler1);
+ final GestureDetector.OnGestureListener gestureListener1 =
+ registerGestureListener(session1);
+
+ final DreamTouchHandler.TouchSession session2 = captureSession(touchHandler2);
+ final GestureDetector.OnGestureListener gestureListener2 =
+ registerGestureListener(session2);
+ when(gestureListener2.onDown(any())).thenReturn(true);
+
+ final MotionEvent gestureEvent = Mockito.mock(MotionEvent.class);
+ environment.publishGestureEvent(
+ onGestureListener -> onGestureListener.onDown(gestureEvent));
+
+ Mockito.clearInvocations(gestureListener1, gestureListener2);
+
+ final MotionEvent followupEvent = Mockito.mock(MotionEvent.class);
+ environment.publishGestureEvent(
+ onGestureListener -> onGestureListener.onDown(followupEvent));
+
+ verify(gestureListener1, never()).onDown(any());
+ verify(gestureListener2).onDown(eq(followupEvent));
+ }
+
+ public GestureDetector.OnGestureListener registerGestureListener(DreamTouchHandler handler) {
+ final GestureDetector.OnGestureListener gestureListener = Mockito.mock(
+ GestureDetector.OnGestureListener.class);
+ final ArgumentCaptor<DreamTouchHandler.TouchSession> sessionCaptor =
+ ArgumentCaptor.forClass(DreamTouchHandler.TouchSession.class);
+ verify(handler).onSessionStart(sessionCaptor.capture());
+ sessionCaptor.getValue().registerGestureListener(gestureListener);
+
+ return gestureListener;
+ }
+
+ public GestureDetector.OnGestureListener registerGestureListener(
+ DreamTouchHandler.TouchSession session) {
+ final GestureDetector.OnGestureListener gestureListener = Mockito.mock(
+ GestureDetector.OnGestureListener.class);
+ session.registerGestureListener(gestureListener);
+
+ return gestureListener;
+ }
+
+ public InputChannelCompat.InputEventListener registerInputEventListener(
+ DreamTouchHandler.TouchSession session) {
+ final InputChannelCompat.InputEventListener eventListener = Mockito.mock(
+ InputChannelCompat.InputEventListener.class);
+ session.registerInputListener(eventListener);
+
+ return eventListener;
+ }
+
+ public DreamTouchHandler.TouchSession captureSession(DreamTouchHandler handler) {
+ final ArgumentCaptor<DreamTouchHandler.TouchSession> sessionCaptor =
+ ArgumentCaptor.forClass(DreamTouchHandler.TouchSession.class);
+ verify(handler).onSessionStart(sessionCaptor.capture());
+ return sessionCaptor.getValue();
+ }
+
+ public InputChannelCompat.InputEventListener registerInputEventListener(
+ DreamTouchHandler handler) {
+ return registerInputEventListener(captureSession(handler));
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dump/LogBufferHelper.kt b/packages/SystemUI/tests/src/com/android/systemui/dump/LogBufferHelper.kt
new file mode 100644
index 000000000000..2cb19393d2c6
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/dump/LogBufferHelper.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.dump
+
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.LogLevel
+import com.android.systemui.log.LogcatEchoTracker
+
+/**
+ * Creates a LogBuffer that will echo everything to logcat, which is useful for debugging tests.
+ */
+fun logcatLogBuffer(name: String = "EchoToLogcatLogBuffer") =
+ LogBuffer(name, 50, 50, LogcatEchoTrackerAlways())
+
+/**
+ * A [LogcatEchoTracker] that always allows echoing to the logcat.
+ */
+class LogcatEchoTrackerAlways : LogcatEchoTracker {
+ override fun isBufferLoggable(bufferName: String, level: LogLevel): Boolean = true
+ override fun isTagLoggable(tagName: String, level: LogLevel): Boolean = true
+}
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 e3a7e3b43b77..71fc8ee6cce8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
@@ -61,6 +61,7 @@ import com.android.systemui.model.SysUiState;
import com.android.systemui.plugins.GlobalActions;
import com.android.systemui.settings.UserContextProvider;
import com.android.systemui.statusbar.NotificationShadeWindowController;
+import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.SystemUIDialogManager;
import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -115,6 +116,7 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase {
@Mock private PackageManager mPackageManager;
@Mock private Handler mHandler;
@Mock private UserContextProvider mUserContextProvider;
+ @Mock private VibratorHelper mVibratorHelper;
@Mock private StatusBar mStatusBar;
@Mock private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@Mock private DialogLaunchAnimator mDialogLaunchAnimator;
@@ -143,7 +145,7 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase {
mTelephonyListenerManager,
mGlobalSettings,
mSecureSettings,
- null,
+ mVibratorHelper,
mResources,
mConfigurationController,
mKeyguardStateController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/hdmi/HdmiCecSetMenuLanguageHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/hdmi/HdmiCecSetMenuLanguageHelperTest.java
new file mode 100644
index 000000000000..1171bd299c1f
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/hdmi/HdmiCecSetMenuLanguageHelperTest.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.hdmi;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.provider.Settings;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.AndroidTestingRunner;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.util.settings.SecureSettings;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Locale;
+import java.util.concurrent.Executor;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class HdmiCecSetMenuLanguageHelperTest extends SysuiTestCase {
+
+ private HdmiCecSetMenuLanguageHelper mHdmiCecSetMenuLanguageHelper;
+
+ @Mock
+ private Executor mExecutor;
+
+ @Mock
+ private SecureSettings mSecureSettings;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ when(mSecureSettings.getString(
+ Settings.Secure.HDMI_CEC_SET_MENU_LANGUAGE_DENYLIST)).thenReturn(null);
+ mHdmiCecSetMenuLanguageHelper =
+ new HdmiCecSetMenuLanguageHelper(mExecutor, mSecureSettings);
+ }
+
+ @Test
+ public void testSetGetLocale() {
+ mHdmiCecSetMenuLanguageHelper.setLocale("en");
+ assertThat(mHdmiCecSetMenuLanguageHelper.getLocale()).isEqualTo(Locale.ENGLISH);
+ }
+
+ @Test
+ public void testIsLocaleDenylisted_EmptyByDefault() {
+ mHdmiCecSetMenuLanguageHelper.setLocale("en");
+ assertThat(mHdmiCecSetMenuLanguageHelper.isLocaleDenylisted()).isEqualTo(false);
+ }
+
+ @Test
+ public void testIsLocaleDenylisted_AcceptLanguage() {
+ mHdmiCecSetMenuLanguageHelper.setLocale("de");
+ mHdmiCecSetMenuLanguageHelper.acceptLocale();
+ assertThat(mHdmiCecSetMenuLanguageHelper.isLocaleDenylisted()).isEqualTo(false);
+ verify(mExecutor).execute(any());
+ }
+
+ @Test
+ public void testIsLocaleDenylisted_DeclineLanguage() {
+ mHdmiCecSetMenuLanguageHelper.setLocale("de");
+ mHdmiCecSetMenuLanguageHelper.declineLocale();
+ assertThat(mHdmiCecSetMenuLanguageHelper.isLocaleDenylisted()).isEqualTo(true);
+ verify(mSecureSettings).putString(
+ Settings.Secure.HDMI_CEC_SET_MENU_LANGUAGE_DENYLIST, "de");
+ }
+
+ @Test
+ public void testIsLocaleDenylisted_DeclineTwoLanguages() {
+ mHdmiCecSetMenuLanguageHelper.setLocale("de");
+ mHdmiCecSetMenuLanguageHelper.declineLocale();
+ assertThat(mHdmiCecSetMenuLanguageHelper.isLocaleDenylisted()).isEqualTo(true);
+ verify(mSecureSettings).putString(
+ Settings.Secure.HDMI_CEC_SET_MENU_LANGUAGE_DENYLIST, "de");
+ mHdmiCecSetMenuLanguageHelper.setLocale("pl");
+ mHdmiCecSetMenuLanguageHelper.declineLocale();
+ assertThat(mHdmiCecSetMenuLanguageHelper.isLocaleDenylisted()).isEqualTo(true);
+ verify(mSecureSettings).putString(
+ Settings.Secure.HDMI_CEC_SET_MENU_LANGUAGE_DENYLIST, "de,pl");
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
index da8ab27d7e3d..d94e2eee9ffa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
@@ -48,6 +48,7 @@ import com.android.keyguard.mediator.ScreenOnCoordinator;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.classifier.FalsingCollectorFake;
+import com.android.systemui.dreams.DreamOverlayStateController;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.statusbar.NotificationShadeDepthController;
@@ -98,6 +99,7 @@ public class KeyguardViewMediatorTest extends SysuiTestCase {
private @Mock InteractionJankMonitor mInteractionJankMonitor;
private @Mock ScreenOnCoordinator mScreenOnCoordinator;
private @Mock Lazy<NotificationShadeWindowController> mNotificationShadeWindowControllerLazy;
+ private @Mock DreamOverlayStateController mDreamOverlayStateController;
private DeviceConfigProxy mDeviceConfig = new DeviceConfigProxyFake();
private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock());
@@ -202,6 +204,7 @@ public class KeyguardViewMediatorTest extends SysuiTestCase {
() -> mNotificationShadeDepthController,
mScreenOnCoordinator,
mInteractionJankMonitor,
+ mDreamOverlayStateController,
mNotificationShadeWindowControllerLazy);
mViewMediator.start();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java
index d7c00fbe1e85..5ed1d656a1f5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java
@@ -38,7 +38,6 @@ import android.graphics.drawable.AnimatedStateListDrawable;
import android.hardware.biometrics.BiometricSourceType;
import android.hardware.biometrics.SensorLocationInternal;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
-import android.os.Vibrator;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.util.Pair;
@@ -62,6 +61,7 @@ import com.android.systemui.dump.DumpManager;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.concurrency.FakeExecutor;
@@ -104,7 +104,7 @@ public class LockIconViewControllerTest extends SysuiTestCase {
private @Mock DumpManager mDumpManager;
private @Mock AccessibilityManager mAccessibilityManager;
private @Mock ConfigurationController mConfigurationController;
- private @Mock Vibrator mVibrator;
+ private @Mock VibratorHelper mVibrator;
private @Mock AuthRippleController mAuthRippleController;
private FakeExecutor mDelayableExecutor = new FakeExecutor(new FakeSystemClock());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt
index a3ffb2fe4b8d..97b3b10ebcbf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt
@@ -25,6 +25,7 @@ import androidx.test.filters.SmallTest
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.controls.controller.ControlsControllerImplTest.Companion.eq
+import com.android.systemui.dreams.DreamOverlayStateController
import com.android.systemui.keyguard.WakefulnessLifecycle
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.NotificationLockscreenUserManager
@@ -85,6 +86,8 @@ class MediaHierarchyManagerTest : SysuiTestCase() {
private lateinit var configurationController: ConfigurationController
@Mock
private lateinit var uniqueObjectHostView: UniqueObjectHostView
+ @Mock
+ private lateinit var dreamOverlayStateController: DreamOverlayStateController
@Captor
private lateinit var wakefullnessObserver: ArgumentCaptor<(WakefulnessLifecycle.Observer)>
@Captor
@@ -110,7 +113,8 @@ class MediaHierarchyManagerTest : SysuiTestCase() {
notificationLockscreenUserManager,
configurationController,
wakefulnessLifecycle,
- statusBarKeyguardViewManager)
+ statusBarKeyguardViewManager,
+ dreamOverlayStateController)
verify(wakefulnessLifecycle).addObserver(wakefullnessObserver.capture())
verify(statusBarStateController).addCallback(statusBarCallback.capture())
setupHost(lockHost, MediaHierarchyManager.LOCATION_LOCKSCREEN)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dream/MediaComplicationViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dream/MediaComplicationViewControllerTest.java
new file mode 100644
index 000000000000..29188da46562
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dream/MediaComplicationViewControllerTest.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.media.dream;
+
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
+
+import android.testing.AndroidTestingRunner;
+import android.widget.FrameLayout;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.media.MediaHost;
+import com.android.systemui.util.animation.UniqueObjectHostView;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class MediaComplicationViewControllerTest extends SysuiTestCase {
+ @Mock
+ private MediaHost mMediaHost;
+
+ @Mock
+ private UniqueObjectHostView mView;
+
+ @Mock
+ private FrameLayout mComplicationContainer;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ mMediaHost.hostView = mView;
+ }
+
+ @Test
+ public void testMediaHostViewInteraction() {
+ final MediaComplicationViewController controller = new MediaComplicationViewController(
+ mComplicationContainer, mMediaHost);
+
+ controller.init();
+
+ controller.onViewAttached();
+ verify(mComplicationContainer).addView(eq(mView));
+
+ controller.onViewDetached();
+ verify(mComplicationContainer).removeView(eq(mView));
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dream/MediaDreamSentinelTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dream/MediaDreamSentinelTest.java
new file mode 100644
index 000000000000..114fc90e8590
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dream/MediaDreamSentinelTest.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.media.dream;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.dreams.DreamOverlayStateController;
+import com.android.systemui.media.MediaData;
+import com.android.systemui.media.MediaDataManager;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class MediaDreamSentinelTest extends SysuiTestCase {
+ @Mock
+ MediaDataManager mMediaDataManager;
+
+ @Mock
+ DreamOverlayStateController mDreamOverlayStateController;
+
+ @Mock
+ MediaDreamComplication mComplication;
+
+ final String mKey = "key";
+ final String mOldKey = "old_key";
+
+ @Mock
+ MediaData mData;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ @Test
+ public void testComplicationAddition() {
+ final MediaDreamSentinel sentinel = new MediaDreamSentinel(mContext, mMediaDataManager,
+ mDreamOverlayStateController, mComplication);
+
+ sentinel.start();
+
+ ArgumentCaptor<MediaDataManager.Listener> listenerCaptor =
+ ArgumentCaptor.forClass(MediaDataManager.Listener.class);
+ verify(mMediaDataManager).addListener(listenerCaptor.capture());
+
+ final MediaDataManager.Listener listener = listenerCaptor.getValue();
+
+ when(mMediaDataManager.hasActiveMedia()).thenReturn(false);
+ listener.onMediaDataLoaded(mKey, mOldKey, mData, true, 0);
+ verify(mDreamOverlayStateController, never()).addComplication(any());
+
+ when(mMediaDataManager.hasActiveMedia()).thenReturn(true);
+ listener.onMediaDataLoaded(mKey, mOldKey, mData, true, 0);
+ verify(mDreamOverlayStateController).addComplication(eq(mComplication));
+
+ listener.onMediaDataRemoved(mKey);
+ verify(mDreamOverlayStateController, never()).removeComplication(any());
+
+ when(mMediaDataManager.hasActiveMedia()).thenReturn(false);
+ listener.onMediaDataRemoved(mKey);
+ verify(mDreamOverlayStateController).removeComplication(eq(mComplication));
+ }
+
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimatorTest.kt
new file mode 100644
index 000000000000..32314159f865
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimatorTest.kt
@@ -0,0 +1,117 @@
+package com.android.systemui.shared.animation
+
+import android.testing.AndroidTestingRunner
+import android.view.View
+import android.view.ViewGroup
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.shared.animation.UnfoldConstantTranslateAnimator.Direction
+import com.android.systemui.shared.animation.UnfoldConstantTranslateAnimator.ViewIdToTranslate
+import com.android.systemui.unfold.UnfoldTransitionProgressProvider
+import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
+import org.junit.Assert.assertEquals
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.Captor
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when` as whenever
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class UnfoldConstantTranslateAnimatorTest : SysuiTestCase() {
+
+ @Mock private lateinit var progressProvider: UnfoldTransitionProgressProvider
+
+ @Mock private lateinit var parent: ViewGroup
+
+ @Captor private lateinit var progressListenerCaptor: ArgumentCaptor<TransitionProgressListener>
+
+ private lateinit var animator: UnfoldConstantTranslateAnimator
+ private lateinit var progressListener: TransitionProgressListener
+
+ private val viewsIdToRegister =
+ setOf(
+ ViewIdToTranslate(LEFT_VIEW_ID, Direction.LEFT),
+ ViewIdToTranslate(RIGHT_VIEW_ID, Direction.RIGHT))
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+
+ animator =
+ UnfoldConstantTranslateAnimator(viewsIdToRegister, progressProvider)
+
+ animator.init(parent, MAX_TRANSLATION)
+
+ verify(progressProvider).addCallback(progressListenerCaptor.capture())
+ progressListener = progressListenerCaptor.value
+ }
+
+ @Test
+ fun onTransition_noMatchingIds() {
+ // GIVEN no views matching any ids
+ // WHEN the transition starts
+ progressListener.onTransitionStarted()
+ progressListener.onTransitionProgress(.1f)
+
+ // THEN nothing... no exceptions
+ }
+
+ @Test
+ fun onTransition_oneMovesLeft() {
+ // GIVEN one view with a matching id
+ val view = View(context)
+ whenever(parent.findViewById<View>(LEFT_VIEW_ID)).thenReturn(view)
+
+ moveAndValidate(listOf(view to LEFT))
+ }
+
+ @Test
+ fun onTransition_oneMovesLeftAndOneMovesRightMultipleTimes() {
+ // GIVEN two views with a matching id
+ val leftView = View(context)
+ val rightView = View(context)
+ whenever(parent.findViewById<View>(LEFT_VIEW_ID)).thenReturn(leftView)
+ whenever(parent.findViewById<View>(RIGHT_VIEW_ID)).thenReturn(rightView)
+
+ moveAndValidate(listOf(leftView to LEFT, rightView to RIGHT))
+ moveAndValidate(listOf(leftView to LEFT, rightView to RIGHT))
+ }
+
+ private fun moveAndValidate(list: List<Pair<View, Int>>) {
+ // Compare values as ints because -0f != 0f
+
+ // WHEN the transition starts
+ progressListener.onTransitionStarted()
+ progressListener.onTransitionProgress(0f)
+
+ list.forEach { (view, direction) ->
+ assertEquals((-MAX_TRANSLATION * direction).toInt(), view.translationX.toInt())
+ }
+
+ // WHEN the transition progresses, translation is updated
+ progressListener.onTransitionProgress(.5f)
+ list.forEach { (view, direction) ->
+ assertEquals((-MAX_TRANSLATION / 2f * direction).toInt(), view.translationX.toInt())
+ }
+
+ // WHEN the transition ends, translation is completed
+ progressListener.onTransitionProgress(1f)
+ progressListener.onTransitionFinished()
+ list.forEach { (view, _) -> assertEquals(0, view.translationX.toInt()) }
+ }
+
+ companion object {
+ private val LEFT = Direction.LEFT.multiplier.toInt()
+ private val RIGHT = Direction.RIGHT.multiplier.toInt()
+
+ private const val MAX_TRANSLATION = 42f
+
+ private const val LEFT_VIEW_ID = 1
+ private const val RIGHT_VIEW_ID = 2
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
index 42647f7b026a..d51d370eecb9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
@@ -227,7 +227,7 @@ class LockscreenShadeTransitionControllerTest : SysuiTestCase() {
fun testDragDownAmountDoesntCallOutInLockedDownShade() {
whenever(nsslController.isInLockedDownShade).thenReturn(true)
transitionController.dragDownAmount = 10f
- verify(nsslController, never()).setTransitionToFullShadeAmount(anyFloat())
+ verify(nsslController, never()).setTransitionToFullShadeAmount(anyFloat(), anyFloat())
verify(mediaHierarchyManager, never()).setTransitionToFullShadeAmount(anyFloat())
verify(scrimController, never()).setTransitionToFullShadeProgress(anyFloat())
verify(notificationPanelController, never()).setTransitionToFullShadeAmount(anyFloat(),
@@ -238,7 +238,7 @@ class LockscreenShadeTransitionControllerTest : SysuiTestCase() {
@Test
fun testDragDownAmountCallsOut() {
transitionController.dragDownAmount = 10f
- verify(nsslController).setTransitionToFullShadeAmount(anyFloat())
+ verify(nsslController).setTransitionToFullShadeAmount(anyFloat(), anyFloat())
verify(mediaHierarchyManager).setTransitionToFullShadeAmount(anyFloat())
verify(scrimController).setTransitionToFullShadeProgress(anyFloat())
verify(notificationPanelController).setTransitionToFullShadeAmount(anyFloat(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
index 83f1d87133c6..7fafb24317b7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
@@ -35,6 +35,7 @@ import android.widget.LinearLayout;
import androidx.test.filters.SmallTest;
+import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
@@ -56,6 +57,7 @@ import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
import com.android.systemui.statusbar.notification.stack.ForegroundServiceSectionController;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.wm.shell.bubbles.Bubbles;
import com.google.android.collect.Lists;
@@ -123,7 +125,9 @@ public class NotificationViewHierarchyManagerTest extends SysuiTestCase {
mock(DynamicChildBindController.class),
mock(LowPriorityInflationHelper.class),
mock(AssistantFeedbackController.class),
- mNotifPipelineFlags);
+ mNotifPipelineFlags,
+ mock(KeyguardUpdateMonitor.class),
+ mock(KeyguardStateController.class));
mViewHierarchyManager.setUpWithPresenter(mPresenter, mStackController, mListContainer);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/VibratorHelperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/VibratorHelperTest.kt
new file mode 100644
index 000000000000..ad908e7f8000
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/VibratorHelperTest.kt
@@ -0,0 +1,93 @@
+package com.android.systemui.statusbar
+
+import android.media.AudioAttributes
+import android.os.UserHandle
+import android.os.VibrationAttributes
+import android.os.VibrationEffect
+import android.os.Vibrator
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.mockito.eq
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.Captor
+import org.mockito.Mock
+import org.mockito.Mockito.`when` as whenever
+import org.mockito.Mockito.any
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.verify
+import org.mockito.junit.MockitoJUnit
+import java.util.concurrent.Executor
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class VibratorHelperTest : SysuiTestCase() {
+
+ @JvmField @Rule
+ var rule = MockitoJUnit.rule()
+
+ @Mock lateinit var vibrator: Vibrator
+ @Mock lateinit var executor: Executor
+ @Captor lateinit var backgroundTaskCaptor: ArgumentCaptor<Runnable>
+ lateinit var vibratorHelper: VibratorHelper
+
+ @Before
+ fun setup() {
+ vibratorHelper = VibratorHelper(vibrator, executor)
+ whenever(vibrator.hasVibrator()).thenReturn(true)
+ }
+
+ @Test
+ fun testVibrate() {
+ vibratorHelper.vibrate(VibrationEffect.EFFECT_CLICK)
+ verifyAsync().vibrate(any(VibrationEffect::class.java),
+ any(VibrationAttributes::class.java))
+ }
+
+ @Test
+ fun testVibrate2() {
+ vibratorHelper.vibrate(UserHandle.USER_CURRENT, "package",
+ mock(VibrationEffect::class.java), "reason",
+ mock(VibrationAttributes::class.java))
+ verifyAsync().vibrate(eq(UserHandle.USER_CURRENT), eq("package"),
+ any(VibrationEffect::class.java), eq("reason"),
+ any(VibrationAttributes::class.java))
+ }
+
+ @Test
+ fun testVibrate3() {
+ vibratorHelper.vibrate(mock(VibrationEffect::class.java), mock(AudioAttributes::class.java))
+ verifyAsync().vibrate(any(VibrationEffect::class.java), any(AudioAttributes::class.java))
+ }
+
+ @Test
+ fun testVibrate4() {
+ vibratorHelper.vibrate(mock(VibrationEffect::class.java))
+ verifyAsync().vibrate(any(VibrationEffect::class.java))
+ }
+
+ @Test
+ fun testHasVibrator() {
+ assertThat(vibratorHelper.hasVibrator()).isTrue()
+ verify(vibrator).hasVibrator()
+ }
+
+ @Test
+ fun testCancel() {
+ vibratorHelper.cancel()
+ verifyAsync().cancel()
+ }
+
+ private fun verifyAsync(): Vibrator {
+ verify(executor).execute(backgroundTaskCaptor.capture())
+ verify(vibrator).hasVibrator()
+ backgroundTaskCaptor.value.run()
+
+ return verify(vibrator)
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java
index 25dd23a955e6..8fb066bb4a39 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java
@@ -112,7 +112,7 @@ public class ShadeListBuilderTest extends SysuiTestCase {
private CollectionReadyForBuildListener mReadyForBuildListener;
private List<NotificationEntryBuilder> mPendingSet = new ArrayList<>();
private List<NotificationEntry> mEntrySet = new ArrayList<>();
- private List<ListEntry> mBuiltList;
+ private List<ListEntry> mBuiltList = new ArrayList<>();
private TestableStabilityManager mStabilityManager;
private TestableNotifFilter mFinalizeFilter;
@@ -368,6 +368,50 @@ public class ShadeListBuilderTest extends SysuiTestCase {
}
@Test
+ public void testGroupsWhoLoseAllChildrenToPromotionSuppressSummary() {
+ // GIVEN a group with two children
+ addGroupChild(0, PACKAGE_2, GROUP_1);
+ addGroupSummary(1, PACKAGE_2, GROUP_1);
+ addGroupChild(2, PACKAGE_2, GROUP_1);
+
+ // GIVEN a promoter that will promote one of children to top level
+ mListBuilder.addPromoter(new IdPromoter(0, 2));
+
+ // WHEN we build the list
+ dispatchBuild();
+
+ // THEN both children end up at top level (because group is now too small)
+ verifyBuiltList(
+ notif(0),
+ notif(2)
+ );
+
+ // THEN the summary is discarded
+ assertNull(mEntrySet.get(1).getParent());
+ }
+
+ @Test
+ public void testGroupsWhoLoseOnlyChildToPromotionSuppressSummary() {
+ // GIVEN a group with two children
+ addGroupChild(0, PACKAGE_2, GROUP_1);
+ addGroupSummary(1, PACKAGE_2, GROUP_1);
+
+ // GIVEN a promoter that will promote one of children to top level
+ mListBuilder.addPromoter(new IdPromoter(0));
+
+ // WHEN we build the list
+ dispatchBuild();
+
+ // THEN both children end up at top level (because group is now too small)
+ verifyBuiltList(
+ notif(0)
+ );
+
+ // THEN the summary is discarded
+ assertNull(mEntrySet.get(1).getParent());
+ }
+
+ @Test
public void testPreviousParentsAreSetProperly() {
// GIVEN a notification that is initially added to the list
PackageFilter filter = new PackageFilter(PACKAGE_2);
@@ -1642,6 +1686,19 @@ public class ShadeListBuilderTest extends SysuiTestCase {
}
@Test
+ public void testPipelineRunDisallowedDueToVisualStability() {
+ // GIVEN pipeline run not allowed due to visual stability
+ mStabilityManager.setAllowPipelineRun(false);
+
+ // WHEN we try to run the pipeline with a change
+ addNotif(0, PACKAGE_1);
+ dispatchBuild();
+
+ // THEN there is no change; the pipeline did not run
+ verifyBuiltList();
+ }
+
+ @Test
public void testIsSorted() {
Comparator<Integer> intCmp = Integer::compare;
assertTrue(ShadeListBuilder.isSorted(Collections.emptyList(), intCmp));
@@ -2037,6 +2094,7 @@ public class ShadeListBuilderTest extends SysuiTestCase {
}
private static class TestableStabilityManager extends NotifStabilityManager {
+ boolean mAllowPipelineRun = true;
boolean mAllowGroupChanges = true;
boolean mAllowSectionChanges = true;
boolean mAllowEntryReodering = true;
@@ -2060,6 +2118,15 @@ public class ShadeListBuilderTest extends SysuiTestCase {
return this;
}
+ TestableStabilityManager setAllowPipelineRun(boolean allowPipelineRun) {
+ mAllowPipelineRun = allowPipelineRun;
+ return this;
+ }
+
+ @Override
+ public boolean isPipelineRunAllowed() {
+ return mAllowPipelineRun;
+ }
@Override
public void onBeginRun() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt
index c67a2331b023..144eefb17283 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt
@@ -15,14 +15,20 @@
*/
package com.android.systemui.statusbar.notification.collection.coordinator
+import android.app.Notification.GROUP_ALERT_ALL
+import android.app.Notification.GROUP_ALERT_SUMMARY
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.dump.logcatLogBuffer
import com.android.systemui.statusbar.NotificationRemoteInputManager
+import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder
import com.android.systemui.statusbar.notification.collection.NotifPipeline
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
+import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeFinalizeFilterListener
+import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeTransformGroupsListener
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
@@ -32,6 +38,7 @@ import com.android.systemui.statusbar.notification.collection.render.NodeControl
import com.android.systemui.statusbar.notification.interruption.HeadsUpViewBinder
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider
import com.android.systemui.statusbar.notification.row.NotifBindPipeline.BindCallback
+import com.android.systemui.statusbar.phone.NotificationGroupTestHelper
import com.android.systemui.statusbar.policy.HeadsUpManager
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener
import com.android.systemui.util.concurrency.FakeExecutor
@@ -64,10 +71,13 @@ class HeadsUpCoordinatorTest : SysuiTestCase() {
private lateinit var mCollectionListener: NotifCollectionListener
private lateinit var mNotifPromoter: NotifPromoter
private lateinit var mNotifLifetimeExtender: NotifLifetimeExtender
+ private lateinit var mBeforeTransformGroupsListener: OnBeforeTransformGroupsListener
+ private lateinit var mBeforeFinalizeFilterListener: OnBeforeFinalizeFilterListener
private lateinit var mOnHeadsUpChangedListener: OnHeadsUpChangedListener
private lateinit var mNotifSectioner: NotifSectioner
private val mNotifPipeline: NotifPipeline = mock()
+ private val mLogger = HeadsUpCoordinatorLogger(logcatLogBuffer(), verbose = true)
private val mHeadsUpManager: HeadsUpManager = mock()
private val mHeadsUpViewBinder: HeadsUpViewBinder = mock()
private val mNotificationInterruptStateProvider: NotificationInterruptStateProvider = mock()
@@ -76,12 +86,24 @@ class HeadsUpCoordinatorTest : SysuiTestCase() {
private val mHeaderController: NodeController = mock()
private lateinit var mEntry: NotificationEntry
- private val mExecutor = FakeExecutor(FakeSystemClock())
+ private lateinit var mGroupSummary: NotificationEntry
+ private lateinit var mGroupPriority: NotificationEntry
+ private lateinit var mGroupSibling1: NotificationEntry
+ private lateinit var mGroupSibling2: NotificationEntry
+ private lateinit var mGroupChild1: NotificationEntry
+ private lateinit var mGroupChild2: NotificationEntry
+ private lateinit var mGroupChild3: NotificationEntry
+ private val mSystemClock = FakeSystemClock()
+ private val mExecutor = FakeExecutor(mSystemClock)
private val mHuns: ArrayList<NotificationEntry> = ArrayList()
+ private lateinit var mHelper: NotificationGroupTestHelper
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
+ mHelper = NotificationGroupTestHelper(mContext)
mCoordinator = HeadsUpCoordinator(
+ mLogger,
+ mSystemClock,
mHeadsUpManager,
mHeadsUpViewBinder,
mNotificationInterruptStateProvider,
@@ -100,6 +122,12 @@ class HeadsUpCoordinatorTest : SysuiTestCase() {
mNotifLifetimeExtender = withArgCaptor {
verify(mNotifPipeline).addNotificationLifetimeExtender(capture())
}
+ mBeforeTransformGroupsListener = withArgCaptor {
+ verify(mNotifPipeline).addOnBeforeTransformGroupsListener(capture())
+ }
+ mBeforeFinalizeFilterListener = withArgCaptor {
+ verify(mNotifPipeline).addOnBeforeFinalizeFilterListener(capture())
+ }
mOnHeadsUpChangedListener = withArgCaptor {
verify(mHeadsUpManager).addListener(capture())
}
@@ -116,6 +144,16 @@ class HeadsUpCoordinatorTest : SysuiTestCase() {
mNotifSectioner = mCoordinator.sectioner
mNotifLifetimeExtender.setCallback(mEndLifetimeExtension)
mEntry = NotificationEntryBuilder().build()
+ // Same summary we can use for either set of children
+ mGroupSummary = mHelper.createSummaryNotification(GROUP_ALERT_ALL, 0, "summary", 500)
+ // One set of children with GROUP_ALERT_SUMMARY
+ mGroupPriority = mHelper.createChildNotification(GROUP_ALERT_SUMMARY, 0, "priority", 400)
+ mGroupSibling1 = mHelper.createChildNotification(GROUP_ALERT_SUMMARY, 1, "sibling", 300)
+ mGroupSibling2 = mHelper.createChildNotification(GROUP_ALERT_SUMMARY, 2, "sibling", 200)
+ // Another set of children with GROUP_ALERT_ALL
+ mGroupChild1 = mHelper.createChildNotification(GROUP_ALERT_ALL, 1, "child", 350)
+ mGroupChild2 = mHelper.createChildNotification(GROUP_ALERT_ALL, 2, "child", 250)
+ mGroupChild3 = mHelper.createChildNotification(GROUP_ALERT_ALL, 3, "child", 150)
}
@Test
@@ -156,6 +194,34 @@ class HeadsUpCoordinatorTest : SysuiTestCase() {
}
@Test
+ fun testPromotesAddedHUN() {
+ // GIVEN the current entry should heads up
+ whenever(mNotificationInterruptStateProvider.shouldHeadsUp(mEntry)).thenReturn(true)
+
+ // WHEN the notification is added but not yet binding
+ mCollectionListener.onEntryAdded(mEntry)
+ verify(mHeadsUpViewBinder, never()).bindHeadsUpView(eq(mEntry), any())
+
+ // THEN only promote mEntry
+ assertTrue(mNotifPromoter.shouldPromoteToTopLevel(mEntry))
+ }
+
+ @Test
+ fun testPromotesBindingHUN() {
+ // GIVEN the current entry should heads up
+ whenever(mNotificationInterruptStateProvider.shouldHeadsUp(mEntry)).thenReturn(true)
+
+ // WHEN the notification started binding on the previous run
+ mCollectionListener.onEntryAdded(mEntry)
+ mBeforeTransformGroupsListener.onBeforeTransformGroups(listOf(mEntry))
+ mBeforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(mEntry))
+ verify(mHeadsUpViewBinder).bindHeadsUpView(eq(mEntry), any())
+
+ // THEN only promote mEntry
+ assertTrue(mNotifPromoter.shouldPromoteToTopLevel(mEntry))
+ }
+
+ @Test
fun testPromotesCurrentHUN() {
// GIVEN the current HUN is set to mEntry
addHUN(mEntry)
@@ -198,6 +264,9 @@ class HeadsUpCoordinatorTest : SysuiTestCase() {
whenever(mNotificationInterruptStateProvider.shouldHeadsUp(mEntry)).thenReturn(true)
mCollectionListener.onEntryAdded(mEntry)
+ mBeforeTransformGroupsListener.onBeforeTransformGroups(listOf(mEntry))
+ mBeforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(mEntry))
+ verify(mHeadsUpManager, never()).showNotification(mEntry)
withArgCaptor<BindCallback> {
verify(mHeadsUpViewBinder).bindHeadsUpView(eq(mEntry), capture())
}.onBindFinished(mEntry)
@@ -211,6 +280,8 @@ class HeadsUpCoordinatorTest : SysuiTestCase() {
// WHEN a notification shouldn't HUN and its inflation is finished
whenever(mNotificationInterruptStateProvider.shouldHeadsUp(mEntry)).thenReturn(false)
mCollectionListener.onEntryAdded(mEntry)
+ mBeforeTransformGroupsListener.onBeforeTransformGroups(listOf(mEntry))
+ mBeforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(mEntry))
// THEN we never bind the heads up view or tell HeadsUpManager to show the notification
verify(mHeadsUpViewBinder, never()).bindHeadsUpView(eq(mEntry), any())
@@ -235,4 +306,296 @@ class HeadsUpCoordinatorTest : SysuiTestCase() {
whenever(mHeadsUpManager.topEntry).thenReturn(entry)
mOnHeadsUpChangedListener.onHeadsUpStateChanged(entry, true)
}
-} \ No newline at end of file
+
+ @Test
+ fun testTransferIsolatedChildAlert_withGroupAlertSummary() {
+ setShouldHeadsUp(mGroupSummary)
+ whenever(mNotifPipeline.allNotifs).thenReturn(listOf(mGroupSummary, mGroupSibling1))
+
+ mCollectionListener.onEntryAdded(mGroupSummary)
+ mCollectionListener.onEntryAdded(mGroupSibling1)
+ mBeforeTransformGroupsListener.onBeforeTransformGroups(listOf(mGroupSibling1))
+ verify(mHeadsUpViewBinder, never()).bindHeadsUpView(any(), any())
+ mBeforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(mGroupSibling1))
+
+ verify(mHeadsUpViewBinder, never()).bindHeadsUpView(eq(mGroupSummary), any())
+ finishBind(mGroupSibling1)
+
+ verify(mHeadsUpManager, never()).showNotification(mGroupSummary)
+ verify(mHeadsUpManager).showNotification(mGroupSibling1)
+ }
+
+ @Test
+ fun testTransferIsolatedChildAlert_withGroupAlertAll() {
+ setShouldHeadsUp(mGroupSummary)
+ whenever(mNotifPipeline.allNotifs).thenReturn(listOf(mGroupSummary, mGroupChild1))
+
+ mCollectionListener.onEntryAdded(mGroupSummary)
+ mCollectionListener.onEntryAdded(mGroupChild1)
+ mBeforeTransformGroupsListener.onBeforeTransformGroups(listOf(mGroupChild1))
+ verify(mHeadsUpViewBinder, never()).bindHeadsUpView(any(), any())
+ mBeforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(mGroupChild1))
+
+ verify(mHeadsUpViewBinder, never()).bindHeadsUpView(eq(mGroupSummary), any())
+ finishBind(mGroupChild1)
+
+ verify(mHeadsUpManager, never()).showNotification(mGroupSummary)
+ verify(mHeadsUpManager).showNotification(mGroupChild1)
+ }
+
+ @Test
+ fun testTransferTwoIsolatedChildAlert_withGroupAlertSummary() {
+ // WHEN a notification should HUN and its inflation is finished
+ setShouldHeadsUp(mGroupSummary)
+ whenever(mNotifPipeline.allNotifs)
+ .thenReturn(listOf(mGroupSummary, mGroupSibling1, mGroupSibling2, mGroupPriority))
+
+ mCollectionListener.onEntryAdded(mGroupSummary)
+ mCollectionListener.onEntryAdded(mGroupSibling1)
+ mCollectionListener.onEntryAdded(mGroupSibling2)
+ val entryList = listOf(mGroupSibling1, mGroupSibling2)
+ mBeforeTransformGroupsListener.onBeforeTransformGroups(entryList)
+ verify(mHeadsUpViewBinder, never()).bindHeadsUpView(any(), any())
+ mBeforeFinalizeFilterListener.onBeforeFinalizeFilter(entryList)
+
+ verify(mHeadsUpViewBinder, never()).bindHeadsUpView(eq(mGroupSummary), any())
+ finishBind(mGroupSibling1)
+ verify(mHeadsUpViewBinder, never()).bindHeadsUpView(eq(mGroupSibling2), any())
+
+ // THEN we tell the HeadsUpManager to show the notification
+ verify(mHeadsUpManager, never()).showNotification(mGroupSummary)
+ verify(mHeadsUpManager).showNotification(mGroupSibling1)
+ verify(mHeadsUpManager, never()).showNotification(mGroupSibling2)
+ }
+
+ @Test
+ fun testTransferTwoIsolatedChildAlert_withGroupAlertAll() {
+ // WHEN a notification should HUN and its inflation is finished
+ setShouldHeadsUp(mGroupSummary)
+ whenever(mNotifPipeline.allNotifs)
+ .thenReturn(listOf(mGroupSummary, mGroupChild1, mGroupChild2, mGroupPriority))
+
+ mCollectionListener.onEntryAdded(mGroupSummary)
+ mCollectionListener.onEntryAdded(mGroupChild1)
+ mCollectionListener.onEntryAdded(mGroupChild2)
+ val entryList = listOf(mGroupChild1, mGroupChild2)
+ mBeforeTransformGroupsListener.onBeforeTransformGroups(entryList)
+ verify(mHeadsUpViewBinder, never()).bindHeadsUpView(any(), any())
+ mBeforeFinalizeFilterListener.onBeforeFinalizeFilter(entryList)
+
+ verify(mHeadsUpViewBinder, never()).bindHeadsUpView(eq(mGroupSummary), any())
+ finishBind(mGroupChild1)
+ verify(mHeadsUpViewBinder, never()).bindHeadsUpView(eq(mGroupChild2), any())
+
+ // THEN we tell the HeadsUpManager to show the notification
+ verify(mHeadsUpManager, never()).showNotification(mGroupSummary)
+ verify(mHeadsUpManager).showNotification(mGroupChild1)
+ verify(mHeadsUpManager, never()).showNotification(mGroupChild2)
+ }
+
+ @Test
+ fun testTransferToPriorityOnAddWithTwoSiblings() {
+ // WHEN a notification should HUN and its inflation is finished
+ setShouldHeadsUp(mGroupSummary)
+ whenever(mNotifPipeline.allNotifs)
+ .thenReturn(listOf(mGroupSummary, mGroupSibling1, mGroupSibling2, mGroupPriority))
+
+ mCollectionListener.onEntryAdded(mGroupSummary)
+ mCollectionListener.onEntryAdded(mGroupPriority)
+ mCollectionListener.onEntryAdded(mGroupSibling1)
+ mCollectionListener.onEntryAdded(mGroupSibling2)
+
+ val beforeTransformGroup = GroupEntryBuilder()
+ .setSummary(mGroupSummary)
+ .setChildren(listOf(mGroupSibling1, mGroupPriority, mGroupSibling2))
+ .build()
+ mBeforeTransformGroupsListener.onBeforeTransformGroups(listOf(beforeTransformGroup))
+ verify(mHeadsUpViewBinder, never()).bindHeadsUpView(any(), any())
+
+ val afterTransformGroup = GroupEntryBuilder()
+ .setSummary(mGroupSummary)
+ .setChildren(listOf(mGroupSibling1, mGroupSibling2))
+ .build()
+ mBeforeFinalizeFilterListener
+ .onBeforeFinalizeFilter(listOf(mGroupPriority, afterTransformGroup))
+
+ verify(mHeadsUpViewBinder, never()).bindHeadsUpView(eq(mGroupSummary), any())
+ finishBind(mGroupPriority)
+ verify(mHeadsUpViewBinder, never()).bindHeadsUpView(eq(mGroupSibling1), any())
+ verify(mHeadsUpViewBinder, never()).bindHeadsUpView(eq(mGroupSibling2), any())
+
+ // THEN we tell the HeadsUpManager to show the notification
+ verify(mHeadsUpManager, never()).showNotification(mGroupSummary)
+ verify(mHeadsUpManager).showNotification(mGroupPriority)
+ verify(mHeadsUpManager, never()).showNotification(mGroupSibling1)
+ verify(mHeadsUpManager, never()).showNotification(mGroupSibling2)
+ }
+
+ @Test
+ fun testTransferToPriorityOnUpdateWithTwoSiblings() {
+ setShouldHeadsUp(mGroupSummary)
+ whenever(mNotifPipeline.allNotifs)
+ .thenReturn(listOf(mGroupSummary, mGroupSibling1, mGroupSibling2, mGroupPriority))
+
+ mCollectionListener.onEntryUpdated(mGroupSummary)
+ mCollectionListener.onEntryUpdated(mGroupPriority)
+ mCollectionListener.onEntryUpdated(mGroupSibling1)
+ mCollectionListener.onEntryUpdated(mGroupSibling2)
+
+ val beforeTransformGroup = GroupEntryBuilder()
+ .setSummary(mGroupSummary)
+ .setChildren(listOf(mGroupSibling1, mGroupPriority, mGroupSibling2))
+ .build()
+ mBeforeTransformGroupsListener.onBeforeTransformGroups(listOf(beforeTransformGroup))
+ verify(mHeadsUpViewBinder, never()).bindHeadsUpView(any(), any())
+
+ val afterTransformGroup = GroupEntryBuilder()
+ .setSummary(mGroupSummary)
+ .setChildren(listOf(mGroupSibling1, mGroupSibling2))
+ .build()
+ mBeforeFinalizeFilterListener
+ .onBeforeFinalizeFilter(listOf(mGroupPriority, afterTransformGroup))
+
+ verify(mHeadsUpViewBinder, never()).bindHeadsUpView(eq(mGroupSummary), any())
+ finishBind(mGroupPriority)
+ verify(mHeadsUpViewBinder, never()).bindHeadsUpView(eq(mGroupSibling1), any())
+ verify(mHeadsUpViewBinder, never()).bindHeadsUpView(eq(mGroupSibling2), any())
+
+ verify(mHeadsUpManager, never()).showNotification(mGroupSummary)
+ verify(mHeadsUpManager).showNotification(mGroupPriority)
+ verify(mHeadsUpManager, never()).showNotification(mGroupSibling1)
+ verify(mHeadsUpManager, never()).showNotification(mGroupSibling2)
+ }
+
+ @Test
+ fun testTransferToPriorityOnUpdateWithTwoNonUpdatedSiblings() {
+ setShouldHeadsUp(mGroupSummary)
+ whenever(mNotifPipeline.allNotifs)
+ .thenReturn(listOf(mGroupSummary, mGroupSibling1, mGroupSibling2, mGroupPriority))
+
+ mCollectionListener.onEntryUpdated(mGroupSummary)
+ mCollectionListener.onEntryUpdated(mGroupPriority)
+
+ val beforeTransformGroup = GroupEntryBuilder()
+ .setSummary(mGroupSummary)
+ .setChildren(listOf(mGroupSibling1, mGroupPriority, mGroupSibling2))
+ .build()
+ mBeforeTransformGroupsListener.onBeforeTransformGroups(listOf(beforeTransformGroup))
+ verify(mHeadsUpViewBinder, never()).bindHeadsUpView(any(), any())
+
+ val afterTransformGroup = GroupEntryBuilder()
+ .setSummary(mGroupSummary)
+ .setChildren(listOf(mGroupSibling1, mGroupSibling2))
+ .build()
+ mBeforeFinalizeFilterListener
+ .onBeforeFinalizeFilter(listOf(mGroupPriority, afterTransformGroup))
+
+ verify(mHeadsUpViewBinder, never()).bindHeadsUpView(eq(mGroupSummary), any())
+ finishBind(mGroupPriority)
+ verify(mHeadsUpViewBinder, never()).bindHeadsUpView(eq(mGroupSibling1), any())
+ verify(mHeadsUpViewBinder, never()).bindHeadsUpView(eq(mGroupSibling2), any())
+
+ verify(mHeadsUpManager, never()).showNotification(mGroupSummary)
+ verify(mHeadsUpManager).showNotification(mGroupPriority)
+ verify(mHeadsUpManager, never()).showNotification(mGroupSibling1)
+ verify(mHeadsUpManager, never()).showNotification(mGroupSibling2)
+ }
+
+ @Test
+ fun testNoTransferToPriorityOnUpdateOfTwoSiblings() {
+ setShouldHeadsUp(mGroupSummary)
+ whenever(mNotifPipeline.allNotifs)
+ .thenReturn(listOf(mGroupSummary, mGroupSibling1, mGroupSibling2, mGroupPriority))
+
+ mCollectionListener.onEntryUpdated(mGroupSummary)
+ mCollectionListener.onEntryUpdated(mGroupSibling1)
+ mCollectionListener.onEntryUpdated(mGroupSibling2)
+
+ val beforeTransformGroup = GroupEntryBuilder()
+ .setSummary(mGroupSummary)
+ .setChildren(listOf(mGroupSibling1, mGroupPriority, mGroupSibling2))
+ .build()
+ mBeforeTransformGroupsListener.onBeforeTransformGroups(listOf(beforeTransformGroup))
+ verify(mHeadsUpViewBinder, never()).bindHeadsUpView(any(), any())
+
+ val afterTransformGroup = GroupEntryBuilder()
+ .setSummary(mGroupSummary)
+ .setChildren(listOf(mGroupSibling1, mGroupSibling2))
+ .build()
+ mBeforeFinalizeFilterListener
+ .onBeforeFinalizeFilter(listOf(mGroupPriority, afterTransformGroup))
+
+ finishBind(mGroupSummary)
+ verify(mHeadsUpViewBinder, never()).bindHeadsUpView(eq(mGroupPriority), any())
+ verify(mHeadsUpViewBinder, never()).bindHeadsUpView(eq(mGroupSibling1), any())
+ verify(mHeadsUpViewBinder, never()).bindHeadsUpView(eq(mGroupSibling2), any())
+
+ verify(mHeadsUpManager).showNotification(mGroupSummary)
+ verify(mHeadsUpManager, never()).showNotification(mGroupPriority)
+ verify(mHeadsUpManager, never()).showNotification(mGroupSibling1)
+ verify(mHeadsUpManager, never()).showNotification(mGroupSibling2)
+ }
+
+ @Test
+ fun testNoTransferTwoChildAlert_withGroupAlertSummary() {
+ setShouldHeadsUp(mGroupSummary)
+ whenever(mNotifPipeline.allNotifs)
+ .thenReturn(listOf(mGroupSummary, mGroupSibling1, mGroupSibling2))
+
+ mCollectionListener.onEntryAdded(mGroupSummary)
+ mCollectionListener.onEntryAdded(mGroupSibling1)
+ mCollectionListener.onEntryAdded(mGroupSibling2)
+ val groupEntry = GroupEntryBuilder()
+ .setSummary(mGroupSummary)
+ .setChildren(listOf(mGroupSibling1, mGroupSibling2))
+ .build()
+ mBeforeTransformGroupsListener.onBeforeTransformGroups(listOf(groupEntry))
+ verify(mHeadsUpViewBinder, never()).bindHeadsUpView(any(), any())
+ mBeforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(groupEntry))
+
+ finishBind(mGroupSummary)
+ verify(mHeadsUpViewBinder, never()).bindHeadsUpView(eq(mGroupSibling1), any())
+ verify(mHeadsUpViewBinder, never()).bindHeadsUpView(eq(mGroupSibling2), any())
+
+ verify(mHeadsUpManager).showNotification(mGroupSummary)
+ verify(mHeadsUpManager, never()).showNotification(mGroupSibling1)
+ verify(mHeadsUpManager, never()).showNotification(mGroupSibling2)
+ }
+
+ @Test
+ fun testNoTransferTwoChildAlert_withGroupAlertAll() {
+ setShouldHeadsUp(mGroupSummary)
+ whenever(mNotifPipeline.allNotifs)
+ .thenReturn(listOf(mGroupSummary, mGroupChild1, mGroupChild2))
+
+ mCollectionListener.onEntryAdded(mGroupSummary)
+ mCollectionListener.onEntryAdded(mGroupChild1)
+ mCollectionListener.onEntryAdded(mGroupChild2)
+ val groupEntry = GroupEntryBuilder()
+ .setSummary(mGroupSummary)
+ .setChildren(listOf(mGroupChild1, mGroupChild2))
+ .build()
+ mBeforeTransformGroupsListener.onBeforeTransformGroups(listOf(groupEntry))
+ verify(mHeadsUpViewBinder, never()).bindHeadsUpView(any(), any())
+ mBeforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(groupEntry))
+
+ finishBind(mGroupSummary)
+ verify(mHeadsUpViewBinder, never()).bindHeadsUpView(eq(mGroupChild1), any())
+ verify(mHeadsUpViewBinder, never()).bindHeadsUpView(eq(mGroupChild2), any())
+
+ verify(mHeadsUpManager).showNotification(mGroupSummary)
+ verify(mHeadsUpManager, never()).showNotification(mGroupChild1)
+ verify(mHeadsUpManager, never()).showNotification(mGroupChild2)
+ }
+
+ private fun setShouldHeadsUp(entry: NotificationEntry, should: Boolean = true) {
+ whenever(mNotificationInterruptStateProvider.shouldHeadsUp(entry)).thenReturn(should)
+ }
+
+ private fun finishBind(entry: NotificationEntry) {
+ verify(mHeadsUpManager, never()).showNotification(entry)
+ withArgCaptor<BindCallback> {
+ verify(mHeadsUpViewBinder).bindHeadsUpView(eq(entry), capture())
+ }.onBindFinished(entry)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinatorTest.kt
index 5fd4174af164..3f84c161db20 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinatorTest.kt
@@ -19,8 +19,11 @@ package com.android.systemui.statusbar.notification.collection.coordinator
import android.os.UserHandle
import android.service.notification.StatusBarNotification
import androidx.test.filters.SmallTest
+import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.SysuiTestCase
+import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.NotificationLockscreenUserManager
+import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.notification.DynamicPrivacyController
import com.android.systemui.statusbar.notification.collection.ListEntry
import com.android.systemui.statusbar.notification.collection.NotifPipeline
@@ -28,9 +31,12 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeRenderListListener
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Invalidator
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Pluggable
+import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.mockito.withArgCaptor
+import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.mock
import org.junit.Test
+import org.mockito.Mockito.never
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when` as whenever
@@ -40,9 +46,13 @@ class SensitiveContentCoordinatorTest : SysuiTestCase() {
val dynamicPrivacyController: DynamicPrivacyController = mock()
val lockscreenUserManager: NotificationLockscreenUserManager = mock()
val pipeline: NotifPipeline = mock()
+ val keyguardUpdateMonitor: KeyguardUpdateMonitor = mock()
+ val statusBarStateController: StatusBarStateController = mock()
+ val keyguardStateController: KeyguardStateController = mock()
val coordinator: SensitiveContentCoordinator = SensitiveContentCoordinatorModule
- .provideCoordinator(dynamicPrivacyController, lockscreenUserManager)
+ .provideCoordinator(dynamicPrivacyController, lockscreenUserManager,
+ keyguardUpdateMonitor, statusBarStateController, keyguardStateController)
@Test
fun onDynamicPrivacyChanged_invokeInvalidationListener() {
@@ -190,6 +200,28 @@ class SensitiveContentCoordinatorTest : SysuiTestCase() {
verify(entry.representativeEntry!!).setSensitive(true, true)
}
+ @Test
+ fun onBeforeRenderList_deviceDynamicallyUnlocked_deviceBiometricBypassingLockScreen() {
+ coordinator.attach(pipeline)
+ val onBeforeRenderListListener = withArgCaptor<OnBeforeRenderListListener> {
+ verify(pipeline).addOnBeforeRenderListListener(capture())
+ }
+
+ whenever(lockscreenUserManager.currentUserId).thenReturn(1)
+ whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true)
+ whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)).thenReturn(false)
+ whenever(dynamicPrivacyController.isDynamicallyUnlocked).thenReturn(true)
+ whenever(statusBarStateController.getState()).thenReturn(StatusBarState.KEYGUARD)
+ whenever(keyguardUpdateMonitor.getUserUnlockedWithBiometricAndIsBypassing(any()))
+ .thenReturn(true)
+
+ val entry = fakeNotification(2, true)
+
+ onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
+
+ verify(entry.representativeEntry!!, never()).setSensitive(any(), any())
+ }
+
private fun fakeNotification(notifUserId: Int, needsRedaction: Boolean): ListEntry {
val mockUserHandle = mock<UserHandle>().apply {
whenever(identifier).thenReturn(notifUserId)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java
index 5df1d28073fc..17b3b1ce2c3e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java
@@ -40,6 +40,7 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifStabilityManager;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Pluggable;
+import com.android.systemui.statusbar.notification.collection.render.NotifPanelEventSource;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.time.FakeSystemClock;
@@ -65,9 +66,11 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase {
@Mock private StatusBarStateController mStatusBarStateController;
@Mock private Pluggable.PluggableListener<NotifStabilityManager> mInvalidateListener;
@Mock private HeadsUpManager mHeadsUpManager;
+ @Mock private NotifPanelEventSource mNotifPanelEventSource;
@Captor private ArgumentCaptor<WakefulnessLifecycle.Observer> mWakefulnessObserverCaptor;
@Captor private ArgumentCaptor<StatusBarStateController.StateListener> mSBStateListenerCaptor;
+ @Captor private ArgumentCaptor<NotifPanelEventSource.Callbacks> mNotifPanelEventsCallbackCaptor;
@Captor private ArgumentCaptor<NotifStabilityManager> mNotifStabilityManagerCaptor;
private FakeSystemClock mFakeSystemClock = new FakeSystemClock();
@@ -75,6 +78,7 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase {
private WakefulnessLifecycle.Observer mWakefulnessObserver;
private StatusBarStateController.StateListener mStatusBarStateListener;
+ private NotifPanelEventSource.Callbacks mNotifPanelEventsCallback;
private NotifStabilityManager mNotifStabilityManager;
private NotificationEntry mEntry;
@@ -83,11 +87,12 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase {
MockitoAnnotations.initMocks(this);
mCoordinator = new VisualStabilityCoordinator(
+ mFakeExecutor,
mDumpManager,
mHeadsUpManager,
- mWakefulnessLifecycle,
+ mNotifPanelEventSource,
mStatusBarStateController,
- mFakeExecutor);
+ mWakefulnessLifecycle);
mCoordinator.attach(mNotifPipeline);
@@ -98,6 +103,9 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase {
verify(mStatusBarStateController).addCallback(mSBStateListenerCaptor.capture());
mStatusBarStateListener = mSBStateListenerCaptor.getValue();
+ verify(mNotifPanelEventSource).registerCallbacks(mNotifPanelEventsCallbackCaptor.capture());
+ mNotifPanelEventsCallback = mNotifPanelEventsCallbackCaptor.getValue();
+
verify(mNotifPipeline).setVisualStabilityManager(mNotifStabilityManagerCaptor.capture());
mNotifStabilityManager = mNotifStabilityManagerCaptor.getValue();
mNotifStabilityManager.setInvalidationListener(mInvalidateListener);
@@ -319,6 +327,58 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase {
}
@Test
+ public void testNotLaunchingActivityAnymore_invalidationCalled() {
+ // GIVEN visual stability is being maintained b/c animation is playing
+ setActivityLaunching(true);
+
+ assertFalse(mNotifStabilityManager.isPipelineRunAllowed());
+
+ // WHEN the animation has stopped playing
+ setActivityLaunching(false);
+
+ // invalidate is called, b/c we were previously suppressing the pipeline from running
+ verify(mInvalidateListener, times(1)).onPluggableInvalidated(mNotifStabilityManager);
+ }
+
+ @Test
+ public void testNotCollapsingPanelAnymore_invalidationCalled() {
+ // GIVEN visual stability is being maintained b/c animation is playing
+ setPanelCollapsing(true);
+
+ assertFalse(mNotifStabilityManager.isPipelineRunAllowed());
+
+ // WHEN the animation has stopped playing
+ setPanelCollapsing(false);
+
+ // invalidate is called, b/c we were previously suppressing the pipeline from running
+ verify(mInvalidateListener, times(1)).onPluggableInvalidated(mNotifStabilityManager);
+ }
+
+ @Test
+ public void testNeverSuppressPipelineRunFromPanelCollapse_noInvalidationCalled() {
+ // GIVEN animation is playing
+ setPanelCollapsing(true);
+
+ // WHEN the animation has stopped playing
+ setPanelCollapsing(false);
+
+ // THEN invalidate is not called, b/c nothing has been suppressed
+ verify(mInvalidateListener, never()).onPluggableInvalidated(mNotifStabilityManager);
+ }
+
+ @Test
+ public void testNeverSuppressPipelineRunFromLaunchActivity_noInvalidationCalled() {
+ // GIVEN animation is playing
+ setActivityLaunching(true);
+
+ // WHEN the animation has stopped playing
+ setActivityLaunching(false);
+
+ // THEN invalidate is not called, b/c nothing has been suppressed
+ verify(mInvalidateListener, never()).onPluggableInvalidated(mNotifStabilityManager);
+ }
+
+ @Test
public void testNotSuppressingEntryReorderingAnymoreWillInvalidate() {
// GIVEN visual stability is being maintained b/c panel is expanded
setPulsing(false);
@@ -370,6 +430,14 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase {
}
+ private void setActivityLaunching(boolean activityLaunching) {
+ mNotifPanelEventsCallback.onLaunchingActivityChanged(activityLaunching);
+ }
+
+ private void setPanelCollapsing(boolean collapsing) {
+ mNotifPanelEventsCallback.onPanelCollapsingChanged(collapsing);
+ }
+
private void setPulsing(boolean pulsing) {
mStatusBarStateListener.onPulsingChanged(pulsing);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDifferTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDifferTest.java
index 15ff5551703b..ab712649a90f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDifferTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDifferTest.java
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.notification.collection.render;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
import android.content.Context;
import android.testing.AndroidTestingRunner;
@@ -138,7 +139,7 @@ public class ShadeViewDifferTest extends SysuiTestCase {
}
@Test
- public void testRemovedGroupsAreKeptTogether() {
+ public void testRemovedGroupsAreBrokenApart() {
// GIVEN a preexisting tree with a group
applySpecAndCheck(
node(mController1),
@@ -154,10 +155,10 @@ public class ShadeViewDifferTest extends SysuiTestCase {
node(mController1)
);
- // THEN the group children are still attached to their parent
- assertEquals(mController2.getView(), mController3.getView().getParent());
- assertEquals(mController2.getView(), mController4.getView().getParent());
- assertEquals(mController2.getView(), mController5.getView().getParent());
+ // THEN the group children are no longer attached to their parent
+ assertNull(mController3.getView().getParent());
+ assertNull(mController4.getView().getParent());
+ assertNull(mController5.getView().getParent());
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/MediaContainerViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/MediaContainerViewTest.kt
new file mode 100644
index 000000000000..0909ff2e5993
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/MediaContainerViewTest.kt
@@ -0,0 +1,38 @@
+package com.android.systemui.statusbar.notification.stack
+
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import android.view.LayoutInflater
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.R
+import junit.framework.Assert.assertTrue
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Tests for {@link MediaContainView}.
+ */
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper
+class MediaContainerViewTest : SysuiTestCase() {
+
+ lateinit var mediaContainerView : MediaContainerView
+
+ @Before
+ fun setUp() {
+ mediaContainerView = LayoutInflater.from(context).inflate(
+ R.layout.keyguard_media_container, null, false) as MediaContainerView
+ }
+
+ @Test
+ fun testUpdateClipping_updatesClipHeight() {
+ assertTrue(mediaContainerView.clipHeight == 0)
+
+ mediaContainerView.actualHeight = 10
+ mediaContainerView.updateClipping()
+ assertTrue(mediaContainerView.clipHeight == 10)
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt
new file mode 100644
index 000000000000..d280f54e32f2
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt
@@ -0,0 +1,153 @@
+package com.android.systemui.statusbar.notification.stack
+
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper.RunWithLooper
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.NotificationShelf
+import junit.framework.Assert.assertFalse
+import junit.framework.Assert.assertTrue
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.*
+import org.mockito.Mockito.`when` as whenever
+
+/**
+ * Tests for {@link NotificationShelf}.
+ */
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@RunWithLooper
+class NotificationShelfTest : SysuiTestCase() {
+
+ private val shelf = NotificationShelf(context, /* attrs */ null)
+ private val shelfState = shelf.viewState as NotificationShelf.ShelfState
+ private val ambientState = mock(AmbientState::class.java)
+
+ @Before
+ fun setUp() {
+ shelf.bind(ambientState, /* hostLayoutController */ null)
+ shelf.layout(/* left */ 0, /* top */ 0, /* right */ 30, /* bottom */5)
+ }
+
+ @Test
+ fun testShadeWidth_BasedOnFractionToShade() {
+ setFractionToShade(0f)
+ setOnLockscreen(true)
+
+ shelf.updateStateWidth(shelfState, /* fraction */ 0f, /* shortestWidth */ 10)
+ assertTrue(shelfState.actualWidth == 10)
+
+ shelf.updateStateWidth(shelfState, /* fraction */ 0.5f, /* shortestWidth */ 10)
+ assertTrue(shelfState.actualWidth == 20)
+
+ shelf.updateStateWidth(shelfState, /* fraction */ 1f, /* shortestWidth */ 10)
+ assertTrue(shelfState.actualWidth == 30)
+ }
+
+ @Test
+ fun testShelfIsLong_WhenNotOnLockscreen() {
+ setFractionToShade(0f)
+ setOnLockscreen(false)
+
+ shelf.updateStateWidth(shelfState, /* fraction */ 0f, /* shortestWidth */ 10)
+ assertTrue(shelfState.actualWidth == 30)
+ }
+
+ @Test
+ fun testX_inViewForClick() {
+ val isXInView = shelf.isXInView(
+ /* localX */ 5f,
+ /* slop */ 5f,
+ /* left */ 0f,
+ /* right */ 10f)
+ assertTrue(isXInView)
+ }
+
+ @Test
+ fun testXSlop_inViewForClick() {
+ val isLeftXSlopInView = shelf.isXInView(
+ /* localX */ -3f,
+ /* slop */ 5f,
+ /* left */ 0f,
+ /* right */ 10f)
+ assertTrue(isLeftXSlopInView)
+
+ val isRightXSlopInView = shelf.isXInView(
+ /* localX */ 13f,
+ /* slop */ 5f,
+ /* left */ 0f,
+ /* right */ 10f)
+ assertTrue(isRightXSlopInView)
+ }
+
+ @Test
+ fun testX_notInViewForClick() {
+ val isXLeftOfShelfInView = shelf.isXInView(
+ /* localX */ -10f,
+ /* slop */ 5f,
+ /* left */ 0f,
+ /* right */ 10f)
+ assertFalse(isXLeftOfShelfInView)
+
+ val isXRightOfShelfInView = shelf.isXInView(
+ /* localX */ 20f,
+ /* slop */ 5f,
+ /* left */ 0f,
+ /* right */ 10f)
+ assertFalse(isXRightOfShelfInView)
+ }
+
+ @Test
+ fun testY_inViewForClick() {
+ val isYInView = shelf.isYInView(
+ /* localY */ 5f,
+ /* slop */ 5f,
+ /* top */ 0f,
+ /* bottom */ 10f)
+ assertTrue(isYInView)
+ }
+
+ @Test
+ fun testYSlop_inViewForClick() {
+ val isTopYSlopInView = shelf.isYInView(
+ /* localY */ -3f,
+ /* slop */ 5f,
+ /* top */ 0f,
+ /* bottom */ 10f)
+ assertTrue(isTopYSlopInView)
+
+ val isBottomYSlopInView = shelf.isYInView(
+ /* localY */ 13f,
+ /* slop */ 5f,
+ /* top */ 0f,
+ /* bottom */ 10f)
+ assertTrue(isBottomYSlopInView)
+ }
+
+ @Test
+ fun testY_notInViewForClick() {
+ val isYAboveShelfInView = shelf.isYInView(
+ /* localY */ -10f,
+ /* slop */ 5f,
+ /* top */ 0f,
+ /* bottom */ 5f)
+ assertFalse(isYAboveShelfInView)
+
+ val isYBelowShelfInView = shelf.isYInView(
+ /* localY */ 15f,
+ /* slop */ 5f,
+ /* top */ 0f,
+ /* bottom */ 5f)
+ assertFalse(isYBelowShelfInView)
+ }
+
+ private fun setFractionToShade(fraction: Float) {
+ shelf.setFractionToShade(fraction)
+ }
+
+ private fun setOnLockscreen(isOnLockscreen: Boolean) {
+ whenever(ambientState.isOnKeyguard).thenReturn(isOnLockscreen)
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index 46ba09795143..bdcbbbc99ea3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -27,6 +27,7 @@ import static com.google.common.truth.Truth.assertWithMessage;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertTrue;
import static org.junit.Assert.assertFalse;
import static org.mockito.ArgumentMatchers.any;
@@ -44,6 +45,7 @@ import android.os.UserHandle;
import android.provider.Settings;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import android.util.MathUtils;
import androidx.test.annotation.UiThreadTest;
import androidx.test.filters.SmallTest;
@@ -165,6 +167,47 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
}
@Test
+ public void testUpdateStackEndHeight_forEndOfStackHeightAnimation() {
+ final float nsslHeight = 10f;
+ final float bottomMargin = 1f;
+ final float topPadding = 1f;
+
+ mStackScroller.updateStackEndHeight(nsslHeight, bottomMargin, topPadding);
+ final float stackEndHeight = nsslHeight - bottomMargin - topPadding;
+ assertTrue(mAmbientState.getStackEndHeight() == stackEndHeight);
+ }
+
+ @Test
+ public void testUpdateStackHeight_withDozeAmount_whenDozeChanging() {
+ final float dozeAmount = 0.5f;
+ mAmbientState.setDozeAmount(dozeAmount);
+
+ final float endHeight = 8f;
+ final float expansionFraction = 1f;
+ float expected = MathUtils.lerp(
+ endHeight * StackScrollAlgorithm.START_FRACTION,
+ endHeight, dozeAmount);
+
+ mStackScroller.updateStackHeight(endHeight, expansionFraction);
+ assertTrue(mAmbientState.getStackHeight() == expected);
+ }
+
+ @Test
+ public void testUpdateStackHeight_withExpansionAmount_whenDozeNotChanging() {
+ final float dozeAmount = 1f;
+ mAmbientState.setDozeAmount(dozeAmount);
+
+ final float endHeight = 8f;
+ final float expansionFraction = 0.5f;
+ final float expected = MathUtils.lerp(
+ endHeight * StackScrollAlgorithm.START_FRACTION,
+ endHeight, expansionFraction);
+
+ mStackScroller.updateStackHeight(endHeight, expansionFraction);
+ assertTrue(mAmbientState.getStackHeight() == expected);
+ }
+
+ @Test
public void testNotDimmedOnKeyguard() {
when(mBarState.getState()).thenReturn(StatusBarState.SHADE);
mStackScroller.setDimmed(true /* dimmed */, false /* animate */);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
index ea681435132e..1da9bbcdb836 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
@@ -69,10 +69,9 @@ class StackScrollAlgorithmTest : SysuiTestCase() {
stackScrollAlgorithm.resetViewStates(ambientState, /* speedBumpIndex= */ 0)
- val closeHandleUnderlapHeight =
- context.resources.getDimensionPixelSize(R.dimen.close_handle_underlap)
- val fullHeight =
- ambientState.layoutMaxHeight + closeHandleUnderlapHeight - ambientState.stackY
+ val marginBottom =
+ context.resources.getDimensionPixelSize(R.dimen.notification_panel_margin_bottom)
+ val fullHeight = ambientState.layoutMaxHeight + marginBottom - ambientState.stackY
val centeredY = ambientState.stackY + fullHeight / 2f - emptyShadeView.height / 2f
assertThat(emptyShadeView.viewState?.yTranslation).isEqualTo(centeredY)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
index 5ca1f21eb021..8c7d22dde0b7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
@@ -46,6 +46,7 @@ import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.keyguard.WakefulnessLifecycle;
+import com.android.systemui.log.SessionTracker;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.NotificationShadeWindowController;
@@ -107,6 +108,8 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase {
private StatusBarStateController mStatusBarStateController;
@Mock
private KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
+ @Mock
+ private SessionTracker mSessionTracker;
private BiometricUnlockController mBiometricUnlockController;
@Before
@@ -129,7 +132,8 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase {
mUpdateMonitor, res.getResources(), mKeyguardBypassController, mDozeParameters,
mMetricsLogger, mDumpManager, mPowerManager,
mNotificationMediaManager, mWakefulnessLifecycle, mScreenLifecycle,
- mAuthController, mStatusBarStateController, mKeyguardUnlockAnimationController);
+ mAuthController, mStatusBarStateController, mKeyguardUnlockAnimationController,
+ mSessionTracker);
mBiometricUnlockController.setKeyguardViewController(mStatusBarKeyguardViewManager);
mBiometricUnlockController.setBiometricModeListener(mBiometricModeListener);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java
index 36a4c1e5ebfc..01e9822e0484 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java
@@ -23,9 +23,13 @@ import static com.android.systemui.statusbar.StatusBarState.SHADE;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.os.UserManager;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.LayoutInflater;
@@ -45,6 +49,7 @@ import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.UserInfoController;
@@ -52,6 +57,7 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -87,6 +93,12 @@ public class KeyguardStatusBarViewControllerTest extends SysuiTestCase {
private SysuiStatusBarStateController mStatusBarStateController;
@Mock
private StatusBarContentInsetsProvider mStatusBarContentInsetsProvider;
+ @Mock
+ private UserManager mUserManager;
+ @Captor
+ private ArgumentCaptor<ConfigurationListener> mConfigurationListenerCaptor;
+ @Captor
+ private ArgumentCaptor<KeyguardUpdateMonitorCallback> mKeyguardCallbackCaptor;
private TestNotificationPanelViewStateProvider mNotificationPanelViewStateProvider;
private KeyguardStatusBarView mKeyguardStatusBarView;
@@ -101,8 +113,8 @@ public class KeyguardStatusBarViewControllerTest extends SysuiTestCase {
allowTestableLooperAsMainThread();
TestableLooper.get(this).runWithLooper(() -> {
mKeyguardStatusBarView =
- (KeyguardStatusBarView) LayoutInflater.from(mContext)
- .inflate(R.layout.keyguard_status_bar, null);
+ spy((KeyguardStatusBarView) LayoutInflater.from(mContext)
+ .inflate(R.layout.keyguard_status_bar, null));
});
mController = new KeyguardStatusBarViewController(
@@ -121,7 +133,8 @@ public class KeyguardStatusBarViewControllerTest extends SysuiTestCase {
mKeyguardUpdateMonitor,
mBiometricUnlockController,
mStatusBarStateController,
- mStatusBarContentInsetsProvider
+ mStatusBarContentInsetsProvider,
+ mUserManager
);
}
@@ -133,6 +146,31 @@ public class KeyguardStatusBarViewControllerTest extends SysuiTestCase {
verify(mAnimationScheduler).addCallback(any());
verify(mUserInfoController).addCallback(any());
verify(mStatusBarIconController).addIconGroup(any());
+ verify(mUserManager).isUserSwitcherEnabled(anyBoolean());
+ }
+
+ @Test
+ public void onConfigurationChanged_updatesUserSwitcherVisibility() {
+ mController.onViewAttached();
+ verify(mConfigurationController).addCallback(mConfigurationListenerCaptor.capture());
+ clearInvocations(mUserManager);
+ clearInvocations(mKeyguardStatusBarView);
+
+ mConfigurationListenerCaptor.getValue().onConfigChanged(null);
+ verify(mUserManager).isUserSwitcherEnabled(anyBoolean());
+ verify(mKeyguardStatusBarView).setUserSwitcherEnabled(anyBoolean());
+ }
+
+ @Test
+ public void onKeyguardVisibilityChanged_updatesUserSwitcherVisibility() {
+ mController.onViewAttached();
+ verify(mKeyguardUpdateMonitor).registerCallback(mKeyguardCallbackCaptor.capture());
+ clearInvocations(mUserManager);
+ clearInvocations(mKeyguardStatusBarView);
+
+ mKeyguardCallbackCaptor.getValue().onKeyguardVisibilityChanged(true);
+ verify(mUserManager).isUserSwitcherEnabled(anyBoolean());
+ verify(mKeyguardStatusBarView).setUserSwitcherEnabled(anyBoolean());
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerLegacyTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerLegacyTest.java
index 5d7b15424fec..d002cebe5cb1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerLegacyTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerLegacyTest.java
@@ -250,15 +250,17 @@ public class NotificationGroupManagerLegacyTest extends SysuiTestCase {
@Notification.GroupAlertBehavior int priorityGroupAlert,
@Notification.GroupAlertBehavior int siblingGroupAlert,
boolean expectAlertOverride) {
+ long when = 10000;
// Create entries in an order so that the priority entry can be deemed the newest child.
NotificationEntry[] siblings = new NotificationEntry[numSiblings];
for (int i = 0; i < numSiblings; i++) {
- siblings[i] = mGroupTestHelper.createChildNotification(siblingGroupAlert, i, "sibling");
+ siblings[i] = mGroupTestHelper
+ .createChildNotification(siblingGroupAlert, i, "sibling", ++when);
}
NotificationEntry priorityEntry =
- mGroupTestHelper.createChildNotification(priorityGroupAlert, 0, "priority");
+ mGroupTestHelper.createChildNotification(priorityGroupAlert, 0, "priority", ++when);
NotificationEntry summaryEntry =
- mGroupTestHelper.createSummaryNotification(summaryGroupAlert, 0, "summary");
+ mGroupTestHelper.createSummaryNotification(summaryGroupAlert, 0, "summary", ++when);
// The priority entry is an important conversation.
when(mPeopleNotificationIdentifier.getPeopleNotificationType(eq(priorityEntry)))
@@ -322,17 +324,19 @@ public class NotificationGroupManagerLegacyTest extends SysuiTestCase {
@Test
public void testAlertOverrideWhenUpdatingSummaryAtEnd() {
+ long when = 10000;
int numSiblings = 2;
int groupAlert = Notification.GROUP_ALERT_SUMMARY;
// Create entries in an order so that the priority entry can be deemed the newest child.
NotificationEntry[] siblings = new NotificationEntry[numSiblings];
for (int i = 0; i < numSiblings; i++) {
- siblings[i] = mGroupTestHelper.createChildNotification(groupAlert, i, "sibling");
+ siblings[i] =
+ mGroupTestHelper.createChildNotification(groupAlert, i, "sibling", ++when);
}
NotificationEntry priorityEntry =
- mGroupTestHelper.createChildNotification(groupAlert, 0, "priority");
+ mGroupTestHelper.createChildNotification(groupAlert, 0, "priority", ++when);
NotificationEntry summaryEntry =
- mGroupTestHelper.createSummaryNotification(groupAlert, 0, "summary");
+ mGroupTestHelper.createSummaryNotification(groupAlert, 0, "summary", ++when);
// The priority entry is an important conversation.
when(mPeopleNotificationIdentifier.getPeopleNotificationType(eq(priorityEntry)))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupTestHelper.java
index ac32b9d6f01a..47f15a1720a9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupTestHelper.java
@@ -59,6 +59,13 @@ public final class NotificationGroupTestHelper {
return createEntry(id, tag, true, groupAlertBehavior);
}
+ public NotificationEntry createSummaryNotification(
+ int groupAlertBehavior, int id, String tag, long when) {
+ NotificationEntry entry = createSummaryNotification(groupAlertBehavior, id, tag);
+ entry.getSbn().getNotification().when = when;
+ return entry;
+ }
+
public NotificationEntry createChildNotification() {
return createChildNotification(Notification.GROUP_ALERT_ALL);
}
@@ -71,6 +78,13 @@ public final class NotificationGroupTestHelper {
return createEntry(id, tag, false, groupAlertBehavior);
}
+ public NotificationEntry createChildNotification(
+ int groupAlertBehavior, int id, String tag, long when) {
+ NotificationEntry entry = createChildNotification(groupAlertBehavior, id, tag);
+ entry.getSbn().getNotification().when = when;
+ return entry;
+ }
+
public NotificationEntry createEntry(int id, String tag, boolean isSummary,
int groupAlertBehavior) {
Notification notif = new Notification.Builder(mContext, TEST_CHANNEL_ID)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationQSContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationQSContainerControllerTest.kt
index 337e64592750..77e90256ab70 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationQSContainerControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationQSContainerControllerTest.kt
@@ -215,6 +215,18 @@ class NotificationQSContainerControllerTest : SysuiTestCase() {
then(expectedContainerPadding = 0)
}
+ @Test
+ fun testNotificationsMarginBottomIsUpdated() {
+ notificationsQSContainerController.splitShadeEnabled = true
+ verify(notificationsQSContainer).setNotificationsMarginBottom(NOTIFICATIONS_MARGIN)
+
+ whenever(notificationsQSContainer.defaultNotificationsMarginBottom).thenReturn(100)
+ notificationsQSContainerController.updateMargins()
+ notificationsQSContainerController.splitShadeEnabled = false
+
+ verify(notificationsQSContainer).setNotificationsMarginBottom(100)
+ }
+
private fun given(
taskbarVisible: Boolean,
navigationMode: Int,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index 5d80bca03e03..bb79941b0e53 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -43,6 +43,7 @@ import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.ViewMediatorCallback;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dock.DockManager;
+import com.android.systemui.dreams.DreamOverlayStateController;
import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
import com.android.systemui.statusbar.NotificationMediaManager;
@@ -97,6 +98,8 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
private KeyguardMessageArea mKeyguardMessageArea;
@Mock
private ShadeController mShadeController;
+ @Mock
+ private DreamOverlayStateController mDreamOverlayStateController;
private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
@@ -116,6 +119,7 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
mStatusBarStateController,
mock(ConfigurationController.class),
mKeyguardUpdateMonitor,
+ mDreamOverlayStateController,
mock(NavigationModeController.class),
mock(DockManager.class),
mock(NotificationShadeWindowController.class),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index b7c00fe5e3a1..1564dfe8cd06 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -87,6 +87,7 @@ import com.android.systemui.classifier.FalsingCollectorFake;
import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.demomode.DemoModeController;
+import com.android.systemui.dreams.DreamOverlayStateController;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.fragments.FragmentService;
@@ -285,6 +286,7 @@ public class StatusBarTest extends SysuiTestCase {
@Mock private NotifLiveDataStore mNotifLiveDataStore;
@Mock private InteractionJankMonitor mJankMonitor;
@Mock private DeviceStateManager mDeviceStateManager;
+ @Mock private DreamOverlayStateController mDreamOverlayStateController;
private ShadeController mShadeController;
private final FakeSystemClock mFakeSystemClock = new FakeSystemClock();
private FakeExecutor mMainExecutor = new FakeExecutor(mFakeSystemClock);
@@ -474,7 +476,8 @@ public class StatusBarTest extends SysuiTestCase {
mActivityLaunchAnimator,
mNotifPipelineFlags,
mJankMonitor,
- mDeviceStateManager);
+ mDeviceStateManager,
+ mDreamOverlayStateController);
when(mKeyguardViewMediator.registerStatusBar(
any(StatusBar.class),
any(NotificationPanelViewController.class),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java
index 6364d2f23299..48b17322da4d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java
@@ -36,6 +36,7 @@ import androidx.test.filters.SmallTest;
import com.android.internal.R;
import com.android.internal.view.RotationPolicy;
+import com.android.settingslib.devicestate.DeviceStateRotationLockSettingsManager;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.time.FakeSystemClock;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchControllerTest.kt
new file mode 100644
index 000000000000..a57f6a11d22f
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchControllerTest.kt
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar.policy
+
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import android.testing.ViewUtils
+import android.view.LayoutInflater
+import android.view.View
+import android.widget.FrameLayout
+import androidx.test.filters.SmallTest
+import com.android.internal.logging.UiEventLogger
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.communal.CommunalStateController
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.keyguard.ScreenLifecycle
+import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.qs.tiles.UserDetailView
+import com.android.systemui.qs.user.UserSwitchDialogController
+import com.android.systemui.statusbar.SysuiStatusBarStateController
+import com.android.systemui.statusbar.phone.DozeParameters
+import com.android.systemui.statusbar.phone.LockscreenGestureLogger
+import com.android.systemui.statusbar.phone.NotificationPanelViewController
+import com.android.systemui.statusbar.phone.ScreenOffAnimationController
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
+import org.mockito.MockitoAnnotations
+import javax.inject.Provider
+
+@SmallTest
+@TestableLooper.RunWithLooper
+@RunWith(AndroidTestingRunner::class)
+class KeyguardQsUserSwitchControllerTest : SysuiTestCase() {
+ @Mock
+ private lateinit var screenLifecycle: ScreenLifecycle
+
+ @Mock
+ private lateinit var userSwitcherController: UserSwitcherController
+
+ @Mock
+ private lateinit var communalStateController: CommunalStateController
+
+ @Mock
+ private lateinit var keyguardStateController: KeyguardStateController
+
+ @Mock
+ private lateinit var falsingManager: FalsingManager
+
+ @Mock
+ private lateinit var configurationController: ConfigurationController
+
+ @Mock
+ private lateinit var statusBarStateController: SysuiStatusBarStateController
+
+ @Mock
+ private lateinit var dozeParameters: DozeParameters
+
+ @Mock
+ private lateinit var userDetailViewAdapterProvider: Provider<UserDetailView.Adapter>
+
+ @Mock
+ private lateinit var screenOffAnimationController: ScreenOffAnimationController
+
+ @Mock
+ private lateinit var featureFlags: FeatureFlags
+
+ @Mock
+ private lateinit var userSwitchDialogController: UserSwitchDialogController
+
+ @Mock
+ private lateinit var uiEventLogger: UiEventLogger
+
+ @Mock
+ private lateinit var notificationPanelViewController: NotificationPanelViewController
+
+ private lateinit var view: FrameLayout
+ private lateinit var testableLooper: TestableLooper
+ private lateinit var keyguardQsUserSwitchController: KeyguardQsUserSwitchController
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ testableLooper = TestableLooper.get(this)
+
+ view = LayoutInflater.from(context)
+ .inflate(R.layout.keyguard_qs_user_switch, null) as FrameLayout
+
+ keyguardQsUserSwitchController = KeyguardQsUserSwitchController(
+ view,
+ context,
+ context.resources,
+ screenLifecycle,
+ userSwitcherController,
+ communalStateController,
+ keyguardStateController,
+ falsingManager,
+ configurationController,
+ statusBarStateController,
+ dozeParameters,
+ userDetailViewAdapterProvider,
+ screenOffAnimationController,
+ featureFlags,
+ userSwitchDialogController,
+ uiEventLogger)
+
+ ViewUtils.attachView(view)
+ testableLooper.processAllMessages()
+ keyguardQsUserSwitchController
+ .setNotificationPanelViewController(notificationPanelViewController)
+ `when`(userSwitcherController.keyguardStateController).thenReturn(keyguardStateController)
+ `when`(userSwitcherController.keyguardStateController.isShowing).thenReturn(true)
+ keyguardQsUserSwitchController.init()
+ }
+
+ @After
+ fun tearDown() {
+ ViewUtils.detachView(view)
+ }
+
+ @Test
+ fun testUiEventLogged() {
+ view.findViewById<View>(R.id.kg_multi_user_avatar)?.performClick()
+ verify(uiEventLogger, times(1))
+ .log(LockscreenGestureLogger.LockscreenUiEvent.LOCKSCREEN_SWITCH_USER_TAP)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt
index fa2a9066d99b..9a7e702152b4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt
@@ -400,4 +400,16 @@ class UserSwitcherControllerTest : SysuiTestCase() {
assertEquals(fgUserName, userSwitcherController.currentUserName)
}
+
+ @Test
+ fun isSystemUser_currentUserIsSystemUser_shouldReturnTrue() {
+ `when`(userTracker.userId).thenReturn(UserHandle.USER_SYSTEM)
+ assertEquals(true, userSwitcherController.isSystemUser)
+ }
+
+ @Test
+ fun isSystemUser_currentUserIsNotSystemUser_shouldReturnFalse() {
+ `when`(userTracker.userId).thenReturn(1)
+ assertEquals(false, userSwitcherController.isSystemUser)
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/service/PackageObserverTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/service/PackageObserverTest.java
new file mode 100644
index 000000000000..a2fd288ef33e
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/service/PackageObserverTest.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util.service;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.verify;
+
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class PackageObserverTest extends SysuiTestCase {
+ @Mock
+ Context mContext;
+
+ @Mock
+ Observer.Callback mCallback;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ @Test
+ public void testChange() {
+ final PackageObserver observer = new PackageObserver(mContext,
+ ComponentName.unflattenFromString("com.foo.bar/baz"));
+ final ArgumentCaptor<BroadcastReceiver> receiverCapture =
+ ArgumentCaptor.forClass(BroadcastReceiver.class);
+
+ observer.addCallback(mCallback);
+
+ // Verify broadcast receiver registered.
+ verify(mContext).registerReceiver(receiverCapture.capture(), any(), anyInt());
+
+ // Simulate package change.
+ receiverCapture.getValue().onReceive(mContext, new Intent());
+
+ // Check that callback was informed.
+ verify(mCallback).onSourceChanged();
+
+ observer.removeCallback(mCallback);
+
+ // Make sure receiver is unregistered on last callback removal
+ verify(mContext).unregisterReceiver(receiverCapture.getValue());
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/service/PersistentConnectionManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/service/PersistentConnectionManagerTest.java
new file mode 100644
index 000000000000..53d4a96b0640
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/service/PersistentConnectionManagerTest.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util.service;
+
+import static org.mockito.Mockito.verify;
+
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.time.FakeSystemClock;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class PersistentConnectionManagerTest extends SysuiTestCase {
+ private static final int MAX_RETRIES = 5;
+ private static final int RETRY_DELAY_MS = 1000;
+ private static final int CONNECTION_MIN_DURATION_MS = 5000;
+
+ private FakeSystemClock mFakeClock = new FakeSystemClock();
+ private FakeExecutor mFakeExecutor = new FakeExecutor(mFakeClock);
+
+ @Mock
+ private ObservableServiceConnection<Proxy> mConnection;
+
+ @Mock
+ private Observer mObserver;
+
+ private static class Proxy {
+ }
+
+ private PersistentConnectionManager<Proxy> mConnectionManager;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+
+ mConnectionManager = new PersistentConnectionManager<>(
+ mFakeClock,
+ mFakeExecutor,
+ mConnection,
+ MAX_RETRIES,
+ RETRY_DELAY_MS,
+ CONNECTION_MIN_DURATION_MS,
+ mObserver);
+ }
+
+ private ObservableServiceConnection.Callback<Proxy> captureCallbackAndSend(
+ ObservableServiceConnection<Proxy> mConnection, Proxy proxy) {
+ ArgumentCaptor<ObservableServiceConnection.Callback<Proxy>> connectionCallbackCaptor =
+ ArgumentCaptor.forClass(ObservableServiceConnection.Callback.class);
+
+ verify(mConnection).addCallback(connectionCallbackCaptor.capture());
+ verify(mConnection).bind();
+ Mockito.clearInvocations(mConnection);
+
+ final ObservableServiceConnection.Callback callback = connectionCallbackCaptor.getValue();
+ if (proxy != null) {
+ callback.onConnected(mConnection, proxy);
+ } else {
+ callback.onDisconnected(mConnection, 0);
+ }
+
+ return callback;
+ }
+
+ /**
+ * Validates initial connection.
+ */
+ @Test
+ public void testConnect() {
+ mConnectionManager.start();
+ captureCallbackAndSend(mConnection, Mockito.mock(Proxy.class));
+ }
+
+ /**
+ * Ensures reconnection on disconnect.
+ */
+ @Test
+ public void testRetryOnBindFailure() {
+ mConnectionManager.start();
+ ArgumentCaptor<ObservableServiceConnection.Callback<Proxy>> connectionCallbackCaptor =
+ ArgumentCaptor.forClass(ObservableServiceConnection.Callback.class);
+
+ verify(mConnection).addCallback(connectionCallbackCaptor.capture());
+
+ // Verify attempts happen. Note that we account for the retries plus initial attempt, which
+ // is not scheduled.
+ for (int attemptCount = 0; attemptCount < MAX_RETRIES + 1; attemptCount++) {
+ verify(mConnection).bind();
+ Mockito.clearInvocations(mConnection);
+ connectionCallbackCaptor.getValue().onDisconnected(mConnection, 0);
+ mFakeExecutor.advanceClockToNext();
+ mFakeExecutor.runAllReady();
+ }
+ }
+
+ /**
+ * Ensures rebind on package change.
+ */
+ @Test
+ public void testAttemptOnPackageChange() {
+ mConnectionManager.start();
+ verify(mConnection).bind();
+ ArgumentCaptor<Observer.Callback> callbackCaptor =
+ ArgumentCaptor.forClass(Observer.Callback.class);
+ captureCallbackAndSend(mConnection, Mockito.mock(Proxy.class));
+
+ verify(mObserver).addCallback(callbackCaptor.capture());
+
+ callbackCaptor.getValue().onSourceChanged();
+ verify(mConnection).bind();
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java
index c9462d651bc0..b3805533cabd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java
@@ -33,7 +33,6 @@ import android.media.IAudioService;
import android.media.session.MediaSession;
import android.os.Handler;
import android.os.Process;
-import android.os.Vibrator;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.accessibility.AccessibilityManager;
@@ -43,6 +42,7 @@ import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.keyguard.WakefulnessLifecycle;
+import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.util.RingerModeLiveData;
import com.android.systemui.util.RingerModeTracker;
import com.android.systemui.util.concurrency.FakeExecutor;
@@ -57,8 +57,6 @@ import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import java.util.Optional;
-
@RunWith(AndroidTestingRunner.class)
@SmallTest
@TestableLooper.RunWithLooper
@@ -81,7 +79,7 @@ public class VolumeDialogControllerImplTest extends SysuiTestCase {
@Mock
private NotificationManager mNotificationManager;
@Mock
- private Vibrator mVibrator;
+ private VibratorHelper mVibrator;
@Mock
private IAudioService mIAudioService;
@Mock
@@ -110,7 +108,7 @@ public class VolumeDialogControllerImplTest extends SysuiTestCase {
mThreadFactory.setLooper(TestableLooper.get(this).getLooper());
mVolumeController = new TestableVolumeDialogControllerImpl(mContext,
mBroadcastDispatcher, mRingerModeTracker, mThreadFactory, mAudioManager,
- mNotificationManager, Optional.of(mVibrator), mIAudioService, mAccessibilityManager,
+ mNotificationManager, mVibrator, mIAudioService, mAccessibilityManager,
mPackageManager, mWakefullnessLifcycle, mCallback);
mVolumeController.setEnableDialogs(true, true);
}
@@ -181,7 +179,7 @@ public class VolumeDialogControllerImplTest extends SysuiTestCase {
ThreadFactory theadFactory,
AudioManager audioManager,
NotificationManager notificationManager,
- Optional<Vibrator> optionalVibrator,
+ VibratorHelper optionalVibrator,
IAudioService iAudioService,
AccessibilityManager accessibilityManager,
PackageManager packageManager,
diff --git a/proto/src/system_messages.proto b/proto/src/system_messages.proto
index e89dda90a2dd..177b080bfbf6 100644
--- a/proto/src/system_messages.proto
+++ b/proto/src/system_messages.proto
@@ -282,6 +282,10 @@ message SystemMessage {
// Notify the user to set up dream
NOTE_SETUP_DREAM = 68;
+ // Inform the user that MTE override is active.
+ // Package: android
+ NOTE_MTE_OVERRIDE_ENABLED = 69;
+
// ADD_NEW_IDS_ABOVE_THIS_LINE
// Legacy IDs with arbitrary values appear below
// Legacy IDs existed as stable non-conflicting constants prior to the O release
diff --git a/services/api/current.txt b/services/api/current.txt
index 50f00524676c..dcf7e64479da 100644
--- a/services/api/current.txt
+++ b/services/api/current.txt
@@ -39,6 +39,7 @@ package com.android.server.am {
public interface ActivityManagerLocal {
method public boolean canStartForegroundService(int, int, @NonNull String);
+ method public boolean startAndBindSupplementalProcessService(@NonNull android.content.Intent, @NonNull android.content.ServiceConnection, int) throws android.os.TransactionTooLargeException;
}
}
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index 2168fb18888f..a65d5b3b94f7 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -3346,8 +3346,10 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
// Isolate the changes relating to RROs. The app info must be copied to prevent
// affecting other parts of system server that may have cached this app info.
oldAppInfo = new ApplicationInfo(oldAppInfo);
- oldAppInfo.overlayPaths = newAppInfo.overlayPaths.clone();
- oldAppInfo.resourceDirs = newAppInfo.resourceDirs.clone();
+ oldAppInfo.overlayPaths = newAppInfo.overlayPaths == null
+ ? null : newAppInfo.overlayPaths.clone();
+ oldAppInfo.resourceDirs = newAppInfo.resourceDirs == null
+ ? null : newAppInfo.resourceDirs.clone();
provider.info.providerInfo.applicationInfo = oldAppInfo;
for (int j = 0, M = provider.widgets.size(); j < M; j++) {
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index aa42e8deb581..1cff3744687e 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -284,7 +284,9 @@ final class AutofillManagerServiceImpl
}
final Session session = mSessions.get(sessionId);
if (session != null && uid == session.uid) {
- session.setAuthenticationResultLocked(data, authenticationId);
+ synchronized (session.mLock) {
+ session.setAuthenticationResultLocked(data, authenticationId);
+ }
}
}
@@ -374,7 +376,9 @@ final class AutofillManagerServiceImpl
+ " hc=" + hasCallback + " f=" + flags + " aa=" + forAugmentedAutofillOnly;
mMaster.logRequestLocked(historyItem);
- newSession.updateLocked(autofillId, virtualBounds, value, ACTION_START_SESSION, flags);
+ synchronized (newSession.mLock) {
+ newSession.updateLocked(autofillId, virtualBounds, value, ACTION_START_SESSION, flags);
+ }
if (forAugmentedAutofillOnly) {
// Must embed the flag in the response, at the high-end side of the long.
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index a4bf52a3ed1b..76ee728fdb07 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -17,6 +17,8 @@
package com.android.server.autofill;
import static android.service.autofill.AutofillFieldClassificationService.EXTRA_SCORES;
+import static android.service.autofill.AutofillService.EXTRA_FILL_RESPONSE;
+import static android.service.autofill.FillRequest.FLAG_ACTIVITY_START;
import static android.service.autofill.FillRequest.FLAG_MANUAL_REQUEST;
import static android.service.autofill.FillRequest.FLAG_PASSWORD_INPUT_TYPE;
import static android.service.autofill.FillRequest.FLAG_VIEW_NOT_FOCUSED;
@@ -46,13 +48,16 @@ import android.annotation.Nullable;
import android.app.Activity;
import android.app.ActivityTaskManager;
import android.app.IAssistDataReceiver;
+import android.app.PendingIntent;
import android.app.assist.AssistStructure;
import android.app.assist.AssistStructure.AutofillOverlay;
import android.app.assist.AssistStructure.ViewNode;
+import android.content.BroadcastReceiver;
import android.content.ClipData;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.IntentFilter;
import android.content.IntentSender;
import android.content.pm.ApplicationInfo;
import android.graphics.Bitmap;
@@ -147,12 +152,16 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
AutoFillUI.AutoFillUiCallback, ValueFinder {
private static final String TAG = "AutofillSession";
+ private static final String ACTION_DELAYED_FILL =
+ "android.service.autofill.action.DELAYED_FILL";
private static final String EXTRA_REQUEST_ID = "android.service.autofill.extra.REQUEST_ID";
+ final Object mLock;
+
private final AutofillManagerServiceImpl mService;
private final Handler mHandler;
- private final Object mLock;
private final AutoFillUI mUi;
+ @NonNull private final Context mContext;
private final MetricsLogger mMetricsLogger = new MetricsLogger();
@@ -267,6 +276,12 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
*/
private boolean mHasCallback;
+ @GuardedBy("mLock")
+ private boolean mDelayedFillBroadcastReceiverRegistered;
+
+ @GuardedBy("mLock")
+ private PendingIntent mDelayedFillPendingIntent;
+
/**
* Extras sent by service on {@code onFillRequest()} calls; the most recent non-null extra is
* saved and used on subsequent {@code onFillRequest()} and {@code onSaveRequest()} calls.
@@ -354,6 +369,32 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
private final AccessibilityManager mAccessibilityManager;
+ // TODO(b/216576510): Share one BroadcastReceiver between all Sessions instead of creating a
+ // new one per Session.
+ private final BroadcastReceiver mDelayedFillBroadcastReceiver =
+ new BroadcastReceiver() {
+ // ErrorProne says mAssistReceiver#processDelayedFillLocked needs to be guarded by
+ // 'Session.this.mLock', which is the same as mLock.
+ @SuppressWarnings("GuardedBy")
+ @Override
+ public void onReceive(final Context context, final Intent intent) {
+ if (!intent.getAction().equals(ACTION_DELAYED_FILL)) {
+ Slog.wtf(TAG, "Unexpected action is received.");
+ return;
+ }
+ if (!intent.hasExtra(EXTRA_REQUEST_ID)) {
+ Slog.e(TAG, "Delay fill action is missing request id extra.");
+ return;
+ }
+ Slog.v(TAG, "mDelayedFillBroadcastReceiver delayed fill action received");
+ synchronized (mLock) {
+ int requestId = intent.getIntExtra(EXTRA_REQUEST_ID, 0);
+ FillResponse response = intent.getParcelableExtra(EXTRA_FILL_RESPONSE);
+ mAssistReceiver.processDelayedFillLocked(requestId, response);
+ }
+ }
+ };
+
void onSwitchInputMethodLocked() {
// One caveat is that for the case where the focus is on a field for which regular autofill
// returns null, and augmented autofill is triggered, and then the user switches the input
@@ -406,28 +447,25 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
*/
private final class SessionFlags {
/** Whether autofill is disabled by the service */
- @GuardedBy("mLock")
private boolean mAutofillDisabled;
/** Whether the autofill service supports inline suggestions */
- @GuardedBy("mLock")
private boolean mInlineSupportedByService;
/** True if session is for augmented only */
- @GuardedBy("mLock")
private boolean mAugmentedAutofillOnly;
/** Whether the session is currently showing the SaveUi. */
- @GuardedBy("mLock")
private boolean mShowingSaveUi;
/** Whether the current {@link FillResponse} is expired. */
- @GuardedBy("mLock")
private boolean mExpiredResponse;
/** Whether the client is using {@link android.view.autofill.AutofillRequestCallback}. */
- @GuardedBy("mLock")
private boolean mClientSuggestionsEnabled;
+
+ /** Whether the fill dialog UI is disabled. */
+ private boolean mFillDialogDisabled;
}
/**
@@ -441,6 +479,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
private InlineSuggestionsRequest mPendingInlineSuggestionsRequest;
@GuardedBy("mLock")
private FillRequest mPendingFillRequest;
+ @GuardedBy("mLock")
+ private FillRequest mLastFillRequest;
@Nullable Consumer<InlineSuggestionsRequest> newAutofillRequestLocked(ViewState viewState,
boolean isInlineRequest) {
@@ -467,6 +507,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
mPendingInlineSuggestionsRequest = inlineRequest;
}
+ @GuardedBy("mLock")
void maybeRequestFillFromServiceLocked() {
if (mPendingFillRequest == null) {
return;
@@ -484,9 +525,12 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
mPendingFillRequest = new FillRequest(mPendingFillRequest.getId(),
mPendingFillRequest.getFillContexts(),
mPendingFillRequest.getClientState(),
- mPendingFillRequest.getFlags(), mPendingInlineSuggestionsRequest);
+ mPendingFillRequest.getFlags(),
+ mPendingInlineSuggestionsRequest,
+ mPendingFillRequest.getDelayedFillIntentSender());
}
}
+ mLastFillRequest = mPendingFillRequest;
mRemoteFillService.onFillRequest(mPendingFillRequest);
mPendingInlineSuggestionsRequest = null;
@@ -588,8 +632,12 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
final ArrayList<FillContext> contexts =
mergePreviousSessionLocked(/* forSave= */ false);
+ mDelayedFillPendingIntent = createPendingIntent(requestId);
request = new FillRequest(requestId, contexts, mClientState, flags,
- /*inlineSuggestionsRequest=*/null);
+ /*inlineSuggestionsRequest=*/ null,
+ /*delayedFillIntentSender=*/ mDelayedFillPendingIntent == null
+ ? null
+ : mDelayedFillPendingIntent.getIntentSender());
mPendingFillRequest = request;
maybeRequestFillFromServiceLocked();
@@ -604,7 +652,70 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
public void onHandleAssistScreenshot(Bitmap screenshot) {
// Do nothing
}
- };
+
+ @GuardedBy("mLock")
+ void processDelayedFillLocked(int requestId, FillResponse response) {
+ if (mLastFillRequest != null && requestId == mLastFillRequest.getId()) {
+ Slog.v(TAG, "processDelayedFillLocked: "
+ + "calling onFillRequestSuccess with new response");
+ onFillRequestSuccess(requestId, response,
+ mService.getServicePackageName(), mLastFillRequest.getFlags());
+ }
+ }
+ }
+
+ /** Creates {@link PendingIntent} for autofill service to send a delayed fill. */
+ private PendingIntent createPendingIntent(int requestId) {
+ Slog.d(TAG, "createPendingIntent for request " + requestId);
+ PendingIntent pendingIntent;
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ Intent intent = new Intent(ACTION_DELAYED_FILL).setPackage("android")
+ .putExtra(EXTRA_REQUEST_ID, requestId);
+ pendingIntent = PendingIntent.getBroadcast(
+ mContext, this.id, intent,
+ PendingIntent.FLAG_MUTABLE
+ | PendingIntent.FLAG_ONE_SHOT
+ | PendingIntent.FLAG_CANCEL_CURRENT);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ return pendingIntent;
+ }
+
+ @GuardedBy("mLock")
+ private void clearPendingIntentLocked() {
+ Slog.d(TAG, "clearPendingIntentLocked");
+ if (mDelayedFillPendingIntent == null) {
+ return;
+ }
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ mDelayedFillPendingIntent.cancel();
+ mDelayedFillPendingIntent = null;
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @GuardedBy("mLock")
+ private void registerDelayedFillBroadcastLocked() {
+ if (!mDelayedFillBroadcastReceiverRegistered) {
+ Slog.v(TAG, "registerDelayedFillBroadcastLocked()");
+ IntentFilter intentFilter = new IntentFilter(ACTION_DELAYED_FILL);
+ mContext.registerReceiver(mDelayedFillBroadcastReceiver, intentFilter);
+ mDelayedFillBroadcastReceiverRegistered = true;
+ }
+ }
+
+ @GuardedBy("mLock")
+ private void unregisterDelayedFillBroadcastLocked() {
+ if (mDelayedFillBroadcastReceiverRegistered) {
+ Slog.v(TAG, "unregisterDelayedFillBroadcastLocked()");
+ mContext.unregisterReceiver(mDelayedFillBroadcastReceiver);
+ mDelayedFillBroadcastReceiverRegistered = false;
+ }
+ }
/**
* Returns the ids of all entries in {@link #mViewStates} in the same order.
@@ -860,7 +971,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
mService.getRemoteInlineSuggestionRenderServiceLocked();
if ((mSessionFlags.mInlineSupportedByService || mSessionFlags.mClientSuggestionsEnabled)
&& remoteRenderService != null
- && isViewFocusedLocked(flags)) {
+ && (isViewFocusedLocked(flags) || (isRequestFromActivityStarted(flags)))) {
final Consumer<InlineSuggestionsRequest> inlineSuggestionsRequestConsumer;
if (mSessionFlags.mClientSuggestionsEnabled) {
final int finalRequestId = requestId;
@@ -906,6 +1017,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
requestAssistStructureLocked(requestId, flags);
}
+ private boolean isRequestFromActivityStarted(int flags) {
+ return (flags & FLAG_ACTIVITY_START) != 0;
+ }
+
@GuardedBy("mLock")
private void requestAssistStructureLocked(int requestId, int flags) {
try {
@@ -954,6 +1069,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
mHasCallback = hasCallback;
mUiLatencyHistory = uiLatencyHistory;
mWtfHistory = wtfHistory;
+ mContext = context;
mComponentName = componentName;
mCompatMode = compatMode;
mSessionState = STATE_ACTIVE;
@@ -1086,6 +1202,12 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
processNullResponseLocked(requestId, requestFlags);
return;
}
+
+ final int flags = response.getFlags();
+ if ((flags & FillResponse.FLAG_DELAY_FILL) != 0) {
+ Slog.v(TAG, "Service requested to wait for delayed fill response.");
+ registerDelayedFillBroadcastLocked();
+ }
}
mService.setLastResponse(id, response);
@@ -1196,6 +1318,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
@Nullable CharSequence message) {
boolean showMessage = !TextUtils.isEmpty(message);
synchronized (mLock) {
+ unregisterDelayedFillBroadcastLocked();
if (mDestroyed) {
Slog.w(TAG, "Call to Session#onFillRequestFailureOrTimeout(req=" + requestId
+ ") rejected - session: " + id + " destroyed");
@@ -1501,6 +1624,23 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
this, intentSender, intent));
}
+ // AutoFillUiCallback
+ @Override
+ public void requestShowSoftInput(AutofillId id) {
+ IAutoFillManagerClient client = getClient();
+ if (client != null) {
+ try {
+ client.requestShowSoftInput(id);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error sending input show up notification", e);
+ }
+ }
+ synchronized (mLock) {
+ // stop to show fill dialog
+ mSessionFlags.mFillDialogDisabled = true;
+ }
+ }
+
private void notifyFillUiHidden(@NonNull AutofillId autofillId) {
synchronized (mLock) {
try {
@@ -2869,6 +3009,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
// View is triggering autofill.
mCurrentViewId = viewState.id;
viewState.update(value, virtualBounds, flags);
+ if (!isRequestFromActivityStarted(flags)) {
+ mSessionFlags.mFillDialogDisabled = true;
+ }
requestNewFillResponseLocked(viewState, ViewState.STATE_STARTED_SESSION, flags);
break;
case ACTION_VALUE_CHANGED:
@@ -2958,6 +3101,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
if (isSameViewEntered) {
+ setFillDialogDisabledAndStartInput();
return;
}
@@ -2968,6 +3112,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
if (Objects.equals(mCurrentViewId, viewState.id)) {
if (sVerbose) Slog.v(TAG, "Exiting view " + id);
mUi.hideFillUi(this);
+ mUi.hideFillDialog(this);
hideAugmentedAutofillLocked(viewState);
// We don't send an empty response to IME so that it doesn't cause UI flicker
// on the IME side if it arrives before the input view is finished on the IME.
@@ -3148,6 +3293,17 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
return;
}
+ if (requestShowFillDialog(response, filledId, filterText)) {
+ synchronized (mLock) {
+ final ViewState currentView = mViewStates.get(mCurrentViewId);
+ currentView.setState(ViewState.STATE_FILL_DIALOG_SHOWN);
+ mService.logDatasetShown(id, mClientState);
+ }
+ return;
+ }
+
+ setFillDialogDisabled();
+
if (response.supportsInlineSuggestions()) {
synchronized (mLock) {
if (requestShowInlineSuggestionsLocked(response, filterText)) {
@@ -3192,6 +3348,81 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
}
+ @GuardedBy("mLock")
+ private void updateFillDialogTriggerIdsLocked() {
+ final FillResponse response = getLastResponseLocked(null);
+
+ if (response == null) return;
+
+ final AutofillId[] ids = response.getFillDialogTriggerIds();
+ notifyClientFillDialogTriggerIds(ids == null ? null : Arrays.asList(ids));
+ }
+
+ private void notifyClientFillDialogTriggerIds(List<AutofillId> fieldIds) {
+ try {
+ if (sVerbose) {
+ Slog.v(TAG, "notifyFillDialogTriggerIds(): " + fieldIds);
+ }
+ getClient().notifyFillDialogTriggerIds(fieldIds);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Cannot set trigger ids for fill dialog", e);
+ }
+ }
+
+ private boolean isFillDialogUiEnabled() {
+ // TODO read from Settings or somewhere
+ final boolean isSettingsEnabledFillDialog = true;
+ synchronized (mLock) {
+ return isSettingsEnabledFillDialog && !mSessionFlags.mFillDialogDisabled;
+ }
+ }
+
+ private void setFillDialogDisabled() {
+ synchronized (mLock) {
+ mSessionFlags.mFillDialogDisabled = true;
+ }
+ notifyClientFillDialogTriggerIds(null);
+ }
+
+ private void setFillDialogDisabledAndStartInput() {
+ if (getUiForShowing().isFillDialogShowing()) {
+ setFillDialogDisabled();
+ final AutofillId id;
+ synchronized (mLock) {
+ id = mCurrentViewId;
+ }
+ requestShowSoftInput(id);
+ }
+ }
+
+ private boolean requestShowFillDialog(FillResponse response,
+ AutofillId filledId, String filterText) {
+ if (!isFillDialogUiEnabled()) {
+ // Unsupported fill dialog UI
+ return false;
+ }
+
+ final AutofillId[] ids = response.getFillDialogTriggerIds();
+ if (ids == null || !ArrayUtils.contains(ids, filledId)) {
+ return false;
+ }
+
+ final Drawable serviceIcon = getServiceIcon();
+
+ getUiForShowing().showFillDialog(filledId, response, filterText,
+ mService.getServicePackageName(), mComponentName, serviceIcon, this);
+ return true;
+ }
+
+ @SuppressWarnings("GuardedBy") // ErrorProne says we need to use mService.mLock, but it's
+ // actually the same object as mLock.
+ // TODO: Expose mService.mLock or redesign instead.
+ private Drawable getServiceIcon() {
+ synchronized (mLock) {
+ return mService.getServiceIconLocked();
+ }
+ }
+
/**
* Returns whether we made a request to show inline suggestions.
*/
@@ -3412,6 +3643,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
@GuardedBy("mLock")
private void processNullResponseLocked(int requestId, int flags) {
+ unregisterDelayedFillBroadcastLocked();
if ((flags & FLAG_MANUAL_REQUEST) != 0) {
getUiForShowing().showError(R.string.autofill_error_cannot_autofill, this);
}
@@ -3584,9 +3816,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
mService.getRemoteInlineSuggestionRenderServiceLocked();
if (remoteRenderService != null
&& (mSessionFlags.mAugmentedAutofillOnly
- || !mSessionFlags.mInlineSupportedByService
- || mSessionFlags.mExpiredResponse)
- && isViewFocusedLocked(flags)) {
+ || !mSessionFlags.mInlineSupportedByService
+ || mSessionFlags.mExpiredResponse)
+ && isViewFocusedLocked(flags)
+ || isFillDialogUiEnabled()) {
if (sDebug) Slog.d(TAG, "Create inline request for augmented autofill");
remoteRenderService.getInlineSuggestionsRendererInfo(new RemoteCallback(
(extras) -> {
@@ -3625,6 +3858,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
// only if handling the current response requires it.
mUi.hideAll(this);
+ if ((newResponse.getFlags() & FillResponse.FLAG_DELAY_FILL) == 0) {
+ Slog.d(TAG, "Service did not request to wait for delayed fill response.");
+ unregisterDelayedFillBroadcastLocked();
+ }
+
final int requestId = newResponse.getRequestId();
if (sVerbose) {
Slog.v(TAG, "processResponseLocked(): mCurrentViewId=" + mCurrentViewId
@@ -3642,6 +3880,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
mClientState = newClientState != null ? newClientState : newResponse.getClientState();
setViewStatesLocked(newResponse, ViewState.STATE_FILLABLE, false);
+ updateFillDialogTriggerIdsLocked();
updateTrackedIdsLocked();
if (mCurrentViewId == null) {
@@ -4176,6 +4415,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
return null;
}
+ clearPendingIntentLocked();
+ unregisterDelayedFillBroadcastLocked();
+
unlinkClientVultureLocked();
mUi.destroyAll(mPendingSaveUi, this, true);
mUi.clearCallback(this);
diff --git a/services/autofill/java/com/android/server/autofill/ViewState.java b/services/autofill/java/com/android/server/autofill/ViewState.java
index adb1e3e43731..4a14f1420cf2 100644
--- a/services/autofill/java/com/android/server/autofill/ViewState.java
+++ b/services/autofill/java/com/android/server/autofill/ViewState.java
@@ -82,6 +82,8 @@ final class ViewState {
public static final int STATE_INLINE_DISABLED = 0x8000;
/** The View is waiting for an inline suggestions request from IME.*/
public static final int STATE_PENDING_CREATE_INLINE_REQUEST = 0x10000;
+ /** Fill dialog were shown for this View. */
+ public static final int STATE_FILL_DIALOG_SHOWN = 0x20000;
public final AutofillId id;
diff --git a/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java b/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
index 71c3c16a2c06..056ab92fffb2 100644
--- a/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
+++ b/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
@@ -66,6 +66,7 @@ public final class AutoFillUI {
private @Nullable FillUi mFillUi;
private @Nullable SaveUi mSaveUi;
+ private @Nullable DialogFillUi mFillDialog;
private @Nullable AutoFillUiCallback mCallback;
@@ -90,6 +91,7 @@ public final class AutoFillUI {
void startIntentSender(IntentSender intentSender, Intent intent);
void dispatchUnhandledKey(AutofillId id, KeyEvent keyEvent);
void cancelSession();
+ void requestShowSoftInput(AutofillId id);
}
public AutoFillUI(@NonNull Context context) {
@@ -155,6 +157,12 @@ public final class AutoFillUI {
}
/**
+ * Hides the fill UI.
+ */
+ public void hideFillDialog(@NonNull AutoFillUiCallback callback) {
+ mHandler.post(() -> hideFillDialogUiThread(callback));
+ }
+ /**
* Filters the options in the fill UI.
*
* @param filterText The filter prefix.
@@ -369,6 +377,62 @@ public final class AutoFillUI {
}
/**
+ * Shows the UI asking the user to choose for autofill.
+ */
+ public void showFillDialog(@NonNull AutofillId focusedId, @NonNull FillResponse response,
+ @Nullable String filterText, @Nullable String servicePackageName,
+ @NonNull ComponentName componentName, @Nullable Drawable serviceIcon,
+ @NonNull AutoFillUiCallback callback) {
+ if (sVerbose) {
+ Slog.v(TAG, "showFillDialog for "
+ + componentName.toShortString() + ": " + response);
+ }
+
+ // TODO: enable LogMaker
+
+ mHandler.post(() -> {
+ if (callback != mCallback) {
+ return;
+ }
+ hideAllUiThread(callback);
+ mFillDialog = new DialogFillUi(mContext, response, focusedId, filterText,
+ serviceIcon, servicePackageName, componentName, mOverlayControl,
+ mUiModeMgr.isNightMode(), new DialogFillUi.UiCallback() {
+ @Override
+ public void onResponsePicked(FillResponse response) {
+ hideFillDialogUiThread(callback);
+ if (mCallback != null) {
+ mCallback.authenticate(response.getRequestId(),
+ AutofillManager.AUTHENTICATION_ID_DATASET_ID_UNDEFINED,
+ response.getAuthentication(), response.getClientState(),
+ /* authenticateInline= */ false);
+ }
+ }
+
+ @Override
+ public void onDatasetPicked(Dataset dataset) {
+ hideFillDialogUiThread(callback);
+ if (mCallback != null) {
+ final int datasetIndex = response.getDatasets().indexOf(dataset);
+ mCallback.fill(response.getRequestId(), datasetIndex, dataset);
+ }
+ }
+
+ @Override
+ public void onCanceled() {
+ hideFillDialogUiThread(callback);
+ callback.requestShowSoftInput(focusedId);
+ }
+
+ @Override
+ public void startIntentSender(IntentSender intentSender) {
+ mCallback.startIntentSenderAndFinishSession(intentSender);
+ }
+ });
+ });
+ }
+
+ /**
* Executes an operation in the pending save UI, if any.
*/
public void onPendingSaveUi(int operation, @NonNull IBinder token) {
@@ -400,6 +464,10 @@ public final class AutoFillUI {
return mSaveUi == null ? false : mSaveUi.isShowing();
}
+ public boolean isFillDialogShowing() {
+ return mFillDialog == null ? false : mFillDialog.isShowing();
+ }
+
public void dump(PrintWriter pw) {
pw.println("Autofill UI");
final String prefix = " ";
@@ -417,6 +485,12 @@ public final class AutoFillUI {
} else {
pw.print(prefix); pw.println("showsSaveUi: false");
}
+ if (mFillDialog != null) {
+ pw.print(prefix); pw.println("showsFillDialog: true");
+ mFillDialog.dump(pw, prefix2);
+ } else {
+ pw.print(prefix); pw.println("showsFillDialog: false");
+ }
}
@android.annotation.UiThread
@@ -442,6 +516,14 @@ public final class AutoFillUI {
}
@android.annotation.UiThread
+ private void hideFillDialogUiThread(@Nullable AutoFillUiCallback callback) {
+ if (mFillDialog != null && (callback == null || callback == mCallback)) {
+ mFillDialog.destroy();
+ mFillDialog = null;
+ }
+ }
+
+ @android.annotation.UiThread
private void destroySaveUiUiThread(@Nullable PendingUi pendingSaveUi, boolean notifyClient) {
if (mSaveUi == null) {
// Calling destroySaveUiUiThread() twice is normal - it usually happens when the
@@ -475,12 +557,14 @@ public final class AutoFillUI {
private void destroyAllUiThread(@Nullable PendingUi pendingSaveUi,
@Nullable AutoFillUiCallback callback, boolean notifyClient) {
hideFillUiUiThread(callback, notifyClient);
+ hideFillDialogUiThread(callback);
destroySaveUiUiThread(pendingSaveUi, notifyClient);
}
@android.annotation.UiThread
private void hideAllUiThread(@Nullable AutoFillUiCallback callback) {
hideFillUiUiThread(callback, true);
+ hideFillDialogUiThread(callback);
final PendingUi pendingSaveUi = hideSaveUiUiThread(callback);
if (pendingSaveUi != null && pendingSaveUi.getState() == PendingUi.STATE_FINISHED) {
if (sDebug) {
diff --git a/services/autofill/java/com/android/server/autofill/ui/DialogFillUi.java b/services/autofill/java/com/android/server/autofill/ui/DialogFillUi.java
new file mode 100644
index 000000000000..e1229939e2ca
--- /dev/null
+++ b/services/autofill/java/com/android/server/autofill/ui/DialogFillUi.java
@@ -0,0 +1,629 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.autofill.ui;
+
+import static com.android.server.autofill.Helper.sDebug;
+import static com.android.server.autofill.Helper.sVerbose;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.Dialog;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.IntentSender;
+import android.graphics.drawable.Drawable;
+import android.service.autofill.Dataset;
+import android.service.autofill.FillResponse;
+import android.text.TextUtils;
+import android.util.PluralsMessageFormatter;
+import android.util.Slog;
+import android.view.ContextThemeWrapper;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.Window;
+import android.view.WindowManager;
+import android.view.accessibility.AccessibilityManager;
+import android.view.autofill.AutofillId;
+import android.view.autofill.AutofillValue;
+import android.widget.AdapterView;
+import android.widget.BaseAdapter;
+import android.widget.Filter;
+import android.widget.Filterable;
+import android.widget.ImageView;
+import android.widget.ListView;
+import android.widget.RemoteViews;
+import android.widget.TextView;
+
+import com.android.internal.R;
+import com.android.server.autofill.AutofillManagerService;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+
+/**
+ * A dialog to show Autofill suggestions.
+ *
+ * This fill dialog UI shows as a bottom sheet style dialog. This dialog UI
+ * provides a larger area to display the suggestions, it provides a more
+ * conspicuous and efficient interface to the user. So it is easy for users
+ * to pay attention to the datasets and selecting one of them.
+ */
+final class DialogFillUi {
+
+ private static final String TAG = "DialogFillUi";
+ private static final int THEME_ID_LIGHT =
+ R.style.Theme_DeviceDefault_Light_Autofill_Save;
+ private static final int THEME_ID_DARK =
+ R.style.Theme_DeviceDefault_Autofill_Save;
+
+ interface UiCallback {
+ void onResponsePicked(@NonNull FillResponse response);
+ void onDatasetPicked(@NonNull Dataset dataset);
+ void onCanceled();
+ void startIntentSender(IntentSender intentSender);
+ }
+
+ private final @NonNull Dialog mDialog;
+ private final @NonNull OverlayControl mOverlayControl;
+ private final String mServicePackageName;
+ private final ComponentName mComponentName;
+ private final int mThemeId;
+ private final @NonNull Context mContext;
+ private final @NonNull UiCallback mCallback;
+ private final @NonNull ListView mListView;
+ private final @Nullable ItemsAdapter mAdapter;
+ private final int mVisibleDatasetsMaxCount;
+
+ private @Nullable String mFilterText;
+ private @Nullable AnnounceFilterResult mAnnounceFilterResult;
+ private boolean mDestroyed;
+
+ DialogFillUi(@NonNull Context context, @NonNull FillResponse response,
+ @NonNull AutofillId focusedViewId, @Nullable String filterText,
+ @Nullable Drawable serviceIcon, @Nullable String servicePackageName,
+ @Nullable ComponentName componentName, @NonNull OverlayControl overlayControl,
+ boolean nightMode, @NonNull UiCallback callback) {
+ if (sVerbose) Slog.v(TAG, "nightMode: " + nightMode);
+ mThemeId = nightMode ? THEME_ID_DARK : THEME_ID_LIGHT;
+ mCallback = callback;
+ mOverlayControl = overlayControl;
+ mServicePackageName = servicePackageName;
+ mComponentName = componentName;
+
+ mContext = new ContextThemeWrapper(context, mThemeId);
+ final LayoutInflater inflater = LayoutInflater.from(mContext);
+ final View decor = inflater.inflate(R.layout.autofill_fill_dialog, null);
+
+ setServiceIcon(decor, serviceIcon);
+ setHeader(decor, response);
+
+ mVisibleDatasetsMaxCount = getVisibleDatasetsMaxCount();
+
+ if (response.getAuthentication() != null) {
+ mListView = null;
+ mAdapter = null;
+ try {
+ initialAuthenticationLayout(decor, response);
+ } catch (RuntimeException e) {
+ callback.onCanceled();
+ Slog.e(TAG, "Error inflating remote views", e);
+ mDialog = null;
+ return;
+ }
+ } else {
+ final List<ViewItem> items = createDatasetItems(response, focusedViewId);
+ mAdapter = new ItemsAdapter(items);
+ mListView = decor.findViewById(R.id.autofill_dialog_list);
+ initialDatasetLayout(decor, filterText);
+ }
+
+ setDismissButton(decor);
+
+ mDialog = new Dialog(mContext, mThemeId);
+ mDialog.setContentView(decor);
+ setDialogParamsAsBottomSheet();
+
+ show();
+ }
+
+ private int getVisibleDatasetsMaxCount() {
+ if (AutofillManagerService.getVisibleDatasetsMaxCount() > 0) {
+ final int maxCount = AutofillManagerService.getVisibleDatasetsMaxCount();
+ if (sVerbose) {
+ Slog.v(TAG, "overriding maximum visible datasets to " + maxCount);
+ }
+ return maxCount;
+ } else {
+ return mContext.getResources()
+ .getInteger(com.android.internal.R.integer.autofill_max_visible_datasets);
+ }
+ }
+
+ private void setDialogParamsAsBottomSheet() {
+ final Window window = mDialog.getWindow();
+ window.setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY);
+ window.addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
+ | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+ | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH);
+ window.addPrivateFlags(WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS);
+ window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);
+ window.setGravity(Gravity.BOTTOM | Gravity.CENTER);
+ window.setCloseOnTouchOutside(true);
+ final WindowManager.LayoutParams params = window.getAttributes();
+ params.width = WindowManager.LayoutParams.MATCH_PARENT;
+ params.accessibilityTitle =
+ mContext.getString(R.string.autofill_picker_accessibility_title);
+ params.windowAnimations = R.style.AutofillSaveAnimation;
+ }
+
+ private void setServiceIcon(View decor, Drawable serviceIcon) {
+ if (serviceIcon == null) {
+ return;
+ }
+
+ final ImageView iconView = decor.findViewById(R.id.autofill_service_icon);
+ final int actualWidth = serviceIcon.getMinimumWidth();
+ final int actualHeight = serviceIcon.getMinimumHeight();
+ if (sDebug) {
+ Slog.d(TAG, "Adding service icon "
+ + "(" + actualWidth + "x" + actualHeight + ")");
+ }
+ iconView.setImageDrawable(serviceIcon);
+ iconView.setVisibility(View.VISIBLE);
+ }
+
+ private void setHeader(View decor, FillResponse response) {
+ final RemoteViews presentation = response.getDialogHeader();
+ if (presentation == null) {
+ return;
+ }
+
+ final ViewGroup container = decor.findViewById(R.id.autofill_dialog_header);
+ final RemoteViews.InteractionHandler interceptionHandler = (view, pendingIntent, r) -> {
+ if (pendingIntent != null) {
+ mCallback.startIntentSender(pendingIntent.getIntentSender());
+ }
+ return true;
+ };
+
+ final View content = presentation.applyWithTheme(
+ mContext, (ViewGroup) decor, interceptionHandler, mThemeId);
+ container.addView(content);
+ container.setVisibility(View.VISIBLE);
+ }
+
+ private void setDismissButton(View decor) {
+ final TextView noButton = decor.findViewById(R.id.autofill_dialog_no);
+ noButton.setOnClickListener((v) -> mCallback.onCanceled());
+ }
+
+ private void setContinueButton(View decor, View.OnClickListener listener) {
+ final TextView yesButton = decor.findViewById(R.id.autofill_dialog_yes);
+ // set "Continue" by default
+ yesButton.setText(R.string.autofill_continue_yes);
+ yesButton.setOnClickListener(listener);
+ }
+
+ private void initialAuthenticationLayout(View decor, FillResponse response) {
+ RemoteViews presentation = response.getDialogPresentation();
+ if (presentation == null) {
+ presentation = response.getPresentation();
+ }
+ if (presentation == null) {
+ throw new RuntimeException("No presentation for fill dialog authentication");
+ }
+
+ // insert authentication item under autofill_dialog_container
+ final ViewGroup container = decor.findViewById(R.id.autofill_dialog_container);
+ final RemoteViews.InteractionHandler interceptionHandler = (view, pendingIntent, r) -> {
+ if (pendingIntent != null) {
+ mCallback.startIntentSender(pendingIntent.getIntentSender());
+ }
+ return true;
+ };
+ final View content = presentation.applyWithTheme(
+ mContext, (ViewGroup) decor, interceptionHandler, mThemeId);
+ container.addView(content);
+ container.setVisibility(View.VISIBLE);
+ container.setFocusable(true);
+ container.setOnClickListener(v -> mCallback.onResponsePicked(response));
+ // just single item, set up continue button
+ setContinueButton(decor, v -> mCallback.onResponsePicked(response));
+ }
+
+ private ArrayList<ViewItem> createDatasetItems(FillResponse response,
+ AutofillId focusedViewId) {
+ final int datasetCount = response.getDatasets().size();
+ if (sVerbose) {
+ Slog.v(TAG, "Number datasets: " + datasetCount + " max visible: "
+ + mVisibleDatasetsMaxCount);
+ }
+
+ final RemoteViews.InteractionHandler interceptionHandler = (view, pendingIntent, r) -> {
+ if (pendingIntent != null) {
+ mCallback.startIntentSender(pendingIntent.getIntentSender());
+ }
+ return true;
+ };
+
+ final ArrayList<ViewItem> items = new ArrayList<>(datasetCount);
+ for (int i = 0; i < datasetCount; i++) {
+ final Dataset dataset = response.getDatasets().get(i);
+ final int index = dataset.getFieldIds().indexOf(focusedViewId);
+ if (index >= 0) {
+ RemoteViews presentation = dataset.getFieldDialogPresentation(index);
+ if (presentation == null) {
+ Slog.w(TAG, "fallback to presentation");
+ presentation = dataset.getFieldPresentation(index);
+ }
+ if (presentation == null) {
+ Slog.w(TAG, "not displaying UI on field " + focusedViewId + " because "
+ + "service didn't provide a presentation for it on " + dataset);
+ continue;
+ }
+ final View view;
+ try {
+ if (sVerbose) Slog.v(TAG, "setting remote view for " + focusedViewId);
+ view = presentation.applyWithTheme(
+ mContext, null, interceptionHandler, mThemeId);
+ } catch (RuntimeException e) {
+ Slog.e(TAG, "Error inflating remote views", e);
+ continue;
+ }
+ // TODO: Extract the shared filtering logic here and in FillUi to a common
+ // method.
+ final Dataset.DatasetFieldFilter filter = dataset.getFilter(index);
+ Pattern filterPattern = null;
+ String valueText = null;
+ boolean filterable = true;
+ if (filter == null) {
+ final AutofillValue value = dataset.getFieldValues().get(index);
+ if (value != null && value.isText()) {
+ valueText = value.getTextValue().toString().toLowerCase();
+ }
+ } else {
+ filterPattern = filter.pattern;
+ if (filterPattern == null) {
+ if (sVerbose) {
+ Slog.v(TAG, "Explicitly disabling filter at id " + focusedViewId
+ + " for dataset #" + index);
+ }
+ filterable = false;
+ }
+ }
+
+ items.add(new ViewItem(dataset, filterPattern, filterable, valueText, view));
+ }
+ }
+ return items;
+ }
+
+ private void initialDatasetLayout(View decor, String filterText) {
+ final AdapterView.OnItemClickListener onItemClickListener =
+ (adapter, view, position, id) -> {
+ final ViewItem vi = mAdapter.getItem(position);
+ mCallback.onDatasetPicked(vi.dataset);
+ };
+
+ mListView.setAdapter(mAdapter);
+ mListView.setVisibility(View.VISIBLE);
+ mListView.setOnItemClickListener(onItemClickListener);
+
+ if (mAdapter.getCount() == 1) {
+ // just single item, set up continue button
+ setContinueButton(decor, (v) ->
+ onItemClickListener.onItemClick(null, null, 0, 0));
+ }
+
+ if (filterText == null) {
+ mFilterText = null;
+ } else {
+ mFilterText = filterText.toLowerCase();
+ }
+
+ final int oldCount = mAdapter.getCount();
+ mAdapter.getFilter().filter(mFilterText, (count) -> {
+ if (mDestroyed) {
+ return;
+ }
+ if (count <= 0) {
+ if (sDebug) {
+ final int size = mFilterText == null ? 0 : mFilterText.length();
+ Slog.d(TAG, "No dataset matches filter with " + size + " chars");
+ }
+ mCallback.onCanceled();
+ } else {
+
+ if (mAdapter.getCount() > mVisibleDatasetsMaxCount) {
+ mListView.setVerticalScrollBarEnabled(true);
+ mListView.onVisibilityAggregated(true);
+ } else {
+ mListView.setVerticalScrollBarEnabled(false);
+ }
+ if (mAdapter.getCount() != oldCount) {
+ mListView.requestLayout();
+ }
+ }
+ });
+ }
+
+ private void show() {
+ Slog.i(TAG, "Showing fill dialog");
+ mDialog.show();
+ mOverlayControl.hideOverlays();
+ }
+
+ boolean isShowing() {
+ return mDialog.isShowing();
+ }
+
+ void hide() {
+ if (sVerbose) Slog.v(TAG, "Hiding fill dialog.");
+ try {
+ mDialog.hide();
+ } finally {
+ mOverlayControl.showOverlays();
+ }
+ }
+
+ void destroy() {
+ try {
+ if (sDebug) Slog.d(TAG, "destroy()");
+ throwIfDestroyed();
+
+ mDialog.dismiss();
+ mDestroyed = true;
+ } finally {
+ mOverlayControl.showOverlays();
+ }
+ }
+
+ private void throwIfDestroyed() {
+ if (mDestroyed) {
+ throw new IllegalStateException("cannot interact with a destroyed instance");
+ }
+ }
+
+ @Override
+ public String toString() {
+ // TODO toString
+ return "NO TITLE";
+ }
+
+ void dump(PrintWriter pw, String prefix) {
+
+ pw.print(prefix); pw.print("service: "); pw.println(mServicePackageName);
+ pw.print(prefix); pw.print("app: "); pw.println(mComponentName.toShortString());
+ pw.print(prefix); pw.print("theme id: "); pw.print(mThemeId);
+ switch (mThemeId) {
+ case THEME_ID_DARK:
+ pw.println(" (dark)");
+ break;
+ case THEME_ID_LIGHT:
+ pw.println(" (light)");
+ break;
+ default:
+ pw.println("(UNKNOWN_MODE)");
+ break;
+ }
+ final View view = mDialog.getWindow().getDecorView();
+ final int[] loc = view.getLocationOnScreen();
+ pw.print(prefix); pw.print("coordinates: ");
+ pw.print('('); pw.print(loc[0]); pw.print(','); pw.print(loc[1]); pw.print(')');
+ pw.print('(');
+ pw.print(loc[0] + view.getWidth()); pw.print(',');
+ pw.print(loc[1] + view.getHeight()); pw.println(')');
+ pw.print(prefix); pw.print("destroyed: "); pw.println(mDestroyed);
+ }
+
+ private void announceSearchResultIfNeeded() {
+ if (AccessibilityManager.getInstance(mContext).isEnabled()) {
+ if (mAnnounceFilterResult == null) {
+ mAnnounceFilterResult = new AnnounceFilterResult();
+ }
+ mAnnounceFilterResult.post();
+ }
+ }
+
+ // TODO: Below code copied from FullUi, Extract the shared filtering logic here
+ // and in FillUi to a common method.
+ private final class AnnounceFilterResult implements Runnable {
+ private static final int SEARCH_RESULT_ANNOUNCEMENT_DELAY = 1000; // 1 sec
+
+ public void post() {
+ remove();
+ mListView.postDelayed(this, SEARCH_RESULT_ANNOUNCEMENT_DELAY);
+ }
+
+ public void remove() {
+ mListView.removeCallbacks(this);
+ }
+
+ @Override
+ public void run() {
+ final int count = mListView.getAdapter().getCount();
+ final String text;
+ if (count <= 0) {
+ text = mContext.getString(R.string.autofill_picker_no_suggestions);
+ } else {
+ Map<String, Object> arguments = new HashMap<>();
+ arguments.put("count", count);
+ text = PluralsMessageFormatter.format(mContext.getResources(),
+ arguments,
+ R.string.autofill_picker_some_suggestions);
+ }
+ mListView.announceForAccessibility(text);
+ }
+ }
+
+ private final class ItemsAdapter extends BaseAdapter implements Filterable {
+ private @NonNull final List<ViewItem> mAllItems;
+
+ private @NonNull final List<ViewItem> mFilteredItems = new ArrayList<>();
+
+ ItemsAdapter(@NonNull List<ViewItem> items) {
+ mAllItems = Collections.unmodifiableList(new ArrayList<>(items));
+ mFilteredItems.addAll(items);
+ }
+
+ @Override
+ public Filter getFilter() {
+ return new Filter() {
+ @Override
+ protected FilterResults performFiltering(CharSequence filterText) {
+ // No locking needed as mAllItems is final an immutable
+ final List<ViewItem> filtered = mAllItems.stream()
+ .filter((item) -> item.matches(filterText))
+ .collect(Collectors.toList());
+ final FilterResults results = new FilterResults();
+ results.values = filtered;
+ results.count = filtered.size();
+ return results;
+ }
+
+ @Override
+ protected void publishResults(CharSequence constraint, FilterResults results) {
+ final boolean resultCountChanged;
+ final int oldItemCount = mFilteredItems.size();
+ mFilteredItems.clear();
+ if (results.count > 0) {
+ @SuppressWarnings("unchecked") final List<ViewItem> items =
+ (List<ViewItem>) results.values;
+ mFilteredItems.addAll(items);
+ }
+ resultCountChanged = (oldItemCount != mFilteredItems.size());
+ if (resultCountChanged) {
+ announceSearchResultIfNeeded();
+ }
+ notifyDataSetChanged();
+ }
+ };
+ }
+
+ @Override
+ public int getCount() {
+ return mFilteredItems.size();
+ }
+
+ @Override
+ public ViewItem getItem(int position) {
+ return mFilteredItems.get(position);
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return position;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ return getItem(position).view;
+ }
+
+ @Override
+ public String toString() {
+ return "ItemsAdapter: [all=" + mAllItems + ", filtered=" + mFilteredItems + "]";
+ }
+ }
+
+
+ /**
+ * An item for the list view - either a (clickable) dataset or a (read-only) header / footer.
+ */
+ private static class ViewItem {
+ public final @Nullable String value;
+ public final @Nullable Dataset dataset;
+ public final @NonNull View view;
+ public final @Nullable Pattern filter;
+ public final boolean filterable;
+
+ /**
+ * Default constructor.
+ *
+ * @param dataset dataset associated with the item
+ * @param filter optional filter set by the service to determine how the item should be
+ * filtered
+ * @param filterable optional flag set by the service to indicate this item should not be
+ * filtered (typically used when the dataset has value but it's sensitive, like a password)
+ * @param value dataset value
+ * @param view dataset presentation.
+ */
+ ViewItem(@NonNull Dataset dataset, @Nullable Pattern filter, boolean filterable,
+ @Nullable String value, @NonNull View view) {
+ this.dataset = dataset;
+ this.value = value;
+ this.view = view;
+ this.filter = filter;
+ this.filterable = filterable;
+ }
+
+ /**
+ * Returns whether this item matches the value input by the user so it can be included
+ * in the filtered datasets.
+ */
+ public boolean matches(CharSequence filterText) {
+ if (TextUtils.isEmpty(filterText)) {
+ // Always show item when the user input is empty
+ return true;
+ }
+ if (!filterable) {
+ // Service explicitly disabled filtering using a null Pattern.
+ return false;
+ }
+ final String constraintLowerCase = filterText.toString().toLowerCase();
+ if (filter != null) {
+ // Uses pattern provided by service
+ return filter.matcher(constraintLowerCase).matches();
+ } else {
+ // Compares it with dataset value with dataset
+ return (value == null)
+ ? (dataset.getAuthentication() == null)
+ : value.toLowerCase().startsWith(constraintLowerCase);
+ }
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder builder = new StringBuilder("ViewItem:[view=")
+ .append(view.getAutofillId());
+ final String datasetId = dataset == null ? null : dataset.getId();
+ if (datasetId != null) {
+ builder.append(", dataset=").append(datasetId);
+ }
+ if (value != null) {
+ // Cannot print value because it could contain PII
+ builder.append(", value=").append(value.length()).append("_chars");
+ }
+ if (filterable) {
+ builder.append(", filterable");
+ }
+ if (filter != null) {
+ // Filter should not have PII, but it could be a huge regexp
+ builder.append(", filter=").append(filter.pattern().length()).append("_chars");
+ }
+ return builder.append(']').toString();
+ }
+ }
+}
diff --git a/services/companion/java/com/android/server/companion/AssociationStoreImpl.java b/services/companion/java/com/android/server/companion/AssociationStoreImpl.java
index cda554e9d0cf..3ccabaaea2fa 100644
--- a/services/companion/java/com/android/server/companion/AssociationStoreImpl.java
+++ b/services/companion/java/com/android/server/companion/AssociationStoreImpl.java
@@ -125,7 +125,7 @@ class AssociationStoreImpl implements AssociationStore {
// Update the MacAddress-to-List<Association> map if needed.
final MacAddress updatedAddress = updated.getDeviceMacAddress();
final MacAddress currentAddress = current.getDeviceMacAddress();
- macAddressChanged = Objects.equals(currentAddress, updatedAddress);
+ macAddressChanged = !Objects.equals(currentAddress, updatedAddress);
if (macAddressChanged) {
if (currentAddress != null) {
mAddressMap.get(currentAddress).remove(id);
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index cfd37988d234..c3ab2a79e288 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -1255,7 +1255,7 @@ public class CompanionDeviceManagerService extends SystemService
}
@Override
- public void onDeviceDisconnected(BluetoothDevice device, @DisconnectReason int reason) {
+ public void onDeviceDisconnected(BluetoothDevice device, int reason) {
Slog.d(LOG_TAG, device.getAddress() + " disconnected w/ reason: (" + reason + ") "
+ BluetoothAdapter.BluetoothConnectionCallback.disconnectReasonText(reason));
CompanionDeviceManagerService.this.onDeviceDisconnected(device.getAddress());
diff --git a/services/companion/java/com/android/server/companion/presence/BleCompanionDeviceScanner.java b/services/companion/java/com/android/server/companion/presence/BleCompanionDeviceScanner.java
index 0eb6b8d24768..627b0bebc905 100644
--- a/services/companion/java/com/android/server/companion/presence/BleCompanionDeviceScanner.java
+++ b/services/companion/java/com/android/server/companion/presence/BleCompanionDeviceScanner.java
@@ -21,8 +21,6 @@ import static android.bluetooth.BluetoothAdapter.ACTION_BLE_STATE_CHANGED;
import static android.bluetooth.BluetoothAdapter.ACTION_STATE_CHANGED;
import static android.bluetooth.BluetoothAdapter.EXTRA_PREVIOUS_STATE;
import static android.bluetooth.BluetoothAdapter.EXTRA_STATE;
-import static android.bluetooth.BluetoothAdapter.STATE_BLE_ON;
-import static android.bluetooth.BluetoothAdapter.STATE_ON;
import static android.bluetooth.BluetoothAdapter.nameForState;
import static android.bluetooth.le.ScanCallback.SCAN_FAILED_ALREADY_STARTED;
import static android.bluetooth.le.ScanCallback.SCAN_FAILED_APPLICATION_REGISTRATION_FAILED;
@@ -156,7 +154,7 @@ class BleCompanionDeviceScanner implements AssociationStore.OnChangeListener {
private void checkBleState() {
enforceInitialized();
- final boolean bleAvailable = isBleAvailable();
+ final boolean bleAvailable = mBtAdapter.isLeEnabled();
if (DEBUG) {
Log.i(TAG, "checkBleState() bleAvailable=" + bleAvailable);
}
@@ -183,16 +181,6 @@ class BleCompanionDeviceScanner implements AssociationStore.OnChangeListener {
}
}
- /**
- * A duplicate of {@code BluetoothAdapter.getLeAccess()} method which has the package-private
- * access level, so it's not accessible from here.
- */
- private boolean isBleAvailable() {
- final int state = mBtAdapter.getLeState();
- if (DEBUG) Log.d(TAG, "getLeAccess() state=" + nameForBtState(state));
- return state == STATE_ON || state == STATE_BLE_ON;
- }
-
@MainThread
private void startScan() {
enforceInitialized();
diff --git a/services/companion/java/com/android/server/companion/presence/BluetoothCompanionDeviceConnectionListener.java b/services/companion/java/com/android/server/companion/presence/BluetoothCompanionDeviceConnectionListener.java
index dbe866b374f1..93cbe973b00e 100644
--- a/services/companion/java/com/android/server/companion/presence/BluetoothCompanionDeviceConnectionListener.java
+++ b/services/companion/java/com/android/server/companion/presence/BluetoothCompanionDeviceConnectionListener.java
@@ -91,7 +91,7 @@ class BluetoothCompanionDeviceConnectionListener
*/
@Override
public void onDeviceDisconnected(@NonNull BluetoothDevice device,
- @DisconnectReason int reason) {
+ int reason) {
if (DEBUG) {
Log.i(TAG, "onDevice_Disconnected() " + btDeviceToString(device));
Log.d(TAG, " reason=" + disconnectReasonText(reason));
diff --git a/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java b/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
index 75acf81a4a3c..bb49ba059d23 100644
--- a/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
+++ b/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
@@ -39,6 +39,7 @@ import android.window.DisplayWindowPolicyController;
import java.util.List;
import java.util.Set;
+import java.util.function.Consumer;
/**
@@ -61,6 +62,7 @@ class GenericWindowPolicyController extends DisplayWindowPolicyController {
private final ArraySet<ComponentName> mAllowedActivities;
@Nullable
private final ArraySet<ComponentName> mBlockedActivities;
+ private Consumer<ActivityInfo> mActivityBlockedCallback;
@NonNull
final ArraySet<Integer> mRunningUids = new ArraySet<>();
@@ -81,10 +83,12 @@ class GenericWindowPolicyController extends DisplayWindowPolicyController {
@NonNull ArraySet<UserHandle> allowedUsers,
@Nullable Set<ComponentName> allowedActivities,
@Nullable Set<ComponentName> blockedActivities,
- @NonNull ActivityListener activityListener) {
+ @NonNull ActivityListener activityListener,
+ @NonNull Consumer<ActivityInfo> activityBlockedCallback) {
mAllowedUsers = allowedUsers;
mAllowedActivities = allowedActivities == null ? null : new ArraySet<>(allowedActivities);
mBlockedActivities = blockedActivities == null ? null : new ArraySet<>(blockedActivities);
+ mActivityBlockedCallback = activityBlockedCallback;
setInterestedWindowFlags(windowFlags, systemWindowFlags);
mActivityListener = activityListener;
}
@@ -96,6 +100,7 @@ class GenericWindowPolicyController extends DisplayWindowPolicyController {
for (int i = 0; i < activityCount; i++) {
final ActivityInfo aInfo = activities.get(i);
if (!canContainActivity(aInfo, /* windowFlags= */ 0, /* systemWindowFlags= */ 0)) {
+ mActivityBlockedCallback.accept(aInfo);
return false;
}
}
@@ -105,7 +110,11 @@ class GenericWindowPolicyController extends DisplayWindowPolicyController {
@Override
public boolean keepActivityOnWindowFlagsChanged(ActivityInfo activityInfo, int windowFlags,
int systemWindowFlags) {
- return canContainActivity(activityInfo, windowFlags, systemWindowFlags);
+ if (!canContainActivity(activityInfo, windowFlags, systemWindowFlags)) {
+ mActivityBlockedCallback.accept(activityInfo);
+ return false;
+ }
+ return true;
}
@Override
diff --git a/services/companion/java/com/android/server/companion/virtual/InputController.java b/services/companion/java/com/android/server/companion/virtual/InputController.java
index 6c56e2f777f3..e6bfd1ff7f1a 100644
--- a/services/companion/java/com/android/server/companion/virtual/InputController.java
+++ b/services/companion/java/com/android/server/companion/virtual/InputController.java
@@ -18,8 +18,11 @@ package com.android.server.companion.virtual;
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.StringDef;
import android.graphics.Point;
import android.graphics.PointF;
+import android.hardware.display.DisplayManagerInternal;
+import android.hardware.input.InputManager;
import android.hardware.input.InputManagerInternal;
import android.hardware.input.VirtualKeyEvent;
import android.hardware.input.VirtualMouseButtonEvent;
@@ -48,6 +51,20 @@ class InputController {
private static final String TAG = "VirtualInputController";
+ private static final AtomicLong sNextPhysId = new AtomicLong(1);
+
+ static final String PHYS_TYPE_KEYBOARD = "Keyboard";
+ static final String PHYS_TYPE_MOUSE = "Mouse";
+ static final String PHYS_TYPE_TOUCHSCREEN = "Touchscreen";
+ @StringDef(prefix = { "PHYS_TYPE_" }, value = {
+ PHYS_TYPE_KEYBOARD,
+ PHYS_TYPE_MOUSE,
+ PHYS_TYPE_TOUCHSCREEN,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface PhysType {
+ }
+
private final Object mLock;
/* Token -> file descriptor associations. */
@@ -56,6 +73,8 @@ class InputController {
final Map<IBinder, InputDeviceDescriptor> mInputDeviceDescriptors = new ArrayMap<>();
private final NativeWrapper mNativeWrapper;
+ private final DisplayManagerInternal mDisplayManagerInternal;
+ private final InputManagerInternal mInputManagerInternal;
/**
* Because the pointer is a singleton, it can only be targeted at one display at a time. Because
@@ -73,6 +92,8 @@ class InputController {
mLock = lock;
mNativeWrapper = nativeWrapper;
mActivePointerDisplayId = Display.INVALID_DISPLAY;
+ mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
+ mInputManagerInternal = LocalServices.getService(InputManagerInternal.class);
}
void close() {
@@ -90,7 +111,9 @@ class InputController {
int productId,
@NonNull IBinder deviceToken,
int displayId) {
- final int fd = mNativeWrapper.openUinputKeyboard(deviceName, vendorId, productId);
+ final String phys = createPhys(PHYS_TYPE_KEYBOARD);
+ setUniqueIdAssociation(displayId, phys);
+ final int fd = mNativeWrapper.openUinputKeyboard(deviceName, vendorId, productId, phys);
if (fd < 0) {
throw new RuntimeException(
"A native error occurred when creating keyboard: " + -fd);
@@ -99,7 +122,7 @@ class InputController {
synchronized (mLock) {
mInputDeviceDescriptors.put(deviceToken,
new InputDeviceDescriptor(fd, binderDeathRecipient,
- InputDeviceDescriptor.TYPE_KEYBOARD, displayId));
+ InputDeviceDescriptor.TYPE_KEYBOARD, displayId, phys));
}
try {
deviceToken.linkToDeath(binderDeathRecipient, /* flags= */ 0);
@@ -114,7 +137,9 @@ class InputController {
int productId,
@NonNull IBinder deviceToken,
int displayId) {
- final int fd = mNativeWrapper.openUinputMouse(deviceName, vendorId, productId);
+ final String phys = createPhys(PHYS_TYPE_MOUSE);
+ setUniqueIdAssociation(displayId, phys);
+ final int fd = mNativeWrapper.openUinputMouse(deviceName, vendorId, productId, phys);
if (fd < 0) {
throw new RuntimeException(
"A native error occurred when creating mouse: " + -fd);
@@ -123,11 +148,9 @@ class InputController {
synchronized (mLock) {
mInputDeviceDescriptors.put(deviceToken,
new InputDeviceDescriptor(fd, binderDeathRecipient,
- InputDeviceDescriptor.TYPE_MOUSE, displayId));
- final InputManagerInternal inputManagerInternal =
- LocalServices.getService(InputManagerInternal.class);
- inputManagerInternal.setVirtualMousePointerDisplayId(displayId);
- inputManagerInternal.setPointerAcceleration(1);
+ InputDeviceDescriptor.TYPE_MOUSE, displayId, phys));
+ mInputManagerInternal.setVirtualMousePointerDisplayId(displayId);
+ mInputManagerInternal.setPointerAcceleration(1);
mActivePointerDisplayId = displayId;
}
try {
@@ -144,7 +167,9 @@ class InputController {
@NonNull IBinder deviceToken,
int displayId,
@NonNull Point screenSize) {
- final int fd = mNativeWrapper.openUinputTouchscreen(deviceName, vendorId, productId,
+ final String phys = createPhys(PHYS_TYPE_TOUCHSCREEN);
+ setUniqueIdAssociation(displayId, phys);
+ final int fd = mNativeWrapper.openUinputTouchscreen(deviceName, vendorId, productId, phys,
screenSize.y, screenSize.x);
if (fd < 0) {
throw new RuntimeException(
@@ -154,7 +179,7 @@ class InputController {
synchronized (mLock) {
mInputDeviceDescriptors.put(deviceToken,
new InputDeviceDescriptor(fd, binderDeathRecipient,
- InputDeviceDescriptor.TYPE_TOUCHSCREEN, displayId));
+ InputDeviceDescriptor.TYPE_TOUCHSCREEN, displayId, phys));
}
try {
deviceToken.linkToDeath(binderDeathRecipient, /* flags= */ 0);
@@ -174,6 +199,7 @@ class InputController {
}
token.unlinkToDeath(inputDeviceDescriptor.getDeathRecipient(), /* flags= */ 0);
mNativeWrapper.closeUinput(inputDeviceDescriptor.getFileDescriptor());
+ InputManager.getInstance().removeUniqueIdAssociation(inputDeviceDescriptor.getPhys());
// Reset values to the default if all virtual mice are unregistered, or set display
// id if there's another mouse (choose the most recent).
@@ -197,9 +223,7 @@ class InputController {
}
}
if (mostRecentlyCreatedMouse != null) {
- final InputManagerInternal inputManagerInternal =
- LocalServices.getService(InputManagerInternal.class);
- inputManagerInternal.setVirtualMousePointerDisplayId(
+ mInputManagerInternal.setVirtualMousePointerDisplayId(
mostRecentlyCreatedMouse.getDisplayId());
mActivePointerDisplayId = mostRecentlyCreatedMouse.getDisplayId();
} else {
@@ -209,14 +233,21 @@ class InputController {
}
private void resetMouseValuesLocked() {
- final InputManagerInternal inputManagerInternal =
- LocalServices.getService(InputManagerInternal.class);
- inputManagerInternal.setVirtualMousePointerDisplayId(Display.INVALID_DISPLAY);
- inputManagerInternal.setPointerAcceleration(
+ mInputManagerInternal.setVirtualMousePointerDisplayId(Display.INVALID_DISPLAY);
+ mInputManagerInternal.setPointerAcceleration(
IInputConstants.DEFAULT_POINTER_ACCELERATION);
mActivePointerDisplayId = Display.INVALID_DISPLAY;
}
+ private static String createPhys(@PhysType String type) {
+ return String.format("virtual%s:%d", type, sNextPhysId.getAndIncrement());
+ }
+
+ private void setUniqueIdAssociation(int displayId, String phys) {
+ final String displayUniqueId = mDisplayManagerInternal.getDisplayInfo(displayId).uniqueId;
+ InputManager.getInstance().addUniqueIdAssociation(phys, displayUniqueId);
+ }
+
boolean sendKeyEvent(@NonNull IBinder token, @NonNull VirtualKeyEvent event) {
synchronized (mLock) {
final InputDeviceDescriptor inputDeviceDescriptor = mInputDeviceDescriptors.get(
@@ -321,17 +352,18 @@ class InputController {
fout.println(" creationOrder: "
+ inputDeviceDescriptor.getCreationOrderNumber());
fout.println(" type: " + inputDeviceDescriptor.getType());
+ fout.println(" phys: " + inputDeviceDescriptor.getPhys());
}
fout.println(" Active mouse display id: " + mActivePointerDisplayId);
}
}
private static native int nativeOpenUinputKeyboard(String deviceName, int vendorId,
- int productId);
- private static native int nativeOpenUinputMouse(String deviceName, int vendorId,
- int productId);
+ int productId, String phys);
+ private static native int nativeOpenUinputMouse(String deviceName, int vendorId, int productId,
+ String phys);
private static native int nativeOpenUinputTouchscreen(String deviceName, int vendorId,
- int productId, int height, int width);
+ int productId, String phys, int height, int width);
private static native boolean nativeCloseUinput(int fd);
private static native boolean nativeWriteKeyEvent(int fd, int androidKeyCode, int action);
private static native boolean nativeWriteButtonEvent(int fd, int buttonCode, int action);
@@ -345,20 +377,18 @@ class InputController {
/** Wrapper around the static native methods for tests. */
@VisibleForTesting
protected static class NativeWrapper {
- public int openUinputKeyboard(String deviceName, int vendorId, int productId) {
- return nativeOpenUinputKeyboard(deviceName, vendorId,
- productId);
+ public int openUinputKeyboard(String deviceName, int vendorId, int productId, String phys) {
+ return nativeOpenUinputKeyboard(deviceName, vendorId, productId, phys);
}
- public int openUinputMouse(String deviceName, int vendorId, int productId) {
- return nativeOpenUinputMouse(deviceName, vendorId,
- productId);
+ public int openUinputMouse(String deviceName, int vendorId, int productId, String phys) {
+ return nativeOpenUinputMouse(deviceName, vendorId, productId, phys);
}
- public int openUinputTouchscreen(String deviceName, int vendorId, int productId, int height,
- int width) {
- return nativeOpenUinputTouchscreen(deviceName, vendorId,
- productId, height, width);
+ public int openUinputTouchscreen(String deviceName, int vendorId,
+ int productId, String phys, int height, int width) {
+ return nativeOpenUinputTouchscreen(deviceName, vendorId, productId, phys, height,
+ width);
}
public boolean closeUinput(int fd) {
@@ -410,15 +440,17 @@ class InputController {
private final IBinder.DeathRecipient mDeathRecipient;
private final @Type int mType;
private final int mDisplayId;
+ private final String mPhys;
// Monotonically increasing number; devices with lower numbers were created earlier.
private final long mCreationOrderNumber;
- InputDeviceDescriptor(int fd, IBinder.DeathRecipient deathRecipient,
- @Type int type, int displayId) {
+ InputDeviceDescriptor(int fd, IBinder.DeathRecipient deathRecipient, @Type int type,
+ int displayId, String phys) {
mFd = fd;
mDeathRecipient = deathRecipient;
mType = type;
mDisplayId = displayId;
+ mPhys = phys;
mCreationOrderNumber = sNextCreationOrderNumber.getAndIncrement();
}
@@ -445,6 +477,10 @@ class InputController {
public long getCreationOrderNumber() {
return mCreationOrderNumber;
}
+
+ public String getPhys() {
+ return mPhys;
+ }
}
private final class BinderDeathRecipient implements IBinder.DeathRecipient {
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 95b9e58a9dfd..98a5ec1c3681 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
@@ -34,6 +34,8 @@ import android.companion.virtual.VirtualDeviceManager.ActivityListener;
import android.companion.virtual.VirtualDeviceParams;
import android.content.ComponentName;
import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
import android.graphics.Point;
import android.graphics.PointF;
import android.hardware.display.DisplayManager;
@@ -57,6 +59,7 @@ import android.util.SparseArray;
import android.window.DisplayWindowPolicyController;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.app.BlockedAppActivity;
import com.android.server.LocalServices;
import java.io.FileDescriptor;
@@ -418,7 +421,8 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
getAllowedUserHandles(),
mParams.getAllowedActivities(),
mParams.getBlockedActivities(),
- createListenerAdapter(displayId));
+ createListenerAdapter(displayId),
+ activityInfo -> onActivityBlocked(displayId, activityInfo));
mWindowPolicyControllers.put(displayId, dwpc);
return dwpc;
}
@@ -441,6 +445,16 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
}
}
+ private void onActivityBlocked(int displayId, ActivityInfo activityInfo) {
+ Intent intent = BlockedAppActivity.createStreamingBlockedIntent(
+ UserHandle.getUserId(activityInfo.applicationInfo.uid), activityInfo,
+ mAssociationInfo.getDisplayName());
+ mContext.startActivityAsUser(
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK),
+ ActivityOptions.makeBasic().setLaunchDisplayId(displayId).toBundle(),
+ mContext.getUser());
+ }
+
private ArraySet<UserHandle> getAllowedUserHandles() {
ArraySet<UserHandle> result = new ArraySet<>();
DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
diff --git a/services/core/java/android/app/usage/UsageStatsManagerInternal.java b/services/core/java/android/app/usage/UsageStatsManagerInternal.java
index 435d294a3e8e..a35aa7c74ee5 100644
--- a/services/core/java/android/app/usage/UsageStatsManagerInternal.java
+++ b/services/core/java/android/app/usage/UsageStatsManagerInternal.java
@@ -21,6 +21,7 @@ import android.annotation.ElapsedRealtimeLong;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
+import android.app.ActivityManager.ProcessState;
import android.app.usage.UsageStatsManager.StandbyBuckets;
import android.content.ComponentName;
import android.content.LocusId;
@@ -375,10 +376,11 @@ public abstract class UsageStatsManagerInternal {
* to this broadcast.
* @param timestampMs time (in millis) when the broadcast was dispatched, in
* {@link SystemClock#elapsedRealtime()} timebase.
+ * @param targetUidProcState process state of the uid that the broadcast is targeted to.
*/
public abstract void reportBroadcastDispatched(int sourceUid, @NonNull String targetPackage,
@NonNull UserHandle targetUser, long idForResponseEvent,
- @ElapsedRealtimeLong long timestampMs);
+ @ElapsedRealtimeLong long timestampMs, @ProcessState int targetUidProcState);
/**
* Reports a notification posted event to the UsageStatsManager.
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index f56bfab7055f..a8eeaf8d0ae2 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -901,32 +901,20 @@ public abstract class PackageManagerInternal implements PackageSettingsSnapshotP
/**
* Perform the given action for each package.
- *
- * @param locked whether to hold the packages lock. If the lock is not held, the objects will
- * be iterated using a temporary data structure. In the vast majority of cases,
- * the lock should not have to be held. This is exposed to mirror the
- * functionality of the other forEach methods, for eventual migration.
* @param action action to be performed
*/
- public abstract void forEachPackageState(boolean locked, Consumer<PackageStateInternal> action);
+ public abstract void forEachPackageState(Consumer<PackageStateInternal> action);
/**
- * {@link #forEachPackageState(boolean, Consumer)} but filtered to only states with packages
+ * {@link #forEachPackageState(Consumer)} but filtered to only states with packages
* on device where {@link PackageStateInternal#getPkg()} is not null.
*/
public abstract void forEachPackage(Consumer<AndroidPackage> action);
/**
* Perform the given action for each installed package for a user.
- * Note that packages lock will be held while performing the actions.
*/
public abstract void forEachInstalledPackage(
- @NonNull Consumer<AndroidPackage> actionLocked, @UserIdInt int userId);
-
- /**
- * Perform the given action for each installed package for a user.
- */
- public abstract void forEachInstalledPackage(boolean locked,
@NonNull Consumer<AndroidPackage> action, @UserIdInt int userId);
/** Returns the list of enabled components */
diff --git a/services/core/java/com/android/server/DockObserver.java b/services/core/java/com/android/server/DockObserver.java
index e5a7b4e4ee23..8a6b54fd9769 100644
--- a/services/core/java/com/android/server/DockObserver.java
+++ b/services/core/java/com/android/server/DockObserver.java
@@ -19,7 +19,6 @@ package com.android.server;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
-import android.content.pm.PackageManager;
import android.media.AudioManager;
import android.media.Ringtone;
import android.media.RingtoneManager;
@@ -32,15 +31,21 @@ import android.os.SystemClock;
import android.os.UEventObserver;
import android.os.UserHandle;
import android.provider.Settings;
-import android.util.Log;
+import android.util.Pair;
import android.util.Slog;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.DumpUtils;
+import com.android.server.ExtconUEventObserver.ExtconInfo;
import java.io.FileDescriptor;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
/**
* DockObserver monitors for a docking station.
@@ -48,9 +53,6 @@ import java.io.PrintWriter;
final class DockObserver extends SystemService {
private static final String TAG = "DockObserver";
- private static final String DOCK_UEVENT_MATCH = "DEVPATH=/devices/virtual/switch/dock";
- private static final String DOCK_STATE_PATH = "/sys/class/switch/dock/state";
-
private static final int MSG_DOCK_STATE_CHANGED = 0;
private final PowerManager mPowerManager;
@@ -69,6 +71,92 @@ final class DockObserver extends SystemService {
private final boolean mAllowTheaterModeWakeFromDock;
+ private final List<ExtconStateConfig> mExtconStateConfigs;
+
+ static final class ExtconStateProvider {
+ private final Map<String, String> mState;
+
+ ExtconStateProvider(Map<String, String> state) {
+ mState = state;
+ }
+
+ String getValue(String key) {
+ return mState.get(key);
+ }
+
+
+ static ExtconStateProvider fromString(String stateString) {
+ Map<String, String> states = new HashMap<>();
+ String[] lines = stateString.split("\n");
+ for (String line : lines) {
+ String[] fields = line.split("=");
+ if (fields.length == 2) {
+ states.put(fields[0], fields[1]);
+ } else {
+ Slog.e(TAG, "Invalid line: " + line);
+ }
+ }
+ return new ExtconStateProvider(states);
+ }
+
+ static ExtconStateProvider fromFile(String stateFilePath) {
+ char[] buffer = new char[1024];
+ try (FileReader file = new FileReader(stateFilePath)) {
+ int len = file.read(buffer, 0, 1024);
+ String stateString = (new String(buffer, 0, len)).trim();
+ return ExtconStateProvider.fromString(stateString);
+ } catch (FileNotFoundException e) {
+ Slog.w(TAG, "No state file found at: " + stateFilePath);
+ return new ExtconStateProvider(new HashMap<>());
+ } catch (Exception e) {
+ Slog.e(TAG, "" , e);
+ return new ExtconStateProvider(new HashMap<>());
+ }
+ }
+ }
+
+ /**
+ * Represents a mapping from extcon state to EXTRA_DOCK_STATE value. Each
+ * instance corresponds to an entry in config_dockExtconStateMapping.
+ */
+ private static final class ExtconStateConfig {
+
+ // The EXTRA_DOCK_STATE that will be used if the extcon key-value pairs match
+ public final int extraStateValue;
+
+ // A list of key-value pairs that must be present in the extcon state for a match
+ // to be considered. An empty list is considered a matching wildcard.
+ public final List<Pair<String, String>> keyValuePairs = new ArrayList<>();
+
+ ExtconStateConfig(int extraStateValue) {
+ this.extraStateValue = extraStateValue;
+ }
+ }
+
+ private static List<ExtconStateConfig> loadExtconStateConfigs(Context context) {
+ String[] rows = context.getResources().getStringArray(
+ com.android.internal.R.array.config_dockExtconStateMapping);
+ try {
+ ArrayList<ExtconStateConfig> configs = new ArrayList<>();
+ for (String row : rows) {
+ String[] rowFields = row.split(",");
+ ExtconStateConfig config = new ExtconStateConfig(Integer.parseInt(rowFields[0]));
+ for (int i = 1; i < rowFields.length; i++) {
+ String[] keyValueFields = rowFields[i].split("=");
+ if (keyValueFields.length != 2) {
+ throw new IllegalArgumentException("Invalid key-value: " + rowFields[i]);
+ }
+ config.keyValuePairs.add(Pair.create(keyValueFields[0], keyValueFields[1]));
+ }
+ configs.add(config);
+ }
+ return configs;
+ } catch (IllegalArgumentException | ArrayIndexOutOfBoundsException e) {
+ Slog.e(TAG, "Could not parse extcon state config", e);
+ return new ArrayList<>();
+ }
+ }
+
public DockObserver(Context context) {
super(context);
@@ -77,9 +165,25 @@ final class DockObserver extends SystemService {
mAllowTheaterModeWakeFromDock = context.getResources().getBoolean(
com.android.internal.R.bool.config_allowTheaterModeWakeFromDock);
- init(); // set initial status
+ mExtconStateConfigs = loadExtconStateConfigs(context);
+
+ List<ExtconInfo> infos = ExtconInfo.getExtconInfoForTypes(new String[] {
+ ExtconInfo.EXTCON_DOCK
+ });
- mObserver.startObserving(DOCK_UEVENT_MATCH);
+ if (!infos.isEmpty()) {
+ ExtconInfo info = infos.get(0);
+ Slog.i(TAG, "Found extcon info devPath: " + info.getDevicePath()
+ + ", statePath: " + info.getStatePath());
+
+ // set initial status
+ setDockStateFromProviderLocked(ExtconStateProvider.fromFile(info.getStatePath()));
+ mPreviousDockState = mActualDockState;
+
+ mExtconUEventObserver.startObserving(info);
+ } else {
+ Slog.i(TAG, "No extcon dock device found in this kernel.");
+ }
}
@Override
@@ -101,26 +205,6 @@ final class DockObserver extends SystemService {
}
}
- private void init() {
- synchronized (mLock) {
- try {
- char[] buffer = new char[1024];
- FileReader file = new FileReader(DOCK_STATE_PATH);
- try {
- int len = file.read(buffer, 0, 1024);
- setActualDockStateLocked(Integer.parseInt((new String(buffer, 0, len)).trim()));
- mPreviousDockState = mActualDockState;
- } finally {
- file.close();
- }
- } catch (FileNotFoundException e) {
- Slog.w(TAG, "This kernel does not have dock station support");
- } catch (Exception e) {
- Slog.e(TAG, "" , e);
- }
- }
- }
-
private void setActualDockStateLocked(int newState) {
mActualDockState = newState;
if (!mUpdatesStopped) {
@@ -234,19 +318,50 @@ final class DockObserver extends SystemService {
}
};
- private final UEventObserver mObserver = new UEventObserver() {
- @Override
- public void onUEvent(UEventObserver.UEvent event) {
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Slog.v(TAG, "Dock UEVENT: " + event.toString());
+ private int getDockedStateExtraValue(ExtconStateProvider state) {
+ for (ExtconStateConfig config : mExtconStateConfigs) {
+ boolean match = true;
+ for (Pair<String, String> keyValue : config.keyValuePairs) {
+ String stateValue = state.getValue(keyValue.first);
+ match = match && keyValue.second.equals(stateValue);
+ if (!match) {
+ break;
+ }
}
- try {
- synchronized (mLock) {
- setActualDockStateLocked(Integer.parseInt(event.get("SWITCH_STATE")));
+ if (match) {
+ return config.extraStateValue;
+ }
+ }
+
+ return Intent.EXTRA_DOCK_STATE_DESK;
+ }
+
+ @VisibleForTesting
+ void setDockStateFromProviderForTesting(ExtconStateProvider provider) {
+ synchronized (mLock) {
+ setDockStateFromProviderLocked(provider);
+ }
+ }
+
+ private void setDockStateFromProviderLocked(ExtconStateProvider provider) {
+ int state = Intent.EXTRA_DOCK_STATE_UNDOCKED;
+ if ("1".equals(provider.getValue("DOCK"))) {
+ state = getDockedStateExtraValue(provider);
+ }
+ setActualDockStateLocked(state);
+ }
+
+ private final ExtconUEventObserver mExtconUEventObserver = new ExtconUEventObserver() {
+ @Override
+ public void onUEvent(ExtconInfo extconInfo, UEventObserver.UEvent event) {
+ synchronized (mLock) {
+ String stateString = event.get("STATE");
+ if (stateString != null) {
+ setDockStateFromProviderLocked(ExtconStateProvider.fromString(stateString));
+ } else {
+ Slog.e(TAG, "Extcon event missing STATE: " + event);
}
- } catch (NumberFormatException e) {
- Slog.e(TAG, "Could not parse switch state from event " + event);
}
}
};
diff --git a/services/core/java/com/android/server/OWNERS b/services/core/java/com/android/server/OWNERS
index efdd7abe85de..4129feb4e082 100644
--- a/services/core/java/com/android/server/OWNERS
+++ b/services/core/java/com/android/server/OWNERS
@@ -10,9 +10,6 @@ per-file ZramWriteback.java = minchan@google.com, rajekumar@google.com
# Userspace reboot
per-file UserspaceRebootLogger.java = ioffe@google.com, dvander@google.com
-# Sensor Privacy
-per-file SensorPrivacyService.java = file:platform/frameworks/native:/libs/sensorprivacy/OWNERS
-
# ServiceWatcher
per-file ServiceWatcher.java = sooniln@google.com
diff --git a/services/core/java/com/android/server/PersistentDataBlockService.java b/services/core/java/com/android/server/PersistentDataBlockService.java
index 16645dfd386d..06c11fa4a20c 100644
--- a/services/core/java/com/android/server/PersistentDataBlockService.java
+++ b/services/core/java/com/android/server/PersistentDataBlockService.java
@@ -673,6 +673,12 @@ public class PersistentDataBlockService extends SystemService {
throw new UnsupportedOperationException("cannot read frp credential");
}
}
+
+ @Override
+ public String getPersistentDataPackageName() {
+ enforcePersistentDataBlockAccess();
+ return mContext.getString(R.string.config_persistentDataPackageName);
+ }
};
private PersistentDataBlockManagerInternal mInternalService =
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index f71f02a6ec4e..8aeae6ae49b9 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -123,6 +123,7 @@ import android.provider.DocumentsContract;
import android.provider.Downloads;
import android.provider.MediaStore;
import android.provider.Settings;
+import android.service.storage.ExternalStorageService;
import android.sysprop.VoldProperties;
import android.text.TextUtils;
import android.text.format.DateUtils;
@@ -491,6 +492,8 @@ class StorageManagerService extends IStorageManager.Stub
@GuardedBy("mAppFuseLock")
private AppFuseBridge mAppFuseBridge = null;
+ private HashMap<Integer, Integer> mUserSharesMediaWith = new HashMap<>();
+
/** Matches known application dir paths. The first group contains the generic part of the path,
* the second group contains the user id (or null if it's a public volume without users), the
* third group contains the package name, and the fourth group the remainder of the path.
@@ -1235,6 +1238,21 @@ class StorageManagerService extends IStorageManager.Stub
private void onUnlockUser(int userId) {
Slog.d(TAG, "onUnlockUser " + userId);
+ if (userId != UserHandle.USER_SYSTEM) {
+ // Check if this user shares media with another user
+ try {
+ Context userContext = mContext.createPackageContextAsUser("system", 0,
+ UserHandle.of(userId));
+ UserManager um = userContext.getSystemService(UserManager.class);
+ if (um != null && um.isMediaSharedWithParent()) {
+ int parentUserId = um.getProfileParent(userId).id;
+ mUserSharesMediaWith.put(userId, parentUserId);
+ mUserSharesMediaWith.put(parentUserId, userId);
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.e(TAG, "Failed to create user context for user " + userId);
+ }
+ }
// We purposefully block here to make sure that user-specific
// staging area is ready so it's ready for zygote-forked apps to
// bind mount against.
@@ -3971,6 +3989,29 @@ class StorageManagerService extends IStorageManager.Stub
final boolean realState = (flags & StorageManager.FLAG_REAL_STATE) != 0;
final boolean includeInvisible = (flags & StorageManager.FLAG_INCLUDE_INVISIBLE) != 0;
final boolean includeRecent = (flags & StorageManager.FLAG_INCLUDE_RECENT) != 0;
+ final boolean includeSharedProfile =
+ (flags & StorageManager.FLAG_INCLUDE_SHARED_PROFILE) != 0;
+
+ // Only Apps with MANAGE_EXTERNAL_STORAGE should call the API with includeSharedProfile
+ if (includeSharedProfile) {
+ try {
+ // Get package name for calling app and
+ // verify it has MANAGE_EXTERNAL_STORAGE permission
+ final String[] packagesFromUid = mIPackageManager.getPackagesForUid(callingUid);
+ if (packagesFromUid == null) {
+ throw new SecurityException("Unknown uid " + callingUid);
+ }
+ // Checking first entry in packagesFromUid is enough as using "sharedUserId"
+ // mechanism is rare and discouraged. Also, Apps that share same UID share the same
+ // permissions.
+ if (!mStorageManagerInternal.hasExternalStorageAccess(callingUid,
+ packagesFromUid[0])) {
+ throw new SecurityException("Only File Manager Apps permitted");
+ }
+ } catch (RemoteException re) {
+ throw new SecurityException("Unknown uid " + callingUid, re);
+ }
+ }
// Report all volumes as unmounted until we've recorded that user 0 has unlocked. There
// are no guarantees that callers will see a consistent view of the volume before that
@@ -4002,6 +4043,7 @@ class StorageManagerService extends IStorageManager.Stub
final ArrayList<StorageVolume> res = new ArrayList<>();
final ArraySet<String> resUuids = new ArraySet<>();
+ final int userIdSharingMedia = mUserSharesMediaWith.getOrDefault(userId, -1);
synchronized (mLock) {
for (int i = 0; i < mVolumes.size(); i++) {
final String volId = mVolumes.keyAt(i);
@@ -4014,6 +4056,11 @@ class StorageManagerService extends IStorageManager.Stub
if (vol.getMountUserId() == userId) {
break;
}
+ if (includeSharedProfile && vol.getMountUserId() == userIdSharingMedia) {
+ // If the volume belongs to a user we share media with,
+ // return it too.
+ break;
+ }
// Skip if emulated volume not for userId
default:
continue;
@@ -4021,10 +4068,12 @@ class StorageManagerService extends IStorageManager.Stub
boolean match = false;
if (forWrite) {
- match = vol.isVisibleForWrite(userId);
+ match = vol.isVisibleForWrite(userId)
+ || (includeSharedProfile && vol.isVisibleForWrite(userIdSharingMedia));
} else {
match = vol.isVisibleForUser(userId)
- || (includeInvisible && vol.getPath() != null);
+ || (includeInvisible && vol.getPath() != null)
+ || (includeSharedProfile && vol.isVisibleForRead(userIdSharingMedia));
}
if (!match) continue;
@@ -4045,9 +4094,13 @@ class StorageManagerService extends IStorageManager.Stub
reportUnmounted = true;
}
- final StorageVolume userVol = vol.buildStorageVolume(mContext, userId,
+ int volUserId = userId;
+ if (volUserId != vol.getMountUserId() && vol.getMountUserId() >= 0) {
+ volUserId = vol.getMountUserId();
+ }
+ final StorageVolume userVol = vol.buildStorageVolume(mContext, volUserId,
reportUnmounted);
- if (vol.isPrimary()) {
+ if (vol.isPrimary() && vol.getMountUserId() == userId) {
res.add(0, userVol);
foundPrimary = true;
} else {
diff --git a/services/core/java/com/android/server/SystemServiceManager.java b/services/core/java/com/android/server/SystemServiceManager.java
index 0bc3fcc83d47..ce30f0348f11 100644
--- a/services/core/java/com/android/server/SystemServiceManager.java
+++ b/services/core/java/com/android/server/SystemServiceManager.java
@@ -576,19 +576,20 @@ public final class SystemServiceManager implements Dumpable {
return () -> {
final TimingsTraceAndSlog t = new TimingsTraceAndSlog(oldTrace);
final String serviceName = service.getClass().getName();
+ final int curUserId = curUser.getUserIdentifier();
+ t.traceBegin("ssm.on" + USER_STARTING + "User-" + curUserId + "_" + serviceName);
try {
- final int curUserId = curUser.getUserIdentifier();
- t.traceBegin("ssm.on" + USER_STARTING + "User-" + curUserId + "_" + serviceName);
long time = SystemClock.elapsedRealtime();
service.onUserStarting(curUser);
warnIfTooLong(SystemClock.elapsedRealtime() - time, service,
"on" + USER_STARTING + "User-" + curUserId);
- t.traceEnd();
} catch (Exception e) {
Slog.wtf(TAG, "Failure reporting " + USER_STARTING + " of user " + curUser
+ " to service " + serviceName, e);
Slog.e(TAG, "Disabling thread pool - please capture a bug report.");
sUseLifecycleThreadPool = false;
+ } finally {
+ t.traceEnd();
}
};
}
@@ -601,11 +602,18 @@ public final class SystemServiceManager implements Dumpable {
final int curUserId = curUser.getUserIdentifier();
t.traceBegin("ssm.on" + USER_COMPLETED_EVENT + "User-" + curUserId
+ "_" + eventType + "_" + serviceName);
- long time = SystemClock.elapsedRealtime();
- service.onUserCompletedEvent(curUser, eventType);
- warnIfTooLong(SystemClock.elapsedRealtime() - time, service,
- "on" + USER_COMPLETED_EVENT + "User-" + curUserId);
- t.traceEnd();
+ try {
+ long time = SystemClock.elapsedRealtime();
+ service.onUserCompletedEvent(curUser, eventType);
+ warnIfTooLong(SystemClock.elapsedRealtime() - time, service,
+ "on" + USER_COMPLETED_EVENT + "User-" + curUserId);
+ } catch (Exception e) {
+ Slog.wtf(TAG, "Failure reporting " + USER_COMPLETED_EVENT + " of user " + curUser
+ + " to service " + serviceName, e);
+ throw e;
+ } finally {
+ t.traceEnd();
+ }
};
}
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index b5c0a67be20f..9353dd832bba 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -2721,7 +2721,8 @@ public final class ActiveServices {
int bindServiceLocked(IApplicationThread caller, IBinder token, Intent service,
String resolvedType, final IServiceConnection connection, int flags,
- String instanceName, String callingPackage, final int userId)
+ String instanceName, boolean isSupplementalProcessService, String callingPackage,
+ final int userId)
throws TransactionTooLargeException {
if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "bindService: " + service
+ " type=" + resolvedType + " conn=" + connection.asBinder()
@@ -2805,10 +2806,9 @@ public final class ActiveServices {
final boolean isBindExternal = (flags & Context.BIND_EXTERNAL_SERVICE) != 0;
final boolean allowInstant = (flags & Context.BIND_ALLOW_INSTANT) != 0;
- ServiceLookupResult res =
- retrieveServiceLocked(service, instanceName, resolvedType, callingPackage,
- callingPid, callingUid, userId, true,
- callerFg, isBindExternal, allowInstant);
+ ServiceLookupResult res = retrieveServiceLocked(service, instanceName,
+ isSupplementalProcessService, resolvedType, callingPackage, callingPid, callingUid,
+ userId, true, callerFg, isBindExternal, allowInstant);
if (res == null) {
return 0;
}
@@ -3228,6 +3228,20 @@ public final class ActiveServices {
int callingPid, int callingUid, int userId,
boolean createIfNeeded, boolean callingFromFg, boolean isBindExternal,
boolean allowInstant) {
+ return retrieveServiceLocked(service, instanceName, false, resolvedType, callingPackage,
+ callingPid, callingUid, userId, createIfNeeded, callingFromFg, isBindExternal,
+ allowInstant);
+ }
+
+ private ServiceLookupResult retrieveServiceLocked(Intent service,
+ String instanceName, boolean isSupplementalProcessService, String resolvedType,
+ String callingPackage, int callingPid, int callingUid, int userId,
+ boolean createIfNeeded, boolean callingFromFg, boolean isBindExternal,
+ boolean allowInstant) {
+ if (isSupplementalProcessService && instanceName == null) {
+ throw new IllegalArgumentException("No instanceName provided for supplemental process");
+ }
+
ServiceRecord r = null;
if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "retrieveServiceLocked: " + service
+ " type=" + resolvedType + " callingUid=" + callingUid);
@@ -3249,7 +3263,6 @@ public final class ActiveServices {
if (instanceName == null) {
comp = service.getComponent();
} else {
- // This is for isolated services
final ComponentName realComp = service.getComponent();
if (realComp == null) {
throw new IllegalArgumentException("Can't use custom instance name '" + instanceName
@@ -3304,12 +3317,19 @@ public final class ActiveServices {
return null;
}
if (instanceName != null
- && (sInfo.flags & ServiceInfo.FLAG_ISOLATED_PROCESS) == 0) {
+ && (sInfo.flags & ServiceInfo.FLAG_ISOLATED_PROCESS) == 0
+ && !isSupplementalProcessService) {
throw new IllegalArgumentException("Can't use instance name '" + instanceName
- + "' with non-isolated service '" + sInfo.name + "'");
+ + "' with non-isolated non-supplemental service '" + sInfo.name + "'");
}
- ComponentName className = new ComponentName(
- sInfo.applicationInfo.packageName, sInfo.name);
+ if (isSupplementalProcessService
+ && (sInfo.flags & ServiceInfo.FLAG_ISOLATED_PROCESS) != 0) {
+ throw new IllegalArgumentException("Service cannot be both supplemental and "
+ + "isolated");
+ }
+
+ ComponentName className = new ComponentName(sInfo.applicationInfo.packageName,
+ sInfo.name);
ComponentName name = comp != null ? comp : className;
if (!mAm.validateAssociationAllowedLocked(callingPackage, callingUid,
name.getPackageName(), sInfo.applicationInfo.uid)) {
@@ -3392,7 +3412,8 @@ public final class ActiveServices {
= new Intent.FilterComparison(service.cloneFilter());
final ServiceRestarter res = new ServiceRestarter();
r = new ServiceRecord(mAm, className, name, definingPackageName,
- definingUid, filter, sInfo, callingFromFg, res);
+ definingUid, filter, sInfo, callingFromFg, res,
+ isSupplementalProcessService);
res.setService(r);
smap.mServicesByInstanceName.put(name, r);
smap.mServicesByIntent.put(filter, r);
diff --git a/services/core/java/com/android/server/am/ActivityManagerLocal.java b/services/core/java/com/android/server/am/ActivityManagerLocal.java
index 9a1bfddcec07..d9ee7d974864 100644
--- a/services/core/java/com/android/server/am/ActivityManagerLocal.java
+++ b/services/core/java/com/android/server/am/ActivityManagerLocal.java
@@ -18,6 +18,9 @@ package com.android.server.am;
import android.annotation.NonNull;
import android.annotation.SystemApi;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.TransactionTooLargeException;
/**
* Interface for in-process calls into
@@ -58,4 +61,24 @@ public interface ActivityManagerLocal {
* @hide
*/
void tempAllowWhileInUsePermissionInFgs(int uid, long durationMs);
+
+ /**
+ * Starts a supplemental process service and binds to it. You can through the arguments here
+ * have the system bring up multiple concurrent processes hosting their own instance of that
+ * service. The <var>userAppUid</var> you provide here identifies the different instances - each
+ * unique uid is attributed to a supplemental process.
+ *
+ * @param service Identifies the supplemental process service to connect to. The Intent must
+ * specify an explicit component name. This value cannot be null.
+ * @param conn Receives information as the service is started and stopped.
+ * This must be a valid ServiceConnection object; it must not be null.
+ * @param userAppUid Uid of the app for which the supplemental process needs to be spawned.
+ * @return {@code true} if the system is in the process of bringing up a
+ * service that your client has permission to bind to; {@code false}
+ * if the system couldn't find the service or if your client doesn't
+ * have permission to bind to it.
+ */
+ boolean startAndBindSupplementalProcessService(@NonNull Intent service,
+ @NonNull ServiceConnection conn, int userAppUid) throws TransactionTooLargeException;
+
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index ae4f79e7922e..442b9de9911d 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -218,6 +218,7 @@ import android.content.IIntentSender;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.LocusId;
+import android.content.ServiceConnection;
import android.content.pm.ActivityInfo;
import android.content.pm.ActivityPresentationInfo;
import android.content.pm.ApplicationInfo;
@@ -256,6 +257,7 @@ import android.os.BinderProxy;
import android.os.BugreportParams;
import android.os.Build;
import android.os.Bundle;
+import android.os.ConditionVariable;
import android.os.Debug;
import android.os.DropBoxManager;
import android.os.FactoryTest;
@@ -336,7 +338,7 @@ import com.android.internal.app.ProcessMap;
import com.android.internal.app.SystemUserHomeActivity;
import com.android.internal.app.procstats.ProcessState;
import com.android.internal.app.procstats.ProcessStats;
-import com.android.internal.content.PackageHelper;
+import com.android.internal.content.InstallLocationUtils;
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.internal.notification.SystemNotificationChannels;
import com.android.internal.os.BackgroundThread;
@@ -4951,7 +4953,7 @@ public class ActivityManagerService extends IActivityManager.Stub
// This line is needed to CTS test for the correct exception handling
// See b/138952436#comment36 for context
Slog.i(TAG, "About to commit checkpoint");
- IStorageManager storageManager = PackageHelper.getStorageManager();
+ IStorageManager storageManager = InstallLocationUtils.getStorageManager();
storageManager.commitChanges();
} catch (Exception e) {
PowerManager pm = (PowerManager)
@@ -5014,6 +5016,7 @@ public class ActivityManagerService extends IActivityManager.Stub
}
// UART is on if init's console service is running, send a warning notification.
showConsoleNotificationIfActive();
+ showMteOverrideNotificationIfActive();
t.traceEnd();
}
@@ -5048,6 +5051,35 @@ public class ActivityManagerService extends IActivityManager.Stub
}
+ private void showMteOverrideNotificationIfActive() {
+ if (!SystemProperties.getBoolean("ro.arm64.memtag.bootctl_supported", false)
+ || !com.android.internal.os.Zygote.nativeSupportsMemoryTagging()) {
+ return;
+ }
+ String title = mContext
+ .getString(com.android.internal.R.string.mte_override_notification_title);
+ String message = mContext
+ .getString(com.android.internal.R.string.mte_override_notification_message);
+ Notification notification =
+ new Notification.Builder(mContext, SystemNotificationChannels.DEVELOPER)
+ .setSmallIcon(com.android.internal.R.drawable.stat_sys_adb)
+ .setOngoing(true)
+ .setTicker(title)
+ .setDefaults(0) // please be quiet
+ .setColor(mContext.getColor(
+ com.android.internal.R.color
+ .system_notification_accent_color))
+ .setContentTitle(title)
+ .setContentText(message)
+ .setVisibility(Notification.VISIBILITY_PUBLIC)
+ .build();
+
+ NotificationManager notificationManager =
+ mContext.getSystemService(NotificationManager.class);
+ notificationManager.notifyAsUser(
+ null, SystemMessage.NOTE_MTE_OVERRIDE_ENABLED, notification, UserHandle.ALL);
+ }
+
@Override
public void bootAnimationComplete() {
if (DEBUG_ALL) Slog.d(TAG, "bootAnimationComplete: Callers=" + Debug.getCallers(4));
@@ -12284,13 +12316,25 @@ public class ActivityManagerService extends IActivityManager.Stub
public int bindService(IApplicationThread caller, IBinder token, Intent service,
String resolvedType, IServiceConnection connection, int flags,
String callingPackage, int userId) throws TransactionTooLargeException {
- return bindIsolatedService(caller, token, service, resolvedType, connection, flags,
+ return bindServiceInstance(caller, token, service, resolvedType, connection, flags,
null, callingPackage, userId);
}
- public int bindIsolatedService(IApplicationThread caller, IBinder token, Intent service,
+ /**
+ * Binds to a service with a given instanceName, creating it if it does not already exist.
+ * If the instanceName field is not supplied, binding to the service occurs as usual.
+ */
+ public int bindServiceInstance(IApplicationThread caller, IBinder token, Intent service,
String resolvedType, IServiceConnection connection, int flags, String instanceName,
String callingPackage, int userId) throws TransactionTooLargeException {
+ return bindServiceInstance(caller, token, service, resolvedType, connection, flags,
+ instanceName, false, callingPackage, userId);
+ }
+
+ private int bindServiceInstance(IApplicationThread caller, IBinder token, Intent service,
+ String resolvedType, IServiceConnection connection, int flags, String instanceName,
+ boolean isSupplementalProcessService, String callingPackage, int userId)
+ throws TransactionTooLargeException {
enforceNotIsolatedCaller("bindService");
// Refuse possible leaked file descriptors
@@ -12302,6 +12346,10 @@ public class ActivityManagerService extends IActivityManager.Stub
throw new IllegalArgumentException("callingPackage cannot be null");
}
+ if (isSupplementalProcessService && instanceName == null) {
+ throw new IllegalArgumentException("No instance name provided for isolated process");
+ }
+
// Ensure that instanceName, which is caller provided, does not contain
// unusual characters.
if (instanceName != null) {
@@ -12315,8 +12363,8 @@ public class ActivityManagerService extends IActivityManager.Stub
}
synchronized(this) {
- return mServices.bindServiceLocked(caller, token, service,
- resolvedType, connection, flags, instanceName, callingPackage, userId);
+ return mServices.bindServiceLocked(caller, token, service, resolvedType, connection,
+ flags, instanceName, isSupplementalProcessService, callingPackage, userId);
}
}
@@ -15448,6 +15496,62 @@ public class ActivityManagerService extends IActivityManager.Stub
}
}
+ /**
+ * Dump the resources structure for the given process
+ *
+ * @param process The process to dump resource info for
+ * @param fd The FileDescriptor to dump it into
+ * @throws RemoteException
+ */
+ public boolean dumpResources(String process, ParcelFileDescriptor fd, RemoteCallback callback)
+ throws RemoteException {
+ synchronized (this) {
+ ProcessRecord proc = findProcessLOSP(process, UserHandle.USER_CURRENT, "dumpResources");
+ IApplicationThread thread;
+ if (proc == null || (thread = proc.getThread()) == null) {
+ throw new IllegalArgumentException("Unknown process: " + process);
+ }
+ thread.dumpResources(fd, callback);
+ return true;
+ }
+ }
+
+ /**
+ * Dump the resources structure for all processes
+ *
+ * @param fd The FileDescriptor to dump it into
+ * @throws RemoteException
+ */
+ public void dumpAllResources(ParcelFileDescriptor fd, PrintWriter pw) throws RemoteException {
+ synchronized (mProcLock) {
+ mProcessList.forEachLruProcessesLOSP(true, app -> {
+ ConditionVariable lock = new ConditionVariable();
+ RemoteCallback
+ finishCallback = new RemoteCallback(result -> lock.open(), null);
+
+ pw.println(String.format("------ DUMP RESOURCES %s (%s) ------",
+ app.processName,
+ app.info.packageName));
+ pw.flush();
+ try {
+ app.getThread().dumpResources(fd.dup(), finishCallback);
+ lock.block(2000);
+ } catch (Exception e) {
+ pw.println(String.format(
+ "------ EXCEPTION DUMPING RESOURCES for %s (%s): %s ------",
+ app.processName,
+ app.info.packageName,
+ e.getMessage()));
+ pw.flush();
+ }
+ pw.println(String.format("------ END DUMP RESOURCES %s (%s) ------",
+ app.processName,
+ app.info.packageName));
+ pw.flush();
+ });
+ }
+ }
+
@Override
public void setDumpHeapDebugLimit(String processName, int uid, long maxMemSize,
String reportPackage) {
@@ -15793,6 +15897,34 @@ public class ActivityManagerService extends IActivityManager.Stub
}
@Override
+ public boolean startAndBindSupplementalProcessService(Intent service,
+ ServiceConnection conn, int userAppUid) throws TransactionTooLargeException {
+ if (service == null) {
+ throw new IllegalArgumentException("intent is null");
+ }
+ if (conn == null) {
+ throw new IllegalArgumentException("connection is null");
+ }
+ if (service.getComponent() == null) {
+ throw new IllegalArgumentException("service must specify explicit component");
+ }
+ if (!UserHandle.isApp(userAppUid)) {
+ throw new IllegalArgumentException("uid is not within application range");
+ }
+
+ Handler handler = mContext.getMainThreadHandler();
+ int flags = Context.BIND_AUTO_CREATE;
+
+ final IServiceConnection sd = mContext.getServiceDispatcher(conn, handler, flags);
+ service.prepareToLeaveProcess(mContext);
+ return ActivityManagerService.this.bindServiceInstance(
+ mContext.getIApplicationThread(), mContext.getActivityToken(), service,
+ service.resolveTypeIfNeeded(mContext.getContentResolver()), sd, flags,
+ Integer.toString(userAppUid), /*isSupplementalProcessService*/ true,
+ mContext.getOpPackageName(), UserHandle.getUserId(userAppUid)) != 0;
+ }
+
+ @Override
public void onUserRemoved(@UserIdInt int userId) {
// Clean up any ActivityTaskManager state (by telling it the user is stopped)
mAtmInternal.onUserStopped(userId);
diff --git a/services/core/java/com/android/server/am/AppBatteryTracker.java b/services/core/java/com/android/server/am/AppBatteryTracker.java
index 8a21a0fb2e0e..14d73f6c8592 100644
--- a/services/core/java/com/android/server/am/AppBatteryTracker.java
+++ b/services/core/java/com/android/server/am/AppBatteryTracker.java
@@ -145,7 +145,7 @@ final class AppBatteryTracker extends BaseAppStateTracker<AppBatteryPolicy>
/**
* The uid battery usage stats data from our last query, it does not include snapshot data.
*/
- // No lock is needed.
+ @GuardedBy("mLock")
private final SparseDoubleArray mLastUidBatteryUsage = new SparseDoubleArray();
// No lock is needed.
@@ -155,12 +155,15 @@ final class AppBatteryTracker extends BaseAppStateTracker<AppBatteryPolicy>
private final SparseDoubleArray mTmpUidBatteryUsage2 = new SparseDoubleArray();
// No lock is needed.
+ private final SparseDoubleArray mTmpUidBatteryUsageInWindow = new SparseDoubleArray();
+
+ // No lock is needed.
private final ArraySet<UserHandle> mTmpUserIds = new ArraySet<>();
/**
* The start timestamp of the battery usage stats result from our last query.
*/
- // No lock is needed.
+ @GuardedBy("mLock")
private long mLastUidBatteryUsageStartTs;
// For debug only.
@@ -296,8 +299,10 @@ final class AppBatteryTracker extends BaseAppStateTracker<AppBatteryPolicy>
checkBatteryUsageStats();
} else {
// We didn't do the battery stats update above, schedule a check later.
- scheduleBatteryUsageStatsUpdateIfNecessary(
- mLastBatteryUsageSamplingTs + mBatteryUsageStatsPollingMinIntervalMs - now);
+ synchronized (mLock) {
+ scheduleBatteryUsageStatsUpdateIfNecessary(
+ mLastBatteryUsageSamplingTs + mBatteryUsageStatsPollingMinIntervalMs - now);
+ }
}
}
@@ -305,7 +310,10 @@ final class AppBatteryTracker extends BaseAppStateTracker<AppBatteryPolicy>
final long now = SystemClock.elapsedRealtime();
final AppBatteryPolicy bgPolicy = mInjector.getPolicy();
try {
- final SparseDoubleArray uidConsumers = mUidBatteryUsageInWindow;
+ final SparseDoubleArray uidConsumers = mTmpUidBatteryUsageInWindow;
+ synchronized (mLock) {
+ copyUidBatteryUsage(mUidBatteryUsageInWindow, uidConsumers);
+ }
final long since = Math.max(0, now - bgPolicy.mBgCurrentDrainWindowMs);
for (int i = 0, size = uidConsumers.size(); i < size; i++) {
final int uid = uidConsumers.keyAt(i);
@@ -408,7 +416,9 @@ final class AppBatteryTracker extends BaseAppStateTracker<AppBatteryPolicy>
if (curDuration >= windowSize) {
// If we do have long enough data for the window, save it.
- copyUidBatteryUsage(buf, mUidBatteryUsageInWindow, windowSize * 1.0d / curDuration);
+ synchronized (mLock) {
+ copyUidBatteryUsage(buf, mUidBatteryUsageInWindow, windowSize * 1.0d / curDuration);
+ }
needUpdateUidBatteryUsageInWindow = false;
}
@@ -416,8 +426,11 @@ final class AppBatteryTracker extends BaseAppStateTracker<AppBatteryPolicy>
mTmpUidBatteryUsage2.clear();
copyUidBatteryUsage(buf, mTmpUidBatteryUsage2);
- final long lastUidBatteryUsageStartTs = mLastUidBatteryUsageStartTs;
- mLastUidBatteryUsageStartTs = curStart;
+ final long lastUidBatteryUsageStartTs;
+ synchronized (mLock) {
+ lastUidBatteryUsageStartTs = mLastUidBatteryUsageStartTs;
+ mLastUidBatteryUsageStartTs = curStart;
+ }
if (curStart > lastUidBatteryUsageStartTs && lastUidBatteryUsageStartTs > 0) {
// The battery usage stats committed data since our last query,
// let's query the snapshots to get the data since last start.
@@ -429,42 +442,47 @@ final class AppBatteryTracker extends BaseAppStateTracker<AppBatteryPolicy>
}
if (needUpdateUidBatteryUsageInWindow && curDuration > windowSize) {
// If we do have long enough data for the window, save it.
- copyUidBatteryUsage(buf, mUidBatteryUsageInWindow, windowSize * 1.0d / curDuration);
+ synchronized (mLock) {
+ copyUidBatteryUsage(buf, mUidBatteryUsageInWindow, windowSize * 1.0d / curDuration);
+ }
needUpdateUidBatteryUsageInWindow = false;
}
// Add the delta into the global records.
- for (int i = 0, size = buf.size(); i < size; i++) {
- final int uid = buf.keyAt(i);
- final int index = mUidBatteryUsage.indexOfKey(uid);
- final double delta = Math.max(0.0d,
- buf.valueAt(i) - mLastUidBatteryUsage.get(uid, 0.0d));
- final double before;
- if (index >= 0) {
- before = mUidBatteryUsage.valueAt(index);
- mUidBatteryUsage.setValueAt(index, before + delta);
- } else {
- before = 0.0d;
- mUidBatteryUsage.put(uid, delta);
- }
- if (DEBUG_BACKGROUND_BATTERY_TRACKER) {
- final double actualDelta = buf.valueAt(i) - mLastUidBatteryUsage.get(uid, 0.0d);
- String msg = "Updating mUidBatteryUsage uid=" + uid + ", before=" + before
- + ", after=" + mUidBatteryUsage.get(uid, 0.0d) + ", delta=" + actualDelta
- + ", last=" + mLastUidBatteryUsage.get(uid, 0.0d)
- + ", curStart=" + curStart
- + ", lastLastStart=" + lastUidBatteryUsageStartTs
- + ", thisLastStart=" + mLastUidBatteryUsageStartTs;
- if (actualDelta < 0.0d) {
- // Something is wrong, the battery usage shouldn't be negative.
- Slog.e(TAG, msg);
+ synchronized (mLock) {
+ for (int i = 0, size = buf.size(); i < size; i++) {
+ final int uid = buf.keyAt(i);
+ final int index = mUidBatteryUsage.indexOfKey(uid);
+ final double delta = Math.max(0.0d,
+ buf.valueAt(i) - mLastUidBatteryUsage.get(uid, 0.0d));
+ final double before;
+ if (index >= 0) {
+ before = mUidBatteryUsage.valueAt(index);
+ mUidBatteryUsage.setValueAt(index, before + delta);
} else {
- Slog.i(TAG, msg);
+ before = 0.0d;
+ mUidBatteryUsage.put(uid, delta);
+ }
+ if (DEBUG_BACKGROUND_BATTERY_TRACKER) {
+ final double actualDelta = buf.valueAt(i) - mLastUidBatteryUsage.get(uid, 0.0d);
+ String msg = "Updating mUidBatteryUsage uid=" + uid + ", before=" + before
+ + ", after=" + mUidBatteryUsage.get(uid, 0.0d)
+ + ", delta=" + actualDelta
+ + ", last=" + mLastUidBatteryUsage.get(uid, 0.0d)
+ + ", curStart=" + curStart
+ + ", lastLastStart=" + lastUidBatteryUsageStartTs
+ + ", thisLastStart=" + mLastUidBatteryUsageStartTs;
+ if (actualDelta < 0.0d) {
+ // Something is wrong, the battery usage shouldn't be negative.
+ Slog.e(TAG, msg);
+ } else {
+ Slog.i(TAG, msg);
+ }
}
}
+ // Now update the mLastUidBatteryUsage with the data we just saved above.
+ copyUidBatteryUsage(mTmpUidBatteryUsage2, mLastUidBatteryUsage);
}
- // Now update the mLastUidBatteryUsage with the data we just saved above.
- copyUidBatteryUsage(mTmpUidBatteryUsage2, mLastUidBatteryUsage);
mTmpUidBatteryUsage2.clear();
if (needUpdateUidBatteryUsageInWindow) {
@@ -473,7 +491,9 @@ final class AppBatteryTracker extends BaseAppStateTracker<AppBatteryPolicy>
.includeProcessStateData()
.aggregateSnapshots(now - windowSize, lastUidBatteryUsageStartTs);
updateBatteryUsageStatsOnceInternal(buf, builder, userIds, batteryStatsInternal);
- copyUidBatteryUsage(buf, mUidBatteryUsageInWindow);
+ synchronized (mLock) {
+ copyUidBatteryUsage(buf, mUidBatteryUsageInWindow);
+ }
}
}
@@ -584,38 +604,40 @@ final class AppBatteryTracker extends BaseAppStateTracker<AppBatteryPolicy>
pw.print(prefix);
pw.println("APP BATTERY STATE TRACKER:");
updateBatteryUsageStatsIfNecessary(mInjector.currentTimeMillis(), true);
- final SparseDoubleArray uidConsumers = mUidBatteryUsageInWindow;
- pw.print(" " + prefix);
- pw.print("Boot=");
- TimeUtils.dumpTime(pw, mBootTimestamp);
- pw.print(" Last battery usage start=");
- TimeUtils.dumpTime(pw, mLastUidBatteryUsageStartTs);
- pw.println();
- pw.print(" " + prefix);
- pw.print("Battery usage over last ");
- final String newPrefix = " " + prefix;
- final AppBatteryPolicy bgPolicy = mInjector.getPolicy();
- final long now = SystemClock.elapsedRealtime();
- final long since = Math.max(0, now - bgPolicy.mBgCurrentDrainWindowMs);
- pw.println(TimeUtils.formatDuration(now - since));
- if (uidConsumers.size() == 0) {
- pw.print(newPrefix);
- pw.println("(none)");
- } else {
- for (int i = 0, size = uidConsumers.size(); i < size; i++) {
- final int uid = uidConsumers.keyAt(i);
- final double bgUsage = uidConsumers.valueAt(i);
- final double exemptedUsage = mAppRestrictionController
- .getUidBatteryExemptedUsageSince(uid, since, now);
- final double reportedUsage = Math.max(0.0d, bgUsage - exemptedUsage);
- pw.format("%s%s: [%s] %.3f mAh (%4.2f%%) | %.3f mAh (%4.2f%%) | "
- + "%.3f mAh (%4.2f%%) | %.3f mAh\n",
- newPrefix, UserHandle.formatUid(uid),
- PowerExemptionManager.reasonCodeToString(bgPolicy.shouldExemptUid(uid)),
- bgUsage , bgPolicy.getPercentage(uid, bgUsage),
- exemptedUsage, bgPolicy.getPercentage(-1, exemptedUsage),
- reportedUsage, bgPolicy.getPercentage(-1, reportedUsage),
- mUidBatteryUsage.get(uid, 0.0d));
+ synchronized (mLock) {
+ final SparseDoubleArray uidConsumers = mUidBatteryUsageInWindow;
+ pw.print(" " + prefix);
+ pw.print("Boot=");
+ TimeUtils.dumpTime(pw, mBootTimestamp);
+ pw.print(" Last battery usage start=");
+ TimeUtils.dumpTime(pw, mLastUidBatteryUsageStartTs);
+ pw.println();
+ pw.print(" " + prefix);
+ pw.print("Battery usage over last ");
+ final String newPrefix = " " + prefix;
+ final AppBatteryPolicy bgPolicy = mInjector.getPolicy();
+ final long now = SystemClock.elapsedRealtime();
+ final long since = Math.max(0, now - bgPolicy.mBgCurrentDrainWindowMs);
+ pw.println(TimeUtils.formatDuration(now - since));
+ if (uidConsumers.size() == 0) {
+ pw.print(newPrefix);
+ pw.println("(none)");
+ } else {
+ for (int i = 0, size = uidConsumers.size(); i < size; i++) {
+ final int uid = uidConsumers.keyAt(i);
+ final double bgUsage = uidConsumers.valueAt(i);
+ final double exemptedUsage = mAppRestrictionController
+ .getUidBatteryExemptedUsageSince(uid, since, now);
+ final double reportedUsage = Math.max(0.0d, bgUsage - exemptedUsage);
+ pw.format("%s%s: [%s] %.3f mAh (%4.2f%%) | %.3f mAh (%4.2f%%) | "
+ + "%.3f mAh (%4.2f%%) | %.3f mAh\n",
+ newPrefix, UserHandle.formatUid(uid),
+ PowerExemptionManager.reasonCodeToString(bgPolicy.shouldExemptUid(uid)),
+ bgUsage , bgPolicy.getPercentage(uid, bgUsage),
+ exemptedUsage, bgPolicy.getPercentage(-1, exemptedUsage),
+ reportedUsage, bgPolicy.getPercentage(-1, reportedUsage),
+ mUidBatteryUsage.get(uid, 0.0d));
+ }
}
}
super.dump(pw, prefix);
diff --git a/services/core/java/com/android/server/am/AppRestrictionController.java b/services/core/java/com/android/server/am/AppRestrictionController.java
index bd63a24e4e41..1315293abaa4 100644
--- a/services/core/java/com/android/server/am/AppRestrictionController.java
+++ b/services/core/java/com/android/server/am/AppRestrictionController.java
@@ -1314,7 +1314,7 @@ public final class AppRestrictionController {
void onSystemReady() {
mContext.registerReceiverForAllUsers(mActionButtonReceiver,
new IntentFilter(ACTION_FGS_MANAGER_TRAMPOLINE),
- MANAGE_ACTIVITY_TASKS, mBgController.mBgHandler);
+ MANAGE_ACTIVITY_TASKS, mBgController.mBgHandler, Context.RECEIVER_NOT_EXPORTED);
}
void postRequestBgRestrictedIfNecessary(String packageName, int uid) {
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index c8ad0e88e995..5da461d8e392 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -1365,7 +1365,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub
}
public void notePhoneDataConnectionState(final int dataType, final boolean hasData,
- final int serviceType) {
+ final int serviceType, final int nrFrequency) {
enforceCallingPermission();
synchronized (mLock) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
@@ -1373,7 +1373,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub
mHandler.post(() -> {
synchronized (mStats) {
mStats.notePhoneDataConnectionStateLocked(dataType, hasData, serviceType,
- elapsedRealtime, uptime);
+ nrFrequency, elapsedRealtime, uptime);
}
});
}
@@ -1962,6 +1962,32 @@ public final class BatteryStatsService extends IBatteryStats.Stub
}
}
+ /**
+ * Bluetooth on stat logging
+ */
+ public void noteBluetoothOn(int uid, int reason, String packageName) {
+ if (Binder.getCallingPid() != Process.myPid()) {
+ mContext.enforcePermission(android.Manifest.permission.BLUETOOTH_CONNECT,
+ Binder.getCallingPid(), uid, null);
+ }
+ FrameworkStatsLog.write_non_chained(FrameworkStatsLog.BLUETOOTH_ENABLED_STATE_CHANGED,
+ uid, null, FrameworkStatsLog.BLUETOOTH_ENABLED_STATE_CHANGED__STATE__ENABLED,
+ reason, packageName);
+ }
+
+ /**
+ * Bluetooth off stat logging
+ */
+ public void noteBluetoothOff(int uid, int reason, String packageName) {
+ if (Binder.getCallingPid() != Process.myPid()) {
+ mContext.enforcePermission(android.Manifest.permission.BLUETOOTH_CONNECT,
+ Binder.getCallingPid(), uid, null);
+ }
+ FrameworkStatsLog.write_non_chained(FrameworkStatsLog.BLUETOOTH_ENABLED_STATE_CHANGED,
+ uid, null, FrameworkStatsLog.BLUETOOTH_ENABLED_STATE_CHANGED__STATE__DISABLED,
+ reason, packageName);
+ }
+
@Override
public void noteBleScanStarted(final WorkSource ws, final boolean isUnoptimized) {
enforceCallingPermission();
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index 0c383ebbbcd7..e2921e9f5e71 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -334,7 +334,7 @@ public final class BroadcastQueue {
mService.updateOomAdjPendingTargetsLocked(OomAdjuster.OOM_ADJ_REASON_START_RECEIVER);
// Tell the application to launch this receiver.
- maybeReportBroadcastDispatchedEventLocked(r);
+ maybeReportBroadcastDispatchedEventLocked(r, r.curReceiver.applicationInfo.uid);
r.intent.setComponent(r.curComponent);
boolean started = false;
@@ -927,7 +927,7 @@ public final class BroadcastQueue {
r.receiverTime = SystemClock.uptimeMillis();
maybeAddAllowBackgroundActivityStartsToken(filter.receiverList.app, r);
maybeScheduleTempAllowlistLocked(filter.owningUid, r, r.options);
- maybeReportBroadcastDispatchedEventLocked(r);
+ maybeReportBroadcastDispatchedEventLocked(r, filter.owningUid);
performReceiveLocked(filter.receiverList.app, filter.receiverList.receiver,
new Intent(r.intent), r.resultCode, r.resultData,
r.resultExtras, r.ordered, r.initialSticky, r.userId);
@@ -1856,7 +1856,7 @@ public final class BroadcastQueue {
return null;
}
- private void maybeReportBroadcastDispatchedEventLocked(BroadcastRecord r) {
+ private void maybeReportBroadcastDispatchedEventLocked(BroadcastRecord r, int targetUid) {
final String targetPackage = getTargetPackage(r);
// Ignore non-explicit broadcasts
if (targetPackage == null) {
@@ -1867,11 +1867,10 @@ public final class BroadcastQueue {
if (r.options == null || r.options.getIdForResponseEvent() <= 0) {
return;
}
- // TODO (206518114): Only report this event when the broadcast is dispatched while the app
- // is in the background state.
getUsageStatsManagerInternal().reportBroadcastDispatched(
r.callingUid, targetPackage, UserHandle.of(r.userId),
- r.options.getIdForResponseEvent(), SystemClock.elapsedRealtime());
+ r.options.getIdForResponseEvent(), SystemClock.elapsedRealtime(),
+ mService.getUidStateLocked(targetUid));
}
@NonNull
diff --git a/services/core/java/com/android/server/am/DataConnectionStats.java b/services/core/java/com/android/server/am/DataConnectionStats.java
index 6e39a4c802d9..f0910dcb0da2 100644
--- a/services/core/java/com/android/server/am/DataConnectionStats.java
+++ b/services/core/java/com/android/server/am/DataConnectionStats.java
@@ -109,7 +109,7 @@ public class DataConnectionStats extends BroadcastReceiver {
}
try {
mBatteryStats.notePhoneDataConnectionState(networkType, visible,
- mServiceState.getState());
+ mServiceState.getState(), mServiceState.getNrFrequencyRange());
} catch (RemoteException e) {
Log.w(TAG, "Error noting data connection state", e);
}
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index 24e7ba4a32b9..da78e2d7504e 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -570,6 +570,14 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN
ComponentName instanceName, String definingPackageName, int definingUid,
Intent.FilterComparison intent, ServiceInfo sInfo, boolean callerIsFg,
Runnable restarter) {
+ this(ams, name, instanceName, definingPackageName, definingUid, intent, sInfo, callerIsFg,
+ restarter, false);
+ }
+
+ ServiceRecord(ActivityManagerService ams, ComponentName name,
+ ComponentName instanceName, String definingPackageName, int definingUid,
+ Intent.FilterComparison intent, ServiceInfo sInfo, boolean callerIsFg,
+ Runnable restarter, boolean isSupplementalProcessService) {
this.ams = ams;
this.name = name;
this.instanceName = instanceName;
@@ -580,7 +588,8 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN
serviceInfo = sInfo;
appInfo = sInfo.applicationInfo;
packageName = sInfo.applicationInfo.packageName;
- if ((sInfo.flags & ServiceInfo.FLAG_ISOLATED_PROCESS) != 0) {
+ if ((sInfo.flags & ServiceInfo.FLAG_ISOLATED_PROCESS) != 0
+ || isSupplementalProcessService) {
processName = sInfo.processName + ":" + instanceName.getClassName();
} else {
processName = sInfo.processName;
diff --git a/services/core/java/com/android/server/app/GameManagerService.java b/services/core/java/com/android/server/app/GameManagerService.java
index f1429a567564..3c9d29d77bbe 100644
--- a/services/core/java/com/android/server/app/GameManagerService.java
+++ b/services/core/java/com/android/server/app/GameManagerService.java
@@ -85,6 +85,7 @@ import com.android.server.SystemService;
import com.android.server.SystemService.TargetUser;
import java.io.FileDescriptor;
+import java.io.PrintWriter;
import java.util.List;
/**
@@ -159,6 +160,29 @@ public final class GameManagerService extends IGameManagerService.Stub {
new GameManagerShellCommand().exec(this, in, out, err, args, callback, result);
}
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
+ if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
+ != PackageManager.PERMISSION_GRANTED) {
+ writer.println("Permission Denial: can't dump GameManagerService from from pid="
+ + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
+ + " without permission " + android.Manifest.permission.DUMP);
+ return;
+ }
+ if (args == null || args.length == 0) {
+ writer.println("*Dump GameManagerService*");
+ dumpAllGameConfigs(writer);
+ }
+ }
+
+ private void dumpAllGameConfigs(PrintWriter pw) {
+ final int userId = ActivityManager.getCurrentUser();
+ String[] packageList = getInstalledGamePackageNames(userId);
+ for (final String packageName : packageList) {
+ pw.println(getInterventionList(packageName));
+ }
+ }
+
class SettingsHandler extends Handler {
SettingsHandler(Looper looper) {
@@ -1266,8 +1290,7 @@ public final class GameManagerService extends IGameManagerService.Stub {
.append(packageName);
return listStrSb.toString();
}
- listStrSb.append("\nPackage name: ")
- .append(packageName)
+ listStrSb.append("\n")
.append(packageConfig.toString());
return listStrSb.toString();
}
diff --git a/services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java b/services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java
index 8578de7ac4b4..af1dd335ab1e 100644
--- a/services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java
+++ b/services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java
@@ -98,7 +98,10 @@ final class GameServiceProviderInstanceImpl implements GameServiceProviderInstan
private final IGameServiceController mGameServiceController =
new IGameServiceController.Stub() {
@Override
+ @RequiresPermission(Manifest.permission.MANAGE_GAME_ACTIVITY)
public void createGameSession(int taskId) {
+ mContext.enforceCallingPermission(Manifest.permission.MANAGE_GAME_ACTIVITY,
+ "createGameSession()");
mBackgroundExecutor.execute(() -> {
GameServiceProviderInstanceImpl.this.createGameSession(taskId);
});
@@ -116,9 +119,10 @@ final class GameServiceProviderInstanceImpl implements GameServiceProviderInstan
});
}
- @RequiresPermission(android.Manifest.permission.FORCE_STOP_PACKAGES)
+ @Override
+ @RequiresPermission(Manifest.permission.MANAGE_GAME_ACTIVITY)
public void restartGame(int taskId) {
- mContext.enforceCallingPermission(Manifest.permission.FORCE_STOP_PACKAGES,
+ mContext.enforceCallingPermission(Manifest.permission.MANAGE_GAME_ACTIVITY,
"restartGame()");
mBackgroundExecutor.execute(() -> {
GameServiceProviderInstanceImpl.this.restartGame(taskId);
@@ -330,7 +334,6 @@ final class GameServiceProviderInstanceImpl implements GameServiceProviderInstan
+ ") is not awaiting game session request. Ignoring.");
return;
}
- mGameSessions.put(taskId, existingGameSessionRecord.withGameSessionRequested());
GameSessionViewHostConfiguration gameSessionViewHostConfiguration =
createViewHostConfigurationForTask(taskId);
@@ -373,12 +376,12 @@ final class GameServiceProviderInstanceImpl implements GameServiceProviderInstan
}, mBackgroundExecutor);
AndroidFuture<Void> unusedPostCreateGameSessionFuture =
- mGameSessionServiceConnector.post(gameService -> {
+ mGameSessionServiceConnector.post(gameSessionService -> {
CreateGameSessionRequest createGameSessionRequest =
new CreateGameSessionRequest(
taskId,
existingGameSessionRecord.getComponentName().getPackageName());
- gameService.create(
+ gameSessionService.create(
mGameSessionController,
createGameSessionRequest,
gameSessionViewHostConfiguration,
diff --git a/services/core/java/com/android/server/apphibernation/AppHibernationService.java b/services/core/java/com/android/server/apphibernation/AppHibernationService.java
index 9d4d1c1b0ff3..366718c65d84 100644
--- a/services/core/java/com/android/server/apphibernation/AppHibernationService.java
+++ b/services/core/java/com/android/server/apphibernation/AppHibernationService.java
@@ -38,6 +38,7 @@ import android.app.StatsManager.StatsPullAtomCallback;
import android.app.usage.UsageEvents;
import android.app.usage.UsageStatsManagerInternal;
import android.app.usage.UsageStatsManagerInternal.UsageEventListener;
+import android.apphibernation.HibernationStats;
import android.apphibernation.IAppHibernationService;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -221,7 +222,7 @@ public final class AppHibernationService extends SystemService {
}
getContext().enforceCallingOrSelfPermission(
android.Manifest.permission.MANAGE_APP_HIBERNATION,
- "Caller does not have MANAGE_APP_HIBERNATION permission.");
+ "Caller did not have permission while calling " + methodName);
userId = handleIncomingUser(userId, methodName);
synchronized (mLock) {
if (!checkUserStatesExist(userId, methodName)) {
@@ -380,6 +381,46 @@ public final class AppHibernationService extends SystemService {
}
/**
+ * Return the stats from app hibernation for each package provided.
+ *
+ * @param packageNames the set of packages to return stats for. Returns all if null
+ * @return map from package to stats for that package
+ */
+ public Map<String, HibernationStats> getHibernationStatsForUser(
+ @Nullable Set<String> packageNames, int userId) {
+ Map<String, HibernationStats> statsMap = new ArrayMap<>();
+ String methodName = "getHibernationStatsForUser";
+ getContext().enforceCallingOrSelfPermission(
+ android.Manifest.permission.MANAGE_APP_HIBERNATION,
+ "Caller does not have MANAGE_APP_HIBERNATION permission.");
+ userId = handleIncomingUser(userId, methodName);
+ synchronized (mLock) {
+ if (!checkUserStatesExist(userId, methodName)) {
+ return statsMap;
+ }
+ final Map<String, UserLevelState> userPackageStates = mUserStates.get(userId);
+ Set<String> pkgs = packageNames != null ? packageNames : userPackageStates.keySet();
+ for (String pkgName : pkgs) {
+ if (!mPackageManagerInternal.canQueryPackage(Binder.getCallingUid(), pkgName)) {
+ // Package not visible to caller
+ continue;
+ }
+ if (!mGlobalHibernationStates.containsKey(pkgName)
+ || !userPackageStates.containsKey(pkgName)) {
+ Slog.w(TAG, String.format(
+ "No hibernation state associated with package %s user %d. Maybe"
+ + "the package was uninstalled? ", pkgName, userId));
+ continue;
+ }
+ HibernationStats stats = new HibernationStats(
+ mGlobalHibernationStates.get(pkgName).savedByte);
+ statsMap.put(pkgName, stats);
+ }
+ }
+ return statsMap;
+ }
+
+ /**
* Put an app into hibernation for a given user, allowing user-level optimizations to occur. Do
* not hold {@link #mLock} while calling this to avoid deadlock scenarios.
*/
@@ -788,6 +829,13 @@ public final class AppHibernationService extends SystemService {
}
@Override
+ public Map<String, HibernationStats> getHibernationStatsForUser(
+ @Nullable List<String> packageNames, int userId) {
+ Set<String> pkgsSet = packageNames != null ? new ArraySet<>(packageNames) : null;
+ return mService.getHibernationStatsForUser(pkgsSet, userId);
+ }
+
+ @Override
public void onShellCommand(@Nullable FileDescriptor in, @Nullable FileDescriptor out,
@Nullable FileDescriptor err, @NonNull String[] args,
@Nullable ShellCallback callback, @NonNull ResultReceiver resultReceiver) {
diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java
index 42fca9b840df..47f31d505867 100644
--- a/services/core/java/com/android/server/audio/BtHelper.java
+++ b/services/core/java/com/android/server/audio/BtHelper.java
@@ -486,9 +486,7 @@ public class BtHelper {
return;
}
final BluetoothDevice btDevice = deviceList.get(0);
- final @BluetoothProfile.BtProfileState int state =
- proxy.getConnectionState(btDevice);
- if (state == BluetoothProfile.STATE_CONNECTED) {
+ if (proxy.getConnectionState(btDevice) == BluetoothProfile.STATE_CONNECTED) {
mDeviceBroker.queueOnBluetoothActiveDeviceChanged(
new AudioDeviceBroker.BtDeviceChangedData(btDevice, null,
new BtProfileConnectionInfo(profile), "mBluetoothProfileServiceListener"));
diff --git a/services/core/java/com/android/server/biometrics/AuthSession.java b/services/core/java/com/android/server/biometrics/AuthSession.java
index 0da6a1ba3109..79705a32c264 100644
--- a/services/core/java/com/android/server/biometrics/AuthSession.java
+++ b/services/core/java/com/android/server/biometrics/AuthSession.java
@@ -53,6 +53,7 @@ import android.hardware.biometrics.IBiometricSensorReceiver;
import android.hardware.biometrics.IBiometricServiceReceiver;
import android.hardware.biometrics.IBiometricSysuiReceiver;
import android.hardware.biometrics.PromptInfo;
+import android.hardware.biometrics.common.OperationContext;
import android.hardware.face.FaceManager;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
@@ -64,6 +65,7 @@ import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.util.FrameworkStatsLog;
+import com.android.server.biometrics.log.BiometricFrameworkStatsLogger;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -681,16 +683,18 @@ public final class AuthSession implements IBinder.DeathRecipient {
+ ", Latency: " + latency);
}
- FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_AUTHENTICATED,
+ final OperationContext operationContext = new OperationContext();
+ operationContext.isCrypto = isCrypto();
+ BiometricFrameworkStatsLogger.getInstance().authenticate(
+ operationContext,
statsModality(),
- mUserId,
- isCrypto(),
+ BiometricsProtoEnums.ACTION_UNKNOWN,
BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT,
- mPreAuthInfo.confirmationRequested,
- FrameworkStatsLog.BIOMETRIC_AUTHENTICATED__STATE__CONFIRMED,
- latency,
mDebugEnabled,
- -1 /* sensorId */,
+ latency,
+ FrameworkStatsLog.BIOMETRIC_AUTHENTICATED__STATE__CONFIRMED,
+ mPreAuthInfo.confirmationRequested,
+ mUserId,
-1f /* ambientLightLux */);
} else {
final long latency = System.currentTimeMillis() - mStartTimeMs;
@@ -711,17 +715,18 @@ public final class AuthSession implements IBinder.DeathRecipient {
+ ", Latency: " + latency);
}
// Auth canceled
- FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_ERROR_OCCURRED,
+ final OperationContext operationContext = new OperationContext();
+ operationContext.isCrypto = isCrypto();
+ BiometricFrameworkStatsLogger.getInstance().error(
+ operationContext,
statsModality(),
- mUserId,
- isCrypto(),
BiometricsProtoEnums.ACTION_AUTHENTICATE,
BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT,
- error,
- 0 /* vendorCode */,
mDebugEnabled,
latency,
- -1 /* sensorId */);
+ error,
+ 0 /* vendorCode */,
+ mUserId);
}
}
diff --git a/services/core/java/com/android/server/biometrics/log/BiometricContext.java b/services/core/java/com/android/server/biometrics/log/BiometricContext.java
new file mode 100644
index 000000000000..c5e266f87149
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/log/BiometricContext.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.log;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.hardware.biometrics.common.OperationContext;
+
+import java.util.function.Consumer;
+
+/**
+ * Cache for system state not directly related to biometric operations that is used for
+ * logging or optimizations.
+ */
+public interface BiometricContext {
+ /** Gets the context source from the system context. */
+ static BiometricContext getInstance(@NonNull Context context) {
+ return BiometricContextProvider.defaultProvider(context);
+ }
+
+ /** Update the given context with the most recent values and return it. */
+ OperationContext updateContext(@NonNull OperationContext operationContext,
+ boolean isCryptoOperation);
+
+ /** The session id for keyguard entry, if active, or null. */
+ @Nullable Integer getKeyguardEntrySessionId();
+
+ /** The session id for biometric prompt usage, if active, or null. */
+ @Nullable Integer getBiometricPromptSessionId();
+
+ /** If the display is in AOD. */
+ boolean isAoD();
+
+ /**
+ * Subscribe to context changes.
+ *
+ * @param context context that will be modified when changed
+ * @param consumer callback when the context is modified
+ */
+ void subscribe(@NonNull OperationContext context, @NonNull Consumer<OperationContext> consumer);
+
+ /** Unsubscribe from context changes. */
+ void unsubscribe(@NonNull OperationContext context);
+}
diff --git a/services/core/java/com/android/server/biometrics/log/BiometricContextProvider.java b/services/core/java/com/android/server/biometrics/log/BiometricContextProvider.java
new file mode 100644
index 000000000000..70acaff05e30
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/log/BiometricContextProvider.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.log;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.StatusBarManager;
+import android.content.Context;
+import android.hardware.biometrics.IBiometricContextListener;
+import android.hardware.biometrics.common.OperationContext;
+import android.hardware.biometrics.common.OperationReason;
+import android.hardware.display.AmbientDisplayConfiguration;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.ServiceManager.ServiceNotFoundException;
+import android.os.UserHandle;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.logging.InstanceId;
+import com.android.internal.statusbar.ISessionListener;
+import com.android.internal.statusbar.IStatusBarService;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.Consumer;
+
+/**
+ * A default provider for {@link BiometricContext}.
+ */
+class BiometricContextProvider implements BiometricContext {
+
+ private static final String TAG = "BiometricContextProvider";
+
+ private static final int SESSION_TYPES =
+ StatusBarManager.SESSION_KEYGUARD | StatusBarManager.SESSION_BIOMETRIC_PROMPT;
+
+ private static BiometricContextProvider sInstance;
+
+ static BiometricContextProvider defaultProvider(@NonNull Context context) {
+ synchronized (BiometricContextProvider.class) {
+ if (sInstance == null) {
+ try {
+ sInstance = new BiometricContextProvider(
+ new AmbientDisplayConfiguration(context),
+ IStatusBarService.Stub.asInterface(ServiceManager.getServiceOrThrow(
+ Context.STATUS_BAR_SERVICE)), null /* handler */);
+ } catch (ServiceNotFoundException e) {
+ throw new IllegalStateException("Failed to find required service", e);
+ }
+ }
+ }
+ return sInstance;
+ }
+
+ @NonNull
+ private final Map<OperationContext, Consumer<OperationContext>> mSubscribers =
+ new ConcurrentHashMap<>();
+
+ @Nullable
+ private final Map<Integer, InstanceId> mSession = new ConcurrentHashMap<>();
+
+ private final AmbientDisplayConfiguration mAmbientDisplayConfiguration;
+ private boolean mIsDozing = false;
+
+ @VisibleForTesting
+ BiometricContextProvider(@NonNull AmbientDisplayConfiguration ambientDisplayConfiguration,
+ @NonNull IStatusBarService service, @Nullable Handler handler) {
+ mAmbientDisplayConfiguration = ambientDisplayConfiguration;
+ try {
+ service.setBiometicContextListener(new IBiometricContextListener.Stub() {
+ @Override
+ public void onDozeChanged(boolean isDozing) {
+ mIsDozing = isDozing;
+ notifyChanged();
+ }
+
+ private void notifyChanged() {
+ if (handler != null) {
+ handler.post(() -> notifySubscribers());
+ } else {
+ notifySubscribers();
+ }
+ }
+ });
+ service.registerSessionListener(SESSION_TYPES, new ISessionListener.Stub() {
+ @Override
+ public void onSessionStarted(int sessionType, InstanceId instance) {
+ mSession.put(sessionType, instance);
+ }
+
+ @Override
+ public void onSessionEnded(int sessionType, InstanceId instance) {
+ final InstanceId id = mSession.remove(sessionType);
+ if (id != null && instance != null && id.getId() != instance.getId()) {
+ Slog.w(TAG, "session id mismatch");
+ }
+ }
+ });
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to register biometric context listener", e);
+ }
+ }
+
+ @Override
+ public OperationContext updateContext(@NonNull OperationContext operationContext,
+ boolean isCryptoOperation) {
+ operationContext.isAoD = isAoD();
+ operationContext.isCrypto = isCryptoOperation;
+ setFirstSessionId(operationContext);
+ return operationContext;
+ }
+
+ private void setFirstSessionId(@NonNull OperationContext operationContext) {
+ Integer sessionId = getKeyguardEntrySessionId();
+ if (sessionId != null) {
+ operationContext.id = sessionId;
+ operationContext.reason = OperationReason.KEYGUARD;
+ return;
+ }
+
+ sessionId = getBiometricPromptSessionId();
+ if (sessionId != null) {
+ operationContext.id = sessionId;
+ operationContext.reason = OperationReason.BIOMETRIC_PROMPT;
+ return;
+ }
+
+ operationContext.id = 0;
+ operationContext.reason = OperationReason.UNKNOWN;
+ }
+
+ @Nullable
+ @Override
+ public Integer getKeyguardEntrySessionId() {
+ final InstanceId id = mSession.get(StatusBarManager.SESSION_KEYGUARD);
+ return id != null ? id.getId() : null;
+ }
+
+ @Nullable
+ @Override
+ public Integer getBiometricPromptSessionId() {
+ final InstanceId id = mSession.get(StatusBarManager.SESSION_BIOMETRIC_PROMPT);
+ return id != null ? id.getId() : null;
+ }
+
+ @Override
+ public boolean isAoD() {
+ return mIsDozing && mAmbientDisplayConfiguration.alwaysOnEnabled(UserHandle.USER_CURRENT);
+ }
+
+ @Override
+ public void subscribe(@NonNull OperationContext context,
+ @NonNull Consumer<OperationContext> consumer) {
+ mSubscribers.put(context, consumer);
+ }
+
+ @Override
+ public void unsubscribe(@NonNull OperationContext context) {
+ mSubscribers.remove(context);
+ }
+
+ private void notifySubscribers() {
+ mSubscribers.forEach((context, consumer) -> {
+ context.isAoD = isAoD();
+ consumer.accept(context);
+ });
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/log/BiometricFrameworkStatsLogger.java b/services/core/java/com/android/server/biometrics/log/BiometricFrameworkStatsLogger.java
index c3471bd1d771..8965227a1bb4 100644
--- a/services/core/java/com/android/server/biometrics/log/BiometricFrameworkStatsLogger.java
+++ b/services/core/java/com/android/server/biometrics/log/BiometricFrameworkStatsLogger.java
@@ -17,6 +17,8 @@
package com.android.server.biometrics.log;
import android.hardware.biometrics.BiometricsProtoEnums;
+import android.hardware.biometrics.common.OperationContext;
+import android.hardware.biometrics.common.OperationReason;
import android.util.Slog;
import com.android.internal.util.FrameworkStatsLog;
@@ -33,42 +35,49 @@ public class BiometricFrameworkStatsLogger {
private BiometricFrameworkStatsLogger() {}
+ /** Shared instance. */
public static BiometricFrameworkStatsLogger getInstance() {
return sInstance;
}
/** {@see FrameworkStatsLog.BIOMETRIC_ACQUIRED}. */
- public void acquired(
+ public void acquired(OperationContext operationContext,
int statsModality, int statsAction, int statsClient, boolean isDebug,
- int acquiredInfo, int vendorCode, boolean isCrypto, int targetUserId) {
+ int acquiredInfo, int vendorCode, int targetUserId) {
FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_ACQUIRED,
statsModality,
targetUserId,
- isCrypto,
+ operationContext.isCrypto,
statsAction,
statsClient,
acquiredInfo,
vendorCode,
isDebug,
- -1 /* sensorId */);
+ -1 /* sensorId */,
+ operationContext.id,
+ sessionType(operationContext.reason),
+ operationContext.isAoD);
}
/** {@see FrameworkStatsLog.BIOMETRIC_AUTHENTICATED}. */
- public void authenticate(
+ public void authenticate(OperationContext operationContext,
int statsModality, int statsAction, int statsClient, boolean isDebug, long latency,
- boolean authenticated, int authState, boolean requireConfirmation, boolean isCrypto,
- int targetUserId, boolean isBiometricPrompt, float ambientLightLux) {
+ int authState, boolean requireConfirmation,
+ int targetUserId, float ambientLightLux) {
FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_AUTHENTICATED,
statsModality,
targetUserId,
- isCrypto,
+ operationContext.isCrypto,
statsClient,
requireConfirmation,
authState,
sanitizeLatency(latency),
isDebug,
-1 /* sensorId */,
- ambientLightLux);
+ ambientLightLux,
+ operationContext.id,
+ sessionType(operationContext.reason),
+ operationContext.isAoD);
}
/** {@see FrameworkStatsLog.BIOMETRIC_ENROLLED}. */
@@ -84,20 +93,23 @@ public class BiometricFrameworkStatsLogger {
}
/** {@see FrameworkStatsLog.BIOMETRIC_ERROR_OCCURRED}. */
- public void error(
+ public void error(OperationContext operationContext,
int statsModality, int statsAction, int statsClient, boolean isDebug, long latency,
- int error, int vendorCode, boolean isCrypto, int targetUserId) {
+ int error, int vendorCode, int targetUserId) {
FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_ERROR_OCCURRED,
statsModality,
targetUserId,
- isCrypto,
+ operationContext.isCrypto,
statsAction,
statsClient,
error,
vendorCode,
isDebug,
sanitizeLatency(latency),
- -1 /* sensorId */);
+ -1 /* sensorId */,
+ operationContext.id,
+ sessionType(operationContext.reason),
+ operationContext.isAoD);
}
/** {@see FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED}. */
@@ -123,4 +135,14 @@ public class BiometricFrameworkStatsLogger {
}
return latency;
}
+
+ private static int sessionType(@OperationReason byte reason) {
+ if (reason == OperationReason.BIOMETRIC_PROMPT) {
+ return BiometricsProtoEnums.SESSION_TYPE_BIOMETRIC_PROMPT;
+ }
+ if (reason == OperationReason.KEYGUARD) {
+ return BiometricsProtoEnums.SESSION_TYPE_KEYGUARD_ENTRY;
+ }
+ return BiometricsProtoEnums.SESSION_TYPE_UNKNOWN;
+ }
}
diff --git a/services/core/java/com/android/server/biometrics/log/BiometricLogger.java b/services/core/java/com/android/server/biometrics/log/BiometricLogger.java
index d029af38c683..2a8d9f1967eb 100644
--- a/services/core/java/com/android/server/biometrics/log/BiometricLogger.java
+++ b/services/core/java/com/android/server/biometrics/log/BiometricLogger.java
@@ -25,6 +25,7 @@ import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricsProtoEnums;
+import android.hardware.biometrics.common.OperationContext;
import android.hardware.face.FaceManager;
import android.hardware.fingerprint.FingerprintManager;
import android.util.Slog;
@@ -79,6 +80,12 @@ public class BiometricLogger {
}
};
+ /** Get a new logger with all unknown fields (for operations that do not require logs). */
+ public static BiometricLogger ofUnknown(@NonNull Context context) {
+ return new BiometricLogger(context, BiometricsProtoEnums.MODALITY_UNKNOWN,
+ BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN);
+ }
+
/**
* @param context system_server context
* @param statsModality One of {@link BiometricsProtoEnums} MODALITY_* constants.
@@ -103,6 +110,11 @@ public class BiometricLogger {
mSensorManager = sensorManager;
}
+ /** Creates a new logger with the action replaced with the new action. */
+ public BiometricLogger swapAction(@NonNull Context context, int statsAction) {
+ return new BiometricLogger(context, mStatsModality, statsAction, mStatsClient);
+ }
+
/** Disable logging metrics and only log critical events, such as system health issues. */
public void disableMetrics() {
mShouldLogMetrics = false;
@@ -133,8 +145,8 @@ public class BiometricLogger {
}
/** Log an acquisition event. */
- public void logOnAcquired(Context context,
- int acquiredInfo, int vendorCode, boolean isCrypto, int targetUserId) {
+ public void logOnAcquired(Context context, OperationContext operationContext,
+ int acquiredInfo, int vendorCode, int targetUserId) {
if (!mShouldLogMetrics) {
return;
}
@@ -154,7 +166,7 @@ public class BiometricLogger {
if (DEBUG) {
Slog.v(TAG, "Acquired! Modality: " + mStatsModality
+ ", User: " + targetUserId
- + ", IsCrypto: " + isCrypto
+ + ", IsCrypto: " + operationContext.isCrypto
+ ", Action: " + mStatsAction
+ ", Client: " + mStatsClient
+ ", AcquiredInfo: " + acquiredInfo
@@ -165,14 +177,14 @@ public class BiometricLogger {
return;
}
- mSink.acquired(mStatsModality, mStatsAction, mStatsClient,
+ mSink.acquired(operationContext, mStatsModality, mStatsAction, mStatsClient,
Utils.isDebugEnabled(context, targetUserId),
- acquiredInfo, vendorCode, isCrypto, targetUserId);
+ acquiredInfo, vendorCode, targetUserId);
}
/** Log an error during an operation. */
- public void logOnError(Context context,
- int error, int vendorCode, boolean isCrypto, int targetUserId) {
+ public void logOnError(Context context, OperationContext operationContext,
+ int error, int vendorCode, int targetUserId) {
if (!mShouldLogMetrics) {
return;
}
@@ -183,7 +195,7 @@ public class BiometricLogger {
if (DEBUG) {
Slog.v(TAG, "Error! Modality: " + mStatsModality
+ ", User: " + targetUserId
- + ", IsCrypto: " + isCrypto
+ + ", IsCrypto: " + operationContext.isCrypto
+ ", Action: " + mStatsAction
+ ", Client: " + mStatsClient
+ ", Error: " + error
@@ -197,14 +209,14 @@ public class BiometricLogger {
return;
}
- mSink.error(mStatsModality, mStatsAction, mStatsClient,
+ mSink.error(operationContext, mStatsModality, mStatsAction, mStatsClient,
Utils.isDebugEnabled(context, targetUserId), latency,
- error, vendorCode, isCrypto, targetUserId);
+ error, vendorCode, targetUserId);
}
/** Log authentication attempt. */
- public void logOnAuthenticated(Context context,
- boolean authenticated, boolean requireConfirmation, boolean isCrypto,
+ public void logOnAuthenticated(Context context, OperationContext operationContext,
+ boolean authenticated, boolean requireConfirmation,
int targetUserId, boolean isBiometricPrompt) {
if (!mShouldLogMetrics) {
return;
@@ -230,7 +242,7 @@ public class BiometricLogger {
if (DEBUG) {
Slog.v(TAG, "Authenticated! Modality: " + mStatsModality
+ ", User: " + targetUserId
- + ", IsCrypto: " + isCrypto
+ + ", IsCrypto: " + operationContext.isCrypto
+ ", Client: " + mStatsClient
+ ", RequireConfirmation: " + requireConfirmation
+ ", State: " + authState
@@ -244,10 +256,9 @@ public class BiometricLogger {
return;
}
- mSink.authenticate(mStatsModality, mStatsAction, mStatsClient,
+ mSink.authenticate(operationContext, mStatsModality, mStatsAction, mStatsClient,
Utils.isDebugEnabled(context, targetUserId),
- latency, authenticated, authState, requireConfirmation, isCrypto,
- targetUserId, isBiometricPrompt, mLastAmbientLux);
+ latency, authState, requireConfirmation, targetUserId, mLastAmbientLux);
}
/** Log enrollment outcome. */
diff --git a/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java b/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java
index 8b8103e6e2c9..0f0032b72b0a 100644
--- a/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java
@@ -29,6 +29,9 @@ import android.os.VibrationEffect;
import android.os.Vibrator;
import android.util.Slog;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
+
import java.util.function.Supplier;
/**
@@ -57,9 +60,9 @@ public abstract class AcquisitionClient<T> extends HalClientMonitor<T> implement
public AcquisitionClient(@NonNull Context context, @NonNull Supplier<T> lazyDaemon,
@NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int userId,
@NonNull String owner, int cookie, int sensorId, boolean shouldVibrate,
- int statsModality, int statsAction, int statsClient) {
- super(context, lazyDaemon, token, listener, userId, owner, cookie, sensorId, statsModality,
- statsAction, statsClient);
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext) {
+ super(context, lazyDaemon, token, listener, userId, owner, cookie, sensorId,
+ logger, biometricContext);
mPowerManager = context.getSystemService(PowerManager.class);
mShouldVibrate = shouldVibrate;
}
@@ -107,8 +110,8 @@ public abstract class AcquisitionClient<T> extends HalClientMonitor<T> implement
// that do not handle lockout under the HAL. In these cases, ensure that the framework only
// sends errors once per ClientMonitor.
if (mShouldSendErrorToClient) {
- getLogger().logOnError(getContext(), errorCode, vendorCode,
- isCryptoOperation(), getTargetUserId());
+ getLogger().logOnError(getContext(), getOperationContext(),
+ errorCode, vendorCode, getTargetUserId());
try {
if (getListener() != null) {
mShouldSendErrorToClient = false;
@@ -166,8 +169,8 @@ public abstract class AcquisitionClient<T> extends HalClientMonitor<T> implement
protected final void onAcquiredInternal(int acquiredInfo, int vendorCode,
boolean shouldSend) {
- getLogger().logOnAcquired(getContext(), acquiredInfo, vendorCode,
- isCryptoOperation(), getTargetUserId());
+ getLogger().logOnAcquired(getContext(), getOperationContext(),
+ acquiredInfo, vendorCode, getTargetUserId());
if (DEBUG) {
Slog.v(TAG, "Acquired: " + acquiredInfo + " " + vendorCode
+ ", shouldSend: " + shouldSend);
diff --git a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
index b715faf3ca8f..54b79e1f8e4a 100644
--- a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
@@ -29,7 +29,6 @@ import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricManager;
import android.hardware.biometrics.BiometricOverlayConstants;
-import android.hardware.biometrics.BiometricsProtoEnums;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.SystemClock;
@@ -39,6 +38,8 @@ import android.util.Slog;
import com.android.server.biometrics.BiometricsProto;
import com.android.server.biometrics.Utils;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import java.util.ArrayList;
import java.util.List;
@@ -93,13 +94,13 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T>
public AuthenticationClient(@NonNull Context context, @NonNull Supplier<T> lazyDaemon,
@NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener,
int targetUserId, long operationId, boolean restricted, @NonNull String owner,
- int cookie, boolean requireConfirmation, int sensorId, boolean isStrongBiometric,
- int statsModality, int statsClient, @Nullable TaskStackListener taskStackListener,
+ int cookie, boolean requireConfirmation, int sensorId,
+ @NonNull BiometricLogger biometricLogger, @NonNull BiometricContext biometricContext,
+ boolean isStrongBiometric, @Nullable TaskStackListener taskStackListener,
@NonNull LockoutTracker lockoutTracker, boolean allowBackgroundAuthentication,
boolean shouldVibrate, boolean isKeyguardBypassEnabled) {
super(context, lazyDaemon, token, listener, targetUserId, owner, cookie, sensorId,
- shouldVibrate, statsModality, BiometricsProtoEnums.ACTION_AUTHENTICATE,
- statsClient);
+ shouldVibrate, biometricLogger, biometricContext);
mIsStrongBiometric = isStrongBiometric;
mOperationId = operationId;
mRequireConfirmation = requireConfirmation;
@@ -165,8 +166,8 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T>
@Override
public void onAuthenticated(BiometricAuthenticator.Identifier identifier,
boolean authenticated, ArrayList<Byte> hardwareAuthToken) {
- getLogger().logOnAuthenticated(getContext(), authenticated, mRequireConfirmation,
- isCryptoOperation(), getTargetUserId(), isBiometricPrompt());
+ getLogger().logOnAuthenticated(getContext(), getOperationContext(),
+ authenticated, mRequireConfirmation, getTargetUserId(), isBiometricPrompt());
final ClientMonitorCallbackConverter listener = getListener();
diff --git a/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java b/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java
index e1f7e2ab5461..1b2e606117e7 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java
@@ -21,12 +21,12 @@ import static com.android.internal.annotations.VisibleForTesting.Visibility;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
-import android.hardware.biometrics.BiometricsProtoEnums;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.log.BiometricLogger;
import java.util.NoSuchElementException;
@@ -50,6 +50,7 @@ public abstract class BaseClientMonitor implements IBinder.DeathRecipient {
@NonNull private final String mOwner;
private final int mSensorId; // sensorId as configured by the framework
@NonNull private final BiometricLogger mLogger;
+ @NonNull private final BiometricContext mBiometricContext;
@Nullable private IBinder mToken;
private long mRequestId;
@@ -82,22 +83,13 @@ public abstract class BaseClientMonitor implements IBinder.DeathRecipient {
* @param owner name of the client that owns this
* @param cookie BiometricPrompt authentication cookie (to be moved into a subclass soon)
* @param sensorId ID of the sensor that the operation should be requested of
- * @param statsModality One of {@link BiometricsProtoEnums} MODALITY_* constants
- * @param statsAction One of {@link BiometricsProtoEnums} ACTION_* constants
- * @param statsClient One of {@link BiometricsProtoEnums} CLIENT_* constants
+ * @param logger framework stats logger
+ * @param biometricContext system context metadata
*/
public BaseClientMonitor(@NonNull Context context,
@Nullable IBinder token, @Nullable ClientMonitorCallbackConverter listener, int userId,
- @NonNull String owner, int cookie, int sensorId, int statsModality, int statsAction,
- int statsClient) {
- this(context, token, listener, userId, owner, cookie, sensorId,
- new BiometricLogger(context, statsModality, statsAction, statsClient));
- }
-
- @VisibleForTesting
- BaseClientMonitor(@NonNull Context context,
- @Nullable IBinder token, @Nullable ClientMonitorCallbackConverter listener, int userId,
- @NonNull String owner, int cookie, int sensorId, @NonNull BiometricLogger logger) {
+ @NonNull String owner, int cookie, int sensorId,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext) {
mSequentialId = sCount++;
mContext = context;
mToken = token;
@@ -108,6 +100,7 @@ public abstract class BaseClientMonitor implements IBinder.DeathRecipient {
mCookie = cookie;
mSensorId = sensorId;
mLogger = logger;
+ mBiometricContext = biometricContext;
try {
if (token != null) {
@@ -207,20 +200,29 @@ public abstract class BaseClientMonitor implements IBinder.DeathRecipient {
return false;
}
+ /** System context that may change during operations. */
+ @NonNull
+ protected BiometricContext getBiometricContext() {
+ return mBiometricContext;
+ }
+
/** Logger for this client */
@NonNull
public BiometricLogger getLogger() {
return mLogger;
}
+ @NonNull
public final Context getContext() {
return mContext;
}
+ @NonNull
public final String getOwnerString() {
return mOwner;
}
+ @Nullable
public final ClientMonitorCallbackConverter getListener() {
return mListener;
}
@@ -229,6 +231,7 @@ public abstract class BaseClientMonitor implements IBinder.DeathRecipient {
return mTargetUserId;
}
+ @Nullable
public final IBinder getToken() {
return mToken;
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java
index 74f4931cf2aa..483ce75eae98 100644
--- a/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java
@@ -20,13 +20,14 @@ import android.annotation.NonNull;
import android.content.Context;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricOverlayConstants;
-import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.fingerprint.FingerprintManager;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
import com.android.server.biometrics.BiometricsProto;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import java.util.Arrays;
import java.util.function.Supplier;
@@ -53,10 +54,10 @@ public abstract class EnrollClient<T> extends AcquisitionClient<T> implements En
public EnrollClient(@NonNull Context context, @NonNull Supplier<T> lazyDaemon,
@NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int userId,
@NonNull byte[] hardwareAuthToken, @NonNull String owner, @NonNull BiometricUtils utils,
- int timeoutSec, int statsModality, int sensorId, boolean shouldVibrate) {
+ int timeoutSec, int sensorId, boolean shouldVibrate,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext) {
super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId,
- shouldVibrate, statsModality, BiometricsProtoEnums.ACTION_ENROLL,
- BiometricsProtoEnums.CLIENT_UNKNOWN);
+ shouldVibrate, logger, biometricContext);
mBiometricUtils = utils;
mHardwareAuthToken = Arrays.copyOf(hardwareAuthToken, hardwareAuthToken.length);
mTimeoutSec = timeoutSec;
diff --git a/services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java
index 9689418b1f1a..2adf0cb3bab4 100644
--- a/services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java
@@ -18,12 +18,13 @@ package com.android.server.biometrics.sensors;
import android.annotation.NonNull;
import android.content.Context;
-import android.hardware.biometrics.BiometricsProtoEnums;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
import com.android.server.biometrics.BiometricsProto;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import java.util.function.Supplier;
@@ -33,10 +34,10 @@ public abstract class GenerateChallengeClient<T> extends HalClientMonitor<T> {
public GenerateChallengeClient(@NonNull Context context, @NonNull Supplier<T> lazyDaemon,
@NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener,
- int userId, @NonNull String owner, int sensorId) {
+ int userId, @NonNull String owner, int sensorId,
+ @NonNull BiometricLogger biometricLogger, @NonNull BiometricContext biometricContext) {
super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId,
- BiometricsProtoEnums.MODALITY_UNKNOWN, BiometricsProtoEnums.ACTION_UNKNOWN,
- BiometricsProtoEnums.CLIENT_UNKNOWN);
+ biometricLogger, biometricContext);
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/HalClientMonitor.java b/services/core/java/com/android/server/biometrics/sensors/HalClientMonitor.java
index 66a1c6e876ab..a6e89115400d 100644
--- a/services/core/java/com/android/server/biometrics/sensors/HalClientMonitor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/HalClientMonitor.java
@@ -19,9 +19,12 @@ package com.android.server.biometrics.sensors;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
-import android.hardware.biometrics.BiometricsProtoEnums;
+import android.hardware.biometrics.common.OperationContext;
import android.os.IBinder;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
+
import java.util.function.Supplier;
/**
@@ -33,6 +36,9 @@ public abstract class HalClientMonitor<T> extends BaseClientMonitor {
@NonNull
protected final Supplier<T> mLazyDaemon;
+ @NonNull
+ private final OperationContext mOperationContext = new OperationContext();
+
/**
* @param context system_server context
* @param lazyDaemon pointer for lazy retrieval of the HAL
@@ -42,16 +48,15 @@ public abstract class HalClientMonitor<T> extends BaseClientMonitor {
* @param owner name of the client that owns this
* @param cookie BiometricPrompt authentication cookie (to be moved into a subclass soon)
* @param sensorId ID of the sensor that the operation should be requested of
- * @param statsModality One of {@link BiometricsProtoEnums} MODALITY_* constants
- * @param statsAction One of {@link BiometricsProtoEnums} ACTION_* constants
- * @param statsClient One of {@link BiometricsProtoEnums} CLIENT_* constants
+ * @param biometricLogger framework stats logger
+ * @param biometricContext system context metadata
*/
public HalClientMonitor(@NonNull Context context, @NonNull Supplier<T> lazyDaemon,
@Nullable IBinder token, @Nullable ClientMonitorCallbackConverter listener, int userId,
- @NonNull String owner, int cookie, int sensorId, int statsModality, int statsAction,
- int statsClient) {
- super(context, token, listener, userId, owner, cookie, sensorId, statsModality,
- statsAction, statsClient);
+ @NonNull String owner, int cookie, int sensorId,
+ @NonNull BiometricLogger biometricLogger, @NonNull BiometricContext biometricContext) {
+ super(context, token, listener, userId, owner, cookie, sensorId,
+ biometricLogger, biometricContext);
mLazyDaemon = lazyDaemon;
}
@@ -71,4 +76,29 @@ public abstract class HalClientMonitor<T> extends BaseClientMonitor {
* {@link #start(ClientMonitorCallback)}.
*/
public abstract void unableToStart();
+
+ @Override
+ public void destroy() {
+ super.destroy();
+
+ // subclasses should do this earlier in most cases, but ensure it happens now
+ unsubscribeBiometricContext();
+ }
+
+ protected OperationContext getOperationContext() {
+ return getBiometricContext().updateContext(mOperationContext, isCryptoOperation());
+ }
+
+ protected ClientMonitorCallback getBiometricContextUnsubscriber() {
+ return new ClientMonitorCallback() {
+ @Override
+ public void onClientFinished(@NonNull BaseClientMonitor monitor, boolean success) {
+ unsubscribeBiometricContext();
+ }
+ };
+ }
+
+ protected void unsubscribeBiometricContext() {
+ getBiometricContext().unsubscribe(mOperationContext);
+ }
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java b/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java
index 0e6d11ec5182..57ea812dbb3a 100644
--- a/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java
@@ -19,11 +19,12 @@ package com.android.server.biometrics.sensors;
import android.annotation.NonNull;
import android.content.Context;
import android.hardware.biometrics.BiometricAuthenticator;
-import android.hardware.biometrics.BiometricsProtoEnums;
import android.os.IBinder;
import android.util.Slog;
import com.android.server.biometrics.BiometricsProto;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import java.util.ArrayList;
import java.util.List;
@@ -101,19 +102,22 @@ public abstract class InternalCleanupClient<S extends BiometricAuthenticator.Ide
protected abstract InternalEnumerateClient<T> getEnumerateClient(Context context,
Supplier<T> lazyDaemon, IBinder token, int userId, String owner,
- List<S> enrolledList, BiometricUtils<S> utils, int sensorId);
+ List<S> enrolledList, BiometricUtils<S> utils, int sensorId,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext);
protected abstract RemovalClient<S, T> getRemovalClient(Context context,
Supplier<T> lazyDaemon, IBinder token, int biometricId, int userId, String owner,
- BiometricUtils<S> utils, int sensorId, Map<Integer, Long> authenticatorIds);
+ BiometricUtils<S> utils, int sensorId,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
+ Map<Integer, Long> authenticatorIds);
protected InternalCleanupClient(@NonNull Context context, @NonNull Supplier<T> lazyDaemon,
- int userId, @NonNull String owner, int sensorId, int statsModality,
+ int userId, @NonNull String owner, int sensorId,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
@NonNull List<S> enrolledList, @NonNull BiometricUtils<S> utils,
@NonNull Map<Integer, Long> authenticatorIds) {
super(context, lazyDaemon, null /* token */, null /* ClientMonitorCallbackConverter */,
- userId, owner, 0 /* cookie */, sensorId, statsModality,
- BiometricsProtoEnums.ACTION_ENUMERATE, BiometricsProtoEnums.CLIENT_UNKNOWN);
+ userId, owner, 0 /* cookie */, sensorId, logger, biometricContext);
mBiometricUtils = utils;
mAuthenticatorIds = authenticatorIds;
mEnrolledList = enrolledList;
@@ -127,7 +131,8 @@ public abstract class InternalCleanupClient<S extends BiometricAuthenticator.Ide
mUnknownHALTemplates.remove(template);
mCurrentTask = getRemovalClient(getContext(), mLazyDaemon, getToken(),
template.mIdentifier.getBiometricId(), template.mUserId,
- getContext().getPackageName(), mBiometricUtils, getSensorId(), mAuthenticatorIds);
+ getContext().getPackageName(), mBiometricUtils, getSensorId(),
+ getLogger(), getBiometricContext(), mAuthenticatorIds);
getLogger().logUnknownEnrollmentInHal();
@@ -145,7 +150,8 @@ public abstract class InternalCleanupClient<S extends BiometricAuthenticator.Ide
// Start enumeration. Removal will start if necessary, when enumeration is completed.
mCurrentTask = getEnumerateClient(getContext(), mLazyDaemon, getToken(), getTargetUserId(),
- getOwnerString(), mEnrolledList, mBiometricUtils, getSensorId());
+ getOwnerString(), mEnrolledList, mBiometricUtils, getSensorId(), getLogger(),
+ getBiometricContext());
Slog.d(TAG, "Starting enumerate: " + mCurrentTask);
mCurrentTask.start(mEnumerateCallback);
diff --git a/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java b/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java
index 5f97f3711a60..7f8f38f3e9d2 100644
--- a/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java
@@ -19,11 +19,12 @@ package com.android.server.biometrics.sensors;
import android.annotation.NonNull;
import android.content.Context;
import android.hardware.biometrics.BiometricAuthenticator;
-import android.hardware.biometrics.BiometricsProtoEnums;
import android.os.IBinder;
import android.util.Slog;
import com.android.server.biometrics.BiometricsProto;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import java.util.ArrayList;
import java.util.List;
@@ -47,12 +48,14 @@ public abstract class InternalEnumerateClient<T> extends HalClientMonitor<T>
protected InternalEnumerateClient(@NonNull Context context, @NonNull Supplier<T> lazyDaemon,
@NonNull IBinder token, int userId, @NonNull String owner,
@NonNull List<? extends BiometricAuthenticator.Identifier> enrolledList,
- @NonNull BiometricUtils utils, int sensorId, int statsModality) {
+ @NonNull BiometricUtils utils, int sensorId,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext) {
// Internal enumerate does not need to send results to anyone. Cleanup (enumerate + remove)
// is all done internally.
super(context, lazyDaemon, token, null /* ClientMonitorCallbackConverter */, userId, owner,
- 0 /* cookie */, sensorId, statsModality, BiometricsProtoEnums.ACTION_ENUMERATE,
- BiometricsProtoEnums.CLIENT_UNKNOWN);
+ 0 /* cookie */, sensorId, logger, biometricContext);
+ //, BiometricsProtoEnums.ACTION_ENUMERATE,
+ // BiometricsProtoEnums.CLIENT_UNKNOWN);
mEnrolledList = enrolledList;
mUtils = utils;
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/InvalidationClient.java b/services/core/java/com/android/server/biometrics/sensors/InvalidationClient.java
index 697d77cfbee6..d5aa5e2c9db6 100644
--- a/services/core/java/com/android/server/biometrics/sensors/InvalidationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/InvalidationClient.java
@@ -19,12 +19,13 @@ package com.android.server.biometrics.sensors;
import android.annotation.NonNull;
import android.content.Context;
import android.hardware.biometrics.BiometricAuthenticator;
-import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.IInvalidationCallback;
import android.os.RemoteException;
import android.util.Slog;
import com.android.server.biometrics.BiometricsProto;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import java.util.Map;
import java.util.function.Supplier;
@@ -42,12 +43,13 @@ public abstract class InvalidationClient<S extends BiometricAuthenticator.Identi
@NonNull private final IInvalidationCallback mInvalidationCallback;
public InvalidationClient(@NonNull Context context, @NonNull Supplier<T> lazyDaemon,
- int userId, int sensorId, @NonNull Map<Integer, Long> authenticatorIds,
+ int userId, int sensorId,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
+ @NonNull Map<Integer, Long> authenticatorIds,
@NonNull IInvalidationCallback callback) {
super(context, lazyDaemon, null /* token */, null /* listener */, userId,
context.getOpPackageName(), 0 /* cookie */, sensorId,
- BiometricsProtoEnums.MODALITY_UNKNOWN, BiometricsProtoEnums.ACTION_UNKNOWN,
- BiometricsProtoEnums.CLIENT_UNKNOWN);
+ logger, biometricContext);
mAuthenticatorIds = authenticatorIds;
mInvalidationCallback = callback;
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/InvalidationRequesterClient.java b/services/core/java/com/android/server/biometrics/sensors/InvalidationRequesterClient.java
index b2661a28012d..1097bb7da684 100644
--- a/services/core/java/com/android/server/biometrics/sensors/InvalidationRequesterClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/InvalidationRequesterClient.java
@@ -20,10 +20,11 @@ import android.annotation.NonNull;
import android.content.Context;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricManager;
-import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.IInvalidationCallback;
import com.android.server.biometrics.BiometricsProto;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
/**
* ClientMonitor subclass responsible for coordination of authenticatorId invalidation of other
@@ -74,11 +75,10 @@ public class InvalidationRequesterClient<S extends BiometricAuthenticator.Identi
};
public InvalidationRequesterClient(@NonNull Context context, int userId, int sensorId,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
@NonNull BiometricUtils<S> utils) {
super(context, null /* token */, null /* listener */, userId,
- context.getOpPackageName(), 0 /* cookie */, sensorId,
- BiometricsProtoEnums.MODALITY_UNKNOWN, BiometricsProtoEnums.ACTION_UNKNOWN,
- BiometricsProtoEnums.CLIENT_UNKNOWN);
+ context.getOpPackageName(), 0 /* cookie */, sensorId, logger, biometricContext);
mBiometricManager = context.getSystemService(BiometricManager.class);
mUtils = utils;
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java b/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java
index a0cef94fcf48..07ce841a7cac 100644
--- a/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java
@@ -19,12 +19,13 @@ package com.android.server.biometrics.sensors;
import android.annotation.NonNull;
import android.content.Context;
import android.hardware.biometrics.BiometricAuthenticator;
-import android.hardware.biometrics.BiometricsProtoEnums;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
import com.android.server.biometrics.BiometricsProto;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import java.util.Map;
import java.util.function.Supplier;
@@ -44,10 +45,12 @@ public abstract class RemovalClient<S extends BiometricAuthenticator.Identifier,
public RemovalClient(@NonNull Context context, @NonNull Supplier<T> lazyDaemon,
@NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener,
int userId, @NonNull String owner, @NonNull BiometricUtils<S> utils, int sensorId,
- @NonNull Map<Integer, Long> authenticatorIds, int statsModality) {
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
+ @NonNull Map<Integer, Long> authenticatorIds) {
super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId,
- statsModality, BiometricsProtoEnums.ACTION_REMOVE,
- BiometricsProtoEnums.CLIENT_UNKNOWN);
+ logger, biometricContext);
+ //, BiometricsProtoEnums.ACTION_REMOVE,
+ // BiometricsProtoEnums.CLIENT_UNKNOWN);
mBiometricUtils = utils;
mAuthenticatorIds = authenticatorIds;
mHasEnrollmentsBeforeStarting = !utils.getBiometricsForUser(context, userId).isEmpty();
diff --git a/services/core/java/com/android/server/biometrics/sensors/RevokeChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/RevokeChallengeClient.java
index 7d8386337ece..88f4da261d62 100644
--- a/services/core/java/com/android/server/biometrics/sensors/RevokeChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/RevokeChallengeClient.java
@@ -18,20 +18,21 @@ package com.android.server.biometrics.sensors;
import android.annotation.NonNull;
import android.content.Context;
-import android.hardware.biometrics.BiometricsProtoEnums;
import android.os.IBinder;
import com.android.server.biometrics.BiometricsProto;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import java.util.function.Supplier;
public abstract class RevokeChallengeClient<T> extends HalClientMonitor<T> {
public RevokeChallengeClient(@NonNull Context context, @NonNull Supplier<T> lazyDaemon,
- @NonNull IBinder token, int userId, @NonNull String owner, int sensorId) {
+ @NonNull IBinder token, int userId, @NonNull String owner, int sensorId,
+ @NonNull BiometricLogger biometricLogger, @NonNull BiometricContext biometricContext) {
super(context, lazyDaemon, token, null /* listener */, userId, owner,
- 0 /* cookie */, sensorId, BiometricsProtoEnums.MODALITY_UNKNOWN,
- BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN);
+ 0 /* cookie */, sensorId, biometricLogger, biometricContext);
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/StartUserClient.java b/services/core/java/com/android/server/biometrics/sensors/StartUserClient.java
index 1bc3248cd0e7..21c9f64eea5b 100644
--- a/services/core/java/com/android/server/biometrics/sensors/StartUserClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/StartUserClient.java
@@ -19,11 +19,12 @@ package com.android.server.biometrics.sensors;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
-import android.hardware.biometrics.BiometricsProtoEnums;
import android.os.IBinder;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.biometrics.BiometricsProto;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import java.util.function.Supplier;
@@ -47,10 +48,10 @@ public abstract class StartUserClient<T, U> extends HalClientMonitor<T> {
public StartUserClient(@NonNull Context context, @NonNull Supplier<T> lazyDaemon,
@Nullable IBinder token, int userId, int sensorId,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
@NonNull UserStartedCallback<U> callback) {
super(context, lazyDaemon, token, null /* listener */, userId, context.getOpPackageName(),
- 0 /* cookie */, sensorId, BiometricsProtoEnums.MODALITY_UNKNOWN,
- BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN);
+ 0 /* cookie */, sensorId, logger, biometricContext);
mUserStartedCallback = callback;
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/StopUserClient.java b/services/core/java/com/android/server/biometrics/sensors/StopUserClient.java
index 3eafbb8ea9d7..e8654dc059a4 100644
--- a/services/core/java/com/android/server/biometrics/sensors/StopUserClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/StopUserClient.java
@@ -19,11 +19,12 @@ package com.android.server.biometrics.sensors;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
-import android.hardware.biometrics.BiometricsProtoEnums;
import android.os.IBinder;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.biometrics.BiometricsProto;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import java.util.function.Supplier;
@@ -47,10 +48,10 @@ public abstract class StopUserClient<T> extends HalClientMonitor<T> {
public StopUserClient(@NonNull Context context, @NonNull Supplier<T> lazyDaemon,
@Nullable IBinder token, int userId, int sensorId,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
@NonNull UserStoppedCallback callback) {
super(context, lazyDaemon, token, null /* listener */, userId, context.getOpPackageName(),
- 0 /* cookie */, sensorId, BiometricsProtoEnums.MODALITY_UNKNOWN,
- BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN);
+ 0 /* cookie */, sensorId, logger, biometricContext);
mUserStoppedCallback = callback;
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
index 039b08e805c1..2e820574b435 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
@@ -57,6 +57,7 @@ import com.android.internal.widget.LockPatternUtils;
import com.android.server.ServiceThread;
import com.android.server.SystemService;
import com.android.server.biometrics.Utils;
+import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.LockoutResetDispatcher;
import com.android.server.biometrics.sensors.LockoutTracker;
@@ -645,7 +646,7 @@ public class FaceService extends SystemService {
try {
final SensorProps[] props = face.getSensorProps();
final FaceProvider provider = new FaceProvider(getContext(), props, instance,
- mLockoutResetDispatcher);
+ mLockoutResetDispatcher, BiometricContext.getInstance(getContext()));
mServiceProviders.add(provider);
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception in getSensorProps: " + fqName);
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/AidlSession.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/AidlSession.java
index 006667ac659f..29eee6b5bb06 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/AidlSession.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/AidlSession.java
@@ -16,11 +16,11 @@
package com.android.server.biometrics.sensors.face.aidl;
+import static com.android.server.biometrics.sensors.face.aidl.Sensor.HalSessionCallback;
+
import android.annotation.NonNull;
import android.hardware.biometrics.face.ISession;
-import static com.android.server.biometrics.sensors.face.aidl.Sensor.HalSessionCallback;
-
/**
* A holder for an AIDL {@link ISession} with additional metadata about the current user
* and the backend.
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
index c4e050215134..7765ab3b8e53 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
@@ -25,10 +25,7 @@ import android.hardware.SensorPrivacyManager;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricFaceConstants;
-import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.common.ICancellationSignal;
-import android.hardware.biometrics.common.OperationContext;
-import android.hardware.biometrics.common.OperationReason;
import android.hardware.biometrics.face.IFace;
import android.hardware.face.FaceAuthenticationFrame;
import android.hardware.face.FaceManager;
@@ -37,7 +34,10 @@ import android.os.RemoteException;
import android.util.Slog;
import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.server.biometrics.Utils;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.AuthenticationClient;
import com.android.server.biometrics.sensors.BiometricNotificationUtils;
import com.android.server.biometrics.sensors.ClientMonitorCallback;
@@ -76,19 +76,36 @@ class FaceAuthenticationClient extends AuthenticationClient<AidlSession>
@NonNull IBinder token, long requestId,
@NonNull ClientMonitorCallbackConverter listener, int targetUserId, long operationId,
boolean restricted, String owner, int cookie, boolean requireConfirmation, int sensorId,
- boolean isStrongBiometric, int statsClient, @NonNull UsageStats usageStats,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
+ boolean isStrongBiometric, @NonNull UsageStats usageStats,
@NonNull LockoutCache lockoutCache, boolean allowBackgroundAuthentication,
boolean isKeyguardBypassEnabled) {
+ this(context, lazyDaemon, token, requestId, listener, targetUserId, operationId,
+ restricted, owner, cookie, requireConfirmation, sensorId, logger, biometricContext,
+ isStrongBiometric, usageStats, lockoutCache, allowBackgroundAuthentication,
+ isKeyguardBypassEnabled, context.getSystemService(SensorPrivacyManager.class));
+ }
+
+ @VisibleForTesting
+ FaceAuthenticationClient(@NonNull Context context,
+ @NonNull Supplier<AidlSession> lazyDaemon,
+ @NonNull IBinder token, long requestId,
+ @NonNull ClientMonitorCallbackConverter listener, int targetUserId, long operationId,
+ boolean restricted, String owner, int cookie, boolean requireConfirmation, int sensorId,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
+ boolean isStrongBiometric, @NonNull UsageStats usageStats,
+ @NonNull LockoutCache lockoutCache, boolean allowBackgroundAuthentication,
+ boolean isKeyguardBypassEnabled, SensorPrivacyManager sensorPrivacyManager) {
super(context, lazyDaemon, token, listener, targetUserId, operationId, restricted,
- owner, cookie, requireConfirmation, sensorId, isStrongBiometric,
- BiometricsProtoEnums.MODALITY_FACE, statsClient, null /* taskStackListener */,
- lockoutCache, allowBackgroundAuthentication, true /* shouldVibrate */,
+ owner, cookie, requireConfirmation, sensorId, logger, biometricContext,
+ isStrongBiometric, null /* taskStackListener */, lockoutCache,
+ allowBackgroundAuthentication, true /* shouldVibrate */,
isKeyguardBypassEnabled);
setRequestId(requestId);
mUsageStats = usageStats;
mLockoutCache = lockoutCache;
mNotificationManager = context.getSystemService(NotificationManager.class);
- mSensorPrivacyManager = context.getSystemService(SensorPrivacyManager.class);
+ mSensorPrivacyManager = sensorPrivacyManager;
final Resources resources = getContext().getResources();
mBiometricPromptIgnoreList = resources.getIntArray(
@@ -138,13 +155,8 @@ class FaceAuthenticationClient extends AuthenticationClient<AidlSession>
final AidlSession session = getFreshDaemon();
if (session.hasContextMethods()) {
- final OperationContext context = new OperationContext();
- // TODO: add reason, id, and isAoD
- context.id = 0;
- context.reason = OperationReason.UNKNOWN;
- context.isAoD = false;
- context.isCrypto = isCryptoOperation();
- return session.getSession().authenticateWithContext(mOperationId, context);
+ return session.getSession().authenticateWithContext(
+ mOperationId, getOperationContext());
} else {
return session.getSession().authenticate(mOperationId);
}
@@ -263,8 +275,8 @@ class FaceAuthenticationClient extends AuthenticationClient<AidlSession>
mLockoutCache.setLockoutModeForUser(getTargetUserId(), LockoutTracker.LOCKOUT_TIMED);
// Lockout metrics are logged as an error code.
final int error = BiometricFaceConstants.FACE_ERROR_LOCKOUT;
- getLogger().logOnError(getContext(), error, 0 /* vendorCode */,
- isCryptoOperation(), getTargetUserId());
+ getLogger().logOnError(getContext(), getOperationContext(),
+ error, 0 /* vendorCode */, getTargetUserId());
try {
getListener().onError(getSensorId(), getCookie(), error, 0 /* vendorCode */);
@@ -279,8 +291,8 @@ class FaceAuthenticationClient extends AuthenticationClient<AidlSession>
mLockoutCache.setLockoutModeForUser(getTargetUserId(), LockoutTracker.LOCKOUT_PERMANENT);
// Lockout metrics are logged as an error code.
final int error = BiometricFaceConstants.FACE_ERROR_LOCKOUT_PERMANENT;
- getLogger().logOnError(getContext(), error, 0 /* vendorCode */,
- isCryptoOperation(), getTargetUserId());
+ getLogger().logOnError(getContext(), getOperationContext(),
+ error, 0 /* vendorCode */, getTargetUserId());
try {
getListener().onError(getSensorId(), getCookie(), error, 0 /* vendorCode */);
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceDetectClient.java
index 3f3db4342a7a..efedcf815228 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceDetectClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceDetectClient.java
@@ -21,15 +21,15 @@ import android.annotation.Nullable;
import android.content.Context;
import android.hardware.SensorPrivacyManager;
import android.hardware.biometrics.BiometricConstants;
-import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.common.ICancellationSignal;
-import android.hardware.biometrics.common.OperationContext;
-import android.hardware.biometrics.common.OperationReason;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.server.biometrics.BiometricsProto;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.AcquisitionClient;
import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
@@ -52,13 +52,26 @@ public class FaceDetectClient extends AcquisitionClient<AidlSession> implements
FaceDetectClient(@NonNull Context context, @NonNull Supplier<AidlSession> lazyDaemon,
@NonNull IBinder token, long requestId,
@NonNull ClientMonitorCallbackConverter listener, int userId,
- @NonNull String owner, int sensorId, boolean isStrongBiometric, int statsClient) {
+ @NonNull String owner, int sensorId,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
+ boolean isStrongBiometric) {
+ this(context, lazyDaemon, token, requestId, listener, userId, owner, sensorId,
+ logger, biometricContext, isStrongBiometric,
+ context.getSystemService(SensorPrivacyManager.class));
+ }
+
+ @VisibleForTesting
+ FaceDetectClient(@NonNull Context context, @NonNull Supplier<AidlSession> lazyDaemon,
+ @NonNull IBinder token, long requestId,
+ @NonNull ClientMonitorCallbackConverter listener, int userId,
+ @NonNull String owner, int sensorId,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
+ boolean isStrongBiometric, SensorPrivacyManager sensorPrivacyManager) {
super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId,
- true /* shouldVibrate */, BiometricsProtoEnums.MODALITY_FACE,
- BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient);
+ true /* shouldVibrate */, logger, biometricContext);
setRequestId(requestId);
mIsStrongBiometric = isStrongBiometric;
- mSensorPrivacyManager = context.getSystemService(SensorPrivacyManager.class);
+ mSensorPrivacyManager = sensorPrivacyManager;
}
@Override
@@ -101,13 +114,7 @@ public class FaceDetectClient extends AcquisitionClient<AidlSession> implements
final AidlSession session = getFreshDaemon();
if (session.hasContextMethods()) {
- final OperationContext context = new OperationContext();
- // TODO: add reason, id, and isAoD
- context.id = 0;
- context.reason = OperationReason.UNKNOWN;
- context.isAoD = false;
- context.isCrypto = isCryptoOperation();
- return session.getSession().detectInteractionWithContext(context);
+ return session.getSession().detectInteractionWithContext(getOperationContext());
} else {
return session.getSession().detectInteraction();
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java
index 8dc53b6346a4..da7853654ebf 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java
@@ -20,10 +20,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.hardware.biometrics.BiometricFaceConstants;
-import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.common.ICancellationSignal;
-import android.hardware.biometrics.common.OperationContext;
-import android.hardware.biometrics.common.OperationReason;
import android.hardware.biometrics.face.EnrollmentType;
import android.hardware.biometrics.face.Feature;
import android.hardware.biometrics.face.IFace;
@@ -40,6 +37,8 @@ import android.view.Surface;
import com.android.internal.R;
import com.android.server.biometrics.HardwareAuthTokenUtils;
import com.android.server.biometrics.Utils;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.BaseClientMonitor;
import com.android.server.biometrics.sensors.BiometricNotificationUtils;
import com.android.server.biometrics.sensors.BiometricUtils;
@@ -89,11 +88,11 @@ public class FaceEnrollClient extends EnrollClient<AidlSession> {
@NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int userId,
@NonNull byte[] hardwareAuthToken, @NonNull String opPackageName, long requestId,
@NonNull BiometricUtils<Face> utils, @NonNull int[] disabledFeatures, int timeoutSec,
- @Nullable Surface previewSurface, int sensorId, int maxTemplatesPerUser,
- boolean debugConsent) {
+ @Nullable Surface previewSurface, int sensorId,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
+ int maxTemplatesPerUser, boolean debugConsent) {
super(context, lazyDaemon, token, listener, userId, hardwareAuthToken, opPackageName, utils,
- timeoutSec, BiometricsProtoEnums.MODALITY_FACE, sensorId,
- false /* shouldVibrate */);
+ timeoutSec, sensorId, false /* shouldVibrate */, logger, biometricContext);
setRequestId(requestId);
mEnrollIgnoreList = getContext().getResources()
.getIntArray(R.array.config_face_acquire_enroll_ignorelist);
@@ -199,14 +198,8 @@ public class FaceEnrollClient extends EnrollClient<AidlSession> {
HardwareAuthTokenUtils.toHardwareAuthToken(mHardwareAuthToken);
if (session.hasContextMethods()) {
- final OperationContext context = new OperationContext();
- // TODO: add reason, id, and isAoD
- context.id = 0;
- context.reason = OperationReason.UNKNOWN;
- context.isAoD = false;
- context.isCrypto = isCryptoOperation();
return session.getSession().enrollWithContext(
- hat, EnrollmentType.DEFAULT, features, mHwPreviewHandle, context);
+ hat, EnrollmentType.DEFAULT, features, mHwPreviewHandle, getOperationContext());
} else {
return session.getSession().enroll(hat, EnrollmentType.DEFAULT, features,
mHwPreviewHandle);
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGenerateChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGenerateChallengeClient.java
index bdad268b9422..165c3a241043 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGenerateChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGenerateChallengeClient.java
@@ -23,6 +23,8 @@ import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.GenerateChallengeClient;
@@ -37,8 +39,10 @@ public class FaceGenerateChallengeClient extends GenerateChallengeClient<AidlSes
FaceGenerateChallengeClient(@NonNull Context context,
@NonNull Supplier<AidlSession> lazyDaemon, @NonNull IBinder token,
@NonNull ClientMonitorCallbackConverter listener, int userId, @NonNull String owner,
- int sensorId) {
- super(context, lazyDaemon, token, listener, userId, owner, sensorId);
+ int sensorId, @NonNull BiometricLogger logger,
+ @NonNull BiometricContext biometricContext) {
+ super(context, lazyDaemon, token, listener, userId, owner, sensorId, logger,
+ biometricContext);
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetAuthenticatorIdClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetAuthenticatorIdClient.java
index 2f3187bb4fa0..1f4f6127dafd 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetAuthenticatorIdClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetAuthenticatorIdClient.java
@@ -18,11 +18,12 @@ package com.android.server.biometrics.sensors.face.aidl;
import android.annotation.NonNull;
import android.content.Context;
-import android.hardware.biometrics.BiometricsProtoEnums;
import android.os.RemoteException;
import android.util.Slog;
import com.android.server.biometrics.BiometricsProto;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.HalClientMonitor;
@@ -38,10 +39,10 @@ class FaceGetAuthenticatorIdClient extends HalClientMonitor<AidlSession> {
FaceGetAuthenticatorIdClient(@NonNull Context context,
@NonNull Supplier<AidlSession> lazyDaemon,
int userId, @NonNull String opPackageName, int sensorId,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
Map<Integer, Long> authenticatorIds) {
super(context, lazyDaemon, null /* token */, null /* listener */, userId, opPackageName,
- 0 /* cookie */, sensorId, BiometricsProtoEnums.MODALITY_FACE,
- BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN);
+ 0 /* cookie */, sensorId, logger, biometricContext);
mAuthenticatorIds = authenticatorIds;
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetFeatureClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetFeatureClient.java
index 79479bebc759..ef3b345402bf 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetFeatureClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetFeatureClient.java
@@ -20,7 +20,6 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.hardware.biometrics.BiometricFaceConstants;
-import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.face.IFace;
import android.os.IBinder;
import android.os.RemoteException;
@@ -28,6 +27,8 @@ import android.provider.Settings;
import android.util.Slog;
import com.android.server.biometrics.BiometricsProto;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.ErrorConsumer;
@@ -48,10 +49,10 @@ public class FaceGetFeatureClient extends HalClientMonitor<AidlSession> implemen
FaceGetFeatureClient(@NonNull Context context, @NonNull Supplier<AidlSession> lazyDaemon,
@NonNull IBinder token, @Nullable ClientMonitorCallbackConverter listener, int userId,
- @NonNull String owner, int sensorId) {
+ @NonNull String owner, int sensorId, @NonNull BiometricLogger logger,
+ @NonNull BiometricContext biometricContext) {
super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId,
- BiometricsProtoEnums.MODALITY_UNKNOWN, BiometricsProtoEnums.ACTION_UNKNOWN,
- BiometricsProtoEnums.CLIENT_UNKNOWN);
+ logger, biometricContext);
mUserId = userId;
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInternalCleanupClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInternalCleanupClient.java
index a2b0339b282f..54f2033b363a 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInternalCleanupClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInternalCleanupClient.java
@@ -18,11 +18,12 @@ package com.android.server.biometrics.sensors.face.aidl;
import android.annotation.NonNull;
import android.content.Context;
-import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.face.IFace;
import android.hardware.face.Face;
import android.os.IBinder;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.BiometricUtils;
import com.android.server.biometrics.sensors.InternalCleanupClient;
import com.android.server.biometrics.sensors.InternalEnumerateClient;
@@ -39,29 +40,32 @@ class FaceInternalCleanupClient extends InternalCleanupClient<Face, AidlSession>
FaceInternalCleanupClient(@NonNull Context context,
@NonNull Supplier<AidlSession> lazyDaemon, int userId, @NonNull String owner,
- int sensorId, @NonNull List<Face> enrolledList, @NonNull BiometricUtils<Face> utils,
- @NonNull Map<Integer, Long> authenticatorIds) {
- super(context, lazyDaemon, userId, owner, sensorId, BiometricsProtoEnums.MODALITY_FACE,
+ int sensorId, @NonNull BiometricLogger logger,
+ @NonNull BiometricContext biometricContext, @NonNull List<Face> enrolledList,
+ @NonNull BiometricUtils<Face> utils, @NonNull Map<Integer, Long> authenticatorIds) {
+ super(context, lazyDaemon, userId, owner, sensorId, logger, biometricContext,
enrolledList, utils, authenticatorIds);
}
@Override
protected InternalEnumerateClient<AidlSession> getEnumerateClient(Context context,
Supplier<AidlSession> lazyDaemon, IBinder token, int userId, String owner,
- List<Face> enrolledList, BiometricUtils<Face> utils, int sensorId) {
+ List<Face> enrolledList, BiometricUtils<Face> utils, int sensorId,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext) {
return new FaceInternalEnumerateClient(context, lazyDaemon, token, userId, owner,
- enrolledList, utils, sensorId);
+ enrolledList, utils, sensorId, logger, biometricContext);
}
@Override
protected RemovalClient<Face, AidlSession> getRemovalClient(Context context,
Supplier<AidlSession> lazyDaemon, IBinder token,
int biometricId, int userId, String owner, BiometricUtils<Face> utils, int sensorId,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
Map<Integer, Long> authenticatorIds) {
// Internal remove does not need to send results to anyone. Cleanup (enumerate + remove)
// is all done internally.
return new FaceRemovalClient(context, lazyDaemon, token,
null /* ClientMonitorCallbackConverter */, new int[] {biometricId}, userId, owner,
- utils, sensorId, authenticatorIds);
+ utils, sensorId, logger, biometricContext, authenticatorIds);
}
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInternalEnumerateClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInternalEnumerateClient.java
index 88c9d3bd1035..d85455e0e76b 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInternalEnumerateClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInternalEnumerateClient.java
@@ -18,13 +18,14 @@ package com.android.server.biometrics.sensors.face.aidl;
import android.annotation.NonNull;
import android.content.Context;
-import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.face.IFace;
import android.hardware.face.Face;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.BiometricUtils;
import com.android.server.biometrics.sensors.InternalEnumerateClient;
@@ -40,9 +41,10 @@ class FaceInternalEnumerateClient extends InternalEnumerateClient<AidlSession> {
FaceInternalEnumerateClient(@NonNull Context context,
@NonNull Supplier<AidlSession> lazyDaemon, @NonNull IBinder token, int userId,
@NonNull String owner, @NonNull List<Face> enrolledList,
- @NonNull BiometricUtils<Face> utils, int sensorId) {
+ @NonNull BiometricUtils<Face> utils, int sensorId,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext) {
super(context, lazyDaemon, token, userId, owner, enrolledList, utils, sensorId,
- BiometricsProtoEnums.MODALITY_FACE);
+ logger, biometricContext);
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInvalidationClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInvalidationClient.java
index 04ea2cfc6eff..39d8de07f652 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInvalidationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInvalidationClient.java
@@ -23,6 +23,8 @@ import android.hardware.face.Face;
import android.os.RemoteException;
import android.util.Slog;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.InvalidationClient;
import java.util.Map;
@@ -33,8 +35,10 @@ public class FaceInvalidationClient extends InvalidationClient<Face, AidlSession
public FaceInvalidationClient(@NonNull Context context,
@NonNull Supplier<AidlSession> lazyDaemon, int userId, int sensorId,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
@NonNull Map<Integer, Long> authenticatorIds, @NonNull IInvalidationCallback callback) {
- super(context, lazyDaemon, userId, sensorId, authenticatorIds, callback);
+ super(context, lazyDaemon, userId, sensorId, logger, biometricContext,
+ authenticatorIds, callback);
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
index 9d7a5529f473..4e03ee9a618c 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
@@ -23,6 +23,7 @@ import android.app.ActivityTaskManager;
import android.app.TaskStackListener;
import android.content.Context;
import android.content.pm.UserInfo;
+import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.ComponentInfoInternal;
import android.hardware.biometrics.IInvalidationCallback;
import android.hardware.biometrics.ITestSession;
@@ -47,6 +48,8 @@ import android.view.Surface;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.biometrics.Utils;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.AuthenticationClient;
import com.android.server.biometrics.sensors.BaseClientMonitor;
import com.android.server.biometrics.sensors.ClientMonitorCallback;
@@ -87,7 +90,7 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
@NonNull private final BiometricTaskStackListener mTaskStackListener;
// for requests that do not use biometric prompt
@NonNull private final AtomicLong mRequestCounter = new AtomicLong(0);
-
+ @NonNull private final BiometricContext mBiometricContext;
@Nullable private IFace mDaemon;
private final class BiometricTaskStackListener extends TaskStackListener {
@@ -125,7 +128,8 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
public FaceProvider(@NonNull Context context, @NonNull SensorProps[] props,
@NonNull String halInstanceName,
- @NonNull LockoutResetDispatcher lockoutResetDispatcher) {
+ @NonNull LockoutResetDispatcher lockoutResetDispatcher,
+ @NonNull BiometricContext biometricContext) {
mContext = context;
mHalInstanceName = halInstanceName;
mSensors = new SparseArray<>();
@@ -134,6 +138,7 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
mLockoutResetDispatcher = lockoutResetDispatcher;
mActivityTaskManager = ActivityTaskManager.getInstance();
mTaskStackListener = new BiometricTaskStackListener();
+ mBiometricContext = biometricContext;
for (SensorProps prop : props) {
final int sensorId = prop.commonProps.sensorId;
@@ -153,7 +158,7 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
prop.supportsDetectInteraction, prop.halControlsPreview,
false /* resetLockoutRequiresChallenge */);
final Sensor sensor = new Sensor(getTag() + "/" + sensorId, this, mContext, mHandler,
- internalProp, lockoutResetDispatcher);
+ internalProp, lockoutResetDispatcher, mBiometricContext);
mSensors.put(sensorId, sensor);
Slog.d(getTag(), "Added: " + internalProp);
@@ -237,6 +242,9 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
final FaceGetAuthenticatorIdClient client = new FaceGetAuthenticatorIdClient(
mContext, mSensors.get(sensorId).getLazySession(), userId,
mContext.getOpPackageName(), sensorId,
+ createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
+ BiometricsProtoEnums.CLIENT_UNKNOWN),
+ mBiometricContext,
mSensors.get(sensorId).getAuthenticatorIds());
scheduleForSensor(sensorId, client);
@@ -247,6 +255,8 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
mHandler.post(() -> {
final InvalidationRequesterClient<Face> client =
new InvalidationRequesterClient<>(mContext, userId, sensorId,
+ BiometricLogger.ofUnknown(mContext),
+ mBiometricContext,
FaceUtils.getInstance(sensorId));
scheduleForSensor(sensorId, client);
});
@@ -285,6 +295,9 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
mHandler.post(() -> {
final FaceInvalidationClient client = new FaceInvalidationClient(mContext,
mSensors.get(sensorId).getLazySession(), userId, sensorId,
+ createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
+ BiometricsProtoEnums.CLIENT_UNKNOWN),
+ mBiometricContext,
mSensors.get(sensorId).getAuthenticatorIds(), callback);
scheduleForSensor(sensorId, client);
});
@@ -311,7 +324,10 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
mHandler.post(() -> {
final FaceGenerateChallengeClient client = new FaceGenerateChallengeClient(mContext,
mSensors.get(sensorId).getLazySession(), token,
- new ClientMonitorCallbackConverter(receiver), userId, opPackageName, sensorId);
+ new ClientMonitorCallbackConverter(receiver), userId, opPackageName, sensorId,
+ createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
+ BiometricsProtoEnums.CLIENT_UNKNOWN),
+ mBiometricContext);
scheduleForSensor(sensorId, client);
});
}
@@ -322,7 +338,9 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
mHandler.post(() -> {
final FaceRevokeChallengeClient client = new FaceRevokeChallengeClient(mContext,
mSensors.get(sensorId).getLazySession(), token, userId, opPackageName, sensorId,
- challenge);
+ createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
+ BiometricsProtoEnums.CLIENT_UNKNOWN),
+ mBiometricContext, challenge);
scheduleForSensor(sensorId, client);
});
}
@@ -340,8 +358,10 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
mSensors.get(sensorId).getLazySession(), token,
new ClientMonitorCallbackConverter(receiver), userId, hardwareAuthToken,
opPackageName, id, FaceUtils.getInstance(sensorId), disabledFeatures,
- ENROLL_TIMEOUT_SEC, previewSurface, sensorId, maxTemplatesPerUser,
- debugConsent);
+ ENROLL_TIMEOUT_SEC, previewSurface, sensorId,
+ createLogger(BiometricsProtoEnums.ACTION_ENROLL,
+ BiometricsProtoEnums.CLIENT_UNKNOWN),
+ mBiometricContext, maxTemplatesPerUser, debugConsent);
scheduleForSensor(sensorId, client, new ClientMonitorCallback() {
@Override
public void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
@@ -372,8 +392,9 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
final boolean isStrongBiometric = Utils.isStrongBiometric(sensorId);
final FaceDetectClient client = new FaceDetectClient(mContext,
mSensors.get(sensorId).getLazySession(),
- token, id, callback, userId, opPackageName,
- sensorId, isStrongBiometric, statsClient);
+ token, id, callback, userId, opPackageName, sensorId,
+ createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient),
+ mBiometricContext, isStrongBiometric);
scheduleForSensor(sensorId, client);
});
@@ -396,7 +417,9 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
final FaceAuthenticationClient client = new FaceAuthenticationClient(
mContext, mSensors.get(sensorId).getLazySession(), token, requestId, callback,
userId, operationId, restricted, opPackageName, cookie,
- false /* requireConfirmation */, sensorId, isStrongBiometric, statsClient,
+ false /* requireConfirmation */, sensorId,
+ createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient),
+ mBiometricContext, isStrongBiometric,
mUsageStats, mSensors.get(sensorId).getLockoutCache(),
allowBackgroundAuthentication, isKeyguardBypassEnabled);
scheduleForSensor(sensorId, client);
@@ -450,6 +473,9 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
mSensors.get(sensorId).getLazySession(), token,
new ClientMonitorCallbackConverter(receiver), faceIds, userId,
opPackageName, FaceUtils.getInstance(sensorId), sensorId,
+ createLogger(BiometricsProtoEnums.ACTION_REMOVE,
+ BiometricsProtoEnums.CLIENT_UNKNOWN),
+ mBiometricContext,
mSensors.get(sensorId).getAuthenticatorIds());
scheduleForSensor(sensorId, client);
});
@@ -460,7 +486,10 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
mHandler.post(() -> {
final FaceResetLockoutClient client = new FaceResetLockoutClient(
mContext, mSensors.get(sensorId).getLazySession(), userId,
- mContext.getOpPackageName(), sensorId, hardwareAuthToken,
+ mContext.getOpPackageName(), sensorId,
+ createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
+ BiometricsProtoEnums.CLIENT_UNKNOWN),
+ mBiometricContext, hardwareAuthToken,
mSensors.get(sensorId).getLockoutCache(), mLockoutResetDispatcher);
scheduleForSensor(sensorId, client);
@@ -481,7 +510,9 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
final FaceSetFeatureClient client = new FaceSetFeatureClient(mContext,
mSensors.get(sensorId).getLazySession(), token,
new ClientMonitorCallbackConverter(receiver), userId,
- mContext.getOpPackageName(), sensorId, feature, enabled, hardwareAuthToken);
+ mContext.getOpPackageName(), sensorId,
+ BiometricLogger.ofUnknown(mContext), mBiometricContext,
+ feature, enabled, hardwareAuthToken);
scheduleForSensor(sensorId, client);
});
}
@@ -498,7 +529,8 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
}
final FaceGetFeatureClient client = new FaceGetFeatureClient(mContext,
mSensors.get(sensorId).getLazySession(), token, callback, userId,
- mContext.getOpPackageName(), sensorId);
+ mContext.getOpPackageName(), sensorId, BiometricLogger.ofUnknown(mContext),
+ mBiometricContext);
scheduleForSensor(sensorId, client);
});
}
@@ -518,13 +550,21 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
final FaceInternalCleanupClient client =
new FaceInternalCleanupClient(mContext,
mSensors.get(sensorId).getLazySession(), userId,
- mContext.getOpPackageName(), sensorId, enrolledList,
+ mContext.getOpPackageName(), sensorId,
+ createLogger(BiometricsProtoEnums.ACTION_ENUMERATE,
+ BiometricsProtoEnums.CLIENT_UNKNOWN),
+ mBiometricContext, enrolledList,
FaceUtils.getInstance(sensorId),
mSensors.get(sensorId).getAuthenticatorIds());
scheduleForSensor(sensorId, client, callback);
});
}
+ private BiometricLogger createLogger(int statsAction, int statsClient) {
+ return new BiometricLogger(mContext, BiometricsProtoEnums.MODALITY_FACE,
+ statsAction, statsClient);
+ }
+
@Override
public void dumpProtoState(int sensorId, @NonNull ProtoOutputStream proto,
boolean clearSchedulerBuffer) {
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceRemovalClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceRemovalClient.java
index 130a05a861d9..0512017394af 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceRemovalClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceRemovalClient.java
@@ -18,13 +18,14 @@ package com.android.server.biometrics.sensors.face.aidl;
import android.annotation.NonNull;
import android.content.Context;
-import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.face.IFace;
import android.hardware.face.Face;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.BiometricUtils;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.RemovalClient;
@@ -44,9 +45,10 @@ class FaceRemovalClient extends RemovalClient<Face, AidlSession> {
@NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener,
int[] biometricIds, int userId, @NonNull String owner,
@NonNull BiometricUtils<Face> utils, int sensorId,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
@NonNull Map<Integer, Long> authenticatorIds) {
super(context, lazyDaemon, token, listener, userId, owner, utils, sensorId,
- authenticatorIds, BiometricsProtoEnums.MODALITY_FACE);
+ logger, biometricContext, authenticatorIds);
mBiometricIds = biometricIds;
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java
index 67bf3f5b2e4f..de0a36a32b66 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java
@@ -18,7 +18,6 @@ package com.android.server.biometrics.sensors.face.aidl;
import android.annotation.NonNull;
import android.content.Context;
-import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.face.IFace;
import android.hardware.keymaster.HardwareAuthToken;
import android.os.RemoteException;
@@ -26,6 +25,8 @@ import android.util.Slog;
import com.android.server.biometrics.BiometricsProto;
import com.android.server.biometrics.HardwareAuthTokenUtils;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.ErrorConsumer;
import com.android.server.biometrics.sensors.HalClientMonitor;
@@ -50,11 +51,11 @@ public class FaceResetLockoutClient extends HalClientMonitor<AidlSession> implem
FaceResetLockoutClient(@NonNull Context context,
@NonNull Supplier<AidlSession> lazyDaemon, int userId, String owner, int sensorId,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
@NonNull byte[] hardwareAuthToken, @NonNull LockoutCache lockoutTracker,
@NonNull LockoutResetDispatcher lockoutResetDispatcher) {
super(context, lazyDaemon, null /* token */, null /* listener */, userId, owner,
- 0 /* cookie */, sensorId, BiometricsProtoEnums.MODALITY_UNKNOWN,
- BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN);
+ 0 /* cookie */, sensorId, logger, biometricContext);
mHardwareAuthToken = HardwareAuthTokenUtils.toHardwareAuthToken(hardwareAuthToken);
mLockoutCache = lockoutTracker;
mLockoutResetDispatcher = lockoutResetDispatcher;
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceRevokeChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceRevokeChallengeClient.java
index acd2e0589ccc..8838345de4d6 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceRevokeChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceRevokeChallengeClient.java
@@ -23,6 +23,8 @@ import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.RevokeChallengeClient;
import java.util.function.Supplier;
@@ -38,8 +40,10 @@ public class FaceRevokeChallengeClient extends RevokeChallengeClient<AidlSession
FaceRevokeChallengeClient(@NonNull Context context,
@NonNull Supplier<AidlSession> lazyDaemon, @NonNull IBinder token,
- int userId, @NonNull String owner, int sensorId, long challenge) {
- super(context, lazyDaemon, token, userId, owner, sensorId);
+ int userId, @NonNull String owner, int sensorId,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
+ long challenge) {
+ super(context, lazyDaemon, token, userId, owner, sensorId, logger, biometricContext);
mChallenge = challenge;
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceSetFeatureClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceSetFeatureClient.java
index 9d535a26e12d..6c143872ff8c 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceSetFeatureClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceSetFeatureClient.java
@@ -18,7 +18,6 @@ package com.android.server.biometrics.sensors.face.aidl;
import android.annotation.NonNull;
import android.content.Context;
-import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.face.IFace;
import android.hardware.keymaster.HardwareAuthToken;
import android.os.IBinder;
@@ -27,6 +26,8 @@ import android.util.Slog;
import com.android.server.biometrics.BiometricsProto;
import com.android.server.biometrics.HardwareAuthTokenUtils;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.ErrorConsumer;
@@ -47,11 +48,11 @@ public class FaceSetFeatureClient extends HalClientMonitor<AidlSession> implemen
FaceSetFeatureClient(@NonNull Context context, @NonNull Supplier<AidlSession> lazyDaemon,
@NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int userId,
- @NonNull String owner, int sensorId, int feature, boolean enabled,
- byte[] hardwareAuthToken) {
+ @NonNull String owner, int sensorId,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
+ int feature, boolean enabled, byte[] hardwareAuthToken) {
super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId,
- BiometricsProtoEnums.MODALITY_UNKNOWN, BiometricsProtoEnums.ACTION_UNKNOWN,
- BiometricsProtoEnums.CLIENT_UNKNOWN);
+ logger, biometricContext);
mFeature = feature;
mEnabled = enabled;
mHardwareAuthToken = HardwareAuthTokenUtils.toHardwareAuthToken(hardwareAuthToken);
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceStartUserClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceStartUserClient.java
index f5a98ff5881b..61e7ab781e66 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceStartUserClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceStartUserClient.java
@@ -27,6 +27,8 @@ import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.StartUserClient;
@@ -40,9 +42,10 @@ public class FaceStartUserClient extends StartUserClient<IFace, ISession> {
public FaceStartUserClient(@NonNull Context context,
@NonNull Supplier<IFace> lazyDaemon,
@Nullable IBinder token, int userId, int sensorId,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
@NonNull ISessionCallback sessionCallback,
@NonNull UserStartedCallback<ISession> callback) {
- super(context, lazyDaemon, token, userId, sensorId, callback);
+ super(context, lazyDaemon, token, userId, sensorId, logger, biometricContext, callback);
mSessionCallback = sessionCallback;
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceStopUserClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceStopUserClient.java
index 48b4856fa4b6..0110ae991ae4 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceStopUserClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceStopUserClient.java
@@ -23,6 +23,8 @@ import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.StopUserClient;
@@ -33,8 +35,9 @@ public class FaceStopUserClient extends StopUserClient<AidlSession> {
public FaceStopUserClient(@NonNull Context context, @NonNull Supplier<AidlSession> lazyDaemon,
@Nullable IBinder token, int userId, int sensorId,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
@NonNull UserStoppedCallback callback) {
- super(context, lazyDaemon, token, userId, sensorId, callback);
+ super(context, lazyDaemon, token, userId, sensorId, logger, biometricContext, callback);
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java
index 33e6fa4ebf93..b69c7600e75a 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java
@@ -42,12 +42,15 @@ import android.os.UserManager;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.FrameworkStatsLog;
import com.android.server.biometrics.HardwareAuthTokenUtils;
import com.android.server.biometrics.SensorServiceStateProto;
import com.android.server.biometrics.SensorStateProto;
import com.android.server.biometrics.UserStateProto;
import com.android.server.biometrics.Utils;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.AuthenticationConsumer;
import com.android.server.biometrics.sensors.BaseClientMonitor;
import com.android.server.biometrics.sensors.BiometricScheduler;
@@ -88,7 +91,8 @@ public class Sensor {
@NonNull private final Supplier<AidlSession> mLazySession;
@Nullable private AidlSession mCurrentSession;
- static class HalSessionCallback extends ISessionCallback.Stub {
+ @VisibleForTesting
+ public static class HalSessionCallback extends ISessionCallback.Stub {
/**
* Interface to sends results to the HalSessionCallback's owner.
*/
@@ -472,7 +476,8 @@ public class Sensor {
Sensor(@NonNull String tag, @NonNull FaceProvider provider, @NonNull Context context,
@NonNull Handler handler, @NonNull FaceSensorPropertiesInternal sensorProperties,
- @NonNull LockoutResetDispatcher lockoutResetDispatcher) {
+ @NonNull LockoutResetDispatcher lockoutResetDispatcher,
+ @NonNull BiometricContext biometricContext) {
mTag = tag;
mProvider = provider;
mContext = context;
@@ -487,7 +492,9 @@ public class Sensor {
@Override
public StopUserClient<?> getStopUserClient(int userId) {
return new FaceStopUserClient(mContext, mLazySession, mToken, userId,
- mSensorProperties.sensorId, () -> mCurrentSession = null);
+ mSensorProperties.sensorId,
+ BiometricLogger.ofUnknown(mContext), biometricContext,
+ () -> mCurrentSession = null);
}
@NonNull
@@ -523,6 +530,7 @@ public class Sensor {
return new FaceStartUserClient(mContext, provider::getHalInstance,
mToken, newUserId, mSensorProperties.sensorId,
+ BiometricLogger.ofUnknown(mContext), biometricContext,
resultController, userStartedCallback);
}
});
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
index 586abe2d6298..73c759f7738c 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
@@ -55,6 +55,8 @@ import com.android.server.biometrics.SensorServiceStateProto;
import com.android.server.biometrics.SensorStateProto;
import com.android.server.biometrics.UserStateProto;
import com.android.server.biometrics.Utils;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.AcquisitionClient;
import com.android.server.biometrics.sensors.AuthenticationConsumer;
import com.android.server.biometrics.sensors.BaseClientMonitor;
@@ -117,6 +119,7 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
@NonNull private final Map<Integer, Long> mAuthenticatorIds;
@Nullable private IBiometricsFace mDaemon;
@NonNull private final HalResultController mHalResultController;
+ @NonNull private final BiometricContext mBiometricContext;
// for requests that do not use biometric prompt
@NonNull private final AtomicLong mRequestCounter = new AtomicLong(0);
private int mCurrentUserId = UserHandle.USER_NULL;
@@ -153,6 +156,7 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
@NonNull private final LockoutHalImpl mLockoutTracker;
@NonNull private final LockoutResetDispatcher mLockoutResetDispatcher;
+
HalResultController(int sensorId, @NonNull Context context, @NonNull Handler handler,
@NonNull BiometricScheduler scheduler, @NonNull LockoutHalImpl lockoutTracker,
@NonNull LockoutResetDispatcher lockoutResetDispatcher) {
@@ -335,12 +339,14 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
@NonNull FaceSensorPropertiesInternal sensorProps,
@NonNull LockoutResetDispatcher lockoutResetDispatcher,
@NonNull Handler handler,
- @NonNull BiometricScheduler scheduler) {
+ @NonNull BiometricScheduler scheduler,
+ @NonNull BiometricContext biometricContext) {
mSensorProperties = sensorProps;
mContext = context;
mSensorId = sensorProps.sensorId;
mScheduler = scheduler;
mHandler = handler;
+ mBiometricContext = biometricContext;
mUsageStats = new UsageStats(context);
mAuthenticatorIds = new HashMap<>();
mLazyDaemon = Face10.this::getDaemon;
@@ -365,7 +371,8 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
final Handler handler = new Handler(Looper.getMainLooper());
return new Face10(context, sensorProps, lockoutResetDispatcher, handler,
new BiometricScheduler(TAG, BiometricScheduler.SENSOR_TYPE_FACE,
- null /* gestureAvailabilityTracker */));
+ null /* gestureAvailabilityTracker */),
+ BiometricContext.getInstance(context));
}
@Override
@@ -533,7 +540,10 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
final FaceGenerateChallengeClient client = new FaceGenerateChallengeClient(mContext,
mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), userId,
- opPackageName, mSensorId, sSystemClock.millis());
+ opPackageName, mSensorId,
+ createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
+ BiometricsProtoEnums.CLIENT_UNKNOWN),
+ mBiometricContext, sSystemClock.millis());
mGeneratedChallengeCache = client;
mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() {
@Override
@@ -562,7 +572,10 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
mGeneratedChallengeCache = null;
final FaceRevokeChallengeClient client = new FaceRevokeChallengeClient(mContext,
- mLazyDaemon, token, userId, opPackageName, mSensorId);
+ mLazyDaemon, token, userId, opPackageName, mSensorId,
+ createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
+ BiometricsProtoEnums.CLIENT_UNKNOWN),
+ mBiometricContext);
mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() {
@Override
public void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
@@ -590,7 +603,10 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
final FaceEnrollClient client = new FaceEnrollClient(mContext, mLazyDaemon, token,
new ClientMonitorCallbackConverter(receiver), userId, hardwareAuthToken,
opPackageName, id, FaceUtils.getLegacyInstance(mSensorId), disabledFeatures,
- ENROLL_TIMEOUT_SEC, previewSurface, mSensorId);
+ ENROLL_TIMEOUT_SEC, previewSurface, mSensorId,
+ createLogger(BiometricsProtoEnums.ACTION_ENROLL,
+ BiometricsProtoEnums.CLIENT_UNKNOWN),
+ mBiometricContext);
mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() {
@Override
@@ -637,8 +653,9 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
final FaceAuthenticationClient client = new FaceAuthenticationClient(mContext,
mLazyDaemon, token, requestId, receiver, userId, operationId, restricted,
opPackageName, cookie, false /* requireConfirmation */, mSensorId,
- isStrongBiometric, statsClient, mLockoutTracker, mUsageStats,
- allowBackgroundAuthentication, isKeyguardBypassEnabled);
+ createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient),
+ mBiometricContext, isStrongBiometric, mLockoutTracker,
+ mUsageStats, allowBackgroundAuthentication, isKeyguardBypassEnabled);
mScheduler.scheduleClientMonitor(client);
});
}
@@ -670,7 +687,10 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
final FaceRemovalClient client = new FaceRemovalClient(mContext, mLazyDaemon, token,
new ClientMonitorCallbackConverter(receiver), faceId, userId, opPackageName,
- FaceUtils.getLegacyInstance(mSensorId), mSensorId, mAuthenticatorIds);
+ FaceUtils.getLegacyInstance(mSensorId), mSensorId,
+ createLogger(BiometricsProtoEnums.ACTION_REMOVE,
+ BiometricsProtoEnums.CLIENT_UNKNOWN),
+ mBiometricContext, mAuthenticatorIds);
mScheduler.scheduleClientMonitor(client);
});
}
@@ -685,7 +705,10 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
final FaceRemovalClient client = new FaceRemovalClient(mContext, mLazyDaemon, token,
new ClientMonitorCallbackConverter(receiver), 0 /* faceId */, userId,
opPackageName,
- FaceUtils.getLegacyInstance(mSensorId), mSensorId, mAuthenticatorIds);
+ FaceUtils.getLegacyInstance(mSensorId), mSensorId,
+ createLogger(BiometricsProtoEnums.ACTION_REMOVE,
+ BiometricsProtoEnums.CLIENT_UNKNOWN),
+ mBiometricContext, mAuthenticatorIds);
mScheduler.scheduleClientMonitor(client);
});
}
@@ -702,7 +725,9 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
final FaceResetLockoutClient client = new FaceResetLockoutClient(mContext,
mLazyDaemon, userId, mContext.getOpPackageName(), mSensorId,
- hardwareAuthToken);
+ createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
+ BiometricsProtoEnums.CLIENT_UNKNOWN),
+ mBiometricContext, hardwareAuthToken);
mScheduler.scheduleClientMonitor(client);
});
}
@@ -723,7 +748,9 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
final int faceId = faces.get(0).getBiometricId();
final FaceSetFeatureClient client = new FaceSetFeatureClient(mContext,
mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), userId,
- opPackageName, mSensorId, feature, enabled, hardwareAuthToken, faceId);
+ opPackageName, mSensorId, BiometricLogger.ofUnknown(mContext),
+ mBiometricContext,
+ feature, enabled, hardwareAuthToken, faceId);
mScheduler.scheduleClientMonitor(client);
});
}
@@ -742,7 +769,9 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
final int faceId = faces.get(0).getBiometricId();
final FaceGetFeatureClient client = new FaceGetFeatureClient(mContext, mLazyDaemon,
- token, listener, userId, opPackageName, mSensorId, feature, faceId);
+ token, listener, userId, opPackageName, mSensorId,
+ BiometricLogger.ofUnknown(mContext), mBiometricContext,
+ feature, faceId);
mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() {
@Override
public void onClientFinished(
@@ -767,7 +796,10 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
final List<Face> enrolledList = getEnrolledFaces(mSensorId, userId);
final FaceInternalCleanupClient client = new FaceInternalCleanupClient(mContext,
- mLazyDaemon, userId, mContext.getOpPackageName(), mSensorId, enrolledList,
+ mLazyDaemon, userId, mContext.getOpPackageName(), mSensorId,
+ createLogger(BiometricsProtoEnums.ACTION_ENUMERATE,
+ BiometricsProtoEnums.CLIENT_UNKNOWN),
+ mBiometricContext, enrolledList,
FaceUtils.getLegacyInstance(mSensorId), mAuthenticatorIds);
mScheduler.scheduleClientMonitor(client, callback);
});
@@ -890,7 +922,9 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
final boolean hasEnrolled = !getEnrolledFaces(mSensorId, targetUserId).isEmpty();
final FaceUpdateActiveUserClient client = new FaceUpdateActiveUserClient(mContext,
mLazyDaemon, targetUserId, mContext.getOpPackageName(), mSensorId,
- hasEnrolled, mAuthenticatorIds);
+ createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
+ BiometricsProtoEnums.CLIENT_UNKNOWN),
+ mBiometricContext, hasEnrolled, mAuthenticatorIds);
mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() {
@Override
public void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
@@ -904,6 +938,11 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
});
}
+ private BiometricLogger createLogger(int statsAction, int statsClient) {
+ return new BiometricLogger(mContext, BiometricsProtoEnums.MODALITY_FACE,
+ statsAction, statsClient);
+ }
+
/**
* Sends a debug message to the HAL with the provided FileDescriptor and arguments.
*/
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java
index 9038435c1021..8d76e9f031f7 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java
@@ -23,7 +23,6 @@ import android.hardware.SensorPrivacyManager;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricFaceConstants;
-import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.face.V1_0.IBiometricsFace;
import android.hardware.face.FaceManager;
import android.os.IBinder;
@@ -32,6 +31,8 @@ import android.util.Slog;
import com.android.internal.R;
import com.android.server.biometrics.Utils;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.AuthenticationClient;
import com.android.server.biometrics.sensors.BiometricNotificationUtils;
import com.android.server.biometrics.sensors.ClientMonitorCallback;
@@ -66,12 +67,13 @@ class FaceAuthenticationClient extends AuthenticationClient<IBiometricsFace> {
@NonNull IBinder token, long requestId,
@NonNull ClientMonitorCallbackConverter listener, int targetUserId, long operationId,
boolean restricted, String owner, int cookie, boolean requireConfirmation, int sensorId,
- boolean isStrongBiometric, int statsClient, @NonNull LockoutTracker lockoutTracker,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
+ boolean isStrongBiometric, @NonNull LockoutTracker lockoutTracker,
@NonNull UsageStats usageStats, boolean allowBackgroundAuthentication,
boolean isKeyguardBypassEnabled) {
super(context, lazyDaemon, token, listener, targetUserId, operationId, restricted,
- owner, cookie, requireConfirmation, sensorId, isStrongBiometric,
- BiometricsProtoEnums.MODALITY_FACE, statsClient, null /* taskStackListener */,
+ owner, cookie, requireConfirmation, sensorId, logger, biometricContext,
+ isStrongBiometric, null /* taskStackListener */,
lockoutTracker, allowBackgroundAuthentication, true /* shouldVibrate */,
isKeyguardBypassEnabled);
setRequestId(requestId);
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceEnrollClient.java
index 92f7253779ed..226e458ad07b 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceEnrollClient.java
@@ -20,7 +20,6 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.hardware.biometrics.BiometricFaceConstants;
-import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.face.V1_0.IBiometricsFace;
import android.hardware.biometrics.face.V1_0.Status;
import android.hardware.face.Face;
@@ -32,6 +31,8 @@ import android.view.Surface;
import com.android.internal.R;
import com.android.server.biometrics.Utils;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.BiometricUtils;
import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
@@ -58,10 +59,10 @@ public class FaceEnrollClient extends EnrollClient<IBiometricsFace> {
@NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int userId,
@NonNull byte[] hardwareAuthToken, @NonNull String owner, long requestId,
@NonNull BiometricUtils<Face> utils, @NonNull int[] disabledFeatures, int timeoutSec,
- @Nullable Surface previewSurface, int sensorId) {
+ @Nullable Surface previewSurface, int sensorId,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext) {
super(context, lazyDaemon, token, listener, userId, hardwareAuthToken, owner, utils,
- timeoutSec, BiometricsProtoEnums.MODALITY_FACE, sensorId,
- false /* shouldVibrate */);
+ timeoutSec, sensorId, false /* shouldVibrate */, logger, biometricContext);
setRequestId(requestId);
mDisabledFeatures = Arrays.copyOf(disabledFeatures, disabledFeatures.length);
mEnrollIgnoreList = getContext().getResources()
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGenerateChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGenerateChallengeClient.java
index b66ad608b4ca..97838a70cbd1 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGenerateChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGenerateChallengeClient.java
@@ -25,6 +25,8 @@ import android.os.RemoteException;
import android.util.Slog;
import com.android.internal.util.Preconditions;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.GenerateChallengeClient;
@@ -51,8 +53,10 @@ public class FaceGenerateChallengeClient extends GenerateChallengeClient<IBiomet
FaceGenerateChallengeClient(@NonNull Context context,
@NonNull Supplier<IBiometricsFace> lazyDaemon, @NonNull IBinder token,
@NonNull ClientMonitorCallbackConverter listener, int userId, @NonNull String owner,
- int sensorId, long now) {
- super(context, lazyDaemon, token, listener, userId, owner, sensorId);
+ int sensorId, @NonNull BiometricLogger logger,
+ @NonNull BiometricContext biometricContext, long now) {
+ super(context, lazyDaemon, token, listener, userId, owner, sensorId, logger,
+ biometricContext);
mCreatedAt = now;
mWaiting = new ArrayList<>();
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGetFeatureClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGetFeatureClient.java
index 1b387bf7879a..981253699322 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGetFeatureClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGetFeatureClient.java
@@ -19,7 +19,6 @@ package com.android.server.biometrics.sensors.face.hidl;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
-import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.face.V1_0.IBiometricsFace;
import android.hardware.biometrics.face.V1_0.OptionalBool;
import android.hardware.biometrics.face.V1_0.Status;
@@ -28,6 +27,8 @@ import android.os.RemoteException;
import android.util.Slog;
import com.android.server.biometrics.BiometricsProto;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.HalClientMonitor;
@@ -48,13 +49,13 @@ public class FaceGetFeatureClient extends HalClientMonitor<IBiometricsFace> {
FaceGetFeatureClient(@NonNull Context context, @NonNull Supplier<IBiometricsFace> lazyDaemon,
@NonNull IBinder token, @Nullable ClientMonitorCallbackConverter listener, int userId,
- @NonNull String owner, int sensorId, int feature, int faceId) {
+ @NonNull String owner, int sensorId,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
+ int feature, int faceId) {
super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId,
- BiometricsProtoEnums.MODALITY_UNKNOWN, BiometricsProtoEnums.ACTION_UNKNOWN,
- BiometricsProtoEnums.CLIENT_UNKNOWN);
+ logger, biometricContext);
mFeature = feature;
mFaceId = faceId;
-
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceInternalCleanupClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceInternalCleanupClient.java
index 93a2913fbfa1..d21a7501e516 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceInternalCleanupClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceInternalCleanupClient.java
@@ -18,11 +18,12 @@ package com.android.server.biometrics.sensors.face.hidl;
import android.annotation.NonNull;
import android.content.Context;
-import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.face.V1_0.IBiometricsFace;
import android.hardware.face.Face;
import android.os.IBinder;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.BiometricUtils;
import com.android.server.biometrics.sensors.InternalCleanupClient;
import com.android.server.biometrics.sensors.InternalEnumerateClient;
@@ -40,29 +41,32 @@ class FaceInternalCleanupClient extends InternalCleanupClient<Face, IBiometricsF
FaceInternalCleanupClient(@NonNull Context context,
@NonNull Supplier<IBiometricsFace> lazyDaemon, int userId, @NonNull String owner,
- int sensorId, @NonNull List<Face> enrolledList, @NonNull BiometricUtils<Face> utils,
- @NonNull Map<Integer, Long> authenticatorIds) {
- super(context, lazyDaemon, userId, owner, sensorId, BiometricsProtoEnums.MODALITY_FACE,
+ int sensorId, @NonNull BiometricLogger logger,
+ @NonNull BiometricContext biometricContext, @NonNull List<Face> enrolledList,
+ @NonNull BiometricUtils<Face> utils, @NonNull Map<Integer, Long> authenticatorIds) {
+ super(context, lazyDaemon, userId, owner, sensorId, logger, biometricContext,
enrolledList, utils, authenticatorIds);
}
@Override
protected InternalEnumerateClient<IBiometricsFace> getEnumerateClient(Context context,
Supplier<IBiometricsFace> lazyDaemon, IBinder token, int userId, String owner,
- List<Face> enrolledList, BiometricUtils<Face> utils, int sensorId) {
+ List<Face> enrolledList, BiometricUtils<Face> utils, int sensorId,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext) {
return new FaceInternalEnumerateClient(context, lazyDaemon, token, userId, owner,
- enrolledList, utils, sensorId);
+ enrolledList, utils, sensorId, logger, biometricContext);
}
@Override
protected RemovalClient<Face, IBiometricsFace> getRemovalClient(Context context,
Supplier<IBiometricsFace> lazyDaemon, IBinder token,
int biometricId, int userId, String owner, BiometricUtils<Face> utils, int sensorId,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
Map<Integer, Long> authenticatorIds) {
// Internal remove does not need to send results to anyone. Cleanup (enumerate + remove)
// is all done internally.
return new FaceRemovalClient(context, lazyDaemon, token,
null /* ClientMonitorCallbackConverter */, biometricId, userId, owner, utils,
- sensorId, authenticatorIds);
+ sensorId, logger, biometricContext, authenticatorIds);
}
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceInternalEnumerateClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceInternalEnumerateClient.java
index f1788de38565..250dd7e0cdef 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceInternalEnumerateClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceInternalEnumerateClient.java
@@ -18,13 +18,14 @@ package com.android.server.biometrics.sensors.face.hidl;
import android.annotation.NonNull;
import android.content.Context;
-import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.face.V1_0.IBiometricsFace;
import android.hardware.face.Face;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.BiometricUtils;
import com.android.server.biometrics.sensors.InternalEnumerateClient;
@@ -41,9 +42,10 @@ class FaceInternalEnumerateClient extends InternalEnumerateClient<IBiometricsFac
FaceInternalEnumerateClient(@NonNull Context context,
@NonNull Supplier<IBiometricsFace> lazyDaemon, @NonNull IBinder token, int userId,
@NonNull String owner, @NonNull List<Face> enrolledList,
- @NonNull BiometricUtils<Face> utils, int sensorId) {
+ @NonNull BiometricUtils<Face> utils, int sensorId,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext) {
super(context, lazyDaemon, token, userId, owner, enrolledList, utils, sensorId,
- BiometricsProtoEnums.MODALITY_FACE);
+ logger, biometricContext);
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceRemovalClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceRemovalClient.java
index cbc23e49f4d8..0ee7a354d5a4 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceRemovalClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceRemovalClient.java
@@ -18,13 +18,14 @@ package com.android.server.biometrics.sensors.face.hidl;
import android.annotation.NonNull;
import android.content.Context;
-import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.face.V1_0.IBiometricsFace;
import android.hardware.face.Face;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.BiometricUtils;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.RemovalClient;
@@ -44,9 +45,11 @@ class FaceRemovalClient extends RemovalClient<Face, IBiometricsFace> {
FaceRemovalClient(@NonNull Context context, @NonNull Supplier<IBiometricsFace> lazyDaemon,
@NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener,
int biometricId, int userId, @NonNull String owner, @NonNull BiometricUtils<Face> utils,
- int sensorId, @NonNull Map<Integer, Long> authenticatorIds) {
- super(context, lazyDaemon, token, listener, userId, owner, utils, sensorId,
- authenticatorIds, BiometricsProtoEnums.MODALITY_FACE);
+ int sensorId, @NonNull BiometricLogger logger,
+ @NonNull BiometricContext biometricContext,
+ @NonNull Map<Integer, Long> authenticatorIds) {
+ super(context, lazyDaemon, token, listener, userId, owner, utils, sensorId, logger,
+ biometricContext, authenticatorIds);
mBiometricId = biometricId;
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceResetLockoutClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceResetLockoutClient.java
index 88e2318b0570..6e74d3622c1a 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceResetLockoutClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceResetLockoutClient.java
@@ -18,12 +18,13 @@ package com.android.server.biometrics.sensors.face.hidl;
import android.annotation.NonNull;
import android.content.Context;
-import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.face.V1_0.IBiometricsFace;
import android.os.RemoteException;
import android.util.Slog;
import com.android.server.biometrics.BiometricsProto;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.HalClientMonitor;
@@ -42,10 +43,10 @@ public class FaceResetLockoutClient extends HalClientMonitor<IBiometricsFace> {
FaceResetLockoutClient(@NonNull Context context,
@NonNull Supplier<IBiometricsFace> lazyDaemon, int userId, String owner, int sensorId,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
@NonNull byte[] hardwareAuthToken) {
super(context, lazyDaemon, null /* token */, null /* listener */, userId, owner,
- 0 /* cookie */, sensorId, BiometricsProtoEnums.MODALITY_UNKNOWN,
- BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN);
+ 0 /* cookie */, sensorId, logger, biometricContext);
mHardwareAuthToken = new ArrayList<>();
for (byte b : hardwareAuthToken) {
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceRevokeChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceRevokeChallengeClient.java
index ab8d16145fab..b7b0dc046633 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceRevokeChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceRevokeChallengeClient.java
@@ -23,6 +23,8 @@ import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.RevokeChallengeClient;
import java.util.function.Supplier;
@@ -37,8 +39,9 @@ public class FaceRevokeChallengeClient extends RevokeChallengeClient<IBiometrics
FaceRevokeChallengeClient(@NonNull Context context,
@NonNull Supplier<IBiometricsFace> lazyDaemon, @NonNull IBinder token,
- int userId, @NonNull String owner, int sensorId) {
- super(context, lazyDaemon, token, userId, owner, sensorId);
+ int userId, @NonNull String owner, int sensorId,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext) {
+ super(context, lazyDaemon, token, userId, owner, sensorId, logger, biometricContext);
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceSetFeatureClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceSetFeatureClient.java
index b2b52e713ec9..3c82f9c73700 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceSetFeatureClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceSetFeatureClient.java
@@ -18,7 +18,6 @@ package com.android.server.biometrics.sensors.face.hidl;
import android.annotation.NonNull;
import android.content.Context;
-import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.face.V1_0.IBiometricsFace;
import android.hardware.biometrics.face.V1_0.Status;
import android.os.IBinder;
@@ -26,6 +25,8 @@ import android.os.RemoteException;
import android.util.Slog;
import com.android.server.biometrics.BiometricsProto;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.HalClientMonitor;
@@ -48,11 +49,11 @@ public class FaceSetFeatureClient extends HalClientMonitor<IBiometricsFace> {
FaceSetFeatureClient(@NonNull Context context, @NonNull Supplier<IBiometricsFace> lazyDaemon,
@NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int userId,
- @NonNull String owner, int sensorId, int feature, boolean enabled,
- byte[] hardwareAuthToken, int faceId) {
+ @NonNull String owner, int sensorId,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
+ int feature, boolean enabled, byte[] hardwareAuthToken, int faceId) {
super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId,
- BiometricsProtoEnums.MODALITY_UNKNOWN, BiometricsProtoEnums.ACTION_UNKNOWN,
- BiometricsProtoEnums.CLIENT_UNKNOWN);
+ logger, biometricContext);
mFeature = feature;
mEnabled = enabled;
mFaceId = faceId;
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceUpdateActiveUserClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceUpdateActiveUserClient.java
index 04b93274e42b..8385c3fa7103 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceUpdateActiveUserClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceUpdateActiveUserClient.java
@@ -18,13 +18,14 @@ package com.android.server.biometrics.sensors.face.hidl;
import android.annotation.NonNull;
import android.content.Context;
-import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.face.V1_0.IBiometricsFace;
import android.os.Environment;
import android.os.RemoteException;
import android.util.Slog;
import com.android.server.biometrics.BiometricsProto;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.HalClientMonitor;
@@ -41,11 +42,11 @@ public class FaceUpdateActiveUserClient extends HalClientMonitor<IBiometricsFace
FaceUpdateActiveUserClient(@NonNull Context context,
@NonNull Supplier<IBiometricsFace> lazyDaemon, int userId, @NonNull String owner,
- int sensorId, boolean hasEnrolledBiometrics,
+ int sensorId, @NonNull BiometricLogger logger,
+ @NonNull BiometricContext biometricContext, boolean hasEnrolledBiometrics,
@NonNull Map<Integer, Long> authenticatorIds) {
super(context, lazyDaemon, null /* token */, null /* listener */, userId, owner,
- 0 /* cookie */, sensorId, BiometricsProtoEnums.MODALITY_UNKNOWN,
- BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN);
+ 0 /* cookie */, sensorId, logger, biometricContext);
mHasEnrolledBiometrics = hasEnrolledBiometrics;
mAuthenticatorIds = authenticatorIds;
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
index 6366e19ef191..b4befd23671f 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
@@ -83,6 +83,7 @@ import com.android.internal.widget.LockPatternUtils;
import com.android.server.ServiceThread;
import com.android.server.SystemService;
import com.android.server.biometrics.Utils;
+import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.LockoutResetDispatcher;
import com.android.server.biometrics.sensors.LockoutTracker;
@@ -815,7 +816,8 @@ public class FingerprintService extends SystemService {
UserHandle.USER_CURRENT) != 0) {
fingerprint21 = Fingerprint21UdfpsMock.newInstance(getContext(),
mFingerprintStateCallback, hidlSensor,
- mLockoutResetDispatcher, mGestureAvailabilityDispatcher);
+ mLockoutResetDispatcher, mGestureAvailabilityDispatcher,
+ BiometricContext.getInstance(getContext()));
} else {
fingerprint21 = Fingerprint21.newInstance(getContext(),
mFingerprintStateCallback, hidlSensor, mHandler,
@@ -843,7 +845,8 @@ public class FingerprintService extends SystemService {
final FingerprintProvider provider =
new FingerprintProvider(getContext(), mFingerprintStateCallback, props,
instance, mLockoutResetDispatcher,
- mGestureAvailabilityDispatcher);
+ mGestureAvailabilityDispatcher,
+ BiometricContext.getInstance(getContext()));
mServiceProviders.add(provider);
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception in getSensorProps: " + fqName);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/AidlSession.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/AidlSession.java
index 727101a69b06..55861bb4cc6f 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/AidlSession.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/AidlSession.java
@@ -16,11 +16,11 @@
package com.android.server.biometrics.sensors.fingerprint.aidl;
+import static com.android.server.biometrics.sensors.fingerprint.aidl.Sensor.HalSessionCallback;
+
import android.annotation.NonNull;
import android.hardware.biometrics.fingerprint.ISession;
-import static com.android.server.biometrics.sensors.fingerprint.aidl.Sensor.HalSessionCallback;
-
/**
* A holder for an AIDL {@link ISession} with additional metadata about the current user
* and the backend.
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
index 2c1c80ccabb3..d26a78008529 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
@@ -23,10 +23,8 @@ import android.content.Context;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricFingerprintConstants;
import android.hardware.biometrics.BiometricFingerprintConstants.FingerprintAcquired;
-import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.common.ICancellationSignal;
import android.hardware.biometrics.common.OperationContext;
-import android.hardware.biometrics.common.OperationReason;
import android.hardware.biometrics.fingerprint.PointerContext;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.hardware.fingerprint.ISidefpsController;
@@ -35,6 +33,8 @@ import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.log.CallbackWithProbe;
import com.android.server.biometrics.log.Probe;
import com.android.server.biometrics.sensors.AuthenticationClient;
@@ -72,15 +72,18 @@ class FingerprintAuthenticationClient extends AuthenticationClient<AidlSession>
@NonNull IBinder token, long requestId,
@NonNull ClientMonitorCallbackConverter listener, int targetUserId, long operationId,
boolean restricted, @NonNull String owner, int cookie, boolean requireConfirmation,
- int sensorId, boolean isStrongBiometric, int statsClient,
+ int sensorId,
+ @NonNull BiometricLogger biometricLogger, @NonNull BiometricContext biometricContext,
+ boolean isStrongBiometric,
@Nullable TaskStackListener taskStackListener, @NonNull LockoutCache lockoutCache,
@Nullable IUdfpsOverlayController udfpsOverlayController,
@Nullable ISidefpsController sidefpsController,
boolean allowBackgroundAuthentication,
@NonNull FingerprintSensorPropertiesInternal sensorProps) {
super(context, lazyDaemon, token, listener, targetUserId, operationId, restricted, owner,
- cookie, requireConfirmation, sensorId, isStrongBiometric,
- BiometricsProtoEnums.MODALITY_FINGERPRINT, statsClient, taskStackListener,
+ cookie, requireConfirmation, sensorId,
+ biometricLogger, biometricContext,
+ isStrongBiometric, taskStackListener,
lockoutCache, allowBackgroundAuthentication, true /* shouldVibrate */,
false /* isKeyguardBypassEnabled */);
setRequestId(requestId);
@@ -105,7 +108,8 @@ class FingerprintAuthenticationClient extends AuthenticationClient<AidlSession>
@NonNull
@Override
protected ClientMonitorCallback wrapCallbackForStart(@NonNull ClientMonitorCallback callback) {
- return new ClientMonitorCompositeCallback(mALSProbeCallback, callback);
+ return new ClientMonitorCompositeCallback(mALSProbeCallback,
+ getBiometricContextUnsubscriber(), callback);
}
@Override
@@ -175,13 +179,17 @@ class FingerprintAuthenticationClient extends AuthenticationClient<AidlSession>
final AidlSession session = getFreshDaemon();
if (session.hasContextMethods()) {
- final OperationContext context = new OperationContext();
- // TODO: add reason, id, and isAoD
- context.id = 0;
- context.reason = OperationReason.UNKNOWN;
- context.isAoD = false;
- context.isCrypto = isCryptoOperation();
- return session.getSession().authenticateWithContext(mOperationId, context);
+ final OperationContext opContext = getOperationContext();
+ final ICancellationSignal cancel = session.getSession().authenticateWithContext(
+ mOperationId, opContext);
+ getBiometricContext().subscribe(opContext, ctx -> {
+ try {
+ session.getSession().onContextChanged(ctx);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to notify context changed", e);
+ }
+ });
+ return cancel;
} else {
return session.getSession().authenticate(mOperationId);
}
@@ -190,6 +198,8 @@ class FingerprintAuthenticationClient extends AuthenticationClient<AidlSession>
@Override
protected void stopHalOperation() {
mSensorOverlays.hide(getSensorId());
+ unsubscribeBiometricContext();
+
if (mCancellationSignal != null) {
try {
mCancellationSignal.cancel();
@@ -219,7 +229,7 @@ class FingerprintAuthenticationClient extends AuthenticationClient<AidlSession>
context.y = y;
context.minor = minor;
context.major = major;
- context.isAoD = false; // TODO; get value
+ context.isAoD = getBiometricContext().isAoD();
session.getSession().onPointerDownWithContext(context);
} else {
session.getSession().onPointerDown(0 /* pointerId */, x, y, minor, major);
@@ -277,8 +287,8 @@ class FingerprintAuthenticationClient extends AuthenticationClient<AidlSession>
mLockoutCache.setLockoutModeForUser(getTargetUserId(), LockoutTracker.LOCKOUT_TIMED);
// Lockout metrics are logged as an error code.
final int error = BiometricFingerprintConstants.FINGERPRINT_ERROR_LOCKOUT;
- getLogger().logOnError(getContext(), error, 0 /* vendorCode */,
- isCryptoOperation(), getTargetUserId());
+ getLogger().logOnError(getContext(), getOperationContext(),
+ error, 0 /* vendorCode */, getTargetUserId());
try {
getListener().onError(getSensorId(), getCookie(), error, 0 /* vendorCode */);
@@ -296,8 +306,8 @@ class FingerprintAuthenticationClient extends AuthenticationClient<AidlSession>
mLockoutCache.setLockoutModeForUser(getTargetUserId(), LockoutTracker.LOCKOUT_PERMANENT);
// Lockout metrics are logged as an error code.
final int error = BiometricFingerprintConstants.FINGERPRINT_ERROR_LOCKOUT_PERMANENT;
- getLogger().logOnError(getContext(), error, 0 /* vendorCode */,
- isCryptoOperation(), getTargetUserId());
+ getLogger().logOnError(getContext(), getOperationContext(),
+ error, 0 /* vendorCode */, getTargetUserId());
try {
getListener().onError(getSensorId(), getCookie(), error, 0 /* vendorCode */);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java
index 6645332c1fac..0e89814c6ad2 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java
@@ -20,16 +20,15 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.hardware.biometrics.BiometricOverlayConstants;
-import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.common.ICancellationSignal;
-import android.hardware.biometrics.common.OperationContext;
-import android.hardware.biometrics.common.OperationReason;
import android.hardware.fingerprint.IUdfpsOverlayController;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
import com.android.server.biometrics.BiometricsProto;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.AcquisitionClient;
import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
@@ -54,11 +53,10 @@ class FingerprintDetectClient extends AcquisitionClient<AidlSession> implements
@NonNull IBinder token, long requestId,
@NonNull ClientMonitorCallbackConverter listener, int userId,
@NonNull String owner, int sensorId,
- @Nullable IUdfpsOverlayController udfpsOverlayController, boolean isStrongBiometric,
- int statsClient) {
+ @NonNull BiometricLogger biometricLogger, @NonNull BiometricContext biometricContext,
+ @Nullable IUdfpsOverlayController udfpsOverlayController, boolean isStrongBiometric) {
super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId,
- true /* shouldVibrate */, BiometricsProtoEnums.MODALITY_FINGERPRINT,
- BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient);
+ true /* shouldVibrate */, biometricLogger, biometricContext);
setRequestId(requestId);
mIsStrongBiometric = isStrongBiometric;
mSensorOverlays = new SensorOverlays(udfpsOverlayController, null /* sideFpsController*/);
@@ -99,13 +97,7 @@ class FingerprintDetectClient extends AcquisitionClient<AidlSession> implements
final AidlSession session = getFreshDaemon();
if (session.hasContextMethods()) {
- final OperationContext context = new OperationContext();
- // TODO: add reason, id, and isAoD
- context.id = 0;
- context.reason = OperationReason.UNKNOWN;
- context.isAoD = false;
- context.isCrypto = isCryptoOperation();
- return session.getSession().detectInteractionWithContext(context);
+ return session.getSession().detectInteractionWithContext(getOperationContext());
} else {
return session.getSession().detectInteraction();
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
index d0c5bb8851e6..e21d901b135d 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
@@ -22,10 +22,8 @@ import android.content.Context;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricFingerprintConstants;
import android.hardware.biometrics.BiometricFingerprintConstants.FingerprintAcquired;
-import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.common.ICancellationSignal;
import android.hardware.biometrics.common.OperationContext;
-import android.hardware.biometrics.common.OperationReason;
import android.hardware.biometrics.fingerprint.PointerContext;
import android.hardware.fingerprint.Fingerprint;
import android.hardware.fingerprint.FingerprintManager;
@@ -38,6 +36,10 @@ import android.os.RemoteException;
import android.util.Slog;
import com.android.server.biometrics.HardwareAuthTokenUtils;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
+import com.android.server.biometrics.log.CallbackWithProbe;
+import com.android.server.biometrics.log.Probe;
import com.android.server.biometrics.sensors.BiometricNotificationUtils;
import com.android.server.biometrics.sensors.BiometricUtils;
import com.android.server.biometrics.sensors.ClientMonitorCallback;
@@ -57,6 +59,7 @@ class FingerprintEnrollClient extends EnrollClient<AidlSession> implements Udfps
@NonNull private final FingerprintSensorPropertiesInternal mSensorProps;
@NonNull private final SensorOverlays mSensorOverlays;
+ @NonNull private final CallbackWithProbe<Probe> mALSProbeCallback;
private final @FingerprintManager.EnrollReason int mEnrollReason;
@Nullable private ICancellationSignal mCancellationSignal;
@@ -68,19 +71,22 @@ class FingerprintEnrollClient extends EnrollClient<AidlSession> implements Udfps
@NonNull ClientMonitorCallbackConverter listener, int userId,
@NonNull byte[] hardwareAuthToken, @NonNull String owner,
@NonNull BiometricUtils<Fingerprint> utils, int sensorId,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
@NonNull FingerprintSensorPropertiesInternal sensorProps,
@Nullable IUdfpsOverlayController udfpsOverlayController,
@Nullable ISidefpsController sidefpsController,
int maxTemplatesPerUser, @FingerprintManager.EnrollReason int enrollReason) {
// UDFPS haptics occur when an image is acquired (instead of when the result is known)
super(context, lazyDaemon, token, listener, userId, hardwareAuthToken, owner, utils,
- 0 /* timeoutSec */, BiometricsProtoEnums.MODALITY_FINGERPRINT, sensorId,
- !sensorProps.isAnyUdfpsType() /* shouldVibrate */);
+ 0 /* timeoutSec */, sensorId,
+ !sensorProps.isAnyUdfpsType() /* shouldVibrate */, logger, biometricContext);
setRequestId(requestId);
mSensorProps = sensorProps;
mSensorOverlays = new SensorOverlays(udfpsOverlayController, sidefpsController);
mMaxTemplatesPerUser = maxTemplatesPerUser;
+ mALSProbeCallback = getLogger().createALSCallback(false /* startWithClient */);
+
mEnrollReason = enrollReason;
if (enrollReason == FingerprintManager.ENROLL_FIND_SENSOR) {
getLogger().disableMetrics();
@@ -90,8 +96,8 @@ class FingerprintEnrollClient extends EnrollClient<AidlSession> implements Udfps
@NonNull
@Override
protected ClientMonitorCallback wrapCallbackForStart(@NonNull ClientMonitorCallback callback) {
- return new ClientMonitorCompositeCallback(
- getLogger().createALSCallback(true /* startWithClient */), callback);
+ return new ClientMonitorCompositeCallback(mALSProbeCallback,
+ getBiometricContextUnsubscriber(), callback);
}
@Override
@@ -140,22 +146,6 @@ class FingerprintEnrollClient extends EnrollClient<AidlSession> implements Udfps
}
@Override
- protected void stopHalOperation() {
- mSensorOverlays.hide(getSensorId());
-
- if (mCancellationSignal != null) {
- try {
- mCancellationSignal.cancel();
- } catch (RemoteException e) {
- Slog.e(TAG, "Remote exception when requesting cancel", e);
- onError(BiometricFingerprintConstants.FINGERPRINT_ERROR_HW_UNAVAILABLE,
- 0 /* vendorCode */);
- mCallback.onClientFinished(this, false /* success */);
- }
- }
- }
-
- @Override
protected void startHalOperation() {
mSensorOverlays.show(getSensorId(), getOverlayReasonFromEnrollReason(mEnrollReason), this);
@@ -176,22 +166,44 @@ class FingerprintEnrollClient extends EnrollClient<AidlSession> implements Udfps
HardwareAuthTokenUtils.toHardwareAuthToken(mHardwareAuthToken);
if (session.hasContextMethods()) {
- final OperationContext context = new OperationContext();
- // TODO: add reason, id, and isAoD
- context.id = 0;
- context.reason = OperationReason.UNKNOWN;
- context.isAoD = false;
- context.isCrypto = isCryptoOperation();
- return session.getSession().enrollWithContext(hat, context);
+ final OperationContext opContext = getOperationContext();
+ final ICancellationSignal cancel = session.getSession().enrollWithContext(
+ hat, opContext);
+ getBiometricContext().subscribe(opContext, ctx -> {
+ try {
+ session.getSession().onContextChanged(ctx);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to notify context changed", e);
+ }
+ });
+ return cancel;
} else {
return session.getSession().enroll(hat);
}
}
@Override
+ protected void stopHalOperation() {
+ mSensorOverlays.hide(getSensorId());
+ unsubscribeBiometricContext();
+
+ if (mCancellationSignal != null) {
+ try {
+ mCancellationSignal.cancel();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception when requesting cancel", e);
+ onError(BiometricFingerprintConstants.FINGERPRINT_ERROR_HW_UNAVAILABLE,
+ 0 /* vendorCode */);
+ mCallback.onClientFinished(this, false /* success */);
+ }
+ }
+ }
+
+ @Override
public void onPointerDown(int x, int y, float minor, float major) {
try {
mIsPointerDown = true;
+ mALSProbeCallback.getProbe().enable();
final AidlSession session = getFreshDaemon();
if (session.hasContextMethods()) {
@@ -201,7 +213,7 @@ class FingerprintEnrollClient extends EnrollClient<AidlSession> implements Udfps
context.y = y;
context.minor = minor;
context.major = major;
- context.isAoD = false;
+ context.isAoD = getBiometricContext().isAoD();
session.getSession().onPointerDownWithContext(context);
} else {
session.getSession().onPointerDown(0 /* pointerId */, x, y, minor, major);
@@ -215,6 +227,7 @@ class FingerprintEnrollClient extends EnrollClient<AidlSession> implements Udfps
public void onPointerUp() {
try {
mIsPointerDown = false;
+ mALSProbeCallback.getProbe().disable();
final AidlSession session = getFreshDaemon();
if (session.hasContextMethods()) {
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGenerateChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGenerateChallengeClient.java
index 04a7ca086ced..ddae8bedf7c1 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGenerateChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGenerateChallengeClient.java
@@ -23,6 +23,8 @@ import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.GenerateChallengeClient;
@@ -38,8 +40,10 @@ class FingerprintGenerateChallengeClient extends GenerateChallengeClient<AidlSes
@NonNull Supplier<AidlSession> lazyDaemon,
@NonNull IBinder token,
@NonNull ClientMonitorCallbackConverter listener,
- int userId, @NonNull String owner, int sensorId) {
- super(context, lazyDaemon, token, listener, userId, owner, sensorId);
+ int userId, @NonNull String owner, int sensorId,
+ @NonNull BiometricLogger biometricLogger, @NonNull BiometricContext biometricContext) {
+ super(context, lazyDaemon, token, listener, userId, owner, sensorId,
+ biometricLogger, biometricContext);
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGetAuthenticatorIdClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGetAuthenticatorIdClient.java
index 3a487fc98ca4..ea1a622c36ab 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGetAuthenticatorIdClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGetAuthenticatorIdClient.java
@@ -18,11 +18,12 @@ package com.android.server.biometrics.sensors.fingerprint.aidl;
import android.annotation.NonNull;
import android.content.Context;
-import android.hardware.biometrics.BiometricsProtoEnums;
import android.os.RemoteException;
import android.util.Slog;
import com.android.server.biometrics.BiometricsProto;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.HalClientMonitor;
@@ -36,11 +37,12 @@ class FingerprintGetAuthenticatorIdClient extends HalClientMonitor<AidlSession>
private final Map<Integer, Long> mAuthenticatorIds;
FingerprintGetAuthenticatorIdClient(@NonNull Context context,
- @NonNull Supplier<AidlSession> lazyDaemon, int userId, @NonNull String owner,
- int sensorId, Map<Integer, Long> authenticatorIds) {
+ @NonNull Supplier<AidlSession> lazyDaemon,
+ int userId, @NonNull String owner, int sensorId,
+ @NonNull BiometricLogger biometricLogger, @NonNull BiometricContext biometricContext,
+ Map<Integer, Long> authenticatorIds) {
super(context, lazyDaemon, null /* token */, null /* listener */, userId, owner,
- 0 /* cookie */, sensorId, BiometricsProtoEnums.MODALITY_FINGERPRINT,
- BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN);
+ 0 /* cookie */, sensorId, biometricLogger, biometricContext);
mAuthenticatorIds = authenticatorIds;
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClient.java
index 0ecad725cbeb..09bdd6de49f0 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClient.java
@@ -22,6 +22,8 @@ import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.fingerprint.Fingerprint;
import android.os.IBinder;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.BiometricUtils;
import com.android.server.biometrics.sensors.InternalCleanupClient;
import com.android.server.biometrics.sensors.InternalEnumerateClient;
@@ -39,28 +41,35 @@ import java.util.function.Supplier;
class FingerprintInternalCleanupClient extends InternalCleanupClient<Fingerprint, AidlSession> {
FingerprintInternalCleanupClient(@NonNull Context context,
- @NonNull Supplier<AidlSession> lazyDaemon, int userId, @NonNull String owner,
- int sensorId, @NonNull List<Fingerprint> enrolledList,
+ @NonNull Supplier<AidlSession> lazyDaemon,
+ int userId, @NonNull String owner, int sensorId,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
+ @NonNull List<Fingerprint> enrolledList,
@NonNull FingerprintUtils utils, @NonNull Map<Integer, Long> authenticatorIds) {
- super(context, lazyDaemon, userId, owner, sensorId,
- BiometricsProtoEnums.MODALITY_FINGERPRINT, enrolledList, utils, authenticatorIds);
+ super(context, lazyDaemon, userId, owner, sensorId, logger, biometricContext,
+ enrolledList, utils, authenticatorIds);
}
@Override
protected InternalEnumerateClient<AidlSession> getEnumerateClient(Context context,
Supplier<AidlSession> lazyDaemon, IBinder token, int userId, String owner,
- List<Fingerprint> enrolledList, BiometricUtils<Fingerprint> utils, int sensorId) {
+ List<Fingerprint> enrolledList, BiometricUtils<Fingerprint> utils, int sensorId,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext) {
return new FingerprintInternalEnumerateClient(context, lazyDaemon, token, userId, owner,
- enrolledList, utils, sensorId);
+ enrolledList, utils, sensorId,
+ logger.swapAction(context, BiometricsProtoEnums.ACTION_ENUMERATE),
+ biometricContext);
}
@Override
protected RemovalClient<Fingerprint, AidlSession> getRemovalClient(Context context,
Supplier<AidlSession> lazyDaemon, IBinder token, int biometricId, int userId,
String owner, BiometricUtils<Fingerprint> utils, int sensorId,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
Map<Integer, Long> authenticatorIds) {
return new FingerprintRemovalClient(context, lazyDaemon, token,
null /* ClientMonitorCallbackConverter */, new int[] {biometricId}, userId, owner,
- utils, sensorId, authenticatorIds);
+ utils, sensorId, logger.swapAction(context, BiometricsProtoEnums.ACTION_REMOVE),
+ biometricContext, authenticatorIds);
}
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalEnumerateClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalEnumerateClient.java
index 06ba6d45b407..a5a832aaaf59 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalEnumerateClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalEnumerateClient.java
@@ -18,12 +18,13 @@ package com.android.server.biometrics.sensors.fingerprint.aidl;
import android.annotation.NonNull;
import android.content.Context;
-import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.fingerprint.Fingerprint;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.BiometricUtils;
import com.android.server.biometrics.sensors.InternalEnumerateClient;
@@ -40,9 +41,10 @@ class FingerprintInternalEnumerateClient extends InternalEnumerateClient<AidlSes
protected FingerprintInternalEnumerateClient(@NonNull Context context,
@NonNull Supplier<AidlSession> lazyDaemon, @NonNull IBinder token, int userId,
@NonNull String owner, @NonNull List<Fingerprint> enrolledList,
- @NonNull BiometricUtils<Fingerprint> utils, int sensorId) {
+ @NonNull BiometricUtils<Fingerprint> utils, int sensorId,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext) {
super(context, lazyDaemon, token, userId, owner, enrolledList, utils, sensorId,
- BiometricsProtoEnums.MODALITY_FINGERPRINT);
+ logger, biometricContext);
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInvalidationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInvalidationClient.java
index 1ee32e986ca6..bc02897ef5c4 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInvalidationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInvalidationClient.java
@@ -23,6 +23,8 @@ import android.hardware.fingerprint.Fingerprint;
import android.os.RemoteException;
import android.util.Slog;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.InvalidationClient;
import java.util.Map;
@@ -33,8 +35,10 @@ public class FingerprintInvalidationClient extends InvalidationClient<Fingerprin
public FingerprintInvalidationClient(@NonNull Context context,
@NonNull Supplier<AidlSession> lazyDaemon, int userId, int sensorId,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
@NonNull Map<Integer, Long> authenticatorIds, @NonNull IInvalidationCallback callback) {
- super(context, lazyDaemon, userId, sensorId, authenticatorIds, callback);
+ super(context, lazyDaemon, userId, sensorId, logger, biometricContext,
+ authenticatorIds, callback);
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
index efc93045f957..f810bca9707d 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
@@ -26,6 +26,7 @@ import android.app.TaskStackListener;
import android.content.Context;
import android.content.pm.UserInfo;
import android.content.res.TypedArray;
+import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.ComponentInfoInternal;
import android.hardware.biometrics.IInvalidationCallback;
import android.hardware.biometrics.ITestSession;
@@ -53,6 +54,8 @@ import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.biometrics.Utils;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.AuthenticationClient;
import com.android.server.biometrics.sensors.BaseClientMonitor;
import com.android.server.biometrics.sensors.ClientMonitorCallback;
@@ -98,7 +101,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
@NonNull private final BiometricTaskStackListener mTaskStackListener;
// for requests that do not use biometric prompt
@NonNull private final AtomicLong mRequestCounter = new AtomicLong(0);
-
+ @NonNull private final BiometricContext mBiometricContext;
@Nullable private IFingerprint mDaemon;
@Nullable private IUdfpsOverlayController mUdfpsOverlayController;
@Nullable private ISidefpsController mSidefpsController;
@@ -141,7 +144,8 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
@NonNull FingerprintStateCallback fingerprintStateCallback,
@NonNull SensorProps[] props, @NonNull String halInstanceName,
@NonNull LockoutResetDispatcher lockoutResetDispatcher,
- @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher) {
+ @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher,
+ @NonNull BiometricContext biometricContext) {
mContext = context;
mFingerprintStateCallback = fingerprintStateCallback;
mHalInstanceName = halInstanceName;
@@ -150,6 +154,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
mLockoutResetDispatcher = lockoutResetDispatcher;
mActivityTaskManager = ActivityTaskManager.getInstance();
mTaskStackListener = new BiometricTaskStackListener();
+ mBiometricContext = biometricContext;
final List<SensorLocationInternal> workaroundLocations = getWorkaroundSensorProps(context);
@@ -181,7 +186,8 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
location.sensorRadius))
.collect(Collectors.toList()));
final Sensor sensor = new Sensor(getTag() + "/" + sensorId, this, mContext, mHandler,
- internalProp, lockoutResetDispatcher, gestureAvailabilityDispatcher);
+ internalProp, lockoutResetDispatcher, gestureAvailabilityDispatcher,
+ mBiometricContext);
mSensors.put(sensorId, sensor);
Slog.d(getTag(), "Added: " + internalProp);
@@ -298,6 +304,9 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
new FingerprintGetAuthenticatorIdClient(mContext,
mSensors.get(sensorId).getLazySession(), userId,
mContext.getOpPackageName(), sensorId,
+ createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
+ BiometricsProtoEnums.CLIENT_UNKNOWN),
+ mBiometricContext,
mSensors.get(sensorId).getAuthenticatorIds());
scheduleForSensor(sensorId, client);
});
@@ -307,6 +316,8 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
mHandler.post(() -> {
final InvalidationRequesterClient<Fingerprint> client =
new InvalidationRequesterClient<>(mContext, userId, sensorId,
+ BiometricLogger.ofUnknown(mContext),
+ mBiometricContext,
FingerprintUtils.getInstance(sensorId));
scheduleForSensor(sensorId, client);
});
@@ -317,7 +328,10 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
mHandler.post(() -> {
final FingerprintResetLockoutClient client = new FingerprintResetLockoutClient(
mContext, mSensors.get(sensorId).getLazySession(), userId,
- mContext.getOpPackageName(), sensorId, hardwareAuthToken,
+ mContext.getOpPackageName(), sensorId,
+ createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
+ BiometricsProtoEnums.CLIENT_UNKNOWN),
+ mBiometricContext, hardwareAuthToken,
mSensors.get(sensorId).getLockoutCache(), mLockoutResetDispatcher);
scheduleForSensor(sensorId, client);
});
@@ -331,7 +345,9 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
new FingerprintGenerateChallengeClient(mContext,
mSensors.get(sensorId).getLazySession(), token,
new ClientMonitorCallbackConverter(receiver), userId, opPackageName,
- sensorId);
+ sensorId, createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
+ BiometricsProtoEnums.CLIENT_UNKNOWN),
+ mBiometricContext);
scheduleForSensor(sensorId, client);
});
}
@@ -343,7 +359,10 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
final FingerprintRevokeChallengeClient client =
new FingerprintRevokeChallengeClient(mContext,
mSensors.get(sensorId).getLazySession(), token,
- userId, opPackageName, sensorId, challenge);
+ userId, opPackageName, sensorId,
+ createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
+ BiometricsProtoEnums.CLIENT_UNKNOWN),
+ mBiometricContext, challenge);
scheduleForSensor(sensorId, client);
});
}
@@ -361,6 +380,9 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
mSensors.get(sensorId).getLazySession(), token, id,
new ClientMonitorCallbackConverter(receiver), userId, hardwareAuthToken,
opPackageName, FingerprintUtils.getInstance(sensorId), sensorId,
+ createLogger(BiometricsProtoEnums.ACTION_ENROLL,
+ BiometricsProtoEnums.CLIENT_UNKNOWN),
+ mBiometricContext,
mSensors.get(sensorId).getSensorProperties(),
mUdfpsOverlayController, mSidefpsController, maxTemplatesPerUser, enrollReason);
scheduleForSensor(sensorId, client, new ClientMonitorCallback() {
@@ -399,8 +421,10 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
final boolean isStrongBiometric = Utils.isStrongBiometric(sensorId);
final FingerprintDetectClient client = new FingerprintDetectClient(mContext,
mSensors.get(sensorId).getLazySession(), token, id, callback, userId,
- opPackageName, sensorId, mUdfpsOverlayController, isStrongBiometric,
- statsClient);
+ opPackageName, sensorId,
+ createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient),
+ mBiometricContext,
+ mUdfpsOverlayController, isStrongBiometric);
scheduleForSensor(sensorId, client, mFingerprintStateCallback);
});
@@ -417,7 +441,9 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
final FingerprintAuthenticationClient client = new FingerprintAuthenticationClient(
mContext, mSensors.get(sensorId).getLazySession(), token, requestId, callback,
userId, operationId, restricted, opPackageName, cookie,
- false /* requireConfirmation */, sensorId, isStrongBiometric, statsClient,
+ false /* requireConfirmation */, sensorId,
+ createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient),
+ mBiometricContext, isStrongBiometric,
mTaskStackListener, mSensors.get(sensorId).getLockoutCache(),
mUdfpsOverlayController, mSidefpsController, allowBackgroundAuthentication,
mSensors.get(sensorId).getSensorProperties());
@@ -479,6 +505,9 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
mSensors.get(sensorId).getLazySession(), token,
new ClientMonitorCallbackConverter(receiver), fingerprintIds, userId,
opPackageName, FingerprintUtils.getInstance(sensorId), sensorId,
+ createLogger(BiometricsProtoEnums.ACTION_REMOVE,
+ BiometricsProtoEnums.CLIENT_UNKNOWN),
+ mBiometricContext,
mSensors.get(sensorId).getAuthenticatorIds());
scheduleForSensor(sensorId, client, mFingerprintStateCallback);
});
@@ -492,14 +521,22 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
final FingerprintInternalCleanupClient client =
new FingerprintInternalCleanupClient(mContext,
mSensors.get(sensorId).getLazySession(), userId,
- mContext.getOpPackageName(), sensorId, enrolledList,
- FingerprintUtils.getInstance(sensorId),
+ mContext.getOpPackageName(), sensorId,
+ createLogger(BiometricsProtoEnums.ACTION_ENUMERATE,
+ BiometricsProtoEnums.CLIENT_UNKNOWN),
+ mBiometricContext,
+ enrolledList, FingerprintUtils.getInstance(sensorId),
mSensors.get(sensorId).getAuthenticatorIds());
scheduleForSensor(sensorId, client, new ClientMonitorCompositeCallback(callback,
mFingerprintStateCallback));
});
}
+ private BiometricLogger createLogger(int statsAction, int statsClient) {
+ return new BiometricLogger(mContext, BiometricsProtoEnums.MODALITY_FINGERPRINT,
+ statsAction, statsClient);
+ }
+
@Override
public boolean isHardwareDetected(int sensorId) {
return hasHalInstance();
@@ -524,6 +561,9 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
final FingerprintInvalidationClient client =
new FingerprintInvalidationClient(mContext,
mSensors.get(sensorId).getLazySession(), userId, sensorId,
+ createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
+ BiometricsProtoEnums.CLIENT_UNKNOWN),
+ mBiometricContext,
mSensors.get(sensorId).getAuthenticatorIds(), callback);
scheduleForSensor(sensorId, client);
});
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRemovalClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRemovalClient.java
index fbc1dc08b726..d559bb1d72f1 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRemovalClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRemovalClient.java
@@ -19,12 +19,13 @@ package com.android.server.biometrics.sensors.fingerprint.aidl;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
-import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.fingerprint.Fingerprint;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.BiometricUtils;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.RemovalClient;
@@ -45,9 +46,10 @@ class FingerprintRemovalClient extends RemovalClient<Fingerprint, AidlSession> {
@NonNull Supplier<AidlSession> lazyDaemon, @NonNull IBinder token,
@Nullable ClientMonitorCallbackConverter listener, int[] biometricIds, int userId,
@NonNull String owner, @NonNull BiometricUtils<Fingerprint> utils, int sensorId,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
@NonNull Map<Integer, Long> authenticatorIds) {
super(context, lazyDaemon, token, listener, userId, owner, utils, sensorId,
- authenticatorIds, BiometricsProtoEnums.MODALITY_FINGERPRINT);
+ logger, biometricContext, authenticatorIds);
mBiometricIds = biometricIds;
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java
index 0e64dab5d325..f90cba79dac2 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java
@@ -18,7 +18,6 @@ package com.android.server.biometrics.sensors.fingerprint.aidl;
import android.annotation.NonNull;
import android.content.Context;
-import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.fingerprint.IFingerprint;
import android.hardware.keymaster.HardwareAuthToken;
import android.os.RemoteException;
@@ -26,6 +25,8 @@ import android.util.Slog;
import com.android.server.biometrics.BiometricsProto;
import com.android.server.biometrics.HardwareAuthTokenUtils;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.ErrorConsumer;
import com.android.server.biometrics.sensors.HalClientMonitor;
@@ -50,11 +51,11 @@ class FingerprintResetLockoutClient extends HalClientMonitor<AidlSession> implem
FingerprintResetLockoutClient(@NonNull Context context,
@NonNull Supplier<AidlSession> lazyDaemon, int userId, String owner, int sensorId,
+ @NonNull BiometricLogger biometricLogger, @NonNull BiometricContext biometricContext,
@NonNull byte[] hardwareAuthToken, @NonNull LockoutCache lockoutTracker,
@NonNull LockoutResetDispatcher lockoutResetDispatcher) {
super(context, lazyDaemon, null /* token */, null /* listener */, userId, owner,
- 0 /* cookie */, sensorId, BiometricsProtoEnums.MODALITY_UNKNOWN,
- BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN);
+ 0 /* cookie */, sensorId, biometricLogger, biometricContext);
mHardwareAuthToken = HardwareAuthTokenUtils.toHardwareAuthToken(hardwareAuthToken);
mLockoutCache = lockoutTracker;
mLockoutResetDispatcher = lockoutResetDispatcher;
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRevokeChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRevokeChallengeClient.java
index fd938677105c..afa62e2e3173 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRevokeChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRevokeChallengeClient.java
@@ -23,6 +23,8 @@ import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.RevokeChallengeClient;
import java.util.function.Supplier;
@@ -38,8 +40,10 @@ class FingerprintRevokeChallengeClient extends RevokeChallengeClient<AidlSession
FingerprintRevokeChallengeClient(@NonNull Context context,
@NonNull Supplier<AidlSession> lazyDaemon, @NonNull IBinder token,
- int userId, @NonNull String owner, int sensorId, long challenge) {
- super(context, lazyDaemon, token, userId, owner, sensorId);
+ int userId, @NonNull String owner, int sensorId,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
+ long challenge) {
+ super(context, lazyDaemon, token, userId, owner, sensorId, logger, biometricContext);
mChallenge = challenge;
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintStartUserClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintStartUserClient.java
index 9dc06e1e0665..52305a31a644 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintStartUserClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintStartUserClient.java
@@ -27,6 +27,8 @@ import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.StartUserClient;
@@ -40,9 +42,10 @@ public class FingerprintStartUserClient extends StartUserClient<IFingerprint, IS
public FingerprintStartUserClient(@NonNull Context context,
@NonNull Supplier<IFingerprint> lazyDaemon,
@Nullable IBinder token, int userId, int sensorId,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
@NonNull ISessionCallback sessionCallback,
@NonNull UserStartedCallback<ISession> callback) {
- super(context, lazyDaemon, token, userId, sensorId, callback);
+ super(context, lazyDaemon, token, userId, sensorId, logger, biometricContext, callback);
mSessionCallback = sessionCallback;
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintStopUserClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintStopUserClient.java
index fac17f2cc16a..2cc1879c0851 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintStopUserClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintStopUserClient.java
@@ -23,6 +23,8 @@ import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.StopUserClient;
@@ -33,8 +35,10 @@ public class FingerprintStopUserClient extends StopUserClient<AidlSession> {
public FingerprintStopUserClient(@NonNull Context context,
@NonNull Supplier<AidlSession> lazyDaemon, @Nullable IBinder token, int userId,
- int sensorId, @NonNull UserStoppedCallback callback) {
- super(context, lazyDaemon, token, userId, sensorId, callback);
+ int sensorId,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
+ @NonNull UserStoppedCallback callback) {
+ super(context, lazyDaemon, token, userId, sensorId, logger, biometricContext, callback);
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java
index 22762329926b..63e345e40ad7 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java
@@ -39,12 +39,15 @@ import android.os.UserManager;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.FrameworkStatsLog;
import com.android.server.biometrics.HardwareAuthTokenUtils;
import com.android.server.biometrics.SensorServiceStateProto;
import com.android.server.biometrics.SensorStateProto;
import com.android.server.biometrics.UserStateProto;
import com.android.server.biometrics.Utils;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.AcquisitionClient;
import com.android.server.biometrics.sensors.AuthenticationConsumer;
import com.android.server.biometrics.sensors.BaseClientMonitor;
@@ -72,7 +75,7 @@ import java.util.function.Supplier;
* {@link android.hardware.biometrics.fingerprint.IFingerprint} HAL.
*/
@SuppressWarnings("deprecation")
-class Sensor {
+public class Sensor {
private boolean mTestHalEnabled;
@@ -89,7 +92,8 @@ class Sensor {
@Nullable private AidlSession mCurrentSession;
@NonNull private final Supplier<AidlSession> mLazySession;
- static class HalSessionCallback extends ISessionCallback.Stub {
+ @VisibleForTesting
+ public static class HalSessionCallback extends ISessionCallback.Stub {
/**
* Interface to sends results to the HalSessionCallback's owner.
@@ -425,7 +429,8 @@ class Sensor {
Sensor(@NonNull String tag, @NonNull FingerprintProvider provider, @NonNull Context context,
@NonNull Handler handler, @NonNull FingerprintSensorPropertiesInternal sensorProperties,
@NonNull LockoutResetDispatcher lockoutResetDispatcher,
- @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher) {
+ @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher,
+ @NonNull BiometricContext biometricContext) {
mTag = tag;
mProvider = provider;
mContext = context;
@@ -442,7 +447,9 @@ class Sensor {
@Override
public StopUserClient<?> getStopUserClient(int userId) {
return new FingerprintStopUserClient(mContext, mLazySession, mToken,
- userId, mSensorProperties.sensorId, () -> mCurrentSession = null);
+ userId, mSensorProperties.sensorId,
+ BiometricLogger.ofUnknown(mContext), biometricContext,
+ () -> mCurrentSession = null);
}
@NonNull
@@ -478,6 +485,7 @@ class Sensor {
return new FingerprintStartUserClient(mContext, provider::getHalInstance,
mToken, newUserId, mSensorProperties.sensorId,
+ BiometricLogger.ofUnknown(mContext), biometricContext,
resultController, userStartedCallback);
}
});
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
index 29d460fc1204..9d60859a4a21 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
@@ -57,6 +57,8 @@ import com.android.server.biometrics.Utils;
import com.android.server.biometrics.fingerprint.FingerprintServiceDumpProto;
import com.android.server.biometrics.fingerprint.FingerprintUserStatsProto;
import com.android.server.biometrics.fingerprint.PerformanceStatsProto;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.AcquisitionClient;
import com.android.server.biometrics.sensors.AuthenticationClient;
import com.android.server.biometrics.sensors.AuthenticationConsumer;
@@ -118,6 +120,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
@NonNull private final HalResultController mHalResultController;
@Nullable private IUdfpsOverlayController mUdfpsOverlayController;
@Nullable private ISidefpsController mSidefpsController;
+ @NonNull private final BiometricContext mBiometricContext;
// for requests that do not use biometric prompt
@NonNull private final AtomicLong mRequestCounter = new AtomicLong(0);
private int mCurrentUserId = UserHandle.USER_NULL;
@@ -318,15 +321,18 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
}
}
+ @VisibleForTesting
Fingerprint21(@NonNull Context context,
@NonNull FingerprintStateCallback fingerprintStateCallback,
@NonNull FingerprintSensorPropertiesInternal sensorProps,
@NonNull BiometricScheduler scheduler,
@NonNull Handler handler,
@NonNull LockoutResetDispatcher lockoutResetDispatcher,
- @NonNull HalResultController controller) {
+ @NonNull HalResultController controller,
+ @NonNull BiometricContext biometricContext) {
mContext = context;
mFingerprintStateCallback = fingerprintStateCallback;
+ mBiometricContext = biometricContext;
mSensorProperties = sensorProps;
mSensorId = sensorProps.sensorId;
@@ -368,7 +374,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
final HalResultController controller = new HalResultController(sensorProps.sensorId,
context, handler, scheduler);
return new Fingerprint21(context, fingerprintStateCallback, sensorProps, scheduler, handler,
- lockoutResetDispatcher, controller);
+ lockoutResetDispatcher, controller, BiometricContext.getInstance(context));
}
@Override
@@ -493,6 +499,9 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
final FingerprintUpdateActiveUserClient client =
new FingerprintUpdateActiveUserClient(mContext, mLazyDaemon, targetUserId,
mContext.getOpPackageName(), mSensorProperties.sensorId,
+ createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
+ BiometricsProtoEnums.CLIENT_UNKNOWN),
+ mBiometricContext,
this::getCurrentUser, hasEnrolled, mAuthenticatorIds, force);
mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() {
@Override
@@ -536,7 +545,10 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
// thread.
mHandler.post(() -> {
final FingerprintResetLockoutClient client = new FingerprintResetLockoutClient(mContext,
- userId, mContext.getOpPackageName(), sensorId, mLockoutTracker);
+ userId, mContext.getOpPackageName(), sensorId,
+ createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
+ BiometricsProtoEnums.CLIENT_UNKNOWN),
+ mBiometricContext, mLockoutTracker);
mScheduler.scheduleClientMonitor(client);
});
}
@@ -548,7 +560,10 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
final FingerprintGenerateChallengeClient client =
new FingerprintGenerateChallengeClient(mContext, mLazyDaemon, token,
new ClientMonitorCallbackConverter(receiver), userId, opPackageName,
- mSensorProperties.sensorId);
+ mSensorProperties.sensorId,
+ createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
+ BiometricsProtoEnums.CLIENT_UNKNOWN),
+ mBiometricContext);
mScheduler.scheduleClientMonitor(client);
});
}
@@ -559,7 +574,10 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
mHandler.post(() -> {
final FingerprintRevokeChallengeClient client = new FingerprintRevokeChallengeClient(
mContext, mLazyDaemon, token, userId, opPackageName,
- mSensorProperties.sensorId);
+ mSensorProperties.sensorId,
+ createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
+ BiometricsProtoEnums.CLIENT_UNKNOWN),
+ mBiometricContext);
mScheduler.scheduleClientMonitor(client);
});
}
@@ -577,7 +595,11 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
mLazyDaemon, token, id, new ClientMonitorCallbackConverter(receiver),
userId, hardwareAuthToken, opPackageName,
FingerprintUtils.getLegacyInstance(mSensorId), ENROLL_TIMEOUT_SEC,
- mSensorProperties.sensorId, mUdfpsOverlayController, mSidefpsController,
+ mSensorProperties.sensorId,
+ createLogger(BiometricsProtoEnums.ACTION_ENROLL,
+ BiometricsProtoEnums.CLIENT_UNKNOWN),
+ mBiometricContext,
+ mUdfpsOverlayController, mSidefpsController,
enrollReason);
mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() {
@Override
@@ -616,8 +638,10 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
final boolean isStrongBiometric = Utils.isStrongBiometric(mSensorProperties.sensorId);
final FingerprintDetectClient client = new FingerprintDetectClient(mContext,
mLazyDaemon, token, id, listener, userId, opPackageName,
- mSensorProperties.sensorId, mUdfpsOverlayController, isStrongBiometric,
- statsClient);
+ mSensorProperties.sensorId,
+ createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient),
+ mBiometricContext, mUdfpsOverlayController,
+ isStrongBiometric);
mScheduler.scheduleClientMonitor(client, mFingerprintStateCallback);
});
@@ -636,7 +660,9 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
final FingerprintAuthenticationClient client = new FingerprintAuthenticationClient(
mContext, mLazyDaemon, token, requestId, listener, userId, operationId,
restricted, opPackageName, cookie, false /* requireConfirmation */,
- mSensorProperties.sensorId, isStrongBiometric, statsClient,
+ mSensorProperties.sensorId,
+ createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient),
+ mBiometricContext, isStrongBiometric,
mTaskStackListener, mLockoutTracker,
mUdfpsOverlayController, mSidefpsController,
allowBackgroundAuthentication, mSensorProperties);
@@ -678,7 +704,10 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
final FingerprintRemovalClient client = new FingerprintRemovalClient(mContext,
mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), fingerId,
userId, opPackageName, FingerprintUtils.getLegacyInstance(mSensorId),
- mSensorProperties.sensorId, mAuthenticatorIds);
+ mSensorProperties.sensorId,
+ createLogger(BiometricsProtoEnums.ACTION_REMOVE,
+ BiometricsProtoEnums.CLIENT_UNKNOWN),
+ mBiometricContext, mAuthenticatorIds);
mScheduler.scheduleClientMonitor(client, mFingerprintStateCallback);
});
}
@@ -695,7 +724,10 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver),
0 /* fingerprintId */, userId, opPackageName,
FingerprintUtils.getLegacyInstance(mSensorId),
- mSensorProperties.sensorId, mAuthenticatorIds);
+ mSensorProperties.sensorId,
+ createLogger(BiometricsProtoEnums.ACTION_REMOVE,
+ BiometricsProtoEnums.CLIENT_UNKNOWN),
+ mBiometricContext, mAuthenticatorIds);
mScheduler.scheduleClientMonitor(client, mFingerprintStateCallback);
});
}
@@ -709,7 +741,10 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
mSensorProperties.sensorId, userId);
final FingerprintInternalCleanupClient client = new FingerprintInternalCleanupClient(
mContext, mLazyDaemon, userId, mContext.getOpPackageName(),
- mSensorProperties.sensorId, enrolledList,
+ mSensorProperties.sensorId,
+ createLogger(BiometricsProtoEnums.ACTION_ENUMERATE,
+ BiometricsProtoEnums.CLIENT_UNKNOWN),
+ mBiometricContext, enrolledList,
FingerprintUtils.getLegacyInstance(mSensorId), mAuthenticatorIds);
mScheduler.scheduleClientMonitor(client, callback);
});
@@ -722,6 +757,11 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
mFingerprintStateCallback));
}
+ private BiometricLogger createLogger(int statsAction, int statsClient) {
+ return new BiometricLogger(mContext, BiometricsProtoEnums.MODALITY_FINGERPRINT,
+ statsAction, statsClient);
+ }
+
@Override
public boolean isHardwareDetected(int sensorId) {
return getDaemon() != null;
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java
index 1694bd92c73c..149526f21fdb 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java
@@ -37,6 +37,7 @@ import android.util.Slog;
import android.util.SparseBooleanArray;
import com.android.internal.R;
+import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.sensors.AuthenticationConsumer;
import com.android.server.biometrics.sensors.BaseClientMonitor;
import com.android.server.biometrics.sensors.BiometricScheduler;
@@ -247,7 +248,8 @@ public class Fingerprint21UdfpsMock extends Fingerprint21 implements TrustManage
@NonNull FingerprintStateCallback fingerprintStateCallback,
@NonNull FingerprintSensorPropertiesInternal sensorProps,
@NonNull LockoutResetDispatcher lockoutResetDispatcher,
- @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher) {
+ @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher,
+ @NonNull BiometricContext biometricContext) {
Slog.d(TAG, "Creating Fingerprint23Mock!");
final Handler handler = new Handler(Looper.getMainLooper());
@@ -256,7 +258,7 @@ public class Fingerprint21UdfpsMock extends Fingerprint21 implements TrustManage
final MockHalResultController controller =
new MockHalResultController(sensorProps.sensorId, context, handler, scheduler);
return new Fingerprint21UdfpsMock(context, fingerprintStateCallback, sensorProps, scheduler,
- handler, lockoutResetDispatcher, controller);
+ handler, lockoutResetDispatcher, controller, biometricContext);
}
private static abstract class FakeFingerRunnable implements Runnable {
@@ -385,9 +387,10 @@ public class Fingerprint21UdfpsMock extends Fingerprint21 implements TrustManage
@NonNull TestableBiometricScheduler scheduler,
@NonNull Handler handler,
@NonNull LockoutResetDispatcher lockoutResetDispatcher,
- @NonNull MockHalResultController controller) {
+ @NonNull MockHalResultController controller,
+ @NonNull BiometricContext biometricContext) {
super(context, fingerprintStateCallback, sensorProps, scheduler, handler,
- lockoutResetDispatcher, controller);
+ lockoutResetDispatcher, controller, biometricContext);
mScheduler = scheduler;
mScheduler.init(this);
mHandler = handler;
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java
index 589bfcfba992..97fbb5f6e4fe 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java
@@ -23,7 +23,6 @@ import android.content.Context;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricFingerprintConstants;
-import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.hardware.fingerprint.ISidefpsController;
@@ -32,6 +31,8 @@ import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.log.CallbackWithProbe;
import com.android.server.biometrics.log.Probe;
import com.android.server.biometrics.sensors.AuthenticationClient;
@@ -69,7 +70,8 @@ class FingerprintAuthenticationClient extends AuthenticationClient<IBiometricsFi
@NonNull IBinder token, long requestId,
@NonNull ClientMonitorCallbackConverter listener, int targetUserId, long operationId,
boolean restricted, @NonNull String owner, int cookie, boolean requireConfirmation,
- int sensorId, boolean isStrongBiometric, int statsClient,
+ int sensorId, @NonNull BiometricLogger logger,
+ @NonNull BiometricContext biometricContext, boolean isStrongBiometric,
@NonNull TaskStackListener taskStackListener,
@NonNull LockoutFrameworkImpl lockoutTracker,
@Nullable IUdfpsOverlayController udfpsOverlayController,
@@ -77,10 +79,9 @@ class FingerprintAuthenticationClient extends AuthenticationClient<IBiometricsFi
boolean allowBackgroundAuthentication,
@NonNull FingerprintSensorPropertiesInternal sensorProps) {
super(context, lazyDaemon, token, listener, targetUserId, operationId, restricted,
- owner, cookie, requireConfirmation, sensorId, isStrongBiometric,
- BiometricsProtoEnums.MODALITY_FINGERPRINT, statsClient, taskStackListener,
- lockoutTracker, allowBackgroundAuthentication, true /* shouldVibrate */,
- false /* isKeyguardBypassEnabled */);
+ owner, cookie, requireConfirmation, sensorId, logger, biometricContext,
+ isStrongBiometric, taskStackListener, lockoutTracker, allowBackgroundAuthentication,
+ true /* shouldVibrate */, false /* isKeyguardBypassEnabled */);
setRequestId(requestId);
mLockoutFrameworkImpl = lockoutTracker;
mSensorOverlays = new SensorOverlays(udfpsOverlayController, sidefpsController);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java
index 88487462e51a..c2929d0f15b2 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java
@@ -22,7 +22,6 @@ import android.content.Context;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricFingerprintConstants;
import android.hardware.biometrics.BiometricOverlayConstants;
-import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
import android.hardware.fingerprint.IUdfpsOverlayController;
import android.os.IBinder;
@@ -30,6 +29,8 @@ import android.os.RemoteException;
import android.util.Slog;
import com.android.server.biometrics.BiometricsProto;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.AcquisitionClient;
import com.android.server.biometrics.sensors.AuthenticationConsumer;
import com.android.server.biometrics.sensors.ClientMonitorCallback;
@@ -59,11 +60,11 @@ class FingerprintDetectClient extends AcquisitionClient<IBiometricsFingerprint>
@NonNull Supplier<IBiometricsFingerprint> lazyDaemon,
@NonNull IBinder token, long requestId,
@NonNull ClientMonitorCallbackConverter listener, int userId, @NonNull String owner,
- int sensorId, @Nullable IUdfpsOverlayController udfpsOverlayController,
- boolean isStrongBiometric, int statsClient) {
+ int sensorId,
+ @NonNull BiometricLogger biometricLogger, @NonNull BiometricContext biometricContext,
+ @Nullable IUdfpsOverlayController udfpsOverlayController, boolean isStrongBiometric) {
super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId,
- true /* shouldVibrate */, BiometricsProtoEnums.MODALITY_FINGERPRINT,
- BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient);
+ true /* shouldVibrate */, biometricLogger, biometricContext);
setRequestId(requestId);
mSensorOverlays = new SensorOverlays(udfpsOverlayController, null /* sideFpsController */);
mIsStrongBiometric = isStrongBiometric;
@@ -129,8 +130,9 @@ class FingerprintDetectClient extends AcquisitionClient<IBiometricsFingerprint>
@Override
public void onAuthenticated(BiometricAuthenticator.Identifier identifier, boolean authenticated,
ArrayList<Byte> hardwareAuthToken) {
- getLogger().logOnAuthenticated(getContext(), authenticated, false /* requireConfirmation */,
- isCryptoOperation(), getTargetUserId(), false /* isBiometricPrompt */);
+ getLogger().logOnAuthenticated(getContext(), getOperationContext(),
+ authenticated, false /* requireConfirmation */,
+ getTargetUserId(), false /* isBiometricPrompt */);
// Do not distinguish between success/failures.
vibrateSuccess();
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java
index c69deac371d9..1d478e53fd38 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java
@@ -21,7 +21,6 @@ import android.annotation.Nullable;
import android.content.Context;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricFingerprintConstants;
-import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
import android.hardware.fingerprint.Fingerprint;
import android.hardware.fingerprint.FingerprintManager;
@@ -31,6 +30,8 @@ import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.BiometricNotificationUtils;
import com.android.server.biometrics.sensors.BiometricUtils;
import com.android.server.biometrics.sensors.ClientMonitorCallback;
@@ -62,12 +63,13 @@ public class FingerprintEnrollClient extends EnrollClient<IBiometricsFingerprint
long requestId, @NonNull ClientMonitorCallbackConverter listener, int userId,
@NonNull byte[] hardwareAuthToken, @NonNull String owner,
@NonNull BiometricUtils<Fingerprint> utils, int timeoutSec, int sensorId,
+ @NonNull BiometricLogger biometricLogger, @NonNull BiometricContext biometricContext,
@Nullable IUdfpsOverlayController udfpsOverlayController,
@Nullable ISidefpsController sidefpsController,
@FingerprintManager.EnrollReason int enrollReason) {
super(context, lazyDaemon, token, listener, userId, hardwareAuthToken, owner, utils,
- timeoutSec, BiometricsProtoEnums.MODALITY_FINGERPRINT, sensorId,
- true /* shouldVibrate */);
+ timeoutSec, sensorId, true /* shouldVibrate */, biometricLogger,
+ biometricContext);
setRequestId(requestId);
mSensorOverlays = new SensorOverlays(udfpsOverlayController, sidefpsController);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintGenerateChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintGenerateChallengeClient.java
index 591f542396ef..3bb7135a3e06 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintGenerateChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintGenerateChallengeClient.java
@@ -23,6 +23,8 @@ import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.GenerateChallengeClient;
@@ -41,8 +43,10 @@ public class FingerprintGenerateChallengeClient
FingerprintGenerateChallengeClient(@NonNull Context context,
@NonNull Supplier<IBiometricsFingerprint> lazyDaemon, @NonNull IBinder token,
@NonNull ClientMonitorCallbackConverter listener, int userId, @NonNull String owner,
- int sensorId) {
- super(context, lazyDaemon, token, listener, userId, owner, sensorId);
+ int sensorId, @NonNull BiometricLogger logger,
+ @NonNull BiometricContext biometricContext) {
+ super(context, lazyDaemon, token, listener, userId, owner, sensorId, logger,
+ biometricContext);
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintInternalCleanupClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintInternalCleanupClient.java
index 403602b8839d..5e7cf3578411 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintInternalCleanupClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintInternalCleanupClient.java
@@ -18,11 +18,12 @@ package com.android.server.biometrics.sensors.fingerprint.hidl;
import android.annotation.NonNull;
import android.content.Context;
-import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
import android.hardware.fingerprint.Fingerprint;
import android.os.IBinder;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.BiometricUtils;
import com.android.server.biometrics.sensors.InternalCleanupClient;
import com.android.server.biometrics.sensors.InternalEnumerateClient;
@@ -42,31 +43,35 @@ class FingerprintInternalCleanupClient
FingerprintInternalCleanupClient(@NonNull Context context,
@NonNull Supplier<IBiometricsFingerprint> lazyDaemon, int userId,
- @NonNull String owner, int sensorId, @NonNull List<Fingerprint> enrolledList,
+ @NonNull String owner, int sensorId,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
+ @NonNull List<Fingerprint> enrolledList,
@NonNull BiometricUtils<Fingerprint> utils,
@NonNull Map<Integer, Long> authenticatorIds) {
- super(context, lazyDaemon, userId, owner, sensorId,
- BiometricsProtoEnums.MODALITY_FINGERPRINT, enrolledList, utils, authenticatorIds);
+ super(context, lazyDaemon, userId, owner, sensorId, logger, biometricContext,
+ enrolledList, utils, authenticatorIds);
}
@Override
protected InternalEnumerateClient<IBiometricsFingerprint> getEnumerateClient(
Context context, Supplier<IBiometricsFingerprint> lazyDaemon, IBinder token,
int userId, String owner, List<Fingerprint> enrolledList,
- BiometricUtils<Fingerprint> utils, int sensorId) {
+ BiometricUtils<Fingerprint> utils, int sensorId,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext) {
return new FingerprintInternalEnumerateClient(context, lazyDaemon, token, userId, owner,
- enrolledList, utils, sensorId);
+ enrolledList, utils, sensorId, logger, biometricContext);
}
@Override
protected RemovalClient<Fingerprint, IBiometricsFingerprint> getRemovalClient(Context context,
Supplier<IBiometricsFingerprint> lazyDaemon, IBinder token,
int biometricId, int userId, String owner, BiometricUtils<Fingerprint> utils,
- int sensorId, Map<Integer, Long> authenticatorIds) {
+ int sensorId, @NonNull BiometricLogger logger,
+ @NonNull BiometricContext biometricContext, Map<Integer, Long> authenticatorIds) {
// Internal remove does not need to send results to anyone. Cleanup (enumerate + remove)
// is all done internally.
return new FingerprintRemovalClient(context, lazyDaemon, token,
null /* ClientMonitorCallbackConverter */, biometricId, userId, owner, utils,
- sensorId, authenticatorIds);
+ sensorId, logger, biometricContext, authenticatorIds);
}
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintInternalEnumerateClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintInternalEnumerateClient.java
index def8ed0735f5..0840f1b36903 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintInternalEnumerateClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintInternalEnumerateClient.java
@@ -18,13 +18,14 @@ package com.android.server.biometrics.sensors.fingerprint.hidl;
import android.annotation.NonNull;
import android.content.Context;
-import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
import android.hardware.fingerprint.Fingerprint;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.BiometricUtils;
import com.android.server.biometrics.sensors.InternalEnumerateClient;
@@ -42,9 +43,10 @@ class FingerprintInternalEnumerateClient extends InternalEnumerateClient<IBiomet
FingerprintInternalEnumerateClient(@NonNull Context context,
@NonNull Supplier<IBiometricsFingerprint> lazyDaemon, @NonNull IBinder token,
int userId, @NonNull String owner, @NonNull List<Fingerprint> enrolledList,
- @NonNull BiometricUtils<Fingerprint> utils, int sensorId) {
+ @NonNull BiometricUtils<Fingerprint> utils, int sensorId,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext) {
super(context, lazyDaemon, token, userId, owner, enrolledList, utils, sensorId,
- BiometricsProtoEnums.MODALITY_FINGERPRINT);
+ logger, biometricContext);
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintRemovalClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintRemovalClient.java
index 77c201c5ec97..9ec56c2a7dcd 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintRemovalClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintRemovalClient.java
@@ -18,13 +18,14 @@ package com.android.server.biometrics.sensors.fingerprint.hidl;
import android.annotation.NonNull;
import android.content.Context;
-import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
import android.hardware.fingerprint.Fingerprint;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.BiometricUtils;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.RemovalClient;
@@ -46,9 +47,10 @@ class FingerprintRemovalClient extends RemovalClient<Fingerprint, IBiometricsFin
@NonNull Supplier<IBiometricsFingerprint> lazyDaemon, @NonNull IBinder token,
@NonNull ClientMonitorCallbackConverter listener, int biometricId, int userId,
@NonNull String owner, @NonNull BiometricUtils<Fingerprint> utils, int sensorId,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
@NonNull Map<Integer, Long> authenticatorIds) {
super(context, lazyDaemon, token, listener, userId, owner, utils, sensorId,
- authenticatorIds, BiometricsProtoEnums.MODALITY_FINGERPRINT);
+ logger, biometricContext, authenticatorIds);
mBiometricId = biometricId;
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintResetLockoutClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintResetLockoutClient.java
index ed28e3ff481e..559ca0633c42 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintResetLockoutClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintResetLockoutClient.java
@@ -18,9 +18,10 @@ package com.android.server.biometrics.sensors.fingerprint.hidl;
import android.annotation.NonNull;
import android.content.Context;
-import android.hardware.biometrics.BiometricsProtoEnums;
import com.android.server.biometrics.BiometricsProto;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.BaseClientMonitor;
import com.android.server.biometrics.sensors.ClientMonitorCallback;
@@ -33,10 +34,11 @@ public class FingerprintResetLockoutClient extends BaseClientMonitor {
@NonNull final LockoutFrameworkImpl mLockoutTracker;
public FingerprintResetLockoutClient(@NonNull Context context, int userId,
- @NonNull String owner, int sensorId, @NonNull LockoutFrameworkImpl lockoutTracker) {
+ @NonNull String owner, int sensorId,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
+ @NonNull LockoutFrameworkImpl lockoutTracker) {
super(context, null /* token */, null /* listener */, userId, owner, 0 /* cookie */,
- sensorId, BiometricsProtoEnums.MODALITY_UNKNOWN,
- BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN);
+ sensorId, logger, biometricContext);
mLockoutTracker = lockoutTracker;
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintRevokeChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintRevokeChallengeClient.java
index 0180a466d7d6..6273417eb0db 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintRevokeChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintRevokeChallengeClient.java
@@ -23,6 +23,8 @@ import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.RevokeChallengeClient;
import java.util.function.Supplier;
@@ -39,8 +41,9 @@ public class FingerprintRevokeChallengeClient
FingerprintRevokeChallengeClient(@NonNull Context context,
@NonNull Supplier<IBiometricsFingerprint> lazyDaemon, @NonNull IBinder token,
- int userId, @NonNull String owner, int sensorId) {
- super(context, lazyDaemon, token, userId, owner, sensorId);
+ int userId, @NonNull String owner, int sensorId,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext) {
+ super(context, lazyDaemon, token, userId, owner, sensorId, logger, biometricContext);
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintUpdateActiveUserClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintUpdateActiveUserClient.java
index cb9c33eb9956..a4e602553101 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintUpdateActiveUserClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintUpdateActiveUserClient.java
@@ -18,7 +18,6 @@ package com.android.server.biometrics.sensors.fingerprint.hidl;
import android.annotation.NonNull;
import android.content.Context;
-import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
import android.os.Build;
import android.os.Environment;
@@ -27,6 +26,8 @@ import android.os.SELinux;
import android.util.Slog;
import com.android.server.biometrics.BiometricsProto;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.HalClientMonitor;
@@ -50,12 +51,13 @@ public class FingerprintUpdateActiveUserClient extends HalClientMonitor<IBiometr
FingerprintUpdateActiveUserClient(@NonNull Context context,
@NonNull Supplier<IBiometricsFingerprint> lazyDaemon, int userId,
- @NonNull String owner, int sensorId, Supplier<Integer> currentUserId,
+ @NonNull String owner, int sensorId,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
+ Supplier<Integer> currentUserId,
boolean hasEnrolledBiometrics, @NonNull Map<Integer, Long> authenticatorIds,
boolean forceUpdateAuthenticatorId) {
super(context, lazyDaemon, null /* token */, null /* listener */, userId, owner,
- 0 /* cookie */, sensorId, BiometricsProtoEnums.MODALITY_UNKNOWN,
- BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN);
+ 0 /* cookie */, sensorId, logger, biometricContext);
mCurrentUserId = currentUserId;
mForceUpdateAuthenticatorId = forceUpdateAuthenticatorId;
mHasEnrolledBiometrics = hasEnrolledBiometrics;
diff --git a/services/core/java/com/android/server/camera/CameraServiceProxy.java b/services/core/java/com/android/server/camera/CameraServiceProxy.java
index 1e00ea9161a8..1b0341c1ce26 100644
--- a/services/core/java/com/android/server/camera/CameraServiceProxy.java
+++ b/services/core/java/com/android/server/camera/CameraServiceProxy.java
@@ -308,7 +308,8 @@ public class CameraServiceProxy extends SystemService
public void onFixedRotationFinished(int displayId) { }
@Override
- public void onKeepClearAreasChanged(int displayId, List<Rect> keepClearArea) { }
+ public void onKeepClearAreasChanged(int displayId, List<Rect> restricted,
+ List<Rect> unrestricted) { }
}
diff --git a/services/core/java/com/android/server/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java
index 5b76695ab0da..28508f46bfe6 100644
--- a/services/core/java/com/android/server/clipboard/ClipboardService.java
+++ b/services/core/java/com/android/server/clipboard/ClipboardService.java
@@ -654,7 +654,7 @@ public class ClipboardService extends SystemService {
final boolean canCopyIntoProfile = !hasRestriction(
UserManager.DISALLOW_SHARE_INTO_MANAGED_PROFILE, id);
if (canCopyIntoProfile) {
- setPrimaryClipInternalLocked(
+ setPrimaryClipInternalNoClassifyLocked(
getClipboardLocked(id), clip, uid, sourcePackage);
}
}
diff --git a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
index c2ca3a502153..d0e39ccb3214 100644
--- a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
+++ b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
@@ -23,7 +23,6 @@ import static android.hardware.devicestate.DeviceStateManager.MINIMUM_DEVICE_STA
import static com.android.server.devicestate.DeviceState.FLAG_CANCEL_OVERRIDE_REQUESTS;
import static com.android.server.devicestate.OverrideRequestController.STATUS_ACTIVE;
import static com.android.server.devicestate.OverrideRequestController.STATUS_CANCELED;
-import static com.android.server.devicestate.OverrideRequestController.STATUS_SUSPENDED;
import android.annotation.IntDef;
import android.annotation.IntRange;
@@ -348,7 +347,7 @@ public final class DeviceStateManagerService extends SystemService {
mBaseState = Optional.of(baseState);
if (baseState.hasFlag(FLAG_CANCEL_OVERRIDE_REQUESTS)) {
- mOverrideRequestController.cancelOverrideRequests();
+ mOverrideRequestController.cancelOverrideRequest();
}
mOverrideRequestController.handleBaseStateChanged();
updatePendingStateLocked();
@@ -503,7 +502,7 @@ public final class DeviceStateManagerService extends SystemService {
@OverrideRequestController.RequestStatus int status) {
if (status == STATUS_ACTIVE) {
mActiveOverride = Optional.of(request);
- } else if (status == STATUS_SUSPENDED || status == STATUS_CANCELED) {
+ } else if (status == STATUS_CANCELED) {
if (mActiveOverride.isPresent() && mActiveOverride.get() == request) {
mActiveOverride = Optional.empty();
}
@@ -528,8 +527,6 @@ public final class DeviceStateManagerService extends SystemService {
// Schedule the notification now.
processRecord.notifyRequestActiveAsync(request.getToken());
}
- } else if (status == STATUS_SUSPENDED) {
- processRecord.notifyRequestSuspendedAsync(request.getToken());
} else {
processRecord.notifyRequestCanceledAsync(request.getToken());
}
@@ -595,15 +592,14 @@ public final class DeviceStateManagerService extends SystemService {
}
}
- private void cancelRequestInternal(int callingPid, @NonNull IBinder token) {
+ private void cancelStateRequestInternal(int callingPid) {
synchronized (mLock) {
final ProcessRecord processRecord = mProcessRecords.get(callingPid);
if (processRecord == null) {
throw new IllegalStateException("Process " + callingPid
+ " has no registered callback.");
}
-
- mOverrideRequestController.cancelRequest(token);
+ mActiveOverride.ifPresent(mOverrideRequestController::cancelRequest);
}
}
@@ -628,6 +624,23 @@ public final class DeviceStateManagerService extends SystemService {
}
}
+ /**
+ * Allow top processes to request or cancel a device state change. If the calling process ID is
+ * not the top app, then check if this process holds the CONTROL_DEVICE_STATE permission.
+ * @param callingPid
+ */
+ private void checkCanControlDeviceState(int callingPid) {
+ // Allow top processes to request a device state change
+ // If the calling process ID is not the top app, then we check if this process
+ // holds a permission to CONTROL_DEVICE_STATE
+ final WindowProcessController topApp = mActivityTaskManagerInternal.getTopApp();
+ if (topApp == null || topApp.getPid() != callingPid) {
+ getContext().enforceCallingOrSelfPermission(CONTROL_DEVICE_STATE,
+ "Permission required to request device state, "
+ + "or the call must come from the top focused app.");
+ }
+ }
+
private final class DeviceStateProviderListener implements DeviceStateProvider.Listener {
@Override
public void onSupportedDeviceStatesChanged(DeviceState[] newDeviceStates) {
@@ -716,24 +729,6 @@ public final class DeviceStateManagerService extends SystemService {
});
}
- public void notifyRequestSuspendedAsync(IBinder token) {
- @RequestStatus Integer lastStatus = mLastNotifiedStatus.get(token);
- if (lastStatus != null
- && (lastStatus == STATUS_SUSPENDED || lastStatus == STATUS_CANCELED)) {
- return;
- }
-
- mLastNotifiedStatus.put(token, STATUS_SUSPENDED);
- mHandler.post(() -> {
- try {
- mCallback.onRequestSuspended(token);
- } catch (RemoteException ex) {
- Slog.w(TAG, "Failed to notify process " + mPid + " that request state changed.",
- ex);
- }
- });
- }
-
public void notifyRequestCanceledAsync(IBinder token) {
@RequestStatus Integer lastStatus = mLastNotifiedStatus.get(token);
if (lastStatus != null && lastStatus == STATUS_CANCELED) {
@@ -782,12 +777,7 @@ public final class DeviceStateManagerService extends SystemService {
// Allow top processes to request a device state change
// If the calling process ID is not the top app, then we check if this process
// holds a permission to CONTROL_DEVICE_STATE
- final WindowProcessController topApp = mActivityTaskManagerInternal.getTopApp();
- if (topApp.getPid() != callingPid) {
- getContext().enforceCallingOrSelfPermission(CONTROL_DEVICE_STATE,
- "Permission required to request device state, "
- + "or the call must come from the top focused app.");
- }
+ checkCanControlDeviceState(callingPid);
if (token == null) {
throw new IllegalArgumentException("Request token must not be null.");
@@ -802,25 +792,16 @@ public final class DeviceStateManagerService extends SystemService {
}
@Override // Binder call
- public void cancelRequest(IBinder token) {
+ public void cancelStateRequest() {
final int callingPid = Binder.getCallingPid();
// Allow top processes to cancel a device state change
// If the calling process ID is not the top app, then we check if this process
// holds a permission to CONTROL_DEVICE_STATE
- final WindowProcessController topApp = mActivityTaskManagerInternal.getTopApp();
- if (topApp.getPid() != callingPid) {
- getContext().enforceCallingOrSelfPermission(CONTROL_DEVICE_STATE,
- "Permission required to cancel device state, "
- + "or the call must come from the top focused app.");
- }
-
- if (token == null) {
- throw new IllegalArgumentException("Request token must not be null.");
- }
+ checkCanControlDeviceState(callingPid);
final long callingIdentity = Binder.clearCallingIdentity();
try {
- cancelRequestInternal(callingPid, token);
+ cancelStateRequestInternal(callingPid);
} finally {
Binder.restoreCallingIdentity(callingIdentity);
}
diff --git a/services/core/java/com/android/server/devicestate/DeviceStateManagerShellCommand.java b/services/core/java/com/android/server/devicestate/DeviceStateManagerShellCommand.java
index eed68f8b1300..659ee75de453 100644
--- a/services/core/java/com/android/server/devicestate/DeviceStateManagerShellCommand.java
+++ b/services/core/java/com/android/server/devicestate/DeviceStateManagerShellCommand.java
@@ -97,7 +97,7 @@ public class DeviceStateManagerShellCommand extends ShellCommand {
try {
if ("reset".equals(nextArg)) {
if (sLastRequest != null) {
- mClient.cancelRequest(sLastRequest);
+ mClient.cancelStateRequest();
sLastRequest = null;
}
} else {
@@ -105,9 +105,6 @@ public class DeviceStateManagerShellCommand extends ShellCommand {
DeviceStateRequest request = DeviceStateRequest.newBuilder(requestedState).build();
mClient.requestState(request, null /* executor */, null /* callback */);
- if (sLastRequest != null) {
- mClient.cancelRequest(sLastRequest);
- }
sLastRequest = request;
}
diff --git a/services/core/java/com/android/server/devicestate/OverrideRequestController.java b/services/core/java/com/android/server/devicestate/OverrideRequestController.java
index 36cb4162accc..01f5a09323cf 100644
--- a/services/core/java/com/android/server/devicestate/OverrideRequestController.java
+++ b/services/core/java/com/android/server/devicestate/OverrideRequestController.java
@@ -18,15 +18,13 @@ package com.android.server.devicestate;
import android.annotation.IntDef;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.hardware.devicestate.DeviceStateRequest;
import android.os.IBinder;
+import android.util.Slog;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
-import java.util.List;
/**
* Manages the lifecycle of override requests.
@@ -36,29 +34,26 @@ import java.util.List;
* <ul>
* <li>A new request is added with {@link #addRequest(OverrideRequest)}, in which case the
* request will become suspended.</li>
- * <li>The request is cancelled with {@link #cancelRequest(IBinder)} or as a side effect
+ * <li>The request is cancelled with {@link #cancelRequest} or as a side effect
* of other methods calls, such as {@link #handleProcessDied(int)}.</li>
* </ul>
*/
final class OverrideRequestController {
+ private static final String TAG = "OverrideRequestController";
+
static final int STATUS_UNKNOWN = 0;
/**
* The request is the top-most request.
*/
static final int STATUS_ACTIVE = 1;
/**
- * The request is still present but is being superseded by another request.
- */
- static final int STATUS_SUSPENDED = 2;
- /**
* The request is not longer valid.
*/
- static final int STATUS_CANCELED = 3;
+ static final int STATUS_CANCELED = 2;
@IntDef(prefix = {"STATUS_"}, value = {
STATUS_UNKNOWN,
STATUS_ACTIVE,
- STATUS_SUSPENDED,
STATUS_CANCELED
})
@Retention(RetentionPolicy.SOURCE)
@@ -68,8 +63,6 @@ final class OverrideRequestController {
switch (status) {
case STATUS_ACTIVE:
return "ACTIVE";
- case STATUS_SUSPENDED:
- return "SUSPENDED";
case STATUS_CANCELED:
return "CANCELED";
case STATUS_UNKNOWN:
@@ -79,15 +72,13 @@ final class OverrideRequestController {
}
private final StatusChangeListener mListener;
- private final List<OverrideRequest> mTmpRequestsToCancel = new ArrayList<>();
- // List of override requests with the most recent override request at the end.
- private final ArrayList<OverrideRequest> mRequests = new ArrayList<>();
+ // Handle to the current override request, null if none.
+ private OverrideRequest mRequest;
private boolean mStickyRequestsAllowed;
- // List of override requests that have outlived their process and will only be cancelled through
- // a call to cancelStickyRequests().
- private final ArrayList<OverrideRequest> mStickyRequests = new ArrayList<>();
+ // The current request has outlived their process.
+ private boolean mStickyRequest;
OverrideRequestController(@NonNull StatusChangeListener listener) {
mListener = listener;
@@ -97,26 +88,26 @@ final class OverrideRequestController {
* Sets sticky requests as either allowed or disallowed. When sticky requests are allowed a call
* to {@link #handleProcessDied(int)} will not result in the request being cancelled
* immediately. Instead, the request will be marked sticky and must be cancelled with a call
- * to {@link #cancelStickyRequests()}.
+ * to {@link #cancelStickyRequest()}.
*/
void setStickyRequestsAllowed(boolean stickyRequestsAllowed) {
mStickyRequestsAllowed = stickyRequestsAllowed;
if (!mStickyRequestsAllowed) {
- cancelStickyRequests();
+ cancelStickyRequest();
}
}
/**
- * Adds a request to the top of the stack and notifies the listener of all changes to request
- * status as a result of this operation.
+ * Sets the new request as active and cancels the previous override request, notifies the
+ * listener of all changes to request status as a result of this operation.
*/
void addRequest(@NonNull OverrideRequest request) {
- mRequests.add(request);
+ OverrideRequest previousRequest = mRequest;
+ mRequest = request;
mListener.onStatusChanged(request, STATUS_ACTIVE);
- if (mRequests.size() > 1) {
- OverrideRequest prevRequest = mRequests.get(mRequests.size() - 2);
- mListener.onStatusChanged(prevRequest, STATUS_SUSPENDED);
+ if (previousRequest != null) {
+ cancelRequestLocked(previousRequest);
}
}
@@ -124,42 +115,32 @@ final class OverrideRequestController {
* Cancels the request with the specified {@code token} and notifies the listener of all changes
* to request status as a result of this operation.
*/
- void cancelRequest(@NonNull IBinder token) {
- int index = getRequestIndex(token);
- if (index == -1) {
+ void cancelRequest(@NonNull OverrideRequest request) {
+ // Either don't have a current request or attempting to cancel an already cancelled request
+ if (!hasRequest(request.getToken())) {
return;
}
-
- OverrideRequest request = mRequests.remove(index);
- if (index == mRequests.size() && mRequests.size() > 0) {
- // We removed the current active request so we need to set the new active request
- // before cancelling this request.
- OverrideRequest newTop = getLast(mRequests);
- mListener.onStatusChanged(newTop, STATUS_ACTIVE);
- }
- mListener.onStatusChanged(request, STATUS_CANCELED);
+ cancelCurrentRequestLocked();
}
/**
- * Cancels all requests that are currently marked sticky and notifies the listener of all
+ * Cancels a request that is currently marked sticky and notifies the listener of all
* changes to request status as a result of this operation.
*
* @see #setStickyRequestsAllowed(boolean)
*/
- void cancelStickyRequests() {
- mTmpRequestsToCancel.clear();
- mTmpRequestsToCancel.addAll(mStickyRequests);
- cancelRequestsLocked(mTmpRequestsToCancel);
+ void cancelStickyRequest() {
+ if (mStickyRequest) {
+ cancelCurrentRequestLocked();
+ }
}
/**
- * Cancels all override requests, this could be due to the device being put
+ * Cancels the current override request, this could be due to the device being put
* into a hardware state that declares the flag "FLAG_CANCEL_OVERRIDE_REQUESTS"
*/
- void cancelOverrideRequests() {
- mTmpRequestsToCancel.clear();
- mTmpRequestsToCancel.addAll(mRequests);
- cancelRequestsLocked(mTmpRequestsToCancel);
+ void cancelOverrideRequest() {
+ cancelCurrentRequestLocked();
}
/**
@@ -167,7 +148,7 @@ final class OverrideRequestController {
* {@code token}, {@code false} otherwise.
*/
boolean hasRequest(@NonNull IBinder token) {
- return getRequestIndex(token) != -1;
+ return mRequest != null && token == mRequest.getToken();
}
/**
@@ -176,139 +157,79 @@ final class OverrideRequestController {
* operation.
*/
void handleProcessDied(int pid) {
- if (mRequests.isEmpty()) {
+ if (mRequest == null) {
return;
}
- mTmpRequestsToCancel.clear();
- OverrideRequest prevActiveRequest = getLast(mRequests);
- for (OverrideRequest request : mRequests) {
- if (request.getPid() == pid) {
- mTmpRequestsToCancel.add(request);
+ if (mRequest.getPid() == pid) {
+ if (mStickyRequestsAllowed) {
+ // Do not cancel the requests now because sticky requests are allowed. These
+ // requests will be cancelled on a call to cancelStickyRequests().
+ mStickyRequest = true;
+ return;
}
+ cancelCurrentRequestLocked();
}
-
- if (mStickyRequestsAllowed) {
- // Do not cancel the requests now because sticky requests are allowed. These
- // requests will be cancelled on a call to cancelStickyRequests().
- mStickyRequests.addAll(mTmpRequestsToCancel);
- return;
- }
-
- cancelRequestsLocked(mTmpRequestsToCancel);
}
/**
* Notifies the controller that the base state has changed. The controller will notify the
* listener of all changes to request status as a result of this change.
- *
- * @return {@code true} if calling this method has lead to a new active request, {@code false}
- * otherwise.
*/
- boolean handleBaseStateChanged() {
- if (mRequests.isEmpty()) {
- return false;
+ void handleBaseStateChanged() {
+ if (mRequest == null) {
+ return;
}
- mTmpRequestsToCancel.clear();
- OverrideRequest prevActiveRequest = getLast(mRequests);
- for (int i = 0; i < mRequests.size(); i++) {
- OverrideRequest request = mRequests.get(i);
- if ((request.getFlags() & DeviceStateRequest.FLAG_CANCEL_WHEN_BASE_CHANGES) != 0) {
- mTmpRequestsToCancel.add(request);
- }
+ if ((mRequest.getFlags()
+ & DeviceStateRequest.FLAG_CANCEL_WHEN_BASE_CHANGES) != 0) {
+ cancelCurrentRequestLocked();
}
-
- final boolean newActiveRequest = cancelRequestsLocked(mTmpRequestsToCancel);
- return newActiveRequest;
}
/**
* Notifies the controller that the set of supported states has changed. The controller will
* notify the listener of all changes to request status as a result of this change.
- *
- * @return {@code true} if calling this method has lead to a new active request, {@code false}
- * otherwise.
*/
- boolean handleNewSupportedStates(int[] newSupportedStates) {
- if (mRequests.isEmpty()) {
- return false;
+ void handleNewSupportedStates(int[] newSupportedStates) {
+ if (mRequest == null) {
+ return;
}
- mTmpRequestsToCancel.clear();
- for (int i = 0; i < mRequests.size(); i++) {
- OverrideRequest request = mRequests.get(i);
- if (!contains(newSupportedStates, request.getRequestedState())) {
- mTmpRequestsToCancel.add(request);
- }
+ if (!contains(newSupportedStates, mRequest.getRequestedState())) {
+ cancelCurrentRequestLocked();
}
-
- final boolean newActiveRequest = cancelRequestsLocked(mTmpRequestsToCancel);
- return newActiveRequest;
}
void dumpInternal(PrintWriter pw) {
- final int requestCount = mRequests.size();
+ OverrideRequest overrideRequest = mRequest;
+ final boolean requestActive = overrideRequest != null;
pw.println();
- pw.println("Override requests: size=" + requestCount);
- for (int i = 0; i < requestCount; i++) {
- OverrideRequest overrideRequest = mRequests.get(i);
- int status = (i == requestCount - 1) ? STATUS_ACTIVE : STATUS_SUSPENDED;
- pw.println(" " + i + ": mPid=" + overrideRequest.getPid()
+ pw.println("Override Request active: " + requestActive);
+ if (requestActive) {
+ pw.println("Request: mPid=" + overrideRequest.getPid()
+ ", mRequestedState=" + overrideRequest.getRequestedState()
+ ", mFlags=" + overrideRequest.getFlags()
- + ", mStatus=" + statusToString(status));
+ + ", mStatus=" + statusToString(STATUS_ACTIVE));
}
}
- /**
- * Handles cancelling a set of requests. If the set of requests to cancel will lead to a new
- * request becoming active this request will also be notified of its change in state.
- *
- * @return {@code true} if calling this method has lead to a new active request, {@code false}
- * otherwise.
- */
- private boolean cancelRequestsLocked(List<OverrideRequest> requestsToCancel) {
- if (requestsToCancel.isEmpty()) {
- return false;
- }
-
- OverrideRequest prevActiveRequest = getLast(mRequests);
- boolean causedNewRequestToBecomeActive = false;
- mRequests.removeAll(requestsToCancel);
- mStickyRequests.removeAll(requestsToCancel);
- if (!mRequests.isEmpty()) {
- OverrideRequest newActiveRequest = getLast(mRequests);
- if (newActiveRequest != prevActiveRequest) {
- mListener.onStatusChanged(newActiveRequest, STATUS_ACTIVE);
- causedNewRequestToBecomeActive = true;
- }
- }
-
- for (int i = 0; i < requestsToCancel.size(); i++) {
- mListener.onStatusChanged(requestsToCancel.get(i), STATUS_CANCELED);
- }
- return causedNewRequestToBecomeActive;
+ private void cancelRequestLocked(@NonNull OverrideRequest requestToCancel) {
+ mListener.onStatusChanged(requestToCancel, STATUS_CANCELED);
}
- private int getRequestIndex(@NonNull IBinder token) {
- final int numberOfRequests = mRequests.size();
- if (numberOfRequests == 0) {
- return -1;
- }
-
- for (int i = 0; i < numberOfRequests; i++) {
- OverrideRequest request = mRequests.get(i);
- if (request.getToken() == token) {
- return i;
- }
+ /**
+ * Handles cancelling {@code mRequest}.
+ * Notifies the listener of the canceled status as well.
+ */
+ private void cancelCurrentRequestLocked() {
+ if (mRequest == null) {
+ Slog.w(TAG, "Attempted to cancel a null OverrideRequest");
+ return;
}
- return -1;
- }
-
- @Nullable
- private static <T> T getLast(List<T> list) {
- return list.size() > 0 ? list.get(list.size() - 1) : null;
+ mStickyRequest = false;
+ mListener.onStatusChanged(mRequest, STATUS_CANCELED);
+ mRequest = null;
}
private static boolean contains(int[] array, int value) {
diff --git a/services/core/java/com/android/server/display/BrightnessThrottler.java b/services/core/java/com/android/server/display/BrightnessThrottler.java
new file mode 100644
index 000000000000..767b2d18a69a
--- /dev/null
+++ b/services/core/java/com/android/server/display/BrightnessThrottler.java
@@ -0,0 +1,262 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display;
+
+import android.content.Context;
+import android.hardware.display.BrightnessInfo;
+import android.os.Handler;
+import android.os.IThermalEventListener;
+import android.os.IThermalService;
+import android.os.PowerManager;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.Temperature;
+import android.util.Slog;
+
+import com.android.server.display.DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel;
+import com.android.server.display.DisplayDeviceConfig.BrightnessThrottlingData;
+
+import java.io.PrintWriter;
+
+/**
+ * This class monitors various conditions, such as skin temperature throttling status, and limits
+ * the allowed brightness range accordingly.
+ */
+class BrightnessThrottler {
+ private static final String TAG = "BrightnessThrottler";
+ private static final boolean DEBUG = false;
+
+ private static final int THROTTLING_INVALID = -1;
+
+ private final Injector mInjector;
+ private final Handler mHandler;
+ private BrightnessThrottlingData mThrottlingData;
+ private final Runnable mThrottlingChangeCallback;
+ private final SkinThermalStatusObserver mSkinThermalStatusObserver;
+ private int mThrottlingStatus;
+ private float mBrightnessCap = PowerManager.BRIGHTNESS_MAX;
+ private @BrightnessInfo.BrightnessMaxReason int mBrightnessMaxReason =
+ BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE;
+
+ BrightnessThrottler(Handler handler, BrightnessThrottlingData throttlingData,
+ Runnable throttlingChangeCallback) {
+ this(new Injector(), handler, throttlingData, throttlingChangeCallback);
+ }
+
+ BrightnessThrottler(Injector injector, Handler handler, BrightnessThrottlingData throttlingData,
+ Runnable throttlingChangeCallback) {
+ mInjector = injector;
+ mHandler = handler;
+ mThrottlingData = throttlingData;
+ mThrottlingChangeCallback = throttlingChangeCallback;
+ mSkinThermalStatusObserver = new SkinThermalStatusObserver(mInjector, mHandler);
+
+ resetThrottlingData(mThrottlingData);
+ }
+
+ boolean deviceSupportsThrottling() {
+ return mThrottlingData != null;
+ }
+
+ float getBrightnessCap() {
+ return mBrightnessCap;
+ }
+
+ int getBrightnessMaxReason() {
+ return mBrightnessMaxReason;
+ }
+
+ boolean isThrottled() {
+ return mBrightnessMaxReason != BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE;
+ }
+
+ void stop() {
+ mSkinThermalStatusObserver.stopObserving();
+
+ // We're asked to stop throttling, so reset brightness restrictions.
+ mBrightnessCap = PowerManager.BRIGHTNESS_MAX;
+ mBrightnessMaxReason = BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE;
+
+ // We set throttling status to an invalid value here so that we act on the first throttling
+ // value received from the thermal service after registration, even if that throttling value
+ // is THROTTLING_NONE.
+ mThrottlingStatus = THROTTLING_INVALID;
+ }
+
+ void resetThrottlingData(BrightnessThrottlingData throttlingData) {
+ stop();
+ mThrottlingData = throttlingData;
+
+ if (deviceSupportsThrottling()) {
+ mSkinThermalStatusObserver.startObserving();
+ }
+ }
+
+ private float verifyAndConstrainBrightnessCap(float brightness) {
+ if (brightness < PowerManager.BRIGHTNESS_MIN) {
+ Slog.e(TAG, "brightness " + brightness + " is lower than the minimum possible "
+ + "brightness " + PowerManager.BRIGHTNESS_MIN);
+ brightness = PowerManager.BRIGHTNESS_MIN;
+ }
+
+ if (brightness > PowerManager.BRIGHTNESS_MAX) {
+ Slog.e(TAG, "brightness " + brightness + " is higher than the maximum possible "
+ + "brightness " + PowerManager.BRIGHTNESS_MAX);
+ brightness = PowerManager.BRIGHTNESS_MAX;
+ }
+
+ return brightness;
+ }
+
+ private void thermalStatusChanged(@Temperature.ThrottlingStatus int newStatus) {
+ if (mThrottlingStatus != newStatus) {
+ mThrottlingStatus = newStatus;
+ updateThrottling();
+ }
+ }
+
+ private void updateThrottling() {
+ if (!deviceSupportsThrottling()) {
+ return;
+ }
+
+ float brightnessCap = PowerManager.BRIGHTNESS_MAX;
+ int brightnessMaxReason = BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE;
+
+ if (mThrottlingStatus != THROTTLING_INVALID) {
+ // Throttling levels are sorted by increasing severity
+ for (ThrottlingLevel level : mThrottlingData.throttlingLevels) {
+ if (level.thermalStatus <= mThrottlingStatus) {
+ brightnessCap = level.brightness;
+ brightnessMaxReason = BrightnessInfo.BRIGHTNESS_MAX_REASON_THERMAL;
+ } else {
+ // Throttling levels that are greater than the current status are irrelevant
+ break;
+ }
+ }
+ }
+
+ if (mBrightnessCap != brightnessCap || mBrightnessMaxReason != brightnessMaxReason) {
+ mBrightnessCap = verifyAndConstrainBrightnessCap(brightnessCap);
+ mBrightnessMaxReason = brightnessMaxReason;
+
+ if (DEBUG) {
+ Slog.d(TAG, "State changed: mBrightnessCap = " + mBrightnessCap
+ + ", mBrightnessMaxReason = "
+ + BrightnessInfo.briMaxReasonToString(mBrightnessMaxReason));
+ }
+
+ if (mThrottlingChangeCallback != null) {
+ mThrottlingChangeCallback.run();
+ }
+ }
+ }
+
+ void dump(PrintWriter pw) {
+ mHandler.runWithScissors(() -> dumpLocal(pw), 1000);
+ }
+
+ private void dumpLocal(PrintWriter pw) {
+ pw.println("BrightnessThrottler:");
+ pw.println(" mThrottlingData=" + mThrottlingData);
+ pw.println(" mThrottlingStatus=" + mThrottlingStatus);
+ pw.println(" mBrightnessCap=" + mBrightnessCap);
+ pw.println(" mBrightnessMaxReason=" +
+ BrightnessInfo.briMaxReasonToString(mBrightnessMaxReason));
+
+ mSkinThermalStatusObserver.dump(pw);
+ }
+
+ private final class SkinThermalStatusObserver extends IThermalEventListener.Stub {
+ private final Injector mInjector;
+ private final Handler mHandler;
+
+ private IThermalService mThermalService;
+ private boolean mStarted;
+
+ SkinThermalStatusObserver(Injector injector, Handler handler) {
+ mInjector = injector;
+ mHandler = handler;
+ }
+
+ @Override
+ public void notifyThrottling(Temperature temp) {
+ if (DEBUG) {
+ Slog.d(TAG, "New thermal throttling status = " + temp.getStatus());
+ }
+ mHandler.post(() -> {
+ final @Temperature.ThrottlingStatus int status = temp.getStatus();
+ thermalStatusChanged(status);
+ });
+ }
+
+ void startObserving() {
+ if (mStarted) {
+ if (DEBUG) {
+ Slog.d(TAG, "Thermal status observer already started");
+ }
+ return;
+ }
+ mThermalService = mInjector.getThermalService();
+ if (mThermalService == null) {
+ Slog.e(TAG, "Could not observe thermal status. Service not available");
+ return;
+ }
+ try {
+ // We get a callback immediately upon registering so there's no need to query
+ // for the current value.
+ mThermalService.registerThermalEventListenerWithType(this, Temperature.TYPE_SKIN);
+ mStarted = true;
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to register thermal status listener", e);
+ }
+ }
+
+ void stopObserving() {
+ if (!mStarted) {
+ if (DEBUG) {
+ Slog.d(TAG, "Stop skipped because thermal status observer not started");
+ }
+ return;
+ }
+ try {
+ mThermalService.unregisterThermalEventListener(this);
+ mStarted = false;
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to unregister thermal status listener", e);
+ }
+ mThermalService = null;
+ }
+
+ void dump(PrintWriter writer) {
+ writer.println(" SkinThermalStatusObserver:");
+ writer.println(" mStarted: " + mStarted);
+ if (mThermalService != null) {
+ writer.println(" ThermalService available");
+ } else {
+ writer.println(" ThermalService not available");
+ }
+ }
+ }
+
+ public static class Injector {
+ public IThermalService getThermalService() {
+ return IThermalService.Stub.asInterface(
+ ServiceManager.getService(Context.THERMAL_SERVICE));
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/display/BrightnessUtils.java b/services/core/java/com/android/server/display/BrightnessUtils.java
new file mode 100644
index 000000000000..84fa0cccbd10
--- /dev/null
+++ b/services/core/java/com/android/server/display/BrightnessUtils.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display;
+
+import android.util.MathUtils;
+
+/**
+ * Utility class providing functions to convert between linear and perceptual gamma space.
+ *
+ * Internally, this implements the Hybrid Log Gamma electro-optical transfer function, which is a
+ * slight improvement to the typical gamma transfer function for displays whose max brightness
+ * exceeds the 120 nit reference point, but doesn't set a specific reference brightness like the PQ
+ * function does.
+ *
+ * Note that this transfer function is only valid if the display's backlight value is a linear
+ * control. If it's calibrated to be something non-linear, then a different transfer function
+ * should be used.
+ *
+ * Note: This code is based on the same class in the com.android.settingslib.display package.
+ */
+public class BrightnessUtils {
+
+ // Hybrid Log Gamma constant values
+ private static final float R = 0.5f;
+ private static final float A = 0.17883277f;
+ private static final float B = 0.28466892f;
+ private static final float C = 0.55991073f;
+
+ /**
+ * A function for converting from the gamma space into the linear space.
+ *
+ * @param val The value in the gamma space [0 .. 1.0]
+ * @return The corresponding value in the linear space [0 .. 1.0].
+ */
+ public static final float convertGammaToLinear(float val) {
+ final float ret;
+ if (val <= R) {
+ ret = MathUtils.sq(val / R);
+ } else {
+ ret = MathUtils.exp((val - C) / A) + B;
+ }
+
+ // HLG is normalized to the range [0, 12], ensure that value is within that range,
+ // it shouldn't be out of bounds.
+ final float normalizedRet = MathUtils.constrain(ret, 0, 12);
+
+ // Re-normalize to the range [0, 1]
+ // in order to derive the correct setting value.
+ return normalizedRet / 12;
+ }
+
+ /**
+ * A function for converting from the linear space into the gamma space.
+ *
+ * @param val The value in linear space [0 .. 1.0]
+ * @return The corresponding value in gamma space [0 .. 1.0]
+ */
+ public static final float convertLinearToGamma(float val) {
+ // For some reason, HLG normalizes to the range [0, 12] rather than [0, 1]
+ final float normalizedVal = val * 12;
+ final float ret;
+ if (normalizedVal <= 1f) {
+ ret = MathUtils.sqrt(normalizedVal) * R;
+ } else {
+ ret = A * MathUtils.log(normalizedVal - B) + C;
+ }
+ return ret;
+ }
+}
diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
index f3969b19e11f..a4a6eb4312cc 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
@@ -33,6 +33,8 @@ import android.view.DisplayAddress;
import com.android.internal.R;
import com.android.internal.display.BrightnessSynchronizer;
import com.android.server.display.config.BrightnessThresholds;
+import com.android.server.display.config.BrightnessThrottlingMap;
+import com.android.server.display.config.BrightnessThrottlingPoint;
import com.android.server.display.config.Density;
import com.android.server.display.config.DisplayConfiguration;
import com.android.server.display.config.DisplayQuirks;
@@ -43,6 +45,7 @@ import com.android.server.display.config.Point;
import com.android.server.display.config.RefreshRateRange;
import com.android.server.display.config.SensorDetails;
import com.android.server.display.config.ThermalStatus;
+import com.android.server.display.config.ThermalThrottling;
import com.android.server.display.config.Thresholds;
import com.android.server.display.config.XmlParser;
@@ -145,6 +148,8 @@ public class DisplayDeviceConfig {
private DensityMap mDensityMap;
private String mLoadedFrom = null;
+ private BrightnessThrottlingData mBrightnessThrottlingData;
+
private DisplayDeviceConfig(Context context) {
mContext = context;
}
@@ -424,6 +429,13 @@ public class DisplayDeviceConfig {
return mDensityMap;
}
+ /**
+ * @return brightness throttling data configuration data for the display.
+ */
+ public BrightnessThrottlingData getBrightnessThrottlingData() {
+ return BrightnessThrottlingData.create(mBrightnessThrottlingData);
+ }
+
@Override
public String toString() {
return "DisplayDeviceConfig{"
@@ -441,6 +453,7 @@ public class DisplayDeviceConfig {
+ ", mQuirks=" + mQuirks
+ ", isHbmEnabled=" + mIsHighBrightnessModeEnabled
+ ", mHbmData=" + mHbmData
+ + ", mBrightnessThrottlingData=" + mBrightnessThrottlingData
+ ", mBrightnessRampFastDecrease=" + mBrightnessRampFastDecrease
+ ", mBrightnessRampFastIncrease=" + mBrightnessRampFastIncrease
+ ", mBrightnessRampSlowDecrease=" + mBrightnessRampSlowDecrease
@@ -502,6 +515,7 @@ public class DisplayDeviceConfig {
loadBrightnessDefaultFromDdcXml(config);
loadBrightnessConstraintsFromConfigXml();
loadBrightnessMap(config);
+ loadBrightnessThrottlingMap(config);
loadHighBrightnessModeData(config);
loadQuirks(config);
loadBrightnessRamps(config);
@@ -664,6 +678,41 @@ public class DisplayDeviceConfig {
constrainNitsAndBacklightArrays();
}
+ private void loadBrightnessThrottlingMap(DisplayConfiguration config) {
+ final ThermalThrottling throttlingConfig = config.getThermalThrottling();
+ if (throttlingConfig == null) {
+ Slog.i(TAG, "no thermal throttling config found");
+ return;
+ }
+
+ final BrightnessThrottlingMap map = throttlingConfig.getBrightnessThrottlingMap();
+ if (map == null) {
+ Slog.i(TAG, "no brightness throttling map found");
+ return;
+ }
+
+ final List<BrightnessThrottlingPoint> points = map.getBrightnessThrottlingPoint();
+ // At least 1 point is guaranteed by the display device config schema
+ List<BrightnessThrottlingData.ThrottlingLevel> throttlingLevels =
+ new ArrayList<>(points.size());
+
+ boolean badConfig = false;
+ for (BrightnessThrottlingPoint point : points) {
+ ThermalStatus status = point.getThermalStatus();
+ if (!thermalStatusIsValid(status)) {
+ badConfig = true;
+ break;
+ }
+
+ throttlingLevels.add(new BrightnessThrottlingData.ThrottlingLevel(
+ convertThermalStatus(status), point.getBrightness().floatValue()));
+ }
+
+ if (!badConfig) {
+ mBrightnessThrottlingData = BrightnessThrottlingData.create(throttlingLevels);
+ }
+ }
+
private void loadBrightnessMapFromConfigXml() {
// Use the config.xml mapping
final Resources res = mContext.getResources();
@@ -931,6 +980,25 @@ public class DisplayDeviceConfig {
}
}
+ private boolean thermalStatusIsValid(ThermalStatus value) {
+ if (value == null) {
+ return false;
+ }
+
+ switch (value) {
+ case none:
+ case light:
+ case moderate:
+ case severe:
+ case critical:
+ case emergency:
+ case shutdown:
+ return true;
+ default:
+ return false;
+ }
+ }
+
private @PowerManager.ThermalStatus int convertThermalStatus(ThermalStatus value) {
if (value == null) {
return PowerManager.THERMAL_STATUS_NONE;
@@ -1061,4 +1129,91 @@ public class DisplayDeviceConfig {
+ "} ";
}
}
+
+ /**
+ * Container for brightness throttling data.
+ */
+ static class BrightnessThrottlingData {
+ static class ThrottlingLevel {
+ public @PowerManager.ThermalStatus int thermalStatus;
+ public float brightness;
+
+ ThrottlingLevel(@PowerManager.ThermalStatus int thermalStatus, float brightness) {
+ this.thermalStatus = thermalStatus;
+ this.brightness = brightness;
+ }
+
+ @Override
+ public String toString() {
+ return "[" + thermalStatus + "," + brightness + "]";
+ }
+ }
+
+ public List<ThrottlingLevel> throttlingLevels;
+
+ static public BrightnessThrottlingData create(List<ThrottlingLevel> throttlingLevels)
+ {
+ if (throttlingLevels == null || throttlingLevels.size() == 0) {
+ Slog.e(TAG, "BrightnessThrottlingData received null or empty throttling levels");
+ return null;
+ }
+
+ ThrottlingLevel prevLevel = throttlingLevels.get(0);
+ final int numLevels = throttlingLevels.size();
+ for (int i = 1; i < numLevels; i++) {
+ ThrottlingLevel thisLevel = throttlingLevels.get(i);
+
+ if (thisLevel.thermalStatus <= prevLevel.thermalStatus) {
+ Slog.e(TAG, "brightnessThrottlingMap must be strictly increasing, ignoring "
+ + "configuration. ThermalStatus " + thisLevel.thermalStatus + " <= "
+ + prevLevel.thermalStatus);
+ return null;
+ }
+
+ if (thisLevel.brightness >= prevLevel.brightness) {
+ Slog.e(TAG, "brightnessThrottlingMap must be strictly decreasing, ignoring "
+ + "configuration. Brightness " + thisLevel.brightness + " >= "
+ + thisLevel.brightness);
+ return null;
+ }
+
+ prevLevel = thisLevel;
+ }
+
+ for (ThrottlingLevel level : throttlingLevels) {
+ // Non-negative brightness values are enforced by device config schema
+ if (level.brightness > PowerManager.BRIGHTNESS_MAX) {
+ Slog.e(TAG, "brightnessThrottlingMap contains a brightness value exceeding "
+ + "system max. Brightness " + level.brightness + " > "
+ + PowerManager.BRIGHTNESS_MAX);
+ return null;
+ }
+ }
+
+ return new BrightnessThrottlingData(throttlingLevels);
+ }
+
+ static public BrightnessThrottlingData create(BrightnessThrottlingData other) {
+ if (other == null)
+ return null;
+
+ return BrightnessThrottlingData.create(other.throttlingLevels);
+ }
+
+
+ @Override
+ public String toString() {
+ return "BrightnessThrottlingData{"
+ + "throttlingLevels:" + throttlingLevels
+ + "} ";
+ }
+
+ private BrightnessThrottlingData(List<ThrottlingLevel> inLevels) {
+ throttlingLevels = new ArrayList<>(inLevels.size());
+ for (ThrottlingLevel level : inLevels) {
+ throttlingLevels.add(new ThrottlingLevel(level.thermalStatus, level.brightness));
+ }
+ }
+
+ }
}
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 7ad497972462..4e88acd11fac 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -141,7 +141,6 @@ import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
-import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
@@ -1756,10 +1755,6 @@ public final class DisplayManagerService extends SystemService {
void setUserPreferredDisplayModeInternal(int displayId, Display.Mode mode) {
synchronized (mSyncRoot) {
- if (Objects.equals(mUserPreferredMode, mode) && displayId == Display.INVALID_DISPLAY) {
- return;
- }
-
if (mode != null && !isResolutionAndRefreshRateValid(mode)
&& displayId == Display.INVALID_DISPLAY) {
throw new IllegalArgumentException("width, height and refresh rate of mode should "
@@ -1813,7 +1808,15 @@ public final class DisplayManagerService extends SystemService {
Settings.Global.putInt(mContext.getContentResolver(),
Settings.Global.USER_PREFERRED_RESOLUTION_WIDTH, resolutionWidth);
mDisplayDeviceRepo.forEachLocked((DisplayDevice device) -> {
- device.setUserPreferredDisplayModeLocked(mode);
+ // If there is a display specific mode, don't override that
+ final Point deviceUserPreferredResolution =
+ mPersistentDataStore.getUserPreferredResolution(device);
+ final float deviceRefreshRate =
+ mPersistentDataStore.getUserPreferredRefreshRate(device);
+ if (!isValidResolution(deviceUserPreferredResolution)
+ && !isValidRefreshRate(deviceRefreshRate)) {
+ device.setUserPreferredDisplayModeLocked(mode);
+ }
});
}
@@ -3533,6 +3536,14 @@ public final class DisplayManagerService extends SystemService {
&& (brightness <= PowerManager.BRIGHTNESS_MAX);
}
+ private static boolean isValidResolution(Point resolution) {
+ return (resolution != null) && (resolution.x > 0) && (resolution.y > 0);
+ }
+
+ private static boolean isValidRefreshRate(float refreshRate) {
+ return !Float.isNaN(refreshRate) && (refreshRate > 0.0f);
+ }
+
private final class LocalService extends DisplayManagerInternal {
@Override
diff --git a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
index a9a1f08c140a..a9875c873bbb 100644
--- a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
+++ b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
@@ -335,7 +335,7 @@ class DisplayManagerShellCommand extends ShellCommand {
}
private int setUserDisabledHdrTypes() {
- final String[] userDisabledHdrTypesText = getAllArgs();
+ String[] userDisabledHdrTypesText = peekRemainingArgs();
if (userDisabledHdrTypesText == null) {
getErrPrintWriter().println("Error: no userDisabledHdrTypes specified");
return 1;
@@ -351,7 +351,6 @@ class DisplayManagerShellCommand extends ShellCommand {
getErrPrintWriter().println("Error: invalid format of userDisabledHdrTypes");
return 1;
}
-
final Context context = mService.getContext();
final DisplayManager dm = context.getSystemService(DisplayManager.class);
dm.setUserDisabledHdrTypes(userDisabledHdrTypes);
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index ec4b91a79ae6..3494342a66fb 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -346,6 +346,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
private boolean mAppliedTemporaryBrightness;
private boolean mAppliedTemporaryAutoBrightnessAdjustment;
private boolean mAppliedBrightnessBoost;
+ private boolean mAppliedThrottling;
// Reason for which the brightness was last changed. See {@link BrightnessReason} for more
// information.
@@ -379,6 +380,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
private final HighBrightnessModeController mHbmController;
+ private final BrightnessThrottler mBrightnessThrottler;
+
private final BrightnessSetting mBrightnessSetting;
private final Runnable mOnBrightnessChangeRunnable;
@@ -538,6 +541,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
mHbmController = createHbmControllerLocked();
+ mBrightnessThrottler = createBrightnessThrottlerLocked();
+
// Seed the cached brightness
saveBrightnessInfo(getScreenBrightnessSetting());
@@ -827,6 +832,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
reloadReduceBrightColours();
mHbmController.resetHbmData(info.width, info.height, token, info.uniqueId,
mDisplayDeviceConfig.getHighBrightnessModeData());
+ mBrightnessThrottler.resetThrottlingData(
+ mDisplayDeviceConfig.getBrightnessThrottlingData());
}
private void sendUpdatePowerState() {
@@ -1007,6 +1014,9 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
mAutomaticBrightnessController.switchToInteractiveScreenBrightnessMode();
}
}
+ if (mDisplayWhiteBalanceController != null) {
+ mDisplayWhiteBalanceController.setStrongModeEnabled(isIdle);
+ }
}
private final Animator.AnimatorListener mAnimatorListener = new Animator.AnimatorListener() {
@@ -1039,6 +1049,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
private void cleanupHandlerThreadAfterStop() {
setProximitySensorEnabled(false);
mHbmController.stop();
+ mBrightnessThrottler.stop();
mHandler.removeCallbacksAndMessages(null);
if (mUnfinishedBusiness) {
mCallbacks.releaseSuspendBlocker();
@@ -1336,14 +1347,6 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
mBrightnessReasonTemp.setReason(BrightnessReason.REASON_MANUAL);
}
- // The current brightness to use has been calculated at this point (minus the adjustments
- // like low-power and dim), and HbmController should be notified so that it can accurately
- // calculate HDR or HBM levels. We specifically do it here instead of having HbmController
- // listen to the brightness setting because certain brightness sources (just as an app
- // override) are not saved to the setting, but should be reflected in HBM
- // calculations.
- mHbmController.onBrightnessChanged(brightnessState);
-
if (updateScreenBrightnessSetting) {
// Tell the rest of the system about the new brightness in case we had to change it
// for things like auto-brightness or high-brightness-mode. Note that we do this
@@ -1390,6 +1393,28 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
mAppliedLowPower = false;
}
+ // Apply brightness throttling after applying all other transforms
+ final float unthrottledBrightnessState = brightnessState;
+ if (mBrightnessThrottler.isThrottled()) {
+ brightnessState = Math.min(brightnessState, mBrightnessThrottler.getBrightnessCap());
+ mBrightnessReasonTemp.addModifier(BrightnessReason.MODIFIER_THROTTLED);
+ if (!mAppliedThrottling) {
+ slowChange = false;
+ }
+ mAppliedThrottling = true;
+ } else if (mAppliedThrottling) {
+ slowChange = false;
+ mAppliedThrottling = false;
+ }
+
+ // The current brightness to use has been calculated at this point, and HbmController should
+ // be notified so that it can accurately calculate HDR or HBM levels. We specifically do it
+ // here instead of having HbmController listen to the brightness setting because certain
+ // brightness sources (such as an app override) are not saved to the setting, but should be
+ // reflected in HBM calculations.
+ mHbmController.onBrightnessChanged(brightnessState, unthrottledBrightnessState,
+ mBrightnessThrottler.getBrightnessMaxReason());
+
// Animate the screen brightness when the screen is on or dozing.
// Skip the animation when the screen is off or suspended or transition to/from VR.
boolean brightnessAdjusted = false;
@@ -1441,6 +1466,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
// use instead. We still preserve the calculated brightness for Standard Dynamic Range
// (SDR) layers, but the main brightness value will be the one for HDR.
float sdrAnimateValue = animateValue;
+ // TODO(b/216365040): The decision to prevent HBM for HDR in low power mode should be
+ // done in HighBrightnessModeController.
if (mHbmController.getHighBrightnessMode() == BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR
&& ((mBrightnessReason.modifier & BrightnessReason.MODIFIER_DIMMED) == 0
|| (mBrightnessReason.modifier & BrightnessReason.MODIFIER_LOW_POWER) == 0)) {
@@ -1618,7 +1645,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
mCachedBrightnessInfo.brightnessMin.value,
mCachedBrightnessInfo.brightnessMax.value,
mCachedBrightnessInfo.hbmMode.value,
- mCachedBrightnessInfo.hbmTransitionPoint.value);
+ mCachedBrightnessInfo.hbmTransitionPoint.value,
+ mCachedBrightnessInfo.brightnessMaxReason.value);
}
}
@@ -1648,6 +1676,9 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
changed |=
mCachedBrightnessInfo.checkAndSetFloat(mCachedBrightnessInfo.hbmTransitionPoint,
mHbmController.getTransitionPoint());
+ changed |=
+ mCachedBrightnessInfo.checkAndSetInt(mCachedBrightnessInfo.brightnessMaxReason,
+ mBrightnessThrottler.getBrightnessMaxReason());
return changed;
}
@@ -1679,6 +1710,18 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
}, mContext);
}
+ private BrightnessThrottler createBrightnessThrottlerLocked() {
+ final DisplayDevice device = mLogicalDisplay.getPrimaryDisplayDeviceLocked();
+ final DisplayDeviceConfig ddConfig = device.getDisplayDeviceConfig();
+ final DisplayDeviceConfig.BrightnessThrottlingData data =
+ ddConfig != null ? ddConfig.getBrightnessThrottlingData() : null;
+ return new BrightnessThrottler(mHandler, data,
+ () -> {
+ sendUpdatePowerStateLocked();
+ postBrightnessChangeRunnable();
+ });
+ }
+
private void blockScreenOn() {
if (mPendingScreenOnUnblocker == null) {
Trace.asyncTraceBegin(Trace.TRACE_TAG_POWER, SCREEN_ON_BLOCKED_TRACE_NAME, 0);
@@ -2346,6 +2389,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
pw.println(" mCachedBrightnessInfo.hbmMode=" + mCachedBrightnessInfo.hbmMode.value);
pw.println(" mCachedBrightnessInfo.hbmTransitionPoint=" +
mCachedBrightnessInfo.hbmTransitionPoint.value);
+ pw.println(" mCachedBrightnessInfo.brightnessMaxReason =" +
+ mCachedBrightnessInfo.brightnessMaxReason .value);
}
pw.println(" mDisplayBlanksAfterDozeConfig=" + mDisplayBlanksAfterDozeConfig);
pw.println(" mBrightnessBucketsInDozeConfig=" + mBrightnessBucketsInDozeConfig);
@@ -2384,6 +2429,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
pw.println(" mAppliedAutoBrightness=" + mAppliedAutoBrightness);
pw.println(" mAppliedDimming=" + mAppliedDimming);
pw.println(" mAppliedLowPower=" + mAppliedLowPower);
+ pw.println(" mAppliedThrottling=" + mAppliedThrottling);
pw.println(" mAppliedScreenBrightnessOverride=" + mAppliedScreenBrightnessOverride);
pw.println(" mAppliedTemporaryBrightness=" + mAppliedTemporaryBrightness);
pw.println(" mDozing=" + mDozing);
@@ -2422,6 +2468,10 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
mHbmController.dump(pw);
}
+ if (mBrightnessThrottler != null) {
+ mBrightnessThrottler.dump(pw);
+ }
+
pw.println();
if (mDisplayWhiteBalanceController != null) {
mDisplayWhiteBalanceController.dump(pw);
@@ -2702,7 +2752,9 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
static final int MODIFIER_DIMMED = 0x1;
static final int MODIFIER_LOW_POWER = 0x2;
static final int MODIFIER_HDR = 0x4;
- static final int MODIFIER_MASK = MODIFIER_DIMMED | MODIFIER_LOW_POWER | MODIFIER_HDR;
+ static final int MODIFIER_THROTTLED = 0x8;
+ static final int MODIFIER_MASK = MODIFIER_DIMMED | MODIFIER_LOW_POWER | MODIFIER_HDR
+ | MODIFIER_THROTTLED;
// ADJUSTMENT_*
// These things can happen at any point, even if the main brightness reason doesn't
@@ -2777,6 +2829,9 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
if ((modifier & MODIFIER_HDR) != 0) {
sb.append(" hdr");
}
+ if ((modifier & MODIFIER_THROTTLED) != 0) {
+ sb.append(" throttled");
+ }
int strlen = sb.length();
if (sb.charAt(strlen - 1) == '[') {
sb.setLength(strlen - 2);
@@ -2813,6 +2868,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
public MutableInt hbmMode = new MutableInt(BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF);
public MutableFloat hbmTransitionPoint =
new MutableFloat(HighBrightnessModeController.HBM_TRANSITION_POINT_INVALID);
+ public MutableInt brightnessMaxReason =
+ new MutableInt(BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE);
public boolean checkAndSetFloat(MutableFloat mf, float f) {
if (mf.value != f) {
diff --git a/services/core/java/com/android/server/display/HighBrightnessModeController.java b/services/core/java/com/android/server/display/HighBrightnessModeController.java
index b3be894b9510..23c17f5af10d 100644
--- a/services/core/java/com/android/server/display/HighBrightnessModeController.java
+++ b/services/core/java/com/android/server/display/HighBrightnessModeController.java
@@ -82,7 +82,15 @@ class HighBrightnessModeController {
private boolean mIsTimeAvailable = false;
private boolean mIsAutoBrightnessEnabled = false;
private boolean mIsAutoBrightnessOffByState = false;
+
+ // The following values are typically reported by DisplayPowerController.
+ // This value includes brightness throttling effects.
private float mBrightness;
+ // This value excludes brightness throttling effects.
+ private float mUnthrottledBrightness;
+ private @BrightnessInfo.BrightnessMaxReason int mThrottlingReason =
+ BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE;
+
private int mHbmMode = BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF;
private boolean mIsHdrLayerPresent = false;
private boolean mIsThermalStatusWithinLimit = true;
@@ -192,11 +200,14 @@ class HighBrightnessModeController {
}
}
- void onBrightnessChanged(float brightness) {
+ void onBrightnessChanged(float brightness, float unthrottledBrightness,
+ @BrightnessInfo.BrightnessMaxReason int throttlingReason) {
if (!deviceSupportsHbm()) {
return;
}
mBrightness = brightness;
+ mUnthrottledBrightness = unthrottledBrightness;
+ mThrottlingReason = throttlingReason;
// If we are starting or ending a high brightness mode session, store the current
// session in mRunningStartTimeMillis, or the old one in mEvents.
@@ -274,6 +285,8 @@ class HighBrightnessModeController {
private void dumpLocal(PrintWriter pw) {
pw.println("HighBrightnessModeController:");
pw.println(" mBrightness=" + mBrightness);
+ pw.println(" mUnthrottledBrightness=" + mUnthrottledBrightness);
+ pw.println(" mThrottlingReason=" + BrightnessInfo.briMaxReasonToString(mThrottlingReason));
pw.println(" mCurrentMin=" + getCurrentBrightnessMin());
pw.println(" mCurrentMax=" + getCurrentBrightnessMax());
pw.println(" mHbmMode=" + BrightnessInfo.hbmToString(mHbmMode)
@@ -431,6 +444,9 @@ class HighBrightnessModeController {
+ ", mIsThermalStatusWithinLimit: " + mIsThermalStatusWithinLimit
+ ", mIsBlockedByLowPowerMode: " + mIsBlockedByLowPowerMode
+ ", mBrightness: " + mBrightness
+ + ", mUnthrottledBrightness: " + mUnthrottledBrightness
+ + ", mThrottlingReason: "
+ + BrightnessInfo.briMaxReasonToString(mThrottlingReason)
+ ", RunningStartTimeMillis: " + mRunningStartTimeMillis
+ ", nextTimeout: " + (nextTimeout != -1 ? (nextTimeout - currentTime) : -1)
+ ", events: " + mEvents);
@@ -446,31 +462,44 @@ class HighBrightnessModeController {
private void updateHbmMode() {
int newHbmMode = calculateHighBrightnessMode();
- updateHbmStats(mHbmMode, newHbmMode);
+ updateHbmStats(newHbmMode);
if (mHbmMode != newHbmMode) {
mHbmMode = newHbmMode;
mHbmChangeCallback.run();
}
}
- private void updateHbmStats(int mode, int newMode) {
+ private void updateHbmStats(int newMode) {
+ final float transitionPoint = mHbmData.transitionPoint;
int state = FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_OFF;
if (newMode == BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR
- && getHdrBrightnessValue() > mHbmData.transitionPoint) {
+ && getHdrBrightnessValue() > transitionPoint) {
state = FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_HDR;
- } else if (newMode == BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT) {
+ } else if (newMode == BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT
+ && mBrightness > transitionPoint) {
state = FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_SUNLIGHT;
}
if (state == mHbmStatsState) {
return;
}
- mHbmStatsState = state;
int reason =
FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_TRANSITION_REASON_UNKNOWN;
- boolean oldHbmSv = (mode == BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT);
- boolean newHbmSv = (newMode == BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT);
+ final boolean oldHbmSv = (mHbmStatsState
+ == FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_SUNLIGHT);
+ final boolean newHbmSv =
+ (state == FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_SUNLIGHT);
if (oldHbmSv && !newHbmSv) {
+ // HighBrightnessModeController (HBMC) currently supports throttling from two sources:
+ // 1. Internal, received from HBMC.SkinThermalStatusObserver.notifyThrottling()
+ // 2. External, received from HBMC.onBrightnessChanged()
+ // TODO(b/216373254): Deprecate internal throttling source
+ final boolean internalThermalThrottling = !mIsThermalStatusWithinLimit;
+ final boolean externalThermalThrottling =
+ mUnthrottledBrightness > transitionPoint && // We would've liked HBM brightness...
+ mBrightness <= transitionPoint && // ...but we got NBM, because of...
+ mThrottlingReason == BrightnessInfo.BRIGHTNESS_MAX_REASON_THERMAL; // ...thermals.
+
// If more than one conditions are flipped and turn off HBM sunlight
// visibility, only one condition will be reported to make it simple.
if (!mIsAutoBrightnessEnabled && mIsAutoBrightnessOffByState) {
@@ -483,7 +512,7 @@ class HighBrightnessModeController {
reason = FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_SV_OFF_LUX_DROP;
} else if (!mIsTimeAvailable) {
reason = FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_SV_OFF_TIME_LIMIT;
- } else if (!mIsThermalStatusWithinLimit) {
+ } else if (internalThermalThrottling || externalThermalThrottling) {
reason = FrameworkStatsLog
.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_SV_OFF_THERMAL_LIMIT;
} else if (mIsHdrLayerPresent) {
@@ -492,10 +521,15 @@ class HighBrightnessModeController {
} else if (mIsBlockedByLowPowerMode) {
reason = FrameworkStatsLog
.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_SV_OFF_BATTERY_SAVE_ON;
+ } else if (mBrightness <= mHbmData.transitionPoint) {
+ // This must be after external thermal check.
+ reason = FrameworkStatsLog
+ .DISPLAY_HBM_STATE_CHANGED__REASON__HBM_SV_OFF_LOW_REQUESTED_BRIGHTNESS;
}
}
mInjector.reportHbmStateChange(mDisplayStatsId, state, reason);
+ mHbmStatsState = state;
}
private String hbmStatsStateToString(int hbmStatsState) {
@@ -572,7 +606,7 @@ class HighBrightnessModeController {
>= ((float) (mWidth * mHeight) * HDR_PERCENT_OF_SCREEN_REQUIRED);
// Calling the brightness update so that we can recalculate
// brightness with HDR in mind.
- onBrightnessChanged(mBrightness);
+ onBrightnessChanged(mBrightness, mUnthrottledBrightness, mThrottlingReason);
});
}
}
diff --git a/services/core/java/com/android/server/display/RampAnimator.java b/services/core/java/com/android/server/display/RampAnimator.java
index ed3b15fb2661..1ebd1f5a535c 100644
--- a/services/core/java/com/android/server/display/RampAnimator.java
+++ b/services/core/java/com/android/server/display/RampAnimator.java
@@ -23,6 +23,8 @@ import android.view.Choreographer;
/**
* A custom animator that progressively updates a property value at
* a given variable rate until it reaches a particular target value.
+ * The ramping at the given rate is done in the perceptual space using
+ * the HLG transfer functions.
*/
class RampAnimator<T> {
private final T mObject;
@@ -57,7 +59,9 @@ class RampAnimator<T> {
* @param rate The convergence rate in units per second, or 0 to set the value immediately.
* @return True if the target differs from the previous target.
*/
- public boolean animateTo(float target, float rate) {
+ public boolean animateTo(float targetLinear, float rate) {
+ // Convert the target from the linear into the HLG space.
+ final float target = BrightnessUtils.convertLinearToGamma(targetLinear);
// Immediately jump to the target the first time.
if (mFirstTime || rate <= 0) {
@@ -156,7 +160,9 @@ class RampAnimator<T> {
final float oldCurrentValue = mCurrentValue;
mCurrentValue = mAnimatedValue;
if (oldCurrentValue != mCurrentValue) {
- mProperty.setValue(mObject, mCurrentValue);
+ // Convert value from HLG into linear space for the property.
+ final float linearCurrentVal = BrightnessUtils.convertGammaToLinear(mCurrentValue);
+ mProperty.setValue(mObject, linearCurrentVal);
}
if (mTargetValue != mCurrentValue) {
postAnimationCallback();
@@ -201,14 +207,14 @@ class RampAnimator<T> {
* If this is the first time the property is being set or if the rate is 0,
* the value jumps directly to the target.
*
- * @param firstTarget The first target value.
- * @param secondTarget The second target value.
+ * @param linearFirstTarget The first target value in linear space.
+ * @param linearSecondTarget The second target value in linear space.
* @param rate The convergence rate in units per second, or 0 to set the value immediately.
* @return True if either target differs from the previous target.
*/
- public boolean animateTo(float firstTarget, float secondTarget, float rate) {
- final boolean firstRetval = mFirst.animateTo(firstTarget, rate);
- final boolean secondRetval = mSecond.animateTo(secondTarget, rate);
+ public boolean animateTo(float linearFirstTarget, float linearSecondTarget, float rate) {
+ final boolean firstRetval = mFirst.animateTo(linearFirstTarget, rate);
+ final boolean secondRetval = mSecond.animateTo(linearSecondTarget, rate);
return firstRetval && secondRetval;
}
diff --git a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java
index 151ec8183269..fb36dc792a67 100644
--- a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java
+++ b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java
@@ -44,26 +44,18 @@ public class DisplayWhiteBalanceController implements
AmbientSensor.AmbientBrightnessSensor.Callbacks,
AmbientSensor.AmbientColorTemperatureSensor.Callbacks {
- protected static final String TAG = "DisplayWhiteBalanceController";
- protected boolean mLoggingEnabled;
+ private static final String TAG = "DisplayWhiteBalanceController";
+ private boolean mLoggingEnabled;
- private boolean mEnabled;
-
- // To decouple the DisplayPowerController from the DisplayWhiteBalanceController, the DPC
- // implements Callbacks and passes itself to the DWBC so it can call back into it without
- // knowing about it.
- private Callbacks mCallbacks;
-
- private AmbientSensor.AmbientBrightnessSensor mBrightnessSensor;
+ private final ColorDisplayServiceInternal mColorDisplayServiceInternal;
+ private final AmbientSensor.AmbientBrightnessSensor mBrightnessSensor;
@VisibleForTesting
AmbientFilter mBrightnessFilter;
- private AmbientSensor.AmbientColorTemperatureSensor mColorTemperatureSensor;
-
+ private final AmbientSensor.AmbientColorTemperatureSensor mColorTemperatureSensor;
@VisibleForTesting
AmbientFilter mColorTemperatureFilter;
- private DisplayWhiteBalanceThrottler mThrottler;
-
+ private final DisplayWhiteBalanceThrottler mThrottler;
// In low brightness conditions the ALS readings are more noisy and produce
// high errors. This default is introduced to provide a fixed display color
// temperature when sensor readings become unreliable.
@@ -74,16 +66,12 @@ public class DisplayWhiteBalanceController implements
private final float mHighLightAmbientColorTemperature;
private float mAmbientColorTemperature;
-
@VisibleForTesting
float mPendingAmbientColorTemperature;
private float mLastAmbientColorTemperature;
- private ColorDisplayServiceInternal mColorDisplayServiceInternal;
-
// The most recent ambient color temperature values are kept for debugging purposes.
- private static final int HISTORY_SIZE = 50;
- private History mAmbientColorTemperatureHistory;
+ private final History mAmbientColorTemperatureHistory;
// Override the ambient color temperature for debugging purposes.
private float mAmbientColorTemperatureOverride;
@@ -91,6 +79,10 @@ public class DisplayWhiteBalanceController implements
// A piecewise linear relationship between ambient and display color temperatures.
private Spline.LinearSpline mAmbientToDisplayColorTemperatureSpline;
+ // A piecewise linear relationship between ambient and display color temperatures, with a
+ // stronger change between the two sets of values.
+ private Spline.LinearSpline mStrongAmbientToDisplayColorTemperatureSpline;
+
// In very low or very high brightness conditions Display White Balance should
// be to set to a default instead of using mAmbientToDisplayColorTemperatureSpline.
// However, setting Display White Balance based on thresholds can cause the
@@ -109,6 +101,17 @@ public class DisplayWhiteBalanceController implements
private float mLatestLowLightBias;
private float mLatestHighLightBias;
+ private boolean mEnabled;
+
+ // Whether a higher-strength adjustment should be applied; this must be enabled in addition to
+ // mEnabled in order to be applied.
+ private boolean mStrongModeEnabled;
+
+ // To decouple the DisplayPowerController from the DisplayWhiteBalanceController, the DPC
+ // implements Callbacks and passes itself to the DWBC so it can call back into it without
+ // knowing about it.
+ private Callbacks mDisplayPowerControllerCallbacks;
+
/**
* @param brightnessSensor
* The sensor used to detect changes in the ambient brightness.
@@ -159,16 +162,18 @@ public class DisplayWhiteBalanceController implements
@NonNull AmbientSensor.AmbientColorTemperatureSensor colorTemperatureSensor,
@NonNull AmbientFilter colorTemperatureFilter,
@NonNull DisplayWhiteBalanceThrottler throttler,
- float[] lowLightAmbientBrightnesses, float[] lowLightAmbientBiases,
+ float[] lowLightAmbientBrightnesses,
+ float[] lowLightAmbientBiases,
float lowLightAmbientColorTemperature,
- float[] highLightAmbientBrightnesses, float[] highLightAmbientBiases,
+ float[] highLightAmbientBrightnesses,
+ float[] highLightAmbientBiases,
float highLightAmbientColorTemperature,
- float[] ambientColorTemperatures, float[] displayColorTemperatures) {
+ float[] ambientColorTemperatures,
+ float[] displayColorTemperatures,
+ float[] strongAmbientColorTemperatures,
+ float[] strongDisplayColorTemperatures) {
validateArguments(brightnessSensor, brightnessFilter, colorTemperatureSensor,
colorTemperatureFilter, throttler);
- mLoggingEnabled = false;
- mEnabled = false;
- mCallbacks = null;
mBrightnessSensor = brightnessSensor;
mBrightnessFilter = brightnessFilter;
mColorTemperatureSensor = colorTemperatureSensor;
@@ -179,7 +184,7 @@ public class DisplayWhiteBalanceController implements
mAmbientColorTemperature = -1.0f;
mPendingAmbientColorTemperature = -1.0f;
mLastAmbientColorTemperature = -1.0f;
- mAmbientColorTemperatureHistory = new History(HISTORY_SIZE);
+ mAmbientColorTemperatureHistory = new History(/* size= */ 50);
mAmbientColorTemperatureOverride = -1.0f;
try {
@@ -235,6 +240,13 @@ public class DisplayWhiteBalanceController implements
mAmbientToDisplayColorTemperatureSpline = null;
}
+ try {
+ mStrongAmbientToDisplayColorTemperatureSpline = new Spline.LinearSpline(
+ strongAmbientColorTemperatures, strongDisplayColorTemperatures);
+ } catch (Exception e) {
+ Slog.e(TAG, "Failed to create strong ambient to display color temperature spline", e);
+ }
+
mColorDisplayServiceInternal = LocalServices.getService(ColorDisplayServiceInternal.class);
}
@@ -255,6 +267,19 @@ public class DisplayWhiteBalanceController implements
}
/**
+ * Enable/disable the stronger adjustment option.
+ *
+ * @param enabled whether the stronger adjustment option should be turned on
+ */
+ public void setStrongModeEnabled(boolean enabled) {
+ mStrongModeEnabled = enabled;
+ if (mEnabled) {
+ updateAmbientColorTemperature();
+ updateDisplayColorTemperature();
+ }
+ }
+
+ /**
* Set an object to call back to when the display color temperature should be updated.
*
* @param callbacks
@@ -263,10 +288,10 @@ public class DisplayWhiteBalanceController implements
* @return Whether the method succeeded or not.
*/
public boolean setCallbacks(Callbacks callbacks) {
- if (mCallbacks == callbacks) {
+ if (mDisplayPowerControllerCallbacks == callbacks) {
return false;
}
- mCallbacks = callbacks;
+ mDisplayPowerControllerCallbacks = callbacks;
return true;
}
@@ -321,7 +346,7 @@ public class DisplayWhiteBalanceController implements
writer.println("DisplayWhiteBalanceController");
writer.println(" mLoggingEnabled=" + mLoggingEnabled);
writer.println(" mEnabled=" + mEnabled);
- writer.println(" mCallbacks=" + mCallbacks);
+ writer.println(" mDisplayPowerControllerCallbacks=" + mDisplayPowerControllerCallbacks);
mBrightnessSensor.dump(writer);
mBrightnessFilter.dump(writer);
mColorTemperatureSensor.dump(writer);
@@ -336,6 +361,8 @@ public class DisplayWhiteBalanceController implements
writer.println(" mAmbientColorTemperatureOverride=" + mAmbientColorTemperatureOverride);
writer.println(" mAmbientToDisplayColorTemperatureSpline="
+ mAmbientToDisplayColorTemperatureSpline);
+ writer.println(" mStrongAmbientToDisplayColorTemperatureSpline="
+ + mStrongAmbientToDisplayColorTemperatureSpline);
writer.println(" mLowLightAmbientBrightnessToBiasSpline="
+ mLowLightAmbientBrightnessToBiasSpline);
writer.println(" mHighLightAmbientBrightnessToBiasSpline="
@@ -364,9 +391,20 @@ public class DisplayWhiteBalanceController implements
float ambientColorTemperature = mColorTemperatureFilter.getEstimate(time);
mLatestAmbientColorTemperature = ambientColorTemperature;
- if (mAmbientToDisplayColorTemperatureSpline != null && ambientColorTemperature != -1.0f) {
- ambientColorTemperature =
- mAmbientToDisplayColorTemperatureSpline.interpolate(ambientColorTemperature);
+ if (mStrongModeEnabled) {
+ if (mStrongAmbientToDisplayColorTemperatureSpline != null
+ && ambientColorTemperature != -1.0f) {
+ ambientColorTemperature =
+ mStrongAmbientToDisplayColorTemperatureSpline.interpolate(
+ ambientColorTemperature);
+ }
+ } else {
+ if (mAmbientToDisplayColorTemperatureSpline != null
+ && ambientColorTemperature != -1.0f) {
+ ambientColorTemperature =
+ mAmbientToDisplayColorTemperatureSpline.interpolate(
+ ambientColorTemperature);
+ }
}
float ambientBrightness = mBrightnessFilter.getEstimate(time);
@@ -409,8 +447,8 @@ public class DisplayWhiteBalanceController implements
Slog.d(TAG, "pending ambient color temperature: " + ambientColorTemperature);
}
mPendingAmbientColorTemperature = ambientColorTemperature;
- if (mCallbacks != null) {
- mCallbacks.updateWhiteBalance();
+ if (mDisplayPowerControllerCallbacks != null) {
+ mDisplayPowerControllerCallbacks.updateWhiteBalance();
}
}
diff --git a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceFactory.java b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceFactory.java
index a72b1ed8f3ec..07821b0a984e 100644
--- a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceFactory.java
+++ b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceFactory.java
@@ -87,15 +87,22 @@ public class DisplayWhiteBalanceFactory {
.config_displayWhiteBalanceHighLightAmbientColorTemperature);
final float[] ambientColorTemperatures = getFloatArray(resources,
com.android.internal.R.array.config_displayWhiteBalanceAmbientColorTemperatures);
- final float[] displayColorTempeartures = getFloatArray(resources,
+ final float[] displayColorTemperatures = getFloatArray(resources,
com.android.internal.R.array.config_displayWhiteBalanceDisplayColorTemperatures);
+ final float[] strongAmbientColorTemperatures = getFloatArray(resources,
+ com.android.internal.R.array
+ .config_displayWhiteBalanceStrongAmbientColorTemperatures);
+ final float[] strongDisplayColorTemperatures = getFloatArray(resources,
+ com.android.internal.R.array
+ .config_displayWhiteBalanceStrongDisplayColorTemperatures);
final DisplayWhiteBalanceController controller = new DisplayWhiteBalanceController(
brightnessSensor, brightnessFilter, colorTemperatureSensor, colorTemperatureFilter,
throttler, displayWhiteBalanceLowLightAmbientBrightnesses,
displayWhiteBalanceLowLightAmbientBiases, lowLightAmbientColorTemperature,
displayWhiteBalanceHighLightAmbientBrightnesses,
displayWhiteBalanceHighLightAmbientBiases, highLightAmbientColorTemperature,
- ambientColorTemperatures, displayColorTempeartures);
+ ambientColorTemperatures, displayColorTemperatures, strongAmbientColorTemperatures,
+ strongDisplayColorTemperatures);
brightnessSensor.setCallbacks(controller);
colorTemperatureSensor.setCallbacks(controller);
return controller;
diff --git a/services/core/java/com/android/server/dreams/DreamManagerService.java b/services/core/java/com/android/server/dreams/DreamManagerService.java
index 611b28850efe..f0a6af3c8834 100644
--- a/services/core/java/com/android/server/dreams/DreamManagerService.java
+++ b/services/core/java/com/android/server/dreams/DreamManagerService.java
@@ -416,13 +416,14 @@ public final class DreamManagerService extends SystemService {
mCurrentDreamCanDoze = canDoze;
mCurrentDreamUserId = userId;
+ if (!mCurrentDreamName.equals(mAmbientDisplayComponent)) {
+ mUiEventLogger.log(DreamManagerEvent.DREAM_START);
+ }
+
PowerManager.WakeLock wakeLock = mPowerManager
.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "startDream");
mHandler.post(wakeLock.wrap(() -> {
mAtmInternal.notifyDreamStateChanged(true);
- if (!mCurrentDreamName.equals(mAmbientDisplayComponent)) {
- mUiEventLogger.log(DreamManagerEvent.DREAM_START);
- }
mController.startDream(newToken, name, isTest, canDoze, userId, wakeLock,
mDreamOverlayServiceName);
}));
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
index c674ffebfe92..454a76ac04f4 100755
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
@@ -901,17 +901,12 @@ abstract class HdmiCecLocalDevice {
protected int handleVendorCommandWithId(HdmiCecMessage message) {
byte[] params = message.getParams();
int vendorId = HdmiUtils.threeBytesToInt(params);
- if (vendorId == mService.getVendorId()) {
- if (!mService.invokeVendorCommandListenersOnReceived(
- mDeviceType, message.getSource(), message.getDestination(), params, true)) {
- return Constants.ABORT_REFUSED;
- }
- } else if (message.getDestination() != Constants.ADDR_BROADCAST
- && message.getSource() != Constants.ADDR_UNREGISTERED) {
- Slog.v(TAG, "Wrong direct vendor command. Replying with <Feature Abort>");
- return Constants.ABORT_UNRECOGNIZED_OPCODE;
- } else {
+ if (message.getDestination() == Constants.ADDR_BROADCAST
+ || message.getSource() == Constants.ADDR_UNREGISTERED) {
Slog.v(TAG, "Wrong broadcast vendor command. Ignoring");
+ } else if (!mService.invokeVendorCommandListenersOnReceived(
+ mDeviceType, message.getSource(), message.getDestination(), params, true)) {
+ return Constants.ABORT_REFUSED;
}
return Constants.HANDLED;
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
index b23395f36e63..f413fbd5c9b2 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
@@ -17,10 +17,15 @@
package com.android.server.hdmi;
import android.annotation.CallSuper;
+import android.content.ActivityNotFoundException;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.hdmi.IHdmiControlCallback;
import android.hardware.tv.cec.V1_0.SendMessageResult;
+import android.os.Binder;
import android.os.Handler;
import android.os.PowerManager;
import android.os.PowerManager.WakeLock;
@@ -408,7 +413,7 @@ public class HdmiCecLocalDevicePlayback extends HdmiCecLocalDeviceSource {
// locale from being chosen. 'eng' in the CEC command, for instance,
// will always be mapped to en-AU among other variants like en-US, en-GB,
// an en-IN, which may not be the expected one.
- LocalePicker.updateLocale(localeInfo.getLocale());
+ startSetMenuLanguageActivity(localeInfo.getLocale());
return Constants.HANDLED;
}
}
@@ -420,6 +425,24 @@ public class HdmiCecLocalDevicePlayback extends HdmiCecLocalDeviceSource {
}
}
+ private void startSetMenuLanguageActivity(Locale locale) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ Context context = mService.getContext();
+ Intent intent = new Intent();
+ intent.putExtra(HdmiControlManager.EXTRA_LOCALE, locale.toLanguageTag());
+ intent.setComponent(
+ ComponentName.unflattenFromString(context.getResources().getString(
+ com.android.internal.R.string.config_hdmiCecSetMenuLanguageActivity)));
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ context.startActivityAsUser(intent, context.getUser());
+ } catch (ActivityNotFoundException e) {
+ Slog.e(TAG, "unable to start HdmiCecSetMenuLanguageActivity");
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
@Override
@Constants.HandleMessageResult
protected int handleSetSystemAudioMode(HdmiCecMessage message) {
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecStandbyModeHandler.java b/services/core/java/com/android/server/hdmi/HdmiCecStandbyModeHandler.java
index 6f7473d60121..57fe9e6a4acc 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecStandbyModeHandler.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecStandbyModeHandler.java
@@ -133,6 +133,7 @@ public final class HdmiCecStandbyModeHandler {
addHandler(Constants.MESSAGE_SET_OSD_NAME, mBypasser);
addHandler(Constants.MESSAGE_DEVICE_VENDOR_ID, mBypasser);
addHandler(Constants.MESSAGE_REPORT_POWER_STATUS, mBypasser);
+ addHandler(Constants.MESSAGE_GIVE_FEATURES, mBypasser);
addHandler(Constants.MESSAGE_USER_CONTROL_PRESSED, mUserControlProcessedHandler);
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 8391e0b4e19a..8ac233114b48 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -1701,11 +1701,11 @@ public class HdmiControlService extends SystemService {
class VendorCommandListenerRecord implements IBinder.DeathRecipient {
private final IHdmiVendorCommandListener mListener;
- private final int mDeviceType;
+ private final int mVendorId;
- public VendorCommandListenerRecord(IHdmiVendorCommandListener listener, int deviceType) {
+ VendorCommandListenerRecord(IHdmiVendorCommandListener listener, int vendorId) {
mListener = listener;
- mDeviceType = deviceType;
+ mVendorId = vendorId;
}
@Override
@@ -2191,10 +2191,10 @@ public class HdmiControlService extends SystemService {
}
@Override
- public void addVendorCommandListener(final IHdmiVendorCommandListener listener,
- final int deviceType) {
+ public void addVendorCommandListener(
+ final IHdmiVendorCommandListener listener, final int vendorId) {
initBinderCall();
- HdmiControlService.this.addVendorCommandListener(listener, deviceType);
+ HdmiControlService.this.addVendorCommandListener(listener, vendorId);
}
@Override
@@ -3354,8 +3354,9 @@ public class HdmiControlService extends SystemService {
mStandbyMessageReceived = false;
}
- private void addVendorCommandListener(IHdmiVendorCommandListener listener, int deviceType) {
- VendorCommandListenerRecord record = new VendorCommandListenerRecord(listener, deviceType);
+ @VisibleForTesting
+ void addVendorCommandListener(IHdmiVendorCommandListener listener, int vendorId) {
+ VendorCommandListenerRecord record = new VendorCommandListenerRecord(listener, vendorId);
try {
listener.asBinder().linkToDeath(record, 0);
} catch (RemoteException e) {
@@ -3374,8 +3375,14 @@ public class HdmiControlService extends SystemService {
return false;
}
for (VendorCommandListenerRecord record : mVendorCommandListenerRecords) {
- if (record.mDeviceType != deviceType) {
- continue;
+ if (hasVendorId) {
+ int vendorId =
+ ((params[0] & 0xFF) << 16)
+ + ((params[1] & 0xFF) << 8)
+ + (params[2] & 0xFF);
+ if (record.mVendorId != vendorId) {
+ continue;
+ }
}
try {
record.mListener.onReceived(srcAddress, destAddress, params, hasVendorId);
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index de933cc47005..783a88ca29bf 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -2285,14 +2285,8 @@ public class InputManagerService extends IInputManager.Stub
nativeNotifyPortAssociationsChanged(mPtr);
}
- /**
- * Add a runtime association between the input device name and the display unique id.
- * @param inputDeviceName The name of the input device.
- * @param displayUniqueId The unique id of the associated display.
- */
@Override // Binder call
- public void addUniqueIdAssociation(@NonNull String inputDeviceName,
- @NonNull String displayUniqueId) {
+ public void addUniqueIdAssociation(@NonNull String inputPort, @NonNull String displayUniqueId) {
if (!checkCallingPermission(
android.Manifest.permission.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY,
"addNameAssociation()")) {
@@ -2300,20 +2294,16 @@ public class InputManagerService extends IInputManager.Stub
"Requires ASSOCIATE_INPUT_DEVICE_TO_DISPLAY permission");
}
- Objects.requireNonNull(inputDeviceName);
+ Objects.requireNonNull(inputPort);
Objects.requireNonNull(displayUniqueId);
synchronized (mAssociationsLock) {
- mUniqueIdAssociations.put(inputDeviceName, displayUniqueId);
+ mUniqueIdAssociations.put(inputPort, displayUniqueId);
}
nativeChangeUniqueIdAssociation(mPtr);
}
- /**
- * Remove the runtime association between the input device and the display.
- * @param inputDeviceName The port of the input device to be cleared.
- */
@Override // Binder call
- public void removeUniqueIdAssociation(@NonNull String inputDeviceName) {
+ public void removeUniqueIdAssociation(@NonNull String inputPort) {
if (!checkCallingPermission(
android.Manifest.permission.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY,
"removeUniqueIdAssociation()")) {
@@ -2321,9 +2311,9 @@ public class InputManagerService extends IInputManager.Stub
"Requires ASSOCIATE_INPUT_DEVICE_TO_DISPLAY permission");
}
- Objects.requireNonNull(inputDeviceName);
+ Objects.requireNonNull(inputPort);
synchronized (mAssociationsLock) {
- mUniqueIdAssociations.remove(inputDeviceName);
+ mUniqueIdAssociations.remove(inputPort);
}
nativeChangeUniqueIdAssociation(mPtr);
}
@@ -2594,6 +2584,13 @@ public class InputManagerService extends IInputManager.Stub
pw.println(" display: " + v);
});
}
+ if (!mUniqueIdAssociations.isEmpty()) {
+ pw.println("Unique Id Associations:");
+ mUniqueIdAssociations.forEach((k, v) -> {
+ pw.print(" port: " + k);
+ pw.println(" uniqueId: " + v);
+ });
+ }
}
}
diff --git a/services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java b/services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java
index c87ca92d632e..6cb3b3b6740d 100644
--- a/services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java
+++ b/services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java
@@ -107,9 +107,11 @@ final class IInputMethodInvoker {
@AnyThread
void initializeInternal(IBinder token, IInputMethodPrivilegedOperations privOps,
- int configChanges, boolean stylusHwSupported) {
+ int configChanges, boolean stylusHwSupported,
+ boolean shouldShowImeSwitcherWhenImeIsShown) {
try {
- mTarget.initializeInternal(token, privOps, configChanges, stylusHwSupported);
+ mTarget.initializeInternal(token, privOps, configChanges, stylusHwSupported,
+ shouldShowImeSwitcherWhenImeIsShown);
} catch (RemoteException e) {
logRemoteException(e);
}
@@ -145,9 +147,20 @@ final class IInputMethodInvoker {
@AnyThread
void startInput(IBinder startInputToken, IInputContext inputContext, EditorInfo attribute,
- boolean restarting) {
+ boolean restarting, boolean shouldShowImeSwitcherWhenImeIsShown) {
try {
- mTarget.startInput(startInputToken, inputContext, attribute, restarting);
+ mTarget.startInput(startInputToken, inputContext, attribute, restarting,
+ shouldShowImeSwitcherWhenImeIsShown);
+ } catch (RemoteException e) {
+ logRemoteException(e);
+ }
+ }
+
+ @AnyThread
+ void onShouldShowImeSwitcherWhenImeIsShownChanged(boolean shouldShowImeSwitcherWhenImeIsShown) {
+ try {
+ mTarget.onShouldShowImeSwitcherWhenImeIsShownChanged(
+ shouldShowImeSwitcherWhenImeIsShown);
} catch (RemoteException e) {
logRemoteException(e);
}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
index 9a53d837f25b..80c83e97256e 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
@@ -162,28 +162,30 @@ public abstract class InputMethodManagerInternal {
}
@Override
- public List<InputMethodInfo> getInputMethodListAsUser(int userId) {
+ public List<InputMethodInfo> getInputMethodListAsUser(@UserIdInt int userId) {
return Collections.emptyList();
}
@Override
- public List<InputMethodInfo> getEnabledInputMethodListAsUser(int userId) {
+ public List<InputMethodInfo> getEnabledInputMethodListAsUser(
+ @UserIdInt int userId) {
return Collections.emptyList();
}
@Override
- public void onCreateInlineSuggestionsRequest(int userId,
+ public void onCreateInlineSuggestionsRequest(@UserIdInt int userId,
InlineSuggestionsRequestInfo requestInfo,
IInlineSuggestionsRequestCallback cb) {
}
@Override
- public boolean switchToInputMethod(String imeId, int userId) {
+ public boolean switchToInputMethod(String imeId, @UserIdInt int userId) {
return false;
}
@Override
- public boolean setInputMethodEnabled(String imeId, boolean enabled, int userId) {
+ public boolean setInputMethodEnabled(String imeId, boolean enabled,
+ @UserIdInt int userId) {
return false;
}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index c6a206bfff52..c207738a4f88 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -2325,10 +2325,12 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
true /* direct */);
}
+ final boolean shouldShowImeSwitcherWhenImeIsShown =
+ shouldShowImeSwitcherWhenImeIsShownLocked();
final SessionState session = mCurClient.curSession;
setEnabledSessionLocked(session);
- session.method.startInput(startInputToken, mCurInputContext, mCurAttribute, restarting);
-
+ session.method.startInput(startInputToken, mCurInputContext, mCurAttribute, restarting,
+ shouldShowImeSwitcherWhenImeIsShown);
if (mShowRequested) {
if (DEBUG) Slog.v(TAG, "Attach new input asks to show input");
showCurrentInputLocked(mCurFocusedWindow, getAppShowFlagsLocked(), null,
@@ -2528,7 +2530,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
+ mCurTokenDisplayId);
}
inputMethod.initializeInternal(token, new InputMethodPrivilegedOperationsImpl(this, token),
- configChanges, supportStylusHw);
+ configChanges, supportStylusHw, shouldShowImeSwitcherWhenImeIsShownLocked());
}
@AnyThread
@@ -2731,6 +2733,12 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
}
@GuardedBy("ImfLock.class")
+ boolean shouldShowImeSwitcherWhenImeIsShownLocked() {
+ return shouldShowImeSwitcherLocked(
+ InputMethodService.IME_ACTIVE | InputMethodService.IME_VISIBLE);
+ }
+
+ @GuardedBy("ImfLock.class")
private boolean shouldShowImeSwitcherLocked(int visibility) {
if (!mShowOngoingImeSwitcherForPhones) return false;
if (mMenuController.getSwitchingDialogLocked() != null) return false;
@@ -2990,6 +2998,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
// the same enabled IMEs list.
mSwitchingController.resetCircularListLocked(mContext);
+ sendShouldShowImeSwitcherWhenImeIsShownLocked();
}
@GuardedBy("ImfLock.class")
@@ -4308,6 +4317,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
updateImeWindowStatus(msg.arg1 == 1);
return true;
}
+
// ---------------------------------------------------------
case MSG_UNBIND_CLIENT:
@@ -4368,6 +4378,9 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
// --------------------------------------------------------------
case MSG_HARD_KEYBOARD_SWITCH_CHANGED:
mMenuController.handleHardKeyboardStatusChange(msg.arg1 == 1);
+ synchronized (ImfLock.class) {
+ sendShouldShowImeSwitcherWhenImeIsShownLocked();
+ }
return true;
case MSG_SYSTEM_UNLOCK_USER: {
final int userId = msg.arg1;
@@ -4638,6 +4651,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
// the same enabled IMEs list.
mSwitchingController.resetCircularListLocked(mContext);
+ sendShouldShowImeSwitcherWhenImeIsShownLocked();
+
// Notify InputMethodListListeners of the new installed InputMethods.
final List<InputMethodInfo> inputMethodList = new ArrayList<>(mMethodList);
mHandler.obtainMessage(MSG_DISPATCH_ON_INPUT_METHOD_LIST_UPDATED,
@@ -4645,6 +4660,17 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
}
@GuardedBy("ImfLock.class")
+ void sendShouldShowImeSwitcherWhenImeIsShownLocked() {
+ final IInputMethodInvoker curMethod = mBindingController.getCurMethod();
+ if (curMethod == null) {
+ // No need to send the data if the IME is not yet bound.
+ return;
+ }
+ curMethod.onShouldShowImeSwitcherWhenImeIsShownChanged(
+ shouldShowImeSwitcherWhenImeIsShownLocked());
+ }
+
+ @GuardedBy("ImfLock.class")
private void updateDefaultVoiceImeIfNeededLocked() {
final String systemSpeechRecognizer =
mContext.getString(com.android.internal.R.string.config_systemSpeechRecognizer);
@@ -4977,28 +5003,28 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
}
@Override
- public List<InputMethodInfo> getInputMethodListAsUser(int userId) {
+ public List<InputMethodInfo> getInputMethodListAsUser(@UserIdInt int userId) {
return mService.getInputMethodListAsUser(userId);
}
@Override
- public List<InputMethodInfo> getEnabledInputMethodListAsUser(int userId) {
+ public List<InputMethodInfo> getEnabledInputMethodListAsUser(@UserIdInt int userId) {
return mService.getEnabledInputMethodListAsUser(userId);
}
@Override
- public void onCreateInlineSuggestionsRequest(int userId,
+ public void onCreateInlineSuggestionsRequest(@UserIdInt int userId,
InlineSuggestionsRequestInfo requestInfo, IInlineSuggestionsRequestCallback cb) {
mService.onCreateInlineSuggestionsRequest(userId, requestInfo, cb);
}
@Override
- public boolean switchToInputMethod(String imeId, int userId) {
+ public boolean switchToInputMethod(String imeId, @UserIdInt int userId) {
return mService.switchToInputMethod(imeId, userId);
}
@Override
- public boolean setInputMethodEnabled(String imeId, boolean enabled, int userId) {
+ public boolean setInputMethodEnabled(String imeId, boolean enabled, @UserIdInt int userId) {
return mService.setInputMethodEnabled(imeId, enabled, userId);
}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java b/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java
index 348bb2d868a2..98bde11ad517 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java
@@ -203,6 +203,7 @@ final class InputMethodMenuController {
attrs.setTitle("Select input method");
w.setAttributes(attrs);
mService.updateSystemUiLocked();
+ mService.sendShouldShowImeSwitcherWhenImeIsShownLocked();
mSwitchingDialog.show();
}
}
@@ -238,6 +239,7 @@ final class InputMethodMenuController {
mSwitchingDialogTitleView = null;
mService.updateSystemUiLocked();
+ mService.sendShouldShowImeSwitcherWhenImeIsShownLocked();
mDialogBuilder = null;
mIms = null;
}
diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java
index ffef80356a66..0c3f9f0e26c6 100644
--- a/services/core/java/com/android/server/location/LocationManagerService.java
+++ b/services/core/java/com/android/server/location/LocationManagerService.java
@@ -17,6 +17,7 @@
package com.android.server.location;
import static android.Manifest.permission.ACCESS_FINE_LOCATION;
+import static android.Manifest.permission.WRITE_SECURE_SETTINGS;
import static android.app.compat.CompatChanges.isChangeEnabled;
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
@@ -829,16 +830,12 @@ public class LocationManagerService extends ILocationManager.Stub implements
"only verified adas packages may use adas gnss bypass requests");
}
if (!isLocationProvider) {
- mContext.enforceCallingOrSelfPermission(
- permission.WRITE_SECURE_SETTINGS,
- "adas gnss bypass requires " + permission.WRITE_SECURE_SETTINGS);
+ LocationPermissions.enforceCallingOrSelfBypassPermission(mContext);
}
}
if (request.isLocationSettingsIgnored()) {
if (!isLocationProvider) {
- mContext.enforceCallingOrSelfPermission(
- permission.WRITE_SECURE_SETTINGS,
- "ignoring location settings requires " + permission.WRITE_SECURE_SETTINGS);
+ LocationPermissions.enforceCallingOrSelfBypassPermission(mContext);
}
}
@@ -933,16 +930,12 @@ public class LocationManagerService extends ILocationManager.Stub implements
"only verified adas packages may use adas gnss bypass requests");
}
if (!isLocationProvider) {
- mContext.enforceCallingOrSelfPermission(
- permission.WRITE_SECURE_SETTINGS,
- "adas gnss bypass requires " + permission.WRITE_SECURE_SETTINGS);
+ LocationPermissions.enforceCallingOrSelfBypassPermission(mContext);
}
}
if (request.isLocationSettingsIgnored()) {
if (!isLocationProvider) {
- mContext.enforceCallingOrSelfPermission(
- permission.WRITE_SECURE_SETTINGS,
- "ignoring location settings requires " + permission.WRITE_SECURE_SETTINGS);
+ LocationPermissions.enforceCallingOrSelfBypassPermission(mContext);
}
}
@@ -1202,7 +1195,7 @@ public class LocationManagerService extends ILocationManager.Stub implements
userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
userId, false, false, "setLocationEnabledForUser", null);
- mContext.enforceCallingOrSelfPermission(permission.WRITE_SECURE_SETTINGS, null);
+ mContext.enforceCallingOrSelfPermission(WRITE_SECURE_SETTINGS, null);
LocationManager.invalidateLocalLocationEnabledCaches();
mInjector.getSettingsHelper().setLocationEnabled(enabled, userId);
@@ -1220,7 +1213,7 @@ public class LocationManagerService extends ILocationManager.Stub implements
userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
userId, false, false, "setAdasGnssLocationEnabledForUser", null);
- mContext.enforceCallingOrSelfPermission(permission.WRITE_SECURE_SETTINGS, null);
+ LocationPermissions.enforceCallingOrSelfBypassPermission(mContext);
mInjector.getLocationSettings().updateUserSettings(userId,
settings -> settings.withAdasGnssLocationEnabled(enabled));
@@ -1239,29 +1232,29 @@ public class LocationManagerService extends ILocationManager.Stub implements
}
@Override
- @RequiresPermission(android.Manifest.permission.AUTOMOTIVE_GNSS_CONTROLS)
- public void setAutoGnssSuspended(boolean suspended) {
- mContext.enforceCallingPermission(permission.AUTOMOTIVE_GNSS_CONTROLS, null);
+ @RequiresPermission(android.Manifest.permission.CONTROL_AUTOMOTIVE_GNSS)
+ public void setAutomotiveGnssSuspended(boolean suspended) {
+ mContext.enforceCallingPermission(permission.CONTROL_AUTOMOTIVE_GNSS, null);
if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
throw new IllegalStateException(
- "setAutoGnssSuspended only allowed on automotive devices");
+ "setAutomotiveGnssSuspended only allowed on automotive devices");
}
- mGnssManagerService.setAutoGnssSuspended(suspended);
+ mGnssManagerService.setAutomotiveGnssSuspended(suspended);
}
@Override
- @RequiresPermission(android.Manifest.permission.AUTOMOTIVE_GNSS_CONTROLS)
- public boolean isAutoGnssSuspended() {
- mContext.enforceCallingPermission(permission.AUTOMOTIVE_GNSS_CONTROLS, null);
+ @RequiresPermission(android.Manifest.permission.CONTROL_AUTOMOTIVE_GNSS)
+ public boolean isAutomotiveGnssSuspended() {
+ mContext.enforceCallingPermission(permission.CONTROL_AUTOMOTIVE_GNSS, null);
if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
throw new IllegalStateException(
- "isAutoGnssSuspended only allowed on automotive devices");
+ "isAutomotiveGnssSuspended only allowed on automotive devices");
}
- return mGnssManagerService.isAutoGnssSuspended();
+ return mGnssManagerService.isAutomotiveGnssSuspended();
}
@Override
diff --git a/services/core/java/com/android/server/location/LocationPermissions.java b/services/core/java/com/android/server/location/LocationPermissions.java
index 7528f8b6ea64..be702d906d3b 100644
--- a/services/core/java/com/android/server/location/LocationPermissions.java
+++ b/services/core/java/com/android/server/location/LocationPermissions.java
@@ -18,6 +18,8 @@ package com.android.server.location;
import static android.Manifest.permission.ACCESS_COARSE_LOCATION;
import static android.Manifest.permission.ACCESS_FINE_LOCATION;
+import static android.Manifest.permission.LOCATION_BYPASS;
+import static android.Manifest.permission.WRITE_SECURE_SETTINGS;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import android.annotation.IntDef;
@@ -121,6 +123,29 @@ public final class LocationPermissions {
}
/**
+ * Throws a security exception if the caller does not hold the required bypass permissions.
+ */
+ public static void enforceCallingOrSelfBypassPermission(Context context) {
+ enforceBypassPermission(context, Binder.getCallingUid(), Binder.getCallingPid());
+ }
+
+ /**
+ * Throws a security exception if the given uid/pid does not hold the required bypass
+ * perissions.
+ */
+ public static void enforceBypassPermission(Context context, int uid, int pid) {
+ if (context.checkPermission(WRITE_SECURE_SETTINGS, pid, uid) == PERMISSION_GRANTED) {
+ // TODO: disallow WRITE_SECURE_SETTINGS permission.
+ return;
+ }
+ if (context.checkPermission(LOCATION_BYPASS, pid, uid) == PERMISSION_GRANTED) {
+ return;
+ }
+ throw new SecurityException("uid" + uid + " does not have " + LOCATION_BYPASS
+ + "or " + WRITE_SECURE_SETTINGS + ".");
+ }
+
+ /**
* Returns false if the caller does not hold the required location permissions.
*/
public static boolean checkCallingOrSelfLocationPermission(Context context,
diff --git a/services/core/java/com/android/server/location/LocationShellCommand.java b/services/core/java/com/android/server/location/LocationShellCommand.java
index b65338d9691d..9c85d18515af 100644
--- a/services/core/java/com/android/server/location/LocationShellCommand.java
+++ b/services/core/java/com/android/server/location/LocationShellCommand.java
@@ -69,6 +69,14 @@ class LocationShellCommand extends BasicShellCommandHandler {
handleSetAdasGnssLocationEnabled();
return 0;
}
+ case "set-automotive-gnss-suspended": {
+ handleSetAutomotiveGnssSuspended();
+ return 0;
+ }
+ case "is-automotive-gnss-suspended": {
+ handleIsAutomotiveGnssSuspended();
+ return 0;
+ }
case "providers": {
String command = getNextArgRequired();
return parseProvidersCommand(command);
@@ -189,6 +197,24 @@ class LocationShellCommand extends BasicShellCommandHandler {
mService.setAdasGnssLocationEnabledForUser(enabled, userId);
}
+ private void handleSetAutomotiveGnssSuspended() {
+ if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
+ throw new IllegalStateException("command only recognized on automotive devices");
+ }
+
+ boolean suspended = Boolean.parseBoolean(getNextArgRequired());
+
+ mService.setAutomotiveGnssSuspended(suspended);
+ }
+
+ private void handleIsAutomotiveGnssSuspended() {
+ if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
+ throw new IllegalStateException("command only recognized on automotive devices");
+ }
+
+ getOutPrintWriter().println(mService.isAutomotiveGnssSuspended());
+ }
+
private void handleAddTestProvider() {
String provider = getNextArgRequired();
@@ -359,6 +385,10 @@ class LocationShellCommand extends BasicShellCommandHandler {
pw.println(" set-adas-gnss-location-enabled true|false [--user <USER_ID>]");
pw.println(" Sets the ADAS GNSS location enabled state. If no user is specified,");
pw.println(" the current user is assumed.");
+ pw.println(" is-automotive-gnss-suspended");
+ pw.println(" Gets the automotive GNSS suspended state.");
+ pw.println(" set-automotive-gnss-suspended true|false");
+ pw.println(" Sets the automotive GNSS suspended state.");
}
pw.println(" providers");
pw.println(" The providers command is followed by a subcommand, as listed below:");
diff --git a/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java b/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java
index 21ea1f600b71..a1ee46b4c943 100644
--- a/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java
+++ b/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java
@@ -465,7 +465,7 @@ public abstract class IContextHubWrapper {
try {
mHub.onHostEndpointConnected(info);
} catch (RemoteException | ServiceSpecificException e) {
- Log.e(TAG, "RemoteException in onHostEndpointConnected");
+ Log.e(TAG, "Exception in onHostEndpointConnected" + e.getMessage());
}
}
@@ -474,7 +474,7 @@ public abstract class IContextHubWrapper {
try {
mHub.onHostEndpointDisconnected((char) hostEndpointId);
} catch (RemoteException | ServiceSpecificException e) {
- Log.e(TAG, "RemoteException in onHostEndpointDisconnected");
+ Log.e(TAG, "Exception in onHostEndpointDisconnected" + e.getMessage());
}
}
@@ -488,6 +488,8 @@ public abstract class IContextHubWrapper {
return ContextHubTransaction.RESULT_SUCCESS;
} catch (RemoteException | ServiceSpecificException e) {
return ContextHubTransaction.RESULT_FAILED_UNKNOWN;
+ } catch (IllegalArgumentException e) {
+ return ContextHubTransaction.RESULT_FAILED_BAD_PARAMS;
}
}
@@ -499,8 +501,10 @@ public abstract class IContextHubWrapper {
try {
mHub.loadNanoapp(contextHubId, aidlNanoAppBinary, transactionId);
return ContextHubTransaction.RESULT_SUCCESS;
- } catch (RemoteException | ServiceSpecificException e) {
+ } catch (RemoteException | ServiceSpecificException | UnsupportedOperationException e) {
return ContextHubTransaction.RESULT_FAILED_UNKNOWN;
+ } catch (IllegalArgumentException e) {
+ return ContextHubTransaction.RESULT_FAILED_BAD_PARAMS;
}
}
@@ -510,8 +514,10 @@ public abstract class IContextHubWrapper {
try {
mHub.unloadNanoapp(contextHubId, nanoappId, transactionId);
return ContextHubTransaction.RESULT_SUCCESS;
- } catch (RemoteException | ServiceSpecificException e) {
+ } catch (RemoteException | ServiceSpecificException | UnsupportedOperationException e) {
return ContextHubTransaction.RESULT_FAILED_UNKNOWN;
+ } catch (IllegalArgumentException e) {
+ return ContextHubTransaction.RESULT_FAILED_BAD_PARAMS;
}
}
@@ -521,8 +527,10 @@ public abstract class IContextHubWrapper {
try {
mHub.enableNanoapp(contextHubId, nanoappId, transactionId);
return ContextHubTransaction.RESULT_SUCCESS;
- } catch (RemoteException | ServiceSpecificException e) {
+ } catch (RemoteException | ServiceSpecificException | UnsupportedOperationException e) {
return ContextHubTransaction.RESULT_FAILED_UNKNOWN;
+ } catch (IllegalArgumentException e) {
+ return ContextHubTransaction.RESULT_FAILED_BAD_PARAMS;
}
}
@@ -532,8 +540,10 @@ public abstract class IContextHubWrapper {
try {
mHub.disableNanoapp(contextHubId, nanoappId, transactionId);
return ContextHubTransaction.RESULT_SUCCESS;
- } catch (RemoteException | ServiceSpecificException e) {
+ } catch (RemoteException | ServiceSpecificException | UnsupportedOperationException e) {
return ContextHubTransaction.RESULT_FAILED_UNKNOWN;
+ } catch (IllegalArgumentException e) {
+ return ContextHubTransaction.RESULT_FAILED_BAD_PARAMS;
}
}
@@ -542,8 +552,10 @@ public abstract class IContextHubWrapper {
try {
mHub.queryNanoapps(contextHubId);
return ContextHubTransaction.RESULT_SUCCESS;
- } catch (RemoteException | ServiceSpecificException e) {
+ } catch (RemoteException | ServiceSpecificException | UnsupportedOperationException e) {
return ContextHubTransaction.RESULT_FAILED_UNKNOWN;
+ } catch (IllegalArgumentException e) {
+ return ContextHubTransaction.RESULT_FAILED_BAD_PARAMS;
}
}
@@ -551,7 +563,7 @@ public abstract class IContextHubWrapper {
mAidlCallbackMap.put(contextHubId, new ContextHubAidlCallback(contextHubId, callback));
try {
mHub.registerCallback(contextHubId, mAidlCallbackMap.get(contextHubId));
- } catch (RemoteException | ServiceSpecificException e) {
+ } catch (RemoteException | ServiceSpecificException | IllegalArgumentException e) {
Log.e(TAG, "Exception while registering callback: " + e.getMessage());
}
}
diff --git a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
index c02411ec4c1b..cd2ba393f6f2 100644
--- a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
@@ -753,7 +753,7 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
* Set whether the GnssLocationProvider is suspended. This method was added to help support
* power management use cases on automotive devices.
*/
- public void setAutoGnssSuspended(boolean suspended) {
+ public void setAutomotiveGnssSuspended(boolean suspended) {
synchronized (mLock) {
mAutomotiveSuspend = suspended;
}
@@ -764,7 +764,7 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
* Return whether the GnssLocationProvider is suspended or not. This method was added to help
* support power management use cases on automotive devices.
*/
- public boolean isAutoGnssSuspended() {
+ public boolean isAutomotiveGnssSuspended() {
synchronized (mLock) {
return mAutomotiveSuspend && !mGpsEnabled;
}
diff --git a/services/core/java/com/android/server/location/gnss/GnssManagerService.java b/services/core/java/com/android/server/location/gnss/GnssManagerService.java
index 11fd727072df..0f9945ccdc6e 100644
--- a/services/core/java/com/android/server/location/gnss/GnssManagerService.java
+++ b/services/core/java/com/android/server/location/gnss/GnssManagerService.java
@@ -113,16 +113,16 @@ public class GnssManagerService {
* Set whether the GnssLocationProvider is suspended on the device. This method was added to
* help support power management use cases on automotive devices.
*/
- public void setAutoGnssSuspended(boolean suspended) {
- mGnssLocationProvider.setAutoGnssSuspended(suspended);
+ public void setAutomotiveGnssSuspended(boolean suspended) {
+ mGnssLocationProvider.setAutomotiveGnssSuspended(suspended);
}
/**
* Return whether the GnssLocationProvider is suspended or not. This method was added to
* help support power management use cases on automotive devices.
*/
- public boolean isAutoGnssSuspended() {
- return mGnssLocationProvider.isAutoGnssSuspended();
+ public boolean isAutomotiveGnssSuspended() {
+ return mGnssLocationProvider.isAutomotiveGnssSuspended();
}
/** Retrieve the IGpsGeofenceHardware. */
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index 8f051303bf03..2d2edfa51896 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -20,6 +20,7 @@ import static android.Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE;
import static android.Manifest.permission.MANAGE_BIOMETRIC;
import static android.Manifest.permission.READ_CONTACTS;
import static android.Manifest.permission.SET_AND_VERIFY_LOCKSCREEN_CREDENTIALS;
+import static android.Manifest.permission.SET_INITIAL_LOCK;
import static android.app.admin.DevicePolicyResources.Strings.Core.PROFILE_ENCRYPTED_DETAIL;
import static android.app.admin.DevicePolicyResources.Strings.Core.PROFILE_ENCRYPTED_MESSAGE;
import static android.app.admin.DevicePolicyResources.Strings.Core.PROFILE_ENCRYPTED_TITLE;
@@ -1650,9 +1651,13 @@ public class LockSettingsService extends ILockSettings.Stub {
"This operation requires secure lock screen feature");
}
if (!hasPermission(PERMISSION) && !hasPermission(SET_AND_VERIFY_LOCKSCREEN_CREDENTIALS)) {
- throw new SecurityException(
- "setLockCredential requires SET_AND_VERIFY_LOCKSCREEN_CREDENTIALS or "
- + PERMISSION);
+ if (hasPermission(SET_INITIAL_LOCK) && savedCredential.isNone()) {
+ // SET_INITIAL_LOCK can only be used if credential is not set.
+ } else {
+ throw new SecurityException(
+ "setLockCredential requires SET_AND_VERIFY_LOCKSCREEN_CREDENTIALS or "
+ + PERMISSION);
+ }
}
final long identity = Binder.clearCallingIdentity();
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index 303ab4669855..7f997df3b222 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -677,9 +677,9 @@ class MediaRouter2ServiceImpl {
UserRecord userRecord = routerRecord.mUserRecord;
userRecord.mRouterRecords.remove(routerRecord);
routerRecord.mUserRecord.mHandler.sendMessage(
- obtainMessage(UserHandler::notifyPreferredFeaturesChangedToManagers,
+ obtainMessage(UserHandler::notifyDiscoveryPreferenceChangedToManagers,
routerRecord.mUserRecord.mHandler,
- routerRecord.mPackageName, /* preferredFeatures=*/ null));
+ routerRecord.mPackageName, null));
userRecord.mHandler.sendMessage(
obtainMessage(UserHandler::updateDiscoveryPreferenceOnHandler,
userRecord.mHandler));
@@ -694,10 +694,10 @@ class MediaRouter2ServiceImpl {
}
routerRecord.mDiscoveryPreference = discoveryRequest;
routerRecord.mUserRecord.mHandler.sendMessage(
- obtainMessage(UserHandler::notifyPreferredFeaturesChangedToManagers,
+ obtainMessage(UserHandler::notifyDiscoveryPreferenceChangedToManagers,
routerRecord.mUserRecord.mHandler,
routerRecord.mPackageName,
- routerRecord.mDiscoveryPreference.getPreferredFeatures()));
+ routerRecord.mDiscoveryPreference));
routerRecord.mUserRecord.mHandler.sendMessage(
obtainMessage(UserHandler::updateDiscoveryPreferenceOnHandler,
routerRecord.mUserRecord.mHandler));
@@ -921,7 +921,7 @@ class MediaRouter2ServiceImpl {
// TODO: UserRecord <-> routerRecord, why do they reference each other?
// How about removing mUserRecord from routerRecord?
routerRecord.mUserRecord.mHandler.sendMessage(
- obtainMessage(UserHandler::notifyPreferredFeaturesChangedToManager,
+ obtainMessage(UserHandler::notifyDiscoveryPreferenceChangedToManager,
routerRecord.mUserRecord.mHandler, routerRecord, manager));
}
@@ -2118,19 +2118,19 @@ class MediaRouter2ServiceImpl {
}
}
- private void notifyPreferredFeaturesChangedToManager(@NonNull RouterRecord routerRecord,
+ private void notifyDiscoveryPreferenceChangedToManager(@NonNull RouterRecord routerRecord,
@NonNull IMediaRouter2Manager manager) {
try {
- manager.notifyPreferredFeaturesChanged(routerRecord.mPackageName,
- routerRecord.mDiscoveryPreference.getPreferredFeatures());
+ manager.notifyDiscoveryPreferenceChanged(routerRecord.mPackageName,
+ routerRecord.mDiscoveryPreference);
} catch (RemoteException ex) {
Slog.w(TAG, "Failed to notify preferred features changed."
+ " Manager probably died.", ex);
}
}
- private void notifyPreferredFeaturesChangedToManagers(@NonNull String routerPackageName,
- @Nullable List<String> preferredFeatures) {
+ private void notifyDiscoveryPreferenceChangedToManagers(@NonNull String routerPackageName,
+ @Nullable RouteDiscoveryPreference discoveryPreference) {
MediaRouter2ServiceImpl service = mServiceRef.get();
if (service == null) {
return;
@@ -2143,7 +2143,8 @@ class MediaRouter2ServiceImpl {
}
for (IMediaRouter2Manager manager : managers) {
try {
- manager.notifyPreferredFeaturesChanged(routerPackageName, preferredFeatures);
+ manager.notifyDiscoveryPreferenceChanged(routerPackageName,
+ discoveryPreference);
} catch (RemoteException ex) {
Slog.w(TAG, "Failed to notify preferred features changed."
+ " Manager probably died.", ex);
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index fac7a40b17a7..8ce67a657740 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -98,8 +98,6 @@ import static android.net.NetworkStats.METERED_YES;
import static android.net.NetworkTemplate.MATCH_CARRIER;
import static android.net.NetworkTemplate.MATCH_MOBILE;
import static android.net.NetworkTemplate.MATCH_WIFI;
-import static android.net.NetworkTemplate.buildTemplateCarrierMetered;
-import static android.net.NetworkTemplate.buildTemplateMobileAll;
import static android.net.netstats.provider.NetworkStatsProvider.QUOTA_UNLIMITED;
import static android.os.Trace.TRACE_TAG_NETWORK;
import static android.provider.Settings.Global.NETPOLICY_OVERRIDE_ENABLED;
@@ -168,7 +166,6 @@ import android.net.ConnectivityManager.NetworkCallback;
import android.net.INetworkManagementEventObserver;
import android.net.INetworkPolicyListener;
import android.net.INetworkPolicyManager;
-import android.net.INetworkStatsService;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkIdentity;
@@ -1324,7 +1321,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
};
/**
- * Check {@link NetworkPolicy} against current {@link INetworkStatsService}
+ * Check {@link NetworkPolicy} against current {@link NetworkStatsManager}
* to show visible notifications as needed.
*/
@GuardedBy("mNetworkPoliciesSecondLock")
@@ -2322,6 +2319,18 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
}
/**
+ * Template to match all metered carrier networks with the given IMSI.
+ *
+ * @hide
+ */
+ public static NetworkTemplate buildTemplateCarrierMetered(@NonNull String subscriberId) {
+ Objects.requireNonNull(subscriberId);
+ return new NetworkTemplate.Builder(MATCH_CARRIER)
+ .setSubscriberIds(Set.of(subscriberId))
+ .setMeteredness(METERED_YES).build();
+ }
+
+ /**
* Update the given {@link NetworkPolicy} based on any carrier-provided
* defaults via {@link SubscriptionPlan} or {@link CarrierConfigManager}.
* Leaves policy untouched if the user has modified it.
@@ -2724,7 +2733,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
out.startTag(null, TAG_NETWORK_POLICY);
writeIntAttribute(out, ATTR_NETWORK_TEMPLATE, template.getMatchRule());
- final String subscriberId = template.getSubscriberId();
+ final String subscriberId = template.getSubscriberIds().isEmpty() ? null
+ : template.getSubscriberIds().iterator().next();
if (subscriberId != null) {
out.attribute(null, ATTR_SUBSCRIBER_ID, subscriberId);
}
@@ -3101,7 +3111,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
}
// When two normalized templates conflict, prefer the most
// restrictive policy
- policy.template = NetworkTemplate.normalize(policy.template, mMergedSubscriberIds);
+ policy.template = normalizeTemplate(policy.template, mMergedSubscriberIds);
final NetworkPolicy existing = mNetworkPolicy.get(policy.template);
if (existing == null || existing.compareTo(policy) > 0) {
if (existing != null) {
@@ -3112,6 +3122,46 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
}
}
+ /**
+ * Examine the given template and normalize it.
+ * We pick the "lowest" merged subscriber as the primary
+ * for key purposes, and expand the template to match all other merged
+ * subscribers.
+ *
+ * There can be multiple merged subscriberIds for multi-SIM devices.
+ *
+ * <p>
+ * For example, given an incoming template matching B, and the currently
+ * active merge set [A,B], we'd return a new template that primarily matches
+ * A, but also matches B.
+ */
+ private static NetworkTemplate normalizeTemplate(@NonNull NetworkTemplate template,
+ @NonNull List<String[]> mergedList) {
+ // Now there are several types of network which uses Subscriber Id to store network
+ // information. For instance:
+ // 1. A merged carrier wifi network which has TYPE_WIFI with a Subscriber Id.
+ // 2. A typical cellular network could have TYPE_MOBILE with a Subscriber Id.
+
+ if (template.getSubscriberIds().isEmpty()) return template;
+
+ for (final String[] merged : mergedList) {
+ // TODO: Handle incompatible subscriberIds if that happens in practice.
+ for (final String subscriberId : template.getSubscriberIds()) {
+ if (com.android.net.module.util.CollectionUtils.contains(merged, subscriberId)) {
+ // Requested template subscriber is part of the merged group; return
+ // a template that matches all merged subscribers.
+ return new NetworkTemplate.Builder(template.getMatchRule())
+ .setWifiNetworkKeys(template.getWifiNetworkKeys())
+ .setSubscriberIds(Set.of(merged))
+ .setMeteredness(template.getMeteredness())
+ .build();
+ }
+ }
+ }
+
+ return template;
+ }
+
@Override
public void snoozeLimit(NetworkTemplate template) {
mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
@@ -5559,7 +5609,11 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
NetworkPolicy[] policies = getNetworkPolicies(mContext.getOpPackageName());
NetworkTemplate templateCarrier = subscriber != null
? buildTemplateCarrierMetered(subscriber) : null;
- NetworkTemplate templateMobile = buildTemplateMobileAll(subscriber);
+ NetworkTemplate templateMobile = subscriber != null
+ ? new NetworkTemplate.Builder(MATCH_MOBILE)
+ .setSubscriberIds(Set.of(subscriber))
+ .setMeteredness(android.net.NetworkStats.METERED_YES)
+ .build() : null;
for (NetworkPolicy policy : policies) {
// All policies loaded from disk will be carrier templates, and setting will also only
// set carrier templates, but we clear mobile templates just in case one is set by
diff --git a/services/core/java/com/android/server/notification/NotificationComparator.java b/services/core/java/com/android/server/notification/NotificationComparator.java
index 583cdd599780..647a89efcbbf 100644
--- a/services/core/java/com/android/server/notification/NotificationComparator.java
+++ b/services/core/java/com/android/server/notification/NotificationComparator.java
@@ -104,6 +104,12 @@ public class NotificationComparator
return -1 * Boolean.compare(leftPeople, rightPeople);
}
+ boolean leftSystemMax = isSystemMax(left);
+ boolean rightSystemMax = isSystemMax(right);
+ if (leftSystemMax != rightSystemMax) {
+ return -1 * Boolean.compare(leftSystemMax, rightSystemMax);
+ }
+
if (leftImportance != rightImportance) {
// by importance, high to low
return -1 * Integer.compare(leftImportance, rightImportance);
@@ -173,6 +179,20 @@ public class NotificationComparator
return mMessagingUtil.isImportantMessaging(record.getSbn(), record.getImportance());
}
+ protected boolean isSystemMax(NotificationRecord record) {
+ if (record.getImportance() < NotificationManager.IMPORTANCE_HIGH) {
+ return false;
+ }
+ String packageName = record.getSbn().getPackageName();
+ if ("android".equals(packageName)) {
+ return true;
+ }
+ if ("com.android.systemui".equals(packageName)) {
+ return true;
+ }
+ return false;
+ }
+
private boolean isOngoing(NotificationRecord record) {
final int ongoingFlags = Notification.FLAG_FOREGROUND_SERVICE;
return (record.getNotification().flags & ongoingFlags) != 0;
diff --git a/services/core/java/com/android/server/notification/PermissionHelper.java b/services/core/java/com/android/server/notification/PermissionHelper.java
index 0cbdbc18ad39..5d18069ea205 100644
--- a/services/core/java/com/android/server/notification/PermissionHelper.java
+++ b/services/core/java/com/android/server/notification/PermissionHelper.java
@@ -19,7 +19,7 @@ package com.android.server.notification;
import static android.content.pm.PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED;
import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET;
import static android.content.pm.PackageManager.GET_PERMISSIONS;
-import static android.permission.PermissionManager.PERMISSION_GRANTED;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import android.Manifest;
import android.annotation.NonNull;
@@ -77,7 +77,8 @@ public final class PermissionHelper {
assertFlag();
final long callingId = Binder.clearCallingIdentity();
try {
- return mPmi.checkUidPermission(uid, NOTIFICATION_PERMISSION) == PERMISSION_GRANTED;
+ return mPmi.checkPostNotificationsPermissionGrantedOrLegacyAccess(uid)
+ == PERMISSION_GRANTED;
} finally {
Binder.restoreCallingIdentity(callingId);
}
diff --git a/services/core/java/com/android/server/notification/ZenModeFiltering.java b/services/core/java/com/android/server/notification/ZenModeFiltering.java
index b186f610d498..29aad63a1f4b 100644
--- a/services/core/java/com/android/server/notification/ZenModeFiltering.java
+++ b/services/core/java/com/android/server/notification/ZenModeFiltering.java
@@ -24,11 +24,14 @@ import android.app.NotificationManager;
import android.content.ComponentName;
import android.content.Context;
import android.media.AudioAttributes;
+import android.net.Uri;
import android.os.Bundle;
import android.os.UserHandle;
import android.provider.Settings.Global;
import android.service.notification.ZenModeConfig;
import android.telecom.TelecomManager;
+import android.telephony.PhoneNumberUtils;
+import android.telephony.TelephonyManager;
import android.util.ArrayMap;
import android.util.Slog;
@@ -36,6 +39,8 @@ import com.android.internal.messages.nano.SystemMessageProto;
import com.android.internal.util.NotificationMessagingUtil;
import java.io.PrintWriter;
+import java.io.UnsupportedEncodingException;
+import java.net.URLDecoder;
import java.util.Date;
public class ZenModeFiltering {
@@ -64,13 +69,22 @@ public class ZenModeFiltering {
pw.print(prefix); pw.print("RepeatCallers.mThresholdMinutes=");
pw.println(REPEAT_CALLERS.mThresholdMinutes);
synchronized (REPEAT_CALLERS) {
- if (!REPEAT_CALLERS.mCalls.isEmpty()) {
- pw.print(prefix); pw.println("RepeatCallers.mCalls=");
- for (int i = 0; i < REPEAT_CALLERS.mCalls.size(); i++) {
+ if (!REPEAT_CALLERS.mTelCalls.isEmpty()) {
+ pw.print(prefix); pw.println("RepeatCallers.mTelCalls=");
+ for (int i = 0; i < REPEAT_CALLERS.mTelCalls.size(); i++) {
pw.print(prefix); pw.print(" ");
- pw.print(REPEAT_CALLERS.mCalls.keyAt(i));
+ pw.print(REPEAT_CALLERS.mTelCalls.keyAt(i));
pw.print(" at ");
- pw.println(ts(REPEAT_CALLERS.mCalls.valueAt(i)));
+ pw.println(ts(REPEAT_CALLERS.mTelCalls.valueAt(i)));
+ }
+ }
+ if (!REPEAT_CALLERS.mOtherCalls.isEmpty()) {
+ pw.print(prefix); pw.println("RepeatCallers.mOtherCalls=");
+ for (int i = 0; i < REPEAT_CALLERS.mOtherCalls.size(); i++) {
+ pw.print(prefix); pw.print(" ");
+ pw.print(REPEAT_CALLERS.mOtherCalls.keyAt(i));
+ pw.print(" at ");
+ pw.println(ts(REPEAT_CALLERS.mOtherCalls.valueAt(i)));
}
}
}
@@ -330,34 +344,39 @@ public class ZenModeFiltering {
}
private static class RepeatCallers {
- // Person : time
- private final ArrayMap<String, Long> mCalls = new ArrayMap<>();
+ // We keep a separate map per uri scheme to do more generous number-matching
+ // handling on telephone numbers specifically. For other inputs, we
+ // simply match directly on the string.
+ private final ArrayMap<String, Long> mTelCalls = new ArrayMap<>();
+ private final ArrayMap<String, Long> mOtherCalls = new ArrayMap<>();
private int mThresholdMinutes;
private synchronized void recordCall(Context context, Bundle extras) {
setThresholdMinutes(context);
if (mThresholdMinutes <= 0 || extras == null) return;
- final String peopleString = peopleString(extras);
- if (peopleString == null) return;
+ final String[] extraPeople = ValidateNotificationPeople.getExtraPeople(extras);
+ if (extraPeople == null || extraPeople.length == 0) return;
final long now = System.currentTimeMillis();
- cleanUp(mCalls, now);
- mCalls.put(peopleString, now);
+ cleanUp(mTelCalls, now);
+ cleanUp(mOtherCalls, now);
+ recordCallers(extraPeople, now);
}
private synchronized boolean isRepeat(Context context, Bundle extras) {
setThresholdMinutes(context);
if (mThresholdMinutes <= 0 || extras == null) return false;
- final String peopleString = peopleString(extras);
- if (peopleString == null) return false;
+ final String[] extraPeople = ValidateNotificationPeople.getExtraPeople(extras);
+ if (extraPeople == null || extraPeople.length == 0) return false;
final long now = System.currentTimeMillis();
- cleanUp(mCalls, now);
- return mCalls.containsKey(peopleString);
+ cleanUp(mTelCalls, now);
+ cleanUp(mOtherCalls, now);
+ return checkCallers(context, extraPeople);
}
private synchronized void cleanUp(ArrayMap<String, Long> calls, long now) {
final int N = calls.size();
for (int i = N - 1; i >= 0; i--) {
- final long time = mCalls.valueAt(i);
+ final long time = calls.valueAt(i);
if (time > now || (now - time) > mThresholdMinutes * 1000 * 60) {
calls.removeAt(i);
}
@@ -367,10 +386,16 @@ public class ZenModeFiltering {
// Clean up all calls that occurred after the given time.
// Used only for tests, to clean up after testing.
private synchronized void cleanUpCallsAfter(long timeThreshold) {
- for (int i = mCalls.size() - 1; i >= 0; i--) {
- final long time = mCalls.valueAt(i);
+ for (int i = mTelCalls.size() - 1; i >= 0; i--) {
+ final long time = mTelCalls.valueAt(i);
if (time > timeThreshold) {
- mCalls.removeAt(i);
+ mTelCalls.removeAt(i);
+ }
+ }
+ for (int j = mOtherCalls.size() - 1; j >= 0; j--) {
+ final long time = mOtherCalls.valueAt(j);
+ if (time > timeThreshold) {
+ mOtherCalls.removeAt(j);
}
}
}
@@ -382,21 +407,65 @@ public class ZenModeFiltering {
}
}
- private static String peopleString(Bundle extras) {
- final String[] extraPeople = ValidateNotificationPeople.getExtraPeople(extras);
- if (extraPeople == null || extraPeople.length == 0) return null;
- final StringBuilder sb = new StringBuilder();
- for (int i = 0; i < extraPeople.length; i++) {
- String extraPerson = extraPeople[i];
- if (extraPerson == null) continue;
- extraPerson = extraPerson.trim();
- if (extraPerson.isEmpty()) continue;
- if (sb.length() > 0) {
- sb.append('|');
+ private synchronized void recordCallers(String[] people, long now) {
+ for (int i = 0; i < people.length; i++) {
+ String person = people[i];
+ if (person == null) continue;
+ final Uri uri = Uri.parse(person);
+ if ("tel".equals(uri.getScheme())) {
+ String tel = uri.getSchemeSpecificPart();
+ // while ideally we should not need to do this, sometimes we have seen tel
+ // numbers given in a url-encoded format
+ try {
+ tel = URLDecoder.decode(tel, "UTF-8");
+ } catch (UnsupportedEncodingException e) {
+ // ignore, keep the original tel string
+ Slog.w(TAG, "unsupported encoding in tel: uri input");
+ }
+ mTelCalls.put(tel, now);
+ } else {
+ // for non-tel calls, store the entire string, uri-component and all
+ mOtherCalls.put(person, now);
}
- sb.append(extraPerson);
}
- return sb.length() == 0 ? null : sb.toString();
+ }
+
+ private synchronized boolean checkCallers(Context context, String[] people) {
+ // get the default country code for checking telephone numbers
+ final String defaultCountryCode =
+ context.getSystemService(TelephonyManager.class).getNetworkCountryIso();
+ for (int i = 0; i < people.length; i++) {
+ String person = people[i];
+ if (person == null) continue;
+ final Uri uri = Uri.parse(person);
+ if ("tel".equals(uri.getScheme())) {
+ String number = uri.getSchemeSpecificPart();
+ if (mTelCalls.containsKey(number)) {
+ // check directly via map first
+ return true;
+ } else {
+ // see if a number that matches via areSameNumber exists
+ String numberToCheck = number;
+ try {
+ numberToCheck = URLDecoder.decode(number, "UTF-8");
+ } catch (UnsupportedEncodingException e) {
+ // ignore, continue to use the original string
+ Slog.w(TAG, "unsupported encoding in tel: uri input");
+ }
+ for (String prev : mTelCalls.keySet()) {
+ if (PhoneNumberUtils.areSamePhoneNumber(
+ numberToCheck, prev, defaultCountryCode)) {
+ return true;
+ }
+ }
+ }
+ } else {
+ if (mOtherCalls.containsKey(person)) {
+ return true;
+ }
+ }
+ }
+ return false;
}
}
diff --git a/services/core/java/com/android/server/pm/AppDataHelper.java b/services/core/java/com/android/server/pm/AppDataHelper.java
index 4b999e9fac7f..9f086e6e4a61 100644
--- a/services/core/java/com/android/server/pm/AppDataHelper.java
+++ b/services/core/java/com/android/server/pm/AppDataHelper.java
@@ -49,6 +49,7 @@ import com.android.server.pm.dex.ArtManagerService;
import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
import com.android.server.pm.pkg.SELinuxUtil;
+import com.android.server.pm.pkg.PackageStateInternal;
import dalvik.system.VMRuntime;
@@ -277,8 +278,8 @@ final class AppDataHelper {
});
}
- public void prepareAppDataContentsLIF(AndroidPackage pkg, @Nullable PackageSetting pkgSetting,
- int userId, int flags) {
+ public void prepareAppDataContentsLIF(AndroidPackage pkg,
+ @Nullable PackageStateInternal pkgSetting, int userId, int flags) {
if (pkg == null) {
Slog.wtf(TAG, "Package was null!", new Throwable());
return;
@@ -287,7 +288,7 @@ final class AppDataHelper {
}
private void prepareAppDataContentsLeafLIF(AndroidPackage pkg,
- @Nullable PackageSetting pkgSetting, int userId, int flags) {
+ @Nullable PackageStateInternal pkgSetting, int userId, int flags) {
final String volumeUuid = pkg.getVolumeUuid();
final String packageName = pkg.getPackageName();
diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java
index b916de328038..5b2e097d531e 100644
--- a/services/core/java/com/android/server/pm/AppsFilter.java
+++ b/services/core/java/com/android/server/pm/AppsFilter.java
@@ -1237,21 +1237,25 @@ public class AppsFilter implements Watchable, Snappable {
// NOTE: this must come after all removals from data structures but before we update the
// cache
if (setting.getSharedUser() != null) {
- for (int i = setting.getSharedUser().packages.size() - 1; i >= 0; i--) {
- if (setting.getSharedUser().packages.valueAt(i) == setting) {
+ final ArraySet<? extends PackageStateInternal> sharedUserPackages =
+ setting.getSharedUser().getPackageStates();
+ for (int i = sharedUserPackages.size() - 1; i >= 0; i--) {
+ if (sharedUserPackages.valueAt(i) == setting) {
continue;
}
addPackageInternal(
- setting.getSharedUser().packages.valueAt(i), settings);
+ sharedUserPackages.valueAt(i), settings);
}
}
synchronized (mCacheLock) {
removeAppIdFromVisibilityCache(setting.getAppId());
if (mShouldFilterCache != null && setting.getSharedUser() != null) {
- for (int i = setting.getSharedUser().packages.size() - 1; i >= 0; i--) {
+ final ArraySet<? extends PackageStateInternal> sharedUserPackages =
+ setting.getSharedUser().getPackageStates();
+ for (int i = sharedUserPackages.size() - 1; i >= 0; i--) {
PackageStateInternal siblingSetting =
- setting.getSharedUser().packages.valueAt(i);
+ sharedUserPackages.valueAt(i);
if (siblingSetting == setting) {
continue;
}
@@ -1368,8 +1372,8 @@ public class AppsFilter implements Watchable, Snappable {
callingSharedPkgSettings = null;
} else {
callingPkgSetting = null;
- callingSharedPkgSettings =
- ((PackageStateInternal) callingSetting).getSharedUser().packages;
+ callingSharedPkgSettings = ((PackageStateInternal) callingSetting)
+ .getSharedUser().getPackageStates();
}
} else {
callingPkgSetting = null;
diff --git a/services/core/java/com/android/server/pm/ChangedPackagesTracker.java b/services/core/java/com/android/server/pm/ChangedPackagesTracker.java
new file mode 100644
index 000000000000..bd12981bfced
--- /dev/null
+++ b/services/core/java/com/android/server/pm/ChangedPackagesTracker.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.content.pm.ChangedPackages;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.function.BiConsumer;
+
+class ChangedPackagesTracker {
+
+ @NonNull
+ private final Object mLock = new Object();
+
+ @GuardedBy("mLock")
+ @NonNull
+ private int mChangedPackagesSequenceNumber;
+ /**
+ * List of changed [installed, removed or updated] packages.
+ * mapping from user id -> sequence number -> package name
+ */
+ @GuardedBy("mLock")
+ @NonNull
+ private final SparseArray<SparseArray<String>> mUserIdToSequenceToPackage = new SparseArray<>();
+ /**
+ * The sequence number of the last change to a package.
+ * mapping from user id -> package name -> sequence number
+ */
+ @GuardedBy("mLock")
+ @NonNull
+ private final SparseArray<Map<String, Integer>> mChangedPackagesSequenceNumbers =
+ new SparseArray<>();
+
+ @Nullable
+ public ChangedPackages getChangedPackages(int sequenceNumber, @UserIdInt int userId) {
+ synchronized (mLock) {
+ if (sequenceNumber >= mChangedPackagesSequenceNumber) {
+ return null;
+ }
+ final SparseArray<String> changedPackages = mUserIdToSequenceToPackage.get(userId);
+ if (changedPackages == null) {
+ return null;
+ }
+ final List<String> packageNames =
+ new ArrayList<>(mChangedPackagesSequenceNumber - sequenceNumber);
+ for (int i = sequenceNumber; i < mChangedPackagesSequenceNumber; i++) {
+ final String packageName = changedPackages.get(i);
+ if (packageName != null) {
+ packageNames.add(packageName);
+ }
+ }
+ return packageNames.isEmpty()
+ ? null : new ChangedPackages(mChangedPackagesSequenceNumber, packageNames);
+ }
+ }
+
+ int getSequenceNumber() {
+ return mChangedPackagesSequenceNumber;
+ }
+
+ void iterateAll(@NonNull BiConsumer<Integer, SparseArray<SparseArray<String>>>
+ sequenceNumberAndValues) {
+ synchronized (mLock) {
+ sequenceNumberAndValues.accept(mChangedPackagesSequenceNumber,
+ mUserIdToSequenceToPackage);
+ }
+ }
+
+ void updateSequenceNumber(@NonNull String packageName, int[] userList) {
+ for (int i = userList.length - 1; i >= 0; --i) {
+ final int userId = userList[i];
+ SparseArray<String> changedPackages = mUserIdToSequenceToPackage.get(userId);
+ if (changedPackages == null) {
+ changedPackages = new SparseArray<>();
+ mUserIdToSequenceToPackage.put(userId, changedPackages);
+ }
+ Map<String, Integer> sequenceNumbers = mChangedPackagesSequenceNumbers.get(userId);
+ if (sequenceNumbers == null) {
+ sequenceNumbers = new HashMap<>();
+ mChangedPackagesSequenceNumbers.put(userId, sequenceNumbers);
+ }
+ final Integer sequenceNumber = sequenceNumbers.get(packageName);
+ if (sequenceNumber != null) {
+ changedPackages.remove(sequenceNumber);
+ }
+ changedPackages.put(mChangedPackagesSequenceNumber, packageName);
+ sequenceNumbers.put(packageName, mChangedPackagesSequenceNumber);
+ }
+ mChangedPackagesSequenceNumber++;
+ }
+}
diff --git a/services/core/java/com/android/server/pm/ComponentResolver.java b/services/core/java/com/android/server/pm/ComponentResolver.java
index ba003d250b59..aa393d20dac7 100644
--- a/services/core/java/com/android/server/pm/ComponentResolver.java
+++ b/services/core/java/com/android/server/pm/ComponentResolver.java
@@ -616,98 +616,113 @@ public class ComponentResolver
}
void dumpActivityResolvers(PrintWriter pw, DumpState dumpState, String packageName) {
- if (mActivities.dump(pw, dumpState.getTitlePrinted() ? "\nActivity Resolver Table:"
- : "Activity Resolver Table:", " ", packageName,
- dumpState.isOptionEnabled(DumpState.OPTION_SHOW_FILTERS), true)) {
- dumpState.setTitlePrinted(true);
+ synchronized (mLock) {
+ if (mActivities.dump(pw, dumpState.getTitlePrinted() ? "\nActivity Resolver Table:"
+ : "Activity Resolver Table:", " ", packageName,
+ dumpState.isOptionEnabled(DumpState.OPTION_SHOW_FILTERS), true)) {
+ dumpState.setTitlePrinted(true);
+ }
}
}
void dumpProviderResolvers(PrintWriter pw, DumpState dumpState, String packageName) {
- if (mProviders.dump(pw, dumpState.getTitlePrinted() ? "\nProvider Resolver Table:"
- : "Provider Resolver Table:", " ", packageName,
- dumpState.isOptionEnabled(DumpState.OPTION_SHOW_FILTERS), true)) {
- dumpState.setTitlePrinted(true);
+ synchronized (mLock) {
+ if (mProviders.dump(pw, dumpState.getTitlePrinted() ? "\nProvider Resolver Table:"
+ : "Provider Resolver Table:", " ", packageName,
+ dumpState.isOptionEnabled(DumpState.OPTION_SHOW_FILTERS), true)) {
+ dumpState.setTitlePrinted(true);
+ }
}
}
void dumpReceiverResolvers(PrintWriter pw, DumpState dumpState, String packageName) {
- if (mReceivers.dump(pw, dumpState.getTitlePrinted() ? "\nReceiver Resolver Table:"
- : "Receiver Resolver Table:", " ", packageName,
- dumpState.isOptionEnabled(DumpState.OPTION_SHOW_FILTERS), true)) {
- dumpState.setTitlePrinted(true);
+ synchronized (mLock) {
+ if (mReceivers.dump(pw, dumpState.getTitlePrinted() ? "\nReceiver Resolver Table:"
+ : "Receiver Resolver Table:", " ", packageName,
+ dumpState.isOptionEnabled(DumpState.OPTION_SHOW_FILTERS), true)) {
+ dumpState.setTitlePrinted(true);
+ }
}
}
void dumpServiceResolvers(PrintWriter pw, DumpState dumpState, String packageName) {
- if (mServices.dump(pw, dumpState.getTitlePrinted() ? "\nService Resolver Table:"
- : "Service Resolver Table:", " ", packageName,
- dumpState.isOptionEnabled(DumpState.OPTION_SHOW_FILTERS), true)) {
- dumpState.setTitlePrinted(true);
+ synchronized (mLock) {
+ if (mServices.dump(pw, dumpState.getTitlePrinted() ? "\nService Resolver Table:"
+ : "Service Resolver Table:", " ", packageName,
+ dumpState.isOptionEnabled(DumpState.OPTION_SHOW_FILTERS), true)) {
+ dumpState.setTitlePrinted(true);
+ }
}
}
void dumpContentProviders(PrintWriter pw, DumpState dumpState, String packageName) {
- boolean printedSomething = false;
- for (ParsedProvider p : mProviders.mProviders.values()) {
- if (packageName != null && !packageName.equals(p.getPackageName())) {
- continue;
- }
- if (!printedSomething) {
- if (dumpState.onTitlePrinted()) {
- pw.println();
+ synchronized (mLock) {
+ boolean printedSomething = false;
+ for (ParsedProvider p : mProviders.mProviders.values()) {
+ if (packageName != null && !packageName.equals(p.getPackageName())) {
+ continue;
}
- pw.println("Registered ContentProviders:");
- printedSomething = true;
- }
- pw.print(" ");
- ComponentName.printShortString(pw, p.getPackageName(), p.getName());
- pw.println(":");
- pw.print(" ");
- pw.println(p.toString());
- }
- printedSomething = false;
- for (Map.Entry<String, ParsedProvider> entry :
- mProvidersByAuthority.entrySet()) {
- ParsedProvider p = entry.getValue();
- if (packageName != null && !packageName.equals(p.getPackageName())) {
- continue;
- }
- if (!printedSomething) {
- if (dumpState.onTitlePrinted()) {
- pw.println();
+ if (!printedSomething) {
+ if (dumpState.onTitlePrinted()) {
+ pw.println();
+ }
+ pw.println("Registered ContentProviders:");
+ printedSomething = true;
}
- pw.println("ContentProvider Authorities:");
- printedSomething = true;
+ pw.print(" ");
+ ComponentName.printShortString(pw, p.getPackageName(), p.getName());
+ pw.println(":");
+ pw.print(" ");
+ pw.println(p.toString());
}
- pw.print(" ["); pw.print(entry.getKey()); pw.println("]:");
- pw.print(" "); pw.println(p.toString());
+ printedSomething = false;
+ for (Map.Entry<String, ParsedProvider> entry :
+ mProvidersByAuthority.entrySet()) {
+ ParsedProvider p = entry.getValue();
+ if (packageName != null && !packageName.equals(p.getPackageName())) {
+ continue;
+ }
+ if (!printedSomething) {
+ if (dumpState.onTitlePrinted()) {
+ pw.println();
+ }
+ pw.println("ContentProvider Authorities:");
+ printedSomething = true;
+ }
+ pw.print(" [");
+ pw.print(entry.getKey());
+ pw.println("]:");
+ pw.print(" ");
+ pw.println(p.toString());
- AndroidPackage pkg = sPackageManagerInternal.getPackage(p.getPackageName());
+ AndroidPackage pkg = sPackageManagerInternal.getPackage(p.getPackageName());
- if (pkg != null) {
- pw.print(" applicationInfo=");
- pw.println(AndroidPackageUtils.generateAppInfoWithoutState(pkg));
+ if (pkg != null) {
+ pw.print(" applicationInfo=");
+ pw.println(AndroidPackageUtils.generateAppInfoWithoutState(pkg));
+ }
}
}
}
void dumpServicePermissions(PrintWriter pw, DumpState dumpState) {
- if (dumpState.onTitlePrinted()) pw.println();
- pw.println("Service permissions:");
-
- final Iterator<Pair<ParsedService, ParsedIntentInfo>> filterIterator =
- mServices.filterIterator();
- while (filterIterator.hasNext()) {
- final Pair<ParsedService, ParsedIntentInfo> pair = filterIterator.next();
- ParsedService service = pair.first;
-
- final String permission = service.getPermission();
- if (permission != null) {
- pw.print(" ");
- pw.print(service.getComponentName().flattenToShortString());
- pw.print(": ");
- pw.println(permission);
+ synchronized (mLock) {
+ if (dumpState.onTitlePrinted()) pw.println();
+ pw.println("Service permissions:");
+
+ final Iterator<Pair<ParsedService, ParsedIntentInfo>> filterIterator =
+ mServices.filterIterator();
+ while (filterIterator.hasNext()) {
+ final Pair<ParsedService, ParsedIntentInfo> pair = filterIterator.next();
+ ParsedService service = pair.first;
+
+ final String permission = service.getPermission();
+ if (permission != null) {
+ pw.print(" ");
+ pw.print(service.getComponentName().flattenToShortString());
+ pw.print(": ");
+ pw.println(permission);
+ }
}
}
}
diff --git a/services/core/java/com/android/server/pm/Computer.java b/services/core/java/com/android/server/pm/Computer.java
index fcf4a0279246..0ae341833e34 100644
--- a/services/core/java/com/android/server/pm/Computer.java
+++ b/services/core/java/com/android/server/pm/Computer.java
@@ -43,12 +43,16 @@ import android.content.pm.UserInfo;
import android.content.pm.VersionedPackage;
import android.util.ArrayMap;
import android.util.ArraySet;
+import android.util.Pair;
import android.util.SparseArray;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.pkg.PackageState;
import com.android.server.pm.pkg.PackageStateInternal;
+import com.android.server.pm.pkg.SharedUserApi;
+import com.android.server.utils.WatchedArrayMap;
+import com.android.server.utils.WatchedLongSparseArray;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -214,7 +218,7 @@ public interface Computer {
@Computer.LiveImplementation(override = Computer.LiveImplementation.NOT_ALLOWED)
String resolveExternalPackageName(AndroidPackage pkg);
@Computer.LiveImplementation(override = Computer.LiveImplementation.NOT_ALLOWED)
- String resolveInternalPackageNameLPr(String packageName, long versionCode);
+ String resolveInternalPackageName(String packageName, long versionCode);
@Computer.LiveImplementation(override = Computer.LiveImplementation.NOT_ALLOWED)
String[] getPackagesForUid(int uid);
@Computer.LiveImplementation(override = Computer.LiveImplementation.NOT_ALLOWED)
@@ -381,11 +385,12 @@ public interface Computer {
String[] getSystemSharedLibraryNames();
/**
- * @return if the given package has a state and isn't filtered by visibility. Provides no
- * guarantee that the package is in any usable state.
+ * @return the state if the given package has a state and isn't filtered by visibility.
+ * Provides no guarantee that the package is in any usable state.
*/
@Computer.LiveImplementation(override = LiveImplementation.MANDATORY)
- boolean isPackageStateAvailableAndVisible(@NonNull String packageName, int callingUid,
+ @Nullable
+ PackageStateInternal getPackageStateFiltered(@NonNull String packageName, int callingUid,
@UserIdInt int userId);
@Computer.LiveImplementation(override = LiveImplementation.MANDATORY)
@@ -623,4 +628,16 @@ public interface Computer {
@Nullable
ArrayMap<String, ProcessInfo> getProcessesForUid(int uid);
// End block
+
+ @Computer.LiveImplementation(override = LiveImplementation.MANDATORY)
+ boolean getBlockUninstall(@UserIdInt int userId, @NonNull String packageName);
+
+ @Computer.LiveImplementation(override = LiveImplementation.MANDATORY)
+ @NonNull
+ WatchedArrayMap<String, WatchedLongSparseArray<SharedLibraryInfo>> getSharedLibraries();
+
+
+ @Computer.LiveImplementation(override = LiveImplementation.MANDATORY)
+ @Nullable
+ Pair<PackageStateInternal, SharedUserApi> getPackageOrSharedUser(int appId);
}
diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java
index cca1b97feac3..691bf9f39619 100644
--- a/services/core/java/com/android/server/pm/ComputerEngine.java
+++ b/services/core/java/com/android/server/pm/ComputerEngine.java
@@ -133,9 +133,9 @@ import com.android.server.pm.pkg.PackageState;
import com.android.server.pm.pkg.PackageStateImpl;
import com.android.server.pm.pkg.PackageStateInternal;
import com.android.server.pm.pkg.PackageStateUtils;
-import com.android.server.pm.pkg.PackageUserState;
import com.android.server.pm.pkg.PackageUserStateInternal;
import com.android.server.pm.pkg.PackageUserStateUtils;
+import com.android.server.pm.pkg.SharedUserApi;
import com.android.server.pm.pkg.component.ParsedActivity;
import com.android.server.pm.pkg.component.ParsedInstrumentation;
import com.android.server.pm.pkg.component.ParsedIntentInfo;
@@ -298,11 +298,6 @@ public class ComputerEngine implements Computer {
public Collection<SharedUserSetting> getAllSharedUsers() {
return mSettings.getAllSharedUsersLPw();
}
-
- @Nullable
- public String getHarmfulAppWarning(@NonNull String packageName, @UserIdInt int userId) {
- return mSettings.getHarmfulAppWarningLPr(packageName, userId);
- }
}
private static final Comparator<ProviderInfo> sProviderInitOrderSorter = (p1, p2) -> {
@@ -829,7 +824,7 @@ public class ComputerEngine implements Computer {
}
public AndroidPackage getPackage(String packageName) {
- packageName = resolveInternalPackageNameLPr(
+ packageName = resolveInternalPackageName(
packageName, PackageManager.VERSION_CODE_HIGHEST);
return mPackages.get(packageName);
}
@@ -903,7 +898,7 @@ public class ComputerEngine implements Computer {
int filterCallingUid, int userId) {
// writer
// Normalize package name to handle renamed packages and static libs
- packageName = resolveInternalPackageNameLPr(packageName,
+ packageName = resolveInternalPackageName(packageName,
PackageManager.VERSION_CODE_HIGHEST);
AndroidPackage p = mPackages.get(packageName);
@@ -1575,7 +1570,7 @@ public class ComputerEngine implements Computer {
PackageInfo pi = new PackageInfo();
pi.packageName = ps.getPackageName();
pi.setLongVersionCode(ps.getVersionCode());
- pi.sharedUserId = (ps.getSharedUser() != null) ? ps.getSharedUser().name : null;
+ pi.sharedUserId = (ps.getSharedUser() != null) ? ps.getSharedUser().getName() : null;
pi.firstInstallTime = state.getFirstInstallTime();
pi.lastUpdateTime = ps.getLastUpdateTime();
@@ -1627,7 +1622,7 @@ public class ComputerEngine implements Computer {
long flags, int filterCallingUid, int userId) {
// reader
// Normalize package name to handle renamed packages and static libs
- packageName = resolveInternalPackageNameLPr(packageName, versionCode);
+ packageName = resolveInternalPackageName(packageName, versionCode);
final boolean matchFactoryOnly = (flags & MATCH_FACTORY_ONLY) != 0;
if (matchFactoryOnly) {
@@ -2111,7 +2106,7 @@ public class ComputerEngine implements Computer {
return packageName;
}
- public final String resolveInternalPackageNameLPr(String packageName, long versionCode) {
+ public final String resolveInternalPackageName(String packageName, long versionCode) {
final int callingUid = Binder.getCallingUid();
return resolveInternalPackageNameInternalLocked(packageName, versionCode,
callingUid);
@@ -3489,7 +3484,9 @@ public class ComputerEngine implements Computer {
return mSettings.getRenamedPackageLPr(packageName);
}
- private WatchedArrayMap<String, WatchedLongSparseArray<SharedLibraryInfo>>
+ @NonNull
+ @Override
+ public WatchedArrayMap<String, WatchedLongSparseArray<SharedLibraryInfo>>
getSharedLibraries() {
return mSharedLibraries.getAll();
}
@@ -4070,10 +4067,13 @@ public class ComputerEngine implements Computer {
}
@Override
- public boolean isPackageStateAvailableAndVisible(@NonNull String packageName, int callingUid,
+ public PackageStateInternal getPackageStateFiltered(@NonNull String packageName, int callingUid,
@UserIdInt int userId) {
- final PackageStateInternal ps = getPackageStateInternal(packageName);
- return ps != null && !shouldFilterApplication(ps, callingUid, userId);
+ final PackageStateInternal packageState = getPackageStateInternal(packageName);
+ if (packageState == null || shouldFilterApplication(packageState, callingUid, userId)) {
+ return null;
+ }
+ return packageState;
}
@Override
@@ -4576,9 +4576,9 @@ public class ComputerEngine implements Computer {
}
} else {
list = new ArrayList<>(mPackages.size());
- for (AndroidPackage p : mPackages.values()) {
- final PackageStateInternal packageState = packageStates.get(p.getPackageName());
- if (packageState == null) {
+ for (PackageStateInternal packageState : packageStates.values()) {
+ final AndroidPackage pkg = packageState.getPkg();
+ if (pkg == null) {
continue;
}
if (filterSharedLibPackage(packageState, Binder.getCallingUid(), userId, flags)) {
@@ -4587,10 +4587,10 @@ public class ComputerEngine implements Computer {
if (shouldFilterApplication(packageState, callingUid, userId)) {
continue;
}
- ApplicationInfo ai = PackageInfoUtils.generateApplicationInfo(p, flags,
+ ApplicationInfo ai = PackageInfoUtils.generateApplicationInfo(pkg, flags,
packageState.getUserStateOrDefault(userId), userId, packageState);
if (ai != null) {
- ai.packageName = resolveExternalPackageName(p);
+ ai.packageName = resolveExternalPackageName(pkg);
list.add(ai);
}
}
@@ -5421,13 +5421,14 @@ public class ComputerEngine implements Computer {
return EmptyArray.STRING;
}
- ArraySet<PackageSetting> packages = packageSetting.getSharedUser().packages;
+ ArraySet<? extends PackageStateInternal> packages =
+ packageSetting.getSharedUser().getPackageStates();
final int numPackages = packages.size();
String[] res = new String[numPackages];
int i = 0;
for (int index = 0; index < numPackages; index++) {
- final PackageSetting ps = packages.valueAt(index);
- if (ps.getInstalled(userId)) {
+ final PackageStateInternal ps = packages.valueAt(index);
+ if (ps.getUserStateOrDefault(userId).isInstalled()) {
res[i++] = ps.getPackageName();
}
}
@@ -5476,7 +5477,11 @@ public class ComputerEngine implements Computer {
+ SET_HARMFUL_APP_WARNINGS + " permission.");
}
- return mSettings.getHarmfulAppWarning(packageName, userId);
+ final PackageStateInternal packageState = getPackageStateInternal(packageName);
+ if (packageState == null) {
+ throw new IllegalArgumentException("Unknown package: " + packageName);
+ }
+ return packageState.getUserStateOrDefault(userId).getHarmfulAppWarning();
}
/**
@@ -5571,4 +5576,22 @@ public class ComputerEngine implements Computer {
}
return null;
}
+
+ @Override
+ public boolean getBlockUninstall(@UserIdInt int userId, @NonNull String packageName) {
+ return mSettings.getBlockUninstall(userId, packageName);
+ }
+
+ @Nullable
+ @Override
+ public Pair<PackageStateInternal, SharedUserApi> getPackageOrSharedUser(int appId) {
+ final SettingBase settingBase = mSettings.getSettingBase(appId);
+ if (settingBase instanceof SharedUserSetting) {
+ return Pair.create(null, (SharedUserApi) settingBase);
+ } else if (settingBase instanceof PackageSetting) {
+ return Pair.create((PackageStateInternal) settingBase, null);
+ } else {
+ return null;
+ }
+ }
}
diff --git a/services/core/java/com/android/server/pm/ComputerLocked.java b/services/core/java/com/android/server/pm/ComputerLocked.java
index 529aca3632ba..40d4c036c964 100644
--- a/services/core/java/com/android/server/pm/ComputerLocked.java
+++ b/services/core/java/com/android/server/pm/ComputerLocked.java
@@ -40,12 +40,16 @@ import android.content.pm.SigningDetails;
import android.content.pm.VersionedPackage;
import android.util.ArrayMap;
import android.util.ArraySet;
+import android.util.Pair;
import android.util.SparseArray;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.pkg.PackageState;
import com.android.server.pm.pkg.PackageStateInternal;
+import com.android.server.pm.pkg.SharedUserApi;
+import com.android.server.utils.WatchedArrayMap;
+import com.android.server.utils.WatchedLongSparseArray;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -390,14 +394,6 @@ public final class ComputerLocked extends ComputerEngine {
}
@Override
- public boolean isPackageStateAvailableAndVisible(@NonNull String packageName, int callingUid,
- @UserIdInt int userId) {
- synchronized (mLock) {
- return super.isPackageStateAvailableAndVisible(packageName, callingUid, userId);
- }
- }
-
- @Override
public int checkSignatures(@NonNull String pkg1,
@NonNull String pkg2) {
synchronized (mLock) {
@@ -825,4 +821,35 @@ public final class ComputerLocked extends ComputerEngine {
return super.getProcessesForUid(uid);
}
}
+
+ @Override
+ public PackageStateInternal getPackageStateFiltered(@NonNull String packageName, int callingUid,
+ @UserIdInt int userId) {
+ synchronized (mLock) {
+ return super.getPackageStateFiltered(packageName, callingUid, userId);
+ }
+ }
+
+ @Override
+ public boolean getBlockUninstall(@UserIdInt int userId, @NonNull String packageName) {
+ synchronized (mLock) {
+ return super.getBlockUninstall(userId, packageName);
+ }
+ }
+
+ @NonNull
+ @Override
+ public WatchedArrayMap<String, WatchedLongSparseArray<SharedLibraryInfo>> getSharedLibraries() {
+ synchronized (mLock) {
+ return super.getSharedLibraries();
+ }
+ }
+
+ @Nullable
+ @Override
+ public Pair<PackageStateInternal, SharedUserApi> getPackageOrSharedUser(int appId) {
+ synchronized (mLock) {
+ return super.getPackageOrSharedUser(appId);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/pm/ComputerTracker.java b/services/core/java/com/android/server/pm/ComputerTracker.java
index 52309ce45e30..24c08d1c8d18 100644
--- a/services/core/java/com/android/server/pm/ComputerTracker.java
+++ b/services/core/java/com/android/server/pm/ComputerTracker.java
@@ -42,11 +42,15 @@ import android.content.pm.UserInfo;
import android.content.pm.VersionedPackage;
import android.util.ArrayMap;
import android.util.ArraySet;
+import android.util.Pair;
import android.util.SparseArray;
import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.pkg.PackageState;
import com.android.server.pm.pkg.PackageStateInternal;
+import com.android.server.pm.pkg.SharedUserApi;
+import com.android.server.utils.WatchedArrayMap;
+import com.android.server.utils.WatchedLongSparseArray;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -63,26 +67,11 @@ public final class ComputerTracker implements Computer {
// a snapshot computer.
private final AtomicInteger mReusedSnapshot = new AtomicInteger(0);
- // The number of times a thread reused a computer in its stack instead of fetching
- // a live computer.
- private final AtomicInteger mReusedLive = new AtomicInteger(0);
-
private final PackageManagerService mService;
ComputerTracker(PackageManagerService s) {
mService = s;
}
- private ThreadComputer live() {
- ThreadComputer current = PackageManagerService.sThreadComputer.get();
- if (current.mRefCount > 0) {
- current.acquire();
- mReusedLive.incrementAndGet();
- } else {
- current.acquire(mService.liveComputer());
- }
- return current;
- }
-
private ThreadComputer snapshot() {
ThreadComputer current = PackageManagerService.sThreadComputer.get();
if (current.mRefCount > 0) {
@@ -133,7 +122,7 @@ public final class ComputerTracker implements Computer {
Intent intent, String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags,
int filterCallingUid, int userId, boolean resolveForStart, boolean allowDynamicSplits,
String pkgName, String instantAppPkgName) {
- ThreadComputer current = live();
+ ThreadComputer current = snapshot();
try {
return current.mComputer.queryIntentActivitiesInternalBody(intent, resolvedType,
flags, filterCallingUid, userId, resolveForStart, allowDynamicSplits,
@@ -154,7 +143,7 @@ public final class ComputerTracker implements Computer {
public ActivityInfo getActivityInfoInternal(ComponentName component,
@PackageManager.ComponentInfoFlagsBits long flags,
int filterCallingUid, int userId) {
- ThreadComputer current = live();
+ ThreadComputer current = snapshot();
try {
return current.mComputer.getActivityInfoInternal(component, flags, filterCallingUid,
userId);
@@ -180,7 +169,7 @@ public final class ComputerTracker implements Computer {
}
public ApplicationInfo generateApplicationInfoFromSettings(String packageName,
long flags, int filterCallingUid, int userId) {
- ThreadComputer current = live();
+ ThreadComputer current = snapshot();
try {
return current.mComputer.generateApplicationInfoFromSettings(packageName, flags,
filterCallingUid, userId);
@@ -199,7 +188,7 @@ public final class ComputerTracker implements Computer {
}
public ApplicationInfo getApplicationInfoInternal(String packageName,
@PackageManager.ApplicationInfoFlagsBits long flags, int filterCallingUid, int userId) {
- ThreadComputer current = live();
+ ThreadComputer current = snapshot();
try {
return current.mComputer.getApplicationInfoInternal(packageName, flags,
filterCallingUid, userId);
@@ -208,7 +197,7 @@ public final class ComputerTracker implements Computer {
}
}
public ComponentName getDefaultHomeActivity(int userId) {
- ThreadComputer current = live();
+ ThreadComputer current = snapshot();
try {
return current.mComputer.getDefaultHomeActivity(userId);
} finally {
@@ -217,7 +206,7 @@ public final class ComputerTracker implements Computer {
}
public ComponentName getHomeActivitiesAsUser(List<ResolveInfo> allHomeCandidates,
int userId) {
- ThreadComputer current = live();
+ ThreadComputer current = snapshot();
try {
return current.mComputer.getHomeActivitiesAsUser(allHomeCandidates, userId);
} finally {
@@ -227,7 +216,7 @@ public final class ComputerTracker implements Computer {
public CrossProfileDomainInfo getCrossProfileDomainPreferredLpr(Intent intent,
String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags, int sourceUserId,
int parentUserId) {
- ThreadComputer current = live();
+ ThreadComputer current = snapshot();
try {
return current.mComputer.getCrossProfileDomainPreferredLpr(intent, resolvedType,
flags, sourceUserId, parentUserId);
@@ -236,7 +225,7 @@ public final class ComputerTracker implements Computer {
}
}
public Intent getHomeIntent() {
- ThreadComputer current = live();
+ ThreadComputer current = snapshot();
try {
return current.mComputer.getHomeIntent();
} finally {
@@ -245,7 +234,7 @@ public final class ComputerTracker implements Computer {
}
public List<CrossProfileIntentFilter> getMatchingCrossProfileIntentFilters(
Intent intent, String resolvedType, int userId) {
- ThreadComputer current = live();
+ ThreadComputer current = snapshot();
try {
return current.mComputer.getMatchingCrossProfileIntentFilters(intent, resolvedType,
userId);
@@ -257,7 +246,7 @@ public final class ComputerTracker implements Computer {
@NonNull List<ResolveInfo> resolveInfos,
String ephemeralPkgName, boolean allowDynamicSplits, int filterCallingUid,
boolean resolveForStart, int userId, Intent intent) {
- ThreadComputer current = live();
+ ThreadComputer current = snapshot();
try {
return current.mComputer.applyPostResolutionFilter(resolveInfos, ephemeralPkgName,
allowDynamicSplits, filterCallingUid, resolveForStart, userId, intent);
@@ -267,7 +256,7 @@ public final class ComputerTracker implements Computer {
}
public PackageInfo generatePackageInfo(PackageStateInternal ps,
@PackageManager.PackageInfoFlagsBits long flags, int userId) {
- ThreadComputer current = live();
+ ThreadComputer current = snapshot();
try {
return current.mComputer.generatePackageInfo(ps, flags, userId);
} finally {
@@ -285,7 +274,7 @@ public final class ComputerTracker implements Computer {
}
public PackageInfo getPackageInfoInternal(String packageName, long versionCode,
long flags, int filterCallingUid, int userId) {
- ThreadComputer current = live();
+ ThreadComputer current = snapshot();
try {
return current.mComputer.getPackageInfoInternal(packageName, versionCode, flags,
filterCallingUid, userId);
@@ -302,7 +291,7 @@ public final class ComputerTracker implements Computer {
}
}
public PackageStateInternal getPackageStateInternal(String packageName, int callingUid) {
- ThreadComputer current = live();
+ ThreadComputer current = snapshot();
try {
return current.mComputer.getPackageStateInternal(packageName, callingUid);
} finally {
@@ -312,7 +301,7 @@ public final class ComputerTracker implements Computer {
@Nullable
public PackageState getPackageStateCopied(@NonNull String packageName) {
- ThreadComputer current = live();
+ ThreadComputer current = snapshot();
try {
return current.mComputer.getPackageStateCopied(packageName);
} finally {
@@ -330,7 +319,7 @@ public final class ComputerTracker implements Computer {
}
public ResolveInfo createForwardingResolveInfoUnchecked(WatchedIntentFilter filter,
int sourceUserId, int targetUserId) {
- ThreadComputer current = live();
+ ThreadComputer current = snapshot();
try {
return current.mComputer.createForwardingResolveInfoUnchecked(filter, sourceUserId,
targetUserId);
@@ -340,7 +329,7 @@ public final class ComputerTracker implements Computer {
}
public ServiceInfo getServiceInfo(ComponentName component,
@PackageManager.ComponentInfoFlagsBits long flags, int userId) {
- ThreadComputer current = live();
+ ThreadComputer current = snapshot();
try {
return current.mComputer.getServiceInfo(component, flags, userId);
} finally {
@@ -380,17 +369,17 @@ public final class ComputerTracker implements Computer {
}
}
public String resolveExternalPackageName(AndroidPackage pkg) {
- ThreadComputer current = live();
+ ThreadComputer current = snapshot();
try {
return current.mComputer.resolveExternalPackageName(pkg);
} finally {
current.release();
}
}
- public String resolveInternalPackageNameLPr(String packageName, long versionCode) {
- ThreadComputer current = live();
+ public String resolveInternalPackageName(String packageName, long versionCode) {
+ ThreadComputer current = snapshot();
try {
- return current.mComputer.resolveInternalPackageNameLPr(packageName, versionCode);
+ return current.mComputer.resolveInternalPackageName(packageName, versionCode);
} finally {
current.release();
}
@@ -404,7 +393,7 @@ public final class ComputerTracker implements Computer {
}
}
public UserInfo getProfileParent(int userId) {
- ThreadComputer current = live();
+ ThreadComputer current = snapshot();
try {
return current.mComputer.getProfileParent(userId);
} finally {
@@ -412,7 +401,7 @@ public final class ComputerTracker implements Computer {
}
}
public boolean canViewInstantApps(int callingUid, int userId) {
- ThreadComputer current = live();
+ ThreadComputer current = snapshot();
try {
return current.mComputer.canViewInstantApps(callingUid, userId);
} finally {
@@ -445,7 +434,7 @@ public final class ComputerTracker implements Computer {
}
public boolean filterSharedLibPackage(@Nullable PackageStateInternal ps, int uid,
int userId, @PackageManager.ComponentInfoFlagsBits long flags) {
- ThreadComputer current = live();
+ ThreadComputer current = snapshot();
try {
return current.mComputer.filterSharedLibPackage(ps, uid, userId, flags);
} finally {
@@ -453,7 +442,7 @@ public final class ComputerTracker implements Computer {
}
}
public boolean isCallerSameApp(String packageName, int uid) {
- ThreadComputer current = live();
+ ThreadComputer current = snapshot();
try {
return current.mComputer.isCallerSameApp(packageName, uid);
} finally {
@@ -461,7 +450,7 @@ public final class ComputerTracker implements Computer {
}
}
public boolean isComponentVisibleToInstantApp(@Nullable ComponentName component) {
- ThreadComputer current = live();
+ ThreadComputer current = snapshot();
try {
return current.mComputer.isComponentVisibleToInstantApp(component);
} finally {
@@ -470,7 +459,7 @@ public final class ComputerTracker implements Computer {
}
public boolean isComponentVisibleToInstantApp(@Nullable ComponentName component,
@PackageManager.ComponentType int type) {
- ThreadComputer current = live();
+ ThreadComputer current = snapshot();
try {
return current.mComputer.isComponentVisibleToInstantApp(component, type);
} finally {
@@ -479,7 +468,7 @@ public final class ComputerTracker implements Computer {
}
public boolean isImplicitImageCaptureIntentAndNotSetByDpcLocked(Intent intent,
int userId, String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags) {
- ThreadComputer current = live();
+ ThreadComputer current = snapshot();
try {
return current.mComputer.isImplicitImageCaptureIntentAndNotSetByDpcLocked(intent,
userId, resolvedType, flags);
@@ -497,7 +486,7 @@ public final class ComputerTracker implements Computer {
}
public boolean isInstantAppInternal(String packageName, @UserIdInt int userId,
int callingUid) {
- ThreadComputer current = live();
+ ThreadComputer current = snapshot();
try {
return current.mComputer.isInstantAppInternal(packageName, userId, callingUid);
} finally {
@@ -506,7 +495,7 @@ public final class ComputerTracker implements Computer {
}
public boolean isSameProfileGroup(@UserIdInt int callerUserId,
@UserIdInt int userId) {
- ThreadComputer current = live();
+ ThreadComputer current = snapshot();
try {
return current.mComputer.isSameProfileGroup(callerUserId, userId);
} finally {
@@ -515,7 +504,7 @@ public final class ComputerTracker implements Computer {
}
public boolean shouldFilterApplication(@NonNull SharedUserSetting sus,
int callingUid, int userId) {
- ThreadComputer current = live();
+ ThreadComputer current = snapshot();
try {
return current.mComputer.shouldFilterApplication(sus, callingUid, userId);
} finally {
@@ -525,7 +514,7 @@ public final class ComputerTracker implements Computer {
public boolean shouldFilterApplication(@Nullable PackageStateInternal ps,
int callingUid, @Nullable ComponentName component,
@PackageManager.ComponentType int componentType, int userId) {
- ThreadComputer current = live();
+ ThreadComputer current = snapshot();
try {
return current.mComputer.shouldFilterApplication(ps, callingUid, component,
componentType, userId);
@@ -535,7 +524,7 @@ public final class ComputerTracker implements Computer {
}
public boolean shouldFilterApplication(@Nullable PackageStateInternal ps,
int callingUid, int userId) {
- ThreadComputer current = live();
+ ThreadComputer current = snapshot();
try {
return current.mComputer.shouldFilterApplication(ps, callingUid, userId);
} finally {
@@ -552,7 +541,7 @@ public final class ComputerTracker implements Computer {
}
public int getPackageUidInternal(String packageName,
@PackageManager.PackageInfoFlagsBits long flags, int userId, int callingUid) {
- ThreadComputer current = live();
+ ThreadComputer current = snapshot();
try {
return current.mComputer.getPackageUidInternal(packageName, flags, userId,
callingUid);
@@ -561,7 +550,7 @@ public final class ComputerTracker implements Computer {
}
}
public long updateFlagsForApplication(long flags, int userId) {
- ThreadComputer current = live();
+ ThreadComputer current = snapshot();
try {
return current.mComputer.updateFlagsForApplication(flags, userId);
} finally {
@@ -569,7 +558,7 @@ public final class ComputerTracker implements Computer {
}
}
public long updateFlagsForComponent(long flags, int userId) {
- ThreadComputer current = live();
+ ThreadComputer current = snapshot();
try {
return current.mComputer.updateFlagsForComponent(flags, userId);
} finally {
@@ -577,7 +566,7 @@ public final class ComputerTracker implements Computer {
}
}
public long updateFlagsForPackage(long flags, int userId) {
- ThreadComputer current = live();
+ ThreadComputer current = snapshot();
try {
return current.mComputer.updateFlagsForPackage(flags, userId);
} finally {
@@ -597,7 +586,7 @@ public final class ComputerTracker implements Computer {
public long updateFlagsForResolve(long flags, int userId, int callingUid,
boolean wantInstantApps, boolean onlyExposedExplicitly,
boolean isImplicitImageCaptureIntentAndNotSetByDpc) {
- ThreadComputer current = live();
+ ThreadComputer current = snapshot();
try {
return current.mComputer.updateFlagsForResolve(flags, userId, callingUid,
wantInstantApps, onlyExposedExplicitly,
@@ -607,7 +596,7 @@ public final class ComputerTracker implements Computer {
}
}
public void dump(int type, FileDescriptor fd, PrintWriter pw, DumpState dumpState) {
- ThreadComputer current = live();
+ ThreadComputer current = snapshot();
try {
current.mComputer.dump(type, fd, pw, dumpState);
} finally {
@@ -616,7 +605,7 @@ public final class ComputerTracker implements Computer {
}
public void enforceCrossUserOrProfilePermission(int callingUid, @UserIdInt int userId,
boolean requireFullPermission, boolean checkShell, String message) {
- ThreadComputer current = live();
+ ThreadComputer current = snapshot();
try {
current.mComputer.enforceCrossUserOrProfilePermission(callingUid, userId,
requireFullPermission, checkShell, message);
@@ -626,7 +615,7 @@ public final class ComputerTracker implements Computer {
}
public void enforceCrossUserPermission(int callingUid, @UserIdInt int userId,
boolean requireFullPermission, boolean checkShell, String message) {
- ThreadComputer current = live();
+ ThreadComputer current = snapshot();
try {
current.mComputer.enforceCrossUserPermission(callingUid, userId,
requireFullPermission, checkShell, message);
@@ -637,7 +626,7 @@ public final class ComputerTracker implements Computer {
public void enforceCrossUserPermission(int callingUid, @UserIdInt int userId,
boolean requireFullPermission, boolean checkShell,
boolean requirePermissionWhenSameUser, String message) {
- ThreadComputer current = live();
+ ThreadComputer current = snapshot();
try {
current.mComputer.enforceCrossUserPermission(callingUid, userId,
requireFullPermission, checkShell, requirePermissionWhenSameUser, message);
@@ -649,7 +638,7 @@ public final class ComputerTracker implements Computer {
Intent intent, String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags,
List<ResolveInfo> query, boolean always, boolean removeMatches, boolean debug,
int userId, boolean queryMayBeFiltered) {
- ThreadComputer current = live();
+ ThreadComputer current = snapshot();
try {
return current.mComputer.findPreferredActivityInternal(intent, resolvedType, flags,
query, always, removeMatches, debug, userId, queryMayBeFiltered);
@@ -660,7 +649,7 @@ public final class ComputerTracker implements Computer {
public ResolveInfo findPersistentPreferredActivityLP(Intent intent,
String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags,
List<ResolveInfo> query, boolean debug, int userId) {
- ThreadComputer current = live();
+ ThreadComputer current = snapshot();
try {
return current.mComputer.findPersistentPreferredActivityLP(intent, resolvedType,
flags, query, debug, userId);
@@ -834,15 +823,6 @@ public final class ComputerTracker implements Computer {
}
@Override
- public boolean isPackageStateAvailableAndVisible(@NonNull String packageName, int callingUid,
- @UserIdInt int userId) {
- try (ThreadComputer current = snapshot()) {
- return current.mComputer.isPackageStateAvailableAndVisible(packageName, callingUid,
- userId);
- }
- }
-
- @Override
public int checkSignatures(@NonNull String pkg1,
@NonNull String pkg2) {
try (ThreadComputer current = snapshot()) {
@@ -1272,4 +1252,35 @@ public final class ComputerTracker implements Computer {
return current.mComputer.getProcessesForUid(uid);
}
}
+
+ @Override
+ public PackageStateInternal getPackageStateFiltered(@NonNull String packageName, int callingUid,
+ @UserIdInt int userId) {
+ try (ThreadComputer current = snapshot()) {
+ return current.mComputer.getPackageStateFiltered(packageName, callingUid, userId);
+ }
+ }
+
+ @Override
+ public boolean getBlockUninstall(@UserIdInt int userId, @NonNull String packageName) {
+ try (ThreadComputer current = snapshot()) {
+ return current.mComputer.getBlockUninstall(userId, packageName);
+ }
+ }
+
+ @NonNull
+ @Override
+ public WatchedArrayMap<String, WatchedLongSparseArray<SharedLibraryInfo>> getSharedLibraries() {
+ try (ThreadComputer current = snapshot()) {
+ return current.mComputer.getSharedLibraries();
+ }
+ }
+
+ @Nullable
+ @Override
+ public Pair<PackageStateInternal, SharedUserApi> getPackageOrSharedUser(int appId) {
+ try (ThreadComputer current = snapshot()) {
+ return current.mComputer.getPackageOrSharedUser(appId);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/pm/DeletePackageHelper.java b/services/core/java/com/android/server/pm/DeletePackageHelper.java
index 48689a8da335..bd36b47292e7 100644
--- a/services/core/java/com/android/server/pm/DeletePackageHelper.java
+++ b/services/core/java/com/android/server/pm/DeletePackageHelper.java
@@ -62,6 +62,7 @@ import com.android.internal.util.ArrayUtils;
import com.android.internal.util.Preconditions;
import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.permission.PermissionManagerServiceInternal;
+import com.android.server.pm.pkg.PackageStateInternal;
import com.android.server.pm.pkg.PackageUserState;
import com.android.server.wm.ActivityTaskManagerInternal;
@@ -220,12 +221,12 @@ final class DeletePackageHelper {
res = deletePackageLIF(packageName, UserHandle.of(removeUser), true, allUsers,
deleteFlags | PackageManager.DELETE_CHATTY, info, true);
}
+ if (res && pkg != null) {
+ mPm.mInstantAppRegistry.onPackageUninstalled(pkg, uninstalledPs,
+ info.mRemovedUsers);
+ }
synchronized (mPm.mLock) {
if (res) {
- if (pkg != null) {
- mPm.mInstantAppRegistry.onPackageUninstalledLPw(pkg, uninstalledPs,
- info.mRemovedUsers);
- }
mPm.updateSequenceNumberLP(uninstalledPs, info.mRemovedUsers);
mPm.updateInstantAppInstallerLocked(packageName);
}
@@ -421,9 +422,7 @@ final class DeletePackageHelper {
}
if (clearPackageStateAndReturn) {
clearPackageStateForUserLIF(ps, userId, outInfo, flags);
- synchronized (mPm.mLock) {
- mPm.scheduleWritePackageRestrictionsLocked(user);
- }
+ mPm.scheduleWritePackageRestrictions(user);
return;
}
}
@@ -622,7 +621,6 @@ final class DeletePackageHelper {
final String packageName = versionedPackage.getPackageName();
final long versionCode = versionedPackage.getLongVersionCode();
- final String internalPackageName;
try {
if (mPm.mInjector.getLocalService(ActivityTaskManagerInternal.class)
@@ -636,10 +634,8 @@ final class DeletePackageHelper {
e.rethrowFromSystemServer();
}
- synchronized (mPm.mLock) {
- // Normalize package name to handle renamed packages and static libs
- internalPackageName = mPm.resolveInternalPackageNameLPr(packageName, versionCode);
- }
+ // Normalize package name to handle renamed packages and static libs
+ final String internalPackageName = mPm.resolveInternalPackageName(packageName, versionCode);
final int uid = Binder.getCallingUid();
if (!isOrphaned(internalPackageName)
@@ -748,13 +744,8 @@ final class DeletePackageHelper {
}
private boolean isOrphaned(String packageName) {
- // reader
- synchronized (mPm.mLock) {
- if (!mPm.mPackages.containsKey(packageName)) {
- return false;
- }
- return mPm.mSettings.isOrphaned(packageName);
- }
+ final PackageStateInternal packageState = mPm.getPackageStateInternal(packageName);
+ return packageState != null && packageState.getInstallSource().isOrphaned;
}
private boolean isCallerAllowedToSilentlyUninstall(int callingUid, String pkgName) {
@@ -894,7 +885,7 @@ final class DeletePackageHelper {
int installedForUsersCount = 0;
synchronized (mPm.mLock) {
// Normalize package name to handle renamed packages and static libs
- final String internalPkgName = mPm.resolveInternalPackageNameLPr(packageName,
+ final String internalPkgName = mPm.resolveInternalPackageName(packageName,
versionCode);
final PackageSetting ps = mPm.mSettings.getPackageLPr(internalPkgName);
if (ps != null) {
diff --git a/services/core/java/com/android/server/pm/DexOptHelper.java b/services/core/java/com/android/server/pm/DexOptHelper.java
index dcad3ecba6af..ba89916e6dfa 100644
--- a/services/core/java/com/android/server/pm/DexOptHelper.java
+++ b/services/core/java/com/android/server/pm/DexOptHelper.java
@@ -71,10 +71,15 @@ final class DexOptHelper {
private final PackageManagerService mPm;
public boolean isDexOptDialogShown() {
- return mDexOptDialogShown;
+ synchronized (mLock) {
+ return mDexOptDialogShown;
+ }
}
- @GuardedBy("mPm.mLock")
+ // TODO: Is this lock really necessary?
+ private final Object mLock = new Object();
+
+ @GuardedBy("mLock")
private boolean mDexOptDialogShown;
DexOptHelper(PackageManagerService pm) {
@@ -191,7 +196,7 @@ final class DexOptHelper {
numberOfPackagesVisited, numberOfPackagesToDexopt), true);
} catch (RemoteException e) {
}
- synchronized (mPm.mLock) {
+ synchronized (mLock) {
mDexOptDialogShown = true;
}
}
@@ -287,13 +292,11 @@ final class DexOptHelper {
public ArraySet<String> getOptimizablePackages() {
ArraySet<String> pkgs = new ArraySet<>();
- synchronized (mPm.mLock) {
- for (AndroidPackage p : mPm.mPackages.values()) {
- if (mPm.mPackageDexOptimizer.canOptimizePackage(p)) {
- pkgs.add(p.getPackageName());
- }
+ mPm.forEachPackageState(packageState -> {
+ if (mPm.mPackageDexOptimizer.canOptimizePackage(packageState.getPkg())) {
+ pkgs.add(packageState.getPackageName());
}
- }
+ });
return pkgs;
}
@@ -415,14 +418,10 @@ final class DexOptHelper {
public void forceDexOpt(String packageName) {
PackageManagerServiceUtils.enforceSystemOrRoot("forceDexOpt");
- AndroidPackage pkg;
- PackageSetting pkgSetting;
- synchronized (mPm.mLock) {
- pkg = mPm.mPackages.get(packageName);
- pkgSetting = mPm.mSettings.getPackageLPr(packageName);
- if (pkg == null || pkgSetting == null) {
- throw new IllegalArgumentException("Unknown package: " + packageName);
- }
+ final PackageStateInternal packageState = mPm.getPackageStateInternal(packageName);
+ final AndroidPackage pkg = packageState == null ? null : packageState.getPkg();
+ if (packageState == null || pkg == null) {
+ throw new IllegalArgumentException("Unknown package: " + packageName);
}
synchronized (mPm.mInstallLock) {
@@ -430,7 +429,7 @@ final class DexOptHelper {
// Whoever is calling forceDexOpt wants a compiled package.
// Don't use profiles since that may cause compilation to be skipped.
- final int res = performDexOptInternalWithDependenciesLI(pkg, pkgSetting,
+ final int res = performDexOptInternalWithDependenciesLI(pkg, packageState,
new DexoptOptions(packageName,
getDefaultCompilerFilter(),
DexoptOptions.DEXOPT_FORCE | DexoptOptions.DEXOPT_BOOT_COMPLETE));
diff --git a/services/core/java/com/android/server/pm/DumpHelper.java b/services/core/java/com/android/server/pm/DumpHelper.java
index 55d1293c616a..5ab0c4ce4ba6 100644
--- a/services/core/java/com/android/server/pm/DumpHelper.java
+++ b/services/core/java/com/android/server/pm/DumpHelper.java
@@ -39,6 +39,7 @@ import com.android.server.pm.verify.domain.proxy.DomainVerificationProxy;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.util.function.BiConsumer;
/**
* Dumps PackageManagerService internal states.
@@ -117,7 +118,7 @@ final class DumpHelper {
}
// Normalize package name to handle renamed packages and static libs
- pkg = mPm.resolveInternalPackageNameLPr(pkg, PackageManager.VERSION_CODE_HIGHEST);
+ pkg = mPm.resolveInternalPackageName(pkg, PackageManager.VERSION_CODE_HIGHEST);
pw.println(mPm.checkPermission(perm, pkg, user));
return;
@@ -369,33 +370,20 @@ final class DumpHelper {
}
}
- if (!checkin
- && dumpState.isDumping(DumpState.DUMP_ACTIVITY_RESOLVERS)) {
- synchronized (mPm.mLock) {
- mPm.mComponentResolver.dumpActivityResolvers(pw, dumpState, packageName);
- }
+ if (!checkin && dumpState.isDumping(DumpState.DUMP_ACTIVITY_RESOLVERS)) {
+ mPm.mComponentResolver.dumpActivityResolvers(pw, dumpState, packageName);
}
- if (!checkin
- && dumpState.isDumping(DumpState.DUMP_RECEIVER_RESOLVERS)) {
- synchronized (mPm.mLock) {
- mPm.mComponentResolver.dumpReceiverResolvers(pw, dumpState, packageName);
- }
+ if (!checkin && dumpState.isDumping(DumpState.DUMP_RECEIVER_RESOLVERS)) {
+ mPm.mComponentResolver.dumpReceiverResolvers(pw, dumpState, packageName);
}
- if (!checkin
- && dumpState.isDumping(DumpState.DUMP_SERVICE_RESOLVERS)) {
- synchronized (mPm.mLock) {
- mPm.mComponentResolver.dumpServiceResolvers(pw, dumpState, packageName);
- }
+ if (!checkin && dumpState.isDumping(DumpState.DUMP_SERVICE_RESOLVERS)) {
+ mPm.mComponentResolver.dumpServiceResolvers(pw, dumpState, packageName);
}
- if (!checkin
- && dumpState.isDumping(DumpState.DUMP_CONTENT_RESOLVERS)) {
- synchronized (mPm.mLock) {
- mPm.mComponentResolver.dumpProviderResolvers(pw, dumpState, packageName);
- }
+ if (!checkin && dumpState.isDumping(DumpState.DUMP_CONTENT_RESOLVERS)) {
+ mPm.mComponentResolver.dumpProviderResolvers(pw, dumpState, packageName);
}
- if (!checkin
- && dumpState.isDumping(DumpState.DUMP_PREFERRED)) {
+ if (!checkin && dumpState.isDumping(DumpState.DUMP_PREFERRED)) {
mPm.dumpComputer(DumpState.DUMP_PREFERRED, fd, pw, dumpState);
}
@@ -405,23 +393,16 @@ final class DumpHelper {
mPm.dumpComputer(DumpState.DUMP_PREFERRED_XML, fd, pw, dumpState);
}
- if (!checkin
- && dumpState.isDumping(DumpState.DUMP_DOMAIN_PREFERRED)) {
+ if (!checkin && dumpState.isDumping(DumpState.DUMP_DOMAIN_PREFERRED)) {
mPm.dumpComputer(DumpState.DUMP_DOMAIN_PREFERRED, fd, pw, dumpState);
}
- if (!checkin
- && dumpState.isDumping(DumpState.DUMP_PERMISSIONS)) {
- synchronized (mPm.mLock) {
- mPm.mSettings.dumpPermissions(pw, packageName, permissionNames, dumpState);
- }
+ if (!checkin && dumpState.isDumping(DumpState.DUMP_PERMISSIONS)) {
+ mPm.mSettings.dumpPermissions(pw, packageName, permissionNames, dumpState);
}
- if (!checkin
- && dumpState.isDumping(DumpState.DUMP_PROVIDERS)) {
- synchronized (mPm.mLock) {
- mPm.mComponentResolver.dumpContentProviders(pw, dumpState, packageName);
- }
+ if (!checkin && dumpState.isDumping(DumpState.DUMP_PROVIDERS)) {
+ mPm.mComponentResolver.dumpContentProviders(pw, dumpState, packageName);
}
if (!checkin
@@ -461,28 +442,28 @@ final class DumpHelper {
pw.println();
}
pw.println("Package Changes:");
- synchronized (mPm.mLock) {
- pw.print(" Sequence number="); pw.println(mPm.mChangedPackagesSequenceNumber);
- final int numChangedPackages = mPm.mChangedPackages.size();
+ mPm.mChangedPackagesTracker.iterateAll((sequenceNumber, values) -> {
+ pw.print(" Sequence number="); pw.println(sequenceNumber);
+ final int numChangedPackages = values.size();
for (int i = 0; i < numChangedPackages; i++) {
- final SparseArray<String> changes = mPm.mChangedPackages.valueAt(i);
- pw.print(" User "); pw.print(mPm.mChangedPackages.keyAt(i)); pw.println(":");
+ final SparseArray<String> changes = values.valueAt(i);
+ pw.print(" User "); pw.print(values.keyAt(i)); pw.println(":");
final int numChanges = changes.size();
if (numChanges == 0) {
pw.print(" "); pw.println("No packages changed");
} else {
for (int j = 0; j < numChanges; j++) {
final String pkgName = changes.valueAt(j);
- final int sequenceNumber = changes.keyAt(j);
+ final int userSequenceNumber = changes.keyAt(j);
pw.print(" ");
pw.print("seq=");
- pw.print(sequenceNumber);
+ pw.print(userSequenceNumber);
pw.print(", package=");
pw.println(pkgName);
}
}
}
- }
+ });
}
if (!checkin
@@ -537,9 +518,7 @@ final class DumpHelper {
if (!checkin
&& dumpState.isDumping(DumpState.DUMP_SERVICE_PERMISSIONS)
&& packageName == null) {
- synchronized (mPm.mLock) {
- mPm.mComponentResolver.dumpServicePermissions(pw, dumpState);
- }
+ mPm.mComponentResolver.dumpServicePermissions(pw, dumpState);
}
if (!checkin
@@ -558,9 +537,7 @@ final class DumpHelper {
if (dumpState.onTitlePrinted()) {
pw.println();
}
- synchronized (mPm.mLock) {
- mPm.mSettings.dumpReadMessagesLPr(pw, dumpState);
- }
+ mPm.mSettings.dumpReadMessages(pw, dumpState);
pw.println();
pw.println("Package warning messages:");
dumpCriticalInfo(pw, null);
diff --git a/services/core/java/com/android/server/pm/IncrementalProgressListener.java b/services/core/java/com/android/server/pm/IncrementalProgressListener.java
index 018501d1163c..fa11924218d4 100644
--- a/services/core/java/com/android/server/pm/IncrementalProgressListener.java
+++ b/services/core/java/com/android/server/pm/IncrementalProgressListener.java
@@ -18,6 +18,8 @@ package com.android.server.pm;
import android.content.pm.IPackageLoadingProgressCallback;
+import com.android.server.pm.pkg.PackageStateInternal;
+
/**
* Loading progress callback, used to listen for progress changes and update package setting
*/
@@ -31,25 +33,24 @@ final class IncrementalProgressListener extends IPackageLoadingProgressCallback.
@Override
public void onPackageLoadingProgressChanged(float progress) {
- final PackageSetting ps;
- synchronized (mPm.mLock) {
- ps = mPm.mSettings.getPackageLPr(mPackageName);
- if (ps == null) {
- return;
- }
+ PackageStateInternal packageState = mPm.getPackageStateInternal(mPackageName);
+ if (packageState == null) {
+ return;
+ }
- boolean wasLoading = ps.isLoading();
- // Due to asynchronous progress reporting, incomplete progress might be received
- // after the app is migrated off incremental. Ignore such progress updates.
- if (wasLoading) {
- ps.setLoadingProgress(progress);
- // Only report the state change when loading state changes from loading to not
- if (!ps.isLoading()) {
- // Unregister progress listener
- mPm.mIncrementalManager.unregisterLoadingProgressCallbacks(ps.getPathString());
- // Make sure the information is preserved
- mPm.scheduleWriteSettings();
- }
+ boolean wasLoading = packageState.isLoading();
+ // Due to asynchronous progress reporting, incomplete progress might be received
+ // after the app is migrated off incremental. Ignore such progress updates.
+ if (wasLoading) {
+ mPm.commitPackageStateMutation(null, mPackageName,
+ state -> state.setLoadingProgress(progress));
+ // Only report the state change when loading state changes from loading to not
+ if (Math.abs(1.0f - progress) < 0.00000001f) {
+ // Unregister progress listener
+ mPm.mIncrementalManager
+ .unregisterLoadingProgressCallbacks(packageState.getPathString());
+ // Make sure the information is preserved
+ mPm.scheduleWriteSettings();
}
}
}
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index b636c8e8df1c..336da2acca67 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -141,7 +141,7 @@ import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.content.F2fsUtils;
-import com.android.internal.content.PackageHelper;
+import com.android.internal.content.InstallLocationUtils;
import com.android.internal.security.VerityUtils;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FrameworkStatsLog;
@@ -347,7 +347,7 @@ final class InstallPackageHelper {
commitPackageSettings(pkg, oldPkg, pkgSetting, oldPkgSetting, scanFlags,
(parseFlags & ParsingPackageUtils.PARSE_CHATTY) != 0 /*chatty*/, reconciledPkg);
if (pkgSetting.getInstantApp(userId)) {
- mPm.mInstantAppRegistry.addInstantAppLPw(userId, pkgSetting.getAppId());
+ mPm.mInstantAppRegistry.addInstantApp(userId, pkgSetting.getAppId());
}
if (!IncrementalManager.isIncrementalPath(pkgSetting.getPathString())) {
@@ -2358,26 +2358,26 @@ final class InstallPackageHelper {
if ((installFlags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) {
// Check for updated system application.
if (installedPkg.isSystem()) {
- return PackageHelper.RECOMMEND_INSTALL_INTERNAL;
+ return InstallLocationUtils.RECOMMEND_INSTALL_INTERNAL;
} else {
// If current upgrade specifies particular preference
if (installLocation == PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY) {
// Application explicitly specified internal.
- return PackageHelper.RECOMMEND_INSTALL_INTERNAL;
+ return InstallLocationUtils.RECOMMEND_INSTALL_INTERNAL;
} else if (
installLocation == PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL) {
// App explicitly prefers external. Let policy decide
} else {
// Prefer previous location
if (installedPkg.isExternalStorage()) {
- return PackageHelper.RECOMMEND_INSTALL_EXTERNAL;
+ return InstallLocationUtils.RECOMMEND_INSTALL_EXTERNAL;
}
- return PackageHelper.RECOMMEND_INSTALL_INTERNAL;
+ return InstallLocationUtils.RECOMMEND_INSTALL_INTERNAL;
}
}
} else {
// Invalid install. Return error code
- return PackageHelper.RECOMMEND_FAILED_ALREADY_EXISTS;
+ return InstallLocationUtils.RECOMMEND_FAILED_ALREADY_EXISTS;
}
}
}
@@ -2614,9 +2614,7 @@ final class InstallPackageHelper {
? res.mRemovedInfo.mInstallerPackageName
: null;
- synchronized (mPm.mLock) {
- mPm.mInstantAppRegistry.onPackageInstalledLPw(res.mPkg, res.mNewUsers);
- }
+ mPm.notifyInstantAppPackageInstalled(res.mPkg.getPackageName(), res.mNewUsers);
// Determine the set of users who are adding this package for
// the first time vs. those who are seeing an update.
diff --git a/services/core/java/com/android/server/pm/InstallParams.java b/services/core/java/com/android/server/pm/InstallParams.java
index d996fe46a4f6..7e845c74617b 100644
--- a/services/core/java/com/android/server/pm/InstallParams.java
+++ b/services/core/java/com/android/server/pm/InstallParams.java
@@ -35,18 +35,17 @@ import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
import android.content.pm.SigningDetails;
import android.content.pm.parsing.PackageLite;
-import android.os.Environment;
import android.os.Message;
import android.os.Trace;
import android.os.UserHandle;
-import android.os.storage.StorageManager;
import android.util.ArrayMap;
import android.util.Pair;
import android.util.Slog;
import com.android.internal.content.F2fsUtils;
-import com.android.internal.content.PackageHelper;
+import com.android.internal.content.InstallLocationUtils;
import com.android.internal.util.Preconditions;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
import java.io.File;
import java.util.ArrayList;
@@ -142,12 +141,8 @@ final class InstallParams extends HandlerParams {
* Only {@link PackageManager#INSTALL_INTERNAL} flag may mutate in
* {@link #mInstallFlags}
*/
- private int overrideInstallLocation(PackageInfoLite pkgLite) {
- final boolean ephemeral = (mInstallFlags & PackageManager.INSTALL_INSTANT_APP) != 0;
- if (DEBUG_INSTANT && ephemeral) {
- Slog.v(TAG, "pkgLite for install: " + pkgLite);
- }
-
+ private int overrideInstallLocation(String packageName, int recommendedInstallLocation,
+ int installLocation) {
if (mOriginInfo.mStaged) {
// If we're already staged, we've firmly committed to an install location
if (mOriginInfo.mFile != null) {
@@ -155,77 +150,35 @@ final class InstallParams extends HandlerParams {
} else {
throw new IllegalStateException("Invalid stage location");
}
- } else if (pkgLite.recommendedInstallLocation
- == PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) {
- /*
- * If we are not staged and have too little free space, try to free cache
- * before giving up.
- */
- // TODO: focus freeing disk space on the target device
- final StorageManager storage = StorageManager.from(mPm.mContext);
- final long lowThreshold = storage.getStorageLowBytes(
- Environment.getDataDirectory());
-
- final long sizeBytes = PackageManagerServiceUtils.calculateInstalledSize(
- mOriginInfo.mResolvedPath, mPackageAbiOverride);
- if (sizeBytes >= 0) {
- synchronized (mPm.mInstallLock) {
- try {
- mPm.mInstaller.freeCache(null, sizeBytes + lowThreshold, 0);
- pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(mPm.mContext,
- mPackageLite, mOriginInfo.mResolvedPath, mInstallFlags,
- mPackageAbiOverride);
- } catch (Installer.InstallerException e) {
- Slog.w(TAG, "Failed to free cache", e);
- }
- }
- }
-
- /*
- * The cache free must have deleted the file we downloaded to install.
- *
- * TODO: fix the "freeCache" call to not delete the file we care about.
- */
- if (pkgLite.recommendedInstallLocation
- == PackageHelper.RECOMMEND_FAILED_INVALID_URI) {
- pkgLite.recommendedInstallLocation =
- PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE;
+ }
+ if (recommendedInstallLocation < 0) {
+ return InstallLocationUtils.getInstallationErrorCode(recommendedInstallLocation);
+ }
+ // Override with defaults if needed.
+ synchronized (mPm.mLock) {
+ // reader
+ AndroidPackage installedPkg = mPm.mPackages.get(packageName);
+ if (installedPkg != null) {
+ // Currently installed package which the new package is attempting to replace
+ recommendedInstallLocation = InstallLocationUtils.installLocationPolicy(
+ installLocation, recommendedInstallLocation, mInstallFlags,
+ installedPkg.isSystem(), installedPkg.isExternalStorage());
}
}
- int ret = INSTALL_SUCCEEDED;
- int loc = pkgLite.recommendedInstallLocation;
- if (loc == PackageHelper.RECOMMEND_FAILED_INVALID_LOCATION) {
- ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
- } else if (loc == PackageHelper.RECOMMEND_FAILED_ALREADY_EXISTS) {
- ret = PackageManager.INSTALL_FAILED_ALREADY_EXISTS;
- } else if (loc == PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) {
- ret = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
- } else if (loc == PackageHelper.RECOMMEND_FAILED_INVALID_APK) {
- ret = PackageManager.INSTALL_FAILED_INVALID_APK;
- } else if (loc == PackageHelper.RECOMMEND_FAILED_INVALID_URI) {
- ret = PackageManager.INSTALL_FAILED_INVALID_URI;
- } else if (loc == PackageHelper.RECOMMEND_MEDIA_UNAVAILABLE) {
- ret = PackageManager.INSTALL_FAILED_MEDIA_UNAVAILABLE;
- } else {
- // Override with defaults if needed.
- loc = mInstallPackageHelper.installLocationPolicy(pkgLite, mInstallFlags);
-
- final boolean onInt = (mInstallFlags & PackageManager.INSTALL_INTERNAL) != 0;
-
- if (!onInt) {
- // Override install location with flags
- if (loc == PackageHelper.RECOMMEND_INSTALL_EXTERNAL) {
- // Set the flag to install on external media.
- mInstallFlags &= ~PackageManager.INSTALL_INTERNAL;
- } else {
- // Make sure the flag for installing on external
- // media is unset
- mInstallFlags |= PackageManager.INSTALL_INTERNAL;
- }
+ final boolean onInt = (mInstallFlags & PackageManager.INSTALL_INTERNAL) != 0;
+
+ if (!onInt) {
+ // Override install location with flags
+ if (recommendedInstallLocation == InstallLocationUtils.RECOMMEND_INSTALL_EXTERNAL) {
+ // Set the flag to install on external media.
+ mInstallFlags &= ~PackageManager.INSTALL_INTERNAL;
+ } else {
+ // Make sure the flag for installing on external media is unset
+ mInstallFlags |= PackageManager.INSTALL_INTERNAL;
}
}
- return ret;
+ return INSTALL_SUCCEEDED;
}
/*
@@ -254,7 +207,21 @@ final class InstallParams extends HandlerParams {
}
}
- mRet = overrideInstallLocation(pkgLite);
+ final boolean ephemeral = (mInstallFlags & PackageManager.INSTALL_INSTANT_APP) != 0;
+ if (DEBUG_INSTANT && ephemeral) {
+ Slog.v(TAG, "pkgLite for install: " + pkgLite);
+ }
+
+ if (!mOriginInfo.mStaged && pkgLite.recommendedInstallLocation
+ == InstallLocationUtils.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) {
+ // If we are not staged and have too little free space, try to free cache
+ // before giving up.
+ pkgLite.recommendedInstallLocation = mPm.freeCacheForInstallation(
+ pkgLite.recommendedInstallLocation, mPackageLite,
+ mOriginInfo.mResolvedPath, mPackageAbiOverride, mInstallFlags);
+ }
+ mRet = overrideInstallLocation(pkgLite.packageName, pkgLite.recommendedInstallLocation,
+ pkgLite.installLocation);
}
@Override
diff --git a/services/core/java/com/android/server/pm/InstantAppRegistry.java b/services/core/java/com/android/server/pm/InstantAppRegistry.java
index 1de239e0e8b6..ea6e4583d7d7 100644
--- a/services/core/java/com/android/server/pm/InstantAppRegistry.java
+++ b/services/core/java/com/android/server/pm/InstantAppRegistry.java
@@ -18,12 +18,13 @@ package com.android.server.pm;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.annotation.UserIdInt;
+import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.InstantAppInfo;
import android.content.pm.PackageManager;
-import android.content.pm.PackageManagerInternal;
import android.content.pm.PermissionInfo;
import android.content.pm.SigningDetails;
import android.graphics.Bitmap;
@@ -93,7 +94,7 @@ import java.util.function.Predicate;
* pruning installed instant apps and meta-data for uninstalled instant apps
* when free space is needed.
*/
-class InstantAppRegistry implements Watchable, Snappable {
+public class InstantAppRegistry implements Watchable, Snappable {
private static final boolean DEBUG = false;
private static final String LOG_TAG = "InstantAppRegistry";
@@ -125,14 +126,17 @@ class InstantAppRegistry implements Watchable, Snappable {
private static final String ATTR_NAME = "name";
private static final String ATTR_GRANTED = "granted";
- private final PackageManagerService mService;
+ private final Context mContext;
private final PermissionManagerServiceInternal mPermissionManager;
+ private final UserManagerInternal mUserManager;
+ private final DeletePackageHelper mDeletePackageHelper;
private final CookiePersistence mCookiePersistence;
- private final PackageManagerInternal mPmInternal;
+
+ private final Object mLock = new Object();
/** State for uninstalled instant apps */
@Watched
- @GuardedBy("mService.mLock")
+ @GuardedBy("mLock")
private final WatchedSparseArray<List<UninstalledInstantAppState>> mUninstalledInstantApps;
/**
@@ -142,12 +146,12 @@ class InstantAppRegistry implements Watchable, Snappable {
* UserID -> TargetAppId -> InstantAppId
*/
@Watched
- @GuardedBy("mService.mLock")
+ @GuardedBy("mLock")
private final WatchedSparseArray<WatchedSparseArray<WatchedSparseBooleanArray>> mInstantGrants;
/** The set of all installed instant apps. UserID -> AppID */
@Watched
- @GuardedBy("mService.mLock")
+ @GuardedBy("mLock")
private final WatchedSparseArray<WatchedSparseBooleanArray> mInstalledInstantAppUids;
/**
@@ -159,6 +163,7 @@ class InstantAppRegistry implements Watchable, Snappable {
* Watchable machinery
*/
private final WatchableImpl mWatchable = new WatchableImpl();
+
public void registerObserver(@NonNull Watcher observer) {
mWatchable.registerObserver(observer);
}
@@ -196,12 +201,14 @@ class InstantAppRegistry implements Watchable, Snappable {
}};
}
- public InstantAppRegistry(PackageManagerService service,
- PermissionManagerServiceInternal permissionManager,
- PackageManagerInternal pmInternal) {
- mService = service;
+ public InstantAppRegistry(@NonNull Context context,
+ @NonNull PermissionManagerServiceInternal permissionManager,
+ @NonNull UserManagerInternal userManager,
+ @NonNull DeletePackageHelper deletePackageHelper) {
+ mContext = context;
mPermissionManager = permissionManager;
- mPmInternal = pmInternal;
+ mUserManager = userManager;
+ mDeletePackageHelper = deletePackageHelper;
mCookiePersistence = new CookiePersistence(BackgroundThread.getHandler().getLooper());
mUninstalledInstantApps = new WatchedSparseArray<List<UninstalledInstantAppState>>();
@@ -220,9 +227,10 @@ class InstantAppRegistry implements Watchable, Snappable {
* The copy constructor is used by PackageManagerService to construct a snapshot.
*/
private InstantAppRegistry(InstantAppRegistry r) {
- mService = r.mService;
+ mContext = r.mContext;
mPermissionManager = r.mPermissionManager;
- mPmInternal = r.mPmInternal;
+ mUserManager = r.mUserManager;
+ mDeletePackageHelper = r.mDeletePackageHelper;
mCookiePersistence = null;
mUninstalledInstantApps = new WatchedSparseArray<List<UninstalledInstantAppState>>(
@@ -243,56 +251,44 @@ class InstantAppRegistry implements Watchable, Snappable {
return mSnapshot.snapshot();
}
- @GuardedBy("mService.mLock")
- public byte[] getInstantAppCookieLPw(@NonNull String packageName,
- @UserIdInt int userId) {
- // Only installed packages can get their own cookie
- AndroidPackage pkg = mService.mPackages.get(packageName);
- if (pkg == null) {
- return null;
- }
-
- byte[] pendingCookie = mCookiePersistence.getPendingPersistCookieLPr(pkg, userId);
- if (pendingCookie != null) {
- return pendingCookie;
- }
- File cookieFile = peekInstantCookieFile(packageName, userId);
- if (cookieFile != null && cookieFile.exists()) {
- try {
- return IoUtils.readFileAsByteArray(cookieFile.toString());
- } catch (IOException e) {
- Slog.w(LOG_TAG, "Error reading cookie file: " + cookieFile);
+ public byte[] getInstantAppCookie(@NonNull AndroidPackage pkg, @UserIdInt int userId) {
+ synchronized (mLock) {
+ byte[] pendingCookie = mCookiePersistence.getPendingPersistCookieLPr(pkg, userId);
+ if (pendingCookie != null) {
+ return pendingCookie;
+ }
+ File cookieFile = peekInstantCookieFile(pkg.getPackageName(), userId);
+ if (cookieFile != null && cookieFile.exists()) {
+ try {
+ return IoUtils.readFileAsByteArray(cookieFile.toString());
+ } catch (IOException e) {
+ Slog.w(LOG_TAG, "Error reading cookie file: " + cookieFile);
+ }
}
+ return null;
}
- return null;
}
- @GuardedBy("mService.mLock")
- public boolean setInstantAppCookieLPw(@NonNull String packageName,
- @Nullable byte[] cookie, @UserIdInt int userId) {
- if (cookie != null && cookie.length > 0) {
- final int maxCookieSize = mService.mContext.getPackageManager()
- .getInstantAppCookieMaxBytes();
- if (cookie.length > maxCookieSize) {
- Slog.e(LOG_TAG, "Instant app cookie for package " + packageName + " size "
- + cookie.length + " bytes while max size is " + maxCookieSize);
- return false;
+ public boolean setInstantAppCookie(@NonNull AndroidPackage pkg,
+ @Nullable byte[] cookie, int instantAppCookieMaxBytes, @UserIdInt int userId) {
+ synchronized (mLock) {
+ if (cookie != null && cookie.length > 0) {
+ if (cookie.length > instantAppCookieMaxBytes) {
+ Slog.e(LOG_TAG, "Instant app cookie for package " + pkg.getPackageName()
+ + " size " + cookie.length + " bytes while max size is "
+ + instantAppCookieMaxBytes);
+ return false;
+ }
}
- }
- // Only an installed package can set its own cookie
- AndroidPackage pkg = mService.mPackages.get(packageName);
- if (pkg == null) {
- return false;
+ mCookiePersistence.schedulePersistLPw(userId, pkg, cookie);
+ return true;
}
-
- mCookiePersistence.schedulePersistLPw(userId, pkg, cookie);
- return true;
}
private void persistInstantApplicationCookie(@Nullable byte[] cookie,
@NonNull String packageName, @NonNull File cookieFile, @UserIdInt int userId) {
- synchronized (mService.mLock) {
+ synchronized (mLock) {
File appDir = getInstantApplicationDir(packageName, userId);
if (!appDir.exists() && !appDir.mkdirs()) {
Slog.e(LOG_TAG, "Cannot create instant app cookie directory");
@@ -315,55 +311,54 @@ class InstantAppRegistry implements Watchable, Snappable {
}
}
- public Bitmap getInstantAppIconLPw(@NonNull String packageName,
- @UserIdInt int userId) {
- File iconFile = new File(getInstantApplicationDir(packageName, userId),
- INSTANT_APP_ICON_FILE);
- if (iconFile.exists()) {
- return BitmapFactory.decodeFile(iconFile.toString());
+ @Nullable
+ public Bitmap getInstantAppIcon(@NonNull String packageName, @UserIdInt int userId) {
+ synchronized (mLock) {
+ File iconFile = new File(getInstantApplicationDir(packageName, userId),
+ INSTANT_APP_ICON_FILE);
+ if (iconFile.exists()) {
+ return BitmapFactory.decodeFile(iconFile.toString());
+ }
+ return null;
}
- return null;
}
- public String getInstantAppAndroidIdLPw(@NonNull String packageName,
- @UserIdInt int userId) {
- File idFile = new File(getInstantApplicationDir(packageName, userId),
- INSTANT_APP_ANDROID_ID_FILE);
- if (idFile.exists()) {
- try {
- return IoUtils.readFileAsString(idFile.getAbsolutePath());
- } catch (IOException e) {
- Slog.e(LOG_TAG, "Failed to read instant app android id file: " + idFile, e);
+ @Nullable
+ public String getInstantAppAndroidId(@NonNull String packageName, @UserIdInt int userId) {
+ synchronized (mLock) {
+ File idFile = new File(getInstantApplicationDir(packageName, userId),
+ INSTANT_APP_ANDROID_ID_FILE);
+ if (idFile.exists()) {
+ try {
+ return IoUtils.readFileAsString(idFile.getAbsolutePath());
+ } catch (IOException e) {
+ Slog.e(LOG_TAG, "Failed to read instant app android id file: " + idFile, e);
+ }
}
- }
- return generateInstantAppAndroidIdLPw(packageName, userId);
- }
- private String generateInstantAppAndroidIdLPw(@NonNull String packageName,
- @UserIdInt int userId) {
- byte[] randomBytes = new byte[8];
- new SecureRandom().nextBytes(randomBytes);
- String id = HexEncoding.encodeToString(randomBytes, false /* upperCase */);
- File appDir = getInstantApplicationDir(packageName, userId);
- if (!appDir.exists() && !appDir.mkdirs()) {
- Slog.e(LOG_TAG, "Cannot create instant app cookie directory");
+ byte[] randomBytes = new byte[8];
+ new SecureRandom().nextBytes(randomBytes);
+ String id = HexEncoding.encodeToString(randomBytes, false /* upperCase */);
+ File appDir = getInstantApplicationDir(packageName, userId);
+ if (!appDir.exists() && !appDir.mkdirs()) {
+ Slog.e(LOG_TAG, "Cannot create instant app cookie directory");
+ return id;
+ }
+ idFile = new File(getInstantApplicationDir(packageName, userId),
+ INSTANT_APP_ANDROID_ID_FILE);
+ try (FileOutputStream fos = new FileOutputStream(idFile)) {
+ fos.write(id.getBytes());
+ } catch (IOException e) {
+ Slog.e(LOG_TAG, "Error writing instant app android id file: " + idFile, e);
+ }
return id;
}
- File idFile = new File(getInstantApplicationDir(packageName, userId),
- INSTANT_APP_ANDROID_ID_FILE);
- try (FileOutputStream fos = new FileOutputStream(idFile)) {
- fos.write(id.getBytes());
- } catch (IOException e) {
- Slog.e(LOG_TAG, "Error writing instant app android id file: " + idFile, e);
- }
- return id;
-
}
- @GuardedBy("mService.mLock")
- public @Nullable List<InstantAppInfo> getInstantAppsLPr(@UserIdInt int userId) {
- List<InstantAppInfo> installedApps = getInstalledInstantApplicationsLPr(userId);
- List<InstantAppInfo> uninstalledApps = getUninstalledInstantApplicationsLPr(userId);
+ @Nullable
+ public List<InstantAppInfo> getInstantApps(@NonNull Computer computer, @UserIdInt int userId) {
+ List<InstantAppInfo> installedApps = getInstalledInstantApplications(computer, userId);
+ List<InstantAppInfo> uninstalledApps = getUninstalledInstantApplications(computer, userId);
if (installedApps != null) {
if (uninstalledApps != null) {
installedApps.addAll(uninstalledApps);
@@ -373,123 +368,130 @@ class InstantAppRegistry implements Watchable, Snappable {
return uninstalledApps;
}
- @GuardedBy("mService.mLock")
- public void onPackageInstalledLPw(@NonNull AndroidPackage pkg, @NonNull int[] userIds) {
- PackageStateInternal ps = mPmInternal.getPackageStateInternal(pkg.getPackageName());
- if (ps == null) {
+ public void onPackageInstalled(@NonNull Computer computer, @NonNull String packageName,
+ @NonNull int[] userIds) {
+ PackageStateInternal ps = computer.getPackageStateInternal(packageName);
+ AndroidPackage pkg = ps == null ? null : ps.getPkg();
+ if (pkg == null) {
return;
}
- for (int userId : userIds) {
- // Ignore not installed apps
- if (mService.mPackages.get(pkg.getPackageName()) == null
- || !ps.getUserStateOrDefault(userId).isInstalled()) {
- continue;
- }
-
- // Propagate permissions before removing any state
- propagateInstantAppPermissionsIfNeeded(pkg, userId);
+ synchronized (mLock) {
+ for (int userId : userIds) {
+ // Ignore not installed apps
+ if (!ps.getUserStateOrDefault(userId).isInstalled()) {
+ continue;
+ }
- // Track instant apps
- if (ps.getUserStateOrDefault(userId).isInstantApp()) {
- addInstantAppLPw(userId, ps.getAppId());
- }
+ // Propagate permissions before removing any state
+ propagateInstantAppPermissionsIfNeeded(pkg, userId);
- // Remove the in-memory state
- removeUninstalledInstantAppStateLPw((UninstalledInstantAppState state) ->
- state.mInstantAppInfo.getPackageName().equals(pkg.getPackageName()),
- userId);
-
- // Remove the on-disk state except the cookie
- File instantAppDir = getInstantApplicationDir(pkg.getPackageName(), userId);
- new File(instantAppDir, INSTANT_APP_METADATA_FILE).delete();
- new File(instantAppDir, INSTANT_APP_ICON_FILE).delete();
+ // Track instant apps
+ if (ps.getUserStateOrDefault(userId).isInstantApp()) {
+ addInstantApp(userId, ps.getAppId());
+ }
- // If app signature changed - wipe the cookie
- File currentCookieFile = peekInstantCookieFile(pkg.getPackageName(), userId);
- if (currentCookieFile == null) {
- continue;
- }
+ // Remove the in-memory state
+ removeUninstalledInstantAppStateLPw((UninstalledInstantAppState state) ->
+ state.mInstantAppInfo.getPackageName().equals(pkg.getPackageName()),
+ userId);
- String cookieName = currentCookieFile.getName();
- String currentCookieSha256 =
- cookieName.substring(INSTANT_APP_COOKIE_FILE_PREFIX.length(),
- cookieName.length() - INSTANT_APP_COOKIE_FILE_SIFFIX.length());
+ // Remove the on-disk state except the cookie
+ File instantAppDir = getInstantApplicationDir(pkg.getPackageName(), userId);
+ new File(instantAppDir, INSTANT_APP_METADATA_FILE).delete();
+ new File(instantAppDir, INSTANT_APP_ICON_FILE).delete();
- // Before we used only the first signature to compute the SHA 256 but some
- // apps could be singed by multiple certs and the cert order is undefined.
- // We prefer the modern computation procedure where all certs are taken
- // into account but also allow the value from the old computation to avoid
- // data loss.
- if (pkg.getSigningDetails().checkCapability(currentCookieSha256,
- SigningDetails.CertCapabilities.INSTALLED_DATA)) {
- return;
- }
+ // If app signature changed - wipe the cookie
+ File currentCookieFile = peekInstantCookieFile(pkg.getPackageName(), userId);
+ if (currentCookieFile == null) {
+ continue;
+ }
- // For backwards compatibility we accept match based on any signature, since we may have
- // recorded only the first for multiply-signed packages
- final String[] signaturesSha256Digests = PackageUtils.computeSignaturesSha256Digests(
- pkg.getSigningDetails().getSignatures());
- for (String s : signaturesSha256Digests) {
- if (s.equals(currentCookieSha256)) {
+ String cookieName = currentCookieFile.getName();
+ String currentCookieSha256 =
+ cookieName.substring(INSTANT_APP_COOKIE_FILE_PREFIX.length(),
+ cookieName.length() - INSTANT_APP_COOKIE_FILE_SIFFIX.length());
+
+ // Before we used only the first signature to compute the SHA 256 but some
+ // apps could be singed by multiple certs and the cert order is undefined.
+ // We prefer the modern computation procedure where all certs are taken
+ // into account but also allow the value from the old computation to avoid
+ // data loss.
+ if (pkg.getSigningDetails().checkCapability(currentCookieSha256,
+ SigningDetails.CertCapabilities.INSTALLED_DATA)) {
return;
}
- }
- // Sorry, you are out of luck - different signatures - nuke data
- Slog.i(LOG_TAG, "Signature for package " + pkg.getPackageName()
- + " changed - dropping cookie");
+ // For backwards compatibility we accept match based on any signature, since we may
+ // have recorded only the first for multiply-signed packages
+ final String[] signaturesSha256Digests =
+ PackageUtils.computeSignaturesSha256Digests(
+ pkg.getSigningDetails().getSignatures());
+ for (String s : signaturesSha256Digests) {
+ if (s.equals(currentCookieSha256)) {
+ return;
+ }
+ }
+
+ // Sorry, you are out of luck - different signatures - nuke data
+ Slog.i(LOG_TAG, "Signature for package " + pkg.getPackageName()
+ + " changed - dropping cookie");
// Make sure a pending write for the old signed app is cancelled
- mCookiePersistence.cancelPendingPersistLPw(pkg, userId);
- currentCookieFile.delete();
+ mCookiePersistence.cancelPendingPersistLPw(pkg, userId);
+ currentCookieFile.delete();
+ }
}
}
- @GuardedBy("mService.mLock")
- public void onPackageUninstalledLPw(@NonNull AndroidPackage pkg, @Nullable PackageSetting ps,
+ public void onPackageUninstalled(@NonNull AndroidPackage pkg, @NonNull PackageSetting ps,
@NonNull int[] userIds) {
if (ps == null) {
return;
}
- for (int userId : userIds) {
- if (mService.mPackages.get(pkg.getPackageName()) != null && ps.getInstalled(userId)) {
- continue;
- }
+ synchronized (mLock) {
+ for (int userId : userIds) {
+ if (ps.getInstalled(userId)) {
+ continue;
+ }
- if (ps.getInstantApp(userId)) {
- // Add a record for an uninstalled instant app
- addUninstalledInstantAppLPw(pkg, userId);
- removeInstantAppLPw(userId, ps.getAppId());
- } else {
- // Deleting an app prunes all instant state such as cookie
- deleteDir(getInstantApplicationDir(pkg.getPackageName(), userId));
- mCookiePersistence.cancelPendingPersistLPw(pkg, userId);
- removeAppLPw(userId, ps.getAppId());
+ if (ps.getInstantApp(userId)) {
+ // Add a record for an uninstalled instant app
+ addUninstalledInstantAppLPw(ps, userId);
+ removeInstantAppLPw(userId, ps.getAppId());
+ } else {
+ // Deleting an app prunes all instant state such as cookie
+ deleteDir(getInstantApplicationDir(pkg.getPackageName(), userId));
+ mCookiePersistence.cancelPendingPersistLPw(pkg, userId);
+ removeAppLPw(userId, ps.getAppId());
+ }
}
}
}
- @GuardedBy("mService.mLock")
- public void onUserRemovedLPw(int userId) {
- mUninstalledInstantApps.remove(userId);
- mInstalledInstantAppUids.remove(userId);
- mInstantGrants.remove(userId);
- deleteDir(getInstantApplicationsDir(userId));
+ public void onUserRemoved(int userId) {
+ synchronized (mLock) {
+ mUninstalledInstantApps.remove(userId);
+ mInstalledInstantAppUids.remove(userId);
+ mInstantGrants.remove(userId);
+ deleteDir(getInstantApplicationsDir(userId));
+ }
}
public boolean isInstantAccessGranted(@UserIdInt int userId, int targetAppId,
int instantAppId) {
- final WatchedSparseArray<WatchedSparseBooleanArray> targetAppList =
- mInstantGrants.get(userId);
- if (targetAppList == null) {
- return false;
- }
- final WatchedSparseBooleanArray instantGrantList = targetAppList.get(targetAppId);
- if (instantGrantList == null) {
- return false;
+ synchronized (mLock) {
+ final WatchedSparseArray<WatchedSparseBooleanArray> targetAppList =
+ mInstantGrants.get(userId);
+ if (targetAppList == null) {
+ return false;
+ }
+ final WatchedSparseBooleanArray instantGrantList = targetAppList.get(targetAppId);
+ if (instantGrantList == null) {
+ return false;
+ }
+ return instantGrantList.get(instantAppId);
}
- return instantGrantList.get(instantAppId);
}
/**
@@ -503,51 +505,54 @@ class InstantAppRegistry implements Watchable, Snappable {
* to the recipient
* @return {@code true} if access is granted.
*/
- @GuardedBy("mService.mLock")
- public boolean grantInstantAccessLPw(@UserIdInt int userId, @Nullable Intent intent,
+ public boolean grantInstantAccess(@UserIdInt int userId, @Nullable Intent intent,
int recipientUid, int instantAppId) {
- if (mInstalledInstantAppUids == null) {
- return false; // no instant apps installed; no need to grant
- }
- WatchedSparseBooleanArray instantAppList = mInstalledInstantAppUids.get(userId);
- if (instantAppList == null || !instantAppList.get(instantAppId)) {
- return false; // instant app id isn't installed; no need to grant
- }
- if (instantAppList.get(recipientUid)) {
- return false; // target app id is an instant app; no need to grant
- }
- if (intent != null && Intent.ACTION_VIEW.equals(intent.getAction())) {
- final Set<String> categories = intent.getCategories();
- if (categories != null && categories.contains(Intent.CATEGORY_BROWSABLE)) {
- return false; // launched via VIEW/BROWSABLE intent; no need to grant
+ synchronized (mLock) {
+ if (mInstalledInstantAppUids == null) {
+ return false; // no instant apps installed; no need to grant
}
+ WatchedSparseBooleanArray instantAppList = mInstalledInstantAppUids.get(userId);
+ if (instantAppList == null || !instantAppList.get(instantAppId)) {
+ return false; // instant app id isn't installed; no need to grant
+ }
+ if (instantAppList.get(recipientUid)) {
+ return false; // target app id is an instant app; no need to grant
+ }
+ if (intent != null && Intent.ACTION_VIEW.equals(intent.getAction())) {
+ final Set<String> categories = intent.getCategories();
+ if (categories != null && categories.contains(Intent.CATEGORY_BROWSABLE)) {
+ return false; // launched via VIEW/BROWSABLE intent; no need to grant
+ }
+ }
+ WatchedSparseArray<WatchedSparseBooleanArray> targetAppList = mInstantGrants.get(
+ userId);
+ if (targetAppList == null) {
+ targetAppList = new WatchedSparseArray<>();
+ mInstantGrants.put(userId, targetAppList);
+ }
+ WatchedSparseBooleanArray instantGrantList = targetAppList.get(recipientUid);
+ if (instantGrantList == null) {
+ instantGrantList = new WatchedSparseBooleanArray();
+ targetAppList.put(recipientUid, instantGrantList);
+ }
+ instantGrantList.put(instantAppId, true /*granted*/);
+ return true;
}
- WatchedSparseArray<WatchedSparseBooleanArray> targetAppList = mInstantGrants.get(userId);
- if (targetAppList == null) {
- targetAppList = new WatchedSparseArray<>();
- mInstantGrants.put(userId, targetAppList);
- }
- WatchedSparseBooleanArray instantGrantList = targetAppList.get(recipientUid);
- if (instantGrantList == null) {
- instantGrantList = new WatchedSparseBooleanArray();
- targetAppList.put(recipientUid, instantGrantList);
- }
- instantGrantList.put(instantAppId, true /*granted*/);
- return true;
}
- @GuardedBy("mService.mLock")
- public void addInstantAppLPw(@UserIdInt int userId, int instantAppId) {
- WatchedSparseBooleanArray instantAppList = mInstalledInstantAppUids.get(userId);
- if (instantAppList == null) {
- instantAppList = new WatchedSparseBooleanArray();
- mInstalledInstantAppUids.put(userId, instantAppList);
+ public void addInstantApp(@UserIdInt int userId, int instantAppId) {
+ synchronized (mLock) {
+ WatchedSparseBooleanArray instantAppList = mInstalledInstantAppUids.get(userId);
+ if (instantAppList == null) {
+ instantAppList = new WatchedSparseBooleanArray();
+ mInstalledInstantAppUids.put(userId, instantAppList);
+ }
+ instantAppList.put(instantAppId, true /*installed*/);
}
- instantAppList.put(instantAppId, true /*installed*/);
onChanged();
}
- @GuardedBy("mService.mLock")
+ @GuardedBy("mLock")
private void removeInstantAppLPw(@UserIdInt int userId, int instantAppId) {
// remove from the installed list
if (mInstalledInstantAppUids == null) {
@@ -578,7 +583,7 @@ class InstantAppRegistry implements Watchable, Snappable {
}
}
- @GuardedBy("mService.mLock")
+ @GuardedBy("mLock")
private void removeAppLPw(@UserIdInt int userId, int targetAppId) {
// remove from the installed list
if (mInstantGrants == null) {
@@ -593,11 +598,11 @@ class InstantAppRegistry implements Watchable, Snappable {
onChanged();
}
- @GuardedBy("mService.mLock")
- private void addUninstalledInstantAppLPw(@NonNull AndroidPackage pkg,
+ @GuardedBy("mLock")
+ private void addUninstalledInstantAppLPw(@NonNull PackageStateInternal packageState,
@UserIdInt int userId) {
InstantAppInfo uninstalledApp = createInstantAppInfoForPackage(
- pkg, userId, false);
+ packageState, userId, false);
if (uninstalledApp == null) {
return;
}
@@ -612,7 +617,7 @@ class InstantAppRegistry implements Watchable, Snappable {
uninstalledAppStates.add(uninstalledAppState);
writeUninstalledInstantAppMetadata(uninstalledApp, userId);
- writeInstantApplicationIconLPw(pkg, userId);
+ writeInstantApplicationIconLPw(packageState.getPkg(), userId);
}
private void writeInstantApplicationIconLPw(@NonNull AndroidPackage pkg,
@@ -624,7 +629,7 @@ class InstantAppRegistry implements Watchable, Snappable {
// TODO(b/135203078): Remove toAppInfo call? Requires significant additions/changes to PM
Drawable icon = AndroidPackageUtils.generateAppInfoWithoutState(pkg)
- .loadIcon(mService.mContext.getPackageManager());
+ .loadIcon(mContext.getPackageManager());
final Bitmap bitmap;
if (icon instanceof BitmapDrawable) {
@@ -647,30 +652,30 @@ class InstantAppRegistry implements Watchable, Snappable {
}
}
- @GuardedBy("mService.mLock")
- boolean hasInstantApplicationMetadataLPr(String packageName, int userId) {
- return hasUninstalledInstantAppStateLPr(packageName, userId)
- || hasInstantAppMetadataLPr(packageName, userId);
+ boolean hasInstantApplicationMetadata(String packageName, int userId) {
+ return hasUninstalledInstantAppState(packageName, userId)
+ || hasInstantAppMetadata(packageName, userId);
}
- @GuardedBy("mService.mLock")
- public void deleteInstantApplicationMetadataLPw(@NonNull String packageName,
+ public void deleteInstantApplicationMetadata(@NonNull String packageName,
@UserIdInt int userId) {
- removeUninstalledInstantAppStateLPw((UninstalledInstantAppState state) ->
- state.mInstantAppInfo.getPackageName().equals(packageName),
- userId);
-
- File instantAppDir = getInstantApplicationDir(packageName, userId);
- new File(instantAppDir, INSTANT_APP_METADATA_FILE).delete();
- new File(instantAppDir, INSTANT_APP_ICON_FILE).delete();
- new File(instantAppDir, INSTANT_APP_ANDROID_ID_FILE).delete();
- File cookie = peekInstantCookieFile(packageName, userId);
- if (cookie != null) {
- cookie.delete();
+ synchronized (mLock) {
+ removeUninstalledInstantAppStateLPw((UninstalledInstantAppState state) ->
+ state.mInstantAppInfo.getPackageName().equals(packageName),
+ userId);
+
+ File instantAppDir = getInstantApplicationDir(packageName, userId);
+ new File(instantAppDir, INSTANT_APP_METADATA_FILE).delete();
+ new File(instantAppDir, INSTANT_APP_ICON_FILE).delete();
+ new File(instantAppDir, INSTANT_APP_ANDROID_ID_FILE).delete();
+ File cookie = peekInstantCookieFile(packageName, userId);
+ if (cookie != null) {
+ cookie.delete();
+ }
}
}
- @GuardedBy("mService.mLock")
+ @GuardedBy("mLock")
private void removeUninstalledInstantAppStateLPw(
@NonNull Predicate<UninstalledInstantAppState> criteria, @UserIdInt int userId) {
if (mUninstalledInstantApps == null) {
@@ -696,27 +701,28 @@ class InstantAppRegistry implements Watchable, Snappable {
}
}
- @GuardedBy("mService.mLock")
- private boolean hasUninstalledInstantAppStateLPr(String packageName, @UserIdInt int userId) {
- if (mUninstalledInstantApps == null) {
- return false;
- }
- final List<UninstalledInstantAppState> uninstalledAppStates =
- mUninstalledInstantApps.get(userId);
- if (uninstalledAppStates == null) {
- return false;
- }
- final int appCount = uninstalledAppStates.size();
- for (int i = 0; i < appCount; i++) {
- final UninstalledInstantAppState uninstalledAppState = uninstalledAppStates.get(i);
- if (packageName.equals(uninstalledAppState.mInstantAppInfo.getPackageName())) {
- return true;
+ private boolean hasUninstalledInstantAppState(String packageName, @UserIdInt int userId) {
+ synchronized (mLock) {
+ if (mUninstalledInstantApps == null) {
+ return false;
+ }
+ final List<UninstalledInstantAppState> uninstalledAppStates =
+ mUninstalledInstantApps.get(userId);
+ if (uninstalledAppStates == null) {
+ return false;
}
+ final int appCount = uninstalledAppStates.size();
+ for (int i = 0; i < appCount; i++) {
+ final UninstalledInstantAppState uninstalledAppState = uninstalledAppStates.get(i);
+ if (packageName.equals(uninstalledAppState.mInstantAppInfo.getPackageName())) {
+ return true;
+ }
+ }
+ return false;
}
- return false;
}
- private boolean hasInstantAppMetadataLPr(String packageName, @UserIdInt int userId) {
+ private boolean hasInstantAppMetadata(String packageName, @UserIdInt int userId) {
final File instantAppDir = getInstantApplicationDir(packageName, userId);
return new File(instantAppDir, INSTANT_APP_METADATA_FILE).exists()
|| new File(instantAppDir, INSTANT_APP_ICON_FILE).exists()
@@ -724,37 +730,41 @@ class InstantAppRegistry implements Watchable, Snappable {
|| peekInstantCookieFile(packageName, userId) != null;
}
- void pruneInstantApps() {
+ void pruneInstantApps(@NonNull Computer computer) {
final long maxInstalledCacheDuration = Settings.Global.getLong(
- mService.mContext.getContentResolver(),
+ mContext.getContentResolver(),
Settings.Global.INSTALLED_INSTANT_APP_MAX_CACHE_PERIOD,
DEFAULT_INSTALLED_INSTANT_APP_MAX_CACHE_PERIOD);
final long maxUninstalledCacheDuration = Settings.Global.getLong(
- mService.mContext.getContentResolver(),
+ mContext.getContentResolver(),
Settings.Global.UNINSTALLED_INSTANT_APP_MAX_CACHE_PERIOD,
DEFAULT_UNINSTALLED_INSTANT_APP_MAX_CACHE_PERIOD);
try {
- pruneInstantApps(Long.MAX_VALUE,
+ pruneInstantApps(computer, Long.MAX_VALUE,
maxInstalledCacheDuration, maxUninstalledCacheDuration);
} catch (IOException e) {
Slog.e(LOG_TAG, "Error pruning installed and uninstalled instant apps", e);
}
}
- boolean pruneInstalledInstantApps(long neededSpace, long maxInstalledCacheDuration) {
+ boolean pruneInstalledInstantApps(@NonNull Computer computer, long neededSpace,
+ long maxInstalledCacheDuration) {
try {
- return pruneInstantApps(neededSpace, maxInstalledCacheDuration, Long.MAX_VALUE);
+ return pruneInstantApps(computer, neededSpace, maxInstalledCacheDuration,
+ Long.MAX_VALUE);
} catch (IOException e) {
Slog.e(LOG_TAG, "Error pruning installed instant apps", e);
return false;
}
}
- boolean pruneUninstalledInstantApps(long neededSpace, long maxUninstalledCacheDuration) {
+ boolean pruneUninstalledInstantApps(@NonNull Computer computer, long neededSpace,
+ long maxUninstalledCacheDuration) {
try {
- return pruneInstantApps(neededSpace, Long.MAX_VALUE, maxUninstalledCacheDuration);
+ return pruneInstantApps(computer, neededSpace, Long.MAX_VALUE,
+ maxUninstalledCacheDuration);
} catch (IOException e) {
Slog.e(LOG_TAG, "Error pruning uninstalled instant apps", e);
return false;
@@ -774,9 +784,9 @@ class InstantAppRegistry implements Watchable, Snappable {
*
* @throws IOException
*/
- private boolean pruneInstantApps(long neededSpace, long maxInstalledCacheDuration,
- long maxUninstalledCacheDuration) throws IOException {
- final StorageManager storage = mService.mContext.getSystemService(StorageManager.class);
+ private boolean pruneInstantApps(@NonNull Computer computer, long neededSpace,
+ long maxInstalledCacheDuration, long maxUninstalledCacheDuration) throws IOException {
+ final StorageManager storage = mContext.getSystemService(StorageManager.class);
final File file = storage.findPathForUuid(StorageManager.UUID_PRIVATE_INTERNAL);
if (file.getUsableSpace() >= neededSpace) {
@@ -789,105 +799,105 @@ class InstantAppRegistry implements Watchable, Snappable {
final long now = System.currentTimeMillis();
// Prune first installed instant apps
- synchronized (mService.mLock) {
- allUsers = mService.mUserManager.getUserIds();
-
- final int packageCount = mService.mPackages.size();
- for (int i = 0; i < packageCount; i++) {
- final AndroidPackage pkg = mService.mPackages.valueAt(i);
- final PackageStateInternal ps =
- mPmInternal.getPackageStateInternal(pkg.getPackageName());
- if (ps == null) {
- continue;
- }
+ allUsers = mUserManager.getUserIds();
+
+ final ArrayMap<String, ? extends PackageStateInternal> packageStates =
+ computer.getPackageStates();
+ final int packageStateCount = packageStates.size();
+ for (int i = 0; i < packageStateCount; i++) {
+ final PackageStateInternal ps = packageStates.valueAt(i);
+ final AndroidPackage pkg = ps == null ? null : ps.getPkg();
+ if (pkg == null) {
+ continue;
+ }
- if (now - ps.getTransientState().getLatestPackageUseTimeInMills()
- < maxInstalledCacheDuration) {
- continue;
- }
+ if (now - ps.getTransientState().getLatestPackageUseTimeInMills()
+ < maxInstalledCacheDuration) {
+ continue;
+ }
- boolean installedOnlyAsInstantApp = false;
- for (int userId : allUsers) {
- final PackageUserStateInternal userState = ps.getUserStateOrDefault(userId);
- if (userState.isInstalled()) {
- if (userState.isInstantApp()) {
- installedOnlyAsInstantApp = true;
- } else {
- installedOnlyAsInstantApp = false;
- break;
- }
+ boolean installedOnlyAsInstantApp = false;
+ for (int userId : allUsers) {
+ final PackageUserStateInternal userState = ps.getUserStateOrDefault(userId);
+ if (userState.isInstalled()) {
+ if (userState.isInstantApp()) {
+ installedOnlyAsInstantApp = true;
+ } else {
+ installedOnlyAsInstantApp = false;
+ break;
}
}
- if (installedOnlyAsInstantApp) {
- if (packagesToDelete == null) {
- packagesToDelete = new ArrayList<>();
- }
- packagesToDelete.add(pkg.getPackageName());
+ }
+ if (installedOnlyAsInstantApp) {
+ if (packagesToDelete == null) {
+ packagesToDelete = new ArrayList<>();
}
+ packagesToDelete.add(pkg.getPackageName());
}
+ }
- if (packagesToDelete != null) {
- packagesToDelete.sort((String lhs, String rhs) -> {
- final AndroidPackage lhsPkg = mService.mPackages.get(lhs);
- final AndroidPackage rhsPkg = mService.mPackages.get(rhs);
- if (lhsPkg == null && rhsPkg == null) {
+ if (packagesToDelete != null) {
+ packagesToDelete.sort((String lhs, String rhs) -> {
+ final PackageStateInternal lhsPkgState = packageStates.get(lhs);
+ final PackageStateInternal rhsPkgState = packageStates.get(rhs);
+ final AndroidPackage lhsPkg = lhsPkgState == null ? null : lhsPkgState.getPkg();
+ final AndroidPackage rhsPkg = rhsPkgState == null ? null : rhsPkgState.getPkg();
+ if (lhsPkg == null && rhsPkg == null) {
+ return 0;
+ } else if (lhsPkg == null) {
+ return -1;
+ } else if (rhsPkg == null) {
+ return 1;
+ } else {
+ final PackageStateInternal lhsPs =
+ packageStates.get(lhsPkg.getPackageName());
+ if (lhsPs == null) {
+ return 0;
+ }
+
+ final PackageStateInternal rhsPs =
+ packageStates.get(rhsPkg.getPackageName());
+ if (rhsPs == null) {
return 0;
- } else if (lhsPkg == null) {
+ }
+
+ if (lhsPs.getTransientState().getLatestPackageUseTimeInMills() >
+ rhsPs.getTransientState().getLatestPackageUseTimeInMills()) {
+ return 1;
+ } else if (lhsPs.getTransientState().getLatestPackageUseTimeInMills() <
+ rhsPs.getTransientState().getLatestPackageUseTimeInMills()) {
return -1;
- } else if (rhsPkg == null) {
+ } else if (
+ PackageStateUtils.getEarliestFirstInstallTime(lhsPs.getUserStates())
+ > PackageStateUtils.getEarliestFirstInstallTime(
+ rhsPs.getUserStates())) {
return 1;
} else {
- final PackageStateInternal lhsPs =
- mPmInternal.getPackageStateInternal(lhsPkg.getPackageName());
- if (lhsPs == null) {
- return 0;
- }
-
- final PackageStateInternal rhsPs =
- mPmInternal.getPackageStateInternal(rhsPkg.getPackageName());
- if (rhsPs == null) {
- return 0;
- }
-
- if (lhsPs.getTransientState().getLatestPackageUseTimeInMills() >
- rhsPs.getTransientState().getLatestPackageUseTimeInMills()) {
- return 1;
- } else if (lhsPs.getTransientState().getLatestPackageUseTimeInMills() <
- rhsPs.getTransientState().getLatestPackageUseTimeInMills()) {
- return -1;
- } else if (
- PackageStateUtils.getEarliestFirstInstallTime(lhsPs.getUserStates())
- > PackageStateUtils.getEarliestFirstInstallTime(
- rhsPs.getUserStates())) {
- return 1;
- } else {
- return -1;
- }
+ return -1;
}
- });
- }
+ }
+ });
}
- if (packagesToDelete != null) {
- final int packageCount = packagesToDelete.size();
- final DeletePackageHelper deletePackageHelper = new DeletePackageHelper(mService);
- for (int i = 0; i < packageCount; i++) {
- final String packageToDelete = packagesToDelete.get(i);
- if (deletePackageHelper.deletePackageX(packageToDelete,
- PackageManager.VERSION_CODE_HIGHEST,
- UserHandle.USER_SYSTEM, PackageManager.DELETE_ALL_USERS,
- true /*removedBySystem*/) == PackageManager.DELETE_SUCCEEDED) {
- if (file.getUsableSpace() >= neededSpace) {
- return true;
+ synchronized (mLock) {
+ if (packagesToDelete != null) {
+ final int packageCount = packagesToDelete.size();
+ for (int i = 0; i < packageCount; i++) {
+ final String packageToDelete = packagesToDelete.get(i);
+ if (mDeletePackageHelper.deletePackageX(packageToDelete,
+ PackageManager.VERSION_CODE_HIGHEST,
+ UserHandle.USER_SYSTEM, PackageManager.DELETE_ALL_USERS,
+ true /*removedBySystem*/) == PackageManager.DELETE_SUCCEEDED) {
+ if (file.getUsableSpace() >= neededSpace) {
+ return true;
+ }
}
}
}
- }
- // Prune uninstalled instant apps
- synchronized (mService.mLock) {
+ // Prune uninstalled instant apps
// TODO: Track last used time for uninstalled instant apps for better pruning
- for (int userId : UserManagerService.getInstance().getUserIds()) {
+ for (int userId : mUserManager.getUserIds()) {
// Prune in-memory state
removeUninstalledInstantAppStateLPw((UninstalledInstantAppState state) -> {
final long elapsedCachingMillis = System.currentTimeMillis() - state.mTimestamp;
@@ -928,21 +938,19 @@ class InstantAppRegistry implements Watchable, Snappable {
return false;
}
- @GuardedBy("mService.mLock")
- private @Nullable List<InstantAppInfo> getInstalledInstantApplicationsLPr(
- @UserIdInt int userId) {
+ private @Nullable List<InstantAppInfo> getInstalledInstantApplications(
+ @NonNull Computer computer, @UserIdInt int userId) {
List<InstantAppInfo> result = null;
- final int packageCount = mService.mPackages.size();
+ final ArrayMap<String, ? extends PackageStateInternal> packageStates =
+ computer.getPackageStates();
+ final int packageCount = packageStates.size();
for (int i = 0; i < packageCount; i++) {
- final AndroidPackage pkg = mService.mPackages.valueAt(i);
- final PackageStateInternal ps =
- mPmInternal.getPackageStateInternal(pkg.getPackageName());
+ final PackageStateInternal ps = packageStates.valueAt(i);
if (ps == null || !ps.getUserStateOrDefault(userId).isInstantApp()) {
continue;
}
- final InstantAppInfo info = createInstantAppInfoForPackage(
- pkg, userId, true);
+ final InstantAppInfo info = createInstantAppInfoForPackage(ps, userId, true);
if (info == null) {
continue;
}
@@ -956,14 +964,10 @@ class InstantAppRegistry implements Watchable, Snappable {
}
private @NonNull
- InstantAppInfo createInstantAppInfoForPackage(
- @NonNull AndroidPackage pkg, @UserIdInt int userId,
- boolean addApplicationInfo) {
- PackageStateInternal ps = mPmInternal.getPackageStateInternal(pkg.getPackageName());
- if (ps == null) {
- return null;
- }
- if (!ps.getUserStateOrDefault(userId).isInstalled()) {
+ InstantAppInfo createInstantAppInfoForPackage(@NonNull PackageStateInternal ps,
+ @UserIdInt int userId, boolean addApplicationInfo) {
+ AndroidPackage pkg = ps.getPkg();
+ if (pkg == null || !ps.getUserStateOrDefault(userId).isInstalled()) {
return null;
}
@@ -982,17 +986,18 @@ class InstantAppRegistry implements Watchable, Snappable {
if (addApplicationInfo) {
return new InstantAppInfo(appInfo, requestedPermissions, grantedPermissions);
} else {
+ // TODO: PMS lock re-entry
return new InstantAppInfo(appInfo.packageName,
- appInfo.loadLabel(mService.mContext.getPackageManager()),
+ appInfo.loadLabel(mContext.getPackageManager()),
requestedPermissions, grantedPermissions);
}
}
- @GuardedBy("mService.mLock")
- private @Nullable List<InstantAppInfo> getUninstalledInstantApplicationsLPr(
+ @Nullable
+ private List<InstantAppInfo> getUninstalledInstantApplications(@NonNull Computer computer,
@UserIdInt int userId) {
List<UninstalledInstantAppState> uninstalledAppStates =
- getUninstalledInstantAppStatesLPr(userId);
+ getUninstalledInstantAppStates(userId);
if (uninstalledAppStates == null || uninstalledAppStates.isEmpty()) {
return null;
}
@@ -1009,6 +1014,7 @@ class InstantAppRegistry implements Watchable, Snappable {
return uninstalledApps;
}
+ @SuppressLint("MissingPermission")
private void propagateInstantAppPermissionsIfNeeded(@NonNull AndroidPackage pkg,
@UserIdInt int userId) {
InstantAppInfo appInfo = peekOrParseUninstalledInstantAppInfo(
@@ -1025,8 +1031,9 @@ class InstantAppRegistry implements Watchable, Snappable {
final boolean propagatePermission = canPropagatePermission(grantedPermission);
if (propagatePermission && pkg.getRequestedPermissions().contains(
grantedPermission)) {
- mService.grantRuntimePermission(pkg.getPackageName(), grantedPermission,
- userId);
+ mContext.getSystemService(PermissionManager.class)
+ .grantRuntimePermission(pkg.getPackageName(), grantedPermission,
+ UserHandle.of(userId));
}
}
} finally {
@@ -1035,8 +1042,8 @@ class InstantAppRegistry implements Watchable, Snappable {
}
private boolean canPropagatePermission(@NonNull String permissionName) {
- final PermissionManager permissionManager = mService.mContext.getSystemService(
- PermissionManager.class);
+ final PermissionManager permissionManager =
+ mContext.getSystemService(PermissionManager.class);
final PermissionInfo permissionInfo = permissionManager.getPermissionInfo(permissionName,
0);
return permissionInfo != null
@@ -1050,16 +1057,19 @@ class InstantAppRegistry implements Watchable, Snappable {
private @NonNull
InstantAppInfo peekOrParseUninstalledInstantAppInfo(
@NonNull String packageName, @UserIdInt int userId) {
- if (mUninstalledInstantApps != null) {
- List<UninstalledInstantAppState> uninstalledAppStates =
- mUninstalledInstantApps.get(userId);
- if (uninstalledAppStates != null) {
- final int appCount = uninstalledAppStates.size();
- for (int i = 0; i < appCount; i++) {
- UninstalledInstantAppState uninstalledAppState = uninstalledAppStates.get(i);
- if (uninstalledAppState.mInstantAppInfo
- .getPackageName().equals(packageName)) {
- return uninstalledAppState.mInstantAppInfo;
+ synchronized (mLock) {
+ if (mUninstalledInstantApps != null) {
+ List<UninstalledInstantAppState> uninstalledAppStates =
+ mUninstalledInstantApps.get(userId);
+ if (uninstalledAppStates != null) {
+ final int appCount = uninstalledAppStates.size();
+ for (int i = 0; i < appCount; i++) {
+ UninstalledInstantAppState uninstalledAppState = uninstalledAppStates.get(
+ i);
+ if (uninstalledAppState.mInstantAppInfo
+ .getPackageName().equals(packageName)) {
+ return uninstalledAppState.mInstantAppInfo;
+ }
}
}
}
@@ -1075,14 +1085,15 @@ class InstantAppRegistry implements Watchable, Snappable {
return uninstalledAppState.mInstantAppInfo;
}
- @GuardedBy("mService.mLock")
- private @Nullable List<UninstalledInstantAppState> getUninstalledInstantAppStatesLPr(
- @UserIdInt int userId) {
+ @Nullable
+ private List<UninstalledInstantAppState> getUninstalledInstantAppStates(@UserIdInt int userId) {
List<UninstalledInstantAppState> uninstalledAppStates = null;
- if (mUninstalledInstantApps != null) {
- uninstalledAppStates = mUninstalledInstantApps.get(userId);
- if (uninstalledAppStates != null) {
- return uninstalledAppStates;
+ synchronized (mLock) {
+ if (mUninstalledInstantApps != null) {
+ uninstalledAppStates = mUninstalledInstantApps.get(userId);
+ if (uninstalledAppStates != null) {
+ return uninstalledAppStates;
+ }
}
}
@@ -1109,7 +1120,9 @@ class InstantAppRegistry implements Watchable, Snappable {
}
}
- mUninstalledInstantApps.put(userId, uninstalledAppStates);
+ synchronized (mLock) {
+ mUninstalledInstantApps.put(userId, uninstalledAppStates);
+ }
return uninstalledAppStates;
}
@@ -1246,7 +1259,7 @@ class InstantAppRegistry implements Watchable, Snappable {
serializer.startTag(null, TAG_PACKAGE);
serializer.attribute(null, ATTR_LABEL, instantApp.loadLabel(
- mService.mContext.getPackageManager()).toString());
+ mContext.getPackageManager()).toString());
serializer.startTag(null, TAG_PERMISSIONS);
for (String permission : instantApp.getRequestedPermissions()) {
diff --git a/services/core/java/com/android/server/pm/KeySetManagerService.java b/services/core/java/com/android/server/pm/KeySetManagerService.java
index db346dabaa2b..afca35081659 100644
--- a/services/core/java/com/android/server/pm/KeySetManagerService.java
+++ b/services/core/java/com/android/server/pm/KeySetManagerService.java
@@ -31,6 +31,7 @@ import android.util.TypedXmlPullParser;
import android.util.TypedXmlSerializer;
import com.android.server.pm.parsing.pkg.AndroidPackage;
+import com.android.server.pm.pkg.PackageStateInternal;
import com.android.server.utils.WatchedArrayMap;
import org.xmlpull.v1.XmlPullParser;
@@ -353,9 +354,9 @@ public class KeySetManagerService {
return mKeySets.get(id) != null;
}
- public boolean shouldCheckUpgradeKeySetLocked(PackageSetting oldPs, int scanFlags) {
+ public boolean shouldCheckUpgradeKeySetLocked(PackageStateInternal oldPs, int scanFlags) {
// Can't rotate keys during boot or if sharedUser.
- if (oldPs == null || (scanFlags&SCAN_INITIAL) != 0 || oldPs.isSharedUser()
+ if (oldPs == null || (scanFlags&SCAN_INITIAL) != 0 || (oldPs.getSharedUser() != null)
|| !oldPs.getKeySetData().isUsingUpgradeKeySets()) {
return false;
}
@@ -374,7 +375,7 @@ public class KeySetManagerService {
return true;
}
- public boolean checkUpgradeKeySetLocked(PackageSetting oldPS, AndroidPackage pkg) {
+ public boolean checkUpgradeKeySetLocked(PackageStateInternal oldPS, AndroidPackage pkg) {
// Upgrade keysets are being used. Determine if new package has a superset of the
// required keys.
long[] upgradeKeySets = oldPS.getKeySetData().getUpgradeKeySets();
diff --git a/services/core/java/com/android/server/pm/OtaDexoptService.java b/services/core/java/com/android/server/pm/OtaDexoptService.java
index c125fe10e899..bd0091480caf 100644
--- a/services/core/java/com/android/server/pm/OtaDexoptService.java
+++ b/services/core/java/com/android/server/pm/OtaDexoptService.java
@@ -30,6 +30,7 @@ import android.os.ResultReceiver;
import android.os.ServiceManager;
import android.os.ShellCallback;
import android.os.storage.StorageManager;
+import android.util.ArrayMap;
import android.util.Log;
import android.util.Slog;
@@ -376,12 +377,13 @@ public class OtaDexoptService extends IOtaDexopt.Stub {
}
// Make a copy of all packages and look into each package.
- final PackageManagerInternal pmInt = LocalServices.getService(PackageManagerInternal.class);
- final ArrayList<AndroidPackage> pkgs = new ArrayList<>();
- pmInt.forEachPackage(pkgs::add);
+ final ArrayMap<String, ? extends PackageStateInternal> packageStates =
+ LocalServices.getService(PackageManagerInternal.class).getPackageStates();
int packagePaths = 0;
int pathsSuccessful = 0;
- for (AndroidPackage pkg : pkgs) {
+ for (int index = 0; index < packageStates.size(); index++) {
+ final PackageStateInternal packageState = packageStates.valueAt(index);
+ final AndroidPackage pkg = packageState.getPkg();
if (pkg == null) {
continue;
}
@@ -404,10 +406,9 @@ public class OtaDexoptService extends IOtaDexopt.Stub {
continue;
}
- PackageStateInternal pkgSetting = pmInt.getPackageStateInternal(pkg.getPackageName());
final String[] instructionSets = getAppDexInstructionSets(
- AndroidPackageUtils.getPrimaryCpuAbi(pkg, pkgSetting),
- AndroidPackageUtils.getSecondaryCpuAbi(pkg, pkgSetting));
+ AndroidPackageUtils.getPrimaryCpuAbi(pkg, packageState),
+ AndroidPackageUtils.getSecondaryCpuAbi(pkg, packageState));
final List<String> paths =
AndroidPackageUtils.getAllCodePathsExcludingResourceOnly(pkg);
final String[] dexCodeInstructionSets = getDexCodeInstructionSets(instructionSets);
diff --git a/services/core/java/com/android/server/pm/PackageHandler.java b/services/core/java/com/android/server/pm/PackageHandler.java
index d1ea41ae5613..ec7194059c4f 100644
--- a/services/core/java/com/android/server/pm/PackageHandler.java
+++ b/services/core/java/com/android/server/pm/PackageHandler.java
@@ -34,9 +34,7 @@ import static com.android.server.pm.PackageManagerService.PACKAGE_VERIFIED;
import static com.android.server.pm.PackageManagerService.POST_INSTALL;
import static com.android.server.pm.PackageManagerService.PRUNE_UNUSED_STATIC_SHARED_LIBRARIES;
import static com.android.server.pm.PackageManagerService.SEND_PENDING_BROADCAST;
-import static com.android.server.pm.PackageManagerService.SNAPSHOT_UNCORK;
import static com.android.server.pm.PackageManagerService.TAG;
-import static com.android.server.pm.PackageManagerService.TRACE_SNAPSHOTS;
import static com.android.server.pm.PackageManagerService.WRITE_PACKAGE_LIST;
import static com.android.server.pm.PackageManagerService.WRITE_PACKAGE_RESTRICTIONS;
import static com.android.server.pm.PackageManagerService.WRITE_SETTINGS;
@@ -379,13 +377,6 @@ final class PackageHandler extends Handler {
mPm.mDomainVerificationManager.runMessage(messageCode, object);
break;
}
- case SNAPSHOT_UNCORK: {
- int corking = mPm.sSnapshotCorked.decrementAndGet();
- if (TRACE_SNAPSHOTS && corking == 0) {
- Log.e(TAG, "snapshot: corking goes to zero in message handler");
- }
- break;
- }
case PRUNE_UNUSED_STATIC_SHARED_LIBRARIES: {
try {
mPm.mInjector.getSharedLibrariesImpl().pruneUnusedStaticSharedLibraries(
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index ccc375ff85f2..8465248a6e46 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -84,7 +84,7 @@ import android.util.Xml;
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
-import com.android.internal.content.PackageHelper;
+import com.android.internal.content.InstallLocationUtils;
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.internal.notification.SystemNotificationChannels;
import com.android.internal.util.ImageUtils;
@@ -782,7 +782,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
// If caller requested explicit location, validity check it, otherwise
// resolve the best internal or adopted location.
if ((params.installFlags & PackageManager.INSTALL_INTERNAL) != 0) {
- if (!PackageHelper.fitsOnInternal(mContext, params)) {
+ if (!InstallLocationUtils.fitsOnInternal(mContext, params)) {
throw new IOException("No suitable internal storage available");
}
} else if ((params.installFlags & PackageManager.INSTALL_FORCE_VOLUME_UUID) != 0) {
@@ -796,7 +796,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
// requested install flags, delta size, and manifest settings.
final long ident = Binder.clearCallingIdentity();
try {
- params.volumeUuid = PackageHelper.resolveInstallVolume(mContext, params);
+ params.volumeUuid = InstallLocationUtils.resolveInstallVolume(mContext, params);
} finally {
Binder.restoreCallingIdentity(ident);
}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 390dd3fb4fc8..7152783e3d64 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -143,8 +143,8 @@ import android.util.apk.ApkSignatureVerifier;
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.content.InstallLocationUtils;
import com.android.internal.content.NativeLibraryHelper;
-import com.android.internal.content.PackageHelper;
import com.android.internal.messages.nano.SystemMessageProto;
import com.android.internal.os.SomeArgs;
import com.android.internal.security.VerityUtils;
@@ -1537,7 +1537,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
if (stageDir != null && lengthBytes > 0) {
mContext.getSystemService(StorageManager.class).allocateBytes(
targetPfd.getFileDescriptor(), lengthBytes,
- PackageHelper.translateAllocateFlags(params.installFlags));
+ InstallLocationUtils.translateAllocateFlags(params.installFlags));
}
if (offsetBytes > 0) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index e0d404a1a8f2..3549c9ec2e74 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -101,6 +101,7 @@ import android.content.pm.KeySet;
import android.content.pm.ModuleInfo;
import android.content.pm.PackageChangeEvent;
import android.content.pm.PackageInfo;
+import android.content.pm.PackageInfoLite;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.ComponentEnabledSetting;
@@ -110,7 +111,6 @@ import android.content.pm.PackageManager.ModuleInfoFlags;
import android.content.pm.PackageManager.Property;
import android.content.pm.PackageManager.PropertyLocation;
import android.content.pm.PackageManagerInternal;
-import android.content.pm.PackageManagerInternal.PackageListObserver;
import android.content.pm.PackageManagerInternal.PrivateResolveFlags;
import android.content.pm.PackagePartitions;
import android.content.pm.ParceledListSlice;
@@ -130,6 +130,7 @@ import android.content.pm.VerifierDeviceIdentity;
import android.content.pm.VersionedPackage;
import android.content.pm.dex.IArtManager;
import android.content.pm.overlay.OverlayPaths;
+import android.content.pm.parsing.PackageLite;
import android.content.res.Resources;
import android.database.ContentObserver;
import android.graphics.Bitmap;
@@ -190,7 +191,7 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.ResolverActivity;
import com.android.internal.content.F2fsUtils;
-import com.android.internal.content.PackageHelper;
+import com.android.internal.content.InstallLocationUtils;
import com.android.internal.content.om.OverlayConfig;
import com.android.internal.telephony.CarrierAppUtils;
import com.android.internal.util.ArrayUtils;
@@ -232,11 +233,14 @@ import com.android.server.pm.pkg.AndroidPackageApi;
import com.android.server.pm.pkg.PackageState;
import com.android.server.pm.pkg.PackageStateInternal;
import com.android.server.pm.pkg.PackageStateUtils;
+import com.android.server.pm.pkg.PackageUserState;
import com.android.server.pm.pkg.PackageUserStateInternal;
+import com.android.server.pm.pkg.SharedUserApi;
import com.android.server.pm.pkg.component.ParsedInstrumentation;
import com.android.server.pm.pkg.component.ParsedMainComponent;
import com.android.server.pm.pkg.mutate.PackageStateMutator;
import com.android.server.pm.pkg.mutate.PackageStateWrite;
+import com.android.server.pm.pkg.mutate.PackageUserStateWrite;
import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
import com.android.server.pm.verify.domain.DomainVerificationService;
@@ -592,12 +596,12 @@ public class PackageManagerService extends IPackageManager.Stub
// the suffix "Locked". Some methods may use the legacy suffix "LP"
final PackageManagerTracedLock mLock;
+ // Ensures order of overlay updates until data storage can be moved to overlay code
+ private final PackageManagerTracedLock mOverlayPathsLock = new PackageManagerTracedLock();
+
// Lock alias for doing package state mutation
private final PackageManagerTracedLock mPackageStateWriteLock;
- // Lock alias to track syncing a consistent Computer
- private final PackageManagerTracedLock mLiveComputerSyncLock;
-
private final PackageStateMutator mPackageStateMutator = new PackageStateMutator(
this::getPackageSettingForMutation,
this::getDisabledPackageSettingForMutation);
@@ -682,23 +686,11 @@ public class PackageManagerService extends IPackageManager.Stub
@Watched
final InstantAppRegistry mInstantAppRegistry;
- @GuardedBy("mLock")
- int mChangedPackagesSequenceNumber;
- /**
- * List of changed [installed, removed or updated] packages.
- * mapping from user id -> sequence number -> package name
- */
- @GuardedBy("mLock")
- final SparseArray<SparseArray<String>> mChangedPackages = new SparseArray<>();
- /**
- * The sequence number of the last change to a package.
- * mapping from user id -> package name -> sequence number
- */
- @GuardedBy("mLock")
- final SparseArray<Map<String, Integer>> mChangedPackagesSequenceNumbers = new SparseArray<>();
+ @NonNull
+ final ChangedPackagesTracker mChangedPackagesTracker;
- @GuardedBy("mLock")
- final private ArraySet<PackageListObserver> mPackageListObservers = new ArraySet<>();
+ @NonNull
+ private final PackageObserverHelper mPackageObserverHelper = new PackageObserverHelper();
private final ModuleInfoProvider mModuleInfoProvider;
@@ -736,21 +728,11 @@ public class PackageManagerService extends IPackageManager.Stub
ApplicationPackageManager.invalidateGetPackagesForUidCache();
ApplicationPackageManager.disableGetPackagesForUidCache();
ApplicationPackageManager.invalidateHasSystemFeatureCache();
-
- // Avoid invalidation-thrashing by preventing cache invalidations from causing property
- // writes if the cache isn't enabled yet. We re-enable writes later when we're
- // done initializing.
- sSnapshotCorked.incrementAndGet();
PackageManager.corkPackageInfoCache();
}
@Override
public void enablePackageCaches() {
- // Uncork cache invalidations and allow clients to cache package information.
- int corking = sSnapshotCorked.decrementAndGet();
- if (TRACE_SNAPSHOTS && corking == 0) {
- Log.i(TAG, "snapshot: corking returns to 0");
- }
PackageManager.uncorkPackageInfoCache();
}
}
@@ -866,8 +848,10 @@ public class PackageManagerService extends IPackageManager.Stub
@Watched
final ComponentResolver mComponentResolver;
- // List of packages names to keep cached, even if they are uninstalled for all users
- private List<String> mKeepUninstalledPackages;
+ // Set of packages names to keep cached, even if they are uninstalled for all users
+ @GuardedBy("mKeepUninstalledPackages")
+ @NonNull
+ private final ArraySet<String> mKeepUninstalledPackages = new ArraySet<>();
// Cached reference to IDevicePolicyManager.
private IDevicePolicyManager mDevicePolicyManager = null;
@@ -904,8 +888,7 @@ public class PackageManagerService extends IPackageManager.Stub
static final int INTEGRITY_VERIFICATION_COMPLETE = 25;
static final int CHECK_PENDING_INTEGRITY_VERIFICATION = 26;
static final int DOMAIN_VERIFICATION = 27;
- static final int SNAPSHOT_UNCORK = 28;
- static final int PRUNE_UNUSED_STATIC_SHARED_LIBRARIES = 29;
+ static final int PRUNE_UNUSED_STATIC_SHARED_LIBRARIES = 28;
static final int DEFERRED_NO_KILL_POST_DELETE_DELAY_MS = 3 * 1000;
private static final int DEFERRED_NO_KILL_INSTALL_OBSERVER_DELAY_MS = 500;
@@ -1018,7 +1001,8 @@ public class PackageManagerService extends IPackageManager.Stub
isolatedOwners = mIsolatedOwnersSnapshot.snapshot();
packages = mPackagesSnapshot.snapshot();
instrumentation = mInstrumentationSnapshot.snapshot();
- resolveComponentName = mResolveComponentName.clone();
+ resolveComponentName = mResolveComponentName == null
+ ? null : mResolveComponentName.clone();
resolveActivity = new ActivityInfo(mResolveActivity);
instantAppInstallerActivity =
(mInstantAppInstallerActivity == null)
@@ -1075,10 +1059,6 @@ public class PackageManagerService extends IPackageManager.Stub
// should only be set true while holding mLock. However, the attribute id guaranteed
// to be set false only while mLock and mSnapshotLock are both held.
private static final AtomicBoolean sSnapshotInvalid = new AtomicBoolean(true);
- // If true, the snapshot is corked. Do not create a new snapshot but use the live
- // computer. This throttles snapshot creation during periods of churn in Package
- // Manager.
- static final AtomicInteger sSnapshotCorked = new AtomicInteger(0);
static final ThreadLocal<ThreadComputer> sThreadComputer =
ThreadLocal.withInitial(ThreadComputer::new);
@@ -1095,16 +1075,9 @@ public class PackageManagerService extends IPackageManager.Stub
* The snapshot statistics. These are collected to track performance and to identify
* situations in which the snapshots are misbehaving.
*/
+ @Nullable
private final SnapshotStatistics mSnapshotStatistics;
- // The snapshot disable/enable switch. An image with the flag set true uses snapshots
- // and an image with the flag set false does not use snapshots.
- private static final boolean SNAPSHOT_ENABLED = true;
-
- // The per-instance snapshot disable/enable flag. This is generally set to false in
- // test instances and set to SNAPSHOT_ENABLED in operational instances.
- private final boolean mSnapshotEnabled;
-
/**
* Return the live computer.
*/
@@ -1117,17 +1090,10 @@ public class PackageManagerService extends IPackageManager.Stub
* The live computer will be returned if snapshots are disabled.
*/
Computer snapshotComputer() {
- if (!mSnapshotEnabled) {
- return mLiveComputer;
- }
if (Thread.holdsLock(mLock)) {
// If the current thread holds mLock then it may have modified state but not
// yet invalidated the snapshot. Always give the thread the live computer.
return mLiveComputer;
- } else if (sSnapshotCorked.get() > 0) {
- // Snapshots are corked, which means new ones should not be built right now.
- mSnapshotStatistics.corked();
- return mLiveComputer;
}
synchronized (mSnapshotLock) {
// This synchronization block serializes access to the snapshot computer and
@@ -1168,7 +1134,9 @@ public class PackageManagerService extends IPackageManager.Stub
mSnapshotComputer = new ComputerEngine(args);
final long done = SystemClock.currentTimeMicro();
- mSnapshotStatistics.rebuild(now, done, hits);
+ if (mSnapshotStatistics != null) {
+ mSnapshotStatistics.rebuild(now, done, hits);
+ }
}
/**
@@ -1397,32 +1365,41 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
- void scheduleWritePackageRestrictionsLocked(UserHandle user) {
+ void scheduleWritePackageRestrictions(UserHandle user) {
final int userId = user == null ? UserHandle.USER_ALL : user.getIdentifier();
- scheduleWritePackageRestrictionsLocked(userId);
+ scheduleWritePackageRestrictions(userId);
}
- void scheduleWritePackageRestrictionsLocked(int userId) {
+ void scheduleWritePackageRestrictions(int userId) {
invalidatePackageInfoCache();
- final int[] userIds = (userId == UserHandle.USER_ALL)
- ? mUserManager.getUserIds() : new int[]{userId};
- for (int nextUserId : userIds) {
- if (!mUserManager.exists(nextUserId)) return;
-
- mDirtyUsers.add(nextUserId);
- if (!mHandler.hasMessages(WRITE_PACKAGE_RESTRICTIONS)) {
- mHandler.sendEmptyMessageDelayed(WRITE_PACKAGE_RESTRICTIONS, WRITE_SETTINGS_DELAY);
+ if (userId == UserHandle.USER_ALL) {
+ synchronized (mDirtyUsers) {
+ for (int aUserId : mUserManager.getUserIds()) {
+ mDirtyUsers.add(aUserId);
+ }
+ }
+ } else {
+ if (!mUserManager.exists(userId)) {
+ return;
+ }
+ synchronized (mDirtyUsers) {
+ mDirtyUsers.add(userId);
}
}
+ if (!mHandler.hasMessages(WRITE_PACKAGE_RESTRICTIONS)) {
+ mHandler.sendEmptyMessageDelayed(WRITE_PACKAGE_RESTRICTIONS, WRITE_SETTINGS_DELAY);
+ }
}
void writePendingRestrictions() {
synchronized (mLock) {
mHandler.removeMessages(WRITE_PACKAGE_RESTRICTIONS);
- for (int userId : mDirtyUsers) {
- mSettings.writePackageRestrictionsLPr(userId);
+ synchronized (mDirtyUsers) {
+ for (int userId : mDirtyUsers) {
+ mSettings.writePackageRestrictionsLPr(userId);
+ }
+ mDirtyUsers.clear();
}
- mDirtyUsers.clear();
}
}
@@ -1431,7 +1408,9 @@ public class PackageManagerService extends IPackageManager.Stub
mHandler.removeMessages(WRITE_SETTINGS);
mHandler.removeMessages(WRITE_PACKAGE_RESTRICTIONS);
writeSettingsLPrTEMP();
- mDirtyUsers.clear();
+ synchronized (mDirtyUsers) {
+ mDirtyUsers.clear();
+ }
}
}
@@ -1523,25 +1502,19 @@ public class PackageManagerService extends IPackageManager.Stub
PackageManagerService m = new PackageManagerService(injector, onlyCore, factoryTest,
PackagePartitions.FINGERPRINT, Build.IS_ENG, Build.IS_USERDEBUG,
- Build.VERSION.SDK_INT, Build.VERSION.INCREMENTAL, SNAPSHOT_ENABLED);
+ Build.VERSION.SDK_INT, Build.VERSION.INCREMENTAL);
t.traceEnd(); // "create package manager"
final CompatChange.ChangeListener selinuxChangeListener = packageName -> {
synchronized (m.mInstallLock) {
- final AndroidPackage pkg;
- final PackageSetting ps;
- final SharedUserSetting sharedUser;
- final String oldSeInfo;
- synchronized (m.mLock) {
- ps = m.mSettings.getPackageLPr(packageName);
- if (ps == null) {
- Slog.e(TAG, "Failed to find package setting " + packageName);
- return;
- }
- pkg = ps.getPkg();
- sharedUser = ps.getSharedUser();
- oldSeInfo = AndroidPackageUtils.getSeInfo(pkg, ps);
+ final PackageStateInternal packageState = m.getPackageStateInternal(packageName);
+ if (packageState == null) {
+ Slog.e(TAG, "Failed to find package setting " + packageName);
+ return;
}
+ AndroidPackage pkg = packageState.getPkg();
+ SharedUserApi sharedUser = packageState.getSharedUser();
+ String oldSeInfo = AndroidPackageUtils.getSeInfo(pkg, packageState);
if (pkg == null) {
Slog.e(TAG, "Failed to find package " + packageName);
@@ -1553,7 +1526,8 @@ public class PackageManagerService extends IPackageManager.Stub
if (!newSeInfo.equals(oldSeInfo)) {
Slog.i(TAG, "Updating seInfo for package " + packageName + " from: "
+ oldSeInfo + " to: " + newSeInfo);
- ps.getPkgState().setOverrideSeInfo(newSeInfo);
+ m.commitPackageStateMutation(null, packageName,
+ state -> state.setOverrideSeInfo(newSeInfo));
m.mAppDataHelper.prepareAppDataAfterInstallLIF(pkg);
}
}
@@ -1573,31 +1547,51 @@ public class PackageManagerService extends IPackageManager.Stub
/** Install/uninstall system packages for all users based on their user-type, as applicable. */
private void installAllowlistedSystemPackages() {
- synchronized (mLock) {
- final boolean scheduleWrite = mUserManager.installWhitelistedSystemPackages(
- isFirstBoot(), isDeviceUpgrading(), mExistingPackages);
- if (scheduleWrite) {
- scheduleWritePackageRestrictionsLocked(UserHandle.USER_ALL);
- scheduleWriteSettings();
- }
+ if (mUserManager.installWhitelistedSystemPackages(isFirstBoot(), isDeviceUpgrading(),
+ mExistingPackages)) {
+ scheduleWritePackageRestrictions(UserHandle.USER_ALL);
+ scheduleWriteSettings();
}
}
// Link watchables to the class
- private void registerObserver() {
- mPackages.registerObserver(mWatcher);
- mSharedLibraries.registerObserver(mWatcher);
- mInstrumentation.registerObserver(mWatcher);
- mWebInstantAppsDisabled.registerObserver(mWatcher);
- mAppsFilter.registerObserver(mWatcher);
- mInstantAppRegistry.registerObserver(mWatcher);
- mSettings.registerObserver(mWatcher);
- mIsolatedOwners.registerObserver(mWatcher);
- mComponentResolver.registerObserver(mWatcher);
- mFrozenPackages.registerObserver(mWatcher);
- // If neither "build" attribute is true then this may be a mockito test, and verification
- // can fail as a false positive.
- Watchable.verifyWatchedAttributes(this, mWatcher, !(mIsEngBuild || mIsUserDebugBuild));
+ private void registerObservers(boolean verify) {
+ // Null check to handle nullable test parameters
+ if (mPackages != null) {
+ mPackages.registerObserver(mWatcher);
+ }
+ if (mSharedLibraries != null) {
+ mSharedLibraries.registerObserver(mWatcher);
+ }
+ if (mInstrumentation != null) {
+ mInstrumentation.registerObserver(mWatcher);
+ }
+ if (mWebInstantAppsDisabled != null) {
+ mWebInstantAppsDisabled.registerObserver(mWatcher);
+ }
+ if (mAppsFilter != null) {
+ mAppsFilter.registerObserver(mWatcher);
+ }
+ if (mInstantAppRegistry != null) {
+ mInstantAppRegistry.registerObserver(mWatcher);
+ }
+ if (mSettings != null) {
+ mSettings.registerObserver(mWatcher);
+ }
+ if (mIsolatedOwners != null) {
+ mIsolatedOwners.registerObserver(mWatcher);
+ }
+ if (mComponentResolver != null) {
+ mComponentResolver.registerObserver(mWatcher);
+ }
+ if (mFrozenPackages != null) {
+ mFrozenPackages.registerObserver(mWatcher);
+ }
+ if (verify) {
+ // If neither "build" attribute is true then this may be a mockito test,
+ // and verification can fail as a false positive.
+ Watchable.verifyWatchedAttributes(this, mWatcher, !(mIsEngBuild || mIsUserDebugBuild));
+ }
}
/**
@@ -1619,7 +1613,6 @@ public class PackageManagerService extends IPackageManager.Stub
mInstallLock = injector.getInstallLock();
mLock = injector.getLock();
mPackageStateWriteLock = mLock;
- mLiveComputerSyncLock = mLock;
mPermissionManager = injector.getPermissionManagerServiceInternal();
mSettings = injector.getSettings();
mUserManager = injector.getUserManagerService();
@@ -1639,6 +1632,7 @@ public class PackageManagerService extends IPackageManager.Stub
mIncrementalManager = testParams.incrementalManager;
mInstallerService = testParams.installerService;
mInstantAppRegistry = testParams.instantAppRegistry;
+ mChangedPackagesTracker = testParams.changedPackagesTracker;
mInstantAppResolverConnection = testParams.instantAppResolverConnection;
mInstantAppResolverSettingsComponent = testParams.instantAppResolverSettingsComponent;
mIsPreNMR1Upgrade = testParams.isPreNmr1Upgrade;
@@ -1678,10 +1672,6 @@ public class PackageManagerService extends IPackageManager.Stub
mOverlayConfigSignaturePackage = testParams.overlayConfigSignaturePackage;
mResolveComponentName = testParams.resolveComponentName;
- // Disable snapshots in this instance of PackageManagerService, which is only used
- // for testing. The instance still needs a live computer. The snapshot computer
- // is set to null since it must never be used by this instance.
- mSnapshotEnabled = false;
mLiveComputer = createLiveComputer();
mSnapshotComputer = null;
mSnapshotStatistics = null;
@@ -1707,13 +1697,13 @@ public class PackageManagerService extends IPackageManager.Stub
mSuspendPackageHelper = testParams.suspendPackageHelper;
mSharedLibraries.setDeletePackageHelper(mDeletePackageHelper);
+ registerObservers(false);
invalidatePackageInfoCache();
}
public PackageManagerService(PackageManagerServiceInjector injector, boolean onlyCore,
boolean factoryTest, final String buildFingerprint, final boolean isEngBuild,
- final boolean isUserDebugBuild, final int sdkVersion, final String incrementalVersion,
- boolean snapshotEnabled) {
+ final boolean isUserDebugBuild, final int sdkVersion, final String incrementalVersion) {
mIsEngBuild = isEngBuild;
mIsUserDebugBuild = isUserDebugBuild;
mSdkVersion = sdkVersion;
@@ -1728,7 +1718,6 @@ public class PackageManagerService extends IPackageManager.Stub
mInjector.bootstrap(this);
mLock = injector.getLock();
mPackageStateWriteLock = mLock;
- mLiveComputerSyncLock = mLock;
mInstallLock = injector.getInstallLock();
LockGuard.installLock(mLock, LockGuard.INDEX_PACKAGES);
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_START,
@@ -1832,7 +1821,10 @@ public class PackageManagerService extends IPackageManager.Stub
mApexManager = injector.getApexManager();
mAppsFilter = mInjector.getAppsFilter();
- mInstantAppRegistry = new InstantAppRegistry(this, mPermissionManager, mPmInternal);
+ mInstantAppRegistry = new InstantAppRegistry(mContext, mPermissionManager,
+ mInjector.getUserManagerInternal(), new DeletePackageHelper(this));
+
+ mChangedPackagesTracker = new ChangedPackagesTracker();
mAppInstallDir = new File(Environment.getDataDirectory(), "app");
@@ -1857,16 +1849,12 @@ public class PackageManagerService extends IPackageManager.Stub
synchronized (mLock) {
// Create the computer as soon as the state objects have been installed. The
// cached computer is the same as the live computer until the end of the
- // constructor, at which time the invalidation method updates it. The cache is
- // corked initially to ensure a cached computer is not built until the end of the
- // constructor.
+ // constructor, at which time the invalidation method updates it.
mSnapshotStatistics = new SnapshotStatistics();
- sSnapshotCorked.set(1);
sSnapshotInvalid.set(true);
mLiveComputer = createLiveComputer();
mSnapshotComputer = null;
- mSnapshotEnabled = snapshotEnabled;
- registerObserver();
+ registerObservers(true);
}
// CHECKSTYLE:OFF IndentationCheck
@@ -2161,7 +2149,7 @@ public class PackageManagerService extends IPackageManager.Stub
|| !ps.getUserStateOrDefault(userId).isInstalled()) {
continue;
}
- mInstantAppRegistry.addInstantAppLPw(userId, ps.getAppId());
+ mInstantAppRegistry.addInstantApp(userId, ps.getAppId());
}
}
@@ -2852,11 +2840,17 @@ public class PackageManagerService extends IPackageManager.Stub
// TODO: Implement
// 7. Consider installed instant apps unused longer than min cache period
- if (internalVolume && mInstantAppRegistry.pruneInstalledInstantApps(bytes,
- android.provider.Settings.Global.getLong(mContext.getContentResolver(),
- Global.INSTALLED_INSTANT_APP_MIN_CACHE_PERIOD,
- InstantAppRegistry.DEFAULT_INSTALLED_INSTANT_APP_MIN_CACHE_PERIOD))) {
- return;
+ if (internalVolume) {
+ if (executeWithConsistentComputerReturning(computer ->
+ mInstantAppRegistry.pruneInstalledInstantApps(computer, bytes,
+ android.provider.Settings.Global.getLong(
+ mContext.getContentResolver(),
+ Global.INSTALLED_INSTANT_APP_MIN_CACHE_PERIOD,
+ InstantAppRegistry
+ .DEFAULT_INSTALLED_INSTANT_APP_MIN_CACHE_PERIOD)))
+ ) {
+ return;
+ }
}
// 8. Consider cached app data (below quotas)
@@ -2873,11 +2867,17 @@ public class PackageManagerService extends IPackageManager.Stub
// TODO: Implement
// 10. Consider instant meta-data (uninstalled apps) older that min cache period
- if (internalVolume && mInstantAppRegistry.pruneUninstalledInstantApps(bytes,
- android.provider.Settings.Global.getLong(mContext.getContentResolver(),
- Global.UNINSTALLED_INSTANT_APP_MIN_CACHE_PERIOD,
- InstantAppRegistry.DEFAULT_UNINSTALLED_INSTANT_APP_MIN_CACHE_PERIOD))) {
- return;
+ if (internalVolume) {
+ if (executeWithConsistentComputerReturning(computer ->
+ mInstantAppRegistry.pruneUninstalledInstantApps(computer, bytes,
+ android.provider.Settings.Global.getLong(
+ mContext.getContentResolver(),
+ Global.UNINSTALLED_INSTANT_APP_MIN_CACHE_PERIOD,
+ InstantAppRegistry
+ .DEFAULT_UNINSTALLED_INSTANT_APP_MIN_CACHE_PERIOD)))
+ ) {
+ return;
+ }
}
// 11. Free storage service cache
@@ -2903,6 +2903,36 @@ public class PackageManagerService extends IPackageManager.Stub
throw new IOException("Failed to free " + bytes + " on storage device at " + file);
}
+ int freeCacheForInstallation(int recommendedInstallLocation, PackageLite pkgLite,
+ String resolvedPath, String mPackageAbiOverride, int installFlags) {
+ // TODO: focus freeing disk space on the target device
+ final StorageManager storage = StorageManager.from(mContext);
+ final long lowThreshold = storage.getStorageLowBytes(Environment.getDataDirectory());
+
+ final long sizeBytes = PackageManagerServiceUtils.calculateInstalledSize(resolvedPath,
+ mPackageAbiOverride);
+ if (sizeBytes >= 0) {
+ synchronized (mInstallLock) {
+ try {
+ mInstaller.freeCache(null, sizeBytes + lowThreshold, 0);
+ PackageInfoLite pkgInfoLite = PackageManagerServiceUtils.getMinimalPackageInfo(
+ mContext, pkgLite, resolvedPath, installFlags,
+ mPackageAbiOverride);
+ // The cache free must have deleted the file we downloaded to install.
+ if (pkgInfoLite.recommendedInstallLocation
+ == InstallLocationUtils.RECOMMEND_FAILED_INVALID_URI) {
+ pkgInfoLite.recommendedInstallLocation =
+ InstallLocationUtils.RECOMMEND_FAILED_INSUFFICIENT_STORAGE;
+ }
+ return pkgInfoLite.recommendedInstallLocation;
+ } catch (Installer.InstallerException e) {
+ Slog.w(TAG, "Failed to free cache", e);
+ }
+ }
+ }
+ return recommendedInstallLocation;
+ }
+
/**
* Update given flags when being used to request {@link PackageInfo}.
*/
@@ -3041,26 +3071,7 @@ public class PackageManagerService extends IPackageManager.Stub
@GuardedBy("mLock")
void updateSequenceNumberLP(PackageSetting pkgSetting, int[] userList) {
- for (int i = userList.length - 1; i >= 0; --i) {
- final int userId = userList[i];
- SparseArray<String> changedPackages = mChangedPackages.get(userId);
- if (changedPackages == null) {
- changedPackages = new SparseArray<>();
- mChangedPackages.put(userId, changedPackages);
- }
- Map<String, Integer> sequenceNumbers = mChangedPackagesSequenceNumbers.get(userId);
- if (sequenceNumbers == null) {
- sequenceNumbers = new HashMap<>();
- mChangedPackagesSequenceNumbers.put(userId, sequenceNumbers);
- }
- final Integer sequenceNumber = sequenceNumbers.get(pkgSetting.getPackageName());
- if (sequenceNumber != null) {
- changedPackages.remove(sequenceNumber);
- }
- changedPackages.put(mChangedPackagesSequenceNumber, pkgSetting.getPackageName());
- sequenceNumbers.put(pkgSetting.getPackageName(), mChangedPackagesSequenceNumber);
- }
- mChangedPackagesSequenceNumber++;
+ mChangedPackagesTracker.updateSequenceNumber(pkgSetting.getPackageName(), userList);
}
@Override
@@ -3073,30 +3084,21 @@ public class PackageManagerService extends IPackageManager.Stub
return null;
}
enforceCrossUserPermission(callingUid, userId, false, false, "getChangedPackages");
- synchronized (mLock) {
- if (sequenceNumber >= mChangedPackagesSequenceNumber) {
- return null;
- }
- final SparseArray<String> changedPackages = mChangedPackages.get(userId);
- if (changedPackages == null) {
- return null;
- }
- final List<String> packageNames =
- new ArrayList<>(mChangedPackagesSequenceNumber - sequenceNumber);
- for (int i = sequenceNumber; i < mChangedPackagesSequenceNumber; i++) {
- final String packageName = changedPackages.get(i);
- if (packageName != null) {
- // Filter out the changes if the calling package should not be able to see it.
- final PackageSetting ps = mSettings.getPackageLPr(packageName);
- if (shouldFilterApplication(ps, callingUid, userId)) {
- continue;
- }
- packageNames.add(packageName);
+ final ChangedPackages changedPackages = mChangedPackagesTracker.getChangedPackages(
+ sequenceNumber, userId);
+
+ if (changedPackages != null) {
+ final List<String> packageNames = changedPackages.getPackageNames();
+ for (int index = packageNames.size() - 1; index >= 0; index--) {
+ // Filter out the changes if the calling package should not be able to see it.
+ final PackageSetting ps = mSettings.getPackageLPr(packageNames.get(index));
+ if (shouldFilterApplication(ps, callingUid, userId)) {
+ packageNames.remove(index);
}
}
- return packageNames.isEmpty()
- ? null : new ChangedPackages(mChangedPackagesSequenceNumber, packageNames);
}
+
+ return changedPackages;
}
@Override
@@ -3143,8 +3145,8 @@ public class PackageManagerService extends IPackageManager.Stub
@Override
public String getPermissionControllerPackageName() {
final int callingUid = Binder.getCallingUid();
- if (mComputer.isPackageStateAvailableAndVisible(mRequiredPermissionControllerPackage,
- callingUid, UserHandle.getUserId(callingUid))) {
+ if (mComputer.getPackageStateFiltered(mRequiredPermissionControllerPackage,
+ callingUid, UserHandle.getUserId(callingUid)) != null) {
return mRequiredPermissionControllerPackage;
}
@@ -3533,12 +3535,11 @@ public class PackageManagerService extends IPackageManager.Stub
}
enforceCrossUserPermission(Binder.getCallingUid(), userId, true /* requireFullPermission */,
false /* checkShell */, "getEphemeralApplications");
- synchronized (mLock) {
- List<InstantAppInfo> instantApps = mInstantAppRegistry
- .getInstantAppsLPr(userId);
- if (instantApps != null) {
- return new ParceledListSlice<>(instantApps);
- }
+
+ List<InstantAppInfo> instantApps = executeWithConsistentComputerReturning(computer ->
+ mInstantAppRegistry.getInstantApps(computer, userId));
+ if (instantApps != null) {
+ return new ParceledListSlice<>(instantApps);
}
return null;
}
@@ -3565,10 +3566,11 @@ public class PackageManagerService extends IPackageManager.Stub
if (!isCallerSameApp(packageName, Binder.getCallingUid())) {
return null;
}
- synchronized (mLock) {
- return mInstantAppRegistry.getInstantAppCookieLPw(
- packageName, userId);
+ PackageStateInternal packageState = getPackageStateInternal(packageName);
+ if (packageState == null || packageState.getPkg() == null) {
+ return null;
}
+ return mInstantAppRegistry.getInstantAppCookie(packageState.getPkg(), userId);
}
@Override
@@ -3582,10 +3584,13 @@ public class PackageManagerService extends IPackageManager.Stub
if (!isCallerSameApp(packageName, Binder.getCallingUid())) {
return false;
}
- synchronized (mLock) {
- return mInstantAppRegistry.setInstantAppCookieLPw(
- packageName, cookie, userId);
+
+ PackageStateInternal packageState = getPackageStateInternal(packageName);
+ if (packageState == null || packageState.getPkg() == null) {
+ return false;
}
+ return mInstantAppRegistry.setInstantAppCookie(packageState.getPkg(), cookie,
+ mContext.getPackageManager().getInstantAppCookieMaxBytes(), userId);
}
@Override
@@ -3601,10 +3606,7 @@ public class PackageManagerService extends IPackageManager.Stub
enforceCrossUserPermission(Binder.getCallingUid(), userId, true /* requireFullPermission */,
false /* checkShell */, "getInstantAppIcon");
- synchronized (mLock) {
- return mInstantAppRegistry.getInstantAppIconLPw(
- packageName, userId);
- }
+ return mInstantAppRegistry.getInstantAppIcon(packageName, userId);
}
boolean isCallerSameApp(String packageName, int uid) {
@@ -3706,7 +3708,7 @@ public class PackageManagerService extends IPackageManager.Stub
// Before everything else, see whether we need to fstrim.
try {
- IStorageManager sm = PackageHelper.getStorageManager();
+ IStorageManager sm = InstallLocationUtils.getStorageManager();
if (sm != null) {
boolean doTrim = false;
final long interval = android.provider.Settings.Global.getLong(
@@ -3722,16 +3724,14 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
if (doTrim) {
- final boolean dexOptDialogShown;
- synchronized (mLock) {
- dexOptDialogShown = mDexOptHelper.isDexOptDialogShown();
- }
- if (!isFirstBoot() && dexOptDialogShown) {
- try {
- ActivityManager.getService().showBootMessage(
- mContext.getResources().getString(
- R.string.android_upgrading_fstrim), true);
- } catch (RemoteException e) {
+ if (!isFirstBoot()) {
+ if (mDexOptHelper.isDexOptDialogShown()) {
+ try {
+ ActivityManager.getService().showBootMessage(
+ mContext.getResources().getString(
+ R.string.android_upgrading_fstrim), true);
+ } catch (RemoteException e) {
+ }
}
}
sm.runMaintenance();
@@ -3751,29 +3751,27 @@ public class PackageManagerService extends IPackageManager.Stub
@Override
public void notifyPackageUse(String packageName, int reason) {
- synchronized (mLock) {
- final int callingUid = Binder.getCallingUid();
- final int callingUserId = UserHandle.getUserId(callingUid);
+ final int callingUid = Binder.getCallingUid();
+ final int callingUserId = UserHandle.getUserId(callingUid);
+ boolean notify = executeWithConsistentComputerReturning(computer -> {
if (getInstantAppPackageName(callingUid) != null) {
- if (!isCallerSameApp(packageName, callingUid)) {
- return;
- }
+ return isCallerSameApp(packageName, callingUid);
} else {
- if (isInstantAppInternal(packageName, callingUserId, Process.SYSTEM_UID)) {
- return;
- }
+ return !isInstantAppInternal(packageName, callingUserId, Process.SYSTEM_UID);
}
- notifyPackageUseLocked(packageName, reason);
+ });
+ if (!notify) {
+ return;
}
+
+ notifyPackageUseInternal(packageName, reason);
}
- @GuardedBy("mLock")
- private void notifyPackageUseLocked(String packageName, int reason) {
- final PackageSetting pkgSetting = mSettings.getPackageLPr(packageName);
- if (pkgSetting == null) {
- return;
- }
- pkgSetting.getPkgState().setLastPackageUsageTimeInMills(reason, System.currentTimeMillis());
+ private void notifyPackageUseInternal(String packageName, int reason) {
+ long time = System.currentTimeMillis();
+ commitPackageStateMutation(null, packageName, packageState -> {
+ packageState.setLastPackageUsageTime(reason, time);
+ });
}
@Override
@@ -3899,10 +3897,12 @@ public class PackageManagerService extends IPackageManager.Stub
// This is the last chance to write out pending restriction settings
if (mHandler.hasMessages(WRITE_PACKAGE_RESTRICTIONS)) {
mHandler.removeMessages(WRITE_PACKAGE_RESTRICTIONS);
- for (int userId : mDirtyUsers) {
- mSettings.writePackageRestrictionsLPr(userId);
+ synchronized (mDirtyUsers) {
+ for (int userId : mDirtyUsers) {
+ mSettings.writePackageRestrictionsLPr(userId);
+ }
+ mDirtyUsers.clear();
}
- mDirtyUsers.clear();
}
}
}
@@ -3918,12 +3918,9 @@ public class PackageManagerService extends IPackageManager.Stub
throw new SecurityException("dumpProfiles");
}
- AndroidPackage pkg;
- synchronized (mLock) {
- pkg = mPackages.get(packageName);
- if (pkg == null) {
- throw new IllegalArgumentException("Unknown package: " + packageName);
- }
+ AndroidPackage pkg = getPackage(packageName);
+ if (pkg == null) {
+ throw new IllegalArgumentException("Unknown package: " + packageName);
}
synchronized (mInstallLock) {
@@ -3946,14 +3943,12 @@ public class PackageManagerService extends IPackageManager.Stub
public Property getProperty(String propertyName, String packageName, String className) {
Objects.requireNonNull(propertyName);
Objects.requireNonNull(packageName);
- synchronized (mLock) {
- final PackageStateInternal ps = getPackageStateInternal(packageName);
- if (shouldFilterApplication(ps, Binder.getCallingUid(),
- UserHandle.getCallingUserId())) {
- return null;
- }
- return mPackageProperty.getProperty(propertyName, packageName, className);
+ PackageStateInternal packageState = mComputer.getPackageStateFiltered(packageName,
+ Binder.getCallingUid(), UserHandle.getCallingUserId());
+ if (packageState == null) {
+ return null;
}
+ return mPackageProperty.getProperty(propertyName, packageName, className);
}
@Override
@@ -4038,66 +4033,33 @@ public class PackageManagerService extends IPackageManager.Stub
@Override
public void notifyPackageAdded(String packageName, int uid) {
- final PackageListObserver[] observers;
- synchronized (mLock) {
- if (mPackageListObservers.size() == 0) {
- return;
- }
- final PackageListObserver[] observerArray =
- new PackageListObserver[mPackageListObservers.size()];
- observers = mPackageListObservers.toArray(observerArray);
- }
- for (int i = observers.length - 1; i >= 0; --i) {
- observers[i].onPackageAdded(packageName, uid);
- }
+ mPackageObserverHelper.notifyAdded(packageName, uid);
}
@Override
public void notifyPackageChanged(String packageName, int uid) {
- final PackageListObserver[] observers;
- synchronized (mLock) {
- if (mPackageListObservers.size() == 0) {
- return;
- }
- final PackageListObserver[] observerArray =
- new PackageListObserver[mPackageListObservers.size()];
- observers = mPackageListObservers.toArray(observerArray);
- }
- for (int i = observers.length - 1; i >= 0; --i) {
- observers[i].onPackageChanged(packageName, uid);
- }
+ mPackageObserverHelper.notifyChanged(packageName, uid);
}
@Override
public void notifyPackageRemoved(String packageName, int uid) {
- final PackageListObserver[] observers;
- synchronized (mLock) {
- if (mPackageListObservers.size() == 0) {
- return;
- }
- final PackageListObserver[] observerArray =
- new PackageListObserver[mPackageListObservers.size()];
- observers = mPackageListObservers.toArray(observerArray);
- }
- for (int i = observers.length - 1; i >= 0; --i) {
- observers[i].onPackageRemoved(packageName, uid);
- }
+ mPackageObserverHelper.notifyRemoved(packageName, uid);
}
- void sendPackageAddedForUser(String packageName, PackageSetting pkgSetting,
+ void sendPackageAddedForUser(String packageName, @NonNull PackageStateInternal packageState,
int userId, int dataLoaderType) {
- final boolean isSystem = PackageManagerServiceUtils.isSystemApp(pkgSetting)
- || PackageManagerServiceUtils.isUpdatedSystemApp(pkgSetting);
- final boolean isInstantApp = pkgSetting.getInstantApp(userId);
+ final PackageUserStateInternal userState = packageState.getUserStateOrDefault(userId);
+ final boolean isSystem = packageState.isSystem();
+ final boolean isInstantApp = userState.isInstantApp();
final int[] userIds = isInstantApp ? EMPTY_INT_ARRAY : new int[] { userId };
final int[] instantUserIds = isInstantApp ? new int[] { userId } : EMPTY_INT_ARRAY;
sendPackageAddedForNewUsers(packageName, isSystem /*sendBootCompleted*/,
- false /*startReceiver*/, pkgSetting.getAppId(), userIds, instantUserIds,
+ false /*startReceiver*/, packageState.getAppId(), userIds, instantUserIds,
dataLoaderType);
// Send a session commit broadcast
final PackageInstaller.SessionInfo info = new PackageInstaller.SessionInfo();
- info.installReason = pkgSetting.getInstallReason(userId);
+ info.installReason = userState.getInstallReason();
info.appPackageName = packageName;
sendSessionCommitBroadcast(info, userId);
}
@@ -4129,7 +4091,6 @@ public class PackageManagerService extends IPackageManager.Stub
public boolean setApplicationHiddenSettingAsUser(String packageName, boolean hidden,
int userId) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USERS, null);
- PackageSetting pkgSetting;
final int callingUid = Binder.getCallingUid();
enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */,
true /* checkShell */, "setApplicationHiddenSetting for user " + userId);
@@ -4139,74 +4100,70 @@ public class PackageManagerService extends IPackageManager.Stub
return false;
}
+ // Do not allow "android" is being disabled
+ if ("android".equals(packageName)) {
+ Slog.w(TAG, "Cannot hide package: android");
+ return false;
+ }
+
final long callingId = Binder.clearCallingIdentity();
try {
- boolean sendAdded = false;
- boolean sendRemoved = false;
- // writer
- synchronized (mLock) {
- pkgSetting = mSettings.getPackageLPr(packageName);
- if (pkgSetting == null) {
- return false;
- }
- if (shouldFilterApplication(pkgSetting, callingUid, userId)) {
- return false;
- }
- // Do not allow "android" is being disabled
- if ("android".equals(packageName)) {
- Slog.w(TAG, "Cannot hide package: android");
+ final PackageStateInternal packageState =
+ mComputer.getPackageStateFiltered(packageName, callingUid, userId);
+ if (packageState == null) {
+ return false;
+ }
+
+ // Cannot hide static shared libs as they are considered
+ // a part of the using app (emulating static linking). Also
+ // static libs are installed always on internal storage.
+ AndroidPackage pkg = packageState.getPkg();
+ if (pkg != null) {
+ // Cannot hide SDK libs as they are controlled by SDK manager.
+ if (pkg.getSdkLibName() != null) {
+ Slog.w(TAG, "Cannot hide package: " + packageName
+ + " providing SDK library: "
+ + pkg.getSdkLibName());
return false;
}
- AndroidPackage pkg = mPackages.get(packageName);
- if (pkg != null) {
- // Cannot hide SDK libs as they are controlled by SDK manager.
- if (pkg.getSdkLibName() != null) {
- Slog.w(TAG, "Cannot hide package: " + packageName
- + " providing SDK library: "
- + pkg.getSdkLibName());
- return false;
- }
- // Cannot hide static shared libs as they are considered
- // a part of the using app (emulating static linking). Also
- // static libs are installed always on internal storage.
- if (pkg.getStaticSharedLibName() != null) {
- Slog.w(TAG, "Cannot hide package: " + packageName
- + " providing static shared library: "
- + pkg.getStaticSharedLibName());
- return false;
- }
- }
- // Only allow protected packages to hide themselves.
- if (hidden && !UserHandle.isSameApp(callingUid, pkgSetting.getAppId())
- && mProtectedPackages.isPackageStateProtected(userId, packageName)) {
- Slog.w(TAG, "Not hiding protected package: " + packageName);
+ // Cannot hide static shared libs as they are considered
+ // a part of the using app (emulating static linking). Also
+ // static libs are installed always on internal storage.
+ if (pkg.getStaticSharedLibName() != null) {
+ Slog.w(TAG, "Cannot hide package: " + packageName
+ + " providing static shared library: "
+ + pkg.getStaticSharedLibName());
return false;
}
-
- if (pkgSetting.getHidden(userId) != hidden) {
- pkgSetting.setHidden(hidden, userId);
- mSettings.writePackageRestrictionsLPr(userId);
- if (hidden) {
- sendRemoved = true;
- } else {
- sendAdded = true;
- }
- }
}
- if (sendAdded) {
- sendPackageAddedForUser(packageName, pkgSetting, userId, DataLoaderType.NONE);
- return true;
+ // Only allow protected packages to hide themselves.
+ if (hidden && !UserHandle.isSameApp(callingUid, packageState.getAppId())
+ && mProtectedPackages.isPackageStateProtected(userId, packageName)) {
+ Slog.w(TAG, "Not hiding protected package: " + packageName);
+ return false;
}
- if (sendRemoved) {
- killApplication(packageName, pkgSetting.getAppId(), userId,
- "hiding pkg");
- sendApplicationHiddenForUser(packageName, pkgSetting, userId);
- return true;
+
+ if (packageState.getUserStateOrDefault(userId).isHidden() == hidden) {
+ return false;
+ }
+
+ commitPackageStateMutation(null, packageName, packageState1 ->
+ packageState1.userState(userId).setHidden(hidden));
+
+ final PackageStateInternal newPackageState = getPackageStateInternal(packageName);
+
+ if (hidden) {
+ killApplication(packageName, newPackageState.getAppId(), userId, "hiding pkg");
+ sendApplicationHiddenForUser(packageName, newPackageState, userId);
+ } else {
+ sendPackageAddedForUser(packageName, newPackageState, userId, DataLoaderType.NONE);
}
+
+ scheduleWritePackageRestrictions(userId);
+ return true;
} finally {
Binder.restoreCallingIdentity(callingId);
}
- return false;
}
@Override
@@ -4219,21 +4176,20 @@ public class PackageManagerService extends IPackageManager.Stub
"setSystemAppHiddenUntilInstalled");
}
- synchronized (mLock) {
- final PackageSetting pkgSetting = mSettings.getPackageLPr(packageName);
- if (pkgSetting == null || !pkgSetting.isSystem()) {
- return;
- }
- if (pkgSetting.getPkg().isCoreApp() && !calledFromSystemOrPhone) {
- throw new SecurityException("Only system or phone callers can modify core apps");
- }
- pkgSetting.getPkgState().setHiddenUntilInstalled(hidden);
- final PackageSetting disabledPs = mSettings.getDisabledSystemPkgLPr(packageName);
- if (disabledPs == null) {
- return;
- }
- disabledPs.getPkgState().setHiddenUntilInstalled(hidden);
+ final PackageStateInternal stateRead = getPackageStateInternal(packageName);
+ if (stateRead == null || !stateRead.isSystem() || stateRead.getPkg() == null) {
+ return;
}
+ if (stateRead.getPkg().isCoreApp() && !calledFromSystemOrPhone) {
+ throw new SecurityException("Only system or phone callers can modify core apps");
+ }
+
+ commitPackageStateMutation(null, mutator -> {
+ mutator.forPackage(packageName)
+ .setHiddenUntilInstalled(hidden);
+ mutator.forDisabledSystemPackage(packageName)
+ .setHiddenUntilInstalled(hidden);
+ });
}
@Override
@@ -4246,19 +4202,17 @@ public class PackageManagerService extends IPackageManager.Stub
"setSystemAppHiddenUntilInstalled");
}
- synchronized (mLock) {
- final PackageSetting pkgSetting = mSettings.getPackageLPr(packageName);
- // The target app should always be in system
- if (pkgSetting == null || !pkgSetting.isSystem()) {
- return false;
- }
- if (pkgSetting.getPkg().isCoreApp() && !calledFromSystemOrPhone) {
- throw new SecurityException("Only system or phone callers can modify core apps");
- }
- // Check if the install state is the same
- if (pkgSetting.getInstalled(userId) == installed) {
- return false;
- }
+ final PackageStateInternal packageState = getPackageStateInternal(packageName);
+ // The target app should always be in system
+ if (packageState == null || !packageState.isSystem() || packageState.getPkg() == null) {
+ return false;
+ }
+ if (packageState.getPkg().isCoreApp() && !calledFromSystemOrPhone) {
+ throw new SecurityException("Only system or phone callers can modify core apps");
+ }
+ // Check if the install state is the same
+ if (packageState.getUserStateOrDefault(userId).isInstalled() == installed) {
+ return false;
}
final long callingId = Binder.clearCallingIdentity();
@@ -4286,14 +4240,14 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
- private void sendApplicationHiddenForUser(String packageName, PackageSetting pkgSetting,
+ private void sendApplicationHiddenForUser(String packageName, PackageStateInternal packageState,
int userId) {
final PackageRemovedInfo info = new PackageRemovedInfo(this);
info.mRemovedPackage = packageName;
- info.mInstallerPackageName = pkgSetting.getInstallSource().installerPackageName;
+ info.mInstallerPackageName = packageState.getInstallSource().installerPackageName;
info.mRemovedUsers = new int[] {userId};
info.mBroadcastUsers = new int[] {userId};
- info.mUid = UserHandle.getUid(userId, pkgSetting.getAppId());
+ info.mUid = UserHandle.getUid(userId, packageState.getAppId());
info.sendPackageRemovedBroadcasts(true /*killApp*/, false /*removedBySystem*/);
}
@@ -4348,45 +4302,52 @@ public class PackageManagerService extends IPackageManager.Stub
final List<String> changedPackagesList = new ArrayList<>(packageNames.length);
final IntArray changedUids = new IntArray(packageNames.length);
final List<String> unactionedPackages = new ArrayList<>(packageNames.length);
- final boolean[] canRestrict = (restrictionFlags != 0)
- ? mSuspendPackageHelper.canSuspendPackageForUser(packageNames, userId, callingUid)
- : null;
- for (int i = 0; i < packageNames.length; i++) {
- final String packageName = packageNames[i];
- final PackageSetting pkgSetting;
- synchronized (mLock) {
- pkgSetting = mSettings.getPackageLPr(packageName);
- if (pkgSetting == null
- || shouldFilterApplication(pkgSetting, callingUid, userId)) {
+ ArraySet<String> changesToCommit = new ArraySet<>();
+ executeWithConsistentComputer(computer -> {
+ final boolean[] canRestrict = (restrictionFlags != 0)
+ ? mSuspendPackageHelper.canSuspendPackageForUser(computer, packageNames, userId,
+ callingUid) : null;
+ for (int i = 0; i < packageNames.length; i++) {
+ final String packageName = packageNames[i];
+ final PackageStateInternal packageState =
+ computer.getPackageStateInternal(packageName);
+ if (packageState == null
+ || shouldFilterApplication(packageState, callingUid, userId)) {
Slog.w(TAG, "Could not find package setting for package: " + packageName
+ ". Skipping...");
unactionedPackages.add(packageName);
continue;
}
- }
- if (canRestrict != null && !canRestrict[i]) {
- unactionedPackages.add(packageName);
- continue;
- }
- synchronized (mLock) {
- final int oldDistractionFlags = pkgSetting.getDistractionFlags(userId);
+ if (canRestrict != null && !canRestrict[i]) {
+ unactionedPackages.add(packageName);
+ continue;
+ }
+ final int oldDistractionFlags = packageState.getUserStateOrDefault(userId)
+ .getDistractionFlags();
if (restrictionFlags != oldDistractionFlags) {
- pkgSetting.setDistractionFlags(restrictionFlags, userId);
changedPackagesList.add(packageName);
- changedUids.add(UserHandle.getUid(userId, pkgSetting.getAppId()));
+ changedUids.add(UserHandle.getUid(userId, packageState.getAppId()));
+ changesToCommit.add(packageName);
}
}
- }
+ });
+
+ commitPackageStateMutation(null, mutator -> {
+ final int size = changesToCommit.size();
+ for (int index = 0; index < size; index++) {
+ mutator.forPackage(changesToCommit.valueAt(index))
+ .userState(userId)
+ .setDistractionFlags(restrictionFlags);
+ }
+ });
if (!changedPackagesList.isEmpty()) {
final String[] changedPackages = changedPackagesList.toArray(
new String[changedPackagesList.size()]);
mHandler.post(() -> mBroadcastHelper.sendDistractingPackagesChanged(
changedPackages, changedUids.toArray(), userId, restrictionFlags));
- synchronized (mLock) {
- scheduleWritePackageRestrictionsLocked(userId);
- }
+ scheduleWritePackageRestrictions(userId);
}
return unactionedPackages.toArray(new String[0]);
}
@@ -4450,10 +4411,9 @@ public class PackageManagerService extends IPackageManager.Stub
}
void unsuspendForSuspendingPackage(String suspendingPackage, int userId) {
- final String[] allPackages;
- synchronized (mLock) {
- allPackages = mPackages.keySet().toArray(new String[mPackages.size()]);
- }
+ // TODO: This can be replaced by a special parameter to iterate all packages, rather than
+ // this weird pre-collect of all packages.
+ final String[] allPackages = getPackageStates().keySet().toArray(new String[0]);
mSuspendPackageHelper.removeSuspensionsBySuspendingPackage(
allPackages, suspendingPackage::equals, userId);
}
@@ -4479,22 +4439,27 @@ public class PackageManagerService extends IPackageManager.Stub
private void removeDistractingPackageRestrictions(String[] packagesToChange, int userId) {
final List<String> changedPackages = new ArrayList<>();
final IntArray changedUids = new IntArray();
- synchronized (mLock) {
- for (String packageName : packagesToChange) {
- final PackageSetting ps = mSettings.getPackageLPr(packageName);
- if (ps != null && ps.getDistractionFlags(userId) != 0) {
- ps.setDistractionFlags(0, userId);
- changedPackages.add(ps.getPackageName());
- changedUids.add(UserHandle.getUid(userId, ps.getAppId()));
- }
+ for (String packageName : packagesToChange) {
+ final PackageStateInternal ps = getPackageStateInternal(packageName);
+ if (ps != null && ps.getUserStateOrDefault(userId).getDistractionFlags() != 0) {
+ changedPackages.add(ps.getPackageName());
+ changedUids.add(UserHandle.getUid(userId, ps.getAppId()));
}
- if (!changedPackages.isEmpty()) {
- final String[] packageArray = changedPackages.toArray(
- new String[changedPackages.size()]);
- mHandler.post(() -> mBroadcastHelper.sendDistractingPackagesChanged(
- packageArray, changedUids.toArray(), userId, 0));
- scheduleWritePackageRestrictionsLocked(userId);
+ }
+ commitPackageStateMutation(null, mutator -> {
+ for (int index = 0; index < changedPackages.size(); index++) {
+ mutator.forPackage(changedPackages.get(index))
+ .userState(userId)
+ .setDistractionFlags(0);
}
+ });
+
+ if (!changedPackages.isEmpty()) {
+ final String[] packageArray = changedPackages.toArray(
+ new String[changedPackages.size()]);
+ mHandler.post(() -> mBroadcastHelper.sendDistractingPackagesChanged(
+ packageArray, changedUids.toArray(), userId, 0));
+ scheduleWritePackageRestrictions(userId);
}
}
@@ -4616,43 +4581,40 @@ public class PackageManagerService extends IPackageManager.Stub
public void setInstallerPackageName(String targetPackage, String installerPackageName) {
final int callingUid = Binder.getCallingUid();
final int callingUserId = UserHandle.getUserId(callingUid);
- if (getInstantAppPackageName(callingUid) != null) {
- return;
- }
- // writer
- synchronized (mLock) {
- PackageSetting targetPackageSetting = mSettings.getPackageLPr(targetPackage);
- if (targetPackageSetting == null
- || shouldFilterApplication(
- targetPackageSetting, callingUid, callingUserId)) {
+ final FunctionalUtils.ThrowingCheckedFunction<Computer, Boolean, RuntimeException>
+ implementation = computer -> {
+ if (computer.getInstantAppPackageName(callingUid) != null) {
+ return false;
+ }
+
+ PackageStateInternal targetPackageState =
+ computer.getPackageStateInternal(targetPackage);
+ if (targetPackageState == null
+ || computer.shouldFilterApplication(targetPackageState, callingUid,
+ callingUserId)) {
throw new IllegalArgumentException("Unknown target package: " + targetPackage);
}
- PackageSetting installerPackageSetting;
+ PackageStateInternal installerPackageState = null;
if (installerPackageName != null) {
- installerPackageSetting = mSettings.getPackageLPr(installerPackageName);
- if (installerPackageSetting == null
+ installerPackageState = computer.getPackageStateInternal(installerPackageName);
+ if (installerPackageState == null
|| shouldFilterApplication(
- installerPackageSetting, callingUid, callingUserId)) {
+ installerPackageState, callingUid, callingUserId)) {
throw new IllegalArgumentException("Unknown installer package: "
+ installerPackageName);
}
- } else {
- installerPackageSetting = null;
}
Signature[] callerSignature;
final int appId = UserHandle.getAppId(callingUid);
- final Object obj = mSettings.getSettingLPr(appId);
- if (obj != null) {
- if (obj instanceof SharedUserSetting) {
- callerSignature =
- ((SharedUserSetting) obj).signatures.mSigningDetails.getSignatures();
- } else if (obj instanceof PackageSetting) {
- callerSignature =
- ((PackageSetting) obj).getSigningDetails().getSignatures();
+ Pair<PackageStateInternal, SharedUserApi> either =
+ computer.getPackageOrSharedUser(appId);
+ if (either != null) {
+ if (either.first != null) {
+ callerSignature = either.first.getSigningDetails().getSignatures();
} else {
- throw new SecurityException("Bad object " + obj + " for uid " + callingUid);
+ callerSignature = either.second.getSigningDetails().getSignatures();
}
} else {
throw new SecurityException("Unknown calling UID: " + callingUid);
@@ -4660,22 +4622,22 @@ public class PackageManagerService extends IPackageManager.Stub
// Verify: can't set installerPackageName to a package that is
// not signed with the same cert as the caller.
- if (installerPackageSetting != null) {
+ if (installerPackageState != null) {
if (compareSignatures(callerSignature,
- installerPackageSetting.getSigningDetails().getSignatures())
+ installerPackageState.getSigningDetails().getSignatures())
!= PackageManager.SIGNATURE_MATCH) {
throw new SecurityException(
"Caller does not have same cert as new installer package "
- + installerPackageName);
+ + installerPackageName);
}
}
// Verify: if target already has an installer package, it must
// be signed with the same cert as the caller.
String targetInstallerPackageName =
- targetPackageSetting.getInstallSource().installerPackageName;
- PackageSetting targetInstallerPkgSetting = targetInstallerPackageName == null ? null :
- mSettings.getPackageLPr(targetInstallerPackageName);
+ targetPackageState.getInstallSource().installerPackageName;
+ PackageStateInternal targetInstallerPkgSetting = targetInstallerPackageName == null
+ ? null : computer.getPackageStateInternal(targetInstallerPackageName);
if (targetInstallerPkgSetting != null) {
if (compareSignatures(callerSignature,
@@ -4683,10 +4645,10 @@ public class PackageManagerService extends IPackageManager.Stub
!= PackageManager.SIGNATURE_MATCH) {
throw new SecurityException(
"Caller does not have same cert as old installer package "
- + targetInstallerPackageName);
+ + targetInstallerPackageName);
}
} else if (mContext.checkCallingOrSelfPermission(Manifest.permission.INSTALL_PACKAGES)
- != PackageManager.PERMISSION_GRANTED) {
+ != PERMISSION_GRANTED) {
// This is probably an attempt to exploit vulnerability b/150857253 of taking
// privileged installer permissions when the installer has been uninstalled or
// was never set.
@@ -4702,17 +4664,39 @@ public class PackageManagerService extends IPackageManager.Stub
+ Manifest.permission.INSTALL_PACKAGES);
} else {
// If change disabled, fail silently for backwards compatibility
- return;
+ return false;
}
} finally {
Binder.restoreCallingIdentity(binderToken);
}
}
- // Okay!
- targetPackageSetting.setInstallerPackageName(installerPackageName);
- mSettings.addInstallerPackageNames(targetPackageSetting.getInstallSource());
- mAppsFilter.addPackage(targetPackageSetting);
+ return true;
+ };
+ PackageStateMutator.InitialState initialState = recordInitialState();
+ boolean allowed = executeWithConsistentComputerReturningThrowing(implementation);
+ if (allowed) {
+ // TODO: Need to lock around here to handle mSettings.addInstallerPackageNames,
+ // should find an alternative which avoids any race conditions
+ PackageStateInternal targetPackageState;
+ synchronized (mLock) {
+ PackageStateMutator.Result result = commitPackageStateMutation(initialState,
+ targetPackage, state -> state.setInstaller(installerPackageName));
+ if (result.isPackagesChanged() || result.isStateChanged()) {
+ synchronized (mPackageStateWriteLock) {
+ allowed = executeWithConsistentComputerReturningThrowing(implementation);
+ if (allowed) {
+ commitPackageStateMutation(null, targetPackage,
+ state -> state.setInstaller(installerPackageName));
+ } else {
+ return;
+ }
+ }
+ }
+ targetPackageState = getPackageStateInternal(targetPackage);
+ mSettings.addInstallerPackageNames(targetPackageState.getInstallSource());
+ }
+ mAppsFilter.addPackage(targetPackageState);
scheduleWriteSettings();
}
}
@@ -4725,22 +4709,43 @@ public class PackageManagerService extends IPackageManager.Stub
}
mInjector.getSystemService(AppOpsManager.class).checkPackage(Binder.getCallingUid(),
callerPackageName);
- synchronized (mLock) {
- PackageSetting ps = mSettings.getPackageLPr(packageName);
- if (ps == null || shouldFilterApplication(
- ps, Binder.getCallingUid(), UserHandle.getCallingUserId())) {
+
+ final PackageStateMutator.InitialState initialState = recordInitialState();
+
+ final FunctionalUtils.ThrowingFunction<Computer, PackageStateMutator.Result>
+ implementation = computer -> {
+ PackageStateInternal packageState = computer.getPackageStateFiltered(packageName,
+ Binder.getCallingUid(), UserHandle.getCallingUserId());
+ if (packageState == null) {
throw new IllegalArgumentException("Unknown target package " + packageName);
}
- if (!Objects.equals(callerPackageName, ps.getInstallSource().installerPackageName)) {
+
+ if (!Objects.equals(callerPackageName,
+ packageState.getInstallSource().installerPackageName)) {
throw new IllegalArgumentException("Calling package " + callerPackageName
+ " is not installer for " + packageName);
}
- if (ps.getCategoryOverride() != categoryHint) {
- ps.setCategoryOverride(categoryHint);
- scheduleWriteSettings();
+ if (packageState.getCategoryOverride() != categoryHint) {
+ return commitPackageStateMutation(initialState,
+ packageName, state -> state.setCategoryOverride(categoryHint));
+ } else {
+ return null;
+ }
+ };
+
+ PackageStateMutator.Result result = executeWithConsistentComputerReturning(implementation);
+ if (result != null && result.isStateChanged() && !result.isSpecificPackageNull()) {
+ // TODO: Specific return value of what state changed?
+ // The installer on record might have changed, retry with lock
+ synchronized (mPackageStateWriteLock) {
+ result = executeWithConsistentComputerReturning(implementation);
}
}
+
+ if (result != null && result.isCommitted()) {
+ scheduleWriteSettings();
+ }
}
/**
@@ -4792,32 +4797,6 @@ public class PackageManagerService extends IPackageManager.Stub
});
}
- // Stop gap method to allow mutating a package setting before commit on write is added
- private void mutateInstalledPackageSetting(@NonNull String packageName, int callingUid,
- @UserIdInt int userId,
- @NonNull FunctionalUtils.ThrowingConsumer<PackageSetting> consumerLocked) {
- synchronized (mLock) {
- PackageSetting ps = mSettings.getPackageLPr(packageName);
- if (ps == null) {
- Slog.w(TAG, "Failed to get package setting. Package " + packageName
- + " is not installed");
- return;
- }
- if (!ps.getInstalled(userId)) {
- Slog.w(TAG, "Failed to get package setting. Package " + packageName
- + " is not installed for user " + userId);
- return;
- }
- if (shouldFilterApplication(ps, callingUid, userId)) {
- Slog.w(TAG, "Failed to get package setting. Package " + packageName
- + " is not visible to the calling app");
- return;
- }
-
- consumerLocked.accept(ps);
- }
- }
-
void notifyPackageChangeObservers(PackageChangeEvent event) {
try {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "notifyPackageChangeObservers");
@@ -4872,9 +4851,8 @@ public class PackageManagerService extends IPackageManager.Stub
return mComputer.resolveExternalPackageName(pkg);
}
- @GuardedBy("mLock")
- String resolveInternalPackageNameLPr(String packageName, long versionCode) {
- return mComputer.resolveInternalPackageNameLPr(packageName, versionCode);
+ String resolveInternalPackageName(String packageName, long versionCode) {
+ return mComputer.resolveInternalPackageName(packageName, versionCode);
}
boolean isCallerVerifier(int callingUid) {
@@ -4947,28 +4925,29 @@ public class PackageManagerService extends IPackageManager.Stub
int userId) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.DELETE_PACKAGES, null);
- // TODO (b/157774108): This should fail on non-existent packages.
- synchronized (mLock) {
- AndroidPackage pkg = mPackages.get(packageName);
- if (pkg != null) {
- // Cannot block uninstall SDK libs as they are controlled by SDK manager.
- if (pkg.getSdkLibName() != null) {
- Slog.w(TAG, "Cannot block uninstall of package: " + packageName
- + " providing SDK library: " + pkg.getSdkLibName());
- return false;
- }
- // Cannot block uninstall of static shared libs as they are
- // considered a part of the using app (emulating static linking).
- // Also static libs are installed always on internal storage.
- if (pkg.getStaticSharedLibName() != null) {
- Slog.w(TAG, "Cannot block uninstall of package: " + packageName
- + " providing static shared library: " + pkg.getStaticSharedLibName());
- return false;
- }
+ PackageStateInternal packageState = getPackageStateInternal(packageName);
+ if (packageState != null && packageState.getPkg() != null) {
+ AndroidPackage pkg = packageState.getPkg();
+ // Cannot block uninstall SDK libs as they are controlled by SDK manager.
+ if (pkg.getSdkLibName() != null) {
+ Slog.w(TAG, "Cannot block uninstall of package: " + packageName
+ + " providing SDK library: " + pkg.getSdkLibName());
+ return false;
+ }
+ // Cannot block uninstall of static shared libs as they are
+ // considered a part of the using app (emulating static linking).
+ // Also static libs are installed always on internal storage.
+ if (pkg.getStaticSharedLibName() != null) {
+ Slog.w(TAG, "Cannot block uninstall of package: " + packageName
+ + " providing static shared library: " + pkg.getStaticSharedLibName());
+ return false;
}
+ }
+ synchronized (mLock) {
mSettings.setBlockUninstallLPw(userId, packageName, blockUninstall);
- mSettings.writePackageRestrictionsLPr(userId);
}
+
+ scheduleWritePackageRestrictions(userId);
return true;
}
@@ -4978,24 +4957,17 @@ public class PackageManagerService extends IPackageManager.Stub
}
@Override
- public boolean setRequiredForSystemUser(String packageName, boolean systemUserApp) {
+ public boolean setRequiredForSystemUser(String packageName, boolean requiredForSystemUser) {
PackageManagerServiceUtils.enforceSystemOrRoot(
"setRequiredForSystemUser can only be run by the system or root");
- synchronized (mLock) {
- PackageSetting ps = mSettings.getPackageLPr(packageName);
- if (ps == null) {
- Log.w(TAG, "Package doesn't exist: " + packageName);
- return false;
- }
- if (systemUserApp) {
- ps.setPrivateFlags(ps.getPrivateFlags()
- | ApplicationInfo.PRIVATE_FLAG_REQUIRED_FOR_SYSTEM_USER);
- } else {
- ps.setPrivateFlags(ps.getPrivateFlags()
- & ~ApplicationInfo.PRIVATE_FLAG_REQUIRED_FOR_SYSTEM_USER);
- }
- writeSettingsLPrTEMP();
+
+ PackageStateMutator.Result result = commitPackageStateMutation(null, packageName,
+ packageState -> packageState.setRequiredForSystemUser(requiredForSystemUser));
+ if (!result.isCommitted()) {
+ return false;
}
+
+ scheduleWriteSettings();
return true;
}
@@ -5004,12 +4976,8 @@ public class PackageManagerService extends IPackageManager.Stub
PackageManagerServiceUtils.enforceSystemOrRoot(
"Only the system can clear all profile data");
- final AndroidPackage pkg;
- synchronized (mLock) {
- pkg = mPackages.get(packageName);
- }
-
- try (PackageFreezer freezer = freezePackage(packageName, "clearApplicationProfileData")) {
+ final AndroidPackage pkg = getPackage(packageName);
+ try (PackageFreezer ignored = freezePackage(packageName, "clearApplicationProfileData")) {
synchronized (mInstallLock) {
mAppDataHelper.clearAppProfilesLIF(pkg);
}
@@ -5026,50 +4994,53 @@ public class PackageManagerService extends IPackageManager.Stub
enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */,
false /* checkShell */, "clear application data");
- final boolean filterApp;
- synchronized (mLock) {
- final PackageSetting ps = mSettings.getPackageLPr(packageName);
- filterApp = shouldFilterApplication(ps, callingUid, userId);
+ if (mComputer.getPackageStateFiltered(packageName, callingUid, userId) == null) {
+ if (observer != null) {
+ mHandler.post(() -> {
+ try {
+ observer.onRemoveCompleted(packageName, false);
+ } catch (RemoteException e) {
+ Log.i(TAG, "Observer no longer exists.");
+ }
+ });
+ }
+ return;
}
- if (!filterApp && mProtectedPackages.isPackageDataProtected(userId, packageName)) {
+ if (mProtectedPackages.isPackageDataProtected(userId, packageName)) {
throw new SecurityException("Cannot clear data for a protected package: "
+ packageName);
}
+
// Queue up an async operation since the package deletion may take a little while.
mHandler.post(new Runnable() {
public void run() {
mHandler.removeCallbacks(this);
final boolean succeeded;
- if (!filterApp) {
- try (PackageFreezer freezer = freezePackage(packageName,
- "clearApplicationUserData")) {
- synchronized (mInstallLock) {
- succeeded = clearApplicationUserDataLIF(packageName, userId);
- }
- synchronized (mLock) {
- mInstantAppRegistry.deleteInstantApplicationMetadataLPw(
- packageName, userId);
- if (succeeded) {
- resetComponentEnabledSettingsIfNeededLPw(packageName, userId);
- }
- }
+ try (PackageFreezer freezer = freezePackage(packageName,
+ "clearApplicationUserData")) {
+ synchronized (mInstallLock) {
+ succeeded = clearApplicationUserDataLIF(packageName, userId);
}
- if (succeeded) {
- // invoke DeviceStorageMonitor's update method to clear any notifications
- DeviceStorageMonitorInternal dsm = LocalServices
- .getService(DeviceStorageMonitorInternal.class);
- if (dsm != null) {
- dsm.checkMemory();
- }
- if (checkPermission(Manifest.permission.SUSPEND_APPS, packageName, userId)
- == PERMISSION_GRANTED) {
- unsuspendForSuspendingPackage(packageName, userId);
- removeAllDistractingPackageRestrictions(userId);
- flushPackageRestrictionsAsUserInternalLocked(userId);
+ mInstantAppRegistry.deleteInstantApplicationMetadata(packageName, userId);
+ synchronized (mLock) {
+ if (succeeded) {
+ resetComponentEnabledSettingsIfNeededLPw(packageName, userId);
}
}
- } else {
- succeeded = false;
+ }
+ if (succeeded) {
+ // invoke DeviceStorageMonitor's update method to clear any notifications
+ DeviceStorageMonitorInternal dsm = LocalServices
+ .getService(DeviceStorageMonitorInternal.class);
+ if (dsm != null) {
+ dsm.checkMemory();
+ }
+ if (checkPermission(Manifest.permission.SUSPEND_APPS, packageName, userId)
+ == PERMISSION_GRANTED) {
+ unsuspendForSuspendingPackage(packageName, userId);
+ removeAllDistractingPackageRestrictions(userId);
+ flushPackageRestrictionsAsUserInternalLocked(userId);
+ }
}
if (observer != null) {
try {
@@ -5089,17 +5060,7 @@ public class PackageManagerService extends IPackageManager.Stub
}
// Try finding details about the requested package
- AndroidPackage pkg;
- PackageSetting ps;
- synchronized (mLock) {
- pkg = mPackages.get(packageName);
- ps = mSettings.getPackageLPr(packageName);
- if (pkg == null) {
- if (ps != null) {
- pkg = ps.getPkg();
- }
- }
- }
+ AndroidPackage pkg = getPackage(packageName);
if (pkg == null) {
Slog.w(TAG, "Package named '" + packageName + "' doesn't exist.");
return false;
@@ -5122,7 +5083,8 @@ public class PackageManagerService extends IPackageManager.Stub
} else {
flags = 0;
}
- mAppDataHelper.prepareAppDataContentsLIF(pkg, ps, userId, flags);
+ mAppDataHelper.prepareAppDataContentsLIF(pkg, getPackageStateInternal(packageName), userId,
+ flags);
return true;
}
@@ -5165,7 +5127,7 @@ public class PackageManagerService extends IPackageManager.Stub
updateSequenceNumberLP(pkgSetting, new int[] { userId });
updateInstantAppInstallerLocked(packageName);
- scheduleWritePackageRestrictionsLocked(userId);
+ scheduleWritePackageRestrictions(userId);
final ArrayList<String> pendingComponents = mPendingBroadcasts.get(userId, packageName);
if (pendingComponents == null) {
@@ -5214,10 +5176,7 @@ public class PackageManagerService extends IPackageManager.Stub
final int hasAccessInstantApps = mContext.checkCallingOrSelfPermission(
android.Manifest.permission.ACCESS_INSTANT_APPS);
- final AndroidPackage pkg;
- synchronized (mLock) {
- pkg = mPackages.get(packageName);
- }
+ final AndroidPackage pkg = getPackage(packageName);
// Queue up an async operation since the package deletion may take a little while.
mHandler.post(() -> {
@@ -5438,8 +5397,8 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
resolver.addFilter(newFilter);
- scheduleWritePackageRestrictionsLocked(sourceUserId);
}
+ scheduleWritePackageRestrictions(sourceUserId);
}
@Override
@@ -5460,8 +5419,8 @@ public class PackageManagerService extends IPackageManager.Stub
resolver.removeFilter(filter);
}
}
- scheduleWritePackageRestrictionsLocked(sourceUserId);
}
+ scheduleWritePackageRestrictions(sourceUserId);
}
// Enforcing that callingUid is owning pkg on userId
@@ -5733,12 +5692,8 @@ public class PackageManagerService extends IPackageManager.Stub
@Override
public void setUpdateAvailable(String packageName, boolean updateAvailable) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES, null);
- synchronized (mLock) {
- final PackageSetting pkgSetting = mSettings.getPackageLPr(packageName);
- if (pkgSetting != null) {
- pkgSetting.setUpdateAvailable(updateAvailable);
- }
- }
+ commitPackageStateMutation(null, packageName, state ->
+ state.setUpdateAvailable(updateAvailable));
}
@Override
@@ -5763,32 +5718,32 @@ public class PackageManagerService extends IPackageManager.Stub
}
int callingUid = Binder.getCallingUid();
-
String componentPkgName = componentName.getPackageName();
- int componentUid = getPackageUid(componentPkgName, 0, userId);
- if (!UserHandle.isSameApp(callingUid, componentUid)) {
- throw new SecurityException("The calling UID (" + callingUid + ")"
- + " does not match the target UID");
- }
- String allowedCallerPkg = mContext.getString(R.string.config_overrideComponentUiPackage);
- if (TextUtils.isEmpty(allowedCallerPkg)) {
- throw new SecurityException(
- "There is no package defined as allowed to change a component's label or icon");
- }
+ boolean changed = executeWithConsistentComputerReturning(computer -> {
+ int componentUid = getPackageUid(componentPkgName, 0, userId);
+ if (!UserHandle.isSameApp(callingUid, componentUid)) {
+ throw new SecurityException("The calling UID (" + callingUid + ")"
+ + " does not match the target UID");
+ }
- int allowedCallerUid = getPackageUid(allowedCallerPkg, PackageManager.MATCH_SYSTEM_ONLY,
- userId);
- if (allowedCallerUid == -1 || !UserHandle.isSameApp(callingUid, allowedCallerUid)) {
- throw new SecurityException("The calling UID (" + callingUid + ")"
- + " is not allowed to change a component's label or icon");
- }
+ String allowedCallerPkg =
+ mContext.getString(R.string.config_overrideComponentUiPackage);
+ if (TextUtils.isEmpty(allowedCallerPkg)) {
+ throw new SecurityException( "There is no package defined as allowed to change a "
+ + "component's label or icon");
+ }
- synchronized (mLock) {
- AndroidPackage pkg = mPackages.get(componentPkgName);
- PackageSetting pkgSetting = getPackageSettingForMutation(componentPkgName);
- if (pkg == null || pkgSetting == null
- || (!pkg.isSystem() && !pkgSetting.getPkgState().isUpdatedSystemApp())) {
+ int allowedCallerUid = getPackageUid(allowedCallerPkg, PackageManager.MATCH_SYSTEM_ONLY,
+ userId);
+ if (allowedCallerUid == -1 || !UserHandle.isSameApp(callingUid, allowedCallerUid)) {
+ throw new SecurityException("The calling UID (" + callingUid + ")"
+ + " is not allowed to change a component's label or icon");
+ }
+ PackageStateInternal packageState = computer.getPackageStateInternal(componentPkgName);
+ if (packageState == null || packageState.getPkg() == null
+ || (!packageState.isSystem()
+ && !packageState.getTransientState().isUpdatedSystemApp())) {
throw new SecurityException(
"Changing the label is not allowed for " + componentName);
}
@@ -5797,13 +5752,23 @@ public class PackageManagerService extends IPackageManager.Stub
throw new IllegalArgumentException("Component " + componentName + " not found");
}
- if (!pkgSetting.overrideNonLocalizedLabelAndIcon(componentName, nonLocalizedLabel,
- icon, userId)) {
- // Nothing changed
- return;
- }
+ Pair<String, Integer> overrideLabelIcon = packageState.getUserStateOrDefault(userId)
+ .getOverrideLabelIconForComponent(componentName);
+
+ String existingLabel = overrideLabelIcon == null ? null : overrideLabelIcon.first;
+ Integer existingIcon = overrideLabelIcon == null ? null : overrideLabelIcon.second;
+
+ return !TextUtils.equals(existingLabel, nonLocalizedLabel)
+ || !Objects.equals(existingIcon, icon);
+ });
+ if (!changed) {
+ return;
}
+ commitPackageStateMutation(null, componentPkgName,
+ state -> state.userState(userId)
+ .setComponentLabelIcon(componentName, nonLocalizedLabel, icon));
+
ArrayList<String> components = mPendingBroadcasts.get(userId, componentPkgName);
if (components == null) {
components = new ArrayList<>();
@@ -6076,7 +6041,7 @@ public class PackageManagerService extends IPackageManager.Stub
if (isSynchronous) {
flushPackageRestrictionsAsUserInternalLocked(userId);
} else {
- scheduleWritePackageRestrictionsLocked(userId);
+ scheduleWritePackageRestrictions(userId);
}
if (scheduleBroadcastMessage) {
if (!mHandler.hasMessages(SEND_PENDING_BROADCAST)) {
@@ -6187,9 +6152,11 @@ public class PackageManagerService extends IPackageManager.Stub
// NOTE: this invokes synchronous disk access, so callers using this
// method should consider running on a background thread
mSettings.writePackageRestrictionsLPr(userId);
- mDirtyUsers.remove(userId);
- if (mDirtyUsers.isEmpty()) {
- mHandler.removeMessages(WRITE_PACKAGE_RESTRICTIONS);
+ synchronized (mDirtyUsers) {
+ mDirtyUsers.remove(userId);
+ if (mDirtyUsers.isEmpty()) {
+ mHandler.removeMessages(WRITE_PACKAGE_RESTRICTIONS);
+ }
}
}
@@ -6216,29 +6183,50 @@ public class PackageManagerService extends IPackageManager.Stub
public void setPackageStoppedState(String packageName, boolean stopped, int userId) {
if (!mUserManager.exists(userId)) return;
final int callingUid = Binder.getCallingUid();
- if (getInstantAppPackageName(callingUid) != null) {
- return;
- }
- final int permission = mContext.checkCallingOrSelfPermission(
- android.Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE);
- final boolean allowedByPermission = (permission == PackageManager.PERMISSION_GRANTED);
- if (!allowedByPermission
- && !ArrayUtils.contains(getPackagesForUid(callingUid), packageName)) {
- throw new SecurityException(
- "Permission Denial: attempt to change stopped state from pid="
- + Binder.getCallingPid()
- + ", uid=" + callingUid + ", package=" + packageName);
- }
- enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */,
- true /* checkShell */, "stop package");
- // writer
- synchronized (mLock) {
- final PackageSetting ps = mSettings.getPackageLPr(packageName);
- if (!shouldFilterApplication(ps, callingUid, userId)
- && mSettings.setPackageStoppedStateLPw(this, packageName, stopped, userId)) {
- scheduleWritePackageRestrictionsLocked(userId);
+ Pair<Boolean, String> wasNotLaunchedAndInstallerPackageName =
+ executeWithConsistentComputerReturningThrowing(computer -> {
+ if (computer.getInstantAppPackageName(callingUid) != null) {
+ return null;
+ }
+ final int permission = mContext.checkCallingOrSelfPermission(
+ android.Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE);
+ final boolean allowedByPermission = (permission == PackageManager.PERMISSION_GRANTED);
+ if (!allowedByPermission
+ && !ArrayUtils.contains(computer.getPackagesForUid(callingUid), packageName)) {
+ throw new SecurityException(
+ "Permission Denial: attempt to change stopped state from pid="
+ + Binder.getCallingPid()
+ + ", uid=" + callingUid + ", package=" + packageName);
+ }
+ computer.enforceCrossUserPermission(callingUid, userId,
+ true /* requireFullPermission */, true /* checkShell */, "stop package");
+
+ final PackageStateInternal packageState = computer.getPackageStateInternal(packageName);
+ final PackageUserState PackageUserState = packageState == null
+ ? null : packageState.getUserStateOrDefault(userId);
+ if (packageState == null
+ || computer.shouldFilterApplication(packageState, callingUid, userId)
+ || PackageUserState.isStopped() == stopped) {
+ return null;
}
+
+ return Pair.create(PackageUserState.isNotLaunched(),
+ packageState.getInstallSource().installerPackageName);
+ });
+ if (wasNotLaunchedAndInstallerPackageName != null) {
+ boolean wasNotLaunched = wasNotLaunchedAndInstallerPackageName.first;
+
+ commitPackageStateMutation(null, packageName, packageState -> {
+ PackageUserStateWrite userState = packageState.userState(userId);
+ userState.setStopped(stopped);
+ if (wasNotLaunched) {
+ userState.setNotLaunched(false);
+ }
+ });
+
+ scheduleWritePackageRestrictions(userId);
}
+
// If this would cause the app to leave force-stop, then also make sure to unhibernate the
// app if needed.
if (!stopped) {
@@ -6490,19 +6478,17 @@ public class PackageManagerService extends IPackageManager.Stub
}
void dumpSnapshotStats(PrintWriter pw, boolean isBrief) {
- if (!mSnapshotEnabled) {
- pw.println(" Snapshots disabled");
- } else {
- int hits = 0;
- int level = sSnapshotCorked.get();
- synchronized (mSnapshotLock) {
- if (mSnapshotComputer != null) {
- hits = mSnapshotComputer.getUsed();
- }
+ if (mSnapshotStatistics == null) {
+ return;
+ }
+ int hits = 0;
+ synchronized (mSnapshotLock) {
+ if (mSnapshotComputer != null) {
+ hits = mSnapshotComputer.getUsed();
}
- final long now = SystemClock.currentTimeMicro();
- mSnapshotStatistics.dump(pw, " ", now, hits, level, isBrief);
}
+ final long now = SystemClock.currentTimeMicro();
+ mSnapshotStatistics.dump(pw, " ", now, hits, -1, isBrief);
}
/**
@@ -6641,8 +6627,9 @@ public class PackageManagerService extends IPackageManager.Stub
if (getInstallLocation() == loc) {
return true;
}
- if (loc == PackageHelper.APP_INSTALL_AUTO || loc == PackageHelper.APP_INSTALL_INTERNAL
- || loc == PackageHelper.APP_INSTALL_EXTERNAL) {
+ if (loc == InstallLocationUtils.APP_INSTALL_AUTO
+ || loc == InstallLocationUtils.APP_INSTALL_INTERNAL
+ || loc == InstallLocationUtils.APP_INSTALL_EXTERNAL) {
android.provider.Settings.Global.putInt(mContext.getContentResolver(),
android.provider.Settings.Global.DEFAULT_INSTALL_LOCATION, loc);
return true;
@@ -6655,21 +6642,23 @@ public class PackageManagerService extends IPackageManager.Stub
// allow instant app access
return android.provider.Settings.Global.getInt(mContext.getContentResolver(),
android.provider.Settings.Global.DEFAULT_INSTALL_LOCATION,
- PackageHelper.APP_INSTALL_AUTO);
+ InstallLocationUtils.APP_INSTALL_AUTO);
}
/** Called by UserManagerService */
void cleanUpUser(UserManagerService userManager, @UserIdInt int userId) {
synchronized (mLock) {
- mDirtyUsers.remove(userId);
+ synchronized (mDirtyUsers) {
+ mDirtyUsers.remove(userId);
+ }
mUserNeedsBadging.delete(userId);
mPermissionManager.onUserRemoved(userId);
mSettings.removeUserLPw(userId);
mPendingBroadcasts.remove(userId);
- mInstantAppRegistry.onUserRemovedLPw(userId);
mDeletePackageHelper.removeUnusedPackagesLPw(userManager, userId);
mAppsFilter.onUserDeleted(userId);
}
+ mInstantAppRegistry.onUserRemoved(userId);
}
/**
@@ -6688,7 +6677,7 @@ public class PackageManagerService extends IPackageManager.Stub
userTypeInstallablePackages, disallowedPackages);
}
synchronized (mLock) {
- scheduleWritePackageRestrictionsLocked(userId);
+ scheduleWritePackageRestrictions(userId);
scheduleWritePackageListLocked(userId);
mAppsFilter.onUserCreated(userId);
}
@@ -6796,20 +6785,23 @@ public class PackageManagerService extends IPackageManager.Stub
return mComputer.isPackageSignedByKeySetExactly(packageName, ks);
}
- @GuardedBy("mLock")
- private void deletePackageIfUnusedLPr(final String packageName) {
- PackageSetting ps = mSettings.getPackageLPr(packageName);
+ private void deletePackageIfUnused(final String packageName) {
+ PackageStateInternal ps = getPackageStateInternal(packageName);
if (ps == null) {
return;
}
- if (!ps.isAnyInstalled(mUserManager.getUserIds())) {
- // TODO Implement atomic delete if package is unused
- // It is currently possible that the package will be deleted even if it is installed
- // after this method returns.
- mHandler.post(() -> mDeletePackageHelper.deletePackageX(
- packageName, PackageManager.VERSION_CODE_HIGHEST,
- 0, PackageManager.DELETE_ALL_USERS, true /*removedBySystem*/));
+ final SparseArray<? extends PackageUserStateInternal> userStates = ps.getUserStates();
+ for (int index = 0; index < userStates.size(); index++) {
+ if (userStates.valueAt(index).isInstalled()) {
+ return;
+ }
}
+ // TODO Implement atomic delete if package is unused
+ // It is currently possible that the package will be deleted even if it is installed
+ // after this method returns.
+ mHandler.post(() -> mDeletePackageHelper.deletePackageX(
+ packageName, PackageManager.VERSION_CODE_HIGHEST,
+ 0, PackageManager.DELETE_ALL_USERS, true /*removedBySystem*/));
}
private AndroidPackage getPackage(String packageName) {
@@ -6985,7 +6977,7 @@ public class PackageManagerService extends IPackageManager.Stub
@Override
public PackageList getPackageList(@Nullable PackageListObserver observer) {
final ArrayList<String> list = new ArrayList<>();
- forEachPackageState(false, packageState -> {
+ forEachPackageState(packageState -> {
AndroidPackage pkg = packageState.getPkg();
if (pkg != null) {
list.add(pkg.getPackageName());
@@ -6993,18 +6985,14 @@ public class PackageManagerService extends IPackageManager.Stub
});
final PackageList packageList = new PackageList(list, observer);
if (observer != null) {
- synchronized (mLock) {
- mPackageListObservers.add(packageList);
- }
+ mPackageObserverHelper.addObserver(packageList);
}
return packageList;
}
@Override
public void removePackageListObserver(PackageListObserver observer) {
- synchronized (mLock) {
- mPackageListObservers.remove(observer);
- }
+ mPackageObserverHelper.removeObserver(observer);
}
@Override
@@ -7203,6 +7191,16 @@ public class PackageManagerService extends IPackageManager.Stub
SparseArray<String> profileOwnerPackages) {
mProtectedPackages.setDeviceAndProfileOwnerPackages(
deviceOwnerUserId, deviceOwnerPackage, profileOwnerPackages);
+ final ArraySet<Integer> usersWithPoOrDo = new ArraySet<>();
+ if (deviceOwnerPackage != null) {
+ usersWithPoOrDo.add(deviceOwnerUserId);
+ }
+ final int sz = profileOwnerPackages.size();
+ for (int i = 0; i < sz; i++) {
+ if (profileOwnerPackages.valueAt(i) != null) {
+ removeAllNonSystemPackageSuspensions(profileOwnerPackages.keyAt(i));
+ }
+ }
}
@Override
@@ -7276,32 +7274,32 @@ public class PackageManagerService extends IPackageManager.Stub
@Override
public void grantImplicitAccess(int userId, Intent intent,
int recipientAppId, int visibleUid, boolean direct, boolean retainOnUpdate) {
- synchronized (mLock) {
- final AndroidPackage visiblePackage = getPackage(visibleUid);
+ boolean accessGranted = executeWithConsistentComputerReturning(computer -> {
+ final AndroidPackage visiblePackage = computer.getPackage(visibleUid);
final int recipientUid = UserHandle.getUid(userId, recipientAppId);
- if (visiblePackage == null || getPackage(recipientUid) == null) {
- return;
+ if (visiblePackage == null || computer.getPackage(recipientUid) == null) {
+ return false;
}
- final boolean instantApp =
- isInstantAppInternal(visiblePackage.getPackageName(), userId, visibleUid);
- final boolean accessGranted;
+ final boolean instantApp = computer.isInstantAppInternal(
+ visiblePackage.getPackageName(), userId, visibleUid);
if (instantApp) {
if (!direct) {
// if the interaction that lead to this granting access to an instant app
// was indirect (i.e.: URI permission grant), do not actually execute the
// grant.
- return;
+ return false;
}
- accessGranted = mInstantAppRegistry.grantInstantAccessLPw(userId, intent,
+ return mInstantAppRegistry.grantInstantAccess(userId, intent,
recipientAppId, UserHandle.getAppId(visibleUid) /*instantAppId*/);
} else {
- accessGranted = mAppsFilter.grantImplicitAccess(recipientUid, visibleUid,
+ return mAppsFilter.grantImplicitAccess(recipientUid, visibleUid,
retainOnUpdate);
}
- if (accessGranted) {
- ApplicationPackageManager.invalidateGetPackagesForUidCache();
- }
+ });
+
+ if (accessGranted) {
+ ApplicationPackageManager.invalidateGetPackagesForUidCache();
}
}
@@ -7314,7 +7312,8 @@ public class PackageManagerService extends IPackageManager.Stub
@Override
public void pruneInstantApps() {
- mInstantAppRegistry.pruneInstantApps();
+ executeWithConsistentComputer(computer ->
+ mInstantAppRegistry.pruneInstantApps(computer));
}
@Override
@@ -7361,7 +7360,7 @@ public class PackageManagerService extends IPackageManager.Stub
@Override
public List<PackageInfo> getOverlayPackages(int userId) {
final ArrayList<PackageInfo> overlayPackages = new ArrayList<>();
- forEachPackageState(false, packageState -> {
+ forEachPackageState(packageState -> {
final AndroidPackage pkg = packageState.getPkg();
if (pkg != null && pkg.getOverlayTarget() != null) {
PackageInfo pkgInfo = generatePackageInfo(packageState, 0, userId);
@@ -7377,7 +7376,7 @@ public class PackageManagerService extends IPackageManager.Stub
@Override
public List<String> getTargetPackageNames(int userId) {
List<String> targetPackages = new ArrayList<>();
- forEachPackageState(false, packageState -> {
+ forEachPackageState(packageState -> {
final AndroidPackage pkg = packageState.getPkg();
if (pkg != null && !pkg.isOverlay()) {
targetPackages.add(pkg.getPackageName());
@@ -7390,53 +7389,8 @@ public class PackageManagerService extends IPackageManager.Stub
public boolean setEnabledOverlayPackages(int userId, @NonNull String targetPackageName,
@Nullable OverlayPaths overlayPaths,
@NonNull Set<String> outUpdatedPackageNames) {
- boolean modified = false;
- synchronized (mLock) {
- final AndroidPackage targetPkg = mPackages.get(targetPackageName);
- if (targetPackageName == null || targetPkg == null) {
- Slog.e(TAG, "failed to find package " + targetPackageName);
- return false;
- }
-
- if (targetPkg.getLibraryNames() != null) {
- // Set the overlay paths for dependencies of the shared library.
- for (final String libName : targetPkg.getLibraryNames()) {
- final SharedLibraryInfo info = getSharedLibraryInfo(libName,
- SharedLibraryInfo.VERSION_UNDEFINED);
- if (info == null) {
- continue;
- }
- final List<VersionedPackage> dependents = getPackagesUsingSharedLibrary(
- info, 0, Process.SYSTEM_UID, userId);
- if (dependents == null) {
- continue;
- }
- for (final VersionedPackage dependent : dependents) {
- final PackageSetting ps = mSettings.getPackageLPr(
- dependent.getPackageName());
- if (ps == null) {
- continue;
- }
- if (ps.setOverlayPathsForLibrary(libName, overlayPaths, userId)) {
- outUpdatedPackageNames.add(dependent.getPackageName());
- modified = true;
- }
- }
- }
- }
-
- final PackageSetting ps = mSettings.getPackageLPr(targetPackageName);
- if (ps.setOverlayPaths(overlayPaths, userId)) {
- outUpdatedPackageNames.add(targetPackageName);
- modified = true;
- }
-
- if (modified) {
- invalidatePackageInfoCache();
- }
- }
-
- return true;
+ return PackageManagerService.this.setEnabledOverlayPackages(userId, targetPackageName,
+ overlayPaths, outUpdatedPackageNames);
}
@Override
@@ -7504,15 +7458,13 @@ public class PackageManagerService extends IPackageManager.Stub
@Override
public boolean hasInstantApplicationMetadata(String packageName, int userId) {
- synchronized (mLock) {
- return mInstantAppRegistry.hasInstantApplicationMetadataLPr(packageName, userId);
- }
+ return mInstantAppRegistry.hasInstantApplicationMetadata(packageName, userId);
}
@Override
public void notifyPackageUse(String packageName, int reason) {
synchronized (mLock) {
- PackageManagerService.this.notifyPackageUseLocked(packageName, reason);
+ PackageManagerService.this.notifyPackageUseInternal(packageName, reason);
}
}
@@ -7549,30 +7501,24 @@ public class PackageManagerService extends IPackageManager.Stub
}
@Override
- public void forEachPackage(Consumer<AndroidPackage> actionLocked) {
- PackageManagerService.this.forEachPackage(actionLocked);
- }
-
- @Override
public void forEachPackageSetting(Consumer<PackageSetting> actionLocked) {
PackageManagerService.this.forEachPackageSetting(actionLocked);
}
@Override
- public void forEachPackageState(boolean locked, Consumer<PackageStateInternal> action) {
- PackageManagerService.this.forEachPackageState(locked, action);
+ public void forEachPackageState(Consumer<PackageStateInternal> action) {
+ PackageManagerService.this.forEachPackageState(action);
}
@Override
- public void forEachInstalledPackage(@NonNull Consumer<AndroidPackage> actionLocked,
- @UserIdInt int userId) {
- forEachInstalledPackage(true, actionLocked, userId);
+ public void forEachPackage(Consumer<AndroidPackage> action) {
+ PackageManagerService.this.forEachPackage(action);
}
@Override
- public void forEachInstalledPackage(boolean locked,
- @NonNull Consumer<AndroidPackage> action, int userId) {
- PackageManagerService.this.forEachInstalledPackage(locked, action, userId);
+ public void forEachInstalledPackage(@NonNull Consumer<AndroidPackage> action,
+ @UserIdInt int userId) {
+ PackageManagerService.this.forEachInstalledPackage(action, userId);
}
@Override
@@ -7695,9 +7641,7 @@ public class PackageManagerService extends IPackageManager.Stub
@Override
public void updateRuntimePermissionsFingerprint(@UserIdInt int userId) {
- synchronized (mLock) {
- mSettings.updateRuntimePermissionsFingerprintLPr(userId);
- }
+ mSettings.updateRuntimePermissionsFingerprint(userId);
}
@Override
@@ -7736,9 +7680,7 @@ public class PackageManagerService extends IPackageManager.Stub
@Override
public boolean isPermissionUpgradeNeeded(int userId) {
- synchronized (mLock) {
- return mSettings.isPermissionUpgradeNeededLPr(userId);
- }
+ return mSettings.isPermissionUpgradeNeeded(userId);
}
@Override
@@ -7910,14 +7852,104 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
+ private boolean setEnabledOverlayPackages(@UserIdInt int userId,
+ @NonNull String targetPackageName, @Nullable OverlayPaths newOverlayPaths,
+ @NonNull Set<String> outUpdatedPackageNames) {
+ synchronized (mOverlayPathsLock) {
+ final ArrayMap<String, ArraySet<String>> libNameToModifiedDependents = new ArrayMap<>();
+ Boolean targetModified = executeWithConsistentComputerReturning(computer -> {
+ final PackageStateInternal packageState = computer.getPackageStateInternal(
+ targetPackageName);
+ final AndroidPackage targetPkg =
+ packageState == null ? null : packageState.getPkg();
+ if (targetPackageName == null || targetPkg == null) {
+ Slog.e(TAG, "failed to find package " + targetPackageName);
+ return null;
+ }
+
+ if (Objects.equals(packageState.getUserStateOrDefault(userId).getOverlayPaths(),
+ newOverlayPaths)) {
+ return false;
+ }
+
+ if (targetPkg.getLibraryNames() != null) {
+ // Set the overlay paths for dependencies of the shared library.
+ for (final String libName : targetPkg.getLibraryNames()) {
+ ArraySet<String> modifiedDependents = null;
+
+ final SharedLibraryInfo info = computer.getSharedLibraryInfo(libName,
+ SharedLibraryInfo.VERSION_UNDEFINED);
+ if (info == null) {
+ continue;
+ }
+ final List<VersionedPackage> dependents = computer
+ .getPackagesUsingSharedLibrary(info, 0, Process.SYSTEM_UID, userId);
+ if (dependents == null) {
+ continue;
+ }
+ for (final VersionedPackage dependent : dependents) {
+ final PackageStateInternal dependentState =
+ computer.getPackageStateInternal(dependent.getPackageName());
+ if (dependentState == null) {
+ continue;
+ }
+ if (!Objects.equals(dependentState.getUserStateOrDefault(userId)
+ .getSharedLibraryOverlayPaths()
+ .get(libName), newOverlayPaths)) {
+ String dependentPackageName = dependent.getPackageName();
+ modifiedDependents = ArrayUtils.add(modifiedDependents,
+ dependentPackageName);
+ outUpdatedPackageNames.add(dependentPackageName);
+ }
+ }
+
+ if (modifiedDependents != null) {
+ libNameToModifiedDependents.put(libName, modifiedDependents);
+ }
+ }
+ }
+
+ outUpdatedPackageNames.add(targetPackageName);
+ return true;
+ });
+
+ if (targetModified == null) {
+ // Null indicates error
+ return false;
+ } else if (!targetModified) {
+ // Treat non-modification as a successful commit
+ return true;
+ }
+
+ commitPackageStateMutation(null, mutator -> {
+ mutator.forPackage(targetPackageName)
+ .userState(userId)
+ .setOverlayPaths(newOverlayPaths);
+
+ for (int mapIndex = 0; mapIndex < libNameToModifiedDependents.size(); mapIndex++) {
+ String libName = libNameToModifiedDependents.keyAt(mapIndex);
+ ArraySet<String> modifiedDependents =
+ libNameToModifiedDependents.valueAt(mapIndex);
+ for (int setIndex = 0; setIndex < modifiedDependents.size(); setIndex++) {
+ mutator.forPackage(modifiedDependents.valueAt(setIndex))
+ .userState(userId)
+ .setOverlayPathsForLibrary(libName, newOverlayPaths);
+ }
+ }
+ });
+ }
+
+ invalidatePackageInfoCache();
+
+ return true;
+ }
+
@Override
public int getRuntimePermissionsVersion(@UserIdInt int userId) {
Preconditions.checkArgumentNonnegative(userId);
enforceAdjustRuntimePermissionsPolicyOrUpgradeRuntimePermissions(
"getRuntimePermissionVersion");
- synchronized (mLock) {
- return mSettings.getDefaultRuntimePermissionsVersionLPr(userId);
- }
+ return mSettings.getDefaultRuntimePermissionsVersion(userId);
}
@Override
@@ -7926,9 +7958,7 @@ public class PackageManagerService extends IPackageManager.Stub
Preconditions.checkArgumentNonnegative(userId);
enforceAdjustRuntimePermissionsPolicyOrUpgradeRuntimePermissions(
"setRuntimePermissionVersion");
- synchronized (mLock) {
- mSettings.setDefaultRuntimePermissionsVersionLPr(version, userId);
- }
+ mSettings.setDefaultRuntimePermissionsVersion(version, userId);
}
private void enforceAdjustRuntimePermissionsPolicyOrUpgradeRuntimePermissions(
@@ -7964,58 +7994,19 @@ public class PackageManagerService extends IPackageManager.Stub
@VisibleForTesting(visibility = Visibility.PRIVATE)
@Nullable
PackageStateInternal getPackageStateInternal(String packageName) {
- Computer computer = snapshotComputer();
- if (computer == mLiveComputer) {
- synchronized (mLiveComputerSyncLock) {
- PackageSetting pkgSetting =
- (PackageSetting) computer.getPackageStateInternal(packageName);
- if (pkgSetting == null) {
- return null;
- }
-
- return new PackageSetting(pkgSetting);
- }
- } else {
- return computer.getPackageStateInternal(packageName);
- }
+ return mComputer.getPackageStateInternal(packageName);
}
@Nullable
PackageStateInternal getPackageStateInternal(String packageName, int callingUid) {
- Computer computer = snapshotComputer();
- if (computer == mLiveComputer) {
- synchronized (mLiveComputerSyncLock) {
- PackageSetting pkgSetting =
- (PackageSetting) computer.getPackageStateInternal(packageName, callingUid);
- if (pkgSetting == null) {
- return null;
- }
-
- return new PackageSetting(pkgSetting);
- }
- } else {
- return computer.getPackageStateInternal(packageName, callingUid);
- }
+ return mComputer.getPackageStateInternal(packageName, callingUid);
}
@Nullable
PackageStateInternal getPackageStateInstalledFiltered(@NonNull String packageName,
int callingUid, @UserIdInt int userId) {
- Computer computer = snapshotComputer();
- if (computer == mLiveComputer) {
- synchronized (mLiveComputerSyncLock) {
- PackageSetting pkgSetting =
- (PackageSetting) filterPackageStateForInstalledAndFiltered(computer,
- packageName, callingUid, userId);
- if (pkgSetting == null) {
- return null;
- }
- return new PackageSetting(pkgSetting);
- }
- } else {
- return filterPackageStateForInstalledAndFiltered(computer, packageName, callingUid,
- userId);
- }
+ return filterPackageStateForInstalledAndFiltered(mComputer, packageName, callingUid,
+ userId);
}
@Nullable
@@ -8057,31 +8048,19 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
- void forEachPackageState(boolean locked, Consumer<PackageStateInternal> action) {
- if (locked) {
- synchronized (mLiveComputerSyncLock) {
- forEachPackageState(mComputer.getPackageStates(), action);
- }
- } else {
- Computer computer = snapshotComputer();
- if (computer == mLiveComputer) {
- synchronized (mLiveComputerSyncLock) {
- forEachPackageState(computer.getPackageStates(), action);
- }
- } else {
- forEachPackageState(computer.getPackageStates(), action);
- }
- }
+ void forEachPackageState(Consumer<PackageStateInternal> consumer) {
+ forEachPackageState(mComputer.getPackageStates(), consumer);
}
- void forEachPackage(Consumer<AndroidPackage> action) {
- Computer computer = snapshotComputer();
- if (computer == mLiveComputer) {
- synchronized (mLiveComputerSyncLock) {
- forEachPackage(computer.getPackageStates(), action);
+ void forEachPackage(Consumer<AndroidPackage> consumer) {
+ final ArrayMap<String, ? extends PackageStateInternal> packageStates =
+ mComputer.getPackageStates();
+ int size = packageStates.size();
+ for (int index = 0; index < size; index++) {
+ PackageStateInternal packageState = packageStates.valueAt(index);
+ if (packageState.getPkg() != null) {
+ consumer.accept(packageState.getPkg());
}
- } else {
- forEachPackage(computer.getPackageStates(), action);
}
}
@@ -8095,19 +8074,7 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
- private void forEachPackage(
- @NonNull ArrayMap<String, ? extends PackageStateInternal> packageStates,
- @NonNull Consumer<AndroidPackage> consumer) {
- int size = packageStates.size();
- for (int index = 0; index < size; index++) {
- PackageStateInternal packageState = packageStates.valueAt(index);
- if (packageState.getPkg() != null) {
- consumer.accept(packageState.getPkg());
- }
- }
- }
-
- void forEachInstalledPackage(boolean locked, @NonNull Consumer<AndroidPackage> action,
+ void forEachInstalledPackage(@NonNull Consumer<AndroidPackage> action,
@UserIdInt int userId) {
Consumer<PackageStateInternal> actionWrapped = packageState -> {
if (packageState.getPkg() != null
@@ -8115,84 +8082,37 @@ public class PackageManagerService extends IPackageManager.Stub
action.accept(packageState.getPkg());
}
};
- if (locked) {
- synchronized (mLiveComputerSyncLock) {
- forEachPackageState(mComputer.getPackageStates(), actionWrapped);
- }
- } else {
- Computer computer = snapshotComputer();
- if (computer == mLiveComputer) {
- synchronized (mLiveComputerSyncLock) {
- forEachPackageState(computer.getPackageStates(), actionWrapped);
- }
- } else {
- forEachPackageState(computer.getPackageStates(), actionWrapped);
- }
- }
+ forEachPackageState(mComputer.getPackageStates(), actionWrapped);
}
- private void executeWithConsistentComputer(
+ // TODO: Make private
+ void executeWithConsistentComputer(
@NonNull FunctionalUtils.ThrowingConsumer<Computer> consumer) {
- Computer computer = snapshotComputer();
- if (computer == mLiveComputer) {
- synchronized (mLiveComputerSyncLock) {
- consumer.accept(computer);
- }
- } else {
- consumer.accept(computer);
- }
+ consumer.accept(snapshotComputer());
}
private <T> T executeWithConsistentComputerReturning(
@NonNull FunctionalUtils.ThrowingFunction<Computer, T> function) {
- Computer computer = snapshotComputer();
- if (computer == mLiveComputer) {
- synchronized (mLiveComputerSyncLock) {
- return function.apply(computer);
- }
- } else {
- return function.apply(computer);
- }
+ return function.apply(snapshotComputer());
}
private <ExceptionType extends Exception> void executeWithConsistentComputerThrowing(
@NonNull FunctionalUtils.ThrowingCheckedConsumer<Computer, ExceptionType> consumer)
throws ExceptionType {
- Computer computer = snapshotComputer();
- if (computer == mLiveComputer) {
- synchronized (mLiveComputerSyncLock) {
- consumer.accept(computer);
- }
- } else {
- consumer.accept(computer);
- }
+ consumer.accept(snapshotComputer());
}
private <ExceptionOne extends Exception, ExceptionTwo extends Exception> void
executeWithConsistentComputerThrowing2(
@NonNull FunctionalUtils.ThrowingChecked2Consumer<Computer, ExceptionOne,
ExceptionTwo> consumer) throws ExceptionOne, ExceptionTwo {
- Computer computer = snapshotComputer();
- if (computer == mLiveComputer) {
- synchronized (mLiveComputerSyncLock) {
- consumer.accept(computer);
- }
- } else {
- consumer.accept(computer);
- }
+ consumer.accept(snapshotComputer());
}
private <T, ExceptionType extends Exception> T executeWithConsistentComputerReturningThrowing(
@NonNull FunctionalUtils.ThrowingCheckedFunction<Computer, T, ExceptionType> function)
throws ExceptionType {
- Computer computer = snapshotComputer();
- if (computer == mLiveComputer) {
- synchronized (mLiveComputerSyncLock) {
- return function.apply(computer);
- }
- } else {
- return function.apply(computer);
- }
+ return function.apply(snapshotComputer());
}
boolean isHistoricalPackageUsageAvailable() {
@@ -8276,9 +8196,7 @@ public class PackageManagerService extends IPackageManager.Stub
if (!isInstantApp(packageName, userId)) {
return null;
}
- synchronized (mLock) {
- return mInstantAppRegistry.getInstantAppAndroidIdLPw(packageName, userId);
- }
+ return mInstantAppRegistry.getInstantAppAndroidId(packageName, userId);
}
@Override
@@ -8333,10 +8251,13 @@ public class PackageManagerService extends IPackageManager.Stub
+ SET_HARMFUL_APP_WARNINGS + " permission.");
}
- synchronized (mLock) {
- mSettings.setHarmfulAppWarningLPw(packageName, warning, userId);
- scheduleWritePackageRestrictionsLocked(userId);
+ PackageStateMutator.Result result = commitPackageStateMutation(null, packageName,
+ packageState -> packageState.userState(userId)
+ .setHarmfulAppWarning(warning == null ? null : warning.toString()));
+ if (result.isSpecificPackageNull()) {
+ throw new IllegalArgumentException("Unknown package: " + packageName);
}
+ scheduleWritePackageRestrictions(userId);
}
@Nullable
@@ -8388,14 +8309,23 @@ public class PackageManagerService extends IPackageManager.Stub
@Override
public void setMimeGroup(String packageName, String mimeGroup, List<String> mimeTypes) {
enforceOwnerRights(packageName, Binder.getCallingUid());
- final boolean changed;
- synchronized (mLock) {
- changed = mSettings.getPackageLPr(packageName).setMimeGroup(mimeGroup,
- new ArraySet<>(mimeTypes));
+ mimeTypes = CollectionUtils.emptyIfNull(mimeTypes);
+ final PackageStateInternal packageState = getPackageStateInternal(packageName);
+ Set<String> existingMimeTypes = packageState.getMimeGroups().get(mimeGroup);
+ if (existingMimeTypes == null) {
+ throw new IllegalArgumentException("Unknown MIME group " + mimeGroup
+ + " for package " + packageName);
}
- if (changed) {
- applyMimeGroupChanges(packageName, mimeGroup);
+ if (existingMimeTypes.size() == mimeTypes.size()
+ && existingMimeTypes.containsAll(mimeTypes)) {
+ return;
}
+
+ ArraySet<String> mimeTypesSet = new ArraySet<>(mimeTypes);
+ commitPackageStateMutation(null, packageName, packageStateWrite -> {
+ packageStateWrite.setMimeGroup(mimeGroup, mimeTypesSet);
+ });
+ applyMimeGroupChanges(packageName, mimeGroup);
}
@Override
@@ -8426,8 +8356,15 @@ public class PackageManagerService extends IPackageManager.Stub
enforceCrossUserPermission(callingUid, userId, false /* requireFullPermission */,
false /* checkShell */, "setSplashScreenTheme");
enforceOwnerRights(packageName, callingUid);
- mutateInstalledPackageSetting(packageName, callingUid, userId,
- pkgSetting -> pkgSetting.setSplashScreenTheme(userId, themeId));
+
+ PackageStateInternal packageState = getPackageStateInstalledFiltered(packageName,
+ callingUid, userId);
+ if (packageState == null) {
+ return;
+ }
+
+ commitPackageStateMutation(null, packageName, state ->
+ state.userState(userId).setSplashScreenTheme(themeId));
}
@Override
@@ -8442,6 +8379,7 @@ public class PackageManagerService extends IPackageManager.Stub
* Temporary method that wraps mSettings.writeLPr() and calls mPermissionManager's
* writeLegacyPermissionsTEMP() beforehand.
*
+ * TODO: In the meantime, can this be moved to a schedule call?
* TODO(b/182523293): This should be removed once we finish migration of permission storage.
*/
void writeSettingsLPrTEMP() {
@@ -8532,57 +8470,54 @@ public class PackageManagerService extends IPackageManager.Stub
}
final int[] allUsers = mInjector.getUserManagerService().getUserIds();
-
- List<PerUidReadTimeouts> result = new ArrayList<>(perPackageReadTimeouts.size());
- synchronized (mLock) {
- for (int i = 0, size = perPackageReadTimeouts.size(); i < size; ++i) {
- final PerPackageReadTimeouts perPackage = perPackageReadTimeouts.get(i);
- final PackageSetting ps = mSettings.mPackages.get(perPackage.packageName);
- if (ps == null) {
- if (DEBUG_PER_UID_READ_TIMEOUTS) {
- Slog.i(TAG, "PerUidReadTimeouts: package not found = "
- + perPackage.packageName);
- }
- continue;
+ final List<PerUidReadTimeouts> result = new ArrayList<>(perPackageReadTimeouts.size());
+ for (int i = 0, size = perPackageReadTimeouts.size(); i < size; ++i) {
+ final PerPackageReadTimeouts perPackage = perPackageReadTimeouts.get(i);
+ final PackageStateInternal ps = getPackageStateInternal(perPackage.packageName);
+ if (ps == null) {
+ if (DEBUG_PER_UID_READ_TIMEOUTS) {
+ Slog.i(TAG, "PerUidReadTimeouts: package not found = "
+ + perPackage.packageName);
}
- if (ps.getAppId() < Process.FIRST_APPLICATION_UID) {
- if (DEBUG_PER_UID_READ_TIMEOUTS) {
- Slog.i(TAG, "PerUidReadTimeouts: package is system, appId="
- + ps.getAppId());
- }
- continue;
+ continue;
+ }
+ if (ps.getAppId() < Process.FIRST_APPLICATION_UID) {
+ if (DEBUG_PER_UID_READ_TIMEOUTS) {
+ Slog.i(TAG, "PerUidReadTimeouts: package is system, appId="
+ + ps.getAppId());
}
+ continue;
+ }
- final AndroidPackage pkg = ps.getPkg();
- if (pkg.getLongVersionCode() < perPackage.versionCodes.minVersionCode
- || pkg.getLongVersionCode() > perPackage.versionCodes.maxVersionCode) {
- if (DEBUG_PER_UID_READ_TIMEOUTS) {
- Slog.i(TAG, "PerUidReadTimeouts: version code is not in range = "
- + perPackage.packageName + ":" + pkg.getLongVersionCode());
- }
- continue;
+ final AndroidPackage pkg = ps.getPkg();
+ if (pkg.getLongVersionCode() < perPackage.versionCodes.minVersionCode
+ || pkg.getLongVersionCode() > perPackage.versionCodes.maxVersionCode) {
+ if (DEBUG_PER_UID_READ_TIMEOUTS) {
+ Slog.i(TAG, "PerUidReadTimeouts: version code is not in range = "
+ + perPackage.packageName + ":" + pkg.getLongVersionCode());
}
- if (perPackage.sha256certificate != null
- && !pkg.getSigningDetails().hasSha256Certificate(
- perPackage.sha256certificate)) {
- if (DEBUG_PER_UID_READ_TIMEOUTS) {
- Slog.i(TAG, "PerUidReadTimeouts: invalid certificate = "
- + perPackage.packageName + ":" + pkg.getLongVersionCode());
- }
- continue;
+ continue;
+ }
+ if (perPackage.sha256certificate != null
+ && !pkg.getSigningDetails().hasSha256Certificate(
+ perPackage.sha256certificate)) {
+ if (DEBUG_PER_UID_READ_TIMEOUTS) {
+ Slog.i(TAG, "PerUidReadTimeouts: invalid certificate = "
+ + perPackage.packageName + ":" + pkg.getLongVersionCode());
}
- for (int userId : allUsers) {
- if (!ps.getInstalled(userId)) {
- continue;
- }
- final int uid = UserHandle.getUid(userId, ps.getAppId());
- final PerUidReadTimeouts perUid = new PerUidReadTimeouts();
- perUid.uid = uid;
- perUid.minTimeUs = perPackage.timeouts.minTimeUs;
- perUid.minPendingTimeUs = perPackage.timeouts.minPendingTimeUs;
- perUid.maxPendingTimeUs = perPackage.timeouts.maxPendingTimeUs;
- result.add(perUid);
+ continue;
+ }
+ for (int userId : allUsers) {
+ if (!ps.getUserStateOrDefault(userId).isInstalled()) {
+ continue;
}
+ final int uid = UserHandle.getUid(userId, ps.getAppId());
+ final PerUidReadTimeouts perUid = new PerUidReadTimeouts();
+ perUid.uid = uid;
+ perUid.minTimeUs = perPackage.timeouts.minTimeUs;
+ perUid.minPendingTimeUs = perPackage.timeouts.minPendingTimeUs;
+ perUid.maxPendingTimeUs = perPackage.timeouts.maxPendingTimeUs;
+ result.add(perUid);
}
}
return result.toArray(new PerUidReadTimeouts[result.size()]);
@@ -8600,33 +8535,23 @@ public class PackageManagerService extends IPackageManager.Stub
private void setKeepUninstalledPackagesInternal(List<String> packageList) {
Preconditions.checkNotNull(packageList);
- List<String> removedFromList = null;
- synchronized (mLock) {
- if (mKeepUninstalledPackages != null) {
- final int packagesCount = mKeepUninstalledPackages.size();
- for (int i = 0; i < packagesCount; i++) {
- String oldPackage = mKeepUninstalledPackages.get(i);
- if (packageList != null && packageList.contains(oldPackage)) {
- continue;
- }
- if (removedFromList == null) {
- removedFromList = new ArrayList<>();
- }
- removedFromList.add(oldPackage);
- }
- }
- mKeepUninstalledPackages = new ArrayList<>(packageList);
- if (removedFromList != null) {
- final int removedCount = removedFromList.size();
- for (int i = 0; i < removedCount; i++) {
- deletePackageIfUnusedLPr(removedFromList.get(i));
- }
+ synchronized (mKeepUninstalledPackages) {
+ List<String> toRemove = new ArrayList<>(mKeepUninstalledPackages);
+ toRemove.removeAll(packageList); // Do not remove anything still in the list
+
+ mKeepUninstalledPackages.clear();
+ mKeepUninstalledPackages.addAll(packageList);
+
+ for (int i = 0; i < toRemove.size(); i++) {
+ deletePackageIfUnused(toRemove.get(i));
}
}
}
boolean shouldKeepUninstalledPackageLPr(String packageName) {
- return mKeepUninstalledPackages != null && mKeepUninstalledPackages.contains(packageName);
+ synchronized (mKeepUninstalledPackages) {
+ return mKeepUninstalledPackages.contains(packageName);
+ }
}
@Override
@@ -8904,7 +8829,7 @@ public class PackageManagerService extends IPackageManager.Stub
*/
@NonNull
public PackageStateMutator.InitialState recordInitialState() {
- return mPackageStateMutator.initialState(mChangedPackagesSequenceNumber);
+ return mPackageStateMutator.initialState(mChangedPackagesTracker.getSequenceNumber());
}
/**
@@ -8917,7 +8842,7 @@ public class PackageManagerService extends IPackageManager.Stub
@NonNull Consumer<PackageStateMutator> consumer) {
synchronized (mPackageStateWriteLock) {
final PackageStateMutator.Result result = mPackageStateMutator.generateResult(
- initialState, mChangedPackagesSequenceNumber);
+ initialState, mChangedPackagesTracker.getSequenceNumber());
if (result != PackageStateMutator.Result.SUCCESS) {
return result;
}
@@ -8939,7 +8864,7 @@ public class PackageManagerService extends IPackageManager.Stub
@NonNull Consumer<PackageStateWrite> consumer) {
synchronized (mPackageStateWriteLock) {
final PackageStateMutator.Result result = mPackageStateMutator.generateResult(
- initialState, mChangedPackagesSequenceNumber);
+ initialState, mChangedPackagesTracker.getSequenceNumber());
if (result != PackageStateMutator.Result.SUCCESS) {
return result;
}
@@ -8951,9 +8876,14 @@ public class PackageManagerService extends IPackageManager.Stub
consumer.accept(state);
}
- onChanged();
+ state.onChanged();
}
return PackageStateMutator.Result.SUCCESS;
}
+
+ void notifyInstantAppPackageInstalled(String packageName, int[] newUsers) {
+ executeWithConsistentComputer(computer ->
+ mInstantAppRegistry.onPackageInstalled(computer, packageName, newUsers));
+ }
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java b/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java
index db606863248a..1caa76d2435a 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java
@@ -57,6 +57,7 @@ public final class PackageManagerServiceTestParams {
public IncrementalManager incrementalManager;
public PackageInstallerService installerService;
public InstantAppRegistry instantAppRegistry;
+ public ChangedPackagesTracker changedPackagesTracker = new ChangedPackagesTracker();
public InstantAppResolverConnection instantAppResolverConnection;
public ComponentName instantAppResolverSettingsComponent;
public boolean isPreNmr1Upgrade;
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index dcc4386a0dc7..d6340b5bc811 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -43,6 +43,7 @@ import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.ComponentInfo;
import android.content.pm.PackageInfoLite;
+import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
import android.content.pm.PackagePartitions;
import android.content.pm.ResolveInfo;
@@ -79,8 +80,8 @@ import android.util.Printer;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
+import com.android.internal.content.InstallLocationUtils;
import com.android.internal.content.NativeLibraryHelper;
-import com.android.internal.content.PackageHelper;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FastPrintWriter;
import com.android.internal.util.HexDump;
@@ -463,18 +464,18 @@ public class PackageManagerServiceUtils {
* <p>The rationale is that {@code disabledPkg} is a PackageSetting backed by xml files in /data
* and is not tamperproof.
*/
- private static boolean matchSignatureInSystem(PackageSetting pkgSetting,
- PackageSetting disabledPkgSetting) {
- if (pkgSetting.getSigningDetails().checkCapability(
+ private static boolean matchSignatureInSystem(@NonNull String packageName,
+ @NonNull SigningDetails signingDetails, PackageSetting disabledPkgSetting) {
+ if (signingDetails.checkCapability(
disabledPkgSetting.getSigningDetails(),
SigningDetails.CertCapabilities.INSTALLED_DATA)
|| disabledPkgSetting.getSigningDetails().checkCapability(
- pkgSetting.getSigningDetails(),
+ signingDetails,
SigningDetails.CertCapabilities.ROLLBACK)) {
return true;
} else {
logCriticalInfo(Log.ERROR, "Updated system app mismatches cert on /system: " +
- pkgSetting.getPackageName());
+ packageName);
return false;
}
}
@@ -536,7 +537,8 @@ public class PackageManagerServiceUtils {
}
if (!match && isApkVerificationForced(disabledPkgSetting)) {
- match = matchSignatureInSystem(pkgSetting, disabledPkgSetting);
+ match = matchSignatureInSystem(packageName, pkgSetting.getSigningDetails(),
+ disabledPkgSetting);
}
if (!match && isRollback) {
@@ -804,27 +806,37 @@ public class PackageManagerServiceUtils {
final PackageInfoLite ret = new PackageInfoLite();
if (packagePath == null || pkg == null) {
Slog.i(TAG, "Invalid package file " + packagePath);
- ret.recommendedInstallLocation = PackageHelper.RECOMMEND_FAILED_INVALID_APK;
+ ret.recommendedInstallLocation = InstallLocationUtils.RECOMMEND_FAILED_INVALID_APK;
return ret;
}
final File packageFile = new File(packagePath);
final long sizeBytes;
try {
- sizeBytes = PackageHelper.calculateInstalledSize(pkg, abiOverride);
+ sizeBytes = InstallLocationUtils.calculateInstalledSize(pkg, abiOverride);
} catch (IOException e) {
if (!packageFile.exists()) {
- ret.recommendedInstallLocation = PackageHelper.RECOMMEND_FAILED_INVALID_URI;
+ ret.recommendedInstallLocation = InstallLocationUtils.RECOMMEND_FAILED_INVALID_URI;
} else {
- ret.recommendedInstallLocation = PackageHelper.RECOMMEND_FAILED_INVALID_APK;
+ ret.recommendedInstallLocation = InstallLocationUtils.RECOMMEND_FAILED_INVALID_APK;
}
return ret;
}
- final int recommendedInstallLocation = PackageHelper.resolveInstallLocation(context,
- pkg.getPackageName(), pkg.getInstallLocation(), sizeBytes, flags);
-
+ final PackageInstaller.SessionParams sessionParams = new PackageInstaller.SessionParams(
+ PackageInstaller.SessionParams.MODE_INVALID);
+ sessionParams.appPackageName = pkg.getPackageName();
+ sessionParams.installLocation = pkg.getInstallLocation();
+ sessionParams.sizeBytes = sizeBytes;
+ sessionParams.installFlags = flags;
+ final int recommendedInstallLocation;
+ try {
+ recommendedInstallLocation = InstallLocationUtils.resolveInstallLocation(context,
+ sessionParams);
+ } catch (IOException e) {
+ throw new IllegalStateException(e);
+ }
ret.packageName = pkg.getPackageName();
ret.splitNames = pkg.getSplitNames();
ret.versionCode = pkg.getVersionCode();
@@ -836,7 +848,6 @@ public class PackageManagerServiceUtils {
ret.recommendedInstallLocation = recommendedInstallLocation;
ret.multiArch = pkg.isMultiArch();
ret.debuggable = pkg.isDebuggable();
-
return ret;
}
@@ -856,7 +867,7 @@ public class PackageManagerServiceUtils {
throw new PackageManagerException(result.getErrorCode(),
result.getErrorMessage(), result.getException());
}
- return PackageHelper.calculateInstalledSize(result.getResult(), abiOverride);
+ return InstallLocationUtils.calculateInstalledSize(result.getResult(), abiOverride);
} catch (PackageManagerException | IOException e) {
Slog.w(TAG, "Failed to calculate installed size: " + e);
return -1;
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 99f70b206b65..d4fcd06a6548 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -98,7 +98,7 @@ import android.util.PrintWriterPrinter;
import android.util.Slog;
import android.util.SparseArray;
-import com.android.internal.content.PackageHelper;
+import com.android.internal.content.InstallLocationUtils;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
@@ -593,7 +593,7 @@ class PackageManagerShellCommand extends ShellCommand {
null /* splitApkPaths */, null /* splitRevisionCodes */,
apkLite.getTargetSdkVersion(), null /* requiredSplitTypes */,
null /* splitTypes */);
- sessionSize += PackageHelper.calculateInstalledSize(pkgLite,
+ sessionSize += InstallLocationUtils.calculateInstalledSize(pkgLite,
params.sessionParams.abiOverride, fd.getFileDescriptor());
} catch (IOException e) {
getErrPrintWriter().println("Error: Failed to parse APK file: " + inPath);
@@ -922,7 +922,7 @@ class PackageManagerShellCommand extends ShellCommand {
final List<SharedLibraryInfo> libs = libsSlice.getList();
for (int l = 0, lsize = libs.size(); l < lsize; ++l) {
SharedLibraryInfo lib = libs.get(l);
- if (lib.getType() == SharedLibraryInfo.TYPE_SDK) {
+ if (lib.getType() == SharedLibraryInfo.TYPE_SDK_PACKAGE) {
name = lib.getName() + ":" + lib.getLongVersion();
break;
}
@@ -1649,11 +1649,11 @@ class PackageManagerShellCommand extends ShellCommand {
private int runGetInstallLocation() throws RemoteException {
int loc = mInterface.getInstallLocation();
String locStr = "invalid";
- if (loc == PackageHelper.APP_INSTALL_AUTO) {
+ if (loc == InstallLocationUtils.APP_INSTALL_AUTO) {
locStr = "auto";
- } else if (loc == PackageHelper.APP_INSTALL_INTERNAL) {
+ } else if (loc == InstallLocationUtils.APP_INSTALL_INTERNAL) {
locStr = "internal";
- } else if (loc == PackageHelper.APP_INSTALL_EXTERNAL) {
+ } else if (loc == InstallLocationUtils.APP_INSTALL_EXTERNAL) {
locStr = "external";
}
getOutPrintWriter().println(loc + "[" + locStr + "]");
diff --git a/services/core/java/com/android/server/pm/PackageObserverHelper.java b/services/core/java/com/android/server/pm/PackageObserverHelper.java
new file mode 100644
index 000000000000..ec42f2ea9eee
--- /dev/null
+++ b/services/core/java/com/android/server/pm/PackageObserverHelper.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import android.annotation.NonNull;
+import android.content.pm.PackageManagerInternal.PackageListObserver;
+import android.util.ArraySet;
+
+import com.android.internal.annotations.GuardedBy;
+
+class PackageObserverHelper {
+
+ @NonNull
+ private final Object mLock = new Object();
+
+ // True set of observers, immutable, used to iterate without blocking the lock, since
+ // callbacks can take a long time to return. The previous alternative used a bunch of
+ // list copies on each notify call, which is suboptimal in cases of few mutations and
+ // lots of notifications.
+ @NonNull
+ @GuardedBy("mLock")
+ private ArraySet<PackageListObserver> mActiveSnapshot = new ArraySet<>();
+
+ public void addObserver(@NonNull PackageListObserver observer) {
+ synchronized (mLock) {
+ ArraySet<PackageListObserver> set = new ArraySet<>(mActiveSnapshot);
+ set.add(observer);
+ mActiveSnapshot = set;
+ }
+ }
+
+ public void removeObserver(@NonNull PackageListObserver observer) {
+ synchronized (mLock) {
+ ArraySet<PackageListObserver> set = new ArraySet<>(mActiveSnapshot);
+ set.remove(observer);
+ mActiveSnapshot = set;
+ }
+ }
+
+ public void notifyAdded(@NonNull String packageName, int uid) {
+ ArraySet<PackageListObserver> observers;
+ synchronized (mLock) {
+ observers = mActiveSnapshot;
+ }
+ final int size = observers.size();
+ for (int index = 0; index < size; index++) {
+ observers.valueAt(index).onPackageAdded(packageName, uid);
+ }
+ }
+
+ public void notifyChanged(@NonNull String packageName, int uid) {
+ ArraySet<PackageListObserver> observers;
+ synchronized (mLock) {
+ observers = mActiveSnapshot;
+ }
+ final int size = observers.size();
+ for (int index = 0; index < size; index++) {
+ observers.valueAt(index).onPackageChanged(packageName, uid);
+ }
+ }
+
+ public void notifyRemoved(@NonNull String packageName, int uid) {
+ ArraySet<PackageListObserver> observers;
+ synchronized (mLock) {
+ observers = mActiveSnapshot;
+ }
+ final int size = observers.size();
+ for (int index = 0; index < size; index++) {
+ observers.valueAt(index).onPackageRemoved(packageName, uid);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/pm/PackageSessionVerifier.java b/services/core/java/com/android/server/pm/PackageSessionVerifier.java
index 9bfb7d19eee1..9db215e9093c 100644
--- a/services/core/java/com/android/server/pm/PackageSessionVerifier.java
+++ b/services/core/java/com/android/server/pm/PackageSessionVerifier.java
@@ -28,7 +28,6 @@ import android.content.pm.PackageInstaller.SessionInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.SigningDetails;
-import com.android.server.pm.pkg.parsing.PackageInfoWithoutStateUtils;
import android.content.pm.parsing.result.ParseResult;
import android.content.pm.parsing.result.ParseTypeImpl;
import android.content.rollback.RollbackInfo;
@@ -43,11 +42,12 @@ import android.util.Slog;
import android.util.apk.ApkSignatureVerifier;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.content.PackageHelper;
+import com.android.internal.content.InstallLocationUtils;
import com.android.server.LocalServices;
import com.android.server.SystemConfig;
import com.android.server.pm.parsing.PackageParser2;
import com.android.server.pm.parsing.pkg.ParsedPackage;
+import com.android.server.pm.pkg.parsing.PackageInfoWithoutStateUtils;
import com.android.server.rollback.RollbackManagerInternal;
import java.io.File;
@@ -291,8 +291,8 @@ final class PackageSessionVerifier {
throws PackageManagerException {
// Before marking the session as ready, start checkpoint service if available
try {
- if (PackageHelper.getStorageManager().supportsCheckpoint()) {
- PackageHelper.getStorageManager().startCheckpoint(2);
+ if (InstallLocationUtils.getStorageManager().supportsCheckpoint()) {
+ InstallLocationUtils.getStorageManager().startCheckpoint(2);
}
} catch (Exception e) {
// Failed to get hold of StorageManager
@@ -544,7 +544,7 @@ final class PackageSessionVerifier {
*/
private void checkActiveSessions() throws PackageManagerException {
try {
- checkActiveSessions(PackageHelper.getStorageManager().supportsCheckpoint());
+ checkActiveSessions(InstallLocationUtils.getStorageManager().supportsCheckpoint());
} catch (RemoteException e) {
throw new PackageManagerException(SessionInfo.SESSION_VERIFICATION_FAILED,
"Can't query fs-checkpoint status : " + e);
diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java
index 5fc840c3d76c..d2abc69ad6f2 100644
--- a/services/core/java/com/android/server/pm/PackageSetting.java
+++ b/services/core/java/com/android/server/pm/PackageSetting.java
@@ -68,10 +68,10 @@ import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
-import java.util.function.Predicate;
/**
* Settings data for a particular package we know about.
+ *
* @hide
*/
@DataClass(genGetters = true, genConstructor = false, genSetters = false, genBuilder = false)
@@ -189,7 +189,7 @@ public class PackageSetting extends SettingBase implements PackageStateInternal
private boolean forceQueryableOverride;
@NonNull
- private PackageStateUnserialized pkgState = new PackageStateUnserialized();
+ private final PackageStateUnserialized pkgState = new PackageStateUnserialized(this);
@NonNull
private UUID mDomainSetId;
@@ -722,10 +722,6 @@ public class PackageSetting extends SettingBase implements PackageStateInternal
return readUserState(userId).getEnabledState();
}
- String getLastDisabledAppCaller(int userId) {
- return readUserState(userId).getLastDisableAppCaller();
- }
-
void setInstalled(boolean inst, int userId) {
modifyUserState(userId).setInstalled(inst);
onChanged();
@@ -753,14 +749,6 @@ public class PackageSetting extends SettingBase implements PackageStateInternal
onChanged();
}
- boolean setOverlayPaths(OverlayPaths overlayPaths, int userId) {
- boolean changed = modifyUserState(userId).setOverlayPaths(overlayPaths);
- if (changed) {
- onChanged();
- }
- return changed;
- }
-
@NonNull
OverlayPaths getOverlayPaths(int userId) {
return readUserState(userId).getOverlayPaths();
@@ -773,11 +761,6 @@ public class PackageSetting extends SettingBase implements PackageStateInternal
return changed;
}
- @NonNull
- Map<String, OverlayPaths> getOverlayPathsForLibrary(int userId) {
- return readUserState(userId).getSharedLibraryOverlayPaths();
- }
-
boolean isAnyInstalled(int[] users) {
for (int user: users) {
if (readUserState(user).isInstalled()) {
@@ -850,55 +833,6 @@ public class PackageSetting extends SettingBase implements PackageStateInternal
onChanged();
}
- boolean getSuspended(int userId) {
- return readUserState(userId).isSuspended();
- }
-
- boolean addOrUpdateSuspension(String suspendingPackage, SuspendDialogInfo dialogInfo,
- PersistableBundle appExtras, PersistableBundle launcherExtras, int userId) {
- final PackageUserStateImpl existingUserState = modifyUserState(userId);
- final SuspendParams newSuspendParams = SuspendParams.getInstanceOrNull(dialogInfo,
- appExtras, launcherExtras);
- if (existingUserState.getSuspendParams() == null) {
- existingUserState.setSuspendParams(new ArrayMap<>());
- }
- final SuspendParams oldSuspendParams =
- existingUserState.getSuspendParams().put(suspendingPackage, newSuspendParams);
- onChanged();
- return !Objects.equals(oldSuspendParams, newSuspendParams);
- }
-
- boolean removeSuspension(String suspendingPackage, int userId) {
- boolean wasModified = false;
- final PackageUserStateImpl existingUserState = modifyUserState(userId);
- if (existingUserState.getSuspendParams() != null) {
- if (existingUserState.getSuspendParams().remove(suspendingPackage) != null) {
- wasModified = true;
- }
- if (existingUserState.getSuspendParams().size() == 0) {
- existingUserState.setSuspendParams(null);
- }
- }
- onChanged();
- return wasModified;
- }
-
- void removeSuspension(Predicate<String> suspendingPackagePredicate, int userId) {
- final PackageUserStateImpl existingUserState = modifyUserState(userId);
- if (existingUserState.getSuspendParams() != null) {
- for (int i = existingUserState.getSuspendParams().size() - 1; i >= 0; i--) {
- final String suspendingPackage = existingUserState.getSuspendParams().keyAt(i);
- if (suspendingPackagePredicate.test(suspendingPackage)) {
- existingUserState.getSuspendParams().removeAt(i);
- }
- }
- if (existingUserState.getSuspendParams().size() == 0) {
- existingUserState.setSuspendParams(null);
- }
- }
- onChanged();
- }
-
public boolean getInstantApp(int userId) {
return readUserState(userId).isInstantApp();
}
@@ -1095,8 +1029,8 @@ public class PackageSetting extends SettingBase implements PackageStateInternal
}
/**
- * TODO (b/170263003) refactor to dump to permissiongr proto
- * Dumps the permissions that are granted to users for this package.
+ * TODO (b/170263003) refactor to dump to permissiongr proto Dumps the permissions that are
+ * granted to users for this package.
*/
void writePackageUserPermissionsProto(ProtoOutputStream proto, long fieldId,
List<UserInfo> users, LegacyPermissionDataProvider dataProvider) {
@@ -1154,16 +1088,6 @@ public class PackageSetting extends SettingBase implements PackageStateInternal
}
}
- void setHarmfulAppWarning(int userId, String harmfulAppWarning) {
- modifyUserState(userId).setHarmfulAppWarning(harmfulAppWarning);
- onChanged();
- }
-
- String getHarmfulAppWarning(int userId) {
- PackageUserState userState = readUserState(userId);
- return userState.getHarmfulAppWarning();
- }
-
/**
* @see #mPath
*/
@@ -1175,9 +1099,8 @@ public class PackageSetting extends SettingBase implements PackageStateInternal
}
/**
- * @see PackageUserStateImpl#overrideLabelAndIcon(ComponentName, String, Integer)
- *
* @param userId the specific user to change the label/icon for
+ * @see PackageUserStateImpl#overrideLabelAndIcon(ComponentName, String, Integer)
*/
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
public boolean overrideNonLocalizedLabelAndIcon(@NonNull ComponentName component,
@@ -1188,9 +1111,8 @@ public class PackageSetting extends SettingBase implements PackageStateInternal
}
/**
- * @see PackageUserStateImpl#resetOverrideComponentLabelIcon()
- *
* @param userId the specific user to reset
+ * @see PackageUserStateImpl#resetOverrideComponentLabelIcon()
*/
public void resetOverrideComponentLabelIcon(@UserIdInt int userId) {
modifyUserState(userId).resetOverrideComponentLabelIcon();
@@ -1198,19 +1120,9 @@ public class PackageSetting extends SettingBase implements PackageStateInternal
}
/**
- * @param userId the specified user to modify the theme for
- * @param themeName the theme name to persist
- * @see android.window.SplashScreen#setSplashScreenTheme(int)
- */
- public void setSplashScreenTheme(@UserIdInt int userId, @Nullable String themeName) {
- modifyUserState(userId).setSplashScreenTheme(themeName);
- onChanged();
- }
-
- /**
* @param userId the specified user to get the theme setting from
- * @return the theme name previously persisted for the user or null
- * if no splashscreen theme is persisted.
+ * @return the theme name previously persisted for the user or null if no splashscreen theme is
+ * persisted.
* @see android.window.SplashScreen#setSplashScreenTheme(int)
*/
@Nullable
diff --git a/services/core/java/com/android/server/pm/PreferredActivityHelper.java b/services/core/java/com/android/server/pm/PreferredActivityHelper.java
index 802f701fedf3..bb82e6a4dc39 100644
--- a/services/core/java/com/android/server/pm/PreferredActivityHelper.java
+++ b/services/core/java/com/android/server/pm/PreferredActivityHelper.java
@@ -102,9 +102,7 @@ final class PreferredActivityHelper {
if (DEBUG_PREFERRED) {
Slog.v(TAG, "Preferred activity bookkeeping changed; writing restrictions");
}
- synchronized (mPm.mLock) {
- mPm.scheduleWritePackageRestrictionsLocked(userId);
- }
+ mPm.scheduleWritePackageRestrictions(userId);
}
if ((DEBUG_PREFERRED || debug) && body.mPreferredResolveInfo == null) {
Slog.v(TAG, "No preferred activity to return");
@@ -121,9 +119,7 @@ final class PreferredActivityHelper {
if (changedUsers.size() > 0) {
updateDefaultHomeNotLocked(changedUsers);
mPm.postPreferredActivityChangedBroadcast(userId);
- synchronized (mPm.mLock) {
- mPm.scheduleWritePackageRestrictionsLocked(userId);
- }
+ mPm.scheduleWritePackageRestrictions(userId);
}
}
@@ -214,7 +210,7 @@ final class PreferredActivityHelper {
Settings.removeFilters(pir, filter, existing);
}
pir.addFilter(new PreferredActivity(filter, match, set, activity, always));
- mPm.scheduleWritePackageRestrictionsLocked(userId);
+ mPm.scheduleWritePackageRestrictions(userId);
}
if (!(isHomeFilter(filter) && updateDefaultHomeNotLocked(userId))) {
mPm.postPreferredActivityChangedBroadcast(userId);
@@ -399,7 +395,7 @@ final class PreferredActivityHelper {
synchronized (mPm.mLock) {
mPm.mSettings.editPersistentPreferredActivitiesLPw(userId).addFilter(
new PersistentPreferredActivity(filter, activity, true));
- mPm.scheduleWritePackageRestrictionsLocked(userId);
+ mPm.scheduleWritePackageRestrictions(userId);
}
if (isHomeFilter(filter)) {
updateDefaultHomeNotLocked(userId);
@@ -420,9 +416,7 @@ final class PreferredActivityHelper {
if (changed) {
updateDefaultHomeNotLocked(userId);
mPm.postPreferredActivityChangedBroadcast(userId);
- synchronized (mPm.mLock) {
- mPm.scheduleWritePackageRestrictionsLocked(userId);
- }
+ mPm.scheduleWritePackageRestrictions(userId);
}
}
@@ -603,9 +597,7 @@ final class PreferredActivityHelper {
}
updateDefaultHomeNotLocked(userId);
resetNetworkPolicies(userId);
- synchronized (mPm.mLock) {
- mPm.scheduleWritePackageRestrictionsLocked(userId);
- }
+ mPm.scheduleWritePackageRestrictions(userId);
} finally {
Binder.restoreCallingIdentity(identity);
}
diff --git a/services/core/java/com/android/server/pm/SELinuxMMAC.java b/services/core/java/com/android/server/pm/SELinuxMMAC.java
index 19f180f1ce4c..c0e191fd1194 100644
--- a/services/core/java/com/android/server/pm/SELinuxMMAC.java
+++ b/services/core/java/com/android/server/pm/SELinuxMMAC.java
@@ -29,6 +29,7 @@ import android.util.Xml;
import com.android.server.compat.PlatformCompat;
import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
+import com.android.server.pm.pkg.SharedUserApi;
import libcore.io.IoUtils;
@@ -346,7 +347,7 @@ public final class SELinuxMMAC {
}
private static int getTargetSdkVersionForSeInfo(AndroidPackage pkg,
- SharedUserSetting sharedUserSetting, PlatformCompat compatibility) {
+ SharedUserApi sharedUser, PlatformCompat compatibility) {
// Apps which share a sharedUserId must be placed in the same selinux domain. If this
// package is the first app installed as this shared user, set seInfoTargetSdkVersion to its
// targetSdkVersion. These are later adjusted in PackageManagerService's constructor to be
@@ -355,8 +356,8 @@ public final class SELinuxMMAC {
// NOTE: As new packages are installed / updated, the shared user's seinfoTargetSdkVersion
// will NOT be modified until next boot, even if a lower targetSdkVersion is used. This
// ensures that all packages continue to run in the same selinux domain.
- if ((sharedUserSetting != null) && (sharedUserSetting.packages.size() != 0)) {
- return sharedUserSetting.seInfoTargetSdkVersion;
+ if ((sharedUser != null) && (sharedUser.getPackages().size() != 0)) {
+ return sharedUser.getSeInfoTargetSdkVersion();
}
final ApplicationInfo appInfo = AndroidPackageUtils.generateAppInfoWithoutState(pkg);
if (compatibility.isChangeEnabledInternal(SELINUX_LATEST_CHANGES, appInfo)) {
@@ -376,18 +377,18 @@ public final class SELinuxMMAC {
* the ApplicationInfo instance of the package.
*
* @param pkg object representing the package to be labeled.
- * @param sharedUserSetting if the app shares a sharedUserId, then this has the shared setting.
+ * @param sharedUser if the app shares a sharedUserId, then this has the shared setting.
* @param compatibility the PlatformCompat service to ask about state of compat changes.
* @return String representing the resulting seinfo.
*/
- public static String getSeInfo(AndroidPackage pkg, SharedUserSetting sharedUserSetting,
+ public static String getSeInfo(AndroidPackage pkg, SharedUserApi sharedUser,
PlatformCompat compatibility) {
- final int targetSdkVersion = getTargetSdkVersionForSeInfo(pkg, sharedUserSetting,
+ final int targetSdkVersion = getTargetSdkVersionForSeInfo(pkg, sharedUser,
compatibility);
// TODO(b/71593002): isPrivileged for sharedUser and appInfo should never be out of sync.
// They currently can be if the sharedUser apps are signed with the platform key.
- final boolean isPrivileged = (sharedUserSetting != null)
- ? sharedUserSetting.isPrivileged() | pkg.isPrivileged() : pkg.isPrivileged();
+ final boolean isPrivileged = (sharedUser != null)
+ ? sharedUser.isPrivileged() | pkg.isPrivileged() : pkg.isPrivileged();
return getSeInfo(pkg, isPrivileged, targetSdkVersion);
}
diff --git a/services/core/java/com/android/server/pm/SettingBase.java b/services/core/java/com/android/server/pm/SettingBase.java
index 4345d5136fb9..b952f80850bb 100644
--- a/services/core/java/com/android/server/pm/SettingBase.java
+++ b/services/core/java/com/android/server/pm/SettingBase.java
@@ -88,7 +88,7 @@ public abstract class SettingBase implements Watchable, Snappable {
/**
* Notify listeners that this object has changed.
*/
- protected void onChanged() {
+ public void onChanged() {
PackageStateMutator.onPackageStateChanged();
dispatchChange(this);
}
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index f21bc93bb81c..13a3c5b1b049 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -1438,19 +1438,19 @@ public final class Settings implements Watchable, Snappable {
}
}
- boolean isPermissionUpgradeNeededLPr(int userId) {
+ boolean isPermissionUpgradeNeeded(int userId) {
return mRuntimePermissionsPersistence.isPermissionUpgradeNeeded(userId);
}
- void updateRuntimePermissionsFingerprintLPr(@UserIdInt int userId) {
+ void updateRuntimePermissionsFingerprint(@UserIdInt int userId) {
mRuntimePermissionsPersistence.updateRuntimePermissionsFingerprint(userId);
}
- int getDefaultRuntimePermissionsVersionLPr(int userId) {
+ int getDefaultRuntimePermissionsVersion(int userId) {
return mRuntimePermissionsPersistence.getVersion(userId);
}
- void setDefaultRuntimePermissionsVersionLPr(int version, int userId) {
+ void setDefaultRuntimePermissionsVersion(int version, int userId) {
mRuntimePermissionsPersistence.setVersion(version, userId);
}
@@ -4288,49 +4288,6 @@ public final class Settings implements Watchable, Snappable {
return pkg.getCurrentEnabledStateLPr(classNameStr, userId);
}
- boolean setPackageStoppedStateLPw(PackageManagerService pm, String packageName,
- boolean stopped, int userId) {
- final PackageSetting pkgSetting = mPackages.get(packageName);
- if (pkgSetting == null) {
- throw new IllegalArgumentException("Unknown package: " + packageName);
- }
- if (DEBUG_STOPPED) {
- if (stopped) {
- RuntimeException e = new RuntimeException("here");
- e.fillInStackTrace();
- Slog.i(TAG, "Stopping package " + packageName, e);
- }
- }
- if (pkgSetting.getStopped(userId) != stopped) {
- pkgSetting.setStopped(stopped, userId);
- if (pkgSetting.getNotLaunched(userId)) {
- if (pkgSetting.getInstallSource().installerPackageName != null) {
- pm.notifyFirstLaunch(pkgSetting.getPackageName(),
- pkgSetting.getInstallSource().installerPackageName, userId);
- }
- pkgSetting.setNotLaunched(false, userId);
- }
- return true;
- }
- return false;
- }
-
- void setHarmfulAppWarningLPw(String packageName, CharSequence warning, int userId) {
- final PackageSetting pkgSetting = mPackages.get(packageName);
- if (pkgSetting == null) {
- throw new IllegalArgumentException("Unknown package: " + packageName);
- }
- pkgSetting.setHarmfulAppWarning(userId, warning == null ? null : warning.toString());
- }
-
- String getHarmfulAppWarningLPr(String packageName, int userId) {
- final PackageSetting pkgSetting = mPackages.get(packageName);
- if (pkgSetting == null) {
- throw new IllegalArgumentException("Unknown package: " + packageName);
- }
- return pkgSetting.getHarmfulAppWarning(userId);
- }
-
/**
* Returns all users on the device, including pre-created and dying users.
*
@@ -4869,6 +4826,9 @@ public final class Settings implements Watchable, Snappable {
date.setTime(pus.getFirstInstallTime());
pw.println(sdf.format(date));
+ pw.print(" uninstallReason=");
+ pw.println(userState.getUninstallReason());
+
if (userState.isSuspended()) {
pw.print(prefix);
pw.println(" Suspend params:");
@@ -5150,7 +5110,7 @@ public final class Settings implements Watchable, Snappable {
}
}
- void dumpReadMessagesLPr(PrintWriter pw, DumpState dumpState) {
+ void dumpReadMessages(PrintWriter pw, DumpState dumpState) {
pw.println("Settings parse messages:");
pw.print(mReadMessages.toString());
}
diff --git a/services/core/java/com/android/server/pm/SharedLibrariesImpl.java b/services/core/java/com/android/server/pm/SharedLibrariesImpl.java
index 2227a7810dec..0638d5eee98b 100644
--- a/services/core/java/com/android/server/pm/SharedLibrariesImpl.java
+++ b/services/core/java/com/android/server/pm/SharedLibrariesImpl.java
@@ -42,12 +42,14 @@ import android.util.Slog;
import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
import com.android.server.SystemConfig;
import com.android.server.compat.PlatformCompat;
import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
import com.android.server.pm.parsing.pkg.ParsedPackage;
+import com.android.server.pm.pkg.PackageStateInternal;
import com.android.server.utils.Snappable;
import com.android.server.utils.SnapshotCache;
import com.android.server.utils.Watchable;
@@ -254,6 +256,7 @@ public final class SharedLibrariesImpl implements SharedLibrariesRead, Watchable
/**
* Given the library name, returns a list of shared libraries on all versions.
+ * TODO: Remove, this is used for live mutation outside of the defined commit path
*/
@GuardedBy("mPm.mLock")
@Override
@@ -262,6 +265,11 @@ public final class SharedLibrariesImpl implements SharedLibrariesRead, Watchable
return mSharedLibraries.get(libName);
}
+ @VisibleForTesting
+ public WatchedArrayMap<String, WatchedLongSparseArray<SharedLibraryInfo>> getSharedLibraries() {
+ return mSharedLibraries;
+ }
+
/**
* Returns the shared library with given library name and version number.
*/
@@ -286,18 +294,19 @@ public final class SharedLibrariesImpl implements SharedLibrariesRead, Watchable
return mStaticLibsByDeclaringPackage.get(declaringPackageName);
}
- @GuardedBy("mPm.mLock")
- private @Nullable PackageSetting getLibraryPackageLPr(@NonNull SharedLibraryInfo libInfo) {
+ @Nullable
+ private PackageStateInternal getLibraryPackage(@NonNull Computer computer,
+ @NonNull SharedLibraryInfo libInfo) {
final VersionedPackage declaringPackage = libInfo.getDeclaringPackage();
if (libInfo.isStatic()) {
// Resolve the package name - we use synthetic package names internally
- final String internalPackageName = mPm.resolveInternalPackageNameLPr(
+ final String internalPackageName = computer.resolveInternalPackageName(
declaringPackage.getPackageName(),
declaringPackage.getLongVersionCode());
- return mPm.mSettings.getPackageLPr(internalPackageName);
+ return computer.getPackageStateInternal(internalPackageName);
}
if (libInfo.isSdk()) {
- return mPm.mSettings.getPackageLPr(declaringPackage.getPackageName());
+ return computer.getPackageStateInternal(declaringPackage.getPackageName());
}
return null;
}
@@ -317,24 +326,26 @@ public final class SharedLibrariesImpl implements SharedLibrariesRead, Watchable
final StorageManager storage = mInjector.getSystemService(StorageManager.class);
final File volume = storage.findPathForUuid(StorageManager.UUID_PRIVATE_INTERNAL);
- List<VersionedPackage> packagesToDelete = null;
+ final ArrayList<VersionedPackage> packagesToDelete = new ArrayList<>();
final long now = System.currentTimeMillis();
// Important: We skip shared libs used for some user since
// in such a case we need to keep the APK on the device. The check for
// a lib being used for any user is performed by the uninstall call.
- synchronized (mPm.mLock) {
- final int libCount = mSharedLibraries.size();
+ mPm.executeWithConsistentComputer(computer -> {
+ final WatchedArrayMap<String, WatchedLongSparseArray<SharedLibraryInfo>>
+ sharedLibraries = computer.getSharedLibraries();
+ final int libCount = sharedLibraries.size();
for (int i = 0; i < libCount; i++) {
final WatchedLongSparseArray<SharedLibraryInfo> versionedLib =
- mSharedLibraries.valueAt(i);
+ sharedLibraries.valueAt(i);
if (versionedLib == null) {
continue;
}
final int versionCount = versionedLib.size();
for (int j = 0; j < versionCount; j++) {
SharedLibraryInfo libInfo = versionedLib.valueAt(j);
- final PackageSetting ps = getLibraryPackageLPr(libInfo);
+ final PackageStateInternal ps = getLibraryPackage(computer, libInfo);
if (ps == null) {
continue;
}
@@ -348,27 +359,22 @@ public final class SharedLibrariesImpl implements SharedLibrariesRead, Watchable
continue;
}
- if (packagesToDelete == null) {
- packagesToDelete = new ArrayList<>();
- }
packagesToDelete.add(new VersionedPackage(ps.getPkg().getPackageName(),
libInfo.getDeclaringPackage().getLongVersionCode()));
}
}
- }
+ });
- if (packagesToDelete != null) {
- final int packageCount = packagesToDelete.size();
- for (int i = 0; i < packageCount; i++) {
- final VersionedPackage pkgToDelete = packagesToDelete.get(i);
- // Delete the package synchronously (will fail of the lib used for any user).
- if (mDeletePackageHelper.deletePackageX(pkgToDelete.getPackageName(),
- pkgToDelete.getLongVersionCode(), UserHandle.USER_SYSTEM,
- PackageManager.DELETE_ALL_USERS,
- true /*removedBySystem*/) == PackageManager.DELETE_SUCCEEDED) {
- if (volume.getUsableSpace() >= neededSpace) {
- return true;
- }
+ final int packageCount = packagesToDelete.size();
+ for (int i = 0; i < packageCount; i++) {
+ final VersionedPackage pkgToDelete = packagesToDelete.get(i);
+ // Delete the package synchronously (will fail of the lib used for any user).
+ if (mDeletePackageHelper.deletePackageX(pkgToDelete.getPackageName(),
+ pkgToDelete.getLongVersionCode(), UserHandle.USER_SYSTEM,
+ PackageManager.DELETE_ALL_USERS,
+ true /*removedBySystem*/) == PackageManager.DELETE_SUCCEEDED) {
+ if (volume.getUsableSpace() >= neededSpace) {
+ return true;
}
}
}
@@ -525,7 +531,7 @@ public final class SharedLibrariesImpl implements SharedLibrariesRead, Watchable
@NonNull Map<String, AndroidPackage> availablePackages)
throws PackageManagerException {
final ArrayList<SharedLibraryInfo> sharedLibraryInfos = collectSharedLibraryInfos(
- pkgSetting.getPkg(), availablePackages, null /* newLibraries */);
+ pkg, availablePackages, null /* newLibraries */);
executeSharedLibrariesUpdateLPw(pkg, pkgSetting, changingLib, changingLibSetting,
sharedLibraryInfos, mPm.mUserManager.getUserIds());
}
diff --git a/services/core/java/com/android/server/pm/SharedLibrariesRead.java b/services/core/java/com/android/server/pm/SharedLibrariesRead.java
index e6f231117b32..84d7478fd072 100644
--- a/services/core/java/com/android/server/pm/SharedLibrariesRead.java
+++ b/services/core/java/com/android/server/pm/SharedLibrariesRead.java
@@ -30,7 +30,7 @@ import java.io.PrintWriter;
* An interface implemented by {@link SharedLibrariesImpl} for {@link Computer} to get current
* shared libraries on the device.
*/
-interface SharedLibrariesRead {
+public interface SharedLibrariesRead {
/**
* Returns all shared libraries on the device.
diff --git a/services/core/java/com/android/server/pm/SharedUserSetting.java b/services/core/java/com/android/server/pm/SharedUserSetting.java
index bc484618bb25..5ef14715e4d1 100644
--- a/services/core/java/com/android/server/pm/SharedUserSetting.java
+++ b/services/core/java/com/android/server/pm/SharedUserSetting.java
@@ -18,6 +18,7 @@ package com.android.server.pm;
import android.annotation.NonNull;
import android.content.pm.ApplicationInfo;
+import android.content.pm.SigningDetails;
import com.android.server.pm.pkg.component.ComponentMutateUtils;
import com.android.server.pm.pkg.component.ParsedProcess;
import com.android.server.pm.pkg.component.ParsedProcessImpl;
@@ -28,6 +29,8 @@ import android.util.proto.ProtoOutputStream;
import com.android.internal.util.ArrayUtils;
import com.android.server.pm.parsing.pkg.AndroidPackage;
+import com.android.server.pm.pkg.PackageStateInternal;
+import com.android.server.pm.pkg.SharedUserApi;
import com.android.server.utils.SnapshotCache;
import libcore.util.EmptyArray;
@@ -40,25 +43,26 @@ import java.util.Map;
/**
* Settings data for a particular shared user ID we know about.
*/
-public final class SharedUserSetting extends SettingBase {
+public final class SharedUserSetting extends SettingBase implements SharedUserApi {
final String name;
int userId;
- // flags that are associated with this uid, regardless of any package flags
+ /** @see SharedUserApi#getUidFlags() **/
int uidFlags;
int uidPrivateFlags;
- // The lowest targetSdkVersion of all apps in the sharedUserSetting, used to assign seinfo so
- // that all apps within the sharedUser run in the same selinux context.
+ /** @see SharedUserApi#getSeInfoTargetSdkVersion() **/
int seInfoTargetSdkVersion;
final ArraySet<PackageSetting> packages;
+ private ArraySet<PackageStateInternal> mPackagesSnapshot;
// It is possible for a system app to leave shared user ID by an update.
// We need to keep track of the shadowed PackageSettings so that it is possible to uninstall
// the update and revert the system app back into the original shared user ID.
final ArraySet<PackageSetting> mDisabledPackages;
+ private ArraySet<PackageStateInternal> mDisabledPackagesSnapshot;
final PackageSignatures signatures = new PackageSignatures();
Boolean signaturesChanged;
@@ -98,7 +102,19 @@ public final class SharedUserSetting extends SettingBase {
uidFlags = orig.uidFlags;
uidPrivateFlags = orig.uidPrivateFlags;
packages = new ArraySet<>(orig.packages);
+ if (!packages.isEmpty()) {
+ mPackagesSnapshot = new ArraySet<>();
+ for (int index = 0; index < packages.size(); index++) {
+ mPackagesSnapshot.add(new PackageSetting(packages.valueAt(index)));
+ }
+ }
mDisabledPackages = new ArraySet<>(orig.mDisabledPackages);
+ if (!mDisabledPackages.isEmpty()) {
+ mDisabledPackagesSnapshot = new ArraySet<>();
+ for (int index = 0; index < mDisabledPackages.size(); index++) {
+ mDisabledPackagesSnapshot.add(new PackageSetting(mDisabledPackages.valueAt(index)));
+ }
+ }
// A SigningDetails seems to consist solely of final attributes, so
// it is safe to copy the reference.
signatures.mSigningDetails = orig.signatures.mSigningDetails;
@@ -184,10 +200,9 @@ public final class SharedUserSetting extends SettingBase {
}
}
- /**
- * @return the list of packages that uses this shared UID
- */
- public @NonNull List<AndroidPackage> getPackages() {
+ @NonNull
+ @Override
+ public List<AndroidPackage> getPackages() {
if (packages == null || packages.size() == 0) {
return Collections.emptyList();
}
@@ -201,6 +216,7 @@ public final class SharedUserSetting extends SettingBase {
return pkgList;
}
+ @Override
public boolean isPrivileged() {
return (this.getPrivateFlags() & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0;
}
@@ -291,4 +307,60 @@ public final class SharedUserSetting extends SettingBase {
onChanged();
return this;
}
+
+ @NonNull
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public int getUserId() {
+ return userId;
+ }
+
+ @Override
+ public int getUidFlags() {
+ return uidFlags;
+ }
+
+ @Override
+ public int getPrivateUidFlags() {
+ return uidPrivateFlags;
+ }
+
+ @Override
+ public int getSeInfoTargetSdkVersion() {
+ return seInfoTargetSdkVersion;
+ }
+
+ @NonNull
+ @Override
+ public ArraySet<? extends PackageStateInternal> getPackageStates() {
+ if (mPackagesSnapshot != null) {
+ return mPackagesSnapshot;
+ }
+ return packages;
+ }
+
+ @NonNull
+ @Override
+ public ArraySet<? extends PackageStateInternal> getDisabledPackageStates() {
+ if (mDisabledPackagesSnapshot != null) {
+ return mDisabledPackagesSnapshot;
+ }
+ return mDisabledPackages;
+ }
+
+ @NonNull
+ @Override
+ public SigningDetails getSigningDetails() {
+ return signatures.mSigningDetails;
+ }
+
+ @NonNull
+ @Override
+ public ArrayMap<String, ParsedProcess> getProcesses() {
+ return processes;
+ }
}
diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java
index 72db2428c8c9..bda25895a4b3 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackage.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackage.java
@@ -1694,15 +1694,6 @@ class ShortcutPackage extends ShortcutPackageItem {
for (int j = 0; j < shareTargetSize; j++) {
mShareTargets.get(j).saveToXml(out);
}
- synchronized (mLock) {
- final Map<String, ShortcutInfo> copy = mShortcuts;
- if (!mTransientShortcuts.isEmpty()) {
- copy.putAll(mTransientShortcuts);
- mTransientShortcuts.clear();
- }
- saveShortcutsAsync(copy.values().stream().filter(ShortcutInfo::usesQuota).collect(
- Collectors.toList()));
- }
}
out.endTag(null, TAG_ROOT);
@@ -2418,6 +2409,18 @@ class ShortcutPackage extends ShortcutPackageItem {
})));
}
+ void persistsAllShortcutsAsync() {
+ synchronized (mLock) {
+ final Map<String, ShortcutInfo> copy = mShortcuts;
+ if (!mTransientShortcuts.isEmpty()) {
+ copy.putAll(mTransientShortcuts);
+ mTransientShortcuts.clear();
+ }
+ saveShortcutsAsync(copy.values().stream().filter(ShortcutInfo::usesQuota).collect(
+ Collectors.toList()));
+ }
+ }
+
private void saveShortcutsAsync(
@NonNull final Collection<ShortcutInfo> shortcuts) {
Objects.requireNonNull(shortcuts);
@@ -2489,7 +2492,7 @@ class ShortcutPackage extends ShortcutPackageItem {
mIsAppSearchSchemaUpToDate = true;
}
} catch (Exception e) {
- Slog.e(TAG, "Failed to invoke app search pkg="
+ Slog.e(TAG, "Failed to create app search session. pkg="
+ getPackageName() + " user=" + mShortcutUser.getUserId(), e);
Objects.requireNonNull(future).completeExceptionally(e);
} finally {
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 8393deea1e7b..27605789891e 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -488,7 +488,7 @@ public class ShortcutService extends IShortcutService.Stub {
mShortcutBitmapSaver = new ShortcutBitmapSaver(this);
mShortcutDumpFiles = new ShortcutDumpFiles(this);
mIsAppSearchEnabled = DeviceConfig.getBoolean(NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.SHORTCUT_APPSEARCH_INTEGRATION, false);
+ SystemUiDeviceConfigFlags.SHORTCUT_APPSEARCH_INTEGRATION, true);
if (onlyForPackageManagerApis) {
return; // Don't do anything further. For unit tests only.
@@ -736,7 +736,7 @@ public class ShortcutService extends IShortcutService.Stub {
Slog.d(TAG, "unloadUserLocked: user=" + userId);
}
// Save all dirty information.
- saveDirtyInfo();
+ saveDirtyInfo(false);
// Unload
mUsers.delete(userId);
@@ -1191,6 +1191,10 @@ public class ShortcutService extends IShortcutService.Stub {
@VisibleForTesting
void saveDirtyInfo() {
+ saveDirtyInfo(true);
+ }
+
+ private void saveDirtyInfo(boolean saveShortcutsInAppSearch) {
if (DEBUG || DEBUG_REBOOT) {
Slog.d(TAG, "saveDirtyInfo");
}
@@ -1205,6 +1209,10 @@ public class ShortcutService extends IShortcutService.Stub {
if (userId == UserHandle.USER_NULL) { // USER_NULL for base state.
saveBaseStateLocked();
} else {
+ if (saveShortcutsInAppSearch) {
+ getUserShortcutsLocked(userId).forAllPackages(
+ ShortcutPackage::persistsAllShortcutsAsync);
+ }
saveUserLocked(userId);
}
}
@@ -3719,13 +3727,16 @@ public class ShortcutService extends IShortcutService.Stub {
private final BroadcastReceiver mShutdownReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
+ if (DEBUG || DEBUG_REBOOT) {
+ Slog.d(TAG, "Shutdown broadcast received.");
+ }
// Since it cleans up the shortcut directory and rewrite the ShortcutPackageItems
// in odrder during saveToXml(), it could lead to shortcuts missing when shutdown.
// We need it so that it can finish up saving before shutdown.
synchronized (mLock) {
if (mHandler.hasCallbacks(mSaveDirtyInfoRunner)) {
mHandler.removeCallbacks(mSaveDirtyInfoRunner);
- saveDirtyInfo();
+ saveDirtyInfo(false);
}
mShutdown.set(true);
}
@@ -4394,7 +4405,7 @@ public class ShortcutService extends IShortcutService.Stub {
// Save to the filesystem.
scheduleSaveUser(userId);
- saveDirtyInfo();
+ saveDirtyInfo(false);
// Note, in case of backup, we don't have to wait on bitmap saving, because we don't
// back up bitmaps anyway.
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index 29de5551cb27..f63f8f4289ed 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -53,7 +53,7 @@ import android.util.TimingsTraceLog;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.content.PackageHelper;
+import com.android.internal.content.InstallLocationUtils;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.Preconditions;
import com.android.server.LocalServices;
@@ -237,7 +237,7 @@ public class StagingManager {
mApexManager.revertActiveSessions();
}
- PackageHelper.getStorageManager().abortChanges(
+ InstallLocationUtils.getStorageManager().abortChanges(
"abort-staged-install", false /*retry*/);
}
} catch (Exception e) {
@@ -674,8 +674,8 @@ public class StagingManager {
boolean needsCheckpoint = false;
boolean supportsCheckpoint = false;
try {
- supportsCheckpoint = PackageHelper.getStorageManager().supportsCheckpoint();
- needsCheckpoint = PackageHelper.getStorageManager().needsCheckpoint();
+ supportsCheckpoint = InstallLocationUtils.getStorageManager().supportsCheckpoint();
+ needsCheckpoint = InstallLocationUtils.getStorageManager().needsCheckpoint();
} catch (RemoteException e) {
// This means that vold has crashed, and device is in a bad state.
throw new IllegalStateException("Failed to get checkpoint status", e);
diff --git a/services/core/java/com/android/server/pm/SuspendPackageHelper.java b/services/core/java/com/android/server/pm/SuspendPackageHelper.java
index f466ca72f681..1ea8b2478a90 100644
--- a/services/core/java/com/android/server/pm/SuspendPackageHelper.java
+++ b/services/core/java/com/android/server/pm/SuspendPackageHelper.java
@@ -46,14 +46,17 @@ import android.util.SparseArray;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.CollectionUtils;
import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.pkg.PackageStateInternal;
import com.android.server.pm.pkg.PackageUserStateInternal;
import com.android.server.pm.pkg.SuspendParams;
+import com.android.server.pm.pkg.mutate.PackageUserStateWrite;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import java.util.Objects;
import java.util.function.Predicate;
public final class SuspendPackageHelper {
@@ -107,57 +110,87 @@ public final class SuspendPackageHelper {
return packageNames;
}
+ final SuspendParams newSuspendParams =
+ SuspendParams.getInstanceOrNull(dialogInfo, appExtras, launcherExtras);
+
final List<String> changedPackagesList = new ArrayList<>(packageNames.length);
final IntArray changedUids = new IntArray(packageNames.length);
- final List<String> modifiedPackagesList = new ArrayList<>(packageNames.length);
final IntArray modifiedUids = new IntArray(packageNames.length);
- final List<String> unactionedPackages = new ArrayList<>(packageNames.length);
- final boolean[] canSuspend =
- suspended ? canSuspendPackageForUser(packageNames, userId, callingUid) : null;
-
- for (int i = 0; i < packageNames.length; i++) {
- final String packageName = packageNames[i];
- if (callingPackage.equals(packageName)) {
- Slog.w(TAG, "Calling package: " + callingPackage + " trying to "
- + (suspended ? "" : "un") + "suspend itself. Ignoring");
- unactionedPackages.add(packageName);
- continue;
- }
- final PackageSetting pkgSetting;
- synchronized (mPm.mLock) {
- pkgSetting = mPm.mSettings.getPackageLPr(packageName);
- if (pkgSetting == null
- || mPm.shouldFilterApplication(pkgSetting, callingUid, userId)) {
+ final List<String> unmodifiablePackages = new ArrayList<>(packageNames.length);
+
+ ArraySet<String> modifiedPackages = new ArraySet<>();
+
+ mPm.executeWithConsistentComputer(computer -> {
+ final boolean[] canSuspend = suspended
+ ? canSuspendPackageForUser(computer, packageNames, userId, callingUid) : null;
+ for (int i = 0; i < packageNames.length; i++) {
+ final String packageName = packageNames[i];
+ if (callingPackage.equals(packageName)) {
+ Slog.w(TAG, "Calling package: " + callingPackage + " trying to "
+ + (suspended ? "" : "un") + "suspend itself. Ignoring");
+ unmodifiablePackages.add(packageName);
+ continue;
+ }
+ final PackageStateInternal packageState =
+ computer.getPackageStateInternal(packageName);
+ if (packageState == null
+ || computer.shouldFilterApplication(packageState, callingUid, userId)) {
Slog.w(TAG, "Could not find package setting for package: " + packageName
+ ". Skipping suspending/un-suspending.");
- unactionedPackages.add(packageName);
+ unmodifiablePackages.add(packageName);
continue;
}
+ if (canSuspend != null && !canSuspend[i]) {
+ unmodifiablePackages.add(packageName);
+ continue;
+ }
+
+ final ArrayMap<String, SuspendParams> suspendParamsMap =
+ packageState.getUserStateOrDefault(userId).getSuspendParams();
+ final SuspendParams suspendParams = suspendParamsMap == null
+ ? null : suspendParamsMap.get(packageName);
+ boolean hasSuspension = suspendParams != null;
+ if (suspended) {
+ if (hasSuspension) {
+ // Skip if there's no changes
+ if (Objects.equals(suspendParams.getDialogInfo(), dialogInfo)
+ && Objects.equals(suspendParams.getAppExtras(), appExtras)
+ && Objects.equals(suspendParams.getLauncherExtras(),
+ launcherExtras)) {
+ // Carried over API behavior, must notify change even if no change
+ changedPackagesList.add(packageName);
+ changedUids.add(UserHandle.getUid(userId, packageState.getAppId()));
+ continue;
+ }
+ }
+ }
+
+ // If size one, the package will be unsuspended from this call
+ boolean packageUnsuspended =
+ !suspended && CollectionUtils.size(suspendParamsMap) <= 1;
+ if (suspended || packageUnsuspended) {
+ changedPackagesList.add(packageName);
+ changedUids.add(UserHandle.getUid(userId, packageState.getAppId()));
+ }
+
+ modifiedPackages.add(packageName);
+ modifiedUids.add(UserHandle.getUid(userId, packageState.getAppId()));
}
- if (canSuspend != null && !canSuspend[i]) {
- unactionedPackages.add(packageName);
- continue;
- }
- final boolean packageUnsuspended;
- final boolean packageModified;
- synchronized (mPm.mLock) {
+ });
+
+ mPm.commitPackageStateMutation(null, mutator -> {
+ final int size = modifiedPackages.size();
+ for (int index = 0; index < size; index++) {
+ final String packageName = modifiedPackages.valueAt(index);
+ final PackageUserStateWrite userState = mutator.forPackage(packageName)
+ .userState(userId);
if (suspended) {
- packageModified = pkgSetting.addOrUpdateSuspension(callingPackage,
- dialogInfo, appExtras, launcherExtras, userId);
+ userState.putSuspendParams(callingPackage, newSuspendParams);
} else {
- packageModified = pkgSetting.removeSuspension(callingPackage, userId);
+ userState.removeSuspension(callingPackage);
}
- packageUnsuspended = !suspended && !pkgSetting.getSuspended(userId);
- }
- if (suspended || packageUnsuspended) {
- changedPackagesList.add(packageName);
- changedUids.add(UserHandle.getUid(userId, pkgSetting.getAppId()));
- }
- if (packageModified) {
- modifiedPackagesList.add(packageName);
- modifiedUids.add(UserHandle.getUid(userId, pkgSetting.getAppId()));
}
- }
+ });
if (!changedPackagesList.isEmpty()) {
final String[] changedPackages = changedPackagesList.toArray(new String[0]);
@@ -166,16 +199,14 @@ public final class SuspendPackageHelper {
: Intent.ACTION_PACKAGES_UNSUSPENDED,
changedPackages, changedUids.toArray(), userId);
sendMyPackageSuspendedOrUnsuspended(changedPackages, suspended, userId);
- synchronized (mPm.mLock) {
- mPm.scheduleWritePackageRestrictionsLocked(userId);
- }
+ mPm.scheduleWritePackageRestrictions(userId);
}
// Send the suspension changed broadcast to ensure suspension state is not stale.
- if (!modifiedPackagesList.isEmpty()) {
+ if (!modifiedPackages.isEmpty()) {
sendPackagesSuspendedForUser(Intent.ACTION_PACKAGES_SUSPENSION_CHANGED,
- modifiedPackagesList.toArray(new String[0]), modifiedUids.toArray(), userId);
+ modifiedPackages.toArray(new String[0]), modifiedUids.toArray(), userId);
}
- return unactionedPackages.toArray(new String[0]);
+ return unmodifiablePackages.toArray(new String[0]);
}
/**
@@ -194,20 +225,22 @@ public final class SuspendPackageHelper {
return packageNames;
}
final ArraySet<String> unactionablePackages = new ArraySet<>();
- final boolean[] canSuspend = canSuspendPackageForUser(packageNames, userId, callingUid);
- for (int i = 0; i < packageNames.length; i++) {
- if (!canSuspend[i]) {
- unactionablePackages.add(packageNames[i]);
- continue;
- }
- synchronized (mPm.mLock) {
- final PackageSetting ps = mPm.mSettings.getPackageLPr(packageNames[i]);
- if (ps == null || mPm.shouldFilterApplication(ps, callingUid, userId)) {
+ mPm.executeWithConsistentComputer(computer -> {
+ final boolean[] canSuspend = canSuspendPackageForUser(computer, packageNames, userId,
+ callingUid);
+ for (int i = 0; i < packageNames.length; i++) {
+ if (!canSuspend[i]) {
+ unactionablePackages.add(packageNames[i]);
+ continue;
+ }
+ final PackageStateInternal packageState =
+ computer.getPackageStateFiltered(packageNames[i], callingUid, userId);
+ if (packageState == null) {
Slog.w(TAG, "Could not find package setting for package: " + packageNames[i]);
unactionablePackages.add(packageNames[i]);
}
}
- }
+ });
return unactionablePackages.toArray(new String[unactionablePackages.size()]);
}
@@ -253,19 +286,53 @@ public final class SuspendPackageHelper {
@NonNull Predicate<String> suspendingPackagePredicate, int userId) {
final List<String> unsuspendedPackages = new ArrayList<>();
final IntArray unsuspendedUids = new IntArray();
- synchronized (mPm.mLock) {
+ final ArrayMap<String, ArraySet<String>> pkgToSuspendingPkgsToCommit = new ArrayMap<>();
+ mPm.executeWithConsistentComputer(computer -> {
for (String packageName : packagesToChange) {
- final PackageSetting ps = mPm.mSettings.getPackageLPr(packageName);
- if (ps != null && ps.getUserStateOrDefault(userId).isSuspended()) {
- ps.removeSuspension(suspendingPackagePredicate, userId);
- if (!ps.getUserStateOrDefault(userId).isSuspended()) {
- unsuspendedPackages.add(ps.getPackageName());
- unsuspendedUids.add(UserHandle.getUid(userId, ps.getAppId()));
+ final PackageStateInternal packageState =
+ computer.getPackageStateInternal(packageName);
+ final PackageUserStateInternal packageUserState = packageState == null
+ ? null : packageState.getUserStateOrDefault(userId);
+ if (packageUserState == null || !packageUserState.isSuspended()) {
+ continue;
+ }
+
+ ArrayMap<String, SuspendParams> suspendParamsMap = packageUserState.getSuspendParams();
+ int countRemoved = 0;
+ for (int index = 0; index < suspendParamsMap.size(); index++) {
+ String suspendingPackage = suspendParamsMap.keyAt(index);
+ if (suspendingPackagePredicate.test(suspendingPackage)) {
+ ArraySet<String> suspendingPkgsToCommit =
+ pkgToSuspendingPkgsToCommit.get(packageName);
+ if (suspendingPkgsToCommit == null) {
+ suspendingPkgsToCommit = new ArraySet<>();
+ pkgToSuspendingPkgsToCommit.put(packageName, suspendingPkgsToCommit);
+ }
+ suspendingPkgsToCommit.add(suspendingPackage);
+ countRemoved++;
}
}
+
+ // Everything would be removed and package unsuspended
+ if (countRemoved == suspendParamsMap.size()) {
+ unsuspendedPackages.add(packageState.getPackageName());
+ unsuspendedUids.add(UserHandle.getUid(userId, packageState.getAppId()));
+ }
}
- mPm.scheduleWritePackageRestrictionsLocked(userId);
- }
+ });
+
+ mPm.commitPackageStateMutation(null, mutator -> {
+ for (int mapIndex = 0; mapIndex < pkgToSuspendingPkgsToCommit.size(); mapIndex++) {
+ String packageName = pkgToSuspendingPkgsToCommit.keyAt(mapIndex);
+ ArraySet<String> packagesToRemove = pkgToSuspendingPkgsToCommit.valueAt(mapIndex);
+ PackageUserStateWrite userState = mutator.forPackage(packageName).userState(userId);
+ for (int setIndex = 0; setIndex < packagesToRemove.size(); setIndex++) {
+ userState.removeSuspension(packagesToRemove.valueAt(setIndex));
+ }
+ }
+ });
+
+ mPm.scheduleWritePackageRestrictions(userId);
if (!unsuspendedPackages.isEmpty()) {
final String[] packageArray = unsuspendedPackages.toArray(
new String[unsuspendedPackages.size()]);
@@ -406,7 +473,8 @@ public final class SuspendPackageHelper {
* @return An array containing results of the checks
*/
@NonNull
- boolean[] canSuspendPackageForUser(@NonNull String[] packageNames, int userId, int callingUid) {
+ boolean[] canSuspendPackageForUser(@NonNull Computer computer, @NonNull String[] packageNames,
+ int userId, int callingUid) {
final boolean[] canSuspend = new boolean[packageNames.length];
final boolean isCallerOwner = isCallerDeviceOrProfileOwner(userId, callingUid);
final long token = Binder.clearCallingIdentity();
@@ -459,37 +527,39 @@ public final class SuspendPackageHelper {
+ "\": required for permissions management");
continue;
}
- synchronized (mPm.mLock) {
- if (mProtectedPackages.isPackageStateProtected(userId, packageName)) {
- Slog.w(TAG, "Cannot suspend package \"" + packageName
- + "\": protected package");
+ if (mProtectedPackages.isPackageStateProtected(userId, packageName)) {
+ Slog.w(TAG, "Cannot suspend package \"" + packageName
+ + "\": protected package");
+ continue;
+ }
+ if (!isCallerOwner && computer.getBlockUninstall(userId, packageName)) {
+ Slog.w(TAG, "Cannot suspend package \"" + packageName
+ + "\": blocked by admin");
+ continue;
+ }
+
+ // Cannot suspend static shared libs as they are considered
+ // a part of the using app (emulating static linking). Also
+ // static libs are installed always on internal storage.
+ PackageStateInternal packageState = computer.getPackageStateInternal(packageName);
+ AndroidPackage pkg = packageState == null ? null : packageState.getPkg();
+ if (pkg != null) {
+ // Cannot suspend SDK libs as they are controlled by SDK manager.
+ if (pkg.isSdkLibrary()) {
+ Slog.w(TAG, "Cannot suspend package: " + packageName
+ + " providing SDK library: "
+ + pkg.getSdkLibName());
continue;
}
- if (!isCallerOwner && mPm.mSettings.getBlockUninstallLPr(userId, packageName)) {
- Slog.w(TAG, "Cannot suspend package \"" + packageName
- + "\": blocked by admin");
+ // Cannot suspend static shared libs as they are considered
+ // a part of the using app (emulating static linking). Also
+ // static libs are installed always on internal storage.
+ if (pkg.isStaticSharedLibrary()) {
+ Slog.w(TAG, "Cannot suspend package: " + packageName
+ + " providing static shared library: "
+ + pkg.getStaticSharedLibName());
continue;
}
-
- AndroidPackage pkg = mPm.mPackages.get(packageName);
- if (pkg != null) {
- // Cannot suspend SDK libs as they are controlled by SDK manager.
- if (pkg.isSdkLibrary()) {
- Slog.w(TAG, "Cannot suspend package: " + packageName
- + " providing SDK library: "
- + pkg.getSdkLibName());
- continue;
- }
- // Cannot suspend static shared libs as they are considered
- // a part of the using app (emulating static linking). Also
- // static libs are installed always on internal storage.
- if (pkg.isStaticSharedLibrary()) {
- Slog.w(TAG, "Cannot suspend package: " + packageName
- + " providing static shared library: "
- + pkg.getStaticSharedLibName());
- continue;
- }
- }
}
if (PLATFORM_PACKAGE_NAME.equals(packageName)) {
Slog.w(TAG, "Cannot suspend the platform package: " + packageName);
diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
index 1fa901352c3d..9bcb7242b645 100644
--- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
+++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
@@ -285,6 +285,19 @@ public class UserRestrictionsUtils {
);
/**
+ * User restrictions available to a device owner whose type is
+ * {@link android.app.admin.DevicePolicyManager#DEVICE_OWNER_TYPE_FINANCED}.
+ */
+ private static final Set<String> FINANCED_DEVICE_OWNER_RESTRICTIONS = Sets.newArraySet(
+ UserManager.DISALLOW_ADD_USER,
+ UserManager.DISALLOW_DEBUGGING_FEATURES,
+ UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES,
+ UserManager.DISALLOW_SAFE_BOOT,
+ UserManager.DISALLOW_CONFIG_DATE_TIME,
+ UserManager.DISALLOW_OUTGOING_CALLS
+ );
+
+ /**
* Returns whether the given restriction name is valid (and logs it if it isn't).
*/
public static boolean isValidRestriction(@NonNull String restriction) {
@@ -458,6 +471,15 @@ public class UserRestrictionsUtils {
}
/**
+ * @return {@code true} only if the restriction is allowed for financed devices and can be set
+ * by a device owner. Otherwise, {@code false} would be returned.
+ */
+ public static boolean canFinancedDeviceOwnerChange(String restriction) {
+ return FINANCED_DEVICE_OWNER_RESTRICTIONS.contains(restriction)
+ && canDeviceOwnerChange(restriction);
+ }
+
+ /**
* Whether given user restriction should be enforced globally.
*/
public static boolean isGlobal(@UserManagerInternal.OwnerType int restrictionOwnerType,
diff --git a/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java b/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java
index a1a6f5a57e3f..9fb1f8f11999 100644
--- a/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java
+++ b/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java
@@ -30,11 +30,13 @@ import android.util.ArraySet;
import android.util.DebugUtils;
import android.util.IndentingPrintWriter;
import android.util.Slog;
+import android.util.SparseArrayMap;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.LocalServices;
import com.android.server.SystemConfig;
import com.android.server.pm.parsing.pkg.AndroidPackage;
+import com.android.server.pm.pkg.PackageStateInternal;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -211,31 +213,66 @@ class UserSystemPackageInstaller {
Slog.i(TAG, "Reviewing whitelisted packages due to "
+ (isFirstBoot ? "[firstBoot]" : "") + (isConsideredUpgrade ? "[upgrade]" : ""));
final PackageManagerInternal pmInt = LocalServices.getService(PackageManagerInternal.class);
+
+ // User ID -> package name -> installed
+ SparseArrayMap<String, Boolean> changesToCommit = new SparseArrayMap<>();
+
// Install/uninstall system packages per user.
for (int userId : mUm.getUserIds()) {
final Set<String> userWhitelist = getInstallablePackagesForUserId(userId);
- pmInt.forEachPackageSetting(pkgSetting -> {
- AndroidPackage pkg = pkgSetting.getPkg();
- if (pkg == null || !pkg.isSystem()) {
- return;
- }
- final boolean install =
- (userWhitelist == null || userWhitelist.contains(pkg.getPackageName()))
- && !pkgSetting.getPkgState().isHiddenUntilInstalled();
- if (pkgSetting.getInstalled(userId) == install
- || !shouldChangeInstallationState(pkgSetting, install, userId, isFirstBoot,
- isConsideredUpgrade, preExistingPackages)) {
- return;
+
+ // If null, run for all packages
+ if (userWhitelist == null) {
+ pmInt.forEachPackageState(packageState -> {
+ if (packageState.getPkg() == null) {
+ return;
+ }
+ final boolean install = !packageState.getTransientState()
+ .isHiddenUntilInstalled();
+ if (packageState.getUserStateOrDefault(userId).isInstalled() != install
+ && shouldChangeInstallationState(packageState, install, userId,
+ isFirstBoot, isConsideredUpgrade, preExistingPackages)) {
+ changesToCommit.add(userId, packageState.getPackageName(), install);
+ }
+ });
+ } else {
+ for (String packageName : userWhitelist) {
+ PackageStateInternal packageState = pmInt.getPackageStateInternal(packageName);
+ if (packageState.getPkg() == null) {
+ continue;
+ }
+
+ final boolean install = !packageState.getTransientState()
+ .isHiddenUntilInstalled();
+ if (packageState.getUserStateOrDefault(userId).isInstalled() != install
+ && shouldChangeInstallationState(packageState, install, userId,
+ isFirstBoot, isConsideredUpgrade, preExistingPackages)) {
+ changesToCommit.add(userId, packageState.getPackageName(), install);
+ }
}
- pkgSetting.setInstalled(install, userId);
- pkgSetting.setUninstallReason(
- install ? PackageManager.UNINSTALL_REASON_UNKNOWN :
- PackageManager.UNINSTALL_REASON_USER_TYPE,
- userId);
- Slog.i(TAG, (install ? "Installed " : "Uninstalled ")
- + pkg.getPackageName() + " for user " + userId);
- });
+ }
}
+
+ pmInt.commitPackageStateMutation(null, packageStateMutator -> {
+ for (int userIndex = 0; userIndex < changesToCommit.numMaps(); userIndex++) {
+ int userId = changesToCommit.keyAt(userIndex);
+ int packagesSize = changesToCommit.numElementsForKey(userId);
+ for (int packageIndex = 0; packageIndex < packagesSize; ++packageIndex) {
+ String packageName = changesToCommit.keyAt(userIndex, packageIndex);
+ boolean installed = changesToCommit.valueAt(userIndex, packageIndex);
+ packageStateMutator.forPackage(packageName)
+ .userState(userId)
+ .setInstalled(installed)
+ .setUninstallReason(installed
+ ? PackageManager.UNINSTALL_REASON_UNKNOWN
+ : PackageManager.UNINSTALL_REASON_USER_TYPE);
+
+ Slog.i(TAG + "CommitDebug", (installed ? "Installed " : "Uninstalled ")
+ + packageName + " for user " + userId);
+ }
+ }
+ });
+
return true;
}
@@ -250,7 +287,7 @@ class UserSystemPackageInstaller {
* @param preOtaPkgs list of packages on the device prior to the upgrade.
* Cannot be null if isUpgrade is true.
*/
- private static boolean shouldChangeInstallationState(PackageSetting pkgSetting,
+ private static boolean shouldChangeInstallationState(PackageStateInternal packageState,
boolean install,
@UserIdInt int userId,
boolean isFirstBoot,
@@ -258,11 +295,12 @@ class UserSystemPackageInstaller {
@Nullable ArraySet<String> preOtaPkgs) {
if (install) {
// Only proceed with install if we are the only reason why it had been uninstalled.
- return pkgSetting.getUninstallReason(userId)
+ return packageState.getUserStateOrDefault(userId).getUninstallReason()
== PackageManager.UNINSTALL_REASON_USER_TYPE;
} else {
// Only proceed with uninstall if the package is new to the device.
- return isFirstBoot || (isUpgrade && !preOtaPkgs.contains(pkgSetting.getPackageName()));
+ return isFirstBoot
+ || (isUpgrade && !preOtaPkgs.contains(packageState.getPackageName()));
}
}
diff --git a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
index 2d0a3ef9338b..63469cb24f2f 100644
--- a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
+++ b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
@@ -34,19 +34,6 @@ import android.content.pm.ProcessInfo;
import android.content.pm.ProviderInfo;
import android.content.pm.ServiceInfo;
import android.content.pm.SharedLibraryInfo;
-import com.android.server.pm.pkg.parsing.PackageInfoWithoutStateUtils;
-import com.android.server.pm.pkg.parsing.ParsingUtils;
-import com.android.server.pm.pkg.component.ComponentParseUtils;
-import com.android.server.pm.pkg.component.ParsedActivity;
-import com.android.server.pm.pkg.component.ParsedComponent;
-import com.android.server.pm.pkg.component.ParsedInstrumentation;
-import com.android.server.pm.pkg.component.ParsedMainComponent;
-import com.android.server.pm.pkg.component.ParsedPermission;
-import com.android.server.pm.pkg.component.ParsedPermissionGroup;
-import com.android.server.pm.pkg.component.ParsedProcess;
-import com.android.server.pm.pkg.component.ParsedProvider;
-import com.android.server.pm.pkg.component.ParsedService;
-import com.android.server.pm.pkg.PackageUserStateUtils;
import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -60,6 +47,19 @@ import com.android.server.pm.parsing.pkg.PackageImpl;
import com.android.server.pm.pkg.PackageStateInternal;
import com.android.server.pm.pkg.PackageStateUnserialized;
import com.android.server.pm.pkg.PackageUserStateInternal;
+import com.android.server.pm.pkg.PackageUserStateUtils;
+import com.android.server.pm.pkg.component.ComponentParseUtils;
+import com.android.server.pm.pkg.component.ParsedActivity;
+import com.android.server.pm.pkg.component.ParsedComponent;
+import com.android.server.pm.pkg.component.ParsedInstrumentation;
+import com.android.server.pm.pkg.component.ParsedMainComponent;
+import com.android.server.pm.pkg.component.ParsedPermission;
+import com.android.server.pm.pkg.component.ParsedPermissionGroup;
+import com.android.server.pm.pkg.component.ParsedProcess;
+import com.android.server.pm.pkg.component.ParsedProvider;
+import com.android.server.pm.pkg.component.ParsedService;
+import com.android.server.pm.pkg.parsing.PackageInfoWithoutStateUtils;
+import com.android.server.pm.pkg.parsing.ParsingUtils;
import libcore.util.EmptyArray;
diff --git a/services/core/java/com/android/server/pm/parsing/ParsedComponentStateUtils.java b/services/core/java/com/android/server/pm/parsing/ParsedComponentStateUtils.java
index 564585b4cbe9..97d526d1c44e 100644
--- a/services/core/java/com/android/server/pm/parsing/ParsedComponentStateUtils.java
+++ b/services/core/java/com/android/server/pm/parsing/ParsedComponentStateUtils.java
@@ -18,10 +18,10 @@ package com.android.server.pm.parsing;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import com.android.server.pm.pkg.component.ParsedComponent;
import android.util.Pair;
import com.android.server.pm.pkg.PackageStateInternal;
+import com.android.server.pm.pkg.component.ParsedComponent;
/**
* For exposing internal fields to the rest of the server, enforcing that any overridden state from
diff --git a/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageUtils.java b/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageUtils.java
index 7e59bd669824..f2b1a7119b84 100644
--- a/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageUtils.java
+++ b/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageUtils.java
@@ -92,7 +92,7 @@ public class AndroidPackageUtils {
AndroidPackageUtils.getAllCodePaths(pkg),
pkg.getSdkLibName(),
pkg.getSdkLibVersionMajor(),
- SharedLibraryInfo.TYPE_SDK,
+ SharedLibraryInfo.TYPE_SDK_PACKAGE,
new VersionedPackage(pkg.getManifestPackageName(),
pkg.getLongVersionCode()),
null, null, false /* isNative */);
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 317730a9f606..79c5ea2efefe 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -17,6 +17,7 @@
package com.android.server.pm.permission;
import static android.Manifest.permission.CAPTURE_AUDIO_HOTWORD;
+import static android.Manifest.permission.POST_NOTIFICATIONS;
import static android.Manifest.permission.RECORD_AUDIO;
import static android.Manifest.permission.UPDATE_APP_OPS_STATS;
import static android.app.AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE;
@@ -608,6 +609,21 @@ public class PermissionManagerService extends IPermissionManager.Stub {
}
@Override
+ public int checkPostNotificationsPermissionGrantedOrLegacyAccess(int uid) {
+ int granted = PermissionManagerService.this.checkUidPermission(uid,
+ POST_NOTIFICATIONS);
+ AndroidPackage pkg = mPackageManagerInt.getPackage(uid);
+ if (granted != PermissionManager.PERMISSION_GRANTED) {
+ int flags = PermissionManagerService.this.getPermissionFlags(pkg.getPackageName(),
+ POST_NOTIFICATIONS, UserHandle.getUserId(uid));
+ if ((flags & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) != 0) {
+ return PermissionManager.PERMISSION_GRANTED;
+ }
+ }
+ return granted;
+ }
+
+ @Override
public void startShellPermissionIdentityDelegation(int uid, @NonNull String packageName,
@Nullable List<String> permissionNames) {
Objects.requireNonNull(packageName, "packageName");
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 8e41c9b39f0c..db9c1b56e4d8 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
@@ -4486,7 +4486,7 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt
@Override
public void readLegacyPermissionStateTEMP() {
final int[] userIds = getAllUserIds();
- mPackageManagerInt.forEachPackageSetting(ps -> {
+ mPackageManagerInt.forEachPackageState(ps -> {
final int appId = ps.getAppId();
final LegacyPermissionState legacyState = ps.getLegacyPermissionState();
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
index d2c4ec4cc5a5..812d7a04dc13 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
@@ -63,6 +63,17 @@ public interface PermissionManagerServiceInternal extends PermissionManagerInter
int checkUidPermission(int uid, @NonNull String permissionName);
/**
+ * Check whether a particular UID has been granted the POST_NOTIFICATIONS permission, or if
+ * access should be granted based on legacy access (currently symbolized by the REVIEW_REQUIRED
+ * permission flag
+ *
+ * @param uid the UID
+ * @return {@code PERMISSION_GRANTED} if the permission is granted, or legacy access is granted,
+ * {@code PERMISSION_DENIED} otherwise
+ */
+ int checkPostNotificationsPermissionGrantedOrLegacyAccess(int uid);
+
+ /**
* Adds a listener for runtime permission state (permissions or flags) changes.
*
* @param listener The listener.
diff --git a/services/core/java/com/android/server/pm/pkg/PackageStateInternal.java b/services/core/java/com/android/server/pm/pkg/PackageStateInternal.java
index 56f62ab87933..fb2fe1f442b7 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageStateInternal.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageStateInternal.java
@@ -24,7 +24,6 @@ import android.util.SparseArray;
import com.android.server.pm.InstallSource;
import com.android.server.pm.PackageKeySetData;
-import com.android.server.pm.SharedUserSetting;
import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.permission.LegacyPermissionState;
@@ -52,7 +51,7 @@ public interface PackageStateInternal extends PackageState {
InstallSource getInstallSource();
@Nullable
- SharedUserSetting getSharedUser();
+ SharedUserApi getSharedUser();
// TODO: Remove this in favor of boolean APIs
int getFlags();
diff --git a/services/core/java/com/android/server/pm/pkg/PackageStateUnserialized.java b/services/core/java/com/android/server/pm/pkg/PackageStateUnserialized.java
index ef2154392d46..7bd720acc799 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageStateUnserialized.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageStateUnserialized.java
@@ -40,6 +40,7 @@ import java.util.stream.Collectors;
* where they would be lost implicitly by re-generating the package object.
*/
@DataClass(genSetters = true, genConstructor = false, genBuilder = false)
+@DataClass.Suppress({"setLastPackageUsageTimeInMills", "setPackageSetting"})
public class PackageStateUnserialized {
private boolean hiddenUntilInstalled;
@@ -58,6 +59,14 @@ public class PackageStateUnserialized {
@Nullable
private String overrideSeInfo;
+ // TODO: Remove in favor of finer grained change notification
+ @NonNull
+ private final PackageSetting mPackageSetting;
+
+ public PackageStateUnserialized(@NonNull PackageSetting packageSetting) {
+ mPackageSetting = packageSetting;
+ }
+
private long[] lazyInitLastPackageUsageTimeInMills() {
return new long[PackageManager.NOTIFY_PACKAGE_USE_REASONS_COUNT];
}
@@ -70,6 +79,7 @@ public class PackageStateUnserialized {
return this;
}
getLastPackageUsageTimeInMills()[reason] = time;
+ mPackageSetting.onChanged();
return this;
}
@@ -108,6 +118,7 @@ public class PackageStateUnserialized {
this.updatedSystemApp = other.updatedSystemApp;
this.lastPackageUsageTimeInMills = other.lastPackageUsageTimeInMills;
this.overrideSeInfo = other.overrideSeInfo;
+ mPackageSetting.onChanged();
}
public @NonNull List<SharedLibraryInfo> getNonNativeUsesLibraryInfos() {
@@ -115,8 +126,45 @@ public class PackageStateUnserialized {
.filter((l) -> !l.isNative()).collect(Collectors.toList());
}
+ public PackageStateUnserialized setHiddenUntilInstalled(boolean value) {
+ hiddenUntilInstalled = value;
+ mPackageSetting.onChanged();
+ return this;
+ }
+
+ public PackageStateUnserialized setUsesLibraryInfos(@NonNull List<SharedLibraryInfo> value) {
+ usesLibraryInfos = value;
+ mPackageSetting.onChanged();
+ return this;
+ }
+
+ public PackageStateUnserialized setUsesLibraryFiles(@NonNull List<String> value) {
+ usesLibraryFiles = value;
+ mPackageSetting.onChanged();
+ return this;
+ }
+
+ public PackageStateUnserialized setUpdatedSystemApp(boolean value) {
+ updatedSystemApp = value;
+ mPackageSetting.onChanged();
+ return this;
+ }
+
+ public PackageStateUnserialized setLastPackageUsageTimeInMills(@NonNull long... value) {
+ lastPackageUsageTimeInMills = value;
+ mPackageSetting.onChanged();
+ return this;
+ }
+
+ public PackageStateUnserialized setOverrideSeInfo(@Nullable String value) {
+ overrideSeInfo = value;
+ mPackageSetting.onChanged();
+ return this;
+ }
+
- // Code below generated by codegen v1.0.14.
+
+ // Code below generated by codegen v1.0.23.
//
// DO NOT MODIFY!
// CHECKSTYLE:OFF Generated code
@@ -169,52 +217,15 @@ public class PackageStateUnserialized {
}
@DataClass.Generated.Member
- public PackageStateUnserialized setHiddenUntilInstalled(boolean value) {
- hiddenUntilInstalled = value;
- return this;
- }
-
- @DataClass.Generated.Member
- public PackageStateUnserialized setUsesLibraryInfos(@NonNull List<SharedLibraryInfo> value) {
- usesLibraryInfos = value;
- com.android.internal.util.AnnotationValidations.validate(
- NonNull.class, null, usesLibraryInfos);
- return this;
- }
-
- @DataClass.Generated.Member
- public PackageStateUnserialized setUsesLibraryFiles(@NonNull List<String> value) {
- usesLibraryFiles = value;
- com.android.internal.util.AnnotationValidations.validate(
- NonNull.class, null, usesLibraryFiles);
- return this;
- }
-
- @DataClass.Generated.Member
- public PackageStateUnserialized setUpdatedSystemApp(boolean value) {
- updatedSystemApp = value;
- return this;
- }
-
- @DataClass.Generated.Member
- public PackageStateUnserialized setLastPackageUsageTimeInMills(@NonNull long... value) {
- lastPackageUsageTimeInMills = value;
- com.android.internal.util.AnnotationValidations.validate(
- NonNull.class, null, lastPackageUsageTimeInMills);
- return this;
- }
-
- @DataClass.Generated.Member
- public PackageStateUnserialized setOverrideSeInfo(@Nullable String value) {
- overrideSeInfo = value;
- return this;
+ public @NonNull PackageSetting getPackageSetting() {
+ return mPackageSetting;
}
@DataClass.Generated(
- time = 1580422870209L,
- codegenVersion = "1.0.14",
+ time = 1642554781099L,
+ codegenVersion = "1.0.23",
sourceFile = "frameworks/base/services/core/java/com/android/server/pm/pkg/PackageStateUnserialized.java",
- inputSignatures = "private boolean hiddenUntilInstalled\nprivate @android.annotation.NonNull java.util.List<android.content.pm.SharedLibraryInfo> usesLibraryInfos\nprivate @android.annotation.NonNull java.util.List<java.lang.String> usesLibraryFiles\nprivate boolean updatedSystemApp\nprivate volatile @android.annotation.NonNull long[] lastPackageUsageTimeInMills\n @android.annotation.Nullable java.lang.String overrideSeInfo\nprivate long[] lazyInitLastPackageUsageTimeInMills()\npublic com.android.server.pm.pkg.PackageStateUnserialized setLastPackageUsageTimeInMills(int,long)\npublic long getLatestPackageUseTimeInMills()\npublic long getLatestForegroundPackageUseTimeInMills()\nclass PackageStateUnserialized extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genSetters=true, genConstructor=false, genBuilder=false)")
+ inputSignatures = "private boolean hiddenUntilInstalled\nprivate @android.annotation.NonNull java.util.List<android.content.pm.SharedLibraryInfo> usesLibraryInfos\nprivate @android.annotation.NonNull java.util.List<java.lang.String> usesLibraryFiles\nprivate boolean updatedSystemApp\nprivate volatile @android.annotation.NonNull long[] lastPackageUsageTimeInMills\nprivate @android.annotation.Nullable java.lang.String overrideSeInfo\nprivate @android.annotation.NonNull com.android.server.pm.PackageSetting mPackageSetting\nprivate long[] lazyInitLastPackageUsageTimeInMills()\npublic com.android.server.pm.pkg.PackageStateUnserialized setLastPackageUsageTimeInMills(int,long)\npublic long getLatestPackageUseTimeInMills()\npublic long getLatestForegroundPackageUseTimeInMills()\npublic void updateFrom(com.android.server.pm.pkg.PackageStateUnserialized)\npublic @android.annotation.NonNull java.util.List<android.content.pm.SharedLibraryInfo> getNonNativeUsesLibraryInfos()\npublic com.android.server.pm.pkg.PackageStateUnserialized setHiddenUntilInstalled(boolean)\npublic com.android.server.pm.pkg.PackageStateUnserialized setUsesLibraryInfos(java.util.List<android.content.pm.SharedLibraryInfo>)\npublic com.android.server.pm.pkg.PackageStateUnserialized setUsesLibraryFiles(java.util.List<java.lang.String>)\npublic com.android.server.pm.pkg.PackageStateUnserialized setUpdatedSystemApp(boolean)\npublic com.android.server.pm.pkg.PackageStateUnserialized setLastPackageUsageTimeInMills(long)\npublic com.android.server.pm.pkg.PackageStateUnserialized setOverrideSeInfo(java.lang.String)\nclass PackageStateUnserialized extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genSetters=true, genConstructor=false, genBuilder=false)")
@Deprecated
private void __metadata() {}
diff --git a/services/core/java/com/android/server/pm/pkg/SharedUserApi.java b/services/core/java/com/android/server/pm/pkg/SharedUserApi.java
new file mode 100644
index 000000000000..43eac536ee34
--- /dev/null
+++ b/services/core/java/com/android/server/pm/pkg/SharedUserApi.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.pkg;
+
+import android.annotation.NonNull;
+import android.annotation.UserIdInt;
+import android.content.pm.SigningDetails;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+
+import com.android.server.pm.parsing.pkg.AndroidPackage;
+import com.android.server.pm.pkg.component.ParsedProcess;
+
+import java.util.List;
+
+public interface SharedUserApi {
+
+ @NonNull
+ String getName();
+
+ @UserIdInt
+ int getUserId();
+
+ // flags that are associated with this uid, regardless of any package flags
+ int getUidFlags();
+ int getPrivateUidFlags();
+
+ // The lowest targetSdkVersion of all apps in the sharedUserSetting, used to assign seinfo so
+ // that all apps within the sharedUser run in the same selinux context.
+ int getSeInfoTargetSdkVersion();
+
+ /**
+ * @return the list of packages that uses this shared UID
+ */
+ @NonNull
+ List<AndroidPackage> getPackages();
+
+ @NonNull
+ ArraySet<? extends PackageStateInternal> getPackageStates();
+
+ // It is possible for a system app to leave shared user ID by an update.
+ // We need to keep track of the shadowed PackageSettings so that it is possible to uninstall
+ // the update and revert the system app back into the original shared user ID.
+ @NonNull
+ ArraySet<? extends PackageStateInternal> getDisabledPackageStates();
+
+ @NonNull
+ SigningDetails getSigningDetails();
+
+ @NonNull
+ ArrayMap<String, ParsedProcess> getProcesses();
+
+ boolean isPrivileged();
+}
diff --git a/services/core/java/com/android/server/pm/pkg/mutate/PackageStateMutator.java b/services/core/java/com/android/server/pm/pkg/mutate/PackageStateMutator.java
index 35d4d9e82fbe..951ddfa62118 100644
--- a/services/core/java/com/android/server/pm/pkg/mutate/PackageStateMutator.java
+++ b/services/core/java/com/android/server/pm/pkg/mutate/PackageStateMutator.java
@@ -18,6 +18,7 @@ package com.android.server.pm.pkg.mutate;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.ComponentName;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.overlay.OverlayPaths;
@@ -177,6 +178,13 @@ public class PackageStateMutator {
}
@Override
+ public void onChanged() {
+ if (mState != null) {
+ mState.onChanged();
+ }
+ }
+
+ @Override
public PackageStateWrite setLastPackageUsageTime(int reason, long timeInMillis) {
if (mState != null) {
mState.getTransientState().setLastPackageUsageTimeInMills(reason, timeInMillis);
@@ -217,6 +225,51 @@ public class PackageStateMutator {
return this;
}
+ @NonNull
+ @Override
+ public PackageStateWrite setCategoryOverride(@ApplicationInfo.Category int category) {
+ if (mState != null) {
+ mState.setCategoryOverride(category);
+ }
+ return this;
+ }
+
+ @NonNull
+ @Override
+ public PackageStateWrite setUpdateAvailable(boolean updateAvailable) {
+ if (mState != null) {
+ mState.setUpdateAvailable(updateAvailable);
+ }
+ return this;
+ }
+
+ @NonNull
+ @Override
+ public PackageStateWrite setLoadingProgress(float progress) {
+ if (mState != null) {
+ mState.setLoadingProgress(progress);
+ }
+ return this;
+ }
+
+ @NonNull
+ @Override
+ public PackageStateWrite setOverrideSeInfo(@Nullable String newSeInfo) {
+ if (mState != null) {
+ mState.getTransientState().setOverrideSeInfo(newSeInfo);
+ }
+ return this;
+ }
+
+ @NonNull
+ @Override
+ public PackageStateWrite setInstaller(@NonNull String installerPackageName) {
+ if (mState != null) {
+ mState.setInstallerPackageName(installerPackageName);
+ }
+ return this;
+ }
+
private static class UserStateWriteWrapper implements PackageUserStateWrite {
@Nullable
@@ -328,6 +381,25 @@ public class PackageStateMutator {
}
return this;
}
+
+ @NonNull
+ @Override
+ public PackageUserStateWrite setSplashScreenTheme(@Nullable String theme) {
+ if (mUserState != null) {
+ mUserState.setSplashScreenTheme(theme);
+ }
+ return this;
+ }
+
+ @NonNull
+ @Override
+ public PackageUserStateWrite setComponentLabelIcon(@NonNull ComponentName componentName,
+ @Nullable String nonLocalizedLabel, @Nullable Integer icon) {
+ if (mUserState != null) {
+ mUserState.overrideLabelAndIcon(componentName, nonLocalizedLabel, icon);
+ }
+ return null;
+ }
}
}
}
diff --git a/services/core/java/com/android/server/pm/pkg/mutate/PackageStateWrite.java b/services/core/java/com/android/server/pm/pkg/mutate/PackageStateWrite.java
index 585becee55c0..1ac0b059d496 100644
--- a/services/core/java/com/android/server/pm/pkg/mutate/PackageStateWrite.java
+++ b/services/core/java/com/android/server/pm/pkg/mutate/PackageStateWrite.java
@@ -17,12 +17,16 @@
package com.android.server.pm.pkg.mutate;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.UserIdInt;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.util.ArraySet;
public interface PackageStateWrite {
+ void onChanged();
+
@NonNull
PackageUserStateWrite userState(@UserIdInt int userId);
@@ -38,4 +42,19 @@ public interface PackageStateWrite {
@NonNull
PackageStateWrite setMimeGroup(@NonNull String mimeGroup, @NonNull ArraySet<String> mimeTypes);
+
+ @NonNull
+ PackageStateWrite setCategoryOverride(@ApplicationInfo.Category int category);
+
+ @NonNull
+ PackageStateWrite setUpdateAvailable(boolean updateAvailable);
+
+ @NonNull
+ PackageStateWrite setLoadingProgress(float progress);
+
+ @NonNull
+ PackageStateWrite setOverrideSeInfo(@Nullable String newSeInfo);
+
+ @NonNull
+ PackageStateWrite setInstaller(@NonNull String installerPackageName);
}
diff --git a/services/core/java/com/android/server/pm/pkg/mutate/PackageUserStateWrite.java b/services/core/java/com/android/server/pm/pkg/mutate/PackageUserStateWrite.java
index e23a1b604a49..11d6d97d3920 100644
--- a/services/core/java/com/android/server/pm/pkg/mutate/PackageUserStateWrite.java
+++ b/services/core/java/com/android/server/pm/pkg/mutate/PackageUserStateWrite.java
@@ -18,6 +18,7 @@ package com.android.server.pm.pkg.mutate;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.ComponentName;
import android.content.pm.PackageManager;
import android.content.pm.overlay.OverlayPaths;
@@ -60,4 +61,11 @@ public interface PackageUserStateWrite {
@NonNull
PackageUserStateWrite setHarmfulAppWarning(@Nullable String warning);
+
+ @NonNull
+ PackageUserStateWrite setSplashScreenTheme(@Nullable String theme);
+
+ @NonNull
+ PackageUserStateWrite setComponentLabelIcon(@NonNull ComponentName componentName,
+ @Nullable String nonLocalizedLabel, @Nullable Integer icon);
}
diff --git a/services/core/java/com/android/server/power/LowPowerStandbyController.java b/services/core/java/com/android/server/power/LowPowerStandbyController.java
new file mode 100644
index 000000000000..cea84b57377c
--- /dev/null
+++ b/services/core/java/com/android/server/power/LowPowerStandbyController.java
@@ -0,0 +1,608 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.power;
+
+import android.app.AlarmManager;
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.res.Resources;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.PowerManager;
+import android.os.PowerManagerInternal;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.util.IndentingPrintWriter;
+import android.util.Slog;
+import android.util.SparseBooleanArray;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.internal.R;
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.LocalServices;
+
+import java.io.PrintWriter;
+import java.util.Arrays;
+
+/**
+ * Controls Low Power Standby state.
+ *
+ * Instantiated by {@link PowerManagerService} only if Low Power Standby is supported.
+ *
+ * <p>Low Power Standby is active when all of the following conditions are met:
+ * <ul>
+ * <li>Low Power Standby is enabled
+ * <li>The device is not interactive, and has been non-interactive for a given timeout
+ * <li>The device is not in a doze maintenance window
+ * </ul>
+ *
+ * <p>When Low Power Standby is active, the following restrictions are applied to applications
+ * with procstate less important than {@link android.app.ActivityManager#PROCESS_STATE_BOUND_TOP}:
+ * <ul>
+ * <li>Network access is blocked
+ * <li>Wakelocks are disabled
+ * </ul>
+ *
+ * @hide
+ */
+public final class LowPowerStandbyController {
+ private static final String TAG = "LowPowerStandbyController";
+ private static final boolean DEBUG = false;
+ private static final boolean DEFAULT_ACTIVE_DURING_MAINTENANCE = false;
+
+ private static final int MSG_STANDBY_TIMEOUT = 0;
+ private static final int MSG_NOTIFY_ACTIVE_CHANGED = 1;
+ private static final int MSG_NOTIFY_ALLOWLIST_CHANGED = 2;
+
+ private final Handler mHandler;
+ private final SettingsObserver mSettingsObserver;
+ private final Object mLock = new Object();
+
+ private final Context mContext;
+ private final Clock mClock;
+ private final AlarmManager.OnAlarmListener mOnStandbyTimeoutExpired =
+ this::onStandbyTimeoutExpired;
+ private final LowPowerStandbyControllerInternal mLocalService = new LocalService();
+ private final SparseBooleanArray mAllowlistUids = new SparseBooleanArray();
+
+ private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ switch (intent.getAction()) {
+ case Intent.ACTION_SCREEN_OFF:
+ onNonInteractive();
+ break;
+ case Intent.ACTION_SCREEN_ON:
+ onInteractive();
+ break;
+ case PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED:
+ onDeviceIdleModeChanged();
+ break;
+ }
+ }
+ };
+
+ @GuardedBy("mLock")
+ private AlarmManager mAlarmManager;
+ @GuardedBy("mLock")
+ private PowerManager mPowerManager;
+ @GuardedBy("mLock")
+ private boolean mSupportedConfig;
+ @GuardedBy("mLock")
+ private boolean mEnabledByDefaultConfig;
+ @GuardedBy("mLock")
+ private int mStandbyTimeoutConfig;
+
+ /** Whether Low Power Standby is enabled in Settings */
+ @GuardedBy("mLock")
+ private boolean mIsEnabled;
+
+ /**
+ * Whether Low Power Standby is currently active (enforcing restrictions).
+ */
+ @GuardedBy("mLock")
+ private boolean mIsActive;
+
+ /** Whether the device is currently interactive */
+ @GuardedBy("mLock")
+ private boolean mIsInteractive;
+
+ /** The time the device was last interactive, in {@link SystemClock#elapsedRealtime()}. */
+ @GuardedBy("mLock")
+ private long mLastInteractiveTimeElapsed;
+
+ /**
+ * Whether we are in device idle mode.
+ * During maintenance windows Low Power Standby is deactivated to allow
+ * apps to run maintenance tasks.
+ */
+ @GuardedBy("mLock")
+ private boolean mIsDeviceIdle;
+
+ /**
+ * Whether the device has entered idle mode since becoming non-interactive.
+ * In the initial non-idle period after turning the screen off, Low Power Standby is already
+ * allowed to become active. Later non-idle periods are treated as maintenance windows, during
+ * which Low Power Standby is deactivated to allow apps to run maintenance tasks.
+ */
+ @GuardedBy("mLock")
+ private boolean mIdleSinceNonInteractive;
+
+ /** Whether Low Power Standby restrictions should be active during doze maintenance mode. */
+ @GuardedBy("mLock")
+ private boolean mActiveDuringMaintenance;
+
+ /** Force Low Power Standby to be active. */
+ @GuardedBy("mLock")
+ private boolean mForceActive;
+
+ /** Functional interface for providing time. */
+ @VisibleForTesting
+ interface Clock {
+ /** Returns milliseconds since boot, including time spent in sleep. */
+ long elapsedRealtime();
+ }
+
+ public LowPowerStandbyController(Context context, Looper looper, Clock clock) {
+ mContext = context;
+ mHandler = new LowPowerStandbyHandler(looper);
+ mClock = clock;
+ mSettingsObserver = new SettingsObserver(mHandler);
+ }
+
+ void systemReady() {
+ final Resources resources = mContext.getResources();
+ synchronized (mLock) {
+ mSupportedConfig = resources.getBoolean(
+ com.android.internal.R.bool.config_lowPowerStandbySupported);
+
+ if (!mSupportedConfig) {
+ return;
+ }
+
+ mAlarmManager = mContext.getSystemService(AlarmManager.class);
+ mPowerManager = mContext.getSystemService(PowerManager.class);
+
+ mStandbyTimeoutConfig = resources.getInteger(
+ R.integer.config_lowPowerStandbyNonInteractiveTimeout);
+ mEnabledByDefaultConfig = resources.getBoolean(
+ R.bool.config_lowPowerStandbyEnabledByDefault);
+
+ mIsInteractive = mPowerManager.isInteractive();
+
+ mContext.getContentResolver().registerContentObserver(Settings.Global.getUriFor(
+ Settings.Global.LOW_POWER_STANDBY_ENABLED),
+ false, mSettingsObserver, UserHandle.USER_ALL);
+ mContext.getContentResolver().registerContentObserver(Settings.Global.getUriFor(
+ Settings.Global.LOW_POWER_STANDBY_ACTIVE_DURING_MAINTENANCE),
+ false, mSettingsObserver, UserHandle.USER_ALL);
+ updateSettingsLocked();
+
+ if (mIsEnabled) {
+ registerBroadcastReceiver();
+ }
+ }
+
+ LocalServices.addService(LowPowerStandbyControllerInternal.class, mLocalService);
+ }
+
+ @GuardedBy("mLock")
+ private void updateSettingsLocked() {
+ final ContentResolver resolver = mContext.getContentResolver();
+ mIsEnabled = mSupportedConfig && Settings.Global.getInt(resolver,
+ Settings.Global.LOW_POWER_STANDBY_ENABLED,
+ mEnabledByDefaultConfig ? 1 : 0) != 0;
+ mActiveDuringMaintenance = Settings.Global.getInt(resolver,
+ Settings.Global.LOW_POWER_STANDBY_ACTIVE_DURING_MAINTENANCE,
+ DEFAULT_ACTIVE_DURING_MAINTENANCE ? 1 : 0) != 0;
+
+ updateActiveLocked();
+ }
+
+ @GuardedBy("mLock")
+ private void updateActiveLocked() {
+ final long now = mClock.elapsedRealtime();
+ final boolean standbyTimeoutExpired =
+ (now - mLastInteractiveTimeElapsed) >= mStandbyTimeoutConfig;
+ final boolean maintenanceMode = mIdleSinceNonInteractive && !mIsDeviceIdle;
+ final boolean newActive =
+ mForceActive || (mIsEnabled && !mIsInteractive && standbyTimeoutExpired
+ && (!maintenanceMode || mActiveDuringMaintenance));
+ if (DEBUG) {
+ Slog.d(TAG, "updateActiveLocked: mIsEnabled=" + mIsEnabled + ", mIsInteractive="
+ + mIsInteractive + ", standbyTimeoutExpired=" + standbyTimeoutExpired
+ + ", mIdleSinceNonInteractive=" + mIdleSinceNonInteractive + ", mIsDeviceIdle="
+ + mIsDeviceIdle + ", mActiveDuringMaintenance=" + mActiveDuringMaintenance
+ + ", mForceActive=" + mForceActive + ", mIsActive=" + mIsActive + ", newActive="
+ + newActive);
+ }
+ if (mIsActive != newActive) {
+ mIsActive = newActive;
+ if (DEBUG) {
+ Slog.d(TAG, "mIsActive changed, mIsActive=" + mIsActive);
+ }
+ enqueueNotifyActiveChangedLocked();
+ }
+ }
+
+ private void onNonInteractive() {
+ if (DEBUG) {
+ Slog.d(TAG, "onNonInteractive");
+ }
+ final long now = mClock.elapsedRealtime();
+ synchronized (mLock) {
+ mIsInteractive = false;
+ mIsDeviceIdle = false;
+ mLastInteractiveTimeElapsed = now;
+
+ if (mStandbyTimeoutConfig > 0) {
+ scheduleStandbyTimeoutAlarmLocked();
+ }
+
+ updateActiveLocked();
+ }
+ }
+
+ private void onInteractive() {
+ if (DEBUG) {
+ Slog.d(TAG, "onInteractive");
+ }
+
+ synchronized (mLock) {
+ cancelStandbyTimeoutAlarmLocked();
+ mIsInteractive = true;
+ mIsDeviceIdle = false;
+ mIdleSinceNonInteractive = false;
+ updateActiveLocked();
+ }
+ }
+
+ @GuardedBy("mLock")
+ private void scheduleStandbyTimeoutAlarmLocked() {
+ final long nextAlarmTime = SystemClock.elapsedRealtime() + mStandbyTimeoutConfig;
+ mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP,
+ nextAlarmTime, "LowPowerStandbyController.StandbyTimeout",
+ mOnStandbyTimeoutExpired, mHandler);
+ }
+
+ @GuardedBy("mLock")
+ private void cancelStandbyTimeoutAlarmLocked() {
+ mAlarmManager.cancel(mOnStandbyTimeoutExpired);
+ }
+
+ private void onDeviceIdleModeChanged() {
+ synchronized (mLock) {
+ mIsDeviceIdle = mPowerManager.isDeviceIdleMode();
+ if (DEBUG) {
+ Slog.d(TAG, "onDeviceIdleModeChanged, mIsDeviceIdle=" + mIsDeviceIdle);
+ }
+
+ mIdleSinceNonInteractive = mIdleSinceNonInteractive || mIsDeviceIdle;
+ updateActiveLocked();
+ }
+ }
+
+ @GuardedBy("mLock")
+ private void onEnabledLocked() {
+ if (DEBUG) {
+ Slog.d(TAG, "onEnabledLocked");
+ }
+
+ if (mPowerManager.isInteractive()) {
+ onInteractive();
+ } else {
+ onNonInteractive();
+ }
+
+ registerBroadcastReceiver();
+ }
+
+ @GuardedBy("mLock")
+ private void onDisabledLocked() {
+ if (DEBUG) {
+ Slog.d(TAG, "onDisabledLocked");
+ }
+
+ cancelStandbyTimeoutAlarmLocked();
+ unregisterBroadcastReceiver();
+ updateActiveLocked();
+ }
+
+ @VisibleForTesting
+ void onSettingsChanged() {
+ if (DEBUG) {
+ Slog.d(TAG, "onSettingsChanged");
+ }
+ synchronized (mLock) {
+ final boolean oldEnabled = mIsEnabled;
+ updateSettingsLocked();
+
+ if (mIsEnabled != oldEnabled) {
+ if (mIsEnabled) {
+ onEnabledLocked();
+ } else {
+ onDisabledLocked();
+ }
+
+ notifyEnabledChangedLocked();
+ }
+ }
+ }
+
+ private void registerBroadcastReceiver() {
+ IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED);
+ intentFilter.addAction(Intent.ACTION_SCREEN_ON);
+ intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
+
+ mContext.registerReceiver(mBroadcastReceiver, intentFilter);
+ }
+
+ private void unregisterBroadcastReceiver() {
+ mContext.unregisterReceiver(mBroadcastReceiver);
+ }
+
+ @GuardedBy("mLock")
+ private void notifyEnabledChangedLocked() {
+ if (DEBUG) {
+ Slog.d(TAG, "notifyEnabledChangedLocked, mIsEnabled=" + mIsEnabled);
+ }
+
+ final Intent intent = new Intent(PowerManager.ACTION_LOW_POWER_STANDBY_ENABLED_CHANGED);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND);
+ mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+ }
+
+ private void onStandbyTimeoutExpired() {
+ if (DEBUG) {
+ Slog.d(TAG, "onStandbyTimeoutExpired");
+ }
+ synchronized (mLock) {
+ updateActiveLocked();
+ }
+ }
+
+ @GuardedBy("mLock")
+ private void enqueueNotifyActiveChangedLocked() {
+ final long now = mClock.elapsedRealtime();
+ final Message msg = mHandler.obtainMessage(MSG_NOTIFY_ACTIVE_CHANGED, mIsActive);
+ mHandler.sendMessageAtTime(msg, now);
+ }
+
+ /** Notify other system components about the updated Low Power Standby active state */
+ private void notifyActiveChanged(boolean active) {
+ final PowerManagerInternal pmi = LocalServices.getService(PowerManagerInternal.class);
+ pmi.setLowPowerStandbyActive(active);
+ }
+
+ @VisibleForTesting
+ boolean isActive() {
+ synchronized (mLock) {
+ return mIsActive;
+ }
+ }
+
+ boolean isSupported() {
+ synchronized (mLock) {
+ return mSupportedConfig;
+ }
+ }
+
+ boolean isEnabled() {
+ synchronized (mLock) {
+ return mSupportedConfig && mIsEnabled;
+ }
+ }
+
+ void setEnabled(boolean enabled) {
+ synchronized (mLock) {
+ if (!mSupportedConfig) {
+ Slog.w(TAG, "Low Power Standby cannot be enabled "
+ + "because it is not supported on this device");
+ return;
+ }
+
+ Settings.Global.putInt(mContext.getContentResolver(),
+ Settings.Global.LOW_POWER_STANDBY_ENABLED, enabled ? 1 : 0);
+ onSettingsChanged();
+ }
+ }
+
+ void setActiveDuringMaintenance(boolean activeDuringMaintenance) {
+ synchronized (mLock) {
+ if (!mSupportedConfig) {
+ Slog.w(TAG, "Low Power Standby settings cannot be changed "
+ + "because it is not supported on this device");
+ return;
+ }
+
+ Settings.Global.putInt(mContext.getContentResolver(),
+ Settings.Global.LOW_POWER_STANDBY_ACTIVE_DURING_MAINTENANCE,
+ activeDuringMaintenance ? 1 : 0);
+ onSettingsChanged();
+ }
+ }
+
+ void forceActive(boolean active) {
+ synchronized (mLock) {
+ mForceActive = active;
+ updateActiveLocked();
+ }
+ }
+
+ void dump(PrintWriter pw) {
+ final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
+
+ ipw.println();
+ ipw.println("Low Power Standby Controller:");
+ ipw.increaseIndent();
+ synchronized (mLock) {
+ ipw.print("mIsActive=");
+ ipw.println(mIsActive);
+ ipw.print("mIsEnabled=");
+ ipw.println(mIsEnabled);
+ ipw.print("mSupportedConfig=");
+ ipw.println(mSupportedConfig);
+ ipw.print("mEnabledByDefaultConfig=");
+ ipw.println(mEnabledByDefaultConfig);
+ ipw.print("mStandbyTimeoutConfig=");
+ ipw.println(mStandbyTimeoutConfig);
+
+ if (mIsActive || mIsEnabled) {
+ ipw.print("mIsInteractive=");
+ ipw.println(mIsInteractive);
+ ipw.print("mLastInteractiveTime=");
+ ipw.println(mLastInteractiveTimeElapsed);
+ ipw.print("mIdleSinceNonInteractive=");
+ ipw.println(mIdleSinceNonInteractive);
+ ipw.print("mIsDeviceIdle=");
+ ipw.println(mIsDeviceIdle);
+ }
+
+ final int[] allowlistUids = getAllowlistUidsLocked();
+ ipw.print("mAllowlistUids=");
+ ipw.println(Arrays.toString(allowlistUids));
+ }
+ ipw.decreaseIndent();
+ }
+
+ void dumpProto(ProtoOutputStream proto, long tag) {
+ synchronized (mLock) {
+ final long token = proto.start(tag);
+ proto.write(LowPowerStandbyControllerDumpProto.IS_ACTIVE, mIsActive);
+ proto.write(LowPowerStandbyControllerDumpProto.IS_ENABLED, mIsEnabled);
+ proto.write(LowPowerStandbyControllerDumpProto.IS_SUPPORTED_CONFIG, mSupportedConfig);
+ proto.write(LowPowerStandbyControllerDumpProto.IS_ENABLED_BY_DEFAULT_CONFIG,
+ mEnabledByDefaultConfig);
+ proto.write(LowPowerStandbyControllerDumpProto.IS_INTERACTIVE, mIsInteractive);
+ proto.write(LowPowerStandbyControllerDumpProto.LAST_INTERACTIVE_TIME,
+ mLastInteractiveTimeElapsed);
+ proto.write(LowPowerStandbyControllerDumpProto.STANDBY_TIMEOUT_CONFIG,
+ mStandbyTimeoutConfig);
+ proto.write(LowPowerStandbyControllerDumpProto.IDLE_SINCE_NON_INTERACTIVE,
+ mIdleSinceNonInteractive);
+ proto.write(LowPowerStandbyControllerDumpProto.IS_DEVICE_IDLE, mIsDeviceIdle);
+
+ final int[] allowlistUids = getAllowlistUidsLocked();
+ for (int appId : allowlistUids) {
+ proto.write(LowPowerStandbyControllerDumpProto.ALLOWLIST, appId);
+ }
+
+ proto.end(token);
+ }
+ }
+
+ private class LowPowerStandbyHandler extends Handler {
+ LowPowerStandbyHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_STANDBY_TIMEOUT:
+ onStandbyTimeoutExpired();
+ break;
+ case MSG_NOTIFY_ACTIVE_CHANGED:
+ boolean active = (boolean) msg.obj;
+ notifyActiveChanged(active);
+ break;
+ case MSG_NOTIFY_ALLOWLIST_CHANGED:
+ final int[] allowlistUids = (int[]) msg.obj;
+ notifyAllowlistChanged(allowlistUids);
+ break;
+ }
+ }
+ }
+
+ private void addToAllowlistInternal(int uid) {
+ if (DEBUG) {
+ Slog.i(TAG, "Adding to allowlist: " + uid);
+ }
+ synchronized (mLock) {
+ if (mSupportedConfig && !mAllowlistUids.get(uid)) {
+ mAllowlistUids.append(uid, true);
+ enqueueNotifyAllowlistChangedLocked();
+ }
+ }
+ }
+
+ private void removeFromAllowlistInternal(int uid) {
+ if (DEBUG) {
+ Slog.i(TAG, "Removing from allowlist: " + uid);
+ }
+ synchronized (mLock) {
+ if (mSupportedConfig && mAllowlistUids.get(uid)) {
+ mAllowlistUids.delete(uid);
+ enqueueNotifyAllowlistChangedLocked();
+ }
+ }
+ }
+
+ @GuardedBy("mLock")
+ private int[] getAllowlistUidsLocked() {
+ final int[] uids = new int[mAllowlistUids.size()];
+ for (int i = 0; i < mAllowlistUids.size(); i++) {
+ uids[i] = mAllowlistUids.keyAt(i);
+ }
+ return uids;
+ }
+
+ @GuardedBy("mLock")
+ private void enqueueNotifyAllowlistChangedLocked() {
+ final long now = mClock.elapsedRealtime();
+ final int[] allowlistUids = getAllowlistUidsLocked();
+ final Message msg = mHandler.obtainMessage(MSG_NOTIFY_ALLOWLIST_CHANGED, allowlistUids);
+ mHandler.sendMessageAtTime(msg, now);
+ }
+
+ private void notifyAllowlistChanged(int[] allowlistUids) {
+ final PowerManagerInternal pmi = LocalServices.getService(PowerManagerInternal.class);
+ pmi.setLowPowerStandbyAllowlist(allowlistUids);
+ }
+
+ private final class LocalService extends LowPowerStandbyControllerInternal {
+ @Override
+ public void addToAllowlist(int uid) {
+ addToAllowlistInternal(uid);
+ }
+
+ @Override
+ public void removeFromAllowlist(int uid) {
+ removeFromAllowlistInternal(uid);
+ }
+ }
+
+ private final class SettingsObserver extends ContentObserver {
+ SettingsObserver(Handler handler) {
+ super(handler);
+ }
+
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ onSettingsChanged();
+ }
+ }
+}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/communal/ICommunalSurfaceCallback.aidl b/services/core/java/com/android/server/power/LowPowerStandbyControllerInternal.java
index 3d5998bffcbf..f6953faa8eec 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/communal/ICommunalSurfaceCallback.aidl
+++ b/services/core/java/com/android/server/power/LowPowerStandbyControllerInternal.java
@@ -14,17 +14,24 @@
* limitations under the License.
*/
-package com.android.systemui.shared.communal;
-
-import android.view.SurfaceControlViewHost.SurfacePackage;
+package com.android.server.power;
/**
-* An interface for receiving the result of a surface request. ICommunalSurfaceCallback is
-* implemented by the CommunalHost (SystemUI) to process the results of a new communal surface.
-*/
-interface ICommunalSurfaceCallback {
- /**
- * Invoked when the CommunalSurface has generated the SurfacePackage to be displayed.
- */
- void onSurface(in SurfacePackage surfacePackage) = 1;
-} \ No newline at end of file
+ * @hide Only for use within the system server.
+ */
+public abstract class LowPowerStandbyControllerInternal {
+ /**
+ * Adds an application to the Low Power Standby allowlist,
+ * exempting it from Low Power Standby restrictions.
+ *
+ * @param uid UID to add to allowlist.
+ */
+ public abstract void addToAllowlist(int uid);
+
+ /**
+ * Removes an application from the Low Power Standby allowlist.
+ *
+ * @param uid UID to remove from allowlist.
+ */
+ public abstract void removeFromAllowlist(int uid);
+}
diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java
index 73ec2cd66ac1..77d63100032c 100644
--- a/services/core/java/com/android/server/power/Notifier.java
+++ b/services/core/java/com/android/server/power/Notifier.java
@@ -33,6 +33,7 @@ import android.metrics.LogMaker;
import android.net.Uri;
import android.os.BatteryStats;
import android.os.Handler;
+import android.os.IWakeLockCallback;
import android.os.Looper;
import android.os.Message;
import android.os.PowerManager;
@@ -215,14 +216,15 @@ public class Notifier {
* Called when a wake lock is acquired.
*/
public void onWakeLockAcquired(int flags, String tag, String packageName,
- int ownerUid, int ownerPid, WorkSource workSource, String historyTag) {
+ int ownerUid, int ownerPid, WorkSource workSource, String historyTag,
+ IWakeLockCallback callback) {
if (DEBUG) {
Slog.d(TAG, "onWakeLockAcquired: flags=" + flags + ", tag=\"" + tag
+ "\", packageName=" + packageName
+ ", ownerUid=" + ownerUid + ", ownerPid=" + ownerPid
+ ", workSource=" + workSource);
}
-
+ notifyWakeLockListener(callback, true);
final int monitorType = getBatteryStatsWakeLockMonitorType(flags);
if (monitorType >= 0) {
try {
@@ -300,8 +302,9 @@ public class Notifier {
*/
public void onWakeLockChanging(int flags, String tag, String packageName,
int ownerUid, int ownerPid, WorkSource workSource, String historyTag,
- int newFlags, String newTag, String newPackageName, int newOwnerUid,
- int newOwnerPid, WorkSource newWorkSource, String newHistoryTag) {
+ IWakeLockCallback callback, int newFlags, String newTag, String newPackageName,
+ int newOwnerUid, int newOwnerPid, WorkSource newWorkSource, String newHistoryTag,
+ IWakeLockCallback newCallback) {
final int monitorType = getBatteryStatsWakeLockMonitorType(flags);
final int newMonitorType = getBatteryStatsWakeLockMonitorType(newFlags);
@@ -323,10 +326,16 @@ public class Notifier {
} catch (RemoteException ex) {
// Ignore
}
+ } else if (!PowerManagerService.isSameCallback(callback, newCallback)) {
+ onWakeLockReleased(flags, tag, packageName, ownerUid, ownerPid, workSource, historyTag,
+ null /* Do not notify the old callback */);
+ onWakeLockAcquired(newFlags, newTag, newPackageName, newOwnerUid, newOwnerPid,
+ newWorkSource, newHistoryTag, newCallback /* notify the new callback */);
} else {
- onWakeLockReleased(flags, tag, packageName, ownerUid, ownerPid, workSource, historyTag);
+ onWakeLockReleased(flags, tag, packageName, ownerUid, ownerPid, workSource, historyTag,
+ callback);
onWakeLockAcquired(newFlags, newTag, newPackageName, newOwnerUid, newOwnerPid,
- newWorkSource, newHistoryTag);
+ newWorkSource, newHistoryTag, newCallback);
}
}
@@ -334,14 +343,15 @@ public class Notifier {
* Called when a wake lock is released.
*/
public void onWakeLockReleased(int flags, String tag, String packageName,
- int ownerUid, int ownerPid, WorkSource workSource, String historyTag) {
+ int ownerUid, int ownerPid, WorkSource workSource, String historyTag,
+ IWakeLockCallback callback) {
if (DEBUG) {
Slog.d(TAG, "onWakeLockReleased: flags=" + flags + ", tag=\"" + tag
+ "\", packageName=" + packageName
+ ", ownerUid=" + ownerUid + ", ownerPid=" + ownerPid
+ ", workSource=" + workSource);
}
-
+ notifyWakeLockListener(callback, false);
final int monitorType = getBatteryStatsWakeLockMonitorType(flags);
if (monitorType >= 0) {
try {
@@ -859,6 +869,18 @@ public class Notifier {
return enabled && dndOff;
}
+ private void notifyWakeLockListener(IWakeLockCallback callback, boolean isEnabled) {
+ if (callback != null) {
+ mHandler.post(() -> {
+ try {
+ callback.onStateChanged(isEnabled);
+ } catch (RemoteException e) {
+ throw new IllegalArgumentException("Wakelock.mCallback is already dead.", e);
+ }
+ });
+ }
+ }
+
private final class NotifierHandler extends Handler {
public NotifierHandler(Looper looper) {
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 4185b2d9e497..38570727742d 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -37,6 +37,7 @@ import static com.android.internal.util.LatencyTracker.ACTION_TURN_ON_SCREEN;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.SynchronousUserSwitchObserver;
@@ -65,6 +66,7 @@ import android.os.Handler;
import android.os.HandlerExecutor;
import android.os.IBinder;
import android.os.IPowerManager;
+import android.os.IWakeLockCallback;
import android.os.Looper;
import android.os.Message;
import android.os.ParcelDuration;
@@ -276,6 +278,7 @@ public final class PowerManagerService extends SystemService
private final BatterySaverPolicy mBatterySaverPolicy;
private final BatterySaverStateMachine mBatterySaverStateMachine;
private final BatterySavingStats mBatterySavingStats;
+ private final LowPowerStandbyController mLowPowerStandbyController;
private final AttentionDetector mAttentionDetector;
private final FaceDownDetector mFaceDownDetector;
private final ScreenUndimDetector mScreenUndimDetector;
@@ -609,12 +612,17 @@ public final class PowerManagerService extends SystemService
// True if we are currently in light device idle mode.
private boolean mLightDeviceIdleMode;
- // Set of app ids that we will always respect the wake locks for.
+ // Set of app ids that we will respect the wake locks for while in device idle mode.
int[] mDeviceIdleWhitelist = new int[0];
// Set of app ids that are temporarily allowed to acquire wakelocks due to high-pri message
int[] mDeviceIdleTempWhitelist = new int[0];
+ // Set of app ids that are allowed to acquire wakelocks while low power standby is active
+ int[] mLowPowerStandbyAllowlist = new int[0];
+
+ private boolean mLowPowerStandbyActive;
+
private final SparseArray<UidState> mUidState = new SparseArray<>();
// A mapping from DisplayGroup Id to PowerGroup. There is a 1-1 mapping between DisplayGroups
@@ -966,6 +974,10 @@ public final class PowerManagerService extends SystemService
void invalidateIsInteractiveCaches() {
PowerManager.invalidateIsInteractiveCaches();
}
+
+ LowPowerStandbyController createLowPowerStandbyController(Context context, Looper looper) {
+ return new LowPowerStandbyController(context, looper, SystemClock::elapsedRealtime);
+ }
}
final Constants mConstants;
@@ -1015,6 +1027,8 @@ public final class PowerManagerService extends SystemService
mBatterySaverStateMachine = mInjector.createBatterySaverStateMachine(mLock, mContext,
mBatterySaverController);
+ mLowPowerStandbyController = mInjector.createLowPowerStandbyController(mContext,
+ Looper.getMainLooper());
mInattentiveSleepWarningOverlayController =
mInjector.createInattentiveSleepWarningController();
@@ -1228,6 +1242,8 @@ public final class PowerManagerService extends SystemService
// Shouldn't happen since in-process.
}
+ mLowPowerStandbyController.systemReady();
+
// Go.
readConfigurationLocked();
updateSettingsLocked();
@@ -1421,7 +1437,8 @@ public final class PowerManagerService extends SystemService
}
private void acquireWakeLockInternal(IBinder lock, int displayId, int flags, String tag,
- String packageName, WorkSource ws, String historyTag, int uid, int pid) {
+ String packageName, WorkSource ws, String historyTag, int uid, int pid,
+ @Nullable IWakeLockCallback callback) {
synchronized (mLock) {
if (displayId != Display.INVALID_DISPLAY) {
final DisplayInfo displayInfo =
@@ -1445,11 +1462,12 @@ public final class PowerManagerService extends SystemService
boolean notifyAcquire;
if (index >= 0) {
wakeLock = mWakeLocks.get(index);
- if (!wakeLock.hasSameProperties(flags, tag, ws, uid, pid)) {
+ if (!wakeLock.hasSameProperties(flags, tag, ws, uid, pid, callback)) {
// Update existing wake lock. This shouldn't happen but is harmless.
notifyWakeLockChangingLocked(wakeLock, flags, tag, packageName,
- uid, pid, ws, historyTag);
- wakeLock.updateProperties(flags, tag, packageName, ws, historyTag, uid, pid);
+ uid, pid, ws, historyTag, callback);
+ wakeLock.updateProperties(flags, tag, packageName, ws, historyTag, uid, pid,
+ callback);
}
notifyAcquire = false;
} else {
@@ -1461,12 +1479,7 @@ public final class PowerManagerService extends SystemService
}
state.mNumWakeLocks++;
wakeLock = new WakeLock(lock, displayId, flags, tag, packageName, ws, historyTag,
- uid, pid, state);
- try {
- lock.linkToDeath(wakeLock, 0);
- } catch (RemoteException ex) {
- throw new IllegalArgumentException("Wake lock is already dead.");
- }
+ uid, pid, state, callback);
mWakeLocks.add(wakeLock);
setWakeLockDisabledStateLocked(wakeLock);
notifyAcquire = true;
@@ -1561,11 +1574,8 @@ public final class PowerManagerService extends SystemService
mRequestWaitForNegativeProximity = true;
}
- try {
- wakeLock.mLock.unlinkToDeath(wakeLock, 0);
- } catch (NoSuchElementException e) {
- Slog.wtf(TAG, "Failed to unlink wakelock", e);
- }
+ wakeLock.unlinkToDeath();
+ wakeLock.setDisabled(true);
removeWakeLockLocked(wakeLock, index);
}
}
@@ -1635,13 +1645,41 @@ public final class PowerManagerService extends SystemService
if (!wakeLock.hasSameWorkSource(ws)) {
notifyWakeLockChangingLocked(wakeLock, wakeLock.mFlags, wakeLock.mTag,
wakeLock.mPackageName, wakeLock.mOwnerUid, wakeLock.mOwnerPid,
- ws, historyTag);
+ ws, historyTag, null);
wakeLock.mHistoryTag = historyTag;
wakeLock.updateWorkSource(ws);
}
}
}
+ private void updateWakeLockCallbackInternal(IBinder lock, IWakeLockCallback callback,
+ int callingUid) {
+ synchronized (mLock) {
+ int index = findWakeLockIndexLocked(lock);
+ if (index < 0) {
+ if (DEBUG_SPEW) {
+ Slog.d(TAG, "updateWakeLockCallbackInternal: lock=" + Objects.hashCode(lock)
+ + " [not found]");
+ }
+ throw new IllegalArgumentException("Wake lock not active: " + lock
+ + " from uid " + callingUid);
+ }
+
+ WakeLock wakeLock = mWakeLocks.get(index);
+ if (DEBUG_SPEW) {
+ Slog.d(TAG, "updateWakeLockCallbackInternal: lock=" + Objects.hashCode(lock)
+ + " [" + wakeLock.mTag + "]");
+ }
+
+ if (!isSameCallback(callback, wakeLock.mCallback)) {
+ notifyWakeLockChangingLocked(wakeLock, wakeLock.mFlags, wakeLock.mTag,
+ wakeLock.mPackageName, wakeLock.mOwnerUid, wakeLock.mOwnerPid,
+ wakeLock.mWorkSource, wakeLock.mHistoryTag, callback);
+ wakeLock.mCallback = callback;
+ }
+ }
+ }
+
@GuardedBy("mLock")
private int findWakeLockIndexLocked(IBinder lock) {
final int count = mWakeLocks.size();
@@ -1654,12 +1692,22 @@ public final class PowerManagerService extends SystemService
}
@GuardedBy("mLock")
+ @VisibleForTesting
+ WakeLock findWakeLockLocked(IBinder lock) {
+ int index = findWakeLockIndexLocked(lock);
+ if (index == -1) {
+ return null;
+ }
+ return mWakeLocks.get(index);
+ }
+
+ @GuardedBy("mLock")
private void notifyWakeLockAcquiredLocked(WakeLock wakeLock) {
if (mSystemReady && !wakeLock.mDisabled) {
wakeLock.mNotifiedAcquired = true;
mNotifier.onWakeLockAcquired(wakeLock.mFlags, wakeLock.mTag, wakeLock.mPackageName,
wakeLock.mOwnerUid, wakeLock.mOwnerPid, wakeLock.mWorkSource,
- wakeLock.mHistoryTag);
+ wakeLock.mHistoryTag, wakeLock.mCallback);
restartNofifyLongTimerLocked(wakeLock);
}
}
@@ -1701,11 +1749,13 @@ public final class PowerManagerService extends SystemService
@GuardedBy("mLock")
private void notifyWakeLockChangingLocked(WakeLock wakeLock, int flags, String tag,
- String packageName, int uid, int pid, WorkSource ws, String historyTag) {
+ String packageName, int uid, int pid, WorkSource ws, String historyTag,
+ IWakeLockCallback callback) {
if (mSystemReady && wakeLock.mNotifiedAcquired) {
mNotifier.onWakeLockChanging(wakeLock.mFlags, wakeLock.mTag, wakeLock.mPackageName,
wakeLock.mOwnerUid, wakeLock.mOwnerPid, wakeLock.mWorkSource,
- wakeLock.mHistoryTag, flags, tag, packageName, uid, pid, ws, historyTag);
+ wakeLock.mHistoryTag, wakeLock.mCallback, flags, tag, packageName, uid, pid, ws,
+ historyTag, callback);
notifyWakeLockLongFinishedLocked(wakeLock);
// Changing the wake lock will count as releasing the old wake lock(s) and
// acquiring the new ones... we do this because otherwise once a wakelock
@@ -1722,7 +1772,7 @@ public final class PowerManagerService extends SystemService
wakeLock.mAcquireTime = 0;
mNotifier.onWakeLockReleased(wakeLock.mFlags, wakeLock.mTag,
wakeLock.mPackageName, wakeLock.mOwnerUid, wakeLock.mOwnerPid,
- wakeLock.mWorkSource, wakeLock.mHistoryTag);
+ wakeLock.mWorkSource, wakeLock.mHistoryTag, wakeLock.mCallback);
notifyWakeLockLongFinishedLocked(wakeLock);
}
}
@@ -3852,6 +3902,24 @@ public final class PowerManagerService extends SystemService
}
}
+ void setLowPowerStandbyAllowlistInternal(int[] appids) {
+ synchronized (mLock) {
+ mLowPowerStandbyAllowlist = appids;
+ if (mLowPowerStandbyActive) {
+ updateWakeLockDisabledStatesLocked();
+ }
+ }
+ }
+
+ void setLowPowerStandbyActiveInternal(boolean active) {
+ synchronized (mLock) {
+ if (mLowPowerStandbyActive != active) {
+ mLowPowerStandbyActive = active;
+ updateWakeLockDisabledStatesLocked();
+ }
+ }
+ }
+
void startUidChangesInternal() {
synchronized (mLock) {
mUidsChanging = true;
@@ -3888,7 +3956,7 @@ public final class PowerManagerService extends SystemService
<= ActivityManager.PROCESS_STATE_RECEIVER;
state.mProcState = procState;
if (state.mNumWakeLocks > 0) {
- if (mDeviceIdleMode) {
+ if (mDeviceIdleMode || mLowPowerStandbyActive) {
handleUidStateChangeLocked();
} else if (!state.mActive && oldShouldAllow !=
(procState <= ActivityManager.PROCESS_STATE_RECEIVER)) {
@@ -3908,7 +3976,7 @@ public final class PowerManagerService extends SystemService
state.mProcState = ActivityManager.PROCESS_STATE_NONEXISTENT;
state.mActive = false;
mUidState.removeAt(index);
- if (mDeviceIdleMode && state.mNumWakeLocks > 0) {
+ if ((mDeviceIdleMode || mLowPowerStandbyActive) && state.mNumWakeLocks > 0) {
handleUidStateChangeLocked();
}
}
@@ -3993,11 +4061,16 @@ public final class PowerManagerService extends SystemService
disabled = true;
}
}
+ if (mLowPowerStandbyActive) {
+ final UidState state = wakeLock.mUidState;
+ if (Arrays.binarySearch(mLowPowerStandbyAllowlist, appid) < 0
+ && state.mProcState != ActivityManager.PROCESS_STATE_NONEXISTENT
+ && state.mProcState > ActivityManager.PROCESS_STATE_BOUND_TOP) {
+ disabled = true;
+ }
+ }
}
- if (wakeLock.mDisabled != disabled) {
- wakeLock.mDisabled = disabled;
- return true;
- }
+ return wakeLock.setDisabled(disabled);
}
return false;
}
@@ -4260,6 +4333,7 @@ public final class PowerManagerService extends SystemService
pw.println("POWER MANAGER (dumpsys power)\n");
final WirelessChargerDetector wcd;
+ final LowPowerStandbyController lowPowerStandbyController;
synchronized (mLock) {
pw.println("Power Manager State:");
mConstants.dump(pw);
@@ -4316,6 +4390,7 @@ public final class PowerManagerService extends SystemService
pw.println(" mDeviceIdleMode=" + mDeviceIdleMode);
pw.println(" mDeviceIdleWhitelist=" + Arrays.toString(mDeviceIdleWhitelist));
pw.println(" mDeviceIdleTempWhitelist=" + Arrays.toString(mDeviceIdleTempWhitelist));
+ pw.println(" mLowPowerStandbyActive=" + mLowPowerStandbyActive);
pw.println(" mLastWakeTime=" + TimeUtils.formatUptime(mLastGlobalWakeTime));
pw.println(" mLastSleepTime=" + TimeUtils.formatUptime(mLastGlobalSleepTime));
pw.println(" mLastSleepReason=" + PowerManager.sleepReasonToString(
@@ -4491,10 +4566,13 @@ public final class PowerManagerService extends SystemService
mFaceDownDetector.dump(pw);
mAmbientDisplaySuppressionController.dump(pw);
+
+ mLowPowerStandbyController.dump(pw);
}
private void dumpProto(FileDescriptor fd) {
final WirelessChargerDetector wcd;
+ final LowPowerStandbyController lowPowerStandbyController;
final ProtoOutputStream proto = new ProtoOutputStream(fd);
synchronized (mLock) {
@@ -4599,6 +4677,9 @@ public final class PowerManagerService extends SystemService
proto.write(PowerManagerServiceDumpProto.DEVICE_IDLE_TEMP_WHITELIST, id);
}
+ proto.write(PowerManagerServiceDumpProto.IS_LOW_POWER_STANDBY_ACTIVE,
+ mLowPowerStandbyActive);
+
proto.write(PowerManagerServiceDumpProto.LAST_WAKE_TIME_MS, mLastGlobalWakeTime);
proto.write(PowerManagerServiceDumpProto.LAST_SLEEP_TIME_MS, mLastGlobalSleepTime);
proto.write(
@@ -4832,6 +4913,7 @@ public final class PowerManagerService extends SystemService
for (SuspendBlocker sb : mSuspendBlockers) {
sb.dumpDebug(proto, PowerManagerServiceDumpProto.SUSPEND_BLOCKERS);
}
+
wcd = mWirelessChargerDetector;
}
@@ -4839,6 +4921,9 @@ public final class PowerManagerService extends SystemService
wcd.dumpDebug(proto, PowerManagerServiceDumpProto.WIRELESS_CHARGER_DETECTOR);
}
+ mLowPowerStandbyController.dumpProto(proto,
+ PowerManagerServiceDumpProto.LOW_POWER_STANDBY_CONTROLLER);
+
proto.flush();
}
@@ -4978,10 +5063,11 @@ public final class PowerManagerService extends SystemService
public boolean mNotifiedAcquired;
public boolean mNotifiedLong;
public boolean mDisabled;
+ public IWakeLockCallback mCallback;
public WakeLock(IBinder lock, int displayId, int flags, String tag, String packageName,
WorkSource workSource, String historyTag, int ownerUid, int ownerPid,
- UidState uidState) {
+ UidState uidState, @Nullable IWakeLockCallback callback) {
mLock = lock;
mDisplayId = displayId;
mFlags = flags;
@@ -4992,15 +5078,43 @@ public final class PowerManagerService extends SystemService
mOwnerUid = ownerUid;
mOwnerPid = ownerPid;
mUidState = uidState;
+ mCallback = callback;
+ linkToDeath();
}
@Override
public void binderDied() {
+ unlinkToDeath();
PowerManagerService.this.handleWakeLockDeath(this);
}
+ private void linkToDeath() {
+ try {
+ mLock.linkToDeath(this, 0);
+ } catch (RemoteException e) {
+ throw new IllegalArgumentException("Wakelock.mLock is already dead.");
+ }
+ }
+
+ @GuardedBy("mLock")
+ void unlinkToDeath() {
+ try {
+ mLock.unlinkToDeath(this, 0);
+ } catch (NoSuchElementException e) {
+ Slog.wtf(TAG, "Failed to unlink Wakelock.mLock", e);
+ }
+ }
+
+ public boolean setDisabled(boolean disabled) {
+ if (mDisabled != disabled) {
+ mDisabled = disabled;
+ return true;
+ } else {
+ return false;
+ }
+ }
public boolean hasSameProperties(int flags, String tag, WorkSource workSource,
- int ownerUid, int ownerPid) {
+ int ownerUid, int ownerPid, IWakeLockCallback callback) {
return mFlags == flags
&& mTag.equals(tag)
&& hasSameWorkSource(workSource)
@@ -5009,7 +5123,8 @@ public final class PowerManagerService extends SystemService
}
public void updateProperties(int flags, String tag, String packageName,
- WorkSource workSource, String historyTag, int ownerUid, int ownerPid) {
+ WorkSource workSource, String historyTag, int ownerUid, int ownerPid,
+ IWakeLockCallback callback) {
if (!mPackageName.equals(packageName)) {
throw new IllegalStateException("Existing wake lock package name changed: "
+ mPackageName + " to " + packageName);
@@ -5026,6 +5141,7 @@ public final class PowerManagerService extends SystemService
mTag = tag;
updateWorkSource(workSource);
mHistoryTag = historyTag;
+ mCallback = callback;
}
public boolean hasSameWorkSource(WorkSource workSource) {
@@ -5092,6 +5208,8 @@ public final class PowerManagerService extends SystemService
(mFlags & PowerManager.ACQUIRE_CAUSES_WAKEUP)!=0);
proto.write(WakeLockProto.WakeLockFlagsProto.IS_ON_AFTER_RELEASE,
(mFlags & PowerManager.ON_AFTER_RELEASE)!=0);
+ proto.write(WakeLockProto.WakeLockFlagsProto.SYSTEM_WAKELOCK,
+ (mFlags & PowerManager.SYSTEM_WAKELOCK) != 0);
proto.end(wakeLockFlagsToken);
proto.write(WakeLockProto.IS_DISABLED, mDisabled);
@@ -5138,6 +5256,9 @@ public final class PowerManagerService extends SystemService
if ((mFlags & PowerManager.ON_AFTER_RELEASE) != 0) {
result += " ON_AFTER_RELEASE";
}
+ if ((mFlags & PowerManager.SYSTEM_WAKELOCK) != 0) {
+ result += " SYSTEM_WAKELOCK";
+ }
return result;
}
}
@@ -5239,11 +5360,12 @@ public final class PowerManagerService extends SystemService
@Override // Binder call
public void acquireWakeLockWithUid(IBinder lock, int flags, String tag,
- String packageName, int uid, int displayId) {
+ String packageName, int uid, int displayId, IWakeLockCallback callback) {
if (uid < 0) {
uid = Binder.getCallingUid();
}
- acquireWakeLock(lock, flags, tag, packageName, new WorkSource(uid), null, displayId);
+ acquireWakeLock(lock, flags, tag, packageName, new WorkSource(uid), null,
+ displayId, callback);
}
@Override // Binder call
@@ -5278,7 +5400,8 @@ public final class PowerManagerService extends SystemService
@Override // Binder call
public void acquireWakeLock(IBinder lock, int flags, String tag, String packageName,
- WorkSource ws, String historyTag, int displayId) {
+ WorkSource ws, String historyTag, int displayId,
+ @Nullable IWakeLockCallback callback) {
if (lock == null) {
throw new IllegalArgumentException("lock must not be null");
}
@@ -5299,12 +5422,26 @@ public final class PowerManagerService extends SystemService
ws = null;
}
- final int uid = Binder.getCallingUid();
- final int pid = Binder.getCallingPid();
+ int uid = Binder.getCallingUid();
+ int pid = Binder.getCallingPid();
+
+ if ((flags & PowerManager.SYSTEM_WAKELOCK) != 0) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER,
+ null);
+ WorkSource workSource = new WorkSource(Binder.getCallingUid(), packageName);
+ if (ws != null && !ws.isEmpty()) {
+ workSource.add(ws);
+ }
+ ws = workSource;
+
+ uid = Process.myUid();
+ pid = Process.myPid();
+ }
+
final long ident = Binder.clearCallingIdentity();
try {
acquireWakeLockInternal(lock, displayId, flags, tag, packageName, ws, historyTag,
- uid, pid);
+ uid, pid, callback);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -5313,7 +5450,8 @@ public final class PowerManagerService extends SystemService
@Override // Binder call
public void acquireWakeLockAsync(IBinder lock, int flags, String tag, String packageName,
WorkSource ws, String historyTag) {
- acquireWakeLock(lock, flags, tag, packageName, ws, historyTag, Display.INVALID_DISPLAY);
+ acquireWakeLock(lock, flags, tag, packageName, ws, historyTag, Display.INVALID_DISPLAY,
+ null);
}
@Override // Binder call
@@ -5381,6 +5519,23 @@ public final class PowerManagerService extends SystemService
}
@Override // Binder call
+ public void updateWakeLockCallback(IBinder lock, IWakeLockCallback callback) {
+ if (lock == null) {
+ throw new IllegalArgumentException("lock must not be null");
+ }
+
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null);
+
+ final int callingUid = Binder.getCallingUid();
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ updateWakeLockCallbackInternal(lock, callback, callingUid);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ @Override // Binder call
public boolean isWakeLockLevelSupported(int level) {
final long ident = Binder.clearCallingIdentity();
try {
@@ -5768,6 +5923,100 @@ public final class PowerManagerService extends SystemService
}
}
+ @Override // Binder call
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.MANAGE_LOW_POWER_STANDBY,
+ android.Manifest.permission.DEVICE_POWER
+ })
+ public boolean isLowPowerStandbySupported() {
+ if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER)
+ != PackageManager.PERMISSION_GRANTED) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.MANAGE_LOW_POWER_STANDBY,
+ "isLowPowerStandbySupported");
+ }
+
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ return mLowPowerStandbyController.isSupported();
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ @Override // Binder call
+ public boolean isLowPowerStandbyEnabled() {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ return mLowPowerStandbyController.isEnabled();
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ @Override // Binder call
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.MANAGE_LOW_POWER_STANDBY,
+ android.Manifest.permission.DEVICE_POWER
+ })
+ public void setLowPowerStandbyEnabled(boolean enabled) {
+ if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER)
+ != PackageManager.PERMISSION_GRANTED) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.MANAGE_LOW_POWER_STANDBY,
+ "setLowPowerStandbyEnabled");
+ }
+
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mLowPowerStandbyController.setEnabled(enabled);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ @Override // Binder call
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.MANAGE_LOW_POWER_STANDBY,
+ android.Manifest.permission.DEVICE_POWER
+ })
+ public void setLowPowerStandbyActiveDuringMaintenance(boolean activeDuringMaintenance) {
+ if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER)
+ != PackageManager.PERMISSION_GRANTED) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.MANAGE_LOW_POWER_STANDBY,
+ "setLowPowerStandbyActiveDuringMaintenance");
+ }
+
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mLowPowerStandbyController.setActiveDuringMaintenance(activeDuringMaintenance);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ @Override // Binder call
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.MANAGE_LOW_POWER_STANDBY,
+ android.Manifest.permission.DEVICE_POWER
+ })
+ public void forceLowPowerStandbyActive(boolean active) {
+ if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER)
+ != PackageManager.PERMISSION_GRANTED) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.MANAGE_LOW_POWER_STANDBY,
+ "forceLowPowerStandbyActive");
+ }
+
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mLowPowerStandbyController.forceActive(active);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
/**
* Gets the reason for the last time the phone had to reboot.
*
@@ -6249,6 +6498,16 @@ public final class PowerManagerService extends SystemService
}
@Override
+ public void setLowPowerStandbyAllowlist(int[] appids) {
+ setLowPowerStandbyAllowlistInternal(appids);
+ }
+
+ @Override
+ public void setLowPowerStandbyActive(boolean enabled) {
+ setLowPowerStandbyActiveInternal(enabled);
+ }
+
+ @Override
public void startUidChanges() {
startUidChangesInternal();
}
@@ -6324,4 +6583,15 @@ public final class PowerManagerService extends SystemService
}
};
+ static boolean isSameCallback(IWakeLockCallback callback1,
+ IWakeLockCallback callback2) {
+ if (callback1 == callback2) {
+ return true;
+ }
+ if (callback1 != null && callback2 != null
+ && callback1.asBinder() == callback2.asBinder()) {
+ return true;
+ }
+ return false;
+ }
}
diff --git a/services/core/java/com/android/server/power/WakeLockLog.java b/services/core/java/com/android/server/power/WakeLockLog.java
index 88c9850966f1..d20c7f1fe9c6 100644
--- a/services/core/java/com/android/server/power/WakeLockLog.java
+++ b/services/core/java/com/android/server/power/WakeLockLog.java
@@ -106,6 +106,7 @@ final class WakeLockLog {
*/
private static final int FLAG_ON_AFTER_RELEASE = 0x8;
private static final int FLAG_ACQUIRE_CAUSES_WAKEUP = 0x10;
+ private static final int FLAG_SYSTEM_WAKELOCK = 0x20;
private static final int MASK_LOWER_6_BITS = 0x3F;
private static final int MASK_LOWER_7_BITS = 0x7F;
@@ -296,6 +297,9 @@ final class WakeLockLog {
if ((flags & PowerManager.ON_AFTER_RELEASE) != 0) {
newFlags |= FLAG_ON_AFTER_RELEASE;
}
+ if ((flags & PowerManager.SYSTEM_WAKELOCK) != 0) {
+ newFlags |= FLAG_SYSTEM_WAKELOCK;
+ }
return newFlags;
}
@@ -455,6 +459,9 @@ final class WakeLockLog {
if ((flags & FLAG_ACQUIRE_CAUSES_WAKEUP) == FLAG_ACQUIRE_CAUSES_WAKEUP) {
sb.append(",acq-causes-wake");
}
+ if ((flags & FLAG_SYSTEM_WAKELOCK) == FLAG_SYSTEM_WAKELOCK) {
+ sb.append(",system-wakelock");
+ }
}
}
diff --git a/services/core/java/com/android/server/resources/ResourcesManagerService.java b/services/core/java/com/android/server/resources/ResourcesManagerService.java
new file mode 100644
index 000000000000..cc275466a5ff
--- /dev/null
+++ b/services/core/java/com/android/server/resources/ResourcesManagerService.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.resources;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.res.IResourcesManager;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.ParcelFileDescriptor;
+import android.os.Process;
+import android.os.RemoteCallback;
+import android.os.RemoteException;
+
+import com.android.server.SystemService;
+import com.android.server.am.ActivityManagerService;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+/**
+ * A service for managing information about ResourcesManagers
+ */
+public class ResourcesManagerService extends SystemService {
+ private ActivityManagerService mActivityManagerService;
+
+ /**
+ * Initializes the system service.
+ * <p>
+ * Subclasses must define a single argument constructor that accepts the context
+ * and passes it to super.
+ * </p>
+ *
+ * @param context The system server context.
+ */
+ public ResourcesManagerService(@NonNull Context context) {
+ super(context);
+ publishBinderService(Context.RESOURCES_SERVICE, mService);
+ }
+
+ @Override
+ public void onStart() {
+ // Intentionally left empty.
+ }
+
+ private final IBinder mService = new IResourcesManager.Stub() {
+ @Override
+ public boolean dumpResources(String process, ParcelFileDescriptor fd,
+ RemoteCallback callback) throws RemoteException {
+ final int callingUid = Binder.getCallingUid();
+ if (callingUid != Process.ROOT_UID && callingUid != Process.SHELL_UID) {
+ callback.sendResult(null);
+ throw new SecurityException("dump should only be called by shell");
+ }
+ return mActivityManagerService.dumpResources(process, fd, callback);
+ }
+
+ @Override
+ protected void dump(@NonNull FileDescriptor fd,
+ @NonNull PrintWriter pw, @Nullable String[] args) {
+ try {
+ mActivityManagerService.dumpAllResources(ParcelFileDescriptor.dup(fd), pw);
+ } catch (Exception e) {
+ pw.println("Exception while trying to dump all resources: " + e.getMessage());
+ e.printStackTrace(pw);
+ }
+ }
+
+ @Override
+ public int handleShellCommand(@NonNull ParcelFileDescriptor in,
+ @NonNull ParcelFileDescriptor out,
+ @NonNull ParcelFileDescriptor err,
+ @NonNull String[] args) {
+ return (new ResourcesManagerShellCommand(this)).exec(
+ this,
+ in.getFileDescriptor(),
+ out.getFileDescriptor(),
+ err.getFileDescriptor(),
+ args);
+ }
+ };
+
+ public void setActivityManagerService(
+ ActivityManagerService activityManagerService) {
+ mActivityManagerService = activityManagerService;
+ }
+}
diff --git a/services/core/java/com/android/server/resources/ResourcesManagerShellCommand.java b/services/core/java/com/android/server/resources/ResourcesManagerShellCommand.java
new file mode 100644
index 000000000000..7d8336a0d3e9
--- /dev/null
+++ b/services/core/java/com/android/server/resources/ResourcesManagerShellCommand.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.resources;
+
+import android.content.res.IResourcesManager;
+import android.os.ConditionVariable;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteCallback;
+import android.os.RemoteException;
+import android.os.ShellCommand;
+import android.util.Slog;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+
+/**
+ * Shell command handler for resources related commands
+ */
+public class ResourcesManagerShellCommand extends ShellCommand {
+ private static final String TAG = "ResourcesManagerShellCommand";
+
+ private final IResourcesManager mInterface;
+
+ public ResourcesManagerShellCommand(IResourcesManager anInterface) {
+ mInterface = anInterface;
+ }
+
+ @Override
+ public int onCommand(String cmd) {
+ if (cmd == null) {
+ return handleDefaultCommands(cmd);
+ }
+ final PrintWriter err = getErrPrintWriter();
+ try {
+ switch (cmd) {
+ case "dump":
+ return dumpResources();
+ default:
+ return handleDefaultCommands(cmd);
+ }
+ } catch (IllegalArgumentException e) {
+ err.println("Error: " + e.getMessage());
+ } catch (RemoteException e) {
+ err.println("Remote exception: " + e);
+ }
+ return -1;
+ }
+
+ private int dumpResources() throws RemoteException {
+ String processId = getNextArgRequired();
+ try {
+ ConditionVariable lock = new ConditionVariable();
+ RemoteCallback
+ finishCallback = new RemoteCallback(result -> lock.open(), null);
+
+ if (!mInterface.dumpResources(processId,
+ ParcelFileDescriptor.dup(getOutFileDescriptor()), finishCallback)) {
+ getErrPrintWriter().println("RESOURCES DUMP FAILED on process " + processId);
+ return -1;
+ }
+ lock.block(5000);
+ return 0;
+ } catch (IOException e) {
+ Slog.e(TAG, "Exception while dumping resources", e);
+ getErrPrintWriter().println("Exception while dumping resources: " + e.getMessage());
+ }
+ return -1;
+ }
+
+ @Override
+ public void onHelp() {
+ final PrintWriter out = getOutPrintWriter();
+ out.println("Resources manager commands:");
+ out.println(" help");
+ out.println(" Print this help text.");
+ out.println(" dump <PROCESS>");
+ out.println(" Dump the Resources objects in use as well as the history of Resources");
+
+ }
+}
diff --git a/services/core/java/com/android/server/sensorprivacy/AllSensorStateController.java b/services/core/java/com/android/server/sensorprivacy/AllSensorStateController.java
new file mode 100644
index 000000000000..f797f09d1afb
--- /dev/null
+++ b/services/core/java/com/android/server/sensorprivacy/AllSensorStateController.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.sensorprivacy;
+
+import android.annotation.NonNull;
+import android.os.Environment;
+import android.os.Handler;
+import android.util.AtomicFile;
+import android.util.Log;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
+import android.util.Xml;
+
+import com.android.internal.util.XmlUtils;
+import com.android.internal.util.dump.DualDumpOutputStream;
+import com.android.internal.util.function.pooled.PooledLambda;
+import com.android.server.IoThread;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Objects;
+
+class AllSensorStateController {
+
+ private static final String LOG_TAG = AllSensorStateController.class.getSimpleName();
+
+ private static final String SENSOR_PRIVACY_XML_FILE = "sensor_privacy.xml";
+ private static final String XML_TAG_SENSOR_PRIVACY = "all-sensor-privacy";
+ private static final String XML_TAG_SENSOR_PRIVACY_LEGACY = "sensor-privacy";
+ private static final String XML_ATTRIBUTE_ENABLED = "enabled";
+
+ private static AllSensorStateController sInstance;
+
+ private final AtomicFile mAtomicFile =
+ new AtomicFile(new File(Environment.getDataSystemDirectory(), SENSOR_PRIVACY_XML_FILE));
+
+ private boolean mEnabled;
+ private SensorPrivacyStateController.AllSensorPrivacyListener mListener;
+ private Handler mListenerHandler;
+
+ static AllSensorStateController getInstance() {
+ if (sInstance == null) {
+ sInstance = new AllSensorStateController();
+ }
+ return sInstance;
+ }
+
+ private AllSensorStateController() {
+ if (!mAtomicFile.exists()) {
+ return;
+ }
+ try (FileInputStream inputStream = mAtomicFile.openRead()) {
+ TypedXmlPullParser parser = Xml.resolvePullParser(inputStream);
+
+ while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
+ String tagName = parser.getName();
+ if (XML_TAG_SENSOR_PRIVACY.equals(tagName)) {
+ mEnabled |= XmlUtils
+ .readBooleanAttribute(parser, XML_ATTRIBUTE_ENABLED, false);
+ break;
+ }
+ if (XML_TAG_SENSOR_PRIVACY_LEGACY.equals(tagName)) {
+ mEnabled |= XmlUtils
+ .readBooleanAttribute(parser, XML_ATTRIBUTE_ENABLED, false);
+ }
+ if ("user".equals(tagName)) { // Migrate from mic/cam toggles format
+ int user = XmlUtils.readIntAttribute(parser, "id", -1);
+ if (user == 0) {
+ mEnabled |=
+ XmlUtils.readBooleanAttribute(parser, XML_ATTRIBUTE_ENABLED);
+ }
+ }
+ XmlUtils.nextElement(parser);
+ }
+ } catch (IOException | XmlPullParserException e) {
+ Log.e(LOG_TAG, "Caught an exception reading the state from storage: ", e);
+ mEnabled = false;
+ }
+ }
+
+ public boolean getAllSensorStateLocked() {
+ return mEnabled;
+ }
+
+ public void setAllSensorStateLocked(boolean enabled) {
+ if (mEnabled != enabled) {
+ mEnabled = enabled;
+ if (mListener != null && mListenerHandler != null) {
+ mListenerHandler.sendMessage(
+ PooledLambda.obtainMessage(mListener::onAllSensorPrivacyChanged, enabled));
+ }
+ }
+ }
+
+ void setAllSensorPrivacyListenerLocked(Handler handler,
+ SensorPrivacyStateController.AllSensorPrivacyListener listener) {
+ Objects.requireNonNull(handler);
+ Objects.requireNonNull(listener);
+ if (mListener != null) {
+ throw new IllegalStateException("Listener is already set");
+ }
+ mListener = listener;
+ mListenerHandler = handler;
+ }
+
+ public void schedulePersistLocked() {
+ IoThread.getHandler().sendMessage(PooledLambda.obtainMessage(this::persist, mEnabled));
+ }
+
+ private void persist(boolean enabled) {
+ FileOutputStream outputStream = null;
+ try {
+ outputStream = mAtomicFile.startWrite();
+ TypedXmlSerializer serializer = Xml.resolveSerializer(outputStream);
+ serializer.startDocument(null, true);
+ serializer.startTag(null, XML_TAG_SENSOR_PRIVACY);
+ serializer.attributeBoolean(null, XML_ATTRIBUTE_ENABLED, enabled);
+ serializer.endTag(null, XML_TAG_SENSOR_PRIVACY);
+ serializer.endDocument();
+ mAtomicFile.finishWrite(outputStream);
+ } catch (IOException e) {
+ Log.e(LOG_TAG, "Caught an exception persisting the sensor privacy state: ", e);
+ mAtomicFile.failWrite(outputStream);
+ }
+ }
+
+ void resetForTesting() {
+ mListener = null;
+ mListenerHandler = null;
+ mEnabled = false;
+ }
+
+ void dumpLocked(@NonNull DualDumpOutputStream dumpStream) {
+ // TODO stub
+ }
+}
diff --git a/services/core/java/com/android/server/sensorprivacy/OWNERS b/services/core/java/com/android/server/sensorprivacy/OWNERS
new file mode 100644
index 000000000000..15e3f7af718c
--- /dev/null
+++ b/services/core/java/com/android/server/sensorprivacy/OWNERS
@@ -0,0 +1 @@
+include platform/frameworks/native:/libs/sensorprivacy/OWNERS \ No newline at end of file
diff --git a/services/core/java/com/android/server/sensorprivacy/PersistedState.java b/services/core/java/com/android/server/sensorprivacy/PersistedState.java
new file mode 100644
index 000000000000..ce9fff599d43
--- /dev/null
+++ b/services/core/java/com/android/server/sensorprivacy/PersistedState.java
@@ -0,0 +1,494 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.sensorprivacy;
+
+import android.hardware.SensorPrivacyManager;
+import android.os.Environment;
+import android.os.UserHandle;
+import android.service.SensorPrivacyIndividualEnabledSensorProto;
+import android.service.SensorPrivacySensorProto;
+import android.service.SensorPrivacyServiceDumpProto;
+import android.service.SensorPrivacyUserProto;
+import android.util.ArrayMap;
+import android.util.AtomicFile;
+import android.util.Log;
+import android.util.Pair;
+import android.util.SparseArray;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
+import android.util.Xml;
+
+import com.android.internal.util.XmlUtils;
+import com.android.internal.util.dump.DualDumpOutputStream;
+import com.android.internal.util.function.QuadConsumer;
+import com.android.internal.util.function.pooled.PooledLambda;
+import com.android.server.IoThread;
+import com.android.server.LocalServices;
+import com.android.server.pm.UserManagerInternal;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+/**
+ * Class for managing persisted state. Synchronization must be handled by the caller.
+ */
+class PersistedState {
+
+ private static final String LOG_TAG = PersistedState.class.getSimpleName();
+
+ /** Version number indicating compatibility parsing the persisted file */
+ private static final int CURRENT_PERSISTENCE_VERSION = 2;
+ /** Version number indicating the persisted data needs upgraded to match new internal data
+ * structures and features */
+ private static final int CURRENT_VERSION = 2;
+
+ private static final String XML_TAG_SENSOR_PRIVACY = "sensor-privacy";
+ private static final String XML_TAG_SENSOR_STATE = "sensor-state";
+ private static final String XML_ATTRIBUTE_PERSISTENCE_VERSION = "persistence-version";
+ private static final String XML_ATTRIBUTE_VERSION = "version";
+ private static final String XML_ATTRIBUTE_TOGGLE_TYPE = "toggle-type";
+ private static final String XML_ATTRIBUTE_USER_ID = "user-id";
+ private static final String XML_ATTRIBUTE_SENSOR = "sensor";
+ private static final String XML_ATTRIBUTE_STATE_TYPE = "state-type";
+ private static final String XML_ATTRIBUTE_LAST_CHANGE = "last-change";
+
+ private final AtomicFile mAtomicFile;
+
+ private ArrayMap<TypeUserSensor, SensorState> mStates = new ArrayMap<>();
+
+ static PersistedState fromFile(String fileName) {
+ return new PersistedState(fileName);
+ }
+
+ private PersistedState(String fileName) {
+ mAtomicFile = new AtomicFile(new File(Environment.getDataSystemDirectory(), fileName));
+ readState();
+ }
+
+ private void readState() {
+ AtomicFile file = mAtomicFile;
+ if (!file.exists()) {
+ AtomicFile fileToMigrateFrom =
+ new AtomicFile(new File(Environment.getDataSystemDirectory(),
+ "sensor_privacy.xml"));
+
+ if (fileToMigrateFrom.exists()) {
+ // Sample the start tag to determine if migration is needed
+ try (FileInputStream inputStream = fileToMigrateFrom.openRead()) {
+ TypedXmlPullParser parser = Xml.resolvePullParser(inputStream);
+ XmlUtils.beginDocument(parser, XML_TAG_SENSOR_PRIVACY);
+ file = fileToMigrateFrom;
+ } catch (IOException e) {
+ Log.e(LOG_TAG, "Caught an exception reading the state from storage: ", e);
+ // Delete the file to prevent the same error on subsequent calls and assume
+ // sensor privacy is not enabled.
+ fileToMigrateFrom.delete();
+ } catch (XmlPullParserException e) {
+ // No migration needed
+ }
+ }
+ }
+
+ Object nonupgradedState = null;
+ if (file.exists()) {
+ try (FileInputStream inputStream = file.openRead()) {
+ TypedXmlPullParser parser = Xml.resolvePullParser(inputStream);
+ XmlUtils.beginDocument(parser, XML_TAG_SENSOR_PRIVACY);
+ final int persistenceVersion = parser.getAttributeInt(null,
+ XML_ATTRIBUTE_PERSISTENCE_VERSION, 0);
+
+ // Use inline string literals for xml tags/attrs when parsing old versions since
+ // these should never be changed even with refactorings.
+ if (persistenceVersion == 0) {
+ int version = 0;
+ PVersion0 version0 = new PVersion0(version);
+ nonupgradedState = version0;
+ readPVersion0(parser, version0);
+ } else if (persistenceVersion == 1) {
+ int version = parser.getAttributeInt(null,
+ "version", 1);
+ PVersion1 version1 = new PVersion1(version);
+ nonupgradedState = version1;
+
+ readPVersion1(parser, version1);
+ } else if (persistenceVersion == CURRENT_PERSISTENCE_VERSION) {
+ int version = parser.getAttributeInt(null,
+ XML_ATTRIBUTE_VERSION, 2);
+ PVersion2 version2 = new PVersion2(version);
+ nonupgradedState = version2;
+
+ readPVersion2(parser, version2);
+ } else {
+ Log.e(LOG_TAG, "Unknown persistence version: " + persistenceVersion
+ + ". Deleting.",
+ new RuntimeException());
+ file.delete();
+ nonupgradedState = null;
+ }
+
+ } catch (IOException | XmlPullParserException | RuntimeException e) {
+ Log.e(LOG_TAG, "Caught an exception reading the state from storage: ", e);
+ // Delete the file to prevent the same error on subsequent calls and assume
+ // sensor privacy is not enabled.
+ file.delete();
+ nonupgradedState = null;
+ }
+ }
+
+ if (nonupgradedState == null) {
+ // New file, default state for current version goes here.
+ nonupgradedState = new PVersion2(2);
+ }
+
+ if (nonupgradedState instanceof PVersion0) {
+ nonupgradedState = PVersion1.fromPVersion0((PVersion0) nonupgradedState);
+ }
+ if (nonupgradedState instanceof PVersion1) {
+ nonupgradedState = PVersion2.fromPVersion1((PVersion1) nonupgradedState);
+ }
+ if (nonupgradedState instanceof PVersion2) {
+ PVersion2 upgradedState = (PVersion2) nonupgradedState;
+ mStates = upgradedState.mStates;
+ } else {
+ Log.e(LOG_TAG, "State not successfully upgraded.");
+ mStates = new ArrayMap<>();
+ }
+ }
+
+ private static void readPVersion0(TypedXmlPullParser parser, PVersion0 version0)
+ throws XmlPullParserException, IOException {
+
+ XmlUtils.nextElement(parser);
+ while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
+ if ("individual-sensor-privacy".equals(parser.getName())) {
+ int sensor = XmlUtils.readIntAttribute(parser, "sensor");
+ boolean indEnabled = XmlUtils.readBooleanAttribute(parser,
+ "enabled");
+ version0.addState(sensor, indEnabled);
+ XmlUtils.skipCurrentTag(parser);
+ } else {
+ XmlUtils.nextElement(parser);
+ }
+ }
+ }
+
+ private static void readPVersion1(TypedXmlPullParser parser, PVersion1 version1)
+ throws XmlPullParserException, IOException {
+ while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
+ XmlUtils.nextElement(parser);
+
+ if ("user".equals(parser.getName())) {
+ int currentUserId = parser.getAttributeInt(null, "id");
+ int depth = parser.getDepth();
+ while (XmlUtils.nextElementWithin(parser, depth)) {
+ if ("individual-sensor-privacy".equals(parser.getName())) {
+ int sensor = parser.getAttributeInt(null, "sensor");
+ boolean isEnabled = parser.getAttributeBoolean(null,
+ "enabled");
+ version1.addState(currentUserId, sensor, isEnabled);
+ }
+ }
+ }
+ }
+ }
+
+ private static void readPVersion2(TypedXmlPullParser parser, PVersion2 version2)
+ throws XmlPullParserException, IOException {
+
+ while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
+ XmlUtils.nextElement(parser);
+
+ if (XML_TAG_SENSOR_STATE.equals(parser.getName())) {
+ int toggleType = parser.getAttributeInt(null, XML_ATTRIBUTE_TOGGLE_TYPE);
+ int userId = parser.getAttributeInt(null, XML_ATTRIBUTE_USER_ID);
+ int sensor = parser.getAttributeInt(null, XML_ATTRIBUTE_SENSOR);
+ int state = parser.getAttributeInt(null, XML_ATTRIBUTE_STATE_TYPE);
+ long lastChange = parser.getAttributeLong(null, XML_ATTRIBUTE_LAST_CHANGE);
+
+ version2.addState(toggleType, userId, sensor, state, lastChange);
+ } else {
+ XmlUtils.skipCurrentTag(parser);
+ }
+ }
+ }
+
+ public SensorState getState(int toggleType, int userId, int sensor) {
+ return mStates.get(new TypeUserSensor(toggleType, userId, sensor));
+ }
+
+ public SensorState setState(int toggleType, int userId, int sensor, SensorState sensorState) {
+ return mStates.put(new TypeUserSensor(toggleType, userId, sensor), sensorState);
+ }
+
+ private static class TypeUserSensor {
+
+ int mType;
+ int mUserId;
+ int mSensor;
+
+ TypeUserSensor(int type, int userId, int sensor) {
+ mType = type;
+ mUserId = userId;
+ mSensor = sensor;
+ }
+
+ TypeUserSensor(TypeUserSensor typeUserSensor) {
+ this(typeUserSensor.mType, typeUserSensor.mUserId, typeUserSensor.mSensor);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof TypeUserSensor)) return false;
+ TypeUserSensor that = (TypeUserSensor) o;
+ return mType == that.mType && mUserId == that.mUserId && mSensor == that.mSensor;
+ }
+
+ @Override
+ public int hashCode() {
+ return 31 * (31 * mType + mUserId) + mSensor;
+ }
+ }
+
+ void schedulePersist() {
+ int numStates = mStates.size();
+
+ ArrayMap<TypeUserSensor, SensorState> statesCopy = new ArrayMap<>();
+ for (int i = 0; i < numStates; i++) {
+ statesCopy.put(new TypeUserSensor(mStates.keyAt(i)),
+ new SensorState(mStates.valueAt(i)));
+ }
+ IoThread.getHandler().sendMessage(
+ PooledLambda.obtainMessage(PersistedState::persist, this, statesCopy));
+ }
+
+ private void persist(ArrayMap<TypeUserSensor, SensorState> states) {
+ FileOutputStream outputStream = null;
+ try {
+ outputStream = mAtomicFile.startWrite();
+ TypedXmlSerializer serializer = Xml.resolveSerializer(outputStream);
+ serializer.startDocument(null, true);
+ serializer.startTag(null, XML_TAG_SENSOR_PRIVACY);
+ serializer.attributeInt(null, XML_ATTRIBUTE_PERSISTENCE_VERSION,
+ CURRENT_PERSISTENCE_VERSION);
+ serializer.attributeInt(null, XML_ATTRIBUTE_VERSION, CURRENT_VERSION);
+ for (int i = 0; i < states.size(); i++) {
+ TypeUserSensor userSensor = states.keyAt(i);
+ SensorState sensorState = states.valueAt(i);
+
+ serializer.startTag(null, XML_TAG_SENSOR_STATE);
+ serializer.attributeInt(null, XML_ATTRIBUTE_TOGGLE_TYPE,
+ userSensor.mType);
+ serializer.attributeInt(null, XML_ATTRIBUTE_USER_ID,
+ userSensor.mUserId);
+ serializer.attributeInt(null, XML_ATTRIBUTE_SENSOR,
+ userSensor.mSensor);
+ serializer.attributeInt(null, XML_ATTRIBUTE_STATE_TYPE,
+ sensorState.getState());
+ serializer.attributeLong(null, XML_ATTRIBUTE_LAST_CHANGE,
+ sensorState.getLastChange());
+ serializer.endTag(null, XML_TAG_SENSOR_STATE);
+ }
+
+ serializer.endTag(null, XML_TAG_SENSOR_PRIVACY);
+ serializer.endDocument();
+ mAtomicFile.finishWrite(outputStream);
+ } catch (IOException e) {
+ Log.e(LOG_TAG, "Caught an exception persisting the sensor privacy state: ", e);
+ mAtomicFile.failWrite(outputStream);
+ }
+ }
+
+ void dump(DualDumpOutputStream dumpStream) {
+ // Collect per user, then per sensor. <toggle type, state>
+ SparseArray<SparseArray<Pair<Integer, SensorState>>> statesMatrix = new SparseArray<>();
+ int numStates = mStates.size();
+ for (int i = 0; i < numStates; i++) {
+ int toggleType = mStates.keyAt(i).mType;
+ int userId = mStates.keyAt(i).mUserId;
+ int sensor = mStates.keyAt(i).mSensor;
+
+ SparseArray<Pair<Integer, SensorState>> userStates = statesMatrix.get(userId);
+ if (userStates == null) {
+ userStates = new SparseArray<>();
+ statesMatrix.put(userId, userStates);
+ }
+ userStates.put(sensor, new Pair<>(toggleType, mStates.valueAt(i)));
+ }
+
+ dumpStream.write("storage_implementation",
+ SensorPrivacyServiceDumpProto.STORAGE_IMPLEMENTATION,
+ SensorPrivacyStateControllerImpl.class.getName());
+
+ int numUsers = statesMatrix.size();
+ for (int i = 0; i < numUsers; i++) {
+ int userId = statesMatrix.keyAt(i);
+ long userToken = dumpStream.start("users", SensorPrivacyServiceDumpProto.USER);
+ dumpStream.write("user_id", SensorPrivacyUserProto.USER_ID, userId);
+ SparseArray<Pair<Integer, SensorState>> userStates = statesMatrix.valueAt(i);
+ int numSensors = userStates.size();
+ for (int j = 0; j < numSensors; j++) {
+ int sensor = userStates.keyAt(j);
+ int toggleType = userStates.valueAt(j).first;
+ SensorState sensorState = userStates.valueAt(j).second;
+ long sensorToken = dumpStream.start("sensors", SensorPrivacyUserProto.SENSORS);
+ dumpStream.write("sensor", SensorPrivacySensorProto.SENSOR, sensor);
+ long toggleToken = dumpStream.start("toggles", SensorPrivacySensorProto.TOGGLES);
+ dumpStream.write("toggle_type",
+ SensorPrivacyIndividualEnabledSensorProto.TOGGLE_TYPE,
+ toggleType);
+ dumpStream.write("state_type",
+ SensorPrivacyIndividualEnabledSensorProto.STATE_TYPE,
+ sensorState.getState());
+ dumpStream.write("last_change",
+ SensorPrivacyIndividualEnabledSensorProto.LAST_CHANGE,
+ sensorState.getLastChange());
+ dumpStream.end(toggleToken);
+ dumpStream.end(sensorToken);
+ }
+ dumpStream.end(userToken);
+ }
+ }
+
+ void forEachKnownState(QuadConsumer<Integer, Integer, Integer, SensorState> consumer) {
+ int numStates = mStates.size();
+ for (int i = 0; i < numStates; i++) {
+ TypeUserSensor tus = mStates.keyAt(i);
+ SensorState sensorState = mStates.valueAt(i);
+ consumer.accept(tus.mType, tus.mUserId, tus.mSensor, sensorState);
+ }
+ }
+
+ // Structure for persistence version 0
+ private static class PVersion0 {
+ private SparseArray<SensorState> mIndividualEnabled = new SparseArray<>();
+
+ private PVersion0(int version) {
+ if (version != 0) {
+ throw new RuntimeException("Only version 0 supported");
+ }
+ }
+
+ private void addState(int sensor, boolean enabled) {
+ mIndividualEnabled.put(sensor, new SensorState(enabled));
+ }
+
+ private void upgrade() {
+ // No op, only version 0 is supported
+ }
+ }
+
+ // Structure for persistence version 1
+ private static class PVersion1 {
+ private SparseArray<SparseArray<SensorState>> mIndividualEnabled = new SparseArray<>();
+
+ private PVersion1(int version) {
+ if (version != 1) {
+ throw new RuntimeException("Only version 1 supported");
+ }
+ }
+
+ private static PVersion1 fromPVersion0(PVersion0 version0) {
+ version0.upgrade();
+
+ PVersion1 result = new PVersion1(1);
+
+ int[] users = {UserHandle.USER_SYSTEM};
+ try {
+ users = LocalServices.getService(UserManagerInternal.class).getUserIds();
+ } catch (Exception e) {
+ Log.e(LOG_TAG, "Unable to get users.", e);
+ }
+
+ // Copy global state to each user
+ for (int i = 0; i < users.length; i++) {
+ int userId = users[i];
+
+ for (int j = 0; j < version0.mIndividualEnabled.size(); j++) {
+ final int sensor = version0.mIndividualEnabled.keyAt(j);
+ final SensorState sensorState = version0.mIndividualEnabled.valueAt(j);
+
+ result.addState(userId, sensor, sensorState.isEnabled());
+ }
+ }
+
+ return result;
+ }
+
+ private void addState(int userId, int sensor, boolean enabled) {
+ SparseArray<SensorState> userIndividualSensorEnabled =
+ mIndividualEnabled.get(userId, new SparseArray<>());
+ mIndividualEnabled.put(userId, userIndividualSensorEnabled);
+
+ userIndividualSensorEnabled
+ .put(sensor, new SensorState(enabled));
+ }
+
+ private void upgrade() {
+ // No op, only version 1 is supported
+ }
+ }
+
+ // Structure for persistence version 2
+ private static class PVersion2 {
+ private ArrayMap<TypeUserSensor, SensorState> mStates = new ArrayMap<>();
+
+ private PVersion2(int version) {
+ if (version != 2) {
+ throw new RuntimeException("Only version 2 supported");
+ }
+ }
+
+ private static PVersion2 fromPVersion1(PVersion1 version1) {
+ version1.upgrade();
+
+ PVersion2 result = new PVersion2(2);
+
+ SparseArray<SparseArray<SensorState>> individualEnabled =
+ version1.mIndividualEnabled;
+ int numUsers = individualEnabled.size();
+ for (int i = 0; i < numUsers; i++) {
+ int userId = individualEnabled.keyAt(i);
+ SparseArray<SensorState> userIndividualEnabled = individualEnabled.valueAt(i);
+ int numSensors = userIndividualEnabled.size();
+ for (int j = 0; j < numSensors; j++) {
+ int sensor = userIndividualEnabled.keyAt(j);
+ SensorState sensorState = userIndividualEnabled.valueAt(j);
+ result.addState(SensorPrivacyManager.ToggleTypes.SOFTWARE,
+ userId, sensor, sensorState.getState(), sensorState.getLastChange());
+ }
+ }
+
+ return result;
+ }
+
+ private void addState(int toggleType, int userId, int sensor, int state,
+ long lastChange) {
+ mStates.put(new TypeUserSensor(toggleType, userId, sensor),
+ new SensorState(state, lastChange));
+ }
+ }
+
+ public void resetForTesting() {
+ mStates = new ArrayMap<>();
+ }
+}
diff --git a/services/core/java/com/android/server/SensorPrivacyService.java b/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java
index 7a4d11c1cc7d..e9b5f1156d39 100644
--- a/services/core/java/com/android/server/SensorPrivacyService.java
+++ b/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server;
+package com.android.server.sensorprivacy;
import static android.Manifest.permission.MANAGE_SENSOR_PRIVACY;
import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_CAMERA;
@@ -40,10 +40,10 @@ import static android.hardware.SensorPrivacyManager.Sources.QS_TILE;
import static android.hardware.SensorPrivacyManager.Sources.SETTINGS;
import static android.hardware.SensorPrivacyManager.Sources.SHELL;
import static android.os.UserHandle.USER_NULL;
-import static android.os.UserHandle.USER_SYSTEM;
import static android.service.SensorPrivacyIndividualEnabledSensorProto.UNKNOWN;
import static com.android.internal.util.FrameworkStatsLog.PRIVACY_SENSOR_TOGGLE_INTERACTION;
+import static com.android.internal.util.FrameworkStatsLog.PRIVACY_SENSOR_TOGGLE_INTERACTION__ACTION__ACTION_UNKNOWN;
import static com.android.internal.util.FrameworkStatsLog.PRIVACY_SENSOR_TOGGLE_INTERACTION__ACTION__TOGGLE_OFF;
import static com.android.internal.util.FrameworkStatsLog.PRIVACY_SENSOR_TOGGLE_INTERACTION__ACTION__TOGGLE_ON;
import static com.android.internal.util.FrameworkStatsLog.PRIVACY_SENSOR_TOGGLE_INTERACTION__SENSOR__CAMERA;
@@ -83,7 +83,6 @@ import android.hardware.SensorPrivacyManager;
import android.hardware.SensorPrivacyManagerInternal;
import android.os.Binder;
import android.os.Bundle;
-import android.os.Environment;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
@@ -97,26 +96,19 @@ import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
-import android.service.SensorPrivacyIndividualEnabledSensorProto;
-import android.service.SensorPrivacyServiceDumpProto;
-import android.service.SensorPrivacyUserProto;
import android.service.voice.VoiceInteractionManagerInternal;
import android.telephony.TelephonyCallback;
import android.telephony.TelephonyManager;
import android.telephony.emergency.EmergencyNumber;
import android.text.Html;
+import android.text.Spanned;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
-import android.util.AtomicFile;
import android.util.IndentingPrintWriter;
import android.util.Log;
import android.util.Pair;
import android.util.SparseArray;
-import android.util.SparseBooleanArray;
-import android.util.TypedXmlPullParser;
-import android.util.TypedXmlSerializer;
-import android.util.Xml;
import android.util.proto.ProtoOutputStream;
import com.android.internal.R;
@@ -125,19 +117,14 @@ import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FunctionalUtils;
-import com.android.internal.util.XmlUtils;
import com.android.internal.util.dump.DualDumpOutputStream;
import com.android.internal.util.function.pooled.PooledLambda;
+import com.android.server.FgThread;
+import com.android.server.LocalServices;
+import com.android.server.SystemService;
import com.android.server.pm.UserManagerInternal;
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.io.File;
import java.io.FileDescriptor;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
@@ -151,32 +138,10 @@ public final class SensorPrivacyService extends SystemService {
private static final boolean DEBUG = false;
private static final boolean DEBUG_LOGGING = false;
- /** Version number indicating compatibility parsing the persisted file */
- private static final int CURRENT_PERSISTENCE_VERSION = 1;
- /** Version number indicating the persisted data needs upgraded to match new internal data
- * structures and features */
- private static final int CURRENT_VERSION = 1;
-
- private static final String SENSOR_PRIVACY_XML_FILE = "sensor_privacy.xml";
- private static final String XML_TAG_SENSOR_PRIVACY = "sensor-privacy";
- private static final String XML_TAG_USER = "user";
- private static final String XML_TAG_INDIVIDUAL_SENSOR_PRIVACY = "individual-sensor-privacy";
- private static final String XML_ATTRIBUTE_ID = "id";
- private static final String XML_ATTRIBUTE_PERSISTENCE_VERSION = "persistence-version";
- private static final String XML_ATTRIBUTE_VERSION = "version";
- private static final String XML_ATTRIBUTE_ENABLED = "enabled";
- private static final String XML_ATTRIBUTE_LAST_CHANGE = "last-change";
- private static final String XML_ATTRIBUTE_SENSOR = "sensor";
-
private static final String SENSOR_PRIVACY_CHANNEL_ID = Context.SENSOR_PRIVACY_SERVICE;
private static final String ACTION_DISABLE_INDIVIDUAL_SENSOR_PRIVACY =
SensorPrivacyService.class.getName() + ".action.disable_sensor_privacy";
- // These are associated with fields that existed for older persisted versions of files
- private static final int VER0_ENABLED = 0;
- private static final int VER0_INDIVIDUAL_ENABLED = 1;
- private static final int VER1_ENABLED = 0;
- private static final int VER1_INDIVIDUAL_ENABLED = 1;
public static final int REMINDER_DIALOG_DELAY_MILLIS = 500;
private final Context mContext;
@@ -200,6 +165,7 @@ public final class SensorPrivacyService extends SystemService {
public SensorPrivacyService(Context context) {
super(context);
+
mContext = context;
mAppOpsManager = context.getSystemService(AppOpsManager.class);
mAppOpsManagerInternal = getLocalService(AppOpsManagerInternal.class);
@@ -247,12 +213,8 @@ public final class SensorPrivacyService extends SystemService {
private final SensorPrivacyHandler mHandler;
private final Object mLock = new Object();
- @GuardedBy("mLock")
- private final AtomicFile mAtomicFile;
- @GuardedBy("mLock")
- private SparseBooleanArray mEnabled = new SparseBooleanArray();
- @GuardedBy("mLock")
- private SparseArray<SparseArray<SensorState>> mIndividualEnabled = new SparseArray<>();
+
+ private SensorPrivacyStateController mSensorPrivacyStateController;
/**
* Packages for which not to show sensor use reminders.
@@ -266,34 +228,6 @@ public final class SensorPrivacyService extends SystemService {
private final ArrayMap<SensorUseReminderDialogInfo, ArraySet<Integer>>
mQueuedSensorUseReminderDialogs = new ArrayMap<>();
- private class SensorState {
- private boolean mEnabled;
- private long mLastChange;
-
- SensorState(boolean enabled) {
- mEnabled = enabled;
- mLastChange = getCurrentTimeMillis();
- }
-
- SensorState(boolean enabled, long lastChange) {
- mEnabled = enabled;
- if (lastChange < 0) {
- mLastChange = getCurrentTimeMillis();
- } else {
- mLastChange = lastChange;
- }
- }
-
- boolean setEnabled(boolean enabled) {
- if (mEnabled != enabled) {
- mEnabled = enabled;
- mLastChange = getCurrentTimeMillis();
- return true;
- }
- return false;
- }
- }
-
private class SensorUseReminderDialogInfo {
private int mTaskId;
private UserHandle mUser;
@@ -323,14 +257,7 @@ public final class SensorPrivacyService extends SystemService {
SensorPrivacyServiceImpl() {
mHandler = new SensorPrivacyHandler(FgThread.get().getLooper(), mContext);
- File sensorPrivacyFile = new File(Environment.getDataSystemDirectory(),
- SENSOR_PRIVACY_XML_FILE);
- mAtomicFile = new AtomicFile(sensorPrivacyFile);
- synchronized (mLock) {
- if (readPersistedSensorPrivacyStateLocked()) {
- persistSensorPrivacyStateLocked();
- }
- }
+ mSensorPrivacyStateController = SensorPrivacyStateController.getInstance();
int[] micAndCameraOps = new int[]{OP_RECORD_AUDIO, OP_PHONE_CALL_MICROPHONE,
OP_CAMERA, OP_PHONE_CALL_CAMERA};
@@ -349,7 +276,26 @@ public final class SensorPrivacyService extends SystemService {
}
}, new IntentFilter(ACTION_DISABLE_INDIVIDUAL_SENSOR_PRIVACY),
MANAGE_SENSOR_PRIVACY, null, Context.RECEIVER_EXPORTED);
+
+ mContext.registerReceiver(new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ mSensorPrivacyStateController.forEachState(
+ (toggleType, userId, sensor, state) ->
+ logSensorPrivacyToggle(OTHER, sensor, state.isEnabled(),
+ state.getLastChange(), true)
+ );
+ }
+ }, new IntentFilter(Intent.ACTION_SHUTDOWN));
+
mUserManagerInternal.addUserRestrictionsListener(this);
+
+ mSensorPrivacyStateController.setAllSensorPrivacyListener(
+ mHandler, mHandler::handleSensorPrivacyChanged);
+ mSensorPrivacyStateController.setSensorPrivacyListener(
+ mHandler,
+ (toggleType, userId, sensor, state) -> mHandler.handleSensorPrivacyChanged(
+ userId, sensor, state.isEnabled()));
}
@Override
@@ -490,8 +436,9 @@ public final class SensorPrivacyService extends SystemService {
}
}
- String inputMethodComponent = Settings.Secure.getString(mContext.getContentResolver(),
- Settings.Secure.DEFAULT_INPUT_METHOD);
+ String inputMethodComponent = Settings.Secure.getStringForUser(
+ mContext.getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD,
+ mCurrentUser);
String inputMethodPackageName = null;
if (inputMethodComponent != null) {
inputMethodPackageName = ComponentName.unflattenFromString(
@@ -546,7 +493,8 @@ public final class SensorPrivacyService extends SystemService {
private void enqueueSensorUseReminderDialogAsync(int taskId, @NonNull UserHandle user,
@NonNull String packageName, int sensor) {
mHandler.sendMessage(PooledLambda.obtainMessage(
- this:: enqueueSensorUseReminderDialog, taskId, user, packageName, sensor));
+ SensorPrivacyServiceImpl::enqueueSensorUseReminderDialog, this, taskId, user,
+ packageName, sensor));
}
private void enqueueSensorUseReminderDialog(int taskId, @NonNull UserHandle user,
@@ -564,8 +512,8 @@ public final class SensorPrivacyService extends SystemService {
sensors.add(sensor);
}
mQueuedSensorUseReminderDialogs.put(info, sensors);
- mHandler.sendMessageDelayed(
- PooledLambda.obtainMessage(this::showSensorUserReminderDialog, info),
+ mHandler.sendMessageDelayed(PooledLambda.obtainMessage(
+ SensorPrivacyServiceImpl::showSensorUserReminderDialog, this, info),
REMINDER_DIALOG_DELAY_MILLIS);
return;
}
@@ -655,28 +603,32 @@ public final class SensorPrivacyService extends SystemService {
notificationManager.createNotificationChannel(channel);
Icon icon = Icon.createWithResource(getUiContext().getResources(), iconRes);
+
+ String contentTitle = getUiContext().getString(messageRes);
+ Spanned contentText = Html.fromHtml(getUiContext().getString(
+ R.string.sensor_privacy_start_use_notification_content_text, packageLabel), 0);
+ PendingIntent contentIntent = PendingIntent.getActivity(mContext, sensor,
+ new Intent(Settings.ACTION_PRIVACY_SETTINGS),
+ PendingIntent.FLAG_IMMUTABLE
+ | PendingIntent.FLAG_UPDATE_CURRENT);
+
+ String actionTitle = getUiContext().getString(
+ R.string.sensor_privacy_start_use_dialog_turn_on_button);
+ PendingIntent actionIntent = PendingIntent.getBroadcast(mContext, sensor,
+ new Intent(ACTION_DISABLE_INDIVIDUAL_SENSOR_PRIVACY)
+ .setPackage(mContext.getPackageName())
+ .putExtra(EXTRA_SENSOR, sensor)
+ .putExtra(Intent.EXTRA_USER, user),
+ PendingIntent.FLAG_IMMUTABLE
+ | PendingIntent.FLAG_UPDATE_CURRENT);
notificationManager.notify(notificationId,
new Notification.Builder(mContext, SENSOR_PRIVACY_CHANNEL_ID)
- .setContentTitle(getUiContext().getString(messageRes))
- .setContentText(Html.fromHtml(getUiContext().getString(
- R.string.sensor_privacy_start_use_notification_content_text,
- packageLabel),0))
+ .setContentTitle(contentTitle)
+ .setContentText(contentText)
.setSmallIcon(icon)
.addAction(new Notification.Action.Builder(icon,
- getUiContext().getString(
- R.string.sensor_privacy_start_use_dialog_turn_on_button),
- PendingIntent.getBroadcast(mContext, sensor,
- new Intent(ACTION_DISABLE_INDIVIDUAL_SENSOR_PRIVACY)
- .setPackage(mContext.getPackageName())
- .putExtra(EXTRA_SENSOR, sensor)
- .putExtra(Intent.EXTRA_USER, user),
- PendingIntent.FLAG_IMMUTABLE
- | PendingIntent.FLAG_UPDATE_CURRENT))
- .build())
- .setContentIntent(PendingIntent.getActivity(mContext, sensor,
- new Intent(Settings.ACTION_PRIVACY_SETTINGS),
- PendingIntent.FLAG_IMMUTABLE
- | PendingIntent.FLAG_UPDATE_CURRENT))
+ actionTitle, actionIntent).build())
+ .setContentIntent(contentIntent)
.extend(new Notification.TvExtender())
.setTimeoutAfter(isTelevision(mContext)
? /* dismiss immediately */ 1
@@ -697,16 +649,7 @@ public final class SensorPrivacyService extends SystemService {
@Override
public void setSensorPrivacy(boolean enable) {
enforceManageSensorPrivacyPermission();
- // Keep the state consistent between all users to make it a single global state
- forAllUsers(userId -> setSensorPrivacy(userId, enable));
- }
-
- private void setSensorPrivacy(@UserIdInt int userId, boolean enable) {
- synchronized (mLock) {
- mEnabled.put(userId, enable);
- persistSensorPrivacyStateLocked();
- }
- mHandler.onSensorPrivacyChanged(enable);
+ mSensorPrivacyStateController.setAllSensorState(enable);
}
@Override
@@ -735,43 +678,23 @@ public final class SensorPrivacyService extends SystemService {
private void setIndividualSensorPrivacyUnchecked(int userId, int source, int sensor,
boolean enable) {
- synchronized (mLock) {
- SparseArray<SensorState> userIndividualEnabled = mIndividualEnabled.get(userId,
- new SparseArray<>());
- SensorState sensorState = userIndividualEnabled.get(sensor);
- long lastChange;
- if (sensorState != null) {
- lastChange = sensorState.mLastChange;
- if (!sensorState.setEnabled(enable)) {
- // State not changing
- return;
- }
- } else {
- sensorState = new SensorState(enable);
- lastChange = sensorState.mLastChange;
- userIndividualEnabled.put(sensor, sensorState);
- }
- mIndividualEnabled.put(userId, userIndividualEnabled);
-
- if (userId == mUserManagerInternal.getProfileParentId(userId)) {
- logSensorPrivacyToggle(source, sensor, sensorState.mEnabled, lastChange);
- }
-
- if (!enable) {
- final long token = Binder.clearCallingIdentity();
- try {
- // Remove any notifications prompting the user to disable sensory privacy
- NotificationManager notificationManager =
- mContext.getSystemService(NotificationManager.class);
-
- notificationManager.cancel(sensor);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
- persistSensorPrivacyState();
- }
- mHandler.onSensorPrivacyChanged(userId, sensor, enable);
+ final long[] lastChange = new long[1];
+ mSensorPrivacyStateController.atomic(() -> {
+ SensorState sensorState = mSensorPrivacyStateController
+ .getState(SensorPrivacyManager.ToggleTypes.SOFTWARE, userId, sensor);
+ lastChange[0] = sensorState.getLastChange();
+ mSensorPrivacyStateController.setState(
+ SensorPrivacyManager.ToggleTypes.SOFTWARE, userId, sensor, enable, mHandler,
+ changeSuccessful -> {
+ if (changeSuccessful) {
+ if (userId == mUserManagerInternal.getProfileParentId(userId)) {
+ mHandler.sendMessage(PooledLambda.obtainMessage(
+ SensorPrivacyServiceImpl::logSensorPrivacyToggle, this,
+ source, sensor, enable, lastChange[0], false));
+ }
+ }
+ });
+ });
}
private boolean canChangeIndividualSensorPrivacy(@UserIdInt int userId, int sensor) {
@@ -801,14 +724,23 @@ public final class SensorPrivacyService extends SystemService {
}
private void logSensorPrivacyToggle(int source, int sensor, boolean enabled,
- long lastChange) {
+ long lastChange, boolean onShutDown) {
long logMins = Math.max(0, (getCurrentTimeMillis() - lastChange) / (1000 * 60));
int logAction = -1;
- if (enabled) {
- logAction = PRIVACY_SENSOR_TOGGLE_INTERACTION__ACTION__TOGGLE_OFF;
+ if (onShutDown) {
+ // TODO ACTION_POWER_OFF_WHILE_(ON/OFF)
+ if (enabled) {
+ logAction = PRIVACY_SENSOR_TOGGLE_INTERACTION__ACTION__ACTION_UNKNOWN;
+ } else {
+ logAction = PRIVACY_SENSOR_TOGGLE_INTERACTION__ACTION__ACTION_UNKNOWN;
+ }
} else {
- logAction = PRIVACY_SENSOR_TOGGLE_INTERACTION__ACTION__TOGGLE_ON;
+ if (enabled) {
+ logAction = PRIVACY_SENSOR_TOGGLE_INTERACTION__ACTION__TOGGLE_OFF;
+ } else {
+ logAction = PRIVACY_SENSOR_TOGGLE_INTERACTION__ACTION__TOGGLE_ON;
+ }
}
int logSensor = -1;
@@ -895,13 +827,7 @@ public final class SensorPrivacyService extends SystemService {
@Override
public boolean isSensorPrivacyEnabled() {
enforceObserveSensorPrivacyPermission();
- return isSensorPrivacyEnabled(USER_SYSTEM);
- }
-
- private boolean isSensorPrivacyEnabled(@UserIdInt int userId) {
- synchronized (mLock) {
- return mEnabled.get(userId, false);
- }
+ return mSensorPrivacyStateController.getAllSensorState();
}
@Override
@@ -918,239 +844,8 @@ public final class SensorPrivacyService extends SystemService {
if (userId == UserHandle.USER_CURRENT) {
userId = mCurrentUser;
}
- synchronized (mLock) {
- return isIndividualSensorPrivacyEnabledLocked(userId, sensor);
- }
- }
-
- private boolean isIndividualSensorPrivacyEnabledLocked(int userId, int sensor) {
- SparseArray<SensorState> states = mIndividualEnabled.get(userId);
- if (states == null) {
- return false;
- }
- SensorState state = states.get(sensor);
- if (state == null) {
- return false;
- }
- return state.mEnabled;
- }
-
- /**
- * Returns the state of sensor privacy from persistent storage.
- */
- private boolean readPersistedSensorPrivacyStateLocked() {
- // if the file does not exist then sensor privacy has not yet been enabled on
- // the device.
-
- SparseArray<Object> map = new SparseArray<>();
- int version = -1;
-
- if (mAtomicFile.exists()) {
- try (FileInputStream inputStream = mAtomicFile.openRead()) {
- TypedXmlPullParser parser = Xml.resolvePullParser(inputStream);
- XmlUtils.beginDocument(parser, XML_TAG_SENSOR_PRIVACY);
- final int persistenceVersion = parser.getAttributeInt(null,
- XML_ATTRIBUTE_PERSISTENCE_VERSION, 0);
-
- // Use inline string literals for xml tags/attrs when parsing old versions since
- // these should never be changed even with refactorings.
- if (persistenceVersion == 0) {
- boolean enabled = parser.getAttributeBoolean(null, "enabled", false);
- SparseArray<SensorState> individualEnabled = new SparseArray<>();
- version = 0;
-
- XmlUtils.nextElement(parser);
- while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
- String tagName = parser.getName();
- if ("individual-sensor-privacy".equals(tagName)) {
- int sensor = XmlUtils.readIntAttribute(parser, "sensor");
- boolean indEnabled = XmlUtils.readBooleanAttribute(parser,
- "enabled");
- individualEnabled.put(sensor, new SensorState(indEnabled));
- XmlUtils.skipCurrentTag(parser);
- } else {
- XmlUtils.nextElement(parser);
- }
- }
- map.put(VER0_ENABLED, enabled);
- map.put(VER0_INDIVIDUAL_ENABLED, individualEnabled);
- } else if (persistenceVersion == CURRENT_PERSISTENCE_VERSION) {
- SparseBooleanArray enabled = new SparseBooleanArray();
- SparseArray<SparseArray<SensorState>> individualEnabled =
- new SparseArray<>();
- version = parser.getAttributeInt(null,
- XML_ATTRIBUTE_VERSION, 1);
-
- int currentUserId = -1;
- while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
- XmlUtils.nextElement(parser);
- String tagName = parser.getName();
- if (XML_TAG_USER.equals(tagName)) {
- currentUserId = parser.getAttributeInt(null, XML_ATTRIBUTE_ID);
- boolean isEnabled = parser.getAttributeBoolean(null,
- XML_ATTRIBUTE_ENABLED);
- if (enabled.indexOfKey(currentUserId) >= 0) {
- Log.e(TAG, "User listed multiple times in file.",
- new RuntimeException());
- mAtomicFile.delete();
- version = -1;
- break;
- }
-
- if (mUserManagerInternal.getUserInfo(currentUserId) == null) {
- // User may no longer exist, skip this user
- currentUserId = -1;
- continue;
- }
-
- enabled.put(currentUserId, isEnabled);
- }
- if (XML_TAG_INDIVIDUAL_SENSOR_PRIVACY.equals(tagName)) {
- if (mUserManagerInternal.getUserInfo(currentUserId) == null) {
- // User may no longer exist or isn't set
- continue;
- }
- int sensor = parser.getAttributeInt(null, XML_ATTRIBUTE_SENSOR);
- boolean isEnabled = parser.getAttributeBoolean(null,
- XML_ATTRIBUTE_ENABLED);
- long lastChange = parser
- .getAttributeLong(null, XML_ATTRIBUTE_LAST_CHANGE, -1);
- SparseArray<SensorState> userIndividualEnabled =
- individualEnabled.get(currentUserId, new SparseArray<>());
-
- userIndividualEnabled
- .put(sensor, new SensorState(isEnabled, lastChange));
- individualEnabled.put(currentUserId, userIndividualEnabled);
- }
- }
-
- map.put(VER1_ENABLED, enabled);
- map.put(VER1_INDIVIDUAL_ENABLED, individualEnabled);
- } else {
- Log.e(TAG, "Unknown persistence version: " + persistenceVersion
- + ". Deleting.",
- new RuntimeException());
- mAtomicFile.delete();
- version = -1;
- }
-
- } catch (IOException | XmlPullParserException e) {
- Log.e(TAG, "Caught an exception reading the state from storage: ", e);
- // Delete the file to prevent the same error on subsequent calls and assume
- // sensor privacy is not enabled.
- mAtomicFile.delete();
- version = -1;
- }
- }
-
- try {
- return upgradeAndInit(version, map);
- } catch (Exception e) {
- Log.wtf(TAG, "Failed to upgrade and set sensor privacy state,"
- + " resetting to default.", e);
- mEnabled = new SparseBooleanArray();
- mIndividualEnabled = new SparseArray<>();
- return true;
- }
- }
-
- private boolean upgradeAndInit(int version, SparseArray map) {
- if (version == -1) {
- // New file, default state for current version goes here.
- mEnabled = new SparseBooleanArray();
- mIndividualEnabled = new SparseArray<>();
- forAllUsers(userId -> mEnabled.put(userId, false));
- forAllUsers(userId -> mIndividualEnabled.put(userId, new SparseArray<>()));
- return true;
- }
- boolean upgraded = false;
- final int[] users = getLocalService(UserManagerInternal.class).getUserIds();
- if (version == 0) {
- final boolean enabled = (boolean) map.get(VER0_ENABLED);
- final SparseArray<SensorState> individualEnabled =
- (SparseArray<SensorState>) map.get(VER0_INDIVIDUAL_ENABLED);
-
- final SparseBooleanArray perUserEnabled = new SparseBooleanArray();
- final SparseArray<SparseArray<SensorState>> perUserIndividualEnabled =
- new SparseArray<>();
-
- // Copy global state to each user
- for (int i = 0; i < users.length; i++) {
- int user = users[i];
- perUserEnabled.put(user, enabled);
- SparseArray<SensorState> userIndividualSensorEnabled = new SparseArray<>();
- perUserIndividualEnabled.put(user, userIndividualSensorEnabled);
- for (int j = 0; j < individualEnabled.size(); j++) {
- final int sensor = individualEnabled.keyAt(j);
- final SensorState isSensorEnabled = individualEnabled.valueAt(j);
- userIndividualSensorEnabled.put(sensor, isSensorEnabled);
- }
- }
-
- map.clear();
- map.put(VER1_ENABLED, perUserEnabled);
- map.put(VER1_INDIVIDUAL_ENABLED, perUserIndividualEnabled);
-
- version = 1;
- upgraded = true;
- }
- if (version == CURRENT_VERSION) {
- mEnabled = (SparseBooleanArray) map.get(VER1_ENABLED);
- mIndividualEnabled =
- (SparseArray<SparseArray<SensorState>>) map.get(VER1_INDIVIDUAL_ENABLED);
- }
- return upgraded;
- }
-
- /**
- * Persists the state of sensor privacy.
- */
- private void persistSensorPrivacyState() {
- synchronized (mLock) {
- persistSensorPrivacyStateLocked();
- }
- }
-
- private void persistSensorPrivacyStateLocked() {
- FileOutputStream outputStream = null;
- try {
- outputStream = mAtomicFile.startWrite();
- TypedXmlSerializer serializer = Xml.resolveSerializer(outputStream);
- serializer.startDocument(null, true);
- serializer.startTag(null, XML_TAG_SENSOR_PRIVACY);
- serializer.attributeInt(
- null, XML_ATTRIBUTE_PERSISTENCE_VERSION, CURRENT_PERSISTENCE_VERSION);
- serializer.attributeInt(null, XML_ATTRIBUTE_VERSION, CURRENT_VERSION);
- forAllUsers(userId -> {
- serializer.startTag(null, XML_TAG_USER);
- serializer.attributeInt(null, XML_ATTRIBUTE_ID, userId);
- serializer.attributeBoolean(
- null, XML_ATTRIBUTE_ENABLED, isSensorPrivacyEnabled(userId));
-
- SparseArray<SensorState> individualEnabled =
- mIndividualEnabled.get(userId, new SparseArray<>());
- int numIndividual = individualEnabled.size();
- for (int i = 0; i < numIndividual; i++) {
- serializer.startTag(null, XML_TAG_INDIVIDUAL_SENSOR_PRIVACY);
- int sensor = individualEnabled.keyAt(i);
- SensorState sensorState = individualEnabled.valueAt(i);
- boolean enabled = sensorState.mEnabled;
- long lastChange = sensorState.mLastChange;
- serializer.attributeInt(null, XML_ATTRIBUTE_SENSOR, sensor);
- serializer.attributeBoolean(null, XML_ATTRIBUTE_ENABLED, enabled);
- serializer.attributeLong(null, XML_ATTRIBUTE_LAST_CHANGE, lastChange);
- serializer.endTag(null, XML_TAG_INDIVIDUAL_SENSOR_PRIVACY);
- }
- serializer.endTag(null, XML_TAG_USER);
-
- });
- serializer.endTag(null, XML_TAG_SENSOR_PRIVACY);
- serializer.endDocument();
- mAtomicFile.finishWrite(outputStream);
- } catch (IOException e) {
- Log.e(TAG, "Caught an exception persisting the sensor privacy state: ", e);
- mAtomicFile.failWrite(outputStream);
- }
+ return mSensorPrivacyStateController.getState(SensorPrivacyManager.ToggleTypes.SOFTWARE,
+ userId, sensor).isEnabled();
}
@Override
@@ -1285,23 +980,23 @@ public final class SensorPrivacyService extends SystemService {
}
private void userSwitching(int from, int to) {
- boolean micState;
- boolean camState;
- boolean prevMicState;
- boolean prevCamState;
- synchronized (mLock) {
- prevMicState = isIndividualSensorPrivacyEnabledLocked(from, MICROPHONE);
- prevCamState = isIndividualSensorPrivacyEnabledLocked(from, CAMERA);
- micState = isIndividualSensorPrivacyEnabledLocked(to, MICROPHONE);
- camState = isIndividualSensorPrivacyEnabledLocked(to, CAMERA);
- }
- if (from == USER_NULL || prevMicState != micState) {
- mHandler.onUserGlobalSensorPrivacyChanged(MICROPHONE, micState);
- setGlobalRestriction(MICROPHONE, micState);
+ final boolean[] micState = new boolean[1];
+ final boolean[] camState = new boolean[1];
+ final boolean[] prevMicState = new boolean[1];
+ final boolean[] prevCamState = new boolean[1];
+ mSensorPrivacyStateController.atomic(() -> {
+ prevMicState[0] = isIndividualSensorPrivacyEnabled(from, MICROPHONE);
+ prevCamState[0] = isIndividualSensorPrivacyEnabled(from, CAMERA);
+ micState[0] = isIndividualSensorPrivacyEnabled(to, MICROPHONE);
+ camState[0] = isIndividualSensorPrivacyEnabled(to, CAMERA);
+ });
+ if (from == USER_NULL || prevMicState[0] != micState[0]) {
+ mHandler.onUserGlobalSensorPrivacyChanged(MICROPHONE, micState[0]);
+ setGlobalRestriction(MICROPHONE, micState[0]);
}
- if (from == USER_NULL || prevCamState != camState) {
- mHandler.onUserGlobalSensorPrivacyChanged(CAMERA, camState);
- setGlobalRestriction(CAMERA, camState);
+ if (from == USER_NULL || prevCamState[0] != camState[0]) {
+ mHandler.onUserGlobalSensorPrivacyChanged(CAMERA, camState[0]);
+ setGlobalRestriction(CAMERA, camState[0]);
}
}
@@ -1395,12 +1090,14 @@ public final class SensorPrivacyService extends SystemService {
final long identity = Binder.clearCallingIdentity();
try {
if (dumpAsProto) {
- dump(new DualDumpOutputStream(new ProtoOutputStream(fd)));
+ mSensorPrivacyStateController.dump(
+ new DualDumpOutputStream(new ProtoOutputStream(fd)));
} else {
pw.println("SENSOR PRIVACY MANAGER STATE (dumpsys "
+ Context.SENSOR_PRIVACY_SERVICE + ")");
- dump(new DualDumpOutputStream(new IndentingPrintWriter(pw, " ")));
+ mSensorPrivacyStateController.dump(
+ new DualDumpOutputStream(new IndentingPrintWriter(pw, " ")));
}
} finally {
Binder.restoreCallingIdentity(identity);
@@ -1408,45 +1105,6 @@ public final class SensorPrivacyService extends SystemService {
}
/**
- * Dump state to {@link DualDumpOutputStream}.
- *
- * @param dumpStream The destination to dump to
- */
- private void dump(@NonNull DualDumpOutputStream dumpStream) {
- synchronized (mLock) {
-
- forAllUsers(userId -> {
- long userToken = dumpStream.start("users", SensorPrivacyServiceDumpProto.USER);
- dumpStream.write("user_id", SensorPrivacyUserProto.USER_ID, userId);
- dumpStream.write("is_enabled", SensorPrivacyUserProto.IS_ENABLED,
- mEnabled.get(userId, false));
-
- SparseArray<SensorState> individualEnabled = mIndividualEnabled.get(userId);
- if (individualEnabled != null) {
- int numIndividualEnabled = individualEnabled.size();
- for (int i = 0; i < numIndividualEnabled; i++) {
- long individualToken = dumpStream.start("individual_enabled_sensor",
- SensorPrivacyUserProto.INDIVIDUAL_ENABLED_SENSOR);
-
- dumpStream.write("sensor",
- SensorPrivacyIndividualEnabledSensorProto.SENSOR,
- individualEnabled.keyAt(i));
- dumpStream.write("is_enabled",
- SensorPrivacyIndividualEnabledSensorProto.IS_ENABLED,
- individualEnabled.valueAt(i).mEnabled);
- // TODO dump last change
-
- dumpStream.end(individualToken);
- }
- }
- dumpStream.end(userToken);
- });
- }
-
- dumpStream.flush();
- }
-
- /**
* Convert a string into a {@link SensorPrivacyManager.Sensors.Sensor id}.
*
* @param sensor The name to convert
@@ -1504,25 +1162,6 @@ public final class SensorPrivacyService extends SystemService {
setIndividualSensorPrivacy(userId, SHELL, sensor, false);
}
break;
- case "reset": {
- int sensor = sensorStrToId(getNextArgRequired());
- if (sensor == UNKNOWN) {
- pw.println("Invalid sensor");
- return -1;
- }
-
- enforceManageSensorPrivacyPermission();
-
- synchronized (mLock) {
- SparseArray<SensorState> individualEnabled =
- mIndividualEnabled.get(userId);
- if (individualEnabled != null) {
- individualEnabled.delete(sensor);
- }
- persistSensorPrivacyState();
- }
- }
- break;
default:
return handleDefaultCommands(cmd);
}
@@ -1545,9 +1184,6 @@ public final class SensorPrivacyService extends SystemService {
pw.println(" disable USER_ID SENSOR");
pw.println(" Disable privacy for a certain sensor.");
pw.println("");
- pw.println(" reset USER_ID SENSOR");
- pw.println(" Reset privacy state for a certain sensor.");
- pw.println("");
}
}).exec(this, in, out, err, args, callback, resultReceiver);
}
@@ -1581,22 +1217,6 @@ public final class SensorPrivacyService extends SystemService {
mContext = context;
}
- public void onSensorPrivacyChanged(boolean enabled) {
- sendMessage(PooledLambda.obtainMessage(SensorPrivacyHandler::handleSensorPrivacyChanged,
- this, enabled));
- sendMessage(
- PooledLambda.obtainMessage(SensorPrivacyServiceImpl::persistSensorPrivacyState,
- mSensorPrivacyServiceImpl));
- }
-
- public void onSensorPrivacyChanged(int userId, int sensor, boolean enabled) {
- sendMessage(PooledLambda.obtainMessage(SensorPrivacyHandler::handleSensorPrivacyChanged,
- this, userId, sensor, enabled));
- sendMessage(
- PooledLambda.obtainMessage(SensorPrivacyServiceImpl::persistSensorPrivacyState,
- mSensorPrivacyServiceImpl));
- }
-
public void onUserGlobalSensorPrivacyChanged(int sensor, boolean enabled) {
sendMessage(PooledLambda.obtainMessage(
SensorPrivacyHandler::handleUserGlobalSensorPrivacyChanged,
@@ -1692,6 +1312,7 @@ public final class SensorPrivacyService extends SystemService {
}
public void handleSensorPrivacyChanged(int userId, int sensor, boolean enabled) {
+ // TODO handle hardware
mSensorPrivacyManagerInternal.dispatch(userId, sensor, enabled);
SparseArray<RemoteCallbackList<ISensorPrivacyListener>> listenersForUser =
mIndividualSensorListeners.get(userId);
@@ -1972,11 +1593,7 @@ public final class SensorPrivacyService extends SystemService {
}
}
- private static long getCurrentTimeMillis() {
- try {
- return SystemClock.currentNetworkTimeMillis();
- } catch (Exception e) {
- return System.currentTimeMillis();
- }
+ static long getCurrentTimeMillis() {
+ return SystemClock.elapsedRealtime();
}
}
diff --git a/services/core/java/com/android/server/sensorprivacy/SensorPrivacyStateController.java b/services/core/java/com/android/server/sensorprivacy/SensorPrivacyStateController.java
new file mode 100644
index 000000000000..96949589447c
--- /dev/null
+++ b/services/core/java/com/android/server/sensorprivacy/SensorPrivacyStateController.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.sensorprivacy;
+
+import android.os.Handler;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.dump.DualDumpOutputStream;
+import com.android.internal.util.function.pooled.PooledLambda;
+
+abstract class SensorPrivacyStateController {
+
+ private static SensorPrivacyStateController sInstance;
+
+ AllSensorStateController mAllSensorStateController = AllSensorStateController.getInstance();
+
+ private final Object mLock = new Object();
+
+ static SensorPrivacyStateController getInstance() {
+ if (sInstance == null) {
+ sInstance = SensorPrivacyStateControllerImpl.getInstance();
+ }
+
+ return sInstance;
+ }
+
+ SensorState getState(int toggleType, int userId, int sensor) {
+ synchronized (mLock) {
+ return getStateLocked(toggleType, userId, sensor);
+ }
+ }
+
+ void setState(int toggleType, int userId, int sensor, boolean enabled, Handler callbackHandler,
+ SetStateResultCallback callback) {
+ synchronized (mLock) {
+ setStateLocked(toggleType, userId, sensor, enabled, callbackHandler, callback);
+ }
+ }
+
+ void setSensorPrivacyListener(Handler handler,
+ SensorPrivacyListener listener) {
+ synchronized (mLock) {
+ setSensorPrivacyListenerLocked(handler, listener);
+ }
+ }
+
+ // Following calls are for the developer settings sensor mute feature
+ boolean getAllSensorState() {
+ synchronized (mLock) {
+ return mAllSensorStateController.getAllSensorStateLocked();
+ }
+ }
+
+ void setAllSensorState(boolean enable) {
+ synchronized (mLock) {
+ mAllSensorStateController.setAllSensorStateLocked(enable);
+ }
+ }
+
+ void setAllSensorPrivacyListener(Handler handler, AllSensorPrivacyListener listener) {
+ synchronized (mLock) {
+ mAllSensorStateController.setAllSensorPrivacyListenerLocked(handler, listener);
+ }
+ }
+
+ void persistAll() {
+ synchronized (mLock) {
+ mAllSensorStateController.schedulePersistLocked();
+ schedulePersistLocked();
+ }
+ }
+
+ void forEachState(SensorPrivacyStateConsumer consumer) {
+ synchronized (mLock) {
+ forEachStateLocked(consumer);
+ }
+ }
+
+ void dump(DualDumpOutputStream dumpStream) {
+ synchronized (mLock) {
+ mAllSensorStateController.dumpLocked(dumpStream);
+ dumpLocked(dumpStream);
+ }
+ dumpStream.flush();
+ }
+
+ public void atomic(Runnable r) {
+ synchronized (mLock) {
+ r.run();
+ }
+ }
+
+ interface SensorPrivacyListener {
+ void onSensorPrivacyChanged(int toggleType, int userId, int sensor, SensorState state);
+ }
+
+ interface SensorPrivacyStateConsumer {
+ void accept(int toggleType, int userId, int sensor, SensorState state);
+ }
+
+ interface SetStateResultCallback {
+ void callback(boolean changed);
+ }
+
+ interface AllSensorPrivacyListener {
+ void onAllSensorPrivacyChanged(boolean enabled);
+ }
+
+ @GuardedBy("mLock")
+ abstract SensorState getStateLocked(int toggleType, int userId, int sensor);
+
+ @GuardedBy("mLock")
+ abstract void setStateLocked(int toggleType, int userId, int sensor, boolean enabled,
+ Handler callbackHandler, SetStateResultCallback callback);
+
+ @GuardedBy("mLock")
+ abstract void setSensorPrivacyListenerLocked(Handler handler,
+ SensorPrivacyListener listener);
+
+ @GuardedBy("mLock")
+ abstract void schedulePersistLocked();
+
+ @GuardedBy("mLock")
+ abstract void forEachStateLocked(SensorPrivacyStateConsumer consumer);
+
+ @GuardedBy("mLock")
+ abstract void dumpLocked(DualDumpOutputStream dumpStream);
+
+ static void sendSetStateCallback(Handler callbackHandler,
+ SetStateResultCallback callback, boolean success) {
+ callbackHandler.sendMessage(PooledLambda.obtainMessage(SetStateResultCallback::callback,
+ callback, success));
+ }
+
+ /**
+ * Used for unit testing
+ */
+ void resetForTesting() {
+ mAllSensorStateController.resetForTesting();
+ resetForTestingImpl();
+ sInstance = null;
+ }
+ abstract void resetForTestingImpl();
+}
diff --git a/services/core/java/com/android/server/sensorprivacy/SensorPrivacyStateControllerImpl.java b/services/core/java/com/android/server/sensorprivacy/SensorPrivacyStateControllerImpl.java
new file mode 100644
index 000000000000..d1ea8e98fe2f
--- /dev/null
+++ b/services/core/java/com/android/server/sensorprivacy/SensorPrivacyStateControllerImpl.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.sensorprivacy;
+
+import android.hardware.SensorPrivacyManager;
+import android.os.Handler;
+
+import com.android.internal.util.dump.DualDumpOutputStream;
+import com.android.internal.util.function.pooled.PooledLambda;
+
+import java.util.Objects;
+
+class SensorPrivacyStateControllerImpl extends SensorPrivacyStateController {
+
+ private static final String SENSOR_PRIVACY_XML_FILE = "sensor_privacy_impl.xml";
+
+ private static SensorPrivacyStateControllerImpl sInstance;
+
+ private PersistedState mPersistedState;
+
+ private SensorPrivacyListener mListener;
+ private Handler mListenerHandler;
+
+ static SensorPrivacyStateController getInstance() {
+ if (sInstance == null) {
+ sInstance = new SensorPrivacyStateControllerImpl();
+ }
+ return sInstance;
+ }
+
+ private SensorPrivacyStateControllerImpl() {
+ mPersistedState = PersistedState.fromFile(SENSOR_PRIVACY_XML_FILE);
+ persistAll();
+ }
+
+ @Override
+ SensorState getStateLocked(int toggleType, int userId, int sensor) {
+ if (toggleType == SensorPrivacyManager.ToggleTypes.HARDWARE) {
+ // Device doesn't support hardware state
+ return getDefaultSensorState();
+ }
+ SensorState sensorState = mPersistedState.getState(toggleType, userId, sensor);
+ if (sensorState != null) {
+ return new SensorState(sensorState);
+ }
+ return getDefaultSensorState();
+ }
+
+ private static SensorState getDefaultSensorState() {
+ return new SensorState(false);
+ }
+
+ @Override
+ void setStateLocked(int toggleType, int userId, int sensor, boolean enabled,
+ Handler callbackHandler, SetStateResultCallback callback) {
+ if (toggleType != SensorPrivacyManager.ToggleTypes.SOFTWARE) {
+ // Implementation only supports software switch
+ callbackHandler.sendMessage(PooledLambda.obtainMessage(
+ SetStateResultCallback::callback, callback, false));
+ return;
+ }
+ // Changing the SensorState's mEnabled updates the timestamp of its last change.
+ // A nonexistent state -> unmuted should not set the timestamp.
+ SensorState lastState = mPersistedState.getState(toggleType, userId, sensor);
+ if (lastState == null) {
+ if (!enabled) {
+ sendSetStateCallback(callbackHandler, callback, false);
+ return;
+ } else if (enabled) {
+ SensorState sensorState = new SensorState(true);
+ mPersistedState.setState(toggleType, userId, sensor, sensorState);
+ notifyStateChangeLocked(toggleType, userId, sensor, sensorState);
+ sendSetStateCallback(callbackHandler, callback, true);
+ return;
+ }
+ }
+ if (lastState.setEnabled(enabled)) {
+ notifyStateChangeLocked(toggleType, userId, sensor, lastState);
+ sendSetStateCallback(callbackHandler, callback, true);
+ return;
+ }
+ sendSetStateCallback(callbackHandler, callback, false);
+ }
+
+ private void notifyStateChangeLocked(int toggleType, int userId, int sensor,
+ SensorState sensorState) {
+ if (mListenerHandler != null && mListener != null) {
+ mListenerHandler.sendMessage(PooledLambda.obtainMessage(
+ SensorPrivacyListener::onSensorPrivacyChanged, mListener,
+ toggleType, userId, sensor, new SensorState(sensorState)));
+ }
+ schedulePersistLocked();
+ }
+
+ @Override
+ void setSensorPrivacyListenerLocked(Handler handler, SensorPrivacyListener listener) {
+ Objects.requireNonNull(handler);
+ Objects.requireNonNull(listener);
+ if (mListener != null) {
+ throw new IllegalStateException("Listener is already set");
+ }
+ mListener = listener;
+ mListenerHandler = handler;
+ }
+
+ @Override
+ void schedulePersistLocked() {
+ mPersistedState.schedulePersist();
+ }
+
+ @Override
+ void forEachStateLocked(SensorPrivacyStateConsumer consumer) {
+ mPersistedState.forEachKnownState(consumer::accept);
+ }
+
+ @Override
+ void resetForTestingImpl() {
+ mPersistedState.resetForTesting();
+ mListener = null;
+ mListenerHandler = null;
+ sInstance = null;
+ }
+
+ @Override
+ void dumpLocked(DualDumpOutputStream dumpStream) {
+ mPersistedState.dump(dumpStream);
+ }
+}
diff --git a/services/core/java/com/android/server/sensorprivacy/SensorState.java b/services/core/java/com/android/server/sensorprivacy/SensorState.java
new file mode 100644
index 000000000000..b92e2c8a8d74
--- /dev/null
+++ b/services/core/java/com/android/server/sensorprivacy/SensorState.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.sensorprivacy;
+
+import static android.hardware.SensorPrivacyManager.StateTypes.DISABLED;
+import static android.hardware.SensorPrivacyManager.StateTypes.ENABLED;
+
+import static com.android.server.sensorprivacy.SensorPrivacyService.getCurrentTimeMillis;
+
+class SensorState {
+
+ private int mStateType;
+ private long mLastChange;
+
+ SensorState(int stateType) {
+ mStateType = stateType;
+ mLastChange = getCurrentTimeMillis();
+ }
+
+ SensorState(int stateType, long lastChange) {
+ mStateType = stateType;
+ mLastChange = Math.min(getCurrentTimeMillis(), lastChange);
+ }
+
+ SensorState(SensorState sensorState) {
+ mStateType = sensorState.getState();
+ mLastChange = sensorState.getLastChange();
+ }
+
+ boolean setState(int stateType) {
+ if (mStateType != stateType) {
+ mStateType = stateType;
+ mLastChange = getCurrentTimeMillis();
+ return true;
+ }
+ return false;
+ }
+
+ int getState() {
+ return mStateType;
+ }
+
+ long getLastChange() {
+ return mLastChange;
+ }
+
+ // Below are some convenience members for when dealing with enabled/disabled
+
+ private static int enabledToState(boolean enabled) {
+ return enabled ? ENABLED : DISABLED;
+ }
+
+ SensorState(boolean enabled) {
+ this(enabledToState(enabled));
+ }
+
+ boolean setEnabled(boolean enabled) {
+ return setState(enabledToState(enabled));
+ }
+
+ boolean isEnabled() {
+ return getState() == ENABLED;
+ }
+
+}
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index e71ff784dc23..8a87c96fcaaa 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -47,6 +47,7 @@ import android.content.pm.ResolveInfo;
import android.graphics.drawable.Icon;
import android.hardware.biometrics.BiometricAuthenticator.Modality;
import android.hardware.biometrics.BiometricManager.BiometricMultiSensorMode;
+import android.hardware.biometrics.IBiometricContextListener;
import android.hardware.biometrics.IBiometricSysuiReceiver;
import android.hardware.biometrics.PromptInfo;
import android.hardware.display.DisplayManager;
@@ -157,6 +158,8 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D
private final SparseArray<UiState> mDisplayUiState = new SparseArray<>();
@GuardedBy("mLock")
private IUdfpsHbmListener mUdfpsHbmListener;
+ @GuardedBy("mLock")
+ private IBiometricContextListener mBiometricContextListener;
@GuardedBy("mCurrentRequestAddTilePackages")
private final ArrayMap<String, Long> mCurrentRequestAddTilePackages = new ArrayMap<>();
@@ -894,6 +897,20 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D
}
@Override
+ public void setBiometicContextListener(IBiometricContextListener listener) {
+ enforceStatusBarService();
+ synchronized (mLock) {
+ mBiometricContextListener = listener;
+ }
+ if (mBar != null) {
+ try {
+ mBar.setBiometicContextListener(listener);
+ } catch (RemoteException ex) {
+ }
+ }
+ }
+
+ @Override
public void setUdfpsHbmListener(IUdfpsHbmListener listener) {
enforceStatusBarService();
if (mBar != null) {
@@ -1315,6 +1332,7 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D
mHandler.post(() -> {
synchronized (mLock) {
setUdfpsHbmListener(mUdfpsHbmListener);
+ setBiometicContextListener(mBiometricContextListener);
}
});
}
diff --git a/services/core/java/com/android/server/tracing/TracingServiceProxy.java b/services/core/java/com/android/server/tracing/TracingServiceProxy.java
index ff2f08bc4a50..27c0beee9c27 100644
--- a/services/core/java/com/android/server/tracing/TracingServiceProxy.java
+++ b/services/core/java/com/android/server/tracing/TracingServiceProxy.java
@@ -15,34 +15,56 @@
*/
package com.android.server.tracing;
+import android.Manifest;
+import android.annotation.NonNull;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ServiceInfo;
import android.os.Binder;
+import android.os.IMessenger;
+import android.os.Message;
+import android.os.ParcelFileDescriptor;
+import android.os.ParcelFileDescriptor.AutoCloseInputStream;
+import android.os.ParcelFileDescriptor.AutoCloseOutputStream;
import android.os.UserHandle;
+import android.service.tracing.TraceReportService;
import android.tracing.ITracingServiceProxy;
+import android.tracing.TraceReportParams;
import android.util.Log;
+import android.util.LruCache;
+import android.util.Slog;
+import com.android.internal.infra.ServiceConnector;
import com.android.server.SystemService;
+import java.io.IOException;
+
/**
* TracingServiceProxy is the system_server intermediary between the Perfetto tracing daemon and the
- * system tracing app Traceur.
+ * other components (e.g. system tracing app Traceur, trace reporting apps).
*
* Access to this service is restricted via SELinux. Normal apps do not have access.
*
* @hide
*/
public class TracingServiceProxy extends SystemService {
- private static final String TAG = "TracingServiceProxy";
-
public static final String TRACING_SERVICE_PROXY_BINDER_NAME = "tracing.proxy";
-
+ private static final String TAG = "TracingServiceProxy";
private static final String TRACING_APP_PACKAGE_NAME = "com.android.traceur";
private static final String TRACING_APP_ACTIVITY = "com.android.traceur.StopTraceService";
+ private static final int MAX_CACHED_REPORTER_SERVICES = 8;
+
+ // The maximum size of the trace allowed if the option |usePipeForTesting| is set.
+ // Note: this size MUST be smaller than the buffer size of the pipe (i.e. what you can
+ // write to the pipe without blocking) to avoid system_server blocking on this.
+ // (on Linux, the minimum value is 4K i.e. 1 minimally sized page).
+ private static final int MAX_FILE_SIZE_BYTES_TO_PIPE = 1024;
+
// Keep this in sync with the definitions in TraceService
private static final String INTENT_ACTION_NOTIFY_SESSION_STOPPED =
"com.android.traceur.NOTIFY_SESSION_STOPPED";
@@ -51,16 +73,22 @@ public class TracingServiceProxy extends SystemService {
private final Context mContext;
private final PackageManager mPackageManager;
+ private final LruCache<ComponentName, ServiceConnector<IMessenger>> mCachedReporterServices;
private final ITracingServiceProxy.Stub mTracingServiceProxy = new ITracingServiceProxy.Stub() {
/**
- * Notifies system tracing app that a tracing session has ended. If a session is repurposed
- * for use in a bugreport, sessionStolen can be set to indicate that tracing has ended but
- * there is no buffer available to dump.
- */
+ * Notifies system tracing app that a tracing session has ended. If a session is repurposed
+ * for use in a bugreport, sessionStolen can be set to indicate that tracing has ended but
+ * there is no buffer available to dump.
+ */
@Override
public void notifyTraceSessionEnded(boolean sessionStolen) {
- notifyTraceur(sessionStolen);
+ TracingServiceProxy.this.notifyTraceur(sessionStolen);
+ }
+
+ @Override
+ public void reportTrace(@NonNull TraceReportParams params) {
+ TracingServiceProxy.this.reportTrace(params);
}
};
@@ -68,6 +96,7 @@ public class TracingServiceProxy extends SystemService {
super(context);
mContext = context;
mPackageManager = context.getPackageManager();
+ mCachedReporterServices = new LruCache<>(MAX_CACHED_REPORTER_SERVICES);
}
@Override
@@ -103,4 +132,119 @@ public class TracingServiceProxy extends SystemService {
Log.e(TAG, "Failed to locate Traceur", e);
}
}
+
+ private void reportTrace(@NonNull TraceReportParams params) {
+ // We don't need to do any permission checks on the caller because access
+ // to this service is guarded by SELinux.
+ ComponentName component = new ComponentName(params.reporterPackageName,
+ params.reporterClassName);
+ if (!hasBindServicePermission(component)) {
+ return;
+ }
+ boolean hasDumpPermission = hasPermission(component, Manifest.permission.DUMP);
+ boolean hasUsageStatsPermission = hasPermission(component,
+ Manifest.permission.PACKAGE_USAGE_STATS);
+ if (!hasDumpPermission || !hasUsageStatsPermission) {
+ return;
+ }
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ reportTrace(getOrCreateReporterService(component), params);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ private void reportTrace(
+ @NonNull ServiceConnector<IMessenger> reporterService,
+ @NonNull TraceReportParams params) {
+ reporterService.post(messenger -> {
+ if (params.usePipeForTesting) {
+ ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createPipe();
+ try (AutoCloseInputStream i = new AutoCloseInputStream(params.fd)) {
+ try (AutoCloseOutputStream o = new AutoCloseOutputStream(pipe[1])) {
+ byte[] array = i.readNBytes(MAX_FILE_SIZE_BYTES_TO_PIPE);
+ if (array.length == MAX_FILE_SIZE_BYTES_TO_PIPE) {
+ throw new IllegalArgumentException(
+ "Trace file too large when |usePipeForTesting| is set.");
+ }
+ o.write(array);
+ }
+ }
+ params.fd = pipe[0];
+ }
+
+ Message message = Message.obtain();
+ message.what = TraceReportService.MSG_REPORT_TRACE;
+ message.obj = params;
+ messenger.send(message);
+ }).whenComplete((res, err) -> {
+ if (err != null) {
+ Slog.e(TAG, "Failed to report trace", err);
+ }
+ try {
+ params.fd.close();
+ } catch (IOException ignored) {
+ }
+ });
+ }
+
+ private ServiceConnector<IMessenger> getOrCreateReporterService(
+ @NonNull ComponentName component) {
+ ServiceConnector<IMessenger> connector = mCachedReporterServices.get(component);
+ if (connector == null) {
+ Intent intent = new Intent();
+ intent.setComponent(component);
+ connector = new ServiceConnector.Impl<IMessenger>(
+ mContext, intent,
+ Context.BIND_AUTO_CREATE | Context.BIND_WAIVE_PRIORITY,
+ mContext.getUser().getIdentifier(), IMessenger.Stub::asInterface) {
+ private static final long DISCONNECT_TIMEOUT_MS = 15_000;
+ private static final long REQUEST_TIMEOUT_MS = 10_000;
+
+ @Override
+ protected long getAutoDisconnectTimeoutMs() {
+ return DISCONNECT_TIMEOUT_MS;
+ }
+
+ @Override
+ protected long getRequestTimeoutMs() {
+ return REQUEST_TIMEOUT_MS;
+ }
+ };
+ mCachedReporterServices.put(intent.getComponent(), connector);
+ }
+ return connector;
+ }
+
+ private boolean hasPermission(@NonNull ComponentName componentName,
+ @NonNull String permission) throws SecurityException {
+ if (mPackageManager.checkPermission(permission, componentName.getPackageName())
+ != PackageManager.PERMISSION_GRANTED) {
+ Slog.e(TAG,
+ "Trace reporting service " + componentName.toShortString() + " does not have "
+ + permission + " permission");
+ return false;
+ }
+ return true;
+ }
+
+ private boolean hasBindServicePermission(@NonNull ComponentName componentName) {
+ ServiceInfo info;
+ try {
+ info = mPackageManager.getServiceInfo(componentName, 0);
+ } catch (NameNotFoundException ex) {
+ Slog.e(TAG,
+ "Trace reporting service " + componentName.toShortString() + " does not exist");
+ return false;
+ }
+ if (!Manifest.permission.BIND_TRACE_REPORT_SERVICE.equals(info.permission)) {
+ Slog.e(TAG,
+ "Trace reporting service " + componentName.toShortString()
+ + " does not request " + Manifest.permission.BIND_TRACE_REPORT_SERVICE
+ + " permission; instead requests " + info.permission);
+ return false;
+ }
+ return true;
+ }
}
diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java
index 7164c6c601ef..ff96aebba708 100644
--- a/services/core/java/com/android/server/wm/ActivityClientController.java
+++ b/services/core/java/com/android/server/wm/ActivityClientController.java
@@ -43,6 +43,7 @@ import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_N
import static com.android.server.wm.ActivityTaskManagerService.TAG_SWITCH;
import static com.android.server.wm.ActivityTaskManagerService.enforceNotIsolatedCaller;
+import android.annotation.ColorInt;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.Activity;
@@ -1081,7 +1082,7 @@ class ActivityClientController extends IActivityClientController.Stub {
@Override
public void overridePendingTransition(IBinder token, String packageName,
- int enterAnim, int exitAnim) {
+ int enterAnim, int exitAnim, @ColorInt int backgroundColor) {
final long origId = Binder.clearCallingIdentity();
synchronized (mGlobalLock) {
final ActivityRecord r = ActivityRecord.isInRootTaskLocked(token);
@@ -1091,7 +1092,7 @@ class ActivityClientController extends IActivityClientController.Stub {
r.mOverrideTaskTransition);
r.mTransitionController.setOverrideAnimation(
TransitionInfo.AnimationOptions.makeCustomAnimOptions(packageName,
- enterAnim, exitAnim, r.mOverrideTaskTransition),
+ enterAnim, exitAnim, backgroundColor, r.mOverrideTaskTransition),
null /* startCallback */, null /* finishCallback */);
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index c0eee6136f34..5f04b7e2621a 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -46,6 +46,9 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.activityTypeToString;
import static android.app.WindowConfiguration.isSplitScreenWindowingMode;
+import static android.app.admin.DevicePolicyResources.Drawables.Source.PROFILE_SWITCH_ANIMATION;
+import static android.app.admin.DevicePolicyResources.Drawables.Style.OUTLINE;
+import static android.app.admin.DevicePolicyResources.Drawables.WORK_PROFILE_ICON;
import static android.content.Intent.ACTION_MAIN;
import static android.content.Intent.CATEGORY_HOME;
import static android.content.Intent.CATEGORY_LAUNCHER;
@@ -241,6 +244,7 @@ import android.app.TaskInfo;
import android.app.TaskInfo.CameraCompatControlState;
import android.app.WaitResult;
import android.app.WindowConfiguration;
+import android.app.admin.DevicePolicyManager;
import android.app.servertransaction.ActivityConfigurationChangeItem;
import android.app.servertransaction.ActivityLifecycleItem;
import android.app.servertransaction.ActivityRelaunchItem;
@@ -272,6 +276,7 @@ import android.graphics.Bitmap;
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
import android.hardware.HardwareBuffer;
import android.net.Uri;
import android.os.Binder;
@@ -538,6 +543,15 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// Tracking splash screen status from previous activity
boolean mSplashScreenStyleEmpty = false;
+ Drawable mEnterpriseThumbnailDrawable;
+
+ private void updateEnterpriseThumbnailDrawable(Context context) {
+ DevicePolicyManager dpm = context.getSystemService(DevicePolicyManager.class);
+ mEnterpriseThumbnailDrawable = dpm.getDrawable(
+ WORK_PROFILE_ICON, OUTLINE, PROFILE_SWITCH_ANIMATION,
+ () -> context.getDrawable(R.drawable.ic_corp_badge));
+ }
+
static final int LAUNCH_SOURCE_TYPE_SYSTEM = 1;
static final int LAUNCH_SOURCE_TYPE_HOME = 2;
static final int LAUNCH_SOURCE_TYPE_SYSTEMUI = 3;
@@ -716,6 +730,11 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
@Nullable
private Rect mLetterboxBoundsForFixedOrientationAndAspectRatio;
+ // Whether the activity is eligible to be letterboxed for fixed orientation with respect to its
+ // requested orientation, even when it's letterbox for another reason (e.g., size compat mode)
+ // and therefore #isLetterboxedForFixedOrientationAndAspectRatio returns false.
+ private boolean mIsEligibleForFixedOrientationLetterbox;
+
// State of the Camera app compat control which is used to correct stretched viewfinder
// in apps that don't handle all possible configurations and changes between them correctly.
@CameraCompatControlState
@@ -1925,6 +1944,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
mAtmService.mPackageConfigPersister.updateConfigIfNeeded(this, mUserId, packageName);
mActivityRecordInputSink = new ActivityRecordInputSink(this);
+
+ updateEnterpriseThumbnailDrawable(mAtmService.mUiContext);
}
/**
@@ -4469,6 +4490,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
pendingOptions.getOverrideTaskTransition());
options = AnimationOptions.makeCustomAnimOptions(pendingOptions.getPackageName(),
pendingOptions.getCustomEnterResId(), pendingOptions.getCustomExitResId(),
+ pendingOptions.getCustomBackgroundColor(),
pendingOptions.getOverrideTaskTransition());
startCallback = pendingOptions.getAnimationStartedListener();
finishCallback = pendingOptions.getAnimationFinishedListener();
@@ -6928,7 +6950,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
@Override
void prepareSurfaces() {
- final boolean show = isVisible() || isAnimating(TRANSITION | PARENTS,
+ final boolean show = isVisible() || isAnimating(PARENTS,
ANIMATION_TYPE_APP_TRANSITION | ANIMATION_TYPE_RECENTS);
if (mSurfaceControl != null) {
@@ -6987,12 +7009,11 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
return;
}
final Rect frame = win.getRelativeFrame();
- final int thumbnailDrawableRes = task.mUserId == mWmService.mCurrentUserId
- ? R.drawable.ic_account_circle
- : R.drawable.ic_corp_badge;
- final HardwareBuffer thumbnail =
- getDisplayContent().mAppTransition
- .createCrossProfileAppsThumbnail(thumbnailDrawableRes, frame);
+ final Drawable thumbnailDrawable = task.mUserId == mWmService.mCurrentUserId
+ ? mAtmService.mUiContext.getDrawable(R.drawable.ic_account_circle)
+ : mEnterpriseThumbnailDrawable;
+ final HardwareBuffer thumbnail = getDisplayContent().mAppTransition
+ .createCrossProfileAppsThumbnail(thumbnailDrawable, frame);
if (thumbnail == null) {
return;
}
@@ -7626,6 +7647,24 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
/**
+ * Whether this activity is eligible for letterbox eduction.
+ *
+ * <p>Conditions that need to be met:
+ *
+ * <ul>
+ * <li>{@link LetterboxConfiguration#getIsEducationEnabled} is true.
+ * <li>The activity is eligible for fixed orientation letterbox.
+ * <li>The activity is in fullscreen.
+ * </ul>
+ */
+ // TODO(b/215316431): Add tests
+ boolean isEligibleForLetterboxEducation() {
+ return mWmService.mLetterboxConfiguration.getIsEducationEnabled()
+ && mIsEligibleForFixedOrientationLetterbox
+ && getWindowingMode() == WINDOWING_MODE_FULLSCREEN;
+ }
+
+ /**
* In some cases, applying insets to bounds changes the orientation. For example, if a
* close-to-square display rotates to portrait to respect a portrait orientation activity, after
* insets such as the status and nav bars are applied, the activity may actually have a
@@ -7687,6 +7726,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
private void resolveFixedOrientationConfiguration(@NonNull Configuration newParentConfig,
int windowingMode) {
mLetterboxBoundsForFixedOrientationAndAspectRatio = null;
+ mIsEligibleForFixedOrientationLetterbox = false;
final Rect parentBounds = newParentConfig.windowConfiguration.getBounds();
final Rect stableBounds = new Rect();
// If orientation is respected when insets are applied, then stableBounds will be empty.
@@ -7726,8 +7766,11 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// make it fit the available bounds by scaling down its bounds.
final int forcedOrientation = getRequestedConfigurationOrientation();
- if (forcedOrientation == ORIENTATION_UNDEFINED
- || (forcedOrientation == parentOrientation && orientationRespectedWithInsets)) {
+ mIsEligibleForFixedOrientationLetterbox = forcedOrientation != ORIENTATION_UNDEFINED
+ && forcedOrientation != parentOrientation;
+
+ if (!mIsEligibleForFixedOrientationLetterbox && (forcedOrientation == ORIENTATION_UNDEFINED
+ || orientationRespectedWithInsets)) {
return;
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index 5d879cefa739..3d7dead7d4c0 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -906,8 +906,7 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
proc.getReportedProcState(), r.getSavedState(), r.getPersistentSavedState(),
results, newIntents, r.takeOptions(), isTransitionForward,
proc.createProfilerInfoIfNeeded(), r.assistToken, activityClientController,
- r.createFixedRotationAdjustmentsIfNeeded(), r.shareableActivityToken,
- r.getLaunchedFromBubble()));
+ r.shareableActivityToken, r.getLaunchedFromBubble()));
// Set desired final state.
final ActivityLifecycleItem lifecycleItem;
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index d5abe4f8ed02..56adcfd76403 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -93,7 +93,6 @@ import static com.android.server.wm.WindowManagerInternal.KeyguardExitAnimationS
import static com.android.server.wm.WindowStateAnimator.ROOT_TASK_CLIP_AFTER_ANIM;
import static com.android.server.wm.WindowStateAnimator.ROOT_TASK_CLIP_NONE;
-import android.annotation.DrawableRes;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ComponentName;
@@ -101,6 +100,7 @@ import android.content.Context;
import android.content.res.Configuration;
import android.content.res.TypedArray;
import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
import android.hardware.HardwareBuffer;
import android.os.Binder;
import android.os.Debug;
@@ -539,8 +539,8 @@ public class AppTransition implements Dump {
* animation.
*/
HardwareBuffer createCrossProfileAppsThumbnail(
- @DrawableRes int thumbnailDrawableRes, Rect frame) {
- return mTransitionAnimation.createCrossProfileAppsThumbnail(thumbnailDrawableRes, frame);
+ Drawable thumbnailDrawable, Rect frame) {
+ return mTransitionAnimation.createCrossProfileAppsThumbnail(thumbnailDrawable, frame);
}
Animation createCrossProfileAppsThumbnailAnimationLocked(Rect appRect) {
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index a1c823e18ccd..c87027dde3ad 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -170,7 +170,6 @@ import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.ColorSpace;
-import android.graphics.Insets;
import android.graphics.Matrix;
import android.graphics.Point;
import android.graphics.Rect;
@@ -1684,9 +1683,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
// adjustments of previous rotated activity should be cleared earlier. Otherwise if
// the current top is in the same process, it may get the rotated state. The transform
// will be cleared later with transition callback to ensure smooth animation.
- if (hasTopFixedRotationLaunchingApp()) {
- mFixedRotationLaunchingApp.notifyFixedRotationTransform(false /* enabled */);
- }
return false;
}
if (!r.getDisplayArea().matchParentBounds()) {
@@ -2191,8 +2187,8 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
outConfig.compatScreenHeightDp = (int) (outConfig.screenHeightDp / mCompatibleScreenScale);
final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270);
- outConfig.compatSmallestScreenWidthDp = computeCompatSmallestWidth(rotated, uiMode, dw,
- dh);
+ outConfig.compatSmallestScreenWidthDp = computeCompatSmallestWidth(rotated, uiMode, dw, dh);
+ outConfig.windowConfiguration.setDisplayRotation(rotation);
}
/**
@@ -5490,26 +5486,46 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
}
void updateKeepClearAreas() {
+ final List<Rect> restrictedKeepClearAreas = new ArrayList();
+ final List<Rect> unrestrictedKeepClearAreas = new ArrayList();
+ getKeepClearAreas(restrictedKeepClearAreas, unrestrictedKeepClearAreas);
mWmService.mDisplayNotificationController.dispatchKeepClearAreasChanged(
- this, getKeepClearAreas());
+ this, restrictedKeepClearAreas, unrestrictedKeepClearAreas);
}
/**
- * Returns all keep-clear areas from visible windows on this display.
+ * Fills {@param outRestricted} with all keep-clear areas from visible, relevant windows
+ * on this display, which set restricted keep-clear areas.
+ * Fills {@param outUnrestricted} with keep-clear areas from visible, relevant windows on this
+ * display, which set unrestricted keep-clear areas.
+ *
+ * For context on restricted vs unrestricted keep-clear areas, see
+ * {@link android.Manifest.permission.USE_UNRESTRICTED_KEEP_CLEAR_AREAS}.
*/
- ArrayList<Rect> getKeepClearAreas() {
- final ArrayList<Rect> keepClearAreas = new ArrayList<Rect>();
+ void getKeepClearAreas(List<Rect> outRestricted, List<Rect> outUnrestricted) {
final Matrix tmpMatrix = new Matrix();
final float[] tmpFloat9 = new float[9];
forAllWindows(w -> {
if (w.isVisible() && !w.inPinnedWindowingMode()) {
- keepClearAreas.addAll(w.getKeepClearAreas(tmpMatrix, tmpFloat9));
+ if (w.mSession.mSetsUnrestrictedKeepClearAreas) {
+ outUnrestricted.addAll(w.getKeepClearAreas(tmpMatrix, tmpFloat9));
+ } else {
+ outRestricted.addAll(w.getKeepClearAreas(tmpMatrix, tmpFloat9));
+ }
}
// We stop traversing when we reach the base of a fullscreen app.
return w.getWindowType() == TYPE_BASE_APPLICATION
&& w.getWindowingMode() == WINDOWING_MODE_FULLSCREEN;
}, true);
+ }
+
+ /**
+ * Returns all keep-clear areas from visible, relevant windows on this display.
+ */
+ ArrayList<Rect> getKeepClearAreas() {
+ final ArrayList<Rect> keepClearAreas = new ArrayList<Rect>();
+ getKeepClearAreas(keepClearAreas, keepClearAreas);
return keepClearAreas;
}
diff --git a/services/core/java/com/android/server/wm/DisplayWindowListenerController.java b/services/core/java/com/android/server/wm/DisplayWindowListenerController.java
index 276dbe9c844a..e18d5396831a 100644
--- a/services/core/java/com/android/server/wm/DisplayWindowListenerController.java
+++ b/services/core/java/com/android/server/wm/DisplayWindowListenerController.java
@@ -120,12 +120,13 @@ class DisplayWindowListenerController {
mDisplayListeners.finishBroadcast();
}
- void dispatchKeepClearAreasChanged(DisplayContent display, List<Rect> keepClearAreas) {
+ void dispatchKeepClearAreasChanged(DisplayContent display, List<Rect> restricted,
+ List<Rect> unrestricted) {
int count = mDisplayListeners.beginBroadcast();
for (int i = 0; i < count; ++i) {
try {
mDisplayListeners.getBroadcastItem(i).onKeepClearAreasChanged(
- display.mDisplayId, keepClearAreas);
+ display.mDisplayId, restricted, unrestricted);
} catch (RemoteException e) {
}
}
diff --git a/services/core/java/com/android/server/wm/LetterboxConfiguration.java b/services/core/java/com/android/server/wm/LetterboxConfiguration.java
index 1955e30aab30..ad2767c41e82 100644
--- a/services/core/java/com/android/server/wm/LetterboxConfiguration.java
+++ b/services/core/java/com/android/server/wm/LetterboxConfiguration.java
@@ -126,6 +126,9 @@ final class LetterboxConfiguration {
@LetterboxReachabilityPosition
private volatile int mLetterboxPositionForReachability;
+ // Whether education is allowed for letterboxed fullscreen apps.
+ private boolean mIsEducationEnabled;
+
LetterboxConfiguration(Context systemUiContext) {
mContext = systemUiContext;
mFixedOrientationLetterboxAspectRatio = mContext.getResources().getFloat(
@@ -143,6 +146,8 @@ final class LetterboxConfiguration {
R.bool.config_letterboxIsReachabilityEnabled);
mDefaultPositionForReachability = readLetterboxReachabilityPositionFromConfig(mContext);
mLetterboxPositionForReachability = mDefaultPositionForReachability;
+ mIsEducationEnabled = mContext.getResources().getBoolean(
+ R.bool.config_letterboxIsEducationEnabled);
}
/**
@@ -501,4 +506,27 @@ final class LetterboxConfiguration {
mLetterboxPositionForReachability = Math.max(mLetterboxPositionForReachability - 1, 0);
}
+ /**
+ * Whether education is allowed for letterboxed fullscreen apps.
+ */
+ boolean getIsEducationEnabled() {
+ return mIsEducationEnabled;
+ }
+
+ /**
+ * Overrides whether education is allowed for letterboxed fullscreen apps.
+ */
+ void setIsEducationEnabled(boolean enabled) {
+ mIsEducationEnabled = enabled;
+ }
+
+ /**
+ * Resets whether education is allowed for letterboxed fullscreen apps to
+ * {@link R.bool.config_letterboxIsEducationEnabled}.
+ */
+ void resetIsEducationEnabled() {
+ mIsEducationEnabled = mContext.getResources().getBoolean(
+ R.bool.config_letterboxIsEducationEnabled);
+ }
+
}
diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java
index a049d6547396..d4a7a5d68929 100644
--- a/services/core/java/com/android/server/wm/RecentTasks.java
+++ b/services/core/java/com/android/server/wm/RecentTasks.java
@@ -380,8 +380,11 @@ class RecentTasks {
final ComponentName cn = ComponentName.unflattenFromString(rawRecentsComponent);
if (cn != null) {
try {
- final ApplicationInfo appInfo = AppGlobals.getPackageManager()
- .getApplicationInfo(cn.getPackageName(), 0, mService.mContext.getUserId());
+ final ApplicationInfo appInfo = AppGlobals.getPackageManager().getApplicationInfo(
+ cn.getPackageName(),
+ PackageManager.MATCH_UNINSTALLED_PACKAGES
+ | PackageManager.MATCH_DISABLED_COMPONENTS,
+ mService.mContext.getUserId());
if (appInfo != null) {
mRecentsUid = appInfo.uid;
mRecentsComponent = cn;
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 98acc4607d18..2ae2b4370522 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -19,6 +19,7 @@ package com.android.server.wm;
import static android.Manifest.permission.HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
import static android.Manifest.permission.HIDE_OVERLAY_WINDOWS;
import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW;
+import static android.Manifest.permission.SET_UNRESTRICTED_KEEP_CLEAR_AREAS;
import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
import static android.Manifest.permission.SYSTEM_APPLICATION_OVERLAY;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
@@ -114,6 +115,7 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
private String mRelayoutTag;
private final InsetsVisibilities mDummyRequestedVisibilities = new InsetsVisibilities();
private final InsetsSourceControl[] mDummyControls = new InsetsSourceControl[0];
+ final boolean mSetsUnrestrictedKeepClearAreas;
public Session(WindowManagerService service, IWindowSessionCallback callback) {
mService = service;
@@ -132,6 +134,9 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
== PERMISSION_GRANTED;
mCanStartTasksFromRecents = service.mContext.checkCallingOrSelfPermission(
START_TASKS_FROM_RECENTS) == PERMISSION_GRANTED;
+ mSetsUnrestrictedKeepClearAreas =
+ service.mContext.checkCallingOrSelfPermission(SET_UNRESTRICTED_KEEP_CLEAR_AREAS)
+ == PERMISSION_GRANTED;
mShowingAlertWindowNotificationAllowed = mService.mShowAlertWindowNotifications;
mDragDropController = mService.mDragDropController;
StringBuilder sb = new StringBuilder();
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 7d06526e18b5..97735a29b027 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -3434,6 +3434,9 @@ class Task extends TaskFragment {
// Whether the direct top activity is in size compat mode on foreground.
info.topActivityInSizeCompat = isTopActivityResumed
&& mReuseActivitiesReport.top.inSizeCompatMode();
+ // Whether the direct top activity is eligible for letterbox education.
+ info.topActivityEligibleForLetterboxEducation = isTopActivityResumed
+ && mReuseActivitiesReport.top.isEligibleForLetterboxEducation();
// Whether the direct top activity requested showing camera compat control.
info.cameraCompatControlState = isTopActivityResumed
? mReuseActivitiesReport.top.getCameraCompatControlState()
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index ded58f48b207..ad4594873cf0 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -1039,6 +1039,8 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
" Rejecting as detached: %s", wc);
continue;
}
+ // The level of transition target should be at least window token.
+ if (wc.asWindowState() != null) continue;
final ChangeInfo changeInfo = changes.get(wc);
diff --git a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
index 0f8587c99958..1cf4c1b0fbb2 100644
--- a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
+++ b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
@@ -822,6 +822,29 @@ public class WindowManagerShellCommand extends ShellCommand {
return 0;
}
+ private int runSetLetterboxIsEducationEnabled(PrintWriter pw) throws RemoteException {
+ String arg = getNextArg();
+ final boolean enabled;
+ switch (arg) {
+ case "true":
+ case "1":
+ enabled = true;
+ break;
+ case "false":
+ case "0":
+ enabled = false;
+ break;
+ default:
+ getErrPrintWriter().println("Error: expected true, 1, false, 0, but got " + arg);
+ return -1;
+ }
+
+ synchronized (mInternal.mGlobalLock) {
+ mLetterboxConfiguration.setIsEducationEnabled(enabled);
+ }
+ return 0;
+ }
+
private int runSetLetterboxStyle(PrintWriter pw) throws RemoteException {
if (peekNextArg() == null) {
getErrPrintWriter().println("Error: No arguments provided.");
@@ -859,6 +882,9 @@ public class WindowManagerShellCommand extends ShellCommand {
case "--defaultPositionForReachability":
runSetLetterboxDefaultPositionForReachability(pw);
break;
+ case "--isEducationEnabled":
+ runSetLetterboxIsEducationEnabled(pw);
+ break;
default:
getErrPrintWriter().println(
"Error: Unrecognized letterbox style option: " + arg);
@@ -903,6 +929,9 @@ public class WindowManagerShellCommand extends ShellCommand {
case "defaultPositionForReachability":
mLetterboxConfiguration.getDefaultPositionForReachability();
break;
+ case "isEducationEnabled":
+ mLetterboxConfiguration.getIsEducationEnabled();
+ break;
default:
getErrPrintWriter().println(
"Error: Unrecognized letterbox style option: " + arg);
@@ -998,6 +1027,7 @@ public class WindowManagerShellCommand extends ShellCommand {
mLetterboxConfiguration.resetLetterboxHorizontalPositionMultiplier();
mLetterboxConfiguration.resetIsReachabilityEnabled();
mLetterboxConfiguration.resetDefaultPositionForReachability();
+ mLetterboxConfiguration.resetIsEducationEnabled();
}
}
@@ -1014,6 +1044,8 @@ public class WindowManagerShellCommand extends ShellCommand {
pw.println("Default position for reachability: "
+ LetterboxConfiguration.letterboxReachabilityPositionToString(
mLetterboxConfiguration.getDefaultPositionForReachability()));
+ pw.println("Is education enabled: "
+ + mLetterboxConfiguration.getIsEducationEnabled());
pw.println("Background type: "
+ LetterboxConfiguration.letterboxBackgroundTypeToString(
@@ -1154,10 +1186,12 @@ public class WindowManagerShellCommand extends ShellCommand {
pw.println(" --defaultPositionForReachability [left|center|right]");
pw.println(" Default horizontal position of app window when reachability is.");
pw.println(" enabled.");
+ pw.println(" --isEducationEnabled [true|1|false|0]");
+ pw.println(" Whether education is allowed for letterboxed fullscreen apps.");
pw.println(" reset-letterbox-style [aspectRatio|cornerRadius|backgroundType");
pw.println(" |backgroundColor|wallpaperBlurRadius|wallpaperDarkScrimAlpha");
pw.println(" |horizontalPositionMultiplier|isReachabilityEnabled");
- pw.println(" |defaultPositionMultiplierForReachability]");
+ pw.println(" isEducationEnabled||defaultPositionMultiplierForReachability]");
pw.println(" Resets overrides to default values for specified properties separated");
pw.println(" by space, e.g. 'reset-letterbox-style aspectRatio cornerRadius'.");
pw.println(" If no arguments provided, all values will be reset.");
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index 6d8203c710bd..db231f6a1747 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -16,7 +16,6 @@
package com.android.server.wm;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
@@ -37,20 +36,15 @@ import static com.android.server.wm.WindowTokenProto.WINDOW_CONTAINER;
import android.annotation.CallSuper;
import android.annotation.Nullable;
-import android.app.servertransaction.FixedRotationAdjustmentsItem;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.Debug;
import android.os.IBinder;
-import android.os.RemoteException;
-import android.util.Slog;
import android.util.proto.ProtoOutputStream;
-import android.view.DisplayAdjustments.FixedRotationAdjustments;
import android.view.DisplayInfo;
import android.view.InsetsState;
import android.view.SurfaceControl;
-import android.view.WindowManager;
import android.view.WindowManager.LayoutParams.WindowType;
import android.window.WindowContext;
@@ -485,9 +479,6 @@ class WindowToken extends WindowContainer<WindowState> {
* This should only be called when {@link #mFixedRotationTransformState} is non-null.
*/
private void onFixedRotationStatePrepared() {
- // Send the adjustment info first so when the client receives configuration change, it can
- // get the rotated display metrics.
- notifyFixedRotationTransform(true /* enabled */);
// Resolve the rotated configuration.
onConfigurationChanged(getParent().getConfiguration());
final ActivityRecord r = asActivityRecord();
@@ -543,7 +534,6 @@ class WindowToken extends WindowContainer<WindowState> {
for (int i = state.mAssociatedTokens.size() - 1; i >= 0; i--) {
final WindowToken token = state.mAssociatedTokens.get(i);
token.mFixedRotationTransformState = null;
- token.notifyFixedRotationTransform(false /* enabled */);
if (applyDisplayRotation == null) {
// Notify cancellation because the display does not change rotation.
token.cancelFixedRotationTransform();
@@ -551,44 +541,6 @@ class WindowToken extends WindowContainer<WindowState> {
}
}
- /** Notifies application side to enable or disable the rotation adjustment of display info. */
- void notifyFixedRotationTransform(boolean enabled) {
- FixedRotationAdjustments adjustments = null;
- // A token may contain windows of the same processes or different processes. The list is
- // used to avoid sending the same adjustments to a process multiple times.
- ArrayList<WindowProcessController> notifiedProcesses = null;
- for (int i = mChildren.size() - 1; i >= 0; i--) {
- final WindowState w = mChildren.get(i);
- final WindowProcessController app;
- if (w.mAttrs.type == TYPE_APPLICATION_STARTING) {
- // Use the host activity because starting window is controlled by window manager.
- final ActivityRecord r = asActivityRecord();
- if (r == null) {
- continue;
- }
- app = r.app;
- } else {
- app = mWmService.mAtmService.mProcessMap.getProcess(w.mSession.mPid);
- }
- if (app == null || !app.hasThread()) {
- continue;
- }
- if (notifiedProcesses == null) {
- notifiedProcesses = new ArrayList<>(2);
- adjustments = enabled ? createFixedRotationAdjustmentsIfNeeded() : null;
- } else if (notifiedProcesses.contains(app)) {
- continue;
- }
- notifiedProcesses.add(app);
- try {
- mWmService.mAtmService.getLifecycleManager().scheduleTransaction(
- app.getThread(), FixedRotationAdjustmentsItem.obtain(token, adjustments));
- } catch (RemoteException e) {
- Slog.w(TAG, "Failed to schedule DisplayAdjustmentsItem to " + app, e);
- }
- }
- }
-
/** Restores the changes that applies to this container. */
private void cancelFixedRotationTransform() {
final WindowContainer<?> parent = getParent();
@@ -609,15 +561,6 @@ class WindowToken extends WindowContainer<WindowState> {
void onCancelFixedRotationTransform(int originalDisplayRotation) {
}
- FixedRotationAdjustments createFixedRotationAdjustmentsIfNeeded() {
- if (!isFixedRotationTransforming()) {
- return null;
- }
- final DisplayInfo displayInfo = mFixedRotationTransformState.mDisplayInfo;
- return new FixedRotationAdjustments(displayInfo.rotation, displayInfo.appWidth,
- displayInfo.appHeight, displayInfo.displayCutout);
- }
-
@Override
void resolveOverrideConfiguration(Configuration newParentConfig) {
super.resolveOverrideConfiguration(newParentConfig);
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 95ef5f76b765..99abf440910e 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -103,6 +103,7 @@ cc_defaults {
"libappfuse",
"libbinder_ndk",
"libbinder",
+ "libchrome",
"libcutils",
"libcrypto",
"liblog",
diff --git a/services/core/jni/com_android_server_companion_virtual_InputController.cpp b/services/core/jni/com_android_server_companion_virtual_InputController.cpp
index 43018a900f4c..adc91fc3f2e8 100644
--- a/services/core/jni/com_android_server_companion_virtual_InputController.cpp
+++ b/services/core/jni/com_android_server_companion_virtual_InputController.cpp
@@ -186,7 +186,7 @@ static std::map<int, int> KEY_CODE_MAPPING = {
};
/** Creates a new uinput device and assigns a file descriptor. */
-static int openUinput(const char* readableName, jint vendorId, jint productId,
+static int openUinput(const char* readableName, jint vendorId, jint productId, const char* phys,
DeviceType deviceType, jint screenHeight, jint screenWidth) {
android::base::unique_fd fd(TEMP_FAILURE_RETRY(::open("/dev/uinput", O_WRONLY | O_NONBLOCK)));
if (fd < 0) {
@@ -194,6 +194,8 @@ static int openUinput(const char* readableName, jint vendorId, jint productId,
return -errno;
}
+ ioctl(fd, UI_SET_PHYS, phys);
+
ioctl(fd, UI_SET_EVBIT, EV_KEY);
ioctl(fd, UI_SET_EVBIT, EV_SYN);
switch (deviceType) {
@@ -295,28 +297,30 @@ static int openUinput(const char* readableName, jint vendorId, jint productId,
return fd.release();
}
-static int openUinputJni(JNIEnv* env, jstring name, jint vendorId, jint productId,
+static int openUinputJni(JNIEnv* env, jstring name, jint vendorId, jint productId, jstring phys,
DeviceType deviceType, int screenHeight, int screenWidth) {
ScopedUtfChars readableName(env, name);
- return openUinput(readableName.c_str(), vendorId, productId, deviceType, screenHeight,
- screenWidth);
+ ScopedUtfChars readablePhys(env, phys);
+ return openUinput(readableName.c_str(), vendorId, productId, readablePhys.c_str(), deviceType,
+ screenHeight, screenWidth);
}
static int nativeOpenUinputKeyboard(JNIEnv* env, jobject thiz, jstring name, jint vendorId,
- jint productId) {
- return openUinputJni(env, name, vendorId, productId, DeviceType::KEYBOARD, /* screenHeight */ 0,
- /* screenWidth */ 0);
+ jint productId, jstring phys) {
+ return openUinputJni(env, name, vendorId, productId, phys, DeviceType::KEYBOARD,
+ /* screenHeight */ 0, /* screenWidth */ 0);
}
static int nativeOpenUinputMouse(JNIEnv* env, jobject thiz, jstring name, jint vendorId,
- jint productId) {
- return openUinputJni(env, name, vendorId, productId, DeviceType::MOUSE, /* screenHeight */ 0,
- /* screenWidth */ 0);
+ jint productId, jstring phys) {
+ return openUinputJni(env, name, vendorId, productId, phys, DeviceType::MOUSE,
+ /* screenHeight */ 0, /* screenWidth */ 0);
}
static int nativeOpenUinputTouchscreen(JNIEnv* env, jobject thiz, jstring name, jint vendorId,
- jint productId, jint height, jint width) {
- return openUinputJni(env, name, vendorId, productId, DeviceType::TOUCHSCREEN, height, width);
+ jint productId, jstring phys, jint height, jint width) {
+ return openUinputJni(env, name, vendorId, productId, phys, DeviceType::TOUCHSCREEN, height,
+ width);
}
static bool nativeCloseUinput(JNIEnv* env, jobject thiz, jint fd) {
@@ -435,9 +439,11 @@ static bool nativeWriteScrollEvent(JNIEnv* env, jobject thiz, jint fd, jfloat xA
}
static JNINativeMethod methods[] = {
- {"nativeOpenUinputKeyboard", "(Ljava/lang/String;II)I", (void*)nativeOpenUinputKeyboard},
- {"nativeOpenUinputMouse", "(Ljava/lang/String;II)I", (void*)nativeOpenUinputMouse},
- {"nativeOpenUinputTouchscreen", "(Ljava/lang/String;IIII)I",
+ {"nativeOpenUinputKeyboard", "(Ljava/lang/String;IILjava/lang/String;)I",
+ (void*)nativeOpenUinputKeyboard},
+ {"nativeOpenUinputMouse", "(Ljava/lang/String;IILjava/lang/String;)I",
+ (void*)nativeOpenUinputMouse},
+ {"nativeOpenUinputTouchscreen", "(Ljava/lang/String;IILjava/lang/String;II)I",
(void*)nativeOpenUinputTouchscreen},
{"nativeCloseUinput", "(I)Z", (void*)nativeCloseUinput},
{"nativeWriteKeyEvent", "(III)Z", (void*)nativeWriteKeyEvent},
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index df5fb2827a16..8cb27e179c19 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -457,6 +457,9 @@ void NativeInputManager::dump(std::string& dump) {
mInputManager->getReader().dump(dump);
dump += "\n";
+ mInputManager->getUnwantedInteractionBlocker().dump(dump);
+ dump += "\n";
+
mInputManager->getClassifier().dump(dump);
dump += "\n";
@@ -704,6 +707,7 @@ void NativeInputManager::ensureSpriteControllerLocked() REQUIRES(mLock) {
void NativeInputManager::notifyInputDevicesChanged(const std::vector<InputDeviceInfo>& inputDevices) {
ATRACE_CALL();
+ mInputManager->getUnwantedInteractionBlocker().notifyInputDevicesChanged(inputDevices);
JNIEnv* env = jniEnv();
size_t count = inputDevices.size();
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index 0da8f7ef0dea..161d7ce350fc 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -1504,11 +1504,14 @@ static jboolean android_location_gnss_hal_GnssNative_set_position_mode(
JNIEnv* /* env */, jclass, jint mode, jint recurrence, jint min_interval,
jint preferred_accuracy, jint preferred_time, jboolean low_power_mode) {
if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
- auto status = gnssHalAidl->setPositionMode(static_cast<IGnssAidl::GnssPositionMode>(mode),
- static_cast<IGnssAidl::GnssPositionRecurrence>(
- recurrence),
- min_interval, preferred_accuracy, preferred_time,
- low_power_mode);
+ IGnssAidl::PositionModeOptions options;
+ options.mode = static_cast<IGnssAidl::GnssPositionMode>(mode);
+ options.recurrence = static_cast<IGnssAidl::GnssPositionRecurrence>(recurrence);
+ options.minIntervalMs = min_interval;
+ options.preferredAccuracyMeters = preferred_accuracy;
+ options.preferredTimeMs = preferred_time;
+ options.lowPowerMode = low_power_mode;
+ auto status = gnssHalAidl->setPositionMode(options);
return checkAidlStatus(status, "IGnssAidl setPositionMode() failed.");
}
diff --git a/services/core/jni/com_android_server_net_NetworkStatsService.cpp b/services/core/jni/com_android_server_net_NetworkStatsService.cpp
index 5178132e4a2e..f8a81682bdcf 100644
--- a/services/core/jni/com_android_server_net_NetworkStatsService.cpp
+++ b/services/core/jni/com_android_server_net_NetworkStatsService.cpp
@@ -16,20 +16,18 @@
#define LOG_TAG "NetworkStatsNative"
+#include <cutils/qtaguid.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-
-#include "core_jni_helpers.h"
#include <jni.h>
#include <nativehelper/ScopedUtfChars.h>
-#include <utils/misc.h>
+#include <sys/stat.h>
+#include <sys/types.h>
#include <utils/Log.h>
+#include <utils/misc.h>
-#include "android-base/unique_fd.h"
#include "bpf/BpfUtils.h"
#include "netdbpf/BpfNetworkStats.h"
@@ -104,10 +102,15 @@ static jlong getUidStat(JNIEnv* env, jclass clazz, jint uid, jint type) {
}
}
+static int deleteTagData(JNIEnv* /* env */, jclass /* clazz */, jint uid) {
+ return qtaguid_deleteTagData(0, uid);
+}
+
static const JNINativeMethod gMethods[] = {
{"nativeGetTotalStat", "(I)J", (void*)getTotalStat},
{"nativeGetIfaceStat", "(Ljava/lang/String;I)J", (void*)getIfaceStat},
{"nativeGetUidStat", "(II)J", (void*)getUidStat},
+ {"nativeDeleteTagData", "(I)I", (void*)deleteTagData},
};
int register_android_server_net_NetworkStatsService(JNIEnv* env) {
diff --git a/services/core/jni/gnss/AGnssRil.cpp b/services/core/jni/gnss/AGnssRil.cpp
index d760b4d2195e..424ffd463713 100644
--- a/services/core/jni/gnss/AGnssRil.cpp
+++ b/services/core/jni/gnss/AGnssRil.cpp
@@ -41,7 +41,7 @@ jboolean AGnssRil::setCallback(const std::unique_ptr<AGnssRilCallback>& callback
jboolean AGnssRil::setSetId(jint type, const jstring& setid_string) {
JNIEnv* env = getJniEnv();
ScopedJniString jniSetId{env, setid_string};
- auto status = mIAGnssRil->setSetId((IAGnssRil::SetIDType)type, jniSetId.c_str());
+ auto status = mIAGnssRil->setSetId((IAGnssRil::SetIdType)type, jniSetId.c_str());
return checkAidlStatus(status, "IAGnssRilAidl setSetId() failed.");
}
diff --git a/services/core/jni/gnss/GnssAntennaInfoCallback.cpp b/services/core/jni/gnss/GnssAntennaInfoCallback.cpp
index fbc000b25d26..99d06eb062da 100644
--- a/services/core/jni/gnss/GnssAntennaInfoCallback.cpp
+++ b/services/core/jni/gnss/GnssAntennaInfoCallback.cpp
@@ -226,17 +226,18 @@ jobject GnssAntennaInfoCallback::translateSingleGnssAntennaInfo(
env->NewObject(class_gnssAntennaInfoBuilder, method_gnssAntennaInfoBuilderCtor);
// Set fields
- env->CallObjectMethod(gnssAntennaInfoBuilderObject,
- method_gnssAntennaInfoBuilderSetCarrierFrequencyMHz,
- gnssAntennaInfo.carrierFrequencyMHz);
- env->CallObjectMethod(gnssAntennaInfoBuilderObject,
- method_gnssAntennaInfoBuilderSetPhaseCenterOffset, phaseCenterOffset);
- env->CallObjectMethod(gnssAntennaInfoBuilderObject,
- method_gnssAntennaInfoBuilderSetPhaseCenterVariationCorrections,
- phaseCenterVariationCorrections);
- env->CallObjectMethod(gnssAntennaInfoBuilderObject,
- method_gnssAntennaInfoBuilderSetSignalGainCorrections,
- signalGainCorrections);
+ callObjectMethodIgnoringResult(env, gnssAntennaInfoBuilderObject,
+ method_gnssAntennaInfoBuilderSetCarrierFrequencyMHz,
+ gnssAntennaInfo.carrierFrequencyMHz);
+ callObjectMethodIgnoringResult(env, gnssAntennaInfoBuilderObject,
+ method_gnssAntennaInfoBuilderSetPhaseCenterOffset,
+ phaseCenterOffset);
+ callObjectMethodIgnoringResult(env, gnssAntennaInfoBuilderObject,
+ method_gnssAntennaInfoBuilderSetPhaseCenterVariationCorrections,
+ phaseCenterVariationCorrections);
+ callObjectMethodIgnoringResult(env, gnssAntennaInfoBuilderObject,
+ method_gnssAntennaInfoBuilderSetSignalGainCorrections,
+ signalGainCorrections);
// build
jobject gnssAntennaInfoObject =
diff --git a/services/core/jni/gnss/GnssMeasurementCallback.cpp b/services/core/jni/gnss/GnssMeasurementCallback.cpp
index fbdeec6b897e..34ca559806c6 100644
--- a/services/core/jni/gnss/GnssMeasurementCallback.cpp
+++ b/services/core/jni/gnss/GnssMeasurementCallback.cpp
@@ -212,13 +212,14 @@ void setMeasurementData(JNIEnv* env, jobject& callbacksObj, jobject clock,
jobject gnssMeasurementsEventBuilderObject =
env->NewObject(class_gnssMeasurementsEventBuilder,
method_gnssMeasurementsEventBuilderCtor);
- env->CallObjectMethod(gnssMeasurementsEventBuilderObject,
- method_gnssMeasurementsEventBuilderSetClock, clock);
- env->CallObjectMethod(gnssMeasurementsEventBuilderObject,
- method_gnssMeasurementsEventBuilderSetMeasurements, measurementArray);
- env->CallObjectMethod(gnssMeasurementsEventBuilderObject,
- method_gnssMeasurementsEventBuilderSetGnssAutomaticGainControls,
- gnssAgcArray);
+ callObjectMethodIgnoringResult(env, gnssMeasurementsEventBuilderObject,
+ method_gnssMeasurementsEventBuilderSetClock, clock);
+ callObjectMethodIgnoringResult(env, gnssMeasurementsEventBuilderObject,
+ method_gnssMeasurementsEventBuilderSetMeasurements,
+ measurementArray);
+ callObjectMethodIgnoringResult(env, gnssMeasurementsEventBuilderObject,
+ method_gnssMeasurementsEventBuilderSetGnssAutomaticGainControls,
+ gnssAgcArray);
jobject gnssMeasurementsEventObject =
env->CallObjectMethod(gnssMeasurementsEventBuilderObject,
method_gnssMeasurementsEventBuilderBuild);
@@ -359,9 +360,7 @@ void GnssMeasurementCallbackAidl::translateAndSetGnssData(const GnssData& data)
jobjectArray measurementArray = translateAllGnssMeasurements(env, data.measurements);
jobjectArray gnssAgcArray = nullptr;
- if (data.gnssAgcs.has_value()) {
- gnssAgcArray = translateAllGnssAgcs(env, data.gnssAgcs.value());
- }
+ gnssAgcArray = translateAllGnssAgcs(env, data.gnssAgcs);
setMeasurementData(env, mCallbacksObj, clock, measurementArray, gnssAgcArray);
env->DeleteLocalRef(clock);
@@ -410,24 +409,24 @@ void GnssMeasurementCallbackAidl::translateSingleGnssMeasurement(JNIEnv* env,
satellitePvt.satClockInfo.satHardwareCodeBiasMeters,
satellitePvt.satClockInfo.satTimeCorrectionMeters,
satellitePvt.satClockInfo.satClkDriftMps);
- env->CallObjectMethod(satellitePvtBuilderObject,
- method_satellitePvtBuilderSetPositionEcef, positionEcef);
- env->CallObjectMethod(satellitePvtBuilderObject,
- method_satellitePvtBuilderSetVelocityEcef, velocityEcef);
- env->CallObjectMethod(satellitePvtBuilderObject, method_satellitePvtBuilderSetClockInfo,
- clockInfo);
+ callObjectMethodIgnoringResult(env, satellitePvtBuilderObject,
+ method_satellitePvtBuilderSetPositionEcef, positionEcef);
+ callObjectMethodIgnoringResult(env, satellitePvtBuilderObject,
+ method_satellitePvtBuilderSetVelocityEcef, velocityEcef);
+ callObjectMethodIgnoringResult(env, satellitePvtBuilderObject,
+ method_satellitePvtBuilderSetClockInfo, clockInfo);
}
if (satFlags & SatellitePvt::HAS_IONO) {
- env->CallObjectMethod(satellitePvtBuilderObject,
- method_satellitePvtBuilderSetIonoDelayMeters,
- satellitePvt.ionoDelayMeters);
+ callObjectMethodIgnoringResult(env, satellitePvtBuilderObject,
+ method_satellitePvtBuilderSetIonoDelayMeters,
+ satellitePvt.ionoDelayMeters);
}
if (satFlags & SatellitePvt::HAS_TROPO) {
- env->CallObjectMethod(satellitePvtBuilderObject,
- method_satellitePvtBuilderSetTropoDelayMeters,
- satellitePvt.tropoDelayMeters);
+ callObjectMethodIgnoringResult(env, satellitePvtBuilderObject,
+ method_satellitePvtBuilderSetTropoDelayMeters,
+ satellitePvt.tropoDelayMeters);
}
jobject satellitePvtObject =
@@ -455,17 +454,19 @@ void GnssMeasurementCallbackAidl::translateSingleGnssMeasurement(JNIEnv* env,
jobject correlationVectorBuilderObject =
env->NewObject(class_correlationVectorBuilder,
method_correlationVectorBuilderCtor);
- env->CallObjectMethod(correlationVectorBuilderObject,
- method_correlationVectorBuilderSetMagnitude, magnitudeArray);
- env->CallObjectMethod(correlationVectorBuilderObject,
- method_correlationVectorBuilderSetFrequencyOffsetMetersPerSecond,
- correlationVector.frequencyOffsetMps);
- env->CallObjectMethod(correlationVectorBuilderObject,
- method_correlationVectorBuilderSetSamplingStartMeters,
- correlationVector.samplingStartM);
- env->CallObjectMethod(correlationVectorBuilderObject,
- method_correlationVectorBuilderSetSamplingWidthMeters,
- correlationVector.samplingWidthM);
+ callObjectMethodIgnoringResult(env, correlationVectorBuilderObject,
+ method_correlationVectorBuilderSetMagnitude,
+ magnitudeArray);
+ callObjectMethodIgnoringResult(
+ env, correlationVectorBuilderObject,
+ method_correlationVectorBuilderSetFrequencyOffsetMetersPerSecond,
+ correlationVector.frequencyOffsetMps);
+ callObjectMethodIgnoringResult(env, correlationVectorBuilderObject,
+ method_correlationVectorBuilderSetSamplingStartMeters,
+ correlationVector.samplingStartM);
+ callObjectMethodIgnoringResult(env, correlationVectorBuilderObject,
+ method_correlationVectorBuilderSetSamplingWidthMeters,
+ correlationVector.samplingWidthM);
jobject correlationVectorObject =
env->CallObjectMethod(correlationVectorBuilderObject,
method_correlationVectorBuilderBuild);
@@ -508,8 +509,8 @@ jobjectArray GnssMeasurementCallbackAidl::translateAllGnssMeasurements(
return gnssMeasurementArray;
}
-jobjectArray GnssMeasurementCallbackAidl::translateAllGnssAgcs(
- JNIEnv* env, const std::vector<std::optional<GnssAgc>>& agcs) {
+jobjectArray GnssMeasurementCallbackAidl::translateAllGnssAgcs(JNIEnv* env,
+ const std::vector<GnssAgc>& agcs) {
if (agcs.size() == 0) {
return nullptr;
}
@@ -518,18 +519,17 @@ jobjectArray GnssMeasurementCallbackAidl::translateAllGnssAgcs(
env->NewObjectArray(agcs.size(), class_gnssAgc, nullptr /* initialElement */);
for (uint16_t i = 0; i < agcs.size(); ++i) {
- if (!agcs[i].has_value()) {
- continue;
- }
- const GnssAgc& gnssAgc = agcs[i].value();
+ const GnssAgc& gnssAgc = agcs[i];
jobject agcBuilderObject = env->NewObject(class_gnssAgcBuilder, method_gnssAgcBuilderCtor);
- env->CallObjectMethod(agcBuilderObject, method_gnssAgcBuilderSetLevelDb,
- gnssAgc.agcLevelDb);
- env->CallObjectMethod(agcBuilderObject, method_gnssAgcBuilderSetConstellationType,
- (int)gnssAgc.constellation);
- env->CallObjectMethod(agcBuilderObject, method_gnssAgcBuilderSetCarrierFrequencyHz,
- gnssAgc.carrierFrequencyHz);
+ callObjectMethodIgnoringResult(env, agcBuilderObject, method_gnssAgcBuilderSetLevelDb,
+ gnssAgc.agcLevelDb);
+ callObjectMethodIgnoringResult(env, agcBuilderObject,
+ method_gnssAgcBuilderSetConstellationType,
+ (int)gnssAgc.constellation);
+ callObjectMethodIgnoringResult(env, agcBuilderObject,
+ method_gnssAgcBuilderSetCarrierFrequencyHz,
+ gnssAgc.carrierFrequencyHz);
jobject agcObject = env->CallObjectMethod(agcBuilderObject, method_gnssAgcBuilderBuild);
env->SetObjectArrayElement(gnssAgcArray, i, agcObject);
diff --git a/services/core/jni/gnss/GnssMeasurementCallback.h b/services/core/jni/gnss/GnssMeasurementCallback.h
index 9b346312db38..17af94939666 100644
--- a/services/core/jni/gnss/GnssMeasurementCallback.h
+++ b/services/core/jni/gnss/GnssMeasurementCallback.h
@@ -62,8 +62,8 @@ private:
jobjectArray translateAllGnssMeasurements(
JNIEnv* env, const std::vector<hardware::gnss::GnssMeasurement>& measurements);
- jobjectArray translateAllGnssAgcs(
- JNIEnv* env, const std::vector<std::optional<hardware::gnss::GnssData::GnssAgc>>& agcs);
+ jobjectArray translateAllGnssAgcs(JNIEnv* env,
+ const std::vector<hardware::gnss::GnssData::GnssAgc>& agcs);
void translateAndSetGnssData(const hardware::gnss::GnssData& data);
diff --git a/services/core/jni/gnss/Utils.cpp b/services/core/jni/gnss/Utils.cpp
index 40a94ce62b05..8f32c47fcb5a 100644
--- a/services/core/jni/gnss/Utils.cpp
+++ b/services/core/jni/gnss/Utils.cpp
@@ -111,6 +111,13 @@ void checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) {
}
}
+void callObjectMethodIgnoringResult(JNIEnv* env, jobject obj, jmethodID mid, ...) {
+ va_list args;
+ va_start(args, mid);
+ env->DeleteLocalRef(env->CallObjectMethodV(obj, mid, args));
+ va_end(args);
+}
+
JavaObject::JavaObject(JNIEnv* env, jclass clazz, jmethodID defaultCtor)
: env_(env), clazz_(clazz) {
object_ = env_->NewObject(clazz_, defaultCtor);
diff --git a/services/core/jni/gnss/Utils.h b/services/core/jni/gnss/Utils.h
index 2640a7774c26..c8ee661bc10a 100644
--- a/services/core/jni/gnss/Utils.h
+++ b/services/core/jni/gnss/Utils.h
@@ -56,6 +56,8 @@ jboolean checkAidlStatus(const android::binder::Status& status, const char* erro
void checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName);
+void callObjectMethodIgnoringResult(JNIEnv* env, jobject obj, jmethodID mid, ...);
+
template <class T>
void logHidlError(hardware::Return<T>& result, const char* errorMessage) {
ALOGE("%s HIDL transport error: %s", errorMessage, result.description().c_str());
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 574dbfd1046d..79d80366efd3 100644
--- a/services/core/xsd/display-device-config/display-device-config.xsd
+++ b/services/core/xsd/display-device-config/display-device-config.xsd
@@ -38,6 +38,10 @@
<xs:annotation name="nonnull"/>
<xs:annotation name="final"/>
</xs:element>
+ <xs:element type="thermalThrottling" name="thermalThrottling">
+ <xs:annotation name="nonnull"/>
+ <xs:annotation name="final"/>
+ </xs:element>
<xs:element type="highBrightnessMode" name="highBrightnessMode" minOccurs="0"
maxOccurs="1"/>
<xs:element type="displayQuirks" name="quirks" minOccurs="0" maxOccurs="1" />
@@ -154,6 +158,37 @@
</xs:restriction>
</xs:simpleType>
+ <xs:complexType name="thermalThrottling">
+ <xs:complexType>
+ <xs:element type="brightnessThrottlingMap" name="brightnessThrottlingMap">
+ <xs:annotation name="nonnull"/>
+ <xs:annotation name="final"/>
+ </xs:element>
+ </xs:complexType>
+ </xs:complexType>
+
+ <xs:complexType name="brightnessThrottlingMap">
+ <xs:sequence>
+ <xs:element name="brightnessThrottlingPoint" type="brightnessThrottlingPoint" maxOccurs="unbounded" minOccurs="1">
+ <xs:annotation name="nonnull"/>
+ <xs:annotation name="final"/>
+ </xs:element>
+ </xs:sequence>
+ </xs:complexType>
+
+ <xs:complexType name="brightnessThrottlingPoint">
+ <xs:sequence>
+ <xs:element type="thermalStatus" name="thermalStatus">
+ <xs:annotation name="nonnull"/>
+ <xs:annotation name="final"/>
+ </xs:element>
+ <xs:element type="nonNegativeDecimal" name="brightness">
+ <xs:annotation name="nonnull"/>
+ <xs:annotation name="final"/>
+ </xs:element>
+ </xs:sequence>
+ </xs:complexType>
+
<xs:complexType name="nitsMap">
<xs:sequence>
<xs:element name="point" type="point" maxOccurs="unbounded" minOccurs="2">
diff --git a/services/core/xsd/display-device-config/schema/current.txt b/services/core/xsd/display-device-config/schema/current.txt
index 04f0916dbc54..0b7df4d0bc7c 100644
--- a/services/core/xsd/display-device-config/schema/current.txt
+++ b/services/core/xsd/display-device-config/schema/current.txt
@@ -7,6 +7,19 @@ package com.android.server.display.config {
method public final void setMinimum(@NonNull java.math.BigDecimal);
}
+ public class BrightnessThrottlingMap {
+ ctor public BrightnessThrottlingMap();
+ method @NonNull public final java.util.List<com.android.server.display.config.BrightnessThrottlingPoint> getBrightnessThrottlingPoint();
+ }
+
+ public class BrightnessThrottlingPoint {
+ ctor public BrightnessThrottlingPoint();
+ method @NonNull public final java.math.BigDecimal getBrightness();
+ method @NonNull public final com.android.server.display.config.ThermalStatus getThermalStatus();
+ method public final void setBrightness(@NonNull java.math.BigDecimal);
+ method public final void setThermalStatus(@NonNull com.android.server.display.config.ThermalStatus);
+ }
+
public class Density {
ctor public Density();
method @NonNull public final java.math.BigInteger getDensity();
@@ -39,6 +52,7 @@ package com.android.server.display.config {
method public final java.math.BigDecimal getScreenBrightnessRampFastIncrease();
method public final java.math.BigDecimal getScreenBrightnessRampSlowDecrease();
method public final java.math.BigDecimal getScreenBrightnessRampSlowIncrease();
+ method @NonNull public final com.android.server.display.config.ThermalThrottling getThermalThrottling();
method public final void setAmbientBrightnessChangeThresholds(@NonNull com.android.server.display.config.Thresholds);
method public final void setAmbientLightHorizonLong(java.math.BigInteger);
method public final void setAmbientLightHorizonShort(java.math.BigInteger);
@@ -54,6 +68,7 @@ package com.android.server.display.config {
method public final void setScreenBrightnessRampFastIncrease(java.math.BigDecimal);
method public final void setScreenBrightnessRampSlowDecrease(java.math.BigDecimal);
method public final void setScreenBrightnessRampSlowIncrease(java.math.BigDecimal);
+ method public final void setThermalThrottling(@NonNull com.android.server.display.config.ThermalThrottling);
}
public class DisplayQuirks {
@@ -131,6 +146,12 @@ package com.android.server.display.config {
enum_constant public static final com.android.server.display.config.ThermalStatus shutdown;
}
+ public class ThermalThrottling {
+ ctor public ThermalThrottling();
+ method @NonNull public final com.android.server.display.config.BrightnessThrottlingMap getBrightnessThrottlingMap();
+ method public final void setBrightnessThrottlingMap(@NonNull com.android.server.display.config.BrightnessThrottlingMap);
+ }
+
public class Thresholds {
ctor public Thresholds();
method @NonNull public final com.android.server.display.config.BrightnessThresholds getBrighteningThresholds();
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 733cfcdfaea5..e34178ab9cd2 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -56,6 +56,8 @@ import static android.app.admin.DevicePolicyManager.DELEGATION_NETWORK_LOGGING;
import static android.app.admin.DevicePolicyManager.DELEGATION_PACKAGE_ACCESS;
import static android.app.admin.DevicePolicyManager.DELEGATION_PERMISSION_GRANT;
import static android.app.admin.DevicePolicyManager.DELEGATION_SECURITY_LOGGING;
+import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_DEFAULT;
+import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_FINANCED;
import static android.app.admin.DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE_PER_USER;
import static android.app.admin.DevicePolicyManager.EXTRA_RESOURCE_ID;
import static android.app.admin.DevicePolicyManager.EXTRA_RESOURCE_TYPE_DRAWABLE;
@@ -66,9 +68,12 @@ import static android.app.admin.DevicePolicyManager.ID_TYPE_INDIVIDUAL_ATTESTATI
import static android.app.admin.DevicePolicyManager.ID_TYPE_MEID;
import static android.app.admin.DevicePolicyManager.ID_TYPE_SERIAL;
import static android.app.admin.DevicePolicyManager.LEAVE_ALL_SYSTEM_APPS_ENABLED;
+import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_GLOBAL_ACTIONS;
import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_HOME;
+import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_KEYGUARD;
import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_NOTIFICATIONS;
import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_OVERVIEW;
+import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_SYSTEM_INFO;
import static android.app.admin.DevicePolicyManager.NEARBY_STREAMING_NOT_CONTROLLED_BY_POLICY;
import static android.app.admin.DevicePolicyManager.NON_ORG_OWNED_PROFILE_KEYGUARD_FEATURES_AFFECT_OWNER;
import static android.app.admin.DevicePolicyManager.OPERATION_SAFETY_REASON_NONE;
@@ -3918,8 +3923,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
final CallerIdentity caller = getCallerIdentity(who);
Preconditions.checkCallAuthorization(
- isProfileOwner(caller) || isDeviceOwner(caller) || isSystemUid(caller)
- || isPasswordLimitingAdminTargetingP(caller));
+ isProfileOwner(caller) || isDefaultDeviceOwner(caller)
+ || isSystemUid(caller) || isPasswordLimitingAdminTargetingP(caller));
if (parent) {
Preconditions.checkCallAuthorization(
@@ -4772,7 +4777,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Objects.requireNonNull(admin, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(admin);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
+ Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller)
+ || isProfileOwner(caller));
Preconditions.checkCallingUser(isManagedProfile(caller.getUserId()));
return !isSeparateProfileChallengeEnabled(caller.getUserId());
@@ -4860,12 +4866,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
enforceUserUnlocked(caller.getUserId());
if (parent) {
Preconditions.checkCallAuthorization(
- isDeviceOwner(caller) || isProfileOwner(caller) || isSystemUid(caller),
+ isDefaultDeviceOwner(caller) || isProfileOwner(caller) || isSystemUid(caller),
"Only profile owner, device owner and system may call this method on parent.");
} else {
Preconditions.checkCallAuthorization(
hasCallingOrSelfPermission(REQUEST_PASSWORD_COMPLEXITY)
- || isDeviceOwner(caller) || isProfileOwner(caller),
+ || isDefaultDeviceOwner(caller) || isProfileOwner(caller),
"Must have " + REQUEST_PASSWORD_COMPLEXITY
+ " permission, or be a profile owner or device owner.");
}
@@ -4888,7 +4894,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
"Provided complexity is not one of the allowed values.");
final CallerIdentity caller = getCallerIdentity();
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
+ Preconditions.checkCallAuthorization(
+ isDefaultDeviceOwner(caller) || isProfileOwner(caller));
Preconditions.checkArgument(!calledOnParent || isProfileOwner(caller));
synchronized (getLockObject()) {
@@ -4968,7 +4975,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
final CallerIdentity caller = getCallerIdentity();
Preconditions.checkCallAuthorization(
- isDeviceOwner(caller) || isProfileOwner(caller));
+ isDefaultDeviceOwner(caller) || isProfileOwner(caller));
Preconditions.checkArgument(!calledOnParent || isProfileOwner(caller));
@@ -5160,7 +5167,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
// If caller has PO (or DO) throw or fail silently depending on its target SDK level.
- if (isDeviceOwner(caller) || isProfileOwner(caller)) {
+ if (isDefaultDeviceOwner(caller) || isProfileOwner(caller)) {
synchronized (getLockObject()) {
ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(caller);
if (getTargetSdk(admin.info.getPackageName(), userHandle) < Build.VERSION_CODES.O) {
@@ -5219,7 +5226,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return false;
}
- boolean callerIsDeviceOwnerAdmin = isDeviceOwner(caller);
+ boolean callerIsDeviceOwnerAdmin = isDefaultDeviceOwner(caller);
boolean doNotAskCredentialsOnBoot =
(flags & DevicePolicyManager.RESET_PASSWORD_DO_NOT_ASK_CREDENTIALS_ON_BOOT) != 0;
if (callerIsDeviceOwnerAdmin && doNotAskCredentialsOnBoot) {
@@ -5406,7 +5413,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Objects.requireNonNull(who, "ComponentName is null");
Preconditions.checkArgument(timeoutMs >= 0, "Timeout must not be a negative number.");
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
+ Preconditions.checkCallAuthorization(
+ isDefaultDeviceOwner(caller) || isProfileOwner(caller));
// timeoutMs with value 0 means that the admin doesn't participate
// timeoutMs is clamped to the interval in case the internal constants change in the future
final long minimumStrongAuthTimeout = getMinimumStrongAuthTimeoutMs();
@@ -5575,7 +5583,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
private boolean canManageCaCerts(CallerIdentity caller) {
- return (caller.hasAdminComponent() && (isDeviceOwner(caller) || isProfileOwner(caller)))
+ return (caller.hasAdminComponent() && (isDefaultDeviceOwner(caller)
+ || isProfileOwner(caller)))
|| (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_CERT_INSTALL))
|| hasCallingOrSelfPermission(MANAGE_CA_CERTIFICATES);
}
@@ -5689,7 +5698,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
final boolean isCallerDelegate = isCallerDelegate(caller, DELEGATION_CERT_INSTALL);
final boolean isCredentialManagementApp = isCredentialManagementApp(caller);
Preconditions.checkCallAuthorization((caller.hasAdminComponent()
- && (isProfileOwner(caller) || isDeviceOwner(caller)))
+ && (isProfileOwner(caller) || isDefaultDeviceOwner(caller)))
|| (caller.hasPackage() && (isCallerDelegate || isCredentialManagementApp)));
if (isCredentialManagementApp) {
Preconditions.checkCallAuthorization(!isUserSelectable, "The credential "
@@ -5754,7 +5763,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
final boolean isCallerDelegate = isCallerDelegate(caller, DELEGATION_CERT_INSTALL);
final boolean isCredentialManagementApp = isCredentialManagementApp(caller);
Preconditions.checkCallAuthorization((caller.hasAdminComponent()
- && (isProfileOwner(caller) || isDeviceOwner(caller)))
+ && (isProfileOwner(caller) || isDefaultDeviceOwner(caller)))
|| (caller.hasPackage() && (isCallerDelegate || isCredentialManagementApp)));
if (isCredentialManagementApp) {
Preconditions.checkCallAuthorization(
@@ -5818,12 +5827,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
private boolean canInstallCertificates(CallerIdentity caller) {
- return isProfileOwner(caller) || isDeviceOwner(caller)
+ return isProfileOwner(caller) || isDefaultDeviceOwner(caller)
|| isCallerDelegate(caller, DELEGATION_CERT_INSTALL);
}
private boolean canChooseCertificates(CallerIdentity caller) {
- return isProfileOwner(caller) || isDeviceOwner(caller)
+ return isProfileOwner(caller) || isDefaultDeviceOwner(caller)
|| isCallerDelegate(caller, DELEGATION_CERT_SELECTION);
}
@@ -5871,7 +5880,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
final CallerIdentity caller = getCallerIdentity(who, callerPackage);
Preconditions.checkCallAuthorization((caller.hasAdminComponent()
- && (isProfileOwner(caller) || isDeviceOwner(caller)))
+ && (isProfileOwner(caller) || isDefaultDeviceOwner(caller)))
|| (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_CERT_SELECTION)));
final int granteeUid;
@@ -5984,7 +5993,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
// If not, fall back to the device owner check.
Preconditions.checkCallAuthorization(
- isDeviceOwner(caller) || isCallerDelegate(caller, DELEGATION_CERT_INSTALL));
+ isDefaultDeviceOwner(caller) || isCallerDelegate(caller, DELEGATION_CERT_INSTALL));
}
@VisibleForTesting
@@ -6047,7 +6056,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
enforceIndividualAttestationSupportedIfRequested(attestationUtilsFlags);
} else {
Preconditions.checkCallAuthorization((caller.hasAdminComponent()
- && (isProfileOwner(caller) || isDeviceOwner(caller)))
+ && (isProfileOwner(caller) || isDefaultDeviceOwner(caller)))
|| (caller.hasPackage() && (isCallerDelegate || isCredentialManagementApp)));
if (isCredentialManagementApp) {
Preconditions.checkCallAuthorization(
@@ -6182,7 +6191,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
final boolean isCallerDelegate = isCallerDelegate(caller, DELEGATION_CERT_INSTALL);
final boolean isCredentialManagementApp = isCredentialManagementApp(caller);
Preconditions.checkCallAuthorization((caller.hasAdminComponent()
- && (isProfileOwner(caller) || isDeviceOwner(caller)))
+ && (isProfileOwner(caller) || isDefaultDeviceOwner(caller)))
|| (caller.hasPackage() && (isCallerDelegate || isCredentialManagementApp)));
if (isCredentialManagementApp) {
Preconditions.checkCallAuthorization(
@@ -6337,14 +6346,15 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
final int userId = caller.getUserId();
// Ensure calling process is device/profile owner.
if (!Collections.disjoint(scopes, DEVICE_OWNER_OR_MANAGED_PROFILE_OWNER_DELEGATIONS)) {
- Preconditions.checkCallAuthorization(isDeviceOwner(caller)
+ Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller)
|| (isProfileOwner(caller) && isManagedProfile(caller.getUserId())));
} else if (!Collections.disjoint(
scopes, DEVICE_OWNER_OR_ORGANIZATION_OWNED_MANAGED_PROFILE_OWNER_DELEGATIONS)) {
- Preconditions.checkCallAuthorization(isDeviceOwner(caller)
+ Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller)
|| isProfileOwnerOfOrganizationOwnedDevice(caller));
} else {
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
+ Preconditions.checkCallAuthorization(
+ isDefaultDeviceOwner(caller) || isProfileOwner(caller));
}
synchronized (getLockObject()) {
@@ -6434,7 +6444,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
// * Either it's a profile owner / device owner, if componentName is provided
// * Or it's an app querying its own delegation scopes
if (caller.hasAdminComponent()) {
- Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller));
+ Preconditions.checkCallAuthorization(
+ isProfileOwner(caller) || isDefaultDeviceOwner(caller));
} else {
Preconditions.checkCallAuthorization(isPackage(caller, delegatePackage),
String.format("Caller with uid %d is not %s", caller.getUid(),
@@ -6467,7 +6478,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
// Retrieve the user ID of the calling process.
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
+ Preconditions.checkCallAuthorization(
+ isDefaultDeviceOwner(caller) || isProfileOwner(caller));
synchronized (getLockObject()) {
return getDelegatePackagesInternalLocked(scope, caller.getUserId());
}
@@ -6600,7 +6612,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
final CallerIdentity caller = getCallerIdentity(who);
// Ensure calling process is device/profile owner.
- Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller));
+ Preconditions.checkCallAuthorization(
+ isProfileOwner(caller) || isDefaultDeviceOwner(caller));
synchronized (getLockObject()) {
final DevicePolicyData policy = getUserData(caller.getUserId());
@@ -6712,7 +6725,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Objects.requireNonNull(who, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
+ Preconditions.checkCallAuthorization(
+ isDefaultDeviceOwner(caller) || isProfileOwner(caller));
checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_ALWAYS_ON_VPN_PACKAGE);
if (vpnPackage == null) {
@@ -6792,7 +6806,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Objects.requireNonNull(admin, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(admin);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
+ Preconditions.checkCallAuthorization(
+ isDefaultDeviceOwner(caller) || isProfileOwner(caller));
return mInjector.binderWithCleanCallingIdentity(
() -> mInjector.getVpnManager().getAlwaysOnVpnPackageForUser(caller.getUserId()));
@@ -6818,7 +6833,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
caller = getCallerIdentity();
} else {
caller = getCallerIdentity(admin);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
+ Preconditions.checkCallAuthorization(
+ isDefaultDeviceOwner(caller) || isProfileOwner(caller));
}
return mInjector.binderWithCleanCallingIdentity(
@@ -6841,7 +6857,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Objects.requireNonNull(admin, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(admin);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
+ Preconditions.checkCallAuthorization(
+ isDefaultDeviceOwner(caller) || isProfileOwner(caller));
return mInjector.binderWithCleanCallingIdentity(
() -> mInjector.getVpnManager().getVpnLockdownAllowlist(caller.getUserId()));
@@ -6946,8 +6963,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
+ "organization-owned device.");
}
if ((flags & WIPE_RESET_PROTECTION_DATA) != 0) {
- Preconditions.checkCallAuthorization(isDeviceOwner(caller)
- || calledByProfileOwnerOnOrgOwnedDevice,
+ Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller)
+ || calledByProfileOwnerOnOrgOwnedDevice
+ || isFinancedDeviceOwner(caller),
"Only device owners or profile owners of organization-owned device can set "
+ "WIPE_RESET_PROTECTION_DATA");
}
@@ -7139,8 +7157,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
Preconditions.checkNotNull(who, "ComponentName is null");
CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller)
- || isProfileOwnerOfOrganizationOwnedDevice(caller));
+ Preconditions.checkCallAuthorization(
+ isDefaultDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller));
checkCanExecuteOrThrowUnsafe(DevicePolicyManager
.OPERATION_SET_FACTORY_RESET_PROTECTION_POLICY);
@@ -7189,7 +7207,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
UserHandle.getUserId(frpManagementAgentUid));
} else {
Preconditions.checkCallAuthorization(
- isDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller));
+ isDefaultDeviceOwner(caller)
+ || isProfileOwnerOfOrganizationOwnedDevice(caller));
admin = getProfileOwnerOrDeviceOwnerLocked(caller);
}
}
@@ -7617,7 +7636,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
public void setRecommendedGlobalProxy(ComponentName who, ProxyInfo proxyInfo) {
Objects.requireNonNull(who, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller));
+ Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller));
checkAllUsersAreAffiliatedWithDevice();
mInjector.binderWithCleanCallingIdentity(
() -> mInjector.getConnectivityManager().setGlobalProxy(proxyInfo));
@@ -7915,7 +7934,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
final CallerIdentity caller = getCallerIdentity();
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
+ Preconditions.checkCallAuthorization(
+ isDefaultDeviceOwner(caller) || isProfileOwner(caller));
synchronized (getLockObject()) {
final ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(caller);
@@ -7934,8 +7954,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
final CallerIdentity caller = getCallerIdentity();
Preconditions.checkCallAuthorization(
- isProfileOwner(caller)
- || isDeviceOwner(caller)
+ isProfileOwner(caller) || isDefaultDeviceOwner(caller)
|| hasCallingOrSelfPermission(permission.READ_NEARBY_STREAMING_POLICY));
synchronized (getLockObject()) {
@@ -7955,7 +7974,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
final CallerIdentity caller = getCallerIdentity();
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
+ Preconditions.checkCallAuthorization(
+ isDefaultDeviceOwner(caller) || isProfileOwner(caller));
synchronized (getLockObject()) {
final ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(caller);
@@ -7974,8 +7994,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
final CallerIdentity caller = getCallerIdentity();
Preconditions.checkCallAuthorization(
- isProfileOwner(caller)
- || isDeviceOwner(caller)
+ isProfileOwner(caller) || isDefaultDeviceOwner(caller)
|| hasCallingOrSelfPermission(permission.READ_NEARBY_STREAMING_POLICY));
synchronized (getLockObject()) {
@@ -8067,7 +8086,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
final CallerIdentity caller = getCallerIdentity(who);
Preconditions.checkCallAuthorization(isProfileOwnerOnUser0(caller)
- || isProfileOwnerOfOrganizationOwnedDevice(caller) || isDeviceOwner(caller));
+ || isProfileOwnerOfOrganizationOwnedDevice(caller) || isDefaultDeviceOwner(caller));
mInjector.binderWithCleanCallingIdentity(() ->
mInjector.settingsGlobalPutInt(Settings.Global.AUTO_TIME, enabled ? 1 : 0));
@@ -8091,7 +8110,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
final CallerIdentity caller = getCallerIdentity(who);
Preconditions.checkCallAuthorization(isProfileOwnerOnUser0(caller)
- || isProfileOwnerOfOrganizationOwnedDevice(caller) || isDeviceOwner(caller));
+ || isProfileOwnerOfOrganizationOwnedDevice(caller) || isDefaultDeviceOwner(caller));
return mInjector.settingsGlobalGetInt(Global.AUTO_TIME, 0) > 0;
}
@@ -8108,7 +8127,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
final CallerIdentity caller = getCallerIdentity(who);
Preconditions.checkCallAuthorization(isProfileOwnerOnUser0(caller)
- || isProfileOwnerOfOrganizationOwnedDevice(caller) || isDeviceOwner(caller));
+ || isProfileOwnerOfOrganizationOwnedDevice(caller) || isDefaultDeviceOwner(caller));
mInjector.binderWithCleanCallingIdentity(() ->
mInjector.settingsGlobalPutInt(Global.AUTO_TIME_ZONE, enabled ? 1 : 0));
@@ -8132,7 +8151,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
final CallerIdentity caller = getCallerIdentity(who);
Preconditions.checkCallAuthorization(isProfileOwnerOnUser0(caller)
- || isProfileOwnerOfOrganizationOwnedDevice(caller) || isDeviceOwner(caller));
+ || isProfileOwnerOfOrganizationOwnedDevice(caller) || isDefaultDeviceOwner(caller));
return mInjector.settingsGlobalGetInt(Global.AUTO_TIME_ZONE, 0) > 0;
}
@@ -8161,7 +8180,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
// which could still contain data related to that user. Should we disallow that, e.g. until
// next boot? Might not be needed given that this still requires user consent.
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller));
+ Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller));
checkAllUsersAreAffiliatedWithDevice();
checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_REQUEST_BUGREPORT);
@@ -8472,7 +8491,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
Objects.requireNonNull(packageList, "packageList is null");
final CallerIdentity caller = getCallerIdentity(who, callerPackage);
- Preconditions.checkCallAuthorization((caller.hasAdminComponent() && isDeviceOwner(caller))
+ Preconditions.checkCallAuthorization((caller.hasAdminComponent()
+ && isDefaultDeviceOwner(caller))
|| (caller.hasPackage()
&& isCallerDelegate(caller, DELEGATION_KEEP_UNINSTALLED_PACKAGES)));
checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_KEEP_UNINSTALLED_PACKAGES);
@@ -8501,7 +8521,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return null;
}
final CallerIdentity caller = getCallerIdentity(who, callerPackage);
- Preconditions.checkCallAuthorization((caller.hasAdminComponent() && isDeviceOwner(caller))
+ Preconditions.checkCallAuthorization((caller.hasAdminComponent()
+ && isDefaultDeviceOwner(caller))
|| (caller.hasPackage()
&& isCallerDelegate(caller, DELEGATION_KEEP_UNINSTALLED_PACKAGES)));
@@ -8615,8 +8636,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@Override
public boolean hasDeviceOwner() {
final CallerIdentity caller = getCallerIdentity();
- Preconditions.checkCallAuthorization(isDeviceOwner(caller)
- || canManageUsers(caller)
+ Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller)
+ || canManageUsers(caller) || isFinancedDeviceOwner(caller)
|| hasCallingOrSelfPermission(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS));
return mOwners.hasDeviceOwner();
}
@@ -8633,17 +8654,29 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
}
- private boolean isDeviceOwner(CallerIdentity caller) {
+ /**
+ * Returns {@code true} <b>only if</b> the caller is the device owner and the device owner type
+ * is {@link DevicePolicyManager#DEVICE_OWNER_TYPE_DEFAULT}. {@code false} is returned for the
+ * case where the caller is not the device owner, there is no device owner, or the device owner
+ * type is not {@link DevicePolicyManager#DEVICE_OWNER_TYPE_DEFAULT}.
+ *
+ */
+ private boolean isDefaultDeviceOwner(CallerIdentity caller) {
synchronized (getLockObject()) {
- if (!mOwners.hasDeviceOwner() || mOwners.getDeviceOwnerUserId() != caller.getUserId()) {
- return false;
- }
+ return isDeviceOwnerLocked(caller) && getDeviceOwnerTypeLocked(
+ mOwners.getDeviceOwnerPackageName()) == DEVICE_OWNER_TYPE_DEFAULT;
+ }
+ }
- if (caller.hasAdminComponent()) {
- return mOwners.getDeviceOwnerComponent().equals(caller.getComponentName());
- } else {
- return isUidDeviceOwnerLocked(caller.getUid());
- }
+ private boolean isDeviceOwnerLocked(CallerIdentity caller) {
+ if (!mOwners.hasDeviceOwner() || mOwners.getDeviceOwnerUserId() != caller.getUserId()) {
+ return false;
+ }
+
+ if (caller.hasAdminComponent()) {
+ return mOwners.getDeviceOwnerComponent().equals(caller.getComponentName());
+ } else {
+ return isUidDeviceOwnerLocked(caller.getUid());
}
}
@@ -9073,8 +9106,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Objects.requireNonNull(who, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller)
- || isProfileOwnerOfOrganizationOwnedDevice(caller));
+ Preconditions.checkCallAuthorization(
+ isDefaultDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller));
mInjector.binderWithCleanCallingIdentity(() ->
mLockPatternUtils.setDeviceOwnerInfo(info != null ? info.toString() : null));
@@ -9258,7 +9291,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
final CallerIdentity caller = getCallerIdentity(who);
final int userId = caller.getUserId();
- Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller));
+ Preconditions.checkCallAuthorization(
+ isProfileOwner(caller) || isDefaultDeviceOwner(caller));
Preconditions.checkCallingUser(isManagedProfile(userId));
synchronized (getLockObject()) {
@@ -9289,7 +9323,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Objects.requireNonNull(who, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
+ Preconditions.checkCallAuthorization(
+ isDefaultDeviceOwner(caller) || isProfileOwner(caller));
mInjector.binderWithCleanCallingIdentity(() -> {
mUserManager.setUserName(caller.getUserId(), profileName);
@@ -9725,7 +9760,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
private void enforceCanCallLockTaskLocked(CallerIdentity caller) {
- Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller));
+ Preconditions.checkCallAuthorization(isProfileOwner(caller)
+ || isDefaultDeviceOwner(caller) || isFinancedDeviceOwner(caller));
final int userId = caller.getUserId();
if (!canUserUseLockTaskLocked(userId)) {
@@ -9982,7 +10018,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
ComponentName activity) {
Objects.requireNonNull(who, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller));
+ Preconditions.checkCallAuthorization(isProfileOwner(caller)
+ || isDefaultDeviceOwner(caller) || isFinancedDeviceOwner(caller));
final int userHandle = caller.getUserId();
synchronized (getLockObject()) {
@@ -10009,7 +10046,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
public void clearPackagePersistentPreferredActivities(ComponentName who, String packageName) {
Objects.requireNonNull(who, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller));
+ Preconditions.checkCallAuthorization(isProfileOwner(caller)
+ || isDefaultDeviceOwner(caller) || isFinancedDeviceOwner(caller));
final int userHandle = caller.getUserId();
synchronized (getLockObject()) {
@@ -10030,7 +10068,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Objects.requireNonNull(admin, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(admin);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller)
+ Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller)
|| (parent && isProfileOwnerOfOrganizationOwnedDevice(caller)));
if (parent) {
mInjector.binderWithCleanCallingIdentity(() -> enforcePackageIsSystemPackage(
@@ -10070,7 +10108,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
String packageName, Bundle settings) {
final CallerIdentity caller = getCallerIdentity(who, callerPackage);
Preconditions.checkCallAuthorization((caller.hasAdminComponent()
- && (isProfileOwner(caller) || isDeviceOwner(caller)))
+ && (isProfileOwner(caller) || isDefaultDeviceOwner(caller)))
|| (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_APP_RESTRICTIONS)));
checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_APPLICATION_RESTRICTIONS);
@@ -10168,7 +10206,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
public void setRestrictionsProvider(ComponentName who, ComponentName permissionProvider) {
Objects.requireNonNull(who, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller));
+ Preconditions.checkCallAuthorization(
+ isProfileOwner(caller) || isDefaultDeviceOwner(caller));
checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_RESTRICTIONS_PROVIDER);
synchronized (getLockObject()) {
@@ -10193,7 +10232,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
public void addCrossProfileIntentFilter(ComponentName who, IntentFilter filter, int flags) {
Objects.requireNonNull(who, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller));
+ Preconditions.checkCallAuthorization(
+ isProfileOwner(caller) || isDefaultDeviceOwner(caller));
int callingUserId = caller.getUserId();
synchronized (getLockObject()) {
long id = mInjector.binderClearCallingIdentity();
@@ -10242,7 +10282,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
public void clearCrossProfileIntentFilters(ComponentName who) {
Objects.requireNonNull(who, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller));
+ Preconditions.checkCallAuthorization(
+ isProfileOwner(caller) || isDefaultDeviceOwner(caller));
int callingUserId = caller.getUserId();
synchronized (getLockObject()) {
@@ -10382,7 +10423,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
Objects.requireNonNull(who, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
+ Preconditions.checkCallAuthorization(
+ isDefaultDeviceOwner(caller) || isProfileOwner(caller));
synchronized (getLockObject()) {
ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(caller);
@@ -10495,7 +10537,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
+ "system input methods when called on the parent instance of an "
+ "organization-owned device");
} else {
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
+ Preconditions.checkCallAuthorization(
+ isDefaultDeviceOwner(caller) || isProfileOwner(caller));
}
if (packageList != null) {
@@ -10553,7 +10596,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (calledOnParentInstance) {
Preconditions.checkCallAuthorization(isProfileOwnerOfOrganizationOwnedDevice(caller));
} else {
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
+ Preconditions.checkCallAuthorization(
+ isDefaultDeviceOwner(caller) || isProfileOwner(caller));
}
synchronized (getLockObject()) {
@@ -10732,7 +10776,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
// Only allow the system user to use this method
Preconditions.checkCallAuthorization(caller.getUserHandle().isSystem(),
"createAndManageUser was called from non-system user");
- Preconditions.checkCallAuthorization(isDeviceOwner(caller));
+ Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller));
checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_CREATE_AND_MANAGE_USER);
final boolean ephemeral = (flags & DevicePolicyManager.MAKE_USER_EPHEMERAL) != 0;
@@ -10974,7 +11018,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Objects.requireNonNull(who, "ComponentName is null");
Objects.requireNonNull(userHandle, "UserHandle is null");
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller));
+ Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller));
checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_REMOVE_USER);
return mInjector.binderWithCleanCallingIdentity(() -> {
@@ -11008,7 +11052,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
public boolean switchUser(ComponentName who, UserHandle userHandle) {
Objects.requireNonNull(who, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller));
+ Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller));
checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SWITCH_USER);
boolean switched = false;
@@ -11083,7 +11127,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Objects.requireNonNull(who, "ComponentName is null");
Objects.requireNonNull(userHandle, "UserHandle is null");
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller));
+ Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller));
checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_START_USER_IN_BACKGROUND);
final int userId = userHandle.getIdentifier();
@@ -11119,7 +11163,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Objects.requireNonNull(who, "ComponentName is null");
Objects.requireNonNull(userHandle, "UserHandle is null");
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller));
+ Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller));
checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_STOP_USER);
final int userId = userHandle.getIdentifier();
@@ -11135,7 +11179,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
public int logoutUser(ComponentName who) {
Objects.requireNonNull(who, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller));
+ Preconditions.checkCallAuthorization(
+ isProfileOwner(caller) || isDefaultDeviceOwner(caller));
checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_LOGOUT_USER);
final int callingUserId = caller.getUserId();
@@ -11224,7 +11269,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
public List<UserHandle> getSecondaryUsers(ComponentName who) {
Objects.requireNonNull(who, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller));
+ Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller));
return mInjector.binderWithCleanCallingIdentity(() -> {
final List<UserInfo> userInfos = mInjector.getUserManager().getAliveUsers();
@@ -11244,7 +11289,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Objects.requireNonNull(who, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
+ Preconditions.checkCallAuthorization(
+ isDefaultDeviceOwner(caller) || isProfileOwner(caller));
return mInjector.binderWithCleanCallingIdentity(
() -> mInjector.getUserManager().isUserEphemeral(caller.getUserId()));
@@ -11255,7 +11301,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
String packageName) {
final CallerIdentity caller = getCallerIdentity(who, callerPackage);
Preconditions.checkCallAuthorization((caller.hasAdminComponent()
- && (isProfileOwner(caller) || isDeviceOwner(caller)))
+ && (isProfileOwner(caller) || isDefaultDeviceOwner(caller)))
|| (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_APP_RESTRICTIONS)));
return mInjector.binderWithCleanCallingIdentity(() -> {
@@ -11306,7 +11352,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Objects.requireNonNull(packageNames, "array of packages cannot be null");
final CallerIdentity caller = getCallerIdentity(who, callerPackage);
Preconditions.checkCallAuthorization((caller.hasAdminComponent()
- && (isProfileOwner(caller) || isDeviceOwner(caller)))
+ && (isProfileOwner(caller) || isDefaultDeviceOwner(caller)))
|| (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_PACKAGE_ACCESS)));
checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_PACKAGES_SUSPENDED);
@@ -11369,7 +11415,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
public boolean isPackageSuspended(ComponentName who, String callerPackage, String packageName) {
final CallerIdentity caller = getCallerIdentity(who, callerPackage);
Preconditions.checkCallAuthorization((caller.hasAdminComponent()
- && (isProfileOwner(caller) || isDeviceOwner(caller)))
+ && (isProfileOwner(caller) || isDefaultDeviceOwner(caller)))
|| (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_PACKAGE_ACCESS)));
synchronized (getLockObject()) {
@@ -11390,8 +11436,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
public List<String> listPolicyExemptApps() {
CallerIdentity caller = getCallerIdentity();
Preconditions.checkCallAuthorization(
- hasCallingOrSelfPermission(permission.MANAGE_DEVICE_ADMINS) || isDeviceOwner(caller)
- || isProfileOwner(caller));
+ hasCallingOrSelfPermission(permission.MANAGE_DEVICE_ADMINS)
+ || isDefaultDeviceOwner(caller) || isProfileOwner(caller));
return listPolicyExemptAppsUnchecked();
}
@@ -11432,12 +11478,19 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
final ActiveAdmin activeAdmin = getParentOfAdminIfRequired(
getProfileOwnerOrDeviceOwnerLocked(caller), parent);
- if (isDeviceOwner(caller)) {
+ if (isDefaultDeviceOwner(caller)) {
if (!UserRestrictionsUtils.canDeviceOwnerChange(key)) {
throw new SecurityException("Device owner cannot set user restriction " + key);
}
Preconditions.checkArgument(!parent,
"Cannot use the parent instance in Device Owner mode");
+ } else if (isFinancedDeviceOwner(caller)) {
+ if (!UserRestrictionsUtils.canFinancedDeviceOwnerChange(key)) {
+ throw new SecurityException("Cannot set user restriction " + key
+ + " when managing a financed device");
+ }
+ Preconditions.checkArgument(!parent,
+ "Cannot use the parent instance in Financed Device Owner mode");
} else {
boolean profileOwnerCanChangeOnItself = !parent
&& UserRestrictionsUtils.canProfileOwnerChange(key, userHandle);
@@ -11546,7 +11599,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Objects.requireNonNull(who, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller)
+ Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller)
+ || isProfileOwner(caller)
|| (parent && isProfileOwnerOfOrganizationOwnedDevice(caller)));
synchronized (getLockObject()) {
@@ -11561,7 +11615,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
boolean hidden, boolean parent) {
final CallerIdentity caller = getCallerIdentity(who, callerPackage);
Preconditions.checkCallAuthorization((caller.hasAdminComponent()
- && (isProfileOwner(caller) || isDeviceOwner(caller)))
+ && (isProfileOwner(caller) || isDefaultDeviceOwner(caller)))
|| (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_PACKAGE_ACCESS)));
List<String> exemptApps = listPolicyExemptAppsUnchecked();
@@ -11607,7 +11661,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
String packageName, boolean parent) {
final CallerIdentity caller = getCallerIdentity(who, callerPackage);
Preconditions.checkCallAuthorization((caller.hasAdminComponent()
- && (isProfileOwner(caller) || isDeviceOwner(caller)))
+ && (isProfileOwner(caller) || isDefaultDeviceOwner(caller)))
|| (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_PACKAGE_ACCESS)));
final int userId = parent ? getProfileParentId(caller.getUserId()) : caller.getUserId();
@@ -11643,7 +11697,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
public void enableSystemApp(ComponentName who, String callerPackage, String packageName) {
final CallerIdentity caller = getCallerIdentity(who, callerPackage);
Preconditions.checkCallAuthorization((caller.hasAdminComponent()
- && (isProfileOwner(caller) || isDeviceOwner(caller)))
+ && (isProfileOwner(caller) || isDefaultDeviceOwner(caller)))
|| (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_ENABLE_SYSTEM_APP)));
synchronized (getLockObject()) {
@@ -11687,7 +11741,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
public int enableSystemAppWithIntent(ComponentName who, String callerPackage, Intent intent) {
final CallerIdentity caller = getCallerIdentity(who, callerPackage);
Preconditions.checkCallAuthorization((caller.hasAdminComponent()
- && (isProfileOwner(caller) || isDeviceOwner(caller)))
+ && (isProfileOwner(caller) || isDefaultDeviceOwner(caller)))
|| (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_ENABLE_SYSTEM_APP)));
int numberOfAppsInstalled = 0;
@@ -11756,7 +11810,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
String packageName) {
final CallerIdentity caller = getCallerIdentity(who, callerPackage);
Preconditions.checkCallAuthorization((caller.hasAdminComponent()
- && (isProfileOwner(caller) || isDeviceOwner(caller)))
+ && (isProfileOwner(caller) || isDefaultDeviceOwner(caller)))
|| (caller.hasPackage()
&& isCallerDelegate(caller, DELEGATION_INSTALL_EXISTING_PACKAGE)));
@@ -11872,7 +11926,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
boolean uninstallBlocked) {
final CallerIdentity caller = getCallerIdentity(who, callerPackage);
Preconditions.checkCallAuthorization((caller.hasAdminComponent()
- && (isProfileOwner(caller) || isDeviceOwner(caller)))
+ && (isProfileOwner(caller) || isDefaultDeviceOwner(caller)
+ || isFinancedDeviceOwner(caller)))
|| (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_BLOCK_UNINSTALL)));
final int userId = caller.getUserId();
@@ -11913,7 +11968,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (who != null) {
final CallerIdentity caller = getCallerIdentity(who);
Preconditions.checkCallAuthorization(
- isProfileOwner(caller) || isDeviceOwner(caller));
+ isProfileOwner(caller) || isDefaultDeviceOwner(caller)
+ || isFinancedDeviceOwner(caller));
}
try {
return mIPackageManager.getBlockUninstallForUser(packageName, userId);
@@ -12087,7 +12143,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Objects.requireNonNull(who, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
+ Preconditions.checkCallAuthorization(
+ isDefaultDeviceOwner(caller) || isProfileOwner(caller));
synchronized (getLockObject()) {
ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(caller);
@@ -12111,7 +12168,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Objects.requireNonNull(who, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
+ Preconditions.checkCallAuthorization(
+ isDefaultDeviceOwner(caller) || isProfileOwner(caller));
synchronized (getLockObject()) {
ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(caller);
@@ -12136,7 +12194,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
// Check can set secondary lockscreen enabled
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
+ Preconditions.checkCallAuthorization(
+ isDefaultDeviceOwner(caller) || isProfileOwner(caller));
Preconditions.checkCallAuthorization(!isManagedProfile(caller.getUserId()),
"User %d is not allowed to call setSecondaryLockscreenEnabled",
caller.getUserId());
@@ -12325,6 +12384,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
final int userHandle = caller.getUserId();
synchronized (getLockObject()) {
enforceCanCallLockTaskLocked(caller);
+ enforceCanSetLockTaskFeaturesOnFinancedDevice(caller, flags);
checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_LOCK_TASK_FEATURES);
setLockTaskFeaturesLocked(userHandle, flags);
}
@@ -12373,6 +12433,24 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
});
}
+ private void enforceCanSetLockTaskFeaturesOnFinancedDevice(CallerIdentity caller, int flags) {
+ int allowedFlags = LOCK_TASK_FEATURE_SYSTEM_INFO | LOCK_TASK_FEATURE_KEYGUARD
+ | LOCK_TASK_FEATURE_HOME | LOCK_TASK_FEATURE_GLOBAL_ACTIONS
+ | LOCK_TASK_FEATURE_NOTIFICATIONS;
+
+ if (!isFinancedDeviceOwner(caller)) {
+ return;
+ }
+
+ if ((flags == 0) || ((flags & ~(allowedFlags)) != 0)) {
+ throw new SecurityException(
+ "Permitted lock task features when managing a financed device: "
+ + "LOCK_TASK_FEATURE_SYSTEM_INFO, LOCK_TASK_FEATURE_KEYGUARD, "
+ + "LOCK_TASK_FEATURE_HOME, LOCK_TASK_FEATURE_GLOBAL_ACTIONS, "
+ + "or LOCK_TASK_FEATURE_NOTIFICATIONS");
+ }
+ }
+
@Override
public void notifyLockTaskModeChanged(boolean isEnabled, String pkg, int userHandle) {
Preconditions.checkCallAuthorization(isSystemUid(getCallerIdentity()),
@@ -12413,7 +12491,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
public void setGlobalSetting(ComponentName who, String setting, String value) {
Objects.requireNonNull(who, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller));
+ Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller));
DevicePolicyEventLogger
.createEvent(DevicePolicyEnums.SET_GLOBAL_SETTING)
@@ -12454,7 +12532,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Objects.requireNonNull(who, "ComponentName is null");
Preconditions.checkStringNotEmpty(setting, "String setting is null or empty");
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller));
+ Preconditions.checkCallAuthorization(
+ isProfileOwner(caller) || isDefaultDeviceOwner(caller));
checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_SYSTEM_SETTING);
synchronized (getLockObject()) {
@@ -12476,8 +12555,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Preconditions.checkNotNull(who, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller)
- || isProfileOwnerOfOrganizationOwnedDevice(caller));
+ Preconditions.checkCallAuthorization(
+ isDefaultDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller));
mInjector.binderWithCleanCallingIdentity(() ->
mInjector.settingsGlobalPutInt(Global.WIFI_DEVICE_OWNER_CONFIGS_LOCKDOWN,
@@ -12498,8 +12577,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Preconditions.checkNotNull(who, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller)
- || isProfileOwnerOfOrganizationOwnedDevice(caller));
+ Preconditions.checkCallAuthorization(
+ isDefaultDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller));
return mInjector.binderWithCleanCallingIdentity(() ->
mInjector.settingsGlobalGetInt(Global.WIFI_DEVICE_OWNER_CONFIGS_LOCKDOWN, 0) > 0);
@@ -12510,7 +12589,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Preconditions.checkNotNull(who, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller));
+ Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller));
UserHandle userHandle = caller.getUserHandle();
if (mIsAutomotive && !locationEnabled) {
@@ -12592,8 +12671,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Objects.requireNonNull(who, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller)
- || isProfileOwnerOfOrganizationOwnedDevice(caller));
+ Preconditions.checkCallAuthorization(
+ isDefaultDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller));
// Don't allow set time when auto time is on.
if (mInjector.settingsGlobalGetInt(Global.AUTO_TIME, 0) == 1) {
@@ -12612,8 +12691,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Objects.requireNonNull(who, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller)
- || isProfileOwnerOfOrganizationOwnedDevice(caller));
+ Preconditions.checkCallAuthorization(
+ isDefaultDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller));
// Don't allow set timezone when auto timezone is on.
if (mInjector.settingsGlobalGetInt(Global.AUTO_TIME_ZONE, 0) == 1) {
@@ -12633,7 +12712,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
public void setSecureSetting(ComponentName who, String setting, String value) {
Objects.requireNonNull(who, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller));
+ Preconditions.checkCallAuthorization(
+ isProfileOwner(caller) || isDefaultDeviceOwner(caller));
int callingUserId = caller.getUserId();
synchronized (getLockObject()) {
@@ -12720,7 +12800,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
public void setMasterVolumeMuted(ComponentName who, boolean on) {
Objects.requireNonNull(who, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller));
+ Preconditions.checkCallAuthorization(
+ isProfileOwner(caller) || isDefaultDeviceOwner(caller));
checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_MASTER_VOLUME_MUTED);
synchronized (getLockObject()) {
@@ -12737,7 +12818,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
public boolean isMasterVolumeMuted(ComponentName who) {
Objects.requireNonNull(who, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller));
+ Preconditions.checkCallAuthorization(
+ isProfileOwner(caller) || isDefaultDeviceOwner(caller));
synchronized (getLockObject()) {
AudioManager audioManager =
@@ -12750,7 +12832,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
public void setUserIcon(ComponentName who, Bitmap icon) {
Objects.requireNonNull(who, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller));
+ Preconditions.checkCallAuthorization(
+ isProfileOwner(caller) || isDefaultDeviceOwner(caller));
synchronized (getLockObject()) {
mInjector.binderWithCleanCallingIdentity(
@@ -12766,7 +12849,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
public boolean setKeyguardDisabled(ComponentName who, boolean disabled) {
Objects.requireNonNull(who, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller));
+ Preconditions.checkCallAuthorization(
+ isProfileOwner(caller) || isDefaultDeviceOwner(caller));
final int userId = caller.getUserId();
synchronized (getLockObject()) {
@@ -12808,7 +12892,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@Override
public boolean setStatusBarDisabled(ComponentName who, boolean disabled) {
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller));
+ Preconditions.checkCallAuthorization(
+ isProfileOwner(caller) || isDefaultDeviceOwner(caller));
int userId = caller.getUserId();
synchronized (getLockObject()) {
@@ -13042,7 +13127,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@Override
public boolean isActiveDeviceOwner(int uid) {
- return isDeviceOwner(new CallerIdentity(uid, null, null));
+ return isDefaultDeviceOwner(new CallerIdentity(uid, null, null));
}
@Override
@@ -13633,7 +13718,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
synchronized (getLockObject()) {
Preconditions.checkCallAuthorization(isProfileOwnerOfOrganizationOwnedDevice(caller)
- || isDeviceOwner(caller));
+ || isDefaultDeviceOwner(caller));
checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_SYSTEM_UPDATE_POLICY);
if (policy == null) {
@@ -13831,7 +13916,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Objects.requireNonNull(admin, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(admin);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
+ Preconditions.checkCallAuthorization(
+ isDefaultDeviceOwner(caller) || isProfileOwner(caller));
return mOwners.getSystemUpdateInfo();
}
@@ -13840,7 +13926,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
public void setPermissionPolicy(ComponentName admin, String callerPackage, int policy) {
final CallerIdentity caller = getCallerIdentity(admin, callerPackage);
Preconditions.checkCallAuthorization((caller.hasAdminComponent()
- && (isProfileOwner(caller) || isDeviceOwner(caller)))
+ && (isProfileOwner(caller) || isDefaultDeviceOwner(caller)))
|| (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_PERMISSION_GRANT)));
checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_PERMISSION_POLICY);
@@ -13882,11 +13968,15 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
final CallerIdentity caller = getCallerIdentity(admin, callerPackage);
Preconditions.checkCallAuthorization((caller.hasAdminComponent()
- && (isProfileOwner(caller) || isDeviceOwner(caller)))
+ && (isProfileOwner(caller) || isDefaultDeviceOwner(caller)
+ || isFinancedDeviceOwner(caller)))
|| (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_PERMISSION_GRANT)));
checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_PERMISSION_GRANT_STATE);
synchronized (getLockObject()) {
+ if (isFinancedDeviceOwner(caller)) {
+ enforceCanSetPermissionGrantOnFinancedDevice(packageName, permission);
+ }
long ident = mInjector.binderClearCallingIdentity();
try {
boolean isPostQAdmin = getTargetSdk(caller.getPackageName(), caller.getUserId())
@@ -13946,12 +14036,23 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
}
+ private void enforceCanSetPermissionGrantOnFinancedDevice(
+ String packageName, String permission) {
+ if (!Manifest.permission.READ_PHONE_STATE.equals(permission)) {
+ throw new SecurityException("Cannot grant " + permission
+ + " when managing a financed device");
+ } else if (!mOwners.getDeviceOwnerPackageName().equals(packageName)) {
+ throw new SecurityException("Cannot grant permission to a package that is not"
+ + " the device owner");
+ }
+ }
+
@Override
public int getPermissionGrantState(ComponentName admin, String callerPackage,
String packageName, String permission) throws RemoteException {
final CallerIdentity caller = getCallerIdentity(admin, callerPackage);
Preconditions.checkCallAuthorization(isSystemUid(caller) || (caller.hasAdminComponent()
- && (isProfileOwner(caller) || isDeviceOwner(caller)))
+ && (isProfileOwner(caller) || isDefaultDeviceOwner(caller)))
|| (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_PERMISSION_GRANT)));
synchronized (getLockObject()) {
@@ -14231,7 +14332,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
private void checkIsDeviceOwner(CallerIdentity caller) {
- Preconditions.checkCallAuthorization(isDeviceOwner(caller), caller.getUid()
+ Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller), caller.getUid()
+ " is not device owner");
}
@@ -14263,8 +14364,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Objects.requireNonNull(admin, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(admin);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller)
- || isProfileOwnerOfOrganizationOwnedDevice(caller));
+ Preconditions.checkCallAuthorization(
+ isDefaultDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller));
return mInjector.binderWithCleanCallingIdentity(() -> {
String[] macAddresses = mInjector.getWifiManager().getFactoryMacAddresses();
@@ -14299,7 +14400,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Objects.requireNonNull(admin, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(admin);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
+ Preconditions.checkCallAuthorization(
+ isDefaultDeviceOwner(caller) || isProfileOwner(caller));
return isManagedProfile(caller.getUserId());
}
@@ -14308,7 +14410,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
public void reboot(ComponentName admin) {
Objects.requireNonNull(admin, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(admin);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller));
+ Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller));
checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_REBOOT);
mInjector.binderWithCleanCallingIdentity(() -> {
// Make sure there are no ongoing calls on the device.
@@ -14542,7 +14644,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return null;
}
final CallerIdentity caller = getCallerIdentity();
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || canManageUsers(caller));
+ Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller)
+ || canManageUsers(caller) || isFinancedDeviceOwner(caller));
synchronized (getLockObject()) {
final ActiveAdmin deviceOwnerAdmin = getDeviceOwnerAdminLocked();
return deviceOwnerAdmin == null ? null : deviceOwnerAdmin.organizationName;
@@ -14576,7 +14679,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Objects.requireNonNull(who);
Objects.requireNonNull(packageNames);
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller),
+ Preconditions.checkCallAuthorization(
+ isDefaultDeviceOwner(caller) || isProfileOwner(caller),
"Admin %s does not own the profile", caller.getComponentName());
if (!mHasFeature) {
@@ -14627,7 +14731,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return new ArrayList<>();
}
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller),
+ Preconditions.checkCallAuthorization(
+ isDefaultDeviceOwner(caller) || isProfileOwner(caller),
"Admin %s does not own the profile", caller.getComponentName());
synchronized (getLockObject()) {
@@ -14766,7 +14871,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
final Set<String> affiliationIds = new ArraySet<>(ids);
final CallerIdentity caller = getCallerIdentity(admin);
- Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller));
+ Preconditions.checkCallAuthorization(
+ isProfileOwner(caller) || isDefaultDeviceOwner(caller));
final int callingUserId = caller.getUserId();
synchronized (getLockObject()) {
@@ -14797,7 +14903,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Objects.requireNonNull(admin);
final CallerIdentity caller = getCallerIdentity(admin);
- Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller));
+ Preconditions.checkCallAuthorization(
+ isProfileOwner(caller) || isDefaultDeviceOwner(caller));
synchronized (getLockObject()) {
return new ArrayList<String>(getUserData(caller.getUserId()).mAffiliationIds);
@@ -14891,7 +14998,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (admin != null) {
Preconditions.checkCallAuthorization(
isProfileOwnerOfOrganizationOwnedDevice(caller)
- || isDeviceOwner(caller));
+ || isDefaultDeviceOwner(caller));
} else {
// A delegate app passes a null admin component, which is expected
Preconditions.checkCallAuthorization(
@@ -14928,7 +15035,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (admin != null) {
Preconditions.checkCallAuthorization(
isProfileOwnerOfOrganizationOwnedDevice(caller)
- || isDeviceOwner(caller));
+ || isDefaultDeviceOwner(caller));
} else {
// A delegate app passes a null admin component, which is expected
Preconditions.checkCallAuthorization(
@@ -14961,7 +15068,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (admin != null) {
Preconditions.checkCallAuthorization(
isProfileOwnerOfOrganizationOwnedDevice(caller)
- || isDeviceOwner(caller));
+ || isDefaultDeviceOwner(caller));
} else {
// A delegate app passes a null admin component, which is expected
Preconditions.checkCallAuthorization(
@@ -15007,7 +15114,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (admin != null) {
Preconditions.checkCallAuthorization(
isProfileOwnerOfOrganizationOwnedDevice(caller)
- || isDeviceOwner(caller));
+ || isDefaultDeviceOwner(caller));
} else {
// A delegate app passes a null admin component, which is expected
Preconditions.checkCallAuthorization(
@@ -15246,7 +15353,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Objects.requireNonNull(admin, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(admin);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
+ Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller)
+ || isProfileOwner(caller) || isFinancedDeviceOwner(caller));
toggleBackupServiceActive(caller.getUserId(), enabled);
}
@@ -15259,7 +15367,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Objects.requireNonNull(admin, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(admin);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
+ Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller)
+ || isProfileOwner(caller) || isFinancedDeviceOwner(caller));
return mInjector.binderWithCleanCallingIdentity(() -> {
synchronized (getLockObject()) {
@@ -15334,7 +15443,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
Objects.requireNonNull(admin);
final CallerIdentity caller = getCallerIdentity(admin);
- Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller));
+ Preconditions.checkCallAuthorization(
+ isProfileOwner(caller) || isDefaultDeviceOwner(caller));
synchronized (getLockObject()) {
final int callingUserId = caller.getUserId();
@@ -15462,7 +15572,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
final boolean isManagedProfileOwner = isProfileOwner(caller)
&& isManagedProfile(caller.getUserId());
Preconditions.checkCallAuthorization((caller.hasAdminComponent()
- && (isDeviceOwner(caller) || isManagedProfileOwner))
+ && (isDefaultDeviceOwner(caller) || isManagedProfileOwner))
|| (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_NETWORK_LOGGING)));
synchronized (getLockObject()) {
@@ -15622,7 +15732,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
final CallerIdentity caller = getCallerIdentity(admin, packageName);
Preconditions.checkCallAuthorization((caller.hasAdminComponent()
- && (isDeviceOwner(caller)
+ && (isDefaultDeviceOwner(caller)
|| (isProfileOwner(caller) && isManagedProfile(caller.getUserId()))))
|| (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_NETWORK_LOGGING))
|| hasCallingOrSelfPermission(permission.MANAGE_USERS));
@@ -15654,7 +15764,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
final boolean isManagedProfileOwner = isProfileOwner(caller)
&& isManagedProfile(caller.getUserId());
Preconditions.checkCallAuthorization((caller.hasAdminComponent()
- && (isDeviceOwner(caller) || isManagedProfileOwner))
+ && (isDefaultDeviceOwner(caller) || isManagedProfileOwner))
|| (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_NETWORK_LOGGING)));
if (mOwners.hasDeviceOwner()) {
checkAllUsersAreAffiliatedWithDevice();
@@ -15815,14 +15925,16 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@Override
public long getLastSecurityLogRetrievalTime() {
final CallerIdentity caller = getCallerIdentity();
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || canManageUsers(caller));
+ Preconditions.checkCallAuthorization(
+ isDefaultDeviceOwner(caller) || canManageUsers(caller));
return getUserData(UserHandle.USER_SYSTEM).mLastSecurityLogRetrievalTime;
}
@Override
public long getLastBugReportRequestTime() {
final CallerIdentity caller = getCallerIdentity();
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || canManageUsers(caller));
+ Preconditions.checkCallAuthorization(
+ isDefaultDeviceOwner(caller) || canManageUsers(caller));
return getUserData(UserHandle.USER_SYSTEM).mLastBugReportRequestTime;
}
@@ -15830,7 +15942,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
public long getLastNetworkLogRetrievalTime() {
final CallerIdentity caller = getCallerIdentity();
- Preconditions.checkCallAuthorization(isDeviceOwner(caller)
+ Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller)
|| (isProfileOwner(caller) && isManagedProfile(caller.getUserId()))
|| canManageUsers(caller));
final int affectedUserId = getNetworkLoggingAffectedUser();
@@ -15846,7 +15958,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
throw new IllegalArgumentException("token must be at least 32-byte long");
}
final CallerIdentity caller = getCallerIdentity(admin);
- Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller));
+ Preconditions.checkCallAuthorization(
+ isProfileOwner(caller) || isDefaultDeviceOwner(caller));
synchronized (getLockObject()) {
final int userHandle = caller.getUserId();
@@ -15870,7 +15983,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return false;
}
final CallerIdentity caller = getCallerIdentity(admin);
- Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller));
+ Preconditions.checkCallAuthorization(
+ isProfileOwner(caller) || isDefaultDeviceOwner(caller));
synchronized (getLockObject()) {
final int userHandle = caller.getUserId();
@@ -15895,7 +16009,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return false;
}
final CallerIdentity caller = getCallerIdentity(admin);
- Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller));
+ Preconditions.checkCallAuthorization(
+ isProfileOwner(caller) || isDefaultDeviceOwner(caller));
synchronized (getLockObject()) {
return isResetPasswordTokenActiveForUserLocked(caller.getUserId());
@@ -15920,7 +16035,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Objects.requireNonNull(token);
final CallerIdentity caller = getCallerIdentity(admin);
- Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller));
+ Preconditions.checkCallAuthorization(
+ isProfileOwner(caller) || isDefaultDeviceOwner(caller));
synchronized (getLockObject()) {
DevicePolicyData policy = getUserData(caller.getUserId());
@@ -15945,7 +16061,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@Override
public boolean isCurrentInputMethodSetByOwner() {
final CallerIdentity caller = getCallerIdentity();
- Preconditions.checkCallAuthorization(isDeviceOwner(caller)
+ Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller)
|| isProfileOwner(caller) || isSystemUid(caller),
"Only profile owner, device owner and system may call this method.");
return getUserData(caller.getUserId()).mCurrentInputMethodSet;
@@ -15956,7 +16072,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
final int userId = user.getIdentifier();
final CallerIdentity caller = getCallerIdentity();
Preconditions.checkCallAuthorization((userId == caller.getUserId())
- || isProfileOwner(caller) || isDeviceOwner(caller)
+ || isProfileOwner(caller) || isDefaultDeviceOwner(caller)
|| hasFullCrossUsersPermission(caller, userId));
synchronized (getLockObject()) {
@@ -15973,7 +16089,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Objects.requireNonNull(callback, "callback is null");
final CallerIdentity caller = getCallerIdentity(admin);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
+ Preconditions.checkCallAuthorization(
+ isDefaultDeviceOwner(caller) || isProfileOwner(caller));
checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_CLEAR_APPLICATION_USER_DATA);
long ident = mInjector.binderClearCallingIdentity();
@@ -16005,7 +16122,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
Objects.requireNonNull(admin, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(admin);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller));
+ Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller));
checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_LOGOUT_ENABLED);
synchronized (getLockObject()) {
@@ -16054,7 +16171,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
"Provided administrator and target have the same package name.");
final CallerIdentity caller = getCallerIdentity(admin);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
+ Preconditions.checkCallAuthorization(
+ isDefaultDeviceOwner(caller) || isProfileOwner(caller));
final int callingUserId = caller.getUserId();
final DevicePolicyData policy = getUserData(callingUserId);
@@ -16096,7 +16214,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (isUserAffiliatedWithDeviceLocked(callingUserId)) {
notifyAffiliatedProfileTransferOwnershipComplete(callingUserId);
}
- } else if (isDeviceOwner(caller)) {
+ } else if (isDefaultDeviceOwner(caller)) {
ownerType = ADMIN_TYPE_DEVICE_OWNER;
prepareTransfer(admin, target, bundle, callingUserId,
ADMIN_TYPE_DEVICE_OWNER);
@@ -16177,7 +16295,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
Objects.requireNonNull(admin, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(admin);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller));
+ Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller));
final String startUserSessionMessageString =
startUserSessionMessage != null ? startUserSessionMessage.toString() : null;
@@ -16202,7 +16320,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
Objects.requireNonNull(admin, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(admin);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller));
+ Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller));
final String endUserSessionMessageString =
endUserSessionMessage != null ? endUserSessionMessage.toString() : null;
@@ -16227,7 +16345,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
Objects.requireNonNull(admin, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(admin);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller));
+ Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller));
synchronized (getLockObject()) {
final ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked();
@@ -16242,7 +16360,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
Objects.requireNonNull(admin, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(admin);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller));
+ Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller));
synchronized (getLockObject()) {
final ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked();
@@ -16258,7 +16376,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@Nullable
public PersistableBundle getTransferOwnershipBundle() {
final CallerIdentity caller = getCallerIdentity();
- Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller));
+ Preconditions.checkCallAuthorization(
+ isProfileOwner(caller) || isDefaultDeviceOwner(caller));
synchronized (getLockObject()) {
final int callingUserId = caller.getUserId();
@@ -16288,7 +16407,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Objects.requireNonNull(who, "ComponentName is null");
Objects.requireNonNull(apnSetting, "ApnSetting is null in addOverrideApn");
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller));
+ Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller));
TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
if (tm != null) {
@@ -16309,7 +16428,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Objects.requireNonNull(who, "ComponentName is null");
Objects.requireNonNull(apnSetting, "ApnSetting is null in updateOverrideApn");
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller));
+ Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller));
if (apnId < 0) {
return false;
@@ -16331,7 +16450,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
Objects.requireNonNull(who, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller));
+ Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller));
return removeOverrideApnUnchecked(apnId);
}
@@ -16352,7 +16471,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
Objects.requireNonNull(who, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller));
+ Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller));
return getOverrideApnsUnchecked();
}
@@ -16373,7 +16492,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
Objects.requireNonNull(who, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller));
+ Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller));
checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_OVERRIDE_APNS_ENABLED);
setOverrideApnsEnabledUnchecked(enabled);
@@ -16393,7 +16512,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
Objects.requireNonNull(who, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller));
+ Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller));
Cursor enforceCursor = mInjector.binderWithCleanCallingIdentity(
() -> mContext.getContentResolver().query(
@@ -16476,7 +16595,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
Objects.requireNonNull(who, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller));
+ Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller));
checkAllUsersAreAffiliatedWithDevice();
checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_GLOBAL_PRIVATE_DNS);
@@ -16515,7 +16634,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
Objects.requireNonNull(who, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller));
+ Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller));
final int currentMode = ConnectivitySettingsManager.getPrivateDnsMode(mContext);
switch (currentMode) {
@@ -16537,7 +16656,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
Objects.requireNonNull(who, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller));
+ Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller));
return mInjector.settingsGlobalGetString(PRIVATE_DNS_SPECIFIER);
}
@@ -16547,8 +16666,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Objects.requireNonNull(admin, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(admin);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller)
- || isProfileOwnerOfOrganizationOwnedDevice(caller));
+ Preconditions.checkCallAuthorization(
+ isDefaultDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller));
checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_INSTALL_SYSTEM_UPDATE);
DevicePolicyEventLogger
@@ -16923,7 +17042,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Objects.requireNonNull(who, "ComponentName is null");
Objects.requireNonNull(packages, "packages is null");
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller));
+ Preconditions.checkCallAuthorization(
+ isDefaultDeviceOwner(caller) || isFinancedDeviceOwner(caller));
checkCanExecuteOrThrowUnsafe(
DevicePolicyManager.OPERATION_SET_USER_CONTROL_DISABLED_PACKAGES);
@@ -16942,7 +17062,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Objects.requireNonNull(who, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller));
+ Preconditions.checkCallAuthorization(
+ isDefaultDeviceOwner(caller) || isFinancedDeviceOwner(caller));
synchronized (getLockObject()) {
return mOwners.getDeviceOwnerProtectedPackages(who.getPackageName());
@@ -16954,7 +17075,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Objects.requireNonNull(who, "Admin component name must be provided");
final CallerIdentity caller = getCallerIdentity(who);
Preconditions.checkCallAuthorization(
- isDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller),
+ isDefaultDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller),
"Common Criteria mode can only be controlled by a device owner or "
+ "a profile owner on an organization-owned device.");
synchronized (getLockObject()) {
@@ -16974,7 +17095,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (who != null) {
final CallerIdentity caller = getCallerIdentity(who);
Preconditions.checkCallAuthorization(
- isDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller),
+ isDefaultDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller),
"Common Criteria mode can only be controlled by a device owner or "
+ "a profile owner on an organization-owned device.");
@@ -17446,7 +17567,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
final CallerIdentity caller = getCallerIdentity(callerPackage);
Preconditions.checkCallAuthorization(
- isDeviceOwner(caller) || isProfileOwner(caller)
+ isDefaultDeviceOwner(caller) || isProfileOwner(caller)
|| isCallerDelegate(caller, DELEGATION_CERT_INSTALL));
synchronized (getLockObject()) {
@@ -17467,7 +17588,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
final CallerIdentity caller = getCallerIdentity(callerPackage);
// Only the DPC can set this ID.
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller),
+ Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller) || isProfileOwner(caller),
"Only a Device Owner or Profile Owner may set the Enterprise ID.");
// Empty enterprise ID must not be provided in calls to this method.
Preconditions.checkArgument(!TextUtils.isEmpty(organizationId),
@@ -17559,6 +17680,13 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
? Collections.emptySet()
: mOverlayPackagesProvider.getNonRequiredApps(
admin, caller.getUserId(), ACTION_PROVISION_MANAGED_PROFILE);
+ if (nonRequiredApps.isEmpty()) {
+ Slogf.i(LOG_TAG, "No disallowed packages for the managed profile.");
+ } else {
+ for (String packageName : nonRequiredApps) {
+ Slogf.i(LOG_TAG, "Disallowed package [" + packageName + "]");
+ }
+ }
userInfo = mUserManager.createProfileForUserEvenWhenDisallowed(
provisioningParams.getProfileName(),
UserManager.USER_TYPE_PROFILE_MANAGED,
@@ -17577,6 +17705,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
startTime,
callerPackage);
+ onCreateAndProvisionManagedProfileStarted(provisioningParams);
+
installExistingAdminPackage(userInfo.id, admin.getPackageName());
if (!enableAdminAndSetProfileOwner(
userInfo.id, caller.getUserId(), admin, provisioningParams.getOwnerName())) {
@@ -17597,6 +17727,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
}
+ onCreateAndProvisionManagedProfileCompleted(provisioningParams);
+
sendProvisioningCompletedBroadcast(
userInfo.id,
ACTION_PROVISION_MANAGED_PROFILE,
@@ -17618,6 +17750,29 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
}
+ /**
+ * Callback called at the beginning of {@link #createAndProvisionManagedProfile(
+ * ManagedProfileProvisioningParams, String)} after the relevant prechecks have passed.
+ *
+ * <p>The logic in this method blocks provisioning.
+ *
+ * <p>This method is meant to be overridden by OEMs.
+ */
+ private void onCreateAndProvisionManagedProfileStarted(
+ ManagedProfileProvisioningParams provisioningParams) {}
+
+ /**
+ * Callback called at the end of {@link #createAndProvisionManagedProfile(
+ * ManagedProfileProvisioningParams, String)} after all the other provisioning tasks
+ * have completed successfully.
+ *
+ * <p>The logic in this method blocks provisioning.
+ *
+ * <p>This method is meant to be overridden by OEMs.
+ */
+ private void onCreateAndProvisionManagedProfileCompleted(
+ ManagedProfileProvisioningParams provisioningParams) {}
+
private void resetInteractAcrossProfilesAppOps() {
mInjector.getCrossProfileApps().clearInteractAcrossProfilesAppOps();
pregrantDefaultInteractAcrossProfilesAppOps();
@@ -17857,6 +18012,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
ERROR_PRE_CONDITION_FAILED,
"Provisioning preconditions failed with result: " + result);
}
+ onProvisionFullyManagedDeviceStarted(provisioningParams);
setTimeAndTimezone(provisioningParams.getTimeZone(), provisioningParams.getLocalTime());
setLocale(provisioningParams.getLocale());
@@ -17882,6 +18038,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
disallowAddUser();
setAdminCanGrantSensorsPermissionForUserUnchecked(deviceOwnerUserId,
provisioningParams.canDeviceOwnerGrantSensorsPermissions());
+ onProvisionFullyManagedDeviceCompleted(provisioningParams);
sendProvisioningCompletedBroadcast(
deviceOwnerUserId,
ACTION_PROVISION_MANAGED_DEVICE,
@@ -17897,6 +18054,29 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
}
+ /**
+ * Callback called at the beginning of {@link #provisionFullyManagedDevice(
+ * FullyManagedDeviceProvisioningParams, String)} after the relevant prechecks have passed.
+ *
+ * <p>The logic in this method blocks provisioning.
+ *
+ * <p>This method is meant to be overridden by OEMs.
+ */
+ private void onProvisionFullyManagedDeviceStarted(
+ FullyManagedDeviceProvisioningParams provisioningParams) {}
+
+ /**
+ * Callback called at the end of {@link #provisionFullyManagedDevice(
+ * FullyManagedDeviceProvisioningParams, String)} after all the other provisioning tasks
+ * have completed successfully.
+ *
+ * <p>The logic in this method blocks provisioning.
+ *
+ * <p>This method is meant to be overridden by OEMs.
+ */
+ private void onProvisionFullyManagedDeviceCompleted(
+ FullyManagedDeviceProvisioningParams provisioningParams) {}
+
private void setTimeAndTimezone(String timeZone, long localTime) {
try {
final AlarmManager alarmManager = mContext.getSystemService(AlarmManager.class);
@@ -18141,27 +18321,55 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@DeviceOwnerType int deviceOwnerType) {
Preconditions.checkCallAuthorization(hasCallingOrSelfPermission(
permission.MANAGE_PROFILE_AND_DEVICE_OWNERS));
- verifyDeviceOwnerTypePreconditions(admin);
-
- final String packageName = admin.getPackageName();
- Preconditions.checkState(!mOwners.isDeviceOwnerTypeSetForDeviceOwner(packageName),
- "The device owner type has already been set for " + packageName);
synchronized (getLockObject()) {
- mOwners.setDeviceOwnerType(packageName, deviceOwnerType);
+ setDeviceOwnerTypeLocked(admin, deviceOwnerType);
}
}
+ private void setDeviceOwnerTypeLocked(ComponentName admin,
+ @DeviceOwnerType int deviceOwnerType) {
+ String packageName = admin.getPackageName();
+ boolean isAdminTestOnly;
+
+ verifyDeviceOwnerTypePreconditionsLocked(admin);
+
+ isAdminTestOnly = isAdminTestOnlyLocked(admin, mOwners.getDeviceOwnerUserId());
+ Preconditions.checkState(isAdminTestOnly
+ || !mOwners.isDeviceOwnerTypeSetForDeviceOwner(packageName),
+ "Test only admins can only set the device owner type more than once");
+
+ mOwners.setDeviceOwnerType(packageName, deviceOwnerType, isAdminTestOnly);
+ }
+
@Override
@DeviceOwnerType
public int getDeviceOwnerType(@NonNull ComponentName admin) {
- verifyDeviceOwnerTypePreconditions(admin);
synchronized (getLockObject()) {
- return mOwners.getDeviceOwnerType(admin.getPackageName());
+ verifyDeviceOwnerTypePreconditionsLocked(admin);
+ return getDeviceOwnerTypeLocked(admin.getPackageName());
+ }
+ }
+
+ @DeviceOwnerType
+ private int getDeviceOwnerTypeLocked(String packageName) {
+ return mOwners.getDeviceOwnerType(packageName);
+ }
+
+ /**
+ * {@code true} is returned <b>only if</b> the caller is the device owner and the device owner
+ * type is {@link DevicePolicyManager#DEVICE_OWNER_TYPE_FINANCED}. {@code false} is returned for
+ * the case where the caller is not the device owner, there is no device owner, or the device
+ * owner type is not {@link DevicePolicyManager#DEVICE_OWNER_TYPE_FINANCED}.
+ */
+ private boolean isFinancedDeviceOwner(CallerIdentity caller) {
+ synchronized (getLockObject()) {
+ return isDeviceOwnerLocked(caller) && getDeviceOwnerTypeLocked(
+ mOwners.getDeviceOwnerPackageName()) == DEVICE_OWNER_TYPE_FINANCED;
}
}
- private void verifyDeviceOwnerTypePreconditions(@NonNull ComponentName admin) {
+ private void verifyDeviceOwnerTypePreconditionsLocked(@NonNull ComponentName admin) {
Preconditions.checkState(mOwners.hasDeviceOwner(), "there is no device owner");
Preconditions.checkState(mOwners.getDeviceOwnerComponent().equals(admin),
"admin is not the device owner");
@@ -18172,7 +18380,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Objects.requireNonNull(packageName, "Admin package name must be provided");
final CallerIdentity caller = getCallerIdentity(packageName);
Preconditions.checkCallAuthorization(
- isDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller),
+ isDefaultDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller),
"USB data signaling can only be controlled by a device owner or "
+ "a profile owner on an organization-owned device.");
Preconditions.checkState(canUsbDataSignalingBeDisabled(),
@@ -18213,7 +18421,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
synchronized (getLockObject()) {
// If the caller is an admin, return the policy set by itself. Otherwise
// return the device-wide policy.
- if (isDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller)) {
+ if (isDefaultDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller)) {
return getProfileOwnerOrDeviceOwnerLocked(caller).mUsbDataSignalingEnabled;
} else {
return isUsbDataSignalingEnabledInternalLocked();
@@ -18254,7 +18462,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
public void setMinimumRequiredWifiSecurityLevel(int level) {
final CallerIdentity caller = getCallerIdentity();
Preconditions.checkCallAuthorization(
- isDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller),
+ isDefaultDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller),
"Wi-Fi minimum security level can only be controlled by a device owner or "
+ "a profile owner on an organization-owned device.");
@@ -18284,7 +18492,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
public void setSsidAllowlist(List<String> ssids) {
final CallerIdentity caller = getCallerIdentity();
Preconditions.checkCallAuthorization(
- isDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller),
+ isDefaultDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller),
"SSID allowlist can only be controlled by a device owner or "
+ "a profile owner on an organization-owned device.");
@@ -18306,10 +18514,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
public List<String> getSsidAllowlist() {
final CallerIdentity caller = getCallerIdentity();
Preconditions.checkCallAuthorization(
- isDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller)
- || isSystemUid(caller),
+ isDefaultDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller)
+ || canQueryAdminPolicy(caller),
"SSID allowlist can only be retrieved by a device owner or "
- + "a profile owner on an organization-owned device or a system app.");
+ + "a profile owner on an organization-owned device or "
+ + "an app with the QUERY_ADMIN_POLICY permission.");
synchronized (getLockObject()) {
final ActiveAdmin admin = getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked(
UserHandle.USER_SYSTEM);
@@ -18322,7 +18531,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
public void setSsidDenylist(List<String> ssids) {
final CallerIdentity caller = getCallerIdentity();
Preconditions.checkCallAuthorization(
- isDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller),
+ isDefaultDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller),
"SSID denylist can only be controlled by a device owner or "
+ "a profile owner on an organization-owned device.");
@@ -18344,10 +18553,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
public List<String> getSsidDenylist() {
final CallerIdentity caller = getCallerIdentity();
Preconditions.checkCallAuthorization(
- isDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller)
- || isSystemUid(caller),
+ isDefaultDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller)
+ || canQueryAdminPolicy(caller),
"SSID denylist can only be retrieved by a device owner or "
- + "a profile owner on an organization-owned device or a system app.");
+ + "a profile owner on an organization-owned device or "
+ + "an app with the QUERY_ADMIN_POLICY permission.");
synchronized (getLockObject()) {
final ActiveAdmin admin = getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked(
UserHandle.USER_SYSTEM);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/OverlayPackagesProvider.java b/services/devicepolicy/java/com/android/server/devicepolicy/OverlayPackagesProvider.java
index 685cf0580a48..598f9e88ac6d 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/OverlayPackagesProvider.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/OverlayPackagesProvider.java
@@ -34,6 +34,7 @@ import android.annotation.NonNull;
import android.annotation.UserIdInt;
import android.app.admin.DeviceAdminReceiver;
import android.app.admin.DevicePolicyManager;
+import android.app.role.RoleManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -41,6 +42,7 @@ import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
+import android.os.Binder;
import android.util.ArraySet;
import android.util.IndentingPrintWriter;
import android.view.inputmethod.InputMethodInfo;
@@ -91,6 +93,8 @@ public class OverlayPackagesProvider {
List<InputMethodInfo> getInputMethodListAsUser(@UserIdInt int userId);
String getActiveApexPackageNameContainingPackage(String packageName);
+
+ String getDeviceManagerRoleHolderPackageName(Context context);
}
private static final class DefaultInjector implements Injector {
@@ -104,6 +108,19 @@ public class OverlayPackagesProvider {
public String getActiveApexPackageNameContainingPackage(String packageName) {
return ApexManager.getInstance().getActiveApexPackageNameContainingPackage(packageName);
}
+
+ @Override
+ public String getDeviceManagerRoleHolderPackageName(Context context) {
+ return Binder.withCleanCallingIdentity(() -> {
+ RoleManager roleManager = context.getSystemService(RoleManager.class);
+ List<String> roleHolders =
+ roleManager.getRoleHolders(RoleManager.ROLE_DEVICE_MANAGER);
+ if (roleHolders.isEmpty()) {
+ return null;
+ }
+ return roleHolders.get(0);
+ });
+ }
}
@VisibleForTesting
@@ -142,9 +159,20 @@ public class OverlayPackagesProvider {
nonRequiredApps.addAll(getDisallowedApps(provisioningAction));
nonRequiredApps.removeAll(
getRequiredAppsMainlineModules(nonRequiredApps, provisioningAction));
+ nonRequiredApps.removeAll(getDeviceManagerRoleHolders());
return nonRequiredApps;
}
+ private Set<String> getDeviceManagerRoleHolders() {
+ HashSet<String> result = new HashSet<>();
+ String deviceManagerRoleHolderPackageName =
+ mInjector.getDeviceManagerRoleHolderPackageName(mContext);
+ if (deviceManagerRoleHolderPackageName != null) {
+ result.add(deviceManagerRoleHolderPackageName);
+ }
+ return result;
+ }
+
/**
* Returns a subset of {@code packageNames} whose packages are mainline modules declared as
* required apps via their app metadata.
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
index 3584728a2e62..fe8f2235ed63 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
@@ -637,13 +637,15 @@ class Owners {
}
}
- void setDeviceOwnerType(String packageName, @DeviceOwnerType int deviceOwnerType) {
+ void setDeviceOwnerType(String packageName, @DeviceOwnerType int deviceOwnerType,
+ boolean isAdminTestOnly) {
synchronized (mLock) {
if (!hasDeviceOwner()) {
Slog.e(TAG, "Attempting to set a device owner type when there is no device owner");
return;
- } else if (isDeviceOwnerTypeSetForDeviceOwner(packageName)) {
- Slog.e(TAG, "Device owner type for " + packageName + " has already been set");
+ } else if (!isAdminTestOnly && isDeviceOwnerTypeSetForDeviceOwner(packageName)) {
+ Slog.e(TAG, "Setting the device owner type more than once is only allowed"
+ + " for test only admins");
return;
}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index f77aa371c3d6..c9aeabd17191 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -176,6 +176,7 @@ import com.android.server.power.hint.HintManagerService;
import com.android.server.powerstats.PowerStatsService;
import com.android.server.profcollect.ProfcollectForwardingService;
import com.android.server.recoverysystem.RecoverySystemService;
+import com.android.server.resources.ResourcesManagerService;
import com.android.server.restrictions.RestrictionsManagerService;
import com.android.server.role.RoleServicePlatformHelper;
import com.android.server.rotationresolver.RotationResolverManagerService;
@@ -183,6 +184,7 @@ import com.android.server.security.AttestationVerificationManagerService;
import com.android.server.security.FileIntegrityService;
import com.android.server.security.KeyAttestationApplicationIdProviderService;
import com.android.server.security.KeyChainSystemService;
+import com.android.server.sensorprivacy.SensorPrivacyService;
import com.android.server.sensors.SensorService;
import com.android.server.signedconfig.SignedConfigService;
import com.android.server.soundtrigger.SoundTriggerService;
@@ -264,8 +266,6 @@ public final class SystemServer implements Dumpable {
"/apex/com.android.os.statsd/javalib/service-statsd.jar";
private static final String CONNECTIVITY_SERVICE_APEX_PATH =
"/apex/com.android.tethering/javalib/service-connectivity.jar";
- private static final String NEARBY_SERVICE_APEX_PATH =
- "/apex/com.android.nearby/javalib/service-nearby.jar";
private static final String STATS_COMPANION_LIFECYCLE_CLASS =
"com.android.server.stats.StatsCompanion$Lifecycle";
private static final String STATS_PULL_ATOM_SERVICE_CLASS =
@@ -276,8 +276,6 @@ public final class SystemServer implements Dumpable {
"com.android.server.usb.UsbService$Lifecycle";
private static final String MIDI_SERVICE_CLASS =
"com.android.server.midi.MidiService$Lifecycle";
- private static final String NEARBY_SERVICE_CLASS =
- "com.android.server.nearby.NearbyService";
private static final String WIFI_APEX_SERVICE_JAR_PATH =
"/apex/com.android.wifi/javalib/service-wifi.jar";
private static final String WIFI_SERVICE_CLASS =
@@ -1288,6 +1286,13 @@ public final class SystemServer implements Dumpable {
mSystemServiceManager.startService(new OverlayManagerService(mSystemContext));
t.traceEnd();
+ // Manages Resources packages
+ t.traceBegin("StartResourcesManagerService");
+ ResourcesManagerService resourcesService = new ResourcesManagerService(mSystemContext);
+ resourcesService.setActivityManagerService(mActivityManagerService);
+ mSystemServiceManager.startService(resourcesService);
+ t.traceEnd();
+
t.traceBegin("StartSensorPrivacyService");
mSystemServiceManager.startService(new SensorPrivacyService(mSystemContext));
t.traceEnd();
@@ -1999,16 +2004,6 @@ public final class SystemServer implements Dumpable {
}
t.traceEnd();
- // Start Nearby Service.
- t.traceBegin("StartNearbyService");
- try {
- mSystemServiceManager.startServiceFromJar(NEARBY_SERVICE_CLASS,
- NEARBY_SERVICE_APEX_PATH);
- } catch (Throwable e) {
- reportWtf("starting NearbyService", e);
- }
- t.traceEnd();
-
t.traceBegin("StartConnectivityService");
// This has to be called after NetworkManagementService, NetworkStatsService
// and NetworkPolicyManager because ConnectivityService needs to take these
diff --git a/services/midi/java/com/android/server/midi/MidiService.java b/services/midi/java/com/android/server/midi/MidiService.java
index d56278629bf2..717168f65c7c 100644
--- a/services/midi/java/com/android/server/midi/MidiService.java
+++ b/services/midi/java/com/android/server/midi/MidiService.java
@@ -90,6 +90,11 @@ public class MidiService extends IMidiManager.Stub {
private static final String TAG = "MidiService";
+ // These limits are much higher than any normal app should need.
+ private static final int MAX_DEVICE_SERVERS_PER_UID = 16;
+ private static final int MAX_LISTENERS_PER_CLIENT = 16;
+ private static final int MAX_CONNECTIONS_PER_CLIENT = 64;
+
private final Context mContext;
// list of all our clients, keyed by Binder token
@@ -161,6 +166,10 @@ public class MidiService extends IMidiManager.Stub {
}
public void addListener(IMidiDeviceListener listener) {
+ if (mListeners.size() >= MAX_LISTENERS_PER_CLIENT) {
+ throw new SecurityException(
+ "too many MIDI listeners for UID = " + mUid);
+ }
// Use asBinder() so that we can match it in removeListener().
// The listener proxy objects themselves do not match.
mListeners.put(listener.asBinder(), listener);
@@ -174,6 +183,10 @@ public class MidiService extends IMidiManager.Stub {
}
public void addDeviceConnection(Device device, IMidiDeviceOpenCallback callback) {
+ if (mDeviceConnections.size() >= MAX_CONNECTIONS_PER_CLIENT) {
+ throw new SecurityException(
+ "too many MIDI connections for UID = " + mUid);
+ }
DeviceConnection connection = new DeviceConnection(device, this, callback);
mDeviceConnections.put(connection.getToken(), connection);
device.addDeviceConnection(connection);
@@ -902,6 +915,19 @@ public class MidiService extends IMidiManager.Stub {
IMidiDeviceServer server, ServiceInfo serviceInfo,
boolean isPrivate, int uid, int defaultProtocol) {
+ // Limit the number of devices per app.
+ int deviceCountForApp = 0;
+ for (Device device : mDevicesByInfo.values()) {
+ if (device.getUid() == uid) {
+ deviceCountForApp++;
+ }
+ }
+ if (deviceCountForApp >= MAX_DEVICE_SERVERS_PER_UID) {
+ throw new SecurityException(
+ "too many MIDI devices already created for UID = "
+ + uid);
+ }
+
int id = mNextDeviceId++;
MidiDeviceInfo deviceInfo = new MidiDeviceInfo(type, id, numInputPorts, numOutputPorts,
inputPortNames, outputPortNames, properties, isPrivate,
diff --git a/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt b/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt
index 5220c8fa7a72..c5709fc24b4a 100644
--- a/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt
+++ b/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt
@@ -19,6 +19,7 @@ package com.android.server.pm.test.override
import android.app.PropertyInvalidatedCache
import android.content.ComponentName
import android.content.Context
+import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
import com.android.server.pm.pkg.component.ParsedActivity
import android.os.Binder
@@ -167,12 +168,12 @@ class PackageManagerComponentLabelIconOverrideTest {
@Parameterized.Parameter(0)
lateinit var params: Params
- private lateinit var testHandler: TestHandler
private lateinit var mockPendingBroadcasts: PendingPackageBroadcasts
private lateinit var mockPkg: AndroidPackage
private lateinit var mockPkgSetting: PackageSetting
private lateinit var service: PackageManagerService
+ private val testHandler = TestHandler(null)
private val userId = UserHandle.getCallingUserId()
private val userIdDifferent = userId + 1
@@ -180,16 +181,16 @@ class PackageManagerComponentLabelIconOverrideTest {
fun setUpMocks() {
makeTestData()
- testHandler = TestHandler(null)
+ mockPendingBroadcasts = PendingPackageBroadcasts()
+ service = mockService()
+
+ testHandler.clear()
+
if (params.result is Result.ChangedWithoutNotify) {
// Case where the handler already has a message and so another should not be sent.
// This case will verify that only 1 message exists, which is the one added here.
testHandler.sendEmptyMessage(SEND_PENDING_BROADCAST)
}
-
- mockPendingBroadcasts = PendingPackageBroadcasts()
-
- service = mockService()
}
@Test
@@ -201,19 +202,22 @@ class PackageManagerComponentLabelIconOverrideTest {
when (val result = params.result) {
Result.Changed, Result.ChangedWithoutNotify, Result.NotChanged -> {
runUpdate()
- verify(mockPkgSetting).overrideNonLocalizedLabelAndIcon(params.componentName!!,
- TEST_LABEL, TEST_ICON, userId)
+ mockPkgSetting.getUserStateOrDefault(userId)
+ .getOverrideLabelIconForComponent(params.componentName!!)
+ .let {
+ assertThat(it?.first).isEqualTo(TEST_LABEL)
+ assertThat(it?.second).isEqualTo(TEST_ICON)
+ }
}
is Result.Exception -> {
assertThrows(result.type) { runUpdate() }
- verify(mockPkgSetting, never()).overrideNonLocalizedLabelAndIcon(
- any<ComponentName>(), any(), anyInt(), anyInt())
}
}
}
@After
fun verifyExpectedResult() {
+ assertServiceInitialized() ?: return
if (params.componentName != null) {
val activityInfo = service.getActivityInfo(params.componentName, 0, userId)
if (activityInfo != null) {
@@ -225,11 +229,14 @@ class PackageManagerComponentLabelIconOverrideTest {
@After
fun verifyDifferentUserUnchanged() {
+ assertServiceInitialized() ?: return
when (params.result) {
Result.Changed, Result.ChangedWithoutNotify -> {
- val activityInfo = service.getActivityInfo(params.componentName, 0, userIdDifferent)
- assertThat(activityInfo.nonLocalizedLabel).isEqualTo(DEFAULT_LABEL)
- assertThat(activityInfo.icon).isEqualTo(DEFAULT_ICON)
+ // Suppress so that failures in @After don't override the actual test failure
+ @Suppress("UNNECESSARY_SAFE_CALL")
+ val activityInfo = service?.getActivityInfo(params.componentName, 0, userIdDifferent)
+ assertThat(activityInfo?.nonLocalizedLabel).isEqualTo(DEFAULT_LABEL)
+ assertThat(activityInfo?.icon).isEqualTo(DEFAULT_ICON)
}
Result.NotChanged, is Result.Exception -> {}
}.run { /*exhaust*/ }
@@ -237,6 +244,7 @@ class PackageManagerComponentLabelIconOverrideTest {
@After
fun verifyHandlerHasMessage() {
+ assertServiceInitialized() ?: return
when (params.result) {
is Result.Changed, is Result.ChangedWithoutNotify -> {
assertThat(testHandler.pendingMessages).hasSize(1)
@@ -251,9 +259,10 @@ class PackageManagerComponentLabelIconOverrideTest {
@After
fun verifyPendingBroadcast() {
+ assertServiceInitialized() ?: return
when (params.result) {
is Result.Changed, Result.ChangedWithoutNotify -> {
- assertThat(mockPendingBroadcasts.get(userId, params.pkgName))
+ assertThat(mockPendingBroadcasts.get(userId, params.pkgName) ?: emptyList<String>())
.containsExactly(params.componentName!!.className)
.inOrder()
}
@@ -271,26 +280,27 @@ class PackageManagerComponentLabelIconOverrideTest {
.apply(block)
.hideAsFinal()
- private fun makePkgSetting(pkgName: String) = spy(
+ private fun makePkgSetting(pkgName: String, pkg: AndroidPackage) =
PackageSetting(
pkgName, null, File("/test"),
null, null, null, null, 0, 0, 0, 0, null, null, null, null, null,
UUID.fromString("3f9d52b7-d7b4-406a-a1da-d9f19984c72c")
- )
- ) {
- this.pkgState.isUpdatedSystemApp = params.isUpdatedSystemApp
- }
+ ).apply {
+ if (params.isSystem) {
+ this.flags = this.flags or ApplicationInfo.FLAG_SYSTEM
+ }
+ this.pkgState.isUpdatedSystemApp = params.isUpdatedSystemApp
+ this.pkg = pkg
+ }
private fun makeTestData() {
mockPkg = makePkg(params.pkgName)
- mockPkgSetting = makePkgSetting(params.pkgName)
+ mockPkgSetting = makePkgSetting(params.pkgName, mockPkg)
if (params.result is Result.NotChanged) {
// If verifying no-op behavior, set the current setting to the test values
mockPkgSetting.overrideNonLocalizedLabelAndIcon(params.componentName!!, TEST_LABEL,
TEST_ICON, userId)
- // Then clear the mock because the line above just incremented it
- clearInvocations(mockPkgSetting)
}
}
@@ -303,9 +313,9 @@ class PackageManagerComponentLabelIconOverrideTest {
INVALID_PKG to makePkg(INVALID_PKG) { uid = Binder.getCallingUid() + 1 }
)
val mockedPkgSettings = mutableMapOf(
- VALID_PKG to makePkgSetting(VALID_PKG),
- SHARED_PKG to makePkgSetting(SHARED_PKG),
- INVALID_PKG to makePkgSetting(INVALID_PKG)
+ VALID_PKG to makePkgSetting(VALID_PKG, mockedPkgs[VALID_PKG]!!),
+ SHARED_PKG to makePkgSetting(SHARED_PKG, mockedPkgs[SHARED_PKG]!!),
+ INVALID_PKG to makePkgSetting(INVALID_PKG, mockedPkgs[INVALID_PKG]!!)
)
var mockActivity: ParsedActivity? = null
@@ -330,6 +340,8 @@ class PackageManagerComponentLabelIconOverrideTest {
whenever(this.componentExists(same(it))) { mockActivity != null }
whenever(this.getActivity(same(it))) { mockActivity }
}
+ whenever(this.snapshot()) { this@mockThrowOnUnmocked }
+ whenever(registerObserver(any())).thenCallRealMethod()
}
val mockUserManagerService: UserManagerService = mockThrowOnUnmocked {
val matcher: (Int) -> Boolean = { it == userId || it == userIdDifferent }
@@ -345,6 +357,8 @@ class PackageManagerComponentLabelIconOverrideTest {
val mockAppsFilter: AppsFilter = mockThrowOnUnmocked {
whenever(this.shouldFilterApplication(anyInt(), any<PackageSetting>(),
any<PackageSetting>(), anyInt())) { false }
+ whenever(this.snapshot()) { this@mockThrowOnUnmocked }
+ whenever(registerObserver(any())).thenCallRealMethod()
}
val mockContext: Context = mockThrowOnUnmocked {
whenever(this.getString(
@@ -354,27 +368,33 @@ class PackageManagerComponentLabelIconOverrideTest {
PackageManager.PERMISSION_GRANTED
}
}
- val mockSharedLibrariesImpl: SharedLibrariesImpl = mock()
+ val mockSharedLibrariesImpl: SharedLibrariesImpl = mock {
+ whenever(this.snapshot()) { this@mock }
+ }
val mockInjector: PackageManagerServiceInjector = mock {
whenever(this.lock) { PackageManagerTracedLock() }
whenever(this.componentResolver) { mockComponentResolver }
whenever(this.userManagerService) { mockUserManagerService }
- whenever(this.getUserManagerInternal()) { mockUserManagerInternal }
+ whenever(this.userManagerInternal) { mockUserManagerInternal }
whenever(this.settings) { mockSettings }
whenever(this.getLocalService(ActivityTaskManagerInternal::class.java)) {
mockActivityTaskManager
}
whenever(this.appsFilter) { mockAppsFilter }
whenever(this.context) { mockContext }
- whenever(this.getHandler()) { testHandler }
+ whenever(this.handler) { testHandler }
whenever(this.sharedLibrariesImpl) { mockSharedLibrariesImpl }
}
val testParams = PackageManagerServiceTestParams().apply {
this.pendingPackageBroadcasts = mockPendingBroadcasts
this.resolveComponentName = ComponentName("android", ".Test")
this.packages = ArrayMap<String, AndroidPackage>().apply { putAll(mockedPkgs) }
+ this.instantAppRegistry = mock()
}
return PackageManagerService(mockInjector, testParams)
}
+
+ // If service isn't initialized, then test setup failed and @Afters should be skipped
+ private fun assertServiceInitialized() = Unit.takeIf { ::service.isInitialized }
}
diff --git a/services/tests/mockingservicestests/assets/SensorPrivacyServiceMockingTest/persisted_file1.xml b/services/tests/mockingservicestests/assets/SensorPrivacyServiceMockingTest/persisted_file1.xml
index a4de08a85487..4e0bb361105e 100644
--- a/services/tests/mockingservicestests/assets/SensorPrivacyServiceMockingTest/persisted_file1.xml
+++ b/services/tests/mockingservicestests/assets/SensorPrivacyServiceMockingTest/persisted_file1.xml
@@ -1,7 +1,7 @@
<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>
<sensor-privacy persistence-version="1" version="1">
<user id="0" enabled="false">
- <individual-sensor-privacy sensor="1" enabled="true" />
- <individual-sensor-privacy sensor="2" enabled="true" />
+ <individual-sensor-privacy sensor="1" enabled="true" last-change="100" />
+ <individual-sensor-privacy sensor="2" enabled="true" last-change="100" />
</user>
</sensor-privacy>
diff --git a/services/tests/mockingservicestests/assets/SensorPrivacyServiceMockingTest/persisted_file7.xml b/services/tests/mockingservicestests/assets/SensorPrivacyServiceMockingTest/persisted_file7.xml
new file mode 100644
index 000000000000..2d192dbe8882
--- /dev/null
+++ b/services/tests/mockingservicestests/assets/SensorPrivacyServiceMockingTest/persisted_file7.xml
@@ -0,0 +1,5 @@
+<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>
+<sensor-privacy persistence-version="2" version="2">
+ <sensor-state toggle-type="1" user-id="0" sensor="1" state-type="1" last-change="123" />
+ <sensor-state toggle-type="1" user-id="0" sensor="2" state-type="2" last-change="123" />
+</sensor-privacy> \ No newline at end of file
diff --git a/services/tests/mockingservicestests/assets/SensorPrivacyServiceMockingTest/persisted_file8.xml b/services/tests/mockingservicestests/assets/SensorPrivacyServiceMockingTest/persisted_file8.xml
new file mode 100644
index 000000000000..7bb38b4e79f8
--- /dev/null
+++ b/services/tests/mockingservicestests/assets/SensorPrivacyServiceMockingTest/persisted_file8.xml
@@ -0,0 +1,5 @@
+<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>
+<sensor-privacy persistence-version="2" version="2">
+ <sensor-state toggle-type="2" user-id="0" sensor="1" state-type="1" last-change="1234" />
+ <sensor-state toggle-type="2" user-id="0" sensor="2" state-type="2" last-change="1234" />
+</sensor-privacy> \ No newline at end of file
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java b/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java
index 303f95565c76..bf46f555004c 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java
@@ -97,7 +97,7 @@ public final class CachedAppOptimizerTest {
@Before
public void setUp() {
- System.loadLibrary("activitymanagermockingservicestestjni");
+ System.loadLibrary("mockingservicestestjni");
mHandlerThread = new HandlerThread("");
mHandlerThread.start();
mHandler = new Handler(mHandlerThread.getLooper());
diff --git a/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java b/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java
index eed2d4251cd7..44b81d4d4100 100644
--- a/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java
@@ -23,20 +23,27 @@ import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.Manifest;
import android.app.GameManager;
import android.app.GameModeInfo;
+import android.app.GameState;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
+import android.hardware.power.Mode;
import android.os.Bundle;
+import android.os.PowerManagerInternal;
import android.os.test.TestLooper;
import android.platform.test.annotations.Presubmit;
import android.provider.DeviceConfig;
@@ -46,6 +53,7 @@ import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.server.LocalServices;
import com.android.server.SystemService;
import org.junit.After;
@@ -76,6 +84,8 @@ public class GameManagerServiceTests {
private TestLooper mTestLooper;
@Mock
private PackageManager mMockPackageManager;
+ @Mock
+ private PowerManagerInternal mMockPowerManager;
// Stolen from ConnectivityServiceTest.MockContext
class MockContext extends ContextWrapper {
@@ -158,10 +168,12 @@ public class GameManagerServiceTests {
.thenReturn(packages);
when(mMockPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), anyInt()))
.thenReturn(applicationInfo);
+ LocalServices.addService(PowerManagerInternal.class, mMockPowerManager);
}
@After
public void tearDown() throws Exception {
+ LocalServices.removeServiceForTest(PowerManagerInternal.class);
GameManagerService gameManagerService = new GameManagerService(mMockContext);
gameManagerService.disableCompatScale(mPackageName);
if (mMockingSession != null) {
@@ -1052,4 +1064,41 @@ public class GameManagerServiceTests {
assertEquals(GameManager.GAME_MODE_UNSUPPORTED, gameModeInfo.getActiveGameMode());
assertEquals(0, gameModeInfo.getAvailableGameModes().length);
}
+
+ @Test
+ public void testGameStateLoadingRequiresPerformanceMode() {
+ mockDeviceConfigNone();
+ mockModifyGameModeGranted();
+ GameManagerService gameManagerService =
+ new GameManagerService(mMockContext, mTestLooper.getLooper());
+ startUser(gameManagerService, USER_ID_1);
+ GameState gameState = new GameState(true, GameState.MODE_NONE);
+ gameManagerService.setGameState(mPackageName, gameState, USER_ID_1);
+ mTestLooper.dispatchAll();
+ verify(mMockPowerManager, never()).setPowerMode(anyInt(), anyBoolean());
+ }
+
+ private void setGameState(boolean isLoading) {
+ mockDeviceConfigNone();
+ mockModifyGameModeGranted();
+ GameManagerService gameManagerService =
+ new GameManagerService(mMockContext, mTestLooper.getLooper());
+ startUser(gameManagerService, USER_ID_1);
+ gameManagerService.setGameMode(
+ mPackageName, GameManager.GAME_MODE_PERFORMANCE, USER_ID_1);
+ GameState gameState = new GameState(isLoading, GameState.MODE_NONE);
+ gameManagerService.setGameState(mPackageName, gameState, USER_ID_1);
+ mTestLooper.dispatchAll();
+ verify(mMockPowerManager, times(1)).setPowerMode(Mode.GAME_LOADING, isLoading);
+ }
+
+ @Test
+ public void testSetGameStateLoading() {
+ setGameState(true);
+ }
+
+ @Test
+ public void testSetGameStateNotLoading() {
+ setGameState(false);
+ }
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java b/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java
index d5e4710095f2..317a51b3fe2d 100644
--- a/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java
@@ -26,6 +26,7 @@ import static com.google.common.collect.Iterables.getOnlyElement;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -33,6 +34,7 @@ import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.verifyZeroInteractions;
+import android.Manifest;
import android.annotation.Nullable;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.ActivityTaskManager;
@@ -83,6 +85,7 @@ import org.mockito.quality.Strictness;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.Objects;
/**
@@ -121,7 +124,7 @@ public final class GameServiceProviderInstanceImplTest {
private WindowManagerInternal mMockWindowManagerInternal;
@Mock
private IActivityManager mMockActivityManager;
- private FakeContext mFakeContext;
+ private MockContext mMockContext;
private FakeGameClassifier mFakeGameClassifier;
private FakeGameService mFakeGameService;
private FakeServiceConnector<IGameService> mFakeGameServiceConnector;
@@ -140,7 +143,7 @@ public final class GameServiceProviderInstanceImplTest {
.strictness(Strictness.LENIENT)
.startMocking();
- mFakeContext = new FakeContext(InstrumentationRegistry.getInstrumentation().getContext());
+ mMockContext = new MockContext(InstrumentationRegistry.getInstrumentation().getContext());
mFakeGameClassifier = new FakeGameClassifier();
mFakeGameClassifier.recordGamePackage(GAME_A_PACKAGE);
@@ -169,7 +172,7 @@ public final class GameServiceProviderInstanceImplTest {
mGameServiceProviderInstance = new GameServiceProviderInstanceImpl(
new UserHandle(USER_ID),
ConcurrentUtils.DIRECT_EXECUTOR,
- mFakeContext,
+ mMockContext,
mFakeGameClassifier,
mMockActivityManager,
mMockActivityTaskManager,
@@ -301,6 +304,7 @@ public final class GameServiceProviderInstanceImplTest {
throws Exception {
mGameServiceProviderInstance.start();
+ mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY);
mFakeGameService.requestCreateGameSession(10);
assertThat(mFakeGameSessionService.getCapturedCreateInvocations()).isEmpty();
@@ -322,6 +326,7 @@ public final class GameServiceProviderInstanceImplTest {
mGameServiceProviderInstance.start();
startTask(10, GAME_A_MAIN_ACTIVITY);
+ mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY);
mFakeGameService.requestCreateGameSession(10);
FakeGameSessionService.CapturedCreateInvocation capturedCreateInvocation =
@@ -336,6 +341,7 @@ public final class GameServiceProviderInstanceImplTest {
mGameServiceProviderInstance.start();
dispatchTaskCreated(10, GAME_A_MAIN_ACTIVITY);
+ mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY);
mFakeGameService.requestCreateGameSession(10);
assertThat(mFakeGameSessionService.getCapturedCreateInvocations()).isEmpty();
@@ -345,6 +351,7 @@ public final class GameServiceProviderInstanceImplTest {
public void gameTaskStartedAndSessionRequested_createsGameSession() throws Exception {
mGameServiceProviderInstance.start();
startTask(10, GAME_A_MAIN_ACTIVITY);
+ mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY);
mFakeGameService.requestCreateGameSession(10);
FakeGameSession gameSession10 = new FakeGameSession();
@@ -362,7 +369,9 @@ public final class GameServiceProviderInstanceImplTest {
mGameServiceProviderInstance.start();
startTask(10, GAME_A_MAIN_ACTIVITY);
+ mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY);
mFakeGameService.requestCreateGameSession(10);
+ mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY);
mFakeGameService.requestCreateGameSession(10);
CreateGameSessionRequest expectedCreateGameSessionRequest = new CreateGameSessionRequest(10,
@@ -376,6 +385,7 @@ public final class GameServiceProviderInstanceImplTest {
public void gameSessionSuccessfullyCreated_createsTaskOverlay() throws Exception {
mGameServiceProviderInstance.start();
startTask(10, GAME_A_MAIN_ACTIVITY);
+ mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY);
mFakeGameService.requestCreateGameSession(10);
FakeGameSession gameSession10 = new FakeGameSession();
@@ -391,6 +401,7 @@ public final class GameServiceProviderInstanceImplTest {
public void gameTaskFocused_propagatedToGameSession() throws Exception {
mGameServiceProviderInstance.start();
startTask(10, GAME_A_MAIN_ACTIVITY);
+ mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY);
mFakeGameService.requestCreateGameSession(10);
FakeGameSession gameSession10 = new FakeGameSession();
@@ -416,6 +427,7 @@ public final class GameServiceProviderInstanceImplTest {
mGameServiceProviderInstance.start();
startTask(10, GAME_A_MAIN_ACTIVITY);
+ mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY);
mFakeGameService.requestCreateGameSession(10);
FakeGameSession gameSession10 = new FakeGameSession();
@@ -432,6 +444,7 @@ public final class GameServiceProviderInstanceImplTest {
mGameServiceProviderInstance.start();
startTask(10, GAME_A_MAIN_ACTIVITY);
+ mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY);
mFakeGameService.requestCreateGameSession(10);
dispatchTaskRemoved(10);
@@ -449,6 +462,7 @@ public final class GameServiceProviderInstanceImplTest {
mGameServiceProviderInstance.start();
startTask(10, GAME_A_MAIN_ACTIVITY);
+ mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY);
mFakeGameService.requestCreateGameSession(10);
FakeGameSession gameSession10 = new FakeGameSession();
@@ -466,6 +480,7 @@ public final class GameServiceProviderInstanceImplTest {
mGameServiceProviderInstance.start();
startTask(10, GAME_A_MAIN_ACTIVITY);
+ mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY);
mFakeGameService.requestCreateGameSession(10);
FakeGameSession gameSession10 = new FakeGameSession();
@@ -486,6 +501,7 @@ public final class GameServiceProviderInstanceImplTest {
mGameServiceProviderInstance.start();
startTask(10, GAME_A_MAIN_ACTIVITY);
+ mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY);
mFakeGameService.requestCreateGameSession(10);
FakeGameSession gameSession10 = new FakeGameSession();
@@ -513,6 +529,7 @@ public final class GameServiceProviderInstanceImplTest {
startTask(10, GAME_A_MAIN_ACTIVITY);
startTask(11, GAME_A_MAIN_ACTIVITY);
+ mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY);
mFakeGameService.requestCreateGameSession(10);
FakeGameSession gameSession10 = new FakeGameSession();
@@ -530,6 +547,7 @@ public final class GameServiceProviderInstanceImplTest {
mGameServiceProviderInstance.start();
startTask(10, GAME_A_MAIN_ACTIVITY);
+ mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY);
mFakeGameService.requestCreateGameSession(10);
FakeGameSession gameSession10 = new FakeGameSession();
@@ -557,6 +575,7 @@ public final class GameServiceProviderInstanceImplTest {
mGameServiceProviderInstance.start();
startTask(10, GAME_A_MAIN_ACTIVITY);
+ mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY);
mFakeGameService.requestCreateGameSession(10);
FakeGameSession gameSession10 = new FakeGameSession();
@@ -586,6 +605,7 @@ public final class GameServiceProviderInstanceImplTest {
mGameServiceProviderInstance.start();
startTask(10, GAME_A_MAIN_ACTIVITY);
+ mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY);
mFakeGameService.requestCreateGameSession(10);
FakeGameSession gameSession10 = new FakeGameSession();
@@ -619,10 +639,19 @@ public final class GameServiceProviderInstanceImplTest {
}
@Test
+ public void createGameSession_failurePermissionDenied() throws Exception {
+ mGameServiceProviderInstance.start();
+ startTask(10, GAME_A_MAIN_ACTIVITY);
+ mockPermissionDenied(Manifest.permission.MANAGE_GAME_ACTIVITY);
+ assertThrows(SecurityException.class, () -> mFakeGameService.requestCreateGameSession(10));
+ }
+
+ @Test
public void stop_severalActiveGameSessions_destroysGameSessionsAndUnbinds() throws Exception {
mGameServiceProviderInstance.start();
startTask(10, GAME_A_MAIN_ACTIVITY);
+ mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY);
mFakeGameService.requestCreateGameSession(10);
FakeGameSession gameSession10 = new FakeGameSession();
@@ -650,6 +679,7 @@ public final class GameServiceProviderInstanceImplTest {
public void takeScreenshot_failureNoBitmapCaptured() throws Exception {
mGameServiceProviderInstance.start();
startTask(10, GAME_A_MAIN_ACTIVITY);
+ mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY);
mFakeGameService.requestCreateGameSession(10);
IGameSessionController gameSessionController = getOnlyElement(
@@ -669,6 +699,7 @@ public final class GameServiceProviderInstanceImplTest {
mGameServiceProviderInstance.start();
startTask(10, GAME_A_MAIN_ACTIVITY);
+ mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY);
mFakeGameService.requestCreateGameSession(10);
IGameSessionController gameSessionController = getOnlyElement(
@@ -683,6 +714,7 @@ public final class GameServiceProviderInstanceImplTest {
@Test
public void restartGame_taskIdAssociatedWithGame_restartsTargetGame() throws Exception {
+ mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY);
Intent launchIntent = new Intent("com.test.ACTION_LAUNCH_GAME_PACKAGE")
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
when(mMockPackageManager.getLaunchIntentForPackage(GAME_A_PACKAGE))
@@ -691,6 +723,7 @@ public final class GameServiceProviderInstanceImplTest {
mGameServiceProviderInstance.start();
startTask(10, GAME_A_MAIN_ACTIVITY);
+ mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY);
mFakeGameService.requestCreateGameSession(10);
FakeGameSession gameSession10 = new FakeGameSession();
@@ -710,14 +743,16 @@ public final class GameServiceProviderInstanceImplTest {
.mGameSessionController.restartGame(10);
verify(mMockActivityManager).forceStopPackage(GAME_A_PACKAGE, UserHandle.USER_CURRENT);
- assertThat(mFakeContext.getLastStartedIntent()).isEqualTo(launchIntent);
+ assertThat(mMockContext.getLastStartedIntent()).isEqualTo(launchIntent);
}
@Test
public void restartGame_taskIdNotAssociatedWithGame_noOp() throws Exception {
+ mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY);
mGameServiceProviderInstance.start();
startTask(10, GAME_A_MAIN_ACTIVITY);
+ mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY);
mFakeGameService.requestCreateGameSession(10);
FakeGameSession gameSession10 = new FakeGameSession();
@@ -730,7 +765,20 @@ public final class GameServiceProviderInstanceImplTest {
.mGameSessionController.restartGame(11);
verifyZeroInteractions(mMockActivityManager);
- assertThat(mFakeContext.getLastStartedIntent()).isNull();
+ assertThat(mMockContext.getLastStartedIntent()).isNull();
+ }
+
+ @Test
+ public void restartGame_failurePermissionDenied() throws Exception {
+ mGameServiceProviderInstance.start();
+ startTask(10, GAME_A_MAIN_ACTIVITY);
+ mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY);
+ mFakeGameService.requestCreateGameSession(10);
+ IGameSessionController gameSessionController = Objects.requireNonNull(getOnlyElement(
+ mFakeGameSessionService.getCapturedCreateInvocations())).mGameSessionController;
+ mockPermissionDenied(Manifest.permission.MANAGE_GAME_ACTIVITY);
+ assertThrows(SecurityException.class,
+ () -> gameSessionController.restartGame(10));
}
private void startTask(int taskId, ComponentName componentName) {
@@ -774,6 +822,14 @@ public final class GameServiceProviderInstanceImplTest {
}
}
+ private void mockPermissionGranted(String permission) {
+ mMockContext.setPermission(permission, PackageManager.PERMISSION_GRANTED);
+ }
+
+ private void mockPermissionDenied(String permission) {
+ mMockContext.setPermission(permission, PackageManager.PERMISSION_DENIED);
+ }
+
static final class FakeGameService extends IGameService.Stub {
private IGameServiceController mGameServiceController;
@@ -900,13 +956,28 @@ public final class GameServiceProviderInstanceImplTest {
}
}
- private final class FakeContext extends ContextWrapper {
+ private final class MockContext extends ContextWrapper {
private Intent mLastStartedIntent;
+ // Map of permission name -> PermissionManager.Permission_{GRANTED|DENIED} constant
+ private final HashMap<String, Integer> mMockedPermissions = new HashMap<>();
- FakeContext(Context base) {
+ MockContext(Context base) {
super(base);
}
+ /**
+ * Mock checks for the specified permission, and have them behave as per {@code granted}.
+ *
+ * <p>Passing null reverts to default behavior, which does a real permission check on the
+ * test package.
+ *
+ * @param granted One of {@link PackageManager#PERMISSION_GRANTED} or
+ * {@link PackageManager#PERMISSION_DENIED}.
+ */
+ public void setPermission(String permission, Integer granted) {
+ mMockedPermissions.put(permission, granted);
+ }
+
@Override
public PackageManager getPackageManager() {
return mMockPackageManager;
@@ -919,7 +990,15 @@ public final class GameServiceProviderInstanceImplTest {
@Override
public void enforceCallingPermission(String permission, @Nullable String message) {
- // Do nothing.
+ final Integer granted = mMockedPermissions.get(permission);
+ if (granted == null) {
+ super.enforceCallingOrSelfPermission(permission, message);
+ return;
+ }
+
+ if (!granted.equals(PackageManager.PERMISSION_GRANTED)) {
+ throw new SecurityException("[Test] permission denied: " + permission);
+ }
}
Intent getLastStartedIntent() {
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
index 1e0f30e1a319..6ae003163e4b 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
@@ -169,24 +169,6 @@ class MockSystem(withSession: (StaticMockitoSessionBuilder) -> Unit = {}) {
mSettingsMap.putAll(mPreExistingSettings)
!mPreExistingSettings.isEmpty()
}
- whenever(mocks.settings.setPackageStoppedStateLPw(any(), any(), anyBoolean(), anyInt())) {
- val pm: PackageManagerService = getArgument(0)
- val pkgSetting = mSettingsMap[getArgument(1)]!!
- val stopped: Boolean = getArgument(2)
- val userId: Int = getArgument(3)
- return@whenever if (pkgSetting.getStopped(userId) != stopped) {
- pkgSetting.setStopped(stopped, userId)
- if (pkgSetting.getNotLaunched(userId)) {
- pkgSetting.installSource.installerPackageName?.let {
- pm.notifyFirstLaunch(pkgSetting.packageName, it, userId)
- }
- pkgSetting.setNotLaunched(false, userId)
- }
- true
- } else {
- false
- }
- }
}
/** Collection of mocks used for PackageManagerService tests. */
@@ -210,7 +192,9 @@ class MockSystem(withSession: (StaticMockitoSessionBuilder) -> Unit = {}) {
val packageParser: PackageParser2 = mock()
val keySetManagerService: KeySetManagerService = mock()
val packageAbiHelper: PackageAbiHelper = mock()
- val appsFilter: AppsFilter = mock()
+ val appsFilter: AppsFilter = mock {
+ whenever(snapshot()) { this@mock }
+ }
val dexManager: DexManager = mock()
val installer: Installer = mock()
val displayMetrics: DisplayMetrics = mock()
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/PackageFreezerTest.kt b/services/tests/mockingservicestests/src/com/android/server/pm/PackageFreezerTest.kt
index edbfecc41b04..ccfeb4c9df51 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/PackageFreezerTest.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/PackageFreezerTest.kt
@@ -59,8 +59,7 @@ class PackageFreezerTest {
false /*isEngBuild*/,
false /*isUserDebugBuild*/,
Build.VERSION_CODES.CUR_DEVELOPMENT,
- Build.VERSION.INCREMENTAL,
- false /*snapshotEnabled*/)
+ Build.VERSION.INCREMENTAL)
rule.system().validateFinalState()
return pms
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/PackageManagerServiceBootTest.kt b/services/tests/mockingservicestests/src/com/android/server/pm/PackageManagerServiceBootTest.kt
index 0820a3c24c7e..bbca121b7fdf 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/PackageManagerServiceBootTest.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/PackageManagerServiceBootTest.kt
@@ -63,8 +63,7 @@ class PackageManagerServiceBootTest {
false /*isEngBuild*/,
false /*isUserDebugBuild*/,
Build.VERSION_CODES.CUR_DEVELOPMENT,
- Build.VERSION.INCREMENTAL,
- false /*snapshotEnabled*/)
+ Build.VERSION.INCREMENTAL)
}
@Test
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/PackageManagerServiceHibernationTests.kt b/services/tests/mockingservicestests/src/com/android/server/pm/PackageManagerServiceHibernationTests.kt
index cfc81e684a7f..a6c7bfb456be 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/PackageManagerServiceHibernationTests.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/PackageManagerServiceHibernationTests.kt
@@ -170,7 +170,6 @@ class PackageManagerServiceHibernationTests {
false /*isEngBuild*/,
false /*isUserDebugBuild*/,
Build.VERSION_CODES.CUR_DEVELOPMENT,
- Build.VERSION.INCREMENTAL,
- false /*snapshotEnabled*/)
+ Build.VERSION.INCREMENTAL)
}
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/SharedLibrariesImplTest.kt b/services/tests/mockingservicestests/src/com/android/server/pm/SharedLibrariesImplTest.kt
index 2735e3d21ef7..b89f36fd1c76 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/SharedLibrariesImplTest.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/SharedLibrariesImplTest.kt
@@ -24,6 +24,7 @@ import android.os.Build
import android.os.storage.StorageManager
import android.util.ArrayMap
import android.util.PackageUtils
+import com.android.internal.util.FunctionalUtils
import com.android.server.SystemConfig.SharedLibraryEntry
import com.android.server.compat.PlatformCompat
import com.android.server.extendedtestutils.wheneverStatic
@@ -34,6 +35,7 @@ import com.android.server.pm.parsing.pkg.ParsedPackage
import com.android.server.testutils.any
import com.android.server.testutils.eq
import com.android.server.testutils.mock
+import com.android.server.testutils.mockThrowOnUnmocked
import com.android.server.testutils.nullable
import com.android.server.testutils.spy
import com.android.server.testutils.whenever
@@ -41,11 +43,14 @@ import com.android.server.utils.WatchedLongSparseArray
import com.google.common.truth.Truth.assertThat
import libcore.util.HexEncoding
import org.junit.Before
+import org.junit.Ignore
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
import org.mockito.Mock
+import org.mockito.Mockito.anyLong
+import org.mockito.Mockito.anyString
import org.mockito.Mockito.doAnswer
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
@@ -96,10 +101,14 @@ class SharedLibrariesImplTest {
mRule.system().stageNominalSystemState()
addExistingPackages()
- val testParams = PackageManagerServiceTestParams().apply {
- packages = mExistingPackages
- }
- mPms = spy(PackageManagerService(mRule.mocks().injector, testParams))
+ mPms = spy(PackageManagerService(mRule.mocks().injector,
+ false /*coreOnly*/,
+ false /*factoryTest*/,
+ MockSystem.DEFAULT_VERSION_INFO.fingerprint,
+ false /*isEngBuild*/,
+ false /*isUserDebugBuild*/,
+ Build.VERSION_CODES.CUR_DEVELOPMENT,
+ Build.VERSION.INCREMENTAL))
mSettings = mRule.mocks().injector.settings
mSharedLibrariesImpl = SharedLibrariesImpl(mPms, mRule.mocks().injector)
mSharedLibrariesImpl.setDeletePackageHelper(mDeletePackageHelper)
@@ -109,7 +118,19 @@ class SharedLibrariesImplTest {
whenever(mRule.mocks().injector.getSystemService(StorageManager::class.java))
.thenReturn(mStorageManager)
whenever(mStorageManager.findPathForUuid(nullable())).thenReturn(mFile)
- doAnswer { it.arguments[0] }.`when`(mPms).resolveInternalPackageNameLPr(any(), any())
+ doAnswer { it.arguments[0] }.`when`(mPms).resolveInternalPackageName(any(), any())
+ doAnswer {
+ it.getArgument<FunctionalUtils.ThrowingConsumer<Computer>>(0).acceptOrThrow(
+ mockThrowOnUnmocked {
+ whenever(sharedLibraries) { mSharedLibrariesImpl.sharedLibraries }
+ whenever(resolveInternalPackageName(anyString(), anyLong())) {
+ mPms.resolveInternalPackageName(getArgument(0), getArgument(1))
+ }
+ whenever(getPackageStateInternal(anyString())) {
+ mPms.getPackageStateInternal(getArgument(0))
+ }
+ })
+ }.`when`(mPms).executeWithConsistentComputer(any())
whenever(mDeletePackageHelper.deletePackageX(any(), any(), any(), any(), any()))
.thenReturn(PackageManager.DELETE_SUCCEEDED)
whenever(mRule.mocks().injector.compatibility).thenReturn(mPlatformCompat)
@@ -232,6 +253,7 @@ class SharedLibrariesImplTest {
assertThat(testPackageSetting.usesLibraryFiles).contains(builtinLibPath(BUILTIN_LIB_NAME))
}
+ @Ignore("b/216603387")
@Test
fun updateSharedLibraries_withStaticLibPackage() {
val testPackageSetting = mExistingSettings[STATIC_LIB_PACKAGE_NAME]!!
@@ -244,6 +266,7 @@ class SharedLibrariesImplTest {
assertThat(testPackageSetting.usesLibraryFiles).contains(apkPath(DYNAMIC_LIB_PACKAGE_NAME))
}
+ @Ignore("b/216603387")
@Test
fun updateSharedLibraries_withConsumerPackage() {
val testPackageSetting = mExistingSettings[CONSUMER_PACKAGE_NAME]!!
@@ -257,6 +280,7 @@ class SharedLibrariesImplTest {
assertThat(testPackageSetting.usesLibraryFiles).contains(apkPath(STATIC_LIB_PACKAGE_NAME))
}
+ @Ignore("b/216603387")
@Test
fun updateAllSharedLibraries() {
mExistingSettings.forEach {
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java
index bdfdf7723c02..64657a91224a 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java
@@ -50,7 +50,7 @@ import android.util.IntArray;
import android.util.SparseArray;
import com.android.dx.mockito.inline.extended.ExtendedMockito;
-import com.android.internal.content.PackageHelper;
+import com.android.internal.content.InstallLocationUtils;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.Preconditions;
@@ -101,12 +101,12 @@ public class StagingManagerTest {
mMockitoSession = ExtendedMockito.mockitoSession()
.strictness(Strictness.LENIENT)
.mockStatic(SystemProperties.class)
- .mockStatic(PackageHelper.class)
+ .mockStatic(InstallLocationUtils.class)
.startMocking();
when(mStorageManager.supportsCheckpoint()).thenReturn(true);
when(mStorageManager.needsCheckpoint()).thenReturn(true);
- when(PackageHelper.getStorageManager()).thenReturn(mStorageManager);
+ when(InstallLocationUtils.getStorageManager()).thenReturn(mStorageManager);
when(SystemProperties.get(eq("ro.apex.updatable"))).thenReturn("true");
when(SystemProperties.get(eq("ro.apex.updatable"), anyString())).thenReturn("true");
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt b/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt
index fe7e2d9eb047..ac406b588d38 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt
@@ -128,7 +128,7 @@ class SuspendPackageHelperTest {
null /* dialogInfo */, DEVICE_OWNER_PACKAGE, TEST_USER_ID, deviceOwnerUid)
testHandler.flush()
- verify(pms).scheduleWritePackageRestrictionsLocked(eq(TEST_USER_ID))
+ verify(pms).scheduleWritePackageRestrictions(eq(TEST_USER_ID))
verify(broadcastHelper).sendPackageBroadcast(eq(Intent.ACTION_PACKAGES_SUSPENDED),
nullable(), bundleCaptor.capture(), anyInt(), nullable(), nullable(), any(),
nullable(), nullable(), nullable())
@@ -214,7 +214,7 @@ class SuspendPackageHelperTest {
null /* dialogInfo */, DEVICE_OWNER_PACKAGE, TEST_USER_ID, deviceOwnerUid)
testHandler.flush()
- verify(pms, times(2)).scheduleWritePackageRestrictionsLocked(eq(TEST_USER_ID))
+ verify(pms, times(2)).scheduleWritePackageRestrictions(eq(TEST_USER_ID))
verify(broadcastHelper).sendPackageBroadcast(eq(Intent.ACTION_PACKAGES_UNSUSPENDED),
nullable(), bundleCaptor.capture(), anyInt(), nullable(), nullable(), any(),
nullable(), nullable(), nullable())
@@ -295,7 +295,7 @@ class SuspendPackageHelperTest {
{ suspendingPackage -> suspendingPackage == DEVICE_OWNER_PACKAGE }, TEST_USER_ID)
testHandler.flush()
- verify(pms, times(2)).scheduleWritePackageRestrictionsLocked(eq(TEST_USER_ID))
+ verify(pms, times(2)).scheduleWritePackageRestrictions(eq(TEST_USER_ID))
verify(broadcastHelper).sendPackageBroadcast(eq(Intent.ACTION_PACKAGES_UNSUSPENDED),
nullable(), bundleCaptor.capture(), anyInt(), nullable(), nullable(), any(),
nullable(), nullable(), nullable())
@@ -499,8 +499,7 @@ class SuspendPackageHelperTest {
false /*isEngBuild*/,
false /*isUserDebugBuild*/,
Build.VERSION_CODES.CUR_DEVELOPMENT,
- Build.VERSION.INCREMENTAL,
- false /*snapshotEnabled*/)
+ Build.VERSION.INCREMENTAL)
rule.system().validateFinalState()
return pms
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/sensorprivacy/SensorPrivacyServiceMockingTest.java b/services/tests/mockingservicestests/src/com/android/server/sensorprivacy/SensorPrivacyServiceMockingTest.java
index 38f01b5acc0c..64e86133919e 100644
--- a/services/tests/mockingservicestests/src/com/android/server/sensorprivacy/SensorPrivacyServiceMockingTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/sensorprivacy/SensorPrivacyServiceMockingTest.java
@@ -16,43 +16,47 @@
package com.android.server.sensorprivacy;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
+import static android.hardware.SensorPrivacyManager.Sensors.CAMERA;
+import static android.hardware.SensorPrivacyManager.Sensors.MICROPHONE;
+import static android.hardware.SensorPrivacyManager.ToggleTypes.HARDWARE;
+import static android.hardware.SensorPrivacyManager.ToggleTypes.SOFTWARE;
+
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
-import android.app.ActivityManager;
-import android.app.ActivityTaskManager;
-import android.app.AppOpsManager;
-import android.app.AppOpsManagerInternal;
import android.content.Context;
-import android.content.pm.UserInfo;
+import android.hardware.SensorPrivacyManager;
import android.os.Environment;
-import android.telephony.TelephonyManager;
+import android.os.Handler;
import android.testing.AndroidTestingRunner;
import androidx.test.platform.app.InstrumentationRegistry;
import com.android.dx.mockito.inline.extended.ExtendedMockito;
import com.android.server.LocalServices;
-import com.android.server.SensorPrivacyService;
-import com.android.server.SystemService;
import com.android.server.pm.UserManagerInternal;
-import org.junit.Assert;
+import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.Mock;
+import org.mockito.ArgumentCaptor;
import org.mockito.MockitoSession;
import org.mockito.quality.Strictness;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
-import java.util.concurrent.CompletableFuture;
+import java.nio.file.StandardCopyOption;
@RunWith(AndroidTestingRunner.class)
public class SensorPrivacyServiceMockingTest {
@@ -71,6 +75,10 @@ public class SensorPrivacyServiceMockingTest {
String.format(PERSISTENCE_FILE_PATHS_TEMPLATE, 5);
public static final String PERSISTENCE_FILE6 =
String.format(PERSISTENCE_FILE_PATHS_TEMPLATE, 6);
+ public static final String PERSISTENCE_FILE7 =
+ String.format(PERSISTENCE_FILE_PATHS_TEMPLATE, 7);
+ public static final String PERSISTENCE_FILE8 =
+ String.format(PERSISTENCE_FILE_PATHS_TEMPLATE, 8);
public static final String PERSISTENCE_FILE_MIC_MUTE_CAM_MUTE =
"SensorPrivacyServiceMockingTest/persisted_file_micMute_camMute.xml";
@@ -81,176 +89,281 @@ public class SensorPrivacyServiceMockingTest {
public static final String PERSISTENCE_FILE_MIC_UNMUTE_CAM_UNMUTE =
"SensorPrivacyServiceMockingTest/persisted_file_micUnmute_camUnmute.xml";
- private Context mContext;
- @Mock
- private AppOpsManager mMockedAppOpsManager;
- @Mock
- private AppOpsManagerInternal mMockedAppOpsManagerInternal;
- @Mock
- private UserManagerInternal mMockedUserManagerInternal;
- @Mock
- private ActivityManager mMockedActivityManager;
- @Mock
- private ActivityTaskManager mMockedActivityTaskManager;
- @Mock
- private TelephonyManager mMockedTelephonyManager;
+ Context mContext = InstrumentationRegistry.getInstrumentation().getContext();
+ String mDataDir = mContext.getApplicationInfo().dataDir;
+
+ @Before
+ public void setUp() {
+ new File(mDataDir, "sensor_privacy.xml").delete();
+ new File(mDataDir, "sensor_privacy_impl.xml").delete();
+ }
+
+ @Test
+ public void testMigration1() throws IOException {
+ PersistedState ps = migrateFromFile(PERSISTENCE_FILE1);
+
+ assertTrue(ps.getState(SOFTWARE, 0, MICROPHONE).isEnabled());
+ assertTrue(ps.getState(SOFTWARE, 0, CAMERA).isEnabled());
+
+ assertNull(ps.getState(SOFTWARE, 10, MICROPHONE));
+ assertNull(ps.getState(SOFTWARE, 10, CAMERA));
+
+ assertNull(ps.getState(HARDWARE, 0, MICROPHONE));
+ assertNull(ps.getState(HARDWARE, 0, CAMERA));
+ assertNull(ps.getState(HARDWARE, 10, MICROPHONE));
+ assertNull(ps.getState(HARDWARE, 10, CAMERA));
+ }
+
+ @Test
+ public void testMigration2() throws IOException {
+ PersistedState ps = migrateFromFile(PERSISTENCE_FILE2);
+
+ assertTrue(ps.getState(SOFTWARE, 0, MICROPHONE).isEnabled());
+ assertTrue(ps.getState(SOFTWARE, 0, CAMERA).isEnabled());
+
+ assertTrue(ps.getState(SOFTWARE, 10, MICROPHONE).isEnabled());
+ assertFalse(ps.getState(SOFTWARE, 10, CAMERA).isEnabled());
+
+ assertNull(ps.getState(SOFTWARE, 11, MICROPHONE));
+ assertNull(ps.getState(SOFTWARE, 11, CAMERA));
+
+ assertTrue(ps.getState(SOFTWARE, 12, MICROPHONE).isEnabled());
+ assertNull(ps.getState(SOFTWARE, 12, CAMERA));
+
+ assertNull(ps.getState(HARDWARE, 0, MICROPHONE));
+ assertNull(ps.getState(HARDWARE, 0, CAMERA));
+ assertNull(ps.getState(HARDWARE, 10, MICROPHONE));
+ assertNull(ps.getState(HARDWARE, 10, CAMERA));
+ assertNull(ps.getState(HARDWARE, 11, MICROPHONE));
+ assertNull(ps.getState(HARDWARE, 11, CAMERA));
+ assertNull(ps.getState(HARDWARE, 12, MICROPHONE));
+ assertNull(ps.getState(HARDWARE, 12, CAMERA));
+ }
+
+ @Test
+ public void testMigration3() throws IOException {
+ PersistedState ps = migrateFromFile(PERSISTENCE_FILE3);
+
+ assertFalse(ps.getState(SOFTWARE, 0, MICROPHONE).isEnabled());
+ assertFalse(ps.getState(SOFTWARE, 0, CAMERA).isEnabled());
+
+ assertNull(ps.getState(SOFTWARE, 10, MICROPHONE));
+ assertNull(ps.getState(SOFTWARE, 10, CAMERA));
+
+ assertNull(ps.getState(HARDWARE, 0, MICROPHONE));
+ assertNull(ps.getState(HARDWARE, 0, CAMERA));
+ assertNull(ps.getState(HARDWARE, 10, MICROPHONE));
+ assertNull(ps.getState(HARDWARE, 10, CAMERA));
+ }
+
+ @Test
+ public void testMigration4() throws IOException {
+ PersistedState ps = migrateFromFile(PERSISTENCE_FILE4);
+
+ assertTrue(ps.getState(SOFTWARE, 0, MICROPHONE).isEnabled());
+ assertFalse(ps.getState(SOFTWARE, 0, CAMERA).isEnabled());
+
+ assertFalse(ps.getState(SOFTWARE, 10, MICROPHONE).isEnabled());
+ assertNull(ps.getState(SOFTWARE, 10, CAMERA));
+
+ assertNull(ps.getState(HARDWARE, 0, MICROPHONE));
+ assertNull(ps.getState(HARDWARE, 0, CAMERA));
+ assertNull(ps.getState(HARDWARE, 10, MICROPHONE));
+ assertNull(ps.getState(HARDWARE, 10, CAMERA));
+ }
+
+ @Test
+ public void testMigration5() throws IOException {
+ PersistedState ps = migrateFromFile(PERSISTENCE_FILE5);
+
+ assertNull(ps.getState(SOFTWARE, 0, MICROPHONE));
+ assertFalse(ps.getState(SOFTWARE, 0, CAMERA).isEnabled());
+
+ assertNull(ps.getState(SOFTWARE, 10, MICROPHONE));
+ assertFalse(ps.getState(SOFTWARE, 10, CAMERA).isEnabled());
+
+ assertNull(ps.getState(HARDWARE, 0, MICROPHONE));
+ assertNull(ps.getState(HARDWARE, 0, CAMERA));
+ assertNull(ps.getState(HARDWARE, 10, MICROPHONE));
+ assertNull(ps.getState(HARDWARE, 10, CAMERA));
+ }
@Test
- public void testServiceInit() throws IOException {
+ public void testMigration6() throws IOException {
+ PersistedState ps = migrateFromFile(PERSISTENCE_FILE6);
+
+ assertNull(ps.getState(SOFTWARE, 0, MICROPHONE));
+ assertNull(ps.getState(SOFTWARE, 0, CAMERA));
+
+ assertNull(ps.getState(SOFTWARE, 10, MICROPHONE));
+ assertNull(ps.getState(SOFTWARE, 10, CAMERA));
+
+ assertNull(ps.getState(HARDWARE, 0, MICROPHONE));
+ assertNull(ps.getState(HARDWARE, 0, CAMERA));
+ assertNull(ps.getState(HARDWARE, 10, MICROPHONE));
+ assertNull(ps.getState(HARDWARE, 10, CAMERA));
+ }
+
+ private PersistedState migrateFromFile(String fileName) throws IOException {
MockitoSession mockitoSession = ExtendedMockito.mockitoSession()
.initMocks(this)
.strictness(Strictness.WARN)
.spyStatic(LocalServices.class)
.spyStatic(Environment.class)
.startMocking();
-
try {
- mContext = InstrumentationRegistry.getInstrumentation().getContext();
- spyOn(mContext);
-
- doReturn(mMockedAppOpsManager).when(mContext).getSystemService(AppOpsManager.class);
- doReturn(mMockedUserManagerInternal)
- .when(() -> LocalServices.getService(UserManagerInternal.class));
- doReturn(mMockedActivityManager).when(mContext).getSystemService(ActivityManager.class);
- doReturn(mMockedActivityTaskManager)
- .when(mContext).getSystemService(ActivityTaskManager.class);
- doReturn(mMockedTelephonyManager).when(mContext).getSystemService(
- TelephonyManager.class);
-
- String dataDir = mContext.getApplicationInfo().dataDir;
- doReturn(new File(dataDir)).when(() -> Environment.getDataSystemDirectory());
-
- File onDeviceFile = new File(dataDir, "sensor_privacy.xml");
- onDeviceFile.delete();
-
- // Try all files with one known user
- doReturn(new int[]{0}).when(mMockedUserManagerInternal).getUserIds();
- doReturn(ExtendedMockito.mock(UserInfo.class)).when(mMockedUserManagerInternal)
- .getUserInfo(0);
- initServiceWithPersistenceFile(onDeviceFile, null);
- initServiceWithPersistenceFile(onDeviceFile, PERSISTENCE_FILE1);
- initServiceWithPersistenceFile(onDeviceFile, PERSISTENCE_FILE2);
- initServiceWithPersistenceFile(onDeviceFile, PERSISTENCE_FILE3);
- initServiceWithPersistenceFile(onDeviceFile, PERSISTENCE_FILE4);
- initServiceWithPersistenceFile(onDeviceFile, PERSISTENCE_FILE5);
- initServiceWithPersistenceFile(onDeviceFile, PERSISTENCE_FILE6);
-
- // Try all files with two known users
- doReturn(new int[]{0, 10}).when(mMockedUserManagerInternal).getUserIds();
- doReturn(ExtendedMockito.mock(UserInfo.class)).when(mMockedUserManagerInternal)
- .getUserInfo(0);
- doReturn(ExtendedMockito.mock(UserInfo.class)).when(mMockedUserManagerInternal)
- .getUserInfo(10);
- initServiceWithPersistenceFile(onDeviceFile, null);
- initServiceWithPersistenceFile(onDeviceFile, PERSISTENCE_FILE1);
- initServiceWithPersistenceFile(onDeviceFile, PERSISTENCE_FILE2);
- initServiceWithPersistenceFile(onDeviceFile, PERSISTENCE_FILE3);
- initServiceWithPersistenceFile(onDeviceFile, PERSISTENCE_FILE4);
- initServiceWithPersistenceFile(onDeviceFile, PERSISTENCE_FILE5);
- initServiceWithPersistenceFile(onDeviceFile, PERSISTENCE_FILE6);
+ doReturn(new File(mDataDir)).when(() -> Environment.getDataSystemDirectory());
+
+ UserManagerInternal umi = mock(UserManagerInternal.class);
+ doReturn(umi).when(() -> LocalServices.getService(UserManagerInternal.class));
+ doReturn(new int[] {0}).when(umi).getUserIds();
+ Files.copy(
+ mContext.getAssets().open(fileName),
+ new File(mDataDir, "sensor_privacy.xml").toPath(),
+ StandardCopyOption.REPLACE_EXISTING);
+
+ return PersistedState.fromFile("sensor_privacy_impl.xml");
} finally {
mockitoSession.finishMocking();
}
}
@Test
- public void testServiceInit_AppOpsRestricted_micMute_camMute() throws IOException {
- testServiceInit_AppOpsRestricted(PERSISTENCE_FILE_MIC_MUTE_CAM_MUTE, true, true);
+ public void testPersistence1Version2() throws IOException {
+ PersistedState ps = getPersistedStateV2(PERSISTENCE_FILE7);
+
+ assertEquals(1, ps.getState(SOFTWARE, 0, MICROPHONE).getState());
+ assertEquals(123L, ps.getState(SOFTWARE, 0, MICROPHONE).getLastChange());
+ assertEquals(2, ps.getState(SOFTWARE, 0, CAMERA).getState());
+ assertEquals(123L, ps.getState(SOFTWARE, 0, CAMERA).getLastChange());
+
+ assertNull(ps.getState(HARDWARE, 0, MICROPHONE));
+ assertNull(ps.getState(HARDWARE, 0, CAMERA));
+ assertNull(ps.getState(SOFTWARE, 10, MICROPHONE));
+ assertNull(ps.getState(SOFTWARE, 10, CAMERA));
}
@Test
- public void testServiceInit_AppOpsRestricted_micMute_camUnmute() throws IOException {
- testServiceInit_AppOpsRestricted(PERSISTENCE_FILE_MIC_MUTE_CAM_UNMUTE, true, false);
+ public void testPersistence2Version2() throws IOException {
+ PersistedState ps = getPersistedStateV2(PERSISTENCE_FILE8);
+
+ assertEquals(1, ps.getState(HARDWARE, 0, MICROPHONE).getState());
+ assertEquals(1234L, ps.getState(HARDWARE, 0, MICROPHONE).getLastChange());
+ assertEquals(2, ps.getState(HARDWARE, 0, CAMERA).getState());
+ assertEquals(1234L, ps.getState(HARDWARE, 0, CAMERA).getLastChange());
+
+ assertNull(ps.getState(SOFTWARE, 0, MICROPHONE));
+ assertNull(ps.getState(SOFTWARE, 0, CAMERA));
+ assertNull(ps.getState(SOFTWARE, 10, MICROPHONE));
+ assertNull(ps.getState(SOFTWARE, 10, CAMERA));
+ assertNull(ps.getState(HARDWARE, 10, MICROPHONE));
+ assertNull(ps.getState(HARDWARE, 10, CAMERA));
}
- @Test
- public void testServiceInit_AppOpsRestricted_micUnmute_camMute() throws IOException {
- testServiceInit_AppOpsRestricted(PERSISTENCE_FILE_MIC_UNMUTE_CAM_MUTE, false, true);
+ private PersistedState getPersistedStateV2(String version2FilePath) throws IOException {
+ MockitoSession mockitoSession = ExtendedMockito.mockitoSession()
+ .initMocks(this)
+ .strictness(Strictness.WARN)
+ .spyStatic(LocalServices.class)
+ .spyStatic(Environment.class)
+ .startMocking();
+ try {
+ doReturn(new File(mDataDir)).when(() -> Environment.getDataSystemDirectory());
+ Files.copy(
+ mContext.getAssets().open(version2FilePath),
+ new File(mDataDir, "sensor_privacy_impl.xml").toPath(),
+ StandardCopyOption.REPLACE_EXISTING);
+
+ return PersistedState.fromFile("sensor_privacy_impl.xml");
+ } finally {
+ mockitoSession.finishMocking();
+ }
}
@Test
- public void testServiceInit_AppOpsRestricted_micUnmute_camUnmute() throws IOException {
- testServiceInit_AppOpsRestricted(PERSISTENCE_FILE_MIC_UNMUTE_CAM_UNMUTE, false, false);
+ public void testGetDefaultState() {
+ MockitoSession mockitoSession = ExtendedMockito.mockitoSession()
+ .initMocks(this)
+ .strictness(Strictness.WARN)
+ .spyStatic(PersistedState.class)
+ .startMocking();
+ try {
+ PersistedState persistedState = mock(PersistedState.class);
+ doReturn(persistedState).when(() -> PersistedState.fromFile(any()));
+ doReturn(null).when(persistedState).getState(anyInt(), anyInt(), anyInt());
+
+ SensorPrivacyStateController sensorPrivacyStateController =
+ getSensorPrivacyStateControllerImpl();
+
+ SensorState micState = sensorPrivacyStateController.getState(SOFTWARE, 0, MICROPHONE);
+ SensorState camState = sensorPrivacyStateController.getState(SOFTWARE, 0, CAMERA);
+
+ assertEquals(SensorPrivacyManager.StateTypes.DISABLED, micState.getState());
+ assertEquals(SensorPrivacyManager.StateTypes.DISABLED, camState.getState());
+ verify(persistedState, times(1)).getState(SOFTWARE, 0, MICROPHONE);
+ verify(persistedState, times(1)).getState(SOFTWARE, 0, CAMERA);
+ } finally {
+ mockitoSession.finishMocking();
+ }
}
- private void testServiceInit_AppOpsRestricted(String persistenceFileMicMuteCamMute,
- boolean expectedMicState, boolean expectedCamState)
- throws IOException {
+ @Test
+ public void testGetSetState() {
MockitoSession mockitoSession = ExtendedMockito.mockitoSession()
.initMocks(this)
.strictness(Strictness.WARN)
- .spyStatic(LocalServices.class)
- .spyStatic(Environment.class)
+ .spyStatic(PersistedState.class)
.startMocking();
-
try {
- mContext = InstrumentationRegistry.getInstrumentation().getContext();
- spyOn(mContext);
-
- doReturn(mMockedAppOpsManager).when(mContext).getSystemService(AppOpsManager.class);
- doReturn(mMockedAppOpsManagerInternal)
- .when(() -> LocalServices.getService(AppOpsManagerInternal.class));
- doReturn(mMockedUserManagerInternal)
- .when(() -> LocalServices.getService(UserManagerInternal.class));
- doReturn(mMockedActivityManager).when(mContext).getSystemService(ActivityManager.class);
- doReturn(mMockedActivityTaskManager)
- .when(mContext).getSystemService(ActivityTaskManager.class);
- doReturn(mMockedTelephonyManager).when(mContext).getSystemService(
- TelephonyManager.class);
-
- String dataDir = mContext.getApplicationInfo().dataDir;
- doReturn(new File(dataDir)).when(() -> Environment.getDataSystemDirectory());
-
- File onDeviceFile = new File(dataDir, "sensor_privacy.xml");
- onDeviceFile.delete();
-
- doReturn(new int[]{0}).when(mMockedUserManagerInternal).getUserIds();
- doReturn(ExtendedMockito.mock(UserInfo.class)).when(mMockedUserManagerInternal)
- .getUserInfo(0);
-
- CompletableFuture<Boolean> micState = new CompletableFuture<>();
- CompletableFuture<Boolean> camState = new CompletableFuture<>();
- doAnswer(invocation -> {
- int code = invocation.getArgument(0);
- boolean restricted = invocation.getArgument(1);
- if (code == AppOpsManager.OP_RECORD_AUDIO) {
- micState.complete(restricted);
- } else if (code == AppOpsManager.OP_CAMERA) {
- camState.complete(restricted);
- }
- return null;
- }).when(mMockedAppOpsManagerInternal).setGlobalRestriction(anyInt(), anyBoolean(),
- any());
-
- initServiceWithPersistenceFile(onDeviceFile, persistenceFileMicMuteCamMute, 0);
-
- Assert.assertTrue(micState.join() == expectedMicState);
- Assert.assertTrue(camState.join() == expectedCamState);
+ PersistedState persistedState = mock(PersistedState.class);
+ SensorState sensorState = mock(SensorState.class);
+ doReturn(persistedState).when(() -> PersistedState.fromFile(any()));
+ doReturn(sensorState).when(persistedState).getState(SOFTWARE, 0, MICROPHONE);
+ doReturn(SensorPrivacyManager.StateTypes.ENABLED).when(sensorState).getState();
+ doReturn(0L).when(sensorState).getLastChange();
+
+ SensorPrivacyStateController sensorPrivacyStateController =
+ getSensorPrivacyStateControllerImpl();
+
+ SensorState micState = sensorPrivacyStateController.getState(SOFTWARE, 0, MICROPHONE);
+ assertEquals(SensorPrivacyManager.StateTypes.ENABLED, micState.getState());
+ assertEquals(0L, micState.getLastChange());
} finally {
mockitoSession.finishMocking();
}
}
- private void initServiceWithPersistenceFile(File onDeviceFile,
- String persistenceFilePath) throws IOException {
- initServiceWithPersistenceFile(onDeviceFile, persistenceFilePath, -1);
- }
+ @Test
+ public void testSetState() {
+ MockitoSession mockitoSession = ExtendedMockito.mockitoSession()
+ .initMocks(this)
+ .strictness(Strictness.WARN)
+ .spyStatic(PersistedState.class)
+ .startMocking();
+ try {
+ PersistedState persistedState = mock(PersistedState.class);
+ doReturn(persistedState).when(() -> PersistedState.fromFile(any()));
- private void initServiceWithPersistenceFile(File onDeviceFile,
- String persistenceFilePath, int startingUserId) throws IOException {
- if (persistenceFilePath != null) {
- Files.copy(mContext.getAssets().open(persistenceFilePath),
- onDeviceFile.toPath());
- }
- SensorPrivacyService service = new SensorPrivacyService(mContext);
- if (startingUserId != -1) {
- SystemService.TargetUser mockedTargetUser =
- ExtendedMockito.mock(SystemService.TargetUser.class);
- doReturn(startingUserId).when(mockedTargetUser).getUserIdentifier();
- service.onUserStarting(mockedTargetUser);
+ SensorPrivacyStateController sensorPrivacyStateController =
+ getSensorPrivacyStateControllerImpl();
+
+ sensorPrivacyStateController.setState(SOFTWARE, 0, MICROPHONE, true,
+ mock(Handler.class), changed -> {});
+
+ ArgumentCaptor<SensorState> captor = ArgumentCaptor.forClass(SensorState.class);
+
+ verify(persistedState, times(1)).setState(eq(SOFTWARE), eq(0), eq(MICROPHONE),
+ captor.capture());
+ assertEquals(SensorPrivacyManager.StateTypes.ENABLED, captor.getValue().getState());
+ } finally {
+ mockitoSession.finishMocking();
}
- onDeviceFile.delete();
+ }
+
+ private SensorPrivacyStateController getSensorPrivacyStateControllerImpl() {
+ SensorPrivacyStateControllerImpl.getInstance().resetForTestingImpl();
+ return SensorPrivacyStateControllerImpl.getInstance();
}
}
diff --git a/services/tests/servicestests/src/com/android/server/DockObserverTest.java b/services/tests/servicestests/src/com/android/server/DockObserverTest.java
new file mode 100644
index 000000000000..c325778a5683
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/DockObserverTest.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Intent;
+import android.os.Looper;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableContext;
+import android.testing.TestableLooper;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import com.android.internal.R;
+import com.android.internal.util.test.BroadcastInterceptingContext;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.ExecutionException;
+
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class DockObserverTest {
+
+ @Rule
+ public TestableContext mContext =
+ new TestableContext(ApplicationProvider.getApplicationContext(), null);
+
+ private final BroadcastInterceptingContext mInterceptingContext =
+ new BroadcastInterceptingContext(mContext);
+
+ BroadcastInterceptingContext.FutureIntent updateExtconDockState(DockObserver observer,
+ String extconDockState) {
+ BroadcastInterceptingContext.FutureIntent futureIntent =
+ mInterceptingContext.nextBroadcastIntent(Intent.ACTION_DOCK_EVENT);
+ observer.setDockStateFromProviderForTesting(
+ DockObserver.ExtconStateProvider.fromString(extconDockState));
+ TestableLooper.get(this).processAllMessages();
+ return futureIntent;
+ }
+
+ DockObserver observerWithMappingConfig(String[] configEntries) {
+ mContext.getOrCreateTestableResources().addOverride(
+ R.array.config_dockExtconStateMapping,
+ configEntries);
+ return new DockObserver(mInterceptingContext);
+ }
+
+ void assertDockEventIntentWithExtraThenUndock(DockObserver observer, String extconDockState,
+ int expectedExtra) throws ExecutionException, InterruptedException {
+ assertThat(updateExtconDockState(observer, extconDockState)
+ .get().getIntExtra(Intent.EXTRA_DOCK_STATE, -1))
+ .isEqualTo(expectedExtra);
+ assertThat(updateExtconDockState(observer, "DOCK=0")
+ .get().getIntExtra(Intent.EXTRA_DOCK_STATE, -1))
+ .isEqualTo(Intent.EXTRA_DOCK_STATE_UNDOCKED);
+ }
+
+ @Before
+ public void setUp() {
+ if (Looper.myLooper() == null) {
+ Looper.prepare();
+ }
+ }
+
+ @Test
+ public void testDockIntentBroadcast_onlyAfterBootReady()
+ throws ExecutionException, InterruptedException {
+ DockObserver observer = new DockObserver(mInterceptingContext);
+ BroadcastInterceptingContext.FutureIntent futureIntent =
+ updateExtconDockState(observer, "DOCK=1");
+ updateExtconDockState(observer, "DOCK=1").assertNotReceived();
+ // Last boot phase reached
+ observer.onBootPhase(SystemService.PHASE_ACTIVITY_MANAGER_READY);
+ TestableLooper.get(this).processAllMessages();
+ assertThat(futureIntent.get().getIntExtra(Intent.EXTRA_DOCK_STATE, -1))
+ .isEqualTo(Intent.EXTRA_DOCK_STATE_DESK);
+ }
+
+ @Test
+ public void testDockIntentBroadcast_customConfigResource()
+ throws ExecutionException, InterruptedException {
+ DockObserver observer = observerWithMappingConfig(
+ new String[] {"2,KEY1=1,KEY2=2", "3,KEY3=3"});
+ observer.onBootPhase(SystemService.PHASE_ACTIVITY_MANAGER_READY);
+
+ // Mapping should not match
+ assertDockEventIntentWithExtraThenUndock(observer, "DOCK=1",
+ Intent.EXTRA_DOCK_STATE_DESK);
+ assertDockEventIntentWithExtraThenUndock(observer, "DOCK=1\nKEY1=1",
+ Intent.EXTRA_DOCK_STATE_DESK);
+ assertDockEventIntentWithExtraThenUndock(observer, "DOCK=1\nKEY2=2",
+ Intent.EXTRA_DOCK_STATE_DESK);
+
+ // 1st mapping now matches
+ assertDockEventIntentWithExtraThenUndock(observer, "DOCK=1\nKEY2=2\nKEY1=1",
+ Intent.EXTRA_DOCK_STATE_CAR);
+
+ // 2nd mapping now matches
+ assertDockEventIntentWithExtraThenUndock(observer, "DOCK=1\nKEY3=3",
+ Intent.EXTRA_DOCK_STATE_LE_DESK);
+ }
+
+ @Test
+ public void testDockIntentBroadcast_customConfigResourceWithWildcard()
+ throws ExecutionException, InterruptedException {
+ DockObserver observer = observerWithMappingConfig(new String[] {
+ "2,KEY2=2",
+ "3,KEY3=3",
+ "4"
+ });
+ observer.onBootPhase(SystemService.PHASE_ACTIVITY_MANAGER_READY);
+ assertDockEventIntentWithExtraThenUndock(observer, "DOCK=1\nKEY5=5",
+ Intent.EXTRA_DOCK_STATE_HE_DESK);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java b/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java
index e40f5439d0c2..e1aa08d9176a 100644
--- a/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java
@@ -23,6 +23,7 @@ import static android.content.pm.PackageManager.MATCH_ANY_USER;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.AdditionalAnswers.returnsArgAt;
import static org.mockito.ArgumentMatchers.any;
@@ -40,6 +41,7 @@ import android.app.IActivityManager;
import android.app.usage.UsageEvents.Event;
import android.app.usage.UsageStatsManagerInternal;
import android.app.usage.UsageStatsManagerInternal.UsageEventListener;
+import android.apphibernation.HibernationStats;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -68,6 +70,8 @@ import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
+import java.util.Set;
import java.util.concurrent.Executor;
/**
@@ -126,7 +130,7 @@ public final class AppHibernationServiceTest {
mUsageEventListener = mUsageEventListenerCaptor.getValue();
doReturn(mUserInfos).when(mUserManager).getUsers();
-
+ doReturn(true).when(mPackageManagerInternal).canQueryPackage(anyInt(), any());
doAnswer(returnsArgAt(2)).when(mIActivityManager).handleIncomingUser(anyInt(), anyInt(),
anyInt(), anyBoolean(), anyBoolean(), any(), any());
@@ -376,6 +380,58 @@ public final class AppHibernationServiceTest {
assertFalse(mAppHibernationService.isHibernatingGlobally(PACKAGE_NAME_1));
}
+ @Test
+ public void testGetHibernationStatsForUser_getsStatsForPackage() {
+ // GIVEN a package is hibernating globally and for a user
+ mAppHibernationService.setHibernatingGlobally(PACKAGE_NAME_1, true);
+ mAppHibernationService.setHibernatingForUser(PACKAGE_NAME_1, USER_ID_1, true);
+
+ // WHEN we ask for the hibernation stats for the package
+ Map<String, HibernationStats> stats =
+ mAppHibernationService.getHibernationStatsForUser(
+ Set.of(PACKAGE_NAME_1), USER_ID_1);
+
+ // THEN the stats exist for the package
+ assertTrue(stats.containsKey(PACKAGE_NAME_1));
+ }
+
+ @Test
+ public void testGetHibernationStatsForUser_noExceptionThrownWhenPackageDoesntExist() {
+ // WHEN we ask for the hibernation stats for a package that doesn't exist
+ Map<String, HibernationStats> stats =
+ mAppHibernationService.getHibernationStatsForUser(
+ Set.of(PACKAGE_NAME_1), USER_ID_1);
+
+ // THEN no exception is thrown and empty stats are returned
+ assertNotNull(stats);
+ }
+
+ @Test
+ public void testGetHibernationStatsForUser_returnsAllIfNoPackagesSpecified()
+ throws RemoteException {
+ // GIVEN an unlocked user with all packages installed and they're all hibernating
+ UserInfo userInfo =
+ addUser(USER_ID_2, new String[]{PACKAGE_NAME_1, PACKAGE_NAME_2, PACKAGE_NAME_3});
+ doReturn(true).when(mUserManager).isUserUnlockingOrUnlocked(USER_ID_2);
+ mAppHibernationService.onUserUnlocking(new SystemService.TargetUser(userInfo));
+ mAppHibernationService.setHibernatingGlobally(PACKAGE_NAME_1, true);
+ mAppHibernationService.setHibernatingForUser(PACKAGE_NAME_1, USER_ID_2, true);
+ mAppHibernationService.setHibernatingGlobally(PACKAGE_NAME_2, true);
+ mAppHibernationService.setHibernatingForUser(PACKAGE_NAME_2, USER_ID_2, true);
+ mAppHibernationService.setHibernatingGlobally(PACKAGE_NAME_3, true);
+ mAppHibernationService.setHibernatingForUser(PACKAGE_NAME_3, USER_ID_2, true);
+
+ // WHEN we ask for the hibernation stats with no package specified
+ Map<String, HibernationStats> stats =
+ mAppHibernationService.getHibernationStatsForUser(
+ null /* packageNames */, USER_ID_2);
+
+ // THEN all the package stats are returned
+ assertTrue(stats.containsKey(PACKAGE_NAME_1));
+ assertTrue(stats.containsKey(PACKAGE_NAME_2));
+ assertTrue(stats.containsKey(PACKAGE_NAME_3));
+ }
+
/**
* Mock a usage event occurring.
*
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricContextProviderTest.java b/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricContextProviderTest.java
new file mode 100644
index 000000000000..5746f6f2d446
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricContextProviderTest.java
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.log;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.same;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.StatusBarManager;
+import android.hardware.biometrics.IBiometricContextListener;
+import android.hardware.biometrics.common.OperationContext;
+import android.hardware.biometrics.common.OperationReason;
+import android.hardware.display.AmbientDisplayConfiguration;
+import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.logging.InstanceId;
+import com.android.internal.statusbar.ISessionListener;
+import com.android.internal.statusbar.IStatusBarService;
+
+import com.google.common.collect.ImmutableList;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Consumer;
+
+@Presubmit
+@SmallTest
+public class BiometricContextProviderTest {
+
+ @Rule
+ public final MockitoRule mockito = MockitoJUnit.rule();
+
+ @Mock
+ private IStatusBarService mStatusBarService;
+ @Mock
+ private ISessionListener mSessionListener;
+ @Mock
+ private AmbientDisplayConfiguration mAmbientDisplayConfiguration;
+
+ private OperationContext mOpContext = new OperationContext();
+ private IBiometricContextListener mListener;
+ private BiometricContextProvider mProvider;
+
+ @Before
+ public void setup() throws RemoteException {
+ when(mAmbientDisplayConfiguration.alwaysOnEnabled(anyInt())).thenReturn(true);
+ mProvider = new BiometricContextProvider(mAmbientDisplayConfiguration, mStatusBarService,
+ null /* handler */);
+ ArgumentCaptor<IBiometricContextListener> captor =
+ ArgumentCaptor.forClass(IBiometricContextListener.class);
+ verify(mStatusBarService).setBiometicContextListener(captor.capture());
+ mListener = captor.getValue();
+ ArgumentCaptor<ISessionListener> sessionCaptor =
+ ArgumentCaptor.forClass(ISessionListener.class);
+ verify(mStatusBarService).registerSessionListener(anyInt(), sessionCaptor.capture());
+ mSessionListener = sessionCaptor.getValue();
+ }
+
+ @Test
+ public void testIsAoD() throws RemoteException {
+ mListener.onDozeChanged(true);
+ assertThat(mProvider.isAoD()).isTrue();
+ mListener.onDozeChanged(false);
+ assertThat(mProvider.isAoD()).isFalse();
+
+ when(mAmbientDisplayConfiguration.alwaysOnEnabled(anyInt())).thenReturn(false);
+ mListener.onDozeChanged(true);
+ assertThat(mProvider.isAoD()).isFalse();
+ mListener.onDozeChanged(false);
+ assertThat(mProvider.isAoD()).isFalse();
+ }
+
+ @Test
+ public void testSubscribesToAoD() throws RemoteException {
+ final List<Boolean> expected = ImmutableList.of(true, false, true, true, false);
+ final List<Boolean> actual = new ArrayList<>();
+
+ mProvider.subscribe(mOpContext, ctx -> {
+ assertThat(ctx).isSameInstanceAs(mOpContext);
+ actual.add(ctx.isAoD);
+ });
+
+ for (boolean v : expected) {
+ mListener.onDozeChanged(v);
+ }
+
+ assertThat(actual).containsExactlyElementsIn(expected).inOrder();
+ }
+
+ @Test
+ public void testUnsubscribes() throws RemoteException {
+ final Consumer<OperationContext> emptyConsumer = mock(Consumer.class);
+ mProvider.subscribe(mOpContext, emptyConsumer);
+ mProvider.unsubscribe(mOpContext);
+
+ mListener.onDozeChanged(true);
+
+ final Consumer<OperationContext> nonEmptyConsumer = mock(Consumer.class);
+ mProvider.subscribe(mOpContext, nonEmptyConsumer);
+ mListener.onDozeChanged(false);
+ mProvider.unsubscribe(mOpContext);
+ mListener.onDozeChanged(true);
+
+ verify(emptyConsumer, never()).accept(any());
+ verify(nonEmptyConsumer).accept(same(mOpContext));
+ }
+
+ @Test
+ public void testSessionId() throws RemoteException {
+ final int keyguardSessionId = 10;
+ final int bpSessionId = 20;
+
+ assertThat(mProvider.getBiometricPromptSessionId()).isNull();
+ assertThat(mProvider.getKeyguardEntrySessionId()).isNull();
+
+ mSessionListener.onSessionStarted(StatusBarManager.SESSION_KEYGUARD,
+ InstanceId.fakeInstanceId(keyguardSessionId));
+
+ assertThat(mProvider.getBiometricPromptSessionId()).isNull();
+ assertThat(mProvider.getKeyguardEntrySessionId()).isEqualTo(keyguardSessionId);
+
+ mSessionListener.onSessionStarted(StatusBarManager.SESSION_BIOMETRIC_PROMPT,
+ InstanceId.fakeInstanceId(bpSessionId));
+
+ assertThat(mProvider.getBiometricPromptSessionId()).isEqualTo(bpSessionId);
+ assertThat(mProvider.getKeyguardEntrySessionId()).isEqualTo(keyguardSessionId);
+
+ mSessionListener.onSessionEnded(StatusBarManager.SESSION_KEYGUARD,
+ InstanceId.fakeInstanceId(keyguardSessionId));
+
+ assertThat(mProvider.getBiometricPromptSessionId()).isEqualTo(bpSessionId);
+ assertThat(mProvider.getKeyguardEntrySessionId()).isNull();
+
+ mSessionListener.onSessionEnded(StatusBarManager.SESSION_BIOMETRIC_PROMPT,
+ InstanceId.fakeInstanceId(bpSessionId));
+
+ assertThat(mProvider.getBiometricPromptSessionId()).isNull();
+ assertThat(mProvider.getKeyguardEntrySessionId()).isNull();
+ }
+
+ @Test
+ public void testUpdate() throws RemoteException {
+ mListener.onDozeChanged(false);
+ OperationContext context = mProvider.updateContext(mOpContext, false /* crypto */);
+
+ // default state when nothing has been set
+ assertThat(context).isSameInstanceAs(mOpContext);
+ assertThat(mOpContext.id).isEqualTo(0);
+ assertThat(mOpContext.reason).isEqualTo(OperationReason.UNKNOWN);
+ assertThat(mOpContext.isAoD).isEqualTo(false);
+ assertThat(mOpContext.isCrypto).isEqualTo(false);
+
+ for (int type : List.of(StatusBarManager.SESSION_BIOMETRIC_PROMPT,
+ StatusBarManager.SESSION_KEYGUARD)) {
+ final int id = 40 + type;
+ final boolean aod = (type & 1) == 0;
+
+ mListener.onDozeChanged(aod);
+ mSessionListener.onSessionStarted(type, InstanceId.fakeInstanceId(id));
+ context = mProvider.updateContext(mOpContext, false /* crypto */);
+ assertThat(context).isSameInstanceAs(mOpContext);
+ assertThat(mOpContext.id).isEqualTo(id);
+ assertThat(mOpContext.reason).isEqualTo(reason(type));
+ assertThat(mOpContext.isAoD).isEqualTo(aod);
+ assertThat(mOpContext.isCrypto).isEqualTo(false);
+
+ mSessionListener.onSessionEnded(type, InstanceId.fakeInstanceId(id));
+ }
+
+ context = mProvider.updateContext(mOpContext, false /* crypto */);
+ assertThat(context).isSameInstanceAs(mOpContext);
+ assertThat(mOpContext.id).isEqualTo(0);
+ assertThat(mOpContext.reason).isEqualTo(OperationReason.UNKNOWN);
+ assertThat(mOpContext.isAoD).isEqualTo(false);
+ assertThat(mOpContext.isCrypto).isEqualTo(false);
+ }
+
+ private static byte reason(int type) {
+ if (type == StatusBarManager.SESSION_BIOMETRIC_PROMPT) {
+ return OperationReason.BIOMETRIC_PROMPT;
+ }
+ if (type == StatusBarManager.SESSION_KEYGUARD) {
+ return OperationReason.KEYGUARD;
+ }
+ return OperationReason.UNKNOWN;
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricLoggerTest.java b/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricLoggerTest.java
index 2b72fabe7cca..fe023374ba88 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricLoggerTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricLoggerTest.java
@@ -31,6 +31,7 @@ import android.hardware.Sensor;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.hardware.biometrics.BiometricsProtoEnums;
+import android.hardware.biometrics.common.OperationContext;
import android.hardware.input.InputSensorInfo;
import android.platform.test.annotations.Presubmit;
import android.testing.TestableContext;
@@ -44,7 +45,8 @@ import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
@Presubmit
@SmallTest
@@ -55,6 +57,9 @@ public class BiometricLoggerTest {
private static final int DEFAULT_CLIENT = BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT;
@Rule
+ public final MockitoRule mockito = MockitoJUnit.rule();
+
+ @Rule
public TestableContext mContext = new TestableContext(
InstrumentationRegistry.getInstrumentation().getContext());
@Mock
@@ -64,12 +69,12 @@ public class BiometricLoggerTest {
@Mock
private BaseClientMonitor mClient;
+ private OperationContext mOpContext;
private BiometricLogger mLogger;
@Before
public void setUp() {
- MockitoAnnotations.initMocks(this);
-
+ mOpContext = new OperationContext();
mContext.addMockSystemService(SensorManager.class, mSensorManager);
when(mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT)).thenReturn(
new Sensor(new InputSensorInfo("", "", 0, 0, Sensor.TYPE_LIGHT, 0, 0, 0, 0, 0, 0,
@@ -91,14 +96,13 @@ public class BiometricLoggerTest {
final int acquiredInfo = 2;
final int vendorCode = 3;
- final boolean isCrypto = true;
final int targetUserId = 9;
- mLogger.logOnAcquired(mContext, acquiredInfo, vendorCode, isCrypto, targetUserId);
+ mLogger.logOnAcquired(mContext, mOpContext, acquiredInfo, vendorCode, targetUserId);
- verify(mSink).acquired(
+ verify(mSink).acquired(eq(mOpContext),
eq(DEFAULT_MODALITY), eq(DEFAULT_ACTION), eq(DEFAULT_CLIENT), anyBoolean(),
- eq(acquiredInfo), eq(vendorCode), eq(isCrypto), eq(targetUserId));
+ eq(acquiredInfo), eq(vendorCode), eq(targetUserId));
}
@Test
@@ -107,17 +111,16 @@ public class BiometricLoggerTest {
final boolean authenticated = true;
final boolean requireConfirmation = false;
- final boolean isCrypto = false;
final int targetUserId = 11;
final boolean isBiometricPrompt = true;
- mLogger.logOnAuthenticated(mContext,
- authenticated, requireConfirmation, isCrypto, targetUserId, isBiometricPrompt);
+ mLogger.logOnAuthenticated(mContext, mOpContext,
+ authenticated, requireConfirmation, targetUserId, isBiometricPrompt);
- verify(mSink).authenticate(
+ verify(mSink).authenticate(eq(mOpContext),
eq(DEFAULT_MODALITY), eq(DEFAULT_ACTION), eq(DEFAULT_CLIENT), anyBoolean(),
- anyLong(), eq(authenticated), anyInt(), eq(requireConfirmation), eq(isCrypto),
- eq(targetUserId), eq(isBiometricPrompt), anyFloat());
+ anyLong(), anyInt(), eq(requireConfirmation),
+ eq(targetUserId), anyFloat());
}
@Test
@@ -141,14 +144,13 @@ public class BiometricLoggerTest {
final int error = 7;
final int vendorCode = 11;
- final boolean isCrypto = false;
final int targetUserId = 9;
- mLogger.logOnError(mContext, error, vendorCode, isCrypto, targetUserId);
+ mLogger.logOnError(mContext, mOpContext, error, vendorCode, targetUserId);
- verify(mSink).error(
+ verify(mSink).error(eq(mOpContext),
eq(DEFAULT_MODALITY), eq(DEFAULT_ACTION), eq(DEFAULT_CLIENT), anyBoolean(),
- anyLong(), eq(error), eq(vendorCode), eq(isCrypto), eq(targetUserId));
+ anyLong(), eq(error), eq(vendorCode), eq(targetUserId));
}
@Test
@@ -173,38 +175,34 @@ public class BiometricLoggerTest {
private void testDisabledMetrics(boolean isBadConfig) {
mLogger.disableMetrics();
- mLogger.logOnAcquired(mContext,
+ mLogger.logOnAcquired(mContext, mOpContext,
0 /* acquiredInfo */,
1 /* vendorCode */,
- true /* isCrypto */,
8 /* targetUserId */);
- mLogger.logOnAuthenticated(mContext,
+ mLogger.logOnAuthenticated(mContext, mOpContext,
true /* authenticated */,
true /* requireConfirmation */,
- false /* isCrypto */,
4 /* targetUserId */,
true/* isBiometricPrompt */);
mLogger.logOnEnrolled(2 /* targetUserId */,
10 /* latency */,
true /* enrollSuccessful */);
- mLogger.logOnError(mContext,
+ mLogger.logOnError(mContext, mOpContext,
4 /* error */,
0 /* vendorCode */,
- false /* isCrypto */,
6 /* targetUserId */);
- verify(mSink, never()).acquired(
+ verify(mSink, never()).acquired(eq(mOpContext),
anyInt(), anyInt(), anyInt(), anyBoolean(),
- anyInt(), anyInt(), anyBoolean(), anyInt());
- verify(mSink, never()).authenticate(
+ anyInt(), anyInt(), anyInt());
+ verify(mSink, never()).authenticate(eq(mOpContext),
anyInt(), anyInt(), anyInt(), anyBoolean(),
- anyLong(), anyBoolean(), anyInt(), anyBoolean(),
- anyBoolean(), anyInt(), anyBoolean(), anyFloat());
+ anyLong(), anyInt(), anyBoolean(), anyInt(), anyFloat());
verify(mSink, never()).enroll(
anyInt(), anyInt(), anyInt(), anyInt(), anyLong(), anyBoolean(), anyFloat());
- verify(mSink, never()).error(
+ verify(mSink, never()).error(eq(mOpContext),
anyInt(), anyInt(), anyInt(), anyBoolean(),
- anyLong(), anyInt(), anyInt(), anyBoolean(), anyInt());
+ anyLong(), anyInt(), anyInt(), anyInt());
mLogger.logUnknownEnrollmentInFramework();
mLogger.logUnknownEnrollmentInHal();
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/AcquisitionClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/AcquisitionClientTest.java
index 9bb722f8a853..2d9d868f2f74 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/AcquisitionClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/AcquisitionClientTest.java
@@ -22,6 +22,7 @@ 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.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
@@ -34,6 +35,9 @@ import android.platform.test.annotations.Presubmit;
import androidx.annotation.NonNull;
import androidx.test.filters.SmallTest;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
+
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
@@ -92,9 +96,8 @@ public class AcquisitionClientTest {
@NonNull Supplier<Object> lazyDaemon, @NonNull IBinder token,
@NonNull ClientMonitorCallbackConverter callback) {
super(context, lazyDaemon, token, callback, 0 /* userId */, "Test", 0 /* cookie */,
- TEST_SENSOR_ID /* sensorId */, true /* shouldVibrate */, 0 /* statsModality */,
- 0 /* statsAction */,
- 0 /* statsClient */);
+ TEST_SENSOR_ID /* sensorId */, true /* shouldVibrate */,
+ mock(BiometricLogger.class), mock(BiometricContext.class));
}
@Override
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BaseClientMonitorTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BaseClientMonitorTest.java
index 51d234d5afeb..8e6d90c839d8 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BaseClientMonitorTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BaseClientMonitorTest.java
@@ -30,6 +30,7 @@ import android.platform.test.annotations.Presubmit;
import androidx.annotation.NonNull;
import androidx.test.filters.SmallTest;
+import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.log.BiometricLogger;
import org.junit.Before;
@@ -49,6 +50,8 @@ public class BaseClientMonitorTest {
@Mock
private BiometricLogger mLogger;
@Mock
+ private BiometricContext mBiometricContext;
+ @Mock
private ClientMonitorCallback mCallback;
private TestClientMonitor mClientMonitor;
@@ -109,7 +112,7 @@ public class BaseClientMonitorTest {
TestClientMonitor() {
super(mContext, mToken, mListener, 9 /* userId */, "foo" /* owner */, 2 /* cookie */,
- 5 /* sensorId */, mLogger);
+ 5 /* sensorId */, mLogger, mBiometricContext);
}
@Override
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerOperationTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerOperationTest.java
index 8751cf3810bf..64be56906d4b 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerOperationTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerOperationTest.java
@@ -36,6 +36,9 @@ import android.testing.TestableLooper;
import androidx.test.filters.SmallTest;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -54,7 +57,8 @@ public class BiometricSchedulerOperationTest {
public abstract static class InterruptableMonitor<T>
extends HalClientMonitor<T> implements Interruptable {
public InterruptableMonitor() {
- super(null, null, null, null, 0, null, 0, 0, 0, 0, 0);
+ super(null, null, null, null, 0, null, 0, 0,
+ mock(BiometricLogger.class), mock(BiometricContext.class));
}
}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java
index ecd9abcb80e6..0fa2b41e8b32 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java
@@ -51,6 +51,8 @@ import androidx.annotation.Nullable;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.nano.BiometricSchedulerProto;
import com.android.server.biometrics.nano.BiometricsProto;
@@ -526,10 +528,10 @@ public class BiometricSchedulerTest {
@NonNull ClientMonitorCallbackConverter listener) {
super(context, lazyDaemon, token, listener, 0 /* targetUserId */, 0 /* operationId */,
false /* restricted */, TAG, 1 /* cookie */, false /* requireConfirmation */,
- TEST_SENSOR_ID, true /* isStrongBiometric */, 0 /* statsModality */,
- 0 /* statsClient */, null /* taskStackListener */, mock(LockoutTracker.class),
- false /* isKeyguard */, true /* shouldVibrate */,
- false /* isKeyguardBypassEnabled */);
+ TEST_SENSOR_ID, mock(BiometricLogger.class), mock(BiometricContext.class),
+ true /* isStrongBiometric */, null /* taskStackListener */,
+ mock(LockoutTracker.class), false /* isKeyguard */,
+ true /* shouldVibrate */, false /* isKeyguardBypassEnabled */);
}
@Override
@@ -573,8 +575,9 @@ public class BiometricSchedulerTest {
@NonNull ClientMonitorCallbackConverter listener) {
super(context, lazyDaemon, token, listener, 0 /* userId */, new byte[69],
"test" /* owner */, mock(BiometricUtils.class),
- 5 /* timeoutSec */, 0 /* statsModality */, TEST_SENSOR_ID,
- true /* shouldVibrate */);
+ 5 /* timeoutSec */, TEST_SENSOR_ID,
+ true /* shouldVibrate */,
+ mock(BiometricLogger.class), mock(BiometricContext.class));
}
@Override
@@ -613,8 +616,8 @@ public class BiometricSchedulerTest {
TestHalClientMonitor(@NonNull Context context, @NonNull IBinder token,
@NonNull Supplier<Object> lazyDaemon, int cookie, int protoEnum) {
super(context, lazyDaemon, token /* token */, null /* listener */, 0 /* userId */,
- TAG, cookie, TEST_SENSOR_ID, 0 /* statsModality */,
- 0 /* statsAction */, 0 /* statsClient */);
+ TAG, cookie, TEST_SENSOR_ID,
+ mock(BiometricLogger.class), mock(BiometricContext.class));
mProtoEnum = protoEnum;
}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/CompositeCallbackTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/CompositeCallbackTest.java
index 587bb600f409..092ca191acba 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/CompositeCallbackTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/CompositeCallbackTest.java
@@ -64,7 +64,7 @@ public class CompositeCallbackTest {
ClientMonitorCompositeCallback callback = new ClientMonitorCompositeCallback(callbacks);
callback.onClientStarted(mClientMonitor);
- final InOrder order = inOrder(expected);
+ final InOrder order = inOrder((Object[]) expected);
for (ClientMonitorCallback cb : expected) {
order.verify(cb).onClientStarted(eq(mClientMonitor));
}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/UserAwareBiometricSchedulerTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/UserAwareBiometricSchedulerTest.java
index 30777cd79a0c..52eee9a55cc7 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/UserAwareBiometricSchedulerTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/UserAwareBiometricSchedulerTest.java
@@ -41,6 +41,9 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.test.filters.SmallTest;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -66,6 +69,10 @@ public class UserAwareBiometricSchedulerTest {
private Context mContext;
@Mock
private IBiometricService mBiometricService;
+ @Mock
+ private BiometricLogger mBiometricLogger;
+ @Mock
+ private BiometricContext mBiometricContext;
private TestUserStartedCallback mUserStartedCallback = new TestUserStartedCallback();
private TestUserStoppedCallback mUserStoppedCallback = new TestUserStoppedCallback();
@@ -88,7 +95,8 @@ public class UserAwareBiometricSchedulerTest {
@Override
public StopUserClient<?> getStopUserClient(int userId) {
return new TestStopUserClient(mContext, Object::new, mToken, userId,
- TEST_SENSOR_ID, mUserStoppedCallback);
+ TEST_SENSOR_ID, mBiometricLogger, mBiometricContext,
+ mUserStoppedCallback);
}
@NonNull
@@ -96,7 +104,8 @@ public class UserAwareBiometricSchedulerTest {
public StartUserClient<?, ?> getStartUserClient(int newUserId) {
mStartUserClientCount++;
return new TestStartUserClient(mContext, Object::new, mToken, newUserId,
- TEST_SENSOR_ID, mUserStartedCallback, mStartOperationsFinish);
+ TEST_SENSOR_ID, mBiometricLogger, mBiometricContext,
+ mUserStartedCallback, mStartOperationsFinish);
}
},
CoexCoordinator.getInstance());
@@ -226,8 +235,10 @@ public class UserAwareBiometricSchedulerTest {
private static class TestStopUserClient extends StopUserClient<Object> {
public TestStopUserClient(@NonNull Context context,
@NonNull Supplier<Object> lazyDaemon, @Nullable IBinder token, int userId,
- int sensorId, @NonNull UserStoppedCallback callback) {
- super(context, lazyDaemon, token, userId, sensorId, callback);
+ int sensorId, @NonNull BiometricLogger logger,
+ @NonNull BiometricContext biometricContext,
+ @NonNull UserStoppedCallback callback) {
+ super(context, lazyDaemon, token, userId, sensorId, logger, biometricContext, callback);
}
@Override
@@ -254,8 +265,10 @@ public class UserAwareBiometricSchedulerTest {
public TestStartUserClient(@NonNull Context context,
@NonNull Supplier<Object> lazyDaemon, @Nullable IBinder token, int userId,
- int sensorId, @NonNull UserStartedCallback<Object> callback, boolean shouldFinish) {
- super(context, lazyDaemon, token, userId, sensorId, callback);
+ int sensorId, @NonNull BiometricLogger logger,
+ @NonNull BiometricContext biometricContext,
+ @NonNull UserStartedCallback<Object> callback, boolean shouldFinish) {
+ super(context, lazyDaemon, token, userId, sensorId, logger, biometricContext, callback);
mShouldFinish = shouldFinish;
}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClientTest.java
new file mode 100644
index 000000000000..aba93b058669
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClientTest.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors.face.aidl;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.same;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.hardware.biometrics.common.OperationContext;
+import android.hardware.biometrics.face.ISession;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
+import android.testing.TestableContext;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
+import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
+import com.android.server.biometrics.sensors.LockoutCache;
+import com.android.server.biometrics.sensors.face.UsageStats;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.InOrder;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+@Presubmit
+@SmallTest
+public class FaceAuthenticationClientTest {
+
+ private static final int USER_ID = 12;
+ private static final long OP_ID = 32;
+
+ @Rule
+ public final TestableContext mContext = new TestableContext(
+ InstrumentationRegistry.getInstrumentation().getTargetContext(), null);
+
+ @Mock
+ private ISession mHal;
+ @Mock
+ private IBinder mToken;
+ @Mock
+ private ClientMonitorCallbackConverter mClientMonitorCallbackConverter;
+ @Mock
+ private BiometricLogger mBiometricLogger;
+ @Mock
+ private BiometricContext mBiometricContext;
+ @Mock
+ private LockoutCache mLockoutCache;
+ @Mock
+ private UsageStats mUsageStats;
+ @Mock
+ private ClientMonitorCallback mCallback;
+ @Mock
+ private Sensor.HalSessionCallback mHalSessionCallback;
+ @Captor
+ private ArgumentCaptor<OperationContext> mOperationContextCaptor;
+
+ @Rule
+ public final MockitoRule mockito = MockitoJUnit.rule();
+
+ @Before
+ public void setup() {
+ when(mBiometricContext.updateContext(any(), anyBoolean())).thenAnswer(
+ i -> i.getArgument(0));
+ }
+
+ @Test
+ public void authNoContext_v1() throws RemoteException {
+ final FaceAuthenticationClient client = createClient(1);
+ client.start(mCallback);
+
+ verify(mHal).authenticate(eq(OP_ID));
+ verify(mHal, never()).authenticateWithContext(anyLong(), any());
+ }
+
+ @Test
+ public void authWithContext_v2() throws RemoteException {
+ final FaceAuthenticationClient client = createClient(2);
+ client.start(mCallback);
+
+ InOrder order = inOrder(mHal, mBiometricContext);
+ order.verify(mBiometricContext).updateContext(
+ mOperationContextCaptor.capture(), anyBoolean());
+ order.verify(mHal).authenticateWithContext(
+ eq(OP_ID), same(mOperationContextCaptor.getValue()));
+ verify(mHal, never()).authenticate(anyLong());
+ }
+
+ private FaceAuthenticationClient createClient(int version) throws RemoteException {
+ when(mHal.getInterfaceVersion()).thenReturn(version);
+
+ final AidlSession aidl = new AidlSession(version, mHal, USER_ID, mHalSessionCallback);
+ return new FaceAuthenticationClient(mContext, () -> aidl, mToken,
+ 2 /* requestId */, mClientMonitorCallbackConverter, 5 /* targetUserId */, OP_ID,
+ false /* restricted */, "test-owner", 4 /* cookie */,
+ false /* requireConfirmation */, 9 /* sensorId */,
+ mBiometricLogger, mBiometricContext, true /* isStrongBiometric */,
+ mUsageStats, mLockoutCache, false /* allowBackgroundAuthentication */,
+ false /* isKeyguardBypassEnabled */, null /* sensorPrivacyManager */);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceDetectClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceDetectClientTest.java
new file mode 100644
index 000000000000..25135c6670db
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceDetectClientTest.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors.face.aidl;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.same;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.hardware.biometrics.common.OperationContext;
+import android.hardware.biometrics.face.ISession;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
+import android.testing.TestableContext;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
+import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.InOrder;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+@Presubmit
+@SmallTest
+public class FaceDetectClientTest {
+
+ private static final int USER_ID = 12;
+
+ @Rule
+ public final TestableContext mContext = new TestableContext(
+ InstrumentationRegistry.getInstrumentation().getTargetContext(), null);
+
+ @Mock
+ private ISession mHal;
+ @Mock
+ private IBinder mToken;
+ @Mock
+ private ClientMonitorCallbackConverter mClientMonitorCallbackConverter;
+ @Mock
+ private BiometricLogger mBiometricLogger;
+ @Mock
+ private BiometricContext mBiometricContext;
+ @Mock
+ private ClientMonitorCallback mCallback;
+ @Mock
+ private Sensor.HalSessionCallback mHalSessionCallback;
+ @Captor
+ private ArgumentCaptor<OperationContext> mOperationContextCaptor;
+
+ @Rule
+ public final MockitoRule mockito = MockitoJUnit.rule();
+
+ @Before
+ public void setup() {
+ when(mBiometricContext.updateContext(any(), anyBoolean())).thenAnswer(
+ i -> i.getArgument(0));
+ }
+
+ @Test
+ public void detectNoContext_v1() throws RemoteException {
+ final FaceDetectClient client = createClient(1);
+ client.start(mCallback);
+
+ verify(mHal).detectInteraction();
+ verify(mHal, never()).detectInteractionWithContext(any());
+ }
+
+ @Test
+ public void detectWithContext_v2() throws RemoteException {
+ final FaceDetectClient client = createClient(2);
+ client.start(mCallback);
+
+ InOrder order = inOrder(mHal, mBiometricContext);
+ order.verify(mBiometricContext).updateContext(
+ mOperationContextCaptor.capture(), anyBoolean());
+ order.verify(mHal).detectInteractionWithContext(same(mOperationContextCaptor.getValue()));
+ verify(mHal, never()).detectInteraction();
+ }
+
+ private FaceDetectClient createClient(int version) throws RemoteException {
+ when(mHal.getInterfaceVersion()).thenReturn(version);
+
+ final AidlSession aidl = new AidlSession(version, mHal, USER_ID, mHalSessionCallback);
+ return new FaceDetectClient(mContext, () -> aidl, mToken,
+ 99 /* requestId */, mClientMonitorCallbackConverter, USER_ID,
+ "own-it", 5 /* sensorId */, mBiometricLogger, mBiometricContext,
+ false /* isStrongBiometric */, null /* sensorPrivacyManager */);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClientTest.java
new file mode 100644
index 000000000000..38e048bc1ba7
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClientTest.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors.face.aidl;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyByte;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.same;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.hardware.biometrics.common.OperationContext;
+import android.hardware.biometrics.face.ISession;
+import android.hardware.face.Face;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
+import android.testing.TestableContext;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
+import com.android.server.biometrics.sensors.BiometricUtils;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
+import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.InOrder;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+@Presubmit
+@SmallTest
+public class FaceEnrollClientTest {
+
+ private static final byte[] HAT = new byte[69];
+ private static final int USER_ID = 12;
+
+ @Rule
+ public final TestableContext mContext = new TestableContext(
+ InstrumentationRegistry.getInstrumentation().getTargetContext(), null);
+
+ @Mock
+ private ISession mHal;
+ @Mock
+ private IBinder mToken;
+ @Mock
+ private ClientMonitorCallbackConverter mClientMonitorCallbackConverter;
+ @Mock
+ private BiometricLogger mBiometricLogger;
+ @Mock
+ private BiometricContext mBiometricContext;
+ @Mock
+ private BiometricUtils<Face> mUtils;
+ @Mock
+ private ClientMonitorCallback mCallback;
+ @Mock
+ private Sensor.HalSessionCallback mHalSessionCallback;
+ @Captor
+ private ArgumentCaptor<OperationContext> mOperationContextCaptor;
+
+ @Rule
+ public final MockitoRule mockito = MockitoJUnit.rule();
+
+ @Before
+ public void setup() {
+ when(mBiometricContext.updateContext(any(), anyBoolean())).thenAnswer(
+ i -> i.getArgument(0));
+ }
+
+ @Test
+ public void enrollNoContext_v1() throws RemoteException {
+ final FaceEnrollClient client = createClient(1);
+ client.start(mCallback);
+
+ verify(mHal).enroll(any(), anyByte(), any(), any());
+ verify(mHal, never()).enrollWithContext(any(), anyByte(), any(), any(), any());
+ }
+
+ @Test
+ public void enrollWithContext_v2() throws RemoteException {
+ final FaceEnrollClient client = createClient(2);
+ client.start(mCallback);
+
+ InOrder order = inOrder(mHal, mBiometricContext);
+ order.verify(mBiometricContext).updateContext(
+ mOperationContextCaptor.capture(), anyBoolean());
+ order.verify(mHal).enrollWithContext(any(), anyByte(), any(), any(),
+ same(mOperationContextCaptor.getValue()));
+ verify(mHal, never()).enroll(any(), anyByte(), any(), any());
+ }
+
+ private FaceEnrollClient createClient(int version) throws RemoteException {
+ when(mHal.getInterfaceVersion()).thenReturn(version);
+
+ final AidlSession aidl = new AidlSession(version, mHal, USER_ID, mHalSessionCallback);
+ return new FaceEnrollClient(mContext, () -> aidl, mToken, mClientMonitorCallbackConverter,
+ USER_ID, HAT, "com.foo.bar", 44 /* requestId */,
+ mUtils, new int[0] /* disabledFeatures */, 6 /* timeoutSec */,
+ null /* previewSurface */, 8 /* sensorId */,
+ mBiometricLogger, mBiometricContext, 5 /* maxTemplatesPerUser */,
+ true /* debugConsent */);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceProviderTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceProviderTest.java
index 0ac00aafbf6c..12b8264fc20c 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceProviderTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceProviderTest.java
@@ -37,6 +37,7 @@ import androidx.annotation.NonNull;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
+import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.sensors.BiometricScheduler;
import com.android.server.biometrics.sensors.HalClientMonitor;
import com.android.server.biometrics.sensors.LockoutResetDispatcher;
@@ -60,6 +61,8 @@ public class FaceProviderTest {
private UserManager mUserManager;
@Mock
private IFace mDaemon;
+ @Mock
+ private BiometricContext mBiometricContext;
private SensorProps[] mSensorProps;
private LockoutResetDispatcher mLockoutResetDispatcher;
@@ -89,7 +92,7 @@ public class FaceProviderTest {
mLockoutResetDispatcher = new LockoutResetDispatcher(mContext);
mFaceProvider = new TestableFaceProvider(mDaemon, mContext, mSensorProps, TAG,
- mLockoutResetDispatcher);
+ mLockoutResetDispatcher, mBiometricContext);
}
@SuppressWarnings("rawtypes")
@@ -139,8 +142,9 @@ public class FaceProviderTest {
@NonNull Context context,
@NonNull SensorProps[] props,
@NonNull String halInstanceName,
- @NonNull LockoutResetDispatcher lockoutResetDispatcher) {
- super(context, props, halInstanceName, lockoutResetDispatcher);
+ @NonNull LockoutResetDispatcher lockoutResetDispatcher,
+ @NonNull BiometricContext biometricContext) {
+ super(context, props, halInstanceName, lockoutResetDispatcher, biometricContext);
mDaemon = daemon;
}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/SensorTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/SensorTest.java
index 61e4776e9c50..b60324e88f15 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/SensorTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/SensorTest.java
@@ -32,6 +32,8 @@ import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.BiometricScheduler;
import com.android.server.biometrics.sensors.CoexCoordinator;
import com.android.server.biometrics.sensors.LockoutCache;
@@ -66,6 +68,10 @@ public class SensorTest {
private Sensor.HalSessionCallback.Callback mHalSessionCallback;
@Mock
private LockoutResetDispatcher mLockoutResetDispatcher;
+ @Mock
+ private BiometricLogger mBiometricLogger;
+ @Mock
+ private BiometricContext mBiometricContext;
private final TestLooper mLooper = new TestLooper();
private final LockoutCache mLockoutCache = new LockoutCache();
@@ -102,7 +108,8 @@ public class SensorTest {
mScheduler.scheduleClientMonitor(new FaceResetLockoutClient(mContext,
() -> new AidlSession(1, mSession, USER_ID, mHalCallback),
- USER_ID, TAG, SENSOR_ID, HAT, mLockoutCache, mLockoutResetDispatcher));
+ USER_ID, TAG, SENSOR_ID, mBiometricLogger, mBiometricContext,
+ HAT, mLockoutCache, mLockoutResetDispatcher));
mLooper.dispatchAll();
verifyNotLocked();
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/Face10Test.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/Face10Test.java
index 21a7a8ae65b9..116d2d5a66a0 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/Face10Test.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/Face10Test.java
@@ -41,6 +41,7 @@ import android.platform.test.annotations.Presubmit;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
+import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.sensors.BiometricScheduler;
import com.android.server.biometrics.sensors.LockoutResetDispatcher;
@@ -70,6 +71,8 @@ public class Face10Test {
private UserManager mUserManager;
@Mock
private BiometricScheduler mScheduler;
+ @Mock
+ private BiometricContext mBiometricContext;
private final Handler mHandler = new Handler(Looper.getMainLooper());
private LockoutResetDispatcher mLockoutResetDispatcher;
@@ -100,7 +103,8 @@ public class Face10Test {
resetLockoutRequiresChallenge);
Face10.sSystemClock = Clock.fixed(Instant.ofEpochMilli(100), ZoneId.of("PST"));
- mFace10 = new Face10(mContext, sensorProps, mLockoutResetDispatcher, mHandler, mScheduler);
+ mFace10 = new Face10(mContext, sensorProps, mLockoutResetDispatcher, mHandler, mScheduler,
+ mBiometricContext);
mBinder = new Binder();
}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/FaceGenerateChallengeClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/FaceGenerateChallengeClientTest.java
index 931fad14888e..ec083294c0bd 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/FaceGenerateChallengeClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/FaceGenerateChallengeClientTest.java
@@ -34,6 +34,8 @@ import android.platform.test.annotations.Presubmit;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.filters.SmallTest;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
@@ -62,6 +64,10 @@ public class FaceGenerateChallengeClientTest {
private IFaceServiceReceiver mOtherReceiver;
@Mock
private ClientMonitorCallback mMonitorCallback;
+ @Mock
+ private BiometricLogger mBiometricLogger;
+ @Mock
+ private BiometricContext mBiometricContext;
private FaceGenerateChallengeClient mClient;
@@ -75,7 +81,7 @@ public class FaceGenerateChallengeClientTest {
mClient = new FaceGenerateChallengeClient(mContext, () -> mIBiometricsFace, new Binder(),
new ClientMonitorCallbackConverter(mClientReceiver), USER_ID,
- TAG, SENSOR_ID, START_TIME);
+ TAG, SENSOR_ID, mBiometricLogger, mBiometricContext , START_TIME);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java
new file mode 100644
index 000000000000..de0f038e8ec5
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java
@@ -0,0 +1,288 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors.fingerprint.aidl;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyFloat;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.anyLong;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.same;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.hardware.biometrics.common.OperationContext;
+import android.hardware.biometrics.fingerprint.ISession;
+import android.hardware.biometrics.fingerprint.PointerContext;
+import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
+import android.hardware.fingerprint.ISidefpsController;
+import android.hardware.fingerprint.IUdfpsOverlayController;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
+import android.testing.TestableContext;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
+import com.android.server.biometrics.log.CallbackWithProbe;
+import com.android.server.biometrics.log.Probe;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
+import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
+import com.android.server.biometrics.sensors.LockoutCache;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.InOrder;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import java.util.function.Consumer;
+
+@Presubmit
+@SmallTest
+public class FingerprintAuthenticationClientTest {
+
+ private static final int USER_ID = 8;
+ private static final long OP_ID = 7;
+ private static final int POINTER_ID = 0;
+ private static final int TOUCH_X = 8;
+ private static final int TOUCH_Y = 20;
+ private static final float TOUCH_MAJOR = 4.4f;
+ private static final float TOUCH_MINOR = 5.5f;
+
+ @Rule
+ public final TestableContext mContext = new TestableContext(
+ InstrumentationRegistry.getInstrumentation().getTargetContext(), null);
+
+ @Mock
+ private ISession mHal;
+ @Mock
+ private IBinder mToken;
+ @Mock
+ private ClientMonitorCallbackConverter mClientMonitorCallbackConverter;
+ @Mock
+ private BiometricLogger mBiometricLogger;
+ @Mock
+ private BiometricContext mBiometricContext;
+ @Mock
+ private LockoutCache mLockoutCache;
+ @Mock
+ private IUdfpsOverlayController mUdfpsOverlayController;
+ @Mock
+ private ISidefpsController mSideFpsController;
+ @Mock
+ private FingerprintSensorPropertiesInternal mSensorProps;
+ @Mock
+ private ClientMonitorCallback mCallback;
+ @Mock
+ private Sensor.HalSessionCallback mHalSessionCallback;
+ @Mock
+ private Probe mLuxProbe;
+ @Captor
+ private ArgumentCaptor<OperationContext> mOperationContextCaptor;
+ @Captor
+ private ArgumentCaptor<PointerContext> mPointerContextCaptor;
+ @Captor
+ private ArgumentCaptor<Consumer<OperationContext>> mContextInjector;
+
+ @Rule
+ public final MockitoRule mockito = MockitoJUnit.rule();
+
+ @Before
+ public void setup() {
+ when(mBiometricLogger.createALSCallback(anyBoolean())).thenAnswer(i ->
+ new CallbackWithProbe<>(mLuxProbe, i.getArgument(0)));
+ when(mBiometricContext.updateContext(any(), anyBoolean())).thenAnswer(
+ i -> i.getArgument(0));
+ }
+
+ @Test
+ public void authNoContext_v1() throws RemoteException {
+ final FingerprintAuthenticationClient client = createClient(1);
+ client.start(mCallback);
+
+ verify(mHal).authenticate(eq(OP_ID));
+ verify(mHal, never()).authenticateWithContext(anyLong(), any());
+ }
+
+ @Test
+ public void authWithContext_v2() throws RemoteException {
+ final FingerprintAuthenticationClient client = createClient(2);
+ client.start(mCallback);
+
+ InOrder order = inOrder(mHal, mBiometricContext);
+ order.verify(mBiometricContext).updateContext(
+ mOperationContextCaptor.capture(), anyBoolean());
+ order.verify(mHal).authenticateWithContext(
+ eq(OP_ID), same(mOperationContextCaptor.getValue()));
+ verify(mHal, never()).authenticate(anyLong());
+ }
+
+ @Test
+ public void pointerUp_v1() throws RemoteException {
+ final FingerprintAuthenticationClient client = createClient(1);
+ client.start(mCallback);
+ client.onPointerUp();
+
+ verify(mHal).onPointerUp(eq(POINTER_ID));
+ verify(mHal, never()).onPointerUpWithContext(any());
+ }
+
+ @Test
+ public void pointerDown_v1() throws RemoteException {
+ final FingerprintAuthenticationClient client = createClient(1);
+ client.start(mCallback);
+ client.onPointerDown(TOUCH_X, TOUCH_Y, TOUCH_MAJOR, TOUCH_MINOR);
+
+ verify(mHal).onPointerDown(eq(0),
+ eq(TOUCH_X), eq(TOUCH_Y), eq(TOUCH_MAJOR), eq(TOUCH_MINOR));
+ verify(mHal, never()).onPointerDownWithContext(any());
+ }
+
+ @Test
+ public void pointerUpWithContext_v2() throws RemoteException {
+ final FingerprintAuthenticationClient client = createClient(2);
+ client.start(mCallback);
+ client.onPointerUp();
+
+ verify(mHal).onPointerUpWithContext(mPointerContextCaptor.capture());
+ verify(mHal, never()).onPointerUp(eq(POINTER_ID));
+
+ final PointerContext pContext = mPointerContextCaptor.getValue();
+ assertThat(pContext.pointerId).isEqualTo(POINTER_ID);
+ }
+
+ @Test
+ public void pointerDownWithContext_v2() throws RemoteException {
+ final FingerprintAuthenticationClient client = createClient(2);
+ client.start(mCallback);
+ client.onPointerDown(TOUCH_X, TOUCH_Y, TOUCH_MAJOR, TOUCH_MINOR);
+
+ verify(mHal).onPointerDownWithContext(mPointerContextCaptor.capture());
+ verify(mHal, never()).onPointerDown(anyInt(), anyInt(), anyInt(), anyFloat(), anyFloat());
+
+ final PointerContext pContext = mPointerContextCaptor.getValue();
+ assertThat(pContext.pointerId).isEqualTo(POINTER_ID);
+ }
+
+ @Test
+ public void luxProbeWhenFingerDown() throws RemoteException {
+ final FingerprintAuthenticationClient client = createClient();
+ client.start(mCallback);
+
+ client.onPointerDown(TOUCH_X, TOUCH_Y, TOUCH_MAJOR, TOUCH_MINOR);
+ verify(mLuxProbe).enable();
+
+ client.onAcquired(2, 0);
+ verify(mLuxProbe, never()).disable();
+
+ client.onPointerUp();
+ verify(mLuxProbe).disable();
+
+ client.onPointerDown(TOUCH_X, TOUCH_Y, TOUCH_MAJOR, TOUCH_MINOR);
+ verify(mLuxProbe, times(2)).enable();
+ }
+
+ @Test
+ public void notifyHalWhenContextChanges() throws RemoteException {
+ final FingerprintAuthenticationClient client = createClient();
+ client.start(mCallback);
+
+ verify(mHal).authenticateWithContext(eq(OP_ID), mOperationContextCaptor.capture());
+ OperationContext opContext = mOperationContextCaptor.getValue();
+
+ // fake an update to the context
+ verify(mBiometricContext).subscribe(eq(opContext), mContextInjector.capture());
+ mContextInjector.getValue().accept(opContext);
+ verify(mHal).onContextChanged(eq(opContext));
+
+ client.stopHalOperation();
+ verify(mBiometricContext).unsubscribe(same(opContext));
+ }
+
+ @Test
+ public void showHideOverlay_cancel() throws RemoteException {
+ showHideOverlay(c -> c.cancel());
+ }
+
+ @Test
+ public void showHideOverlay_stop() throws RemoteException {
+ showHideOverlay(c -> c.stopHalOperation());
+ }
+
+ @Test
+ public void showHideOverlay_error() throws RemoteException {
+ showHideOverlay(c -> c.onError(0, 0));
+ verify(mCallback).onClientFinished(any(), eq(false));
+ }
+
+ @Test
+ public void showHideOverlay_lockout() throws RemoteException {
+ showHideOverlay(c -> c.onLockoutTimed(5000));
+ }
+
+ @Test
+ public void showHideOverlay_lockoutPerm() throws RemoteException {
+ showHideOverlay(c -> c.onLockoutPermanent());
+ }
+
+ private void showHideOverlay(Consumer<FingerprintAuthenticationClient> block)
+ throws RemoteException {
+ final FingerprintAuthenticationClient client = createClient();
+
+ client.start(mCallback);
+
+ verify(mUdfpsOverlayController).showUdfpsOverlay(anyInt(), anyInt(), any());
+ verify(mSideFpsController).show(anyInt(), anyInt());
+
+ block.accept(client);
+
+ verify(mUdfpsOverlayController).hideUdfpsOverlay(anyInt());
+ verify(mSideFpsController).hide(anyInt());
+ }
+
+ private FingerprintAuthenticationClient createClient() throws RemoteException {
+ return createClient(100);
+ }
+
+ private FingerprintAuthenticationClient createClient(int version) throws RemoteException {
+ when(mHal.getInterfaceVersion()).thenReturn(version);
+
+ final AidlSession aidl = new AidlSession(version, mHal, USER_ID, mHalSessionCallback);
+ return new FingerprintAuthenticationClient(mContext, () -> aidl, mToken,
+ 2 /* requestId */, mClientMonitorCallbackConverter, 5 /* targetUserId */, OP_ID,
+ false /* restricted */, "test-owner", 4 /* cookie */, false /* requireConfirmation */,
+ 9 /* sensorId */, mBiometricLogger, mBiometricContext,
+ true /* isStrongBiometric */,
+ null /* taskStackListener */, mLockoutCache,
+ mUdfpsOverlayController, mSideFpsController,
+ false /* allowBackgroundAuthentication */, mSensorProps);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClientTest.java
new file mode 100644
index 000000000000..93cbef19aca9
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClientTest.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors.fingerprint.aidl;
+
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.same;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.hardware.biometrics.common.OperationContext;
+import android.hardware.biometrics.fingerprint.ISession;
+import android.hardware.fingerprint.IUdfpsOverlayController;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
+import android.testing.TestableContext;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
+import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.InOrder;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+@Presubmit
+@SmallTest
+public class FingerprintDetectClientTest {
+
+ private static final int USER_ID = 8;
+
+ @Rule
+ public final TestableContext mContext = new TestableContext(
+ InstrumentationRegistry.getInstrumentation().getTargetContext(), null);
+
+ @Mock
+ private ISession mHal;
+ @Mock
+ private IBinder mToken;
+ @Mock
+ private ClientMonitorCallbackConverter mClientMonitorCallbackConverter;
+ @Mock
+ private BiometricLogger mBiometricLogger;
+ @Mock
+ private BiometricContext mBiometricContext;
+ @Mock
+ private IUdfpsOverlayController mUdfpsOverlayController;
+ @Mock
+ private ClientMonitorCallback mCallback;
+ @Mock
+ private Sensor.HalSessionCallback mHalSessionCallback;
+ @Captor
+ private ArgumentCaptor<OperationContext> mOperationContextCaptor;
+
+ @Rule
+ public final MockitoRule mockito = MockitoJUnit.rule();
+
+ @Before
+ public void setup() {
+ when(mBiometricContext.updateContext(any(), anyBoolean())).thenAnswer(
+ i -> i.getArgument(0));
+ }
+
+ @Test
+ public void detectNoContext_v1() throws RemoteException {
+ final FingerprintDetectClient client = createClient(1);
+
+ client.start(mCallback);
+
+ verify(mHal).detectInteraction();
+ verify(mHal, never()).detectInteractionWithContext(any());
+ }
+
+ @Test
+ public void detectNoContext_v2() throws RemoteException {
+ final FingerprintDetectClient client = createClient(2);
+
+ client.start(mCallback);
+
+ InOrder order = inOrder(mHal, mBiometricContext);
+ order.verify(mBiometricContext).updateContext(
+ mOperationContextCaptor.capture(), anyBoolean());
+ order.verify(mHal).detectInteractionWithContext(same(mOperationContextCaptor.getValue()));
+ verify(mHal, never()).detectInteraction();
+ }
+
+ private FingerprintDetectClient createClient(int version) throws RemoteException {
+ when(mHal.getInterfaceVersion()).thenReturn(version);
+
+ final AidlSession aidl = new AidlSession(version, mHal, USER_ID, mHalSessionCallback);
+ return new FingerprintDetectClient(mContext, () -> aidl, mToken,
+ 6 /* requestId */, mClientMonitorCallbackConverter, 2 /* userId */,
+ "a-test", 1 /* sensorId */, mBiometricLogger, mBiometricContext,
+ mUdfpsOverlayController, true /* isStrongBiometric */);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java
new file mode 100644
index 000000000000..5a96f5cca52a
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java
@@ -0,0 +1,282 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors.fingerprint.aidl;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyFloat;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.same;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.hardware.biometrics.common.OperationContext;
+import android.hardware.biometrics.fingerprint.ISession;
+import android.hardware.biometrics.fingerprint.PointerContext;
+import android.hardware.fingerprint.Fingerprint;
+import android.hardware.fingerprint.FingerprintManager;
+import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
+import android.hardware.fingerprint.ISidefpsController;
+import android.hardware.fingerprint.IUdfpsOverlayController;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
+import android.testing.TestableContext;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
+import com.android.server.biometrics.log.CallbackWithProbe;
+import com.android.server.biometrics.log.Probe;
+import com.android.server.biometrics.sensors.BiometricUtils;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
+import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.InOrder;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import java.util.function.Consumer;
+
+@Presubmit
+@SmallTest
+public class FingerprintEnrollClientTest {
+
+ private static final byte[] HAT = new byte[69];
+ private static final int USER_ID = 8;
+ private static final int POINTER_ID = 0;
+ private static final int TOUCH_X = 8;
+ private static final int TOUCH_Y = 20;
+ private static final float TOUCH_MAJOR = 4.4f;
+ private static final float TOUCH_MINOR = 5.5f;
+
+ @Rule
+ public final TestableContext mContext = new TestableContext(
+ InstrumentationRegistry.getInstrumentation().getTargetContext(), null);
+
+ @Mock
+ private ISession mHal;
+ @Mock
+ private IBinder mToken;
+ @Mock
+ private ClientMonitorCallbackConverter mClientMonitorCallbackConverter;
+ @Mock
+ private BiometricLogger mBiometricLogger;
+ @Mock
+ private BiometricContext mBiometricContext;
+ @Mock
+ private BiometricUtils<Fingerprint> mBiometricUtils;
+ @Mock
+ private IUdfpsOverlayController mUdfpsOverlayController;
+ @Mock
+ private ISidefpsController mSideFpsController;
+ @Mock
+ private FingerprintSensorPropertiesInternal mSensorProps;
+ @Mock
+ private ClientMonitorCallback mCallback;
+ @Mock
+ private Sensor.HalSessionCallback mHalSessionCallback;
+ @Mock
+ private Probe mLuxProbe;
+ @Captor
+ private ArgumentCaptor<OperationContext> mOperationContextCaptor;
+ @Captor
+ private ArgumentCaptor<PointerContext> mPointerContextCaptor;
+ @Captor
+ private ArgumentCaptor<Consumer<OperationContext>> mContextInjector;
+
+ @Rule
+ public final MockitoRule mockito = MockitoJUnit.rule();
+
+ @Before
+ public void setup() {
+ when(mBiometricLogger.createALSCallback(anyBoolean())).thenAnswer(i ->
+ new CallbackWithProbe<>(mLuxProbe, i.getArgument(0)));
+ when(mBiometricContext.updateContext(any(), anyBoolean())).thenAnswer(
+ i -> i.getArgument(0));
+ }
+
+ @Test
+ public void enrollNoContext_v1() throws RemoteException {
+ final FingerprintEnrollClient client = createClient(1);
+
+ client.start(mCallback);
+
+ verify(mHal).enroll(any());
+ verify(mHal, never()).enrollWithContext(any(), any());
+ }
+
+ @Test
+ public void enrollWithContext_v2() throws RemoteException {
+ final FingerprintEnrollClient client = createClient(2);
+
+ client.start(mCallback);
+
+ InOrder order = inOrder(mHal, mBiometricContext);
+ order.verify(mBiometricContext).updateContext(
+ mOperationContextCaptor.capture(), anyBoolean());
+ order.verify(mHal).enrollWithContext(any(), same(mOperationContextCaptor.getValue()));
+ verify(mHal, never()).enroll(any());
+ }
+
+ @Test
+ public void pointerUp_v1() throws RemoteException {
+ final FingerprintEnrollClient client = createClient(1);
+ client.start(mCallback);
+ client.onPointerUp();
+
+ verify(mHal).onPointerUp(eq(POINTER_ID));
+ verify(mHal, never()).onPointerUpWithContext(any());
+ }
+
+ @Test
+ public void pointerDown_v1() throws RemoteException {
+ final FingerprintEnrollClient client = createClient(1);
+ client.start(mCallback);
+ client.onPointerDown(TOUCH_X, TOUCH_Y, TOUCH_MAJOR, TOUCH_MINOR);
+
+ verify(mHal).onPointerDown(eq(0),
+ eq(TOUCH_X), eq(TOUCH_Y), eq(TOUCH_MAJOR), eq(TOUCH_MINOR));
+ verify(mHal, never()).onPointerDownWithContext(any());
+ }
+
+ @Test
+ public void pointerUpWithContext_v2() throws RemoteException {
+ final FingerprintEnrollClient client = createClient(2);
+ client.start(mCallback);
+ client.onPointerUp();
+
+ verify(mHal).onPointerUpWithContext(mPointerContextCaptor.capture());
+ verify(mHal, never()).onPointerUp(eq(POINTER_ID));
+
+ final PointerContext pContext = mPointerContextCaptor.getValue();
+ assertThat(pContext.pointerId).isEqualTo(POINTER_ID);
+ }
+
+ @Test
+ public void pointerDownWithContext_v2() throws RemoteException {
+ final FingerprintEnrollClient client = createClient(2);
+ client.start(mCallback);
+ client.onPointerDown(TOUCH_X, TOUCH_Y, TOUCH_MAJOR, TOUCH_MINOR);
+
+ verify(mHal).onPointerDownWithContext(mPointerContextCaptor.capture());
+ verify(mHal, never()).onPointerDown(anyInt(), anyInt(), anyInt(), anyFloat(), anyFloat());
+
+ final PointerContext pContext = mPointerContextCaptor.getValue();
+ assertThat(pContext.pointerId).isEqualTo(POINTER_ID);
+ }
+
+ @Test
+ public void luxProbeWhenFingerDown() throws RemoteException {
+ final FingerprintEnrollClient client = createClient();
+ client.start(mCallback);
+
+ client.onPointerDown(TOUCH_X, TOUCH_Y, TOUCH_MAJOR, TOUCH_MINOR);
+ verify(mLuxProbe).enable();
+
+ client.onAcquired(2, 0);
+ verify(mLuxProbe, never()).disable();
+
+ client.onPointerUp();
+ verify(mLuxProbe).disable();
+
+ client.onPointerDown(TOUCH_X, TOUCH_Y, TOUCH_MAJOR, TOUCH_MINOR);
+ verify(mLuxProbe, times(2)).enable();
+ }
+
+ @Test
+ public void notifyHalWhenContextChanges() throws RemoteException {
+ final FingerprintEnrollClient client = createClient();
+ client.start(mCallback);
+
+ verify(mHal).enrollWithContext(any(), mOperationContextCaptor.capture());
+ OperationContext opContext = mOperationContextCaptor.getValue();
+
+ // fake an update to the context
+ verify(mBiometricContext).subscribe(eq(opContext), mContextInjector.capture());
+ mContextInjector.getValue().accept(opContext);
+ verify(mHal).onContextChanged(eq(opContext));
+
+ client.stopHalOperation();
+ verify(mBiometricContext).unsubscribe(same(opContext));
+ }
+
+ @Test
+ public void showHideOverlay_cancel() throws RemoteException {
+ showHideOverlay(c -> c.cancel());
+ }
+
+ @Test
+ public void showHideOverlay_stop() throws RemoteException {
+ showHideOverlay(c -> c.stopHalOperation());
+ }
+
+ @Test
+ public void showHideOverlay_error() throws RemoteException {
+ showHideOverlay(c -> c.onError(0, 0));
+ verify(mCallback).onClientFinished(any(), eq(false));
+ }
+
+ @Test
+ public void showHideOverlay_result() throws RemoteException {
+ showHideOverlay(c -> c.onEnrollResult(new Fingerprint("", 1, 1), 0));
+ }
+
+ private void showHideOverlay(Consumer<FingerprintEnrollClient> block)
+ throws RemoteException {
+ final FingerprintEnrollClient client = createClient();
+
+ client.start(mCallback);
+
+ verify(mUdfpsOverlayController).showUdfpsOverlay(anyInt(), anyInt(), any());
+ verify(mSideFpsController).show(anyInt(), anyInt());
+
+ block.accept(client);
+
+ verify(mUdfpsOverlayController).hideUdfpsOverlay(anyInt());
+ verify(mSideFpsController).hide(anyInt());
+ }
+
+ private FingerprintEnrollClient createClient() throws RemoteException {
+ return createClient(500);
+ }
+
+ private FingerprintEnrollClient createClient(int version) throws RemoteException {
+ when(mHal.getInterfaceVersion()).thenReturn(version);
+
+ final AidlSession aidl = new AidlSession(version, mHal, USER_ID, mHalSessionCallback);
+ return new FingerprintEnrollClient(mContext, () -> aidl, mToken, 6 /* requestId */,
+ mClientMonitorCallbackConverter, 0 /* userId */,
+ HAT, "owner", mBiometricUtils, 8 /* sensorId */,
+ mBiometricLogger, mBiometricContext, mSensorProps, mUdfpsOverlayController,
+ mSideFpsController, 6 /* maxTemplatesPerUser */, FingerprintManager.ENROLL_ENROLL);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java
index 73f1516562bc..5a1a02eb39c8 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java
@@ -40,6 +40,7 @@ import androidx.annotation.NonNull;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
+import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.sensors.BiometricScheduler;
import com.android.server.biometrics.sensors.HalClientMonitor;
import com.android.server.biometrics.sensors.LockoutResetDispatcher;
@@ -71,6 +72,8 @@ public class FingerprintProviderTest {
private GestureAvailabilityDispatcher mGestureAvailabilityDispatcher;
@Mock
private FingerprintStateCallback mFingerprintStateCallback;
+ @Mock
+ private BiometricContext mBiometricContext;
private SensorProps[] mSensorProps;
private LockoutResetDispatcher mLockoutResetDispatcher;
@@ -105,7 +108,7 @@ public class FingerprintProviderTest {
mFingerprintProvider = new TestableFingerprintProvider(mDaemon, mContext,
mFingerprintStateCallback, mSensorProps, TAG, mLockoutResetDispatcher,
- mGestureAvailabilityDispatcher);
+ mGestureAvailabilityDispatcher, mBiometricContext);
}
@SuppressWarnings("rawtypes")
@@ -157,9 +160,10 @@ public class FingerprintProviderTest {
@NonNull SensorProps[] props,
@NonNull String halInstanceName,
@NonNull LockoutResetDispatcher lockoutResetDispatcher,
- @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher) {
+ @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher,
+ @NonNull BiometricContext biometricContext) {
super(context, fingerprintStateCallback, props, halInstanceName, lockoutResetDispatcher,
- gestureAvailabilityDispatcher);
+ gestureAvailabilityDispatcher, biometricContext);
mDaemon = daemon;
}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/SensorTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/SensorTest.java
index 8b7b484b8462..e1a4a2d9f969 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/SensorTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/SensorTest.java
@@ -32,6 +32,8 @@ import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.BiometricScheduler;
import com.android.server.biometrics.sensors.CoexCoordinator;
import com.android.server.biometrics.sensors.LockoutCache;
@@ -66,6 +68,10 @@ public class SensorTest {
private Sensor.HalSessionCallback.Callback mHalSessionCallback;
@Mock
private LockoutResetDispatcher mLockoutResetDispatcher;
+ @Mock
+ private BiometricLogger mLogger;
+ @Mock
+ private BiometricContext mBiometricContext;
private final TestLooper mLooper = new TestLooper();
private final LockoutCache mLockoutCache = new LockoutCache();
@@ -102,7 +108,8 @@ public class SensorTest {
mScheduler.scheduleClientMonitor(new FingerprintResetLockoutClient(mContext,
() -> new AidlSession(1, mSession, USER_ID, mHalCallback),
- USER_ID, TAG, SENSOR_ID, HAT, mLockoutCache, mLockoutResetDispatcher));
+ USER_ID, TAG, SENSOR_ID, mLogger, mBiometricContext, HAT, mLockoutCache,
+ mLockoutResetDispatcher));
mLooper.dispatchAll();
verifyNotLocked();
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21Test.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21Test.java
index f6b92097a9fb..529f994f2773 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21Test.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21Test.java
@@ -39,6 +39,7 @@ import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import com.android.internal.R;
+import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.sensors.BiometricScheduler;
import com.android.server.biometrics.sensors.LockoutResetDispatcher;
import com.android.server.biometrics.sensors.fingerprint.FingerprintStateCallback;
@@ -70,6 +71,8 @@ public class Fingerprint21Test {
private BiometricScheduler mScheduler;
@Mock
private FingerprintStateCallback mFingerprintStateCallback;
+ @Mock
+ private BiometricContext mBiometricContext;
private LockoutResetDispatcher mLockoutResetDispatcher;
private Fingerprint21 mFingerprint21;
@@ -101,7 +104,7 @@ public class Fingerprint21Test {
mFingerprint21 = new TestableFingerprint21(mContext, mFingerprintStateCallback, sensorProps,
mScheduler, new Handler(Looper.getMainLooper()), mLockoutResetDispatcher,
- mHalResultController);
+ mHalResultController, mBiometricContext);
}
@Test
@@ -126,9 +129,10 @@ public class Fingerprint21Test {
@NonNull FingerprintSensorPropertiesInternal sensorProps,
@NonNull BiometricScheduler scheduler, @NonNull Handler handler,
@NonNull LockoutResetDispatcher lockoutResetDispatcher,
- @NonNull HalResultController controller) {
+ @NonNull HalResultController controller,
+ @NonNull BiometricContext biometricContext) {
super(context, fingerprintStateCallback, sensorProps, scheduler, handler,
- lockoutResetDispatcher, controller);
+ lockoutResetDispatcher, controller, biometricContext);
}
@Override
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/InputControllerTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/InputControllerTest.java
index ff1b6f66ef85..83fa7ac02503 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/InputControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/InputControllerTest.java
@@ -17,17 +17,23 @@
package com.android.server.companion.virtual;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import android.hardware.display.DisplayManagerInternal;
+import android.hardware.input.IInputManager;
+import android.hardware.input.InputManager;
import android.hardware.input.InputManagerInternal;
import android.os.Binder;
import android.os.IBinder;
import android.os.IInputConstants;
import android.platform.test.annotations.Presubmit;
import android.view.Display;
+import android.view.DisplayInfo;
import androidx.test.runner.AndroidJUnit4;
@@ -46,18 +52,31 @@ public class InputControllerTest {
@Mock
private InputManagerInternal mInputManagerInternalMock;
@Mock
+ private DisplayManagerInternal mDisplayManagerInternalMock;
+ @Mock
private InputController.NativeWrapper mNativeWrapperMock;
+ @Mock
+ private IInputManager mIInputManagerMock;
private InputController mInputController;
@Before
- public void setUp() {
+ public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
doNothing().when(mInputManagerInternalMock).setVirtualMousePointerDisplayId(anyInt());
LocalServices.removeServiceForTest(InputManagerInternal.class);
LocalServices.addService(InputManagerInternal.class, mInputManagerInternalMock);
+ final DisplayInfo displayInfo = new DisplayInfo();
+ displayInfo.uniqueId = "uniqueId";
+ doReturn(displayInfo).when(mDisplayManagerInternalMock).getDisplayInfo(anyInt());
+ LocalServices.removeServiceForTest(DisplayManagerInternal.class);
+ LocalServices.addService(DisplayManagerInternal.class, mDisplayManagerInternalMock);
+
+ InputManager.resetInstance(mIInputManagerMock);
+ doNothing().when(mIInputManagerMock).addUniqueIdAssociation(anyString(), anyString());
+ doNothing().when(mIInputManagerMock).removeUniqueIdAssociation(anyString());
mInputController = new InputController(new Object(), mNativeWrapperMock);
}
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 e36263e247bf..33540c874c0a 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
@@ -25,6 +25,7 @@ import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.doCallRealMethod;
import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -57,6 +58,7 @@ import android.os.WorkSource;
import android.platform.test.annotations.Presubmit;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import android.view.DisplayInfo;
import android.view.KeyEvent;
import androidx.test.InstrumentationRegistry;
@@ -80,6 +82,8 @@ public class VirtualDeviceManagerServiceTest {
private static final int DISPLAY_ID = 2;
private static final int PRODUCT_ID = 10;
private static final int VENDOR_ID = 5;
+ private static final String UNIQUE_ID = "uniqueid";
+ private static final String PHYS = "phys";
private static final int HEIGHT = 1800;
private static final int WIDTH = 900;
private static final Binder BINDER = new Binder("binder");
@@ -116,6 +120,12 @@ public class VirtualDeviceManagerServiceTest {
LocalServices.removeServiceForTest(InputManagerInternal.class);
LocalServices.addService(InputManagerInternal.class, mInputManagerInternalMock);
+ final DisplayInfo displayInfo = new DisplayInfo();
+ displayInfo.uniqueId = UNIQUE_ID;
+ doReturn(displayInfo).when(mDisplayManagerInternalMock).getDisplayInfo(anyInt());
+ LocalServices.removeServiceForTest(DisplayManagerInternal.class);
+ LocalServices.addService(DisplayManagerInternal.class, mDisplayManagerInternalMock);
+
mContext = Mockito.spy(new ContextWrapper(InstrumentationRegistry.getTargetContext()));
doNothing().when(mContext).enforceCallingOrSelfPermission(
eq(Manifest.permission.CREATE_VIRTUAL_DEVICE), anyString());
@@ -150,11 +160,11 @@ public class VirtualDeviceManagerServiceTest {
mDeviceImpl.onVirtualDisplayCreatedLocked(displayId);
verify(mIPowerManagerMock, never()).acquireWakeLock(any(Binder.class), anyInt(),
nullable(String.class), nullable(String.class), nullable(WorkSource.class),
- nullable(String.class), anyInt());
+ nullable(String.class), anyInt(), eq(null));
TestableLooper.get(this).processAllMessages();
verify(mIPowerManagerMock, Mockito.times(1)).acquireWakeLock(any(Binder.class), anyInt(),
nullable(String.class), nullable(String.class), nullable(WorkSource.class),
- nullable(String.class), eq(displayId));
+ nullable(String.class), eq(displayId), eq(null));
}
@Test
@@ -167,7 +177,7 @@ public class VirtualDeviceManagerServiceTest {
TestableLooper.get(this).processAllMessages();
verify(mIPowerManagerMock, Mockito.times(1)).acquireWakeLock(any(Binder.class), anyInt(),
nullable(String.class), nullable(String.class), nullable(WorkSource.class),
- nullable(String.class), eq(displayId));
+ nullable(String.class), eq(displayId), eq(null));
}
@Test
@@ -186,7 +196,7 @@ public class VirtualDeviceManagerServiceTest {
verify(mIPowerManagerMock, Mockito.times(1)).acquireWakeLock(wakeLockCaptor.capture(),
anyInt(),
nullable(String.class), nullable(String.class), nullable(WorkSource.class),
- nullable(String.class), eq(displayId));
+ nullable(String.class), eq(displayId), eq(null));
IBinder wakeLock = wakeLockCaptor.getValue();
mDeviceImpl.onVirtualDisplayRemovedLocked(displayId);
@@ -202,7 +212,7 @@ public class VirtualDeviceManagerServiceTest {
verify(mIPowerManagerMock, Mockito.times(1)).acquireWakeLock(wakeLockCaptor.capture(),
anyInt(),
nullable(String.class), nullable(String.class), nullable(WorkSource.class),
- nullable(String.class), eq(displayId));
+ nullable(String.class), eq(displayId), eq(null));
IBinder wakeLock = wakeLockCaptor.getValue();
// Close the VirtualDevice without first notifying it of the VirtualDisplay removal.
@@ -274,7 +284,8 @@ public class VirtualDeviceManagerServiceTest {
BINDER);
assertWithMessage("Virtual keyboard should register fd when the display matches")
.that(mInputController.mInputDeviceDescriptors).isNotEmpty();
- verify(mNativeWrapperMock).openUinputKeyboard(DEVICE_NAME, VENDOR_ID, PRODUCT_ID);
+ verify(mNativeWrapperMock).openUinputKeyboard(eq(DEVICE_NAME), eq(VENDOR_ID),
+ eq(PRODUCT_ID), anyString());
}
@Test
@@ -284,7 +295,8 @@ public class VirtualDeviceManagerServiceTest {
BINDER);
assertWithMessage("Virtual keyboard should register fd when the display matches")
.that(mInputController.mInputDeviceDescriptors).isNotEmpty();
- verify(mNativeWrapperMock).openUinputMouse(DEVICE_NAME, VENDOR_ID, PRODUCT_ID);
+ verify(mNativeWrapperMock).openUinputMouse(eq(DEVICE_NAME), eq(VENDOR_ID), eq(PRODUCT_ID),
+ anyString());
}
@Test
@@ -294,8 +306,8 @@ public class VirtualDeviceManagerServiceTest {
BINDER, new Point(WIDTH, HEIGHT));
assertWithMessage("Virtual keyboard should register fd when the display matches")
.that(mInputController.mInputDeviceDescriptors).isNotEmpty();
- verify(mNativeWrapperMock).openUinputTouchscreen(DEVICE_NAME, VENDOR_ID, PRODUCT_ID, HEIGHT,
- WIDTH);
+ verify(mNativeWrapperMock).openUinputTouchscreen(eq(DEVICE_NAME), eq(VENDOR_ID),
+ eq(PRODUCT_ID), anyString(), eq(HEIGHT), eq(WIDTH));
}
@Test
@@ -315,7 +327,7 @@ public class VirtualDeviceManagerServiceTest {
final int action = VirtualKeyEvent.ACTION_UP;
mInputController.mInputDeviceDescriptors.put(BINDER,
new InputController.InputDeviceDescriptor(fd, () -> {}, /* type= */ 1,
- /* displayId= */ 1));
+ /* displayId= */ 1, PHYS));
mDeviceImpl.sendKeyEvent(BINDER, new VirtualKeyEvent.Builder().setKeyCode(keyCode)
.setAction(action).build());
verify(mNativeWrapperMock).writeKeyEvent(fd, keyCode, action);
@@ -340,7 +352,7 @@ public class VirtualDeviceManagerServiceTest {
final int action = VirtualMouseButtonEvent.ACTION_BUTTON_PRESS;
mInputController.mInputDeviceDescriptors.put(BINDER,
new InputController.InputDeviceDescriptor(fd, () -> {}, /* type= */ 2,
- /* displayId= */ 1));
+ /* displayId= */ 1, PHYS));
mInputController.mActivePointerDisplayId = 1;
mDeviceImpl.sendButtonEvent(BINDER, new VirtualMouseButtonEvent.Builder()
.setButtonCode(buttonCode)
@@ -355,7 +367,7 @@ public class VirtualDeviceManagerServiceTest {
final int action = VirtualMouseButtonEvent.ACTION_BUTTON_PRESS;
mInputController.mInputDeviceDescriptors.put(BINDER,
new InputController.InputDeviceDescriptor(fd, () -> {}, /* type= */ 2,
- /* displayId= */ 1));
+ /* displayId= */ 1, PHYS));
assertThrows(
IllegalStateException.class,
() ->
@@ -381,7 +393,7 @@ public class VirtualDeviceManagerServiceTest {
final float y = 0.7f;
mInputController.mInputDeviceDescriptors.put(BINDER,
new InputController.InputDeviceDescriptor(fd, () -> {}, /* type= */ 2,
- /* displayId= */ 1));
+ /* displayId= */ 1, PHYS));
mInputController.mActivePointerDisplayId = 1;
mDeviceImpl.sendRelativeEvent(BINDER, new VirtualMouseRelativeEvent.Builder()
.setRelativeX(x).setRelativeY(y).build());
@@ -395,7 +407,7 @@ public class VirtualDeviceManagerServiceTest {
final float y = 0.7f;
mInputController.mInputDeviceDescriptors.put(BINDER,
new InputController.InputDeviceDescriptor(fd, () -> {}, /* type= */ 2,
- /* displayId= */ 1));
+ /* displayId= */ 1, PHYS));
assertThrows(
IllegalStateException.class,
() ->
@@ -422,7 +434,7 @@ public class VirtualDeviceManagerServiceTest {
final float y = 1f;
mInputController.mInputDeviceDescriptors.put(BINDER,
new InputController.InputDeviceDescriptor(fd, () -> {}, /* type= */ 2,
- /* displayId= */ 1));
+ /* displayId= */ 1, PHYS));
mInputController.mActivePointerDisplayId = 1;
mDeviceImpl.sendScrollEvent(BINDER, new VirtualMouseScrollEvent.Builder()
.setXAxisMovement(x)
@@ -437,7 +449,7 @@ public class VirtualDeviceManagerServiceTest {
final float y = 1f;
mInputController.mInputDeviceDescriptors.put(BINDER,
new InputController.InputDeviceDescriptor(fd, () -> {}, /* type= */ 2,
- /* displayId= */ 1));
+ /* displayId= */ 1, PHYS));
assertThrows(
IllegalStateException.class,
() ->
@@ -470,7 +482,7 @@ public class VirtualDeviceManagerServiceTest {
final int action = VirtualTouchEvent.ACTION_UP;
mInputController.mInputDeviceDescriptors.put(BINDER,
new InputController.InputDeviceDescriptor(fd, () -> {}, /* type= */ 3,
- /* displayId= */ 1));
+ /* displayId= */ 1, PHYS));
mDeviceImpl.sendTouchEvent(BINDER, new VirtualTouchEvent.Builder().setX(x)
.setY(y).setAction(action).setPointerId(pointerId).setToolType(toolType).build());
verify(mNativeWrapperMock).writeTouchEvent(fd, pointerId, toolType, action, x, y, Float.NaN,
@@ -489,7 +501,7 @@ public class VirtualDeviceManagerServiceTest {
final float majorAxisSize = 10.0f;
mInputController.mInputDeviceDescriptors.put(BINDER,
new InputController.InputDeviceDescriptor(fd, () -> {}, /* type= */ 3,
- /* displayId= */ 1));
+ /* displayId= */ 1, PHYS));
mDeviceImpl.sendTouchEvent(BINDER, new VirtualTouchEvent.Builder().setX(x)
.setY(y).setAction(action).setPointerId(pointerId).setToolType(toolType)
.setPressure(pressure).setMajorAxisSize(majorAxisSize).build());
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 564c4e439d86..c877bd141220 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -28,6 +28,14 @@ import static android.app.admin.DevicePolicyManager.ID_TYPE_BASE_INFO;
import static android.app.admin.DevicePolicyManager.ID_TYPE_IMEI;
import static android.app.admin.DevicePolicyManager.ID_TYPE_MEID;
import static android.app.admin.DevicePolicyManager.ID_TYPE_SERIAL;
+import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_BLOCK_ACTIVITY_START_IN_TASK;
+import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_GLOBAL_ACTIONS;
+import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_HOME;
+import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_KEYGUARD;
+import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_NONE;
+import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_NOTIFICATIONS;
+import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_OVERVIEW;
+import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_SYSTEM_INFO;
import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_HIGH;
import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_LOW;
import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_MEDIUM;
@@ -101,6 +109,7 @@ import android.app.admin.WifiSsidPolicy;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Intent;
+import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
@@ -138,6 +147,9 @@ import com.android.internal.widget.LockscreenCredential;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.devicepolicy.DevicePolicyManagerService.RestrictionsListener;
+import com.android.server.pm.RestrictionsSet;
+import com.android.server.pm.UserManagerInternal;
+import com.android.server.pm.UserRestrictionsUtils;
import org.hamcrest.BaseMatcher;
import org.hamcrest.Description;
@@ -7725,30 +7737,20 @@ public class DevicePolicyManagerTest extends DpmTestBase {
dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_FINANCED);
- int returnedDeviceOwnerType = dpm.getDeviceOwnerType(admin1);
- assertThat(dpms.mOwners.hasDeviceOwner()).isTrue();
- assertThat(returnedDeviceOwnerType).isEqualTo(DEVICE_OWNER_TYPE_FINANCED);
-
+ assertThat(dpm.getDeviceOwnerType(admin1)).isEqualTo(DEVICE_OWNER_TYPE_FINANCED);
initializeDpms();
-
- returnedDeviceOwnerType = dpm.getDeviceOwnerType(admin1);
- assertThat(dpms.mOwners.hasDeviceOwner()).isTrue();
- assertThat(returnedDeviceOwnerType).isEqualTo(DEVICE_OWNER_TYPE_FINANCED);
+ assertThat(dpm.getDeviceOwnerType(admin1)).isEqualTo(DEVICE_OWNER_TYPE_FINANCED);
}
@Test
- public void testSetDeviceOwnerType_asDeviceOwner_throwsExceptionWhenSetDeviceOwnerTypeAgain()
+ public void testSetDeviceOwnerType_asDeviceOwner_setDeviceOwnerTypeTwice_success()
throws Exception {
setDeviceOwner();
+ dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_DEFAULT);
dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_FINANCED);
- int returnedDeviceOwnerType = dpm.getDeviceOwnerType(admin1);
- assertThat(dpms.mOwners.hasDeviceOwner()).isTrue();
- assertThat(returnedDeviceOwnerType).isEqualTo(DEVICE_OWNER_TYPE_FINANCED);
-
- assertThrows(IllegalStateException.class,
- () -> dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_DEFAULT));
+ assertThat(dpm.getDeviceOwnerType(admin1)).isEqualTo(DEVICE_OWNER_TYPE_FINANCED);
}
@Test
@@ -7764,6 +7766,296 @@ public class DevicePolicyManagerTest extends DpmTestBase {
}
@Test
+ public void testSetUserRestriction_financeDo_invalidRestrictions_restrictionNotSet()
+ throws Exception {
+ setDeviceOwner();
+ dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_FINANCED);
+
+ for (String restriction : UserRestrictionsUtils.USER_RESTRICTIONS) {
+ if (!UserRestrictionsUtils.canFinancedDeviceOwnerChange(restriction)) {
+ assertNoDeviceOwnerRestrictions();
+ assertExpectException(SecurityException.class, /* messageRegex= */ null,
+ () -> dpm.addUserRestriction(admin1, restriction));
+
+ verify(getServices().userManagerInternal, never())
+ .setDevicePolicyUserRestrictions(anyInt(), any(), any(), anyBoolean());
+ }
+ }
+ }
+
+ @Test
+ public void testSetUserRestriction_financeDo_validRestrictions_setsRestriction()
+ throws Exception {
+ setDeviceOwner();
+ dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_FINANCED);
+
+ for (String restriction : UserRestrictionsUtils.USER_RESTRICTIONS) {
+ if (UserRestrictionsUtils.canFinancedDeviceOwnerChange(restriction)) {
+ assertNoDeviceOwnerRestrictions();
+ dpm.addUserRestriction(admin1, restriction);
+
+ Bundle globalRestrictions =
+ dpms.getDeviceOwnerAdminLocked().getGlobalUserRestrictions(
+ UserManagerInternal.OWNER_TYPE_DEVICE_OWNER);
+ RestrictionsSet localRestrictions = new RestrictionsSet();
+ localRestrictions.updateRestrictions(
+ UserHandle.USER_SYSTEM,
+ dpms.getDeviceOwnerAdminLocked().getLocalUserRestrictions(
+ UserManagerInternal.OWNER_TYPE_DEVICE_OWNER));
+ verify(getServices().userManagerInternal)
+ .setDevicePolicyUserRestrictions(eq(UserHandle.USER_SYSTEM),
+ MockUtils.checkUserRestrictions(globalRestrictions),
+ MockUtils.checkUserRestrictions(
+ UserHandle.USER_SYSTEM, localRestrictions),
+ eq(true));
+ reset(getServices().userManagerInternal);
+
+ dpm.clearUserRestriction(admin1, restriction);
+ reset(getServices().userManagerInternal);
+ }
+ }
+ }
+
+ @Test
+ public void testSetLockTaskFeatures_financeDo_validLockTaskFeatures_lockTaskFeaturesSet()
+ throws Exception {
+ int validLockTaskFeatures = LOCK_TASK_FEATURE_SYSTEM_INFO | LOCK_TASK_FEATURE_KEYGUARD
+ | LOCK_TASK_FEATURE_HOME | LOCK_TASK_FEATURE_GLOBAL_ACTIONS
+ | LOCK_TASK_FEATURE_NOTIFICATIONS;
+ setDeviceOwner();
+ dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_FINANCED);
+
+ dpm.setLockTaskFeatures(admin1, validLockTaskFeatures);
+
+ verify(getServices().iactivityTaskManager)
+ .updateLockTaskFeatures(eq(UserHandle.USER_SYSTEM), eq(validLockTaskFeatures));
+ }
+
+ @Test
+ public void testSetLockTaskFeatures_financeDo_invalidLockTaskFeatures_throwsException()
+ throws Exception {
+ int invalidLockTaskFeatures = LOCK_TASK_FEATURE_NONE | LOCK_TASK_FEATURE_OVERVIEW
+ | LOCK_TASK_FEATURE_HOME | LOCK_TASK_FEATURE_BLOCK_ACTIVITY_START_IN_TASK;
+ setDeviceOwner();
+ dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_FINANCED);
+ // Called during setup.
+ verify(getServices().iactivityTaskManager).updateLockTaskFeatures(anyInt(), anyInt());
+
+ assertExpectException(SecurityException.class, /* messageRegex= */ null,
+ () -> dpm.setLockTaskFeatures(admin1, invalidLockTaskFeatures));
+
+ verifyNoMoreInteractions(getServices().iactivityTaskManager);
+ }
+
+ @Test
+ public void testIsUninstallBlocked_financeDo_success() throws Exception {
+ String packageName = "com.android.foo.package";
+ setDeviceOwner();
+ dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_FINANCED);
+ when(getServices().ipackageManager.getBlockUninstallForUser(
+ eq(packageName), eq(UserHandle.USER_SYSTEM)))
+ .thenReturn(true);
+
+ assertThat(dpm.isUninstallBlocked(admin1, packageName)).isTrue();
+ }
+
+ @Test
+ public void testSetUninstallBlocked_financeDo_success() throws Exception {
+ String packageName = "com.android.foo.package";
+ setDeviceOwner();
+ dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_FINANCED);
+
+ dpm.setUninstallBlocked(admin1, packageName, false);
+
+ verify(getServices().ipackageManager)
+ .setBlockUninstallForUser(eq(packageName), eq(false),
+ eq(UserHandle.USER_SYSTEM));
+ }
+
+ @Test
+ public void testSetUserControlDisabledPackages_financeDo_success() throws Exception {
+ List<String> packages = new ArrayList<>();
+ packages.add("com.android.foo.package");
+ setDeviceOwner();
+ dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_FINANCED);
+
+ dpm.setUserControlDisabledPackages(admin1, packages);
+
+ verify(getServices().packageManagerInternal)
+ .setDeviceOwnerProtectedPackages(eq(admin1.getPackageName()), eq(packages));
+ }
+
+ @Test
+ public void testGetUserControlDisabledPackages_financeDo_success() throws Exception {
+ setDeviceOwner();
+ dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_FINANCED);
+
+ assertThat(dpm.getUserControlDisabledPackages(admin1)).isEmpty();
+ }
+
+ @Test
+ public void testSetOrganizationName_financeDo_success() throws Exception {
+ String organizationName = "Test Organization";
+ setDeviceOwner();
+ dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_FINANCED);
+
+ dpm.setOrganizationName(admin1, organizationName);
+
+ assertThat(dpm.getDeviceOwnerOrganizationName()).isEqualTo(organizationName);
+ }
+
+ @Test
+ public void testSetShortSupportMessage_financeDo_success() throws Exception {
+ String supportMessage = "Test short support message";
+ setDeviceOwner();
+ dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_FINANCED);
+
+ dpm.setShortSupportMessage(admin1, supportMessage);
+
+ assertThat(dpm.getShortSupportMessage(admin1)).isEqualTo(supportMessage);
+ }
+
+ @Test
+ public void testIsBackupServiceEnabled_financeDo_success() throws Exception {
+ setDeviceOwner();
+ dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_FINANCED);
+ when(getServices().ibackupManager.isBackupServiceActive(eq(UserHandle.USER_SYSTEM)))
+ .thenReturn(true);
+
+ assertThat(dpm.isBackupServiceEnabled(admin1)).isTrue();
+ }
+
+ @Test
+ public void testSetBackupServiceEnabled_financeDo_success() throws Exception {
+ setDeviceOwner();
+ dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_FINANCED);
+
+ dpm.setBackupServiceEnabled(admin1, true);
+
+ verify(getServices().ibackupManager)
+ .setBackupServiceActive(eq(UserHandle.USER_SYSTEM), eq(true));
+ }
+
+ @Test
+ public void testIsLockTaskPermitted_financeDo_success() throws Exception {
+ String packageName = "com.android.foo.package";
+ mockPolicyExemptApps(packageName);
+ mockVendorPolicyExemptApps();
+ setDeviceOwner();
+ dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_FINANCED);
+
+ assertThat(dpm.isLockTaskPermitted(packageName)).isTrue();
+ }
+
+ @Test
+ public void testSetLockTaskPackages_financeDo_success() throws Exception {
+ String[] packages = {"com.android.foo.package"};
+ mockEmptyPolicyExemptApps();
+ setDeviceOwner();
+ dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_FINANCED);
+
+ dpm.setLockTaskPackages(admin1, packages);
+
+ verify(getServices().iactivityManager)
+ .updateLockTaskPackages(eq(UserHandle.USER_SYSTEM), eq(packages));
+ }
+
+ @Test
+ public void testAddPersistentPreferredActivity_financeDo_success() throws Exception {
+ IntentFilter filter = new IntentFilter();
+ ComponentName target = new ComponentName(admin2.getPackageName(), "test.class");
+ setDeviceOwner();
+ dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_FINANCED);
+
+ dpm.addPersistentPreferredActivity(admin1, filter, target);
+
+ verify(getServices().ipackageManager)
+ .addPersistentPreferredActivity(eq(filter), eq(target), eq(UserHandle.USER_SYSTEM));
+ verify(getServices().ipackageManager)
+ .flushPackageRestrictionsAsUser(eq(UserHandle.USER_SYSTEM));
+ }
+
+ @Test
+ public void testClearPackagePersistentPreferredActvities_financeDo_success() throws Exception {
+ String packageName = admin2.getPackageName();
+ setDeviceOwner();
+ dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_FINANCED);
+
+ dpm.clearPackagePersistentPreferredActivities(admin1, packageName);
+
+ verify(getServices().ipackageManager)
+ .clearPackagePersistentPreferredActivities(
+ eq(packageName), eq(UserHandle.USER_SYSTEM));
+ verify(getServices().ipackageManager)
+ .flushPackageRestrictionsAsUser(eq(UserHandle.USER_SYSTEM));
+ }
+
+ @Test
+ public void testWipeData_financeDo_success() throws Exception {
+ setDeviceOwner();
+ dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_FINANCED);
+ when(getServices().userManager.getUserRestrictionSource(
+ UserManager.DISALLOW_FACTORY_RESET,
+ UserHandle.SYSTEM))
+ .thenReturn(UserManager.RESTRICTION_SOURCE_DEVICE_OWNER);
+ when(mMockContext.getResources()
+ .getString(R.string.work_profile_deleted_description_dpm_wipe))
+ .thenReturn("Test string");
+
+ dpm.wipeData(0);
+
+ verifyRebootWipeUserData(/* wipeEuicc= */ false);
+ }
+
+ @Test
+ public void testIsDeviceOwnerApp_financeDo_success() throws Exception {
+ setDeviceOwner();
+ dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_FINANCED);
+
+ assertThat(dpm.isDeviceOwnerApp(admin1.getPackageName())).isTrue();
+ }
+
+ @Test
+ public void testClearDeviceOwnerApp_financeDo_success() throws Exception {
+ setDeviceOwner();
+ dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_FINANCED);
+
+ dpm.clearDeviceOwnerApp(admin1.getPackageName());
+
+ assertThat(dpm.getDeviceOwnerComponentOnAnyUser()).isNull();
+ assertThat(dpm.isAdminActiveAsUser(admin1, UserHandle.USER_SYSTEM)).isFalse();
+ verify(mMockContext.spiedContext, times(2))
+ .sendBroadcastAsUser(
+ MockUtils.checkIntentAction(
+ DevicePolicyManager.ACTION_DEVICE_OWNER_CHANGED),
+ eq(UserHandle.SYSTEM));
+ }
+
+ @Test
+ public void testSetPermissionGrantState_financeDo_notReadPhoneStatePermission_throwsException()
+ throws Exception {
+ setDeviceOwner();
+ dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_FINANCED);
+
+ assertExpectException(SecurityException.class, /* messageRegex= */ null,
+ () -> dpm.setPermissionGrantState(admin1, admin1.getPackageName(),
+ permission.READ_CALENDAR,
+ DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED));
+ }
+
+ @Test
+ public void testSetPermissionGrantState_financeDo_grantPermissionToNonDeviceOwnerPackage_throwsException()
+ throws Exception {
+ setDeviceOwner();
+ dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_FINANCED);
+
+ assertExpectException(SecurityException.class, /* messageRegex= */ null,
+ () -> dpm.setPermissionGrantState(admin1, "com.android.foo.package",
+ permission.READ_PHONE_STATE,
+ DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED));
+ }
+
+ @Test
public void testSetUsbDataSignalingEnabled_noDeviceOwnerOrPoOfOrgOwnedDevice() {
assertThrows(SecurityException.class,
() -> dpm.setUsbDataSignalingEnabled(true));
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/MockUtils.java b/services/tests/servicestests/src/com/android/server/devicepolicy/MockUtils.java
index 15f3ed1be552..4cb46b4047b0 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/MockUtils.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/MockUtils.java
@@ -109,6 +109,14 @@ public class MockUtils {
public static Bundle checkUserRestrictions(String... keys) {
final Bundle expected = DpmTestUtils.newRestrictions(
java.util.Objects.requireNonNull(keys));
+ return checkUserRestrictions(expected);
+ }
+
+ public static Bundle checkUserRestrictions(Bundle expected) {
+ return createUserRestrictionsBundleMatcher(expected);
+ }
+
+ private static Bundle createUserRestrictionsBundleMatcher(Bundle expected) {
final Matcher<Bundle> m = new BaseMatcher<Bundle>() {
@Override
public boolean matches(Object item) {
@@ -129,6 +137,15 @@ public class MockUtils {
public static RestrictionsSet checkUserRestrictions(int userId, String... keys) {
final RestrictionsSet expected = DpmTestUtils.newRestrictions(userId,
java.util.Objects.requireNonNull(keys));
+ return checkUserRestrictions(userId, expected);
+ }
+
+ public static RestrictionsSet checkUserRestrictions(int userId, RestrictionsSet expected) {
+ return createUserRestrictionsSetMatcher(userId, expected);
+ }
+
+ private static RestrictionsSet createUserRestrictionsSetMatcher(
+ int userId, RestrictionsSet expected) {
final Matcher<RestrictionsSet> m = new BaseMatcher<RestrictionsSet>() {
@Override
public boolean matches(Object item) {
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/OverlayPackagesProviderTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/OverlayPackagesProviderTest.java
index a8f24ce5e11d..533fb2d8d214 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/OverlayPackagesProviderTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/OverlayPackagesProviderTest.java
@@ -26,6 +26,7 @@ import static android.app.admin.DevicePolicyManager.REQUIRED_APP_MANAGED_USER;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.when;
@@ -76,6 +77,7 @@ public class OverlayPackagesProviderTest {
private static final ComponentName TEST_MDM_COMPONENT_NAME = new ComponentName(
TEST_DPC_PACKAGE_NAME, "pc.package.name.DeviceAdmin");
private static final int TEST_USER_ID = 123;
+ private static final String ROLE_HOLDER_PACKAGE_NAME = "test.role.holder.package.name";
private @Mock Resources mResources;
@@ -305,6 +307,26 @@ public class OverlayPackagesProviderTest {
ACTION_PROVISION_MANAGED_PROFILE, "package1", "package2", "package3");
}
+ @Test
+ public void testGetNonRequiredApps_managedProfile_roleHolder_works() {
+ when(mInjector.getDeviceManagerRoleHolderPackageName(any()))
+ .thenReturn(ROLE_HOLDER_PACKAGE_NAME);
+ setSystemAppsWithLauncher("package1", "package2", ROLE_HOLDER_PACKAGE_NAME);
+
+ verifyAppsAreNonRequired(
+ ACTION_PROVISION_MANAGED_PROFILE, "package1", "package2");
+ }
+
+ @Test
+ public void testGetNonRequiredApps_managedDevice_roleHolder_works() {
+ when(mInjector.getDeviceManagerRoleHolderPackageName(any()))
+ .thenReturn(ROLE_HOLDER_PACKAGE_NAME);
+ setSystemAppsWithLauncher("package1", "package2", ROLE_HOLDER_PACKAGE_NAME);
+
+ verifyAppsAreNonRequired(
+ ACTION_PROVISION_MANAGED_DEVICE, "package1", "package2");
+ }
+
private void setupRegularModulesWithManagedUser(String... regularModules) {
setupRegularModulesWithMetadata(regularModules, REQUIRED_APP_MANAGED_USER);
}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/OwnersTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/OwnersTest.java
index 02a8ae8fbecd..9a5254d3e00d 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/OwnersTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/OwnersTest.java
@@ -93,7 +93,7 @@ public class OwnersTest extends DpmTestBase {
assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(21)).isFalse();
owners.setDeviceOwnerType(owners.getDeviceOwnerPackageName(),
- DEVICE_OWNER_TYPE_FINANCED);
+ DEVICE_OWNER_TYPE_FINANCED, /* isAdminTestOnly= */ false);
// There is no device owner, so the default owner type should be returned.
assertThat(owners.getDeviceOwnerType(owners.getDeviceOwnerPackageName())).isEqualTo(
DEVICE_OWNER_TYPE_DEFAULT);
@@ -367,7 +367,7 @@ public class OwnersTest extends DpmTestBase {
owners.setDeviceOwnerUserRestrictionsMigrated();
owners.setDeviceOwnerType(owners.getDeviceOwnerPackageName(),
- DEVICE_OWNER_TYPE_FINANCED);
+ DEVICE_OWNER_TYPE_FINANCED, /* isAdminTestOnly= */ false);
assertThat(owners.getDeviceOwnerType(owners.getDeviceOwnerPackageName())).isEqualTo(
DEVICE_OWNER_TYPE_FINANCED);
@@ -399,7 +399,7 @@ public class OwnersTest extends DpmTestBase {
owners.setProfileOwnerUserRestrictionsMigrated(11);
owners.setDeviceOwnerType(owners.getDeviceOwnerPackageName(),
- DEVICE_OWNER_TYPE_DEFAULT);
+ DEVICE_OWNER_TYPE_DEFAULT, /* isAdminTestOnly= */ false);
// The previous device owner type should persist.
assertThat(owners.getDeviceOwnerType(owners.getDeviceOwnerPackageName())).isEqualTo(
DEVICE_OWNER_TYPE_FINANCED);
@@ -585,7 +585,8 @@ public class OwnersTest extends DpmTestBase {
assertThat(owners.getProfileOwnerFile(11).exists()).isTrue();
String previousDeviceOwnerPackageName = owners.getDeviceOwnerPackageName();
- owners.setDeviceOwnerType(previousDeviceOwnerPackageName, DEVICE_OWNER_TYPE_FINANCED);
+ owners.setDeviceOwnerType(previousDeviceOwnerPackageName, DEVICE_OWNER_TYPE_FINANCED,
+ /* isAdminTestOnly= */ false);
assertThat(owners.getDeviceOwnerType(previousDeviceOwnerPackageName)).isEqualTo(
DEVICE_OWNER_TYPE_FINANCED);
owners.setDeviceOwnerProtectedPackages(
diff --git a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
index d2cff0ea1968..fe3034dc569d 100644
--- a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
@@ -326,7 +326,7 @@ public final class DeviceStateManagerServiceTest {
assertEquals(callback.getLastNotifiedInfo().currentState,
OTHER_DEVICE_STATE.getIdentifier());
- mService.getBinderService().cancelRequest(token);
+ mService.getBinderService().cancelStateRequest();
flushHandler();
assertEquals(callback.getLastNotifiedStatus(token),
@@ -378,9 +378,9 @@ public final class DeviceStateManagerServiceTest {
mPolicy.resumeConfigureOnce();
flushHandler();
- // First request status is now suspended as there is another pending request.
+ // First request status is now canceled as there is another pending request.
assertEquals(callback.getLastNotifiedStatus(firstRequestToken),
- TestDeviceStateManagerCallback.STATUS_SUSPENDED);
+ TestDeviceStateManagerCallback.STATUS_CANCELED);
// Second request status still unknown because the service is still awaiting policy
// callback.
assertEquals(callback.getLastNotifiedStatus(secondRequestToken),
@@ -402,19 +402,19 @@ public final class DeviceStateManagerServiceTest {
DEFAULT_DEVICE_STATE.getIdentifier());
// Now cancel the second request to make the first request active.
- mService.getBinderService().cancelRequest(secondRequestToken);
+ mService.getBinderService().cancelStateRequest();
flushHandler();
assertEquals(callback.getLastNotifiedStatus(firstRequestToken),
- TestDeviceStateManagerCallback.STATUS_ACTIVE);
+ TestDeviceStateManagerCallback.STATUS_CANCELED);
assertEquals(callback.getLastNotifiedStatus(secondRequestToken),
TestDeviceStateManagerCallback.STATUS_CANCELED);
- assertEquals(mService.getCommittedState(), Optional.of(OTHER_DEVICE_STATE));
+ assertEquals(mService.getCommittedState(), Optional.of(DEFAULT_DEVICE_STATE));
assertEquals(mService.getPendingState(), Optional.empty());
assertEquals(mService.getBaseState(), Optional.of(DEFAULT_DEVICE_STATE));
assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
- OTHER_DEVICE_STATE.getIdentifier());
+ DEFAULT_DEVICE_STATE.getIdentifier());
}
@Test
@@ -656,11 +656,6 @@ public final class DeviceStateManagerServiceTest {
}
@Override
- public void onRequestSuspended(IBinder token) {
- mLastNotifiedStatus.put(token, STATUS_SUSPENDED);
- }
-
- @Override
public void onRequestCanceled(IBinder token) {
mLastNotifiedStatus.put(token, STATUS_CANCELED);
}
diff --git a/services/tests/servicestests/src/com/android/server/devicestate/OverrideRequestControllerTest.java b/services/tests/servicestests/src/com/android/server/devicestate/OverrideRequestControllerTest.java
index b94fc4308ce2..2297c91818c0 100644
--- a/services/tests/servicestests/src/com/android/server/devicestate/OverrideRequestControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicestate/OverrideRequestControllerTest.java
@@ -18,7 +18,6 @@ package com.android.server.devicestate;
import static com.android.server.devicestate.OverrideRequestController.STATUS_ACTIVE;
import static com.android.server.devicestate.OverrideRequestController.STATUS_CANCELED;
-import static com.android.server.devicestate.OverrideRequestController.STATUS_SUSPENDED;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNull;
@@ -66,7 +65,7 @@ public final class OverrideRequestControllerTest {
}
@Test
- public void addRequest_suspendExistingRequest() {
+ public void addRequest_cancelExistingRequestThroughNewRequest() {
OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */,
0 /* requestedState */, 0 /* flags */);
assertNull(mStatusListener.getLastStatus(firstRequest));
@@ -75,92 +74,52 @@ public final class OverrideRequestControllerTest {
assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_ACTIVE);
OverrideRequest secondRequest = new OverrideRequest(new Binder(), 0 /* pid */,
- 0 /* requestedState */, 0 /* flags */);
+ 1 /* requestedState */, 0 /* flags */);
assertNull(mStatusListener.getLastStatus(secondRequest));
mController.addRequest(secondRequest);
assertEquals(mStatusListener.getLastStatus(secondRequest).intValue(), STATUS_ACTIVE);
- assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_SUSPENDED);
+ assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_CANCELED);
}
@Test
public void addRequest_cancelActiveRequest() {
OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */,
0 /* requestedState */, 0 /* flags */);
- OverrideRequest secondRequest = new OverrideRequest(new Binder(), 0 /* pid */,
- 0 /* requestedState */, 0 /* flags */);
mController.addRequest(firstRequest);
- mController.addRequest(secondRequest);
-
- assertEquals(mStatusListener.getLastStatus(secondRequest).intValue(), STATUS_ACTIVE);
- assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_SUSPENDED);
- mController.cancelRequest(secondRequest.getToken());
-
- assertEquals(mStatusListener.getLastStatus(secondRequest).intValue(), STATUS_CANCELED);
assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_ACTIVE);
- }
- @Test
- public void addRequest_cancelSuspendedRequest() {
- OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */,
- 0 /* requestedState */, 0 /* flags */);
- OverrideRequest secondRequest = new OverrideRequest(new Binder(), 0 /* pid */,
- 0 /* requestedState */, 0 /* flags */);
+ mController.cancelOverrideRequest();
- mController.addRequest(firstRequest);
- mController.addRequest(secondRequest);
-
- assertEquals(mStatusListener.getLastStatus(secondRequest).intValue(), STATUS_ACTIVE);
- assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_SUSPENDED);
-
- mController.cancelRequest(firstRequest.getToken());
-
- assertEquals(mStatusListener.getLastStatus(secondRequest).intValue(), STATUS_ACTIVE);
assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_CANCELED);
}
@Test
public void handleBaseStateChanged() {
OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */,
- 0 /* requestedState */, 0 /* flags */);
- OverrideRequest secondRequest = new OverrideRequest(new Binder(), 0 /* pid */,
0 /* requestedState */,
DeviceStateRequest.FLAG_CANCEL_WHEN_BASE_CHANGES /* flags */);
mController.addRequest(firstRequest);
- mController.addRequest(secondRequest);
- assertEquals(mStatusListener.getLastStatus(secondRequest).intValue(), STATUS_ACTIVE);
- assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_SUSPENDED);
+ assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_ACTIVE);
mController.handleBaseStateChanged();
- assertEquals(mStatusListener.getLastStatus(secondRequest).intValue(), STATUS_CANCELED);
- assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_ACTIVE);
+ assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_CANCELED);
}
@Test
public void handleProcessDied() {
OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */,
0 /* requestedState */, 0 /* flags */);
- OverrideRequest secondRequest = new OverrideRequest(new Binder(), 1 /* pid */,
- 0 /* requestedState */, 0 /* flags */);
mController.addRequest(firstRequest);
- mController.addRequest(secondRequest);
-
- assertEquals(mStatusListener.getLastStatus(secondRequest).intValue(), STATUS_ACTIVE);
- assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_SUSPENDED);
-
- mController.handleProcessDied(1);
-
- assertEquals(mStatusListener.getLastStatus(secondRequest).intValue(), STATUS_CANCELED);
assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_ACTIVE);
mController.handleProcessDied(0);
-
assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_CANCELED);
}
@@ -170,46 +129,29 @@ public final class OverrideRequestControllerTest {
OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */,
0 /* requestedState */, 0 /* flags */);
- OverrideRequest secondRequest = new OverrideRequest(new Binder(), 1 /* pid */,
- 0 /* requestedState */, 0 /* flags */);
mController.addRequest(firstRequest);
- mController.addRequest(secondRequest);
-
- assertEquals(mStatusListener.getLastStatus(secondRequest).intValue(), STATUS_ACTIVE);
- assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_SUSPENDED);
-
- mController.handleProcessDied(1);
-
- assertEquals(mStatusListener.getLastStatus(secondRequest).intValue(), STATUS_ACTIVE);
- assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_SUSPENDED);
-
- mController.cancelStickyRequests();
+ assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_ACTIVE);
- assertEquals(mStatusListener.getLastStatus(secondRequest).intValue(), STATUS_CANCELED);
+ mController.handleProcessDied(0);
assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_ACTIVE);
+
+ mController.cancelStickyRequest();
+ assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_CANCELED);
}
@Test
public void handleNewSupportedStates() {
OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */,
1 /* requestedState */, 0 /* flags */);
- OverrideRequest secondRequest = new OverrideRequest(new Binder(), 0 /* pid */,
- 2 /* requestedState */, 0 /* flags */);
mController.addRequest(firstRequest);
- mController.addRequest(secondRequest);
-
- assertEquals(mStatusListener.getLastStatus(secondRequest).intValue(), STATUS_ACTIVE);
- assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_SUSPENDED);
+ assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_ACTIVE);
mController.handleNewSupportedStates(new int[]{ 0, 1 });
-
- assertEquals(mStatusListener.getLastStatus(secondRequest).intValue(), STATUS_CANCELED);
assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_ACTIVE);
mController.handleNewSupportedStates(new int[]{ 0 });
-
assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_CANCELED);
}
@@ -217,18 +159,11 @@ public final class OverrideRequestControllerTest {
public void cancelOverrideRequestsTest() {
OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */,
1 /* requestedState */, 0 /* flags */);
- OverrideRequest secondRequest = new OverrideRequest(new Binder(), 0 /* pid */,
- 2 /* requestedState */, 0 /* flags */);
mController.addRequest(firstRequest);
- mController.addRequest(secondRequest);
-
- assertEquals(mStatusListener.getLastStatus(secondRequest).intValue(), STATUS_ACTIVE);
- assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_SUSPENDED);
-
- mController.cancelOverrideRequests();
+ assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_ACTIVE);
- assertEquals(mStatusListener.getLastStatus(secondRequest).intValue(), STATUS_CANCELED);
+ mController.cancelOverrideRequest();
assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_CANCELED);
}
diff --git a/services/tests/servicestests/src/com/android/server/display/BrightnessThrottlerTest.java b/services/tests/servicestests/src/com/android/server/display/BrightnessThrottlerTest.java
new file mode 100644
index 000000000000..0ed90d27db99
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/display/BrightnessThrottlerTest.java
@@ -0,0 +1,321 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.hardware.display.BrightnessInfo;
+import android.os.Handler;
+import android.os.IThermalEventListener;
+import android.os.IThermalService;
+import android.os.Message;
+import android.os.PowerManager;
+import android.os.Temperature.ThrottlingStatus;
+import android.os.Temperature;
+import android.os.test.TestLooper;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.display.BrightnessThrottler.Injector;
+import com.android.server.display.DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel;
+import com.android.server.display.DisplayDeviceConfig.BrightnessThrottlingData;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+@SmallTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class BrightnessThrottlerTest {
+ private static final float EPSILON = 0.000001f;
+
+ private Handler mHandler;
+ private TestLooper mTestLooper;
+
+ @Mock IThermalService mThermalServiceMock;
+ @Mock Injector mInjectorMock;
+
+ @Captor ArgumentCaptor<IThermalEventListener> mThermalEventListenerCaptor;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ when(mInjectorMock.getThermalService()).thenReturn(mThermalServiceMock);
+ mTestLooper = new TestLooper();
+ mHandler = new Handler(mTestLooper.getLooper(), new Handler.Callback() {
+ @Override
+ public boolean handleMessage(Message msg) {
+ return true;
+ }
+ });
+
+ }
+
+ /////////////////
+ // Test Methods
+ /////////////////
+
+ @Test
+ public void testBrightnessThrottlingData() {
+ List<ThrottlingLevel> singleLevel = new ArrayList<>();
+ singleLevel.add(new ThrottlingLevel(PowerManager.THERMAL_STATUS_CRITICAL, 0.25f));
+
+ List<ThrottlingLevel> validLevels = new ArrayList<>();
+ validLevels.add(new ThrottlingLevel(PowerManager.THERMAL_STATUS_MODERATE, 0.62f));
+ validLevels.add(new ThrottlingLevel(PowerManager.THERMAL_STATUS_CRITICAL, 0.25f));
+
+ List<ThrottlingLevel> unsortedThermalLevels = new ArrayList<>();
+ unsortedThermalLevels.add(new ThrottlingLevel(PowerManager.THERMAL_STATUS_CRITICAL, 0.62f));
+ unsortedThermalLevels.add(new ThrottlingLevel(PowerManager.THERMAL_STATUS_MODERATE, 0.25f));
+
+ List<ThrottlingLevel> unsortedBrightnessLevels = new ArrayList<>();
+ unsortedBrightnessLevels.add(
+ new ThrottlingLevel(PowerManager.THERMAL_STATUS_MODERATE, 0.25f));
+ unsortedBrightnessLevels.add(
+ new ThrottlingLevel(PowerManager.THERMAL_STATUS_CRITICAL, 0.62f));
+
+ List<ThrottlingLevel> unsortedLevels = new ArrayList<>();
+ unsortedLevels.add(new ThrottlingLevel(PowerManager.THERMAL_STATUS_CRITICAL, 0.25f));
+ unsortedLevels.add(new ThrottlingLevel(PowerManager.THERMAL_STATUS_MODERATE, 0.62f));
+
+ List<ThrottlingLevel> invalidLevel = new ArrayList<>();
+ invalidLevel.add(new ThrottlingLevel(PowerManager.THERMAL_STATUS_CRITICAL,
+ PowerManager.BRIGHTNESS_MAX + EPSILON));
+
+ // Test invalid data
+ BrightnessThrottlingData data;
+ data = BrightnessThrottlingData.create((List<ThrottlingLevel>)null);
+ assertEquals(data, null);
+ data = BrightnessThrottlingData.create((BrightnessThrottlingData)null);
+ assertEquals(data, null);
+ data = BrightnessThrottlingData.create(new ArrayList<ThrottlingLevel>());
+ assertEquals(data, null);
+ data = BrightnessThrottlingData.create(unsortedThermalLevels);
+ assertEquals(data, null);
+ data = BrightnessThrottlingData.create(unsortedBrightnessLevels);
+ assertEquals(data, null);
+ data = BrightnessThrottlingData.create(unsortedLevels);
+ assertEquals(data, null);
+ data = BrightnessThrottlingData.create(invalidLevel);
+ assertEquals(data, null);
+
+ // Test valid data
+ data = BrightnessThrottlingData.create(singleLevel);
+ assertNotEquals(data, null);
+ assertThrottlingLevelsEquals(singleLevel, data.throttlingLevels);
+
+ data = BrightnessThrottlingData.create(validLevels);
+ assertNotEquals(data, null);
+ assertThrottlingLevelsEquals(validLevels, data.throttlingLevels);
+ }
+
+ @Test
+ public void testThrottlingUnsupported() throws Exception {
+ final BrightnessThrottler throttler = createThrottlerUnsupported();
+ assertFalse(throttler.deviceSupportsThrottling());
+
+ // Thermal listener shouldn't be registered if throttling is unsupported
+ verify(mInjectorMock, never()).getThermalService();
+
+ // Ensure that brightness is uncapped when the device doesn't support throttling
+ assertEquals(PowerManager.BRIGHTNESS_MAX, throttler.getBrightnessCap(), 0f);
+ }
+
+ @Test
+ public void testThrottlingSingleLevel() throws Exception {
+ final ThrottlingLevel level = new ThrottlingLevel(PowerManager.THERMAL_STATUS_CRITICAL,
+ 0.25f);
+
+ List<ThrottlingLevel> levels = new ArrayList<>();
+ levels.add(level);
+ final BrightnessThrottlingData data = BrightnessThrottlingData.create(levels);
+ final BrightnessThrottler throttler = createThrottlerSupported(data);
+ assertTrue(throttler.deviceSupportsThrottling());
+
+ verify(mThermalServiceMock).registerThermalEventListenerWithType(
+ mThermalEventListenerCaptor.capture(), eq(Temperature.TYPE_SKIN));
+ final IThermalEventListener listener = mThermalEventListenerCaptor.getValue();
+
+ // Set status too low to trigger throttling
+ listener.notifyThrottling(getSkinTemp(level.thermalStatus - 1));
+ mTestLooper.dispatchAll();
+ assertEquals(PowerManager.BRIGHTNESS_MAX, throttler.getBrightnessCap(), 0f);
+ assertFalse(throttler.isThrottled());
+ assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE, throttler.getBrightnessMaxReason());
+
+ // Set status just high enough to trigger throttling
+ listener.notifyThrottling(getSkinTemp(level.thermalStatus));
+ mTestLooper.dispatchAll();
+ assertEquals(level.brightness, throttler.getBrightnessCap(), 0f);
+ assertTrue(throttler.isThrottled());
+ assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_THERMAL,
+ throttler.getBrightnessMaxReason());
+
+ // Set status more than high enough to trigger throttling
+ listener.notifyThrottling(getSkinTemp(level.thermalStatus + 1));
+ mTestLooper.dispatchAll();
+ assertEquals(level.brightness, throttler.getBrightnessCap(), 0f);
+ assertTrue(throttler.isThrottled());
+ assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_THERMAL,
+ throttler.getBrightnessMaxReason());
+
+ // Return to the lower throttling level
+ listener.notifyThrottling(getSkinTemp(level.thermalStatus));
+ mTestLooper.dispatchAll();
+ assertEquals(level.brightness, throttler.getBrightnessCap(), 0f);
+ assertTrue(throttler.isThrottled());
+ assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_THERMAL,
+ throttler.getBrightnessMaxReason());
+
+ // Cool down
+ listener.notifyThrottling(getSkinTemp(level.thermalStatus - 1));
+ mTestLooper.dispatchAll();
+ assertEquals(PowerManager.BRIGHTNESS_MAX, throttler.getBrightnessCap(), 0f);
+ assertFalse(throttler.isThrottled());
+ assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE,
+ throttler.getBrightnessMaxReason());
+ }
+
+ @Test
+ public void testThrottlingMultiLevel() throws Exception {
+ final ThrottlingLevel levelLo = new ThrottlingLevel(PowerManager.THERMAL_STATUS_MODERATE,
+ 0.62f);
+ final ThrottlingLevel levelHi = new ThrottlingLevel(PowerManager.THERMAL_STATUS_CRITICAL,
+ 0.25f);
+
+ List<ThrottlingLevel> levels = new ArrayList<>();
+ levels.add(levelLo);
+ levels.add(levelHi);
+ final BrightnessThrottlingData data = BrightnessThrottlingData.create(levels);
+ final BrightnessThrottler throttler = createThrottlerSupported(data);
+ assertTrue(throttler.deviceSupportsThrottling());
+
+ verify(mThermalServiceMock).registerThermalEventListenerWithType(
+ mThermalEventListenerCaptor.capture(), eq(Temperature.TYPE_SKIN));
+ final IThermalEventListener listener = mThermalEventListenerCaptor.getValue();
+
+ // Set status too low to trigger throttling
+ listener.notifyThrottling(getSkinTemp(levelLo.thermalStatus - 1));
+ mTestLooper.dispatchAll();
+ assertEquals(PowerManager.BRIGHTNESS_MAX, throttler.getBrightnessCap(), 0f);
+ assertFalse(throttler.isThrottled());
+ assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE, throttler.getBrightnessMaxReason());
+
+ // Set status just high enough to trigger throttling
+ listener.notifyThrottling(getSkinTemp(levelLo.thermalStatus));
+ mTestLooper.dispatchAll();
+ assertEquals(levelLo.brightness, throttler.getBrightnessCap(), 0f);
+ assertTrue(throttler.isThrottled());
+ assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_THERMAL,
+ throttler.getBrightnessMaxReason());
+
+ // Set status to an intermediate throttling level
+ listener.notifyThrottling(getSkinTemp(levelLo.thermalStatus + 1));
+ mTestLooper.dispatchAll();
+ assertEquals(levelLo.brightness, throttler.getBrightnessCap(), 0f);
+ assertTrue(throttler.isThrottled());
+ assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_THERMAL,
+ throttler.getBrightnessMaxReason());
+
+ // Set status to the highest configured throttling level
+ listener.notifyThrottling(getSkinTemp(levelHi.thermalStatus));
+ mTestLooper.dispatchAll();
+ assertEquals(levelHi.brightness, throttler.getBrightnessCap(), 0f);
+ assertTrue(throttler.isThrottled());
+ assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_THERMAL,
+ throttler.getBrightnessMaxReason());
+
+ // Set status to exceed the highest configured throttling level
+ listener.notifyThrottling(getSkinTemp(levelHi.thermalStatus + 1));
+ mTestLooper.dispatchAll();
+ assertEquals(levelHi.brightness, throttler.getBrightnessCap(), 0f);
+ assertTrue(throttler.isThrottled());
+ assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_THERMAL,
+ throttler.getBrightnessMaxReason());
+
+ // Return to an intermediate throttling level
+ listener.notifyThrottling(getSkinTemp(levelLo.thermalStatus + 1));
+ mTestLooper.dispatchAll();
+ assertEquals(levelLo.brightness, throttler.getBrightnessCap(), 0f);
+ assertTrue(throttler.isThrottled());
+ assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_THERMAL,
+ throttler.getBrightnessMaxReason());
+
+ // Return to the lowest configured throttling level
+ listener.notifyThrottling(getSkinTemp(levelLo.thermalStatus));
+ mTestLooper.dispatchAll();
+ assertEquals(levelLo.brightness, throttler.getBrightnessCap(), 0f);
+ assertTrue(throttler.isThrottled());
+ assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_THERMAL,
+ throttler.getBrightnessMaxReason());
+
+ // Cool down
+ listener.notifyThrottling(getSkinTemp(levelLo.thermalStatus - 1));
+ mTestLooper.dispatchAll();
+ assertEquals(PowerManager.BRIGHTNESS_MAX, throttler.getBrightnessCap(), 0f);
+ assertFalse(throttler.isThrottled());
+ assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE, throttler.getBrightnessMaxReason());
+ }
+
+ private void assertThrottlingLevelsEquals(
+ List<ThrottlingLevel> expected,
+ List<ThrottlingLevel> actual) {
+ assertEquals(expected.size(), actual.size());
+
+ for (int i = 0; i < expected.size(); i++) {
+ ThrottlingLevel expectedLevel = expected.get(i);
+ ThrottlingLevel actualLevel = actual.get(i);
+
+ assertEquals(expectedLevel.thermalStatus, actualLevel.thermalStatus);
+ assertEquals(expectedLevel.brightness, actualLevel.brightness, 0.0f);
+ }
+ }
+
+ private BrightnessThrottler createThrottlerUnsupported() {
+ return new BrightnessThrottler(mInjectorMock, mHandler, null, () -> {});
+ }
+
+ private BrightnessThrottler createThrottlerSupported(BrightnessThrottlingData data) {
+ assertNotNull(data);
+ return new BrightnessThrottler(mInjectorMock, mHandler, data, () -> {});
+ }
+
+ private Temperature getSkinTemp(@ThrottlingStatus int status) {
+ return new Temperature(30.0f, Temperature.TYPE_SKIN, "test_skin_temp", status);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
index 418831f47c1a..40c039210939 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
@@ -1461,7 +1461,8 @@ public class DisplayModeDirectorTest {
// Turn on HBM, with brightness in the HBM range
when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
new BrightnessInfo(TRANSITION_POINT + FLOAT_TOLERANCE, 0.0f, 1.0f,
- BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR, TRANSITION_POINT));
+ BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR, TRANSITION_POINT,
+ BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE));
listener.onDisplayChanged(DISPLAY_ID);
vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
assertVoteForRefreshRate(vote, hbmRefreshRate);
@@ -1469,7 +1470,8 @@ public class DisplayModeDirectorTest {
// Turn on HBM, with brightness below the HBM range
when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
new BrightnessInfo(TRANSITION_POINT - FLOAT_TOLERANCE, 0.0f, 1.0f,
- BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR, TRANSITION_POINT));
+ BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR, TRANSITION_POINT,
+ BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE));
listener.onDisplayChanged(DISPLAY_ID);
vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
assertNull(vote);
@@ -1477,7 +1479,8 @@ public class DisplayModeDirectorTest {
// Turn off HBM
when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
new BrightnessInfo(0.45f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF,
- TRANSITION_POINT));
+ TRANSITION_POINT,
+ BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE));
listener.onDisplayChanged(DISPLAY_ID);
vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
assertNull(vote);
@@ -1485,7 +1488,8 @@ public class DisplayModeDirectorTest {
// Turn on HBM, with brightness in the HBM range
when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
new BrightnessInfo(TRANSITION_POINT + FLOAT_TOLERANCE, 0.0f, 1.0f,
- BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR, TRANSITION_POINT));
+ BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR, TRANSITION_POINT,
+ BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE));
listener.onDisplayChanged(DISPLAY_ID);
vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
assertVoteForRefreshRate(vote, hbmRefreshRate);
@@ -1493,7 +1497,7 @@ public class DisplayModeDirectorTest {
// Turn off HBM
when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
new BrightnessInfo(0.45f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF,
- TRANSITION_POINT));
+ TRANSITION_POINT, BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE));
listener.onDisplayChanged(DISPLAY_ID);
vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
assertNull(vote);
@@ -1501,7 +1505,8 @@ public class DisplayModeDirectorTest {
// Turn on HBM, with brightness below the HBM range
when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
new BrightnessInfo(TRANSITION_POINT - FLOAT_TOLERANCE, 0.0f, 1.0f,
- BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR, TRANSITION_POINT));
+ BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR, TRANSITION_POINT,
+ BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE));
listener.onDisplayChanged(DISPLAY_ID);
vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
assertNull(vote);
@@ -1509,7 +1514,7 @@ public class DisplayModeDirectorTest {
// Turn off HBM
when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
new BrightnessInfo(0.45f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF,
- TRANSITION_POINT));
+ TRANSITION_POINT, BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE));
listener.onDisplayChanged(DISPLAY_ID);
vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
assertNull(vote);
@@ -1580,7 +1585,7 @@ public class DisplayModeDirectorTest {
// Turn on HBM
when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
new BrightnessInfo(1.0f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT,
- TRANSITION_POINT));
+ TRANSITION_POINT, BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE));
listener.onDisplayChanged(DISPLAY_ID);
vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
assertVoteForRefreshRate(vote, initialRefreshRate);
@@ -1598,7 +1603,7 @@ public class DisplayModeDirectorTest {
// Turn off HBM
when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
new BrightnessInfo(0.43f, 0.1f, 0.8f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF,
- TRANSITION_POINT));
+ TRANSITION_POINT, BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE));
listener.onDisplayChanged(DISPLAY_ID);
vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
assertNull(vote);
@@ -1606,7 +1611,7 @@ public class DisplayModeDirectorTest {
// Turn HBM on again and ensure the updated vote value stuck
when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
new BrightnessInfo(1.0f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT,
- TRANSITION_POINT));
+ TRANSITION_POINT, BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE));
listener.onDisplayChanged(DISPLAY_ID);
vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
assertVoteForRefreshRate(vote, updatedRefreshRate);
@@ -1622,7 +1627,7 @@ public class DisplayModeDirectorTest {
// Turn off HBM
when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
new BrightnessInfo(0.43f, 0.1f, 0.8f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF,
- TRANSITION_POINT));
+ TRANSITION_POINT, BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE));
listener.onDisplayChanged(DISPLAY_ID);
vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
assertNull(vote);
@@ -1654,7 +1659,7 @@ public class DisplayModeDirectorTest {
// Turn on HBM
when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
new BrightnessInfo(1.0f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT,
- TRANSITION_POINT));
+ TRANSITION_POINT, BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE));
listener.onDisplayChanged(DISPLAY_ID);
vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
assertNull(vote);
@@ -1662,7 +1667,7 @@ public class DisplayModeDirectorTest {
// Turn off HBM
when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
new BrightnessInfo(0.43f, 0.1f, 0.8f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF,
- TRANSITION_POINT));
+ TRANSITION_POINT, BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE));
listener.onDisplayChanged(DISPLAY_ID);
vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
assertNull(vote);
@@ -1694,7 +1699,7 @@ public class DisplayModeDirectorTest {
// Turn on HBM when HBM is supported; expect a valid transition point and a vote.
when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
new BrightnessInfo(1.0f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT,
- TRANSITION_POINT));
+ TRANSITION_POINT, BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE));
listener.onDisplayChanged(DISPLAY_ID);
vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
assertVoteForRefreshRate(vote, 60.0f);
@@ -1702,7 +1707,7 @@ public class DisplayModeDirectorTest {
// Turn off HBM
when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
new BrightnessInfo(1.0f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF,
- TRANSITION_POINT));
+ TRANSITION_POINT, BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE));
listener.onDisplayChanged(DISPLAY_ID);
vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
assertNull(vote);
@@ -1711,7 +1716,7 @@ public class DisplayModeDirectorTest {
// no vote.
when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
new BrightnessInfo(1.0f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT,
- HBM_TRANSITION_POINT_INVALID));
+ HBM_TRANSITION_POINT_INVALID, BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE));
listener.onDisplayChanged(DISPLAY_ID);
vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
assertNull(vote);
@@ -1720,7 +1725,7 @@ public class DisplayModeDirectorTest {
// no vote.
when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
new BrightnessInfo(1.0f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR,
- HBM_TRANSITION_POINT_INVALID));
+ HBM_TRANSITION_POINT_INVALID, BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE));
listener.onDisplayChanged(DISPLAY_ID);
vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
assertNull(vote);
@@ -1728,7 +1733,7 @@ public class DisplayModeDirectorTest {
// Turn off HBM
when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
new BrightnessInfo(1.0f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF,
- TRANSITION_POINT));
+ TRANSITION_POINT, BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE));
listener.onDisplayChanged(DISPLAY_ID);
vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
assertNull(vote);
@@ -1737,7 +1742,8 @@ public class DisplayModeDirectorTest {
private void setHbmAndAssertRefreshRate(
DisplayModeDirector director, DisplayListener listener, int mode, float rr) {
when(mInjector.getBrightnessInfo(DISPLAY_ID))
- .thenReturn(new BrightnessInfo(1.0f, 0.0f, 1.0f, mode, TRANSITION_POINT));
+ .thenReturn(new BrightnessInfo(1.0f, 0.0f, 1.0f, mode, TRANSITION_POINT,
+ BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE));
listener.onDisplayChanged(DISPLAY_ID);
final Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
@@ -1817,7 +1823,7 @@ public class DisplayModeDirectorTest {
// Turn on HBM
when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
new BrightnessInfo(1.0f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT,
- TRANSITION_POINT));
+ TRANSITION_POINT, BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE));
listener.onDisplayChanged(DISPLAY_ID);
vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
assertVoteForRefreshRate(vote, 60.f);
@@ -1978,7 +1984,8 @@ public class DisplayModeDirectorTest {
when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
new BrightnessInfo(floatBri, floatAdjBri, 0.0f, 1.0f,
- BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF, TRANSITION_POINT));
+ BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF, TRANSITION_POINT,
+ BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE));
listener.onDisplayChanged(DISPLAY_ID);
}
diff --git a/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java b/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java
index 4bb5d7482e25..6203c2f54f07 100644
--- a/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java
@@ -16,6 +16,8 @@
package com.android.server.display;
+import static android.hardware.display.BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE;
+import static android.hardware.display.BrightnessInfo.BRIGHTNESS_MAX_REASON_THERMAL;
import static android.hardware.display.BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR;
import static android.hardware.display.BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF;
import static android.hardware.display.BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT;
@@ -201,7 +203,7 @@ public class HighBrightnessModeControllerTest {
hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED);
hbmc.onAmbientLuxChange(MINIMUM_LUX + 1);
- hbmc.onBrightnessChanged(TRANSITION_POINT + 0.01f);
+ hbmcOnBrightnessChanged(hbmc, TRANSITION_POINT + 0.01f);
// Verify we are in HBM
assertState(hbmc, DEFAULT_MIN, DEFAULT_MAX, HIGH_BRIGHTNESS_MODE_SUNLIGHT);
@@ -233,7 +235,7 @@ public class HighBrightnessModeControllerTest {
hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED);
hbmc.onAmbientLuxChange(MINIMUM_LUX + 1);
- hbmc.onBrightnessChanged(TRANSITION_POINT + 0.01f);
+ hbmcOnBrightnessChanged(hbmc, TRANSITION_POINT + 0.01f);
// Verify we are in HBM
assertState(hbmc, DEFAULT_MIN, DEFAULT_MAX, HIGH_BRIGHTNESS_MODE_SUNLIGHT);
@@ -258,18 +260,18 @@ public class HighBrightnessModeControllerTest {
hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED);
hbmc.onAmbientLuxChange(MINIMUM_LUX + 1);
- hbmc.onBrightnessChanged(TRANSITION_POINT + 0.01f);
+ hbmcOnBrightnessChanged(hbmc, TRANSITION_POINT + 0.01f);
advanceTime(TIME_ALLOWED_IN_WINDOW_MILLIS / 2);
// Verify we are in HBM
assertState(hbmc, DEFAULT_MIN, DEFAULT_MAX, HIGH_BRIGHTNESS_MODE_SUNLIGHT);
- hbmc.onBrightnessChanged(TRANSITION_POINT - 0.01f);
+ hbmcOnBrightnessChanged(hbmc, TRANSITION_POINT - 0.01f);
advanceTime(1);
assertState(hbmc, DEFAULT_MIN, DEFAULT_MAX, HIGH_BRIGHTNESS_MODE_SUNLIGHT);
- hbmc.onBrightnessChanged(TRANSITION_POINT + 0.01f);
+ hbmcOnBrightnessChanged(hbmc, TRANSITION_POINT + 0.01f);
advanceTime(TIME_ALLOWED_IN_WINDOW_MILLIS / 2);
assertState(hbmc, DEFAULT_MIN, DEFAULT_MAX, HIGH_BRIGHTNESS_MODE_SUNLIGHT);
@@ -288,13 +290,13 @@ public class HighBrightnessModeControllerTest {
hbmc.onAmbientLuxChange(MINIMUM_LUX + 1);
// Go into HBM for half the allowed window
- hbmc.onBrightnessChanged(TRANSITION_POINT + 0.01f);
+ hbmcOnBrightnessChanged(hbmc, TRANSITION_POINT + 0.01f);
advanceTime(TIME_ALLOWED_IN_WINDOW_MILLIS / 2);
assertState(hbmc, DEFAULT_MIN, DEFAULT_MAX, HIGH_BRIGHTNESS_MODE_SUNLIGHT);
// Move lux below threshold (ending first event);
hbmc.onAmbientLuxChange(MINIMUM_LUX - 1);
- hbmc.onBrightnessChanged(TRANSITION_POINT);
+ hbmcOnBrightnessChanged(hbmc, TRANSITION_POINT);
assertState(hbmc, DEFAULT_MIN, TRANSITION_POINT, HIGH_BRIGHTNESS_MODE_OFF);
// Move up some amount of time so that there's still time in the window even after a
@@ -304,7 +306,7 @@ public class HighBrightnessModeControllerTest {
// Go into HBM for just under the second half of allowed window
hbmc.onAmbientLuxChange(MINIMUM_LUX + 1);
- hbmc.onBrightnessChanged(TRANSITION_POINT + 1);
+ hbmcOnBrightnessChanged(hbmc, TRANSITION_POINT + 1);
advanceTime((TIME_ALLOWED_IN_WINDOW_MILLIS / 2) - 1);
assertState(hbmc, DEFAULT_MIN, DEFAULT_MAX, HIGH_BRIGHTNESS_MODE_SUNLIGHT);
@@ -434,7 +436,7 @@ public class HighBrightnessModeControllerTest {
float brightness = 0.5f;
float expectedHdrBrightness = MathUtils.map(DEFAULT_MIN, TRANSITION_POINT,
DEFAULT_MIN, DEFAULT_MAX, brightness); // map value from normal range to hdr range
- hbmc.onBrightnessChanged(brightness);
+ hbmcOnBrightnessChanged(hbmc, brightness);
advanceTime(0);
assertEquals(expectedHdrBrightness, hbmc.getHdrBrightnessValue(), EPSILON);
@@ -442,21 +444,21 @@ public class HighBrightnessModeControllerTest {
brightness = 0.33f;
expectedHdrBrightness = MathUtils.map(DEFAULT_MIN, TRANSITION_POINT,
DEFAULT_MIN, DEFAULT_MAX, brightness); // map value from normal range to hdr range
- hbmc.onBrightnessChanged(brightness);
+ hbmcOnBrightnessChanged(hbmc, brightness);
advanceTime(0);
assertEquals(expectedHdrBrightness, hbmc.getHdrBrightnessValue(), EPSILON);
// Try the min value
brightness = DEFAULT_MIN;
expectedHdrBrightness = DEFAULT_MIN;
- hbmc.onBrightnessChanged(brightness);
+ hbmcOnBrightnessChanged(hbmc, brightness);
advanceTime(0);
assertEquals(expectedHdrBrightness, hbmc.getHdrBrightnessValue(), EPSILON);
// Try the max value
brightness = TRANSITION_POINT;
expectedHdrBrightness = DEFAULT_MAX;
- hbmc.onBrightnessChanged(brightness);
+ hbmcOnBrightnessChanged(hbmc, brightness);
advanceTime(0);
assertEquals(expectedHdrBrightness, hbmc.getHdrBrightnessValue(), EPSILON);
}
@@ -467,7 +469,7 @@ public class HighBrightnessModeControllerTest {
final int displayStatsId = mDisplayUniqueId.hashCode();
hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED);
- hbmc.onBrightnessChanged(TRANSITION_POINT);
+ hbmcOnBrightnessChanged(hbmc, TRANSITION_POINT);
hbmc.getHdrListener().onHdrInfoChanged(null /*displayToken*/, 1 /*numberOfHdrLayers*/,
DISPLAY_WIDTH, DISPLAY_HEIGHT, 0 /*flags*/);
advanceTime(0);
@@ -489,7 +491,7 @@ public class HighBrightnessModeControllerTest {
hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED);
hbmc.onAmbientLuxChange(MINIMUM_LUX + 1);
- hbmc.onBrightnessChanged(TRANSITION_POINT + 0.01f);
+ hbmcOnBrightnessChanged(hbmc, TRANSITION_POINT + 0.01f);
// Verify Stats HBM_ON_SUNLIGHT
verify(mInjectorMock).reportHbmStateChange(eq(displayStatsId),
@@ -506,12 +508,12 @@ public class HighBrightnessModeControllerTest {
}
@Test
- public void tetHbmStats_NbmHdrNoReport() {
+ public void testHbmStats_NbmHdrNoReport() {
final HighBrightnessModeController hbmc = createDefaultHbm(new OffsettableClock());
final int displayStatsId = mDisplayUniqueId.hashCode();
hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED);
- hbmc.onBrightnessChanged(DEFAULT_MIN);
+ hbmcOnBrightnessChanged(hbmc, DEFAULT_MIN);
hbmc.getHdrListener().onHdrInfoChanged(null /*displayToken*/, 1 /*numberOfHdrLayers*/,
DISPLAY_WIDTH, DISPLAY_HEIGHT, 0 /*flags*/);
advanceTime(0);
@@ -524,7 +526,27 @@ public class HighBrightnessModeControllerTest {
}
@Test
- public void testHbmStats_ThermalOff() throws Exception {
+ public void testHbmStats_HighLuxLowBrightnessNoReport() {
+ final HighBrightnessModeController hbmc = createDefaultHbm(new OffsettableClock());
+ final int displayStatsId = mDisplayUniqueId.hashCode();
+
+ hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED);
+ hbmc.onAmbientLuxChange(MINIMUM_LUX + 1);
+ hbmcOnBrightnessChanged(hbmc, DEFAULT_MIN);
+ advanceTime(0);
+ // verify in HBM sunlight mode
+ assertEquals(HIGH_BRIGHTNESS_MODE_SUNLIGHT, hbmc.getHighBrightnessMode());
+
+ // Verify Stats HBM_ON_SUNLIGHT not report
+ verify(mInjectorMock, never()).reportHbmStateChange(eq(displayStatsId),
+ eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_SUNLIGHT),
+ anyInt());
+ }
+
+ // Test reporting of thermal throttling when triggered by HighBrightnessModeController's
+ // internal thermal throttling.
+ @Test
+ public void testHbmStats_InternalThermalOff() throws Exception {
final HighBrightnessModeController hbmc = createDefaultHbm(new OffsettableClock());
final int displayStatsId = mDisplayUniqueId.hashCode();
@@ -534,7 +556,7 @@ public class HighBrightnessModeControllerTest {
hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED);
hbmc.onAmbientLuxChange(MINIMUM_LUX + 1);
- hbmc.onBrightnessChanged(TRANSITION_POINT + 0.01f);
+ hbmcOnBrightnessChanged(hbmc, TRANSITION_POINT + 0.01f);
advanceTime(1);
verify(mInjectorMock).reportHbmStateChange(eq(displayStatsId),
eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_SUNLIGHT),
@@ -548,6 +570,37 @@ public class HighBrightnessModeControllerTest {
eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_SV_OFF_THERMAL_LIMIT));
}
+ // Test reporting of thermal throttling when triggered externally through
+ // HighBrightnessModeController.onBrightnessChanged()
+ @Test
+ public void testHbmStats_ExternalThermalOff() throws Exception {
+ final HighBrightnessModeController hbmc = createDefaultHbm(new OffsettableClock());
+ final int displayStatsId = mDisplayUniqueId.hashCode();
+ final float hbmBrightness = TRANSITION_POINT + 0.01f;
+ final float nbmBrightness = TRANSITION_POINT - 0.01f;
+
+ hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED);
+ hbmc.onAmbientLuxChange(MINIMUM_LUX + 1);
+ // Brightness is unthrottled, HBM brightness granted
+ hbmc.onBrightnessChanged(hbmBrightness, hbmBrightness, BRIGHTNESS_MAX_REASON_NONE);
+ advanceTime(1);
+ verify(mInjectorMock).reportHbmStateChange(eq(displayStatsId),
+ eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_SUNLIGHT),
+ eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_TRANSITION_REASON_UNKNOWN));
+
+ // Brightness is thermally throttled, HBM brightness denied (NBM brightness granted)
+ hbmc.onBrightnessChanged(nbmBrightness, hbmBrightness, BRIGHTNESS_MAX_REASON_THERMAL);
+ advanceTime(1);
+ // We expect HBM mode to remain set to sunlight, indicating that HBMC *allows* this mode.
+ // However, we expect the HBM state reported by HBMC to be off, since external thermal
+ // throttling (reported to HBMC through onBrightnessChanged()) lowers brightness to below
+ // the HBM transition point.
+ assertEquals(HIGH_BRIGHTNESS_MODE_SUNLIGHT, hbmc.getHighBrightnessMode());
+ verify(mInjectorMock).reportHbmStateChange(eq(displayStatsId),
+ eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_OFF),
+ eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_SV_OFF_THERMAL_LIMIT));
+ }
+
@Test
public void testHbmStats_TimeOut() {
final HighBrightnessModeController hbmc = createDefaultHbm(new OffsettableClock());
@@ -555,7 +608,7 @@ public class HighBrightnessModeControllerTest {
hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED);
hbmc.onAmbientLuxChange(MINIMUM_LUX + 1);
- hbmc.onBrightnessChanged(TRANSITION_POINT + 0.01f);
+ hbmcOnBrightnessChanged(hbmc, TRANSITION_POINT + 0.01f);
advanceTime(0);
verify(mInjectorMock).reportHbmStateChange(eq(displayStatsId),
eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_SUNLIGHT),
@@ -576,7 +629,7 @@ public class HighBrightnessModeControllerTest {
hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED);
hbmc.onAmbientLuxChange(MINIMUM_LUX + 1);
- hbmc.onBrightnessChanged(TRANSITION_POINT + 0.01f);
+ hbmcOnBrightnessChanged(hbmc, TRANSITION_POINT + 0.01f);
advanceTime(0);
verify(mInjectorMock).reportHbmStateChange(eq(displayStatsId),
eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_SUNLIGHT),
@@ -595,7 +648,7 @@ public class HighBrightnessModeControllerTest {
hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED);
hbmc.onAmbientLuxChange(MINIMUM_LUX + 1);
- hbmc.onBrightnessChanged(TRANSITION_POINT + 0.01f);
+ hbmcOnBrightnessChanged(hbmc, TRANSITION_POINT + 0.01f);
advanceTime(0);
verify(mInjectorMock).reportHbmStateChange(eq(displayStatsId),
eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_SUNLIGHT),
@@ -610,6 +663,30 @@ public class HighBrightnessModeControllerTest {
eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_SV_OFF_HDR_PLAYING));
}
+ @Test
+ public void tetHbmStats_LowRequestedBrightness() {
+ final HighBrightnessModeController hbmc = createDefaultHbm(new OffsettableClock());
+ final int displayStatsId = mDisplayUniqueId.hashCode();
+
+ hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED);
+ hbmc.onAmbientLuxChange(MINIMUM_LUX + 1);
+ hbmcOnBrightnessChanged(hbmc, TRANSITION_POINT + 0.01f);
+ advanceTime(0);
+ // verify in HBM sunlight mode
+ assertEquals(HIGH_BRIGHTNESS_MODE_SUNLIGHT, hbmc.getHighBrightnessMode());
+ // verify HBM_ON_SUNLIGHT
+ verify(mInjectorMock).reportHbmStateChange(eq(displayStatsId),
+ eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_SUNLIGHT),
+ eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_TRANSITION_REASON_UNKNOWN));
+
+ hbmcOnBrightnessChanged(hbmc, DEFAULT_MIN);
+ // verify HBM_SV_OFF due to LOW_REQUESTED_BRIGHTNESS
+ verify(mInjectorMock).reportHbmStateChange(eq(displayStatsId),
+ eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_OFF),
+ eq(FrameworkStatsLog
+ .DISPLAY_HBM_STATE_CHANGED__REASON__HBM_SV_OFF_LOW_REQUESTED_BRIGHTNESS));
+ }
+
private void assertState(HighBrightnessModeController hbmc,
float brightnessMin, float brightnessMax, int hbmMode) {
assertEquals(brightnessMin, hbmc.getCurrentBrightnessMin(), EPSILON);
@@ -649,4 +726,8 @@ public class HighBrightnessModeControllerTest {
private Temperature getSkinTemp(@ThrottlingStatus int status) {
return new Temperature(30.0f, Temperature.TYPE_SKIN, "test_skin_temp", status);
}
+
+ private void hbmcOnBrightnessChanged(HighBrightnessModeController hbmc, float brightness) {
+ hbmc.onBrightnessChanged(brightness, brightness, BRIGHTNESS_MAX_REASON_NONE);
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/display/whitebalance/AmbientLuxTest.java b/services/tests/servicestests/src/com/android/server/display/whitebalance/AmbientLuxTest.java
index 0f3742f8577d..ac97911027bf 100644
--- a/services/tests/servicestests/src/com/android/server/display/whitebalance/AmbientLuxTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/whitebalance/AmbientLuxTest.java
@@ -59,6 +59,8 @@ import java.util.List;
@RunWith(JUnit4.class)
public final class AmbientLuxTest {
+
+ private static final float ALLOWED_ERROR_DELTA = 0.001f;
private static final int AMBIENT_COLOR_TYPE = 20705;
private static final String AMBIENT_COLOR_TYPE_STR = "colorSensoryDensoryDoc";
private static final float LOW_LIGHT_AMBIENT_COLOR_TEMPERATURE = 5432.1f;
@@ -78,6 +80,8 @@ public final class AmbientLuxTest {
@Mock private TypedArray mHighLightBiases;
@Mock private TypedArray mAmbientColorTemperatures;
@Mock private TypedArray mDisplayColorTemperatures;
+ @Mock private TypedArray mStrongAmbientColorTemperatures;
+ @Mock private TypedArray mStrongDisplayColorTemperatures;
@Mock private ColorDisplayService.ColorDisplayServiceInternal mColorDisplayServiceInternalMock;
@Before
@@ -110,6 +114,12 @@ public final class AmbientLuxTest {
when(mResourcesSpy.obtainTypedArray(
R.array.config_displayWhiteBalanceDisplayColorTemperatures))
.thenReturn(mDisplayColorTemperatures);
+ when(mResourcesSpy.obtainTypedArray(
+ R.array.config_displayWhiteBalanceStrongAmbientColorTemperatures))
+ .thenReturn(mStrongAmbientColorTemperatures);
+ when(mResourcesSpy.obtainTypedArray(
+ R.array.config_displayWhiteBalanceStrongDisplayColorTemperatures))
+ .thenReturn(mStrongDisplayColorTemperatures);
when(mResourcesSpy.obtainTypedArray(
R.array.config_displayWhiteBalanceLowLightAmbientBrightnesses))
@@ -375,6 +385,43 @@ public final class AmbientLuxTest {
}
@Test
+ public void testStrongMode() {
+ final float lowerBrightness = 10.0f;
+ final float upperBrightness = 50.0f;
+ setBrightnesses(lowerBrightness, upperBrightness);
+ setBiases(0.0f, 1.0f);
+ final int ambientColorTempLow = 6000;
+ final int ambientColorTempHigh = 8000;
+ final int displayColorTempLow = 6400;
+ final int displayColorTempHigh = 7400;
+ setStrongAmbientColorTemperatures(ambientColorTempLow, ambientColorTempHigh);
+ setStrongDisplayColorTemperatures(displayColorTempLow, displayColorTempHigh);
+
+ DisplayWhiteBalanceController controller =
+ DisplayWhiteBalanceFactory.create(mHandler, mSensorManagerMock, mResourcesSpy);
+ controller.setStrongModeEnabled(true);
+ controller.mBrightnessFilter = spy(new AmbientFilterStubber());
+
+ for (float ambientTempFraction = 0.0f; ambientTempFraction <= 1.0f;
+ ambientTempFraction += 0.1f) {
+ final float ambientTemp =
+ (ambientColorTempHigh - ambientColorTempLow) * ambientTempFraction
+ + ambientColorTempLow;
+ setEstimatedColorTemperature(controller, ambientTemp);
+ for (float brightnessFraction = 0.0f; brightnessFraction <= 1.0f;
+ brightnessFraction += 0.1f) {
+ setEstimatedBrightnessAndUpdate(controller,
+ mix(lowerBrightness, upperBrightness, brightnessFraction));
+ assertEquals(controller.mPendingAmbientColorTemperature,
+ mix(LOW_LIGHT_AMBIENT_COLOR_TEMPERATURE,
+ mix(displayColorTempLow, displayColorTempHigh, ambientTempFraction),
+ brightnessFraction),
+ ALLOWED_ERROR_DELTA);
+ }
+ }
+ }
+
+ @Test
public void testLowLight_DefaultAmbient() throws Exception {
final float lowerBrightness = 10.0f;
final float upperBrightness = 50.0f;
@@ -486,6 +533,14 @@ public final class AmbientLuxTest {
setFloatArrayResource(mDisplayColorTemperatures, vals);
}
+ private void setStrongAmbientColorTemperatures(float... vals) {
+ setFloatArrayResource(mStrongAmbientColorTemperatures, vals);
+ }
+
+ private void setStrongDisplayColorTemperatures(float... vals) {
+ setFloatArrayResource(mStrongDisplayColorTemperatures, vals);
+ }
+
private void setFloatArrayResource(TypedArray array, float[] vals) {
when(array.length()).thenReturn(vals.length);
for (int i = 0; i < vals.length; i++) {
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
index 7751ef564138..c48a974ddbb2 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
@@ -42,6 +42,7 @@ import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiPortInfo;
import android.hardware.hdmi.IHdmiCecVolumeControlFeatureListener;
import android.hardware.hdmi.IHdmiControlStatusChangeListener;
+import android.hardware.hdmi.IHdmiVendorCommandListener;
import android.os.Binder;
import android.os.Looper;
import android.os.RemoteException;
@@ -747,6 +748,114 @@ public class HdmiControlServiceTest {
}
@Test
+ public void addVendorCommandListener_receiveCallback_VendorCmdNoIdTest() {
+ int destAddress = mHdmiControlServiceSpy.playback().getDeviceInfo().getLogicalAddress();
+ int sourceAddress = Constants.ADDR_TV;
+ byte[] params = {0x00, 0x01, 0x02, 0x03};
+ int vendorId = 0x123456;
+
+ VendorCommandListener vendorCmdListener =
+ new VendorCommandListener(sourceAddress, destAddress, params, vendorId);
+ mHdmiControlServiceSpy.addVendorCommandListener(vendorCmdListener, vendorId);
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage vendorCommandNoId =
+ HdmiCecMessageBuilder.buildVendorCommand(sourceAddress, destAddress, params);
+ mNativeWrapper.onCecMessage(vendorCommandNoId);
+ mTestLooper.dispatchAll();
+ assertThat(vendorCmdListener.mVendorCommandCallbackReceived).isTrue();
+ assertThat(vendorCmdListener.mParamsCorrect).isTrue();
+ assertThat(vendorCmdListener.mHasVendorId).isFalse();
+ }
+
+ @Test
+ public void addVendorCommandListener_receiveCallback_VendorCmdWithIdTest() {
+ int destAddress = mHdmiControlServiceSpy.playback().getDeviceInfo().getLogicalAddress();
+ int sourceAddress = Constants.ADDR_TV;
+ byte[] params = {0x00, 0x01, 0x02, 0x03};
+ int vendorId = 0x123456;
+
+ VendorCommandListener vendorCmdListener =
+ new VendorCommandListener(sourceAddress, destAddress, params, vendorId);
+ mHdmiControlServiceSpy.addVendorCommandListener(vendorCmdListener, vendorId);
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage vendorCommandWithId =
+ HdmiCecMessageBuilder.buildVendorCommandWithId(
+ sourceAddress, destAddress, vendorId, params);
+ mNativeWrapper.onCecMessage(vendorCommandWithId);
+ mTestLooper.dispatchAll();
+ assertThat(vendorCmdListener.mVendorCommandCallbackReceived).isTrue();
+ assertThat(vendorCmdListener.mParamsCorrect).isTrue();
+ assertThat(vendorCmdListener.mHasVendorId).isTrue();
+ }
+
+ @Test
+ public void addVendorCommandListener_noCallback_VendorCmdDiffIdTest() {
+ int destAddress = mHdmiControlServiceSpy.playback().getDeviceInfo().getLogicalAddress();
+ int sourceAddress = Constants.ADDR_TV;
+ byte[] params = {0x00, 0x01, 0x02, 0x03};
+ int vendorId = 0x123456;
+ int diffVendorId = 0x345678;
+
+ VendorCommandListener vendorCmdListener =
+ new VendorCommandListener(sourceAddress, destAddress, params, vendorId);
+ mHdmiControlServiceSpy.addVendorCommandListener(vendorCmdListener, vendorId);
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage vendorCommandWithDiffId =
+ HdmiCecMessageBuilder.buildVendorCommandWithId(
+ sourceAddress, destAddress, diffVendorId, params);
+ mNativeWrapper.onCecMessage(vendorCommandWithDiffId);
+ mTestLooper.dispatchAll();
+ assertThat(vendorCmdListener.mVendorCommandCallbackReceived).isFalse();
+ }
+
+ private static class VendorCommandListener extends IHdmiVendorCommandListener.Stub {
+ boolean mVendorCommandCallbackReceived = false;
+ boolean mParamsCorrect = false;
+ boolean mHasVendorId = false;
+
+ int mSourceAddress;
+ int mDestAddress;
+ byte[] mParams;
+ int mVendorId;
+
+ VendorCommandListener(int sourceAddress, int destAddress, byte[] params, int vendorId) {
+ this.mSourceAddress = sourceAddress;
+ this.mDestAddress = destAddress;
+ this.mParams = params.clone();
+ this.mVendorId = vendorId;
+ }
+
+ @Override
+ public void onReceived(
+ int sourceAddress, int destAddress, byte[] params, boolean hasVendorId) {
+ mVendorCommandCallbackReceived = true;
+ if (mSourceAddress == sourceAddress && mDestAddress == destAddress) {
+ byte[] expectedParams;
+ if (hasVendorId) {
+ // If the command has vendor ID, we have to add it to mParams.
+ expectedParams = new byte[params.length];
+ expectedParams[0] = (byte) ((mVendorId >> 16) & 0xFF);
+ expectedParams[1] = (byte) ((mVendorId >> 8) & 0xFF);
+ expectedParams[2] = (byte) (mVendorId & 0xFF);
+ System.arraycopy(mParams, 0, expectedParams, 3, mParams.length);
+ } else {
+ expectedParams = params.clone();
+ }
+ if (Arrays.equals(expectedParams, params)) {
+ mParamsCorrect = true;
+ }
+ }
+ mHasVendorId = hasVendorId;
+ }
+
+ @Override
+ public void onControlStateChanged(boolean enabled, int reason) {}
+ }
+
+ @Test
public void dispatchMessageToLocalDevice_broadcastMessage_returnsHandled() {
HdmiCecMessage message = HdmiCecMessageBuilder.buildStandby(
Constants.ADDR_TV,
diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
index e80721a10e90..94cf20f9c15b 100644
--- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
@@ -51,10 +51,9 @@ import static android.net.NetworkPolicyManager.uidRulesToString;
import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK;
import static android.net.NetworkStats.METERED_NO;
import static android.net.NetworkStats.METERED_YES;
+import static android.net.NetworkTemplate.MATCH_CARRIER;
import static android.net.NetworkTemplate.MATCH_MOBILE;
-import static android.net.NetworkTemplate.buildTemplateCarrierMetered;
-import static android.net.NetworkTemplate.buildTemplateWifi;
-import static android.net.TrafficStats.MB_IN_BYTES;
+import static android.net.NetworkTemplate.MATCH_WIFI;
import static android.telephony.CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED;
import static android.telephony.CarrierConfigManager.DATA_CYCLE_THRESHOLD_DISABLED;
import static android.telephony.CarrierConfigManager.DATA_CYCLE_USE_PLATFORM_DEFAULT;
@@ -205,6 +204,7 @@ import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import java.util.TimeZone;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
@@ -229,10 +229,12 @@ public class NetworkPolicyManagerServiceTest {
private static final int TEST_SUB_ID = 42;
private static final Network TEST_NETWORK = mock(Network.class, CALLS_REAL_METHODS);
-
- private static NetworkTemplate sTemplateWifi = buildTemplateWifi(TEST_WIFI_NETWORK_KEY);
+ private static NetworkTemplate sTemplateWifi = new NetworkTemplate.Builder(MATCH_WIFI)
+ .setWifiNetworkKeys(Set.of(TEST_WIFI_NETWORK_KEY)).build();
private static NetworkTemplate sTemplateCarrierMetered =
- buildTemplateCarrierMetered(TEST_IMSI);
+ new NetworkTemplate.Builder(MATCH_CARRIER)
+ .setSubscriberIds(Set.of(TEST_IMSI))
+ .setMeteredness(METERED_YES).build();
/**
* Path on assets where files used by {@link NetPolicyXml} are located.
@@ -1160,11 +1162,12 @@ public class NetworkPolicyManagerServiceTest {
mPolicyListener.expect().onMeteredIfacesChanged(any());
setNetworkPolicies(new NetworkPolicy(
- sTemplateWifi, CYCLE_DAY, TIMEZONE_UTC, 1 * MB_IN_BYTES, 2 * MB_IN_BYTES, false));
+ sTemplateWifi, CYCLE_DAY, TIMEZONE_UTC, DataUnit.MEBIBYTES.toBytes(1),
+ DataUnit.MEBIBYTES.toBytes(2), false));
mPolicyListener.waitAndVerify().onMeteredIfacesChanged(eq(new String[]{TEST_IFACE}));
verify(mNetworkManager, atLeastOnce()).setInterfaceQuota(TEST_IFACE,
- (2 * MB_IN_BYTES) - 512);
+ DataUnit.MEBIBYTES.toBytes(2) - 512);
}
@Test
@@ -1252,7 +1255,7 @@ public class NetworkPolicyManagerServiceTest {
reset(mTelephonyManager, mNetworkManager, mNotifManager);
TelephonyManager tmSub = expectMobileDefaults();
- mService.snoozeLimit(NetworkTemplate.buildTemplateCarrierMetered(TEST_IMSI));
+ mService.snoozeLimit(sTemplateCarrierMetered);
mService.updateNetworks();
verify(tmSub, atLeastOnce()).setPolicyDataEnabled(true);
@@ -1955,7 +1958,7 @@ public class NetworkPolicyManagerServiceTest {
assertEquals("Unexpected number of network policies", 1, policies.length);
NetworkPolicy actualPolicy = policies[0];
assertEquals("Unexpected template match rule in network policies",
- NetworkTemplate.MATCH_WIFI,
+ MATCH_WIFI,
actualPolicy.template.getMatchRule());
assertEquals("Unexpected subscriberIds size in network policies",
actualPolicy.template.getSubscriberIds().size(), 0);
@@ -2026,7 +2029,10 @@ public class NetworkPolicyManagerServiceTest {
private static NetworkPolicy buildFakeCarrierPolicy(int cycleDay, long warningBytes,
long limitBytes, boolean inferred) {
- final NetworkTemplate template = buildTemplateCarrierMetered(FAKE_SUBSCRIBER_ID);
+ // TODO: Refactor this to use sTemplateCarrierMetered.
+ final NetworkTemplate template = new NetworkTemplate.Builder(MATCH_CARRIER)
+ .setSubscriberIds(Set.of(FAKE_SUBSCRIBER_ID))
+ .setMeteredness(METERED_YES).build();
return new NetworkPolicy(template, cycleDay, TimeZone.getDefault().getID(), warningBytes,
limitBytes, SNOOZE_NEVER, SNOOZE_NEVER, true, inferred);
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
index 6c9a60ac47fb..ac836426b536 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
@@ -315,18 +315,19 @@ public class PackageManagerSettingsTests {
.setNeutralButtonAction(BUTTON_ACTION_UNSUSPEND)
.build();
- ps1.addOrUpdateSuspension("suspendingPackage1", dialogInfo1, appExtras1, launcherExtras1,
- 0);
- ps1.addOrUpdateSuspension("suspendingPackage2", dialogInfo2, appExtras2, launcherExtras2,
- 0);
+ ps1.modifyUserState(0).putSuspendParams( "suspendingPackage1",
+ SuspendParams.getInstanceOrNull(dialogInfo1, appExtras1, launcherExtras1));
+ ps1.modifyUserState(0).putSuspendParams( "suspendingPackage2",
+ SuspendParams.getInstanceOrNull(dialogInfo2, appExtras2, launcherExtras2));
settingsUnderTest.mPackages.put(PACKAGE_NAME_1, ps1);
watcher.verifyChangeReported("put package 1");
- ps2.addOrUpdateSuspension("suspendingPackage3", null, appExtras1, null, 0);
+ ps2.modifyUserState(0).putSuspendParams( "suspendingPackage3",
+ SuspendParams.getInstanceOrNull(null, appExtras1, null));
settingsUnderTest.mPackages.put(PACKAGE_NAME_2, ps2);
watcher.verifyChangeReported("put package 2");
- ps3.removeSuspension("irrelevant", 0);
+ ps3.modifyUserState(0).removeSuspension("irrelevant");
settingsUnderTest.mPackages.put(PACKAGE_NAME_3, ps3);
watcher.verifyChangeReported("put package 3");
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerTests.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerTests.java
index b621a4408f40..869ac8877d6d 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerTests.java
@@ -70,7 +70,7 @@ import androidx.test.filters.SmallTest;
import androidx.test.filters.Suppress;
import com.android.frameworks.servicestests.R;
-import com.android.internal.content.PackageHelper;
+import com.android.internal.content.InstallLocationUtils;
import com.android.server.pm.pkg.parsing.ParsingPackage;
import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
@@ -106,11 +106,11 @@ public class PackageManagerTests extends AndroidTestCase {
private static final String SECURE_CONTAINERS_PREFIX = "/mnt/asec";
- private static final int APP_INSTALL_AUTO = PackageHelper.APP_INSTALL_AUTO;
+ private static final int APP_INSTALL_AUTO = InstallLocationUtils.APP_INSTALL_AUTO;
- private static final int APP_INSTALL_DEVICE = PackageHelper.APP_INSTALL_INTERNAL;
+ private static final int APP_INSTALL_DEVICE = InstallLocationUtils.APP_INSTALL_INTERNAL;
- private static final int APP_INSTALL_SDCARD = PackageHelper.APP_INSTALL_EXTERNAL;
+ private static final int APP_INSTALL_SDCARD = InstallLocationUtils.APP_INSTALL_EXTERNAL;
void failStr(String errMsg) {
Log.w(TAG, "errMsg=" + errMsg);
@@ -1214,7 +1214,7 @@ public class PackageManagerTests extends AndroidTestCase {
int origDefaultLoc = getDefaultInstallLoc();
InstallParams ip = null;
try {
- setInstallLoc(PackageHelper.APP_INSTALL_AUTO);
+ setInstallLoc(InstallLocationUtils.APP_INSTALL_AUTO);
// Install first
ip = installFromRawResource("install.apk", rawResId, installFlags, false,
false, -1, PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
@@ -1303,7 +1303,7 @@ public class PackageManagerTests extends AndroidTestCase {
InstallParams ip = null;
try {
PackageManager pm = getPm();
- setInstallLoc(PackageHelper.APP_INSTALL_AUTO);
+ setInstallLoc(InstallLocationUtils.APP_INSTALL_AUTO);
// Install first
ip = installFromRawResource("install.apk", R.raw.install, installFlags, false,
false, -1, PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
@@ -1517,11 +1517,11 @@ public class PackageManagerTests extends AndroidTestCase {
int iloc = PackageInfo.INSTALL_LOCATION_UNSPECIFIED;
boolean enable = getUserSettingSetInstallLocation();
if (enable) {
- if (userSetting == PackageHelper.APP_INSTALL_AUTO) {
+ if (userSetting == InstallLocationUtils.APP_INSTALL_AUTO) {
iloc = PackageInfo.INSTALL_LOCATION_AUTO;
- } else if (userSetting == PackageHelper.APP_INSTALL_EXTERNAL) {
+ } else if (userSetting == InstallLocationUtils.APP_INSTALL_EXTERNAL) {
iloc = PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL;
- } else if (userSetting == PackageHelper.APP_INSTALL_INTERNAL) {
+ } else if (userSetting == InstallLocationUtils.APP_INSTALL_INTERNAL) {
iloc = PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY;
}
}
@@ -1552,7 +1552,7 @@ public class PackageManagerTests extends AndroidTestCase {
}
@LargeTest
public void testExistingIUserI() throws Exception {
- int userSetting = PackageHelper.APP_INSTALL_INTERNAL;
+ int userSetting = InstallLocationUtils.APP_INSTALL_INTERNAL;
int iFlags = PackageManager.INSTALL_INTERNAL;
setExistingXUserX(userSetting, iFlags, PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
}
@@ -1564,14 +1564,14 @@ public class PackageManagerTests extends AndroidTestCase {
return;
}
- int userSetting = PackageHelper.APP_INSTALL_EXTERNAL;
+ int userSetting = InstallLocationUtils.APP_INSTALL_EXTERNAL;
int iFlags = PackageManager.INSTALL_INTERNAL;
setExistingXUserX(userSetting, iFlags, PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
}
@LargeTest
public void testExistingIUserA() throws Exception {
- int userSetting = PackageHelper.APP_INSTALL_AUTO;
+ int userSetting = InstallLocationUtils.APP_INSTALL_AUTO;
int iFlags = PackageManager.INSTALL_INTERNAL;
setExistingXUserX(userSetting, iFlags, PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
}
@@ -1616,7 +1616,7 @@ public class PackageManagerTests extends AndroidTestCase {
}
@LargeTest
public void testUserI() throws Exception {
- int userSetting = PackageHelper.APP_INSTALL_INTERNAL;
+ int userSetting = InstallLocationUtils.APP_INSTALL_INTERNAL;
int iloc = getExpectedInstallLocation(userSetting);
setUserX(true, userSetting, iloc);
}
@@ -1628,14 +1628,14 @@ public class PackageManagerTests extends AndroidTestCase {
return;
}
- int userSetting = PackageHelper.APP_INSTALL_EXTERNAL;
+ int userSetting = InstallLocationUtils.APP_INSTALL_EXTERNAL;
int iloc = getExpectedInstallLocation(userSetting);
setUserX(true, userSetting, iloc);
}
@LargeTest
public void testUserA() throws Exception {
- int userSetting = PackageHelper.APP_INSTALL_AUTO;
+ int userSetting = InstallLocationUtils.APP_INSTALL_AUTO;
int iloc = getExpectedInstallLocation(userSetting);
setUserX(true, userSetting, iloc);
}
@@ -1646,7 +1646,7 @@ public class PackageManagerTests extends AndroidTestCase {
*/
@LargeTest
public void testUserPrefOffUserI() throws Exception {
- int userSetting = PackageHelper.APP_INSTALL_INTERNAL;
+ int userSetting = InstallLocationUtils.APP_INSTALL_INTERNAL;
int iloc = PackageInfo.INSTALL_LOCATION_UNSPECIFIED;
setUserX(false, userSetting, iloc);
}
@@ -1658,14 +1658,14 @@ public class PackageManagerTests extends AndroidTestCase {
return;
}
- int userSetting = PackageHelper.APP_INSTALL_EXTERNAL;
+ int userSetting = InstallLocationUtils.APP_INSTALL_EXTERNAL;
int iloc = PackageInfo.INSTALL_LOCATION_UNSPECIFIED;
setUserX(false, userSetting, iloc);
}
@LargeTest
public void testUserPrefOffA() throws Exception {
- int userSetting = PackageHelper.APP_INSTALL_AUTO;
+ int userSetting = InstallLocationUtils.APP_INSTALL_AUTO;
int iloc = PackageInfo.INSTALL_LOCATION_UNSPECIFIED;
setUserX(false, userSetting, iloc);
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java
index 1e4134ec25d8..6c7236993df0 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java
@@ -41,6 +41,7 @@ import com.android.server.pm.pkg.SuspendParams;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mockito;
@Presubmit
@RunWith(AndroidJUnit4.class)
@@ -323,30 +324,31 @@ public class PackageUserStateTest {
}
@Test
public void testPackageUseReasons() throws Exception {
- final PackageStateUnserialized testState1 = new PackageStateUnserialized();
+ PackageSetting packageSetting = Mockito.mock(PackageSetting.class);
+ final PackageStateUnserialized testState1 = new PackageStateUnserialized(packageSetting);
testState1.setLastPackageUsageTimeInMills(-1, 10L);
assertLastPackageUsageUnset(testState1);
- final PackageStateUnserialized testState2 = new PackageStateUnserialized();
+ final PackageStateUnserialized testState2 = new PackageStateUnserialized(packageSetting);
testState2.setLastPackageUsageTimeInMills(
PackageManager.NOTIFY_PACKAGE_USE_REASONS_COUNT, 20L);
assertLastPackageUsageUnset(testState2);
- final PackageStateUnserialized testState3 = new PackageStateUnserialized();
+ final PackageStateUnserialized testState3 = new PackageStateUnserialized(packageSetting);
testState3.setLastPackageUsageTimeInMills(Integer.MAX_VALUE, 30L);
assertLastPackageUsageUnset(testState3);
- final PackageStateUnserialized testState4 = new PackageStateUnserialized();
+ final PackageStateUnserialized testState4 = new PackageStateUnserialized(packageSetting);
testState4.setLastPackageUsageTimeInMills(0, 40L);
assertLastPackageUsageSet(testState4, 0, 40L);
- final PackageStateUnserialized testState5 = new PackageStateUnserialized();
+ final PackageStateUnserialized testState5 = new PackageStateUnserialized(packageSetting);
testState5.setLastPackageUsageTimeInMills(
PackageManager.NOTIFY_PACKAGE_USE_CONTENT_PROVIDER, 50L);
assertLastPackageUsageSet(
testState5, PackageManager.NOTIFY_PACKAGE_USE_CONTENT_PROVIDER, 50L);
- final PackageStateUnserialized testState6 = new PackageStateUnserialized();
+ final PackageStateUnserialized testState6 = new PackageStateUnserialized(packageSetting);
testState6.setLastPackageUsageTimeInMills(
PackageManager.NOTIFY_PACKAGE_USE_REASONS_COUNT - 1, 60L);
assertLastPackageUsageSet(
diff --git a/services/tests/servicestests/src/com/android/server/pm/ScanTests.java b/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
index 4a24bbdb09ac..8e53ca1d599d 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
@@ -17,7 +17,7 @@
package com.android.server.pm;
import static android.content.pm.SharedLibraryInfo.TYPE_DYNAMIC;
-import static android.content.pm.SharedLibraryInfo.TYPE_SDK;
+import static android.content.pm.SharedLibraryInfo.TYPE_SDK_PACKAGE;
import static android.content.pm.SharedLibraryInfo.TYPE_STATIC;
import static android.content.pm.SharedLibraryInfo.VERSION_UNDEFINED;
@@ -258,7 +258,7 @@ public class ScanTests {
assertThat(scanResult.mSdkSharedLibraryInfo.getPackageName(), is("ogl.sdk_123"));
assertThat(scanResult.mSdkSharedLibraryInfo.getName(), is("ogl.sdk"));
assertThat(scanResult.mSdkSharedLibraryInfo.getLongVersion(), is(123L));
- assertThat(scanResult.mSdkSharedLibraryInfo.getType(), is(TYPE_SDK));
+ assertThat(scanResult.mSdkSharedLibraryInfo.getType(), is(TYPE_SDK_PACKAGE));
assertThat(scanResult.mSdkSharedLibraryInfo.getDeclaringPackage().getPackageName(),
is("ogl.sdk_123"));
assertThat(scanResult.mSdkSharedLibraryInfo.getDeclaringPackage().getLongVersionCode(),
diff --git a/services/tests/servicestests/src/com/android/server/power/LowPowerStandbyControllerTest.java b/services/tests/servicestests/src/com/android/server/power/LowPowerStandbyControllerTest.java
new file mode 100644
index 000000000000..4ae9613a36d3
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/power/LowPowerStandbyControllerTest.java
@@ -0,0 +1,410 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.power;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.AlarmManager;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.os.IPowerManager;
+import android.os.PowerManager;
+import android.os.PowerManagerInternal;
+import android.os.test.TestLooper;
+import android.provider.Settings;
+import android.test.mock.MockContentResolver;
+
+import androidx.test.InstrumentationRegistry;
+
+import com.android.internal.util.test.BroadcastInterceptingContext;
+import com.android.internal.util.test.FakeSettingsProvider;
+import com.android.server.LocalServices;
+import com.android.server.testutils.OffsettableClock;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.InOrder;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Tests for {@link com.android.server.power.LowPowerStandbyController}.
+ *
+ * Build/Install/Run:
+ * atest LowPowerStandbyControllerTest
+ */
+public class LowPowerStandbyControllerTest {
+ private static final int STANDBY_TIMEOUT = 5000;
+
+ private LowPowerStandbyController mController;
+ private BroadcastInterceptingContext mContextSpy;
+ private Resources mResourcesSpy;
+ private OffsettableClock mClock;
+ private TestLooper mTestLooper;
+
+ @Mock
+ private AlarmManager mAlarmManagerMock;
+ @Mock
+ private IPowerManager mIPowerManagerMock;
+ @Mock
+ private PowerManagerInternal mPowerManagerInternalMock;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ mContextSpy = spy(new BroadcastInterceptingContext(InstrumentationRegistry.getContext()));
+ when(mContextSpy.getSystemService(AlarmManager.class)).thenReturn(mAlarmManagerMock);
+ PowerManager powerManager = new PowerManager(mContextSpy, mIPowerManagerMock, null, null);
+ when(mContextSpy.getSystemService(PowerManager.class)).thenReturn(powerManager);
+ addLocalServiceMock(PowerManagerInternal.class, mPowerManagerInternalMock);
+
+ when(mIPowerManagerMock.isInteractive()).thenReturn(true);
+
+ mResourcesSpy = spy(mContextSpy.getResources());
+ when(mContextSpy.getResources()).thenReturn(mResourcesSpy);
+ when(mResourcesSpy.getBoolean(
+ com.android.internal.R.bool.config_lowPowerStandbySupported))
+ .thenReturn(true);
+ when(mResourcesSpy.getInteger(
+ com.android.internal.R.integer.config_lowPowerStandbyNonInteractiveTimeout))
+ .thenReturn(STANDBY_TIMEOUT);
+ when(mResourcesSpy.getBoolean(
+ com.android.internal.R.bool.config_lowPowerStandbyEnabledByDefault))
+ .thenReturn(false);
+
+ FakeSettingsProvider.clearSettingsProvider();
+ MockContentResolver cr = new MockContentResolver(mContextSpy);
+ cr.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
+ when(mContextSpy.getContentResolver()).thenReturn(cr);
+
+ mClock = new OffsettableClock.Stopped();
+ mTestLooper = new TestLooper(mClock::now);
+
+ mController = new LowPowerStandbyController(mContextSpy, mTestLooper.getLooper(),
+ () -> mClock.now());
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ LocalServices.removeServiceForTest(PowerManagerInternal.class);
+ LocalServices.removeServiceForTest(LowPowerStandbyControllerInternal.class);
+ }
+
+ @Test
+ public void testOnSystemReady_isInactivate() {
+ setLowPowerStandbySupportedConfig(true);
+ mController.systemReady();
+
+ assertThat(mController.isActive()).isFalse();
+ verify(mPowerManagerInternalMock, never()).setLowPowerStandbyActive(anyBoolean());
+ }
+
+ @Test
+ public void testActivate() throws Exception {
+ setLowPowerStandbySupportedConfig(true);
+ mController.systemReady();
+ mController.setEnabled(true);
+ setNonInteractive();
+ setDeviceIdleMode(true);
+ awaitStandbyTimeoutAlarm();
+ assertThat(mController.isActive()).isTrue();
+ verify(mPowerManagerInternalMock, times(1)).setLowPowerStandbyActive(true);
+ }
+
+ private void awaitStandbyTimeoutAlarm() {
+ ArgumentCaptor<Long> timeArg = ArgumentCaptor.forClass(Long.class);
+ ArgumentCaptor<AlarmManager.OnAlarmListener> listenerArg =
+ ArgumentCaptor.forClass(AlarmManager.OnAlarmListener.class);
+ verify(mAlarmManagerMock).setExact(
+ eq(AlarmManager.ELAPSED_REALTIME_WAKEUP),
+ timeArg.capture(), anyString(),
+ listenerArg.capture(), any());
+ mClock.reset();
+ mClock.fastForward(timeArg.getValue());
+ listenerArg.getValue().onAlarm();
+ mTestLooper.dispatchAll();
+ }
+
+ @Test
+ public void testOnNonInteractive_notImmediatelyActive() throws Exception {
+ setLowPowerStandbySupportedConfig(true);
+ mController.systemReady();
+ mController.setEnabled(true);
+
+ setNonInteractive();
+ mTestLooper.dispatchAll();
+
+ assertThat(mController.isActive()).isFalse();
+ verify(mPowerManagerInternalMock, never()).setLowPowerStandbyActive(anyBoolean());
+ }
+
+ @Test
+ public void testOnNonInteractive_activateAfterStandbyTimeout() throws Exception {
+ setLowPowerStandbySupportedConfig(true);
+ mController.systemReady();
+ mController.setEnabled(true);
+
+ setNonInteractive();
+ awaitStandbyTimeoutAlarm();
+
+ assertThat(mController.isActive()).isTrue();
+ verify(mPowerManagerInternalMock, times(1)).setLowPowerStandbyActive(true);
+ }
+
+ @Test
+ public void testOnNonInteractive_doesNotActivateWhenBecomingInteractive() throws Exception {
+ setLowPowerStandbySupportedConfig(true);
+ mController.systemReady();
+ mController.setEnabled(true);
+
+ setNonInteractive();
+ advanceTime(STANDBY_TIMEOUT / 2);
+ setInteractive();
+ verifyStandbyAlarmCancelled();
+
+ assertThat(mController.isActive()).isFalse();
+ verify(mPowerManagerInternalMock, never()).setLowPowerStandbyActive(anyBoolean());
+ }
+
+ private void verifyStandbyAlarmCancelled() {
+ InOrder inOrder = inOrder(mAlarmManagerMock);
+ inOrder.verify(mAlarmManagerMock, atLeast(0)).setExact(anyInt(), anyLong(), anyString(),
+ any(), any());
+ inOrder.verify(mAlarmManagerMock).cancel((AlarmManager.OnAlarmListener) any());
+ inOrder.verifyNoMoreInteractions();
+ }
+
+ @Test
+ public void testOnInteractive_deactivate() throws Exception {
+ setLowPowerStandbySupportedConfig(true);
+ mController.systemReady();
+ mController.setEnabled(true);
+ setNonInteractive();
+ setDeviceIdleMode(true);
+ awaitStandbyTimeoutAlarm();
+
+ setInteractive();
+ mTestLooper.dispatchAll();
+
+ assertThat(mController.isActive()).isFalse();
+ verify(mPowerManagerInternalMock, times(1)).setLowPowerStandbyActive(false);
+ }
+
+ @Test
+ public void testOnDozeMaintenance_deactivate() throws Exception {
+ setLowPowerStandbySupportedConfig(true);
+ mController.systemReady();
+ mController.setEnabled(true);
+ mController.setActiveDuringMaintenance(false);
+ setNonInteractive();
+ setDeviceIdleMode(true);
+ awaitStandbyTimeoutAlarm();
+
+ setDeviceIdleMode(false);
+ mTestLooper.dispatchAll();
+
+ assertThat(mController.isActive()).isFalse();
+ verify(mPowerManagerInternalMock, times(1)).setLowPowerStandbyActive(false);
+ }
+
+ @Test
+ public void testOnDozeMaintenance_activeDuringMaintenance_staysActive() throws Exception {
+ setLowPowerStandbySupportedConfig(true);
+ mController.systemReady();
+ mController.setEnabled(true);
+ mController.setActiveDuringMaintenance(true);
+ setNonInteractive();
+ setDeviceIdleMode(true);
+ awaitStandbyTimeoutAlarm();
+
+ setDeviceIdleMode(false);
+ mTestLooper.dispatchAll();
+
+ assertThat(mController.isActive()).isTrue();
+ verify(mPowerManagerInternalMock, never()).setLowPowerStandbyActive(false);
+ }
+
+ @Test
+ public void testOnDozeMaintenanceEnds_activate() throws Exception {
+ setLowPowerStandbySupportedConfig(true);
+ mController.systemReady();
+ mController.setEnabled(true);
+ setNonInteractive();
+ setDeviceIdleMode(true);
+ awaitStandbyTimeoutAlarm();
+
+ setDeviceIdleMode(false);
+ advanceTime(1000);
+ setDeviceIdleMode(true);
+ mTestLooper.dispatchAll();
+
+ assertThat(mController.isActive()).isTrue();
+ verify(mPowerManagerInternalMock, times(2)).setLowPowerStandbyActive(true);
+ }
+
+ @Test
+ public void testLowPowerStandbyDisabled_doesNotActivate() throws Exception {
+ setLowPowerStandbySupportedConfig(true);
+ mController.systemReady();
+ mController.setEnabled(false);
+ setNonInteractive();
+
+ assertThat(mController.isActive()).isFalse();
+ verify(mAlarmManagerMock, never()).setExact(anyInt(), anyLong(), anyString(), any(), any());
+ verify(mPowerManagerInternalMock, never()).setLowPowerStandbyActive(anyBoolean());
+ }
+
+ @Test
+ public void testLowPowerStandbyEnabled_EnabledChangedBroadcastsAreSent() throws Exception {
+ setLowPowerStandbySupportedConfig(true);
+ mController.systemReady();
+
+ BroadcastInterceptingContext.FutureIntent futureIntent = mContextSpy.nextBroadcastIntent(
+ PowerManager.ACTION_LOW_POWER_STANDBY_ENABLED_CHANGED);
+ mController.setEnabled(false);
+ futureIntent.assertNotReceived();
+
+ futureIntent = mContextSpy.nextBroadcastIntent(
+ PowerManager.ACTION_LOW_POWER_STANDBY_ENABLED_CHANGED);
+ mController.setEnabled(true);
+ assertThat(futureIntent.get(1, TimeUnit.SECONDS)).isNotNull();
+
+ futureIntent = mContextSpy.nextBroadcastIntent(
+ PowerManager.ACTION_LOW_POWER_STANDBY_ENABLED_CHANGED);
+ mController.setEnabled(true);
+ futureIntent.assertNotReceived();
+
+ futureIntent = mContextSpy.nextBroadcastIntent(
+ PowerManager.ACTION_LOW_POWER_STANDBY_ENABLED_CHANGED);
+
+ mController.setEnabled(false);
+ assertThat(futureIntent.get(1, TimeUnit.SECONDS)).isNotNull();
+ }
+
+ @Test
+ public void testSetEnabled_WhenNotSupported_DoesNotEnable() throws Exception {
+ setLowPowerStandbySupportedConfig(false);
+ mController.systemReady();
+
+ mController.setEnabled(true);
+
+ assertThat(mController.isEnabled()).isFalse();
+ }
+
+ @Test
+ public void testIsSupported_WhenSupported() throws Exception {
+ setLowPowerStandbySupportedConfig(true);
+ mController.systemReady();
+
+ assertThat(mController.isSupported()).isTrue();
+ }
+
+ @Test
+ public void testIsSupported_WhenNotSupported() throws Exception {
+ setLowPowerStandbySupportedConfig(false);
+ mController.systemReady();
+
+ assertThat(mController.isSupported()).isFalse();
+ }
+
+ @Test
+ public void testAllowlistChange_servicesAreNotified() throws Exception {
+ setLowPowerStandbySupportedConfig(true);
+ mController.systemReady();
+
+ LowPowerStandbyControllerInternal service = LocalServices.getService(
+ LowPowerStandbyControllerInternal.class);
+ service.addToAllowlist(10);
+ mTestLooper.dispatchAll();
+ verify(mPowerManagerInternalMock).setLowPowerStandbyAllowlist(new int[] {10});
+
+ service.removeFromAllowlist(10);
+ mTestLooper.dispatchAll();
+ verify(mPowerManagerInternalMock).setLowPowerStandbyAllowlist(new int[] {});
+ }
+
+ @Test
+ public void testForceActive() throws Exception {
+ setLowPowerStandbySupportedConfig(false);
+ mController.systemReady();
+
+ mController.forceActive(true);
+ mTestLooper.dispatchAll();
+
+ assertThat(mController.isActive()).isTrue();
+ verify(mPowerManagerInternalMock).setLowPowerStandbyActive(true);
+
+ mController.forceActive(false);
+ mTestLooper.dispatchAll();
+
+ assertThat(mController.isActive()).isFalse();
+ verify(mPowerManagerInternalMock).setLowPowerStandbyActive(false);
+ }
+
+ private void setLowPowerStandbySupportedConfig(boolean supported) {
+ when(mResourcesSpy.getBoolean(
+ com.android.internal.R.bool.config_lowPowerStandbySupported))
+ .thenReturn(supported);
+ }
+
+ private void setInteractive() throws Exception {
+ when(mIPowerManagerMock.isInteractive()).thenReturn(true);
+ mContextSpy.sendBroadcast(new Intent(Intent.ACTION_SCREEN_ON));
+ }
+
+ private void setNonInteractive() throws Exception {
+ when(mIPowerManagerMock.isInteractive()).thenReturn(false);
+ mContextSpy.sendBroadcast(new Intent(Intent.ACTION_SCREEN_OFF));
+ }
+
+ private void setDeviceIdleMode(boolean idle) throws Exception {
+ when(mIPowerManagerMock.isDeviceIdleMode()).thenReturn(idle);
+ mContextSpy.sendBroadcast(new Intent(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED));
+ }
+
+ private void advanceTime(long timeMs) {
+ mClock.fastForward(timeMs);
+ mTestLooper.dispatchAll();
+ }
+
+ /**
+ * 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);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
index d35c679c18bd..827349ad433a 100644
--- a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
@@ -16,6 +16,8 @@
package com.android.server.power;
+import static android.app.ActivityManager.PROCESS_STATE_BOUND_TOP;
+import static android.app.ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
import static android.os.PowerManagerInternal.WAKEFULNESS_ASLEEP;
import static android.os.PowerManagerInternal.WAKEFULNESS_AWAKE;
import static android.os.PowerManagerInternal.WAKEFULNESS_DOZING;
@@ -62,9 +64,11 @@ import android.os.BatterySaverPolicyConfig;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
+import android.os.IWakeLockCallback;
import android.os.Looper;
import android.os.PowerManager;
import android.os.PowerSaveState;
+import android.os.SystemClock;
import android.os.UserHandle;
import android.os.test.TestLooper;
import android.provider.Settings;
@@ -86,6 +90,7 @@ import com.android.server.power.PowerManagerService.BinderService;
import com.android.server.power.PowerManagerService.Injector;
import com.android.server.power.PowerManagerService.NativeWrapper;
import com.android.server.power.PowerManagerService.UserSwitchedReceiver;
+import com.android.server.power.PowerManagerService.WakeLock;
import com.android.server.power.batterysaver.BatterySaverController;
import com.android.server.power.batterysaver.BatterySaverPolicy;
import com.android.server.power.batterysaver.BatterySaverStateMachine;
@@ -98,6 +103,7 @@ import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.ArgumentMatcher;
import org.mockito.Mock;
+import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.mockito.stubbing.Answer;
@@ -283,6 +289,13 @@ public class PowerManagerServiceTest {
void invalidateIsInteractiveCaches() {
// Avoids an SELinux failure.
}
+
+ @Override
+ LowPowerStandbyController createLowPowerStandbyController(Context context,
+ Looper looper) {
+ return new LowPowerStandbyController(context, mTestLooper.getLooper(),
+ SystemClock::elapsedRealtime);
+ }
});
return mService;
}
@@ -293,6 +306,7 @@ public class PowerManagerServiceTest {
LocalServices.removeServiceForTest(DisplayManagerInternal.class);
LocalServices.removeServiceForTest(BatteryManagerInternal.class);
LocalServices.removeServiceForTest(ActivityManagerInternal.class);
+ LocalServices.removeServiceForTest(LowPowerStandbyControllerInternal.class);
FakeSettingsProvider.clearSettingsProvider();
}
@@ -467,21 +481,21 @@ public class PowerManagerServiceTest {
// First, ensure that a normal full wake lock does not cause a wakeup
int flags = PowerManager.FULL_WAKE_LOCK;
mService.getBinderServiceInstance().acquireWakeLock(token, flags, tag, packageName,
- null /* workSource */, null /* historyTag */, Display.INVALID_DISPLAY);
+ null /* workSource */, null /* historyTag */, Display.INVALID_DISPLAY, null);
assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_ASLEEP);
mService.getBinderServiceInstance().releaseWakeLock(token, 0 /* flags */);
// Ensure that the flag does *NOT* work with a partial wake lock.
flags = PowerManager.PARTIAL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP;
mService.getBinderServiceInstance().acquireWakeLock(token, flags, tag, packageName,
- null /* workSource */, null /* historyTag */, Display.INVALID_DISPLAY);
+ null /* workSource */, null /* historyTag */, Display.INVALID_DISPLAY, null);
assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_ASLEEP);
mService.getBinderServiceInstance().releaseWakeLock(token, 0 /* flags */);
// Verify that flag forces a wakeup when paired to a FULL_WAKE_LOCK
flags = PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP;
mService.getBinderServiceInstance().acquireWakeLock(token, flags, tag, packageName,
- null /* workSource */, null /* historyTag */, Display.INVALID_DISPLAY);
+ null /* workSource */, null /* historyTag */, Display.INVALID_DISPLAY, null);
assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE);
mService.getBinderServiceInstance().releaseWakeLock(token, 0 /* flags */);
}
@@ -649,12 +663,12 @@ public class PowerManagerServiceTest {
wakelockMap.put((String) inv.getArguments()[1], (int) inv.getArguments()[0]);
return null;
}).when(mNotifierMock).onWakeLockAcquired(anyInt(), anyString(), anyString(), anyInt(),
- anyInt(), any(), any());
+ anyInt(), any(), any(), any());
doAnswer(inv -> {
wakelockMap.remove((String) inv.getArguments()[1]);
return null;
}).when(mNotifierMock).onWakeLockReleased(anyInt(), anyString(), anyString(), anyInt(),
- anyInt(), any(), any());
+ anyInt(), any(), any(), any());
//
// TEST STARTS HERE
@@ -667,7 +681,7 @@ public class PowerManagerServiceTest {
// Create a wakelock
mService.getBinderServiceInstance().acquireWakeLock(new Binder(), flags, tag, pkg,
- null /* workSource */, null /* historyTag */, Display.INVALID_DISPLAY);
+ null /* workSource */, null /* historyTag */, Display.INVALID_DISPLAY, null);
assertThat(wakelockMap.get(tag)).isEqualTo(flags); // Verify wakelock is active.
// Confirm that the wakelocks have been disabled when the forceSuspend is in flight.
@@ -725,7 +739,7 @@ public class PowerManagerServiceTest {
// Take a nap and verify we no longer hold the blocker
int flags = PowerManager.DOZE_WAKE_LOCK;
mService.getBinderServiceInstance().acquireWakeLock(token, flags, tag, packageName,
- null /* workSource */, null /* historyTag */, Display.INVALID_DISPLAY);
+ null /* workSource */, null /* historyTag */, Display.INVALID_DISPLAY, null);
when(mDreamManagerInternalMock.isDreaming()).thenReturn(true);
mService.getBinderServiceInstance().goToSleep(mClock.now(),
PowerManager.GO_TO_SLEEP_REASON_APPLICATION, 0);
@@ -881,7 +895,7 @@ public class PowerManagerServiceTest {
mService.getBinderServiceInstance().acquireWakeLock(token,
PowerManager.SCREEN_BRIGHT_WAKE_LOCK, tag, pkg,
- null /* workSource */, null /* historyTag */, Display.INVALID_DISPLAY);
+ null /* workSource */, null /* historyTag */, Display.INVALID_DISPLAY, null);
assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE);
advanceTime(60);
@@ -907,7 +921,7 @@ public class PowerManagerServiceTest {
mService.getBinderServiceInstance().acquireWakeLock(token,
PowerManager.SCREEN_BRIGHT_WAKE_LOCK | PowerManager.ON_AFTER_RELEASE, tag, pkg,
- null /* workSource */, null /* historyTag */, Display.DEFAULT_DISPLAY);
+ null /* workSource */, null /* historyTag */, Display.DEFAULT_DISPLAY, null);
advanceTime(1500);
mService.getBinderServiceInstance().releaseWakeLock(token, 0 /* flags */);
@@ -983,7 +997,7 @@ public class PowerManagerServiceTest {
mService.getBinderServiceInstance().acquireWakeLock(token,
PowerManager.SCREEN_BRIGHT_WAKE_LOCK, tag, pkg,
- null /* workSource */, null /* historyTag */, Display.DEFAULT_DISPLAY);
+ null /* workSource */, null /* historyTag */, Display.DEFAULT_DISPLAY, null);
assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE);
assertThat(mService.getWakefulnessLocked(Display.DEFAULT_DISPLAY_GROUP)).isEqualTo(
@@ -1023,7 +1037,7 @@ public class PowerManagerServiceTest {
mService.getBinderServiceInstance().acquireWakeLock(token,
PowerManager.SCREEN_BRIGHT_WAKE_LOCK, tag, pkg,
- null /* workSource */, null /* historyTag */, Display.INVALID_DISPLAY);
+ null /* workSource */, null /* historyTag */, Display.INVALID_DISPLAY, null);
assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE);
assertThat(mService.getWakefulnessLocked(Display.DEFAULT_DISPLAY_GROUP)).isEqualTo(
@@ -1064,7 +1078,7 @@ public class PowerManagerServiceTest {
mService.getBinderServiceInstance().acquireWakeLock(token,
PowerManager.SCREEN_BRIGHT_WAKE_LOCK, tag, pkg,
- null /* workSource */, null /* historyTag */, nonDefaultDisplay);
+ null /* workSource */, null /* historyTag */, nonDefaultDisplay, null);
assertThat(mService.getWakefulnessLocked(Display.DEFAULT_DISPLAY_GROUP)).isEqualTo(
WAKEFULNESS_AWAKE);
@@ -1575,4 +1589,118 @@ public class PowerManagerServiceTest {
.setFullPowerSavePolicy(mockSetPolicyConfig)).isTrue();
verify(mBatterySaverStateMachineMock).setFullBatterySaverPolicy(eq(mockSetPolicyConfig));
}
+
+ @Test
+ public void testLowPowerStandby_whenInactive_FgsWakeLockEnabled() {
+ createService();
+ mService.systemReady();
+ WakeLock wakeLock = acquireWakeLock("fgsWakeLock", PowerManager.PARTIAL_WAKE_LOCK);
+ mService.updateUidProcStateInternal(wakeLock.mOwnerUid, PROCESS_STATE_FOREGROUND_SERVICE);
+ mService.setDeviceIdleModeInternal(true);
+
+ assertThat(wakeLock.mDisabled).isFalse();
+ }
+
+ @Test
+ public void testLowPowerStandby_whenActive_FgsWakeLockDisabled() {
+ createService();
+ mService.systemReady();
+ WakeLock wakeLock = acquireWakeLock("fgsWakeLock", PowerManager.PARTIAL_WAKE_LOCK);
+ mService.updateUidProcStateInternal(wakeLock.mOwnerUid, PROCESS_STATE_FOREGROUND_SERVICE);
+ mService.setDeviceIdleModeInternal(true);
+ mService.setLowPowerStandbyActiveInternal(true);
+
+ assertThat(wakeLock.mDisabled).isTrue();
+ }
+
+ @Test
+ public void testLowPowerStandby_whenActive_FgsWakeLockEnabledIfAllowlisted() {
+ createService();
+ mService.systemReady();
+ WakeLock wakeLock = acquireWakeLock("fgsWakeLock", PowerManager.PARTIAL_WAKE_LOCK);
+ mService.updateUidProcStateInternal(wakeLock.mOwnerUid, PROCESS_STATE_FOREGROUND_SERVICE);
+ mService.setDeviceIdleModeInternal(true);
+ mService.setLowPowerStandbyActiveInternal(true);
+ mService.setLowPowerStandbyAllowlistInternal(new int[]{wakeLock.mOwnerUid});
+
+ assertThat(wakeLock.mDisabled).isFalse();
+ }
+
+ @Test
+ public void testLowPowerStandby_whenActive_BoundTopWakeLockDisabled() {
+ createService();
+ mService.systemReady();
+ WakeLock wakeLock = acquireWakeLock("BoundTopWakeLock", PowerManager.PARTIAL_WAKE_LOCK);
+ mService.updateUidProcStateInternal(wakeLock.mOwnerUid, PROCESS_STATE_BOUND_TOP);
+ mService.setDeviceIdleModeInternal(true);
+ mService.setLowPowerStandbyActiveInternal(true);
+
+ assertThat(wakeLock.mDisabled).isFalse();
+ }
+
+ private WakeLock acquireWakeLock(String tag, int flags) {
+ IBinder token = new Binder();
+ String packageName = "pkg.name";
+ mService.getBinderServiceInstance().acquireWakeLock(token, flags, tag, packageName,
+ null /* workSource */, null /* historyTag */, Display.INVALID_DISPLAY,
+ null /* callback */);
+ return mService.findWakeLockLocked(token);
+ }
+
+ /**
+ * Test IPowerManager.acquireWakeLock() with a IWakeLockCallback.
+ */
+ @Test
+ public void testNotifyWakeLockCallback() {
+ createService();
+ startSystem();
+ final String tag = "wakelock1";
+ final String packageName = "pkg.name";
+ final IBinder token = new Binder();
+ final int flags = PowerManager.PARTIAL_WAKE_LOCK;
+ final IWakeLockCallback callback = Mockito.mock(IWakeLockCallback.class);
+ final IBinder callbackBinder = Mockito.mock(Binder.class);
+ when(callback.asBinder()).thenReturn(callbackBinder);
+ mService.getBinderServiceInstance().acquireWakeLock(token, flags, tag, packageName,
+ null /* workSource */, null /* historyTag */, Display.INVALID_DISPLAY, callback);
+ verify(mNotifierMock).onWakeLockAcquired(anyInt(), eq(tag), eq(packageName), anyInt(),
+ anyInt(), any(), any(), same(callback));
+
+ mService.getBinderServiceInstance().releaseWakeLock(token, 0);
+ verify(mNotifierMock).onWakeLockReleased(anyInt(), eq(tag), eq(packageName), anyInt(),
+ anyInt(), any(), any(), same(callback));
+ }
+
+ /**
+ * Test IPowerManager.updateWakeLockCallback() with a new IWakeLockCallback.
+ */
+ @Test
+ public void testNotifyWakeLockCallbackChange() {
+ createService();
+ startSystem();
+ final String tag = "wakelock1";
+ final String packageName = "pkg.name";
+ final IBinder token = new Binder();
+ int flags = PowerManager.PARTIAL_WAKE_LOCK;
+ final IWakeLockCallback callback1 = Mockito.mock(IWakeLockCallback.class);
+ final IBinder callbackBinder1 = Mockito.mock(Binder.class);
+ when(callback1.asBinder()).thenReturn(callbackBinder1);
+ mService.getBinderServiceInstance().acquireWakeLock(token, flags, tag, packageName,
+ null /* workSource */, null /* historyTag */, Display.INVALID_DISPLAY, callback1);
+ verify(mNotifierMock).onWakeLockAcquired(anyInt(), eq(tag), eq(packageName), anyInt(),
+ anyInt(), any(), any(), same(callback1));
+
+ final IWakeLockCallback callback2 = Mockito.mock(IWakeLockCallback.class);
+ final IBinder callbackBinder2 = Mockito.mock(Binder.class);
+ when(callback2.asBinder()).thenReturn(callbackBinder2);
+ mService.getBinderServiceInstance().updateWakeLockCallback(token, callback2);
+ verify(mNotifierMock).onWakeLockChanging(anyInt(), eq(tag), eq(packageName),
+ anyInt(), anyInt(), any(), any(), same(callback1),
+ anyInt(), eq(tag), eq(packageName), anyInt(), anyInt(), any(), any(),
+ same(callback2));
+
+ mService.getBinderServiceInstance().releaseWakeLock(token, 0);
+ verify(mNotifierMock).onWakeLockReleased(anyInt(), eq(tag), eq(packageName), anyInt(),
+ anyInt(), any(), any(), same(callback2));
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/power/WakeLockLogTest.java b/services/tests/servicestests/src/com/android/server/power/WakeLockLogTest.java
index 09612e33b98a..a73fcb89da27 100644
--- a/services/tests/servicestests/src/com/android/server/power/WakeLockLogTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/WakeLockLogTest.java
@@ -211,6 +211,25 @@ public class WakeLockLogTest {
dumpLog(log, false));
}
+ @Test
+ public void testAddSystemWakelock() {
+ final int tagDatabaseSize = 6;
+ final int logSize = 10;
+ TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
+ WakeLockLog log = new WakeLockLog(injectorSpy);
+
+ when(injectorSpy.currentTimeMillis()).thenReturn(1000L);
+ log.onWakeLockAcquired("TagPartial", 101,
+ PowerManager.PARTIAL_WAKE_LOCK | PowerManager.SYSTEM_WAKELOCK);
+
+ assertEquals("Wake Lock Log\n"
+ + " 01-01 00:00:01.000 - 101 - ACQ TagPartial (partial,system-wakelock)\n"
+ + " -\n"
+ + " Events: 1, Time-Resets: 0\n"
+ + " Buffer, Bytes used: 3\n",
+ dumpLog(log, false));
+ }
+
private String dumpLog(WakeLockLog log, boolean includeTagDb) {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
diff --git a/services/tests/servicestests/utils/com/android/server/testutils/TestHandler.java b/services/tests/servicestests/utils/com/android/server/testutils/TestHandler.java
index a74615d4f175..22d383a84177 100644
--- a/services/tests/servicestests/utils/com/android/server/testutils/TestHandler.java
+++ b/services/tests/servicestests/utils/com/android/server/testutils/TestHandler.java
@@ -107,6 +107,13 @@ public class TestHandler extends Handler {
}
}
+ /**
+ * Deletes all messages in queue.
+ */
+ public void clear() {
+ mMessages.clear();
+ }
+
public PriorityQueue<MsgInfo> getPendingMessages() {
return new PriorityQueue<>(mMessages);
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationComparatorTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationComparatorTest.java
index 5eed30be9279..91d4f8f63f38 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationComparatorTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationComparatorTest.java
@@ -67,6 +67,7 @@ public class NotificationComparatorTest extends UiServiceTestCase {
@Mock Vibrator mVibrator;
private final String callPkg = "com.android.server.notification";
+ private final String sysPkg = "android";
private final int callUid = 10;
private String smsPkg;
private final int smsUid = 11;
@@ -79,6 +80,7 @@ public class NotificationComparatorTest extends UiServiceTestCase {
private NotificationRecord mRecordHighCall;
private NotificationRecord mRecordHighCallStyle;
private NotificationRecord mRecordEmail;
+ private NotificationRecord mRecordSystemMax;
private NotificationRecord mRecordInlineReply;
private NotificationRecord mRecordSms;
private NotificationRecord mRecordStarredContact;
@@ -191,6 +193,12 @@ public class NotificationComparatorTest extends UiServiceTestCase {
mRecordContact.setContactAffinity(ValidateNotificationPeople.VALID_CONTACT);
mRecordContact.setSystemImportance(NotificationManager.IMPORTANCE_DEFAULT);
+ Notification nSystemMax = new Notification.Builder(mContext, TEST_CHANNEL_ID).build();
+ mRecordSystemMax = new NotificationRecord(mContext, new StatusBarNotification(sysPkg,
+ sysPkg, 1, "systemmax", uid2, uid2, nSystemMax, new UserHandle(userId),
+ "", 1244), getDefaultChannel());
+ mRecordSystemMax.setSystemImportance(NotificationManager.IMPORTANCE_HIGH);
+
Notification n8 = new Notification.Builder(mContext, TEST_CHANNEL_ID).build();
mRecordUrgent = new NotificationRecord(mContext, new StatusBarNotification(pkg2,
pkg2, 1, "urgent", uid2, uid2, n8, new UserHandle(userId),
@@ -267,6 +275,7 @@ public class NotificationComparatorTest extends UiServiceTestCase {
}
expected.add(mRecordStarredContact);
expected.add(mRecordContact);
+ expected.add(mRecordSystemMax);
expected.add(mRecordEmail);
expected.add(mRecordUrgent);
expected.add(mNoMediaSessionMedia);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java
index fa294dd61ea3..3b6718207c83 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java
@@ -20,8 +20,8 @@ import static android.content.pm.PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED;
import static android.content.pm.PackageManager.FLAG_PERMISSION_SYSTEM_FIXED;
import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET;
import static android.content.pm.PackageManager.GET_PERMISSIONS;
-import static android.permission.PermissionManager.PERMISSION_GRANTED;
-import static android.permission.PermissionManager.PERMISSION_SOFT_DENIED;
+import static android.content.pm.PackageManager.PERMISSION_DENIED;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static com.google.common.truth.Truth.assertThat;
@@ -130,13 +130,13 @@ public class PermissionHelperTest extends UiServiceTestCase {
@Test
public void testHasPermission() throws Exception {
- when(mPmi.checkUidPermission(anyInt(), eq(Manifest.permission.POST_NOTIFICATIONS)))
+ when(mPmi.checkPostNotificationsPermissionGrantedOrLegacyAccess(anyInt()))
.thenReturn(PERMISSION_GRANTED);
assertThat(mPermissionHelper.hasPermission(1)).isTrue();
- when(mPmi.checkUidPermission(anyInt(), eq(Manifest.permission.POST_NOTIFICATIONS)))
- .thenReturn(PERMISSION_SOFT_DENIED);
+ when(mPmi.checkPostNotificationsPermissionGrantedOrLegacyAccess(anyInt()))
+ .thenReturn(PERMISSION_DENIED);
assertThat(mPermissionHelper.hasPermission(1)).isFalse();
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeFilteringTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeFilteringTest.java
index fb1508842c9d..0f18cc61a95a 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeFilteringTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeFilteringTest.java
@@ -17,7 +17,6 @@
package com.android.server.notification;
import static android.app.Notification.CATEGORY_CALL;
-import static android.app.Notification.CATEGORY_MESSAGE;
import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
import static android.app.NotificationManager.Policy.CONVERSATION_SENDERS_ANYONE;
import static android.app.NotificationManager.Policy.CONVERSATION_SENDERS_IMPORTANT;
@@ -25,6 +24,7 @@ import static android.app.NotificationManager.Policy.CONVERSATION_SENDERS_NONE;
import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_CALLS;
import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_CONVERSATIONS;
import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_MESSAGES;
+import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_REPEAT_CALLERS;
import static android.app.NotificationManager.Policy.PRIORITY_SENDERS_ANY;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_STATUS_BAR;
import static android.provider.Settings.Global.ZEN_MODE_ALARMS;
@@ -43,8 +43,10 @@ import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager.Policy;
import android.media.AudioAttributes;
+import android.os.Bundle;
import android.os.UserHandle;
import android.service.notification.StatusBarNotification;
+import android.telephony.TelephonyManager;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -68,10 +70,15 @@ public class ZenModeFilteringTest extends UiServiceTestCase {
private NotificationMessagingUtil mMessagingUtil;
private ZenModeFiltering mZenModeFiltering;
+ @Mock private TelephonyManager mTelephonyManager;
+
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mZenModeFiltering = new ZenModeFiltering(mContext, mMessagingUtil);
+
+ // for repeat callers / matchesCallFilter
+ mContext.addMockSystemService(TelephonyManager.class, mTelephonyManager);
}
private NotificationRecord getNotificationRecord() {
@@ -95,6 +102,23 @@ public class ZenModeFilteringTest extends UiServiceTestCase {
return r;
}
+ private Bundle makeExtrasBundleWithPeople(String[] people) {
+ Bundle extras = new Bundle();
+ extras.putObject(Notification.EXTRA_PEOPLE_LIST, people);
+ return extras;
+ }
+
+ private NotificationRecord getNotificationRecordWithPeople(String[] people) {
+ // set up notification record
+ NotificationRecord r = mock(NotificationRecord.class);
+ StatusBarNotification sbn = mock(StatusBarNotification.class);
+ Notification notification = mock(Notification.class);
+ notification.extras = makeExtrasBundleWithPeople(people);
+ when(sbn.getNotification()).thenReturn(notification);
+ when(r.getSbn()).thenReturn(sbn);
+ return r;
+ }
+
@Test
public void testIsMessage() {
NotificationRecord r = getNotificationRecord();
@@ -309,4 +333,111 @@ public class ZenModeFilteringTest extends UiServiceTestCase {
assertFalse(mZenModeFiltering.shouldIntercept(ZEN_MODE_IMPORTANT_INTERRUPTIONS, policy, r));
}
+
+ @Test
+ public void testMatchesCallFilter_repeatCallers_directMatch() {
+ // after calls given an email with an exact string match, make sure that
+ // matchesCallFilter returns the right thing
+ String[] mailSource = new String[]{"mailto:hello.world"};
+ mZenModeFiltering.recordCall(getNotificationRecordWithPeople(mailSource));
+
+ // set up policy to only allow repeat callers
+ Policy policy = new Policy(
+ PRIORITY_CATEGORY_REPEAT_CALLERS, 0, 0, 0, CONVERSATION_SENDERS_NONE);
+
+ // check whether matchesCallFilter returns the right thing
+ Bundle inputMatches = makeExtrasBundleWithPeople(new String[]{"mailto:hello.world"});
+ Bundle inputWrong = makeExtrasBundleWithPeople(new String[]{"mailto:nope"});
+ assertTrue(ZenModeFiltering.matchesCallFilter(mContext, ZEN_MODE_IMPORTANT_INTERRUPTIONS,
+ policy, UserHandle.SYSTEM,
+ inputMatches, null, 0, 0));
+ assertFalse(ZenModeFiltering.matchesCallFilter(mContext, ZEN_MODE_IMPORTANT_INTERRUPTIONS,
+ policy, UserHandle.SYSTEM,
+ inputWrong, null, 0, 0));
+ }
+
+ @Test
+ public void testMatchesCallFilter_repeatCallers_telephoneVariants() {
+ // set up telephony manager behavior
+ when(mTelephonyManager.getNetworkCountryIso()).thenReturn("us");
+
+ String[] telSource = new String[]{"tel:+1-617-555-1212"};
+ mZenModeFiltering.recordCall(getNotificationRecordWithPeople(telSource));
+
+ // set up policy to only allow repeat callers
+ Policy policy = new Policy(
+ PRIORITY_CATEGORY_REPEAT_CALLERS, 0, 0, 0, CONVERSATION_SENDERS_NONE);
+
+ // cases to test:
+ // - identical number
+ // - same number, different formatting
+ // - different number
+ // - garbage
+ Bundle identical = makeExtrasBundleWithPeople(new String[]{"tel:+1-617-555-1212"});
+ Bundle same = makeExtrasBundleWithPeople(new String[]{"tel:16175551212"});
+ Bundle different = makeExtrasBundleWithPeople(new String[]{"tel:123-456-7890"});
+ Bundle garbage = makeExtrasBundleWithPeople(new String[]{"asdfghjkl;"});
+
+ assertTrue("identical numbers should match",
+ ZenModeFiltering.matchesCallFilter(mContext, ZEN_MODE_IMPORTANT_INTERRUPTIONS,
+ policy, UserHandle.SYSTEM,
+ identical, null, 0, 0));
+ assertTrue("equivalent but non-identical numbers should match",
+ ZenModeFiltering.matchesCallFilter(mContext, ZEN_MODE_IMPORTANT_INTERRUPTIONS,
+ policy, UserHandle.SYSTEM,
+ same, null, 0, 0));
+ assertFalse("non-equivalent numbers should not match",
+ ZenModeFiltering.matchesCallFilter(mContext, ZEN_MODE_IMPORTANT_INTERRUPTIONS,
+ policy, UserHandle.SYSTEM,
+ different, null, 0, 0));
+ assertFalse("non-tel strings should not match",
+ ZenModeFiltering.matchesCallFilter(mContext, ZEN_MODE_IMPORTANT_INTERRUPTIONS,
+ policy, UserHandle.SYSTEM,
+ garbage, null, 0, 0));
+ }
+
+ @Test
+ public void testMatchesCallFilter_repeatCallers_urlEncodedTels() {
+ // this is not intended to be a supported case but is one that we have seen
+ // sometimes in the wild, so make sure we handle url-encoded telephone numbers correctly
+ // when somebody provides one.
+
+ // set up telephony manager behavior
+ when(mTelephonyManager.getNetworkCountryIso()).thenReturn("us");
+
+ String[] telSource = new String[]{"tel:%2B16175551212"};
+ mZenModeFiltering.recordCall(getNotificationRecordWithPeople(telSource));
+
+ // set up policy to only allow repeat callers
+ Policy policy = new Policy(
+ PRIORITY_CATEGORY_REPEAT_CALLERS, 0, 0, 0, CONVERSATION_SENDERS_NONE);
+
+ // test cases for various forms of the same phone number and different ones
+ Bundle same1 = makeExtrasBundleWithPeople(new String[]{"tel:+1-617-555-1212"});
+ Bundle same2 = makeExtrasBundleWithPeople(new String[]{"tel:%2B1-617-555-1212"});
+ Bundle same3 = makeExtrasBundleWithPeople(new String[]{"tel:6175551212"});
+ Bundle different1 = makeExtrasBundleWithPeople(new String[]{"tel:%2B16175553434"});
+ Bundle different2 = makeExtrasBundleWithPeople(new String[]{"tel:+16175553434"});
+
+ assertTrue("same number should match",
+ ZenModeFiltering.matchesCallFilter(mContext, ZEN_MODE_IMPORTANT_INTERRUPTIONS,
+ policy, UserHandle.SYSTEM,
+ same1, null, 0, 0));
+ assertTrue("same number should match",
+ ZenModeFiltering.matchesCallFilter(mContext, ZEN_MODE_IMPORTANT_INTERRUPTIONS,
+ policy, UserHandle.SYSTEM,
+ same2, null, 0, 0));
+ assertTrue("same number should match",
+ ZenModeFiltering.matchesCallFilter(mContext, ZEN_MODE_IMPORTANT_INTERRUPTIONS,
+ policy, UserHandle.SYSTEM,
+ same3, null, 0, 0));
+ assertFalse("different number should not match",
+ ZenModeFiltering.matchesCallFilter(mContext, ZEN_MODE_IMPORTANT_INTERRUPTIONS,
+ policy, UserHandle.SYSTEM,
+ different1, null, 0, 0));
+ assertFalse("different number should not match",
+ ZenModeFiltering.matchesCallFilter(mContext, ZEN_MODE_IMPORTANT_INTERRUPTIONS,
+ policy, UserHandle.SYSTEM,
+ different2, null, 0, 0));
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index 30ad1f93bdf3..3298d1184cdc 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -85,7 +85,6 @@ import static com.android.server.wm.ActivityRecord.State.STARTED;
import static com.android.server.wm.ActivityRecord.State.STOPPED;
import static com.android.server.wm.ActivityRecord.State.STOPPING;
import static com.android.server.wm.ActivityTaskManagerService.INSTRUMENTATION_KEY_DISPATCHING_TIMEOUT_MILLIS;
-import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
import static com.android.server.wm.TaskFragment.TASK_FRAGMENT_VISIBILITY_INVISIBLE;
import static com.android.server.wm.TaskFragment.TASK_FRAGMENT_VISIBILITY_VISIBLE;
import static com.android.server.wm.TaskFragment.TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT;
@@ -3114,7 +3113,7 @@ public class ActivityRecordTests extends WindowTestsBase {
}
@Test
- public void testInClosingAnimation_doNotHideSurface() {
+ public void testInClosingAnimation_visibilityNotCommitted_doNotHideSurface() {
final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
makeWindowVisibleAndDrawn(app);
@@ -3123,16 +3122,45 @@ public class ActivityRecordTests extends WindowTestsBase {
mDisplayContent.mClosingApps.add(app.mActivityRecord);
mDisplayContent.prepareAppTransition(TRANSIT_CLOSE);
- // Update visibility and call to remove window
- app.mActivityRecord.commitVisibility(false, false);
+ // Remove window during transition, so it is requested to hide, but won't be committed until
+ // the transition is finished.
+ app.mActivityRecord.onRemovedFromDisplay();
+
+ assertTrue(mDisplayContent.mClosingApps.contains(app.mActivityRecord));
+ assertFalse(app.mActivityRecord.isVisibleRequested());
+ assertTrue(app.mActivityRecord.isVisible());
+ assertTrue(app.mActivityRecord.isSurfaceShowing());
+
+ // Start transition.
app.mActivityRecord.prepareSurfaces();
// Because the app is waiting for transition, it should not hide the surface.
assertTrue(app.mActivityRecord.isSurfaceShowing());
+ }
+
+ @Test
+ public void testInClosingAnimation_visibilityCommitted_hideSurface() {
+ final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
+ makeWindowVisibleAndDrawn(app);
+
+ // Put the activity in close transition.
+ mDisplayContent.mOpeningApps.clear();
+ mDisplayContent.mClosingApps.add(app.mActivityRecord);
+ mDisplayContent.prepareAppTransition(TRANSIT_CLOSE);
+
+ // Commit visibility before start transition.
+ app.mActivityRecord.commitVisibility(false, false);
+
+ assertFalse(app.mActivityRecord.isVisibleRequested());
+ assertFalse(app.mActivityRecord.isVisible());
+ assertTrue(app.mActivityRecord.isSurfaceShowing());
+
+ // Start transition.
+ app.mActivityRecord.prepareSurfaces();
- // Ensure onAnimationFinished will callback when the closing animation is finished.
- verify(app.mActivityRecord).onAnimationFinished(eq(ANIMATION_TYPE_APP_TRANSITION),
- eq(null));
+ // Because the app visibility has been committed before the transition start, it should hide
+ // the surface.
+ assertFalse(app.mActivityRecord.isSurfaceShowing());
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
index 7c340ecac2c7..8ada97147dd3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
@@ -191,7 +191,8 @@ public class ActivityTaskManagerServiceTests extends WindowTestsBase {
public void onFixedRotationFinished(int displayId) {}
@Override
- public void onKeepClearAreasChanged(int displayId, List<Rect> keepClearAreas) {}
+ public void onKeepClearAreasChanged(int displayId, List<Rect> restricted,
+ List<Rect> unrestricted) {}
};
int[] displayIds = mAtm.mWindowManager.registerDisplayWindowListener(listener);
for (int i = 0; i < displayIds.length; i++) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index ea032505f657..efc9a49023b1 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -106,7 +106,6 @@ import static org.mockito.Mockito.when;
import android.app.ActivityTaskManager;
import android.app.WindowConfiguration;
-import android.app.servertransaction.FixedRotationAdjustmentsItem;
import android.content.res.Configuration;
import android.graphics.Insets;
import android.graphics.Point;
@@ -1666,34 +1665,6 @@ public class DisplayContentTests extends WindowTestsBase {
}
@Test
- public void testClearIntermediateFixedRotationAdjustments() throws RemoteException {
- final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build();
- mDisplayContent.setFixedRotationLaunchingApp(activity,
- (mDisplayContent.getRotation() + 1) % 4);
- // Create a window so FixedRotationAdjustmentsItem can be sent.
- createWindow(null, TYPE_APPLICATION_STARTING, activity, "AppWin");
- final ActivityRecord activity2 = new ActivityBuilder(mAtm).setCreateTask(true).build();
- activity2.setVisible(false);
- clearInvocations(mAtm.getLifecycleManager());
- // The first activity has applied fixed rotation but the second activity becomes the top
- // before the transition is done and it has the same rotation as display, so the dispatched
- // rotation adjustment of first activity must be cleared.
- mDisplayContent.handleTopActivityLaunchingInDifferentOrientation(activity2,
- false /* checkOpening */);
-
- final ArgumentCaptor<FixedRotationAdjustmentsItem> adjustmentsCaptor =
- ArgumentCaptor.forClass(FixedRotationAdjustmentsItem.class);
- verify(mAtm.getLifecycleManager(), atLeastOnce()).scheduleTransaction(
- eq(activity.app.getThread()), adjustmentsCaptor.capture());
- // The transformation is kept for animation in real case.
- assertTrue(activity.hasFixedRotationTransform());
- final FixedRotationAdjustmentsItem clearAdjustments = FixedRotationAdjustmentsItem.obtain(
- activity.token, null /* fixedRotationAdjustments */);
- // The captor may match other items. The first one must be the item to clear adjustments.
- assertEquals(clearAdjustments, adjustmentsCaptor.getAllValues().get(0));
- }
-
- @Test
public void testRemoteRotation() {
DisplayContent dc = createNewDisplay();
diff --git a/services/usage/java/com/android/server/usage/BroadcastResponseStatsTracker.java b/services/usage/java/com/android/server/usage/BroadcastResponseStatsTracker.java
index aa01f3152bb4..27e8d69aa762 100644
--- a/services/usage/java/com/android/server/usage/BroadcastResponseStatsTracker.java
+++ b/services/usage/java/com/android/server/usage/BroadcastResponseStatsTracker.java
@@ -16,16 +16,23 @@
package com.android.server.usage;
+import static android.app.ActivityManager.procStateToString;
+
+import static com.android.server.usage.UsageStatsService.DEBUG_RESPONSE_STATS;
+
import android.annotation.ElapsedRealtimeLong;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
+import android.app.ActivityManager.ProcessState;
import android.app.usage.BroadcastResponseStats;
import android.os.UserHandle;
+import android.text.TextUtils;
import android.util.LongSparseArray;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.TimeUtils;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.IndentingPrintWriter;
@@ -65,10 +72,27 @@ class BroadcastResponseStatsTracker {
private SparseArray<SparseArray<UserBroadcastResponseStats>> mUserResponseStats =
new SparseArray<>();
+ private AppStandbyInternal mAppStandby;
+
+ BroadcastResponseStatsTracker(@NonNull AppStandbyInternal appStandby) {
+ mAppStandby = appStandby;
+ }
+
// TODO (206518114): Move all callbacks handling to a handler thread.
void reportBroadcastDispatchEvent(int sourceUid, @NonNull String targetPackage,
UserHandle targetUser, long idForResponseEvent,
- @ElapsedRealtimeLong long timestampMs) {
+ @ElapsedRealtimeLong long timestampMs, @ProcessState int targetUidProcState) {
+ if (DEBUG_RESPONSE_STATS) {
+ Slog.d(TAG, TextUtils.formatSimple("reportBroadcastDispatchEvent; "
+ + "srcUid=%d, tgtPkg=%s, tgtUsr=%d, id=%d, ts=%s, state=%s",
+ sourceUid, targetPackage, targetUser, idForResponseEvent,
+ TimeUtils.formatDuration(timestampMs), procStateToString(targetUidProcState)));
+ }
+ if (targetUidProcState <= mAppStandby.getBroadcastResponseFgThresholdState()) {
+ // No need to track the broadcast response state while the target app is
+ // in the foreground.
+ return;
+ }
synchronized (mLock) {
final LongSparseArray<BroadcastEvent> broadcastEvents =
getOrCreateBroadcastEventsLocked(targetPackage, targetUser);
@@ -99,6 +123,12 @@ class BroadcastResponseStatsTracker {
private void reportNotificationEvent(@NotificationEvent int event,
@NonNull String packageName, UserHandle user, @ElapsedRealtimeLong long timestampMs) {
+ if (DEBUG_RESPONSE_STATS) {
+ Slog.d(TAG, TextUtils.formatSimple(
+ "reportNotificationEvent; event=<%s>, pkg=%s, usr=%d, ts=%s",
+ notificationEventToString(event), packageName, user.getIdentifier(),
+ TimeUtils.formatDuration(timestampMs)));
+ }
// TODO (206518114): Store last N events to dump for debugging purposes.
synchronized (mLock) {
final LongSparseArray<BroadcastEvent> broadcastEvents =
@@ -116,8 +146,7 @@ class BroadcastResponseStatsTracker {
if (dispatchTimestampMs >= timestampMs) {
continue;
}
- // TODO (206518114): Make the constant configurable.
- if (elapsedDurationMs <= 2 * 60 * 1000) {
+ if (elapsedDurationMs <= mAppStandby.getBroadcastResponseWindowDurationMs()) {
final BroadcastEvent broadcastEvent = broadcastEvents.valueAt(i);
final BroadcastResponseStats responseStats =
getBroadcastResponseStats(broadcastEvent);
@@ -288,6 +317,20 @@ class BroadcastResponseStatsTracker {
return userResponseStats.getOrCreateBroadcastResponseStats(broadcastEvent);
}
+ @NonNull
+ private String notificationEventToString(@NotificationEvent int event) {
+ switch (event) {
+ case NOTIFICATION_EVENT_POSTED:
+ return "posted";
+ case NOTIFICATION_EVENT_UPDATED:
+ return "updated";
+ case NOTIFICATION_EVENT_CANCELLED:
+ return "cancelled";
+ default:
+ return String.valueOf(event);
+ }
+ }
+
void dump(@NonNull IndentingPrintWriter ipw) {
ipw.println("Broadcast response stats:");
ipw.increaseIndent();
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index e28839efa4bc..98a41bcf5adf 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -42,6 +42,7 @@ import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
+import android.app.ActivityManager.ProcessState;
import android.app.AppOpsManager;
import android.app.IUidObserver;
import android.app.PendingIntent;
@@ -89,6 +90,7 @@ import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AtomicFile;
+import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseIntArray;
@@ -141,6 +143,7 @@ public class UsageStatsService extends SystemService implements
= SystemProperties.getBoolean("persist.debug.time_correction", true);
static final boolean DEBUG = false; // Never submit with true
+ static final boolean DEBUG_RESPONSE_STATS = DEBUG || Log.isLoggable(TAG, Log.DEBUG);
static final boolean COMPRESS_TIME = false;
private static final long TEN_SECONDS = 10 * 1000;
@@ -279,7 +282,7 @@ public class UsageStatsService extends SystemService implements
mHandler = new H(BackgroundThread.get().getLooper());
mAppStandby = mInjector.getAppStandbyController(getContext());
- mResponseStatsTracker = new BroadcastResponseStatsTracker();
+ mResponseStatsTracker = new BroadcastResponseStatsTracker(mAppStandby);
mAppTimeLimit = new AppTimeLimitController(getContext(),
new AppTimeLimitController.TimeLimitCallbackListener() {
@@ -3040,9 +3043,9 @@ public class UsageStatsService extends SystemService implements
@Override
public void reportBroadcastDispatched(int sourceUid, @NonNull String targetPackage,
@NonNull UserHandle targetUser, long idForResponseEvent,
- @ElapsedRealtimeLong long timestampMs) {
+ @ElapsedRealtimeLong long timestampMs, @ProcessState int targetUidProcState) {
mResponseStatsTracker.reportBroadcastDispatchEvent(sourceUid, targetPackage,
- targetUser, idForResponseEvent, timestampMs);
+ targetUser, idForResponseEvent, timestampMs, targetUidProcState);
}
@Override
diff --git a/services/usb/java/com/android/server/usb/UsbUniversalMidiDevice.java b/services/usb/java/com/android/server/usb/UsbUniversalMidiDevice.java
index db0c80f189d3..13d404caf6a2 100644
--- a/services/usb/java/com/android/server/usb/UsbUniversalMidiDevice.java
+++ b/services/usb/java/com/android/server/usb/UsbUniversalMidiDevice.java
@@ -218,14 +218,13 @@ public final class UsbUniversalMidiDevice implements Closeable {
if (doesInterfaceContainInput
&& doesInterfaceContainOutput) {
UsbDeviceConnection connection = manager.openDevice(mUsbDevice);
- if (!connection.claimInterface(interfaceDescriptor.toAndroid(mParser), true)) {
- Log.d(TAG, "Can't claim control interface");
- continue;
- }
+
+ // The ALSA does not handle switching to the MIDI 2.0 interface correctly
+ // and stops exposing /dev/snd/midiC1D0 after calling connection.setInterface().
+ // Thus, simply use the control interface (interface zero).
int defaultMidiProtocol = mMidiBlockParser.calculateMidiType(connection,
- interfaceDescriptor.getInterfaceNumber(),
+ 0,
interfaceDescriptor.getAlternateSetting());
-
connection.close();
return defaultMidiProtocol;
}
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
index 90ccec852e1e..a061618b1ca7 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
@@ -69,6 +69,7 @@ import com.android.server.FgThread;
import com.android.server.LocalServices;
import com.android.server.am.AssistDataRequester;
import com.android.server.am.AssistDataRequester.AssistDataRequesterCallbacks;
+import com.android.server.power.LowPowerStandbyControllerInternal;
import com.android.server.statusbar.StatusBarManagerInternal;
import com.android.server.uri.UriGrantsManagerInternal;
import com.android.server.wm.ActivityAssistInfo;
@@ -89,6 +90,11 @@ final class VoiceInteractionSessionConnection implements ServiceConnection,
static final int POWER_BOOST_TIMEOUT_MS = Integer.parseInt(
System.getProperty("vendor.powerhal.interaction.max", "200"));
static final int BOOST_TIMEOUT_MS = 300;
+ /**
+ * The maximum time an app can stay on the Low Power Standby allowlist when
+ * the session is shown. There to safeguard against apps that don't call hide.
+ */
+ private static final int LOW_POWER_STANDBY_ALLOWLIST_TIMEOUT_MS = 120_000;
// TODO: To avoid ap doesn't call hide, only 10 secs for now, need a better way to manage it
// in the future.
static final int MAX_POWER_BOOST_TIMEOUT = 10_000;
@@ -124,6 +130,10 @@ final class VoiceInteractionSessionConnection implements ServiceConnection,
Executors.newSingleThreadScheduledExecutor();
private final List<VisibleActivityInfo> mVisibleActivityInfos = new ArrayList<>();
private final PowerManagerInternal mPowerManagerInternal;
+ private final LowPowerStandbyControllerInternal mLowPowerStandbyControllerInternal;
+ private final Runnable mRemoveFromLowPowerStandbyAllowlistRunnable =
+ this::removeFromLowPowerStandbyAllowlist;
+ private boolean mLowPowerStandbyAllowlisted;
private PowerBoostSetter mSetPowerBoostRunnable;
private final Handler mFgHandler;
@@ -211,6 +221,8 @@ final class VoiceInteractionSessionConnection implements ServiceConnection,
mIWindowManager = IWindowManager.Stub.asInterface(
ServiceManager.getService(Context.WINDOW_SERVICE));
mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
+ mLowPowerStandbyControllerInternal = LocalServices.getService(
+ LowPowerStandbyControllerInternal.class);
mAppOps = context.getSystemService(AppOpsManager.class);
mFgHandler = FgThread.getHandler();
mAssistDataRequester = new AssistDataRequester(mContext, mIWindowManager,
@@ -322,6 +334,15 @@ final class VoiceInteractionSessionConnection implements ServiceConnection,
mSetPowerBoostRunnable = new PowerBoostSetter(
Instant.now().plusMillis(MAX_POWER_BOOST_TIMEOUT));
mFgHandler.post(mSetPowerBoostRunnable);
+
+ if (mLowPowerStandbyControllerInternal != null) {
+ mLowPowerStandbyControllerInternal.addToAllowlist(mCallingUid);
+ mLowPowerStandbyAllowlisted = true;
+ mFgHandler.removeCallbacks(mRemoveFromLowPowerStandbyAllowlistRunnable);
+ mFgHandler.postDelayed(mRemoveFromLowPowerStandbyAllowlistRunnable,
+ LOW_POWER_STANDBY_ALLOWLIST_TIMEOUT_MS);
+ }
+
mCallback.onSessionShown(this);
return true;
}
@@ -493,6 +514,9 @@ final class VoiceInteractionSessionConnection implements ServiceConnection,
}
// A negative value indicates canceling previous boost.
mPowerManagerInternal.setPowerBoost(Boost.INTERACTION, /* durationMs */ -1);
+ if (mLowPowerStandbyControllerInternal != null) {
+ removeFromLowPowerStandbyAllowlist();
+ }
mCallback.onSessionHidden(this);
}
if (mFullyBound) {
@@ -730,6 +754,16 @@ final class VoiceInteractionSessionConnection implements ServiceConnection,
}
}
+ private void removeFromLowPowerStandbyAllowlist() {
+ synchronized (mLock) {
+ if (mLowPowerStandbyAllowlisted) {
+ mFgHandler.removeCallbacks(mRemoveFromLowPowerStandbyAllowlistRunnable);
+ mLowPowerStandbyControllerInternal.removeFromAllowlist(mCallingUid);
+ mLowPowerStandbyAllowlisted = false;
+ }
+ }
+ }
+
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
synchronized (mLock) {
diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java
index c5fc4365df7a..27d423b3bc1e 100755
--- a/telecomm/java/android/telecom/ConnectionService.java
+++ b/telecomm/java/android/telecom/ConnectionService.java
@@ -18,6 +18,7 @@ package android.telecom;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
import android.annotation.SystemApi;
import android.annotation.TestApi;
@@ -3156,15 +3157,27 @@ public abstract class ConnectionService extends Service {
}
/**
- * Create a {@code Connection} for a new unknown call. An unknown call is a call originating
- * from the ConnectionService that was neither a user-initiated outgoing call, nor an incoming
- * call created using
- * {@code TelecomManager#addNewIncomingCall(PhoneAccountHandle, android.os.Bundle)}.
+ * Calls of this type are created using
+ * {@link TelecomManager#addNewUnknownCall(PhoneAccountHandle, Bundle)}. Unknown calls
+ * are used for representing calls which become known to the {@link ConnectionService}
+ * midway through the call.
+ *
+ * For example, a call transferred from one device to answer would surface as an active
+ * call in Telecom instead of going through a typical Ringing to Active transition, or
+ * Dialing to Active transition.
+ *
+ * A {@link ConnectionService} can return {@code null} (the default behavior)
+ * if it is not able to handle a request for the requested unknown connection.
+ *
+ * {@link TelecomManager#addNewIncomingCall(PhoneAccountHandle, android.os.Bundle)}.
*
* @hide
*/
- public Connection onCreateUnknownConnection(PhoneAccountHandle connectionManagerPhoneAccount,
- ConnectionRequest request) {
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ public @Nullable Connection onCreateUnknownConnection(
+ @NonNull PhoneAccountHandle connectionManagerPhoneAccount,
+ @NonNull ConnectionRequest request) {
return null;
}
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 7ba4b11caea3..3c277b7de018 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -4540,9 +4540,7 @@ public class CarrierConfigManager {
* Passing this value as {@link #KEY_SUBSCRIPTION_GROUP_UUID_STRING} will remove the
* subscription from a group instead of adding it to a group.
*
- * TODO: Expose in a future release.
- *
- * @hide
+ * <p>This value will work all the way back to {@link android.os.Build.VERSION_CODES#Q}.
*/
public static final String REMOVE_GROUP_UUID_STRING = "00000000-0000-0000-0000-000000000000";
@@ -4555,9 +4553,7 @@ public class CarrierConfigManager {
* <p>If set to {@link #REMOVE_GROUP_UUID_STRING}, then the subscription will be removed from
* its current group.
*
- * TODO: unhide this key.
- *
- * @hide
+ * <p>This key will work all the way back to {@link android.os.Build.VERSION_CODES#Q}.
*/
public static final String KEY_SUBSCRIPTION_GROUP_UUID_STRING =
"subscription_group_uuid_string";
@@ -4605,17 +4601,15 @@ public class CarrierConfigManager {
"data_switch_validation_min_gap_long";
/**
- * A boolean property indicating whether this subscription should be managed as an opportunistic
- * subscription.
- *
- * If true, then this subscription will be selected based on available coverage and will not be
- * available for a user in settings menus for selecting macro network providers. If unset,
- * defaults to “false”.
- *
- * TODO: unhide this key.
- *
- * @hide
- */
+ * A boolean property indicating whether this subscription should be managed as an opportunistic
+ * subscription.
+ *
+ * If true, then this subscription will be selected based on available coverage and will not be
+ * available for a user in settings menus for selecting macro network providers. If unset,
+ * defaults to “false”.
+ *
+ * <p>This key will work all the way back to {@link android.os.Build.VERSION_CODES#Q}.
+ */
public static final String KEY_IS_OPPORTUNISTIC_SUBSCRIPTION_BOOL =
"is_opportunistic_subscription_bool";
@@ -8898,8 +8892,8 @@ public class CarrierConfigManager {
sDefaults.putStringArray(
KEY_TELEPHONY_DATA_SETUP_RETRY_RULES_STRING_ARRAY, new String[] {
"capabilities=eims, retry_interval=1000, maximum_retries=20",
- "fail_causes=8|27|28|29|30|32|33|35|50|51|111|-5|-6|65537|65538|-3|2253|"
- + "2254, maximum_retries=0", // No retry for those causes
+ "fail_causes=8|27|28|29|30|32|33|35|50|51|111|-5|-6|65537|65538|-3|2252|"
+ + "2253|2254, maximum_retries=0", // No retry for those causes
"capabilities=mms|supl|cbs, retry_interval=2000",
"capabilities=internet|enterprise|dun|ims|fota, retry_interval=2500|3000|"
+ "5000|10000|15000|20000|40000|60000|120000|240000|"
@@ -8920,7 +8914,7 @@ public class CarrierConfigManager {
sDefaults.putBoolean(KEY_STORE_SIM_PIN_FOR_UNATTENDED_REBOOT_BOOL, true);
sDefaults.putBoolean(KEY_HIDE_ENABLE_2G, false);
sDefaults.putStringArray(KEY_ALLOWED_INITIAL_ATTACH_APN_TYPES_STRING_ARRAY,
- new String[]{"ia", "default", "mms", "dun"});
+ new String[]{"ia", "default"});
sDefaults.putBoolean(KEY_CARRIER_PROVISIONS_WIFI_MERGED_NETWORKS_BOOL, false);
sDefaults.putBoolean(KEY_USE_IP_FOR_CALLING_INDICATOR_BOOL, false);
sDefaults.putBoolean(KEY_DISPLAY_CALL_STRENGTH_INDICATOR_BOOL, true);
diff --git a/tests/ApkVerityTest/Android.bp b/tests/ApkVerityTest/Android.bp
index 4e98f4264e11..d4fa1dda8bdf 100644
--- a/tests/ApkVerityTest/Android.bp
+++ b/tests/ApkVerityTest/Android.bp
@@ -24,14 +24,21 @@ package {
java_test_host {
name: "ApkVerityTest",
srcs: ["src/**/*.java"],
- libs: ["tradefed", "compatibility-tradefed", "compatibility-host-util"],
+ libs: [
+ "tradefed",
+ "compatibility-tradefed",
+ "compatibility-host-util",
+ ],
static_libs: [
"block_device_writer_jar",
"frameworks-base-hostutils",
],
- test_suites: ["general-tests", "vts"],
- target_required: [
- "block_device_writer_module",
+ test_suites: [
+ "general-tests",
+ "vts",
+ ],
+ data_device_bins: [
+ "block_device_writer",
],
data: [
":ApkVerityTestCertDer",
diff --git a/tests/ApkVerityTest/block_device_writer/Android.bp b/tests/ApkVerityTest/block_device_writer/Android.bp
index 0b5f0f611916..e5d009dc10fd 100644
--- a/tests/ApkVerityTest/block_device_writer/Android.bp
+++ b/tests/ApkVerityTest/block_device_writer/Android.bp
@@ -24,12 +24,7 @@ package {
}
cc_test {
- // Depending on how the test runs, the executable may be uploaded to different location.
- // Before the bug in the file pusher is fixed, workaround by making the name unique.
- // See b/124718249#comment12.
- name: "block_device_writer_module",
- stem: "block_device_writer",
-
+ name: "block_device_writer",
srcs: ["block_device_writer.cpp"],
cflags: [
"-D_FILE_OFFSET_BITS=64",
@@ -38,31 +33,25 @@ cc_test {
"-Wextra",
"-g",
],
- shared_libs: ["libbase", "libutils"],
- // For some reasons, cuttlefish (x86) uses x86_64 test suites for testing. Unfortunately, when
- // the uploader does not pick up the executable from correct output location. The following
- // workaround allows the test to:
- // * upload the 32-bit exectuable for both 32 and 64 bits devices to use
- // * refer to the same executable name in Java
- // * no need to force the Java test to be archiecture specific.
- //
- // See b/145573317 for details.
- multilib: {
- lib32: {
- suffix: "",
- },
- lib64: {
- suffix: "64", // not really used
- },
- },
+ shared_libs: [
+ "libbase",
+ "libutils",
+ ],
+ compile_multilib: "first",
auto_gen_config: false,
- test_suites: ["general-tests", "pts", "vts"],
+ test_suites: [
+ "general-tests",
+ "vts",
+ ],
gtest: false,
}
java_library_host {
name: "block_device_writer_jar",
srcs: ["src/**/*.java"],
- libs: ["tradefed", "junit"],
+ libs: [
+ "tradefed",
+ "junit",
+ ],
}
diff --git a/tests/ApkVerityTest/block_device_writer/src/com/android/blockdevicewriter/BlockDeviceWriter.java b/tests/ApkVerityTest/block_device_writer/src/com/android/blockdevicewriter/BlockDeviceWriter.java
index 5c2c15b22bb0..730daf32f20d 100644
--- a/tests/ApkVerityTest/block_device_writer/src/com/android/blockdevicewriter/BlockDeviceWriter.java
+++ b/tests/ApkVerityTest/block_device_writer/src/com/android/blockdevicewriter/BlockDeviceWriter.java
@@ -32,7 +32,7 @@ import java.util.ArrayList;
* <p>To use this class, please push block_device_writer binary to /data/local/tmp.
* 1. In Android.bp, add:
* <pre>
- * target_required: ["block_device_writer_module"],
+ * data_device_bins: ["block_device_writer"],
* </pre>
* 2. In AndroidText.xml, add:
* <pre>
diff --git a/tools/lint/README.md b/tools/lint/README.md
index 2b6d65b318e5..b534b62cb395 100644
--- a/tools/lint/README.md
+++ b/tools/lint/README.md
@@ -40,6 +40,9 @@ m out/soong/.intermediates/frameworks/base/services/autofill/services.autofill/a
- If you want to build lint reports for more than 1 module and they include a common module in their
`defaults` field, e.g. `platform_service_defaults`, you can add the `lint` property to that common
module instead of adding it in every module.
+- If you want to run a single lint type, use the `ANDROID_LINT_CHECK`
+ environment variable with the id of the lint. For example:
+ `ANDROID_LINT_CHECK=UnusedTokenOfOriginalCallingIdentity m out/[...]/lint-report.html`
## Create or update a baseline