summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.bp36
-rw-r--r--api/current.txt249
-rw-r--r--api/system-current.txt223
-rw-r--r--api/test-current.txt117
-rw-r--r--cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java46
-rw-r--r--cmds/idmap2/idmap2/Create.cpp8
-rw-r--r--cmds/idmap2/idmap2d/Idmap2Service.cpp20
-rw-r--r--cmds/idmap2/include/idmap2/FileUtils.h5
-rw-r--r--cmds/idmap2/libidmap2/FileUtils.cpp30
-rw-r--r--cmds/idmap2/tests/FileUtilsTests.cpp23
-rw-r--r--cmds/idmap2/tests/Idmap2BinaryTests.cpp23
-rw-r--r--cmds/statsd/Android.bp2
-rw-r--r--cmds/statsd/src/StatsService.cpp24
-rw-r--r--cmds/statsd/src/StatsService.h11
-rw-r--r--cmds/statsd/src/atoms.proto32
-rw-r--r--cmds/statsd/src/external/ResourceThermalManagerPuller.cpp147
-rw-r--r--cmds/statsd/src/external/StatsPullerManager.cpp4
-rw-r--r--cmds/statsd/src/logd/LogEvent.cpp46
-rw-r--r--cmds/statsd/src/logd/LogEvent.h6
-rw-r--r--cmds/statsd/src/packages/UidMap.cpp1
-rw-r--r--cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java3
-rw-r--r--config/hiddenapi-greylist.txt2
-rw-r--r--core/java/android/app/Activity.java4
-rw-r--r--core/java/android/app/ActivityThread.java14
-rw-r--r--core/java/android/app/ActivityView.java63
-rw-r--r--core/java/android/app/IApplicationThread.aidl3
-rw-r--r--core/java/android/app/KeyguardManager.java33
-rw-r--r--core/java/android/app/NotificationManager.java3
-rw-r--r--core/java/android/app/admin/DevicePolicyManager.java208
-rw-r--r--core/java/android/app/admin/IDevicePolicyManager.aidl7
-rw-r--r--core/java/android/app/assist/AssistStructure.java5
-rw-r--r--core/java/android/app/backup/BackupAgent.java20
-rw-r--r--core/java/android/app/backup/IBackupManager.aidl13
-rw-r--r--core/java/android/app/role/RoleManager.java9
-rw-r--r--core/java/android/app/usage/IUsageStatsManager.aidl3
-rw-r--r--core/java/android/app/usage/UsageStatsManager.java82
-rw-r--r--core/java/android/app/usage/UsageStatsManagerInternal.java37
-rw-r--r--core/java/android/bluetooth/BluetoothAdapter.java39
-rw-r--r--core/java/android/bluetooth/BluetoothDevice.java86
-rw-r--r--core/java/android/bluetooth/BluetoothHearingAid.java81
-rw-r--r--core/java/android/bluetooth/BluetoothProfile.java1
-rw-r--r--core/java/android/content/Intent.java7
-rw-r--r--core/java/android/content/pm/ActivityInfo.java2
-rw-r--r--core/java/android/content/pm/ApplicationInfo.java5
-rw-r--r--core/java/android/content/pm/ILauncherApps.aidl4
-rw-r--r--core/java/android/content/pm/LauncherApps.aidl19
-rw-r--r--core/java/android/content/pm/LauncherApps.java103
-rw-r--r--core/java/android/content/pm/OrgApacheHttpLegacyUpdater.java6
-rw-r--r--core/java/android/content/pm/PackageBackwardCompatibility.java25
-rw-r--r--core/java/android/content/pm/PackageInstaller.java7
-rw-r--r--core/java/android/content/pm/PackageManager.java7
-rw-r--r--core/java/android/content/pm/PackageParser.java2
-rw-r--r--core/java/android/content/pm/RegisteredServicesCache.java8
-rw-r--r--core/java/android/hardware/camera2/CameraCharacteristics.java10
-rw-r--r--core/java/android/hardware/camera2/CameraMetadata.java81
-rw-r--r--core/java/android/hardware/camera2/CaptureRequest.java1
-rw-r--r--core/java/android/hardware/camera2/CaptureResult.java18
-rw-r--r--core/java/android/hardware/display/ColorDisplayManager.java312
-rw-r--r--core/java/android/hardware/display/IColorDisplayManager.aidl14
-rw-r--r--core/java/android/hardware/display/Time.aidl19
-rw-r--r--core/java/android/hardware/display/Time.java77
-rw-r--r--core/java/android/hardware/usb/IUsbManager.aidl3
-rw-r--r--core/java/android/hardware/usb/ParcelableUsbPort.java34
-rw-r--r--core/java/android/hardware/usb/UsbManager.java14
-rw-r--r--core/java/android/hardware/usb/UsbPort.java77
-rw-r--r--core/java/android/hardware/usb/UsbPortStatus.java125
-rw-r--r--core/java/android/net/ConnectivityManager.java108
-rw-r--r--core/java/android/net/IConnectivityManager.aidl9
-rw-r--r--core/java/android/net/IpPrefix.java4
-rw-r--r--core/java/android/net/LinkAddress.java2
-rw-r--r--core/java/android/net/LinkProperties.java11
-rw-r--r--core/java/android/net/NattSocketKeepalive.java75
-rw-r--r--core/java/android/net/ProxyInfo.java57
-rw-r--r--core/java/android/net/RouteInfo.java2
-rw-r--r--core/java/android/net/SocketKeepalive.java224
-rw-r--r--core/java/android/net/VpnService.java9
-rw-r--r--core/java/android/net/metrics/IpConnectivityLog.java4
-rw-r--r--core/java/android/net/metrics/RaEvent.java26
-rw-r--r--core/java/android/os/AppZygote.java2
-rw-r--r--core/java/android/os/GraphicsEnvironment.java69
-rw-r--r--core/java/android/os/ParcelFileDescriptor.java32
-rw-r--r--core/java/android/os/Process.java19
-rw-r--r--core/java/android/os/VibrationEffect.java42
-rw-r--r--core/java/android/os/ZygoteProcess.java456
-rw-r--r--core/java/android/provider/CalendarContract.java18
-rw-r--r--core/java/android/provider/DeviceConfig.java89
-rw-r--r--core/java/android/provider/MediaStore.java26
-rw-r--r--core/java/android/provider/Settings.java167
-rw-r--r--core/java/android/service/autofill/augmented/AugmentedAutofillService.java17
-rw-r--r--core/java/android/service/autofill/augmented/FillCallback.java5
-rw-r--r--core/java/android/service/autofill/augmented/FillRequest.java2
-rw-r--r--core/java/android/service/autofill/augmented/FillResponse.java41
-rw-r--r--core/java/android/service/autofill/augmented/FillWindow.java36
-rw-r--r--core/java/android/service/autofill/augmented/IFillCallback.aidl3
-rw-r--r--core/java/android/service/autofill/augmented/PresentationParams.java2
-rw-r--r--core/java/android/service/contentcapture/ContentCaptureService.java11
-rw-r--r--core/java/android/service/contentcapture/IContentCaptureService.aidl2
-rw-r--r--core/java/android/service/notification/Adjustment.java9
-rw-r--r--core/java/android/service/notification/NotificationAssistantService.java8
-rw-r--r--core/java/android/service/notification/NotificationListenerService.java6
-rw-r--r--core/java/android/service/notification/NotificationStats.java9
-rw-r--r--core/java/android/service/voice/VoiceInteractionService.java67
-rw-r--r--core/java/android/util/LongArrayQueue.java165
-rw-r--r--core/java/android/view/RemoteAnimationTarget.java37
-rw-r--r--core/java/android/view/View.java75
-rw-r--r--core/java/android/view/WindowInsetsController.java53
-rw-r--r--core/java/android/view/WindowManager.java9
-rw-r--r--core/java/android/view/accessibility/AccessibilityManager.java5
-rw-r--r--core/java/android/view/accessibility/IAccessibilityManager.aidl2
-rw-r--r--core/java/android/view/autofill/AutofillId.java113
-rw-r--r--core/java/android/view/autofill/AutofillManager.java148
-rw-r--r--core/java/android/view/contentcapture/ContentCaptureContext.java76
-rw-r--r--core/java/android/view/contentcapture/ContentCaptureEvent.java8
-rw-r--r--core/java/android/view/contentcapture/ContentCaptureHelper.java40
-rw-r--r--core/java/android/view/contentcapture/ContentCaptureManager.java16
-rw-r--r--core/java/android/view/contentcapture/ContentCaptureSession.java38
-rw-r--r--core/java/android/view/contentcapture/IContentCaptureManager.aidl6
-rw-r--r--core/java/android/view/contentcapture/MainContentCaptureSession.java62
-rw-r--r--core/java/android/view/contentcapture/UserDataRemovalRequest.aidl19
-rw-r--r--core/java/android/view/contentcapture/UserDataRemovalRequest.java100
-rw-r--r--core/java/android/view/contentcapture/ViewNode.java2
-rw-r--r--core/java/android/view/inputmethod/InputMethodManager.java13
-rw-r--r--core/java/android/view/inspector/InspectableProperty.java1
-rw-r--r--core/java/android/view/inspector/IntEnumMapping.java102
-rw-r--r--core/java/android/view/inspector/IntFlagMapping.java7
-rw-r--r--core/java/android/view/inspector/PropertyMapper.java5
-rw-r--r--core/java/android/view/textclassifier/ActionsSuggestionsHelper.java11
-rw-r--r--core/java/android/view/textclassifier/TextClassificationManager.java17
-rw-r--r--core/java/android/view/textclassifier/TextClassifierEvent.java63
-rw-r--r--core/java/android/view/textclassifier/TextClassifierEventTronLogger.java60
-rw-r--r--core/java/android/view/textclassifier/TextClassifierImpl.java12
-rw-r--r--core/java/android/webkit/WebViewZygote.java58
-rw-r--r--core/java/android/widget/TextView.java2
-rw-r--r--core/java/com/android/internal/app/AssistUtils.java43
-rw-r--r--core/java/com/android/internal/app/ChooserActivity.java24
-rw-r--r--core/java/com/android/internal/app/ColorDisplayController.java204
-rw-r--r--core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl15
-rw-r--r--core/java/com/android/internal/app/IVoiceInteractionSessionListener.aidl16
-rw-r--r--core/java/com/android/internal/net/NetworkStatsFactory.java35
-rw-r--r--core/java/com/android/internal/net/VpnConfig.java5
-rw-r--r--core/java/com/android/internal/net/VpnInfo.java10
-rw-r--r--core/java/com/android/internal/os/BinderCallsStats.java2
-rw-r--r--core/java/com/android/internal/os/WebViewZygoteInit.java47
-rw-r--r--core/java/com/android/internal/os/Zygote.java596
-rw-r--r--core/java/com/android/internal/os/ZygoteArguments.java430
-rw-r--r--core/java/com/android/internal/os/ZygoteConnection.java631
-rw-r--r--core/java/com/android/internal/os/ZygoteInit.java237
-rw-r--r--core/java/com/android/internal/os/ZygoteServer.java194
-rw-r--r--core/java/com/android/internal/statusbar/NotificationVisibility.java59
-rw-r--r--core/java/com/android/internal/usb/DumpUtils.java13
-rw-r--r--core/java/com/android/server/SystemConfig.java22
-rw-r--r--core/jni/AndroidRuntime.cpp4
-rwxr-xr-xcore/jni/android/graphics/Bitmap.cpp16
-rw-r--r--core/jni/android_hardware_input_InputApplicationHandle.cpp12
-rw-r--r--core/jni/android_hardware_input_InputApplicationHandle.h8
-rw-r--r--core/jni/android_hardware_input_InputWindowHandle.cpp14
-rw-r--r--core/jni/android_hardware_input_InputWindowHandle.h8
-rw-r--r--core/jni/android_view_SurfaceControl.cpp2
-rw-r--r--core/jni/android_view_ThreadedRenderer.cpp3
-rw-r--r--core/jni/com_android_internal_os_Zygote.cpp4
-rw-r--r--core/jni/fd_utils.cpp8
-rw-r--r--core/proto/android/app/settings_enums.proto10
-rw-r--r--core/proto/android/providers/settings/global.proto17
-rw-r--r--core/proto/android/providers/settings/secure.proto5
-rw-r--r--core/proto/android/server/connectivity/data_stall_event.proto2
-rw-r--r--core/proto/android/server/jobscheduler.proto82
-rw-r--r--core/proto/android/server/powermanagerservice.proto8
-rw-r--r--core/proto/android/service/usb.proto10
-rw-r--r--core/proto/android/stats/devicepolicy/device_policy_enums.proto3
-rw-r--r--core/proto/android/view/remote_animation_target.proto2
-rw-r--r--core/res/AndroidManifest.xml12
-rw-r--r--core/res/res/values/attrs_manifest.xml23
-rw-r--r--core/res/res/values/config.xml14
-rw-r--r--core/res/res/values/public.xml4
-rw-r--r--core/res/res/values/strings.xml12
-rw-r--r--core/res/res/values/symbols.xml11
-rw-r--r--core/res/res/values/themes_device_defaults.xml2
-rw-r--r--core/tests/BroadcastRadioTests/src/android/hardware/radio/tests/functional/RadioTunerTest.java11
-rw-r--r--core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java3
-rw-r--r--core/tests/coretests/src/android/content/pm/PackageBackwardCompatibilityTest.java42
-rw-r--r--core/tests/coretests/src/android/content/pm/PackageParserTest.java4
-rw-r--r--core/tests/coretests/src/android/provider/SettingsBackupTest.java13
-rw-r--r--core/tests/coretests/src/android/util/LongArrayQueueTest.java238
-rw-r--r--core/tests/coretests/src/android/view/autofill/AutofillIdTest.java78
-rw-r--r--core/tests/coretests/src/android/view/contentcapture/ContentCaptureManagerTest.java51
-rw-r--r--core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java25
-rw-r--r--core/tests/coretests/src/android/view/contentcapture/UserDataRemovalRequestTest.java88
-rw-r--r--core/tests/coretests/src/android/view/contentcapture/ViewNodeTest.java2
-rw-r--r--core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java3
-rw-r--r--core/tests/coretests/src/android/view/textclassifier/logging/TextClassifierEventTronLoggerTest.java17
-rw-r--r--core/tests/coretests/src/com/android/internal/statusbar/NotificationVisibilityTest.java45
-rw-r--r--graphics/java/android/graphics/Bitmap.java25
-rw-r--r--jarjar_rules_hidl.txt1
-rw-r--r--libs/hwui/DeviceInfo.cpp9
-rw-r--r--libs/hwui/DeviceInfo.h2
-rw-r--r--libs/hwui/HardwareBitmapUploader.cpp18
-rw-r--r--libs/hwui/hwui/Bitmap.cpp9
-rw-r--r--libs/hwui/hwui/Bitmap.h1
-rw-r--r--libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp2
-rw-r--r--libs/hwui/pipeline/skia/SkiaPipeline.cpp2
-rw-r--r--libs/hwui/pipeline/skia/SkiaPipeline.h1
-rw-r--r--libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp18
-rw-r--r--libs/hwui/renderthread/EglManager.cpp28
-rw-r--r--libs/hwui/renderthread/EglManager.h2
-rw-r--r--libs/hwui/renderthread/RenderProxy.cpp6
-rw-r--r--libs/hwui/renderthread/VulkanManager.cpp13
-rw-r--r--libs/hwui/renderthread/VulkanManager.h6
-rw-r--r--libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp3
-rw-r--r--libs/hwui/utils/Color.cpp14
-rw-r--r--libs/hwui/utils/Color.h1
-rw-r--r--media/Android.bp127
-rw-r--r--media/apex/java/android/media/Controller2Link.aidl (renamed from media/java/android/media/Controller2Link.aidl)0
-rw-r--r--media/apex/java/android/media/Controller2Link.java (renamed from media/java/android/media/Controller2Link.java)0
-rw-r--r--media/apex/java/android/media/IMediaController2.aidl (renamed from media/java/android/media/IMediaController2.aidl)0
-rw-r--r--media/apex/java/android/media/IMediaSession2.aidl (renamed from media/java/android/media/IMediaSession2.aidl)0
-rw-r--r--media/apex/java/android/media/IMediaSession2Service.aidl (renamed from media/java/android/media/IMediaSession2Service.aidl)0
-rw-r--r--media/apex/java/android/media/MediaConstants.java (renamed from media/java/android/media/MediaConstants.java)0
-rw-r--r--media/apex/java/android/media/MediaController2.java (renamed from media/java/android/media/MediaController2.java)0
-rw-r--r--media/apex/java/android/media/MediaItem2.java (renamed from media/java/android/media/MediaItem2.java)0
-rw-r--r--media/apex/java/android/media/MediaMetadata.aidl (renamed from media/java/android/media/MediaMetadata.aidl)0
-rw-r--r--media/apex/java/android/media/MediaMetadata.java (renamed from media/java/android/media/MediaMetadata.java)20
-rw-r--r--media/apex/java/android/media/MediaParceledListSlice.aidl (renamed from media/java/android/media/MediaParceledListSlice.aidl)0
-rw-r--r--media/apex/java/android/media/MediaParceledListSlice.java (renamed from media/java/android/media/MediaParceledListSlice.java)0
-rw-r--r--media/apex/java/android/media/MediaPlayer2.java306
-rw-r--r--media/apex/java/android/media/MediaSession2.java (renamed from media/java/android/media/MediaSession2.java)0
-rw-r--r--media/apex/java/android/media/MediaSession2Service.java (renamed from media/java/android/media/MediaSession2Service.java)0
-rw-r--r--media/apex/java/android/media/Session2Command.aidl (renamed from media/java/android/media/Session2Command.aidl)0
-rw-r--r--media/apex/java/android/media/Session2Command.java (renamed from media/java/android/media/Session2Command.java)0
-rw-r--r--media/apex/java/android/media/Session2CommandGroup.java (renamed from media/java/android/media/Session2CommandGroup.java)7
-rw-r--r--media/apex/java/android/media/Session2Link.java (renamed from media/java/android/media/Session2Link.java)0
-rw-r--r--media/apex/java/android/media/Session2Token.aidl (renamed from media/java/android/media/Session2Token.aidl)0
-rw-r--r--media/apex/java/android/media/Session2Token.java (renamed from media/java/android/media/Session2Token.java)0
-rw-r--r--media/apex/java/android/media/VolumeProvider.java (renamed from media/java/android/media/VolumeProvider.java)8
-rw-r--r--media/apex/java/android/media/browse/MediaBrowser.aidl (renamed from media/java/android/media/browse/MediaBrowser.aidl)0
-rw-r--r--media/apex/java/android/media/browse/MediaBrowser.java (renamed from media/java/android/media/browse/MediaBrowser.java)24
-rw-r--r--media/apex/java/android/media/browse/MediaBrowserUtils.java (renamed from media/java/android/media/browse/MediaBrowserUtils.java)6
-rw-r--r--media/apex/java/android/media/session/ControllerCallbackLink.aidl (renamed from media/java/android/media/session/ControllerCallbackLink.aidl)0
-rw-r--r--media/apex/java/android/media/session/ControllerCallbackLink.java (renamed from media/java/android/media/session/ControllerCallbackLink.java)6
-rw-r--r--media/apex/java/android/media/session/ControllerLink.aidl (renamed from media/java/android/media/session/ControllerLink.aidl)0
-rw-r--r--media/apex/java/android/media/session/ControllerLink.java (renamed from media/java/android/media/session/ControllerLink.java)0
-rw-r--r--media/apex/java/android/media/session/ISession.aidl (renamed from media/java/android/media/session/ISession.aidl)0
-rw-r--r--media/apex/java/android/media/session/ISessionCallback.aidl (renamed from media/java/android/media/session/ISessionCallback.aidl)0
-rw-r--r--media/apex/java/android/media/session/ISessionController.aidl (renamed from media/java/android/media/session/ISessionController.aidl)0
-rw-r--r--media/apex/java/android/media/session/ISessionControllerCallback.aidl (renamed from media/java/android/media/session/ISessionControllerCallback.aidl)0
-rw-r--r--media/apex/java/android/media/session/MediaController.aidl (renamed from media/java/android/media/session/MediaController.aidl)0
-rw-r--r--media/apex/java/android/media/session/MediaController.java (renamed from media/java/android/media/session/MediaController.java)11
-rw-r--r--media/apex/java/android/media/session/MediaSessionEngine.java (renamed from media/java/android/media/session/MediaSessionEngine.java)6
-rw-r--r--media/apex/java/android/media/session/MediaSessionProviderService.java (renamed from media/java/android/media/session/MediaSessionProviderService.java)0
-rw-r--r--media/apex/java/android/media/session/PlaybackState.aidl (renamed from media/java/android/media/session/PlaybackState.aidl)0
-rw-r--r--media/apex/java/android/media/session/PlaybackState.java (renamed from media/java/android/media/session/PlaybackState.java)55
-rw-r--r--media/apex/java/android/media/session/SessionCallbackLink.aidl (renamed from media/java/android/media/session/SessionCallbackLink.aidl)0
-rw-r--r--media/apex/java/android/media/session/SessionCallbackLink.java (renamed from media/java/android/media/session/SessionCallbackLink.java)6
-rw-r--r--media/apex/java/android/media/session/SessionLink.aidl (renamed from media/java/android/media/session/SessionLink.aidl)0
-rw-r--r--media/apex/java/android/media/session/SessionLink.java (renamed from media/java/android/media/session/SessionLink.java)6
-rw-r--r--media/apex/java/android/service/media/IMediaBrowserService.aidl (renamed from media/java/android/service/media/IMediaBrowserService.aidl)2
-rw-r--r--media/apex/java/android/service/media/IMediaBrowserServiceCallbacks.aidl (renamed from media/java/android/service/media/IMediaBrowserServiceCallbacks.aidl)1
-rw-r--r--media/apex/java/android/service/media/MediaBrowserService.java (renamed from media/java/android/service/media/MediaBrowserService.java)21
-rw-r--r--media/java/android/media/MediaDrm.java25
-rw-r--r--media/java/android/media/session/MediaSession.java2
-rw-r--r--media/jni/android_media_MediaDrm.cpp51
-rw-r--r--media/jni/android_media_MediaDrm.h4
-rw-r--r--media/packages/MediaCore/Android.bp.bak (renamed from media/packages/MediaCore/Android.bp)0
-rw-r--r--native/android/libandroid.map.txt1
-rw-r--r--native/android/surface_control.cpp91
-rw-r--r--packages/CarSystemUI/Android.bp2
-rw-r--r--packages/CarSystemUI/res/layout/car_navigation_bar.xml3
-rw-r--r--packages/ExtServices/src/android/ext/services/notification/AssistantSettings.java92
-rw-r--r--packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java4
-rw-r--r--packages/ExtServices/tests/src/android/ext/services/notification/AssistantSettingsTest.java86
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java4
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/applications/AppUtils.java9
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java30
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java109
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java39
-rw-r--r--packages/SystemUI/Android.bp4
-rw-r--r--packages/SystemUI/AndroidManifest.xml9
-rw-r--r--packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationMenuRowPlugin.java12
-rw-r--r--packages/SystemUI/res/values/strings.xml8
-rw-r--r--packages/SystemUI/src/com/android/systemui/assist/AssistManager.java53
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/MediaArtworkProcessor.kt80
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java18
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java22
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java61
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java62
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java27
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java35
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationGestureAction.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationPrototypeController.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/usb/UsbContaminantActivity.java73
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/ExpansionStateLoggerTest.java48
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java55
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java135
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java6
-rw-r--r--proto/src/metrics_constants/metrics_constants.proto54
-rw-r--r--proto/src/system_messages.proto8
-rw-r--r--proto/src/wifi.proto158
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java247
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java179
-rw-r--r--services/autofill/java/com/android/server/autofill/AutofillManagerService.java34
-rw-r--r--services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java5
-rw-r--r--services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java2
-rw-r--r--services/autofill/java/com/android/server/autofill/Session.java25
-rw-r--r--services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java2
-rw-r--r--services/backup/java/com/android/server/backup/Trampoline.java205
-rw-r--r--services/backup/java/com/android/server/backup/UserBackupManagerService.java26
-rw-r--r--services/backup/java/com/android/server/backup/fullbackup/PerformAdbBackupTask.java3
-rw-r--r--services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java9
-rw-r--r--services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java2
-rw-r--r--services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java7
-rw-r--r--services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java2
-rw-r--r--services/backup/java/com/android/server/backup/utils/AppBackupUtils.java56
-rw-r--r--services/backup/java/com/android/server/backup/utils/TarBackupReader.java7
-rw-r--r--services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java10
-rw-r--r--services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java38
-rw-r--r--services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java8
-rw-r--r--services/core/java/com/android/server/AlarmManagerService.java360
-rw-r--r--services/core/java/com/android/server/BluetoothManagerService.java17
-rw-r--r--services/core/java/com/android/server/ConnectivityService.java200
-rw-r--r--services/core/java/com/android/server/LocationManagerService.java66
-rw-r--r--services/core/java/com/android/server/TEST_MAPPING1
-rw-r--r--services/core/java/com/android/server/TelephonyRegistry.java10
-rw-r--r--services/core/java/com/android/server/accounts/AccountManagerService.java2
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerConstants.java83
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java27
-rw-r--r--services/core/java/com/android/server/am/AppCompactor.java451
-rw-r--r--services/core/java/com/android/server/am/BaseErrorDialog.java6
-rw-r--r--services/core/java/com/android/server/am/CoreSettingsObserver.java2
-rw-r--r--services/core/java/com/android/server/am/OomAdjuster.java15
-rw-r--r--services/core/java/com/android/server/am/PendingIntentRecord.java10
-rw-r--r--services/core/java/com/android/server/am/ProcessList.java15
-rw-r--r--services/core/java/com/android/server/connectivity/KeepaliveTracker.java109
-rw-r--r--services/core/java/com/android/server/connectivity/NetworkAgentInfo.java29
-rw-r--r--services/core/java/com/android/server/connectivity/NetworkNotificationManager.java33
-rw-r--r--services/core/java/com/android/server/connectivity/ProxyTracker.java18
-rw-r--r--services/core/java/com/android/server/connectivity/Vpn.java78
-rw-r--r--services/core/java/com/android/server/display/ColorDisplayService.java477
-rw-r--r--services/core/java/com/android/server/job/JobConcurrencyManager.java126
-rw-r--r--services/core/java/com/android/server/job/JobSchedulerService.java170
-rw-r--r--services/core/java/com/android/server/notification/NotificationManagerService.java15
-rw-r--r--services/core/java/com/android/server/pm/BackgroundDexOptService.java202
-rw-r--r--services/core/java/com/android/server/pm/LauncherAppsService.java28
-rw-r--r--services/core/java/com/android/server/pm/PackageDexOptimizer.java6
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java3
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerShellCommand.java8
-rw-r--r--services/core/java/com/android/server/pm/UserRestrictionsUtils.java1
-rw-r--r--services/core/java/com/android/server/pm/dex/DexManager.java110
-rw-r--r--services/core/java/com/android/server/policy/PhoneWindowManager.java6
-rw-r--r--services/core/java/com/android/server/policy/WindowManagerPolicy.java8
-rw-r--r--services/core/java/com/android/server/policy/role/LegacyRoleResolutionPolicy.java12
-rw-r--r--services/core/java/com/android/server/power/batterysaver/BatterySaverController.java1
-rw-r--r--services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java125
-rw-r--r--services/core/java/com/android/server/role/RoleManagerService.java1
-rw-r--r--services/core/java/com/android/server/rollback/LocalIntentReceiver.java47
-rw-r--r--services/core/java/com/android/server/rollback/RollbackData.java8
-rw-r--r--services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java107
-rw-r--r--services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java94
-rw-r--r--services/core/java/com/android/server/signedconfig/SignatureVerifier.java4
-rw-r--r--services/core/java/com/android/server/stats/StatsCompanionService.java25
-rw-r--r--services/core/java/com/android/server/uri/UriGrantsManagerService.java3
-rw-r--r--services/core/java/com/android/server/wm/ActivityStarter.java57
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java3
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerService.java51
-rw-r--r--services/core/java/com/android/server/wm/AppTransition.java30
-rw-r--r--services/core/java/com/android/server/wm/AppTransitionController.java94
-rw-r--r--services/core/java/com/android/server/wm/AppWindowThumbnail.java27
-rw-r--r--services/core/java/com/android/server/wm/AppWindowToken.java154
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java7
-rw-r--r--services/core/java/com/android/server/wm/DisplayPolicy.java4
-rw-r--r--services/core/java/com/android/server/wm/DockedStackDividerController.java5
-rw-r--r--services/core/java/com/android/server/wm/InputMonitor.java12
-rw-r--r--services/core/java/com/android/server/wm/RecentsAnimationController.java6
-rw-r--r--services/core/java/com/android/server/wm/RemoteAnimationController.java153
-rw-r--r--services/core/java/com/android/server/wm/StatusBarController.java5
-rw-r--r--services/core/java/com/android/server/wm/WallpaperController.java12
-rw-r--r--services/core/java/com/android/server/wm/WindowChangeAnimationSpec.java203
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerInternal.java6
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java44
-rw-r--r--services/core/jni/com_android_server_input_InputManagerService.cpp4
-rw-r--r--services/core/jni/com_android_server_lights_LightsService.cpp44
-rw-r--r--services/core/jni/onload.cpp4
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java7
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java164
-rw-r--r--services/java/com/android/server/SystemServer.java14
-rw-r--r--services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java2
-rw-r--r--services/robotests/src/com/android/server/testing/shadows/ShadowAppBackupUtils.java2
-rw-r--r--services/robotests/src/com/android/server/testing/shadows/ShadowBackupActivityThread.java79
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java298
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/power/batterysaver/BatterySaverStateMachineTest.java (renamed from services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverStateMachineTest.java)238
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInputFilterTest.java275
-rw-r--r--services/tests/servicestests/src/com/android/server/am/AppCompactorTest.java379
-rw-r--r--services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java290
-rw-r--r--services/tests/servicestests/src/com/android/server/backup/testutils/IPackageManagerStub.java1172
-rw-r--r--services/tests/servicestests/src/com/android/server/backup/testutils/PackageManagerStub.java12
-rw-r--r--services/tests/servicestests/src/com/android/server/backup/utils/AppBackupUtilsTest.java71
-rw-r--r--services/tests/servicestests/src/com/android/server/backup/utils/TarBackupReaderTest.java37
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java39
-rw-r--r--services/tests/servicestests/src/com/android/server/display/ColorDisplayServiceTest.java43
-rw-r--r--services/tests/servicestests/src/com/android/server/job/MaxJobCountsTest.java13
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java7
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java65
-rw-r--r--services/tests/servicestests/src/com/android/server/usage/AppTimeLimitControllerTests.java340
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java4
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java7
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/BoundsAnimationControllerTests.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java79
-rw-r--r--services/usage/java/com/android/server/usage/AppTimeLimitController.java180
-rw-r--r--services/usage/java/com/android/server/usage/UsageStatsService.java76
-rw-r--r--services/usb/Android.bp1
-rw-r--r--services/usb/java/com/android/server/usb/UsbPortManager.java488
-rw-r--r--services/usb/java/com/android/server/usb/UsbService.java29
-rw-r--r--services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java6
-rw-r--r--services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java165
-rw-r--r--startop/OWNERS2
-rw-r--r--telecomm/java/android/telecom/CallScreeningService.java126
-rw-r--r--telecomm/java/android/telecom/TelecomManager.java27
-rw-r--r--telecomm/java/com/android/internal/telecom/ITelecomService.aidl3
-rw-r--r--telephony/java/android/provider/Telephony.java9
-rw-r--r--telephony/java/android/telephony/CarrierConfigManager.java34
-rw-r--r--telephony/java/android/telephony/SmsManager.java120
-rw-r--r--telephony/java/android/telephony/SubscriptionInfo.java31
-rw-r--r--telephony/java/android/telephony/SubscriptionManager.java227
-rw-r--r--telephony/java/android/telephony/ims/ImsMmTelManager.java2
-rw-r--r--telephony/java/android/telephony/ims/ProvisioningManager.java139
-rw-r--r--telephony/java/android/telephony/ims/feature/CapabilityChangeRequest.java14
-rw-r--r--telephony/java/com/android/ims/ImsConfig.java6
-rwxr-xr-xtelephony/java/com/android/internal/telephony/ISub.aidl29
-rw-r--r--telephony/java/com/android/internal/telephony/ITelephony.aidl18
-rw-r--r--telephony/java/com/android/internal/telephony/TelephonyPermissions.java58
-rw-r--r--tests/RollbackTest/Android.mk13
-rw-r--r--tests/RollbackTest/TestApp/ACrashingV2.xml37
-rw-r--r--tests/RollbackTest/TestApp/src/com/android/tests/rollback/testapp/CrashingMainActivity.java (renamed from cmds/statsd/src/external/ResourceThermalManagerPuller.h)30
-rw-r--r--tests/RollbackTest/src/com/android/tests/rollback/RollbackTest.java61
-rw-r--r--tests/RollbackTest/src/com/android/tests/rollback/RollbackTestUtils.java11
-rw-r--r--tests/UsageStatsTest/AndroidManifest.xml1
-rw-r--r--tests/UsageStatsTest/res/menu/main.xml2
-rw-r--r--tests/UsageStatsTest/src/com/android/tests/usagestats/UsageStatsActivity.java56
-rw-r--r--tests/net/java/com/android/server/ConnectivityServiceTest.java314
-rw-r--r--tests/net/java/com/android/server/connectivity/NetworkNotificationManagerTest.java65
-rw-r--r--tests/net/java/com/android/server/connectivity/VpnTest.java103
-rw-r--r--tests/utils/testutils/java/com/android/test/filters/SelectTestTests.java13
-rw-r--r--tools/aapt2/cmd/Convert_test.cpp12
-rw-r--r--tools/aapt2/cmd/Link_test.cpp12
-rw-r--r--tools/aapt2/test/Fixture.cpp16
-rw-r--r--tools/aapt2/test/Fixture.h6
-rw-r--r--tools/processors/unsupportedappusage/Android.bp4
-rw-r--r--tools/processors/view_inspector/Android.bp4
-rw-r--r--tools/signedconfig/debug_key.pem6
-rw-r--r--tools/signedconfig/debug_public.pem4
-rwxr-xr-xtools/signedconfig/debug_sign.sh2
-rw-r--r--wifi/java/android/net/wifi/IWifiManager.aidl8
-rw-r--r--wifi/java/android/net/wifi/IWifiUsabilityStatsListener.aidl41
-rw-r--r--wifi/java/android/net/wifi/WifiConfiguration.java25
-rw-r--r--wifi/java/android/net/wifi/WifiManager.java112
-rw-r--r--wifi/java/android/net/wifi/WifiUsabilityStatsEntry.aidl19
-rw-r--r--wifi/java/android/net/wifi/WifiUsabilityStatsEntry.java148
-rw-r--r--wifi/java/com/android/server/wifi/BaseWifiService.java17
-rw-r--r--wifi/tests/src/android/net/wifi/WifiConfigurationTest.java4
-rw-r--r--wifi/tests/src/android/net/wifi/WifiManagerTest.java40
-rw-r--r--wifi/tests/src/android/net/wifi/WifiUsabilityStatsEntryTest.java104
475 files changed, 18312 insertions, 4928 deletions
diff --git a/Android.bp b/Android.bp
index b68312b47c46..a5cc89cd0ea1 100644
--- a/Android.bp
+++ b/Android.bp
@@ -49,6 +49,9 @@ java_defaults {
"rs/java/**/*.java",
":framework-javastream-protos",
+ // TODO: Resolve circular library dependency and remove media1-srcs and mediasession2-srcs
+ ":media1-srcs",
+ ":mediasession2-srcs",
"core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl",
"core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl",
@@ -471,14 +474,11 @@ java_defaults {
"media/java/android/media/IAudioRoutesObserver.aidl",
"media/java/android/media/IAudioService.aidl",
"media/java/android/media/IAudioServerStateDispatcher.aidl",
- "media/java/android/media/IMediaController2.aidl",
"media/java/android/media/IMediaHTTPConnection.aidl",
"media/java/android/media/IMediaHTTPService.aidl",
"media/java/android/media/IMediaResourceMonitor.aidl",
"media/java/android/media/IMediaRouterClient.aidl",
"media/java/android/media/IMediaRouterService.aidl",
- "media/java/android/media/IMediaSession2.aidl",
- "media/java/android/media/IMediaSession2Service.aidl",
"media/java/android/media/IMediaScannerListener.aidl",
"media/java/android/media/IMediaScannerService.aidl",
"media/java/android/media/IPlaybackConfigDispatcher.aidl",
@@ -504,11 +504,7 @@ java_defaults {
"media/java/android/media/session/ICallback.aidl",
"media/java/android/media/session/IOnMediaKeyListener.aidl",
"media/java/android/media/session/IOnVolumeKeyLongPressListener.aidl",
- "media/java/android/media/session/ISession.aidl",
"media/java/android/media/session/ISession2TokensListener.aidl",
- "media/java/android/media/session/ISessionCallback.aidl",
- "media/java/android/media/session/ISessionController.aidl",
- "media/java/android/media/session/ISessionControllerCallback.aidl",
"media/java/android/media/session/ISessionManager.aidl",
"media/java/android/media/soundtrigger/ISoundTriggerDetectionService.aidl",
"media/java/android/media/soundtrigger/ISoundTriggerDetectionServiceClient.aidl",
@@ -523,8 +519,6 @@ java_defaults {
"media/java/android/media/tv/ITvInputSessionCallback.aidl",
"media/java/android/media/tv/ITvRemoteProvider.aidl",
"media/java/android/media/tv/ITvRemoteServiceInput.aidl",
- "media/java/android/service/media/IMediaBrowserService.aidl",
- "media/java/android/service/media/IMediaBrowserServiceCallbacks.aidl",
"telecomm/java/com/android/internal/telecom/ICallRedirectionAdapter.aidl",
"telecomm/java/com/android/internal/telecom/ICallRedirectionService.aidl",
"telecomm/java/com/android/internal/telecom/ICallScreeningAdapter.aidl",
@@ -637,6 +631,7 @@ java_defaults {
"wifi/java/android/net/wifi/ISoftApCallback.aidl",
"wifi/java/android/net/wifi/ITrafficStateCallback.aidl",
"wifi/java/android/net/wifi/IWifiManager.aidl",
+ "wifi/java/android/net/wifi/IWifiUsabilityStatsListener.aidl",
"wifi/java/android/net/wifi/aware/IWifiAwareDiscoverySessionCallback.aidl",
"wifi/java/android/net/wifi/aware/IWifiAwareEventCallback.aidl",
"wifi/java/android/net/wifi/aware/IWifiAwareMacAddressProvider.aidl",
@@ -691,6 +686,7 @@ java_defaults {
"location/java",
"lowpan/java",
"media/java",
+ "media/apex/java",
"media/mca/effect/java",
"media/mca/filterfw/java",
"media/mca/filterpacks/java",
@@ -723,8 +719,6 @@ java_defaults {
exclude_srcs: [
// See comment on framework-atb-backward-compatibility module below
"core/java/android/content/pm/AndroidTestBaseUpdater.java",
- // See comment on framework-oahl-backward-compatibility module below
- "core/java/android/content/pm/OrgApacheHttpLegacyUpdater.java",
],
no_framework_libs: true,
@@ -732,6 +726,8 @@ java_defaults {
"ext",
],
+ jarjar_rules: ":framework-hidl-jarjar",
+
static_libs: [
"apex_aidl_interface-java",
"networkstack-aidl-interfaces-java",
@@ -749,6 +745,7 @@ java_defaults {
"android.hardware.tv.input-V1.0-java-constants",
"android.hardware.usb-V1.0-java-constants",
"android.hardware.usb-V1.1-java-constants",
+ "android.hardware.usb-V1.2-java-constants",
"android.hardware.vibrator-V1.0-java",
"android.hardware.vibrator-V1.1-java",
"android.hardware.vibrator-V1.2-java",
@@ -791,6 +788,11 @@ filegroup {
],
}
+filegroup {
+ name: "framework-hidl-jarjar",
+ srcs: ["jarjar_rules_hidl.txt"],
+}
+
java_library {
name: "framework",
defaults: ["framework-defaults"],
@@ -801,11 +803,7 @@ java_library {
name: "framework-annotation-proc",
defaults: ["framework-defaults"],
// Use UsedByApps annotation processor
- annotation_processors: ["unsupportedappusage-annotation-processor"],
- // b/25860419: annotation processors must be explicitly specified for grok
- annotation_processor_classes: [
- "android.processor.unsupportedappusage.UsedByAppsProcessor",
- ],
+ plugins: ["unsupportedappusage-annotation-processor"],
}
// A host library including just UnsupportedAppUsage.java so that the annotation
@@ -1264,7 +1262,7 @@ stubs_defaults {
":non_openjdk_javadoc_files",
":android_icu4j_src_files_for_docs",
":conscrypt_public_api_files",
- ":media2-srcs",
+ ":media-srcs-without-aidls",
"test-mock/src/**/*.java",
"test-runner/src/**/*.java",
],
@@ -1326,7 +1324,7 @@ stubs_defaults {
":non_openjdk_javadoc_files",
":android_icu4j_src_files_for_docs",
":conscrypt_public_api_files",
- ":media2-srcs",
+ ":media-srcs-without-aidls",
],
srcs_lib: "framework",
srcs_lib_whitelist_dirs: frameworks_base_subdirs,
@@ -1772,6 +1770,7 @@ filegroup {
name: "framework-media-annotation-srcs",
srcs: [
"core/java/android/annotation/CallbackExecutor.java",
+ "core/java/android/annotation/CallSuper.java",
"core/java/android/annotation/DrawableRes.java",
"core/java/android/annotation/IntDef.java",
"core/java/android/annotation/LongDef.java",
@@ -1780,6 +1779,7 @@ filegroup {
"core/java/android/annotation/RequiresPermission.java",
"core/java/android/annotation/SdkConstant.java",
"core/java/android/annotation/StringDef.java",
+ "core/java/android/annotation/SystemApi.java",
"core/java/android/annotation/TestApi.java",
"core/java/android/annotation/UnsupportedAppUsage.java",
"core/java/com/android/internal/annotations/GuardedBy.java",
diff --git a/api/current.txt b/api/current.txt
index 0ea7ecc8d6b1..c0a9c48266e6 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -5793,6 +5793,7 @@ package android.app {
method public java.util.List<android.app.NotificationChannel> getNotificationChannels();
method @Nullable public String getNotificationDelegate();
method public android.app.NotificationManager.Policy getNotificationPolicy();
+ method public boolean isNotificationAssistantAccessGranted(android.content.ComponentName);
method public boolean isNotificationListenerAccessGranted(android.content.ComponentName);
method public boolean isNotificationPolicyAccessGranted();
method public void notify(int, android.app.Notification);
@@ -6578,7 +6579,6 @@ package android.app.admin {
}
public class DevicePolicyManager {
- method public void addCrossProfileCalendarPackage(@NonNull android.content.ComponentName, @NonNull String);
method public void addCrossProfileIntentFilter(@NonNull android.content.ComponentName, android.content.IntentFilter, int);
method public boolean addCrossProfileWidgetProvider(@NonNull android.content.ComponentName, String);
method public int addOverrideApn(@NonNull android.content.ComponentName, @NonNull android.telephony.data.ApnSetting);
@@ -6600,6 +6600,7 @@ package android.app.admin {
method @Nullable public String[] getAccountTypesWithManagementDisabled();
method @Nullable public java.util.List<android.content.ComponentName> getActiveAdmins();
method @NonNull public java.util.Set<java.lang.String> getAffiliationIds(@NonNull android.content.ComponentName);
+ method public java.util.List<java.lang.String> getAlwaysOnVpnLockdownWhitelist(@NonNull android.content.ComponentName);
method @Nullable public String getAlwaysOnVpnPackage(@NonNull android.content.ComponentName);
method @WorkerThread @NonNull public android.os.Bundle getApplicationRestrictions(@Nullable android.content.ComponentName, String);
method @Deprecated @Nullable public String getApplicationRestrictionsManagingPackage(@NonNull android.content.ComponentName);
@@ -6608,7 +6609,7 @@ package android.app.admin {
method public boolean getBluetoothContactSharingDisabled(@NonNull android.content.ComponentName);
method public boolean getCameraDisabled(@Nullable android.content.ComponentName);
method @Deprecated @Nullable public String getCertInstallerPackage(@NonNull android.content.ComponentName) throws java.lang.SecurityException;
- method @NonNull public java.util.Set<java.lang.String> getCrossProfileCalendarPackages(@NonNull android.content.ComponentName);
+ method @Nullable public java.util.Set<java.lang.String> getCrossProfileCalendarPackages(@NonNull android.content.ComponentName);
method public boolean getCrossProfileCallerIdDisabled(@NonNull android.content.ComponentName);
method public boolean getCrossProfileContactsSearchDisabled(@NonNull android.content.ComponentName);
method @NonNull public java.util.List<java.lang.String> getCrossProfileWidgetProviders(@NonNull android.content.ComponentName);
@@ -6674,6 +6675,7 @@ package android.app.admin {
method public boolean isActivePasswordSufficient();
method public boolean isAdminActive(@NonNull android.content.ComponentName);
method public boolean isAffiliatedUser();
+ method public boolean isAlwaysOnVpnLockdownEnabled(@NonNull android.content.ComponentName);
method public boolean isApplicationHidden(@NonNull android.content.ComponentName, String);
method public boolean isBackupServiceEnabled(@NonNull android.content.ComponentName);
method @Deprecated public boolean isCallerApplicationRestrictionsManagingPackage();
@@ -6698,7 +6700,6 @@ package android.app.admin {
method public int logoutUser(@NonNull android.content.ComponentName);
method public void reboot(@NonNull android.content.ComponentName);
method public void removeActiveAdmin(@NonNull android.content.ComponentName);
- method public boolean removeCrossProfileCalendarPackage(@NonNull android.content.ComponentName, @NonNull String);
method public boolean removeCrossProfileWidgetProvider(@NonNull android.content.ComponentName, String);
method public boolean removeKeyPair(@Nullable android.content.ComponentName, @NonNull String);
method public boolean removeOverrideApn(@NonNull android.content.ComponentName, int);
@@ -6712,6 +6713,7 @@ package android.app.admin {
method public void setAccountManagementDisabled(@NonNull android.content.ComponentName, String, boolean);
method public void setAffiliationIds(@NonNull android.content.ComponentName, @NonNull java.util.Set<java.lang.String>);
method public void setAlwaysOnVpnPackage(@NonNull android.content.ComponentName, @Nullable String, boolean) throws android.content.pm.PackageManager.NameNotFoundException, java.lang.UnsupportedOperationException;
+ method public void setAlwaysOnVpnPackage(@NonNull android.content.ComponentName, @Nullable String, boolean, @Nullable java.util.List<java.lang.String>) throws android.content.pm.PackageManager.NameNotFoundException, java.lang.UnsupportedOperationException;
method public boolean setApplicationHidden(@NonNull android.content.ComponentName, String, boolean);
method @WorkerThread public void setApplicationRestrictions(@Nullable android.content.ComponentName, String, android.os.Bundle);
method @Deprecated public void setApplicationRestrictionsManagingPackage(@NonNull android.content.ComponentName, @Nullable String) throws android.content.pm.PackageManager.NameNotFoundException;
@@ -6720,6 +6722,7 @@ package android.app.admin {
method public void setBluetoothContactSharingDisabled(@NonNull android.content.ComponentName, boolean);
method public void setCameraDisabled(@NonNull android.content.ComponentName, boolean);
method @Deprecated public void setCertInstallerPackage(@NonNull android.content.ComponentName, @Nullable String) throws java.lang.SecurityException;
+ method public void setCrossProfileCalendarPackages(@NonNull android.content.ComponentName, @Nullable java.util.Set<java.lang.String>);
method public void setCrossProfileCallerIdDisabled(@NonNull android.content.ComponentName, boolean);
method public void setCrossProfileContactsSearchDisabled(@NonNull android.content.ComponentName, boolean);
method public void setDelegatedScopes(@NonNull android.content.ComponentName, @NonNull String, @NonNull java.util.List<java.lang.String>);
@@ -6787,7 +6790,7 @@ package android.app.admin {
method public void uninstallCaCert(@Nullable android.content.ComponentName, byte[]);
method public boolean updateOverrideApn(@NonNull android.content.ComponentName, int, @NonNull android.telephony.data.ApnSetting);
method public void wipeData(int);
- method public void wipeData(int, CharSequence);
+ method public void wipeData(int, @NonNull CharSequence);
field public static final String ACTION_ADD_DEVICE_ADMIN = "android.app.action.ADD_DEVICE_ADMIN";
field public static final String ACTION_ADMIN_POLICY_COMPLIANCE = "android.app.action.ADMIN_POLICY_COMPLIANCE";
field public static final String ACTION_APPLICATION_DELEGATION_SCOPES_CHANGED = "android.app.action.APPLICATION_DELEGATION_SCOPES_CHANGED";
@@ -6931,6 +6934,7 @@ package android.app.admin {
field public static final int WIPE_EUICC = 4; // 0x4
field public static final int WIPE_EXTERNAL_STORAGE = 1; // 0x1
field public static final int WIPE_RESET_PROTECTION_DATA = 2; // 0x2
+ field public static final int WIPE_SILENTLY = 8; // 0x8
}
public abstract static class DevicePolicyManager.InstallUpdateCallback {
@@ -8610,6 +8614,13 @@ package android.bluetooth {
method @Deprecated @BinderThread public void onHealthChannelStateChange(android.bluetooth.BluetoothHealthAppConfiguration, android.bluetooth.BluetoothDevice, int, int, android.os.ParcelFileDescriptor, int);
}
+ public final class BluetoothHearingAid implements android.bluetooth.BluetoothProfile {
+ method public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices();
+ method public int getConnectionState(android.bluetooth.BluetoothDevice);
+ method public java.util.List<android.bluetooth.BluetoothDevice> getDevicesMatchingConnectionStates(int[]);
+ field public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.hearingaid.profile.action.CONNECTION_STATE_CHANGED";
+ }
+
public final class BluetoothHidDevice implements android.bluetooth.BluetoothProfile {
method public boolean connect(android.bluetooth.BluetoothDevice);
method public boolean disconnect(android.bluetooth.BluetoothDevice);
@@ -8705,6 +8716,7 @@ package android.bluetooth {
field public static final int GATT_SERVER = 8; // 0x8
field public static final int HEADSET = 1; // 0x1
field @Deprecated public static final int HEALTH = 3; // 0x3
+ field public static final int HEARING_AID = 21; // 0x15
field public static final int HID_DEVICE = 19; // 0x13
field public static final int SAP = 10; // 0xa
field public static final int STATE_CONNECTED = 2; // 0x2
@@ -10213,6 +10225,7 @@ package android.content {
field public static final String ACTION_MEDIA_REMOVED = "android.intent.action.MEDIA_REMOVED";
field public static final String ACTION_MEDIA_SCANNER_FINISHED = "android.intent.action.MEDIA_SCANNER_FINISHED";
field public static final String ACTION_MEDIA_SCANNER_SCAN_FILE = "android.intent.action.MEDIA_SCANNER_SCAN_FILE";
+ field public static final String ACTION_MEDIA_SCANNER_SCAN_VOLUME = "android.intent.action.MEDIA_SCANNER_SCAN_VOLUME";
field public static final String ACTION_MEDIA_SCANNER_STARTED = "android.intent.action.MEDIA_SCANNER_STARTED";
field public static final String ACTION_MEDIA_SHARED = "android.intent.action.MEDIA_SHARED";
field public static final String ACTION_MEDIA_UNMOUNTABLE = "android.intent.action.MEDIA_UNMOUNTABLE";
@@ -11210,6 +11223,7 @@ package android.content.pm {
public class LauncherApps {
method public java.util.List<android.content.pm.LauncherActivityInfo> getActivityList(String, android.os.UserHandle);
+ method @Nullable public android.content.pm.LauncherApps.AppUsageLimit getAppUsageLimit(String, android.os.UserHandle);
method public android.content.pm.ApplicationInfo getApplicationInfo(@NonNull String, int, @NonNull android.os.UserHandle) throws android.content.pm.PackageManager.NameNotFoundException;
method public android.content.pm.LauncherApps.PinItemRequest getPinItemRequest(android.content.Intent);
method public java.util.List<android.os.UserHandle> getProfiles();
@@ -11237,6 +11251,14 @@ package android.content.pm {
field public static final String EXTRA_PIN_ITEM_REQUEST = "android.content.pm.extra.PIN_ITEM_REQUEST";
}
+ public static final class LauncherApps.AppUsageLimit implements android.os.Parcelable {
+ method public int describeContents();
+ method public long getTotalUsageLimit();
+ method public long getUsageRemaining();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.content.pm.LauncherApps.AppUsageLimit> CREATOR;
+ }
+
public abstract static class LauncherApps.Callback {
ctor public LauncherApps.Callback();
method public abstract void onPackageAdded(String, android.os.UserHandle);
@@ -11433,6 +11455,7 @@ package android.content.pm {
method public void setAppIcon(@Nullable android.graphics.Bitmap);
method public void setAppLabel(@Nullable CharSequence);
method public void setAppPackageName(@Nullable String);
+ method public void setInstallAsApex();
method public void setInstallLocation(int);
method public void setInstallReason(int);
method public void setMultiPackage();
@@ -11675,6 +11698,7 @@ package android.content.pm {
field public static final String FEATURE_TELEPHONY_CDMA = "android.hardware.telephony.cdma";
field public static final String FEATURE_TELEPHONY_EUICC = "android.hardware.telephony.euicc";
field public static final String FEATURE_TELEPHONY_GSM = "android.hardware.telephony.gsm";
+ field public static final String FEATURE_TELEPHONY_IMS = "android.hardware.telephony.ims";
field public static final String FEATURE_TELEPHONY_MBMS = "android.hardware.telephony.mbms";
field @Deprecated public static final String FEATURE_TELEVISION = "android.hardware.type.television";
field public static final String FEATURE_TOUCHSCREEN = "android.hardware.touchscreen";
@@ -17078,6 +17102,7 @@ package android.hardware.camera2 {
field public static final android.hardware.camera2.CaptureResult.Key<float[]> LENS_POSE_TRANSLATION;
field @Deprecated public static final android.hardware.camera2.CaptureResult.Key<float[]> LENS_RADIAL_DISTORTION;
field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> LENS_STATE;
+ field public static final android.hardware.camera2.CaptureResult.Key<java.lang.String> LOGICAL_MULTI_CAMERA_ACTIVE_PHYSICAL_ID;
field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> NOISE_REDUCTION_MODE;
field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Float> REPROCESS_EFFECTIVE_EXPOSURE_FACTOR;
field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Byte> REQUEST_PIPELINE_DEPTH;
@@ -24639,6 +24664,7 @@ package android.media {
method @android.media.MediaDrm.SecurityLevel public int getSecurityLevel(@NonNull byte[]);
method public static boolean isCryptoSchemeSupported(@NonNull java.util.UUID);
method public static boolean isCryptoSchemeSupported(@NonNull java.util.UUID, @NonNull String);
+ method public static boolean isCryptoSchemeSupported(@NonNull java.util.UUID, @NonNull String, @android.media.MediaDrm.SecurityLevel int);
method @NonNull public byte[] openSession() throws android.media.NotProvisionedException, android.media.ResourceBusyException;
method @NonNull public byte[] openSession(@android.media.MediaDrm.SecurityLevel int) throws android.media.NotProvisionedException, android.media.ResourceBusyException;
method @Nullable public byte[] provideKeyResponse(@NonNull byte[], @NonNull byte[]) throws android.media.DeniedByServerException, android.media.NotProvisionedException;
@@ -25408,6 +25434,7 @@ package android.media {
method public void addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
method public Object attachAuxEffect(int);
method public boolean cancelCommand(@NonNull Object);
+ method public void clearDrmEventCallback();
method public Object clearNextDataSources();
method public void clearPendingCommands();
method public void close();
@@ -28469,6 +28496,7 @@ package android.net {
public class ConnectivityManager {
method public void addDefaultNetworkActiveListener(android.net.ConnectivityManager.OnNetworkActiveListener);
method public boolean bindProcessToNetwork(@Nullable android.net.Network);
+ method public android.net.SocketKeepalive createSocketKeepalive(@NonNull android.net.Network, @NonNull android.net.IpSecManager.UdpEncapsulationSocket, @NonNull java.net.InetAddress, @NonNull java.net.InetAddress, @NonNull java.util.concurrent.Executor, @NonNull android.net.SocketKeepalive.Callback);
method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) @Nullable public android.net.Network getActiveNetwork();
method @Deprecated @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public android.net.NetworkInfo getActiveNetworkInfo();
method @Deprecated @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public android.net.NetworkInfo[] getAllNetworkInfo();
@@ -28973,6 +29001,29 @@ package android.net {
ctor public SSLSessionCache(android.content.Context);
}
+ public abstract class SocketKeepalive implements java.lang.AutoCloseable {
+ method public final void close();
+ method public final void start(@IntRange(from=0xa, to=0xe10) int);
+ method public final void stop();
+ field public static final int ERROR_HARDWARE_ERROR = -31; // 0xffffffe1
+ field public static final int ERROR_HARDWARE_UNSUPPORTED = -30; // 0xffffffe2
+ field public static final int ERROR_INVALID_INTERVAL = -24; // 0xffffffe8
+ field public static final int ERROR_INVALID_IP_ADDRESS = -21; // 0xffffffeb
+ field public static final int ERROR_INVALID_LENGTH = -23; // 0xffffffe9
+ field public static final int ERROR_INVALID_NETWORK = -20; // 0xffffffec
+ field public static final int ERROR_INVALID_PORT = -22; // 0xffffffea
+ field public static final int ERROR_INVALID_SOCKET = -25; // 0xffffffe7
+ field public static final int ERROR_SOCKET_NOT_IDLE = -26; // 0xffffffe6
+ }
+
+ public static class SocketKeepalive.Callback {
+ ctor public SocketKeepalive.Callback();
+ method public void onDataReceived();
+ method public void onError(int);
+ method public void onStarted();
+ method public void onStopped();
+ }
+
public class TrafficStats {
ctor public TrafficStats();
method public static void clearThreadStatsTag();
@@ -29183,6 +29234,7 @@ package android.net {
method public android.os.ParcelFileDescriptor establish();
method public android.net.VpnService.Builder setBlocking(boolean);
method public android.net.VpnService.Builder setConfigureIntent(android.app.PendingIntent);
+ method public android.net.VpnService.Builder setHttpProxy(android.net.ProxyInfo);
method public android.net.VpnService.Builder setMtu(int);
method public android.net.VpnService.Builder setSession(String);
method public android.net.VpnService.Builder setUnderlyingNetworks(android.net.Network[]);
@@ -35249,11 +35301,16 @@ package android.os {
public abstract class VibrationEffect implements android.os.Parcelable {
method public static android.os.VibrationEffect createOneShot(long, int);
+ method public static android.os.VibrationEffect createPrebaked(int);
method public static android.os.VibrationEffect createWaveform(long[], int);
method public static android.os.VibrationEffect createWaveform(long[], int[], int);
method public int describeContents();
field public static final android.os.Parcelable.Creator<android.os.VibrationEffect> CREATOR;
field public static final int DEFAULT_AMPLITUDE = -1; // 0xffffffff
+ field public static final int EFFECT_CLICK = 0; // 0x0
+ field public static final int EFFECT_DOUBLE_CLICK = 1; // 0x1
+ field public static final int EFFECT_HEAVY_CLICK = 5; // 0x5
+ field public static final int EFFECT_TICK = 2; // 0x2
}
public abstract class Vibrator {
@@ -38194,6 +38251,8 @@ package android.provider {
field public static final String MEDIA_SCANNER_VOLUME = "volume";
field public static final String META_DATA_STILL_IMAGE_CAMERA_PREWARM_SERVICE = "android.media.still_image_camera_preview_service";
field public static final String UNKNOWN_STRING = "<unknown>";
+ field public static final String VOLUME_EXTERNAL = "external";
+ field public static final String VOLUME_INTERNAL = "internal";
}
public static final class MediaStore.Audio {
@@ -38400,28 +38459,28 @@ package android.provider {
field public static final android.net.Uri INTERNAL_CONTENT_URI;
}
- public static class MediaStore.Images.Thumbnails implements android.provider.BaseColumns {
- ctor public MediaStore.Images.Thumbnails();
+ @Deprecated public static class MediaStore.Images.Thumbnails implements android.provider.BaseColumns {
+ ctor @Deprecated public MediaStore.Images.Thumbnails();
method @Deprecated public static void cancelThumbnailRequest(android.content.ContentResolver, long);
method @Deprecated public static void cancelThumbnailRequest(android.content.ContentResolver, long, long);
- method public static android.net.Uri getContentUri(String);
+ method @Deprecated public static android.net.Uri getContentUri(String);
method @Deprecated public static android.graphics.Bitmap getThumbnail(android.content.ContentResolver, long, int, android.graphics.BitmapFactory.Options);
method @Deprecated public static android.graphics.Bitmap getThumbnail(android.content.ContentResolver, long, long, int, android.graphics.BitmapFactory.Options);
- method public static final android.database.Cursor query(android.content.ContentResolver, android.net.Uri, String[]);
- method public static final android.database.Cursor queryMiniThumbnail(android.content.ContentResolver, long, int, String[]);
- method public static final android.database.Cursor queryMiniThumbnails(android.content.ContentResolver, android.net.Uri, int, String[]);
+ method @Deprecated public static final android.database.Cursor query(android.content.ContentResolver, android.net.Uri, String[]);
+ method @Deprecated public static final android.database.Cursor queryMiniThumbnail(android.content.ContentResolver, long, int, String[]);
+ method @Deprecated public static final android.database.Cursor queryMiniThumbnails(android.content.ContentResolver, android.net.Uri, int, String[]);
field @Deprecated public static final String DATA = "_data";
- field public static final String DEFAULT_SORT_ORDER = "image_id ASC";
- field public static final android.net.Uri EXTERNAL_CONTENT_URI;
- field public static final int FULL_SCREEN_KIND = 2; // 0x2
- field public static final String HEIGHT = "height";
- field public static final String IMAGE_ID = "image_id";
- field public static final android.net.Uri INTERNAL_CONTENT_URI;
- field public static final String KIND = "kind";
- field public static final int MICRO_KIND = 3; // 0x3
- field public static final int MINI_KIND = 1; // 0x1
- field public static final String THUMB_DATA = "thumb_data";
- field public static final String WIDTH = "width";
+ field @Deprecated public static final String DEFAULT_SORT_ORDER = "image_id ASC";
+ field @Deprecated public static final android.net.Uri EXTERNAL_CONTENT_URI;
+ field @Deprecated public static final int FULL_SCREEN_KIND = 2; // 0x2
+ field @Deprecated public static final String HEIGHT = "height";
+ field @Deprecated public static final String IMAGE_ID = "image_id";
+ field @Deprecated public static final android.net.Uri INTERNAL_CONTENT_URI;
+ field @Deprecated public static final String KIND = "kind";
+ field @Deprecated public static final int MICRO_KIND = 3; // 0x3
+ field @Deprecated public static final int MINI_KIND = 1; // 0x1
+ field @Deprecated public static final String THUMB_DATA = "thumb_data";
+ field @Deprecated public static final String WIDTH = "width";
}
public static interface MediaStore.MediaColumns extends android.provider.BaseColumns {
@@ -38473,24 +38532,24 @@ package android.provider {
field public static final android.net.Uri INTERNAL_CONTENT_URI;
}
- public static class MediaStore.Video.Thumbnails implements android.provider.BaseColumns {
- ctor public MediaStore.Video.Thumbnails();
+ @Deprecated public static class MediaStore.Video.Thumbnails implements android.provider.BaseColumns {
+ ctor @Deprecated public MediaStore.Video.Thumbnails();
method @Deprecated public static void cancelThumbnailRequest(android.content.ContentResolver, long);
method @Deprecated public static void cancelThumbnailRequest(android.content.ContentResolver, long, long);
- method public static android.net.Uri getContentUri(String);
+ method @Deprecated public static android.net.Uri getContentUri(String);
method @Deprecated public static android.graphics.Bitmap getThumbnail(android.content.ContentResolver, long, int, android.graphics.BitmapFactory.Options);
method @Deprecated public static android.graphics.Bitmap getThumbnail(android.content.ContentResolver, long, long, int, android.graphics.BitmapFactory.Options);
field @Deprecated public static final String DATA = "_data";
- field public static final String DEFAULT_SORT_ORDER = "video_id ASC";
- field public static final android.net.Uri EXTERNAL_CONTENT_URI;
- field public static final int FULL_SCREEN_KIND = 2; // 0x2
- field public static final String HEIGHT = "height";
- field public static final android.net.Uri INTERNAL_CONTENT_URI;
- field public static final String KIND = "kind";
- field public static final int MICRO_KIND = 3; // 0x3
- field public static final int MINI_KIND = 1; // 0x1
- field public static final String VIDEO_ID = "video_id";
- field public static final String WIDTH = "width";
+ field @Deprecated public static final String DEFAULT_SORT_ORDER = "video_id ASC";
+ field @Deprecated public static final android.net.Uri EXTERNAL_CONTENT_URI;
+ field @Deprecated public static final int FULL_SCREEN_KIND = 2; // 0x2
+ field @Deprecated public static final String HEIGHT = "height";
+ field @Deprecated public static final android.net.Uri INTERNAL_CONTENT_URI;
+ field @Deprecated public static final String KIND = "kind";
+ field @Deprecated public static final int MICRO_KIND = 3; // 0x3
+ field @Deprecated public static final int MINI_KIND = 1; // 0x1
+ field @Deprecated public static final String VIDEO_ID = "video_id";
+ field @Deprecated public static final String WIDTH = "width";
}
public static interface MediaStore.Video.VideoColumns extends android.provider.MediaStore.MediaColumns {
@@ -39071,6 +39130,7 @@ package android.provider {
field public static final String CONTENT_ID = "cid";
field public static final String CONTENT_LOCATION = "cl";
field public static final String CONTENT_TYPE = "ct";
+ field public static final android.net.Uri CONTENT_URI;
field public static final String CT_START = "ctt_s";
field public static final String CT_TYPE = "ctt_t";
field public static final String FILENAME = "fn";
@@ -41332,6 +41392,22 @@ package android.service.media {
package android.service.notification {
+ public final class Adjustment implements android.os.Parcelable {
+ ctor public Adjustment(String, String, android.os.Bundle, CharSequence, int);
+ method public int describeContents();
+ method public CharSequence getExplanation();
+ method public String getKey();
+ method public String getPackage();
+ method public android.os.Bundle getSignals();
+ method public int getUser();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.service.notification.Adjustment> CREATOR;
+ field public static final String KEY_IMPORTANCE = "key_importance";
+ field public static final String KEY_SMART_ACTIONS = "key_smart_actions";
+ field public static final String KEY_SMART_REPLIES = "key_smart_replies";
+ field public static final String KEY_USER_SENTIMENT = "key_user_sentiment";
+ }
+
public final class Condition implements android.os.Parcelable {
ctor public Condition(android.net.Uri, String, int);
ctor public Condition(android.net.Uri, String, String, String, int, int, int);
@@ -41378,6 +41454,24 @@ package android.service.notification {
field @Deprecated public static final String SERVICE_INTERFACE = "android.service.notification.ConditionProviderService";
}
+ public abstract class NotificationAssistantService extends android.service.notification.NotificationListenerService {
+ ctor public NotificationAssistantService();
+ method public final void adjustNotification(android.service.notification.Adjustment);
+ method public final void adjustNotifications(java.util.List<android.service.notification.Adjustment>);
+ method public void onActionInvoked(@NonNull String, @NonNull android.app.Notification.Action, int);
+ method public final android.os.IBinder onBind(android.content.Intent);
+ method public void onNotificationDirectReplied(@NonNull String);
+ method public android.service.notification.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification);
+ method public android.service.notification.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification, android.app.NotificationChannel);
+ method public void onNotificationExpansionChanged(@NonNull String, boolean, boolean);
+ method public void onNotificationRemoved(android.service.notification.StatusBarNotification, android.service.notification.NotificationListenerService.RankingMap, android.service.notification.NotificationStats, int);
+ method public void onNotificationsSeen(java.util.List<java.lang.String>);
+ method public void onSuggestedReplySent(@NonNull String, @NonNull CharSequence, int);
+ field public static final String SERVICE_INTERFACE = "android.service.notification.NotificationAssistantService";
+ field public static final int SOURCE_FROM_APP = 0; // 0x0
+ field public static final int SOURCE_FROM_ASSISTANT = 1; // 0x1
+ }
+
public abstract class NotificationListenerService extends android.app.Service {
ctor public NotificationListenerService();
method public final void cancelAllNotifications();
@@ -41458,6 +41552,8 @@ package android.service.notification {
method public long getLastAudiblyAlertedMillis();
method public String getOverrideGroupKey();
method public int getRank();
+ method public java.util.List<android.app.Notification.Action> getSmartActions();
+ method public java.util.List<java.lang.CharSequence> getSmartReplies();
method public int getSuppressedVisualEffects();
method public int getUserSentiment();
method public boolean isAmbient();
@@ -41476,6 +41572,37 @@ package android.service.notification {
field public static final android.os.Parcelable.Creator<android.service.notification.NotificationListenerService.RankingMap> CREATOR;
}
+ public final class NotificationStats implements android.os.Parcelable {
+ ctor public NotificationStats();
+ method public int describeContents();
+ method public int getDismissalSentiment();
+ method public int getDismissalSurface();
+ method public boolean hasDirectReplied();
+ method public boolean hasExpanded();
+ method public boolean hasInteracted();
+ method public boolean hasSeen();
+ method public boolean hasSnoozed();
+ method public boolean hasViewedSettings();
+ method public void setDirectReplied();
+ method public void setDismissalSentiment(int);
+ method public void setDismissalSurface(int);
+ method public void setExpanded();
+ method public void setSeen();
+ method public void setSnoozed();
+ method public void setViewedSettings();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.service.notification.NotificationStats> CREATOR;
+ field public static final int DISMISSAL_AOD = 2; // 0x2
+ field public static final int DISMISSAL_NOT_DISMISSED = -1; // 0xffffffff
+ field public static final int DISMISSAL_OTHER = 0; // 0x0
+ field public static final int DISMISSAL_PEEK = 1; // 0x1
+ field public static final int DISMISSAL_SHADE = 3; // 0x3
+ field public static final int DISMISS_SENTIMENT_NEGATIVE = 0; // 0x0
+ field public static final int DISMISS_SENTIMENT_NEUTRAL = 1; // 0x1
+ field public static final int DISMISS_SENTIMENT_POSITIVE = 2; // 0x2
+ field public static final int DISMISS_SENTIMENT_UNKNOWN = -1000; // 0xfffffc18
+ }
+
public class StatusBarNotification implements android.os.Parcelable {
ctor @Deprecated public StatusBarNotification(String, String, int, String, int, int, int, android.app.Notification, android.os.UserHandle, long);
ctor public StatusBarNotification(android.os.Parcel);
@@ -41670,6 +41797,7 @@ package android.service.voice {
public class VoiceInteractionService extends android.app.Service {
ctor public VoiceInteractionService();
+ method public final void clearTranscription(boolean);
method public final android.service.voice.AlwaysOnHotwordDetector createAlwaysOnHotwordDetector(String, java.util.Locale, android.service.voice.AlwaysOnHotwordDetector.Callback);
method public int getDisabledShowContext();
method public static boolean isActiveService(android.content.Context, android.content.ComponentName);
@@ -41679,9 +41807,15 @@ package android.service.voice {
method public void onReady();
method public void onShutdown();
method public void setDisabledShowContext(int);
+ method public final void setTranscription(@NonNull String);
+ method public final void setVoiceState(int);
method public void showSession(android.os.Bundle, int);
field public static final String SERVICE_INTERFACE = "android.service.voice.VoiceInteractionService";
field public static final String SERVICE_META_DATA = "android.voice_interaction";
+ field public static final int VOICE_STATE_CONDITIONAL_LISTENING = 1; // 0x1
+ field public static final int VOICE_STATE_FULFILLING = 3; // 0x3
+ field public static final int VOICE_STATE_LISTENING = 2; // 0x2
+ field public static final int VOICE_STATE_NONE = 0; // 0x0
}
public class VoiceInteractionSession implements android.content.ComponentCallbacks2 android.view.KeyEvent.Callback {
@@ -43042,6 +43176,15 @@ package android.telecom {
method public abstract void onScreenCall(@NonNull android.telecom.Call.Details);
method public final void provideCallIdentification(@NonNull android.telecom.Call.Details, @NonNull android.telecom.CallIdentification);
method public final void respondToCall(@NonNull android.telecom.Call.Details, @NonNull android.telecom.CallScreeningService.CallResponse);
+ field public static final String ACTION_NUISANCE_CALL_STATUS_CHANGED = "android.telecom.action.NUISANCE_CALL_STATUS_CHANGED";
+ field public static final int CALL_DURATION_LONG = 4; // 0x4
+ field public static final int CALL_DURATION_MEDIUM = 3; // 0x3
+ field public static final int CALL_DURATION_SHORT = 2; // 0x2
+ field public static final int CALL_DURATION_VERY_SHORT = 1; // 0x1
+ field public static final String EXTRA_CALL_DURATION = "android.telecom.extra.CALL_DURATION";
+ field public static final String EXTRA_CALL_HANDLE = "android.telecom.extra.CALL_HANDLE";
+ field public static final String EXTRA_CALL_TYPE = "android.telecom.extra.CALL_TYPE";
+ field public static final String EXTRA_IS_NUISANCE = "android.telecom.extra.IS_NUISANCE";
field public static final String SERVICE_INTERFACE = "android.telecom.CallScreeningService";
}
@@ -43647,6 +43790,7 @@ package android.telecom {
method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public boolean isVoiceMailNumber(android.telecom.PhoneAccountHandle, String);
method @RequiresPermission(anyOf={android.Manifest.permission.CALL_PHONE, android.Manifest.permission.MANAGE_OWN_CALLS}) public void placeCall(android.net.Uri, android.os.Bundle);
method public void registerPhoneAccount(android.telecom.PhoneAccount);
+ method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public void reportNuisanceCallStatus(@NonNull android.net.Uri, boolean);
method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public void showInCallScreen(boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void silenceRinger();
method public void unregisterPhoneAccount(android.telecom.PhoneAccountHandle);
@@ -43894,7 +44038,9 @@ package android.telephony {
field public static final String KEY_CARRIER_NAME_OVERRIDE_BOOL = "carrier_name_override_bool";
field public static final String KEY_CARRIER_NAME_STRING = "carrier_name_string";
field public static final String KEY_CARRIER_SETTINGS_ENABLE_BOOL = "carrier_settings_enable_bool";
+ field public static final String KEY_CARRIER_SUPPORTS_SS_OVER_UT_BOOL = "carrier_supports_ss_over_ut_bool";
field public static final String KEY_CARRIER_USE_IMS_FIRST_FOR_EMERGENCY_BOOL = "carrier_use_ims_first_for_emergency_bool";
+ field public static final String KEY_CARRIER_UT_PROVISIONING_REQUIRED_BOOL = "carrier_ut_provisioning_required_bool";
field public static final String KEY_CARRIER_VOLTE_AVAILABLE_BOOL = "carrier_volte_available_bool";
field public static final String KEY_CARRIER_VOLTE_PROVISIONED_BOOL = "carrier_volte_provisioned_bool";
field public static final String KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL = "carrier_volte_provisioning_required_bool";
@@ -44658,6 +44804,7 @@ package android.telephony {
method public String getNumber();
method public int getSimSlotIndex();
method public int getSubscriptionId();
+ method public int getSubscriptionType();
method public boolean isEmbedded();
method public boolean isOpportunistic();
method public void writeToParcel(android.os.Parcel, int);
@@ -44708,6 +44855,8 @@ package android.telephony {
field public static final String EXTRA_SUBSCRIPTION_INDEX = "android.telephony.extra.SUBSCRIPTION_INDEX";
field public static final int INVALID_SIM_SLOT_INDEX = -1; // 0xffffffff
field public static final int INVALID_SUBSCRIPTION_ID = -1; // 0xffffffff
+ field public static final int SUBSCRIPTION_TYPE_LOCAL_SIM = 0; // 0x0
+ field public static final int SUBSCRIPTION_TYPE_REMOTE_SIM = 1; // 0x1
}
public static class SubscriptionManager.OnOpportunisticSubscriptionsChangedListener {
@@ -52620,6 +52769,7 @@ package android.view.contentcapture {
public static final class ContentCaptureContext.Builder {
ctor public ContentCaptureContext.Builder();
method public android.view.contentcapture.ContentCaptureContext build();
+ method @NonNull public android.view.contentcapture.ContentCaptureContext.Builder setAction(@NonNull String);
method @NonNull public android.view.contentcapture.ContentCaptureContext.Builder setExtras(@NonNull android.os.Bundle);
method @NonNull public android.view.contentcapture.ContentCaptureContext.Builder setUri(@NonNull android.net.Uri);
}
@@ -52636,12 +52786,12 @@ package android.view.contentcapture {
method @NonNull public final android.view.contentcapture.ContentCaptureSession createContentCaptureSession(@NonNull android.view.contentcapture.ContentCaptureContext);
method public final void destroy();
method public final android.view.contentcapture.ContentCaptureSessionId getContentCaptureSessionId();
- method @NonNull public android.view.autofill.AutofillId newAutofillId(@NonNull android.view.autofill.AutofillId, int);
- method @NonNull public final android.view.ViewStructure newVirtualViewStructure(@NonNull android.view.autofill.AutofillId, int);
+ method @NonNull public android.view.autofill.AutofillId newAutofillId(@NonNull android.view.autofill.AutofillId, long);
+ method @NonNull public final android.view.ViewStructure newVirtualViewStructure(@NonNull android.view.autofill.AutofillId, long);
method public final void notifyViewAppeared(@NonNull android.view.ViewStructure);
method public final void notifyViewDisappeared(@NonNull android.view.autofill.AutofillId);
method public final void notifyViewTextChanged(@NonNull android.view.autofill.AutofillId, @Nullable CharSequence, int);
- method public final void notifyViewsDisappeared(@NonNull android.view.autofill.AutofillId, @NonNull int[]);
+ method public final void notifyViewsDisappeared(@NonNull android.view.autofill.AutofillId, @NonNull long[]);
}
public final class ContentCaptureSessionId implements android.os.Parcelable {
@@ -52980,8 +53130,8 @@ package android.view.inputmethod {
method @Deprecated public boolean isWatchingCursor(android.view.View);
method public void restartInput(android.view.View);
method public void sendAppPrivateCommand(android.view.View, String, android.os.Bundle);
- method public void setAdditionalInputMethodSubtypes(String, android.view.inputmethod.InputMethodSubtype[]);
- method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean setCurrentInputMethodSubtype(android.view.inputmethod.InputMethodSubtype);
+ method @Deprecated public void setAdditionalInputMethodSubtypes(String, android.view.inputmethod.InputMethodSubtype[]);
+ method @Deprecated @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean setCurrentInputMethodSubtype(android.view.inputmethod.InputMethodSubtype);
method @Deprecated public void setInputMethod(android.os.IBinder, String);
method @Deprecated public void setInputMethodAndSubtype(@NonNull android.os.IBinder, String, android.view.inputmethod.InputMethodSubtype);
method @Deprecated public boolean shouldOfferSwitchingToNextInputMethod(android.os.IBinder);
@@ -53078,6 +53228,16 @@ package android.view.inspector {
ctor public InspectionCompanion.UninitializedPropertyMapException();
}
+ public final class IntEnumMapping {
+ method @Nullable public String get(int);
+ }
+
+ public static final class IntEnumMapping.Builder {
+ ctor public IntEnumMapping.Builder();
+ method @NonNull public android.view.inspector.IntEnumMapping.Builder addValue(@NonNull String, int);
+ method @NonNull public android.view.inspector.IntEnumMapping build();
+ }
+
public final class IntFlagMapping {
method @NonNull public java.util.Set<java.lang.String> get(int);
}
@@ -53098,7 +53258,7 @@ package android.view.inspector {
method public int mapFloat(@NonNull String, @AttrRes int);
method public int mapGravity(@NonNull String, @AttrRes int);
method public int mapInt(@NonNull String, @AttrRes int);
- method public int mapIntEnum(@NonNull String, @AttrRes int, @NonNull android.util.SparseArray<java.lang.String>);
+ method public int mapIntEnum(@NonNull String, @AttrRes int, @NonNull android.view.inspector.IntEnumMapping);
method public int mapIntFlag(@NonNull String, @AttrRes int, @NonNull android.view.inspector.IntFlagMapping);
method public int mapLong(@NonNull String, @AttrRes int);
method public int mapObject(@NonNull String, @AttrRes int);
@@ -53335,6 +53495,7 @@ package android.view.textclassifier {
public final class TextClassificationManager {
method @NonNull public android.view.textclassifier.TextClassifier createTextClassificationSession(@NonNull android.view.textclassifier.TextClassificationContext);
+ method @NonNull public android.view.textclassifier.TextClassifier getLocalTextClassifier();
method @NonNull public android.view.textclassifier.TextClassifier getTextClassifier();
method public void setTextClassificationSessionFactory(@Nullable android.view.textclassifier.TextClassificationSessionFactory);
method public void setTextClassifier(@Nullable android.view.textclassifier.TextClassifier);
@@ -53412,7 +53573,7 @@ package android.view.textclassifier {
public final class TextClassifierEvent implements android.os.Parcelable {
method public int describeContents();
method @NonNull public int[] getActionIndices();
- method @Nullable public String getEntityType();
+ method @NonNull public String[] getEntityTypes();
method public int getEventCategory();
method @Nullable public android.view.textclassifier.TextClassificationContext getEventContext();
method public int getEventIndex();
@@ -53425,6 +53586,7 @@ package android.view.textclassifier {
method public int getRelativeWordEndIndex();
method public int getRelativeWordStartIndex();
method @Nullable public String getResultId();
+ method public float getScore();
method public void writeToParcel(android.os.Parcel, int);
field public static final int CATEGORY_CONVERSATION_ACTIONS = 3; // 0x3
field public static final int CATEGORY_LANGUAGE_DETECTION = 4; // 0x4
@@ -53459,7 +53621,7 @@ package android.view.textclassifier {
ctor public TextClassifierEvent.Builder(int, int);
method @NonNull public android.view.textclassifier.TextClassifierEvent build();
method @NonNull public android.view.textclassifier.TextClassifierEvent.Builder setActionIndices(@NonNull int...);
- method @NonNull public android.view.textclassifier.TextClassifierEvent.Builder setEntityType(@Nullable String);
+ method @NonNull public android.view.textclassifier.TextClassifierEvent.Builder setEntityTypes(@NonNull java.lang.String...);
method @NonNull public android.view.textclassifier.TextClassifierEvent.Builder setEventContext(@Nullable android.view.textclassifier.TextClassificationContext);
method @NonNull public android.view.textclassifier.TextClassifierEvent.Builder setEventIndex(int);
method @NonNull public android.view.textclassifier.TextClassifierEvent.Builder setEventTime(long);
@@ -53470,6 +53632,7 @@ package android.view.textclassifier {
method @NonNull public android.view.textclassifier.TextClassifierEvent.Builder setRelativeWordEndIndex(int);
method @NonNull public android.view.textclassifier.TextClassifierEvent.Builder setRelativeWordStartIndex(int);
method @NonNull public android.view.textclassifier.TextClassifierEvent.Builder setResultId(@Nullable String);
+ method @NonNull public android.view.textclassifier.TextClassifierEvent.Builder setScore(float);
}
public final class TextLanguage implements android.os.Parcelable {
@@ -56846,7 +57009,7 @@ package android.widget {
method public boolean isCursorVisible();
method public boolean isElegantTextHeight();
method public boolean isFallbackLineSpacing();
- method public final boolean isHorizontallyScrolling();
+ method public final boolean isHorizontallyScrollable();
method public boolean isInputMethodTarget();
method public boolean isSingleLine();
method public boolean isSuggestionsEnabled();
diff --git a/api/system-current.txt b/api/system-current.txt
index 15d6ab7b63fc..fe2b4d9e262f 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -244,6 +244,7 @@ package android {
}
public static final class R.style {
+ field public static final int Theme_DeviceDefault_DocumentsUI = 16974562; // 0x10302e2
field public static final int Theme_Leanback_FormWizard = 16974544; // 0x10302d0
}
@@ -1052,6 +1053,7 @@ package android.app.role {
method @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public void removeRoleHolderAsUser(@NonNull String, @NonNull String, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull android.app.role.RoleManagerCallback);
method @RequiresPermission("com.android.permissioncontroller.permission.MANAGE_ROLES_FROM_CONTROLLER") public boolean removeRoleHolderFromController(@NonNull String, @NonNull String);
method @RequiresPermission("com.android.permissioncontroller.permission.MANAGE_ROLES_FROM_CONTROLLER") public void setRoleNamesFromController(@NonNull java.util.List<java.lang.String>);
+ field public static final String ROLE_ASSISTANT = "android.app.role.ASSISTANT";
}
public interface RoleManagerCallback {
@@ -1112,6 +1114,7 @@ package android.app.usage {
method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public int getAppStandbyBucket(String);
method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public java.util.Map<java.lang.String,java.lang.Integer> getAppStandbyBuckets();
method public int getUsageSource();
+ method @RequiresPermission(allOf={android.Manifest.permission.SUSPEND_APPS, android.Manifest.permission.OBSERVE_APP_USAGE}) public void registerAppUsageLimitObserver(int, @NonNull String[], long, @NonNull java.util.concurrent.TimeUnit, @NonNull android.app.PendingIntent);
method @RequiresPermission(android.Manifest.permission.OBSERVE_APP_USAGE) public void registerAppUsageObserver(int, @NonNull String[], long, @NonNull java.util.concurrent.TimeUnit, @NonNull android.app.PendingIntent);
method @RequiresPermission(android.Manifest.permission.OBSERVE_APP_USAGE) public void registerUsageSessionObserver(int, @NonNull String[], long, @NonNull java.util.concurrent.TimeUnit, long, @NonNull java.util.concurrent.TimeUnit, @NonNull android.app.PendingIntent, @Nullable android.app.PendingIntent);
method public void reportUsageStart(@NonNull android.app.Activity, @NonNull String);
@@ -1119,6 +1122,7 @@ package android.app.usage {
method public void reportUsageStop(@NonNull android.app.Activity, @NonNull String);
method @RequiresPermission(android.Manifest.permission.CHANGE_APP_IDLE_STATE) public void setAppStandbyBucket(String, int);
method @RequiresPermission(android.Manifest.permission.CHANGE_APP_IDLE_STATE) public void setAppStandbyBuckets(java.util.Map<java.lang.String,java.lang.Integer>);
+ method @RequiresPermission(allOf={android.Manifest.permission.SUSPEND_APPS, android.Manifest.permission.OBSERVE_APP_USAGE}) public void unregisterAppUsageLimitObserver(int);
method @RequiresPermission(android.Manifest.permission.OBSERVE_APP_USAGE) public void unregisterAppUsageObserver(int);
method @RequiresPermission(android.Manifest.permission.OBSERVE_APP_USAGE) public void unregisterUsageSessionObserver(int);
method @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST) public void whitelistAppTemporarily(String, long, android.os.UserHandle);
@@ -1147,7 +1151,7 @@ package android.bluetooth {
field public static final String ACTION_REQUEST_BLE_SCAN_ALWAYS_AVAILABLE = "android.bluetooth.adapter.action.REQUEST_BLE_SCAN_ALWAYS_AVAILABLE";
}
- public abstract class BluetoothAdapter.MetadataListener {
+ public abstract static class BluetoothAdapter.MetadataListener {
ctor public BluetoothAdapter.MetadataListener();
method public void onMetadataChanged(android.bluetooth.BluetoothDevice, int, String);
}
@@ -1155,14 +1159,18 @@ package android.bluetooth {
public final class BluetoothDevice implements android.os.Parcelable {
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean cancelBondProcess();
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public String getMetadata(int);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean getSilenceMode();
method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public boolean isConnected();
method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public boolean isEncrypted();
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean removeBond();
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setMetadata(int, String);
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setPhonebookAccessPermission(int);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setSilenceMode(boolean);
field public static final int ACCESS_ALLOWED = 1; // 0x1
field public static final int ACCESS_REJECTED = 2; // 0x2
field public static final int ACCESS_UNKNOWN = 0; // 0x0
+ field public static final String ACTION_SILENCE_MODE_CHANGED = "android.bluetooth.device.action.SILENCE_MODE_CHANGED";
+ field public static final String EXTRA_SILENCE_ENABLED = "android.bluetooth.device.extra.SILENCE_ENABLED";
field public static final int METADATA_COMPANION_APP = 4; // 0x4
field public static final int METADATA_ENHANCED_SETTINGS_UI_URI = 16; // 0x10
field public static final int METADATA_HARDWARE_VERSION = 3; // 0x3
@@ -1804,9 +1812,16 @@ package android.hardware.display {
}
public final class ColorDisplayManager {
+ method @RequiresPermission(android.Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS) public int getNightDisplayAutoMode();
method @RequiresPermission(android.Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS) public int getTransformCapabilities();
method @RequiresPermission(android.Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS) public boolean setAppSaturationLevel(@NonNull String, @IntRange(from=0, to=100) int);
+ method @RequiresPermission(android.Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS) public boolean setNightDisplayAutoMode(int);
+ method @RequiresPermission(android.Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS) public boolean setNightDisplayCustomEndTime(@NonNull java.time.LocalTime);
+ method @RequiresPermission(android.Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS) public boolean setNightDisplayCustomStartTime(@NonNull java.time.LocalTime);
method @RequiresPermission(android.Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS) public boolean setSaturationLevel(@IntRange(from=0, to=100) int);
+ field public static final int AUTO_MODE_CUSTOM_TIME = 1; // 0x1
+ field public static final int AUTO_MODE_DISABLED = 0; // 0x0
+ field public static final int AUTO_MODE_TWILIGHT = 2; // 0x2
field public static final int CAPABILITY_HARDWARE_ACCELERATION_GLOBAL = 2; // 0x2
field public static final int CAPABILITY_HARDWARE_ACCELERATION_PER_APP = 4; // 0x4
field public static final int CAPABILITY_NONE = 0; // 0x0
@@ -3394,6 +3409,15 @@ package android.media {
method @NonNull public android.media.TimedMetaData.Builder setTimedMetaData(long, @NonNull byte[]);
}
+ public abstract class VolumeProvider {
+ method public void setCallback(android.media.VolumeProvider.Callback);
+ }
+
+ public abstract static class VolumeProvider.Callback {
+ ctor public VolumeProvider.Callback();
+ method public abstract void onVolumeChanged(android.media.VolumeProvider);
+ }
+
}
package android.media.audiopolicy {
@@ -3569,6 +3593,10 @@ package android.media.session {
method public void onSetMediaButtonEventDelegate(@NonNull android.media.session.MediaSessionEngine.MediaButtonEventDelegate);
}
+ public static final class MediaSession.Token implements android.os.Parcelable {
+ method public android.media.session.ControllerLink getControllerLink();
+ }
+
public final class MediaSessionEngine implements java.lang.AutoCloseable {
ctor public MediaSessionEngine(@NonNull android.content.Context, @NonNull android.media.session.SessionLink, @NonNull android.media.session.SessionCallbackLink, @NonNull android.media.session.MediaSessionEngine.CallbackStub, int);
method public void close();
@@ -4008,6 +4036,7 @@ package android.net {
}
public class ConnectivityManager {
+ method @RequiresPermission("android.permission.PACKET_KEEPALIVE_OFFLOAD") public android.net.SocketKeepalive createNattKeepalive(@NonNull android.net.Network, @NonNull java.io.FileDescriptor, @NonNull java.net.InetAddress, @NonNull java.net.InetAddress, @NonNull java.util.concurrent.Executor, @NonNull android.net.SocketKeepalive.Callback);
method public boolean getAvoidBadWifi();
method @RequiresPermission(android.Manifest.permission.LOCAL_MAC_ADDRESS) public String getCaptivePortalServerUrl();
method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public boolean isTetheringSupported();
@@ -4028,6 +4057,10 @@ package android.net {
method public void onTetheringStarted();
}
+ public final class IpPrefix implements android.os.Parcelable {
+ ctor public IpPrefix(java.net.InetAddress, int);
+ }
+
public final class IpSecManager {
method @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS) public void applyTunnelModeTransform(@NonNull android.net.IpSecManager.IpSecTunnelInterface, int, @NonNull android.net.IpSecTransform) throws java.io.IOException;
method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS) public android.net.IpSecManager.IpSecTunnelInterface createIpSecTunnelInterface(@NonNull java.net.InetAddress, @NonNull java.net.InetAddress, @NonNull android.net.Network) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException;
@@ -4060,6 +4093,7 @@ package android.net {
}
public class LinkAddress implements android.os.Parcelable {
+ ctor public LinkAddress(java.net.InetAddress, int, int, int);
ctor public LinkAddress(java.net.InetAddress, int);
ctor public LinkAddress(String);
method public boolean isGlobalPreferred();
@@ -4070,9 +4104,12 @@ package android.net {
public final class LinkProperties implements android.os.Parcelable {
ctor public LinkProperties();
+ ctor public LinkProperties(android.net.LinkProperties);
method public boolean addDnsServer(java.net.InetAddress);
method public boolean addRoute(android.net.RouteInfo);
method public void clear();
+ method @Nullable public android.net.IpPrefix getNat64Prefix();
+ method public java.util.List<java.net.InetAddress> getPcscfServers();
method public String getTcpBufferSizes();
method public java.util.List<java.net.InetAddress> getValidatedPrivateDnsServers();
method public boolean hasGlobalIPv6Address();
@@ -4090,6 +4127,8 @@ package android.net {
method public void setInterfaceName(String);
method public void setLinkAddresses(java.util.Collection<android.net.LinkAddress>);
method public void setMtu(int);
+ method public void setNat64Prefix(android.net.IpPrefix);
+ method public void setPcscfServers(java.util.Collection<java.net.InetAddress>);
method public void setPrivateDnsServerName(@Nullable String);
method public void setTcpBufferSizes(String);
method public void setUsePrivateDns(boolean);
@@ -4144,6 +4183,7 @@ package android.net {
}
public final class RouteInfo implements android.os.Parcelable {
+ ctor public RouteInfo(android.net.IpPrefix, java.net.InetAddress, String, int);
method public int getType();
field public static final int RTN_THROW = 9; // 0x9
field public static final int RTN_UNICAST = 1; // 0x1
@@ -4277,6 +4317,7 @@ package android.net.metrics {
}
public class IpConnectivityLog {
+ ctor public IpConnectivityLog();
method public boolean log(long, android.net.metrics.IpConnectivityLog.Event);
method public boolean log(String, android.net.metrics.IpConnectivityLog.Event);
method public boolean log(android.net.Network, int[], android.net.metrics.IpConnectivityLog.Event);
@@ -4325,6 +4366,20 @@ package android.net.metrics {
field public static final int NETWORK_VALIDATION_FAILED = 3; // 0x3
}
+ public final class RaEvent implements android.net.metrics.IpConnectivityLog.Event {
+ }
+
+ public static class RaEvent.Builder {
+ ctor public RaEvent.Builder();
+ method public android.net.metrics.RaEvent build();
+ method public android.net.metrics.RaEvent.Builder updateDnsslLifetime(long);
+ method public android.net.metrics.RaEvent.Builder updatePrefixPreferredLifetime(long);
+ method public android.net.metrics.RaEvent.Builder updatePrefixValidLifetime(long);
+ method public android.net.metrics.RaEvent.Builder updateRdnssLifetime(long);
+ method public android.net.metrics.RaEvent.Builder updateRouteInfoLifetime(long);
+ method public android.net.metrics.RaEvent.Builder updateRouterLifetime(long);
+ }
+
public final class ValidationProbeEvent implements android.net.metrics.IpConnectivityLog.Event {
method public static String getProbeName(int);
field public static final int DNS_FAILURE = 0; // 0x0
@@ -4596,6 +4651,7 @@ package android.net.wifi {
}
public class WifiManager {
+ method @RequiresPermission("android.permission.WIFI_UPDATE_USABILITY_STATS_SCORE") public void addWifiUsabilityStatsListener(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.WifiUsabilityStatsListener);
method @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD, "android.permission.NETWORK_STACK"}) public void connect(android.net.wifi.WifiConfiguration, android.net.wifi.WifiManager.ActionListener);
method @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD, "android.permission.NETWORK_STACK"}) public void connect(int, android.net.wifi.WifiManager.ActionListener);
method @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD, "android.permission.NETWORK_STACK"}) public void disable(int, android.net.wifi.WifiManager.ActionListener);
@@ -4611,6 +4667,7 @@ package android.net.wifi {
method @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public boolean isWifiApEnabled();
method public boolean isWifiScannerSupported();
method @RequiresPermission("android.permission.NETWORK_SETTINGS") public void registerNetworkRequestMatchCallback(@NonNull android.net.wifi.WifiManager.NetworkRequestMatchCallback, @Nullable android.os.Handler);
+ method @RequiresPermission("android.permission.WIFI_UPDATE_USABILITY_STATS_SCORE") public void removeWifiUsabilityStatsListener(@NonNull android.net.wifi.WifiManager.WifiUsabilityStatsListener);
method @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD, "android.permission.NETWORK_STACK"}) public void save(android.net.wifi.WifiConfiguration, android.net.wifi.WifiManager.ActionListener);
method @RequiresPermission("android.permission.WIFI_SET_DEVICE_MOBILITY_STATE") public void setDeviceMobilityState(int);
method @RequiresPermission(android.Manifest.permission.CHANGE_WIFI_STATE) public boolean setWifiApConfiguration(android.net.wifi.WifiConfiguration);
@@ -4620,6 +4677,7 @@ package android.net.wifi {
method @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD}) public void startSubscriptionProvisioning(android.net.wifi.hotspot2.OsuProvider, android.net.wifi.hotspot2.ProvisioningCallback, @Nullable android.os.Handler);
method @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD}) public void stopEasyConnectSession();
method @RequiresPermission("android.permission.NETWORK_SETTINGS") public void unregisterNetworkRequestMatchCallback(@NonNull android.net.wifi.WifiManager.NetworkRequestMatchCallback);
+ method @RequiresPermission("android.permission.WIFI_UPDATE_USABILITY_STATS_SCORE") public void updateWifiUsabilityScore(int, int, int);
field public static final int CHANGE_REASON_ADDED = 0; // 0x0
field public static final int CHANGE_REASON_CONFIG_CHANGE = 2; // 0x2
field public static final int CHANGE_REASON_REMOVED = 1; // 0x1
@@ -4668,6 +4726,10 @@ package android.net.wifi {
method public void select(@NonNull android.net.wifi.WifiConfiguration);
}
+ public static interface WifiManager.WifiUsabilityStatsListener {
+ method public void onStatsUpdated(int, boolean, android.net.wifi.WifiUsabilityStatsEntry);
+ }
+
public class WifiNetworkConnectionStatistics implements android.os.Parcelable {
ctor public WifiNetworkConnectionStatistics(int, int);
ctor public WifiNetworkConnectionStatistics();
@@ -4797,6 +4859,31 @@ package android.net.wifi {
field @Deprecated public int unchangedSampleSize;
}
+ public final class WifiUsabilityStatsEntry implements android.os.Parcelable {
+ method public int describeContents();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.net.wifi.WifiUsabilityStatsEntry> CREATOR;
+ field public final int linkSpeedMbps;
+ field public final int rssi;
+ field public final long timeStampMs;
+ field public final long totalBackgroundScanTimeMs;
+ field public final long totalBeaconRx;
+ field public final long totalCcaBusyFreqTimeMs;
+ field public final long totalHotspot2ScanTimeMs;
+ field public final long totalNanScanTimeMs;
+ field public final long totalPnoScanTimeMs;
+ field public final long totalRadioOnFreqTimeMs;
+ field public final long totalRadioOnTimeMs;
+ field public final long totalRadioRxTimeMs;
+ field public final long totalRadioTxTimeMs;
+ field public final long totalRoamScanTimeMs;
+ field public final long totalRxSuccess;
+ field public final long totalScanTimeMs;
+ field public final long totalTxBad;
+ field public final long totalTxRetries;
+ field public final long totalTxSuccess;
+ }
+
}
package android.net.wifi.aware {
@@ -5552,6 +5639,30 @@ package android.provider {
field public static final String NAMESPACE_NOTIFICATION_ASSISTANT = "notification_assistant";
}
+ public static interface DeviceConfig.ActivityManager {
+ field public static final String KEY_COMPACT_ACTION_1 = "compact_action_1";
+ field public static final String KEY_COMPACT_ACTION_2 = "compact_action_2";
+ field public static final String KEY_COMPACT_THROTTLE_1 = "compact_throttle_1";
+ field public static final String KEY_COMPACT_THROTTLE_2 = "compact_throttle_2";
+ field public static final String KEY_COMPACT_THROTTLE_3 = "compact_throttle_3";
+ field public static final String KEY_COMPACT_THROTTLE_4 = "compact_throttle_4";
+ field public static final String KEY_MAX_CACHED_PROCESSES = "max_cached_processes";
+ field public static final String KEY_USE_COMPACTION = "use_compaction";
+ field public static final String NAMESPACE = "activity_manager";
+ }
+
+ public static interface DeviceConfig.FsiBoot {
+ field public static final String NAMESPACE = "fsi_boot";
+ field public static final String OOB_ENABLED = "oob_enabled";
+ field public static final String OOB_WHITELIST = "oob_whitelist";
+ }
+
+ public static interface DeviceConfig.IntelligenceAttention {
+ field public static final String NAMESPACE = "intelligence_attention";
+ field public static final String PROPERTY_ATTENTION_CHECK_ENABLED = "attention_check_enabled";
+ field public static final String PROPERTY_ATTENTION_CHECK_SETTINGS = "attention_check_settings";
+ }
+
public static interface DeviceConfig.OnPropertyChangedListener {
method public void onPropertyChanged(String, String, String);
}
@@ -5743,6 +5854,7 @@ package android.provider {
field public static final String LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS = "lock_screen_allow_private_notifications";
field public static final String LOCK_SCREEN_SHOW_NOTIFICATIONS = "lock_screen_show_notifications";
field public static final String MANUAL_RINGER_TOGGLE_COUNT = "manual_ringer_toggle_count";
+ field public static final String THEME_CUSTOMIZATION_OVERLAY_PACKAGES = "theme_customization_overlay_packages";
field public static final String USER_SETUP_COMPLETE = "user_setup_complete";
field public static final int USER_SETUP_PERSONALIZATION_COMPLETE = 10; // 0xa
field public static final int USER_SETUP_PERSONALIZATION_NOT_STARTED = 0; // 0x0
@@ -6018,10 +6130,7 @@ package android.service.autofill.augmented {
method public int getTaskId();
}
- public final class FillResponse implements android.os.Parcelable {
- method public int describeContents();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator<android.service.autofill.augmented.FillResponse> CREATOR;
+ public final class FillResponse {
}
public static final class FillResponse.Builder {
@@ -6035,7 +6144,6 @@ package android.service.autofill.augmented {
ctor public FillWindow();
method public void destroy();
method public boolean update(@NonNull android.service.autofill.augmented.PresentationParams.Area, @NonNull android.view.View, long);
- field public static final long FLAG_METADATA_ADDRESS = 1L; // 0x1L
}
public abstract class PresentationParams {
@@ -6261,77 +6369,6 @@ package android.service.euicc {
package android.service.notification {
- public final class Adjustment implements android.os.Parcelable {
- ctor public Adjustment(String, String, android.os.Bundle, CharSequence, int);
- ctor protected Adjustment(android.os.Parcel);
- method public int describeContents();
- method public CharSequence getExplanation();
- method public String getKey();
- method public String getPackage();
- method public android.os.Bundle getSignals();
- method public int getUser();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator<android.service.notification.Adjustment> CREATOR;
- field public static final String KEY_IMPORTANCE = "key_importance";
- field public static final String KEY_PEOPLE = "key_people";
- field public static final String KEY_SMART_ACTIONS = "key_smart_actions";
- field public static final String KEY_SMART_REPLIES = "key_smart_replies";
- field public static final String KEY_SNOOZE_CRITERIA = "key_snooze_criteria";
- field public static final String KEY_USER_SENTIMENT = "key_user_sentiment";
- }
-
- public abstract class NotificationAssistantService extends android.service.notification.NotificationListenerService {
- ctor public NotificationAssistantService();
- method public final void adjustNotification(android.service.notification.Adjustment);
- method public final void adjustNotifications(java.util.List<android.service.notification.Adjustment>);
- method public void onActionInvoked(@NonNull String, @NonNull android.app.Notification.Action, int);
- method public final android.os.IBinder onBind(android.content.Intent);
- method public void onNotificationDirectReplied(@NonNull String);
- method public android.service.notification.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification);
- method public android.service.notification.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification, android.app.NotificationChannel);
- method public void onNotificationExpansionChanged(@NonNull String, boolean, boolean);
- method public void onNotificationRemoved(android.service.notification.StatusBarNotification, android.service.notification.NotificationListenerService.RankingMap, android.service.notification.NotificationStats, int);
- method public abstract void onNotificationSnoozedUntilContext(android.service.notification.StatusBarNotification, String);
- method public void onNotificationsSeen(java.util.List<java.lang.String>);
- method public void onSuggestedReplySent(@NonNull String, @NonNull CharSequence, int);
- method public final void unsnoozeNotification(String);
- field public static final String SERVICE_INTERFACE = "android.service.notification.NotificationAssistantService";
- field public static final int SOURCE_FROM_APP = 0; // 0x0
- field public static final int SOURCE_FROM_ASSISTANT = 1; // 0x1
- }
-
- public final class NotificationStats implements android.os.Parcelable {
- ctor public NotificationStats();
- ctor protected NotificationStats(android.os.Parcel);
- method public int describeContents();
- method public int getDismissalSentiment();
- method public int getDismissalSurface();
- method public boolean hasDirectReplied();
- method public boolean hasExpanded();
- method public boolean hasInteracted();
- method public boolean hasSeen();
- method public boolean hasSnoozed();
- method public boolean hasViewedSettings();
- method public void setDirectReplied();
- method public void setDismissalSentiment(int);
- method public void setDismissalSurface(int);
- method public void setExpanded();
- method public void setSeen();
- method public void setSnoozed();
- method public void setViewedSettings();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator<android.service.notification.NotificationStats> CREATOR;
- field public static final int DISMISSAL_AOD = 2; // 0x2
- field public static final int DISMISSAL_NOT_DISMISSED = -1; // 0xffffffff
- field public static final int DISMISSAL_OTHER = 0; // 0x0
- field public static final int DISMISSAL_PEEK = 1; // 0x1
- field public static final int DISMISSAL_SHADE = 3; // 0x3
- field public static final int DISMISS_SENTIMENT_NEGATIVE = 0; // 0x0
- field public static final int DISMISS_SENTIMENT_NEUTRAL = 1; // 0x1
- field public static final int DISMISS_SENTIMENT_POSITIVE = 2; // 0x2
- field public static final int DISMISS_SENTIMENT_UNKNOWN = -1000; // 0xfffffc18
- }
-
public final class SnoozeCriterion implements android.os.Parcelable {
ctor public SnoozeCriterion(String, CharSequence, CharSequence);
ctor protected SnoozeCriterion(android.os.Parcel);
@@ -7572,10 +7609,13 @@ package android.telephony {
public class SubscriptionManager {
method public java.util.List<android.telephony.SubscriptionInfo> getAvailableSubscriptionInfoList();
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getEnabledSubscriptionId(int);
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isSubscriptionEnabled(int);
method public void requestEmbeddedSubscriptionInfoListRefresh();
method public void requestEmbeddedSubscriptionInfoListRefresh(int);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDefaultDataSubId(int);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDefaultSmsSubId(int);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setSubscriptionEnabled(int, boolean);
field public static final android.net.Uri ADVANCED_CALLING_ENABLED_CONTENT_URI;
field public static final int PROFILE_CLASS_DEFAULT = -1; // 0xffffffff
field public static final int PROFILE_CLASS_OPERATIONAL = 2; // 0x2
@@ -8648,12 +8688,20 @@ package android.telephony.ims {
public class ProvisioningManager {
method public static android.telephony.ims.ProvisioningManager createForSubscriptionId(android.content.Context, int);
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getProvisioningIntValue(int);
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getProvisioningStringValue(int);
+ method @WorkerThread @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getProvisioningIntValue(int);
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean getProvisioningStatusForCapability(@android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability int, int);
+ method @WorkerThread @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getProvisioningStringValue(int);
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void registerProvisioningChangedCallback(java.util.concurrent.Executor, @NonNull android.telephony.ims.ProvisioningManager.Callback);
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int setProvisioningIntValue(int, int);
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int setProvisioningStringValue(int, String);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public int setProvisioningIntValue(int, int);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setProvisioningStatusForCapability(@android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability int, int, boolean);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public int setProvisioningStringValue(int, String);
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void unregisterProvisioningChangedCallback(@NonNull android.telephony.ims.ProvisioningManager.Callback);
+ field public static final int KEY_VOICE_OVER_WIFI_MODE_OVERRIDE = 27; // 0x1b
+ field public static final int KEY_VOICE_OVER_WIFI_ROAMING_ENABLED_OVERRIDE = 26; // 0x1a
+ field public static final int PROVISIONING_VALUE_DISABLED = 0; // 0x0
+ field public static final int PROVISIONING_VALUE_ENABLED = 1; // 0x1
+ field public static final String STRING_QUERY_RESULT_ERROR_GENERIC = "STRING_QUERY_RESULT_ERROR_GENERIC";
+ field public static final String STRING_QUERY_RESULT_ERROR_NOT_READY = "STRING_QUERY_RESULT_ERROR_NOT_READY";
}
public static class ProvisioningManager.Callback {
@@ -9080,9 +9128,22 @@ package android.view.accessibility {
}
+package android.view.autofill {
+
+ public final class AutofillManager {
+ method @NonNull public java.util.Set<android.content.ComponentName> getAugmentedAutofillDisabledActivities();
+ method @NonNull public java.util.Set<java.lang.String> getAugmentedAutofillDisabledPackages();
+ method public void setActivityAugmentedAutofillEnabled(@NonNull android.content.ComponentName, boolean);
+ method public void setAugmentedAutofillWhitelist(@Nullable java.util.List<java.lang.String>, @Nullable java.util.List<android.content.ComponentName>);
+ method public void setPackageAugmentedAutofillEnabled(@NonNull String, boolean);
+ }
+
+}
+
package android.view.contentcapture {
public final class ContentCaptureContext implements android.os.Parcelable {
+ method @Nullable public String getAction();
method @Nullable public android.content.ComponentName getActivityComponent();
method public int getDisplayId();
method @Nullable public android.os.Bundle getExtras();
diff --git a/api/test-current.txt b/api/test-current.txt
index 1f894c27d5ea..8e638fde6cc1 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -14,6 +14,10 @@ package android {
field public static final String WRITE_OBB = "android.permission.WRITE_OBB";
}
+ public static final class R.array {
+ field public static final int config_defaultRoleHolders = 17235974; // 0x1070006
+ }
+
}
package android.animation {
@@ -332,6 +336,7 @@ package android.app.role {
method @NonNull @RequiresPermission("android.permission.MANAGE_ROLE_HOLDERS") public java.util.List<java.lang.String> getRoleHolders(@NonNull String);
method @NonNull @RequiresPermission("android.permission.MANAGE_ROLE_HOLDERS") public java.util.List<java.lang.String> getRoleHoldersAsUser(@NonNull String, @NonNull android.os.UserHandle);
method @RequiresPermission("android.permission.MANAGE_ROLE_HOLDERS") public void removeRoleHolderAsUser(@NonNull String, @NonNull String, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull android.app.role.RoleManagerCallback);
+ field public static final String ROLE_ASSISTANT = "android.app.role.ASSISTANT";
}
public interface RoleManagerCallback {
@@ -802,11 +807,16 @@ package android.net {
field public static final String EXTRA_CAPTIVE_PORTAL_USER_AGENT = "android.net.extra.CAPTIVE_PORTAL_USER_AGENT";
}
+ public final class IpPrefix implements android.os.Parcelable {
+ ctor public IpPrefix(java.net.InetAddress, int);
+ }
+
public final class IpSecManager {
field public static final int INVALID_SECURITY_PARAMETER_INDEX = 0; // 0x0
}
public class LinkAddress implements android.os.Parcelable {
+ ctor public LinkAddress(java.net.InetAddress, int, int, int);
method public boolean isGlobalPreferred();
method public boolean isIPv4();
method public boolean isIPv6();
@@ -814,7 +824,10 @@ package android.net {
}
public final class LinkProperties implements android.os.Parcelable {
+ ctor public LinkProperties(android.net.LinkProperties);
method public boolean addDnsServer(java.net.InetAddress);
+ method @Nullable public android.net.IpPrefix getNat64Prefix();
+ method public java.util.List<java.net.InetAddress> getPcscfServers();
method public String getTcpBufferSizes();
method public java.util.List<java.net.InetAddress> getValidatedPrivateDnsServers();
method public boolean hasGlobalIPv6Address();
@@ -826,6 +839,8 @@ package android.net {
method public boolean isReachable(java.net.InetAddress);
method public boolean removeDnsServer(java.net.InetAddress);
method public boolean removeRoute(android.net.RouteInfo);
+ method public void setNat64Prefix(android.net.IpPrefix);
+ method public void setPcscfServers(java.util.Collection<java.net.InetAddress>);
method public void setPrivateDnsServerName(@Nullable String);
method public void setTcpBufferSizes(String);
method public void setUsePrivateDns(boolean);
@@ -843,6 +858,7 @@ package android.net {
}
public final class RouteInfo implements android.os.Parcelable {
+ ctor public RouteInfo(android.net.IpPrefix, java.net.InetAddress, String, int);
method public int getType();
field public static final int RTN_THROW = 9; // 0x9
field public static final int RTN_UNICAST = 1; // 0x1
@@ -929,6 +945,7 @@ package android.net.metrics {
}
public class IpConnectivityLog {
+ ctor public IpConnectivityLog();
method public boolean log(long, android.net.metrics.IpConnectivityLog.Event);
method public boolean log(String, android.net.metrics.IpConnectivityLog.Event);
method public boolean log(android.net.Network, int[], android.net.metrics.IpConnectivityLog.Event);
@@ -977,6 +994,20 @@ package android.net.metrics {
field public static final int NETWORK_VALIDATION_FAILED = 3; // 0x3
}
+ public final class RaEvent implements android.net.metrics.IpConnectivityLog.Event {
+ }
+
+ public static class RaEvent.Builder {
+ ctor public RaEvent.Builder();
+ method public android.net.metrics.RaEvent build();
+ method public android.net.metrics.RaEvent.Builder updateDnsslLifetime(long);
+ method public android.net.metrics.RaEvent.Builder updatePrefixPreferredLifetime(long);
+ method public android.net.metrics.RaEvent.Builder updatePrefixValidLifetime(long);
+ method public android.net.metrics.RaEvent.Builder updateRdnssLifetime(long);
+ method public android.net.metrics.RaEvent.Builder updateRouteInfoLifetime(long);
+ method public android.net.metrics.RaEvent.Builder updateRouterLifetime(long);
+ }
+
public final class ValidationProbeEvent implements android.net.metrics.IpConnectivityLog.Event {
method public static String getProbeName(int);
field public static final int DNS_FAILURE = 0; // 0x0
@@ -1273,15 +1304,11 @@ package android.os {
method @Nullable public static android.os.VibrationEffect get(android.net.Uri, android.content.Context);
method public abstract long getDuration();
method protected static int scale(int, float, int);
- field public static final int EFFECT_CLICK = 0; // 0x0
- field public static final int EFFECT_DOUBLE_CLICK = 1; // 0x1
- field public static final int EFFECT_HEAVY_CLICK = 5; // 0x5
field public static final int EFFECT_POP = 4; // 0x4
field public static final int EFFECT_STRENGTH_LIGHT = 0; // 0x0
field public static final int EFFECT_STRENGTH_MEDIUM = 1; // 0x1
field public static final int EFFECT_STRENGTH_STRONG = 2; // 0x2
field public static final int EFFECT_THUD = 3; // 0x3
- field public static final int EFFECT_TICK = 2; // 0x2
field public static final int[] RINGTONES;
}
@@ -1688,10 +1715,7 @@ package android.service.autofill.augmented {
method public int getTaskId();
}
- public final class FillResponse implements android.os.Parcelable {
- method public int describeContents();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator<android.service.autofill.augmented.FillResponse> CREATOR;
+ public final class FillResponse {
}
public static final class FillResponse.Builder {
@@ -1705,7 +1729,6 @@ package android.service.autofill.augmented {
ctor public FillWindow();
method public void destroy();
method public boolean update(@NonNull android.service.autofill.augmented.PresentationParams.Area, @NonNull android.view.View, long);
- field public static final long FLAG_METADATA_ADDRESS = 1L; // 0x1L
}
public abstract class PresentationParams {
@@ -1729,84 +1752,14 @@ package android.service.autofill.augmented {
package android.service.notification {
- public final class Adjustment implements android.os.Parcelable {
- ctor public Adjustment(String, String, android.os.Bundle, CharSequence, int);
- ctor protected Adjustment(android.os.Parcel);
- method public int describeContents();
- method public CharSequence getExplanation();
- method public String getKey();
- method public String getPackage();
- method public android.os.Bundle getSignals();
- method public int getUser();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator<android.service.notification.Adjustment> CREATOR;
- field public static final String KEY_IMPORTANCE = "key_importance";
- field public static final String KEY_PEOPLE = "key_people";
- field public static final String KEY_SMART_ACTIONS = "key_smart_actions";
- field public static final String KEY_SMART_REPLIES = "key_smart_replies";
- field public static final String KEY_SNOOZE_CRITERIA = "key_snooze_criteria";
- field public static final String KEY_USER_SENTIMENT = "key_user_sentiment";
- }
-
@Deprecated public abstract class ConditionProviderService extends android.app.Service {
method @Deprecated public boolean isBound();
}
- public abstract class NotificationAssistantService extends android.service.notification.NotificationListenerService {
- ctor public NotificationAssistantService();
- method public final void adjustNotification(android.service.notification.Adjustment);
- method public final void adjustNotifications(java.util.List<android.service.notification.Adjustment>);
- method public void onActionInvoked(@NonNull String, @NonNull android.app.Notification.Action, int);
- method public final android.os.IBinder onBind(android.content.Intent);
- method public void onNotificationDirectReplied(@NonNull String);
- method public android.service.notification.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification);
- method public android.service.notification.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification, android.app.NotificationChannel);
- method public void onNotificationExpansionChanged(@NonNull String, boolean, boolean);
- method public abstract void onNotificationSnoozedUntilContext(android.service.notification.StatusBarNotification, String);
- method public void onNotificationsSeen(java.util.List<java.lang.String>);
- method public void onSuggestedReplySent(@NonNull String, @NonNull CharSequence, int);
- method public final void unsnoozeNotification(String);
- field public static final String SERVICE_INTERFACE = "android.service.notification.NotificationAssistantService";
- field public static final int SOURCE_FROM_APP = 0; // 0x0
- field public static final int SOURCE_FROM_ASSISTANT = 1; // 0x1
- }
-
public abstract class NotificationListenerService extends android.app.Service {
method public void onNotificationRemoved(android.service.notification.StatusBarNotification, android.service.notification.NotificationListenerService.RankingMap, android.service.notification.NotificationStats, int);
}
- public final class NotificationStats implements android.os.Parcelable {
- ctor public NotificationStats();
- ctor protected NotificationStats(android.os.Parcel);
- method public int describeContents();
- method public int getDismissalSentiment();
- method public int getDismissalSurface();
- method public boolean hasDirectReplied();
- method public boolean hasExpanded();
- method public boolean hasInteracted();
- method public boolean hasSeen();
- method public boolean hasSnoozed();
- method public boolean hasViewedSettings();
- method public void setDirectReplied();
- method public void setDismissalSentiment(int);
- method public void setDismissalSurface(int);
- method public void setExpanded();
- method public void setSeen();
- method public void setSnoozed();
- method public void setViewedSettings();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator<android.service.notification.NotificationStats> CREATOR;
- field public static final int DISMISSAL_AOD = 2; // 0x2
- field public static final int DISMISSAL_NOT_DISMISSED = -1; // 0xffffffff
- field public static final int DISMISSAL_OTHER = 0; // 0x0
- field public static final int DISMISSAL_PEEK = 1; // 0x1
- field public static final int DISMISSAL_SHADE = 3; // 0x3
- field public static final int DISMISS_SENTIMENT_NEGATIVE = 0; // 0x0
- field public static final int DISMISS_SENTIMENT_NEUTRAL = 1; // 0x1
- field public static final int DISMISS_SENTIMENT_POSITIVE = 2; // 0x2
- field public static final int DISMISS_SENTIMENT_UNKNOWN = -1000; // 0xfffffc18
- }
-
public final class SnoozeCriterion implements android.os.Parcelable {
ctor public SnoozeCriterion(String, CharSequence, CharSequence);
ctor protected SnoozeCriterion(android.os.Parcel);
@@ -2341,6 +2294,12 @@ package android.view.autofill {
}
public final class AutofillManager {
+ method @NonNull public java.util.Set<android.content.ComponentName> getAugmentedAutofillDisabledActivities();
+ method @NonNull public java.util.Set<java.lang.String> getAugmentedAutofillDisabledPackages();
+ method public void setActivityAugmentedAutofillEnabled(@NonNull android.content.ComponentName, boolean);
+ method public void setAugmentedAutofillWhitelist(@Nullable java.util.List<java.lang.String>, @Nullable java.util.List<android.content.ComponentName>);
+ method public void setPackageAugmentedAutofillEnabled(@NonNull String, boolean);
+ field public static final int FLAG_SMART_SUGGESTION_SYSTEM = 1; // 0x1
field public static final int MAX_TEMP_AUGMENTED_SERVICE_DURATION_MS = 120000; // 0x1d4c0
}
diff --git a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
index 3defdc5467c8..062ba655640e 100644
--- a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
+++ b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
@@ -102,7 +102,17 @@ public class Bmgr {
String op = nextArg();
Slog.v(TAG, "Running " + op + " for user:" + userId);
- if (!isBmgrActive(userId)) {
+ if (mBmgr == null) {
+ System.err.println(BMGR_NOT_RUNNING_ERR);
+ return;
+ }
+
+ if ("activate".equals(op)) {
+ doActivateService(userId);
+ return;
+ }
+
+ if (!isBackupActive(userId)) {
return;
}
@@ -175,12 +185,7 @@ public class Bmgr {
showUsage();
}
- boolean isBmgrActive(@UserIdInt int userId) {
- if (mBmgr == null) {
- System.err.println(BMGR_NOT_RUNNING_ERR);
- return false;
- }
-
+ boolean isBackupActive(@UserIdInt int userId) {
try {
if (!mBmgr.isBackupServiceActive(userId)) {
System.err.println(BMGR_NOT_RUNNING_ERR);
@@ -845,6 +850,27 @@ public class Bmgr {
}
}
+ private void doActivateService(int userId) {
+ String arg = nextArg();
+ if (arg == null) {
+ showUsage();
+ return;
+ }
+
+ try {
+ boolean activate = Boolean.parseBoolean(arg);
+ mBmgr.setBackupServiceActive(userId, activate);
+ System.out.println(
+ "Backup service now "
+ + (activate ? "activated" : "deactivated")
+ + " for user "
+ + userId);
+ } catch (RemoteException e) {
+ System.err.println(e.toString());
+ System.err.println(BMGR_NOT_RUNNING_ERR);
+ }
+ }
+
private String nextArg() {
if (mNextArg >= mArgs.length) {
return null;
@@ -880,6 +906,7 @@ public class Bmgr {
System.err.println(" bmgr backupnow [--monitor|--monitor-verbose] --all|PACKAGE...");
System.err.println(" bmgr cancel backups");
System.err.println(" bmgr init TRANSPORT...");
+ System.err.println(" bmgr activate BOOL");
System.err.println("");
System.err.println("The '--user' option specifies the user on which the operation is run.");
System.err.println("It must be the first argument before the operation.");
@@ -946,6 +973,11 @@ public class Bmgr {
System.err.println("");
System.err.println("The 'init' command initializes the given transports, wiping all data");
System.err.println("from their backing data stores.");
+ System.err.println("");
+ System.err.println("The 'activate' command activates or deactivates the backup service.");
+ System.err.println("If the argument is 'true' it will be activated, otherwise it will be");
+ System.err.println("deactivated. When deactivated, the service will not be running and no");
+ System.err.println("operations can be performed until activation.");
}
private static class BackupMonitor extends IBackupManagerMonitor.Stub {
diff --git a/cmds/idmap2/idmap2/Create.cpp b/cmds/idmap2/idmap2/Create.cpp
index c455ac0f83af..0c581f3b1a98 100644
--- a/cmds/idmap2/idmap2/Create.cpp
+++ b/cmds/idmap2/idmap2/Create.cpp
@@ -39,6 +39,7 @@ using android::idmap2::PolicyBitmask;
using android::idmap2::PolicyFlags;
using android::idmap2::Result;
using android::idmap2::utils::kIdmapFilePermissionMask;
+using android::idmap2::utils::UidHasWriteAccessToPath;
bool Create(const std::vector<std::string>& args, std::ostream& out_error) {
std::string target_apk_path;
@@ -66,6 +67,13 @@ bool Create(const std::vector<std::string>& args, std::ostream& out_error) {
return false;
}
+ const uid_t uid = getuid();
+ if (!UidHasWriteAccessToPath(uid, idmap_path)) {
+ out_error << "error: uid " << uid << " does not have write access to " << idmap_path
+ << std::endl;
+ return false;
+ }
+
PolicyBitmask fulfilled_policies = 0;
if (auto result = PoliciesToBitmask(policies, out_error)) {
fulfilled_policies |= *result;
diff --git a/cmds/idmap2/idmap2d/Idmap2Service.cpp b/cmds/idmap2/idmap2d/Idmap2Service.cpp
index a3c752718ee2..f30ce9b08d6e 100644
--- a/cmds/idmap2/idmap2d/Idmap2Service.cpp
+++ b/cmds/idmap2/idmap2d/Idmap2Service.cpp
@@ -27,6 +27,7 @@
#include "android-base/macros.h"
#include "android-base/stringprintf.h"
+#include "binder/IPCThreadState.h"
#include "utils/String8.h"
#include "utils/Trace.h"
@@ -38,18 +39,19 @@
#include "idmap2d/Idmap2Service.h"
+using android::IPCThreadState;
using android::binder::Status;
using android::idmap2::BinaryStreamVisitor;
using android::idmap2::Idmap;
using android::idmap2::IdmapHeader;
using android::idmap2::PolicyBitmask;
using android::idmap2::Result;
+using android::idmap2::utils::kIdmapCacheDir;
using android::idmap2::utils::kIdmapFilePermissionMask;
+using android::idmap2::utils::UidHasWriteAccessToPath;
namespace {
-constexpr const char* kIdmapCacheDir = "/data/resource-cache";
-
Status ok() {
return Status::ok();
}
@@ -77,7 +79,13 @@ Status Idmap2Service::getIdmapPath(const std::string& overlay_apk_path,
Status Idmap2Service::removeIdmap(const std::string& overlay_apk_path,
int32_t user_id ATTRIBUTE_UNUSED, bool* _aidl_return) {
assert(_aidl_return);
+ const uid_t uid = IPCThreadState::self()->getCallingUid();
const std::string idmap_path = Idmap::CanonicalIdmapPathFor(kIdmapCacheDir, overlay_apk_path);
+ if (!UidHasWriteAccessToPath(uid, idmap_path)) {
+ *_aidl_return = false;
+ return error(base::StringPrintf("failed to unlink %s: calling uid %d lacks write access",
+ idmap_path.c_str(), uid));
+ }
if (unlink(idmap_path.c_str()) != 0) {
*_aidl_return = false;
return error("failed to unlink " + idmap_path + ": " + strerror(errno));
@@ -118,6 +126,13 @@ Status Idmap2Service::createIdmap(const std::string& target_apk_path,
const PolicyBitmask policy_bitmask = ConvertAidlArgToPolicyBitmask(fulfilled_policies);
+ const std::string idmap_path = Idmap::CanonicalIdmapPathFor(kIdmapCacheDir, overlay_apk_path);
+ const uid_t uid = IPCThreadState::self()->getCallingUid();
+ if (!UidHasWriteAccessToPath(uid, idmap_path)) {
+ return error(base::StringPrintf("will not write to %s: calling uid %d lacks write accesss",
+ idmap_path.c_str(), uid));
+ }
+
const std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
if (!target_apk) {
return error("failed to load apk " + target_apk_path);
@@ -137,7 +152,6 @@ Status Idmap2Service::createIdmap(const std::string& target_apk_path,
}
umask(kIdmapFilePermissionMask);
- const std::string idmap_path = Idmap::CanonicalIdmapPathFor(kIdmapCacheDir, overlay_apk_path);
std::ofstream fout(idmap_path);
if (fout.fail()) {
return error("failed to open idmap path " + idmap_path);
diff --git a/cmds/idmap2/include/idmap2/FileUtils.h b/cmds/idmap2/include/idmap2/FileUtils.h
index 5c41c49906cd..3f03236d5e1a 100644
--- a/cmds/idmap2/include/idmap2/FileUtils.h
+++ b/cmds/idmap2/include/idmap2/FileUtils.h
@@ -17,6 +17,8 @@
#ifndef IDMAP2_INCLUDE_IDMAP2_FILEUTILS_H_
#define IDMAP2_INCLUDE_IDMAP2_FILEUTILS_H_
+#include <sys/types.h>
+
#include <functional>
#include <memory>
#include <string>
@@ -24,6 +26,7 @@
namespace android::idmap2::utils {
+constexpr const char* kIdmapCacheDir = "/data/resource-cache";
constexpr const mode_t kIdmapFilePermissionMask = 0133; // u=rw,g=r,o=r
typedef std::function<bool(unsigned char type /* DT_* from dirent.h */, const std::string& path)>
@@ -35,6 +38,8 @@ std::unique_ptr<std::string> ReadFile(int fd);
std::unique_ptr<std::string> ReadFile(const std::string& path);
+bool UidHasWriteAccessToPath(uid_t uid, const std::string& path);
+
} // namespace android::idmap2::utils
#endif // IDMAP2_INCLUDE_IDMAP2_FILEUTILS_H_
diff --git a/cmds/idmap2/libidmap2/FileUtils.cpp b/cmds/idmap2/libidmap2/FileUtils.cpp
index 0255727fc8c6..a9b68cd6d5d5 100644
--- a/cmds/idmap2/libidmap2/FileUtils.cpp
+++ b/cmds/idmap2/libidmap2/FileUtils.cpp
@@ -19,12 +19,20 @@
#include <unistd.h>
#include <cerrno>
+#include <climits>
+#include <cstdlib>
+#include <cstring>
#include <fstream>
#include <memory>
#include <string>
#include <utility>
#include <vector>
+#include "android-base/file.h"
+#include "android-base/macros.h"
+#include "android-base/stringprintf.h"
+#include "private/android_filesystem_config.h"
+
#include "idmap2/FileUtils.h"
namespace android::idmap2::utils {
@@ -77,4 +85,26 @@ std::unique_ptr<std::string> ReadFile(int fd) {
return r == 0 ? std::move(str) : nullptr;
}
+#ifdef __ANDROID__
+bool UidHasWriteAccessToPath(uid_t uid, const std::string& path) {
+ // resolve symlinks and relative paths; the directories must exist
+ std::string canonical_path;
+ if (!base::Realpath(base::Dirname(path), &canonical_path)) {
+ return false;
+ }
+
+ const std::string cache_subdir = base::StringPrintf("%s/", kIdmapCacheDir);
+ if (canonical_path == kIdmapCacheDir ||
+ canonical_path.compare(0, cache_subdir.size(), cache_subdir) == 0) {
+ // limit access to /data/resource-cache to root and system
+ return uid == AID_ROOT || uid == AID_SYSTEM;
+ }
+ return true;
+}
+#else
+bool UidHasWriteAccessToPath(uid_t uid ATTRIBUTE_UNUSED, const std::string& path ATTRIBUTE_UNUSED) {
+ return true;
+}
+#endif
+
} // namespace android::idmap2::utils
diff --git a/cmds/idmap2/tests/FileUtilsTests.cpp b/cmds/idmap2/tests/FileUtilsTests.cpp
index d9d9a7f829cf..45f84fe341cc 100644
--- a/cmds/idmap2/tests/FileUtilsTests.cpp
+++ b/cmds/idmap2/tests/FileUtilsTests.cpp
@@ -22,6 +22,8 @@
#include "gtest/gtest.h"
#include "android-base/macros.h"
+#include "android-base/stringprintf.h"
+#include "private/android_filesystem_config.h"
#include "idmap2/FileUtils.h"
@@ -71,4 +73,25 @@ TEST(FileUtilsTests, ReadFile) {
close(pipefd[0]);
}
+#ifdef __ANDROID__
+TEST(FileUtilsTests, UidHasWriteAccessToPath) {
+ constexpr const char* tmp_path = "/data/local/tmp/test@idmap";
+ const std::string cache_path(base::StringPrintf("%s/test@idmap", kIdmapCacheDir));
+ const std::string sneaky_cache_path(base::StringPrintf("/data/../%s/test@idmap", kIdmapCacheDir));
+
+ ASSERT_TRUE(UidHasWriteAccessToPath(AID_ROOT, tmp_path));
+ ASSERT_TRUE(UidHasWriteAccessToPath(AID_ROOT, cache_path));
+ ASSERT_TRUE(UidHasWriteAccessToPath(AID_ROOT, sneaky_cache_path));
+
+ ASSERT_TRUE(UidHasWriteAccessToPath(AID_SYSTEM, tmp_path));
+ ASSERT_TRUE(UidHasWriteAccessToPath(AID_SYSTEM, cache_path));
+ ASSERT_TRUE(UidHasWriteAccessToPath(AID_SYSTEM, sneaky_cache_path));
+
+ constexpr const uid_t AID_SOME_APP = AID_SYSTEM + 1;
+ ASSERT_TRUE(UidHasWriteAccessToPath(AID_SOME_APP, tmp_path));
+ ASSERT_FALSE(UidHasWriteAccessToPath(AID_SOME_APP, cache_path));
+ ASSERT_FALSE(UidHasWriteAccessToPath(AID_SOME_APP, sneaky_cache_path));
+}
+#endif
+
} // namespace android::idmap2::utils
diff --git a/cmds/idmap2/tests/Idmap2BinaryTests.cpp b/cmds/idmap2/tests/Idmap2BinaryTests.cpp
index 4334fa60767b..c550eafe5ffe 100644
--- a/cmds/idmap2/tests/Idmap2BinaryTests.cpp
+++ b/cmds/idmap2/tests/Idmap2BinaryTests.cpp
@@ -38,6 +38,7 @@
#include "gtest/gtest.h"
#include "androidfw/PosixUtils.h"
+#include "private/android_filesystem_config.h"
#include "idmap2/FileUtils.h"
#include "idmap2/Idmap.h"
@@ -69,9 +70,23 @@ void AssertIdmap(const Idmap& idmap, const std::string& target_apk_path,
ASSERT_NO_FATAL_FAILURE(AssertIdmap(idmap_ref, target_apk_path, overlay_apk_path)); \
} while (0)
+#ifdef __ANDROID__
+#define SKIP_TEST_IF_CANT_EXEC_IDMAP2 \
+ do { \
+ const uid_t uid = getuid(); \
+ if (uid != AID_ROOT && uid != AID_SYSTEM) { \
+ GTEST_SKIP(); \
+ } \
+ } while (0)
+#else
+#define SKIP_TEST_IF_CANT_EXEC_IDMAP2
+#endif
+
} // namespace
TEST_F(Idmap2BinaryTests, Create) {
+ SKIP_TEST_IF_CANT_EXEC_IDMAP2;
+
// clang-format off
auto result = ExecuteBinary({"idmap2",
"create",
@@ -97,6 +112,8 @@ TEST_F(Idmap2BinaryTests, Create) {
}
TEST_F(Idmap2BinaryTests, Dump) {
+ SKIP_TEST_IF_CANT_EXEC_IDMAP2;
+
// clang-format off
auto result = ExecuteBinary({"idmap2",
"create",
@@ -144,6 +161,8 @@ TEST_F(Idmap2BinaryTests, Dump) {
}
TEST_F(Idmap2BinaryTests, Scan) {
+ SKIP_TEST_IF_CANT_EXEC_IDMAP2;
+
const std::string overlay_static_1_apk_path = GetTestDataPath() + "/overlay/overlay-static-1.apk";
const std::string overlay_static_2_apk_path = GetTestDataPath() + "/overlay/overlay-static-2.apk";
const std::string idmap_static_1_path =
@@ -236,6 +255,8 @@ TEST_F(Idmap2BinaryTests, Scan) {
}
TEST_F(Idmap2BinaryTests, Lookup) {
+ SKIP_TEST_IF_CANT_EXEC_IDMAP2;
+
// clang-format off
auto result = ExecuteBinary({"idmap2",
"create",
@@ -285,6 +306,8 @@ TEST_F(Idmap2BinaryTests, Lookup) {
}
TEST_F(Idmap2BinaryTests, InvalidCommandLineOptions) {
+ SKIP_TEST_IF_CANT_EXEC_IDMAP2;
+
const std::string invalid_target_apk_path = GetTestDataPath() + "/DOES-NOT-EXIST";
// missing mandatory options
diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp
index 59b2aa6ac3d1..ca104823b7cf 100644
--- a/cmds/statsd/Android.bp
+++ b/cmds/statsd/Android.bp
@@ -74,7 +74,6 @@ cc_defaults {
"src/external/SubsystemSleepStatePuller.cpp",
"src/external/PowerStatsPuller.cpp",
"src/external/ResourceHealthManagerPuller.cpp",
- "src/external/ResourceThermalManagerPuller.cpp",
"src/external/StatsPullerManager.cpp",
"src/external/puller_util.cpp",
"src/logd/LogEvent.cpp",
@@ -136,7 +135,6 @@ cc_defaults {
"android.hardware.power@1.0",
"android.hardware.power@1.1",
"android.hardware.power.stats@1.0",
- "android.hardware.thermal@2.0",
"libpackagelistparser",
"libsysutils",
"libcutils",
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index 820da556eaaa..9c320d3e2b03 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -1097,6 +1097,30 @@ hardware::Return<void> StatsService::reportUsbPortOverheatEvent(
return hardware::Void();
}
+hardware::Return<void> StatsService::reportSpeechDspStat(
+ const SpeechDspStat& speechDspStat) {
+ LogEvent event(getWallClockSec() * NS_PER_SEC, getElapsedRealtimeNs(), speechDspStat);
+ mProcessor->OnLogEvent(&event);
+
+ return hardware::Void();
+}
+
+hardware::Return<void> StatsService::reportVendorAtom(const VendorAtom& vendorAtom) {
+ std::string reverseDomainName = (std::string) vendorAtom.reverseDomainName;
+ if (vendorAtom.atomId < 100000 || vendorAtom.atomId >= 200000) {
+ ALOGE("Atom ID %ld is not a valid vendor atom ID", (long) vendorAtom.atomId);
+ return hardware::Void();
+ }
+ if (reverseDomainName.length() > 50) {
+ ALOGE("Vendor atom reverse domain name %s is too long.", reverseDomainName.c_str());
+ return hardware::Void();
+ }
+ LogEvent event(getWallClockSec() * NS_PER_SEC, getElapsedRealtimeNs(), vendorAtom);
+ mProcessor->OnLogEvent(&event);
+
+ return hardware::Void();
+}
+
void StatsService::binderDied(const wp <IBinder>& who) {
ALOGW("statscompanion service died");
StatsdStats::getInstance().noteSystemServerRestart(getWallClockSec());
diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h
index e9b3d4f2f4db..fe0504fc034f 100644
--- a/cmds/statsd/src/StatsService.h
+++ b/cmds/statsd/src/StatsService.h
@@ -205,6 +205,17 @@ public:
virtual Return<void> reportUsbPortOverheatEvent(
const UsbPortOverheatEvent& usbPortOverheatEvent) override;
+ /**
+ * Binder call to get Speech DSP state atom.
+ */
+ virtual Return<void> reportSpeechDspStat(
+ const SpeechDspStat& speechDspStat) override;
+
+ /**
+ * Binder call to get vendor atom.
+ */
+ virtual Return<void> reportVendorAtom(const VendorAtom& vendorAtom) override;
+
/** IBinder::DeathRecipient */
virtual void binderDied(const wp<IBinder>& who) override;
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 60b2e259d729..8fb01b4a17c7 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -34,6 +34,7 @@ import "frameworks/base/core/proto/android/server/connectivity/data_stall_event.
import "frameworks/base/core/proto/android/server/enums.proto";
import "frameworks/base/core/proto/android/server/location/enums.proto";
import "frameworks/base/core/proto/android/service/procstats_enum.proto";
+import "frameworks/base/core/proto/android/service/usb.proto";
import "frameworks/base/core/proto/android/stats/enums.proto";
import "frameworks/base/core/proto/android/stats/docsui/docsui_enums.proto";
import "frameworks/base/core/proto/android/stats/launcher/launcher.proto";
@@ -206,6 +207,8 @@ message Atom {
BroadcastDispatchLatencyReported broadcast_dispatch_latency_reported = 142;
AttentionManagerServiceResultReported attention_manager_service_result_reported = 143;
AdbConnectionChanged adb_connection_changed = 144;
+ SpeechDspStatReported speech_dsp_stat_reported = 145;
+ UsbContaminantReported usb_contaminant_reported = 146;
}
// Pulled events will start at field 10000.
@@ -3018,8 +3021,8 @@ message DiskSpace {
/**
* Pulls battery coulomb counter, which is the remaining battery charge in uAh.
- * Pulled from:
- * frameworks/base/cmds/statsd/src/external/ResourceHealthManagerPuller.cpp
+ *
+ * Pulled from StatsCompanionService.java
*/
message RemainingBatteryCapacity {
optional int32 charge_micro_ampere_hour = 1;
@@ -4521,3 +4524,28 @@ message AdbConnectionChanged {
// True if the 'always allow' option was selected for this system.
optional bool always_allow = 4;
}
+
+/*
+ * Logs the reported speech DSP status.
+ *
+ * Logged from:
+ * Vendor audio implementation.
+ */
+message SpeechDspStatReported {
+ // The total Speech DSP uptime in milliseconds.
+ optional int32 total_uptime_millis = 1;
+ // The total Speech DSP downtime in milliseconds.
+ optional int32 total_downtime_millis = 2;
+ optional int32 total_crash_count = 3;
+ optional int32 total_recover_count = 4;
+}
+
+/**
+ * Logs USB connector contaminant status.
+ *
+ * Logged from: USB Service.
+ */
+message UsbContaminantReported {
+ optional string id = 1;
+ optional android.service.usb.ContaminantPresenceStatus status = 2;
+}
diff --git a/cmds/statsd/src/external/ResourceThermalManagerPuller.cpp b/cmds/statsd/src/external/ResourceThermalManagerPuller.cpp
deleted file mode 100644
index 53709f14564d..000000000000
--- a/cmds/statsd/src/external/ResourceThermalManagerPuller.cpp
+++ /dev/null
@@ -1,147 +0,0 @@
-/*
- * Copyright (C) 2018 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 DEBUG false // STOPSHIP if true
-#include "Log.h"
-
-#include <android/hardware/thermal/2.0/IThermal.h>
-#include "external/ResourceThermalManagerPuller.h"
-#include "external/StatsPuller.h"
-
-#include "ResourceThermalManagerPuller.h"
-#include "logd/LogEvent.h"
-#include "statslog.h"
-#include "stats_log_util.h"
-
-#include <chrono>
-
-using android::hardware::hidl_death_recipient;
-using android::hardware::hidl_vec;
-using android::hidl::base::V1_0::IBase;
-using ::android::hardware::thermal::V2_0::IThermal;
-using ::android::hardware::thermal::V2_0::Temperature;
-using ::android::hardware::thermal::V2_0::TemperatureType;
-using ::android::hardware::thermal::V1_0::ThermalStatus;
-using ::android::hardware::thermal::V1_0::ThermalStatusCode;
-using android::hardware::Return;
-using android::hardware::Void;
-
-using std::chrono::duration_cast;
-using std::chrono::nanoseconds;
-using std::chrono::system_clock;
-using std::make_shared;
-using std::shared_ptr;
-
-namespace android {
-namespace os {
-namespace statsd {
-
-bool getThermalHalLocked();
-sp<android::hardware::thermal::V2_0::IThermal> gThermalHal = nullptr;
-std::mutex gThermalHalMutex;
-
-struct ThermalHalDeathRecipient : virtual public hidl_death_recipient {
- virtual void serviceDied(uint64_t cookie, const wp<IBase>& who) override {
- std::lock_guard<std::mutex> lock(gThermalHalMutex);
- ALOGE("ThermalHAL just died");
- gThermalHal = nullptr;
- getThermalHalLocked();
- }
-};
-
-sp<ThermalHalDeathRecipient> gThermalHalDeathRecipient = nullptr;
-
-// The caller must be holding gThermalHalMutex.
-bool getThermalHalLocked() {
- if (gThermalHal == nullptr) {
- gThermalHal = IThermal::getService();
- if (gThermalHal == nullptr) {
- ALOGE("Unable to get Thermal service.");
- } else {
- if (gThermalHalDeathRecipient == nullptr) {
- gThermalHalDeathRecipient = new ThermalHalDeathRecipient();
- }
- hardware::Return<bool> linked = gThermalHal->linkToDeath(
- gThermalHalDeathRecipient, 0x451F /* cookie */);
- if (!linked.isOk()) {
- ALOGE("Transaction error in linking to ThermalHAL death: %s",
- linked.description().c_str());
- gThermalHal = nullptr;
- } else if (!linked) {
- ALOGW("Unable to link to ThermalHal death notifications");
- gThermalHal = nullptr;
- } else {
- ALOGD("Link to death notification successful");
- }
- }
- }
- return gThermalHal != nullptr;
-}
-
-ResourceThermalManagerPuller::ResourceThermalManagerPuller() :
- StatsPuller(android::util::TEMPERATURE) {
-}
-
-bool ResourceThermalManagerPuller::PullInternal(vector<shared_ptr<LogEvent>>* data) {
- std::lock_guard<std::mutex> lock(gThermalHalMutex);
- if (!getThermalHalLocked()) {
- ALOGE("Thermal Hal not loaded");
- return false;
- }
-
- int64_t wallClockTimestampNs = getWallClockNs();
- int64_t elapsedTimestampNs = getElapsedRealtimeNs();
-
- data->clear();
- bool resultSuccess = true;
-
- Return<void> ret = gThermalHal->getCurrentTemperatures(false, TemperatureType::SKIN,
- [&](ThermalStatus status, const hidl_vec<Temperature>& temps) {
- if (status.code != ThermalStatusCode::SUCCESS) {
- ALOGE("Failed to get temperatures from ThermalHAL. Status: %d", status.code);
- resultSuccess = false;
- return;
- }
- if (mTagId == android::util::TEMPERATURE) {
- for (size_t i = 0; i < temps.size(); ++i) {
- auto ptr = make_shared<LogEvent>(android::util::TEMPERATURE,
- wallClockTimestampNs, elapsedTimestampNs);
- ptr->write((static_cast<int>(temps[i].type)));
- ptr->write(temps[i].name);
- // Convert the temperature to an int.
- int32_t temp = static_cast<int>(temps[i].value * 10);
- ptr->write(temp);
- ptr->init();
- data->push_back(ptr);
- }
- } else {
- ALOGE("Unsupported tag in ResourceThermalManagerPuller: %d", mTagId);
- }
- });
- if (!ret.isOk()) {
- ALOGE("getThermalHalLocked() failed: thermal HAL service not available. Description: %s",
- ret.description().c_str());
- if (ret.isDeadObject()) {
- gThermalHal = nullptr;
- }
- return false;
- }
- return resultSuccess;
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp
index 19a7389cb3d9..7e56beeefbf6 100644
--- a/cmds/statsd/src/external/StatsPullerManager.cpp
+++ b/cmds/statsd/src/external/StatsPullerManager.cpp
@@ -28,7 +28,6 @@
#include "../statscompanion_util.h"
#include "PowerStatsPuller.h"
#include "ResourceHealthManagerPuller.h"
-#include "ResourceThermalManagerPuller.h"
#include "StatsCompanionServicePuller.h"
#include "StatsPullerManager.h"
#include "SubsystemSleepStatePuller.h"
@@ -150,7 +149,8 @@ const std::map<int, PullAtomInfo> StatsPullerManager::kAllPullAtomInfo = {
.puller =
new StatsCompanionServicePuller(android::util::PROCESS_MEMORY_HIGH_WATER_MARK)}},
// temperature
- {android::util::TEMPERATURE, {.puller = new ResourceThermalManagerPuller()}},
+ {android::util::TEMPERATURE,
+ {.puller = new StatsCompanionServicePuller(android::util::TEMPERATURE)}},
// binder_calls
{android::util::BINDER_CALLS,
{.additiveFields = {4, 5, 6, 8, 12},
diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp
index 78a75c5ffaf0..eaba9bee6bf0 100644
--- a/cmds/statsd/src/logd/LogEvent.cpp
+++ b/cmds/statsd/src/logd/LogEvent.cpp
@@ -267,6 +267,22 @@ LogEvent::LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs, con
}
LogEvent::LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs,
+ const SpeechDspStat& speechDspStat) {
+ mLogdTimestampNs = wallClockTimestampNs;
+ mElapsedTimestampNs = elapsedTimestampNs;
+ mTagId = android::util::SPEECH_DSP_STAT_REPORTED;
+
+ mValues.push_back(FieldValue(Field(mTagId, getSimpleField(1)),
+ Value(speechDspStat.totalUptimeMillis)));
+ mValues.push_back(FieldValue(Field(mTagId, getSimpleField(2)),
+ Value(speechDspStat.totalDowntimeMillis)));
+ mValues.push_back(FieldValue(Field(mTagId, getSimpleField(3)),
+ Value(speechDspStat.totalCrashCount)));
+ mValues.push_back(FieldValue(Field(mTagId, getSimpleField(4)),
+ Value(speechDspStat.totalRecoverCount)));
+}
+
+LogEvent::LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs,
const BatteryCausedShutdown& batteryCausedShutdown) {
mLogdTimestampNs = wallClockTimestampNs;
mElapsedTimestampNs = elapsedTimestampNs;
@@ -294,6 +310,36 @@ LogEvent::LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs,
Value(usbPortOverheatEvent.timeToInactive)));
}
+LogEvent::LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs,
+ const VendorAtom& vendorAtom) {
+ mLogdTimestampNs = wallClockTimestampNs;
+ mElapsedTimestampNs = elapsedTimestampNs;
+ mTagId = vendorAtom.atomId;
+
+ mValues.push_back(
+ FieldValue(Field(mTagId, getSimpleField(1)), Value(vendorAtom.reverseDomainName)));
+ for (int i = 0; i < (int)vendorAtom.values.size(); i++) {
+ switch (vendorAtom.values[i].getDiscriminator()) {
+ case VendorAtom::Value::hidl_discriminator::intValue:
+ mValues.push_back(FieldValue(Field(mTagId, getSimpleField(i + 2)),
+ Value(vendorAtom.values[i].intValue())));
+ break;
+ case VendorAtom::Value::hidl_discriminator::longValue:
+ mValues.push_back(FieldValue(Field(mTagId, getSimpleField(i + 2)),
+ Value(vendorAtom.values[i].longValue())));
+ break;
+ case VendorAtom::Value::hidl_discriminator::floatValue:
+ mValues.push_back(FieldValue(Field(mTagId, getSimpleField(i + 2)),
+ Value(vendorAtom.values[i].floatValue())));
+ break;
+ case VendorAtom::Value::hidl_discriminator::stringValue:
+ mValues.push_back(FieldValue(Field(mTagId, getSimpleField(i + 2)),
+ Value(vendorAtom.values[i].stringValue())));
+ break;
+ }
+ }
+}
+
LogEvent::LogEvent(int32_t tagId, int64_t timestampNs) : LogEvent(tagId, timestampNs, 0) {}
LogEvent::LogEvent(int32_t tagId, int64_t timestampNs, int32_t uid) {
diff --git a/cmds/statsd/src/logd/LogEvent.h b/cmds/statsd/src/logd/LogEvent.h
index 3f47b7efdfb5..784376a1580c 100644
--- a/cmds/statsd/src/logd/LogEvent.h
+++ b/cmds/statsd/src/logd/LogEvent.h
@@ -121,6 +121,12 @@ public:
explicit LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs,
const UsbPortOverheatEvent& usbPortOverheatEvent);
+ explicit LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs,
+ const SpeechDspStat& speechDspStat);
+
+ explicit LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs,
+ const VendorAtom& vendorAtom);
+
~LogEvent();
/**
diff --git a/cmds/statsd/src/packages/UidMap.cpp b/cmds/statsd/src/packages/UidMap.cpp
index e9c43cdbf31f..5cf012638dce 100644
--- a/cmds/statsd/src/packages/UidMap.cpp
+++ b/cmds/statsd/src/packages/UidMap.cpp
@@ -548,6 +548,7 @@ const std::map<string, uint32_t> UidMap::sAidToUidMapping = {{"AID_ROOT", 0},
{"AID_LMKD", 1069},
{"AID_LLKD", 1070},
{"AID_IORAPD", 1071},
+ {"AID_NETWORK_STACK", 1073},
{"AID_SHELL", 2000},
{"AID_CACHE", 2001},
{"AID_DIAG", 2002}};
diff --git a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java
index de818a8fd77d..d9aba61e31c9 100644
--- a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java
+++ b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java
@@ -55,7 +55,8 @@ public class TestDrive {
"AID_ROOT",
"AID_BLUETOOTH",
"AID_LMKD",
- "com.android.managedprovisioning"
+ "com.android.managedprovisioning",
+ "AID_NETWORK_STACK"
};
private static final Logger LOGGER = Logger.getLogger(TestDrive.class.getName());
diff --git a/config/hiddenapi-greylist.txt b/config/hiddenapi-greylist.txt
index fc47f67f174c..ee2fe8f5750f 100644
--- a/config/hiddenapi-greylist.txt
+++ b/config/hiddenapi-greylist.txt
@@ -3518,7 +3518,7 @@ Lcom/android/internal/telephony/SubscriptionController;->mDefaultPhoneId:I
Lcom/android/internal/telephony/SubscriptionController;->mLock:Ljava/lang/Object;
Lcom/android/internal/telephony/SubscriptionController;->notifySubscriptionInfoChanged()V
Lcom/android/internal/telephony/SubscriptionController;->setDefaultDataSubId(I)V
-Lcom/android/internal/telephony/SubscriptionController;->setDefaultFallbackSubId(I)V
+Lcom/android/internal/telephony/SubscriptionController;->setDefaultFallbackSubId(II)V
Lcom/android/internal/telephony/SubscriptionController;->setDefaultSmsSubId(I)V
Lcom/android/internal/telephony/SubscriptionController;->setDefaultVoiceSubId(I)V
Lcom/android/internal/telephony/SubscriptionController;->setPlmnSpn(IZLjava/lang/String;ZLjava/lang/String;)Z
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 1063be4c5c7d..92f47e767d2e 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -8218,10 +8218,10 @@ public class Activity extends ContextThemeWrapper
final AutofillId autofillId = autofillIds[i];
final View view = autofillClientFindViewByAutofillIdTraversal(autofillId);
if (view != null) {
- if (!autofillId.isVirtual()) {
+ if (!autofillId.isVirtualInt()) {
visible[i] = view.isVisibleToUser();
} else {
- visible[i] = view.isVisibleToUserForAutofill(autofillId.getVirtualChildId());
+ visible[i] = view.isVisibleToUserForAutofill(autofillId.getVirtualChildIntId());
}
}
}
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index a3243a5de72a..ee3d27c7dac8 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -77,9 +77,7 @@ import android.graphics.ImageDecoder;
import android.hardware.display.DisplayManagerGlobal;
import android.net.ConnectivityManager;
import android.net.IConnectivityManager;
-import android.net.Network;
import android.net.Proxy;
-import android.net.ProxyInfo;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Binder;
@@ -1033,15 +1031,10 @@ public final class ActivityThread extends ClientTransactionHandler {
NetworkEventDispatcher.getInstance().onNetworkConfigurationChanged();
}
- public void setHttpProxy(String host, String port, String exclList, Uri pacFileUrl) {
+ public void updateHttpProxy() {
final ConnectivityManager cm = ConnectivityManager.from(
getApplication() != null ? getApplication() : getSystemContext());
- final Network network = cm.getBoundNetworkForProcess();
- if (network != null) {
- Proxy.setHttpProxySystemProperty(cm.getDefaultProxy());
- } else {
- Proxy.setHttpProxySystemProperty(host, port, exclList, pacFileUrl);
- }
+ Proxy.setHttpProxySystemProperty(cm.getDefaultProxy());
}
public void processInBackground() {
@@ -5960,8 +5953,7 @@ public final class ActivityThread extends ClientTransactionHandler {
// crash if we can't get it.
final IConnectivityManager service = IConnectivityManager.Stub.asInterface(b);
try {
- final ProxyInfo proxyInfo = service.getProxyForNetwork(null);
- Proxy.setHttpProxySystemProperty(proxyInfo);
+ Proxy.setHttpProxySystemProperty(service.getProxyForNetwork(null));
} catch (RemoteException e) {
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
throw e.rethrowFromSystemServer();
diff --git a/core/java/android/app/ActivityView.java b/core/java/android/app/ActivityView.java
index 4d3711ae7ff5..47398674d74c 100644
--- a/core/java/android/app/ActivityView.java
+++ b/core/java/android/app/ActivityView.java
@@ -23,6 +23,7 @@ import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLI
import android.annotation.NonNull;
import android.annotation.UnsupportedAppUsage;
import android.app.ActivityManager.StackInfo;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.hardware.display.DisplayManager;
@@ -119,6 +120,7 @@ public class ActivityView extends ViewGroup {
/** Callback that notifies when the container is ready or destroyed. */
public abstract static class StateCallback {
+
/**
* Called when the container is ready for launching activities. Calling
* {@link #startActivity(Intent)} prior to this callback will result in an
@@ -127,6 +129,7 @@ public class ActivityView extends ViewGroup {
* @see #startActivity(Intent)
*/
public abstract void onActivityViewReady(ActivityView view);
+
/**
* Called when the container can no longer launch activities. Calling
* {@link #startActivity(Intent)} after this callback will result in an
@@ -135,11 +138,24 @@ public class ActivityView extends ViewGroup {
* @see #startActivity(Intent)
*/
public abstract void onActivityViewDestroyed(ActivityView view);
+
+ /**
+ * Called when a task is created inside the container.
+ * This is a filtered version of {@link TaskStackListener}
+ */
+ public void onTaskCreated(int taskId, ComponentName componentName) { }
+
/**
* Called when a task is moved to the front of the stack inside the container.
* This is a filtered version of {@link TaskStackListener}
*/
public void onTaskMovedToFront(ActivityManager.StackInfo stackInfo) { }
+
+ /**
+ * Called when a task is about to be removed from the stack inside the container.
+ * This is a filtered version of {@link TaskStackListener}
+ */
+ public void onTaskRemovalStarted(int taskId) { }
}
/**
@@ -508,14 +524,45 @@ public class ActivityView extends ViewGroup {
@Override
public void onTaskMovedToFront(int taskId) throws RemoteException {
- if (mActivityViewCallback != null) {
- StackInfo stackInfo = getTopMostStackInfo();
- // if StackInfo was null or unrelated to the "move to front" then there's no use
- // notifying the callback
- if (stackInfo != null
- && taskId == stackInfo.taskIds[stackInfo.taskIds.length - 1]) {
- mActivityViewCallback.onTaskMovedToFront(stackInfo);
- }
+ if (mActivityViewCallback == null) {
+ return;
+ }
+
+ StackInfo stackInfo = getTopMostStackInfo();
+ // if StackInfo was null or unrelated to the "move to front" then there's no use
+ // notifying the callback
+ if (stackInfo != null
+ && taskId == stackInfo.taskIds[stackInfo.taskIds.length - 1]) {
+ mActivityViewCallback.onTaskMovedToFront(stackInfo);
+ }
+ }
+
+ @Override
+ public void onTaskCreated(int taskId, ComponentName componentName) throws RemoteException {
+ if (mActivityViewCallback == null) {
+ return;
+ }
+
+ StackInfo stackInfo = getTopMostStackInfo();
+ // if StackInfo was null or unrelated to the task creation then there's no use
+ // notifying the callback
+ if (stackInfo != null
+ && taskId == stackInfo.taskIds[stackInfo.taskIds.length - 1]) {
+ mActivityViewCallback.onTaskCreated(taskId, componentName);
+ }
+ }
+
+ @Override
+ public void onTaskRemovalStarted(int taskId) throws RemoteException {
+ if (mActivityViewCallback == null) {
+ return;
+ }
+ StackInfo stackInfo = getTopMostStackInfo();
+ // if StackInfo was null or task is on a different display then there's no use
+ // notifying the callback
+ if (stackInfo != null
+ && taskId == stackInfo.taskIds[stackInfo.taskIds.length - 1]) {
+ mActivityViewCallback.onTaskRemovalStarted(taskId);
}
}
diff --git a/core/java/android/app/IApplicationThread.aidl b/core/java/android/app/IApplicationThread.aidl
index fcb6c14d052c..c64fcf3e7526 100644
--- a/core/java/android/app/IApplicationThread.aidl
+++ b/core/java/android/app/IApplicationThread.aidl
@@ -100,8 +100,7 @@ oneway interface IApplicationThread {
void dumpActivity(in ParcelFileDescriptor fd, IBinder servicetoken, in String prefix,
in String[] args);
void clearDnsCache();
- void setHttpProxy(in String proxy, in String port, in String exclList,
- in Uri pacFileUrl);
+ void updateHttpProxy();
void setCoreSettings(in Bundle coreSettings);
void updatePackageCompatibilityInfo(in String pkg, in CompatibilityInfo info);
void scheduleTrimMemory(int level);
diff --git a/core/java/android/app/KeyguardManager.java b/core/java/android/app/KeyguardManager.java
index 75c90542ebce..181accea2e6a 100644
--- a/core/java/android/app/KeyguardManager.java
+++ b/core/java/android/app/KeyguardManager.java
@@ -101,6 +101,12 @@ public class KeyguardManager {
public static final String EXTRA_DESCRIPTION = "android.app.extra.DESCRIPTION";
/**
+ * A boolean value to forward to {@link android.hardware.biometrics.BiometricPrompt}.
+ * @hide
+ */
+ public static final String EXTRA_USE_IMPLICIT = "android.app.extra.USE_IMPLICIT";
+
+ /**
* A CharSequence description to show to the user on the alternate button when used with
* {@link #ACTION_CONFIRM_FRP_CREDENTIAL}.
* @hide
@@ -123,14 +129,39 @@ public class KeyguardManager {
* {@link android.app.Activity#startActivityForResult(Intent, int)} and check for
* {@link android.app.Activity#RESULT_OK} if the user successfully completes the challenge.
*
+ * @param title Title to be shown on the dialog.
+ * @param description Description to be shown on the dialog.
* @return the intent for launching the activity or null if no password is required.
**/
@RequiresFeature(PackageManager.FEATURE_SECURE_LOCK_SCREEN)
public Intent createConfirmDeviceCredentialIntent(CharSequence title, CharSequence description) {
- if (!isDeviceSecure()) return null;
+ return createConfirmDeviceCredentialIntent(title, description, false /* useImplicit */);
+ }
+
+ /**
+ * Get an intent to prompt the user to confirm credentials (pin, pattern or password)
+ * for the current user of the device. The caller is expected to launch this activity using
+ * {@link android.app.Activity#startActivityForResult(Intent, int)} and check for
+ * {@link android.app.Activity#RESULT_OK} if the user successfully completes the challenge.
+ *
+ * @param title Title to be shown on the dialog.
+ * @param description Description to be shown on the dialog.
+ * @param useImplicit If useImplicit is set to true, ConfirmDeviceCredentials will invoke
+ * {@link android.hardware.biometrics.BiometricPrompt} with
+ * {@link android.hardware.biometrics.BiometricPrompt.Builder#setRequireConfirmation(
+ * boolean)} set to false.
+ * @return the intent for launching the activity or null if no password is required.
+ * @hide
+ */
+ public Intent createConfirmDeviceCredentialIntent(CharSequence title, CharSequence description,
+ boolean useImplicit) {
+ if (!isDeviceSecure()) {
+ return null;
+ }
Intent intent = new Intent(ACTION_CONFIRM_DEVICE_CREDENTIAL);
intent.putExtra(EXTRA_TITLE, title);
intent.putExtra(EXTRA_DESCRIPTION, description);
+ intent.putExtra(EXTRA_USE_IMPLICIT, useImplicit);
// explicitly set the package for security
intent.setPackage(getSettingsPackageForIntent(intent));
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 43614feb28a4..c4b4b4070ce7 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -1137,9 +1137,6 @@ public class NotificationManager {
}
}
- /**
- * @hide
- */
public boolean isNotificationAssistantAccessGranted(ComponentName assistant) {
INotificationManager service = getService();
try {
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 55a3acb16b11..8ca3544b8d67 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -3291,7 +3291,7 @@ public class DevicePolicyManager {
*/
public int getPasswordMaximumLength(int quality) {
PackageManager pm = mContext.getPackageManager();
- if (!pm.hasSystemFeature(PackageManager.FEATURE_DEVICE_ID_ATTESTATION)) {
+ if (!pm.hasSystemFeature(PackageManager.FEATURE_SECURE_LOCK_SCREEN)) {
return 0;
}
// Kind-of arbitrary.
@@ -3980,6 +3980,10 @@ public class DevicePolicyManager {
*/
public static final int WIPE_EUICC = 0x0004;
+ /**
+ * Flag for {@link #wipeData(int)}: won't show reason for wiping to the user.
+ */
+ public static final int WIPE_SILENTLY = 0x0008;
/**
* Ask that all user data be wiped. If called as a secondary user, the user will be removed and
@@ -3991,9 +3995,10 @@ public class DevicePolicyManager {
* be able to call this method; if it has not, a security exception will be thrown.
*
* @param flags Bit mask of additional options: currently supported flags are
- * {@link #WIPE_EXTERNAL_STORAGE} and {@link #WIPE_RESET_PROTECTION_DATA}.
+ * {@link #WIPE_EXTERNAL_STORAGE}, {@link #WIPE_RESET_PROTECTION_DATA},
+ * {@link #WIPE_EUICC} and {@link #WIPE_SILENTLY}.
* @throws SecurityException if the calling application does not own an active administrator
- * that uses {@link DeviceAdminInfo#USES_POLICY_WIPE_DATA}
+ * that uses {@link DeviceAdminInfo#USES_POLICY_WIPE_DATA}
*/
public void wipeData(int flags) {
throwIfParentInstance("wipeData");
@@ -4013,16 +4018,21 @@ public class DevicePolicyManager {
* be able to call this method; if it has not, a security exception will be thrown.
*
* @param flags Bit mask of additional options: currently supported flags are
- * {@link #WIPE_EXTERNAL_STORAGE} and {@link #WIPE_RESET_PROTECTION_DATA}.
+ * {@link #WIPE_EXTERNAL_STORAGE}, {@link #WIPE_RESET_PROTECTION_DATA} and
+ * {@link #WIPE_EUICC}.
* @param reason a string that contains the reason for wiping data, which can be
- * presented to the user. If the string is null or empty, user won't be notified.
+ * presented to the user.
* @throws SecurityException if the calling application does not own an active administrator
- * that uses {@link DeviceAdminInfo#USES_POLICY_WIPE_DATA}
- * @throws IllegalArgumentException if the input reason string is null or empty.
+ * that uses {@link DeviceAdminInfo#USES_POLICY_WIPE_DATA}
+ * @throws IllegalArgumentException if the input reason string is null or empty, or if
+ * {@link #WIPE_SILENTLY} is set.
*/
- public void wipeData(int flags, CharSequence reason) {
+ public void wipeData(int flags, @NonNull CharSequence reason) {
throwIfParentInstance("wipeData");
- wipeDataInternal(flags, reason != null ? reason.toString() : null);
+ Preconditions.checkNotNull(reason, "reason string is null");
+ Preconditions.checkStringNotEmpty(reason, "reason string is empty");
+ Preconditions.checkArgument((flags & WIPE_SILENTLY) == 0, "WIPE_SILENTLY cannot be set");
+ wipeDataInternal(flags, reason.toString());
}
/**
@@ -4033,7 +4043,7 @@ public class DevicePolicyManager {
* @see #wipeData(int, CharSequence)
* @hide
*/
- private void wipeDataInternal(int flags, String wipeReasonForUser) {
+ private void wipeDataInternal(int flags, @NonNull String wipeReasonForUser) {
if (mService != null) {
try {
mService.wipeDataWithReason(flags, wipeReasonForUser);
@@ -5049,11 +5059,16 @@ public class DevicePolicyManager {
}
/**
+ * Service-specific error code used in implementation of {@code setAlwaysOnVpnPackage} methods.
+ * @hide
+ */
+ public static final int ERROR_VPN_PACKAGE_NOT_FOUND = 1;
+
+ /**
* Called by a device or profile owner to configure an always-on VPN connection through a
* specific application for the current user. This connection is automatically granted and
* persisted after a reboot.
- * <p>
- * To support the always-on feature, an app must
+ * <p> To support the always-on feature, an app must
* <ul>
* <li>declare a {@link android.net.VpnService} in its manifest, guarded by
* {@link android.Manifest.permission#BIND_VPN_SERVICE};</li>
@@ -5062,12 +5077,13 @@ public class DevicePolicyManager {
* {@link android.net.VpnService#SERVICE_META_DATA_SUPPORTS_ALWAYS_ON}.</li>
* </ul>
* The call will fail if called with the package name of an unsupported VPN app.
+ * <p> Enabling lockdown via {@code lockdownEnabled} argument carries the risk that any failure
+ * of the VPN provider could break networking for all apps.
*
* @param vpnPackage The package name for an installed VPN app on the device, or {@code null} to
* remove an existing always-on VPN configuration.
* @param lockdownEnabled {@code true} to disallow networking when the VPN is not connected or
- * {@code false} otherwise. This carries the risk that any failure of the VPN provider
- * could break networking for all apps. This has no effect when clearing.
+ * {@code false} otherwise. This has no effect when clearing.
* @throws SecurityException if {@code admin} is not a device or a profile owner.
* @throws NameNotFoundException if {@code vpnPackage} is not installed.
* @throws UnsupportedOperationException if {@code vpnPackage} exists but does not support being
@@ -5076,11 +5092,46 @@ public class DevicePolicyManager {
public void setAlwaysOnVpnPackage(@NonNull ComponentName admin, @Nullable String vpnPackage,
boolean lockdownEnabled)
throws NameNotFoundException, UnsupportedOperationException {
+ setAlwaysOnVpnPackage(admin, vpnPackage, lockdownEnabled, Collections.emptyList());
+ }
+
+ /**
+ * A version of {@link #setAlwaysOnVpnPackage(ComponentName, String, boolean)} that allows the
+ * admin to specify a set of apps that should be able to access the network directly when VPN
+ * is not connected. When VPN connects these apps switch over to VPN if allowed to use that VPN.
+ * System apps can always bypass VPN.
+ * <p> Note that the system doesn't update the whitelist when packages are installed or
+ * uninstalled, the admin app must call this method to keep the list up to date.
+ *
+ * @param vpnPackage package name for an installed VPN app on the device, or {@code null}
+ * to remove an existing always-on VPN configuration
+ * @param lockdownEnabled {@code true} to disallow networking when the VPN is not connected or
+ * {@code false} otherwise. This has no effect when clearing.
+ * @param lockdownWhitelist Packages that will be able to access the network directly when VPN
+ * is in lockdown mode but not connected. Has no effect when clearing.
+ * @throws SecurityException if {@code admin} is not a device or a profile
+ * owner.
+ * @throws NameNotFoundException if {@code vpnPackage} or one of
+ * {@code lockdownWhitelist} is not installed.
+ * @throws UnsupportedOperationException if {@code vpnPackage} exists but does
+ * not support being set as always-on, or if always-on VPN is not
+ * available.
+ */
+ public void setAlwaysOnVpnPackage(@NonNull ComponentName admin, @Nullable String vpnPackage,
+ boolean lockdownEnabled, @Nullable List<String> lockdownWhitelist)
+ throws NameNotFoundException, UnsupportedOperationException {
throwIfParentInstance("setAlwaysOnVpnPackage");
if (mService != null) {
try {
- if (!mService.setAlwaysOnVpnPackage(admin, vpnPackage, lockdownEnabled)) {
- throw new NameNotFoundException(vpnPackage);
+ mService.setAlwaysOnVpnPackage(
+ admin, vpnPackage, lockdownEnabled, lockdownWhitelist);
+ } catch (ServiceSpecificException e) {
+ switch (e.errorCode) {
+ case ERROR_VPN_PACKAGE_NOT_FOUND:
+ throw new NameNotFoundException(e.getMessage());
+ default:
+ throw new RuntimeException(
+ "Unknown error setting always-on VPN: " + e.errorCode);
}
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -5089,6 +5140,51 @@ public class DevicePolicyManager {
}
/**
+ * Called by device or profile owner to query whether current always-on VPN is configured in
+ * lockdown mode. Returns {@code false} when no always-on configuration is set.
+ *
+ * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+ *
+ * @throws SecurityException if {@code admin} is not a device or a profile owner.
+ *
+ * @see #setAlwaysOnVpnPackage(ComponentName, String, boolean)
+ */
+ public boolean isAlwaysOnVpnLockdownEnabled(@NonNull ComponentName admin) {
+ throwIfParentInstance("isAlwaysOnVpnLockdownEnabled");
+ if (mService != null) {
+ try {
+ return mService.isAlwaysOnVpnLockdownEnabled(admin);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Called by device or profile owner to query the list of packages that are allowed to access
+ * the network directly when always-on VPN is in lockdown mode but not connected. Returns
+ * {@code null} when always-on VPN is not active or not in lockdown mode.
+ *
+ * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+ *
+ * @throws SecurityException if {@code admin} is not a device or a profile owner.
+ *
+ * @see #setAlwaysOnVpnPackage(ComponentName, String, boolean, List)
+ */
+ public List<String> getAlwaysOnVpnLockdownWhitelist(@NonNull ComponentName admin) {
+ throwIfParentInstance("getAlwaysOnVpnLockdownWhitelist");
+ if (mService != null) {
+ try {
+ return mService.getAlwaysOnVpnLockdownWhitelist(admin);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ return null;
+ }
+
+ /**
* Called by a device or profile owner to read the name of the package administering an
* always-on VPN connection for the current user. If there is no such package, or the always-on
* VPN is provided by the system instead of by an application, {@code null} will be returned.
@@ -10428,76 +10524,53 @@ public class DevicePolicyManager {
}
/**
- * Whitelists a package that is allowed to access cross profile calendar APIs.
+ * Whitelists a set of packages that are allowed to access cross-profile calendar APIs.
*
* <p>Called by a profile owner of a managed profile.
*
- * @param admin which {@link DeviceAdminReceiver} this request is associated with.
- * @param packageName name of the package to be whitelisted.
- * @throws SecurityException if {@code admin} is not a profile owner.
- *
- * @see #removeCrossProfileCalendarPackage(ComponentName, String)
- * @see #getCrossProfileCalendarPackages(ComponentName)
- */
- public void addCrossProfileCalendarPackage(@NonNull ComponentName admin,
- @NonNull String packageName) {
- throwIfParentInstance("addCrossProfileCalendarPackage");
- if (mService != null) {
- try {
- mService.addCrossProfileCalendarPackage(admin, packageName);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
- }
-
- /**
- * Removes a package that was allowed to access cross profile calendar APIs
- * from the whitelist.
- *
- * <p>Called by a profile owner of a managed profile.
+ * <p>Calling with a null value for the set disables the restriction so that all packages
+ * are allowed to access cross-profile calendar APIs. Calling with an empty set disallows
+ * all packages from accessing cross-profile calendar APIs. If this method isn't called,
+ * no package will be allowed to access cross-profile calendar APIs by default.
*
* @param admin which {@link DeviceAdminReceiver} this request is associated with.
- * @param packageName name of the package to be removed from the whitelist.
- * @return {@code true} if the package is successfully removed from the whitelist,
- * {@code false} otherwise.
+ * @param packageNames set of packages to be whitelisted.
* @throws SecurityException if {@code admin} is not a profile owner.
*
- * @see #addCrossProfileCalendarPackage(ComponentName, String)
* @see #getCrossProfileCalendarPackages(ComponentName)
*/
- public boolean removeCrossProfileCalendarPackage(@NonNull ComponentName admin,
- @NonNull String packageName) {
- throwIfParentInstance("removeCrossProfileCalendarPackage");
+ public void setCrossProfileCalendarPackages(@NonNull ComponentName admin,
+ @Nullable Set<String> packageNames) {
+ throwIfParentInstance("setCrossProfileCalendarPackages");
if (mService != null) {
try {
- return mService.removeCrossProfileCalendarPackage(admin, packageName);
+ mService.setCrossProfileCalendarPackages(admin, packageNames == null ? null
+ : new ArrayList<>(packageNames));
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
- return false;
}
/**
- * Gets a set of package names that are whitelisted to access cross profile calendar APIs.
+ * Gets a set of package names that are whitelisted to access cross-profile calendar APIs.
*
* <p>Called by a profile owner of a managed profile.
*
* @param admin which {@link DeviceAdminReceiver} this request is associated with.
* @return the set of names of packages that were previously whitelisted via
- * {@link #addCrossProfileCalendarPackage(ComponentName, String)}, or an
+ * {@link #setCrossProfileCalendarPackages(ComponentName, Set)}, or an
* empty set if none have been whitelisted.
* @throws SecurityException if {@code admin} is not a profile owner.
*
- * @see #addCrossProfileCalendarPackage(ComponentName, String)
- * @see #removeCrossProfileCalendarPackage(ComponentName, String)
+ * @see #setCrossProfileCalendarPackages(ComponentName, Set)
*/
- public @NonNull Set<String> getCrossProfileCalendarPackages(@NonNull ComponentName admin) {
+ public @Nullable Set<String> getCrossProfileCalendarPackages(@NonNull ComponentName admin) {
throwIfParentInstance("getCrossProfileCalendarPackages");
if (mService != null) {
try {
- return new ArraySet<>(mService.getCrossProfileCalendarPackages(admin));
+ final List<String> packageNames = mService.getCrossProfileCalendarPackages(admin);
+ return packageNames == null ? null : new ArraySet<>(packageNames);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -10506,22 +10579,21 @@ public class DevicePolicyManager {
}
/**
- * Returns if a package is whitelisted to access cross profile calendar APIs.
+ * Returns if a package is whitelisted to access cross-profile calendar APIs.
*
* <p>To query for a specific user, use
* {@link Context#createPackageContextAsUser(String, int, UserHandle)} to create a context for
* that user, and get a {@link DevicePolicyManager} from this context.
*
* @param packageName the name of the package
- * @return {@code true} if the package is whitelisted to access cross profile calendar APIs.
+ * @return {@code true} if the package is whitelisted to access cross-profile calendar APIs.
* {@code false} otherwise.
*
- * @see #addCrossProfileCalendarPackage(ComponentName, String)
- * @see #removeCrossProfileCalendarPackage(ComponentName, String)
+ * @see #setCrossProfileCalendarPackages(ComponentName, Set)
* @see #getCrossProfileCalendarPackages(ComponentName)
* @hide
*/
- public @NonNull boolean isPackageAllowedToAccessCalendar(@NonNull String packageName) {
+ public boolean isPackageAllowedToAccessCalendar(@NonNull String packageName) {
throwIfParentInstance("isPackageAllowedToAccessCalendar");
if (mService != null) {
try {
@@ -10535,27 +10607,27 @@ public class DevicePolicyManager {
}
/**
- * Gets a set of package names that are whitelisted to access cross profile calendar APIs.
+ * Gets a set of package names that are whitelisted to access cross-profile calendar APIs.
*
* <p>To query for a specific user, use
* {@link Context#createPackageContextAsUser(String, int, UserHandle)} to create a context for
* that user, and get a {@link DevicePolicyManager} from this context.
*
* @return the set of names of packages that were previously whitelisted via
- * {@link #addCrossProfileCalendarPackage(ComponentName, String)}, or an
+ * {@link #setCrossProfileCalendarPackages(ComponentName, Set)}, or an
* empty set if none have been whitelisted.
*
- * @see #addCrossProfileCalendarPackage(ComponentName, String)
- * @see #removeCrossProfileCalendarPackage(ComponentName, String)
+ * @see #setCrossProfileCalendarPackages(ComponentName, Set)
* @see #getCrossProfileCalendarPackages(ComponentName)
* @hide
*/
- public @NonNull Set<String> getCrossProfileCalendarPackages() {
+ public @Nullable Set<String> getCrossProfileCalendarPackages() {
throwIfParentInstance("getCrossProfileCalendarPackages");
if (mService != null) {
try {
- return new ArraySet<>(mService.getCrossProfileCalendarPackagesForUser(
- myUserId()));
+ final List<String> packageNames = mService.getCrossProfileCalendarPackagesForUser(
+ myUserId());
+ return packageNames == null ? null : new ArraySet<>(packageNames);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 568becfcdd1a..5790fda718a7 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -187,8 +187,10 @@ interface IDevicePolicyManager {
void setCertInstallerPackage(in ComponentName who, String installerPackage);
String getCertInstallerPackage(in ComponentName who);
- boolean setAlwaysOnVpnPackage(in ComponentName who, String vpnPackage, boolean lockdown);
+ boolean setAlwaysOnVpnPackage(in ComponentName who, String vpnPackage, boolean lockdown, in List<String> lockdownWhitelist);
String getAlwaysOnVpnPackage(in ComponentName who);
+ boolean isAlwaysOnVpnLockdownEnabled(in ComponentName who);
+ List<String> getAlwaysOnVpnLockdownWhitelist(in ComponentName who);
void addPersistentPreferredActivity(in ComponentName admin, in IntentFilter filter, in ComponentName activity);
void clearPackagePersistentPreferredActivities(in ComponentName admin, String packageName);
@@ -424,8 +426,7 @@ interface IDevicePolicyManager {
void installUpdateFromFile(in ComponentName admin, in ParcelFileDescriptor updateFileDescriptor, in StartInstallingUpdateCallback listener);
- void addCrossProfileCalendarPackage(in ComponentName admin, String packageName);
- boolean removeCrossProfileCalendarPackage(in ComponentName admin, String packageName);
+ void setCrossProfileCalendarPackages(in ComponentName admin, in List<String> packageNames);
List<String> getCrossProfileCalendarPackages(in ComponentName admin);
boolean isPackageAllowedToAccessCalendarForUser(String packageName, int userHandle);
List<String> getCrossProfileCalendarPackagesForUser(int userHandle);
diff --git a/core/java/android/app/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java
index 7d03f00611d4..6006ad2f5ed3 100644
--- a/core/java/android/app/assist/AssistStructure.java
+++ b/core/java/android/app/assist/AssistStructure.java
@@ -894,7 +894,7 @@ public class AssistStructure implements Parcelable {
}
if (mAutofillId != null) {
autofillFlags |= AUTOFILL_FLAGS_HAS_AUTOFILL_VIEW_ID;
- if (mAutofillId.isVirtual()) {
+ if (mAutofillId.isVirtualInt()) {
autofillFlags |= AUTOFILL_FLAGS_HAS_AUTOFILL_VIRTUAL_VIEW_ID;
}
}
@@ -961,8 +961,9 @@ public class AssistStructure implements Parcelable {
if ((autofillFlags & AUTOFILL_FLAGS_HAS_AUTOFILL_VIEW_ID) != 0) {
out.writeInt(mAutofillId.getViewId());
if ((autofillFlags & AUTOFILL_FLAGS_HAS_AUTOFILL_VIRTUAL_VIEW_ID) != 0) {
- out.writeInt(mAutofillId.getVirtualChildId());
+ out.writeInt(mAutofillId.getVirtualChildIntId());
}
+ // TODO(b/113593220): write session id as well
}
if ((autofillFlags & AUTOFILL_FLAGS_HAS_AUTOFILL_TYPE) != 0) {
out.writeInt(mAutofillType);
diff --git a/core/java/android/app/backup/BackupAgent.java b/core/java/android/app/backup/BackupAgent.java
index c983d4f68710..24580b40aa29 100644
--- a/core/java/android/app/backup/BackupAgent.java
+++ b/core/java/android/app/backup/BackupAgent.java
@@ -16,6 +16,7 @@
package android.app.backup;
+import android.annotation.Nullable;
import android.app.IBackupAgent;
import android.app.QueuedWork;
import android.app.backup.FullBackup.BackupScheme.PathWithRequiredFlags;
@@ -181,6 +182,8 @@ public abstract class BackupAgent extends ContextWrapper {
Handler mHandler = null;
+ @Nullable private UserHandle mUser;
+
Handler getHandler() {
if (mHandler == null) {
mHandler = new Handler(Looper.getMainLooper());
@@ -232,6 +235,8 @@ public abstract class BackupAgent extends ContextWrapper {
*/
public void onCreate(UserHandle user) {
onCreate();
+
+ mUser = user;
}
/**
@@ -528,6 +533,10 @@ public abstract class BackupAgent extends ContextWrapper {
public void onQuotaExceeded(long backupDataBytes, long quotaBytes) {
}
+ private int getBackupUserId() {
+ return mUser == null ? super.getUserId() : mUser.getIdentifier();
+ }
+
/**
* Check whether the xml yielded any <include/> tag for the provided <code>domainToken</code>.
* If so, perform a {@link #fullBackupFileTree} which backs up the file or recurses if the path
@@ -1033,7 +1042,7 @@ public abstract class BackupAgent extends ContextWrapper {
Binder.restoreCallingIdentity(ident);
try {
- callbackBinder.opComplete(token, 0);
+ callbackBinder.opCompleteForUser(getBackupUserId(), token, 0);
} catch (RemoteException e) {
// we'll time out anyway, so we're safe
}
@@ -1082,7 +1091,7 @@ public abstract class BackupAgent extends ContextWrapper {
Binder.restoreCallingIdentity(ident);
try {
- callbackBinder.opComplete(token, 0);
+ callbackBinder.opCompleteForUser(getBackupUserId(), token, 0);
} catch (RemoteException e) {
// we'll time out anyway, so we're safe
}
@@ -1112,7 +1121,8 @@ public abstract class BackupAgent extends ContextWrapper {
} finally {
Binder.restoreCallingIdentity(ident);
try {
- callbackBinder.opComplete(token, measureOutput.getSize());
+ callbackBinder.opCompleteForUser(getBackupUserId(), token,
+ measureOutput.getSize());
} catch (RemoteException e) {
// timeout, so we're safe
}
@@ -1137,7 +1147,7 @@ public abstract class BackupAgent extends ContextWrapper {
Binder.restoreCallingIdentity(ident);
try {
- callbackBinder.opComplete(token, 0);
+ callbackBinder.opCompleteForUser(getBackupUserId(), token, 0);
} catch (RemoteException e) {
// we'll time out anyway, so we're safe
}
@@ -1162,7 +1172,7 @@ public abstract class BackupAgent extends ContextWrapper {
Binder.restoreCallingIdentity(ident);
try {
- callbackBinder.opComplete(token, 0);
+ callbackBinder.opCompleteForUser(getBackupUserId(), token, 0);
} catch (RemoteException e) {
// we'll time out anyway, so we're safe
}
diff --git a/core/java/android/app/backup/IBackupManager.aidl b/core/java/android/app/backup/IBackupManager.aidl
index f8c5a815a32e..eda8981d7e0e 100644
--- a/core/java/android/app/backup/IBackupManager.aidl
+++ b/core/java/android/app/backup/IBackupManager.aidl
@@ -549,6 +549,19 @@ interface IBackupManager {
/**
* Notify the backup manager that a BackupAgent has completed the operation
+ * corresponding to the given token and user id.
+ *
+ * @param userId User id for which the operation has been completed.
+ * @param token The transaction token passed to the BackupAgent method being
+ * invoked.
+ * @param result In the case of a full backup measure operation, the estimated
+ * total file size that would result from the operation. Unused in all other
+ * cases.
+ */
+ void opCompleteForUser(int userId, int token, long result);
+
+ /**
+ * Notify the backup manager that a BackupAgent has completed the operation
* corresponding to the given token.
*
* @param token The transaction token passed to the BackupAgent method being
diff --git a/core/java/android/app/role/RoleManager.java b/core/java/android/app/role/RoleManager.java
index a6abe0b30f79..ddd531339d39 100644
--- a/core/java/android/app/role/RoleManager.java
+++ b/core/java/android/app/role/RoleManager.java
@@ -172,6 +172,15 @@ public final class RoleManager {
public static final String ROLE_CALL_COMPANION_APP = "android.app.role.CALL_COMPANION_APP";
/**
+ * The name of the assistant app role.
+ *
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ public static final String ROLE_ASSISTANT = "android.app.role.ASSISTANT";
+
+ /**
* The action used to request user approval of a role for an application.
*
* @hide
diff --git a/core/java/android/app/usage/IUsageStatsManager.aidl b/core/java/android/app/usage/IUsageStatsManager.aidl
index d2934b9f5a21..b1500c193820 100644
--- a/core/java/android/app/usage/IUsageStatsManager.aidl
+++ b/core/java/android/app/usage/IUsageStatsManager.aidl
@@ -55,6 +55,9 @@ interface IUsageStatsManager {
long sessionThresholdTimeMs, in PendingIntent limitReachedCallbackIntent,
in PendingIntent sessionEndCallbackIntent, String callingPackage);
void unregisterUsageSessionObserver(int sessionObserverId, String callingPackage);
+ void registerAppUsageLimitObserver(int observerId, in String[] packages, long timeLimitMs,
+ in PendingIntent callback, String callingPackage);
+ void unregisterAppUsageLimitObserver(int observerId, String callingPackage);
void reportUsageStart(in IBinder activity, String token, String callingPackage);
void reportPastUsageStart(in IBinder activity, String token, long timeAgoMs,
String callingPackage);
diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java
index d2de8872c1bd..51397a243420 100644
--- a/core/java/android/app/usage/UsageStatsManager.java
+++ b/core/java/android/app/usage/UsageStatsManager.java
@@ -619,7 +619,7 @@ public final class UsageStatsManager {
* @param timeLimit The total time the set of apps can be in the foreground before the
* callbackIntent is delivered. Must be at least one minute.
* @param timeUnit The unit for time specified in {@code timeLimit}. Cannot be null.
- * @param callbackIntent The PendingIntent that will be dispatched when the time limit is
+ * @param callbackIntent The PendingIntent that will be dispatched when the usage limit is
* exceeded by the group of apps. The delivered Intent will also contain
* the extras {@link #EXTRA_OBSERVER_ID}, {@link #EXTRA_TIME_LIMIT} and
* {@link #EXTRA_TIME_USED}. Cannot be null.
@@ -682,14 +682,14 @@ public final class UsageStatsManager {
* @param sessionThresholdTimeUnit The unit for time specified in {@code sessionThreshold}.
* Cannot be null.
* @param limitReachedCallbackIntent The {@link PendingIntent} that will be dispatched when the
- * time limit is exceeded by the group of apps. The delivered
- * Intent will also contain the extras {@link
+ * usage limit is exceeded by the group of apps. The
+ * delivered Intent will also contain the extras {@link
* #EXTRA_OBSERVER_ID}, {@link #EXTRA_TIME_LIMIT} and {@link
* #EXTRA_TIME_USED}. Cannot be null.
* @param sessionEndCallbackIntent The {@link PendingIntent} that will be dispatched when the
- * session has ended after the time limit has been exceeded. The
- * session is considered at its end after the {@code observed}
- * usage has stopped and an additional {@code
+ * session has ended after the usage limit has been exceeded.
+ * The session is considered at its end after the {@code
+ * observed} usage has stopped and an additional {@code
* sessionThresholdTime} has passed. The delivered Intent will
* also contain the extras {@link #EXTRA_OBSERVER_ID} and {@link
* #EXTRA_TIME_USED}. Can be null.
@@ -736,6 +736,74 @@ public final class UsageStatsManager {
}
/**
+ * Register a usage limit observer that receives a callback on the provided intent when the
+ * sum of usages of apps and tokens in the provided {@code observedEntities} array exceeds the
+ * {@code timeLimit} specified. The structure of a token is a {@link String} with the reporting
+ * package's name and a token that the calling app will use, separated by the forward slash
+ * character. Example: com.reporting.package/5OM3*0P4QU3-7OK3N
+ * <p>
+ * Registering an {@code observerId} that was already registered will override the previous one.
+ * No more than 1000 unique {@code observerId} may be registered by a single uid
+ * at any one time.
+ * A limit may be unregistered via {@link #unregisterAppUsageLimitObserver}
+ * <p>
+ * This method is similar to {@link #registerAppUsageObserver}, but the usage limit set here
+ * will be visible to the launcher so that it can report the limit to the user and how much
+ * of it is remaining.
+ * @see android.content.pm.LauncherApps#getAppUsageLimit
+ *
+ * @param observerId A unique id associated with the group of apps to be monitored. There can
+ * be multiple groups with common packages and different time limits.
+ * @param observedEntities The list of packages and token to observe for usage time. Cannot be
+ * null and must include at least one package or token.
+ * @param timeLimit The total time the set of apps can be in the foreground before the
+ * callbackIntent is delivered. Must be at least one minute.
+ * @param timeUnit The unit for time specified in {@code timeLimit}. Cannot be null.
+ * @param callbackIntent The PendingIntent that will be dispatched when the usage limit is
+ * exceeded by the group of apps. The delivered Intent will also contain
+ * the extras {@link #EXTRA_OBSERVER_ID}, {@link #EXTRA_TIME_LIMIT} and
+ * {@link #EXTRA_TIME_USED}. Cannot be null.
+ * @throws SecurityException if the caller doesn't have both SUSPEND_APPS and OBSERVE_APP_USAGE
+ * permissions.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.SUSPEND_APPS,
+ android.Manifest.permission.OBSERVE_APP_USAGE})
+ public void registerAppUsageLimitObserver(int observerId, @NonNull String[] observedEntities,
+ long timeLimit, @NonNull TimeUnit timeUnit, @NonNull PendingIntent callbackIntent) {
+ try {
+ mService.registerAppUsageLimitObserver(observerId, observedEntities,
+ timeUnit.toMillis(timeLimit), callbackIntent, mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Unregister the app usage limit observer specified by the {@code observerId}.
+ * This will only apply to any observer registered by this application. Unregistering
+ * an observer that was already unregistered or never registered will have no effect.
+ *
+ * @param observerId The id of the observer that was previously registered.
+ * @throws SecurityException if the caller doesn't have both SUSPEND_APPS and OBSERVE_APP_USAGE
+ * permissions.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.SUSPEND_APPS,
+ android.Manifest.permission.OBSERVE_APP_USAGE})
+ public void unregisterAppUsageLimitObserver(int observerId) {
+ try {
+ mService.unregisterAppUsageLimitObserver(observerId, mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Report usage associated with a particular {@code token} has started. Tokens are app defined
* strings used to represent usage of in-app features. Apps with the {@link
* android.Manifest.permission#OBSERVE_APP_USAGE} permission can register time limit observers
@@ -743,6 +811,7 @@ public final class UsageStatsManager {
* and usage will be considered stopped if the activity stops or crashes.
* @see #registerAppUsageObserver
* @see #registerUsageSessionObserver
+ * @see #registerAppUsageLimitObserver
*
* @param activity The activity {@code token} is associated with.
* @param token The token to report usage against.
@@ -766,6 +835,7 @@ public final class UsageStatsManager {
* {@code activity} and usage will be considered stopped if the activity stops or crashes.
* @see #registerAppUsageObserver
* @see #registerUsageSessionObserver
+ * @see #registerAppUsageLimitObserver
*
* @param activity The activity {@code token} is associated with.
* @param token The token to report usage against.
diff --git a/core/java/android/app/usage/UsageStatsManagerInternal.java b/core/java/android/app/usage/UsageStatsManagerInternal.java
index d2d0cf9ca90b..3d3c03ae3daa 100644
--- a/core/java/android/app/usage/UsageStatsManagerInternal.java
+++ b/core/java/android/app/usage/UsageStatsManagerInternal.java
@@ -20,6 +20,7 @@ import android.annotation.UserIdInt;
import android.app.usage.UsageStatsManager.StandbyBuckets;
import android.content.ComponentName;
import android.content.res.Configuration;
+import android.os.UserHandle;
import java.util.List;
import java.util.Set;
@@ -270,4 +271,40 @@ public abstract class UsageStatsManagerInternal {
* @param userId which user the app is associated with
*/
public abstract void reportExemptedSyncStart(String packageName, @UserIdInt int userId);
+
+ /**
+ * Returns an object describing the app usage limit for the given package which was set via
+ * {@link UsageStatsManager#registerAppUsageLimitObserver}.
+ * If there are multiple limits that apply to the package, the one with the smallest
+ * time remaining will be returned.
+ *
+ * @param packageName name of the package whose app usage limit will be returned
+ * @param user the user associated with the limit
+ * @return an {@link AppUsageLimitData} object describing the app time limit containing
+ * the given package, with the smallest time remaining.
+ */
+ public abstract AppUsageLimitData getAppUsageLimit(String packageName, UserHandle user);
+
+ /** A class which is used to share the usage limit data for an app or a group of apps. */
+ public static class AppUsageLimitData {
+ private final boolean mGroupLimit;
+ private final long mTotalUsageLimit;
+ private final long mUsageRemaining;
+
+ public AppUsageLimitData(boolean groupLimit, long totalUsageLimit, long usageRemaining) {
+ this.mGroupLimit = groupLimit;
+ this.mTotalUsageLimit = totalUsageLimit;
+ this.mUsageRemaining = usageRemaining;
+ }
+
+ public boolean isGroupLimit() {
+ return mGroupLimit;
+ }
+ public long getTotalUsageLimit() {
+ return mTotalUsageLimit;
+ }
+ public long getUsageRemaining() {
+ return mUsageRemaining;
+ }
+ }
}
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 97bc0796e9ce..ab8c196edccd 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -1900,6 +1900,20 @@ public final class BluetoothAdapter {
}
/**
+ * Return true if Hearing Aid Profile is supported.
+ *
+ * @return true if phone supports Hearing Aid Profile
+ */
+ private boolean isHearingAidProfileSupported() {
+ try {
+ return mManagerService.isHearingAidProfileSupported();
+ } catch (RemoteException e) {
+ Log.e(TAG, "remote expection when calling isHearingAidProfileSupported", e);
+ return false;
+ }
+ }
+
+ /**
* Get the maximum number of connected audio devices.
*
* @return the maximum number of connected audio devices
@@ -2051,6 +2065,11 @@ public final class BluetoothAdapter {
supportedProfiles.add(i);
}
}
+ } else {
+ // Bluetooth is disabled. Just fill in known supported Profiles
+ if (isHearingAidProfileSupported()) {
+ supportedProfiles.add(BluetoothProfile.HEARING_AID);
+ }
}
}
} catch (RemoteException e) {
@@ -2468,15 +2487,16 @@ public final class BluetoothAdapter {
* Get the profile proxy object associated with the profile.
*
* <p>Profile can be one of {@link BluetoothProfile#HEADSET}, {@link BluetoothProfile#A2DP},
- * {@link BluetoothProfile#GATT}, or {@link BluetoothProfile#GATT_SERVER}. Clients must
- * implement {@link BluetoothProfile.ServiceListener} to get notified of the connection status
- * and to get the proxy object.
+ * {@link BluetoothProfile#GATT}, {@link BluetoothProfile#HEARING_AID}, or {@link
+ * BluetoothProfile#GATT_SERVER}. Clients must implement {@link
+ * BluetoothProfile.ServiceListener} to get notified of the connection status and to get the
+ * proxy object.
*
* @param context Context of the application
* @param listener The service Listener for connection callbacks.
* @param profile The Bluetooth profile; either {@link BluetoothProfile#HEADSET},
- * {@link BluetoothProfile#A2DP}. {@link BluetoothProfile#GATT} or
- * {@link BluetoothProfile#GATT_SERVER}.
+ * {@link BluetoothProfile#A2DP}, {@link BluetoothProfile#GATT}, {@link
+ * BluetoothProfile#HEARING_AID} or {@link BluetoothProfile#GATT_SERVER}.
* @return true on success, false on error
*/
public boolean getProfileProxy(Context context, BluetoothProfile.ServiceListener listener,
@@ -2525,8 +2545,11 @@ public final class BluetoothAdapter {
BluetoothHidDevice hidDevice = new BluetoothHidDevice(context, listener);
return true;
} else if (profile == BluetoothProfile.HEARING_AID) {
- BluetoothHearingAid hearingAid = new BluetoothHearingAid(context, listener);
- return true;
+ if (isHearingAidProfileSupported()) {
+ BluetoothHearingAid hearingAid = new BluetoothHearingAid(context, listener);
+ return true;
+ }
+ return false;
} else {
return false;
}
@@ -3253,7 +3276,7 @@ public final class BluetoothAdapter {
* @hide
*/
@SystemApi
- public abstract class MetadataListener {
+ public abstract static class MetadataListener {
/**
* Callback triggered if the metadata of {@link BluetoothDevice} registered in
* {@link #registerMetadataListener}.
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index 17cf702bbdea..4d8dc35d7148 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -532,6 +532,28 @@ public final class BluetoothDevice implements Parcelable {
"android.bluetooth.device.action.CONNECTION_ACCESS_CANCEL";
/**
+ * Intent to broadcast silence mode changed.
+ * Alway contains the extra field {@link #EXTRA_DEVICE}
+ * Alway contains the extra field {@link #EXTRA_SILENCE_ENABLED}
+ *
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ @SystemApi
+ public static final String ACTION_SILENCE_MODE_CHANGED =
+ "android.bluetooth.device.action.SILENCE_MODE_CHANGED";
+
+ /**
+ * Used as an extra field in {@link #ACTION_SILENCE_MODE_CHANGED} intent,
+ * contains whether device is in silence mode as boolean.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String EXTRA_SILENCE_ENABLED =
+ "android.bluetooth.device.extra.SILENCE_ENABLED";
+
+ /**
* Used as an extra field in {@link #ACTION_CONNECTION_ACCESS_REQUEST} intent.
*
* @hide
@@ -1592,6 +1614,70 @@ public final class BluetoothDevice implements Parcelable {
}
/**
+ * Set the Bluetooth device silence mode.
+ *
+ * When the {@link BluetoothDevice} enters silence mode, and the {@link BluetoothDevice}
+ * is an active device (for A2DP or HFP), the active device for that profile
+ * will be set to null.
+ * If the {@link BluetoothDevice} exits silence mode while the A2DP or HFP
+ * active device is null, the {@link BluetoothDevice} will be set as the
+ * active device for that profile.
+ * If the {@link BluetoothDevice} is disconnected, it exits silence mode.
+ * If the {@link BluetoothDevice} is set as the active device for A2DP or
+ * HFP, while silence mode is enabled, then the device will exit silence mode.
+ * If the {@link BluetoothDevice} is in silence mode, AVRCP position change
+ * event and HFP AG indicators will be disabled.
+ * If the {@link BluetoothDevice} is not connected with A2DP or HFP, it cannot
+ * enter silence mode.
+ *
+ * <p> Requires {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED}.
+ *
+ * @param silence true to enter silence mode, false to exit
+ * @return true on success, false on error.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
+ public boolean setSilenceMode(boolean silence) {
+ final IBluetooth service = sService;
+ if (service == null) {
+ return false;
+ }
+ try {
+ if (getSilenceMode() == silence) {
+ return true;
+ }
+ return service.setSilenceMode(this, silence);
+ } catch (RemoteException e) {
+ Log.e(TAG, "setSilenceMode fail", e);
+ return false;
+ }
+ }
+
+ /**
+ * Get the device silence mode status
+ *
+ * <p> Requires {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED}.
+ *
+ * @return true on device in silence mode, otherwise false.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
+ public boolean getSilenceMode() {
+ final IBluetooth service = sService;
+ if (service == null) {
+ return false;
+ }
+ try {
+ return service.getSilenceMode(this);
+ } catch (RemoteException e) {
+ Log.e(TAG, "getSilenceMode fail", e);
+ return false;
+ }
+ }
+
+ /**
* Sets whether the phonebook access is allowed to this device.
* <p>Requires {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED}.
*
diff --git a/core/java/android/bluetooth/BluetoothHearingAid.java b/core/java/android/bluetooth/BluetoothHearingAid.java
index 6ed7942492b2..82cc1bcf053c 100644
--- a/core/java/android/bluetooth/BluetoothHearingAid.java
+++ b/core/java/android/bluetooth/BluetoothHearingAid.java
@@ -39,15 +39,14 @@ import java.util.List;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
- * This class provides the public APIs to control the Bluetooth Hearing Aid
- * profile.
+ * This class provides the public APIs to control the Hearing Aid profile.
*
* <p>BluetoothHearingAid is a proxy object for controlling the Bluetooth Hearing Aid
* Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get
* the BluetoothHearingAid proxy object.
*
- * <p> Each method is protected with its appropriate permission.
- * @hide
+ * <p> Android only supports one set of connected Bluetooth Hearing Aid device at a time. Each
+ * method is protected with its appropriate permission.
*/
public final class BluetoothHearingAid implements BluetoothProfile {
private static final String TAG = "BluetoothHearingAid";
@@ -56,7 +55,8 @@ public final class BluetoothHearingAid implements BluetoothProfile {
/**
* Intent used to broadcast the change in connection state of the Hearing Aid
- * profile.
+ * profile. Please note that in the binaural case, there will be two different LE devices for
+ * the left and right side and each device will have their own connection state changes.S
*
* <p>This intent will have 3 extras:
* <ul>
@@ -77,27 +77,6 @@ public final class BluetoothHearingAid implements BluetoothProfile {
"android.bluetooth.hearingaid.profile.action.CONNECTION_STATE_CHANGED";
/**
- * Intent used to broadcast the change in the Playing state of the Hearing Aid
- * profile.
- *
- * <p>This intent will have 3 extras:
- * <ul>
- * <li> {@link #EXTRA_STATE} - The current state of the profile. </li>
- * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile. </li>
- * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
- * </ul>
- *
- * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
- * {@link #STATE_PLAYING}, {@link #STATE_NOT_PLAYING},
- *
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
- * receive.
- */
- @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
- public static final String ACTION_PLAYING_STATE_CHANGED =
- "android.bluetooth.hearingaid.profile.action.PLAYING_STATE_CHANGED";
-
- /**
* Intent used to broadcast the selection of a connected device as active.
*
* <p>This intent will have one extra:
@@ -108,6 +87,8 @@ public final class BluetoothHearingAid implements BluetoothProfile {
*
* <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
* receive.
+ *
+ * @hide
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
@UnsupportedAppUsage
@@ -115,32 +96,38 @@ public final class BluetoothHearingAid implements BluetoothProfile {
"android.bluetooth.hearingaid.profile.action.ACTIVE_DEVICE_CHANGED";
/**
- * Hearing Aid device is streaming music. This state can be one of
- * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of
- * {@link #ACTION_PLAYING_STATE_CHANGED} intent.
+ * This device represents Left Hearing Aid.
+ *
+ * @hide
*/
- public static final int STATE_PLAYING = 10;
+ public static final int SIDE_LEFT = IBluetoothHearingAid.SIDE_LEFT;
/**
- * Hearing Aid device is NOT streaming music. This state can be one of
- * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of
- * {@link #ACTION_PLAYING_STATE_CHANGED} intent.
+ * This device represents Right Hearing Aid.
+ *
+ * @hide
*/
- public static final int STATE_NOT_PLAYING = 11;
-
- /** This device represents Left Hearing Aid. */
- public static final int SIDE_LEFT = IBluetoothHearingAid.SIDE_LEFT;
-
- /** This device represents Right Hearing Aid. */
public static final int SIDE_RIGHT = IBluetoothHearingAid.SIDE_RIGHT;
- /** This device is Monaural. */
+ /**
+ * This device is Monaural.
+ *
+ * @hide
+ */
public static final int MODE_MONAURAL = IBluetoothHearingAid.MODE_MONAURAL;
- /** This device is Binaural (should receive only left or right audio). */
+ /**
+ * This device is Binaural (should receive only left or right audio).
+ *
+ * @hide
+ */
public static final int MODE_BINAURAL = IBluetoothHearingAid.MODE_BINAURAL;
- /** Can't read ClientID for this device */
+ /**
+ * Indicates the HiSyncID could not be read and is unavailable.
+ *
+ * @hide
+ */
public static final long HI_SYNC_ID_INVALID = IBluetoothHearingAid.HI_SYNC_ID_INVALID;
private Context mContext;
@@ -236,12 +223,6 @@ public final class BluetoothHearingAid implements BluetoothProfile {
}
}
- @Override
- public void finalize() {
- // The empty finalize needs to be kept or the
- // cts signature tests would fail.
- }
-
/**
* Initiate connection to a profile of the remote bluetooth device.
*
@@ -538,10 +519,6 @@ public final class BluetoothHearingAid implements BluetoothProfile {
return "connected";
case STATE_DISCONNECTING:
return "disconnecting";
- case STATE_PLAYING:
- return "playing";
- case STATE_NOT_PLAYING:
- return "not playing";
default:
return "<unknown state " + state + ">";
}
diff --git a/core/java/android/bluetooth/BluetoothProfile.java b/core/java/android/bluetooth/BluetoothProfile.java
index 3c87c739e1f6..b8670dbeadad 100644
--- a/core/java/android/bluetooth/BluetoothProfile.java
+++ b/core/java/android/bluetooth/BluetoothProfile.java
@@ -185,7 +185,6 @@ public interface BluetoothProfile {
/**
* Hearing Aid Device
*
- * @hide
*/
int HEARING_AID = 21;
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index b46203c7a682..edd765b05415 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -3047,6 +3047,13 @@ public class Intent implements Parcelable, Cloneable {
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_MEDIA_SCANNER_SCAN_FILE = "android.intent.action.MEDIA_SCANNER_SCAN_FILE";
+ /**
+ * Broadcast Action: Request the media scanner to scan a storage volume and add it to the media database.
+ * The path to the storage volume is contained in the Intent.mData field.
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_MEDIA_SCANNER_SCAN_VOLUME = "android.intent.action.MEDIA_SCANNER_SCAN_VOLUME";
+
/**
* Broadcast Action: The "Media Button" was pressed. Includes a single
* extra field, {@link #EXTRA_KEY_EVENT}, containing the key event that
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index 3cfbe0c6f08a..47034a6df8a7 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -508,7 +508,7 @@ public class ActivityInfo extends ComponentInfo implements Parcelable {
/**
* Bit in {@link #privateFlags} indicating if the activity should be shown when locked in case
* an activity behind this can also be shown when locked.
- * See android.R.attr#inheritShowWhenLocked
+ * See {@link android.R.attr#inheritShowWhenLocked}.
* @hide
*/
public static final int FLAG_INHERIT_SHOW_WHEN_LOCKED = 0x1;
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 576466f21bcb..5d6d1444eaf3 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -1955,6 +1955,11 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
return (privateFlags & ApplicationInfo.PRIVATE_FLAG_PRODUCT_SERVICES) != 0;
}
+ /** @hide */
+ public boolean isCodeIntegrityPreferred() {
+ return (privateFlags & PRIVATE_FLAG_PREFER_CODE_INTEGRITY) != 0;
+ }
+
/**
* Returns whether or not this application was installed as a virtual preload.
*/
diff --git a/core/java/android/content/pm/ILauncherApps.aidl b/core/java/android/content/pm/ILauncherApps.aidl
index db2b6fd235d3..d1bc37791d40 100644
--- a/core/java/android/content/pm/ILauncherApps.aidl
+++ b/core/java/android/content/pm/ILauncherApps.aidl
@@ -23,6 +23,7 @@ import android.content.IntentSender;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.IOnAppsChangedListener;
+import android.content.pm.LauncherApps;
import android.content.pm.ParceledListSlice;
import android.content.pm.ResolveInfo;
import android.content.pm.ShortcutInfo;
@@ -56,6 +57,9 @@ interface ILauncherApps {
ApplicationInfo getApplicationInfo(
String callingPackage, String packageName, int flags, in UserHandle user);
+ LauncherApps.AppUsageLimit getAppUsageLimit(String callingPackage, String packageName,
+ in UserHandle user);
+
ParceledListSlice getShortcuts(String callingPackage, long changedSince, String packageName,
in List shortcutIds, in ComponentName componentName, int flags, in UserHandle user);
void pinShortcuts(String callingPackage, String packageName, in List<String> shortcutIds,
diff --git a/core/java/android/content/pm/LauncherApps.aidl b/core/java/android/content/pm/LauncherApps.aidl
new file mode 100644
index 000000000000..1d98ad1abd32
--- /dev/null
+++ b/core/java/android/content/pm/LauncherApps.aidl
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm;
+
+parcelable LauncherApps.AppUsageLimit;
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index 766c5660012b..89630e15972e 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -758,6 +758,27 @@ public class LauncherApps {
}
/**
+ * Returns an object describing the app usage limit for the given package.
+ * If there are multiple limits that apply to the package, the one with the smallest
+ * time remaining will be returned.
+ *
+ * @param packageName name of the package whose app usage limit will be returned
+ * @param user the user of the package
+ *
+ * @return an {@link AppUsageLimit} object describing the app time limit containing
+ * the given package with the smallest time remaining, or {@code null} if none exist.
+ * @throws SecurityException when the caller is not the active launcher.
+ */
+ @Nullable
+ public LauncherApps.AppUsageLimit getAppUsageLimit(String packageName, UserHandle user) {
+ try {
+ return mService.getAppUsageLimit(mContext.getPackageName(), packageName, user);
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Checks if the activity exists and it enabled for a profile.
*
* @param component The activity to check.
@@ -1632,4 +1653,86 @@ public class LauncherApps {
return 0;
}
}
+
+ /**
+ * A class that encapsulates information about the usage limit set for an app or
+ * a group of apps.
+ *
+ * <p>The launcher can query specifics about the usage limit such as if it is a group limit,
+ * how much usage time the limit has, and how much of the total usage time is remaining
+ * via the APIs available in this class.
+ *
+ * @see #getAppUsageLimit(String, UserHandle)
+ */
+ public static final class AppUsageLimit implements Parcelable {
+ private final boolean mGroupLimit;
+ private final long mTotalUsageLimit;
+ private final long mUsageRemaining;
+
+ /** @hide */
+ public AppUsageLimit(boolean groupLimit, long totalUsageLimit, long usageRemaining) {
+ this.mGroupLimit = groupLimit;
+ this.mTotalUsageLimit = totalUsageLimit;
+ this.mUsageRemaining = usageRemaining;
+ }
+
+ /**
+ * Returns whether this limit refers to a group of apps.
+ *
+ * @return {@code TRUE} if the limit refers to a group of apps, {@code FALSE} otherwise.
+ * @hide
+ */
+ public boolean isGroupLimit() {
+ return mGroupLimit;
+ }
+
+ /**
+ * Returns the total usage limit in milliseconds set for an app or a group of apps.
+ *
+ * @return the total usage limit in milliseconds
+ */
+ public long getTotalUsageLimit() {
+ return mTotalUsageLimit;
+ }
+
+ /**
+ * Returns the usage remaining in milliseconds for an app or the group of apps
+ * this limit refers to.
+ *
+ * @return the usage remaining in milliseconds
+ */
+ public long getUsageRemaining() {
+ return mUsageRemaining;
+ }
+
+ private AppUsageLimit(Parcel source) {
+ mGroupLimit = source.readBoolean();
+ mTotalUsageLimit = source.readLong();
+ mUsageRemaining = source.readLong();
+ }
+
+ public static final Creator<AppUsageLimit> CREATOR = new Creator<AppUsageLimit>() {
+ @Override
+ public AppUsageLimit createFromParcel(Parcel source) {
+ return new AppUsageLimit(source);
+ }
+
+ @Override
+ public AppUsageLimit[] newArray(int size) {
+ return new AppUsageLimit[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeBoolean(mGroupLimit);
+ dest.writeLong(mTotalUsageLimit);
+ dest.writeLong(mUsageRemaining);
+ }
+ }
}
diff --git a/core/java/android/content/pm/OrgApacheHttpLegacyUpdater.java b/core/java/android/content/pm/OrgApacheHttpLegacyUpdater.java
index 81e4105febee..7790067b03de 100644
--- a/core/java/android/content/pm/OrgApacheHttpLegacyUpdater.java
+++ b/core/java/android/content/pm/OrgApacheHttpLegacyUpdater.java
@@ -25,12 +25,6 @@ import com.android.internal.annotations.VisibleForTesting;
* Updates a package to ensure that if it targets < P that the org.apache.http.legacy library is
* included by default.
*
- * <p>This is separated out so that it can be conditionally included at build time depending on
- * whether org.apache.http.legacy is on the bootclasspath or not. In order to include this at
- * build time, and remove org.apache.http.legacy from the bootclasspath pass
- * REMOVE_OAHL_FROM_BCP=true on the build command line, otherwise this class will not be included
- * and the
- *
* @hide
*/
@VisibleForTesting
diff --git a/core/java/android/content/pm/PackageBackwardCompatibility.java b/core/java/android/content/pm/PackageBackwardCompatibility.java
index 03eefedd2b30..b19196a9b636 100644
--- a/core/java/android/content/pm/PackageBackwardCompatibility.java
+++ b/core/java/android/content/pm/PackageBackwardCompatibility.java
@@ -45,13 +45,9 @@ public class PackageBackwardCompatibility extends PackageSharedLibraryUpdater {
static {
final List<PackageSharedLibraryUpdater> packageUpdaters = new ArrayList<>();
- // Attempt to load and add the optional updater that will only be available when
- // REMOVE_OAHL_FROM_BCP=true. If that could not be found then add the default updater that
- // will remove any references to org.apache.http.library from the package so that it does
- // not try and load the library when it is on the bootclasspath.
- boolean bootClassPathContainsOAHL = !addOptionalUpdater(packageUpdaters,
- "android.content.pm.OrgApacheHttpLegacyUpdater",
- RemoveUnnecessaryOrgApacheHttpLegacyLibrary::new);
+ // Automatically add the org.apache.http.legacy library to the app classpath if the app
+ // targets < P.
+ packageUpdaters.add(new OrgApacheHttpLegacyUpdater());
packageUpdaters.add(new AndroidHidlUpdater());
@@ -70,7 +66,7 @@ public class PackageBackwardCompatibility extends PackageSharedLibraryUpdater {
PackageSharedLibraryUpdater[] updaterArray = packageUpdaters
.toArray(new PackageSharedLibraryUpdater[0]);
INSTANCE = new PackageBackwardCompatibility(
- bootClassPathContainsOAHL, bootClassPathContainsATB, updaterArray);
+ bootClassPathContainsATB, updaterArray);
}
/**
@@ -116,15 +112,12 @@ public class PackageBackwardCompatibility extends PackageSharedLibraryUpdater {
return INSTANCE;
}
- private final boolean mBootClassPathContainsOAHL;
-
private final boolean mBootClassPathContainsATB;
private final PackageSharedLibraryUpdater[] mPackageUpdaters;
- public PackageBackwardCompatibility(boolean bootClassPathContainsOAHL,
+ public PackageBackwardCompatibility(
boolean bootClassPathContainsATB, PackageSharedLibraryUpdater[] packageUpdaters) {
- this.mBootClassPathContainsOAHL = bootClassPathContainsOAHL;
this.mBootClassPathContainsATB = bootClassPathContainsATB;
this.mPackageUpdaters = packageUpdaters;
}
@@ -148,14 +141,6 @@ public class PackageBackwardCompatibility extends PackageSharedLibraryUpdater {
}
/**
- * True if the org.apache.http.legacy is on the bootclasspath, false otherwise.
- */
- @VisibleForTesting
- public static boolean bootClassPathContainsOAHL() {
- return INSTANCE.mBootClassPathContainsOAHL;
- }
-
- /**
* True if the android.test.base is on the bootclasspath, false otherwise.
*/
@VisibleForTesting
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 94b7c4538c51..73b1f4e7e536 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -1543,6 +1543,13 @@ public class PackageInstaller {
this.isStaged = true;
}
+ /**
+ * Set this session to be installing an APEX package.
+ */
+ public void setInstallAsApex() {
+ installFlags |= PackageManager.INSTALL_APEX;
+ }
+
/** {@hide} */
public void dump(IndentingPrintWriter pw) {
pw.printPair("mode", mode);
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 9e2f31684395..783ee641d1f7 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -2223,6 +2223,13 @@ public abstract class PackageManager {
public static final String FEATURE_TELEPHONY_MBMS = "android.hardware.telephony.mbms";
/**
+ * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: The device
+ * supports attaching to IMS implementations using the ImsService API in telephony.
+ */
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_TELEPHONY_IMS = "android.hardware.telephony.ims";
+
+ /**
* Feature for {@link #getSystemAvailableFeatures} and
* {@link #hasSystemFeature}: The device supports connecting to USB devices
* as the USB host.
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 88a240f240cd..eb59cfc0fc4b 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -8512,7 +8512,9 @@ public class PackageParser {
collectCerts ? PackageParser.PARSE_COLLECT_CERTIFICATES : 0);
pi.packageName = apk.packageName;
+ ai.packageName = apk.packageName;
pi.setLongVersionCode(apk.getLongVersionCode());
+ ai.setVersionCode(apk.getLongVersionCode());
if (collectCerts) {
if (apk.signingDetails.hasPastSigningCertificates()) {
diff --git a/core/java/android/content/pm/RegisteredServicesCache.java b/core/java/android/content/pm/RegisteredServicesCache.java
index 6e519c1863e8..a8c3b889421b 100644
--- a/core/java/android/content/pm/RegisteredServicesCache.java
+++ b/core/java/android/content/pm/RegisteredServicesCache.java
@@ -475,14 +475,6 @@ public abstract class RegisteredServicesCache<V> {
final List<ResolveInfo> resolveInfos = queryIntentServices(userId);
for (ResolveInfo resolveInfo : resolveInfos) {
try {
- // if this package is not one of those changedUids, we don't need to scan it,
- // since nothing in it changed, so save a call to parseServiceInfo, which
- // can cause a large amount of the package apk to be loaded into memory.
- // if this is the initial scan, changedUids will be null, and containsUid will
- // trivially return true, and will call parseServiceInfo
- if (!containsUid(changedUids, resolveInfo.serviceInfo.applicationInfo.uid)) {
- continue;
- }
ServiceInfo<V> info = parseServiceInfo(resolveInfo);
if (info == null) {
Log.w(TAG, "Unable to load service info " + resolveInfo.toString());
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 105ae6815589..97583089736e 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -3722,12 +3722,12 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
/**
* <p>String containing the ids of the underlying physical cameras.</p>
- * <p>For a logical camera, this is concatenation of all underlying physical camera ids.
- * The null terminator for physical camera id must be preserved so that the whole string
- * can be tokenized using '\0' to generate list of physical camera ids.</p>
- * <p>For example, if the physical camera ids of the logical camera are "2" and "3", the
+ * <p>For a logical camera, this is concatenation of all underlying physical camera IDs.
+ * The null terminator for physical camera ID must be preserved so that the whole string
+ * can be tokenized using '\0' to generate list of physical camera IDs.</p>
+ * <p>For example, if the physical camera IDs of the logical camera are "2" and "3", the
* value of this tag will be ['2', '\0', '3', '\0'].</p>
- * <p>The number of physical camera ids must be no less than 2.</p>
+ * <p>The number of physical camera IDs must be no less than 2.</p>
* <p><b>Units</b>: UTF-8 null-terminated string</p>
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
* <p><b>Limited capability</b> -
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index 402472af65a3..6302aa536214 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -428,6 +428,10 @@ public abstract class CameraMetadata<TKey> {
* <p>If this is supported, {@link CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP android.scaler.streamConfigurationMap} will
* additionally return a min frame duration that is greater than
* zero for each supported size-format combination.</p>
+ * <p>For camera devices with LOGICAL_MULTI_CAMERA capability, when the underlying active
+ * physical camera switches, exposureTime, sensitivity, and lens properties may change
+ * even if AE/AF is locked. However, the overall auto exposure and auto focus experience
+ * for users will be consistent. Refer to LOGICAL_MULTI_CAMERA capability for details.</p>
*
* @see CaptureRequest#BLACK_LEVEL_LOCK
* @see CaptureRequest#CONTROL_AE_LOCK
@@ -485,6 +489,10 @@ public abstract class CameraMetadata<TKey> {
* will accurately report the values applied by AWB in the result.</p>
* <p>A given camera device may also support additional post-processing
* controls, but this capability only covers the above list of controls.</p>
+ * <p>For camera devices with LOGICAL_MULTI_CAMERA capability, when underlying active
+ * physical camera switches, tonemap, white balance, and shading map may change even if
+ * awb is locked. However, the overall post-processing experience for users will be
+ * consistent. Refer to LOGICAL_MULTI_CAMERA capability for details.</p>
*
* @see CaptureRequest#COLOR_CORRECTION_ABERRATION_MODE
* @see CameraCharacteristics#COLOR_CORRECTION_AVAILABLE_ABERRATION_MODES
@@ -847,7 +855,7 @@ public abstract class CameraMetadata<TKey> {
* </li>
* <li>The SENSOR_INFO_TIMESTAMP_SOURCE of the logical device and physical devices must be
* the same.</li>
- * <li>The logical camera device must be LIMITED or higher device.</li>
+ * <li>The logical camera must be LIMITED or higher device.</li>
* </ul>
* <p>Both the logical camera device and its underlying physical devices support the
* mandatory stream combinations required for their device levels.</p>
@@ -867,13 +875,84 @@ public abstract class CameraMetadata<TKey> {
* <p>Using physical streams in place of a logical stream of the same size and format will
* not slow down the frame rate of the capture, as long as the minimum frame duration
* of the physical and logical streams are the same.</p>
+ * <p>A logical camera device's dynamic metadata may contain
+ * {@link CaptureResult#LOGICAL_MULTI_CAMERA_ACTIVE_PHYSICAL_ID android.logicalMultiCamera.activePhysicalId} to notify the application of the current
+ * active physical camera Id. An active physical camera is the physical camera from which
+ * the logical camera's main image data outputs (YUV or RAW) and metadata come from.
+ * In addition, this serves as an indication which physical camera is used to output to
+ * a RAW stream, or in case only physical cameras support RAW, which physical RAW stream
+ * the application should request.</p>
+ * <p>Logical camera's static metadata tags below describe the default active physical
+ * camera. An active physical camera is default if it's used when application directly
+ * uses requests built from a template. All templates will default to the same active
+ * physical camera.</p>
+ * <ul>
+ * <li>{@link CameraCharacteristics#SENSOR_INFO_SENSITIVITY_RANGE android.sensor.info.sensitivityRange}</li>
+ * <li>{@link CameraCharacteristics#SENSOR_INFO_COLOR_FILTER_ARRANGEMENT android.sensor.info.colorFilterArrangement}</li>
+ * <li>{@link CameraCharacteristics#SENSOR_INFO_EXPOSURE_TIME_RANGE android.sensor.info.exposureTimeRange}</li>
+ * <li>{@link CameraCharacteristics#SENSOR_INFO_MAX_FRAME_DURATION android.sensor.info.maxFrameDuration}</li>
+ * <li>{@link CameraCharacteristics#SENSOR_INFO_PHYSICAL_SIZE android.sensor.info.physicalSize}</li>
+ * <li>{@link CameraCharacteristics#SENSOR_INFO_WHITE_LEVEL android.sensor.info.whiteLevel}</li>
+ * <li>{@link CameraCharacteristics#SENSOR_INFO_LENS_SHADING_APPLIED android.sensor.info.lensShadingApplied}</li>
+ * <li>{@link CameraCharacteristics#SENSOR_REFERENCE_ILLUMINANT1 android.sensor.referenceIlluminant1}</li>
+ * <li>{@link CameraCharacteristics#SENSOR_REFERENCE_ILLUMINANT2 android.sensor.referenceIlluminant2}</li>
+ * <li>{@link CameraCharacteristics#SENSOR_CALIBRATION_TRANSFORM1 android.sensor.calibrationTransform1}</li>
+ * <li>{@link CameraCharacteristics#SENSOR_CALIBRATION_TRANSFORM2 android.sensor.calibrationTransform2}</li>
+ * <li>{@link CameraCharacteristics#SENSOR_COLOR_TRANSFORM1 android.sensor.colorTransform1}</li>
+ * <li>{@link CameraCharacteristics#SENSOR_COLOR_TRANSFORM2 android.sensor.colorTransform2}</li>
+ * <li>{@link CameraCharacteristics#SENSOR_FORWARD_MATRIX1 android.sensor.forwardMatrix1}</li>
+ * <li>{@link CameraCharacteristics#SENSOR_FORWARD_MATRIX2 android.sensor.forwardMatrix2}</li>
+ * <li>{@link CameraCharacteristics#SENSOR_BLACK_LEVEL_PATTERN android.sensor.blackLevelPattern}</li>
+ * <li>{@link CameraCharacteristics#SENSOR_MAX_ANALOG_SENSITIVITY android.sensor.maxAnalogSensitivity}</li>
+ * <li>{@link CameraCharacteristics#SENSOR_OPTICAL_BLACK_REGIONS android.sensor.opticalBlackRegions}</li>
+ * <li>{@link CameraCharacteristics#SENSOR_AVAILABLE_TEST_PATTERN_MODES android.sensor.availableTestPatternModes}</li>
+ * <li>{@link CameraCharacteristics#LENS_INFO_HYPERFOCAL_DISTANCE android.lens.info.hyperfocalDistance}</li>
+ * <li>{@link CameraCharacteristics#LENS_INFO_MINIMUM_FOCUS_DISTANCE android.lens.info.minimumFocusDistance}</li>
+ * <li>{@link CameraCharacteristics#LENS_INFO_FOCUS_DISTANCE_CALIBRATION android.lens.info.focusDistanceCalibration}</li>
+ * <li>{@link CameraCharacteristics#LENS_POSE_ROTATION android.lens.poseRotation}</li>
+ * <li>{@link CameraCharacteristics#LENS_POSE_TRANSLATION android.lens.poseTranslation}</li>
+ * <li>{@link CameraCharacteristics#LENS_INTRINSIC_CALIBRATION android.lens.intrinsicCalibration}</li>
+ * <li>{@link CameraCharacteristics#LENS_POSE_REFERENCE android.lens.poseReference}</li>
+ * <li>{@link CameraCharacteristics#LENS_DISTORTION android.lens.distortion}</li>
+ * </ul>
+ * <p>To maintain backward compatibility, the capture request and result metadata tags
+ * required for basic camera functionalities will be solely based on the
+ * logical camera capabiltity. Other request and result metadata tags, on the other
+ * hand, will be based on current active physical camera. For example, the physical
+ * cameras' sensor sensitivity and lens capability could be different from each other.
+ * So when the application manually controls sensor exposure time/gain, or does manual
+ * focus control, it must checks the current active physical camera's exposure, gain,
+ * and focus distance range.</p>
*
* @see CameraCharacteristics#LENS_DISTORTION
+ * @see CameraCharacteristics#LENS_INFO_FOCUS_DISTANCE_CALIBRATION
+ * @see CameraCharacteristics#LENS_INFO_HYPERFOCAL_DISTANCE
+ * @see CameraCharacteristics#LENS_INFO_MINIMUM_FOCUS_DISTANCE
* @see CameraCharacteristics#LENS_INTRINSIC_CALIBRATION
* @see CameraCharacteristics#LENS_POSE_REFERENCE
* @see CameraCharacteristics#LENS_POSE_ROTATION
* @see CameraCharacteristics#LENS_POSE_TRANSLATION
+ * @see CaptureResult#LOGICAL_MULTI_CAMERA_ACTIVE_PHYSICAL_ID
* @see CameraCharacteristics#LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE
+ * @see CameraCharacteristics#SENSOR_AVAILABLE_TEST_PATTERN_MODES
+ * @see CameraCharacteristics#SENSOR_BLACK_LEVEL_PATTERN
+ * @see CameraCharacteristics#SENSOR_CALIBRATION_TRANSFORM1
+ * @see CameraCharacteristics#SENSOR_CALIBRATION_TRANSFORM2
+ * @see CameraCharacteristics#SENSOR_COLOR_TRANSFORM1
+ * @see CameraCharacteristics#SENSOR_COLOR_TRANSFORM2
+ * @see CameraCharacteristics#SENSOR_FORWARD_MATRIX1
+ * @see CameraCharacteristics#SENSOR_FORWARD_MATRIX2
+ * @see CameraCharacteristics#SENSOR_INFO_COLOR_FILTER_ARRANGEMENT
+ * @see CameraCharacteristics#SENSOR_INFO_EXPOSURE_TIME_RANGE
+ * @see CameraCharacteristics#SENSOR_INFO_LENS_SHADING_APPLIED
+ * @see CameraCharacteristics#SENSOR_INFO_MAX_FRAME_DURATION
+ * @see CameraCharacteristics#SENSOR_INFO_PHYSICAL_SIZE
+ * @see CameraCharacteristics#SENSOR_INFO_SENSITIVITY_RANGE
+ * @see CameraCharacteristics#SENSOR_INFO_WHITE_LEVEL
+ * @see CameraCharacteristics#SENSOR_MAX_ANALOG_SENSITIVITY
+ * @see CameraCharacteristics#SENSOR_OPTICAL_BLACK_REGIONS
+ * @see CameraCharacteristics#SENSOR_REFERENCE_ILLUMINANT1
+ * @see CameraCharacteristics#SENSOR_REFERENCE_ILLUMINANT2
* @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
*/
public static final int REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA = 11;
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index 8ebaf2f7db44..5f05cfb459bf 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -2146,7 +2146,6 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
/**
* <p>32 characters describing GPS algorithm to
* include in EXIF.</p>
- * <p><b>Units</b>: UTF-8 null-terminated string</p>
* <p>This key is available on all devices.</p>
* @hide
*/
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index 3d70c516b577..585c597c58e4 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -2470,7 +2470,6 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> {
/**
* <p>32 characters describing GPS algorithm to
* include in EXIF.</p>
- * <p><b>Units</b>: UTF-8 null-terminated string</p>
* <p>This key is available on all devices.</p>
* @hide
*/
@@ -4638,6 +4637,23 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> {
new Key<Float>("android.reprocess.effectiveExposureFactor", float.class);
/**
+ * <p>String containing the ID of the underlying active physical camera.</p>
+ * <p>The ID of the active physical camera that's backing the logical camera. All camera
+ * streams and metadata that are not physical camera specific will be originating from this
+ * physical camera. This must be one of valid physical IDs advertised in the physicalIds
+ * static tag.</p>
+ * <p>For a logical camera made up of physical cameras where each camera's lenses have
+ * different characteristics, the camera device may choose to switch between the physical
+ * cameras when application changes FOCAL_LENGTH or SCALER_CROP_REGION.
+ * At the time of lens switch, this result metadata reflects the new active physical camera
+ * ID.</p>
+ * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+ */
+ @PublicKey
+ public static final Key<String> LOGICAL_MULTI_CAMERA_ACTIVE_PHYSICAL_ID =
+ new Key<String>("android.logicalMultiCamera.activePhysicalId", String.class);
+
+ /**
* <p>Mode of operation for the lens distortion correction block.</p>
* <p>The lens distortion correction block attempts to improve image quality by fixing
* radial, tangential, or other geometric aberrations in the camera device's optics. If
diff --git a/core/java/android/hardware/display/ColorDisplayManager.java b/core/java/android/hardware/display/ColorDisplayManager.java
index fa335c8b9924..28e953593f23 100644
--- a/core/java/android/hardware/display/ColorDisplayManager.java
+++ b/core/java/android/hardware/display/ColorDisplayManager.java
@@ -24,15 +24,19 @@ import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.content.Context;
+import android.metrics.LogMaker;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.ServiceManager.ServiceNotFoundException;
import com.android.internal.R;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.time.LocalTime;
/**
* Manages the display's color transforms and modes.
@@ -81,7 +85,46 @@ public final class ColorDisplayManager {
@SystemApi
public static final int CAPABILITY_HARDWARE_ACCELERATION_PER_APP = 0x4;
+ /**
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({ AUTO_MODE_DISABLED, AUTO_MODE_CUSTOM_TIME, AUTO_MODE_TWILIGHT })
+ public @interface AutoMode {}
+
+ /**
+ * Auto mode value to prevent Night display from being automatically activated. It can still
+ * be activated manually via {@link #setNightDisplayActivated(boolean)}.
+ *
+ * @see #setNightDisplayAutoMode(int)
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int AUTO_MODE_DISABLED = 0;
+ /**
+ * Auto mode value to automatically activate Night display at a specific start and end time.
+ *
+ * @see #setNightDisplayAutoMode(int)
+ * @see #setNightDisplayCustomStartTime(LocalTime)
+ * @see #setNightDisplayCustomEndTime(LocalTime)
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int AUTO_MODE_CUSTOM_TIME = 1;
+ /**
+ * Auto mode value to automatically activate Night display from sunset to sunrise.
+ *
+ * @see #setNightDisplayAutoMode(int)
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int AUTO_MODE_TWILIGHT = 2;
+
private final ColorDisplayManagerInternal mManager;
+ private MetricsLogger mMetricsLogger;
/**
* @hide
@@ -91,6 +134,158 @@ public final class ColorDisplayManager {
}
/**
+ * (De)activates the night display transform.
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS)
+ public boolean setNightDisplayActivated(boolean activated) {
+ return mManager.setNightDisplayActivated(activated);
+ }
+
+ /**
+ * Returns whether the night display transform is currently active.
+ *
+ * @hide
+ */
+ public boolean isNightDisplayActivated() {
+ return mManager.isNightDisplayActivated();
+ }
+
+ /**
+ * Sets the color temperature of the night display transform.
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS)
+ public boolean setNightDisplayColorTemperature(int temperature) {
+ return mManager.setNightDisplayColorTemperature(temperature);
+ }
+
+ /**
+ * Gets the color temperature of the night display transform.
+ *
+ * @hide
+ */
+ public int getNightDisplayColorTemperature() {
+ return mManager.getNightDisplayColorTemperature();
+ }
+
+ /**
+ * Returns the current auto mode value controlling when Night display will be automatically
+ * activated. One of {@link #AUTO_MODE_DISABLED}, {@link #AUTO_MODE_CUSTOM_TIME}, or
+ * {@link #AUTO_MODE_TWILIGHT}.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS)
+ public @AutoMode int getNightDisplayAutoMode() {
+ return mManager.getNightDisplayAutoMode();
+ }
+
+ /**
+ * Returns the current auto mode value, without validation, or {@code 1} if the auto mode has
+ * never been set.
+ *
+ * @hide
+ */
+ public int getNightDisplayAutoModeRaw() {
+ return mManager.getNightDisplayAutoModeRaw();
+ }
+
+ /**
+ * Sets the current auto mode value controlling when Night display will be automatically
+ * activated. One of {@link #AUTO_MODE_DISABLED}, {@link #AUTO_MODE_CUSTOM_TIME}, or
+ * {@link #AUTO_MODE_TWILIGHT}.
+ *
+ * @param autoMode the new auto mode to use
+ * @return {@code true} if new auto mode was set successfully
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS)
+ public boolean setNightDisplayAutoMode(@AutoMode int autoMode) {
+ if (autoMode != AUTO_MODE_DISABLED
+ && autoMode != AUTO_MODE_CUSTOM_TIME
+ && autoMode != AUTO_MODE_TWILIGHT) {
+ throw new IllegalArgumentException("Invalid autoMode: " + autoMode);
+ }
+ if (mManager.getNightDisplayAutoMode() != autoMode) {
+ getMetricsLogger().write(new LogMaker(
+ MetricsEvent.ACTION_NIGHT_DISPLAY_AUTO_MODE_CHANGED)
+ .setType(MetricsEvent.TYPE_ACTION)
+ .setSubtype(autoMode));
+ }
+ return mManager.setNightDisplayAutoMode(autoMode);
+ }
+
+ /**
+ * Returns the local time when Night display will be automatically activated when using
+ * {@link ColorDisplayManager#AUTO_MODE_CUSTOM_TIME}.
+ *
+ * @hide
+ */
+ public @NonNull LocalTime getNightDisplayCustomStartTime() {
+ return mManager.getNightDisplayCustomStartTime().getLocalTime();
+ }
+
+ /**
+ * Sets the local time when Night display will be automatically activated when using
+ * {@link ColorDisplayManager#AUTO_MODE_CUSTOM_TIME}.
+ *
+ * @param startTime the local time to automatically activate Night display
+ * @return {@code true} if the new custom start time was set successfully
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS)
+ public boolean setNightDisplayCustomStartTime(@NonNull LocalTime startTime) {
+ if (startTime == null) {
+ throw new IllegalArgumentException("startTime cannot be null");
+ }
+ getMetricsLogger().write(new LogMaker(
+ MetricsEvent.ACTION_NIGHT_DISPLAY_AUTO_MODE_CUSTOM_TIME_CHANGED)
+ .setType(MetricsEvent.TYPE_ACTION)
+ .setSubtype(0));
+ return mManager.setNightDisplayCustomStartTime(new Time(startTime));
+ }
+
+ /**
+ * Returns the local time when Night display will be automatically deactivated when using
+ * {@link #AUTO_MODE_CUSTOM_TIME}.
+ *
+ * @hide
+ */
+ public @NonNull LocalTime getNightDisplayCustomEndTime() {
+ return mManager.getNightDisplayCustomEndTime().getLocalTime();
+ }
+
+ /**
+ * Sets the local time when Night display will be automatically deactivated when using
+ * {@link #AUTO_MODE_CUSTOM_TIME}.
+ *
+ * @param endTime the local time to automatically deactivate Night display
+ * @return {@code true} if the new custom end time was set successfully
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS)
+ public boolean setNightDisplayCustomEndTime(@NonNull LocalTime endTime) {
+ if (endTime == null) {
+ throw new IllegalArgumentException("endTime cannot be null");
+ }
+ getMetricsLogger().write(new LogMaker(
+ MetricsEvent.ACTION_NIGHT_DISPLAY_AUTO_MODE_CUSTOM_TIME_CHANGED)
+ .setType(MetricsEvent.TYPE_ACTION)
+ .setSubtype(1));
+ return mManager.setNightDisplayCustomEndTime(new Time(endTime));
+ }
+
+ /**
* Returns whether the device has a wide color gamut display.
*
* @hide
@@ -138,6 +333,28 @@ public final class ColorDisplayManager {
}
/**
+ * Returns the minimum allowed color temperature (in Kelvin) to tint the display when
+ * activated.
+ *
+ * @hide
+ */
+ public static int getMinimumColorTemperature(Context context) {
+ return context.getResources()
+ .getInteger(R.integer.config_nightDisplayColorTemperatureMin);
+ }
+
+ /**
+ * Returns the maximum allowed color temperature (in Kelvin) to tint the display when
+ * activated.
+ *
+ * @hide
+ */
+ public static int getMaximumColorTemperature(Context context) {
+ return context.getResources()
+ .getInteger(R.integer.config_nightDisplayColorTemperatureMax);
+ }
+
+ /**
* Returns {@code true} if display white balance is supported by the device.
*
* @hide
@@ -167,6 +384,13 @@ public final class ColorDisplayManager {
return mManager.getTransformCapabilities();
}
+ private MetricsLogger getMetricsLogger() {
+ if (mMetricsLogger == null) {
+ mMetricsLogger = new MetricsLogger();
+ }
+ return mMetricsLogger;
+ }
+
private static class ColorDisplayManagerInternal {
private static ColorDisplayManagerInternal sInstance;
@@ -192,6 +416,94 @@ public final class ColorDisplayManager {
}
}
+ boolean isNightDisplayActivated() {
+ try {
+ return mCdm.isNightDisplayActivated();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ boolean setNightDisplayActivated(boolean activated) {
+ try {
+ return mCdm.setNightDisplayActivated(activated);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ int getNightDisplayColorTemperature() {
+ try {
+ return mCdm.getNightDisplayColorTemperature();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ boolean setNightDisplayColorTemperature(int temperature) {
+ try {
+ return mCdm.setNightDisplayColorTemperature(temperature);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ int getNightDisplayAutoMode() {
+ try {
+ return mCdm.getNightDisplayAutoMode();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ int getNightDisplayAutoModeRaw() {
+ try {
+ return mCdm.getNightDisplayAutoModeRaw();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ boolean setNightDisplayAutoMode(int autoMode) {
+ try {
+ return mCdm.setNightDisplayAutoMode(autoMode);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ Time getNightDisplayCustomStartTime() {
+ try {
+ return mCdm.getNightDisplayCustomStartTime();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ boolean setNightDisplayCustomStartTime(Time startTime) {
+ try {
+ return mCdm.setNightDisplayCustomStartTime(startTime);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ Time getNightDisplayCustomEndTime() {
+ try {
+ return mCdm.getNightDisplayCustomEndTime();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ boolean setNightDisplayCustomEndTime(Time endTime) {
+ try {
+ return mCdm.setNightDisplayCustomEndTime(endTime);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
boolean isDeviceColorManaged() {
try {
return mCdm.isDeviceColorManaged();
diff --git a/core/java/android/hardware/display/IColorDisplayManager.aidl b/core/java/android/hardware/display/IColorDisplayManager.aidl
index 53cb8db8cc3d..1918fd5d8cb1 100644
--- a/core/java/android/hardware/display/IColorDisplayManager.aidl
+++ b/core/java/android/hardware/display/IColorDisplayManager.aidl
@@ -16,6 +16,8 @@
package android.hardware.display;
+import android.hardware.display.Time;
+
/** @hide */
interface IColorDisplayManager {
boolean isDeviceColorManaged();
@@ -24,4 +26,16 @@ interface IColorDisplayManager {
boolean setAppSaturationLevel(String packageName, int saturationLevel);
int getTransformCapabilities();
+
+ boolean isNightDisplayActivated();
+ boolean setNightDisplayActivated(boolean activated);
+ int getNightDisplayColorTemperature();
+ boolean setNightDisplayColorTemperature(int temperature);
+ int getNightDisplayAutoMode();
+ int getNightDisplayAutoModeRaw();
+ boolean setNightDisplayAutoMode(int autoMode);
+ Time getNightDisplayCustomStartTime();
+ boolean setNightDisplayCustomStartTime(in Time time);
+ Time getNightDisplayCustomEndTime();
+ boolean setNightDisplayCustomEndTime(in Time time);
} \ No newline at end of file
diff --git a/core/java/android/hardware/display/Time.aidl b/core/java/android/hardware/display/Time.aidl
new file mode 100644
index 000000000000..95cb5631e72f
--- /dev/null
+++ b/core/java/android/hardware/display/Time.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.display;
+
+parcelable Time; \ No newline at end of file
diff --git a/core/java/android/hardware/display/Time.java b/core/java/android/hardware/display/Time.java
new file mode 100644
index 000000000000..b943ac65ab06
--- /dev/null
+++ b/core/java/android/hardware/display/Time.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.display;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.time.LocalTime;
+
+/**
+ * @hide
+ */
+public final class Time implements Parcelable {
+
+ private final int mHour;
+ private final int mMinute;
+ private final int mSecond;
+ private final int mNano;
+
+ public Time(LocalTime localTime) {
+ mHour = localTime.getHour();
+ mMinute = localTime.getMinute();
+ mSecond = localTime.getSecond();
+ mNano = localTime.getNano();
+ }
+
+ public Time(Parcel parcel) {
+ mHour = parcel.readInt();
+ mMinute = parcel.readInt();
+ mSecond = parcel.readInt();
+ mNano = parcel.readInt();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel parcel, int parcelableFlags) {
+ parcel.writeInt(mHour);
+ parcel.writeInt(mMinute);
+ parcel.writeInt(mSecond);
+ parcel.writeInt(mNano);
+ }
+
+ public LocalTime getLocalTime() {
+ return LocalTime.of(mHour, mMinute, mSecond, mNano);
+ }
+
+ public static final Parcelable.Creator<Time> CREATOR = new Parcelable.Creator<Time>() {
+
+ @Override
+ public Time createFromParcel(Parcel source) {
+ return new Time(source);
+ }
+
+ @Override
+ public Time[] newArray(int size) {
+ return new Time[size];
+ }
+ };
+}
diff --git a/core/java/android/hardware/usb/IUsbManager.aidl b/core/java/android/hardware/usb/IUsbManager.aidl
index edc3f9466efc..299a00a426d4 100644
--- a/core/java/android/hardware/usb/IUsbManager.aidl
+++ b/core/java/android/hardware/usb/IUsbManager.aidl
@@ -120,6 +120,9 @@ interface IUsbManager
/* Sets the port's current role. */
void setPortRoles(in String portId, int powerRole, int dataRole);
+ /* Enable/disable contaminant detection */
+ void enableContaminantDetection(in String portId, boolean enable);
+
/* Sets USB device connection handler. */
void setUsbDeviceConnectionHandler(in ComponentName usbDeviceConnectionHandler);
}
diff --git a/core/java/android/hardware/usb/ParcelableUsbPort.java b/core/java/android/hardware/usb/ParcelableUsbPort.java
index 7f7ba96b40f2..30388afc0d13 100644
--- a/core/java/android/hardware/usb/ParcelableUsbPort.java
+++ b/core/java/android/hardware/usb/ParcelableUsbPort.java
@@ -31,10 +31,21 @@ import com.android.internal.annotations.Immutable;
public final class ParcelableUsbPort implements Parcelable {
private final @NonNull String mId;
private final int mSupportedModes;
+ private final int mSupportedContaminantProtectionModes;
+ private final boolean mSupportsEnableContaminantPresenceProtection;
+ private final boolean mSupportsEnableContaminantPresenceDetection;
- private ParcelableUsbPort(@NonNull String id, int supportedModes) {
+ private ParcelableUsbPort(@NonNull String id, int supportedModes,
+ int supportedContaminantProtectionModes,
+ boolean supportsEnableContaminantPresenceProtection,
+ boolean supportsEnableContaminantPresenceDetection) {
mId = id;
mSupportedModes = supportedModes;
+ mSupportedContaminantProtectionModes = supportedContaminantProtectionModes;
+ mSupportsEnableContaminantPresenceProtection =
+ supportsEnableContaminantPresenceProtection;
+ mSupportsEnableContaminantPresenceDetection =
+ supportsEnableContaminantPresenceDetection;
}
/**
@@ -45,7 +56,10 @@ public final class ParcelableUsbPort implements Parcelable {
* @return The parcelable version of the port
*/
public static @NonNull ParcelableUsbPort of(@NonNull UsbPort port) {
- return new ParcelableUsbPort(port.getId(), port.getSupportedModes());
+ return new ParcelableUsbPort(port.getId(), port.getSupportedModes(),
+ port.getSupportedContaminantProtectionModes(),
+ port.supportsEnableContaminantPresenceProtection(),
+ port.supportsEnableContaminantPresenceDetection());
}
/**
@@ -56,7 +70,9 @@ public final class ParcelableUsbPort implements Parcelable {
* @return The UsbPort for this object
*/
public @NonNull UsbPort getUsbPort(@NonNull UsbManager usbManager) {
- return new UsbPort(usbManager, mId, mSupportedModes);
+ return new UsbPort(usbManager, mId, mSupportedModes, mSupportedContaminantProtectionModes,
+ mSupportsEnableContaminantPresenceProtection,
+ mSupportsEnableContaminantPresenceDetection);
}
@Override
@@ -68,6 +84,9 @@ public final class ParcelableUsbPort implements Parcelable {
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(mId);
dest.writeInt(mSupportedModes);
+ dest.writeInt(mSupportedContaminantProtectionModes);
+ dest.writeBoolean(mSupportsEnableContaminantPresenceProtection);
+ dest.writeBoolean(mSupportsEnableContaminantPresenceDetection);
}
public static final Creator<ParcelableUsbPort> CREATOR =
@@ -76,7 +95,14 @@ public final class ParcelableUsbPort implements Parcelable {
public ParcelableUsbPort createFromParcel(Parcel in) {
String id = in.readString();
int supportedModes = in.readInt();
- return new ParcelableUsbPort(id, supportedModes);
+ int supportedContaminantProtectionModes = in.readInt();
+ boolean supportsEnableContaminantPresenceProtection = in.readBoolean();
+ boolean supportsEnableContaminantPresenceDetection = in.readBoolean();
+
+ return new ParcelableUsbPort(id, supportedModes,
+ supportedContaminantProtectionModes,
+ supportsEnableContaminantPresenceProtection,
+ supportsEnableContaminantPresenceDetection);
}
@Override
diff --git a/core/java/android/hardware/usb/UsbManager.java b/core/java/android/hardware/usb/UsbManager.java
index 601447814952..eb148b94ac7b 100644
--- a/core/java/android/hardware/usb/UsbManager.java
+++ b/core/java/android/hardware/usb/UsbManager.java
@@ -854,6 +854,20 @@ public class UsbManager {
}
/**
+ * Enables USB port contaminant detection algorithm.
+ *
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.MANAGE_USB)
+ void enableContaminantDetection(@NonNull UsbPort port, boolean enable) {
+ try {
+ mService.enableContaminantDetection(port.getId(), enable);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Sets the component that will handle USB device connection.
* <p>
* Setting component allows to specify external USB host manager to handle use cases, where
diff --git a/core/java/android/hardware/usb/UsbPort.java b/core/java/android/hardware/usb/UsbPort.java
index 37154e4c81b2..c674480c2a75 100644
--- a/core/java/android/hardware/usb/UsbPort.java
+++ b/core/java/android/hardware/usb/UsbPort.java
@@ -16,6 +16,10 @@
package android.hardware.usb;
+import static android.hardware.usb.UsbPortStatus.CONTAMINANT_DETECTION_DETECTED;
+import static android.hardware.usb.UsbPortStatus.CONTAMINANT_DETECTION_DISABLED;
+import static android.hardware.usb.UsbPortStatus.CONTAMINANT_DETECTION_NOT_DETECTED;
+import static android.hardware.usb.UsbPortStatus.CONTAMINANT_DETECTION_NOT_SUPPORTED;
import static android.hardware.usb.UsbPortStatus.DATA_ROLE_DEVICE;
import static android.hardware.usb.UsbPortStatus.DATA_ROLE_HOST;
import static android.hardware.usb.UsbPortStatus.DATA_ROLE_NONE;
@@ -48,16 +52,21 @@ public final class UsbPort {
private final String mId;
private final int mSupportedModes;
private final UsbManager mUsbManager;
+ private final int mSupportedContaminantProtectionModes;
+ private final boolean mSupportsEnableContaminantPresenceProtection;
+ private final boolean mSupportsEnableContaminantPresenceDetection;
private static final int NUM_DATA_ROLES = Constants.PortDataRole.NUM_DATA_ROLES;
-
/**
* Points to the first power role in the IUsb HAL.
*/
private static final int POWER_ROLE_OFFSET = Constants.PortPowerRole.NONE;
/** @hide */
- public UsbPort(@NonNull UsbManager usbManager, @NonNull String id, int supportedModes) {
+ public UsbPort(@NonNull UsbManager usbManager, @NonNull String id, int supportedModes,
+ int supportedContaminantProtectionModes,
+ boolean supportsEnableContaminantPresenceProtection,
+ boolean supportsEnableContaminantPresenceDetection) {
Preconditions.checkNotNull(id);
Preconditions.checkFlagsArgument(supportedModes,
MODE_DFP | MODE_UFP | MODE_AUDIO_ACCESSORY | MODE_DEBUG_ACCESSORY);
@@ -65,6 +74,11 @@ public final class UsbPort {
mUsbManager = usbManager;
mId = id;
mSupportedModes = supportedModes;
+ mSupportedContaminantProtectionModes = supportedContaminantProtectionModes;
+ mSupportsEnableContaminantPresenceProtection =
+ supportsEnableContaminantPresenceProtection;
+ mSupportsEnableContaminantPresenceDetection =
+ supportsEnableContaminantPresenceDetection;
}
/**
@@ -93,6 +107,36 @@ public final class UsbPort {
return mSupportedModes;
}
+ /**
+ * Gets the supported port proctection modes when the port is contaminated.
+ * <p>
+ * The actual mode of the port is decided by the hardware
+ * </p>
+ *
+ * @hide
+ */
+ public int getSupportedContaminantProtectionModes() {
+ return mSupportedContaminantProtectionModes;
+ }
+
+ /**
+ * Tells if UsbService can enable/disable contaminant presence protection.
+ *
+ * @hide
+ */
+ public boolean supportsEnableContaminantPresenceProtection() {
+ return mSupportsEnableContaminantPresenceProtection;
+ }
+
+ /**
+ * Tells if UsbService can enable/disable contaminant presence detection.
+ *
+ * @hide
+ */
+ public boolean supportsEnableContaminantPresenceDetection() {
+ return mSupportsEnableContaminantPresenceDetection;
+ }
+
/**
* Gets the status of this USB port.
*
@@ -131,6 +175,12 @@ public final class UsbPort {
}
/**
+ * @hide
+ **/
+ public void enableContaminantDetection(boolean enable) {
+ mUsbManager.enableContaminantDetection(this, enable);
+ }
+ /**
* Combines one power and one data role together into a unique value with
* exactly one bit set. This can be used to efficiently determine whether
* a combination of roles is supported by testing whether that bit is present
@@ -206,6 +256,22 @@ public final class UsbPort {
}
/** @hide */
+ public static String contaminantPresenceStatusToString(int contaminantPresenceStatus) {
+ switch (contaminantPresenceStatus) {
+ case CONTAMINANT_DETECTION_NOT_SUPPORTED:
+ return "not-supported";
+ case CONTAMINANT_DETECTION_DISABLED:
+ return "disabled";
+ case CONTAMINANT_DETECTION_DETECTED:
+ return "detected";
+ case CONTAMINANT_DETECTION_NOT_DETECTED:
+ return "not detected";
+ default:
+ return Integer.toString(contaminantPresenceStatus);
+ }
+ }
+
+ /** @hide */
public static String roleCombinationsToString(int combo) {
StringBuilder result = new StringBuilder();
result.append("[");
@@ -264,6 +330,11 @@ public final class UsbPort {
@Override
public String toString() {
- return "UsbPort{id=" + mId + ", supportedModes=" + modeToString(mSupportedModes) + "}";
+ return "UsbPort{id=" + mId + ", supportedModes=" + modeToString(mSupportedModes)
+ + "supportedContaminantProtectionModes=" + mSupportedContaminantProtectionModes
+ + "supportsEnableContaminantPresenceProtection="
+ + mSupportsEnableContaminantPresenceProtection
+ + "supportsEnableContaminantPresenceDetection="
+ + mSupportsEnableContaminantPresenceDetection;
}
}
diff --git a/core/java/android/hardware/usb/UsbPortStatus.java b/core/java/android/hardware/usb/UsbPortStatus.java
index d30201a597bf..426dba88c399 100644
--- a/core/java/android/hardware/usb/UsbPortStatus.java
+++ b/core/java/android/hardware/usb/UsbPortStatus.java
@@ -39,6 +39,8 @@ public final class UsbPortStatus implements Parcelable {
private final @UsbPowerRole int mCurrentPowerRole;
private final @UsbDataRole int mCurrentDataRole;
private final int mSupportedRoleCombinations;
+ private final @ContaminantProtectionStatus int mContaminantProtectionStatus;
+ private final @ContaminantDetectionStatus int mContaminantDetectionStatus;
/**
* Power role: This USB port does not have a power role.
@@ -131,6 +133,93 @@ public final class UsbPortStatus implements Parcelable {
public static final int MODE_DEBUG_ACCESSORY =
android.hardware.usb.V1_1.Constants.PortMode_1_1.DEBUG_ACCESSORY;
+ /**
+ * Contaminant presence detection not supported by the device.
+ * @hide
+ */
+ public static final int CONTAMINANT_DETECTION_NOT_SUPPORTED =
+ android.hardware.usb.V1_2.Constants.ContaminantDetectionStatus.NOT_SUPPORTED;
+
+ /**
+ * Contaminant presence detection supported but disabled.
+ * @hide
+ */
+ public static final int CONTAMINANT_DETECTION_DISABLED =
+ android.hardware.usb.V1_2.Constants.ContaminantDetectionStatus.DISABLED;
+
+ /**
+ * Contaminant presence enabled but not detected.
+ * @hide
+ */
+ public static final int CONTAMINANT_DETECTION_NOT_DETECTED =
+ android.hardware.usb.V1_2.Constants.ContaminantDetectionStatus.NOT_DETECTED;
+
+ /**
+ * Contaminant presence enabled and detected.
+ * @hide
+ */
+ public static final int CONTAMINANT_DETECTION_DETECTED =
+ android.hardware.usb.V1_2.Constants.ContaminantDetectionStatus.DETECTED;
+
+ /**
+ * Contaminant protection - No action performed upon detection of
+ * contaminant presence.
+ * @hide
+ */
+ public static final int CONTAMINANT_PROTECTION_NONE =
+ android.hardware.usb.V1_2.Constants.ContaminantProtectionStatus.NONE;
+
+ /**
+ * Contaminant protection - Port is forced to sink upon detection of
+ * contaminant presence.
+ * @hide
+ */
+ public static final int CONTAMINANT_PROTECTION_SINK =
+ android.hardware.usb.V1_2.Constants.ContaminantProtectionStatus.FORCE_SINK;
+
+ /**
+ * Contaminant protection - Port is forced to source upon detection of
+ * contaminant presence.
+ * @hide
+ */
+ public static final int CONTAMINANT_PROTECTION_SOURCE =
+ android.hardware.usb.V1_2.Constants.ContaminantProtectionStatus.FORCE_SOURCE;
+
+ /**
+ * Contaminant protection - Port is disabled upon detection of
+ * contaminant presence.
+ * @hide
+ */
+ public static final int CONTAMINANT_PROTECTION_FORCE_DISABLE =
+ android.hardware.usb.V1_2.Constants.ContaminantProtectionStatus.FORCE_DISABLE;
+
+ /**
+ * Contaminant protection - Port is disabled upon detection of
+ * contaminant presence.
+ * @hide
+ */
+ public static final int CONTAMINANT_PROTECTION_DISABLED =
+ android.hardware.usb.V1_2.Constants.ContaminantProtectionStatus.DISABLED;
+
+ @IntDef(prefix = { "CONTAMINANT_DETECION_" }, flag = true, value = {
+ CONTAMINANT_DETECTION_NOT_SUPPORTED,
+ CONTAMINANT_DETECTION_DISABLED,
+ CONTAMINANT_DETECTION_NOT_DETECTED,
+ CONTAMINANT_DETECTION_DETECTED,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface ContaminantDetectionStatus{}
+
+ @IntDef(prefix = { "CONTAMINANT_PROTECTION_" }, flag = true, value = {
+ CONTAMINANT_PROTECTION_NONE,
+ CONTAMINANT_PROTECTION_SINK,
+ CONTAMINANT_PROTECTION_SOURCE,
+ CONTAMINANT_PROTECTION_FORCE_DISABLE,
+ CONTAMINANT_PROTECTION_DISABLED,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface ContaminantProtectionStatus{}
+
@IntDef(prefix = { "MODE_" }, flag = true, value = {
MODE_NONE,
MODE_DFP,
@@ -142,12 +231,15 @@ public final class UsbPortStatus implements Parcelable {
@interface UsbPortMode{}
/** @hide */
- public UsbPortStatus(int currentMode, @UsbPowerRole int currentPowerRole,
- @UsbDataRole int currentDataRole, int supportedRoleCombinations) {
+ public UsbPortStatus(int currentMode, int currentPowerRole, int currentDataRole,
+ int supportedRoleCombinations, int contaminantProtectionStatus,
+ int contaminantDetectionStatus) {
mCurrentMode = currentMode;
mCurrentPowerRole = currentPowerRole;
mCurrentDataRole = currentDataRole;
mSupportedRoleCombinations = supportedRoleCombinations;
+ mContaminantProtectionStatus = contaminantProtectionStatus;
+ mContaminantDetectionStatus = contaminantDetectionStatus;
}
/**
@@ -212,6 +304,24 @@ public final class UsbPortStatus implements Parcelable {
return mSupportedRoleCombinations;
}
+ /**
+ * Returns contaminant detection status.
+ *
+ * @hide
+ */
+ public @ContaminantDetectionStatus int getContaminantDetectionStatus() {
+ return mContaminantDetectionStatus;
+ }
+
+ /**
+ * Returns contamiant protection status.
+ *
+ * @hide
+ */
+ public @ContaminantProtectionStatus int getContaminantProtectionStatus() {
+ return mContaminantProtectionStatus;
+ }
+
@Override
public String toString() {
return "UsbPortStatus{connected=" + isConnected()
@@ -220,6 +330,10 @@ public final class UsbPortStatus implements Parcelable {
+ ", currentDataRole=" + UsbPort.dataRoleToString(mCurrentDataRole)
+ ", supportedRoleCombinations="
+ UsbPort.roleCombinationsToString(mSupportedRoleCombinations)
+ + ", contaminantDetectionStatus="
+ + getContaminantDetectionStatus()
+ + ", contaminantProtectionStatus="
+ + getContaminantProtectionStatus()
+ "}";
}
@@ -234,6 +348,8 @@ public final class UsbPortStatus implements Parcelable {
dest.writeInt(mCurrentPowerRole);
dest.writeInt(mCurrentDataRole);
dest.writeInt(mSupportedRoleCombinations);
+ dest.writeInt(mContaminantProtectionStatus);
+ dest.writeInt(mContaminantDetectionStatus);
}
public static final Parcelable.Creator<UsbPortStatus> CREATOR =
@@ -244,8 +360,11 @@ public final class UsbPortStatus implements Parcelable {
int currentPowerRole = in.readInt();
int currentDataRole = in.readInt();
int supportedRoleCombinations = in.readInt();
+ int contaminantProtectionStatus = in.readInt();
+ int contaminantDetectionStatus = in.readInt();
return new UsbPortStatus(currentMode, currentPowerRole, currentDataRole,
- supportedRoleCombinations);
+ supportedRoleCombinations, contaminantProtectionStatus,
+ contaminantDetectionStatus);
}
@Override
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index c809ccad5907..243b0ebab8f9 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -15,6 +15,9 @@
*/
package android.net;
+import static android.net.IpSecManager.INVALID_RESOURCE_ID;
+
+import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -28,6 +31,8 @@ import android.annotation.UnsupportedAppUsage;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
+import android.net.IpSecManager.UdpEncapsulationSocket;
+import android.net.SocketKeepalive.Callback;
import android.os.Binder;
import android.os.Build;
import android.os.Build.VERSION_CODES;
@@ -58,6 +63,7 @@ import com.android.internal.util.Protocol;
import libcore.net.event.NetworkEventDispatcher;
+import java.io.FileDescriptor;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.net.InetAddress;
@@ -66,6 +72,7 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.concurrent.Executor;
/**
* Class that answers queries about the state of network connectivity. It also
@@ -1007,14 +1014,20 @@ public class ConnectivityManager {
* to remove an existing always-on VPN configuration.
* @param lockdownEnabled {@code true} to disallow networking when the VPN is not connected or
* {@code false} otherwise.
+ * @param lockdownWhitelist The list of packages that are allowed to access network directly
+ * when VPN is in lockdown mode but is not running. Non-existent packages are ignored so
+ * this method must be called when a package that should be whitelisted is installed or
+ * uninstalled.
* @return {@code true} if the package is set as always-on VPN controller;
* {@code false} otherwise.
* @hide
*/
+ @RequiresPermission(android.Manifest.permission.CONTROL_ALWAYS_ON_VPN)
public boolean setAlwaysOnVpnPackageForUser(int userId, @Nullable String vpnPackage,
- boolean lockdownEnabled) {
+ boolean lockdownEnabled, @Nullable List<String> lockdownWhitelist) {
try {
- return mService.setAlwaysOnVpnPackage(userId, vpnPackage, lockdownEnabled);
+ return mService.setAlwaysOnVpnPackage(
+ userId, vpnPackage, lockdownEnabled, lockdownWhitelist);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1029,6 +1042,7 @@ public class ConnectivityManager {
* or {@code null} if none is set.
* @hide
*/
+ @RequiresPermission(android.Manifest.permission.CONTROL_ALWAYS_ON_VPN)
public String getAlwaysOnVpnPackageForUser(int userId) {
try {
return mService.getAlwaysOnVpnPackage(userId);
@@ -1038,6 +1052,36 @@ public class ConnectivityManager {
}
/**
+ * @return whether always-on VPN is in lockdown mode.
+ *
+ * @hide
+ **/
+ @RequiresPermission(android.Manifest.permission.CONTROL_ALWAYS_ON_VPN)
+ public boolean isVpnLockdownEnabled(int userId) {
+ try {
+ return mService.isVpnLockdownEnabled(userId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+
+ }
+
+ /**
+ * @return the list of packages that are allowed to access network when always-on VPN is in
+ * lockdown mode but not connected. Returns {@code null} when VPN lockdown is not active.
+ *
+ * @hide
+ **/
+ @RequiresPermission(android.Manifest.permission.CONTROL_ALWAYS_ON_VPN)
+ public List<String> getVpnLockdownWhitelist(int userId) {
+ try {
+ return mService.getVpnLockdownWhitelist(userId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Returns details about the currently active default data network
* for a given uid. This is for internal use only to avoid spying
* other apps.
@@ -1699,6 +1743,8 @@ public class ConnectivityManager {
* {@link PacketKeepaliveCallback#onStopped} if the operation was successful or
* {@link PacketKeepaliveCallback#onError} if an error occurred.
*
+ * @deprecated Use {@link SocketKeepalive} instead.
+ *
* @hide
*/
public class PacketKeepalive {
@@ -1802,6 +1848,8 @@ public class ConnectivityManager {
/**
* Starts an IPsec NAT-T keepalive packet with the specified parameters.
*
+ * @deprecated Use {@link #createSocketKeepalive} instead.
+ *
* @hide
*/
@UnsupportedAppUsage
@@ -1821,6 +1869,62 @@ public class ConnectivityManager {
}
/**
+ * Request that keepalives be started on a IPsec NAT-T socket.
+ *
+ * @param network The {@link Network} the socket is on.
+ * @param socket The socket that needs to be kept alive.
+ * @param source The source address of the {@link UdpEncapsulationSocket}.
+ * @param destination The destination address of the {@link UdpEncapsulationSocket}.
+ * @param executor The executor on which callback will be invoked. The provided {@link Executor}
+ * must run callback sequentially, otherwise the order of callbacks cannot be
+ * guaranteed.
+ * @param callback A {@link SocketKeepalive.Callback}. Used for notifications about keepalive
+ * changes. Must be extended by applications that use this API.
+ *
+ * @return A {@link SocketKeepalive} object, which can be used to control this keepalive object.
+ **/
+ public SocketKeepalive createSocketKeepalive(@NonNull Network network,
+ @NonNull UdpEncapsulationSocket socket,
+ @NonNull InetAddress source,
+ @NonNull InetAddress destination,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull Callback callback) {
+ return new NattSocketKeepalive(mService, network, socket.getFileDescriptor(),
+ socket.getResourceId(), source, destination, executor, callback);
+ }
+
+ /**
+ * Request that keepalives be started on a IPsec NAT-T socket file descriptor. Directly called
+ * by system apps which don't use IpSecService to create {@link UdpEncapsulationSocket}.
+ *
+ * @param network The {@link Network} the socket is on.
+ * @param fd The {@link FileDescriptor} that needs to be kept alive. The provided
+ * {@link FileDescriptor} must be bound to a port and the keepalives will be sent from
+ * that port.
+ * @param source The source address of the {@link UdpEncapsulationSocket}.
+ * @param destination The destination address of the {@link UdpEncapsulationSocket}. The
+ * keepalive packets will always be sent to port 4500 of the given {@code destination}.
+ * @param executor The executor on which callback will be invoked. The provided {@link Executor}
+ * must run callback sequentially, otherwise the order of callbacks cannot be
+ * guaranteed.
+ * @param callback A {@link SocketKeepalive.Callback}. Used for notifications about keepalive
+ * changes. Must be extended by applications that use this API.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.PACKET_KEEPALIVE_OFFLOAD)
+ public SocketKeepalive createNattKeepalive(@NonNull Network network,
+ @NonNull FileDescriptor fd,
+ @NonNull InetAddress source,
+ @NonNull InetAddress destination,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull Callback callback) {
+ return new NattSocketKeepalive(mService, network, fd, INVALID_RESOURCE_ID /* Unused */,
+ source, destination, executor, callback);
+ }
+
+ /**
* Ensure that a network route exists to deliver traffic to the specified
* host via the specified network interface. An attempt to add a route that
* already exists is ignored, but treated as successful.
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index 131925ec28e9..fd7360fd4c17 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -125,8 +125,11 @@ interface IConnectivityManager
boolean updateLockdownVpn();
boolean isAlwaysOnVpnPackageSupported(int userId, String packageName);
- boolean setAlwaysOnVpnPackage(int userId, String packageName, boolean lockdown);
+ boolean setAlwaysOnVpnPackage(int userId, String packageName, boolean lockdown,
+ in List<String> lockdownWhitelist);
String getAlwaysOnVpnPackage(int userId);
+ boolean isVpnLockdownEnabled(int userId);
+ List<String> getVpnLockdownWhitelist(int userId);
int checkMobileProvisioning(int suggestedTimeOutMs);
@@ -181,6 +184,10 @@ interface IConnectivityManager
void startNattKeepalive(in Network network, int intervalSeconds, in Messenger messenger,
in IBinder binder, String srcAddr, int srcPort, String dstAddr);
+ void startNattKeepaliveWithFd(in Network network, in FileDescriptor fd, int resourceId,
+ int intervalSeconds, in Messenger messenger, in IBinder binder, String srcAddr,
+ String dstAddr);
+
void stopKeepalive(in Network network, int slot);
String getCaptivePortalServerUrl();
diff --git a/core/java/android/net/IpPrefix.java b/core/java/android/net/IpPrefix.java
index 4631c565962f..b996cdab5164 100644
--- a/core/java/android/net/IpPrefix.java
+++ b/core/java/android/net/IpPrefix.java
@@ -16,6 +16,8 @@
package android.net;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Pair;
@@ -83,6 +85,8 @@ public final class IpPrefix implements Parcelable {
* @param prefixLength the prefix length. Must be &gt;= 0 and &lt;= (32 or 128) (IPv4 or IPv6).
* @hide
*/
+ @SystemApi
+ @TestApi
public IpPrefix(InetAddress address, int prefixLength) {
// We don't reuse the (byte[], int) constructor because it calls clone() on the byte array,
// which is unnecessary because getAddress() already returns a clone.
diff --git a/core/java/android/net/LinkAddress.java b/core/java/android/net/LinkAddress.java
index a536d08876d6..fbd602c7b2d0 100644
--- a/core/java/android/net/LinkAddress.java
+++ b/core/java/android/net/LinkAddress.java
@@ -162,6 +162,8 @@ public class LinkAddress implements Parcelable {
* {@link OsConstants#RT_SCOPE_LINK} or {@link OsConstants#RT_SCOPE_SITE}).
* @hide
*/
+ @SystemApi
+ @TestApi
public LinkAddress(InetAddress address, int prefixLength, int flags, int scope) {
init(address, prefixLength, flags, scope);
}
diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java
index 21b6a8eb1990..662870182eea 100644
--- a/core/java/android/net/LinkProperties.java
+++ b/core/java/android/net/LinkProperties.java
@@ -174,7 +174,8 @@ public final class LinkProperties implements Parcelable {
/**
* @hide
*/
- @UnsupportedAppUsage
+ @SystemApi
+ @TestApi
public LinkProperties(LinkProperties source) {
if (source != null) {
mIfaceName = source.mIfaceName;
@@ -576,6 +577,8 @@ public final class LinkProperties implements Parcelable {
* @param addresses The {@link Collection} of PCSCF servers to set in this object.
* @hide
*/
+ @SystemApi
+ @TestApi
public void setPcscfServers(Collection<InetAddress> pcscfServers) {
mPcscfs.clear();
for (InetAddress pcscfServer: pcscfServers) {
@@ -590,6 +593,8 @@ public final class LinkProperties implements Parcelable {
* this link.
* @hide
*/
+ @SystemApi
+ @TestApi
public List<InetAddress> getPcscfServers() {
return Collections.unmodifiableList(mPcscfs);
}
@@ -781,6 +786,8 @@ public final class LinkProperties implements Parcelable {
* @return the NAT64 prefix.
* @hide
*/
+ @SystemApi
+ @TestApi
public @Nullable IpPrefix getNat64Prefix() {
return mNat64Prefix;
}
@@ -794,6 +801,8 @@ public final class LinkProperties implements Parcelable {
* @param prefix the NAT64 prefix.
* @hide
*/
+ @SystemApi
+ @TestApi
public void setNat64Prefix(IpPrefix prefix) {
if (prefix != null && prefix.getPrefixLength() != 96) {
throw new IllegalArgumentException("Only 96-bit prefixes are supported: " + prefix);
diff --git a/core/java/android/net/NattSocketKeepalive.java b/core/java/android/net/NattSocketKeepalive.java
new file mode 100644
index 000000000000..88631aea3a88
--- /dev/null
+++ b/core/java/android/net/NattSocketKeepalive.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import android.annotation.NonNull;
+import android.os.Binder;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.io.FileDescriptor;
+import java.net.InetAddress;
+import java.util.concurrent.Executor;
+
+/** @hide */
+public final class NattSocketKeepalive extends SocketKeepalive {
+ /** The NAT-T destination port for IPsec */
+ public static final int NATT_PORT = 4500;
+
+ @NonNull private final InetAddress mSource;
+ @NonNull private final InetAddress mDestination;
+ @NonNull private final FileDescriptor mFd;
+ private final int mResourceId;
+
+ NattSocketKeepalive(@NonNull IConnectivityManager service,
+ @NonNull Network network,
+ @NonNull FileDescriptor fd,
+ int resourceId,
+ @NonNull InetAddress source,
+ @NonNull InetAddress destination,
+ @NonNull Executor executor,
+ @NonNull Callback callback) {
+ super(service, network, executor, callback);
+ mSource = source;
+ mDestination = destination;
+ mFd = fd;
+ mResourceId = resourceId;
+ }
+
+ @Override
+ void startImpl(int intervalSec) {
+ try {
+ mService.startNattKeepaliveWithFd(mNetwork, mFd, mResourceId, intervalSec, mMessenger,
+ new Binder(), mSource.getHostAddress(), mDestination.getHostAddress());
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error starting packet keepalive: ", e);
+ stopLooper();
+ }
+ }
+
+ @Override
+ void stopImpl() {
+ try {
+ if (mSlot != null) {
+ mService.stopKeepalive(mNetwork, mSlot);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error stopping packet keepalive: ", e);
+ stopLooper();
+ }
+ }
+}
diff --git a/core/java/android/net/ProxyInfo.java b/core/java/android/net/ProxyInfo.java
index e926fda336f4..ef2269a145d0 100644
--- a/core/java/android/net/ProxyInfo.java
+++ b/core/java/android/net/ProxyInfo.java
@@ -39,12 +39,12 @@ import java.util.Locale;
*/
public class ProxyInfo implements Parcelable {
- private String mHost;
- private int mPort;
- private String mExclusionList;
- private String[] mParsedExclusionList;
+ private final String mHost;
+ private final int mPort;
+ private final String mExclusionList;
+ private final String[] mParsedExclusionList;
+ private final Uri mPacFileUrl;
- private Uri mPacFileUrl;
/**
*@hide
*/
@@ -96,7 +96,8 @@ public class ProxyInfo implements Parcelable {
public ProxyInfo(String host, int port, String exclList) {
mHost = host;
mPort = port;
- setExclusionList(exclList);
+ mExclusionList = exclList;
+ mParsedExclusionList = parseExclusionList(mExclusionList);
mPacFileUrl = Uri.EMPTY;
}
@@ -107,7 +108,8 @@ public class ProxyInfo implements Parcelable {
public ProxyInfo(Uri pacFileUrl) {
mHost = LOCAL_HOST;
mPort = LOCAL_PORT;
- setExclusionList(LOCAL_EXCL_LIST);
+ mExclusionList = LOCAL_EXCL_LIST;
+ mParsedExclusionList = parseExclusionList(mExclusionList);
if (pacFileUrl == null) {
throw new NullPointerException();
}
@@ -121,7 +123,8 @@ public class ProxyInfo implements Parcelable {
public ProxyInfo(String pacFileUrl) {
mHost = LOCAL_HOST;
mPort = LOCAL_PORT;
- setExclusionList(LOCAL_EXCL_LIST);
+ mExclusionList = LOCAL_EXCL_LIST;
+ mParsedExclusionList = parseExclusionList(mExclusionList);
mPacFileUrl = Uri.parse(pacFileUrl);
}
@@ -132,13 +135,22 @@ public class ProxyInfo implements Parcelable {
public ProxyInfo(Uri pacFileUrl, int localProxyPort) {
mHost = LOCAL_HOST;
mPort = localProxyPort;
- setExclusionList(LOCAL_EXCL_LIST);
+ mExclusionList = LOCAL_EXCL_LIST;
+ mParsedExclusionList = parseExclusionList(mExclusionList);
if (pacFileUrl == null) {
throw new NullPointerException();
}
mPacFileUrl = pacFileUrl;
}
+ private static String[] parseExclusionList(String exclusionList) {
+ if (exclusionList == null) {
+ return new String[0];
+ } else {
+ return exclusionList.toLowerCase(Locale.ROOT).split(",");
+ }
+ }
+
private ProxyInfo(String host, int port, String exclList, String[] parsedExclList) {
mHost = host;
mPort = port;
@@ -159,6 +171,10 @@ public class ProxyInfo implements Parcelable {
mExclusionList = source.getExclusionListAsString();
mParsedExclusionList = source.mParsedExclusionList;
} else {
+ mHost = null;
+ mPort = 0;
+ mExclusionList = null;
+ mParsedExclusionList = null;
mPacFileUrl = Uri.EMPTY;
}
}
@@ -214,24 +230,14 @@ public class ProxyInfo implements Parcelable {
return mExclusionList;
}
- // comma separated
- private void setExclusionList(String exclusionList) {
- mExclusionList = exclusionList;
- if (mExclusionList == null) {
- mParsedExclusionList = new String[0];
- } else {
- mParsedExclusionList = exclusionList.toLowerCase(Locale.ROOT).split(",");
- }
- }
-
/**
* @hide
*/
public boolean isValid() {
if (!Uri.EMPTY.equals(mPacFileUrl)) return true;
return Proxy.PROXY_VALID == Proxy.validate(mHost == null ? "" : mHost,
- mPort == 0 ? "" : Integer.toString(mPort),
- mExclusionList == null ? "" : mExclusionList);
+ mPort == 0 ? "" : Integer.toString(mPort),
+ mExclusionList == null ? "" : mExclusionList);
}
/**
@@ -262,7 +268,7 @@ public class ProxyInfo implements Parcelable {
sb.append("] ");
sb.append(Integer.toString(mPort));
if (mExclusionList != null) {
- sb.append(" xl=").append(mExclusionList);
+ sb.append(" xl=").append(mExclusionList);
}
} else {
sb.append("[ProxyProperties.mHost == null]");
@@ -308,8 +314,8 @@ public class ProxyInfo implements Parcelable {
*/
public int hashCode() {
return ((null == mHost) ? 0 : mHost.hashCode())
- + ((null == mExclusionList) ? 0 : mExclusionList.hashCode())
- + mPort;
+ + ((null == mExclusionList) ? 0 : mExclusionList.hashCode())
+ + mPort;
}
/**
@@ -352,8 +358,7 @@ public class ProxyInfo implements Parcelable {
}
String exclList = in.readString();
String[] parsedExclList = in.readStringArray();
- ProxyInfo proxyProperties =
- new ProxyInfo(host, port, exclList, parsedExclList);
+ ProxyInfo proxyProperties = new ProxyInfo(host, port, exclList, parsedExclList);
return proxyProperties;
}
diff --git a/core/java/android/net/RouteInfo.java b/core/java/android/net/RouteInfo.java
index 6bf2c67da990..5c0f7582091d 100644
--- a/core/java/android/net/RouteInfo.java
+++ b/core/java/android/net/RouteInfo.java
@@ -110,6 +110,8 @@ public final class RouteInfo implements Parcelable {
*
* @hide
*/
+ @SystemApi
+ @TestApi
public RouteInfo(IpPrefix destination, InetAddress gateway, String iface, int type) {
switch (type) {
case RTN_UNICAST:
diff --git a/core/java/android/net/SocketKeepalive.java b/core/java/android/net/SocketKeepalive.java
new file mode 100644
index 000000000000..97d50f4bac05
--- /dev/null
+++ b/core/java/android/net/SocketKeepalive.java
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import android.annotation.IntDef;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.Process;
+import android.util.Log;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.concurrent.Executor;
+
+/**
+ * Allows applications to request that the system periodically send specific packets on their
+ * behalf, using hardware offload to save battery power.
+ *
+ * To request that the system send keepalives, call one of the methods that return a
+ * {@link SocketKeepalive} object, such as {@link ConnectivityManager#createSocketKeepalive},
+ * passing in a non-null callback. If the {@link SocketKeepalive} is successfully
+ * started, the callback's {@code onStarted} method will be called. If an error occurs,
+ * {@code onError} will be called, specifying one of the {@code ERROR_*} constants in this
+ * class.
+ *
+ * To stop an existing keepalive, call {@link SocketKeepalive#stop}. The system will call
+ * {@link SocketKeepalive.Callback#onStopped} if the operation was successful or
+ * {@link SocketKeepalive.Callback#onError} if an error occurred.
+ */
+public abstract class SocketKeepalive implements AutoCloseable {
+ static final String TAG = "SocketKeepalive";
+
+ /** @hide */
+ public static final int SUCCESS = 0;
+
+ /** @hide */
+ public static final int NO_KEEPALIVE = -1;
+
+ /** @hide */
+ public static final int DATA_RECEIVED = -2;
+
+ /** @hide */
+ public static final int BINDER_DIED = -10;
+
+ /** The specified {@code Network} is not connected. */
+ public static final int ERROR_INVALID_NETWORK = -20;
+ /** The specified IP addresses are invalid. For example, the specified source IP address is
+ * not configured on the specified {@code Network}. */
+ public static final int ERROR_INVALID_IP_ADDRESS = -21;
+ /** The requested port is invalid. */
+ public static final int ERROR_INVALID_PORT = -22;
+ /** The packet length is invalid (e.g., too long). */
+ public static final int ERROR_INVALID_LENGTH = -23;
+ /** The packet transmission interval is invalid (e.g., too short). */
+ public static final int ERROR_INVALID_INTERVAL = -24;
+ /** The target socket is invalid. */
+ public static final int ERROR_INVALID_SOCKET = -25;
+ /** The target socket is not idle. */
+ public static final int ERROR_SOCKET_NOT_IDLE = -26;
+
+ /** The hardware does not support this request. */
+ public static final int ERROR_HARDWARE_UNSUPPORTED = -30;
+ /** The hardware returned an error. */
+ public static final int ERROR_HARDWARE_ERROR = -31;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = { "ERROR_" }, value = {
+ ERROR_INVALID_NETWORK,
+ ERROR_INVALID_IP_ADDRESS,
+ ERROR_INVALID_PORT,
+ ERROR_INVALID_LENGTH,
+ ERROR_INVALID_INTERVAL,
+ ERROR_INVALID_SOCKET,
+ ERROR_SOCKET_NOT_IDLE
+ })
+ public @interface ErrorCode {}
+
+ /**
+ * The minimum interval in seconds between keepalive packet transmissions.
+ *
+ * @hide
+ **/
+ public static final int MIN_INTERVAL_SEC = 10;
+
+ /**
+ * The maximum interval in seconds between keepalive packet transmissions.
+ *
+ * @hide
+ **/
+ public static final int MAX_INTERVAL_SEC = 3600;
+
+ @NonNull final IConnectivityManager mService;
+ @NonNull final Network mNetwork;
+ @NonNull private final Executor mExecutor;
+ @NonNull private final SocketKeepalive.Callback mCallback;
+ @NonNull private final Looper mLooper;
+ @NonNull final Messenger mMessenger;
+ @NonNull Integer mSlot;
+
+ SocketKeepalive(@NonNull IConnectivityManager service, @NonNull Network network,
+ @NonNull Executor executor, @NonNull Callback callback) {
+ mService = service;
+ mNetwork = network;
+ mExecutor = executor;
+ mCallback = callback;
+ // TODO: 1. Use other thread modeling instead of create one thread for every instance to
+ // reduce the memory cost.
+ // 2. support restart.
+ // 3. Fix race condition which caused by rapidly start and stop.
+ HandlerThread thread = new HandlerThread(TAG, Process.THREAD_PRIORITY_BACKGROUND
+ + Process.THREAD_PRIORITY_LESS_FAVORABLE);
+ thread.start();
+ mLooper = thread.getLooper();
+ mMessenger = new Messenger(new Handler(mLooper) {
+ @Override
+ public void handleMessage(Message message) {
+ switch (message.what) {
+ case NetworkAgent.EVENT_PACKET_KEEPALIVE:
+ final int status = message.arg2;
+ try {
+ if (status == SUCCESS) {
+ if (mSlot == null) {
+ mSlot = message.arg1;
+ mExecutor.execute(() -> mCallback.onStarted());
+ } else {
+ mSlot = null;
+ stopLooper();
+ mExecutor.execute(() -> mCallback.onStopped());
+ }
+ } else if (status == DATA_RECEIVED) {
+ stopLooper();
+ mExecutor.execute(() -> mCallback.onDataReceived());
+ } else {
+ stopLooper();
+ mExecutor.execute(() -> mCallback.onError(status));
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Exception in keepalive callback(" + status + ")", e);
+ }
+ break;
+ default:
+ Log.e(TAG, "Unhandled message " + Integer.toHexString(message.what));
+ break;
+ }
+ }
+ });
+ }
+
+ /**
+ * Request that keepalive be started with the given {@code intervalSec}. See
+ * {@link SocketKeepalive}.
+ *
+ * @param intervalSec The target interval in seconds between keepalive packet transmissions.
+ * The interval should be between 10 seconds and 3600 seconds, otherwise
+ * {@link #ERROR_INVALID_INTERVAL} will be returned.
+ */
+ public final void start(@IntRange(from = MIN_INTERVAL_SEC, to = MAX_INTERVAL_SEC)
+ int intervalSec) {
+ startImpl(intervalSec);
+ }
+
+ abstract void startImpl(int intervalSec);
+
+ /** @hide */
+ protected void stopLooper() {
+ // TODO: remove this after changing thread modeling.
+ mLooper.quit();
+ }
+
+ /**
+ * Requests that keepalive be stopped. The application must wait for {@link Callback#onStopped}
+ * before using the object. See {@link SocketKeepalive}.
+ */
+ public final void stop() {
+ stopImpl();
+ }
+
+ abstract void stopImpl();
+
+ /**
+ * Deactivate this {@link SocketKeepalive} and free allocated resources. The instance won't be
+ * usable again if {@code close()} is called.
+ */
+ @Override
+ public final void close() {
+ stop();
+ stopLooper();
+ }
+
+ /**
+ * The callback which app can use to learn the status changes of {@link SocketKeepalive}. See
+ * {@link SocketKeepalive}.
+ */
+ public static class Callback {
+ /** The requested keepalive was successfully started. */
+ public void onStarted() {}
+ /** The keepalive was successfully stopped. */
+ public void onStopped() {}
+ /** An error occurred. */
+ public void onError(@ErrorCode int error) {}
+ /** The keepalive on a TCP socket was stopped because the socket received data. */
+ public void onDataReceived() {}
+ }
+}
diff --git a/core/java/android/net/VpnService.java b/core/java/android/net/VpnService.java
index 37bf3a71ce62..dc099a46aa2a 100644
--- a/core/java/android/net/VpnService.java
+++ b/core/java/android/net/VpnService.java
@@ -509,6 +509,15 @@ public class VpnService extends Service {
}
/**
+ * Sets an HTTP proxy for the VPN network. This proxy is only a recommendation
+ * and it is possible that some apps will ignore it.
+ */
+ public Builder setHttpProxy(ProxyInfo proxyInfo) {
+ mConfig.proxyInfo = proxyInfo;
+ return this;
+ }
+
+ /**
* Add a network address to the VPN interface. Both IPv4 and IPv6
* addresses are supported. At least one address must be set before
* calling {@link #establish}.
diff --git a/core/java/android/net/metrics/IpConnectivityLog.java b/core/java/android/net/metrics/IpConnectivityLog.java
index 16aea31b97c9..5b5a23578954 100644
--- a/core/java/android/net/metrics/IpConnectivityLog.java
+++ b/core/java/android/net/metrics/IpConnectivityLog.java
@@ -18,7 +18,6 @@ package android.net.metrics;
import android.annotation.SystemApi;
import android.annotation.TestApi;
-import android.annotation.UnsupportedAppUsage;
import android.net.ConnectivityMetricsEvent;
import android.net.IIpConnectivityMetrics;
import android.net.Network;
@@ -51,7 +50,8 @@ public class IpConnectivityLog {
public interface Event extends Parcelable {}
/** @hide */
- @UnsupportedAppUsage
+ @SystemApi
+ @TestApi
public IpConnectivityLog() {
}
diff --git a/core/java/android/net/metrics/RaEvent.java b/core/java/android/net/metrics/RaEvent.java
index d308246f4d77..04a2e6e3102c 100644
--- a/core/java/android/net/metrics/RaEvent.java
+++ b/core/java/android/net/metrics/RaEvent.java
@@ -16,7 +16,8 @@
package android.net.metrics;
-import android.annotation.UnsupportedAppUsage;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.os.Parcel;
import android.os.Parcelable;
@@ -24,19 +25,28 @@ import android.os.Parcelable;
* An event logged when the APF packet socket receives an RA packet.
* {@hide}
*/
+@SystemApi
+@TestApi
public final class RaEvent implements IpConnectivityLog.Event {
- public static final long NO_LIFETIME = -1L;
+ private static final long NO_LIFETIME = -1L;
// Lifetime in seconds of options found in a single RA packet.
// When an option is not set, the value of the associated field is -1;
+ /** @hide */
public final long routerLifetime;
+ /** @hide */
public final long prefixValidLifetime;
+ /** @hide */
public final long prefixPreferredLifetime;
+ /** @hide */
public final long routeInfoLifetime;
+ /** @hide */
public final long rdnssLifetime;
+ /** @hide */
public final long dnsslLifetime;
+ /** @hide */
public RaEvent(long routerLifetime, long prefixValidLifetime, long prefixPreferredLifetime,
long routeInfoLifetime, long rdnssLifetime, long dnsslLifetime) {
this.routerLifetime = routerLifetime;
@@ -47,6 +57,7 @@ public final class RaEvent implements IpConnectivityLog.Event {
this.dnsslLifetime = dnsslLifetime;
}
+ /** @hide */
private RaEvent(Parcel in) {
routerLifetime = in.readLong();
prefixValidLifetime = in.readLong();
@@ -56,6 +67,7 @@ public final class RaEvent implements IpConnectivityLog.Event {
dnsslLifetime = in.readLong();
}
+ /** @hide */
@Override
public void writeToParcel(Parcel out, int flags) {
out.writeLong(routerLifetime);
@@ -66,6 +78,7 @@ public final class RaEvent implements IpConnectivityLog.Event {
out.writeLong(dnsslLifetime);
}
+ /** @hide */
@Override
public int describeContents() {
return 0;
@@ -83,6 +96,7 @@ public final class RaEvent implements IpConnectivityLog.Event {
.toString();
}
+ /** @hide */
public static final Parcelable.Creator<RaEvent> CREATOR = new Parcelable.Creator<RaEvent>() {
public RaEvent createFromParcel(Parcel in) {
return new RaEvent(in);
@@ -102,47 +116,39 @@ public final class RaEvent implements IpConnectivityLog.Event {
long rdnssLifetime = NO_LIFETIME;
long dnsslLifetime = NO_LIFETIME;
- @UnsupportedAppUsage
public Builder() {
}
- @UnsupportedAppUsage
public RaEvent build() {
return new RaEvent(routerLifetime, prefixValidLifetime, prefixPreferredLifetime,
routeInfoLifetime, rdnssLifetime, dnsslLifetime);
}
- @UnsupportedAppUsage
public Builder updateRouterLifetime(long lifetime) {
routerLifetime = updateLifetime(routerLifetime, lifetime);
return this;
}
- @UnsupportedAppUsage
public Builder updatePrefixValidLifetime(long lifetime) {
prefixValidLifetime = updateLifetime(prefixValidLifetime, lifetime);
return this;
}
- @UnsupportedAppUsage
public Builder updatePrefixPreferredLifetime(long lifetime) {
prefixPreferredLifetime = updateLifetime(prefixPreferredLifetime, lifetime);
return this;
}
- @UnsupportedAppUsage
public Builder updateRouteInfoLifetime(long lifetime) {
routeInfoLifetime = updateLifetime(routeInfoLifetime, lifetime);
return this;
}
- @UnsupportedAppUsage
public Builder updateRdnssLifetime(long lifetime) {
rdnssLifetime = updateLifetime(rdnssLifetime, lifetime);
return this;
}
- @UnsupportedAppUsage
public Builder updateDnsslLifetime(long lifetime) {
dnsslLifetime = updateLifetime(dnsslLifetime, lifetime);
return this;
diff --git a/core/java/android/os/AppZygote.java b/core/java/android/os/AppZygote.java
index 950f38167754..6daa5b4dc6d8 100644
--- a/core/java/android/os/AppZygote.java
+++ b/core/java/android/os/AppZygote.java
@@ -103,7 +103,7 @@ public class AppZygote {
String abi = mAppInfo.primaryCpuAbi != null ? mAppInfo.primaryCpuAbi :
Build.SUPPORTED_ABIS[0];
try {
- mZygote = Process.zygoteProcess.startChildZygote(
+ mZygote = Process.ZYGOTE_PROCESS.startChildZygote(
"com.android.internal.os.AppZygoteInit",
mAppInfo.processName + "_zygote",
mZygoteUid,
diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java
index 5bf909566f3b..efcad3ece97d 100644
--- a/core/java/android/os/GraphicsEnvironment.java
+++ b/core/java/android/os/GraphicsEnvironment.java
@@ -369,26 +369,6 @@ public class GraphicsEnvironment {
}
/**
- * Attempt to setup ANGLE with a (temporary) default rules file: b/121153494
- * True: Rules file was loaded.
- * False: Rules file was *not* loaded.
- */
- private boolean setupAngleRulesDebug(String packageName, String paths, String devOptIn) {
- // b/121153494
- // Skip APK rules file checking.
- if (!DEBUG) {
- Log.v(TAG, "Skipping loading the rules file.");
- // Fill in some default values for now, so the loader can get an answer when it asks.
- // Most importantly, we need to indicate which app we are init'ing and what the
- // developer options for it are so we can turn on ANGLE if needed.
- setAngleInfo(paths, packageName, devOptIn, null, 0, 0);
- return true;
- }
-
- return false;
- }
-
- /**
* Attempt to setup ANGLE with a rules file loaded from the ANGLE APK.
* True: APK rules file was loaded.
* False: APK rules file was *not* loaded.
@@ -425,16 +405,57 @@ public class GraphicsEnvironment {
}
/**
+ * Pull ANGLE whitelist from GlobalSettings and compare against current package
+ */
+ private boolean checkAngleWhitelist(Bundle bundle, String packageName) {
+ List<String> angleWhitelist =
+ getGlobalSettingsString(bundle,
+ Settings.Global.GLOBAL_SETTINGS_ANGLE_WHITELIST);
+
+ return angleWhitelist.contains(packageName);
+ }
+
+ /**
* Pass ANGLE details down to trigger enable logic
*/
public void setupAngle(Context context, Bundle bundle, String packageName) {
- String devOptIn = getDriverForPkg(bundle, packageName);
+ if (packageName.isEmpty()) {
+ Log.v(TAG, "No package name available yet, skipping ANGLE setup");
+ return;
+ }
+ String devOptIn = getDriverForPkg(bundle, packageName);
if (DEBUG) {
Log.v(TAG, "ANGLE Developer option for '" + packageName + "' "
+ "set to: '" + devOptIn + "'");
}
+ // We only need to check rules if the app is whitelisted or the developer has
+ // explicitly chosen something other than default driver.
+ //
+ // The whitelist will be generated by the ANGLE APK at both boot time and
+ // ANGLE update time. It will only include apps mentioned in the rules file.
+ //
+ // If the user has set the developer option to something other than default,
+ // we need to call setupAngleRulesApk() with the package name and the developer
+ // option value (native/angle/other). Then later when we are actually trying to
+ // load a driver, GraphicsEnv::shouldUseAngle() has seen the package name before
+ // and can confidently answer yes/no based on the previously set developer
+ // option value.
+ boolean whitelisted = checkAngleWhitelist(bundle, packageName);
+ boolean defaulted = devOptIn.equals(sDriverMap.get(OpenGlDriverChoice.DEFAULT));
+ boolean rulesCheck = (whitelisted || !defaulted);
+ if (!rulesCheck) {
+ return;
+ }
+
+ if (whitelisted) {
+ Log.v(TAG, "ANGLE whitelist includes " + packageName);
+ }
+ if (!defaulted) {
+ Log.v(TAG, "ANGLE developer option for " + packageName + ": " + devOptIn);
+ }
+
String anglePkgName = getAnglePackageName(context);
if (anglePkgName.isEmpty()) {
Log.e(TAG, "Failed to find ANGLE package.");
@@ -466,12 +487,6 @@ public class GraphicsEnvironment {
return;
}
- // b/121153494
- if (setupAngleRulesDebug(packageName, paths, devOptIn)) {
- // We setup ANGLE with defaults, so we're done here.
- return;
- }
-
if (setupAngleRulesApk(anglePkgName, angleInfo, context, packageName, paths, devOptIn)) {
// We setup ANGLE with rules from the APK, so we're done here.
return;
diff --git a/core/java/android/os/ParcelFileDescriptor.java b/core/java/android/os/ParcelFileDescriptor.java
index 63912ec327a4..630bd2e509ff 100644
--- a/core/java/android/os/ParcelFileDescriptor.java
+++ b/core/java/android/os/ParcelFileDescriptor.java
@@ -54,6 +54,7 @@ import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InterruptedIOException;
+import java.io.UncheckedIOException;
import java.net.DatagramSocket;
import java.net.Socket;
import java.nio.ByteOrder;
@@ -393,26 +394,41 @@ public class ParcelFileDescriptor implements Parcelable, Closeable {
* @param socket The Socket whose FileDescriptor is used to create
* a new ParcelFileDescriptor.
*
- * @return A new ParcelFileDescriptor with the FileDescriptor of the
- * specified Socket.
+ * @return A new ParcelFileDescriptor with a duped copy of the
+ * FileDescriptor of the specified Socket.
+ *
+ * @throws UncheckedIOException if {@link #dup(FileDescriptor)} throws IOException.
*/
public static ParcelFileDescriptor fromSocket(Socket socket) {
FileDescriptor fd = socket.getFileDescriptor$();
- return fd != null ? new ParcelFileDescriptor(fd) : null;
+ try {
+ return fd != null ? ParcelFileDescriptor.dup(fd) : null;
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
}
/**
- * Create a new ParcelFileDescriptor from the specified DatagramSocket.
+ * Create a new ParcelFileDescriptor from the specified DatagramSocket. The
+ * new ParcelFileDescriptor holds a dup of the original FileDescriptor in
+ * the DatagramSocket, so you must still close the DatagramSocket as well
+ * as the new ParcelFileDescriptor.
*
* @param datagramSocket The DatagramSocket whose FileDescriptor is used
* to create a new ParcelFileDescriptor.
*
- * @return A new ParcelFileDescriptor with the FileDescriptor of the
- * specified DatagramSocket.
+ * @return A new ParcelFileDescriptor with a duped copy of the
+ * FileDescriptor of the specified Socket.
+ *
+ * @throws UncheckedIOException if {@link #dup(FileDescriptor)} throws IOException.
*/
public static ParcelFileDescriptor fromDatagramSocket(DatagramSocket datagramSocket) {
FileDescriptor fd = datagramSocket.getFileDescriptor$();
- return fd != null ? new ParcelFileDescriptor(fd) : null;
+ try {
+ return fd != null ? ParcelFileDescriptor.dup(fd) : null;
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
}
/**
@@ -542,7 +558,7 @@ public class ParcelFileDescriptor implements Parcelable, Closeable {
}
file.deactivate();
FileDescriptor fd = file.getFileDescriptor();
- return fd != null ? new ParcelFileDescriptor(fd) : null;
+ return fd != null ? ParcelFileDescriptor.dup(fd) : null;
}
/**
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index f2a9adb6feea..d2ab053eb4e6 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -32,16 +32,6 @@ public class Process {
private static final String LOG_TAG = "Process";
/**
- * @hide for internal use only.
- */
- public static final String ZYGOTE_SOCKET = "zygote";
-
- /**
- * @hide for internal use only.
- */
- public static final String SECONDARY_ZYGOTE_SOCKET = "zygote_secondary";
-
- /**
* An invalid UID value.
*/
public static final int INVALID_UID = -1;
@@ -479,8 +469,7 @@ public class Process {
* State associated with the zygote process.
* @hide
*/
- public static final ZygoteProcess zygoteProcess =
- new ZygoteProcess(ZYGOTE_SOCKET, SECONDARY_ZYGOTE_SOCKET);
+ public static final ZygoteProcess ZYGOTE_PROCESS = new ZygoteProcess();
/**
* Start a new process.
@@ -538,10 +527,10 @@ public class Process {
@Nullable String[] packagesForUid,
@Nullable String[] visibleVols,
@Nullable String[] zygoteArgs) {
- return zygoteProcess.start(processClass, niceName, uid, gid, gids,
+ return ZYGOTE_PROCESS.start(processClass, niceName, uid, gid, gids,
runtimeFlags, mountExternal, targetSdkVersion, seInfo,
abi, instructionSet, appDataDir, invokeWith, packageName,
- packagesForUid, visibleVols, zygoteArgs);
+ packagesForUid, visibleVols, /*useBlastulaPool=*/ true, zygoteArgs);
}
/** @hide */
@@ -562,7 +551,7 @@ public class Process {
return WebViewZygote.getProcess().start(processClass, niceName, uid, gid, gids,
runtimeFlags, mountExternal, targetSdkVersion, seInfo,
abi, instructionSet, appDataDir, invokeWith, packageName,
- packagesForUid, visibleVols, zygoteArgs);
+ packagesForUid, visibleVols, /*useBlastulaPool=*/ false, zygoteArgs);
}
/**
diff --git a/core/java/android/os/VibrationEffect.java b/core/java/android/os/VibrationEffect.java
index 01d85c6c6c85..99fb608b80dc 100644
--- a/core/java/android/os/VibrationEffect.java
+++ b/core/java/android/os/VibrationEffect.java
@@ -16,6 +16,7 @@
package android.os;
+import android.annotation.IntDef;
import android.annotation.Nullable;
import android.annotation.TestApi;
import android.content.ContentResolver;
@@ -25,6 +26,8 @@ import android.hardware.vibrator.V1_2.Effect;
import android.net.Uri;
import android.util.MathUtils;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.Arrays;
/**
@@ -52,26 +55,20 @@ public abstract class VibrationEffect implements Parcelable {
* A click effect.
*
* @see #get(int)
- * @hide
*/
- @TestApi
public static final int EFFECT_CLICK = Effect.CLICK;
/**
* A double click effect.
*
* @see #get(int)
- * @hide
*/
- @TestApi
public static final int EFFECT_DOUBLE_CLICK = Effect.DOUBLE_CLICK;
/**
* A tick effect.
* @see #get(int)
- * @hide
*/
- @TestApi
public static final int EFFECT_TICK = Effect.TICK;
/**
@@ -93,9 +90,7 @@ public abstract class VibrationEffect implements Parcelable {
/**
* A heavy click effect.
* @see #get(int)
- * @hide
*/
- @TestApi
public static final int EFFECT_HEAVY_CLICK = Effect.HEAVY_CLICK;
/** {@hide} */
@@ -136,6 +131,16 @@ public abstract class VibrationEffect implements Parcelable {
Effect.RINGTONE_15
};
+ /** @hide */
+ @IntDef(prefix = { "EFFECT_" }, value = {
+ EFFECT_TICK,
+ EFFECT_CLICK,
+ EFFECT_HEAVY_CLICK,
+ EFFECT_DOUBLE_CLICK,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface EffectType {}
+
/** @hide to prevent subclassing from outside of the framework */
public VibrationEffect() { }
@@ -219,6 +224,27 @@ public abstract class VibrationEffect implements Parcelable {
}
/**
+ * Create a predefined vibration effect.
+ *
+ * Predefined effects are a set of common vibration effects that should be identical, regardless
+ * of the app they come from, in order to provide a cohesive experience for users across
+ * the entire device. They also may be custom tailored to the device hardware in order to
+ * provide a better experience than you could otherwise build using the generic building
+ * blocks.
+ *
+ * This will fallback to a generic pattern if one exists and there does not exist a
+ * hardware-specific implementation of the effect.
+ *
+ * @param effectId The ID of the effect to perform:
+ * {@link #EFFECT_CLICK}, {@link #EFFECT_DOUBLE_CLICK}, {@link #EFFECT_TICK}
+ *
+ * @return The desired effect.
+ */
+ public static VibrationEffect createPrebaked(@EffectType int effectId) {
+ return get(effectId, true);
+ }
+
+ /**
* Get a predefined vibration effect.
*
* Predefined effects are a set of common vibration effects that should be identical, regardless
diff --git a/core/java/android/os/ZygoteProcess.java b/core/java/android/os/ZygoteProcess.java
index ec7782177c73..9e47179e9152 100644
--- a/core/java/android/os/ZygoteProcess.java
+++ b/core/java/android/os/ZygoteProcess.java
@@ -62,87 +62,161 @@ import java.util.UUID;
* {@hide}
*/
public class ZygoteProcess {
+
+ /**
+ * @hide for internal use only.
+ */
+ public static final String ZYGOTE_SOCKET_NAME = "zygote";
+
+ /**
+ * @hide for internal use only.
+ */
+ public static final String ZYGOTE_SECONDARY_SOCKET_NAME = "zygote_secondary";
+
+ /**
+ * @hide for internal use only
+ */
+ public static final String BLASTULA_POOL_SOCKET_NAME = "blastula_pool";
+
+ /**
+ * @hide for internal use only
+ */
+ public static final String BLASTULA_POOL_SECONDARY_SOCKET_NAME = "blastula_pool_secondary";
+
+ /**
+ * @hide for internal use only
+ */
private static final String LOG_TAG = "ZygoteProcess";
/**
* The name of the socket used to communicate with the primary zygote.
*/
- private final LocalSocketAddress mSocket;
+ private final LocalSocketAddress mZygoteSocketAddress;
/**
* The name of the secondary (alternate ABI) zygote socket.
*/
- private final LocalSocketAddress mSecondarySocket;
+ private final LocalSocketAddress mZygoteSecondarySocketAddress;
+ /**
+ * The name of the socket used to communicate with the primary blastula pool.
+ */
+ private final LocalSocketAddress mBlastulaPoolSocketAddress;
- public ZygoteProcess(String primarySocket, String secondarySocket) {
- this(new LocalSocketAddress(primarySocket, LocalSocketAddress.Namespace.RESERVED),
- new LocalSocketAddress(secondarySocket, LocalSocketAddress.Namespace.RESERVED));
+ /**
+ * The name of the socket used to communicate with the secondary (alternate ABI) blastula pool.
+ */
+ private final LocalSocketAddress mBlastulaPoolSecondarySocketAddress;
+
+ public ZygoteProcess() {
+ mZygoteSocketAddress =
+ new LocalSocketAddress(ZYGOTE_SOCKET_NAME, LocalSocketAddress.Namespace.RESERVED);
+ mZygoteSecondarySocketAddress =
+ new LocalSocketAddress(ZYGOTE_SECONDARY_SOCKET_NAME,
+ LocalSocketAddress.Namespace.RESERVED);
+
+ mBlastulaPoolSocketAddress =
+ new LocalSocketAddress(BLASTULA_POOL_SOCKET_NAME,
+ LocalSocketAddress.Namespace.RESERVED);
+ mBlastulaPoolSecondarySocketAddress =
+ new LocalSocketAddress(BLASTULA_POOL_SECONDARY_SOCKET_NAME,
+ LocalSocketAddress.Namespace.RESERVED);
}
- public ZygoteProcess(LocalSocketAddress primarySocket, LocalSocketAddress secondarySocket) {
- mSocket = primarySocket;
- mSecondarySocket = secondarySocket;
+ public ZygoteProcess(LocalSocketAddress primarySocketAddress,
+ LocalSocketAddress secondarySocketAddress) {
+ mZygoteSocketAddress = primarySocketAddress;
+ mZygoteSecondarySocketAddress = secondarySocketAddress;
+
+ mBlastulaPoolSocketAddress = null;
+ mBlastulaPoolSecondarySocketAddress = null;
}
public LocalSocketAddress getPrimarySocketAddress() {
- return mSocket;
+ return mZygoteSocketAddress;
}
/**
* State for communicating with the zygote process.
*/
public static class ZygoteState {
- final LocalSocket socket;
- final DataInputStream inputStream;
- final BufferedWriter writer;
- final List<String> abiList;
-
- boolean mClosed;
-
- private ZygoteState(LocalSocket socket, DataInputStream inputStream,
- BufferedWriter writer, List<String> abiList) {
- this.socket = socket;
- this.inputStream = inputStream;
- this.writer = writer;
- this.abiList = abiList;
- }
+ final LocalSocketAddress mZygoteSocketAddress;
+ final LocalSocketAddress mBlastulaSocketAddress;
+
+ private final LocalSocket mZygoteSessionSocket;
+
+ final DataInputStream mZygoteInputStream;
+ final BufferedWriter mZygoteOutputWriter;
+
+ private final List<String> mABIList;
+
+ private boolean mClosed;
+
+ private ZygoteState(LocalSocketAddress zygoteSocketAddress,
+ LocalSocketAddress blastulaSocketAddress,
+ LocalSocket zygoteSessionSocket,
+ DataInputStream zygoteInputStream,
+ BufferedWriter zygoteOutputWriter,
+ List<String> abiList) {
+ this.mZygoteSocketAddress = zygoteSocketAddress;
+ this.mBlastulaSocketAddress = blastulaSocketAddress;
+ this.mZygoteSessionSocket = zygoteSessionSocket;
+ this.mZygoteInputStream = zygoteInputStream;
+ this.mZygoteOutputWriter = zygoteOutputWriter;
+ this.mABIList = abiList;
+ }
+
+ /**
+ * Create a new ZygoteState object by connecting to the given Zygote socket and saving the
+ * given blastula socket address.
+ *
+ * @param zygoteSocketAddress Zygote socket to connect to
+ * @param blastulaSocketAddress Blastula socket address to save for later
+ * @return A new ZygoteState object containing a session socket for the given Zygote socket
+ * address
+ * @throws IOException
+ */
+ public static ZygoteState connect(LocalSocketAddress zygoteSocketAddress,
+ LocalSocketAddress blastulaSocketAddress)
+ throws IOException {
- public static ZygoteState connect(LocalSocketAddress address) throws IOException {
DataInputStream zygoteInputStream = null;
- BufferedWriter zygoteWriter = null;
- final LocalSocket zygoteSocket = new LocalSocket();
+ BufferedWriter zygoteOutputWriter = null;
+ final LocalSocket zygoteSessionSocket = new LocalSocket();
try {
- zygoteSocket.connect(address);
-
- zygoteInputStream = new DataInputStream(zygoteSocket.getInputStream());
-
- zygoteWriter = new BufferedWriter(new OutputStreamWriter(
- zygoteSocket.getOutputStream()), 256);
+ zygoteSessionSocket.connect(zygoteSocketAddress);
+ zygoteInputStream = new DataInputStream(zygoteSessionSocket.getInputStream());
+ zygoteOutputWriter =
+ new BufferedWriter(
+ new OutputStreamWriter(zygoteSessionSocket.getOutputStream()),
+ Zygote.SOCKET_BUFFER_SIZE);
} catch (IOException ex) {
try {
- zygoteSocket.close();
- } catch (IOException ignore) {
- }
+ zygoteSessionSocket.close();
+ } catch (IOException ignore) { }
throw ex;
}
- String abiListString = getAbiList(zygoteWriter, zygoteInputStream);
- Log.i("Zygote", "Process: zygote socket " + address.getNamespace() + "/"
- + address.getName() + " opened, supported ABIS: " + abiListString);
+ return new ZygoteState(zygoteSocketAddress, blastulaSocketAddress,
+ zygoteSessionSocket, zygoteInputStream, zygoteOutputWriter,
+ getAbiList(zygoteOutputWriter, zygoteInputStream));
+ }
- return new ZygoteState(zygoteSocket, zygoteInputStream, zygoteWriter,
- Arrays.asList(abiListString.split(",")));
+ LocalSocket getBlastulaSessionSocket() throws IOException {
+ final LocalSocket blastulaSessionSocket = new LocalSocket();
+ blastulaSessionSocket.connect(this.mBlastulaSocketAddress);
+
+ return blastulaSessionSocket;
}
boolean matches(String abi) {
- return abiList.contains(abi);
+ return mABIList.contains(abi);
}
public void close() {
try {
- socket.close();
+ mZygoteSessionSocket.close();
} catch (IOException ex) {
Log.e(LOG_TAG,"I/O exception on routine close", ex);
}
@@ -237,12 +311,13 @@ public class ZygoteProcess {
@Nullable String packageName,
@Nullable String[] packagesForUid,
@Nullable String[] visibleVols,
+ boolean useBlastulaPool,
@Nullable String[] zygoteArgs) {
try {
return startViaZygote(processClass, niceName, uid, gid, gids,
runtimeFlags, mountExternal, targetSdkVersion, seInfo,
- abi, instructionSet, appDataDir, invokeWith, false /* startChildZygote */,
- packageName, packagesForUid, visibleVols, zygoteArgs);
+ abi, instructionSet, appDataDir, invokeWith, /*startChildZygote=*/false,
+ packageName, packagesForUid, visibleVols, useBlastulaPool, zygoteArgs);
} catch (ZygoteStartFailedEx ex) {
Log.e(LOG_TAG,
"Starting VM process through Zygote failed");
@@ -260,7 +335,7 @@ public class ZygoteProcess {
* @throws ZygoteStartFailedEx if the query failed.
*/
@GuardedBy("mLock")
- private static String getAbiList(BufferedWriter writer, DataInputStream inputStream)
+ private static List<String> getAbiList(BufferedWriter writer, DataInputStream inputStream)
throws IOException {
// Each query starts with the argument count (1 in this case)
writer.write("1");
@@ -276,7 +351,9 @@ public class ZygoteProcess {
byte[] bytes = new byte[numBytes];
inputStream.readFully(bytes);
- return new String(bytes, StandardCharsets.US_ASCII);
+ String rawList = new String(bytes, StandardCharsets.US_ASCII);
+
+ return Arrays.asList(rawList.split(","));
}
/**
@@ -288,59 +365,128 @@ public class ZygoteProcess {
*/
@GuardedBy("mLock")
private static Process.ProcessStartResult zygoteSendArgsAndGetResult(
- ZygoteState zygoteState, ArrayList<String> args)
+ ZygoteState zygoteState, boolean useBlastulaPool, ArrayList<String> args)
throws ZygoteStartFailedEx {
- try {
- // Throw early if any of the arguments are malformed. This means we can
- // avoid writing a partial response to the zygote.
- int sz = args.size();
- for (int i = 0; i < sz; i++) {
- if (args.get(i).indexOf('\n') >= 0) {
- throw new ZygoteStartFailedEx("embedded newlines not allowed");
- }
+ // Throw early if any of the arguments are malformed. This means we can
+ // avoid writing a partial response to the zygote.
+ for (String arg : args) {
+ if (arg.indexOf('\n') >= 0) {
+ throw new ZygoteStartFailedEx("embedded newlines not allowed");
}
+ }
- /**
- * See com.android.internal.os.SystemZygoteInit.readArgumentList()
- * Presently the wire format to the zygote process is:
- * a) a count of arguments (argc, in essence)
- * b) a number of newline-separated argument strings equal to count
- *
- * After the zygote process reads these it will write the pid of
- * the child or -1 on failure, followed by boolean to
- * indicate whether a wrapper process was used.
- */
- final BufferedWriter writer = zygoteState.writer;
- final DataInputStream inputStream = zygoteState.inputStream;
-
- writer.write(Integer.toString(args.size()));
- writer.newLine();
+ /**
+ * See com.android.internal.os.SystemZygoteInit.readArgumentList()
+ * Presently the wire format to the zygote process is:
+ * a) a count of arguments (argc, in essence)
+ * b) a number of newline-separated argument strings equal to count
+ *
+ * After the zygote process reads these it will write the pid of
+ * the child or -1 on failure, followed by boolean to
+ * indicate whether a wrapper process was used.
+ */
+ String msgStr = Integer.toString(args.size()) + "\n"
+ + String.join("\n", args) + "\n";
- for (int i = 0; i < sz; i++) {
- String arg = args.get(i);
- writer.write(arg);
- writer.newLine();
+ // Should there be a timeout on this?
+ Process.ProcessStartResult result = new Process.ProcessStartResult();
+
+ // TODO (chriswailes): Move branch body into separate function.
+ if (useBlastulaPool && Zygote.BLASTULA_POOL_ENABLED && isValidBlastulaCommand(args)) {
+ LocalSocket blastulaSessionSocket = null;
+
+ try {
+ blastulaSessionSocket = zygoteState.getBlastulaSessionSocket();
+
+ final BufferedWriter blastulaWriter =
+ new BufferedWriter(
+ new OutputStreamWriter(blastulaSessionSocket.getOutputStream()),
+ Zygote.SOCKET_BUFFER_SIZE);
+ final DataInputStream blastulaReader =
+ new DataInputStream(blastulaSessionSocket.getInputStream());
+
+ blastulaWriter.write(msgStr);
+ blastulaWriter.flush();
+
+ result.pid = blastulaReader.readInt();
+ // Blastulas can't be used to spawn processes that need wrappers.
+ result.usingWrapper = false;
+
+ if (result.pid < 0) {
+ throw new ZygoteStartFailedEx("Blastula specialization failed");
+ }
+
+ return result;
+ } catch (IOException ex) {
+ // If there was an IOException using the blastula pool we will log the error and
+ // attempt to start the process through the Zygote.
+ Log.e(LOG_TAG, "IO Exception while communicating with blastula pool - "
+ + ex.toString());
+ } finally {
+ try {
+ blastulaSessionSocket.close();
+ } catch (IOException ex) {
+ Log.e(LOG_TAG, "Failed to close blastula session socket: " + ex.getMessage());
+ }
}
+ }
- writer.flush();
+ try {
+ final BufferedWriter zygoteWriter = zygoteState.mZygoteOutputWriter;
+ final DataInputStream zygoteInputStream = zygoteState.mZygoteInputStream;
- // Should there be a timeout on this?
- Process.ProcessStartResult result = new Process.ProcessStartResult();
+ zygoteWriter.write(msgStr);
+ zygoteWriter.flush();
// Always read the entire result from the input stream to avoid leaving
// bytes in the stream for future process starts to accidentally stumble
// upon.
- result.pid = inputStream.readInt();
- result.usingWrapper = inputStream.readBoolean();
-
- if (result.pid < 0) {
- throw new ZygoteStartFailedEx("fork() failed");
- }
- return result;
+ result.pid = zygoteInputStream.readInt();
+ result.usingWrapper = zygoteInputStream.readBoolean();
} catch (IOException ex) {
zygoteState.close();
+ Log.e(LOG_TAG, "IO Exception while communicating with Zygote - "
+ + ex.toString());
throw new ZygoteStartFailedEx(ex);
}
+
+ if (result.pid < 0) {
+ throw new ZygoteStartFailedEx("fork() failed");
+ }
+
+ return result;
+ }
+
+ /**
+ * Flags that may not be passed to a blastula.
+ */
+ private static final String[] INVALID_BLASTULA_FLAGS = {
+ "--query-abi-list",
+ "--get-pid",
+ "--preload-default",
+ "--preload-package",
+ "--preload-app",
+ "--start-child-zygote",
+ "--set-api-blacklist-exemptions",
+ "--hidden-api-log-sampling-rate",
+ "--invoke-with"
+ };
+
+ /**
+ * Tests a command list to see if it is valid to send to a blastula.
+ * @param args Zygote/Blastula command arguments
+ * @return True if the command can be passed to a blastula; false otherwise
+ */
+ private static boolean isValidBlastulaCommand(ArrayList<String> args) {
+ for (String flag : args) {
+ for (String badFlag : INVALID_BLASTULA_FLAGS) {
+ if (flag.startsWith(badFlag)) {
+ return false;
+ }
+ }
+ }
+
+ return true;
}
/**
@@ -382,6 +528,7 @@ public class ZygoteProcess {
@Nullable String packageName,
@Nullable String[] packagesForUid,
@Nullable String[] visibleVols,
+ boolean useBlastulaPool,
@Nullable String[] extraArgs)
throws ZygoteStartFailedEx {
ArrayList<String> argsForZygote = new ArrayList<String>();
@@ -488,7 +635,9 @@ public class ZygoteProcess {
}
synchronized(mLock) {
- return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), argsForZygote);
+ return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi),
+ useBlastulaPool,
+ argsForZygote);
}
}
@@ -528,18 +677,18 @@ public class ZygoteProcess {
ZygoteState state = openZygoteSocketIfNeeded(abi);
// Each query starts with the argument count (1 in this case)
- state.writer.write("1");
+ state.mZygoteOutputWriter.write("1");
// ... followed by a new-line.
- state.writer.newLine();
+ state.mZygoteOutputWriter.newLine();
// ... followed by our only argument.
- state.writer.write("--get-pid");
- state.writer.newLine();
- state.writer.flush();
+ state.mZygoteOutputWriter.write("--get-pid");
+ state.mZygoteOutputWriter.newLine();
+ state.mZygoteOutputWriter.flush();
// The response is a length prefixed stream of ASCII bytes.
- int numBytes = state.inputStream.readInt();
+ int numBytes = state.mZygoteInputStream.readInt();
byte[] bytes = new byte[numBytes];
- state.inputStream.readFully(bytes);
+ state.mZygoteInputStream.readFully(bytes);
return Integer.parseInt(new String(bytes, StandardCharsets.US_ASCII));
}
@@ -593,16 +742,16 @@ public class ZygoteProcess {
return true;
}
try {
- state.writer.write(Integer.toString(mApiBlacklistExemptions.size() + 1));
- state.writer.newLine();
- state.writer.write("--set-api-blacklist-exemptions");
- state.writer.newLine();
+ state.mZygoteOutputWriter.write(Integer.toString(mApiBlacklistExemptions.size() + 1));
+ state.mZygoteOutputWriter.newLine();
+ state.mZygoteOutputWriter.write("--set-api-blacklist-exemptions");
+ state.mZygoteOutputWriter.newLine();
for (int i = 0; i < mApiBlacklistExemptions.size(); ++i) {
- state.writer.write(mApiBlacklistExemptions.get(i));
- state.writer.newLine();
+ state.mZygoteOutputWriter.write(mApiBlacklistExemptions.get(i));
+ state.mZygoteOutputWriter.newLine();
}
- state.writer.flush();
- int status = state.inputStream.readInt();
+ state.mZygoteOutputWriter.flush();
+ int status = state.mZygoteInputStream.readInt();
if (status != 0) {
Slog.e(LOG_TAG, "Failed to set API blacklist exemptions; status " + status);
}
@@ -622,13 +771,13 @@ public class ZygoteProcess {
return;
}
try {
- state.writer.write(Integer.toString(1));
- state.writer.newLine();
- state.writer.write("--hidden-api-log-sampling-rate="
+ state.mZygoteOutputWriter.write(Integer.toString(1));
+ state.mZygoteOutputWriter.newLine();
+ state.mZygoteOutputWriter.write("--hidden-api-log-sampling-rate="
+ Integer.toString(mHiddenApiAccessLogSampleRate));
- state.writer.newLine();
- state.writer.flush();
- int status = state.inputStream.readInt();
+ state.mZygoteOutputWriter.newLine();
+ state.mZygoteOutputWriter.flush();
+ int status = state.mZygoteInputStream.readInt();
if (status != 0) {
Slog.e(LOG_TAG, "Failed to set hidden API log sampling rate; status " + status);
}
@@ -638,22 +787,29 @@ public class ZygoteProcess {
}
/**
- * Tries to open socket to Zygote process if not already open. If
- * already open, does nothing. May block and retry. Requires that mLock be held.
+ * Tries to open a session socket to a Zygote process with a compatible ABI if one is not
+ * already open. If a compatible session socket is already open that session socket is returned.
+ * This function may block and may have to try connecting to multiple Zygotes to find the
+ * appropriate one. Requires that mLock be held.
*/
@GuardedBy("mLock")
- private ZygoteState openZygoteSocketIfNeeded(String abi) throws ZygoteStartFailedEx {
+ private ZygoteState openZygoteSocketIfNeeded(String abi)
+ throws ZygoteStartFailedEx {
+
Preconditions.checkState(Thread.holdsLock(mLock), "ZygoteProcess lock not held");
if (primaryZygoteState == null || primaryZygoteState.isClosed()) {
try {
- primaryZygoteState = ZygoteState.connect(mSocket);
+ primaryZygoteState =
+ ZygoteState.connect(mZygoteSocketAddress, mBlastulaPoolSocketAddress);
} catch (IOException ioe) {
throw new ZygoteStartFailedEx("Error connecting to primary zygote", ioe);
}
+
maybeSetApiBlacklistExemptions(primaryZygoteState, false);
maybeSetHiddenApiAccessLogSampleRate(primaryZygoteState);
}
+
if (primaryZygoteState.matches(abi)) {
return primaryZygoteState;
}
@@ -661,10 +817,13 @@ public class ZygoteProcess {
// The primary zygote didn't match. Try the secondary.
if (secondaryZygoteState == null || secondaryZygoteState.isClosed()) {
try {
- secondaryZygoteState = ZygoteState.connect(mSecondarySocket);
+ secondaryZygoteState =
+ ZygoteState.connect(mZygoteSecondarySocketAddress,
+ mBlastulaPoolSecondarySocketAddress);
} catch (IOException ioe) {
throw new ZygoteStartFailedEx("Error connecting to secondary zygote", ioe);
}
+
maybeSetApiBlacklistExemptions(secondaryZygoteState, false);
maybeSetHiddenApiAccessLogSampleRate(secondaryZygoteState);
}
@@ -685,11 +844,11 @@ public class ZygoteProcess {
IOException {
synchronized (mLock) {
ZygoteState state = openZygoteSocketIfNeeded(abi);
- state.writer.write("2");
- state.writer.newLine();
+ state.mZygoteOutputWriter.write("2");
+ state.mZygoteOutputWriter.newLine();
- state.writer.write("--preload-app");
- state.writer.newLine();
+ state.mZygoteOutputWriter.write("--preload-app");
+ state.mZygoteOutputWriter.newLine();
// Zygote args needs to be strings, so in order to pass ApplicationInfo,
// write it to a Parcel, and base64 the raw Parcel bytes to the other side.
@@ -697,12 +856,12 @@ public class ZygoteProcess {
appInfo.writeToParcel(parcel, 0 /* flags */);
String encodedParcelData = Base64.getEncoder().encodeToString(parcel.marshall());
parcel.recycle();
- state.writer.write(encodedParcelData);
- state.writer.newLine();
+ state.mZygoteOutputWriter.write(encodedParcelData);
+ state.mZygoteOutputWriter.newLine();
- state.writer.flush();
+ state.mZygoteOutputWriter.flush();
- return (state.inputStream.readInt() == 0);
+ return (state.mZygoteInputStream.readInt() == 0);
}
}
@@ -715,27 +874,27 @@ public class ZygoteProcess {
IOException {
synchronized(mLock) {
ZygoteState state = openZygoteSocketIfNeeded(abi);
- state.writer.write("5");
- state.writer.newLine();
+ state.mZygoteOutputWriter.write("5");
+ state.mZygoteOutputWriter.newLine();
- state.writer.write("--preload-package");
- state.writer.newLine();
+ state.mZygoteOutputWriter.write("--preload-package");
+ state.mZygoteOutputWriter.newLine();
- state.writer.write(packagePath);
- state.writer.newLine();
+ state.mZygoteOutputWriter.write(packagePath);
+ state.mZygoteOutputWriter.newLine();
- state.writer.write(libsPath);
- state.writer.newLine();
+ state.mZygoteOutputWriter.write(libsPath);
+ state.mZygoteOutputWriter.newLine();
- state.writer.write(libFileName);
- state.writer.newLine();
+ state.mZygoteOutputWriter.write(libFileName);
+ state.mZygoteOutputWriter.newLine();
- state.writer.write(cacheKey);
- state.writer.newLine();
+ state.mZygoteOutputWriter.write(cacheKey);
+ state.mZygoteOutputWriter.newLine();
- state.writer.flush();
+ state.mZygoteOutputWriter.flush();
- return (state.inputStream.readInt() == 0);
+ return (state.mZygoteInputStream.readInt() == 0);
}
}
@@ -749,13 +908,13 @@ public class ZygoteProcess {
synchronized (mLock) {
ZygoteState state = openZygoteSocketIfNeeded(abi);
// Each query starts with the argument count (1 in this case)
- state.writer.write("1");
- state.writer.newLine();
- state.writer.write("--preload-default");
- state.writer.newLine();
- state.writer.flush();
+ state.mZygoteOutputWriter.write("1");
+ state.mZygoteOutputWriter.newLine();
+ state.mZygoteOutputWriter.write("--preload-default");
+ state.mZygoteOutputWriter.newLine();
+ state.mZygoteOutputWriter.flush();
- return (state.inputStream.readInt() == 0);
+ return (state.mZygoteInputStream.readInt() == 0);
}
}
@@ -763,20 +922,21 @@ public class ZygoteProcess {
* Try connecting to the Zygote over and over again until we hit a time-out.
* @param socketName The name of the socket to connect to.
*/
- public static void waitForConnectionToZygote(String socketName) {
- final LocalSocketAddress address =
- new LocalSocketAddress(socketName, LocalSocketAddress.Namespace.RESERVED);
- waitForConnectionToZygote(address);
+ public static void waitForConnectionToZygote(String zygoteSocketName) {
+ final LocalSocketAddress zygoteSocketAddress =
+ new LocalSocketAddress(zygoteSocketName, LocalSocketAddress.Namespace.RESERVED);
+ waitForConnectionToZygote(zygoteSocketAddress);
}
/**
* Try connecting to the Zygote over and over again until we hit a time-out.
* @param address The name of the socket to connect to.
*/
- public static void waitForConnectionToZygote(LocalSocketAddress address) {
+ public static void waitForConnectionToZygote(LocalSocketAddress zygoteSocketAddress) {
for (int n = 20; n >= 0; n--) {
try {
- final ZygoteState zs = ZygoteState.connect(address);
+ final ZygoteState zs =
+ ZygoteState.connect(zygoteSocketAddress, null);
zs.close();
return;
} catch (IOException ioe) {
@@ -789,7 +949,8 @@ public class ZygoteProcess {
} catch (InterruptedException ie) {
}
}
- Slog.wtf(LOG_TAG, "Failed to connect to Zygote through socket " + address.getName());
+ Slog.wtf(LOG_TAG, "Failed to connect to Zygote through socket "
+ + zygoteSocketAddress.getName());
}
/**
@@ -839,7 +1000,8 @@ public class ZygoteProcess {
gids, runtimeFlags, 0 /* mountExternal */, 0 /* targetSdkVersion */, seInfo,
abi, instructionSet, null /* appDataDir */, null /* invokeWith */,
true /* startChildZygote */, null /* packageName */,
- null /* packagesForUid */, null /* visibleVolumes */, extraArgs);
+ null /* packagesForUid */, null /* visibleVolumes */,
+ false /* useBlastulaPool */, extraArgs);
} catch (ZygoteStartFailedEx ex) {
throw new RuntimeException("Starting child-zygote through Zygote failed", ex);
}
diff --git a/core/java/android/provider/CalendarContract.java b/core/java/android/provider/CalendarContract.java
index c167ea18f0c5..8bd75d779154 100644
--- a/core/java/android/provider/CalendarContract.java
+++ b/core/java/android/provider/CalendarContract.java
@@ -44,6 +44,8 @@ import android.util.Log;
import com.android.internal.util.Preconditions;
+import java.util.Set;
+
/**
* <p>
* The contract between the calendar provider and applications. Contains
@@ -217,7 +219,7 @@ public final class CalendarContract {
* The intent will have its action set to
* {@link CalendarContract#ACTION_VIEW_WORK_CALENDAR_EVENT} and contain extras
* corresponding to the API's arguments. A calendar app intending to support
- * cross profile events viewing should handle this intent, parse the arguments
+ * cross-profile events viewing should handle this intent, parse the arguments
* and show the appropriate UI.
*
* @param context the context.
@@ -767,9 +769,10 @@ public final class CalendarContract {
* projection of the query to this uri that are not contained in the above list.
*
* <p>This uri will return an empty cursor if the calling user is not a parent profile
- * of a managed profile, or cross profile calendar is disabled in Settings, or this uri is
+ * of a managed profile, or cross-profile calendar is disabled in Settings, or this uri is
* queried from a package that is not whitelisted by profile owner of the managed profile
- * via {@link DevicePolicyManager#addCrossProfileCalendarPackage(ComponentName, String)}.
+ * via
+ * {@link DevicePolicyManager#setCrossProfileCalendarPackages(ComponentName, Set)}.
*
* @see DevicePolicyManager#getCrossProfileCalendarPackages(ComponentName)
* @see Settings.Secure#CROSS_PROFILE_CALENDAR_ENABLED
@@ -1758,9 +1761,10 @@ public final class CalendarContract {
* projection of the query to this uri that are not contained in the above list.
*
* <p>This uri will return an empty cursor if the calling user is not a parent profile
- * of a managed profile, or cross profile calendar is disabled in Settings, or this uri is
+ * of a managed profile, or cross-profile calendar is disabled in Settings, or this uri is
* queried from a package that is not whitelisted by profile owner of the managed profile
- * via {@link DevicePolicyManager#addCrossProfileCalendarPackage(ComponentName, String)}.
+ * via
+ * {@link DevicePolicyManager#setCrossProfileCalendarPackages(ComponentName, Set)}.
*
* @see DevicePolicyManager#getCrossProfileCalendarPackages(ComponentName)
* @see Settings.Secure#CROSS_PROFILE_CALENDAR_ENABLED
@@ -1968,10 +1972,10 @@ public final class CalendarContract {
* projection of the query to this uri that are not contained in the above list.
*
* <p>This uri will return an empty cursor if the calling user is not a parent profile
- * of a managed profile, or cross profile calendar for the managed profile is disabled in
+ * of a managed profile, or cross-profile calendar for the managed profile is disabled in
* Settings, or this uri is queried from a package that is not whitelisted by
* profile owner of the managed profile via
- * {@link DevicePolicyManager#addCrossProfileCalendarPackage(ComponentName, String)}.
+ * {@link DevicePolicyManager#setCrossProfileCalendarPackages(ComponentName, Set)}.
*
* @see DevicePolicyManager#getCrossProfileCalendarPackages(ComponentName)
* @see Settings.Secure#CROSS_PROFILE_CALENDAR_ENABLED
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index 87efbf3b2397..cd823a9c8997 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -54,6 +54,7 @@ public final class DeviceConfig {
/**
* Namespace for all Game Driver features.
+ *
* @hide
*/
@SystemApi
@@ -104,6 +105,24 @@ public final class DeviceConfig {
public static final String NAMESPACE_NOTIFICATION_ASSISTANT = "notification_assistant";
/**
+ * Namespace for attention-based features provided by on-device machine intelligence.
+ *
+ * @hide
+ */
+ @SystemApi
+ public interface IntelligenceAttention {
+ String NAMESPACE = "intelligence_attention";
+ /**
+ * If {@code true}, enables the attention check.
+ */
+ String PROPERTY_ATTENTION_CHECK_ENABLED = "attention_check_enabled";
+ /**
+ * Settings for performing the attention check.
+ */
+ String PROPERTY_ATTENTION_CHECK_SETTINGS = "attention_check_settings";
+ }
+
+ /**
* Telephony related properties definitions.
*
* @hide
@@ -121,6 +140,47 @@ public final class DeviceConfig {
String PROPERTY_RAMPING_RINGER_DURATION = "ramping_duration";
}
+ /**
+ * Namespace for Full Stack Integrity to run privileged apps only in JIT mode. The flag applies
+ * at process start, so reboot is a way to bring the device to a clean state.
+ *
+ * @hide
+ */
+ @SystemApi
+ public interface FsiBoot {
+ String NAMESPACE = "fsi_boot";
+ String OOB_ENABLED = "oob_enabled";
+ String OOB_WHITELIST = "oob_whitelist";
+ }
+
+ /**
+ * Namespace for activity manager related features. These features will be applied
+ * immediately upon change.
+ *
+ * @hide
+ */
+ @SystemApi
+ public interface ActivityManager {
+ String NAMESPACE = "activity_manager";
+
+ /**
+ * App compaction flags. See {@link com.android.server.am.AppCompactor}.
+ */
+ String KEY_USE_COMPACTION = "use_compaction";
+ String KEY_COMPACT_ACTION_1 = "compact_action_1";
+ String KEY_COMPACT_ACTION_2 = "compact_action_2";
+ String KEY_COMPACT_THROTTLE_1 = "compact_throttle_1";
+ String KEY_COMPACT_THROTTLE_2 = "compact_throttle_2";
+ String KEY_COMPACT_THROTTLE_3 = "compact_throttle_3";
+ String KEY_COMPACT_THROTTLE_4 = "compact_throttle_4";
+
+ /**
+ * Maximum number of cached processes. See
+ * {@link com.android.server.am.ActivityManagerConstants}.
+ */
+ String KEY_MAX_CACHED_PROCESSES = "max_cached_processes";
+ }
+
private static final Object sLock = new Object();
@GuardedBy("sLock")
private static Map<OnPropertyChangedListener, Pair<String, Executor>> sListeners =
@@ -136,9 +196,8 @@ public final class DeviceConfig {
* Look up the value of a property for a particular namespace.
*
* @param namespace The namespace containing the property to look up.
- * @param name The name of the property to look up.
+ * @param name The name of the property to look up.
* @return the corresponding value, or null if not present.
- *
* @hide
*/
@SystemApi
@@ -160,14 +219,13 @@ public final class DeviceConfig {
* All properties stored for a particular scope can be reverted to their default values
* by passing the namespace to {@link #resetToDefaults(int, String)}.
*
- * @param namespace The namespace containing the property to create or update.
- * @param name The name of the property to create or update.
- * @param value The value to store for the property.
+ * @param namespace The namespace containing the property to create or update.
+ * @param name The name of the property to create or update.
+ * @param value The value to store for the property.
* @param makeDefault Whether to make the new value the default one.
* @return True if the value was set, false if the storage implementation throws errors.
- * @see #resetToDefaults(int, String).
- *
* @hide
+ * @see #resetToDefaults(int, String).
*/
@SystemApi
@RequiresPermission(WRITE_DEVICE_CONFIG)
@@ -186,9 +244,8 @@ public final class DeviceConfig {
*
* @param resetMode The reset mode to use.
* @param namespace Optionally, the specific namespace which resets will be limited to.
- * @see #setProperty(String, String, String, boolean)
- *
* @hide
+ * @see #setProperty(String, String, String, boolean)
*/
@SystemApi
@RequiresPermission(WRITE_DEVICE_CONFIG)
@@ -205,12 +262,11 @@ public final class DeviceConfig {
* will replace the old namespace and executor. Remove the listener entirely by calling
* {@link #removeOnPropertyChangedListener(OnPropertyChangedListener)}.
*
- * @param namespace The namespace containing properties to monitor.
- * @param executor The executor which will be used to run callbacks.
+ * @param namespace The namespace containing properties to monitor.
+ * @param executor The executor which will be used to run callbacks.
* @param onPropertyChangedListener The listener to add.
- * @see #removeOnPropertyChangedListener(OnPropertyChangedListener)
- *
* @hide
+ * @see #removeOnPropertyChangedListener(OnPropertyChangedListener)
*/
@SystemApi
@RequiresPermission(READ_DEVICE_CONFIG)
@@ -242,9 +298,8 @@ public final class DeviceConfig {
* property changes.
*
* @param onPropertyChangedListener The listener to remove.
- * @see #addOnPropertyChangedListener(String, Executor, OnPropertyChangedListener)
- *
* @hide
+ * @see #addOnPropertyChangedListener(String, Executor, OnPropertyChangedListener)
*/
@SystemApi
public static void removeOnPropertyChangedListener(
@@ -345,8 +400,8 @@ public final class DeviceConfig {
* Called when a property has changed.
*
* @param namespace The namespace containing the property which has changed.
- * @param name The name of the property which has changed.
- * @param value The new value of the property which has changed.
+ * @param name The name of the property which has changed.
+ * @param value The new value of the property which has changed.
*/
void onPropertyChanged(String namespace, String name, String value);
}
diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java
index 487198ba4d45..f5c442f194ba 100644
--- a/core/java/android/provider/MediaStore.java
+++ b/core/java/android/provider/MediaStore.java
@@ -89,9 +89,19 @@ public final class MediaStore {
/** A content:// style uri to the authority for the media provider */
public static final Uri AUTHORITY_URI = Uri.parse("content://" + AUTHORITY);
- /** {@hide} */
+ /**
+ * Volume name used for content on "internal" storage of device. This
+ * volume contains media distributed with the device, such as built-in
+ * ringtones and wallpapers.
+ */
public static final String VOLUME_INTERNAL = "internal";
- /** {@hide} */
+
+ /**
+ * Volume name used for content on "external" storage of device. This only
+ * includes media on the primary shared storage device; the contents of any
+ * secondary storage devices can be obtained using
+ * {@link #getAllVolumeNames(Context)}.
+ */
public static final String VOLUME_EXTERNAL = "external";
/**
@@ -1566,7 +1576,13 @@ public final class MediaStore {
/**
* This class provides utility methods to obtain thumbnails for various
* {@link Images} items.
+ *
+ * @deprecated Callers should migrate to using
+ * {@link ContentResolver#loadThumbnail}, since it offers
+ * richer control over requested thumbnail sizes and
+ * cancellation behavior.
*/
+ @Deprecated
public static class Thumbnails implements BaseColumns {
public static final Cursor query(ContentResolver cr, Uri uri, String[] projection) {
return cr.query(uri, projection, null, null, DEFAULT_SORT_ORDER);
@@ -2743,7 +2759,13 @@ public final class MediaStore {
/**
* This class provides utility methods to obtain thumbnails for various
* {@link Video} items.
+ *
+ * @deprecated Callers should migrate to using
+ * {@link ContentResolver#loadThumbnail}, since it offers
+ * richer control over requested thumbnail sizes and
+ * cancellation behavior.
*/
+ @Deprecated
public static class Thumbnails implements BaseColumns {
/**
* Cancel any outstanding {@link #getThumbnail} requests, causing
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index fa3f54ae0c3f..0961bc37afca 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -5800,6 +5800,16 @@ public final class Settings {
public static final String ALWAYS_ON_VPN_LOCKDOWN = "always_on_vpn_lockdown";
/**
+ * Comma separated list of packages that are allowed to access the network when VPN is in
+ * lockdown mode but not running.
+ * @see #ALWAYS_ON_VPN_LOCKDOWN
+ *
+ * @hide
+ */
+ public static final String ALWAYS_ON_VPN_LOCKDOWN_WHITELIST =
+ "always_on_vpn_lockdown_whitelist";
+
+ /**
* Whether applications can be installed for this user via the system's
* {@link Intent#ACTION_INSTALL_PACKAGE} mechanism.
*
@@ -7802,6 +7812,9 @@ public final class Settings {
* or an activity that handles ACTION_ASSIST, or empty which means using the default
* handling.
*
+ * <p>This should be set indirectly by setting the {@link
+ * android.app.role.RoleManager#ROLE_ASSISTANT assistant role}.
+ *
* @hide
*/
@UnsupportedAppUsage
@@ -8236,6 +8249,16 @@ public final class Settings {
private static final Validator NOTIFICATION_BADGING_VALIDATOR = BOOLEAN_VALIDATOR;
/**
+ * Whether notifications are dismissed by a right-to-left swipe (instead of a left-to-right
+ * swipe).
+ *
+ * @hide
+ */
+ public static final String NOTIFICATION_DISMISS_RTL = "notification_dismiss_rtl";
+
+ private static final Validator NOTIFICATION_DISMISS_RTL_VALIDATOR = BOOLEAN_VALIDATOR;
+
+ /**
* Comma separated list of QS tiles that have been auto-added already.
* @hide
*/
@@ -8421,6 +8444,27 @@ public final class Settings {
public static final String LOCATION_ACCESS_CHECK_DELAY_MILLIS =
"location_access_check_delay_millis";
+
+ /**
+ * Comma separated list of enabled overlay packages for all android.theme.customization.*
+ * categories. If there is no corresponding package included for a category, then all
+ * overlay packages in that category must be disabled.
+ * @hide
+ */
+ @SystemApi
+ public static final String THEME_CUSTOMIZATION_OVERLAY_PACKAGES =
+ "theme_customization_overlay_packages";
+
+ private static final Validator THEME_CUSTOMIZATION_OVERLAY_PACKAGES_VALIDATOR =
+ new SettingsValidators.PackageNameListValidator(",");
+
+ /**
+ * Controls whether aware is enabled.
+ * @hide
+ */
+ public static final String AWARE_ENABLED = "aware_enabled";
+
+ private static final Validator AWARE_ENABLED_VALIDATOR = BOOLEAN_VALIDATOR;
/**
* This are the settings to be backed up.
*
@@ -8516,6 +8560,7 @@ public final class Settings {
ASSIST_GESTURE_WAKE_ENABLED,
VR_DISPLAY_MODE,
NOTIFICATION_BADGING,
+ NOTIFICATION_DISMISS_RTL,
QS_AUTO_ADDED_TILES,
SCREENSAVER_ENABLED,
SCREENSAVER_COMPONENTS,
@@ -8544,6 +8589,8 @@ public final class Settings {
LOCK_SCREEN_WHEN_TRUST_LOST,
SKIP_GESTURE,
SILENCE_GESTURE,
+ THEME_CUSTOMIZATION_OVERLAY_PACKAGES,
+ AWARE_ENABLED,
};
/**
@@ -8676,6 +8723,7 @@ public final class Settings {
VALIDATORS.put(ASSIST_GESTURE_WAKE_ENABLED, ASSIST_GESTURE_WAKE_ENABLED_VALIDATOR);
VALIDATORS.put(VR_DISPLAY_MODE, VR_DISPLAY_MODE_VALIDATOR);
VALIDATORS.put(NOTIFICATION_BADGING, NOTIFICATION_BADGING_VALIDATOR);
+ VALIDATORS.put(NOTIFICATION_DISMISS_RTL, NOTIFICATION_DISMISS_RTL_VALIDATOR);
VALIDATORS.put(QS_AUTO_ADDED_TILES, QS_AUTO_ADDED_TILES_VALIDATOR);
VALIDATORS.put(SCREENSAVER_ENABLED, SCREENSAVER_ENABLED_VALIDATOR);
VALIDATORS.put(SCREENSAVER_COMPONENTS, SCREENSAVER_COMPONENTS_VALIDATOR);
@@ -8714,6 +8762,9 @@ public final class Settings {
VALIDATORS.put(LOCK_SCREEN_WHEN_TRUST_LOST, LOCK_SCREEN_WHEN_TRUST_LOST_VALIDATOR);
VALIDATORS.put(SKIP_GESTURE, SKIP_GESTURE_VALIDATOR);
VALIDATORS.put(SILENCE_GESTURE, SILENCE_GESTURE_VALIDATOR);
+ VALIDATORS.put(THEME_CUSTOMIZATION_OVERLAY_PACKAGES,
+ THEME_CUSTOMIZATION_OVERLAY_PACKAGES_VALIDATOR);
+ VALIDATORS.put(AWARE_ENABLED, AWARE_ENABLED_VALIDATOR);
}
/**
@@ -9452,23 +9503,6 @@ public final class Settings {
"hdmi_control_auto_device_off_enabled";
/**
- * If <b>true</b>, enables out-of-the-box execution for priv apps.
- * Default: false
- * Values: 0 = false, 1 = true
- *
- * @hide
- */
- public static final String PRIV_APP_OOB_ENABLED = "priv_app_oob_enabled";
-
- /**
- * Comma separated list of privileged package names, which will be running out-of-box APK.
- * Default: "ALL"
- *
- * @hide
- */
- public static final String PRIV_APP_OOB_LIST = "priv_app_oob_list";
-
- /**
* The interval in milliseconds at which location requests will be throttled when they are
* coming from the background.
*
@@ -9493,6 +9527,14 @@ public final class Settings {
"location_background_throttle_package_whitelist";
/**
+ * Packages that are whitelisted for ignoring location settings (may retrieve location even
+ * when user location settings are off), for emergency purposes.
+ * @hide
+ */
+ public static final String LOCATION_IGNORE_SETTINGS_PACKAGE_WHITELIST =
+ "location_ignore_settings_package_whitelist";
+
+ /**
* Whether to disable location status callbacks in preparation for deprecation.
* @hide
*/
@@ -12121,6 +12163,13 @@ public final class Settings {
"angle_gl_driver_selection_values";
/**
+ * List of package names that should check ANGLE rules
+ * @hide
+ */
+ public static final String GLOBAL_SETTINGS_ANGLE_WHITELIST =
+ "angle_whitelist";
+
+ /**
* Game Update Package global preference for all Apps.
* 0 = Default
* 1 = All Apps use Game Update Package
@@ -12150,6 +12199,14 @@ public final class Settings {
public static final String GUP_BLACKLIST = "gup_blacklist";
/**
+ * Apps on the whitelist that are allowed to use Game Driver.
+ * The string is a list of application package names, seperated by comma.
+ * i.e. <apk1>,<apk2>,...,<apkN>
+ * @hide
+ */
+ public static final String GAME_DRIVER_WHITELIST = "game_driver_whitelist";
+
+ /**
* Ordered GPU debug layer list for Vulkan
* i.e. <layer1>:<layer2>:...:<layerN>
* @hide
@@ -12194,6 +12251,31 @@ public final class Settings {
public static final String LOW_POWER_MODE_STICKY = "low_power_sticky";
/**
+ * When a device is unplugged from a changer (or is rebooted), do not re-activate battery
+ * saver even if {@link #LOW_POWER_MODE_STICKY} is 1, if the battery level is equal to or
+ * above this threshold.
+ *
+ * @hide
+ */
+ public static final String LOW_POWER_MODE_STICKY_AUTO_DISABLE_LEVEL =
+ "low_power_sticky_auto_disable_level";
+
+ private static final Validator LOW_POWER_MODE_STICKY_AUTO_DISABLE_LEVEL_VALIDATOR =
+ new SettingsValidators.InclusiveIntegerRangeValidator(0, 100);
+
+ /**
+ * Whether sticky battery saver should be deactivated once the battery level has reached the
+ * threshold specified by {@link #LOW_POWER_MODE_STICKY_AUTO_DISABLE_LEVEL}.
+ *
+ * @hide
+ */
+ public static final String LOW_POWER_MODE_STICKY_AUTO_DISABLE_ENABLED =
+ "low_power_sticky_auto_disable_enabled";
+
+ private static final Validator LOW_POWER_MODE_STICKY_AUTO_DISABLE_ENABLED_VALIDATOR =
+ new SettingsValidators.DiscreteValueValidator(new String[] {"0", "1"});
+
+ /**
* Battery level [1-100] at which low power mode automatically turns on.
* Pre-Q If 0, it will not automatically turn on. Q and newer it will only automatically
* turn on if the {@link #AUTOMATIC_POWER_SAVER_MODE} setting is also set to
@@ -12205,7 +12287,6 @@ public final class Settings {
*/
public static final String LOW_POWER_MODE_TRIGGER_LEVEL = "low_power_trigger_level";
-
private static final Validator LOW_POWER_MODE_TRIGGER_LEVEL_VALIDATOR =
new SettingsValidators.InclusiveIntegerRangeValidator(0, 100);
@@ -12959,48 +13040,37 @@ public final class Settings {
"sms_access_restriction_enabled";
/**
- * If set to 1, an app must have the READ_PRIVILEGED_PHONE_STATE permission (or be a device
- * / profile owner with the READ_PHONE_STATE permission) to access device identifiers.
- *
- * STOPSHIP: Remove this once we ship with the new device identifier check enabled.
- *
- * @hide
- */
- public static final String PRIVILEGED_DEVICE_IDENTIFIER_CHECK_ENABLED =
- "privileged_device_identifier_check_enabled";
-
- /**
- * If set to 1, an app that is targeting Q and does not meet the new requirements to access
- * device identifiers will receive a SecurityException.
+ * If set to 1, the device identifier check will be relaxed to the previous READ_PHONE_STATE
+ * permission check for 3P apps.
*
* STOPSHIP: Remove this once we ship with the new device identifier check enabled.
*
* @hide
*/
- public static final String PRIVILEGED_DEVICE_IDENTIFIER_TARGET_Q_BEHAVIOR_ENABLED =
- "privileged_device_identifier_target_q_behavior_enabled";
+ public static final String PRIVILEGED_DEVICE_IDENTIFIER_3P_CHECK_RELAXED =
+ "privileged_device_identifier_3p_check_relaxed";
/**
* If set to 1, the device identifier check will be relaxed to the previous READ_PHONE_STATE
- * permission check for 3P apps.
+ * permission check for preloaded non-privileged apps.
*
* STOPSHIP: Remove this once we ship with the new device identifier check enabled.
*
* @hide
*/
- public static final String PRIVILEGED_DEVICE_IDENTIFIER_3P_CHECK_RELAXED =
- "privileged_device_identifier_3p_check_relaxed";
+ public static final String PRIVILEGED_DEVICE_IDENTIFIER_NON_PRIV_CHECK_RELAXED =
+ "privileged_device_identifier_non_priv_check_relaxed";
/**
* If set to 1, the device identifier check will be relaxed to the previous READ_PHONE_STATE
- * permission check for preloaded non-privileged apps.
+ * permission check for preloaded privileged apps.
*
* STOPSHIP: Remove this once we ship with the new device identifier check enabled.
*
* @hide
*/
- public static final String PRIVILEGED_DEVICE_IDENTIFIER_NON_PRIV_CHECK_RELAXED =
- "privileged_device_identifier_non_priv_check_relaxed";
+ public static final String PRIVILEGED_DEVICE_IDENTIFIER_PRIV_CHECK_RELAXED =
+ "privileged_device_identifier_priv_check_relaxed";
/**
* If set to 1, SettingsProvider's restoreAnyVersion="true" attribute will be ignored
@@ -13196,6 +13266,8 @@ public final class Settings {
ENCODED_SURROUND_OUTPUT,
ENCODED_SURROUND_OUTPUT_ENABLED_FORMATS,
LOW_POWER_MODE_TRIGGER_LEVEL,
+ LOW_POWER_MODE_STICKY_AUTO_DISABLE_ENABLED,
+ LOW_POWER_MODE_STICKY_AUTO_DISABLE_LEVEL,
BLUETOOTH_ON,
PRIVATE_DNS_MODE,
PRIVATE_DNS_SPECIFIER,
@@ -13234,6 +13306,10 @@ public final class Settings {
VALIDATORS.put(ENCODED_SURROUND_OUTPUT, ENCODED_SURROUND_OUTPUT_VALIDATOR);
VALIDATORS.put(ENCODED_SURROUND_OUTPUT_ENABLED_FORMATS,
ENCODED_SURROUND_OUTPUT_ENABLED_FORMATS_VALIDATOR);
+ VALIDATORS.put(LOW_POWER_MODE_STICKY_AUTO_DISABLE_LEVEL,
+ LOW_POWER_MODE_STICKY_AUTO_DISABLE_LEVEL_VALIDATOR);
+ VALIDATORS.put(LOW_POWER_MODE_STICKY_AUTO_DISABLE_ENABLED,
+ LOW_POWER_MODE_STICKY_AUTO_DISABLE_ENABLED_VALIDATOR);
VALIDATORS.put(LOW_POWER_MODE_TRIGGER_LEVEL, LOW_POWER_MODE_TRIGGER_LEVEL_VALIDATOR);
VALIDATORS.put(LOW_POWER_MODE_TRIGGER_LEVEL_MAX,
LOW_POWER_MODE_TRIGGER_LEVEL_VALIDATOR);
@@ -13748,6 +13824,19 @@ public final class Settings {
"user_preferred_sub2","user_preferred_sub3"};
/**
+ * Which subscription is enabled for a physical slot.
+ * @hide
+ */
+ public static final String ENABLED_SUBSCRIPTION_FOR_SLOT = "enabled_subscription_for_slot";
+
+ /**
+ * Whether corresponding logical modem is enabled for a physical slot.
+ * The value 1 - enable, 0 - disable
+ * @hide
+ */
+ public static final String MODEM_STACK_ENABLED_FOR_SLOT = "modem_stack_enabled_for_slot";
+
+ /**
* Whether to enable new contacts aggregator or not.
* The value 1 - enable, 0 - disable
* @hide
diff --git a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
index aaba85bd36a7..8e0f522b5665 100644
--- a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
+++ b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
@@ -67,7 +67,7 @@ public abstract class AugmentedAutofillService extends Service {
private static final String TAG = AugmentedAutofillService.class.getSimpleName();
- // TODO(b/111330312): STOPSHIP use dynamic value, or change to false
+ // TODO(b/123100811): STOPSHIP use dynamic value, or change to false
static final boolean DEBUG = true;
static final boolean VERBOSE = false;
@@ -127,8 +127,6 @@ public abstract class AugmentedAutofillService extends Service {
return false;
}
- // TODO(b/111330312): add methods to disable autofill per app / activity?
-
/**
* Asks the service to handle an "augmented" autofill request.
*
@@ -175,12 +173,11 @@ public abstract class AugmentedAutofillService extends Service {
focusedValue, requestTime, callback);
mAutofillProxies.put(sessionId, proxy);
} else {
- // TODO(b/111330312): figure out if it's ok to reuse the proxy; add logging
- // TODO(b/111330312): also make sure to cover scenario on CTS test
+ // TODO(b/123099468): figure out if it's ok to reuse the proxy; add logging
if (DEBUG) Log.d(TAG, "Reusing proxy for session " + sessionId);
proxy.update(focusedId, focusedValue);
}
- // TODO(b/111330312): set cancellation signal
+ // TODO(b/123101711): set cancellation signal
final CancellationSignal cancellationSignal = null;
onFillRequest(new FillRequest(proxy), cancellationSignal, new FillController(proxy),
new FillCallback(proxy));
@@ -193,7 +190,7 @@ public abstract class AugmentedAutofillService extends Service {
final int sessionId = mAutofillProxies.keyAt(i);
final AutofillProxy proxy = mAutofillProxies.valueAt(i);
if (proxy == null) {
- // TODO(b/111330312): this might be fine, in which case we should logv it
+ // TODO(b/123100811): this might be fine, in which case we should logv it
Log.w(TAG, "No proxy for session " + sessionId);
return;
}
@@ -303,7 +300,7 @@ public abstract class AugmentedAutofillService extends Service {
this.mFocusedId = focusedId;
this.mFocusedValue = focusedValue;
this.mRequestTime = requestTime;
- // TODO(b/111330312): linkToDeath
+ // TODO(b/123099468): linkToDeath
}
@NonNull
@@ -366,7 +363,7 @@ public abstract class AugmentedAutofillService extends Service {
private void update(@NonNull AutofillId focusedId, @NonNull AutofillValue focusedValue) {
synchronized (mLock) {
- // TODO(b/111330312): should we close the popupwindow if the focused id changed?
+ // TODO(b/123099468): should we close the popupwindow if the focused id changed?
mFocusedId = focusedId;
mFocusedValue = focusedValue;
}
@@ -425,7 +422,7 @@ public abstract class AugmentedAutofillService extends Service {
default:
Slog.w(TAG, "invalid event reported: " + event);
}
- // TODO(b/111330312): log metrics as well
+ // TODO(b/122858578): log metrics as well
}
public void dump(@NonNull String prefix, @NonNull PrintWriter pw) {
diff --git a/core/java/android/service/autofill/augmented/FillCallback.java b/core/java/android/service/autofill/augmented/FillCallback.java
index bfb4aadaeed9..f2a7a35b2825 100644
--- a/core/java/android/service/autofill/augmented/FillCallback.java
+++ b/core/java/android/service/autofill/augmented/FillCallback.java
@@ -59,7 +59,8 @@ public final class FillCallback {
if (fillWindow != null) {
fillWindow.show();
}
- // TODO(b/111330312): properly implement on server-side by updating the Session state
- // accordingly (and adding CTS tests)
+ // TODO(b/123099468): must notify the server so it can update the session state to avoid
+ // showing conflicting UIs (for example, if a new request is made to the main autofill
+ // service and it now wants to show something).
}
}
diff --git a/core/java/android/service/autofill/augmented/FillRequest.java b/core/java/android/service/autofill/augmented/FillRequest.java
index dad506763641..af9905f480df 100644
--- a/core/java/android/service/autofill/augmented/FillRequest.java
+++ b/core/java/android/service/autofill/augmented/FillRequest.java
@@ -29,7 +29,7 @@ import android.view.autofill.AutofillValue;
* @hide
*/
@SystemApi
-// TODO(b/111330312): pass a requestId and/or sessionId
+// TODO(b/123100811): pass a requestId and/or sessionId?
@TestApi
// TODO(b/122654591): @TestApi is needed because CtsAutoFillServiceTestCases hosts the service
// in the same package as the test, and that module is compiled with SDK=test_current
diff --git a/core/java/android/service/autofill/augmented/FillResponse.java b/core/java/android/service/autofill/augmented/FillResponse.java
index 5285132b31a5..f1e904a7d8bc 100644
--- a/core/java/android/service/autofill/augmented/FillResponse.java
+++ b/core/java/android/service/autofill/augmented/FillResponse.java
@@ -19,8 +19,6 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.annotation.TestApi;
-import android.os.Parcel;
-import android.os.Parcelable;
import android.view.autofill.AutofillId;
import java.util.List;
@@ -34,7 +32,7 @@ import java.util.List;
@TestApi
//TODO(b/122654591): @TestApi is needed because CtsAutoFillServiceTestCases hosts the service
//in the same package as the test, and that module is compiled with SDK=test_current
-public final class FillResponse implements Parcelable {
+public final class FillResponse {
private final FillWindow mFillWindow;
@@ -70,8 +68,8 @@ public final class FillResponse implements Parcelable {
* @return this builder
*/
public Builder setFillWindow(@NonNull FillWindow fillWindow) {
- // TODO(b/111330312): implement / check not null / unit test
- // TODO(b/111330312): throw exception if FillWindow not updated yet
+ // TODO(b/123100712): check not null / unit test / throw exception if FillWindow not
+ // updated yet
mFillWindow = fillWindow;
return this;
}
@@ -85,7 +83,7 @@ public final class FillResponse implements Parcelable {
* @return this builder
*/
public Builder setIgnoredIds(@NonNull List<AutofillId> ids) {
- // TODO(b/111330312): implement / check not null / unit test
+ // TODO(b/123100695): implement / check not null / unit test
return this;
}
@@ -102,37 +100,10 @@ public final class FillResponse implements Parcelable {
* @return A built response.
*/
public FillResponse build() {
- // TODO(b/111330312): check conditions / add unit test
+ // TODO(b/123100712): check conditions / add unit test
return new FillResponse(this);
}
-
- // TODO(b/111330312): add methods to disable app / activity, either here or on manager
- }
-
- // TODO(b/111330312): implement to String
-
- @Override
- public int describeContents() {
- return 0;
}
- @Override
- public void writeToParcel(Parcel parcel, int flags) {
- // TODO(b/111330312): implement
- }
-
- public static final Parcelable.Creator<FillResponse> CREATOR =
- new Parcelable.Creator<FillResponse>() {
-
- @Override
- public FillResponse createFromParcel(Parcel parcel) {
- // TODO(b/111330312): implement
- return null;
- }
-
- @Override
- public FillResponse[] newArray(int size) {
- return new FillResponse[size];
- }
- };
+ // TODO(b/123100811): implement to String
}
diff --git a/core/java/android/service/autofill/augmented/FillWindow.java b/core/java/android/service/autofill/augmented/FillWindow.java
index 51b0f01af6ae..40e3a1219501 100644
--- a/core/java/android/service/autofill/augmented/FillWindow.java
+++ b/core/java/android/service/autofill/augmented/FillWindow.java
@@ -20,7 +20,6 @@ import static android.service.autofill.augmented.AugmentedAutofillService.VERBOS
import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
-import android.annotation.LongDef;
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.annotation.TestApi;
@@ -42,8 +41,6 @@ import com.android.internal.util.Preconditions;
import dalvik.system.CloseGuard;
import java.io.PrintWriter;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
/**
* Handle to a window used to display the augmented autofill UI.
@@ -71,18 +68,6 @@ import java.lang.annotation.RetentionPolicy;
public final class FillWindow implements AutoCloseable {
private static final String TAG = "FillWindow";
- /** Indicates the data being shown is a physical address */
- public static final long FLAG_METADATA_ADDRESS = 0x1;
-
- // TODO(b/111330312): add more flags
-
- /** @hide */
- @LongDef(prefix = { "FLAG" }, value = {
- FLAG_METADATA_ADDRESS,
- })
- @Retention(RetentionPolicy.SOURCE)
- @interface Flags{}
-
private final Object mLock = new Object();
private final CloseGuard mCloseGuard = CloseGuard.get();
@@ -108,29 +93,22 @@ public final class FillWindow implements AutoCloseable {
*
* @param rootView new root view
* @param area coordinates to render the view.
- * @param flags optional flags such as metadata of what will be rendered in the window. The
- * Smart Suggestion host might decide whether or not to render the UI based on them.
+ * @param flags currently not used.
*
* @return boolean whether the window was updated or not.
*
* @throws IllegalArgumentException if the area is not compatible with this window
*/
- public boolean update(@NonNull Area area, @NonNull View rootView, @Flags long flags) {
+ public boolean update(@NonNull Area area, @NonNull View rootView, long flags) {
if (DEBUG) {
Log.d(TAG, "Updating " + area + " + with " + rootView);
}
- // TODO(b/111330312): add test case for null
+ // TODO(b/123100712): add test case for null
Preconditions.checkNotNull(area);
Preconditions.checkNotNull(rootView);
- // TODO(b/111330312): must check the area is a valid object returned by
+ // TODO(b/123100712): must check the area is a valid object returned by
// SmartSuggestionParams, throw IAE if not
- // TODO(b/111330312): must some how pass metadata to the SmartSuggestiongs provider
-
-
- // TODO(b/111330312): use a SurfaceControl approach; for now, we're manually creating
- // the window underneath the existing view.
-
final PresentationParams smartSuggestion = area.proxy.getSmartSuggestionParams();
if (smartSuggestion == null) {
Log.w(TAG, "No SmartSuggestionParams");
@@ -148,12 +126,12 @@ public final class FillWindow implements AutoCloseable {
mProxy = area.proxy;
- // TODO(b/111330312): once we have the SurfaceControl approach, we should update the
+ // TODO(b/123227534): once we have the SurfaceControl approach, we should update the
// window instead of destroying. In fact, it might be better to allocate a full window
// initially, which is transparent (and let touches get through) everywhere but in the
// rect boundaries.
- // TODO(b/111330312): make sure all touch events are handled, window is always closed,
+ // TODO(b/123099468): make sure all touch events are handled, window is always closed,
// etc.
mWm = rootView.getContext().getSystemService(WindowManager.class);
@@ -181,7 +159,7 @@ public final class FillWindow implements AutoCloseable {
/** @hide */
void show() {
- // TODO(b/111330312): check if updated first / throw exception
+ // TODO(b/123100712): check if updated first / throw exception
if (DEBUG) Log.d(TAG, "show()");
synchronized (mLock) {
checkNotDestroyedLocked();
diff --git a/core/java/android/service/autofill/augmented/IFillCallback.aidl b/core/java/android/service/autofill/augmented/IFillCallback.aidl
index dac75904f4fc..2b0726649759 100644
--- a/core/java/android/service/autofill/augmented/IFillCallback.aidl
+++ b/core/java/android/service/autofill/augmented/IFillCallback.aidl
@@ -24,8 +24,7 @@ import android.os.ICancellationSignal;
* @hide
*/
interface IFillCallback {
- // TODO(b/111330312): add cancellation (after we have CTS tests, so we can test it)
+ // TODO(b/123101711): add cancellation (after we have CTS tests, so we can test it)
// void onCancellable(in ICancellationSignal cancellation);
- // TODO(b/111330312): might need to pass the response (once IME implements Smart Suggestions)
void onSuccess();
}
diff --git a/core/java/android/service/autofill/augmented/PresentationParams.java b/core/java/android/service/autofill/augmented/PresentationParams.java
index b60064e9dd69..1fb9032c9af5 100644
--- a/core/java/android/service/autofill/augmented/PresentationParams.java
+++ b/core/java/android/service/autofill/augmented/PresentationParams.java
@@ -190,7 +190,7 @@ public abstract class PresentationParams {
*/
@Nullable
public Area getSubArea(@NonNull Rect bounds) {
- // TODO(b/111330312): implement / check boundaries / throw IAE / add unit test
+ // TODO(b/123100712): implement / check boundaries / throw IAE / add unit test
return null;
}
diff --git a/core/java/android/service/contentcapture/ContentCaptureService.java b/core/java/android/service/contentcapture/ContentCaptureService.java
index 302e1a656833..020de7f24048 100644
--- a/core/java/android/service/contentcapture/ContentCaptureService.java
+++ b/core/java/android/service/contentcapture/ContentCaptureService.java
@@ -116,6 +116,13 @@ public abstract class ContentCaptureService extends Service {
mHandler.sendMessage(obtainMessage(ContentCaptureService::handleFinishSession,
ContentCaptureService.this, sessionId));
}
+
+ @Override
+ public void onUserDataRemovalRequest(UserDataRemovalRequest request) {
+ mHandler.sendMessage(
+ obtainMessage(ContentCaptureService::handleOnUserDataRemovalRequest,
+ ContentCaptureService.this, request));
+ }
};
/**
@@ -431,6 +438,10 @@ public abstract class ContentCaptureService extends Service {
onDestroyContentCaptureSession(new ContentCaptureSessionId(sessionId));
}
+ private void handleOnUserDataRemovalRequest(@NonNull UserDataRemovalRequest request) {
+ onUserDataRemovalRequest(request);
+ }
+
/**
* Checks if the given {@code uid} owns the session associated with the event.
*/
diff --git a/core/java/android/service/contentcapture/IContentCaptureService.aidl b/core/java/android/service/contentcapture/IContentCaptureService.aidl
index a8dd21392337..d92fb3bed679 100644
--- a/core/java/android/service/contentcapture/IContentCaptureService.aidl
+++ b/core/java/android/service/contentcapture/IContentCaptureService.aidl
@@ -19,6 +19,7 @@ package android.service.contentcapture;
import android.os.IBinder;
import android.service.contentcapture.SnapshotData;
import android.view.contentcapture.ContentCaptureContext;
+import android.view.contentcapture.UserDataRemovalRequest;
import com.android.internal.os.IResultReceiver;
@@ -36,4 +37,5 @@ oneway interface IContentCaptureService {
in IResultReceiver clientReceiver);
void onSessionFinished(String sessionId);
void onActivitySnapshot(String sessionId, in SnapshotData snapshotData);
+ void onUserDataRemovalRequest(in UserDataRemovalRequest request);
}
diff --git a/core/java/android/service/notification/Adjustment.java b/core/java/android/service/notification/Adjustment.java
index de532b74375f..b6788f578bd6 100644
--- a/core/java/android/service/notification/Adjustment.java
+++ b/core/java/android/service/notification/Adjustment.java
@@ -15,8 +15,6 @@
*/
package android.service.notification;
-import android.annotation.SystemApi;
-import android.annotation.TestApi;
import android.app.Notification;
import android.os.Bundle;
import android.os.Parcel;
@@ -24,10 +22,7 @@ import android.os.Parcelable;
/**
* Ranking updates from the Assistant.
- * @hide
*/
-@SystemApi
-@TestApi
public final class Adjustment implements Parcelable {
private final String mPackage;
private final String mKey;
@@ -39,6 +34,7 @@ public final class Adjustment implements Parcelable {
* Data type: ArrayList of {@code String}, where each is a representation of a
* {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI}.
* See {@link android.app.Notification.Builder#addPerson(String)}.
+ * @hide
*/
public static final String KEY_PEOPLE = "key_people";
/**
@@ -46,6 +42,7 @@ public final class Adjustment implements Parcelable {
* users. If a user chooses to snooze a notification until one of these criterion, the
* assistant will be notified via
* {@link NotificationAssistantService#onNotificationSnoozedUntilContext}.
+ * @hide
*/
public static final String KEY_SNOOZE_CRITERIA = "key_snooze_criteria";
/**
@@ -112,7 +109,7 @@ public final class Adjustment implements Parcelable {
mUser = user;
}
- protected Adjustment(Parcel in) {
+ private Adjustment(Parcel in) {
if (in.readInt() == 1) {
mPackage = in.readString();
} else {
diff --git a/core/java/android/service/notification/NotificationAssistantService.java b/core/java/android/service/notification/NotificationAssistantService.java
index ad34ab3bddb1..e93b1580bc66 100644
--- a/core/java/android/service/notification/NotificationAssistantService.java
+++ b/core/java/android/service/notification/NotificationAssistantService.java
@@ -21,8 +21,6 @@ import static java.lang.annotation.RetentionPolicy.SOURCE;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.SdkConstant;
-import android.annotation.SystemApi;
-import android.annotation.TestApi;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.admin.DevicePolicyManager;
@@ -61,11 +59,7 @@ import java.util.List;
* <p>
* All callbacks are called on the main thread.
* </p>
- *
- * @hide
*/
-@SystemApi
-@TestApi
public abstract class NotificationAssistantService extends NotificationListenerService {
private static final String TAG = "NotificationAssistants";
@@ -109,6 +103,7 @@ public abstract class NotificationAssistantService extends NotificationListenerS
*
* @param sbn the notification to snooze
* @param snoozeCriterionId the {@link SnoozeCriterion#getId()} representing a device context.
+ * @hide
*/
abstract public void onNotificationSnoozedUntilContext(StatusBarNotification sbn,
String snoozeCriterionId);
@@ -250,6 +245,7 @@ public abstract class NotificationAssistantService extends NotificationListenerS
* {@link #onNotificationPosted(StatusBarNotification, RankingMap)} callback for the
* notification.
* @param key The key of the notification to snooze
+ * @hide
*/
public final void unsnoozeNotification(String key) {
if (!isBound()) return;
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index 0e63cd37f3f2..c734b630759b 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -1617,14 +1617,16 @@ public abstract class NotificationListenerService extends Service {
}
/**
- * @hide
+ * Returns a list of smart {@link Notification.Action} that can be added by the
+ * {@link NotificationAssistantService}
*/
public List<Notification.Action> getSmartActions() {
return mSmartActions;
}
/**
- * @hide
+ * Returns a list of smart replies that can be added by the
+ * {@link NotificationAssistantService}
*/
public List<CharSequence> getSmartReplies() {
return mSmartReplies;
diff --git a/core/java/android/service/notification/NotificationStats.java b/core/java/android/service/notification/NotificationStats.java
index e5f3dfbe2a1c..814b4772a395 100644
--- a/core/java/android/service/notification/NotificationStats.java
+++ b/core/java/android/service/notification/NotificationStats.java
@@ -16,8 +16,6 @@
package android.service.notification;
import android.annotation.IntDef;
-import android.annotation.SystemApi;
-import android.annotation.TestApi;
import android.app.RemoteInput;
import android.os.Parcel;
import android.os.Parcelable;
@@ -25,11 +23,6 @@ import android.os.Parcelable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-/**
- * @hide
- */
-@TestApi
-@SystemApi
public final class NotificationStats implements Parcelable {
private boolean mSeen;
@@ -105,7 +98,7 @@ public final class NotificationStats implements Parcelable {
public NotificationStats() {
}
- protected NotificationStats(Parcel in) {
+ private NotificationStats(Parcel in) {
mSeen = in.readByte() != 0;
mExpanded = in.readByte() != 0;
mDirectReplied = in.readByte() != 0;
diff --git a/core/java/android/service/voice/VoiceInteractionService.java b/core/java/android/service/voice/VoiceInteractionService.java
index e105fdf6cfb8..2789651c4eaf 100644
--- a/core/java/android/service/voice/VoiceInteractionService.java
+++ b/core/java/android/service/voice/VoiceInteractionService.java
@@ -16,6 +16,7 @@
package android.service.voice;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SdkConstant;
@@ -40,6 +41,8 @@ import com.android.internal.util.function.pooled.PooledLambda;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
@@ -77,6 +80,33 @@ public class VoiceInteractionService extends Service {
*/
public static final String SERVICE_META_DATA = "android.voice_interaction";
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"VOICE_STATE_"}, value = {
+ VOICE_STATE_NONE,
+ VOICE_STATE_CONDITIONAL_LISTENING,
+ VOICE_STATE_LISTENING,
+ VOICE_STATE_FULFILLING})
+ public @interface VoiceState {
+ }
+
+ /**
+ * Voice assistant inactive.
+ */
+ public static final int VOICE_STATE_NONE = 0;
+ /**
+ * Voice assistant listening, but will only trigger if it hears a request it can fulfill.
+ */
+ public static final int VOICE_STATE_CONDITIONAL_LISTENING = 1;
+ /**
+ * Voice assistant is listening to user speech.
+ */
+ public static final int VOICE_STATE_LISTENING = 2;
+ /**
+ * Voice assistant is fulfilling an action requested by the user.
+ */
+ public static final int VOICE_STATE_FULFILLING = 3;
+
IVoiceInteractionService mInterface = new IVoiceInteractionService.Stub() {
@Override
public void ready() {
@@ -341,6 +371,43 @@ public class VoiceInteractionService extends Service {
}
}
+ /**
+ * Requests that the voice state UI indicate the given state.
+ *
+ * @param state value indicating whether the assistant is listening, fulfilling, etc.
+ */
+ public final void setVoiceState(@VoiceState int state) {
+ try {
+ mSystemService.setVoiceState(state);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Displays the given voice transcription contents.
+ */
+ public final void setTranscription(@NonNull String transcription) {
+ try {
+ mSystemService.setTranscription(transcription);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Hides transcription.
+ *
+ * @param immediate if {@code true}, remove before transcription animation completes.
+ */
+ public final void clearTranscription(boolean immediate) {
+ try {
+ mSystemService.clearTranscription(immediate);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println("VOICE INTERACTION");
diff --git a/core/java/android/util/LongArrayQueue.java b/core/java/android/util/LongArrayQueue.java
new file mode 100644
index 000000000000..d5f048434b32
--- /dev/null
+++ b/core/java/android/util/LongArrayQueue.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.util;
+
+import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.GrowingArrayUtils;
+
+import libcore.util.EmptyArray;
+
+import java.util.NoSuchElementException;
+
+/**
+ * A lightweight implementation for a queue with long values.
+ * Additionally supports getting an element with a specified position from the head of the queue.
+ * The queue grows in size if needed to accommodate new elements.
+ *
+ * @hide
+ */
+public class LongArrayQueue {
+
+ private long[] mValues;
+ private int mSize;
+ private int mHead;
+ private int mTail;
+
+ /**
+ * Initializes a queue with the given starting capacity.
+ *
+ * @param initialCapacity the capacity.
+ */
+ public LongArrayQueue(int initialCapacity) {
+ if (initialCapacity == 0) {
+ mValues = EmptyArray.LONG;
+ } else {
+ mValues = ArrayUtils.newUnpaddedLongArray(initialCapacity);
+ }
+ mSize = 0;
+ mHead = mTail = 0;
+ }
+
+ /**
+ * Initializes a queue with default starting capacity.
+ */
+ public LongArrayQueue() {
+ this(16);
+ }
+
+ private void grow() {
+ if (mSize < mValues.length) {
+ throw new IllegalStateException("Queue not full yet!");
+ }
+ final int newSize = GrowingArrayUtils.growSize(mSize);
+ final long[] newArray = ArrayUtils.newUnpaddedLongArray(newSize);
+ final int r = mValues.length - mHead; // Number of elements on and to the right of head.
+ System.arraycopy(mValues, mHead, newArray, 0, r);
+ System.arraycopy(mValues, 0, newArray, r, mHead);
+ mValues = newArray;
+ mHead = 0;
+ mTail = mSize;
+ }
+
+ /**
+ * Returns the number of elements in the queue.
+ */
+ public int size() {
+ return mSize;
+ }
+
+ /**
+ * Removes all elements from this queue.
+ */
+ public void clear() {
+ mSize = 0;
+ mHead = mTail = 0;
+ }
+
+ /**
+ * Adds a value to the tail of the queue.
+ *
+ * @param value the value to be added.
+ */
+ public void addLast(long value) {
+ if (mSize == mValues.length) {
+ grow();
+ }
+ mValues[mTail] = value;
+ mTail = (mTail + 1) % mValues.length;
+ mSize++;
+ }
+
+ /**
+ * Removes an element from the head of the queue.
+ *
+ * @return the element at the head of the queue.
+ * @throws NoSuchElementException if the queue is empty.
+ */
+ public long removeFirst() {
+ if (mSize == 0) {
+ throw new NoSuchElementException("Queue is empty!");
+ }
+ final long ret = mValues[mHead];
+ mHead = (mHead + 1) % mValues.length;
+ mSize--;
+ return ret;
+ }
+
+ /**
+ * Returns the element at the given position from the head of the queue, where 0 represents the
+ * head of the queue.
+ *
+ * @param position the position from the head of the queue.
+ * @return the element found at the given position.
+ * @throws IndexOutOfBoundsException if {@code position} < {@code 0} or
+ * {@code position} >= {@link #size()}
+ */
+ public long get(int position) {
+ if (position < 0 || position >= mSize) {
+ throw new IndexOutOfBoundsException("Index " + position
+ + " not valid for a queue of size " + mSize);
+ }
+ final int index = (mHead + position) % mValues.length;
+ return mValues[index];
+ }
+
+ /**
+ * Returns the element at the head of the queue, without removing it.
+ *
+ * @return the element at the head of the queue.
+ * @throws NoSuchElementException if the queue is empty
+ */
+ public long peekFirst() {
+ if (mSize == 0) {
+ throw new NoSuchElementException("Queue is empty!");
+ }
+ return mValues[mHead];
+ }
+
+ /**
+ * Returns the element at the tail of the queue.
+ *
+ * @return the element at the tail of the queue.
+ * @throws NoSuchElementException if the queue is empty.
+ */
+ public long peekLast() {
+ if (mSize == 0) {
+ throw new NoSuchElementException("Queue is empty!");
+ }
+ final int index = (mTail == 0) ? mValues.length - 1 : mTail - 1;
+ return mValues[index];
+ }
+}
diff --git a/core/java/android/view/RemoteAnimationTarget.java b/core/java/android/view/RemoteAnimationTarget.java
index 567b279ead11..1d2cf4b78756 100644
--- a/core/java/android/view/RemoteAnimationTarget.java
+++ b/core/java/android/view/RemoteAnimationTarget.java
@@ -24,6 +24,8 @@ import static android.view.RemoteAnimationTargetProto.MODE;
import static android.view.RemoteAnimationTargetProto.POSITION;
import static android.view.RemoteAnimationTargetProto.PREFIX_ORDER_INDEX;
import static android.view.RemoteAnimationTargetProto.SOURCE_CONTAINER_BOUNDS;
+import static android.view.RemoteAnimationTargetProto.START_BOUNDS;
+import static android.view.RemoteAnimationTargetProto.START_LEASH;
import static android.view.RemoteAnimationTargetProto.TASK_ID;
import static android.view.RemoteAnimationTargetProto.WINDOW_CONFIGURATION;
@@ -57,9 +59,15 @@ public class RemoteAnimationTarget implements Parcelable {
*/
public static final int MODE_CLOSING = 1;
+ /**
+ * The app is in the set of resizing apps (eg. mode change) of this transition.
+ */
+ public static final int MODE_CHANGING = 2;
+
@IntDef(prefix = { "MODE_" }, value = {
MODE_OPENING,
- MODE_CLOSING
+ MODE_CLOSING,
+ MODE_CHANGING
})
@Retention(RetentionPolicy.SOURCE)
public @interface Mode {}
@@ -83,6 +91,13 @@ public class RemoteAnimationTarget implements Parcelable {
public final SurfaceControl leash;
/**
+ * The {@link SurfaceControl} for the starting state of a target if this transition is
+ * MODE_CHANGING, {@code null)} otherwise. This is relative to the app window.
+ */
+ @UnsupportedAppUsage
+ public final SurfaceControl startLeash;
+
+ /**
* Whether the app is translucent and may reveal apps behind.
*/
@UnsupportedAppUsage
@@ -128,6 +143,15 @@ public class RemoteAnimationTarget implements Parcelable {
public final Rect sourceContainerBounds;
/**
+ * The starting bounds of the source container in screen space coordinates. This is {@code null}
+ * if the animation target isn't MODE_CHANGING. Since this is the starting bounds, it's size
+ * should be equivalent to the size of the starting thumbnail. Note that sourceContainerBounds
+ * is the end bounds of a change transition.
+ */
+ @UnsupportedAppUsage
+ public final Rect startBounds;
+
+ /**
* The window configuration for the target.
*/
@UnsupportedAppUsage
@@ -141,7 +165,8 @@ public class RemoteAnimationTarget implements Parcelable {
public RemoteAnimationTarget(int taskId, int mode, SurfaceControl leash, boolean isTranslucent,
Rect clipRect, Rect contentInsets, int prefixOrderIndex, Point position,
- Rect sourceContainerBounds, WindowConfiguration windowConfig, boolean isNotInRecents) {
+ Rect sourceContainerBounds, WindowConfiguration windowConfig, boolean isNotInRecents,
+ SurfaceControl startLeash, Rect startBounds) {
this.mode = mode;
this.taskId = taskId;
this.leash = leash;
@@ -153,6 +178,8 @@ public class RemoteAnimationTarget implements Parcelable {
this.sourceContainerBounds = new Rect(sourceContainerBounds);
this.windowConfiguration = windowConfig;
this.isNotInRecents = isNotInRecents;
+ this.startLeash = startLeash;
+ this.startBounds = startBounds == null ? null : new Rect(startBounds);
}
public RemoteAnimationTarget(Parcel in) {
@@ -167,6 +194,8 @@ public class RemoteAnimationTarget implements Parcelable {
sourceContainerBounds = in.readParcelable(null);
windowConfiguration = in.readParcelable(null);
isNotInRecents = in.readBoolean();
+ startLeash = in.readParcelable(null);
+ startBounds = in.readParcelable(null);
}
@Override
@@ -187,6 +216,8 @@ public class RemoteAnimationTarget implements Parcelable {
dest.writeParcelable(sourceContainerBounds, 0 /* flags */);
dest.writeParcelable(windowConfiguration, 0 /* flags */);
dest.writeBoolean(isNotInRecents);
+ dest.writeParcelable(startLeash, 0 /* flags */);
+ dest.writeParcelable(startBounds, 0 /* flags */);
}
public void dump(PrintWriter pw, String prefix) {
@@ -215,6 +246,8 @@ public class RemoteAnimationTarget implements Parcelable {
position.writeToProto(proto, POSITION);
sourceContainerBounds.writeToProto(proto, SOURCE_CONTAINER_BOUNDS);
windowConfiguration.writeToProto(proto, WINDOW_CONFIGURATION);
+ startLeash.writeToProto(proto, START_LEASH);
+ startBounds.writeToProto(proto, START_BOUNDS);
proto.end(token);
}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 2014ec2417ac..991b385a6cca 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -3985,6 +3985,15 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
public static final int SCROLL_AXIS_VERTICAL = 1 << 1;
/**
+ * If a MotionEvent has CLASSIFICATION_AMBIGUOUS_GESTURE set, then certain the default
+ * long press action will be inhibited. However, to account for the possibility of incorrect
+ * classification, the default long press timeout will instead be increased for some situations
+ * by the following factor.
+ * Likewise, the touch slop for allowing long press will be increased when gesture is uncertain.
+ */
+ private static final int AMBIGUOUS_GESTURE_MULTIPLIER = 2;
+
+ /**
* Controls the over-scroll mode for this view.
* See {@link #overScrollBy(int, int, int, int, int, int, int, int, boolean)},
* {@link #OVER_SCROLL_ALWAYS}, {@link #OVER_SCROLL_IF_CONTENT_SCROLLS},
@@ -8203,10 +8212,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* {@link ContentCaptureSession#notifyViewDisappeared(AutofillId)}, and
* {@link ContentCaptureSession#notifyViewTextChanged(AutofillId, CharSequence, int)}
* respectively. The structure for the a child must be created using
- * {@link ContentCaptureSession#newVirtualViewStructure(AutofillId, int)}, and the
+ * {@link ContentCaptureSession#newVirtualViewStructure(AutofillId, long)}, and the
* {@code autofillId} for a child can be obtained either through
* {@code childStructure.getAutofillId()} or
- * {@link ContentCaptureSession#newAutofillId(AutofillId, int)}.
+ * {@link ContentCaptureSession#newAutofillId(AutofillId, long)}.
*
* <p><b>Note: </b>the following methods of the {@code structure} will be ignored:
* <ul>
@@ -8608,7 +8617,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
if (isAttachedToWindow()) {
throw new IllegalStateException("Cannot set autofill id when view is attached");
}
- if (id != null && id.isVirtual()) {
+ if (id != null && !id.isNonVirtual()) {
throw new IllegalStateException("Cannot set autofill id assigned to virtual views");
}
if (id == null && (mPrivateFlags3 & PFLAG3_AUTOFILLID_EXPLICITLY_SET) == 0) {
@@ -13994,7 +14003,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
if (clickable) {
setPressed(true, x, y);
}
- checkForLongClick(0, x, y);
+ checkForLongClick(ViewConfiguration.getLongPressTimeout(), x, y);
return true;
}
}
@@ -14735,7 +14744,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
mHasPerformedLongPress = false;
if (!clickable) {
- checkForLongClick(0, x, y);
+ checkForLongClick(ViewConfiguration.getLongPressTimeout(), x, y);
break;
}
@@ -14759,7 +14768,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
} else {
// Not inside a scrolling container, so show the feedback right away
setPressed(true, x, y);
- checkForLongClick(0, x, y);
+ checkForLongClick(ViewConfiguration.getLongPressTimeout(), x, y);
}
break;
@@ -14780,8 +14789,27 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
drawableHotspotChanged(x, y);
}
+ final int motionClassification = event.getClassification();
+ final boolean ambiguousGesture =
+ motionClassification == MotionEvent.CLASSIFICATION_AMBIGUOUS_GESTURE;
+ int touchSlop = mTouchSlop;
+ if (ambiguousGesture && hasPendingLongPressCallback()) {
+ if (!pointInView(x, y, touchSlop)) {
+ // The default action here is to cancel long press. But instead, we
+ // just extend the timeout here, in case the classification
+ // stays ambiguous.
+ removeLongPressCallback();
+ long delay = ViewConfiguration.getLongPressTimeout()
+ * AMBIGUOUS_GESTURE_MULTIPLIER;
+ // Subtract the time already spent
+ delay -= event.getEventTime() - event.getDownTime();
+ checkForLongClick(delay, x, y);
+ }
+ touchSlop *= AMBIGUOUS_GESTURE_MULTIPLIER;
+ }
+
// Be lenient about moving outside of buttons
- if (!pointInView(x, y, mTouchSlop)) {
+ if (!pointInView(x, y, touchSlop)) {
// Outside button
// Remove any future long press/tap checks
removeTapCallback();
@@ -14791,6 +14819,15 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
mPrivateFlags3 &= ~PFLAG3_FINGER_DOWN;
}
+
+ final boolean deepPress =
+ motionClassification == MotionEvent.CLASSIFICATION_DEEP_PRESS;
+ if (deepPress && hasPendingLongPressCallback()) {
+ // process the long click action immediately
+ removeLongPressCallback();
+ checkForLongClick(0 /* send immediately */, x, y);
+ }
+
break;
}
@@ -14825,6 +14862,21 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
/**
+ * Return true if the long press callback is scheduled to run sometime in the future.
+ * Return false if there is no scheduled long press callback at the moment.
+ */
+ private boolean hasPendingLongPressCallback() {
+ if (mPendingCheckForLongPress == null) {
+ return false;
+ }
+ final AttachInfo attachInfo = mAttachInfo;
+ if (attachInfo == null) {
+ return false;
+ }
+ return attachInfo.mHandler.hasCallbacks(mPendingCheckForLongPress);
+ }
+
+ /**
* Remove the pending click action
*/
@UnsupportedAppUsage
@@ -25434,7 +25486,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
}
- private void checkForLongClick(int delayOffset, float x, float y) {
+ private void checkForLongClick(long delay, float x, float y) {
if ((mViewFlags & LONG_CLICKABLE) == LONG_CLICKABLE || (mViewFlags & TOOLTIP) == TOOLTIP) {
mHasPerformedLongPress = false;
@@ -25444,8 +25496,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
mPendingCheckForLongPress.setAnchor(x, y);
mPendingCheckForLongPress.rememberWindowAttachCount();
mPendingCheckForLongPress.rememberPressedState();
- postDelayed(mPendingCheckForLongPress,
- ViewConfiguration.getLongPressTimeout() - delayOffset);
+ postDelayed(mPendingCheckForLongPress, delay);
}
}
@@ -27035,7 +27086,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
public void run() {
mPrivateFlags &= ~PFLAG_PREPRESSED;
setPressed(true, x, y);
- checkForLongClick(ViewConfiguration.getTapTimeout(), x, y);
+ final long delay =
+ ViewConfiguration.getLongPressTimeout() - ViewConfiguration.getTapTimeout();
+ checkForLongClick(delay, x, y);
}
}
diff --git a/core/java/android/view/WindowInsetsController.java b/core/java/android/view/WindowInsetsController.java
index a35be273f3bf..b70832315e2c 100644
--- a/core/java/android/view/WindowInsetsController.java
+++ b/core/java/android/view/WindowInsetsController.java
@@ -16,6 +16,8 @@
package android.view;
+import static android.view.WindowInsets.Type.ime;
+
import android.annotation.NonNull;
import android.view.WindowInsets.Type.InsetType;
@@ -32,11 +34,11 @@ public interface WindowInsetsController {
* <p>
* Note that if the window currently doesn't have control over a certain type, it will apply the
* change as soon as the window gains control. The app can listen to the event by observing
- * {@link View#onApplyWindowInsets} and checking visibility with "TODO at method" in
- * {@link WindowInsets}.
+ * {@link View#onApplyWindowInsets} and checking visibility with {@link WindowInsets#isVisible}.
*
* @param types A bitmask of {@link WindowInsets.Type.InsetType} specifying what windows the app
* would like to make appear on screen.
+ * @hide
*/
void show(@InsetType int types);
@@ -45,11 +47,11 @@ public interface WindowInsetsController {
* <p>
* Note that if the window currently doesn't have control over a certain type, it will apply the
* change as soon as the window gains control. The app can listen to the event by observing
- * {@link View#onApplyWindowInsets} and checking visibility with "TODO at method" in
- * {@link WindowInsets}.
+ * {@link View#onApplyWindowInsets} and checking visibility with {@link WindowInsets#isVisible}.
*
* @param types A bitmask of {@link WindowInsets.Type.InsetType} specifying what windows the app
* would like to make disappear.
+ * @hide
*/
void hide(@InsetType int types);
@@ -60,7 +62,50 @@ public interface WindowInsetsController {
* @param types The {@link InsetType}s the application has requested to control.
* @param listener The {@link WindowInsetsAnimationControlListener} that gets called when the
* windows are ready to be controlled, among other callbacks.
+ * @hide
*/
void controlWindowInsetsAnimation(@InsetType int types,
@NonNull WindowInsetsAnimationControlListener listener);
+
+ /**
+ * Lets the application control the animation for showing the IME in a frame-by-frame manner by
+ * modifying the position of the IME when it's causing insets.
+ *
+ * @param listener The {@link WindowInsetsAnimationControlListener} that gets called when the
+ * IME are ready to be controlled, among other callbacks.
+ */
+ default void controlInputMethodAnimation(
+ @NonNull WindowInsetsAnimationControlListener listener) {
+ controlWindowInsetsAnimation(ime(), listener);
+ }
+
+ /**
+ * Makes the IME appear on screen.
+ * <p>
+ * Note that if the window currently doesn't have control over the IME, because it doesn't have
+ * focus, it will apply the change as soon as the window gains control. The app can listen to
+ * the event by observing {@link View#onApplyWindowInsets} and checking visibility with
+ * {@link WindowInsets#isVisible}.
+ *
+ * @see #controlInputMethodAnimation(WindowInsetsAnimationControlListener)
+ * @see #hideInputMethod()
+ */
+ default void showInputMethod() {
+ show(ime());
+ }
+
+ /**
+ * Makes the IME disappear on screen.
+ * <p>
+ * Note that if the window currently doesn't have control over IME, because it doesn't have
+ * focus, it will apply the change as soon as the window gains control. The app can listen to
+ * the event by observing {@link View#onApplyWindowInsets} and checking visibility with
+ * {@link WindowInsets#isVisible}.
+ *
+ * @see #controlInputMethodAnimation(WindowInsetsAnimationControlListener)
+ * @see #showInputMethod()
+ */
+ default void hideInputMethod() {
+ hide(ime());
+ }
}
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 45c36516b885..6326c591aa04 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -255,6 +255,12 @@ public interface WindowManager extends ViewManager {
int TRANSIT_CRASHING_ACTIVITY_CLOSE = 26;
/**
+ * A task is changing windowing modes
+ * @hide
+ */
+ int TRANSIT_TASK_CHANGE_WINDOWING_MODE = 27;
+
+ /**
* @hide
*/
@IntDef(prefix = { "TRANSIT_" }, value = {
@@ -280,7 +286,8 @@ public interface WindowManager extends ViewManager {
TRANSIT_KEYGUARD_UNOCCLUDE,
TRANSIT_TRANSLUCENT_ACTIVITY_OPEN,
TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE,
- TRANSIT_CRASHING_ACTIVITY_CLOSE
+ TRANSIT_CRASHING_ACTIVITY_CLOSE,
+ TRANSIT_TASK_CHANGE_WINDOWING_MODE
})
@Retention(RetentionPolicy.SOURCE)
@interface TransitionType {}
diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
index c5c1bcae232a..6aafa348e3f5 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -1166,9 +1166,10 @@ public final class AccessibilityManager {
/**
* Notifies that the accessibility button in the system's navigation area has been clicked
*
+ * @param displayId The logical display id.
* @hide
*/
- public void notifyAccessibilityButtonClicked() {
+ public void notifyAccessibilityButtonClicked(int displayId) {
final IAccessibilityManager service;
synchronized (mLock) {
service = getServiceLocked();
@@ -1177,7 +1178,7 @@ public final class AccessibilityManager {
}
}
try {
- service.notifyAccessibilityButtonClicked();
+ service.notifyAccessibilityButtonClicked(displayId);
} catch (RemoteException re) {
Log.e(LOG_TAG, "Error while dispatching accessibility button click", re);
}
diff --git a/core/java/android/view/accessibility/IAccessibilityManager.aidl b/core/java/android/view/accessibility/IAccessibilityManager.aidl
index 38dac94340bb..486b35d9cc0f 100644
--- a/core/java/android/view/accessibility/IAccessibilityManager.aidl
+++ b/core/java/android/view/accessibility/IAccessibilityManager.aidl
@@ -63,7 +63,7 @@ interface IAccessibilityManager {
IBinder getWindowToken(int windowId, int userId);
- void notifyAccessibilityButtonClicked();
+ void notifyAccessibilityButtonClicked(int displayId);
void notifyAccessibilityButtonVisibilityChanged(boolean available);
diff --git a/core/java/android/view/autofill/AutofillId.java b/core/java/android/view/autofill/AutofillId.java
index 9c935af09cca..f1c7b695ce05 100644
--- a/core/java/android/view/autofill/AutofillId.java
+++ b/core/java/android/view/autofill/AutofillId.java
@@ -29,12 +29,14 @@ public final class AutofillId implements Parcelable {
/** @hide */
public static final int NO_SESSION = 0;
- private static final int FLAG_IS_VIRTUAL = 0x1;
- private static final int FLAG_HAS_SESSION = 0x2;
+ private static final int FLAG_IS_VIRTUAL_INT = 0x1;
+ private static final int FLAG_IS_VIRTUAL_LONG = 0x2;
+ private static final int FLAG_HAS_SESSION = 0x4;
private final int mViewId;
private final int mFlags;
- private final int mVirtualId;
+ private final int mVirtualIntId;
+ private final long mVirtualLongId;
private final int mSessionId;
/** @hide */
@@ -46,40 +48,89 @@ public final class AutofillId implements Parcelable {
/** @hide */
@TestApi
public AutofillId(@NonNull AutofillId parent, int virtualChildId) {
- this(FLAG_IS_VIRTUAL, parent.mViewId, virtualChildId, NO_SESSION);
+ this(FLAG_IS_VIRTUAL_INT, parent.mViewId, virtualChildId, NO_SESSION);
}
/** @hide */
public AutofillId(int parentId, int virtualChildId) {
- this(FLAG_IS_VIRTUAL, parentId, virtualChildId, NO_SESSION);
+ this(FLAG_IS_VIRTUAL_INT, parentId, virtualChildId, NO_SESSION);
}
/** @hide */
- public AutofillId(@NonNull AutofillId parent, int virtualChildId, int sessionId) {
- this(FLAG_IS_VIRTUAL | FLAG_HAS_SESSION, parent.mViewId, virtualChildId, sessionId);
+ public AutofillId(@NonNull AutofillId parent, long virtualChildId, int sessionId) {
+ this(FLAG_IS_VIRTUAL_LONG | FLAG_HAS_SESSION, parent.mViewId, virtualChildId, sessionId);
}
- private AutofillId(int flags, int parentId, int virtualChildId, int sessionId) {
+ private AutofillId(int flags, int parentId, long virtualChildId, int sessionId) {
mFlags = flags;
mViewId = parentId;
- mVirtualId = virtualChildId;
+ mVirtualIntId = ((flags & FLAG_IS_VIRTUAL_INT) != 0) ? (int) virtualChildId : View.NO_ID;
+ mVirtualLongId = ((flags & FLAG_IS_VIRTUAL_LONG) != 0) ? virtualChildId : View.NO_ID;
mSessionId = sessionId;
}
-
/** @hide */
public int getViewId() {
return mViewId;
}
- /** @hide */
- public int getVirtualChildId() {
- return mVirtualId;
+ /**
+ * Gets the virtual child id.
+ *
+ * <p>Should only be used on subsystems where such id is represented by an {@code int}
+ * (Assist and Autofill).
+ *
+ * @hide
+ */
+ public int getVirtualChildIntId() {
+ return mVirtualIntId;
}
- /** @hide */
- public boolean isVirtual() {
- return (mFlags & FLAG_IS_VIRTUAL) != 0;
+ /**
+ * Gets the virtual child id.
+ *
+ * <p>Should only be used on subsystems where such id is represented by a {@code long}
+ * (ContentCapture).
+ *
+ * @hide
+ */
+ public long getVirtualChildLongId() {
+ return mVirtualLongId;
+ }
+
+ /**
+ * Checks whether this node represents a virtual child, whose id is represented by an
+ * {@code int}.
+ *
+ * <p>Should only be used on subsystems where such id is represented by an {@code int}
+ * (Assist and Autofill).
+ *
+ * @hide
+ */
+ public boolean isVirtualInt() {
+ return (mFlags & FLAG_IS_VIRTUAL_INT) != 0;
+ }
+
+ /**
+ * Checks whether this node represents a virtual child, whose id is represented by an
+ * {@code long}.
+ *
+ * <p>Should only be used on subsystems where such id is represented by a {@code long}
+ * (ContentCapture).
+ *
+ * @hide
+ */
+ public boolean isVirtualLong() {
+ return (mFlags & FLAG_IS_VIRTUAL_LONG) != 0;
+ }
+
+ /**
+ * Checks whether this node represents a non-virtual child.
+ *
+ * @hide
+ */
+ public boolean isNonVirtual() {
+ return !isVirtualInt() && !isVirtualLong();
}
private boolean hasSession() {
@@ -100,7 +151,8 @@ public final class AutofillId implements Parcelable {
final int prime = 31;
int result = 1;
result = prime * result + mViewId;
- result = prime * result + mVirtualId;
+ result = prime * result + mVirtualIntId;
+ result = prime * result + (int) (mVirtualLongId ^ (mVirtualLongId >>> 32));
result = prime * result + mSessionId;
return result;
}
@@ -112,7 +164,8 @@ public final class AutofillId implements Parcelable {
if (getClass() != obj.getClass()) return false;
final AutofillId other = (AutofillId) obj;
if (mViewId != other.mViewId) return false;
- if (mVirtualId != other.mVirtualId) return false;
+ if (mVirtualIntId != other.mVirtualIntId) return false;
+ if (mVirtualLongId != other.mVirtualLongId) return false;
if (mSessionId != other.mSessionId) return false;
return true;
}
@@ -120,9 +173,12 @@ public final class AutofillId implements Parcelable {
@Override
public String toString() {
final StringBuilder builder = new StringBuilder().append(mViewId);
- if (isVirtual()) {
- builder.append(':').append(mVirtualId);
+ if (isVirtualInt()) {
+ builder.append(':').append(mVirtualIntId);
+ } else if (isVirtualLong()) {
+ builder.append(':').append(mVirtualLongId);
}
+
if (hasSession()) {
builder.append('@').append(mSessionId);
}
@@ -138,12 +194,14 @@ public final class AutofillId implements Parcelable {
public void writeToParcel(Parcel parcel, int flags) {
parcel.writeInt(mViewId);
parcel.writeInt(mFlags);
- if (isVirtual()) {
- parcel.writeInt(mVirtualId);
- }
if (hasSession()) {
parcel.writeInt(mSessionId);
}
+ if (isVirtualInt()) {
+ parcel.writeInt(mVirtualIntId);
+ } else if (isVirtualLong()) {
+ parcel.writeLong(mVirtualLongId);
+ }
}
public static final Parcelable.Creator<AutofillId> CREATOR =
@@ -152,9 +210,14 @@ public final class AutofillId implements Parcelable {
public AutofillId createFromParcel(Parcel source) {
final int viewId = source.readInt();
final int flags = source.readInt();
- final int virtualId = (flags & FLAG_IS_VIRTUAL) != 0 ? source.readInt() : View.NO_ID;
final int sessionId = (flags & FLAG_HAS_SESSION) != 0 ? source.readInt() : NO_SESSION;
- return new AutofillId(flags, viewId, virtualId, sessionId);
+ if ((flags & FLAG_IS_VIRTUAL_INT) != 0) {
+ return new AutofillId(flags, viewId, source.readInt(), sessionId);
+ }
+ if ((flags & FLAG_IS_VIRTUAL_LONG) != 0) {
+ return new AutofillId(flags, viewId, source.readLong(), sessionId);
+ }
+ return new AutofillId(flags, viewId, View.NO_ID, sessionId);
}
@Override
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 888a4c57751e..64c34f612048 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -17,6 +17,7 @@
package android.view.autofill;
import static android.service.autofill.FillRequest.FLAG_MANUAL_REQUEST;
+import static android.util.DebugUtils.flagsToString;
import static android.view.autofill.Helper.sDebug;
import static android.view.autofill.Helper.sVerbose;
@@ -25,6 +26,7 @@ import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresFeature;
+import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.TestApi;
import android.content.ComponentName;
@@ -77,6 +79,7 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
+import java.util.Set;
//TODO: use java.lang.ref.Cleaner once Android supports Java 9
import sun.misc.Cleaner;
@@ -336,6 +339,25 @@ public final class AutofillManager {
public static final int MAX_TEMP_AUGMENTED_SERVICE_DURATION_MS = 1_000 * 60 * 2; // 2 minutes
/**
+ * Displays the Augment Autofill window using the same mechanism (such as a popup-window
+ * attached to the focused view) as the standard autofill.
+ *
+ * @hide
+ */
+ @TestApi
+ public static final int FLAG_SMART_SUGGESTION_SYSTEM = 0x1;
+
+ /** @hide */ // TODO(b/123233342): remove when not used anymore
+ public static final int FLAG_SMART_SUGGESTION_LEGACY = 0x2;
+
+ /** @hide */
+ @IntDef(flag = true, prefix = { "FLAG_SMART_SUGGESTION_" }, value = {
+ FLAG_SMART_SUGGESTION_SYSTEM
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface SmartSuggestionMode {}
+
+ /**
* Makes an authentication id from a request id and a dataset id.
*
* @param requestId The request id.
@@ -1686,7 +1708,7 @@ public final class AutofillManager {
final IAutoFillManager service = mService;
final IAutoFillManagerClient serviceClient = mServiceClient;
mServiceClientCleaner = Cleaner.create(this, () -> {
- // TODO(b/111330312): call service to also remove reference to
+ // TODO(b/123100811): call service to also remove reference to
// mAugmentedAutofillServiceClient
try {
service.removeClient(serviceClient, userId);
@@ -1746,6 +1768,108 @@ public final class AutofillManager {
}
}
+ /**
+ * Defines whether augmented autofill should be triggered for activities with such
+ * {@link android.content.ComponentName}.
+ *
+ * <p>Useful to blacklist a particular activity.
+ *
+ * <p><b>Note:</b> This method should only be called by the app providing the augmented autofill
+ * service, and it's ignored if the caller isn't it.
+ *
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ //TODO(b/122654591): @TestApi is needed because CtsAutoFillServiceTestCases hosts the service
+ //in the same package as the test, and that module is compiled with SDK=test_current
+ public void setActivityAugmentedAutofillEnabled(@NonNull ComponentName activity,
+ boolean enabled) {
+ // TODO(b/123100824): implement
+ }
+
+ /**
+ * Defines whether augmented autofill should be triggered for activities of the app with such
+ * {@code packageName}.
+ *
+ * <p>Useful to blacklist any activity from a particular app.
+ *
+ * <p><b>Note:</b> This method should only be called by the app providing the augmented autofill
+ * service, and it's ignored if the caller isn't it.
+ *
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ //TODO(b/122654591): @TestApi is needed because CtsAutoFillServiceTestCases hosts the service
+ //in the same package as the test, and that module is compiled with SDK=test_current
+ public void setPackageAugmentedAutofillEnabled(@NonNull String packageName, boolean enabled) {
+ // TODO(b/123100824): implement
+ }
+
+ /**
+ * Explicitly limits augmented autofill to the given packages and activities.
+ *
+ * <p>When the whitelist is set, it overrides the values passed to
+ * {@link #setActivityAugmentedAutofillEnabled(ComponentName, boolean)}
+ * and {@link #setPackageAugmentedAutofillEnabled(String, boolean)}.
+ *
+ * <p>To reset the whitelist, call it passing {@code null} to both arguments.
+ *
+ * <p>Useful when the service wants to restrict augmented autofill to a category of apps, like
+ * apps that uses addresses. For example, if the service wants to support augmented autofill on
+ * all activities of app {@code AddressApp1} and just activities {@code act1} and {@code act2}
+ * of {@code AddressApp2}, it would call:
+ * {@code setAugmentedAutofillWhitelist(Arrays.asList("AddressApp1"),
+ * Arrays.asList(new ComponentName("AddressApp2", "act1"),
+ * new ComponentName("AddressApp2", "act2")));}
+ *
+ * <p><b>Note:</b> This method should only be called by the app providing the augmented autofill
+ * service, and it's ignored if the caller isn't it.
+ *
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ //TODO(b/122654591): @TestApi is needed because CtsAutoFillServiceTestCases hosts the service
+ //in the same package as the test, and that module is compiled with SDK=test_current
+ public void setAugmentedAutofillWhitelist(@Nullable List<String> packages,
+ @Nullable List<ComponentName> activities) {
+ // TODO(b/123100824): implement
+ }
+
+ /**
+ * Gets the activities where augmented autofill was disabled by
+ * {@link #setActivityAugmentedAutofillEnabled(ComponentName, boolean)}.
+ *
+ * <p><b>Note:</b> This method should only be called by the app providing the augmented autofill
+ * service, and it's ignored if the caller isn't it.
+ *
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ @NonNull
+ public Set<ComponentName> getAugmentedAutofillDisabledActivities() {
+ return null; // TODO(b/123100824): implement
+ }
+
+ /**
+ * Gets the apps where content capture was disabled by
+ * {@link #setPackageAugmentedAutofillEnabled(String, boolean)}.
+ *
+ * <p><b>Note:</b> This method should only be called by the app providing the augmented autofill
+ * service, and it's ignored if the caller isn't it.
+ *
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ @NonNull
+ public Set<String> getAugmentedAutofillDisabledPackages() {
+ return null; // TODO(b/123100824): implement
+ }
+
private void requestShowFillUi(int sessionId, AutofillId id, int width, int height,
Rect anchorBounds, IAutofillWindowPresenter presenter) {
final View anchor = findView(id);
@@ -1769,8 +1893,8 @@ public final class AutofillManager {
}
if (callback != null) {
- if (id.isVirtual()) {
- callback.onAutofillEvent(anchor, id.getVirtualChildId(),
+ if (id.isVirtualInt()) {
+ callback.onAutofillEvent(anchor, id.getVirtualChildIntId(),
AutofillCallback.EVENT_INPUT_SHOWN);
} else {
callback.onAutofillEvent(anchor, AutofillCallback.EVENT_INPUT_SHOWN);
@@ -1896,7 +2020,7 @@ public final class AutofillManager {
failedIds.add(id);
continue;
}
- if (id.isVirtual()) {
+ if (id.isVirtualInt()) {
if (virtualValues == null) {
// Most likely there will be just one view with virtual children.
virtualValues = new ArrayMap<>(1);
@@ -1907,7 +2031,7 @@ public final class AutofillManager {
valuesByParent = new SparseArray<>(5);
virtualValues.put(view, valuesByParent);
}
- valuesByParent.put(id.getVirtualChildId(), value);
+ valuesByParent.put(id.getVirtualChildIntId(), value);
} else {
// Mark the view as to be autofilled with 'value'
if (mLastAutofilledData == null) {
@@ -2142,8 +2266,8 @@ public final class AutofillManager {
}
if (callback != null) {
- if (id.isVirtual()) {
- callback.onAutofillEvent(anchor, id.getVirtualChildId(),
+ if (id.isVirtualInt()) {
+ callback.onAutofillEvent(anchor, id.getVirtualChildIntId(),
AutofillCallback.EVENT_INPUT_HIDDEN);
} else {
callback.onAutofillEvent(anchor, AutofillCallback.EVENT_INPUT_HIDDEN);
@@ -2169,8 +2293,8 @@ public final class AutofillManager {
}
if (callback != null) {
- if (id.isVirtual()) {
- callback.onAutofillEvent(anchor, id.getVirtualChildId(),
+ if (id.isVirtualInt()) {
+ callback.onAutofillEvent(anchor, id.getVirtualChildIntId(),
AutofillCallback.EVENT_INPUT_UNAVAILABLE);
} else {
callback.onAutofillEvent(anchor, AutofillCallback.EVENT_INPUT_UNAVAILABLE);
@@ -2296,6 +2420,11 @@ public final class AutofillManager {
}
}
+ /** @hide */
+ public static String getSmartSuggestionModeToString(@SmartSuggestionMode int flags) {
+ return flagsToString(AutofillManager.class, "FLAG_SMART_SUGGESTION_", flags);
+ }
+
@GuardedBy("mLock")
private boolean isActiveLocked() {
return mState == STATE_ACTIVE;
@@ -2972,7 +3101,6 @@ public final class AutofillManager {
@Override
public Rect getViewCoordinates(@NonNull AutofillId id) {
- // TODO(b/111330312): use handler / callback?
final AutofillManager afm = mAfm.get();
if (afm == null) return null;
diff --git a/core/java/android/view/contentcapture/ContentCaptureContext.java b/core/java/android/view/contentcapture/ContentCaptureContext.java
index 2d2987a035da..19286135532b 100644
--- a/core/java/android/view/contentcapture/ContentCaptureContext.java
+++ b/core/java/android/view/contentcapture/ContentCaptureContext.java
@@ -22,6 +22,7 @@ import android.annotation.SystemApi;
import android.app.TaskInfo;
import android.content.ComponentName;
import android.content.Context;
+import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.Parcel;
@@ -84,9 +85,9 @@ public final class ContentCaptureContext implements Parcelable {
// Fields below are set by app on Builder
private final @Nullable Bundle mExtras;
private final @Nullable Uri mUri;
+ private final @Nullable String mAction;
// Fields below are set by server when the session starts
- // TODO(b/111276913): create new object for taskId + componentName / reuse on other places
private final @Nullable ComponentName mComponentName;
private final int mTaskId;
private final int mDisplayId;
@@ -102,10 +103,12 @@ public final class ContentCaptureContext implements Parcelable {
mHasClientContext = true;
mExtras = clientContext.mExtras;
mUri = clientContext.mUri;
+ mAction = clientContext.mAction;
} else {
mHasClientContext = false;
mExtras = null;
mUri = null;
+ mAction = null;
}
mComponentName = Preconditions.checkNotNull(componentName);
mTaskId = taskId;
@@ -117,13 +120,14 @@ public final class ContentCaptureContext implements Parcelable {
mHasClientContext = true;
mExtras = builder.mExtras;
mUri = builder.mUri;
+ mAction = builder.mAction;
mComponentName = null;
mTaskId = mFlags = mDisplayId = 0;
}
/**
- * Gets the (optional) extras set by the app.
+ * Gets the (optional) extras set by the app (through {@link Builder#setExtras(Bundle)}).
*
* <p>It can be used to provide vendor-specific data that can be modified and examined.
*
@@ -136,7 +140,7 @@ public final class ContentCaptureContext implements Parcelable {
}
/**
- * Gets the (optional) URI set by the app.
+ * Gets the (optional) URI set by the app (through {@link Builder#setUri(Uri)}).
*
* @hide
*/
@@ -147,6 +151,17 @@ public final class ContentCaptureContext implements Parcelable {
}
/**
+ * Gets the (optional) action set by the app (through {@link Builder#setAction(String)}).
+ *
+ * @hide
+ */
+ @SystemApi
+ @Nullable
+ public String getAction() {
+ return mAction;
+ }
+
+ /**
* Gets the id of the {@link TaskInfo task} associated with this context.
*
* @hide
@@ -213,6 +228,8 @@ public final class ContentCaptureContext implements Parcelable {
public static final class Builder {
private Bundle mExtras;
private Uri mUri;
+ private boolean mDestroyed;
+ private String mAction;
/**
* Sets extra options associated with this context.
@@ -221,11 +238,13 @@ public final class ContentCaptureContext implements Parcelable {
*
* @param extras extra options.
* @return this builder.
+ *
+ * @throws IllegalStateException if {@link #build()} was already called.
*/
@NonNull
public Builder setExtras(@NonNull Bundle extras) {
- // TODO(b/111276913): check build just once / throw exception / test / document
mExtras = Preconditions.checkNotNull(extras);
+ throwIfDestroyed();
return this;
}
@@ -236,23 +255,51 @@ public final class ContentCaptureContext implements Parcelable {
*
* @param uri URI associated with this context.
* @return this builder.
+ *
+ * @throws IllegalStateException if {@link #build()} was already called.
*/
@NonNull
public Builder setUri(@NonNull Uri uri) {
- // TODO(b/111276913): check build just once / throw exception / test / document
mUri = Preconditions.checkNotNull(uri);
+ throwIfDestroyed();
+ return this;
+ }
+
+ /**
+ * Sets an {@link Intent#getAction() intent action} associated with this context.
+ *
+ * @param action intent action
+ *
+ * @return this builder
+ *
+ * @throws IllegalStateException if {@link #build()} was already called.
+ */
+ @NonNull
+ public Builder setAction(@NonNull String action) {
+ mAction = Preconditions.checkNotNull(action);
+ throwIfDestroyed();
return this;
}
/**
* Builds the {@link ContentCaptureContext}.
+ *
+ * @throws IllegalStateException if {@link #build()} was already called or no call to either
+ * {@link #setExtras(Bundle)}, {@link #setAction(String)}, or {@link #setUri(Uri)} was made.
+ *
+ * @return the built {@code ContentCaptureContext}
*/
public ContentCaptureContext build() {
- // TODO(b/111276913): check build just once / throw exception / test / document
- // TODO(b/111276913): make sure it at least one property (uri / extras) / test /
- // throw exception / documment
+ throwIfDestroyed();
+ Preconditions.checkState(mExtras != null || mUri != null || mAction != null,
+ "Must call setUri() or setExtras() or setUri() before calling build()");
+ mDestroyed = true;
return new ContentCaptureContext(this);
}
+
+ private void throwIfDestroyed() {
+ Preconditions.checkState(!mDestroyed, "Already called #build()");
+ }
}
/**
@@ -277,6 +324,10 @@ public final class ContentCaptureContext implements Parcelable {
// NOTE: cannot dump because it could contain PII
pw.print(", hasUri");
}
+ if (mAction != null) {
+ // NOTE: cannot dump because it could contain PII
+ pw.print(", hasAction");
+ }
}
@Override
@@ -297,6 +348,10 @@ public final class ContentCaptureContext implements Parcelable {
// NOTE: cannot print because it could contain PII
builder.append(", hasUri");
}
+ if (mAction != null) {
+ // NOTE: cannot print because it could contain PII
+ builder.append(", hasAction");
+ }
return builder.append(']').toString();
}
@@ -310,6 +365,7 @@ public final class ContentCaptureContext implements Parcelable {
parcel.writeInt(mHasClientContext ? 1 : 0);
if (mHasClientContext) {
parcel.writeParcelable(mUri, flags);
+ parcel.writeString(mAction);
parcel.writeBundle(mExtras);
}
parcel.writeParcelable(mComponentName, flags);
@@ -329,12 +385,14 @@ public final class ContentCaptureContext implements Parcelable {
final ContentCaptureContext clientContext;
if (hasClientContext) {
+ // Must reconstruct the client context using the Builder API
final Builder builder = new Builder();
final Uri uri = parcel.readParcelable(null);
+ final String action = parcel.readString();
final Bundle extras = parcel.readBundle();
if (uri != null) builder.setUri(uri);
+ if (action != null) builder.setAction(action);
if (extras != null) builder.setExtras(extras);
- // Must reconstruct the client context using the Builder API
clientContext = new ContentCaptureContext(builder);
} else {
clientContext = null;
diff --git a/core/java/android/view/contentcapture/ContentCaptureEvent.java b/core/java/android/view/contentcapture/ContentCaptureEvent.java
index 43963c3054e5..a6d44729aee5 100644
--- a/core/java/android/view/contentcapture/ContentCaptureEvent.java
+++ b/core/java/android/view/contentcapture/ContentCaptureEvent.java
@@ -15,6 +15,8 @@
*/
package android.view.contentcapture;
+import static android.view.contentcapture.ContentCaptureHelper.getSanitizedString;
+
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -267,8 +269,7 @@ public final class ContentCaptureEvent implements Parcelable {
pw.print(", parentSessionId="); pw.print(mParentSessionId);
}
if (mText != null) {
- // Cannot print content because could have PII
- pw.print(", text="); pw.print(mText.length()); pw.print("_chars");
+ pw.print(", text="); pw.println(getSanitizedString(mText));
}
}
@@ -293,6 +294,9 @@ public final class ContentCaptureEvent implements Parcelable {
}
string.append(", id=").append(mNode.getAutofillId());
}
+ if (mText != null) {
+ string.append(", text=").append(getSanitizedString(mText));
+ }
return string.append(']').toString();
}
diff --git a/core/java/android/view/contentcapture/ContentCaptureHelper.java b/core/java/android/view/contentcapture/ContentCaptureHelper.java
new file mode 100644
index 000000000000..508880feb3c3
--- /dev/null
+++ b/core/java/android/view/contentcapture/ContentCaptureHelper.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.view.contentcapture;
+
+import android.annotation.Nullable;
+
+/**
+ * Helpe class for this package.
+ */
+final class ContentCaptureHelper {
+
+ // TODO(b/121044306): define a way to dynamically set them(for example, using settings?)
+ static final boolean VERBOSE = false;
+ static final boolean DEBUG = true; // STOPSHIP if not set to false
+
+ /**
+ * Used to log text that could contain PII.
+ */
+ @Nullable
+ public static String getSanitizedString(@Nullable CharSequence text) {
+ return text == null ? null : text.length() + "_chars";
+ }
+
+ private ContentCaptureHelper() {
+ throw new UnsupportedOperationException("contains only static methods");
+ }
+}
diff --git a/core/java/android/view/contentcapture/ContentCaptureManager.java b/core/java/android/view/contentcapture/ContentCaptureManager.java
index 413f1a5a8955..b9017b365f90 100644
--- a/core/java/android/view/contentcapture/ContentCaptureManager.java
+++ b/core/java/android/view/contentcapture/ContentCaptureManager.java
@@ -15,6 +15,8 @@
*/
package android.view.contentcapture;
+import static android.view.contentcapture.ContentCaptureHelper.VERBOSE;
+
import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
import android.annotation.NonNull;
@@ -57,10 +59,6 @@ public final class ContentCaptureManager {
*/
private static final int SYNC_CALLS_TIMEOUT_MS = 5000;
- // TODO(b/121044306): define a way to dynamically set them(for example, using settings?)
- static final boolean VERBOSE = false;
- static final boolean DEBUG = true; // STOPSHIP if not set to false
-
private final Object mLock = new Object();
@GuardedBy("mLock")
@@ -191,13 +189,19 @@ public final class ContentCaptureManager {
}
/**
- * Called by the ap to request the Content Capture service to remove user-data associated with
+ * Called by the app to request the Content Capture service to remove user-data associated with
* some context.
*
* @param request object specifying what user data should be removed.
*/
public void removeUserData(@NonNull UserDataRemovalRequest request) {
- //TODO(b/111276913): implement
+ Preconditions.checkNotNull(request);
+
+ try {
+ mService.removeUserData(mContext.getUserId(), request);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
}
/** @hide */
diff --git a/core/java/android/view/contentcapture/ContentCaptureSession.java b/core/java/android/view/contentcapture/ContentCaptureSession.java
index b620ab1eb7c3..c425e7bd3700 100644
--- a/core/java/android/view/contentcapture/ContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/ContentCaptureSession.java
@@ -15,8 +15,8 @@
*/
package android.view.contentcapture;
-import static android.view.contentcapture.ContentCaptureManager.DEBUG;
-import static android.view.contentcapture.ContentCaptureManager.VERBOSE;
+import static android.view.contentcapture.ContentCaptureHelper.DEBUG;
+import static android.view.contentcapture.ContentCaptureHelper.VERBOSE;
import android.annotation.CallSuper;
import android.annotation.IntDef;
@@ -34,8 +34,6 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.Preconditions;
-import dalvik.system.CloseGuard;
-
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -148,9 +146,6 @@ public abstract class ContentCaptureSession implements AutoCloseable {
@Retention(RetentionPolicy.SOURCE)
@interface FlushReason{}
-
- private final CloseGuard mCloseGuard = CloseGuard.get();
-
private final Object mLock = new Object();
/**
@@ -185,7 +180,6 @@ public abstract class ContentCaptureSession implements AutoCloseable {
@VisibleForTesting
public ContentCaptureSession(@NonNull String id) {
mId = Preconditions.checkNotNull(id);
- mCloseGuard.open("destroy");
}
/** @hide */
@@ -246,13 +240,11 @@ public abstract class ContentCaptureSession implements AutoCloseable {
public final void destroy() {
synchronized (mLock) {
if (mDestroyed) {
- Log.e(TAG, "destroy(" + mId + "): already destroyed");
+ if (DEBUG) Log.d(TAG, "destroy(" + mId + "): already destroyed");
return;
}
mDestroyed = true;
- mCloseGuard.close();
-
// TODO(b/111276913): check state (for example, how to handle if it's waiting for remote
// id) and send it to the cache of batched commands
if (VERBOSE) {
@@ -288,18 +280,6 @@ public abstract class ContentCaptureSession implements AutoCloseable {
destroy();
}
- @Override
- protected void finalize() throws Throwable {
- try {
- if (mCloseGuard != null) {
- mCloseGuard.warnIfOpen();
- }
- destroy();
- } finally {
- super.finalize();
- }
- }
-
/**
* Notifies the Content Capture Service that a node has been added to the view structure.
*
@@ -352,14 +332,14 @@ public abstract class ContentCaptureSession implements AutoCloseable {
* @throws IllegalArgumentException if {@code virtualIds} is empty
*/
public final void notifyViewsDisappeared(@NonNull AutofillId hostId,
- @NonNull int[] virtualIds) {
- Preconditions.checkArgument(!hostId.isVirtual(), "parent cannot be virtual");
+ @NonNull long[] virtualIds) {
+ Preconditions.checkArgument(hostId.isNonVirtual(), "parent cannot be virtual");
Preconditions.checkArgument(!ArrayUtils.isEmpty(virtualIds), "virtual ids cannot be empty");
if (!isContentCaptureEnabled()) return;
// TODO(b/123036895): use a internalNotifyViewsDisappeared that optimizes how the event is
// parcelized
- for (int id : virtualIds) {
+ for (long id : virtualIds) {
internalNotifyViewDisappeared(new AutofillId(hostId, id, getIdAsInt()));
}
}
@@ -405,9 +385,9 @@ public abstract class ContentCaptureSession implements AutoCloseable {
*
* @throws IllegalArgumentException if the {@code parentId} is a virtual child id.
*/
- public @NonNull AutofillId newAutofillId(@NonNull AutofillId parentId, int virtualChildId) {
+ public @NonNull AutofillId newAutofillId(@NonNull AutofillId parentId, long virtualChildId) {
Preconditions.checkNotNull(parentId);
- Preconditions.checkArgument(!parentId.isVirtual(), "virtual ids cannot have children");
+ Preconditions.checkArgument(parentId.isNonVirtual(), "virtual ids cannot have children");
return new AutofillId(parentId, virtualChildId, getIdAsInt());
}
@@ -423,7 +403,7 @@ public abstract class ContentCaptureSession implements AutoCloseable {
*/
@NonNull
public final ViewStructure newVirtualViewStructure(@NonNull AutofillId parentId,
- int virtualId) {
+ long virtualId) {
return new ViewNode.ViewStructureImpl(parentId, virtualId, getIdAsInt());
}
diff --git a/core/java/android/view/contentcapture/IContentCaptureManager.aidl b/core/java/android/view/contentcapture/IContentCaptureManager.aidl
index be9c00f04d99..51aea162f1db 100644
--- a/core/java/android/view/contentcapture/IContentCaptureManager.aidl
+++ b/core/java/android/view/contentcapture/IContentCaptureManager.aidl
@@ -19,6 +19,7 @@ package android.view.contentcapture;
import android.content.ComponentName;
import android.view.contentcapture.ContentCaptureContext;
import android.view.contentcapture.ContentCaptureEvent;
+import android.view.contentcapture.UserDataRemovalRequest;
import android.os.IBinder;
import com.android.internal.os.IResultReceiver;
@@ -56,4 +57,9 @@ oneway interface IContentCaptureManager {
* provided {@code Bundle} with key "{@code EXTRA}".
*/
void getReceiverServiceComponentName(int userId, in IResultReceiver result);
+
+ /**
+ * Requests the removal of user data for the provided {@code userId}.
+ */
+ void removeUserData(int userId, in UserDataRemovalRequest request);
}
diff --git a/core/java/android/view/contentcapture/MainContentCaptureSession.java b/core/java/android/view/contentcapture/MainContentCaptureSession.java
index 103d7e6dc256..9e99c88da3ca 100644
--- a/core/java/android/view/contentcapture/MainContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/MainContentCaptureSession.java
@@ -20,8 +20,9 @@ import static android.view.contentcapture.ContentCaptureEvent.TYPE_SESSION_START
import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_APPEARED;
import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_DISAPPEARED;
import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_TEXT_CHANGED;
-import static android.view.contentcapture.ContentCaptureManager.DEBUG;
-import static android.view.contentcapture.ContentCaptureManager.VERBOSE;
+import static android.view.contentcapture.ContentCaptureHelper.DEBUG;
+import static android.view.contentcapture.ContentCaptureHelper.VERBOSE;
+import static android.view.contentcapture.ContentCaptureHelper.getSanitizedString;
import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
@@ -269,6 +270,7 @@ public final class MainContentCaptureSession extends ContentCaptureSession {
private void handleSendEvent(@NonNull ContentCaptureEvent event, boolean forceFlush) {
final int eventType = event.getType();
+ if (VERBOSE) Log.v(TAG, "handleSendEvent(" + getDebugState() + "): " + event);
if (!handleHasStarted() && eventType != ContentCaptureEvent.TYPE_SESSION_STARTED) {
// TODO(b/120494182): comment when this could happen (dialogs?)
Log.v(TAG, "handleSendEvent(" + getDebugState() + ", "
@@ -276,12 +278,16 @@ public final class MainContentCaptureSession extends ContentCaptureSession {
+ "): session not started yet");
return;
}
- if (VERBOSE) Log.v(TAG, "handleSendEvent(" + getDebugState() + "): " + event);
+ if (mDisabled.get()) {
+ // This happens when the event was queued in the handler before the sesison was ready,
+ // then handleSessionStarted() returned and set it as disabled - we need to drop it,
+ // otherwise it will keep triggering handleScheduleFlush()
+ if (VERBOSE) Log.v(TAG, "handleSendEvent(): ignoring when disabled");
+ return;
+ }
if (mEvents == null) {
if (VERBOSE) {
- Log.v(TAG, "handleSendEvent(" + getDebugState() + ", "
- + ContentCaptureEvent.getTypeAsString(eventType)
- + "): creating buffer for " + MAX_BUFFER_SIZE + " events");
+ Log.v(TAG, "handleSendEvent(): creating buffer for " + MAX_BUFFER_SIZE + " events");
}
mEvents = new ArrayList<>(MAX_BUFFER_SIZE);
}
@@ -296,8 +302,8 @@ public final class MainContentCaptureSession extends ContentCaptureSession {
if (lastEvent.getType() == TYPE_VIEW_TEXT_CHANGED
&& lastEvent.getId().equals(event.getId())) {
if (VERBOSE) {
- Log.v(TAG, "Buffering VIEW_TEXT_CHANGED event, updated text = "
- + event.getText());
+ Log.v(TAG, "Buffering VIEW_TEXT_CHANGED event, updated text="
+ + getSanitizedString(event.getText()));
}
lastEvent.setText(event.getText());
addEvent = false;
@@ -365,8 +371,20 @@ public final class MainContentCaptureSession extends ContentCaptureSession {
}
private void handleScheduleFlush(@FlushReason int reason, boolean checkExisting) {
+ if (VERBOSE) {
+ Log.v(TAG, "handleScheduleFlush(" + getDebugState(reason)
+ + ", checkExisting=" + checkExisting);
+ }
if (!handleHasStarted()) {
- Log.v(TAG, "handleScheduleFlush(" + getDebugState() + "): session not started yet");
+ if (VERBOSE) Log.v(TAG, "handleScheduleFlush(): session not started yet");
+ return;
+ }
+
+ if (mDisabled.get()) {
+ // Should not be called on this state, as handleSendEvent checks.
+ // But we rather add one if check and log than re-schedule and keep the session alive...
+ Log.e(TAG, "handleScheduleFlush(" + getDebugState(reason) + "): should not be called "
+ + "when disabled. events=" + (mEvents == null ? null : mEvents.size()));
return;
}
if (checkExisting && mHandler.hasMessages(MSG_FLUSH)) {
@@ -375,8 +393,7 @@ public final class MainContentCaptureSession extends ContentCaptureSession {
}
mNextFlush = System.currentTimeMillis() + FLUSHING_FREQUENCY_MS;
if (VERBOSE) {
- Log.v(TAG, "handleScheduleFlush(" + getDebugState()
- + ", reason=" + getflushReasonAsString(reason) + "): scheduled to flush in "
+ Log.v(TAG, "handleScheduleFlush(): scheduled to flush in "
+ FLUSHING_FREQUENCY_MS + "ms: " + TimeUtils.logTimeOfDay(mNextFlush));
}
mHandler.sendMessageDelayed(
@@ -395,11 +412,16 @@ public final class MainContentCaptureSession extends ContentCaptureSession {
private void handleForceFlush(@FlushReason int reason) {
if (mEvents == null) return;
+ if (mDisabled.get()) {
+ Log.e(TAG, "handleForceFlush(" + getDebugState(reason) + "): should not be when "
+ + "disabled");
+ return;
+ }
+
if (mDirectServiceInterface == null) {
if (VERBOSE) {
- Log.v(TAG, "handleForceFlush(" + getDebugState()
- + ", reason=" + getflushReasonAsString(reason)
- + "): hold your horses, client not ready: " + mEvents);
+ Log.v(TAG, "handleForceFlush(" + getDebugState(reason) + "): hold your horses, "
+ + "client not ready: " + mEvents);
}
if (!mHandler.hasMessages(MSG_FLUSH)) {
handleScheduleFlush(reason, /* checkExisting= */ false);
@@ -410,8 +432,7 @@ public final class MainContentCaptureSession extends ContentCaptureSession {
final int numberEvents = mEvents.size();
final String reasonString = getflushReasonAsString(reason);
if (DEBUG) {
- Log.d(TAG, "Flushing " + numberEvents + " event(s) for " + getDebugState()
- + ". Reason: " + reasonString);
+ Log.d(TAG, "Flushing " + numberEvents + " event(s) for " + getDebugState(reason));
}
// Logs reason, size, max size, idle timeout
final String logRecord = "r=" + reasonString + " s=" + numberEvents
@@ -592,7 +613,14 @@ public final class MainContentCaptureSession extends ContentCaptureSession {
: "act:" + mComponentName.flattenToShortString();
}
+ @NonNull
private String getDebugState() {
- return getActivityName() + " (state=" + getStateAsString(mState) + ")";
+ return getActivityName() + " [state=" + getStateAsString(mState) + ", disabled="
+ + mDisabled.get() + "]";
+ }
+
+ @NonNull
+ private String getDebugState(@FlushReason int reason) {
+ return getDebugState() + ", reason=" + getflushReasonAsString(reason);
}
}
diff --git a/core/java/android/view/contentcapture/UserDataRemovalRequest.aidl b/core/java/android/view/contentcapture/UserDataRemovalRequest.aidl
new file mode 100644
index 000000000000..fbe47e08ea7c
--- /dev/null
+++ b/core/java/android/view/contentcapture/UserDataRemovalRequest.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.contentcapture;
+
+parcelable UserDataRemovalRequest;
diff --git a/core/java/android/view/contentcapture/UserDataRemovalRequest.java b/core/java/android/view/contentcapture/UserDataRemovalRequest.java
index 0261b70825a5..8ee63ef74685 100644
--- a/core/java/android/view/contentcapture/UserDataRemovalRequest.java
+++ b/core/java/android/view/contentcapture/UserDataRemovalRequest.java
@@ -17,10 +17,15 @@ package android.view.contentcapture;
import android.annotation.NonNull;
import android.annotation.SystemApi;
+import android.app.ActivityThread;
import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
+import android.util.IntArray;
+import com.android.internal.util.Preconditions;
+
+import java.util.ArrayList;
import java.util.List;
/**
@@ -29,8 +34,35 @@ import java.util.List;
*/
public final class UserDataRemovalRequest implements Parcelable {
- private UserDataRemovalRequest(Builder builder) {
- // TODO(b/111276913): implement
+ private final String mPackageName;
+
+ private final boolean mForEverything;
+ private ArrayList<UriRequest> mUriRequests;
+
+ private UserDataRemovalRequest(@NonNull Builder builder) {
+ mPackageName = ActivityThread.currentActivityThread().getApplication().getPackageName();
+ mForEverything = builder.mForEverything;
+ if (builder.mUris != null) {
+ final int size = builder.mUris.size();
+ mUriRequests = new ArrayList<>(size);
+ for (int i = 0; i < size; i++) {
+ mUriRequests.add(new UriRequest(builder.mUris.get(i),
+ builder.mRecursive.get(i) == 1));
+ }
+ }
+ }
+
+ private UserDataRemovalRequest(@NonNull Parcel parcel) {
+ mPackageName = parcel.readString();
+ mForEverything = parcel.readBoolean();
+ if (!mForEverything) {
+ final int size = parcel.readInt();
+ mUriRequests = new ArrayList<>(size);
+ for (int i = 0; i < size; i++) {
+ mUriRequests.add(new UriRequest((Uri) parcel.readValue(null),
+ parcel.readBoolean()));
+ }
+ }
}
/**
@@ -40,9 +72,7 @@ public final class UserDataRemovalRequest implements Parcelable {
@SystemApi
@NonNull
public String getPackageName() {
- // TODO(b/111276913): implement
- // TODO(b/111276913): make sure it's set on system_service so it cannot be faked by app
- return null;
+ return mPackageName;
}
/**
@@ -52,8 +82,7 @@ public final class UserDataRemovalRequest implements Parcelable {
*/
@SystemApi
public boolean isForEverything() {
- // TODO(b/111276913): implement
- return false;
+ return mForEverything;
}
/**
@@ -64,8 +93,7 @@ public final class UserDataRemovalRequest implements Parcelable {
@SystemApi
@NonNull
public List<UriRequest> getUriRequests() {
- // TODO(b/111276913): implement
- return null;
+ return mUriRequests;
}
/**
@@ -73,6 +101,12 @@ public final class UserDataRemovalRequest implements Parcelable {
*/
public static final class Builder {
+ private boolean mForEverything;
+ private ArrayList<Uri> mUris;
+ private IntArray mRecursive;
+
+ private boolean mDestroyed;
+
/**
* Requests servive to remove all user data associated with the app's package.
*
@@ -80,7 +114,12 @@ public final class UserDataRemovalRequest implements Parcelable {
*/
@NonNull
public Builder forEverything() {
- // TODO(b/111276913): implement
+ throwIfDestroyed();
+ if (mUris != null) {
+ throw new IllegalStateException("Already added Uris");
+ }
+
+ mForEverything = true;
return this;
}
@@ -94,7 +133,19 @@ public final class UserDataRemovalRequest implements Parcelable {
* @return this builder
*/
public Builder addUri(@NonNull Uri uri, boolean recursive) {
- // TODO(b/111276913): implement
+ throwIfDestroyed();
+ if (mForEverything) {
+ throw new IllegalStateException("Already is for everything");
+ }
+ Preconditions.checkNotNull(uri);
+
+ if (mUris == null) {
+ mUris = new ArrayList<>();
+ mRecursive = new IntArray();
+ }
+
+ mUris.add(uri);
+ mRecursive.add(recursive ? 1 : 0);
return this;
}
@@ -103,8 +154,16 @@ public final class UserDataRemovalRequest implements Parcelable {
*/
@NonNull
public UserDataRemovalRequest build() {
- // TODO(b/111276913): implement / unit test / check built / document exceptions
- return null;
+ throwIfDestroyed();
+
+ Preconditions.checkState(mForEverything || mUris != null);
+
+ mDestroyed = true;
+ return new UserDataRemovalRequest(this);
+ }
+
+ private void throwIfDestroyed() {
+ Preconditions.checkState(!mDestroyed, "Already destroyed!");
}
}
@@ -115,7 +174,17 @@ public final class UserDataRemovalRequest implements Parcelable {
@Override
public void writeToParcel(Parcel parcel, int flags) {
- // TODO(b/111276913): implement
+ parcel.writeString(mPackageName);
+ parcel.writeBoolean(mForEverything);
+ if (!mForEverything) {
+ final int size = mUriRequests.size();
+ parcel.writeInt(size);
+ for (int i = 0; i < size; i++) {
+ final UriRequest request = mUriRequests.get(i);
+ parcel.writeValue(request.getUri());
+ parcel.writeBoolean(request.isRecursive());
+ }
+ }
}
public static final Parcelable.Creator<UserDataRemovalRequest> CREATOR =
@@ -123,8 +192,7 @@ public final class UserDataRemovalRequest implements Parcelable {
@Override
public UserDataRemovalRequest createFromParcel(Parcel parcel) {
- // TODO(b/111276913): implement
- return null;
+ return new UserDataRemovalRequest(parcel);
}
@Override
diff --git a/core/java/android/view/contentcapture/ViewNode.java b/core/java/android/view/contentcapture/ViewNode.java
index cbc946b773ca..0cabafa21b17 100644
--- a/core/java/android/view/contentcapture/ViewNode.java
+++ b/core/java/android/view/contentcapture/ViewNode.java
@@ -617,7 +617,7 @@ public final class ViewNode extends AssistStructure.ViewNode {
}
@VisibleForTesting // Must be public to be accessed from FrameworkCoreTests' apk.
- public ViewStructureImpl(@NonNull AutofillId parentId, int virtualId, int sessionId) {
+ public ViewStructureImpl(@NonNull AutofillId parentId, long virtualId, int sessionId) {
mNode.mParentAutofillId = Preconditions.checkNotNull(parentId);
mNode.mAutofillId = new AutofillId(parentId, virtualId, sessionId);
}
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 15088d2070d2..0cb1800996c9 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -2490,7 +2490,14 @@ public final class InputMethodManager {
* @param subtype A new input method subtype to switch.
* @return true if the current subtype was successfully switched. When the specified subtype is
* null, this method returns false.
+ * @deprecated If the calling process is an IME, use
+ * {@link InputMethodService#switchInputMethod(String, InputMethodSubtype)}, which
+ * does not require any permission as long as the caller is the current IME.
+ * If the calling process is some privileged app that already has
+ * {@link android.Manifest.permission#WRITE_SECURE_SETTINGS} permission, just
+ * directly update {@link Settings.Secure#SELECTED_INPUT_METHOD_SUBTYPE}.
*/
+ @Deprecated
@RequiresPermission(WRITE_SECURE_SETTINGS)
public boolean setCurrentInputMethodSubtype(InputMethodSubtype subtype) {
if (Process.myUid() == Process.SYSTEM_UID) {
@@ -2675,7 +2682,13 @@ public final class InputMethodManager {
*
* @param imiId Id of InputMethodInfo which additional input method subtypes will be added to.
* @param subtypes subtypes will be added as additional subtypes of the current input method.
+ * @deprecated For IMEs that have already implemented features like customizable/downloadable
+ * keyboard layouts/languages, please start migration to other approaches. One idea
+ * would be exposing only one unified {@link InputMethodSubtype} then implement
+ * IME's own language switching mechanism within that unified subtype. The support
+ * of "Additional Subtype" may be completely dropped in a future version of Android.
*/
+ @Deprecated
public void setAdditionalInputMethodSubtypes(String imiId, InputMethodSubtype[] subtypes) {
try {
mService.setAdditionalInputMethodSubtypes(imiId, subtypes);
diff --git a/core/java/android/view/inspector/InspectableProperty.java b/core/java/android/view/inspector/InspectableProperty.java
index 355ff1d85e1f..f85952108f07 100644
--- a/core/java/android/view/inspector/InspectableProperty.java
+++ b/core/java/android/view/inspector/InspectableProperty.java
@@ -106,6 +106,7 @@ public @interface InspectableProperty {
/**
* One entry in an enumeration packed into a primitive {int}.
*
+ * @see IntEnumMapping
* @hide
*/
@Target({TYPE})
diff --git a/core/java/android/view/inspector/IntEnumMapping.java b/core/java/android/view/inspector/IntEnumMapping.java
new file mode 100644
index 000000000000..147bb46f2aa4
--- /dev/null
+++ b/core/java/android/view/inspector/IntEnumMapping.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.inspector;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.util.SparseArray;
+
+import java.util.Objects;
+
+/**
+ * Maps the values of an {@code int} property to strings for properties that encode an enumeration.
+ *
+ * An {@link InspectionCompanion} may provide an instance of this class to a {@link PropertyMapper}
+ * for flag values packed into primitive {@code int} properties.
+ *
+ * This class is an immutable wrapper for {@link SparseArray}, and must be constructed by a
+ * {@link Builder}.
+ *
+ * @see PropertyMapper#mapIntEnum(String, int, IntEnumMapping)
+ */
+public final class IntEnumMapping {
+ private final SparseArray<String> mValues;
+
+ /**
+ * Get the name for the given property value
+ *
+ * @param value The value of the property
+ * @return The name of the value in the enumeration, or null if no value is defined
+ */
+ @Nullable
+ public String get(int value) {
+ return mValues.get(value);
+ }
+
+ /**
+ * Create a new instance from a builder.
+ *
+ * This constructor is private, use {@link Builder#build()} instead.
+ *
+ * @param builder A builder to create from
+ */
+ private IntEnumMapping(Builder builder) {
+ mValues = builder.mValues.clone();
+ }
+
+ /**
+ * A builder for {@link IntEnumMapping}.
+ */
+ public static final class Builder {
+ @NonNull
+ private SparseArray<String> mValues;
+ private boolean mMustCloneValues = false;
+
+ public Builder() {
+ mValues = new SparseArray<>();
+ }
+
+ /**
+ * Add a new enumerated value.
+ *
+ * @param name The string name of the enumeration value
+ * @param value The {@code int} value of the enumeration value
+ * @return This builder
+ */
+ @NonNull
+ public Builder addValue(@NonNull String name, int value) {
+ // Save an allocation, only re-clone if the builder is used again after building
+ if (mMustCloneValues) {
+ mValues = mValues.clone();
+ }
+
+ mValues.put(value, Objects.requireNonNull(name));
+ return this;
+ }
+
+ /**
+ * Build a new {@link IntEnumMapping} from this builder.
+ *
+ * @return A new mapping
+ */
+ @NonNull
+ public IntEnumMapping build() {
+ mMustCloneValues = true;
+ return new IntEnumMapping(this);
+ }
+ }
+}
diff --git a/core/java/android/view/inspector/IntFlagMapping.java b/core/java/android/view/inspector/IntFlagMapping.java
index 8f7dfd5c5144..2409081ca3a5 100644
--- a/core/java/android/view/inspector/IntFlagMapping.java
+++ b/core/java/android/view/inspector/IntFlagMapping.java
@@ -21,10 +21,11 @@ import android.annotation.NonNull;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
+import java.util.Objects;
import java.util.Set;
/**
- * Maps the values of an {@code int} property to arrays of string for properties that encode flags.
+ * Maps the values of an {@code int} property to sets of string for properties that encode flags.
*
* An {@link InspectionCompanion} may provide an instance of this class to a {@link PropertyMapper}
* for flag values packed into primitive {@code int} properties.
@@ -45,7 +46,7 @@ public final class IntFlagMapping {
* Get an array of the names of enabled flags for a given property value.
*
* @param value The value of the property
- * @return The names of the enabled flags
+ * @return The names of the enabled flags, empty if no flags enabled
*/
@NonNull
public Set<String> get(int value) {
@@ -136,7 +137,7 @@ public final class IntFlagMapping {
private final int mMask;
private Flag(@NonNull String name, int target, int mask) {
- mName = name;
+ mName = Objects.requireNonNull(name);
mTarget = target;
mMask = mask;
}
diff --git a/core/java/android/view/inspector/PropertyMapper.java b/core/java/android/view/inspector/PropertyMapper.java
index e20582bf3ee4..00b18d172eed 100644
--- a/core/java/android/view/inspector/PropertyMapper.java
+++ b/core/java/android/view/inspector/PropertyMapper.java
@@ -18,7 +18,6 @@ package android.view.inspector;
import android.annotation.AttrRes;
import android.annotation.NonNull;
-import android.util.SparseArray;
/**
* An interface for mapping the string names of inspectable properties to integer identifiers.
@@ -155,14 +154,14 @@ public interface PropertyMapper {
int mapIntEnum(
@NonNull String name,
@AttrRes int attributeId,
- @NonNull SparseArray<String> mapping);
+ @NonNull IntEnumMapping mapping);
/**
* Map a string name to an integer ID for a flag set packed into an int property.
*
* @param name The name of the property
* @param attributeId If the property is from an XML attribute, the resource ID of the property
- * @param mapping A mapping from int to an array of strings
+ * @param mapping A mapping from int to a set of strings
* @return An integer ID for the property
* @throws PropertyConflictException If the property name is already mapped as another type.
*/
diff --git a/core/java/android/view/textclassifier/ActionsSuggestionsHelper.java b/core/java/android/view/textclassifier/ActionsSuggestionsHelper.java
index 77cb4cd28763..fdc34b3f68d0 100644
--- a/core/java/android/view/textclassifier/ActionsSuggestionsHelper.java
+++ b/core/java/android/view/textclassifier/ActionsSuggestionsHelper.java
@@ -103,10 +103,9 @@ public final class ActionsSuggestionsHelper {
final String modelName = String.format(
Locale.US, "%s_v%d", localesJoiner.toString(), modelVersion);
final int hash = Objects.hash(
- messages.stream()
- .map(ConversationActions.Message::getText)
- .collect(Collectors.toList()),
- context.getPackageName());
+ messages.stream().mapToInt(ActionsSuggestionsHelper::hashMessage),
+ context.getPackageName(),
+ System.currentTimeMillis());
return SelectionSessionLogger.SignatureParser.createSignature(
SelectionSessionLogger.CLASSIFIER_ID, modelName, hash);
}
@@ -128,4 +127,8 @@ public final class ActionsSuggestionsHelper {
return result;
}
}
+
+ private static int hashMessage(ConversationActions.Message message) {
+ return Objects.hash(message.getAuthor(), message.getText(), message.getReferenceTime());
+ }
}
diff --git a/core/java/android/view/textclassifier/TextClassificationManager.java b/core/java/android/view/textclassifier/TextClassificationManager.java
index ed862064be67..10c7adef28fd 100644
--- a/core/java/android/view/textclassifier/TextClassificationManager.java
+++ b/core/java/android/view/textclassifier/TextClassificationManager.java
@@ -73,9 +73,16 @@ public final class TextClassificationManager {
/**
* Returns the text classifier that was set via {@link #setTextClassifier(TextClassifier)}.
* If this is null, this method returns a default text classifier (i.e. either the system text
- * classifier if one exists, or a local text classifier running in this app.)
+ * classifier if one exists, or a local text classifier running in this process.)
+ * <p>
+ * Note that if system textclassifier is in use, requests will be sent to a textclassifier
+ * package provided from OEM. If you want to make sure the requests are handled in your own
+ * process, you should consider {@link #getLocalTextClassifier()} instead. However, the local
+ * textclassifier may return inferior results to those returned by the system
+ * textclassifier.
*
* @see #setTextClassifier(TextClassifier)
+ * @see #getLocalTextClassifier()
*/
@NonNull
public TextClassifier getTextClassifier() {
@@ -215,7 +222,13 @@ public final class TextClassificationManager {
return TextClassifier.NO_OP;
}
- private TextClassifier getLocalTextClassifier() {
+ /**
+ * Returns a local textclassifier, which is running in this process.
+ *
+ * @see #getTextClassifier()
+ */
+ @NonNull
+ public TextClassifier getLocalTextClassifier() {
synchronized (mLock) {
if (mLocalTextClassifier == null) {
if (getSettings().isLocalTextClassifierEnabled()) {
diff --git a/core/java/android/view/textclassifier/TextClassifierEvent.java b/core/java/android/view/textclassifier/TextClassifierEvent.java
index b84f6f07e414..cd13cc0ec577 100644
--- a/core/java/android/view/textclassifier/TextClassifierEvent.java
+++ b/core/java/android/view/textclassifier/TextClassifierEvent.java
@@ -72,7 +72,7 @@ public final class TextClassifierEvent implements Parcelable {
TYPE_ACTIONS_SHOWN, TYPE_LINK_CLICKED, TYPE_OVERTYPE, TYPE_COPY_ACTION,
TYPE_PASTE_ACTION, TYPE_CUT_ACTION, TYPE_SHARE_ACTION, TYPE_SMART_ACTION,
TYPE_SELECTION_DRAG, TYPE_SELECTION_DESTROYED, TYPE_OTHER_ACTION, TYPE_SELECT_ALL,
- TYPE_SELECTION_RESET, TYPE_MANUAL_REPLY})
+ TYPE_SELECTION_RESET, TYPE_MANUAL_REPLY, TYPE_ACTIONS_GENERATED})
public @interface Type {
// For custom event types, use range 1,000,000+.
}
@@ -121,7 +121,7 @@ public final class TextClassifierEvent implements Parcelable {
@Category private final int mEventCategory;
@Type private final int mEventType;
- @Nullable private final String mEntityType;
+ @Nullable private final String[] mEntityTypes;
@Nullable private final TextClassificationContext mEventContext;
@Nullable private final String mResultId;
private final int mEventIndex;
@@ -139,11 +139,12 @@ public final class TextClassifierEvent implements Parcelable {
// Language detection.
@Nullable private final String mLanguage;
+ private final float mScore;
private TextClassifierEvent(
int eventCategory,
int eventType,
- String entityType,
+ String[] entityTypes,
TextClassificationContext eventContext,
String resultId,
int eventIndex,
@@ -154,10 +155,11 @@ public final class TextClassifierEvent implements Parcelable {
int relativeSuggestedWordStartIndex,
int relativeSuggestedWordEndIndex,
int[] actionIndex,
- String language) {
+ String language,
+ float score) {
mEventCategory = eventCategory;
mEventType = eventType;
- mEntityType = entityType;
+ mEntityTypes = entityTypes;
mEventContext = eventContext;
mResultId = resultId;
mEventIndex = eventIndex;
@@ -169,6 +171,7 @@ public final class TextClassifierEvent implements Parcelable {
mRelativeSuggestedWordEndIndex = relativeSuggestedWordEndIndex;
mActionIndices = actionIndex;
mLanguage = language;
+ mScore = score;
}
@Override
@@ -180,7 +183,7 @@ public final class TextClassifierEvent implements Parcelable {
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(mEventCategory);
dest.writeInt(mEventType);
- dest.writeString(mEntityType);
+ dest.writeStringArray(mEntityTypes);
dest.writeParcelable(mEventContext, flags);
dest.writeString(mResultId);
dest.writeInt(mEventIndex);
@@ -192,13 +195,14 @@ public final class TextClassifierEvent implements Parcelable {
dest.writeInt(mRelativeSuggestedWordEndIndex);
dest.writeIntArray(mActionIndices);
dest.writeString(mLanguage);
+ dest.writeFloat(mScore);
}
private static TextClassifierEvent readFromParcel(Parcel in) {
return new TextClassifierEvent(
/* eventCategory= */ in.readInt(),
/* eventType= */ in.readInt(),
- /* entityType= */ in.readString(),
+ /* entityTypes=*/ in.readStringArray(),
/* eventContext= */ in.readParcelable(null),
/* resultId= */ in.readString(),
/* eventIndex= */ in.readInt(),
@@ -209,7 +213,8 @@ public final class TextClassifierEvent implements Parcelable {
/* relativeSuggestedWordStartIndex= */ in.readInt(),
/* relativeSuggestedWordEndIndex= */ in.readInt(),
/* actionIndices= */ in.createIntArray(),
- /* language= */ in.readString());
+ /* language= */ in.readString(),
+ /* score= */ in.readFloat());
}
/**
@@ -229,11 +234,11 @@ public final class TextClassifierEvent implements Parcelable {
}
/**
- * Returns the entity type. e.g. {@link TextClassifier#TYPE_ADDRESS}.
+ * Returns an array of entity types. e.g. {@link TextClassifier#TYPE_ADDRESS}.
*/
- @Nullable
- public String getEntityType() {
- return mEntityType;
+ @NonNull
+ public String[] getEntityTypes() {
+ return mEntityTypes;
}
/**
@@ -327,13 +332,20 @@ public final class TextClassifierEvent implements Parcelable {
}
/**
+ * Returns the score of the suggestion.
+ */
+ public float getScore() {
+ return mScore;
+ }
+
+ /**
* Builder to build a text classifier event.
*/
public static final class Builder {
private final int mEventCategory;
private final int mEventType;
- @Nullable private String mEntityType;
+ private String[] mEntityTypes = new String[0];
@Nullable private TextClassificationContext mEventContext;
@Nullable private String mResultId;
private int mEventIndex;
@@ -345,6 +357,7 @@ public final class TextClassifierEvent implements Parcelable {
private int mRelativeSuggestedWordEndIndex;
private int[] mActionIndices = new int[0];
@Nullable private String mLanguage;
+ private float mScore;
/**
* Creates a builder for building {@link TextClassifierEvent}s.
@@ -358,11 +371,12 @@ public final class TextClassifierEvent implements Parcelable {
}
/**
- * Sets the entity type. e.g. {@link TextClassifier#TYPE_ADDRESS}.
+ * Sets the entity types. e.g. {@link TextClassifier#TYPE_ADDRESS}.
*/
@NonNull
- public Builder setEntityType(@Nullable String entityType) {
- mEntityType = entityType;
+ public Builder setEntityTypes(@NonNull String... entityTypes) {
+ mEntityTypes = new String[entityTypes.length];
+ System.arraycopy(entityTypes, 0, mEntityTypes, 0, entityTypes.length);
return this;
}
@@ -478,6 +492,15 @@ public final class TextClassifierEvent implements Parcelable {
}
/**
+ * Sets the score of the suggestion.
+ */
+ @NonNull
+ public Builder setScore(float score) {
+ mScore = score;
+ return this;
+ }
+
+ /**
* Builds and returns a text classifier event.
*/
@NonNull
@@ -486,7 +509,7 @@ public final class TextClassifierEvent implements Parcelable {
return new TextClassifierEvent(
mEventCategory,
mEventType,
- mEntityType,
+ mEntityTypes,
mEventContext,
mResultId,
mEventIndex,
@@ -497,7 +520,8 @@ public final class TextClassifierEvent implements Parcelable {
mRelativeSuggestedWordStartIndex,
mRelativeSuggestedWordEndIndex,
mActionIndices,
- mLanguage);
+ mLanguage,
+ mScore);
}
// TODO: Add build(boolean validate).
}
@@ -507,7 +531,7 @@ public final class TextClassifierEvent implements Parcelable {
StringBuilder out = new StringBuilder(128);
out.append("TextClassifierEvent{");
out.append("mEventCategory=").append(mEventCategory);
- out.append(", mEventType=").append(mEventType);
+ out.append(", mEventTypes=").append(Arrays.toString(mEntityTypes));
out.append(", mEventContext=").append(mEventContext);
out.append(", mResultId=").append(mResultId);
out.append(", mEventIndex=").append(mEventIndex);
@@ -519,6 +543,7 @@ public final class TextClassifierEvent implements Parcelable {
out.append(", mRelativeSuggestedWordEndIndex=").append(mRelativeSuggestedWordEndIndex);
out.append(", mActionIndices=").append(Arrays.toString(mActionIndices));
out.append(", mLanguage=").append(mLanguage);
+ out.append(", mScore=").append(mScore);
out.append("}");
return out.toString();
}
diff --git a/core/java/android/view/textclassifier/TextClassifierEventTronLogger.java b/core/java/android/view/textclassifier/TextClassifierEventTronLogger.java
index 439e594cc8fe..5563dfc2eee5 100644
--- a/core/java/android/view/textclassifier/TextClassifierEventTronLogger.java
+++ b/core/java/android/view/textclassifier/TextClassifierEventTronLogger.java
@@ -15,12 +15,15 @@
*/
package android.view.textclassifier;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_SELECTION_ENTITY_TYPE;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_SELECTION_SESSION_ID;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_SELECTION_WIDGET_TYPE;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_SELECTION_WIDGET_VERSION;
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TEXTCLASSIFIER_MODEL;
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TEXT_CLASSIFIER_EVENT_TIME;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TEXT_CLASSIFIER_FIRST_ENTITY_TYPE;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TEXT_CLASSIFIER_SCORE;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TEXT_CLASSIFIER_SECOND_ENTITY_TYPE;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TEXT_CLASSIFIER_SESSION_ID;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TEXT_CLASSIFIER_THIRD_ENTITY_TYPE;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TEXT_CLASSIFIER_WIDGET_TYPE;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TEXT_CLASSIFIER_WIDGET_VERSION;
import android.metrics.LogMaker;
@@ -60,16 +63,30 @@ public final class TextClassifierEventTronLogger {
return;
}
final LogMaker log = new LogMaker(category)
- .setType(getLogType(event))
- .addTaggedData(FIELD_SELECTION_SESSION_ID, event.getResultId())
+ .setSubtype(getLogType(event))
+ .addTaggedData(FIELD_TEXT_CLASSIFIER_SESSION_ID, event.getResultId())
.addTaggedData(FIELD_TEXT_CLASSIFIER_EVENT_TIME, event.getEventTime())
.addTaggedData(FIELD_TEXTCLASSIFIER_MODEL,
SelectionSessionLogger.SignatureParser.getModelName(event.getResultId()))
- .addTaggedData(FIELD_SELECTION_ENTITY_TYPE, event.getEntityType());
+ .addTaggedData(FIELD_TEXT_CLASSIFIER_SCORE, event.getScore());
+
+ String[] entityTypes = event.getEntityTypes();
+ // TRON does not support a field of list type, and thus workaround by store them
+ // in three separate fields. This is no longer an issue once we have moved to Westworld.
+ if (entityTypes.length >= 1) {
+ log.addTaggedData(FIELD_TEXT_CLASSIFIER_FIRST_ENTITY_TYPE, entityTypes[0]);
+ }
+ if (entityTypes.length >= 2) {
+ log.addTaggedData(FIELD_TEXT_CLASSIFIER_SECOND_ENTITY_TYPE, entityTypes[1]);
+ }
+ if (entityTypes.length >= 3) {
+ log.addTaggedData(FIELD_TEXT_CLASSIFIER_THIRD_ENTITY_TYPE, entityTypes[2]);
+ }
TextClassificationContext eventContext = event.getEventContext();
if (eventContext != null) {
- log.addTaggedData(FIELD_SELECTION_WIDGET_TYPE, eventContext.getWidgetType());
- log.addTaggedData(FIELD_SELECTION_WIDGET_VERSION, eventContext.getWidgetVersion());
+ log.addTaggedData(FIELD_TEXT_CLASSIFIER_WIDGET_TYPE, eventContext.getWidgetType());
+ log.addTaggedData(FIELD_TEXT_CLASSIFIER_WIDGET_VERSION,
+ eventContext.getWidgetVersion());
log.setPackageName(eventContext.getPackageName());
}
mMetricsLogger.write(log);
@@ -94,6 +111,8 @@ public final class TextClassifierEventTronLogger {
return MetricsEvent.ACTION_TEXT_CLASSIFIER_ACTIONS_SHOWN;
case TextClassifierEvent.TYPE_MANUAL_REPLY:
return MetricsEvent.ACTION_TEXT_CLASSIFIER_MANUAL_REPLY;
+ case TextClassifierEvent.TYPE_ACTIONS_GENERATED:
+ return MetricsEvent.ACTION_TEXT_CLASSIFIER_ACTIONS_GENERATED;
default:
return MetricsEvent.VIEW_UNKNOWN;
}
@@ -127,14 +146,22 @@ public final class TextClassifierEventTronLogger {
if (!Log.ENABLE_FULL_LOGGING) {
return;
}
- final String id = String.valueOf(log.getTaggedData(FIELD_SELECTION_SESSION_ID));
+ final String id = String.valueOf(log.getTaggedData(FIELD_TEXT_CLASSIFIER_SESSION_ID));
final String categoryName = toCategoryName(log.getCategory());
- final String eventName = toEventName(log.getType());
- final String widgetType = String.valueOf(log.getTaggedData(FIELD_SELECTION_WIDGET_TYPE));
+ final String eventName = toEventName(log.getSubtype());
+ final String widgetType =
+ String.valueOf(log.getTaggedData(FIELD_TEXT_CLASSIFIER_WIDGET_TYPE));
final String widgetVersion =
- String.valueOf(log.getTaggedData(FIELD_SELECTION_WIDGET_VERSION));
+ String.valueOf(log.getTaggedData(FIELD_TEXT_CLASSIFIER_WIDGET_VERSION));
final String model = String.valueOf(log.getTaggedData(FIELD_TEXTCLASSIFIER_MODEL));
- final String entityType = String.valueOf(log.getTaggedData(FIELD_SELECTION_ENTITY_TYPE));
+ final String firstEntityType =
+ String.valueOf(log.getTaggedData(FIELD_TEXT_CLASSIFIER_FIRST_ENTITY_TYPE));
+ final String secondEntityType =
+ String.valueOf(log.getTaggedData(FIELD_TEXT_CLASSIFIER_SECOND_ENTITY_TYPE));
+ final String thirdEntityType =
+ String.valueOf(log.getTaggedData(FIELD_TEXT_CLASSIFIER_THIRD_ENTITY_TYPE));
+ final String score =
+ String.valueOf(log.getTaggedData(FIELD_TEXT_CLASSIFIER_SCORE));
StringBuilder builder = new StringBuilder();
builder.append("writeEvent: ");
@@ -144,7 +171,10 @@ public final class TextClassifierEventTronLogger {
builder.append(", widgetType=").append(widgetType);
builder.append(", widgetVersion=").append(widgetVersion);
builder.append(", model=").append(model);
- builder.append(", entityType=").append(entityType);
+ builder.append(", firstEntityType=").append(firstEntityType);
+ builder.append(", secondEntityType=").append(secondEntityType);
+ builder.append(", thirdEntityType=").append(thirdEntityType);
+ builder.append(", score=").append(score);
Log.v(TAG, builder.toString());
}
diff --git a/core/java/android/view/textclassifier/TextClassifierImpl.java b/core/java/android/view/textclassifier/TextClassifierImpl.java
index 9ab963e372b7..a5b7c621be38 100644
--- a/core/java/android/view/textclassifier/TextClassifierImpl.java
+++ b/core/java/android/view/textclassifier/TextClassifierImpl.java
@@ -78,6 +78,9 @@ import java.util.concurrent.TimeUnit;
*/
public final class TextClassifierImpl implements TextClassifier {
+ /** @hide */
+ public static final String ACTIONS_INTENTS = "actions-intents";
+
private static final String LOG_TAG = DEFAULT_LOG_TAG;
private static final boolean DEBUG = false;
@@ -567,6 +570,7 @@ public final class TextClassifierImpl implements TextClassifier {
// TODO: Make this configurable.
final float foreignTextThreshold = typeCount == 0 ? 0.5f : 0.7f;
boolean isPrimaryAction = true;
+ final ArrayList<Intent> sourceIntents = new ArrayList<>();
for (LabeledIntent labeledIntent : IntentFactory.create(
mContext, classifiedText, isForeignText(classifiedText, foreignTextThreshold),
referenceTime, highestScoringResult)) {
@@ -586,9 +590,15 @@ public final class TextClassifierImpl implements TextClassifier {
isPrimaryAction = false;
}
builder.addAction(action);
+ sourceIntents.add(labeledIntent.getIntent());
}
- return builder.setId(createId(text, start, end)).build();
+ final Bundle extras = new Bundle();
+ extras.putParcelableArrayList(ACTIONS_INTENTS, sourceIntents);
+
+ return builder.setId(createId(text, start, end))
+ .setExtras(extras)
+ .build();
}
private boolean isForeignText(String text, float threshold) {
diff --git a/core/java/android/webkit/WebViewZygote.java b/core/java/android/webkit/WebViewZygote.java
index 29b3b3cff044..de1f3df61462 100644
--- a/core/java/android/webkit/WebViewZygote.java
+++ b/core/java/android/webkit/WebViewZygote.java
@@ -150,7 +150,8 @@ public class WebViewZygote {
}
try {
- sZygote = Process.zygoteProcess.startChildZygote(
+ String abi = sPackage.applicationInfo.primaryCpuAbi;
+ sZygote = Process.ZYGOTE_PROCESS.startChildZygote(
"com.android.internal.os.WebViewZygoteInit",
"webview_zygote",
Process.WEBVIEW_ZYGOTE_UID,
@@ -158,39 +159,40 @@ public class WebViewZygote {
null, // gids
0, // runtimeFlags
"webview_zygote", // seInfo
- sPackage.applicationInfo.primaryCpuAbi, // abi
+ abi, // abi
TextUtils.join(",", Build.SUPPORTED_ABIS),
null, // instructionSet
Process.FIRST_ISOLATED_UID,
Process.LAST_ISOLATED_UID);
-
- // All the work below is usually done by LoadedApk, but the zygote can't talk to
- // PackageManager or construct a LoadedApk since it's single-threaded pre-fork, so
- // doesn't have an ActivityThread and can't use Binder.
- // Instead, figure out the paths here, in the system server where we have access to
- // the package manager. Reuse the logic from LoadedApk to determine the correct
- // paths and pass them to the zygote as strings.
- final List<String> zipPaths = new ArrayList<>(10);
- final List<String> libPaths = new ArrayList<>(10);
- LoadedApk.makePaths(null, false, sPackage.applicationInfo, zipPaths, libPaths);
- final String librarySearchPath = TextUtils.join(File.pathSeparator, libPaths);
- final String zip = (zipPaths.size() == 1) ? zipPaths.get(0) :
- TextUtils.join(File.pathSeparator, zipPaths);
-
- String libFileName = WebViewFactory.getWebViewLibrary(sPackage.applicationInfo);
-
- // In the case where the ApplicationInfo has been modified by the stub WebView,
- // we need to use the original ApplicationInfo to determine what the original classpath
- // would have been to use as a cache key.
- LoadedApk.makePaths(null, false, sPackageOriginalAppInfo, zipPaths, null);
- final String cacheKey = (zipPaths.size() == 1) ? zipPaths.get(0) :
- TextUtils.join(File.pathSeparator, zipPaths);
-
ZygoteProcess.waitForConnectionToZygote(sZygote.getPrimarySocketAddress());
- Log.d(LOGTAG, "Preloading package " + zip + " " + librarySearchPath);
- sZygote.preloadPackageForAbi(zip, librarySearchPath, libFileName, cacheKey,
- Build.SUPPORTED_ABIS[0]);
+ if (sPackageOriginalAppInfo.sourceDir.equals(sPackage.applicationInfo.sourceDir)) {
+ // No stub WebView is involved here, so we can preload the package the "clean" way
+ // using the ApplicationInfo.
+ sZygote.preloadApp(sPackage.applicationInfo, abi);
+ } else {
+ // Legacy path to support the stub WebView.
+ // Reuse the logic from LoadedApk to determine the correct paths and pass them to
+ // the zygote as strings.
+ final List<String> zipPaths = new ArrayList<>(10);
+ final List<String> libPaths = new ArrayList<>(10);
+ LoadedApk.makePaths(null, false, sPackage.applicationInfo, zipPaths, libPaths);
+ final String librarySearchPath = TextUtils.join(File.pathSeparator, libPaths);
+ final String zip = (zipPaths.size() == 1) ? zipPaths.get(0) :
+ TextUtils.join(File.pathSeparator, zipPaths);
+
+ String libFileName = WebViewFactory.getWebViewLibrary(sPackage.applicationInfo);
+
+ // Use the original ApplicationInfo to determine what the original classpath would
+ // have been to use as a cache key.
+ LoadedApk.makePaths(null, false, sPackageOriginalAppInfo, zipPaths, null);
+ final String cacheKey = (zipPaths.size() == 1) ? zipPaths.get(0) :
+ TextUtils.join(File.pathSeparator, zipPaths);
+
+ Log.d(LOGTAG, "Preloading package " + zip + " " + librarySearchPath);
+ sZygote.preloadPackageForAbi(zip, librarySearchPath, libFileName, cacheKey,
+ Build.SUPPORTED_ABIS[0]);
+ }
} catch (Exception e) {
Log.e(LOGTAG, "Error connecting to webview zygote", e);
stopZygoteLocked();
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 1085e5dadb42..780fe8d821d6 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -5117,7 +5117,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
* @attr ref android.R.styleable#TextView_scrollHorizontally
* @see #setHorizontallyScrolling(boolean)
*/
- public final boolean isHorizontallyScrolling() {
+ public final boolean isHorizontallyScrollable() {
return mHorizontallyScrolling;
}
diff --git a/core/java/com/android/internal/app/AssistUtils.java b/core/java/com/android/internal/app/AssistUtils.java
index 7c371cb18878..d0102a72e703 100644
--- a/core/java/com/android/internal/app/AssistUtils.java
+++ b/core/java/com/android/internal/app/AssistUtils.java
@@ -17,13 +17,10 @@
package com.android.internal.app;
import android.annotation.NonNull;
-import android.app.SearchManager;
import android.content.ComponentName;
import android.content.Context;
-import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
@@ -31,8 +28,6 @@ import android.os.ServiceManager;
import android.provider.Settings;
import android.util.Log;
-import com.android.internal.R;
-
import java.util.ArrayList;
import java.util.Set;
@@ -44,14 +39,6 @@ public class AssistUtils {
private static final String TAG = "AssistUtils";
- /**
- * Sentinel value for "no default assistant specified."
- *
- * Empty string is already used to represent an explicit setting of No Assistant. null cannot
- * be used because we can't represent a null value in XML.
- */
- private static final String UNSET = "#+UNSET";
-
private final Context mContext;
private final IVoiceInteractionManagerService mVoiceInteractionManagerService;
@@ -186,37 +173,9 @@ public class AssistUtils {
Settings.Secure.ASSISTANT, userId);
if (setting != null) {
return ComponentName.unflattenFromString(setting);
- }
-
- final String defaultSetting = mContext.getResources().getString(
- R.string.config_defaultAssistantComponentName);
- if (defaultSetting != null && !defaultSetting.equals(UNSET)) {
- return ComponentName.unflattenFromString(defaultSetting);
- }
-
- // Fallback to keep backward compatible behavior when there is no user setting.
- if (activeServiceSupportsAssistGesture()) {
- return getActiveServiceComponentName();
- }
-
- if (UNSET.equals(defaultSetting)) {
+ } else {
return null;
}
-
- final SearchManager searchManager =
- (SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE);
- if (searchManager == null) {
- return null;
- }
- final Intent intent = searchManager.getAssistIntent(false);
- PackageManager pm = mContext.getPackageManager();
- ResolveInfo info = pm.resolveActivityAsUser(intent, PackageManager.MATCH_DEFAULT_ONLY,
- userId);
- if (info != null) {
- return new ComponentName(info.activityInfo.applicationInfo.packageName,
- info.activityInfo.name);
- }
- return null;
}
public static boolean isPreinstalledAssistant(Context context, ComponentName assistant) {
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index b4d8322c7552..30137e3893ff 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -103,7 +103,9 @@ public class ChooserActivity extends ResolverActivity {
* binding to every ChooserTargetService implementation.
*/
// TODO(b/121287573): Replace with a system flag (setprop?)
- private static final boolean USE_SHORTCUT_MANAGER_FOR_DIRECT_TARGETS = false;
+ private static final boolean USE_SHORTCUT_MANAGER_FOR_DIRECT_TARGETS = true;
+ private static final boolean USE_CHOOSER_TARGET_SERVICE_FOR_DIRECT_TARGETS = true;
+
// TODO(b/121287224): Re-evaluate this limit
private static final int SHARE_TARGET_QUERY_PACKAGE_LIMIT = 20;
@@ -136,6 +138,7 @@ public class ChooserActivity extends ResolverActivity {
private static final int CHOOSER_TARGET_SERVICE_RESULT = 1;
private static final int CHOOSER_TARGET_SERVICE_WATCHDOG_TIMEOUT = 2;
private static final int SHORTCUT_MANAGER_SHARE_TARGET_RESULT = 3;
+ private static final int SHORTCUT_MANAGER_SHARE_TARGET_RESULT_COMPLETED = 4;
private final Handler mChooserHandler = new Handler() {
@Override
@@ -182,6 +185,9 @@ public class ChooserActivity extends ResolverActivity {
mChooserListAdapter.addServiceResults(resultInfo.originalTarget,
resultInfo.resultTargets);
}
+ break;
+
+ case SHORTCUT_MANAGER_SHARE_TARGET_RESULT_COMPLETED:
sendVoiceChoicesIfNeeded();
mChooserListAdapter.setShowServiceTargets(true);
break;
@@ -630,6 +636,7 @@ public class ChooserActivity extends ResolverActivity {
// Match ShareShortcutInfos with DisplayResolveInfos to be able to use the old code path
// for direct share targets. After ShareSheet is refactored we should use the
// ShareShortcutInfos directly.
+ boolean resultMessageSent = false;
for (int i = 0; i < driList.size(); i++) {
List<ChooserTarget> chooserTargets = new ArrayList<>();
for (int j = 0; j < resultList.size(); j++) {
@@ -646,6 +653,13 @@ public class ChooserActivity extends ResolverActivity {
msg.what = SHORTCUT_MANAGER_SHARE_TARGET_RESULT;
msg.obj = new ServiceResultInfo(driList.get(i), chooserTargets, null);
mChooserHandler.sendMessage(msg);
+ resultMessageSent = true;
+ }
+
+ if (resultMessageSent) {
+ final Message msg = Message.obtain();
+ msg.what = SHORTCUT_MANAGER_SHARE_TARGET_RESULT_COMPLETED;
+ mChooserHandler.sendMessage(msg);
}
});
}
@@ -1178,13 +1192,17 @@ public class ChooserActivity extends ResolverActivity {
mTargetsNeedPruning = true;
}
}
+
if (USE_SHORTCUT_MANAGER_FOR_DIRECT_TARGETS) {
if (DEBUG) {
Log.d(TAG, "querying direct share targets from ShortcutManager");
}
queryDirectShareTargets(this);
- } else {
- if (DEBUG) Log.d(TAG, "List built querying services");
+ }
+ if (USE_CHOOSER_TARGET_SERVICE_FOR_DIRECT_TARGETS) {
+ if (DEBUG) {
+ Log.d(TAG, "List built querying services");
+ }
queryTargetServices(this);
}
}
diff --git a/core/java/com/android/internal/app/ColorDisplayController.java b/core/java/com/android/internal/app/ColorDisplayController.java
index c093fe512186..b03fde7a3fad 100644
--- a/core/java/com/android/internal/app/ColorDisplayController.java
+++ b/core/java/com/android/internal/app/ColorDisplayController.java
@@ -22,7 +22,8 @@ import android.app.ActivityManager;
import android.content.ContentResolver;
import android.content.Context;
import android.database.ContentObserver;
-import android.metrics.LogMaker;
+import android.hardware.display.ColorDisplayManager;
+import android.hardware.display.ColorDisplayManager.AutoMode;
import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
@@ -32,12 +33,9 @@ import android.provider.Settings.System;
import android.util.Slog;
import com.android.internal.R;
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-import java.time.LocalDateTime;
import java.time.LocalTime;
/**
@@ -52,33 +50,7 @@ public final class ColorDisplayController {
private static final boolean DEBUG = false;
@Retention(RetentionPolicy.SOURCE)
- @IntDef({ AUTO_MODE_DISABLED, AUTO_MODE_CUSTOM, AUTO_MODE_TWILIGHT })
- public @interface AutoMode {}
-
- /**
- * Auto mode value to prevent Night display from being automatically activated. It can still
- * be activated manually via {@link #setActivated(boolean)}.
- *
- * @see #setAutoMode(int)
- */
- public static final int AUTO_MODE_DISABLED = 0;
- /**
- * Auto mode value to automatically activate Night display at a specific start and end time.
- *
- * @see #setAutoMode(int)
- * @see #setCustomStartTime(LocalTime)
- * @see #setCustomEndTime(LocalTime)
- */
- public static final int AUTO_MODE_CUSTOM = 1;
- /**
- * Auto mode value to automatically activate Night display from sunset to sunrise.
- *
- * @see #setAutoMode(int)
- */
- public static final int AUTO_MODE_TWILIGHT = 2;
-
- @Retention(RetentionPolicy.SOURCE)
- @IntDef({ COLOR_MODE_NATURAL, COLOR_MODE_BOOSTED, COLOR_MODE_SATURATED, COLOR_MODE_AUTOMATIC })
+ @IntDef({COLOR_MODE_NATURAL, COLOR_MODE_BOOSTED, COLOR_MODE_SATURATED, COLOR_MODE_AUTOMATIC})
public @interface ColorMode {}
/**
@@ -108,10 +80,10 @@ public final class ColorDisplayController {
private final Context mContext;
private final int mUserId;
+ private final ColorDisplayManager mColorDisplayManager;
private ContentObserver mContentObserver;
private Callback mCallback;
- private MetricsLogger mMetricsLogger;
public ColorDisplayController(@NonNull Context context) {
this(context, ActivityManager.getCurrentUser());
@@ -120,14 +92,14 @@ public final class ColorDisplayController {
public ColorDisplayController(@NonNull Context context, int userId) {
mContext = context.getApplicationContext();
mUserId = userId;
+ mColorDisplayManager = mContext.getSystemService(ColorDisplayManager.class);
}
/**
* Returns {@code true} when Night display is activated (the display is tinted red).
*/
public boolean isActivated() {
- return Secure.getIntForUser(mContext.getContentResolver(),
- Secure.NIGHT_DISPLAY_ACTIVATED, 0, mUserId) == 1;
+ return mColorDisplayManager.isNightDisplayActivated();
}
/**
@@ -137,40 +109,16 @@ public final class ColorDisplayController {
* @return {@code true} if the activated value was set successfully
*/
public boolean setActivated(boolean activated) {
- if (isActivated() != activated) {
- Secure.putStringForUser(mContext.getContentResolver(),
- Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME,
- LocalDateTime.now().toString(),
- mUserId);
- }
- return Secure.putIntForUser(mContext.getContentResolver(),
- Secure.NIGHT_DISPLAY_ACTIVATED, activated ? 1 : 0, mUserId);
+ return mColorDisplayManager.setNightDisplayActivated(activated);
}
/**
* Returns the current auto mode value controlling when Night display will be automatically
- * activated. One of {@link #AUTO_MODE_DISABLED}, {@link #AUTO_MODE_CUSTOM}, or
- * {@link #AUTO_MODE_TWILIGHT}.
+ * activated. One of {@link ColorDisplayManager#AUTO_MODE_DISABLED}, {@link
+ * ColorDisplayManager#AUTO_MODE_CUSTOM_TIME} or {@link ColorDisplayManager#AUTO_MODE_TWILIGHT}.
*/
public @AutoMode int getAutoMode() {
- int autoMode = Secure.getIntForUser(mContext.getContentResolver(),
- Secure.NIGHT_DISPLAY_AUTO_MODE, -1, mUserId);
- if (autoMode == -1) {
- if (DEBUG) {
- Slog.d(TAG, "Using default value for setting: " + Secure.NIGHT_DISPLAY_AUTO_MODE);
- }
- autoMode = mContext.getResources().getInteger(
- R.integer.config_defaultNightDisplayAutoMode);
- }
-
- if (autoMode != AUTO_MODE_DISABLED
- && autoMode != AUTO_MODE_CUSTOM
- && autoMode != AUTO_MODE_TWILIGHT) {
- Slog.e(TAG, "Invalid autoMode: " + autoMode);
- autoMode = AUTO_MODE_DISABLED;
- }
-
- return autoMode;
+ return mColorDisplayManager.getNightDisplayAutoMode();
}
/**
@@ -178,138 +126,64 @@ public final class ColorDisplayController {
* never been set.
*/
public int getAutoModeRaw() {
- return Secure.getIntForUser(mContext.getContentResolver(), Secure.NIGHT_DISPLAY_AUTO_MODE,
- -1, mUserId);
+ return mColorDisplayManager.getNightDisplayAutoModeRaw();
}
/**
* Sets the current auto mode value controlling when Night display will be automatically
- * activated. One of {@link #AUTO_MODE_DISABLED}, {@link #AUTO_MODE_CUSTOM}, or
- * {@link #AUTO_MODE_TWILIGHT}.
+ * activated. One of {@link ColorDisplayManager#AUTO_MODE_DISABLED}, {@link
+ * ColorDisplayManager#AUTO_MODE_CUSTOM_TIME} or {@link ColorDisplayManager#AUTO_MODE_TWILIGHT}.
*
* @param autoMode the new auto mode to use
* @return {@code true} if new auto mode was set successfully
*/
public boolean setAutoMode(@AutoMode int autoMode) {
- if (autoMode != AUTO_MODE_DISABLED
- && autoMode != AUTO_MODE_CUSTOM
- && autoMode != AUTO_MODE_TWILIGHT) {
- throw new IllegalArgumentException("Invalid autoMode: " + autoMode);
- }
-
- if (getAutoMode() != autoMode) {
- Secure.putStringForUser(mContext.getContentResolver(),
- Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME,
- null,
- mUserId);
- getMetricsLogger().write(new LogMaker(
- MetricsEvent.ACTION_NIGHT_DISPLAY_AUTO_MODE_CHANGED)
- .setType(MetricsEvent.TYPE_ACTION)
- .setSubtype(autoMode));
- }
-
- return Secure.putIntForUser(mContext.getContentResolver(),
- Secure.NIGHT_DISPLAY_AUTO_MODE, autoMode, mUserId);
+ return mColorDisplayManager.setNightDisplayAutoMode(autoMode);
}
/**
- * Returns the local time when Night display will be automatically activated when using
- * {@link #AUTO_MODE_CUSTOM}.
+ * Returns the local time when Night display will be automatically activated when using {@link
+ * ColorDisplayManager#AUTO_MODE_CUSTOM_TIME}.
*/
public @NonNull LocalTime getCustomStartTime() {
- int startTimeValue = Secure.getIntForUser(mContext.getContentResolver(),
- Secure.NIGHT_DISPLAY_CUSTOM_START_TIME, -1, mUserId);
- if (startTimeValue == -1) {
- if (DEBUG) {
- Slog.d(TAG, "Using default value for setting: "
- + Secure.NIGHT_DISPLAY_CUSTOM_START_TIME);
- }
- startTimeValue = mContext.getResources().getInteger(
- R.integer.config_defaultNightDisplayCustomStartTime);
- }
-
- return LocalTime.ofSecondOfDay(startTimeValue / 1000);
+ return mColorDisplayManager.getNightDisplayCustomStartTime();
}
/**
- * Sets the local time when Night display will be automatically activated when using
- * {@link #AUTO_MODE_CUSTOM}.
+ * Sets the local time when Night display will be automatically activated when using {@link
+ * ColorDisplayManager#AUTO_MODE_CUSTOM_TIME}.
*
* @param startTime the local time to automatically activate Night display
* @return {@code true} if the new custom start time was set successfully
*/
public boolean setCustomStartTime(@NonNull LocalTime startTime) {
- if (startTime == null) {
- throw new IllegalArgumentException("startTime cannot be null");
- }
- getMetricsLogger().write(new LogMaker(
- MetricsEvent.ACTION_NIGHT_DISPLAY_AUTO_MODE_CUSTOM_TIME_CHANGED)
- .setType(MetricsEvent.TYPE_ACTION)
- .setSubtype(0));
- return Secure.putIntForUser(mContext.getContentResolver(),
- Secure.NIGHT_DISPLAY_CUSTOM_START_TIME, startTime.toSecondOfDay() * 1000, mUserId);
+ return mColorDisplayManager.setNightDisplayCustomStartTime(startTime);
}
/**
- * Returns the local time when Night display will be automatically deactivated when using
- * {@link #AUTO_MODE_CUSTOM}.
+ * Returns the local time when Night display will be automatically deactivated when using {@link
+ * ColorDisplayManager#AUTO_MODE_CUSTOM_TIME}.
*/
public @NonNull LocalTime getCustomEndTime() {
- int endTimeValue = Secure.getIntForUser(mContext.getContentResolver(),
- Secure.NIGHT_DISPLAY_CUSTOM_END_TIME, -1, mUserId);
- if (endTimeValue == -1) {
- if (DEBUG) {
- Slog.d(TAG, "Using default value for setting: "
- + Secure.NIGHT_DISPLAY_CUSTOM_END_TIME);
- }
- endTimeValue = mContext.getResources().getInteger(
- R.integer.config_defaultNightDisplayCustomEndTime);
- }
-
- return LocalTime.ofSecondOfDay(endTimeValue / 1000);
+ return mColorDisplayManager.getNightDisplayCustomEndTime();
}
/**
- * Sets the local time when Night display will be automatically deactivated when using
- * {@link #AUTO_MODE_CUSTOM}.
+ * Sets the local time when Night display will be automatically deactivated when using {@link
+ * ColorDisplayManager#AUTO_MODE_CUSTOM_TIME}.
*
* @param endTime the local time to automatically deactivate Night display
* @return {@code true} if the new custom end time was set successfully
*/
public boolean setCustomEndTime(@NonNull LocalTime endTime) {
- if (endTime == null) {
- throw new IllegalArgumentException("endTime cannot be null");
- }
- getMetricsLogger().write(new LogMaker(
- MetricsEvent.ACTION_NIGHT_DISPLAY_AUTO_MODE_CUSTOM_TIME_CHANGED)
- .setType(MetricsEvent.TYPE_ACTION)
- .setSubtype(1));
- return Secure.putIntForUser(mContext.getContentResolver(),
- Secure.NIGHT_DISPLAY_CUSTOM_END_TIME, endTime.toSecondOfDay() * 1000, mUserId);
+ return mColorDisplayManager.setNightDisplayCustomEndTime(endTime);
}
/**
* Returns the color temperature (in Kelvin) to tint the display when activated.
*/
public int getColorTemperature() {
- int colorTemperature = Secure.getIntForUser(mContext.getContentResolver(),
- Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE, -1, mUserId);
- if (colorTemperature == -1) {
- if (DEBUG) {
- Slog.d(TAG, "Using default value for setting: "
- + Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE);
- }
- colorTemperature = getDefaultColorTemperature();
- }
- final int minimumTemperature = getMinimumColorTemperature();
- final int maximumTemperature = getMaximumColorTemperature();
- if (colorTemperature < minimumTemperature) {
- colorTemperature = minimumTemperature;
- } else if (colorTemperature > maximumTemperature) {
- colorTemperature = maximumTemperature;
- }
-
- return colorTemperature;
+ return mColorDisplayManager.getNightDisplayColorTemperature();
}
/**
@@ -319,8 +193,7 @@ public final class ColorDisplayController {
* @return {@code true} if new temperature was set successfully.
*/
public boolean setColorTemperature(int colorTemperature) {
- return Secure.putIntForUser(mContext.getContentResolver(),
- Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE, colorTemperature, mUserId);
+ return mColorDisplayManager.setNightDisplayColorTemperature(colorTemperature);
}
/**
@@ -411,24 +284,14 @@ public final class ColorDisplayController {
* Returns the minimum allowed color temperature (in Kelvin) to tint the display when activated.
*/
public int getMinimumColorTemperature() {
- return mContext.getResources().getInteger(
- R.integer.config_nightDisplayColorTemperatureMin);
+ return ColorDisplayManager.getMinimumColorTemperature(mContext);
}
/**
* Returns the maximum allowed color temperature (in Kelvin) to tint the display when activated.
*/
public int getMaximumColorTemperature() {
- return mContext.getResources().getInteger(
- R.integer.config_nightDisplayColorTemperatureMax);
- }
-
- /**
- * Returns the default color temperature (in Kelvin) to tint the display when activated.
- */
- public int getDefaultColorTemperature() {
- return mContext.getResources().getInteger(
- R.integer.config_nightDisplayColorTemperatureDefault);
+ return ColorDisplayManager.getMaximumColorTemperature(mContext);
}
/**
@@ -526,13 +389,6 @@ public final class ColorDisplayController {
}
}
- private MetricsLogger getMetricsLogger() {
- if (mMetricsLogger == null) {
- mMetricsLogger = new MetricsLogger();
- }
- return mMetricsLogger;
- }
-
/**
* Callback invoked whenever the Night display settings are changed.
*/
diff --git a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
index 5088ccae5c1f..b85488ffe70c 100644
--- a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
+++ b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
@@ -151,4 +151,19 @@ interface IVoiceInteractionManagerService {
*/
void getActiveServiceSupportedActions(in List<String> voiceActions,
in IVoiceActionCheckCallback callback);
+
+ /**
+ * Sets the transcribed voice to the given string.
+ */
+ void setTranscription(String transcription);
+
+ /**
+ * Indicates that the transcription session is finished.
+ */
+ void clearTranscription(boolean immediate);
+
+ /**
+ * Sets the voice state indication based upon the given value.
+ */
+ void setVoiceState(int state);
}
diff --git a/core/java/com/android/internal/app/IVoiceInteractionSessionListener.aidl b/core/java/com/android/internal/app/IVoiceInteractionSessionListener.aidl
index 87749d26e4a0..674ad5b4ab67 100644
--- a/core/java/com/android/internal/app/IVoiceInteractionSessionListener.aidl
+++ b/core/java/com/android/internal/app/IVoiceInteractionSessionListener.aidl
@@ -26,4 +26,20 @@
* Called when a voice session is hidden.
*/
void onVoiceSessionHidden();
+
+ /**
+ * Called when voice assistant transcription has been updated to the given string.
+ */
+ void onTranscriptionUpdate(in String transcription);
+
+ /**
+ * Called when voice transcription is completed.
+ */
+ void onTranscriptionComplete(in boolean immediate);
+
+ /**
+ * Called when the voice assistant's state has changed. Values are from
+ * VoiceInteractionService's VOICE_STATE* constants.
+ */
+ void onVoiceStateChange(in int state);
} \ No newline at end of file
diff --git a/core/java/com/android/internal/net/NetworkStatsFactory.java b/core/java/com/android/internal/net/NetworkStatsFactory.java
index 9bacf9b6c2b9..f8483461c2d2 100644
--- a/core/java/com/android/internal/net/NetworkStatsFactory.java
+++ b/core/java/com/android/internal/net/NetworkStatsFactory.java
@@ -64,6 +64,9 @@ public class NetworkStatsFactory {
private boolean mUseBpfStats;
+ // A persistent Snapshot since device start for eBPF stats
+ private final NetworkStats mPersistSnapshot;
+
// TODO: only do adjustments in NetworkStatsService and remove this.
/**
* (Stacked interface) -> (base interface) association for all connected ifaces since boot.
@@ -135,6 +138,7 @@ public class NetworkStatsFactory {
mStatsXtIfaceFmt = new File(procRoot, "net/xt_qtaguid/iface_stat_fmt");
mStatsXtUid = new File(procRoot, "net/xt_qtaguid/stats");
mUseBpfStats = useBpfStats;
+ mPersistSnapshot = new NetworkStats(SystemClock.elapsedRealtime(), -1);
}
public NetworkStats readBpfNetworkStatsDev() throws IOException {
@@ -268,6 +272,7 @@ public class NetworkStatsFactory {
return stats;
}
+ // TODO: delete the lastStats parameter
private NetworkStats readNetworkStatsDetailInternal(int limitUid, String[] limitIfaces,
int limitTag, NetworkStats lastStats) throws IOException {
if (USE_NATIVE_PARSING) {
@@ -278,16 +283,28 @@ public class NetworkStatsFactory {
} else {
stats = new NetworkStats(SystemClock.elapsedRealtime(), -1);
}
- if (nativeReadNetworkStatsDetail(stats, mStatsXtUid.getAbsolutePath(), limitUid,
- limitIfaces, limitTag, mUseBpfStats) != 0) {
- throw new IOException("Failed to parse network stats");
- }
- if (SANITY_CHECK_NATIVE) {
- final NetworkStats javaStats = javaReadNetworkStatsDetail(mStatsXtUid, limitUid,
- limitIfaces, limitTag);
- assertEquals(javaStats, stats);
+ if (mUseBpfStats) {
+ if (nativeReadNetworkStatsDetail(stats, mStatsXtUid.getAbsolutePath(), UID_ALL,
+ null, TAG_ALL, mUseBpfStats) != 0) {
+ throw new IOException("Failed to parse network stats");
+ }
+ mPersistSnapshot.setElapsedRealtime(stats.getElapsedRealtime());
+ mPersistSnapshot.combineAllValues(stats);
+ NetworkStats result = mPersistSnapshot.clone();
+ result.filter(limitUid, limitIfaces, limitTag);
+ return result;
+ } else {
+ if (nativeReadNetworkStatsDetail(stats, mStatsXtUid.getAbsolutePath(), limitUid,
+ limitIfaces, limitTag, mUseBpfStats) != 0) {
+ throw new IOException("Failed to parse network stats");
+ }
+ if (SANITY_CHECK_NATIVE) {
+ final NetworkStats javaStats = javaReadNetworkStatsDetail(mStatsXtUid, limitUid,
+ limitIfaces, limitTag);
+ assertEquals(javaStats, stats);
+ }
+ return stats;
}
- return stats;
} else {
return javaReadNetworkStatsDetail(mStatsXtUid, limitUid, limitIfaces, limitTag);
}
diff --git a/core/java/com/android/internal/net/VpnConfig.java b/core/java/com/android/internal/net/VpnConfig.java
index fd03b3f16348..da8605e645b4 100644
--- a/core/java/com/android/internal/net/VpnConfig.java
+++ b/core/java/com/android/internal/net/VpnConfig.java
@@ -28,6 +28,7 @@ import android.content.res.Resources;
import android.net.IpPrefix;
import android.net.LinkAddress;
import android.net.Network;
+import android.net.ProxyInfo;
import android.net.RouteInfo;
import android.os.Parcel;
import android.os.Parcelable;
@@ -104,6 +105,7 @@ public class VpnConfig implements Parcelable {
public boolean allowIPv4;
public boolean allowIPv6;
public Network[] underlyingNetworks;
+ public ProxyInfo proxyInfo;
public void updateAllowedFamilies(InetAddress address) {
if (address instanceof Inet4Address) {
@@ -164,6 +166,7 @@ public class VpnConfig implements Parcelable {
out.writeInt(allowIPv4 ? 1 : 0);
out.writeInt(allowIPv6 ? 1 : 0);
out.writeTypedArray(underlyingNetworks, flags);
+ out.writeParcelable(proxyInfo, flags);
}
public static final Parcelable.Creator<VpnConfig> CREATOR =
@@ -189,6 +192,7 @@ public class VpnConfig implements Parcelable {
config.allowIPv4 = in.readInt() != 0;
config.allowIPv6 = in.readInt() != 0;
config.underlyingNetworks = in.createTypedArray(Network.CREATOR);
+ config.proxyInfo = in.readParcelable(null);
return config;
}
@@ -220,6 +224,7 @@ public class VpnConfig implements Parcelable {
.append(", allowIPv4=").append(allowIPv4)
.append(", allowIPv6=").append(allowIPv6)
.append(", underlyingNetworks=").append(Arrays.toString(underlyingNetworks))
+ .append(", proxyInfo=").append(proxyInfo.toString())
.append("}")
.toString();
}
diff --git a/core/java/com/android/internal/net/VpnInfo.java b/core/java/com/android/internal/net/VpnInfo.java
index a676dacb0c49..b1a412871bd2 100644
--- a/core/java/com/android/internal/net/VpnInfo.java
+++ b/core/java/com/android/internal/net/VpnInfo.java
@@ -32,11 +32,11 @@ public class VpnInfo implements Parcelable {
@Override
public String toString() {
- return "VpnInfo{" +
- "ownerUid=" + ownerUid +
- ", vpnIface='" + vpnIface + '\'' +
- ", primaryUnderlyingIface='" + primaryUnderlyingIface + '\'' +
- '}';
+ return "VpnInfo{"
+ + "ownerUid=" + ownerUid
+ + ", vpnIface='" + vpnIface + '\''
+ + ", primaryUnderlyingIface='" + primaryUnderlyingIface + '\''
+ + '}';
}
@Override
diff --git a/core/java/com/android/internal/os/BinderCallsStats.java b/core/java/com/android/internal/os/BinderCallsStats.java
index 64543522893e..2c272dea073b 100644
--- a/core/java/com/android/internal/os/BinderCallsStats.java
+++ b/core/java/com/android/internal/os/BinderCallsStats.java
@@ -49,7 +49,7 @@ import java.util.function.ToDoubleFunction;
* per thread, uid or call description.
*/
public class BinderCallsStats implements BinderInternal.Observer {
- public static final boolean ENABLED_DEFAULT = false;
+ public static final boolean ENABLED_DEFAULT = true;
public static final boolean DETAILED_TRACKING_DEFAULT = true;
public static final int PERIODIC_SAMPLING_INTERVAL_DEFAULT = 100;
public static final int MAX_BINDER_CALL_STATS_COUNT_DEFAULT = 5000;
diff --git a/core/java/com/android/internal/os/WebViewZygoteInit.java b/core/java/com/android/internal/os/WebViewZygoteInit.java
index 0b329d70f7af..c8d30b27d4dc 100644
--- a/core/java/com/android/internal/os/WebViewZygoteInit.java
+++ b/core/java/com/android/internal/os/WebViewZygoteInit.java
@@ -17,8 +17,9 @@
package com.android.internal.os;
import android.app.ApplicationLoaders;
+import android.app.LoadedApk;
+import android.content.pm.ApplicationInfo;
import android.net.LocalSocket;
-import android.os.Build;
import android.text.TextUtils;
import android.util.Log;
import android.webkit.WebViewFactory;
@@ -66,6 +67,34 @@ class WebViewZygoteInit {
}
@Override
+ protected boolean canPreloadApp() {
+ return true;
+ }
+
+ @Override
+ protected void handlePreloadApp(ApplicationInfo appInfo) {
+ Log.i(TAG, "Beginning application preload for " + appInfo.packageName);
+ LoadedApk loadedApk = new LoadedApk(null, appInfo, null, null, false, true, false);
+ ClassLoader loader = loadedApk.getClassLoader();
+ doPreload(loader, WebViewFactory.getWebViewLibrary(appInfo));
+
+ // Add the APK to the Zygote's list of allowed files for children.
+ Zygote.nativeAllowFileAcrossFork(appInfo.sourceDir);
+ if (appInfo.splitSourceDirs != null) {
+ for (String path : appInfo.splitSourceDirs) {
+ Zygote.nativeAllowFileAcrossFork(path);
+ }
+ }
+ if (appInfo.sharedLibraryFiles != null) {
+ for (String path : appInfo.sharedLibraryFiles) {
+ Zygote.nativeAllowFileAcrossFork(path);
+ }
+ }
+
+ Log.i(TAG, "Application preload done");
+ }
+
+ @Override
protected void handlePreloadPackage(String packagePath, String libsPath, String libFileName,
String cacheKey) {
Log.i(TAG, "Beginning package preload");
@@ -76,16 +105,22 @@ class WebViewZygoteInit {
ClassLoader loader = ApplicationLoaders.getDefault().createAndCacheWebViewClassLoader(
packagePath, libsPath, cacheKey);
- // Load the native library using WebViewLibraryLoader to share the RELRO data with other
- // processes.
- WebViewLibraryLoader.loadNativeLibrary(loader, libFileName);
-
// Add the APK to the Zygote's list of allowed files for children.
String[] packageList = TextUtils.split(packagePath, File.pathSeparator);
for (String packageEntry : packageList) {
Zygote.nativeAllowFileAcrossFork(packageEntry);
}
+ doPreload(loader, libFileName);
+
+ Log.i(TAG, "Package preload done");
+ }
+
+ private void doPreload(ClassLoader loader, String libFileName) {
+ // Load the native library using WebViewLibraryLoader to share the RELRO data with other
+ // processes.
+ WebViewLibraryLoader.loadNativeLibrary(loader, libFileName);
+
// Once we have the classloader, look up the WebViewFactoryProvider implementation and
// call preloadInZygote() on it to give it the opportunity to preload the native library
// and perform any other initialisation work that should be shared among the children.
@@ -114,8 +149,6 @@ class WebViewZygoteInit {
} catch (IOException ioe) {
throw new IllegalStateException("Error writing to command socket", ioe);
}
-
- Log.i(TAG, "Package preload done");
}
}
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index b28f4cdd5a87..069413fe6678 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -16,13 +16,33 @@
package com.android.internal.os;
+import static android.system.OsConstants.O_CLOEXEC;
+
+import static com.android.internal.os.ZygoteConnectionConstants.MAX_ZYGOTE_ARGC;
+
+import android.net.Credentials;
+import android.net.LocalServerSocket;
+import android.net.LocalSocket;
+import android.os.FactoryTest;
import android.os.IVold;
+import android.os.Process;
+import android.os.SystemProperties;
import android.os.Trace;
import android.system.ErrnoException;
import android.system.Os;
+import android.util.Log;
import dalvik.system.ZygoteHooks;
+import libcore.io.IoUtils;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayOutputStream;
+import java.io.DataOutputStream;
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.io.InputStreamReader;
+
/** @hide */
public final class Zygote {
/*
@@ -94,6 +114,24 @@ public final class Zygote {
/** Read-write external storage should be mounted instead of package sandbox */
public static final int MOUNT_EXTERNAL_FULL = IVold.REMOUNT_MODE_FULL;
+ /** Number of bytes sent to the Zygote over blastula pipes or the pool event FD */
+ public static final int BLASTULA_MANAGEMENT_MESSAGE_BYTES = 8;
+
+ /**
+ * If the blastula pool should be created and used to start applications.
+ *
+ * Setting this value to false will disable the creation, maintenance, and use of the blastula
+ * pool. When the blastula pool is disabled the application lifecycle will be identical to
+ * previous versions of Android.
+ */
+ public static final boolean BLASTULA_POOL_ENABLED = false;
+
+ /**
+ * File descriptor used for communication between the signal handler and the ZygoteServer poll
+ * loop.
+ * */
+ protected static FileDescriptor sBlastulaPoolEventFD;
+
private static final ZygoteHooks VM_HOOKS = new ZygoteHooks();
/**
@@ -123,6 +161,48 @@ public final class Zygote {
*/
public static final String CHILD_ZYGOTE_UID_RANGE_END = "--uid-range-end=";
+ /** Prefix prepended to socket names created by init */
+ private static final String ANDROID_SOCKET_PREFIX = "ANDROID_SOCKET_";
+
+ /**
+ * The maximum value that the sBlastulaPoolMax variable may take. This value
+ * is a mirror of BLASTULA_POOL_MAX_LIMIT found in com_android_internal_os_Zygote.cpp.
+ */
+ static final int BLASTULA_POOL_MAX_LIMIT = 10;
+
+ /**
+ * The minimum value that the sBlastulaPoolMin variable may take.
+ */
+ static final int BLASTULA_POOL_MIN_LIMIT = 1;
+
+ /**
+ * The runtime-adjustable maximum Blastula pool size.
+ */
+ static int sBlastulaPoolMax = BLASTULA_POOL_MAX_LIMIT;
+
+ /**
+ * The runtime-adjustable minimum Blastula pool size.
+ */
+ static int sBlastulaPoolMin = BLASTULA_POOL_MIN_LIMIT;
+
+ /**
+ * The runtime-adjustable value used to determine when to re-fill the
+ * blastula pool. The pool will be re-filled when
+ * (sBlastulaPoolMax - gBlastulaPoolCount) >= sBlastulaPoolRefillThreshold.
+ */
+ // TODO (chriswailes): This must be updated at the same time as sBlastulaPoolMax.
+ static int sBlastulaPoolRefillThreshold = (sBlastulaPoolMax / 2);
+
+ /**
+ * @hide for internal use only
+ */
+ public static final int SOCKET_BUFFER_SIZE = 256;
+
+ private static LocalServerSocket sBlastulaPoolSocket = null;
+
+ /** a prototype instance for a future List.toArray() */
+ protected static final int[][] INT_ARRAY_2D = new int[0][0];
+
private Zygote() {}
/** Called for some security initialization before any fork. */
@@ -165,14 +245,14 @@ public final class Zygote {
public static int forkAndSpecialize(int uid, int gid, int[] gids, int runtimeFlags,
int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose,
int[] fdsToIgnore, boolean startChildZygote, String instructionSet, String appDataDir,
- String packageName, String[] packagesForUid, String[] visibleVolIds) {
+ String packageName, String[] packagesForUID, String[] visibleVolIDs) {
VM_HOOKS.preFork();
// Resets nice priority for zygote process.
resetNicePriority();
int pid = nativeForkAndSpecialize(
uid, gid, gids, runtimeFlags, rlimits, mountExternal, seInfo, niceName, fdsToClose,
fdsToIgnore, startChildZygote, instructionSet, appDataDir, packageName,
- packagesForUid, visibleVolIds);
+ packagesForUID, visibleVolIDs);
// Enable tracing as soon as possible for the child process.
if (pid == 0) {
Trace.setTracingEnabled(true, runtimeFlags);
@@ -187,12 +267,57 @@ public final class Zygote {
private static native int nativeForkAndSpecialize(int uid, int gid, int[] gids,
int runtimeFlags, int[][] rlimits, int mountExternal, String seInfo, String niceName,
int[] fdsToClose, int[] fdsToIgnore, boolean startChildZygote, String instructionSet,
- String appDataDir, String packageName, String[] packagesForUid, String[] visibleVolIds);
+ String appDataDir, String packageName, String[] packagesForUID, String[] visibleVolIDs);
+
+ /**
+ * Specialize a Blastula instance. The current VM must have been started
+ * with the -Xzygote flag.
+ *
+ * @param uid The UNIX uid that the new process should setuid() to before spawning any threads
+ * @param gid The UNIX gid that the new process should setgid() to before spawning any threads
+ * @param gids null-ok; A list of UNIX gids that the new process should
+ * setgroups() to before spawning any threads
+ * @param runtimeFlags Bit flags that enable ART features
+ * @param rlimits null-ok An array of rlimit tuples, with the second
+ * dimension having a length of 3 and representing
+ * (resource, rlim_cur, rlim_max). These are set via the posix
+ * setrlimit(2) call.
+ * @param seInfo null-ok A string specifying SELinux information for
+ * the new process.
+ * @param niceName null-ok A string specifying the process name.
+ * @param startChildZygote If true, the new child process will itself be a
+ * new zygote process.
+ * @param instructionSet null-ok The instruction set to use.
+ * @param appDataDir null-ok The data directory of the app.
+ */
+ public static void specializeBlastula(int uid, int gid, int[] gids, int runtimeFlags,
+ int[][] rlimits, int mountExternal, String seInfo, String niceName,
+ boolean startChildZygote, String instructionSet, String appDataDir, String packageName,
+ String[] packagesForUID, String[] visibleVolIDs) {
+
+ nativeSpecializeBlastula(uid, gid, gids, runtimeFlags, rlimits, mountExternal, seInfo,
+ niceName, startChildZygote, instructionSet, appDataDir,
+ packageName, packagesForUID, visibleVolIDs);
+
+ // Enable tracing as soon as possible for the child process.
+ Trace.setTracingEnabled(true, runtimeFlags);
+
+ // Note that this event ends at the end of handleChildProc.
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "PostFork");
+
+ /*
+ * This is called here (instead of after the fork but before the specialize) to maintain
+ * consistancy with the code paths for forkAndSpecialize.
+ *
+ * TODO (chriswailes): Look into moving this to immediately after the fork.
+ */
+ VM_HOOKS.postForkCommon();
+ }
private static native void nativeSpecializeBlastula(int uid, int gid, int[] gids,
int runtimeFlags, int[][] rlimits, int mountExternal, String seInfo, String niceName,
boolean startChildZygote, String instructionSet, String appDataDir, String packageName,
- String[] packagesForUid, String[] visibleVolIds);
+ String[] packagesForUID, String[] visibleVolIDs);
/**
* Called to do any initialization before starting an application.
@@ -259,20 +384,483 @@ public final class Zygote {
*/
protected static native void nativeUnmountStorageOnInit();
+ /**
+ * Get socket file descriptors (opened by init) from the environment and
+ * store them for access from native code later.
+ *
+ * @param isPrimary True if this is the zygote process, false if it is zygote_secondary
+ */
+ public static void getSocketFDs(boolean isPrimary) {
+ nativeGetSocketFDs(isPrimary);
+ }
+
protected static native void nativeGetSocketFDs(boolean isPrimary);
+ /**
+ * Initialize the blastula pool and fill it with the desired number of
+ * processes.
+ */
+ protected static Runnable initBlastulaPool() {
+ if (BLASTULA_POOL_ENABLED) {
+ sBlastulaPoolEventFD = getBlastulaPoolEventFD();
+
+ return fillBlastulaPool(null);
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Checks to see if the current policy says that pool should be refilled, and spawns new
+ * blastulas if necessary.
+ *
+ * NOTE: This function doesn't need to be guarded with BLASTULA_POOL_ENABLED because it is
+ * only called from contexts that are only valid if the pool is enabled.
+ *
+ * @param sessionSocketRawFDs Anonymous session sockets that are currently open
+ * @return In the Zygote process this function will always return null; in blastula processes
+ * this function will return a Runnable object representing the new application that is
+ * passed up from blastulaMain.
+ */
+ protected static Runnable fillBlastulaPool(int[] sessionSocketRawFDs) {
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Zygote:FillBlastulaPool");
+
+ int blastulaPoolCount = getBlastulaPoolCount();
+
+ int numBlastulasToSpawn = sBlastulaPoolMax - blastulaPoolCount;
+
+ if (blastulaPoolCount < sBlastulaPoolMin
+ || numBlastulasToSpawn >= sBlastulaPoolRefillThreshold) {
+
+ // Disable some VM functionality and reset some system values
+ // before forking.
+ VM_HOOKS.preFork();
+ resetNicePriority();
+
+ while (blastulaPoolCount++ < sBlastulaPoolMax) {
+ Runnable caller = forkBlastula(sessionSocketRawFDs);
+
+ if (caller != null) {
+ return caller;
+ }
+ }
+
+ // Re-enable runtime services for the Zygote. Blastula services
+ // are re-enabled in specializeBlastula.
+ VM_HOOKS.postForkCommon();
+
+ Log.i("zygote", "Filled the blastula pool. New blastulas: " + numBlastulasToSpawn);
+ }
+
+ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+
+ return null;
+ }
+
+ /**
+ * @return Number of blastulas currently in the pool
+ */
+ private static int getBlastulaPoolCount() {
+ return nativeGetBlastulaPoolCount();
+ }
+
private static native int nativeGetBlastulaPoolCount();
+ /**
+ * @return The event FD used for communication between the signal handler and the ZygoteServer
+ * poll loop
+ */
+ private static FileDescriptor getBlastulaPoolEventFD() {
+ FileDescriptor fd = new FileDescriptor();
+ fd.setInt$(nativeGetBlastulaPoolEventFD());
+
+ return fd;
+ }
+
private static native int nativeGetBlastulaPoolEventFD();
+ /**
+ * Fork a new blastula process from the zygote
+ *
+ * @param sessionSocketRawFDs Anonymous session sockets that are currently open
+ * @return In the Zygote process this function will always return null; in blastula processes
+ * this function will return a Runnable object representing the new application that is
+ * passed up from blastulaMain.
+ */
+ private static Runnable forkBlastula(int[] sessionSocketRawFDs) {
+ FileDescriptor[] pipeFDs = null;
+
+ try {
+ pipeFDs = Os.pipe2(O_CLOEXEC);
+ } catch (ErrnoException errnoEx) {
+ throw new IllegalStateException("Unable to create blastula pipe.", errnoEx);
+ }
+
+ int pid =
+ nativeForkBlastula(pipeFDs[0].getInt$(), pipeFDs[1].getInt$(), sessionSocketRawFDs);
+
+ if (pid == 0) {
+ IoUtils.closeQuietly(pipeFDs[0]);
+ return blastulaMain(pipeFDs[1]);
+ } else {
+ // The read-end of the pipe will be closed by the native code.
+ // See removeBlastulaTableEntry();
+ IoUtils.closeQuietly(pipeFDs[1]);
+ return null;
+ }
+ }
+
private static native int nativeForkBlastula(int readPipeFD,
int writePipeFD,
int[] sessionSocketRawFDs);
+ /**
+ * This function is used by blastulas to wait for specialization requests from the system
+ * server.
+ *
+ * @param writePipe The write end of the reporting pipe used to communicate with the poll loop
+ * of the ZygoteServer.
+ * @return A runnable oject representing the new application.
+ */
+ static Runnable blastulaMain(FileDescriptor writePipe) {
+ final int pid = Process.myPid();
+
+ LocalSocket sessionSocket = null;
+ DataOutputStream blastulaOutputStream = null;
+ Credentials peerCredentials = null;
+ String[] argStrings = null;
+
+ while (true) {
+ try {
+ sessionSocket = sBlastulaPoolSocket.accept();
+
+ BufferedReader blastulaReader =
+ new BufferedReader(new InputStreamReader(sessionSocket.getInputStream()));
+ blastulaOutputStream =
+ new DataOutputStream(sessionSocket.getOutputStream());
+
+ peerCredentials = sessionSocket.getPeerCredentials();
+
+ argStrings = readArgumentList(blastulaReader);
+
+ if (argStrings != null) {
+ break;
+ } else {
+ Log.e("Blastula", "Truncated command received.");
+ IoUtils.closeQuietly(sessionSocket);
+ }
+ } catch (IOException ioEx) {
+ Log.e("Blastula", "Failed to read command: " + ioEx.getMessage());
+ IoUtils.closeQuietly(sessionSocket);
+ }
+ }
+
+ ZygoteArguments args = new ZygoteArguments(argStrings);
+
+ // TODO (chriswailes): Should this only be run for debug builds?
+ validateBlastulaCommand(args);
+
+ applyUidSecurityPolicy(args, peerCredentials);
+ applyDebuggerSystemProperty(args);
+
+ int[][] rlimits = null;
+
+ if (args.mRLimits != null) {
+ rlimits = args.mRLimits.toArray(INT_ARRAY_2D);
+ }
+
+ // This must happen before the SELinux policy for this process is
+ // changed when specializing.
+ try {
+ // Used by ZygoteProcess.zygoteSendArgsAndGetResult to fill in a
+ // Process.ProcessStartResult object.
+ blastulaOutputStream.writeInt(pid);
+ } catch (IOException ioEx) {
+ Log.e("Blastula", "Failed to write response to session socket: " + ioEx.getMessage());
+ System.exit(-1);
+ } finally {
+ IoUtils.closeQuietly(sessionSocket);
+ IoUtils.closeQuietly(sBlastulaPoolSocket);
+ }
+
+ try {
+ ByteArrayOutputStream buffer =
+ new ByteArrayOutputStream(Zygote.BLASTULA_MANAGEMENT_MESSAGE_BYTES);
+ DataOutputStream outputStream = new DataOutputStream(buffer);
+
+ // This is written as a long so that the blastula reporting pipe and blastula pool
+ // event FD handlers in ZygoteServer.runSelectLoop can be unified. These two cases
+ // should both send/receive 8 bytes.
+ outputStream.writeLong(pid);
+ outputStream.flush();
+
+ Os.write(writePipe, buffer.toByteArray(), 0, buffer.size());
+ } catch (Exception ex) {
+ Log.e("Blastula",
+ String.format("Failed to write PID (%d) to pipe (%d): %s",
+ pid, writePipe.getInt$(), ex.getMessage()));
+ System.exit(-1);
+ } finally {
+ IoUtils.closeQuietly(writePipe);
+ }
+
+ specializeBlastula(args.mUid, args.mGid, args.mGids,
+ args.mRuntimeFlags, rlimits, args.mMountExternal,
+ args.mSeInfo, args.mNiceName, args.mStartChildZygote,
+ args.mInstructionSet, args.mAppDataDir, args.mPackageName,
+ args.mPackagesForUid, args.mVisibleVolIds);
+
+ if (args.mNiceName != null) {
+ Process.setArgV0(args.mNiceName);
+ }
+
+ // End of the postFork event.
+ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+
+ return ZygoteInit.zygoteInit(args.mTargetSdkVersion,
+ args.mRemainingArgs,
+ null /* classLoader */);
+ }
+
+ private static final String BLASTULA_ERROR_PREFIX = "Invalid command to blastula: ";
+
+ /**
+ * Checks a set of zygote arguments to see if they can be handled by a blastula. Throws an
+ * exception if an invalid arugment is encountered.
+ * @param args The arguments to test
+ */
+ static void validateBlastulaCommand(ZygoteArguments args) {
+ if (args.mAbiListQuery) {
+ throw new IllegalArgumentException(BLASTULA_ERROR_PREFIX + "--query-abi-list");
+ } else if (args.mPidQuery) {
+ throw new IllegalArgumentException(BLASTULA_ERROR_PREFIX + "--get-pid");
+ } else if (args.mPreloadDefault) {
+ throw new IllegalArgumentException(BLASTULA_ERROR_PREFIX + "--preload-default");
+ } else if (args.mPreloadPackage != null) {
+ throw new IllegalArgumentException(BLASTULA_ERROR_PREFIX + "--preload-package");
+ } else if (args.mPreloadApp != null) {
+ throw new IllegalArgumentException(BLASTULA_ERROR_PREFIX + "--preload-app");
+ } else if (args.mStartChildZygote) {
+ throw new IllegalArgumentException(BLASTULA_ERROR_PREFIX + "--start-child-zygote");
+ } else if (args.mApiBlacklistExemptions != null) {
+ throw new IllegalArgumentException(
+ BLASTULA_ERROR_PREFIX + "--set-api-blacklist-exemptions");
+ } else if (args.mHiddenApiAccessLogSampleRate != -1) {
+ throw new IllegalArgumentException(
+ BLASTULA_ERROR_PREFIX + "--hidden-api-log-sampling-rate=");
+ } else if (args.mInvokeWith != null) {
+ throw new IllegalArgumentException(BLASTULA_ERROR_PREFIX + "--invoke-with");
+ } else if (args.mPermittedCapabilities != 0 || args.mEffectiveCapabilities != 0) {
+ throw new ZygoteSecurityException("Client may not specify capabilities: "
+ + "permitted=0x" + Long.toHexString(args.mPermittedCapabilities)
+ + ", effective=0x" + Long.toHexString(args.mEffectiveCapabilities));
+ }
+ }
+
+ /**
+ * @return Raw file descriptors for the read-end of blastula reporting pipes.
+ */
+ protected static int[] getBlastulaPipeFDs() {
+ return nativeGetBlastulaPipeFDs();
+ }
+
private static native int[] nativeGetBlastulaPipeFDs();
+ /**
+ * Remove the blastula table entry for the provided process ID.
+ *
+ * @param blastulaPID Process ID of the entry to remove
+ * @return True if the entry was removed; false if it doesn't exist
+ */
+ protected static boolean removeBlastulaTableEntry(int blastulaPID) {
+ return nativeRemoveBlastulaTableEntry(blastulaPID);
+ }
+
private static native boolean nativeRemoveBlastulaTableEntry(int blastulaPID);
+ /**
+ * uid 1000 (Process.SYSTEM_UID) may specify any uid &gt; 1000 in normal
+ * operation. It may also specify any gid and setgroups() list it chooses.
+ * In factory test mode, it may specify any UID.
+ *
+ * @param args non-null; zygote spawner arguments
+ * @param peer non-null; peer credentials
+ * @throws ZygoteSecurityException
+ */
+ protected static void applyUidSecurityPolicy(ZygoteArguments args, Credentials peer)
+ throws ZygoteSecurityException {
+
+ if (peer.getUid() == Process.SYSTEM_UID) {
+ /* In normal operation, SYSTEM_UID can only specify a restricted
+ * set of UIDs. In factory test mode, SYSTEM_UID may specify any uid.
+ */
+ boolean uidRestricted = FactoryTest.getMode() == FactoryTest.FACTORY_TEST_OFF;
+
+ if (uidRestricted && args.mUidSpecified && (args.mUid < Process.SYSTEM_UID)) {
+ throw new ZygoteSecurityException(
+ "System UID may not launch process with UID < "
+ + Process.SYSTEM_UID);
+ }
+ }
+
+ // If not otherwise specified, uid and gid are inherited from peer
+ if (!args.mUidSpecified) {
+ args.mUid = peer.getUid();
+ args.mUidSpecified = true;
+ }
+ if (!args.mGidSpecified) {
+ args.mGid = peer.getGid();
+ args.mGidSpecified = true;
+ }
+ }
+
+ /**
+ * Applies debugger system properties to the zygote arguments.
+ *
+ * If "ro.debuggable" is "1", all apps are debuggable. Otherwise,
+ * the debugger state is specified via the "--enable-jdwp" flag
+ * in the spawn request.
+ *
+ * @param args non-null; zygote spawner args
+ */
+ protected static void applyDebuggerSystemProperty(ZygoteArguments args) {
+ if (RoSystemProperties.DEBUGGABLE) {
+ args.mRuntimeFlags |= Zygote.DEBUG_ENABLE_JDWP;
+ }
+ }
+
+ /**
+ * Applies zygote security policy.
+ * Based on the credentials of the process issuing a zygote command:
+ * <ol>
+ * <li> uid 0 (root) may specify --invoke-with to launch Zygote with a
+ * wrapper command.
+ * <li> Any other uid may not specify any invoke-with argument.
+ * </ul>
+ *
+ * @param args non-null; zygote spawner arguments
+ * @param peer non-null; peer credentials
+ * @throws ZygoteSecurityException
+ */
+ protected static void applyInvokeWithSecurityPolicy(ZygoteArguments args, Credentials peer)
+ throws ZygoteSecurityException {
+ int peerUid = peer.getUid();
+
+ if (args.mInvokeWith != null && peerUid != 0
+ && (args.mRuntimeFlags & Zygote.DEBUG_ENABLE_JDWP) == 0) {
+ throw new ZygoteSecurityException("Peer is permitted to specify an"
+ + "explicit invoke-with wrapper command only for debuggable"
+ + "applications.");
+ }
+ }
+
+ /**
+ * Applies invoke-with system properties to the zygote arguments.
+ *
+ * @param args non-null; zygote args
+ */
+ protected static void applyInvokeWithSystemProperty(ZygoteArguments args) {
+ if (args.mInvokeWith == null && args.mNiceName != null) {
+ String property = "wrap." + args.mNiceName;
+ args.mInvokeWith = SystemProperties.get(property);
+ if (args.mInvokeWith != null && args.mInvokeWith.length() == 0) {
+ args.mInvokeWith = null;
+ }
+ }
+ }
+
+ /**
+ * Reads an argument list from the provided socket
+ * @return Argument list or null if EOF is reached
+ * @throws IOException passed straight through
+ */
+ static String[] readArgumentList(BufferedReader socketReader) throws IOException {
+
+ /**
+ * See android.os.Process.zygoteSendArgsAndGetPid()
+ * Presently the wire format to the zygote process is:
+ * a) a count of arguments (argc, in essence)
+ * b) a number of newline-separated argument strings equal to count
+ *
+ * After the zygote process reads these it will write the pid of
+ * the child or -1 on failure.
+ */
+
+ int argc;
+
+ try {
+ String argc_string = socketReader.readLine();
+
+ if (argc_string == null) {
+ // EOF reached.
+ return null;
+ }
+ argc = Integer.parseInt(argc_string);
+
+ } catch (NumberFormatException ex) {
+ Log.e("Zygote", "Invalid Zygote wire format: non-int at argc");
+ throw new IOException("Invalid wire format");
+ }
+
+ // See bug 1092107: large argc can be used for a DOS attack
+ if (argc > MAX_ZYGOTE_ARGC) {
+ throw new IOException("Max arg count exceeded");
+ }
+
+ String[] args = new String[argc];
+ for (int arg_index = 0; arg_index < argc; arg_index++) {
+ args[arg_index] = socketReader.readLine();
+ if (args[arg_index] == null) {
+ // We got an unexpected EOF.
+ throw new IOException("Truncated request");
+ }
+ }
+
+ return args;
+ }
+
+ /**
+ * Creates a managed object representing the Blastula pool socket that has
+ * already been initialized and bound by init.
+ *
+ * TODO (chriswailes): Move the name selection logic into this function.
+ *
+ * @throws RuntimeException when open fails
+ */
+ static void createBlastulaSocket(String socketName) {
+ if (BLASTULA_POOL_ENABLED && sBlastulaPoolSocket == null) {
+ sBlastulaPoolSocket = createManagedSocketFromInitSocket(socketName);
+ }
+ }
+
+ /**
+ * Creates a managed LocalServerSocket object using a file descriptor
+ * created by an init.rc script. The init scripts that specify the
+ * sockets name can be found in system/core/rootdir. The socket is bound
+ * to the file system in the /dev/sockets/ directory, and the file
+ * descriptor is shared via the ANDROID_SOCKET_<socketName> environment
+ * variable.
+ */
+ static LocalServerSocket createManagedSocketFromInitSocket(String socketName) {
+ int fileDesc;
+ final String fullSocketName = ANDROID_SOCKET_PREFIX + socketName;
+
+ try {
+ String env = System.getenv(fullSocketName);
+ fileDesc = Integer.parseInt(env);
+ } catch (RuntimeException ex) {
+ throw new RuntimeException("Socket unset or invalid: " + fullSocketName, ex);
+ }
+
+ try {
+ FileDescriptor fd = new FileDescriptor();
+ fd.setInt$(fileDesc);
+ return new LocalServerSocket(fd);
+ } catch (IOException ex) {
+ throw new RuntimeException(
+ "Error building socket from file descriptor: " + fileDesc, ex);
+ }
+ }
private static void callPostForkSystemServerHooks() {
// SystemServer specific post fork hooks run before child post fork hooks.
diff --git a/core/java/com/android/internal/os/ZygoteArguments.java b/core/java/com/android/internal/os/ZygoteArguments.java
new file mode 100644
index 000000000000..24a08ca5b1e0
--- /dev/null
+++ b/core/java/com/android/internal/os/ZygoteArguments.java
@@ -0,0 +1,430 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.os;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+/**
+ * Handles argument parsing for args related to the zygote spawner.
+ *
+ * Current recognized args:
+ * <ul>
+ * <li> --setuid=<i>uid of child process, defaults to 0</i>
+ * <li> --setgid=<i>gid of child process, defaults to 0</i>
+ * <li> --setgroups=<i>comma-separated list of supplimentary gid's</i>
+ * <li> --capabilities=<i>a pair of comma-separated integer strings
+ * indicating Linux capabilities(2) set for child. The first string
+ * represents the <code>permitted</code> set, and the second the
+ * <code>effective</code> set. Precede each with 0 or
+ * 0x for octal or hexidecimal value. If unspecified, both default to 0.
+ * This parameter is only applied if the uid of the new process will
+ * be non-0. </i>
+ * <li> --rlimit=r,c,m<i>tuple of values for setrlimit() call.
+ * <code>r</code> is the resource, <code>c</code> and <code>m</code>
+ * are the settings for current and max value.</i>
+ * <li> --instruction-set=<i>instruction-set-string</i> which instruction set to use/emulate.
+ * <li> --nice-name=<i>nice name to appear in ps</i>
+ * <li> --package-name=<i>package name this process belongs to</i>
+ * <li> --runtime-args indicates that the remaining arg list should
+ * be handed off to com.android.internal.os.RuntimeInit, rather than
+ * processed directly.
+ * Android runtime startup (eg, Binder initialization) is also eschewed.
+ * <li> [--] &lt;args for RuntimeInit &gt;
+ * </ul>
+ */
+class ZygoteArguments {
+
+ /**
+ * from --setuid
+ */
+ int mUid = 0;
+ boolean mUidSpecified;
+
+ /**
+ * from --setgid
+ */
+ int mGid = 0;
+ boolean mGidSpecified;
+
+ /**
+ * from --setgroups
+ */
+ int[] mGids;
+
+ /**
+ * From --runtime-flags.
+ */
+ int mRuntimeFlags;
+
+ /**
+ * From --mount-external
+ */
+ int mMountExternal = Zygote.MOUNT_EXTERNAL_NONE;
+
+ /**
+ * from --target-sdk-version.
+ */
+ int mTargetSdkVersion;
+ boolean mTargetSdkVersionSpecified;
+
+ /**
+ * from --nice-name
+ */
+ String mNiceName;
+
+ /**
+ * from --capabilities
+ */
+ boolean mCapabilitiesSpecified;
+ long mPermittedCapabilities;
+ long mEffectiveCapabilities;
+
+ /**
+ * from --seinfo
+ */
+ boolean mSeInfoSpecified;
+ String mSeInfo;
+
+ /**
+ * from all --rlimit=r,c,m
+ */
+ ArrayList<int[]> mRLimits;
+
+ /**
+ * from --invoke-with
+ */
+ String mInvokeWith;
+
+ /** from --package-name */
+ String mPackageName;
+
+ /** from --packages-for-uid */
+ String[] mPackagesForUid;
+
+ /** from --visible-vols */
+ String[] mVisibleVolIds;
+
+ /**
+ * Any args after and including the first non-option arg (or after a '--')
+ */
+ String[] mRemainingArgs;
+
+ /**
+ * Whether the current arguments constitute an ABI list query.
+ */
+ boolean mAbiListQuery;
+
+ /**
+ * The instruction set to use, or null when not important.
+ */
+ String mInstructionSet;
+
+ /**
+ * The app data directory. May be null, e.g., for the system server. Note that this might not be
+ * reliable in the case of process-sharing apps.
+ */
+ String mAppDataDir;
+
+ /**
+ * The APK path of the package to preload, when using --preload-package.
+ */
+ String mPreloadPackage;
+
+ /**
+ * A Base64 string representing a serialize ApplicationInfo Parcel,
+ when using --preload-app.
+ */
+ String mPreloadApp;
+
+ /**
+ * The native library path of the package to preload, when using --preload-package.
+ */
+ String mPreloadPackageLibs;
+
+ /**
+ * The filename of the native library to preload, when using --preload-package.
+ */
+ String mPreloadPackageLibFileName;
+
+ /**
+ * The cache key under which to enter the preloaded package into the classloader cache, when
+ * using --preload-package.
+ */
+ String mPreloadPackageCacheKey;
+
+ /**
+ * Whether this is a request to start preloading the default resources and classes. This
+ * argument only makes sense when the zygote is in lazy preload mode (i.e, when it's started
+ * with --enable-lazy-preload).
+ */
+ boolean mPreloadDefault;
+
+ /**
+ * Whether this is a request to start a zygote process as a child of this zygote. Set with
+ * --start-child-zygote. The remaining arguments must include the CHILD_ZYGOTE_SOCKET_NAME_ARG
+ * flag to indicate the abstract socket name that should be used for communication.
+ */
+ boolean mStartChildZygote;
+
+ /**
+ * Whether the current arguments constitute a request for the zygote's PID.
+ */
+ boolean mPidQuery;
+
+ /**
+ * Exemptions from API blacklisting. These are sent to the pre-forked zygote at boot time, or
+ * when they change, via --set-api-blacklist-exemptions.
+ */
+ String[] mApiBlacklistExemptions;
+
+ /**
+ * Sampling rate for logging hidden API accesses to the event log. This is sent to the
+ * pre-forked zygote at boot time, or when it changes, via --hidden-api-log-sampling-rate.
+ */
+ int mHiddenApiAccessLogSampleRate = -1;
+
+ /**
+ * Constructs instance and parses args
+ *
+ * @param args zygote command-line args
+ */
+ ZygoteArguments(String[] args) throws IllegalArgumentException {
+ parseArgs(args);
+ }
+
+ /**
+ * Parses the commandline arguments intended for the Zygote spawner (such as "--setuid=" and
+ * "--setgid=") and creates an array containing the remaining args.
+ *
+ * Per security review bug #1112214, duplicate args are disallowed in critical cases to make
+ * injection harder.
+ */
+ private void parseArgs(String[] args)
+ throws IllegalArgumentException {
+ int curArg = 0;
+
+ boolean seenRuntimeArgs = false;
+
+ boolean expectRuntimeArgs = true;
+ for ( /* curArg */ ; curArg < args.length; curArg++) {
+ String arg = args[curArg];
+
+ if (arg.equals("--")) {
+ curArg++;
+ break;
+ } else if (arg.startsWith("--setuid=")) {
+ if (mUidSpecified) {
+ throw new IllegalArgumentException(
+ "Duplicate arg specified");
+ }
+ mUidSpecified = true;
+ mUid = Integer.parseInt(
+ arg.substring(arg.indexOf('=') + 1));
+ } else if (arg.startsWith("--setgid=")) {
+ if (mGidSpecified) {
+ throw new IllegalArgumentException(
+ "Duplicate arg specified");
+ }
+ mGidSpecified = true;
+ mGid = Integer.parseInt(
+ arg.substring(arg.indexOf('=') + 1));
+ } else if (arg.startsWith("--target-sdk-version=")) {
+ if (mTargetSdkVersionSpecified) {
+ throw new IllegalArgumentException(
+ "Duplicate target-sdk-version specified");
+ }
+ mTargetSdkVersionSpecified = true;
+ mTargetSdkVersion = Integer.parseInt(
+ arg.substring(arg.indexOf('=') + 1));
+ } else if (arg.equals("--runtime-args")) {
+ seenRuntimeArgs = true;
+ } else if (arg.startsWith("--runtime-flags=")) {
+ mRuntimeFlags = Integer.parseInt(
+ arg.substring(arg.indexOf('=') + 1));
+ } else if (arg.startsWith("--seinfo=")) {
+ if (mSeInfoSpecified) {
+ throw new IllegalArgumentException(
+ "Duplicate arg specified");
+ }
+ mSeInfoSpecified = true;
+ mSeInfo = arg.substring(arg.indexOf('=') + 1);
+ } else if (arg.startsWith("--capabilities=")) {
+ if (mCapabilitiesSpecified) {
+ throw new IllegalArgumentException(
+ "Duplicate arg specified");
+ }
+ mCapabilitiesSpecified = true;
+ String capString = arg.substring(arg.indexOf('=') + 1);
+
+ String[] capStrings = capString.split(",", 2);
+
+ if (capStrings.length == 1) {
+ mEffectiveCapabilities = Long.decode(capStrings[0]);
+ mPermittedCapabilities = mEffectiveCapabilities;
+ } else {
+ mPermittedCapabilities = Long.decode(capStrings[0]);
+ mEffectiveCapabilities = Long.decode(capStrings[1]);
+ }
+ } else if (arg.startsWith("--rlimit=")) {
+ // Duplicate --rlimit arguments are specifically allowed.
+ String[] limitStrings = arg.substring(arg.indexOf('=') + 1).split(",");
+
+ if (limitStrings.length != 3) {
+ throw new IllegalArgumentException(
+ "--rlimit= should have 3 comma-delimited ints");
+ }
+ int[] rlimitTuple = new int[limitStrings.length];
+
+ for (int i = 0; i < limitStrings.length; i++) {
+ rlimitTuple[i] = Integer.parseInt(limitStrings[i]);
+ }
+
+ if (mRLimits == null) {
+ mRLimits = new ArrayList();
+ }
+
+ mRLimits.add(rlimitTuple);
+ } else if (arg.startsWith("--setgroups=")) {
+ if (mGids != null) {
+ throw new IllegalArgumentException(
+ "Duplicate arg specified");
+ }
+
+ String[] params = arg.substring(arg.indexOf('=') + 1).split(",");
+
+ mGids = new int[params.length];
+
+ for (int i = params.length - 1; i >= 0; i--) {
+ mGids[i] = Integer.parseInt(params[i]);
+ }
+ } else if (arg.equals("--invoke-with")) {
+ if (mInvokeWith != null) {
+ throw new IllegalArgumentException(
+ "Duplicate arg specified");
+ }
+ try {
+ mInvokeWith = args[++curArg];
+ } catch (IndexOutOfBoundsException ex) {
+ throw new IllegalArgumentException(
+ "--invoke-with requires argument");
+ }
+ } else if (arg.startsWith("--nice-name=")) {
+ if (mNiceName != null) {
+ throw new IllegalArgumentException(
+ "Duplicate arg specified");
+ }
+ mNiceName = arg.substring(arg.indexOf('=') + 1);
+ } else if (arg.equals("--mount-external-default")) {
+ mMountExternal = Zygote.MOUNT_EXTERNAL_DEFAULT;
+ } else if (arg.equals("--mount-external-read")) {
+ mMountExternal = Zygote.MOUNT_EXTERNAL_READ;
+ } else if (arg.equals("--mount-external-write")) {
+ mMountExternal = Zygote.MOUNT_EXTERNAL_WRITE;
+ } else if (arg.equals("--mount-external-full")) {
+ mMountExternal = Zygote.MOUNT_EXTERNAL_FULL;
+ } else if (arg.equals("--mount-external-installer")) {
+ mMountExternal = Zygote.MOUNT_EXTERNAL_INSTALLER;
+ } else if (arg.equals("--mount-external-legacy")) {
+ mMountExternal = Zygote.MOUNT_EXTERNAL_LEGACY;
+ } else if (arg.equals("--query-abi-list")) {
+ mAbiListQuery = true;
+ } else if (arg.equals("--get-pid")) {
+ mPidQuery = true;
+ } else if (arg.startsWith("--instruction-set=")) {
+ mInstructionSet = arg.substring(arg.indexOf('=') + 1);
+ } else if (arg.startsWith("--app-data-dir=")) {
+ mAppDataDir = arg.substring(arg.indexOf('=') + 1);
+ } else if (arg.equals("--preload-app")) {
+ mPreloadApp = args[++curArg];
+ } else if (arg.equals("--preload-package")) {
+ mPreloadPackage = args[++curArg];
+ mPreloadPackageLibs = args[++curArg];
+ mPreloadPackageLibFileName = args[++curArg];
+ mPreloadPackageCacheKey = args[++curArg];
+ } else if (arg.equals("--preload-default")) {
+ mPreloadDefault = true;
+ expectRuntimeArgs = false;
+ } else if (arg.equals("--start-child-zygote")) {
+ mStartChildZygote = true;
+ } else if (arg.equals("--set-api-blacklist-exemptions")) {
+ // consume all remaining args; this is a stand-alone command, never included
+ // with the regular fork command.
+ mApiBlacklistExemptions = Arrays.copyOfRange(args, curArg + 1, args.length);
+ curArg = args.length;
+ expectRuntimeArgs = false;
+ } else if (arg.startsWith("--hidden-api-log-sampling-rate=")) {
+ String rateStr = arg.substring(arg.indexOf('=') + 1);
+ try {
+ mHiddenApiAccessLogSampleRate = Integer.parseInt(rateStr);
+ } catch (NumberFormatException nfe) {
+ throw new IllegalArgumentException(
+ "Invalid log sampling rate: " + rateStr, nfe);
+ }
+ expectRuntimeArgs = false;
+ } else if (arg.startsWith("--package-name=")) {
+ if (mPackageName != null) {
+ throw new IllegalArgumentException("Duplicate arg specified");
+ }
+ mPackageName = arg.substring(arg.indexOf('=') + 1);
+ } else if (arg.startsWith("--packages-for-uid=")) {
+ mPackagesForUid = arg.substring(arg.indexOf('=') + 1).split(",");
+ } else if (arg.startsWith("--visible-vols=")) {
+ mVisibleVolIds = arg.substring(arg.indexOf('=') + 1).split(",");
+ } else {
+ break;
+ }
+ }
+
+ if (mAbiListQuery || mPidQuery) {
+ if (args.length - curArg > 0) {
+ throw new IllegalArgumentException("Unexpected arguments after --query-abi-list.");
+ }
+ } else if (mPreloadPackage != null) {
+ if (args.length - curArg > 0) {
+ throw new IllegalArgumentException(
+ "Unexpected arguments after --preload-package.");
+ }
+ } else if (mPreloadApp != null) {
+ if (args.length - curArg > 0) {
+ throw new IllegalArgumentException(
+ "Unexpected arguments after --preload-app.");
+ }
+ } else if (expectRuntimeArgs) {
+ if (!seenRuntimeArgs) {
+ throw new IllegalArgumentException("Unexpected argument : " + args[curArg]);
+ }
+
+ mRemainingArgs = new String[args.length - curArg];
+ System.arraycopy(args, curArg, mRemainingArgs, 0, mRemainingArgs.length);
+ }
+
+ if (mStartChildZygote) {
+ boolean seenChildSocketArg = false;
+ for (String arg : mRemainingArgs) {
+ if (arg.startsWith(Zygote.CHILD_ZYGOTE_SOCKET_NAME_ARG)) {
+ seenChildSocketArg = true;
+ break;
+ }
+ }
+ if (!seenChildSocketArg) {
+ throw new IllegalArgumentException("--start-child-zygote specified "
+ + "without " + Zygote.CHILD_ZYGOTE_SOCKET_NAME_ARG);
+ }
+ }
+ }
+}
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index ced798cb6445..ffbe8ebccb9e 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -24,16 +24,13 @@ import static android.system.OsConstants.STDIN_FILENO;
import static android.system.OsConstants.STDOUT_FILENO;
import static com.android.internal.os.ZygoteConnectionConstants.CONNECTION_TIMEOUT_MILLIS;
-import static com.android.internal.os.ZygoteConnectionConstants.MAX_ZYGOTE_ARGC;
import static com.android.internal.os.ZygoteConnectionConstants.WRAPPED_PID_TIMEOUT_MILLIS;
import android.content.pm.ApplicationInfo;
import android.net.Credentials;
import android.net.LocalSocket;
-import android.os.FactoryTest;
import android.os.Parcel;
import android.os.Process;
-import android.os.SystemProperties;
import android.os.Trace;
import android.system.ErrnoException;
import android.system.Os;
@@ -52,8 +49,6 @@ import java.io.FileDescriptor;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Base64;
/**
@@ -62,9 +57,6 @@ import java.util.Base64;
class ZygoteConnection {
private static final String TAG = "Zygote";
- /** a prototype instance for a future List.toArray() */
- private static final int[][] intArray2d = new int[0][0];
-
/**
* The command socket.
*
@@ -113,7 +105,7 @@ class ZygoteConnection {
*
* @return null-ok; file descriptor
*/
- FileDescriptor getFileDesciptor() {
+ FileDescriptor getFileDescriptor() {
return mSocket.getFileDescriptor();
}
@@ -127,11 +119,13 @@ class ZygoteConnection {
*/
Runnable processOneCommand(ZygoteServer zygoteServer) {
String args[];
- Arguments parsedArgs = null;
+ ZygoteArguments parsedArgs = null;
FileDescriptor[] descriptors;
try {
- args = readArgumentList();
+ args = Zygote.readArgumentList(mSocketReader);
+
+ // TODO (chriswailes): Remove this and add an assert.
descriptors = mSocket.getAncillaryFileDescriptors();
} catch (IOException ex) {
throw new IllegalStateException("IOException on command socket", ex);
@@ -148,26 +142,26 @@ class ZygoteConnection {
FileDescriptor childPipeFd = null;
FileDescriptor serverPipeFd = null;
- parsedArgs = new Arguments(args);
+ parsedArgs = new ZygoteArguments(args);
- if (parsedArgs.abiListQuery) {
+ if (parsedArgs.mAbiListQuery) {
handleAbiListQuery();
return null;
}
- if (parsedArgs.pidQuery) {
+ if (parsedArgs.mPidQuery) {
handlePidQuery();
return null;
}
- if (parsedArgs.preloadDefault) {
+ if (parsedArgs.mPreloadDefault) {
handlePreload();
return null;
}
- if (parsedArgs.preloadPackage != null) {
- handlePreloadPackage(parsedArgs.preloadPackage, parsedArgs.preloadPackageLibs,
- parsedArgs.preloadPackageLibFileName, parsedArgs.preloadPackageCacheKey);
+ if (parsedArgs.mPreloadPackage != null) {
+ handlePreloadPackage(parsedArgs.mPreloadPackage, parsedArgs.mPreloadPackageLibs,
+ parsedArgs.mPreloadPackageLibFileName, parsedArgs.mPreloadPackageCacheKey);
return null;
}
@@ -186,37 +180,37 @@ class ZygoteConnection {
return null;
}
- if (parsedArgs.apiBlacklistExemptions != null) {
- handleApiBlacklistExemptions(parsedArgs.apiBlacklistExemptions);
+ if (parsedArgs.mApiBlacklistExemptions != null) {
+ handleApiBlacklistExemptions(parsedArgs.mApiBlacklistExemptions);
return null;
}
- if (parsedArgs.hiddenApiAccessLogSampleRate != -1) {
- handleHiddenApiAccessLogSampleRate(parsedArgs.hiddenApiAccessLogSampleRate);
+ if (parsedArgs.mHiddenApiAccessLogSampleRate != -1) {
+ handleHiddenApiAccessLogSampleRate(parsedArgs.mHiddenApiAccessLogSampleRate);
return null;
}
- if (parsedArgs.permittedCapabilities != 0 || parsedArgs.effectiveCapabilities != 0) {
- throw new ZygoteSecurityException("Client may not specify capabilities: " +
- "permitted=0x" + Long.toHexString(parsedArgs.permittedCapabilities) +
- ", effective=0x" + Long.toHexString(parsedArgs.effectiveCapabilities));
+ if (parsedArgs.mPermittedCapabilities != 0 || parsedArgs.mEffectiveCapabilities != 0) {
+ throw new ZygoteSecurityException("Client may not specify capabilities: "
+ + "permitted=0x" + Long.toHexString(parsedArgs.mPermittedCapabilities)
+ + ", effective=0x" + Long.toHexString(parsedArgs.mEffectiveCapabilities));
}
- applyUidSecurityPolicy(parsedArgs, peer);
- applyInvokeWithSecurityPolicy(parsedArgs, peer);
+ Zygote.applyUidSecurityPolicy(parsedArgs, peer);
+ Zygote.applyInvokeWithSecurityPolicy(parsedArgs, peer);
- applyDebuggerSystemProperty(parsedArgs);
- applyInvokeWithSystemProperty(parsedArgs);
+ Zygote.applyDebuggerSystemProperty(parsedArgs);
+ Zygote.applyInvokeWithSystemProperty(parsedArgs);
int[][] rlimits = null;
- if (parsedArgs.rlimits != null) {
- rlimits = parsedArgs.rlimits.toArray(intArray2d);
+ if (parsedArgs.mRLimits != null) {
+ rlimits = parsedArgs.mRLimits.toArray(Zygote.INT_ARRAY_2D);
}
int[] fdsToIgnore = null;
- if (parsedArgs.invokeWith != null) {
+ if (parsedArgs.mInvokeWith != null) {
try {
FileDescriptor[] pipeFds = Os.pipe2(O_CLOEXEC);
childPipeFd = pipeFds[1];
@@ -248,7 +242,7 @@ class ZygoteConnection {
fdsToClose[0] = fd.getInt$();
}
- fd = zygoteServer.getServerSocketFileDescriptor();
+ fd = zygoteServer.getZygoteSocketFileDescriptor();
if (fd != null) {
fdsToClose[1] = fd.getInt$();
@@ -256,11 +250,11 @@ class ZygoteConnection {
fd = null;
- pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids,
- parsedArgs.runtimeFlags, rlimits, parsedArgs.mountExternal, parsedArgs.seInfo,
- parsedArgs.niceName, fdsToClose, fdsToIgnore, parsedArgs.startChildZygote,
- parsedArgs.instructionSet, parsedArgs.appDataDir, parsedArgs.packageName,
- parsedArgs.packagesForUid, parsedArgs.visibleVolIds);
+ pid = Zygote.forkAndSpecialize(parsedArgs.mUid, parsedArgs.mGid, parsedArgs.mGids,
+ parsedArgs.mRuntimeFlags, rlimits, parsedArgs.mMountExternal, parsedArgs.mSeInfo,
+ parsedArgs.mNiceName, fdsToClose, fdsToIgnore, parsedArgs.mStartChildZygote,
+ parsedArgs.mInstructionSet, parsedArgs.mAppDataDir, parsedArgs.mPackageName,
+ parsedArgs.mPackagesForUid, parsedArgs.mVisibleVolIds);
try {
if (pid == 0) {
@@ -272,7 +266,7 @@ class ZygoteConnection {
serverPipeFd = null;
return handleChildProc(parsedArgs, descriptors, childPipeFd,
- parsedArgs.startChildZygote);
+ parsedArgs.mStartChildZygote);
} else {
// In the parent. A pid < 0 indicates a failure and will be handled in
// handleParentProc.
@@ -387,541 +381,6 @@ class ZygoteConnection {
}
/**
- * Handles argument parsing for args related to the zygote spawner.
- *
- * Current recognized args:
- * <ul>
- * <li> --setuid=<i>uid of child process, defaults to 0</i>
- * <li> --setgid=<i>gid of child process, defaults to 0</i>
- * <li> --setgroups=<i>comma-separated list of supplimentary gid's</i>
- * <li> --capabilities=<i>a pair of comma-separated integer strings
- * indicating Linux capabilities(2) set for child. The first string
- * represents the <code>permitted</code> set, and the second the
- * <code>effective</code> set. Precede each with 0 or
- * 0x for octal or hexidecimal value. If unspecified, both default to 0.
- * This parameter is only applied if the uid of the new process will
- * be non-0. </i>
- * <li> --rlimit=r,c,m<i>tuple of values for setrlimit() call.
- * <code>r</code> is the resource, <code>c</code> and <code>m</code>
- * are the settings for current and max value.</i>
- * <li> --instruction-set=<i>instruction-set-string</i> which instruction set to use/emulate.
- * <li> --nice-name=<i>nice name to appear in ps</i>
- * <li> --package-name=<i>package name this process belongs to</i>
- * <li> --runtime-args indicates that the remaining arg list should
- * be handed off to com.android.internal.os.RuntimeInit, rather than
- * processed directly.
- * Android runtime startup (eg, Binder initialization) is also eschewed.
- * <li> [--] &lt;args for RuntimeInit &gt;
- * </ul>
- */
- static class Arguments {
- /** from --setuid */
- int uid = 0;
- boolean uidSpecified;
-
- /** from --setgid */
- int gid = 0;
- boolean gidSpecified;
-
- /** from --setgroups */
- int[] gids;
-
- /**
- * From --runtime-flags.
- */
- int runtimeFlags;
-
- /** From --mount-external */
- int mountExternal = Zygote.MOUNT_EXTERNAL_NONE;
-
- /** from --target-sdk-version. */
- int targetSdkVersion;
- boolean targetSdkVersionSpecified;
-
- /** from --nice-name */
- String niceName;
-
- /** from --capabilities */
- boolean capabilitiesSpecified;
- long permittedCapabilities;
- long effectiveCapabilities;
-
- /** from --seinfo */
- boolean seInfoSpecified;
- String seInfo;
-
- /** from all --rlimit=r,c,m */
- ArrayList<int[]> rlimits;
-
- /** from --invoke-with */
- String invokeWith;
-
- /** from --package-name */
- String packageName;
-
- /** from --packages-for-uid */
- String[] packagesForUid;
-
- /** from --visible-vols */
- String[] visibleVolIds;
-
- /**
- * Any args after and including the first non-option arg
- * (or after a '--')
- */
- String remainingArgs[];
-
- /**
- * Whether the current arguments constitute an ABI list query.
- */
- boolean abiListQuery;
-
- /**
- * The instruction set to use, or null when not important.
- */
- String instructionSet;
-
- /**
- * The app data directory. May be null, e.g., for the system server. Note that this might
- * not be reliable in the case of process-sharing apps.
- */
- String appDataDir;
-
- /**
- * The APK path of the package to preload, when using --preload-package.
- */
- String preloadPackage;
-
- /**
- * A Base64 string representing a serialize ApplicationInfo Parcel,
- when using --preload-app.
- */
- String mPreloadApp;
-
- /**
- * The native library path of the package to preload, when using --preload-package.
- */
- String preloadPackageLibs;
-
- /**
- * The filename of the native library to preload, when using --preload-package.
- */
- String preloadPackageLibFileName;
-
- /**
- * The cache key under which to enter the preloaded package into the classloader cache,
- * when using --preload-package.
- */
- String preloadPackageCacheKey;
-
- /**
- * Whether this is a request to start preloading the default resources and classes.
- * This argument only makes sense when the zygote is in lazy preload mode (i.e, when
- * it's started with --enable-lazy-preload).
- */
- boolean preloadDefault;
-
- /**
- * Whether this is a request to start a zygote process as a child of this zygote.
- * Set with --start-child-zygote. The remaining arguments must include the
- * CHILD_ZYGOTE_SOCKET_NAME_ARG flag to indicate the abstract socket name that
- * should be used for communication.
- */
- boolean startChildZygote;
-
- /**
- * Whether the current arguments constitute a request for the zygote's PID.
- */
- boolean pidQuery;
-
- /**
- * Exemptions from API blacklisting. These are sent to the pre-forked zygote at boot time,
- * or when they change, via --set-api-blacklist-exemptions.
- */
- String[] apiBlacklistExemptions;
-
- /**
- * Sampling rate for logging hidden API accesses to the event log. This is sent to the
- * pre-forked zygote at boot time, or when it changes, via --hidden-api-log-sampling-rate.
- */
- int hiddenApiAccessLogSampleRate = -1;
-
- /**
- * Constructs instance and parses args
- * @param args zygote command-line args
- * @throws IllegalArgumentException
- */
- Arguments(String args[]) throws IllegalArgumentException {
- parseArgs(args);
- }
-
- /**
- * Parses the commandline arguments intended for the Zygote spawner
- * (such as "--setuid=" and "--setgid=") and creates an array
- * containing the remaining args.
- *
- * Per security review bug #1112214, duplicate args are disallowed in
- * critical cases to make injection harder.
- */
- private void parseArgs(String args[])
- throws IllegalArgumentException {
- int curArg = 0;
-
- boolean seenRuntimeArgs = false;
-
- boolean expectRuntimeArgs = true;
- for ( /* curArg */ ; curArg < args.length; curArg++) {
- String arg = args[curArg];
-
- if (arg.equals("--")) {
- curArg++;
- break;
- } else if (arg.startsWith("--setuid=")) {
- if (uidSpecified) {
- throw new IllegalArgumentException(
- "Duplicate arg specified");
- }
- uidSpecified = true;
- uid = Integer.parseInt(
- arg.substring(arg.indexOf('=') + 1));
- } else if (arg.startsWith("--setgid=")) {
- if (gidSpecified) {
- throw new IllegalArgumentException(
- "Duplicate arg specified");
- }
- gidSpecified = true;
- gid = Integer.parseInt(
- arg.substring(arg.indexOf('=') + 1));
- } else if (arg.startsWith("--target-sdk-version=")) {
- if (targetSdkVersionSpecified) {
- throw new IllegalArgumentException(
- "Duplicate target-sdk-version specified");
- }
- targetSdkVersionSpecified = true;
- targetSdkVersion = Integer.parseInt(
- arg.substring(arg.indexOf('=') + 1));
- } else if (arg.equals("--runtime-args")) {
- seenRuntimeArgs = true;
- } else if (arg.startsWith("--runtime-flags=")) {
- runtimeFlags = Integer.parseInt(
- arg.substring(arg.indexOf('=') + 1));
- } else if (arg.startsWith("--seinfo=")) {
- if (seInfoSpecified) {
- throw new IllegalArgumentException(
- "Duplicate arg specified");
- }
- seInfoSpecified = true;
- seInfo = arg.substring(arg.indexOf('=') + 1);
- } else if (arg.startsWith("--capabilities=")) {
- if (capabilitiesSpecified) {
- throw new IllegalArgumentException(
- "Duplicate arg specified");
- }
- capabilitiesSpecified = true;
- String capString = arg.substring(arg.indexOf('=')+1);
-
- String[] capStrings = capString.split(",", 2);
-
- if (capStrings.length == 1) {
- effectiveCapabilities = Long.decode(capStrings[0]);
- permittedCapabilities = effectiveCapabilities;
- } else {
- permittedCapabilities = Long.decode(capStrings[0]);
- effectiveCapabilities = Long.decode(capStrings[1]);
- }
- } else if (arg.startsWith("--rlimit=")) {
- // Duplicate --rlimit arguments are specifically allowed.
- String[] limitStrings
- = arg.substring(arg.indexOf('=')+1).split(",");
-
- if (limitStrings.length != 3) {
- throw new IllegalArgumentException(
- "--rlimit= should have 3 comma-delimited ints");
- }
- int[] rlimitTuple = new int[limitStrings.length];
-
- for(int i=0; i < limitStrings.length; i++) {
- rlimitTuple[i] = Integer.parseInt(limitStrings[i]);
- }
-
- if (rlimits == null) {
- rlimits = new ArrayList();
- }
-
- rlimits.add(rlimitTuple);
- } else if (arg.startsWith("--setgroups=")) {
- if (gids != null) {
- throw new IllegalArgumentException(
- "Duplicate arg specified");
- }
-
- String[] params
- = arg.substring(arg.indexOf('=') + 1).split(",");
-
- gids = new int[params.length];
-
- for (int i = params.length - 1; i >= 0 ; i--) {
- gids[i] = Integer.parseInt(params[i]);
- }
- } else if (arg.equals("--invoke-with")) {
- if (invokeWith != null) {
- throw new IllegalArgumentException(
- "Duplicate arg specified");
- }
- try {
- invokeWith = args[++curArg];
- } catch (IndexOutOfBoundsException ex) {
- throw new IllegalArgumentException(
- "--invoke-with requires argument");
- }
- } else if (arg.startsWith("--nice-name=")) {
- if (niceName != null) {
- throw new IllegalArgumentException(
- "Duplicate arg specified");
- }
- niceName = arg.substring(arg.indexOf('=') + 1);
- } else if (arg.equals("--mount-external-default")) {
- mountExternal = Zygote.MOUNT_EXTERNAL_DEFAULT;
- } else if (arg.equals("--mount-external-read")) {
- mountExternal = Zygote.MOUNT_EXTERNAL_READ;
- } else if (arg.equals("--mount-external-write")) {
- mountExternal = Zygote.MOUNT_EXTERNAL_WRITE;
- } else if (arg.equals("--mount-external-full")) {
- mountExternal = Zygote.MOUNT_EXTERNAL_FULL;
- } else if (arg.equals("--mount-external-installer")) {
- mountExternal = Zygote.MOUNT_EXTERNAL_INSTALLER;
- } else if (arg.equals("--mount-external-legacy")) {
- mountExternal = Zygote.MOUNT_EXTERNAL_LEGACY;
- } else if (arg.equals("--query-abi-list")) {
- abiListQuery = true;
- } else if (arg.equals("--get-pid")) {
- pidQuery = true;
- } else if (arg.startsWith("--instruction-set=")) {
- instructionSet = arg.substring(arg.indexOf('=') + 1);
- } else if (arg.startsWith("--app-data-dir=")) {
- appDataDir = arg.substring(arg.indexOf('=') + 1);
- } else if (arg.equals("--preload-app")) {
- mPreloadApp = args[++curArg];
- } else if (arg.equals("--preload-package")) {
- preloadPackage = args[++curArg];
- preloadPackageLibs = args[++curArg];
- preloadPackageLibFileName = args[++curArg];
- preloadPackageCacheKey = args[++curArg];
- } else if (arg.equals("--preload-default")) {
- preloadDefault = true;
- expectRuntimeArgs = false;
- } else if (arg.equals("--start-child-zygote")) {
- startChildZygote = true;
- } else if (arg.equals("--set-api-blacklist-exemptions")) {
- // consume all remaining args; this is a stand-alone command, never included
- // with the regular fork command.
- apiBlacklistExemptions = Arrays.copyOfRange(args, curArg + 1, args.length);
- curArg = args.length;
- expectRuntimeArgs = false;
- } else if (arg.startsWith("--hidden-api-log-sampling-rate=")) {
- String rateStr = arg.substring(arg.indexOf('=') + 1);
- try {
- hiddenApiAccessLogSampleRate = Integer.parseInt(rateStr);
- } catch (NumberFormatException nfe) {
- throw new IllegalArgumentException(
- "Invalid log sampling rate: " + rateStr, nfe);
- }
- expectRuntimeArgs = false;
- } else if (arg.startsWith("--package-name=")) {
- if (packageName != null) {
- throw new IllegalArgumentException("Duplicate arg specified");
- }
- packageName = arg.substring(arg.indexOf('=') + 1);
- } else if (arg.startsWith("--packages-for-uid=")) {
- packagesForUid = arg.substring(arg.indexOf('=') + 1).split(",");
- } else if (arg.startsWith("--visible-vols=")) {
- visibleVolIds = arg.substring(arg.indexOf('=') + 1).split(",");
- } else {
- break;
- }
- }
-
- if (abiListQuery || pidQuery) {
- if (args.length - curArg > 0) {
- throw new IllegalArgumentException("Unexpected arguments after --query-abi-list.");
- }
- } else if (preloadPackage != null) {
- if (args.length - curArg > 0) {
- throw new IllegalArgumentException(
- "Unexpected arguments after --preload-package.");
- }
- } else if (mPreloadApp != null) {
- if (args.length - curArg > 0) {
- throw new IllegalArgumentException(
- "Unexpected arguments after --preload-app.");
- }
- } else if (expectRuntimeArgs) {
- if (!seenRuntimeArgs) {
- throw new IllegalArgumentException("Unexpected argument : " + args[curArg]);
- }
-
- remainingArgs = new String[args.length - curArg];
- System.arraycopy(args, curArg, remainingArgs, 0, remainingArgs.length);
- }
-
- if (startChildZygote) {
- boolean seenChildSocketArg = false;
- for (String arg : remainingArgs) {
- if (arg.startsWith(Zygote.CHILD_ZYGOTE_SOCKET_NAME_ARG)) {
- seenChildSocketArg = true;
- break;
- }
- }
- if (!seenChildSocketArg) {
- throw new IllegalArgumentException("--start-child-zygote specified " +
- "without " + Zygote.CHILD_ZYGOTE_SOCKET_NAME_ARG);
- }
- }
- }
- }
-
- /**
- * Reads an argument list from the command socket/
- * @return Argument list or null if EOF is reached
- * @throws IOException passed straight through
- */
- private String[] readArgumentList()
- throws IOException {
-
- /**
- * See android.os.Process.zygoteSendArgsAndGetPid()
- * Presently the wire format to the zygote process is:
- * a) a count of arguments (argc, in essence)
- * b) a number of newline-separated argument strings equal to count
- *
- * After the zygote process reads these it will write the pid of
- * the child or -1 on failure.
- */
-
- int argc;
-
- try {
- String s = mSocketReader.readLine();
-
- if (s == null) {
- // EOF reached.
- return null;
- }
- argc = Integer.parseInt(s);
- } catch (NumberFormatException ex) {
- Log.e(TAG, "invalid Zygote wire format: non-int at argc");
- throw new IOException("invalid wire format");
- }
-
- // See bug 1092107: large argc can be used for a DOS attack
- if (argc > MAX_ZYGOTE_ARGC) {
- throw new IOException("max arg count exceeded");
- }
-
- String[] result = new String[argc];
- for (int i = 0; i < argc; i++) {
- result[i] = mSocketReader.readLine();
- if (result[i] == null) {
- // We got an unexpected EOF.
- throw new IOException("truncated request");
- }
- }
-
- return result;
- }
-
- /**
- * uid 1000 (Process.SYSTEM_UID) may specify any uid &gt; 1000 in normal
- * operation. It may also specify any gid and setgroups() list it chooses.
- * In factory test mode, it may specify any UID.
- *
- * @param args non-null; zygote spawner arguments
- * @param peer non-null; peer credentials
- * @throws ZygoteSecurityException
- */
- private static void applyUidSecurityPolicy(Arguments args, Credentials peer)
- throws ZygoteSecurityException {
-
- if (peer.getUid() == Process.SYSTEM_UID) {
- /* In normal operation, SYSTEM_UID can only specify a restricted
- * set of UIDs. In factory test mode, SYSTEM_UID may specify any uid.
- */
- boolean uidRestricted = FactoryTest.getMode() == FactoryTest.FACTORY_TEST_OFF;
-
- if (uidRestricted && args.uidSpecified && (args.uid < Process.SYSTEM_UID)) {
- throw new ZygoteSecurityException(
- "System UID may not launch process with UID < "
- + Process.SYSTEM_UID);
- }
- }
-
- // If not otherwise specified, uid and gid are inherited from peer
- if (!args.uidSpecified) {
- args.uid = peer.getUid();
- args.uidSpecified = true;
- }
- if (!args.gidSpecified) {
- args.gid = peer.getGid();
- args.gidSpecified = true;
- }
- }
-
- /**
- * Applies debugger system properties to the zygote arguments.
- *
- * If "ro.debuggable" is "1", all apps are debuggable. Otherwise,
- * the debugger state is specified via the "--enable-jdwp" flag
- * in the spawn request.
- *
- * @param args non-null; zygote spawner args
- */
- public static void applyDebuggerSystemProperty(Arguments args) {
- if (RoSystemProperties.DEBUGGABLE) {
- args.runtimeFlags |= Zygote.DEBUG_ENABLE_JDWP;
- }
- }
-
- /**
- * Applies zygote security policy.
- * Based on the credentials of the process issuing a zygote command:
- * <ol>
- * <li> uid 0 (root) may specify --invoke-with to launch Zygote with a
- * wrapper command.
- * <li> Any other uid may not specify any invoke-with argument.
- * </ul>
- *
- * @param args non-null; zygote spawner arguments
- * @param peer non-null; peer credentials
- * @throws ZygoteSecurityException
- */
- private static void applyInvokeWithSecurityPolicy(Arguments args, Credentials peer)
- throws ZygoteSecurityException {
- int peerUid = peer.getUid();
-
- if (args.invokeWith != null && peerUid != 0 &&
- (args.runtimeFlags & Zygote.DEBUG_ENABLE_JDWP) == 0) {
- throw new ZygoteSecurityException("Peer is permitted to specify an"
- + "explicit invoke-with wrapper command only for debuggable"
- + "applications.");
- }
- }
-
- /**
- * Applies invoke-with system properties to the zygote arguments.
- *
- * @param args non-null; zygote args
- */
- public static void applyInvokeWithSystemProperty(Arguments args) {
- if (args.invokeWith == null && args.niceName != null) {
- String property = "wrap." + args.niceName;
- args.invokeWith = SystemProperties.get(property);
- if (args.invokeWith != null && args.invokeWith.length() == 0) {
- args.invokeWith = null;
- }
- }
- }
-
- /**
* Handles post-fork setup of child proc, closing sockets as appropriate,
* reopen stdio as appropriate, and ultimately throwing MethodAndArgsCaller
* if successful or returning if failed.
@@ -931,7 +390,7 @@ class ZygoteConnection {
* @param pipeFd null-ok; pipe for communication back to Zygote.
* @param isZygote whether this new child process is itself a new Zygote.
*/
- private Runnable handleChildProc(Arguments parsedArgs, FileDescriptor[] descriptors,
+ private Runnable handleChildProc(ZygoteArguments parsedArgs, FileDescriptor[] descriptors,
FileDescriptor pipeFd, boolean isZygote) {
/**
* By the time we get here, the native code has closed the two actual Zygote
@@ -954,27 +413,27 @@ class ZygoteConnection {
}
}
- if (parsedArgs.niceName != null) {
- Process.setArgV0(parsedArgs.niceName);
+ if (parsedArgs.mNiceName != null) {
+ Process.setArgV0(parsedArgs.mNiceName);
}
// End of the postFork event.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
- if (parsedArgs.invokeWith != null) {
- WrapperInit.execApplication(parsedArgs.invokeWith,
- parsedArgs.niceName, parsedArgs.targetSdkVersion,
+ if (parsedArgs.mInvokeWith != null) {
+ WrapperInit.execApplication(parsedArgs.mInvokeWith,
+ parsedArgs.mNiceName, parsedArgs.mTargetSdkVersion,
VMRuntime.getCurrentInstructionSet(),
- pipeFd, parsedArgs.remainingArgs);
+ pipeFd, parsedArgs.mRemainingArgs);
// Should not get here.
throw new IllegalStateException("WrapperInit.execApplication unexpectedly returned");
} else {
if (!isZygote) {
- return ZygoteInit.zygoteInit(parsedArgs.targetSdkVersion, parsedArgs.remainingArgs,
- null /* classLoader */);
+ return ZygoteInit.zygoteInit(parsedArgs.mTargetSdkVersion,
+ parsedArgs.mRemainingArgs, null /* classLoader */);
} else {
- return ZygoteInit.childZygoteInit(parsedArgs.targetSdkVersion,
- parsedArgs.remainingArgs, null /* classLoader */);
+ return ZygoteInit.childZygoteInit(parsedArgs.mTargetSdkVersion,
+ parsedArgs.mRemainingArgs, null /* classLoader */);
}
}
}
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index c2c6ae6712ab..e3e55ed28c6f 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -21,7 +21,6 @@ import static android.system.OsConstants.S_IRWXO;
import android.content.res.Resources;
import android.content.res.TypedArray;
-import android.opengl.EGL14;
import android.os.Build;
import android.os.Environment;
import android.os.IInstalld;
@@ -71,16 +70,16 @@ import java.security.Security;
/**
* Startup class for the zygote process.
*
- * Pre-initializes some classes, and then waits for commands on a UNIX domain
- * socket. Based on these commands, forks off child processes that inherit
- * the initial state of the VM.
+ * Pre-initializes some classes, and then waits for commands on a UNIX domain socket. Based on these
+ * commands, forks off child processes that inherit the initial state of the VM.
*
- * Please see {@link ZygoteConnection.Arguments} for documentation on the
- * client protocol.
+ * Please see {@link ZygoteArguments} for documentation on the client protocol.
*
* @hide
*/
public class ZygoteInit {
+
+ // TODO (chriswailes): Change this so it is set with Zygote or ZygoteSecondary as appropriate
private static final String TAG = "Zygote";
private static final String PROPERTY_DISABLE_OPENGL_PRELOADING = "ro.zygote.disable_gl_preload";
@@ -89,11 +88,15 @@ public class ZygoteInit {
private static final int LOG_BOOT_PROGRESS_PRELOAD_START = 3020;
private static final int LOG_BOOT_PROGRESS_PRELOAD_END = 3030;
- /** when preloading, GC after allocating this many bytes */
+ /**
+ * when preloading, GC after allocating this many bytes
+ */
private static final int PRELOAD_GC_THRESHOLD = 50000;
private static final String ABI_LIST_ARG = "--abi-list=";
+ // TODO (chriswailes): Re-name this --zygote-socket-name= and then add a
+ // --blastula-socket-name parameter.
private static final String SOCKET_NAME_ARG = "--socket-name=";
/**
@@ -106,7 +109,9 @@ public class ZygoteInit {
*/
private static final String PRELOADED_CLASSES = "/system/etc/preloaded-classes";
- /** Controls whether we should preload resources during zygote init. */
+ /**
+ * Controls whether we should preload resources during zygote init.
+ */
public static final boolean PRELOAD_RESOURCES = true;
private static final int UNPRIVILEGED_UID = 9999;
@@ -173,6 +178,7 @@ public class ZygoteInit {
}
native private static void nativePreloadAppProcessHALs();
+
native private static void nativePreloadOpenGL();
private static void preloadOpenGL() {
@@ -191,8 +197,8 @@ public class ZygoteInit {
/**
* Register AndroidKeyStoreProvider and warm up the providers that are already registered.
*
- * By doing it here we avoid that each app does it when requesting a service from the
- * provider for the first time.
+ * By doing it here we avoid that each app does it when requesting a service from the provider
+ * for the first time.
*/
private static void warmUpJcaProviders() {
long startTime = SystemClock.uptimeMillis();
@@ -218,11 +224,10 @@ public class ZygoteInit {
}
/**
- * Performs Zygote process initialization. Loads and initializes
- * commonly used classes.
+ * Performs Zygote process initialization. Loads and initializes commonly used classes.
*
- * Most classes only cause a few hundred bytes to be allocated, but
- * a few will allocate a dozen Kbytes (in one case, 500+K).
+ * Most classes only cause a few hundred bytes to be allocated, but a few will allocate a dozen
+ * Kbytes (in one case, 500+K).
*/
private static void preloadClasses() {
final VMRuntime runtime = VMRuntime.getRuntime();
@@ -263,8 +268,8 @@ public class ZygoteInit {
runtime.setTargetHeapUtilization(0.8f);
try {
- BufferedReader br
- = new BufferedReader(new InputStreamReader(is), 256);
+ BufferedReader br =
+ new BufferedReader(new InputStreamReader(is), Zygote.SOCKET_BUFFER_SIZE);
int count = 0;
String line;
@@ -305,7 +310,7 @@ public class ZygoteInit {
}
Log.i(TAG, "...preloaded " + count + " classes in "
- + (SystemClock.uptimeMillis()-startTime) + "ms.");
+ + (SystemClock.uptimeMillis() - startTime) + "ms.");
} catch (IOException e) {
Log.e(TAG, "Error reading " + PRELOADED_CLASSES + ".", e);
} finally {
@@ -331,11 +336,10 @@ public class ZygoteInit {
}
/**
- * Load in commonly used resources, so they can be shared across
- * processes.
+ * Load in commonly used resources, so they can be shared across processes.
*
- * These tend to be a few Kbytes, but are frequently in the 20-40K
- * range, and occasionally even larger.
+ * These tend to be a few Kbytes, but are frequently in the 20-40K range, and occasionally even
+ * larger.
*/
private static void preloadResources() {
final VMRuntime runtime = VMRuntime.getRuntime();
@@ -352,7 +356,7 @@ public class ZygoteInit {
int N = preloadDrawables(ar);
ar.recycle();
Log.i(TAG, "...preloaded " + N + " resources in "
- + (SystemClock.uptimeMillis()-startTime) + "ms.");
+ + (SystemClock.uptimeMillis() - startTime) + "ms.");
startTime = SystemClock.uptimeMillis();
ar = mResources.obtainTypedArray(
@@ -360,7 +364,7 @@ public class ZygoteInit {
N = preloadColorStateLists(ar);
ar.recycle();
Log.i(TAG, "...preloaded " + N + " resources in "
- + (SystemClock.uptimeMillis()-startTime) + "ms.");
+ + (SystemClock.uptimeMillis() - startTime) + "ms.");
if (mResources.getBoolean(
com.android.internal.R.bool.config_freeformWindowManagement)) {
@@ -381,7 +385,7 @@ public class ZygoteInit {
private static int preloadColorStateLists(TypedArray ar) {
int N = ar.length();
- for (int i=0; i<N; i++) {
+ for (int i = 0; i < N; i++) {
int id = ar.getResourceId(i, 0);
if (false) {
Log.v(TAG, "Preloading resource #" + Integer.toHexString(id));
@@ -390,8 +394,8 @@ public class ZygoteInit {
if (mResources.getColorStateList(id, null) == null) {
throw new IllegalArgumentException(
"Unable to find preloaded color resource #0x"
- + Integer.toHexString(id)
- + " (" + ar.getString(i) + ")");
+ + Integer.toHexString(id)
+ + " (" + ar.getString(i) + ")");
}
}
}
@@ -401,7 +405,7 @@ public class ZygoteInit {
private static int preloadDrawables(TypedArray ar) {
int N = ar.length();
- for (int i=0; i<N; i++) {
+ for (int i = 0; i < N; i++) {
int id = ar.getResourceId(i, 0);
if (false) {
Log.v(TAG, "Preloading resource #" + Integer.toHexString(id));
@@ -410,8 +414,8 @@ public class ZygoteInit {
if (mResources.getDrawable(id, null) == null) {
throw new IllegalArgumentException(
"Unable to find preloaded drawable resource #0x"
- + Integer.toHexString(id)
- + " (" + ar.getString(i) + ")");
+ + Integer.toHexString(id)
+ + " (" + ar.getString(i) + ")");
}
}
}
@@ -419,9 +423,8 @@ public class ZygoteInit {
}
/**
- * Runs several special GCs to try to clean up a few generations of
- * softly- and final-reachable objects, along with any other garbage.
- * This is only useful just before a fork().
+ * Runs several special GCs to try to clean up a few generations of softly- and final-reachable
+ * objects, along with any other garbage. This is only useful just before a fork().
*/
private static void gcAndFinalize() {
ZygoteHooks.gcAndFinalize();
@@ -430,12 +433,12 @@ public class ZygoteInit {
/**
* Finish remaining work for the newly forked system server process.
*/
- private static Runnable handleSystemServerProcess(ZygoteConnection.Arguments parsedArgs) {
+ private static Runnable handleSystemServerProcess(ZygoteArguments parsedArgs) {
// set umask to 0077 so new files and directories will default to owner-only permissions.
Os.umask(S_IRWXG | S_IRWXO);
- if (parsedArgs.niceName != null) {
- Process.setArgV0(parsedArgs.niceName);
+ if (parsedArgs.mNiceName != null) {
+ Process.setArgV0(parsedArgs.mNiceName);
}
final String systemServerClasspath = Os.getenv("SYSTEMSERVERCLASSPATH");
@@ -454,8 +457,8 @@ public class ZygoteInit {
}
}
- if (parsedArgs.invokeWith != null) {
- String[] args = parsedArgs.remainingArgs;
+ if (parsedArgs.mInvokeWith != null) {
+ String[] args = parsedArgs.mRemainingArgs;
// If we have a non-null system server class path, we'll have to duplicate the
// existing arguments and append the classpath to it. ART will handle the classpath
// correctly when we exec a new process.
@@ -467,15 +470,15 @@ public class ZygoteInit {
args = amendedArgs;
}
- WrapperInit.execApplication(parsedArgs.invokeWith,
- parsedArgs.niceName, parsedArgs.targetSdkVersion,
+ WrapperInit.execApplication(parsedArgs.mInvokeWith,
+ parsedArgs.mNiceName, parsedArgs.mTargetSdkVersion,
VMRuntime.getCurrentInstructionSet(), null, args);
throw new IllegalStateException("Unexpected return from WrapperInit.execApplication");
} else {
ClassLoader cl = null;
if (systemServerClasspath != null) {
- cl = createPathClassLoader(systemServerClasspath, parsedArgs.targetSdkVersion);
+ cl = createPathClassLoader(systemServerClasspath, parsedArgs.mTargetSdkVersion);
Thread.currentThread().setContextClassLoader(cl);
}
@@ -483,16 +486,17 @@ public class ZygoteInit {
/*
* Pass the remaining arguments to SystemServer.
*/
- return ZygoteInit.zygoteInit(parsedArgs.targetSdkVersion, parsedArgs.remainingArgs, cl);
+ return ZygoteInit.zygoteInit(parsedArgs.mTargetSdkVersion,
+ parsedArgs.mRemainingArgs, cl);
}
/* should never reach here */
}
/**
- * Note that preparing the profiles for system server does not require special
- * selinux permissions. From the installer perspective the system server is a regular package
- * which can capture profile information.
+ * Note that preparing the profiles for system server does not require special selinux
+ * permissions. From the installer perspective the system server is a regular package which can
+ * capture profile information.
*/
private static void prepareSystemServerProfile(String systemServerClasspath)
throws RemoteException {
@@ -544,8 +548,8 @@ public class ZygoteInit {
}
/**
- * Performs dex-opt on the elements of {@code classPath}, if needed. We
- * choose the instruction set of the current runtime.
+ * Performs dex-opt on the elements of {@code classPath}, if needed. We choose the instruction
+ * set of the current runtime.
*/
private static void performSystemServerDexOpt(String classPath) {
final String[] classPathElements = classPath.split(":");
@@ -563,8 +567,9 @@ public class ZygoteInit {
int dexoptNeeded;
try {
dexoptNeeded = DexFile.getDexOptNeeded(
- classPathElement, instructionSet, systemServerFilter,
- null /* classLoaderContext */, false /* newProfile */, false /* downgrade */);
+ classPathElement, instructionSet, systemServerFilter,
+ null /* classLoaderContext */, false /* newProfile */,
+ false /* downgrade */);
} catch (FileNotFoundException ignored) {
// Do not add to the classpath.
Log.w(TAG, "Missing classpath element for system server: " + classPathElement);
@@ -607,8 +612,8 @@ public class ZygoteInit {
}
/**
- * Encodes the system server class loader context in a format that is accepted by dexopt.
- * This assumes the system server is always loaded with a {@link dalvik.system.PathClassLoader}.
+ * Encodes the system server class loader context in a format that is accepted by dexopt. This
+ * assumes the system server is always loaded with a {@link dalvik.system.PathClassLoader}.
*
* Note that ideally we would use the {@code DexoptUtils} to compute this. However we have no
* dependency here on the server so we hard code the logic again.
@@ -619,10 +624,11 @@ public class ZygoteInit {
/**
* Encodes the class path in a format accepted by dexopt.
- * @param classPath the old class path (may be empty).
- * @param newElement the new class path elements
- * @return the class path encoding resulted from appending {@code newElement} to
- * {@code classPath}.
+ *
+ * @param classPath The old class path (may be empty).
+ * @param newElement The new class path elements
+ * @return The class path encoding resulted from appending {@code newElement} to {@code
+ * classPath}.
*/
private static String encodeSystemServerClassPath(String classPath, String newElement) {
return (classPath == null || classPath.isEmpty())
@@ -633,25 +639,25 @@ public class ZygoteInit {
/**
* Prepare the arguments and forks for the system server process.
*
- * Returns an {@code Runnable} that provides an entrypoint into system_server code in the
- * child process, and {@code null} in the parent.
+ * @return A {@code Runnable} that provides an entrypoint into system_server code in the child
+ * process; {@code null} in the parent.
*/
private static Runnable forkSystemServer(String abiList, String socketName,
ZygoteServer zygoteServer) {
long capabilities = posixCapabilitiesAsBits(
- OsConstants.CAP_IPC_LOCK,
- OsConstants.CAP_KILL,
- OsConstants.CAP_NET_ADMIN,
- OsConstants.CAP_NET_BIND_SERVICE,
- OsConstants.CAP_NET_BROADCAST,
- OsConstants.CAP_NET_RAW,
- OsConstants.CAP_SYS_MODULE,
- OsConstants.CAP_SYS_NICE,
- OsConstants.CAP_SYS_PTRACE,
- OsConstants.CAP_SYS_TIME,
- OsConstants.CAP_SYS_TTY_CONFIG,
- OsConstants.CAP_WAKE_ALARM,
- OsConstants.CAP_BLOCK_SUSPEND
+ OsConstants.CAP_IPC_LOCK,
+ OsConstants.CAP_KILL,
+ OsConstants.CAP_NET_ADMIN,
+ OsConstants.CAP_NET_BIND_SERVICE,
+ OsConstants.CAP_NET_BROADCAST,
+ OsConstants.CAP_NET_RAW,
+ OsConstants.CAP_SYS_MODULE,
+ OsConstants.CAP_SYS_NICE,
+ OsConstants.CAP_SYS_PTRACE,
+ OsConstants.CAP_SYS_TIME,
+ OsConstants.CAP_SYS_TTY_CONFIG,
+ OsConstants.CAP_WAKE_ALARM,
+ OsConstants.CAP_BLOCK_SUSPEND
);
/* Containers run without some capabilities, so drop any caps that are not available. */
StructCapUserHeader header = new StructCapUserHeader(
@@ -666,38 +672,39 @@ public class ZygoteInit {
/* Hardcoded command line to start the system server */
String args[] = {
- "--setuid=1000",
- "--setgid=1000",
- "--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,1021,1023,1024,1032,1065,3001,3002,3003,3006,3007,3009,3010",
- "--capabilities=" + capabilities + "," + capabilities,
- "--nice-name=system_server",
- "--runtime-args",
- "--target-sdk-version=" + VMRuntime.SDK_VERSION_CUR_DEVELOPMENT,
- "com.android.server.SystemServer",
+ "--setuid=1000",
+ "--setgid=1000",
+ "--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,1021,1023,"
+ + "1024,1032,1065,3001,3002,3003,3006,3007,3009,3010",
+ "--capabilities=" + capabilities + "," + capabilities,
+ "--nice-name=system_server",
+ "--runtime-args",
+ "--target-sdk-version=" + VMRuntime.SDK_VERSION_CUR_DEVELOPMENT,
+ "com.android.server.SystemServer",
};
- ZygoteConnection.Arguments parsedArgs = null;
+ ZygoteArguments parsedArgs = null;
int pid;
try {
- parsedArgs = new ZygoteConnection.Arguments(args);
- ZygoteConnection.applyDebuggerSystemProperty(parsedArgs);
- ZygoteConnection.applyInvokeWithSystemProperty(parsedArgs);
+ parsedArgs = new ZygoteArguments(args);
+ Zygote.applyDebuggerSystemProperty(parsedArgs);
+ Zygote.applyInvokeWithSystemProperty(parsedArgs);
boolean profileSystemServer = SystemProperties.getBoolean(
"dalvik.vm.profilesystemserver", false);
if (profileSystemServer) {
- parsedArgs.runtimeFlags |= Zygote.PROFILE_SYSTEM_SERVER;
+ parsedArgs.mRuntimeFlags |= Zygote.PROFILE_SYSTEM_SERVER;
}
/* Request to fork the system server process */
pid = Zygote.forkSystemServer(
- parsedArgs.uid, parsedArgs.gid,
- parsedArgs.gids,
- parsedArgs.runtimeFlags,
+ parsedArgs.mUid, parsedArgs.mGid,
+ parsedArgs.mGids,
+ parsedArgs.mRuntimeFlags,
null,
- parsedArgs.permittedCapabilities,
- parsedArgs.effectiveCapabilities);
+ parsedArgs.mPermittedCapabilities,
+ parsedArgs.mEffectiveCapabilities);
} catch (IllegalArgumentException ex) {
throw new RuntimeException(ex);
}
@@ -743,7 +750,7 @@ public class ZygoteInit {
throw new RuntimeException("Failed to setpgid(0,0)", ex);
}
- final Runnable caller;
+ Runnable caller;
try {
// Report Zygote start time to tron unless it is a runtime restart
if (!"1".equals(SystemProperties.get("sys.boot_completed"))) {
@@ -779,16 +786,26 @@ public class ZygoteInit {
throw new RuntimeException("No ABI list supplied.");
}
- zygoteServer.registerServerSocketFromEnv(socketName);
+ // TODO (chriswailes): Wrap these three calls in a helper function?
+ final String blastulaSocketName =
+ socketName.equals(ZygoteProcess.ZYGOTE_SOCKET_NAME)
+ ? ZygoteProcess.BLASTULA_POOL_SOCKET_NAME
+ : ZygoteProcess.BLASTULA_POOL_SECONDARY_SOCKET_NAME;
+
+ zygoteServer.createZygoteSocket(socketName);
+ Zygote.createBlastulaSocket(blastulaSocketName);
+
+ Zygote.getSocketFDs(socketName.equals(ZygoteProcess.ZYGOTE_SOCKET_NAME));
+
// In some configurations, we avoid preloading resources and classes eagerly.
// In such cases, we will preload things prior to our first fork.
if (!enableLazyPreload) {
bootTimingsTraceLog.traceBegin("ZygotePreload");
EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START,
- SystemClock.uptimeMillis());
+ SystemClock.uptimeMillis());
preload(bootTimingsTraceLog);
EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END,
- SystemClock.uptimeMillis());
+ SystemClock.uptimeMillis());
bootTimingsTraceLog.traceEnd(); // ZygotePreload
} else {
Zygote.resetNicePriority();
@@ -822,11 +839,18 @@ public class ZygoteInit {
}
}
- Log.i(TAG, "Accepting command socket connections");
+ // If the return value is null then this is the zygote process
+ // returning to the normal control flow. If it returns a Runnable
+ // object then this is a blastula that has finished specializing.
+ caller = Zygote.initBlastulaPool();
+
+ if (caller == null) {
+ Log.i(TAG, "Accepting command socket connections");
- // The select loop returns early in the child process after a fork and
- // loops forever in the zygote.
- caller = zygoteServer.runSelectLoop(abiList);
+ // The select loop returns early in the child process after a fork and
+ // loops forever in the zygote.
+ caller = zygoteServer.runSelectLoop(abiList);
+ }
} catch (Throwable ex) {
Log.e(TAG, "System zygote died with exception", ex);
throw ex;
@@ -844,17 +868,16 @@ public class ZygoteInit {
/**
* Return {@code true} if this device configuration has another zygote.
*
- * We determine this by comparing the device ABI list with this zygotes
- * list. If this zygote supports all ABIs this device supports, there won't
- * be another zygote.
+ * We determine this by comparing the device ABI list with this zygotes list. If this zygote
+ * supports all ABIs this device supports, there won't be another zygote.
*/
private static boolean hasSecondZygote(String abiList) {
return !SystemProperties.get("ro.product.cpu.abilist").equals(abiList);
}
private static void waitForSecondaryZygote(String socketName) {
- String otherZygoteName = Process.ZYGOTE_SOCKET.equals(socketName) ?
- Process.SECONDARY_ZYGOTE_SOCKET : Process.ZYGOTE_SOCKET;
+ String otherZygoteName = ZygoteProcess.ZYGOTE_SOCKET_NAME.equals(socketName)
+ ? ZygoteProcess.ZYGOTE_SECONDARY_SOCKET_NAME : ZygoteProcess.ZYGOTE_SOCKET_NAME;
ZygoteProcess.waitForConnectionToZygote(otherZygoteName);
}
@@ -869,9 +892,8 @@ public class ZygoteInit {
}
/**
- * The main function called when started through the zygote process. This
- * could be unified with main(), if the native code in nativeFinishInit()
- * were rationalized with Zygote startup.<p>
+ * The main function called when started through the zygote process. This could be unified with
+ * main(), if the native code in nativeFinishInit() were rationalized with Zygote startup.<p>
*
* Current recognized args:
* <ul>
@@ -881,7 +903,8 @@ public class ZygoteInit {
* @param targetSdkVersion target SDK version
* @param argv arg strings
*/
- public static final Runnable zygoteInit(int targetSdkVersion, String[] argv, ClassLoader classLoader) {
+ public static final Runnable zygoteInit(int targetSdkVersion, String[] argv,
+ ClassLoader classLoader) {
if (RuntimeInit.DEBUG) {
Slog.d(RuntimeInit.TAG, "RuntimeInit: Starting application from zygote");
}
@@ -895,9 +918,9 @@ public class ZygoteInit {
}
/**
- * The main function called when starting a child zygote process. This is used as an
- * alternative to zygoteInit(), which skips calling into initialization routines that
- * start the Binder threadpool.
+ * The main function called when starting a child zygote process. This is used as an alternative
+ * to zygoteInit(), which skips calling into initialization routines that start the Binder
+ * threadpool.
*/
static final Runnable childZygoteInit(
int targetSdkVersion, String[] argv, ClassLoader classLoader) {
diff --git a/core/java/com/android/internal/os/ZygoteServer.java b/core/java/com/android/internal/os/ZygoteServer.java
index fecf9b9da5dd..a78c095a8deb 100644
--- a/core/java/com/android/internal/os/ZygoteServer.java
+++ b/core/java/com/android/internal/os/ZygoteServer.java
@@ -20,14 +20,16 @@ import static android.system.OsConstants.POLLIN;
import android.net.LocalServerSocket;
import android.net.LocalSocket;
-import android.system.Os;
import android.system.ErrnoException;
+import android.system.Os;
import android.system.StructPollfd;
import android.util.Log;
-
import android.util.Slog;
-import java.io.IOException;
+
+import java.io.ByteArrayInputStream;
+import java.io.DataInputStream;
import java.io.FileDescriptor;
+import java.io.IOException;
import java.util.ArrayList;
/**
@@ -40,18 +42,17 @@ import java.util.ArrayList;
* client protocol.
*/
class ZygoteServer {
+ // TODO (chriswailes): Change this so it is set with Zygote or ZygoteSecondary as appropriate
public static final String TAG = "ZygoteServer";
- private static final String ANDROID_SOCKET_PREFIX = "ANDROID_SOCKET_";
-
/**
* Listening socket that accepts new server connections.
*/
- private LocalServerSocket mServerSocket;
+ private LocalServerSocket mZygoteSocket;
/**
- * Whether or not mServerSocket's underlying FD should be closed directly.
- * If mServerSocket is created with an existing FD, closing the socket does
+ * Whether or not mZygoteSocket's underlying FD should be closed directly.
+ * If mZygoteSocket is created with an existing FD, closing the socket does
* not close the FD and it must be closed explicitly. If the socket is created
* with a name instead, then closing the socket will close the underlying FD
* and it should not be double-closed.
@@ -63,39 +64,24 @@ class ZygoteServer {
*/
private boolean mIsForkChild;
- ZygoteServer() {
- }
+ ZygoteServer() { }
void setForkChild() {
mIsForkChild = true;
}
/**
- * Registers a server socket for zygote command connections. This locates the server socket
- * file descriptor through an ANDROID_SOCKET_ environment variable.
+ * Creates a managed object representing the Zygote socket that has already
+ * been initialized and bound by init.
+ *
+ * TODO (chriswailes): Move the name selection logic into this function.
*
* @throws RuntimeException when open fails
*/
- void registerServerSocketFromEnv(String socketName) {
- if (mServerSocket == null) {
- int fileDesc;
- final String fullSocketName = ANDROID_SOCKET_PREFIX + socketName;
- try {
- String env = System.getenv(fullSocketName);
- fileDesc = Integer.parseInt(env);
- } catch (RuntimeException ex) {
- throw new RuntimeException(fullSocketName + " unset or invalid", ex);
- }
-
- try {
- FileDescriptor fd = new FileDescriptor();
- fd.setInt$(fileDesc);
- mServerSocket = new LocalServerSocket(fd);
- mCloseSocketFd = true;
- } catch (IOException ex) {
- throw new RuntimeException(
- "Error binding to local socket '" + fileDesc + "'", ex);
- }
+ void createZygoteSocket(String socketName) {
+ if (mZygoteSocket == null) {
+ mZygoteSocket = Zygote.createManagedSocketFromInitSocket(socketName);
+ mCloseSocketFd = true;
}
}
@@ -104,9 +90,9 @@ class ZygoteServer {
* at the specified name in the abstract socket namespace.
*/
void registerServerSocketAtAbstractName(String socketName) {
- if (mServerSocket == null) {
+ if (mZygoteSocket == null) {
try {
- mServerSocket = new LocalServerSocket(socketName);
+ mZygoteSocket = new LocalServerSocket(socketName);
mCloseSocketFd = false;
} catch (IOException ex) {
throw new RuntimeException(
@@ -121,7 +107,7 @@ class ZygoteServer {
*/
private ZygoteConnection acceptCommandPeer(String abiList) {
try {
- return createNewConnection(mServerSocket.accept(), abiList);
+ return createNewConnection(mZygoteSocket.accept(), abiList);
} catch (IOException ex) {
throw new RuntimeException(
"IOException during accept()", ex);
@@ -139,9 +125,9 @@ class ZygoteServer {
*/
void closeServerSocket() {
try {
- if (mServerSocket != null) {
- FileDescriptor fd = mServerSocket.getFileDescriptor();
- mServerSocket.close();
+ if (mZygoteSocket != null) {
+ FileDescriptor fd = mZygoteSocket.getFileDescriptor();
+ mZygoteSocket.close();
if (fd != null && mCloseSocketFd) {
Os.close(fd);
}
@@ -152,7 +138,7 @@ class ZygoteServer {
Log.e(TAG, "Zygote: error closing descriptor", ex);
}
- mServerSocket = null;
+ mZygoteSocket = null;
}
/**
@@ -161,8 +147,8 @@ class ZygoteServer {
* closure after a child process is forked off.
*/
- FileDescriptor getServerSocketFileDescriptor() {
- return mServerSocket.getFileDescriptor();
+ FileDescriptor getZygoteSocketFileDescriptor() {
+ return mZygoteSocket.getFileDescriptor();
}
/**
@@ -171,36 +157,67 @@ class ZygoteServer {
* worth at a time.
*/
Runnable runSelectLoop(String abiList) {
- ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>();
+ ArrayList<FileDescriptor> socketFDs = new ArrayList<FileDescriptor>();
ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();
- fds.add(mServerSocket.getFileDescriptor());
+ socketFDs.add(mZygoteSocket.getFileDescriptor());
peers.add(null);
while (true) {
- StructPollfd[] pollFds = new StructPollfd[fds.size()];
- for (int i = 0; i < pollFds.length; ++i) {
- pollFds[i] = new StructPollfd();
- pollFds[i].fd = fds.get(i);
- pollFds[i].events = (short) POLLIN;
+ int[] blastulaPipeFDs = Zygote.getBlastulaPipeFDs();
+
+ // Space for all of the socket FDs, the Blastula Pool Event FD, and
+ // all of the open blastula read pipe FDs.
+ StructPollfd[] pollFDs =
+ new StructPollfd[socketFDs.size() + 1 + blastulaPipeFDs.length];
+
+ int pollIndex = 0;
+ for (FileDescriptor socketFD : socketFDs) {
+ pollFDs[pollIndex] = new StructPollfd();
+ pollFDs[pollIndex].fd = socketFD;
+ pollFDs[pollIndex].events = (short) POLLIN;
+ ++pollIndex;
}
+
+ final int blastulaPoolEventFDIndex = pollIndex;
+ pollFDs[pollIndex] = new StructPollfd();
+ pollFDs[pollIndex].fd = Zygote.sBlastulaPoolEventFD;
+ pollFDs[pollIndex].events = (short) POLLIN;
+ ++pollIndex;
+
+ for (int blastulaPipeFD : blastulaPipeFDs) {
+ FileDescriptor managedFd = new FileDescriptor();
+ managedFd.setInt$(blastulaPipeFD);
+
+ pollFDs[pollIndex] = new StructPollfd();
+ pollFDs[pollIndex].fd = managedFd;
+ pollFDs[pollIndex].events = (short) POLLIN;
+ ++pollIndex;
+ }
+
try {
- Os.poll(pollFds, -1);
+ Os.poll(pollFDs, -1);
} catch (ErrnoException ex) {
throw new RuntimeException("poll failed", ex);
}
- for (int i = pollFds.length - 1; i >= 0; --i) {
- if ((pollFds[i].revents & POLLIN) == 0) {
+
+ while (--pollIndex >= 0) {
+ if ((pollFDs[pollIndex].revents & POLLIN) == 0) {
continue;
}
- if (i == 0) {
+ if (pollIndex == 0) {
+ // Zygote server socket
+
ZygoteConnection newPeer = acceptCommandPeer(abiList);
peers.add(newPeer);
- fds.add(newPeer.getFileDesciptor());
- } else {
+ socketFDs.add(newPeer.getFileDescriptor());
+
+ } else if (pollIndex < blastulaPoolEventFDIndex) {
+ // Session socket accepted from the Zygote server socket
+
try {
- ZygoteConnection connection = peers.get(i);
+ ZygoteConnection connection = peers.get(pollIndex);
final Runnable command = connection.processOneCommand(this);
if (mIsForkChild) {
@@ -218,12 +235,12 @@ class ZygoteServer {
}
// We don't know whether the remote side of the socket was closed or
- // not until we attempt to read from it from processOneCommand. This shows up as
- // a regular POLLIN event in our regular processing loop.
+ // not until we attempt to read from it from processOneCommand. This
+ // shows up as a regular POLLIN event in our regular processing loop.
if (connection.isClosedByPeer()) {
connection.closeSocket();
- peers.remove(i);
- fds.remove(i);
+ peers.remove(pollIndex);
+ socketFDs.remove(pollIndex);
}
}
} catch (Exception e) {
@@ -235,13 +252,13 @@ class ZygoteServer {
Slog.e(TAG, "Exception executing zygote command: ", e);
- // Make sure the socket is closed so that the other end knows immediately
- // that something has gone wrong and doesn't time out waiting for a
- // response.
- ZygoteConnection conn = peers.remove(i);
+ // Make sure the socket is closed so that the other end knows
+ // immediately that something has gone wrong and doesn't time out
+ // waiting for a response.
+ ZygoteConnection conn = peers.remove(pollIndex);
conn.closeSocket();
- fds.remove(i);
+ socketFDs.remove(pollIndex);
} else {
// We're in the child so any exception caught here has happened post
// fork and before we execute ActivityThread.main (or any other main()
@@ -255,6 +272,55 @@ class ZygoteServer {
// is returned.
mIsForkChild = false;
}
+ } else {
+ // Either the blastula pool event FD or a blastula reporting pipe.
+
+ // If this is the event FD the payload will be the number of blastulas removed.
+ // If this is a reporting pipe FD the payload will be the PID of the blastula
+ // that was just specialized.
+ long messagePayload = -1;
+
+ try {
+ byte[] buffer = new byte[Zygote.BLASTULA_MANAGEMENT_MESSAGE_BYTES];
+ int readBytes = Os.read(pollFDs[pollIndex].fd, buffer, 0, buffer.length);
+
+ if (readBytes == Zygote.BLASTULA_MANAGEMENT_MESSAGE_BYTES) {
+ DataInputStream inputStream =
+ new DataInputStream(new ByteArrayInputStream(buffer));
+
+ messagePayload = inputStream.readLong();
+ } else {
+ Log.e(TAG, "Incomplete read from blastula management FD of size "
+ + readBytes);
+ continue;
+ }
+ } catch (Exception ex) {
+ if (pollIndex == blastulaPoolEventFDIndex) {
+ Log.e(TAG, "Failed to read from blastula pool event FD: "
+ + ex.getMessage());
+ } else {
+ Log.e(TAG, "Failed to read from blastula reporting pipe: "
+ + ex.getMessage());
+ }
+
+ continue;
+ }
+
+ if (pollIndex > blastulaPoolEventFDIndex) {
+ Zygote.removeBlastulaTableEntry((int) messagePayload);
+ }
+
+ int[] sessionSocketRawFDs =
+ socketFDs.subList(1, socketFDs.size())
+ .stream()
+ .mapToInt(fd -> fd.getInt$())
+ .toArray();
+
+ final Runnable command = Zygote.fillBlastulaPool(sessionSocketRawFDs);
+
+ if (command != null) {
+ return command;
+ }
}
}
}
diff --git a/core/java/com/android/internal/statusbar/NotificationVisibility.java b/core/java/com/android/internal/statusbar/NotificationVisibility.java
index a7203e7e17d2..24bb789c3cc5 100644
--- a/core/java/com/android/internal/statusbar/NotificationVisibility.java
+++ b/core/java/com/android/internal/statusbar/NotificationVisibility.java
@@ -21,6 +21,8 @@ import android.os.Parcel;
import android.os.Parcelable;
import android.util.Log;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+
import java.util.ArrayDeque;
import java.util.Collection;
@@ -33,18 +35,53 @@ public class NotificationVisibility implements Parcelable {
public int rank;
public int count;
public boolean visible = true;
+ /** The visible location of the notification, could be e.g. notification shade or HUN. */
+ public NotificationLocation location;
/*package*/ int id;
+ /**
+ * The UI location of the notification.
+ *
+ * There is a one-to-one mapping between this enum and
+ * MetricsProto.MetricsEvent.NotificationLocation.
+ */
+ public enum NotificationLocation {
+ LOCATION_UNKNOWN(MetricsEvent.LOCATION_UNKNOWN),
+ LOCATION_FIRST_HEADS_UP(MetricsEvent.LOCATION_FIRST_HEADS_UP), // visible heads-up
+ LOCATION_HIDDEN_TOP(MetricsEvent.LOCATION_HIDDEN_TOP), // hidden/scrolled away on the top
+ LOCATION_MAIN_AREA(MetricsEvent.LOCATION_MAIN_AREA), // visible in the shade
+ // in the bottom stack, and peeking
+ LOCATION_BOTTOM_STACK_PEEKING(MetricsEvent.LOCATION_BOTTOM_STACK_PEEKING),
+ // in the bottom stack, and hidden
+ LOCATION_BOTTOM_STACK_HIDDEN(MetricsEvent.LOCATION_BOTTOM_STACK_HIDDEN),
+ LOCATION_GONE(MetricsEvent.LOCATION_GONE); // the view isn't laid out at all
+
+ private final int mMetricsEventNotificationLocation;
+
+ NotificationLocation(int metricsEventNotificationLocation) {
+ mMetricsEventNotificationLocation = metricsEventNotificationLocation;
+ }
+
+ /**
+ * Returns the field from MetricsEvent.NotificationLocation that corresponds to this object.
+ */
+ public int toMetricsEventEnum() {
+ return mMetricsEventNotificationLocation;
+ }
+ }
+
private NotificationVisibility() {
id = sNexrId++;
}
- private NotificationVisibility(String key, int rank, int count, boolean visibile) {
+ private NotificationVisibility(String key, int rank, int count, boolean visible,
+ NotificationLocation location) {
this();
this.key = key;
this.rank = rank;
this.count = count;
- this.visible = visibile;
+ this.visible = visible;
+ this.location = location;
}
@Override
@@ -54,12 +91,13 @@ public class NotificationVisibility implements Parcelable {
+ " rank=" + rank
+ " count=" + count
+ (visible?" visible":"")
+ + " location=" + location.name()
+ " )";
}
@Override
public NotificationVisibility clone() {
- return obtain(this.key, this.rank, this.count, this.visible);
+ return obtain(this.key, this.rank, this.count, this.visible, this.location);
}
@Override
@@ -89,6 +127,7 @@ public class NotificationVisibility implements Parcelable {
out.writeInt(this.rank);
out.writeInt(this.count);
out.writeInt(this.visible ? 1 : 0);
+ out.writeString(this.location.name());
}
private void readFromParcel(Parcel in) {
@@ -96,18 +135,28 @@ public class NotificationVisibility implements Parcelable {
this.rank = in.readInt();
this.count = in.readInt();
this.visible = in.readInt() != 0;
+ this.location = NotificationLocation.valueOf(in.readString());
}
/**
- * Return a new NotificationVisibility instance from the global pool. Allows us to
- * avoid allocating new objects in many cases.
+ * Create a new NotificationVisibility object.
*/
public static NotificationVisibility obtain(String key, int rank, int count, boolean visible) {
+ return obtain(key, rank, count, visible,
+ NotificationVisibility.NotificationLocation.LOCATION_UNKNOWN);
+ }
+
+ /**
+ * Create a new NotificationVisibility object.
+ */
+ public static NotificationVisibility obtain(String key, int rank, int count, boolean visible,
+ NotificationLocation location) {
NotificationVisibility vo = obtain();
vo.key = key;
vo.rank = rank;
vo.count = count;
vo.visible = visible;
+ vo.location = location;
return vo;
}
diff --git a/core/java/com/android/internal/usb/DumpUtils.java b/core/java/com/android/internal/usb/DumpUtils.java
index 240c2e757faf..32601368f2fb 100644
--- a/core/java/com/android/internal/usb/DumpUtils.java
+++ b/core/java/com/android/internal/usb/DumpUtils.java
@@ -196,6 +196,15 @@ public class DumpUtils {
}
}
+ private static void writeContaminantPresenceStatus(@NonNull DualDumpOutputStream dump,
+ @NonNull String idName, long id, int contaminantPresenceStatus) {
+ if (dump.isProto()) {
+ dump.write(idName, id, contaminantPresenceStatus);
+ } else {
+ dump.write(idName, id,
+ UsbPort.contaminantPresenceStatusToString(contaminantPresenceStatus));
+ }
+ }
public static void writePortStatus(@NonNull DualDumpOutputStream dump, @NonNull String idName,
long id, @NonNull UsbPortStatus status) {
@@ -232,6 +241,10 @@ public class DumpUtils {
dump.end(roleCombinationToken);
}
+ writeContaminantPresenceStatus(dump, "contaminant_presence_status",
+ UsbPortStatusProto.CONTAMINANT_PRESENCE_STATUS,
+ status.getContaminantDetectionStatus());
+
dump.end(token);
}
}
diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java
index 841e5b679f5f..3537465db682 100644
--- a/core/java/com/android/server/SystemConfig.java
+++ b/core/java/com/android/server/SystemConfig.java
@@ -140,6 +140,10 @@ public class SystemConfig {
// without throttling, as read from the configuration files.
final ArraySet<String> mAllowUnthrottledLocation = new ArraySet<>();
+ // These are the packages that are white-listed to be able to retrieve location even when user
+ // location settings are off, for emergency purposes, as read from the configuration files.
+ final ArraySet<String> mAllowIgnoreLocationSettings = new ArraySet<>();
+
// These are the action strings of broadcasts which are whitelisted to
// be delivered anonymously even to apps which target O+.
final ArraySet<String> mAllowImplicitBroadcasts = new ArraySet<>();
@@ -255,6 +259,10 @@ public class SystemConfig {
return mAllowUnthrottledLocation;
}
+ public ArraySet<String> getAllowIgnoreLocationSettings() {
+ return mAllowIgnoreLocationSettings;
+ }
+
public ArraySet<String> getLinkedApps() {
return mLinkedApps;
}
@@ -682,6 +690,20 @@ public class SystemConfig {
}
XmlUtils.skipCurrentTag(parser);
} break;
+ case "allow-ignore-location-settings": {
+ if (allowAll) {
+ String pkgname = parser.getAttributeValue(null, "package");
+ if (pkgname == null) {
+ Slog.w(TAG, "<" + name + "> without package in "
+ + permFile + " at " + parser.getPositionDescription());
+ } else {
+ mAllowIgnoreLocationSettings.add(pkgname);
+ }
+ } else {
+ logNotAllowedInPartition(name, permFile, parser);
+ }
+ XmlUtils.skipCurrentTag(parser);
+ } break;
case "allow-implicit-broadcast": {
if (allowAll) {
String action = parser.getAttributeValue(null, "action");
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 18d9b5aeb1c8..f458299468b1 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -152,6 +152,8 @@ extern int register_android_graphics_text_MeasuredText(JNIEnv* env);
extern int register_android_graphics_text_LineBreaker(JNIEnv *env);
extern int register_android_view_DisplayEventReceiver(JNIEnv* env);
extern int register_android_view_DisplayListCanvas(JNIEnv* env);
+extern int register_android_view_InputApplicationHandle(JNIEnv* env);
+extern int register_android_view_InputWindowHandle(JNIEnv* env);
extern int register_android_view_TextureLayer(JNIEnv* env);
extern int register_android_view_RenderNode(JNIEnv* env);
extern int register_android_view_RenderNodeAnimator(JNIEnv* env);
@@ -1370,6 +1372,8 @@ static const RegJNIRec gRegJNI[] = {
REG_JNI(register_android_view_RenderNode),
REG_JNI(register_android_view_RenderNodeAnimator),
REG_JNI(register_android_view_DisplayListCanvas),
+ REG_JNI(register_android_view_InputApplicationHandle),
+ REG_JNI(register_android_view_InputWindowHandle),
REG_JNI(register_android_view_TextureLayer),
REG_JNI(register_android_view_ThreadedRenderer),
REG_JNI(register_android_view_Surface),
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index ad51c4701d84..5de088397690 100755
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -19,6 +19,7 @@
#include <hwui/Paint.h>
#include <hwui/Bitmap.h>
#include <renderthread/RenderProxy.h>
+#include <utils/Color.h>
#include <android_runtime/android_hardware_HardwareBuffer.h>
@@ -602,6 +603,14 @@ static jint Bitmap_getGenerationId(JNIEnv* env, jobject, jlong bitmapHandle) {
return static_cast<jint>(bitmap->getGenerationID());
}
+static jboolean Bitmap_isConfigF16(JNIEnv* env, jobject, jlong bitmapHandle) {
+ LocalScopedBitmap bitmap(bitmapHandle);
+ if (bitmap->info().colorType() == kRGBA_F16_SkColorType) {
+ return JNI_TRUE;
+ }
+ return JNI_FALSE;
+}
+
static jboolean Bitmap_isPremultiplied(JNIEnv* env, jobject, jlong bitmapHandle) {
LocalScopedBitmap bitmap(bitmapHandle);
if (bitmap->info().alphaType() == kPremul_SkAlphaType) {
@@ -1120,7 +1129,8 @@ static jobject Bitmap_createHardwareBitmap(JNIEnv* env, jobject, jobject graphic
sp<GraphicBuffer> buffer(graphicBufferForJavaObject(env, graphicBuffer));
// To support any color space, we need to pass an additional ColorSpace argument to
// java Bitmap.createHardwareBitmap.
- sk_sp<Bitmap> bitmap = Bitmap::createFrom(buffer, SkColorSpace::MakeSRGB());
+ SkColorType ct = uirenderer::PixelFormatToColorType(buffer->getPixelFormat());
+ sk_sp<Bitmap> bitmap = Bitmap::createFrom(buffer, ct, SkColorSpace::MakeSRGB());
if (!bitmap.get()) {
ALOGW("failed to create hardware bitmap from graphic buffer");
return NULL;
@@ -1133,7 +1143,8 @@ static jobject Bitmap_wrapHardwareBufferBitmap(JNIEnv* env, jobject, jobject har
AHardwareBuffer* hwBuf = android_hardware_HardwareBuffer_getNativeHardwareBuffer(env,
hardwareBuffer);
sp<GraphicBuffer> buffer(AHardwareBuffer_to_GraphicBuffer(hwBuf));
- sk_sp<Bitmap> bitmap = Bitmap::createFrom(buffer,
+ SkColorType ct = uirenderer::PixelFormatToColorType(buffer->getPixelFormat());
+ sk_sp<Bitmap> bitmap = Bitmap::createFrom(buffer, ct,
GraphicsJNI::getNativeColorSpace(colorSpacePtr));
if (!bitmap.get()) {
ALOGW("failed to create hardware bitmap from hardware buffer");
@@ -1193,6 +1204,7 @@ static const JNINativeMethod gBitmapMethods[] = {
{ "nativeErase", "(JJJ)V", (void*)Bitmap_eraseLong },
{ "nativeRowBytes", "(J)I", (void*)Bitmap_rowBytes },
{ "nativeConfig", "(J)I", (void*)Bitmap_config },
+ { "nativeIsConfigF16", "(J)Z", (void*)Bitmap_isConfigF16 },
{ "nativeHasAlpha", "(J)Z", (void*)Bitmap_hasAlpha },
{ "nativeIsPremultiplied", "(J)Z", (void*)Bitmap_isPremultiplied},
{ "nativeSetHasAlpha", "(JZZ)V", (void*)Bitmap_setHasAlpha},
diff --git a/core/jni/android_hardware_input_InputApplicationHandle.cpp b/core/jni/android_hardware_input_InputApplicationHandle.cpp
index 10005ddcb5ef..1ab58432c686 100644
--- a/core/jni/android_hardware_input_InputApplicationHandle.cpp
+++ b/core/jni/android_hardware_input_InputApplicationHandle.cpp
@@ -93,7 +93,7 @@ bool NativeInputApplicationHandle::updateInfo() {
// --- Global functions ---
-sp<InputApplicationHandle> android_server_InputApplicationHandle_getHandle(
+sp<InputApplicationHandle> android_view_InputApplicationHandle_getHandle(
JNIEnv* env, jobject inputApplicationHandleObj) {
if (!inputApplicationHandleObj) {
return NULL;
@@ -108,7 +108,7 @@ sp<InputApplicationHandle> android_server_InputApplicationHandle_getHandle(
} else {
jweak objWeak = env->NewWeakGlobalRef(inputApplicationHandleObj);
handle = new NativeInputApplicationHandle(objWeak);
- handle->incStrong((void*)android_server_InputApplicationHandle_getHandle);
+ handle->incStrong((void*)android_view_InputApplicationHandle_getHandle);
env->SetLongField(inputApplicationHandleObj, gInputApplicationHandleClassInfo.ptr,
reinterpret_cast<jlong>(handle));
}
@@ -118,7 +118,7 @@ sp<InputApplicationHandle> android_server_InputApplicationHandle_getHandle(
// --- JNI ---
-static void android_server_InputApplicationHandle_nativeDispose(JNIEnv* env, jobject obj) {
+static void android_view_InputApplicationHandle_nativeDispose(JNIEnv* env, jobject obj) {
AutoMutex _l(gHandleMutex);
jlong ptr = env->GetLongField(obj, gInputApplicationHandleClassInfo.ptr);
@@ -126,7 +126,7 @@ static void android_server_InputApplicationHandle_nativeDispose(JNIEnv* env, job
env->SetLongField(obj, gInputApplicationHandleClassInfo.ptr, 0);
NativeInputApplicationHandle* handle = reinterpret_cast<NativeInputApplicationHandle*>(ptr);
- handle->decStrong((void*)android_server_InputApplicationHandle_getHandle);
+ handle->decStrong((void*)android_view_InputApplicationHandle_getHandle);
}
}
@@ -134,7 +134,7 @@ static void android_server_InputApplicationHandle_nativeDispose(JNIEnv* env, job
static const JNINativeMethod gInputApplicationHandleMethods[] = {
/* name, signature, funcPtr */
{ "nativeDispose", "()V",
- (void*) android_server_InputApplicationHandle_nativeDispose },
+ (void*) android_view_InputApplicationHandle_nativeDispose },
};
#define FIND_CLASS(var, className) \
@@ -145,7 +145,7 @@ static const JNINativeMethod gInputApplicationHandleMethods[] = {
var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \
LOG_FATAL_IF(! (var), "Unable to find field " fieldName);
-int register_android_server_InputApplicationHandle(JNIEnv* env) {
+int register_android_view_InputApplicationHandle(JNIEnv* env) {
int res = jniRegisterNativeMethods(env, "android/view/InputApplicationHandle",
gInputApplicationHandleMethods, NELEM(gInputApplicationHandleMethods));
(void) res; // Faked use when LOG_NDEBUG.
diff --git a/core/jni/android_hardware_input_InputApplicationHandle.h b/core/jni/android_hardware_input_InputApplicationHandle.h
index 711561150e51..5abeab454141 100644
--- a/core/jni/android_hardware_input_InputApplicationHandle.h
+++ b/core/jni/android_hardware_input_InputApplicationHandle.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef _ANDROID_SERVER_INPUT_APPLICATION_HANDLE_H
-#define _ANDROID_SERVER_INPUT_APPLICATION_HANDLE_H
+#ifndef _ANDROID_VIEW_INPUT_APPLICATION_HANDLE_H
+#define _ANDROID_VIEW_INPUT_APPLICATION_HANDLE_H
#include <string>
@@ -40,9 +40,9 @@ private:
};
-extern sp<InputApplicationHandle> android_server_InputApplicationHandle_getHandle(
+extern sp<InputApplicationHandle> android_view_InputApplicationHandle_getHandle(
JNIEnv* env, jobject inputApplicationHandleObj);
} // namespace android
-#endif // _ANDROID_SERVER_INPUT_APPLICATION_HANDLE_H
+#endif // _ANDROID_VIEW_INPUT_APPLICATION_HANDLE_H
diff --git a/core/jni/android_hardware_input_InputWindowHandle.cpp b/core/jni/android_hardware_input_InputWindowHandle.cpp
index 76920f57ed08..c0e45b12cf76 100644
--- a/core/jni/android_hardware_input_InputWindowHandle.cpp
+++ b/core/jni/android_hardware_input_InputWindowHandle.cpp
@@ -159,7 +159,7 @@ bool NativeInputWindowHandle::updateInfo() {
gInputWindowHandleClassInfo.inputApplicationHandle);
if (inputApplicationHandleObj) {
sp<InputApplicationHandle> inputApplicationHandle =
- android_server_InputApplicationHandle_getHandle(env, inputApplicationHandleObj);
+ android_view_InputApplicationHandle_getHandle(env, inputApplicationHandleObj);
if (inputApplicationHandle != nullptr) {
inputApplicationHandle->updateInfo();
mInfo.applicationInfo = *(inputApplicationHandle->getInfo());
@@ -174,7 +174,7 @@ bool NativeInputWindowHandle::updateInfo() {
// --- Global functions ---
-sp<NativeInputWindowHandle> android_server_InputWindowHandle_getHandle(
+sp<NativeInputWindowHandle> android_view_InputWindowHandle_getHandle(
JNIEnv* env, jobject inputWindowHandleObj) {
if (!inputWindowHandleObj) {
return NULL;
@@ -189,7 +189,7 @@ sp<NativeInputWindowHandle> android_server_InputWindowHandle_getHandle(
} else {
jweak objWeak = env->NewWeakGlobalRef(inputWindowHandleObj);
handle = new NativeInputWindowHandle(objWeak);
- handle->incStrong((void*)android_server_InputWindowHandle_getHandle);
+ handle->incStrong((void*)android_view_InputWindowHandle_getHandle);
env->SetLongField(inputWindowHandleObj, gInputWindowHandleClassInfo.ptr,
reinterpret_cast<jlong>(handle));
}
@@ -199,7 +199,7 @@ sp<NativeInputWindowHandle> android_server_InputWindowHandle_getHandle(
// --- JNI ---
-static void android_server_InputWindowHandle_nativeDispose(JNIEnv* env, jobject obj) {
+static void android_view_InputWindowHandle_nativeDispose(JNIEnv* env, jobject obj) {
AutoMutex _l(gHandleMutex);
jlong ptr = env->GetLongField(obj, gInputWindowHandleClassInfo.ptr);
@@ -207,7 +207,7 @@ static void android_server_InputWindowHandle_nativeDispose(JNIEnv* env, jobject
env->SetLongField(obj, gInputWindowHandleClassInfo.ptr, 0);
NativeInputWindowHandle* handle = reinterpret_cast<NativeInputWindowHandle*>(ptr);
- handle->decStrong((void*)android_server_InputWindowHandle_getHandle);
+ handle->decStrong((void*)android_view_InputWindowHandle_getHandle);
}
}
@@ -215,7 +215,7 @@ static void android_server_InputWindowHandle_nativeDispose(JNIEnv* env, jobject
static const JNINativeMethod gInputWindowHandleMethods[] = {
/* name, signature, funcPtr */
{ "nativeDispose", "()V",
- (void*) android_server_InputWindowHandle_nativeDispose },
+ (void*) android_view_InputWindowHandle_nativeDispose },
};
#define FIND_CLASS(var, className) \
@@ -226,7 +226,7 @@ static const JNINativeMethod gInputWindowHandleMethods[] = {
var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \
LOG_FATAL_IF(! (var), "Unable to find field " fieldName);
-int register_android_server_InputWindowHandle(JNIEnv* env) {
+int register_android_view_InputWindowHandle(JNIEnv* env) {
int res = jniRegisterNativeMethods(env, "android/view/InputWindowHandle",
gInputWindowHandleMethods, NELEM(gInputWindowHandleMethods));
(void) res; // Faked use when LOG_NDEBUG.
diff --git a/core/jni/android_hardware_input_InputWindowHandle.h b/core/jni/android_hardware_input_InputWindowHandle.h
index 54b89f5b7918..de5bd6ef97f4 100644
--- a/core/jni/android_hardware_input_InputWindowHandle.h
+++ b/core/jni/android_hardware_input_InputWindowHandle.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef _ANDROID_SERVER_INPUT_WINDOW_HANDLE_H
-#define _ANDROID_SERVER_INPUT_WINDOW_HANDLE_H
+#ifndef _ANDROID_VIEW_INPUT_WINDOW_HANDLE_H
+#define _ANDROID_VIEW_INPUT_WINDOW_HANDLE_H
#include <input/InputWindow.h>
@@ -38,9 +38,9 @@ private:
};
-extern sp<NativeInputWindowHandle> android_server_InputWindowHandle_getHandle(
+extern sp<NativeInputWindowHandle> android_view_InputWindowHandle_getHandle(
JNIEnv* env, jobject inputWindowHandleObj);
} // namespace android
-#endif // _ANDROID_SERVER_INPUT_WINDOW_HANDLE_H
+#endif // _ANDROID_VIEW_INPUT_WINDOW_HANDLE_H
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 0453195e6a1d..69877c7d3930 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -360,7 +360,7 @@ static void nativeSetInputWindowInfo(JNIEnv* env, jclass clazz, jlong transactio
jlong nativeObject, jobject inputWindow) {
auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
- sp<NativeInputWindowHandle> handle = android_server_InputWindowHandle_getHandle(
+ sp<NativeInputWindowHandle> handle = android_view_InputWindowHandle_getHandle(
env, inputWindow);
handle->updateInfo();
diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp
index 318ec9b2ff0d..40529191a42c 100644
--- a/core/jni/android_view_ThreadedRenderer.cpp
+++ b/core/jni/android_view_ThreadedRenderer.cpp
@@ -1038,8 +1038,9 @@ static jobject android_view_ThreadedRenderer_createHardwareBitmapFromRenderNode(
// Continue I guess?
}
+ SkColorType ct = uirenderer::PixelFormatToColorType(buffer->getPixelFormat());
sk_sp<SkColorSpace> cs = uirenderer::DataSpaceToColorSpace(bufferItem.mDataSpace);
- sk_sp<Bitmap> bitmap = Bitmap::createFrom(buffer, cs);
+ sk_sp<Bitmap> bitmap = Bitmap::createFrom(buffer, ct, cs);
return bitmap::createBitmap(env, bitmap.release(),
android::bitmap::kBitmapCreateFlag_Premultiplied);
}
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 2e7184b4f0fb..8681d4b3f42e 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -1472,7 +1472,7 @@ static jint com_android_internal_os_Zygote_nativeForkAndSpecialize(
fds_to_close.insert(fds_to_close.end(), blastula_pipes.begin(), blastula_pipes.end());
fds_to_ignore.insert(fds_to_ignore.end(), blastula_pipes.begin(), blastula_pipes.end());
-// fds_to_close.push_back(gBlastulaPoolSocketFD);
+ fds_to_close.push_back(gBlastulaPoolSocketFD);
if (gBlastulaPoolEventFD != -1) {
fds_to_close.push_back(gBlastulaPoolEventFD);
@@ -1498,7 +1498,7 @@ static jint com_android_internal_os_Zygote_nativeForkSystemServer(
std::vector<int> fds_to_close(MakeBlastulaPipeReadFDVector()),
fds_to_ignore(fds_to_close);
-// fds_to_close.push_back(gBlastulaPoolSocketFD);
+ fds_to_close.push_back(gBlastulaPoolSocketFD);
if (gBlastulaPoolEventFD != -1) {
fds_to_close.push_back(gBlastulaPoolEventFD);
diff --git a/core/jni/fd_utils.cpp b/core/jni/fd_utils.cpp
index 53dde80edd89..4e486630adae 100644
--- a/core/jni/fd_utils.cpp
+++ b/core/jni/fd_utils.cpp
@@ -72,6 +72,7 @@ bool FileDescriptorWhitelist::IsAllowed(const std::string& path) const {
return true;
}
+ // Framework jars are allowed.
static const char* kFrameworksPrefix = "/system/framework/";
static const char* kJarSuffix = ".jar";
if (android::base::StartsWith(path, kFrameworksPrefix)
@@ -79,6 +80,13 @@ bool FileDescriptorWhitelist::IsAllowed(const std::string& path) const {
return true;
}
+ // Jars from the runtime apex are allowed.
+ static const char* kRuntimeApexPrefix = "/apex/com.android.runtime/javalib/";
+ if (android::base::StartsWith(path, kRuntimeApexPrefix)
+ && android::base::EndsWith(path, kJarSuffix)) {
+ return true;
+ }
+
// Whitelist files needed for Runtime Resource Overlay, like these:
// /system/vendor/overlay/framework-res.apk
// /system/vendor/overlay-subdir/pg/framework-res.apk
diff --git a/core/proto/android/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto
index f68c760a9dbb..eb716ac280e2 100644
--- a/core/proto/android/app/settings_enums.proto
+++ b/core/proto/android/app/settings_enums.proto
@@ -2179,4 +2179,14 @@ enum PageId {
// OPEN: Settings > Network & internet > Click Mobile network to land on a page with a list of
// SIM/eSIM subscriptions.
MOBILE_NETWORK_LIST = 1627;
+
+ // OPEN: Settings > Display > Adaptive sleep
+ // OS: Q
+ SETTINGS_ADAPTIVE_SLEEP = 1628;
+
+ // OPEN: Settings > System > Aware
+ SETTINGS_AWARE = 1632;
+
+ // OPEN: Settings > System > Aware > Disable > Dialog
+ DIALOG_AWARE_DISABLE = 1633;
}
diff --git a/core/proto/android/providers/settings/global.proto b/core/proto/android/providers/settings/global.proto
index 415857771899..f06165cc7e00 100644
--- a/core/proto/android/providers/settings/global.proto
+++ b/core/proto/android/providers/settings/global.proto
@@ -449,6 +449,10 @@ message GlobalSettingsProto {
optional SettingProto gup_dev_opt_out_apps = 10;
// GUP - List of Apps that are forbidden to use Game Update Package
optional SettingProto gup_blacklist = 11;
+ // List of Apps that are allowed to use Game Driver package.
+ optional SettingProto game_driver_whitelist = 12;
+ // ANGLE - List of Apps that can check ANGLE rules
+ optional SettingProto angle_whitelist = 13;
}
optional Gpu gpu = 59;
@@ -535,6 +539,16 @@ message GlobalSettingsProto {
// Whether automatic battery saver mode is controlled via percentage,
// {@link #DYNAMIC_POWER_SAVINGS_ENABLED} or disabled.
optional SettingProto automatic_power_saver_mode = 4 [ (android.privacy).dest = DEST_AUTOMATIC];
+ // If 1, battery saver (low_power_mode) will be re-activated after the device is
+ // unplugged from a charger or rebooted.
+ optional SettingProto sticky_enabled = 5;
+ // Whether sticky battery saver should be deactivated once the battery level has reached the
+ // threshold specified by sticky_disable_level.
+ optional SettingProto sticky_auto_disable_enabled = 6;
+ // When a device is unplugged from a changer (or is rebooted), do not re-activate battery
+ // saver even if {@link #LOW_POWER_MODE_STICKY} is 1, if the battery level is equal to or
+ // above this threshold.
+ optional SettingProto sticky_auto_disable_level = 7;
}
optional LowPowerMode low_power_mode = 70;
@@ -731,8 +745,7 @@ message GlobalSettingsProto {
// Defines global runtime overrides to window policy.
optional SettingProto policy_control = 92;
optional SettingProto power_manager_constants = 93;
- // If true, out-of-the-box execution for priv apps is enabled.
- optional SettingProto priv_app_oob_enabled = 94 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ reserved 94; // Used to be priv_app_oob_enabled
message PrepaidSetup {
option (android.msg_privacy).dest = DEST_EXPLICIT;
diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto
index 4bfd4d236abb..aaf6c63b2978 100644
--- a/core/proto/android/providers/settings/secure.proto
+++ b/core/proto/android/providers/settings/secure.proto
@@ -528,8 +528,11 @@ message SecureSettingsProto {
optional SettingProto skip_gesture_enabled = 74 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto silence_gesture_enabled = 75 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ optional SettingProto theme_customization_overlay_packages = 76 [ (android.privacy).dest = DEST_AUTOMATIC ];
+
+ optional SettingProto aware_enabled = 77 [ (android.privacy).dest = DEST_AUTOMATIC ];
// Please insert fields in alphabetical order and group them into messages
// if possible (to avoid reaching the method limit).
- // Next tag = 76;
+ // Next tag = 78;
}
diff --git a/core/proto/android/server/connectivity/data_stall_event.proto b/core/proto/android/server/connectivity/data_stall_event.proto
index b70bb677d7a0..21717d886266 100644
--- a/core/proto/android/server/connectivity/data_stall_event.proto
+++ b/core/proto/android/server/connectivity/data_stall_event.proto
@@ -41,7 +41,7 @@ enum RadioTech {
RADIO_TECHNOLOGY_UMTS = 3;
RADIO_TECHNOLOGY_IS95A = 4;
RADIO_TECHNOLOGY_IS95B = 5;
- RADIO_TECHNOLOGY_1xRTT = 6;
+ RADIO_TECHNOLOGY_1XRTT = 6;
RADIO_TECHNOLOGY_EVDO_0 = 7;
RADIO_TECHNOLOGY_EVDO_A = 8;
RADIO_TECHNOLOGY_HSDPA = 9;
diff --git a/core/proto/android/server/jobscheduler.proto b/core/proto/android/server/jobscheduler.proto
index e68f9dbbc9b7..188769d930d1 100644
--- a/core/proto/android/server/jobscheduler.proto
+++ b/core/proto/android/server/jobscheduler.proto
@@ -31,6 +31,7 @@ import "frameworks/base/core/proto/android/os/persistablebundle.proto";
import "frameworks/base/core/proto/android/server/forceappstandbytracker.proto";
import "frameworks/base/libs/incident/proto/android/privacy.proto";
+// Next tag: 21
message JobSchedulerServiceDumpProto {
option (.android.msg_privacy).dest = DEST_AUTOMATIC;
@@ -139,9 +140,13 @@ message JobSchedulerServiceDumpProto {
// The current limit on the number of concurrent JobServiceContext entries
// we want to keep actively running a job.
optional int32 max_active_jobs = 13;
+
+ // Dump from JobConcurrencyManager.
+ optional JobConcurrencyManagerProto concurrency_manager = 20;
}
// A com.android.server.job.JobSchedulerService.Constants object.
+// Next tag: 29
message ConstantsProto {
option (.android.msg_privacy).dest = DEST_AUTOMATIC;
@@ -273,7 +278,39 @@ message ConstantsProto {
}
optional QuotaController quota_controller = 24;
- // Next tag: 26
+ // Max number of jobs, when screen is ON.
+ optional MaxJobCountsPerMemoryTrimLevelProto max_job_counts_screen_on = 26;
+
+ // Max number of jobs, when screen is OFF.
+ optional MaxJobCountsPerMemoryTrimLevelProto max_job_counts_screen_off = 27;
+
+ // In this time after screen turns on, we increase job concurrency.
+ optional int32 screen_off_job_concurrency_increase_delay_ms = 28;
+}
+
+// Next tag: 4
+message MaxJobCountsProto {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ // Total number of jobs to run simultaneously.
+ optional int32 total_jobs = 1;
+
+ // Max number of BG (== owned by non-TOP apps) jobs to run simultaneously.
+ optional int32 max_bg = 2;
+
+ // We try to run at least this many BG (== owned by non-TOP apps) jobs, when there are any
+ // pending, rather than always running the TOTAL number of FG jobs.
+ optional int32 min_bg = 3;
+}
+
+// Next tag: 5
+message MaxJobCountsPerMemoryTrimLevelProto {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ optional MaxJobCountsProto normal = 1;
+ optional MaxJobCountsProto moderate = 2;
+ optional MaxJobCountsProto low = 3;
+ optional MaxJobCountsProto critical = 4;
}
message StateControllerProto {
@@ -807,3 +844,46 @@ message JobStatusDumpProto {
// Next tag: 28
}
+
+// Dump from com.android.server.job.JobConcurrencyManager.
+// Next tag: 7
+message JobConcurrencyManagerProto {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ // Whether the device is interactive (== screen on) now or not.
+ optional bool current_interactive = 1;
+ // Similar to current_interactive, screen on or not, but it takes into account the off timeout.
+ optional bool effective_interactive = 2;
+ // How many milliseconds have passed since the last screen on. (i.e. 1000 == 1 sec ago)
+ optional int64 time_since_last_screen_on_ms = 3;
+ // How many milliseconds have passed since the last screen off. (i.e. 1000 == 1 sec ago)
+ optional int64 time_since_last_screen_off_ms = 4;
+ // Current max number of jobs.
+ optional JobCountTrackerProto job_count_tracker = 5;
+ // Current memory trim level.
+ optional int32 memory_trim_level = 6;
+}
+
+// Dump from com.android.server.job.JobConcurrencyManager.JobCountTracker.
+// Next tag: 8
+message JobCountTrackerProto {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ // Number of total jos that can run simultaneously.
+ optional int32 config_num_max_total_jobs = 1;
+ // Number of background jos that can run simultaneously.
+ optional int32 config_num_max_bg_jobs = 2;
+ // Out of total jobs, this many background jobs should be guaranteed to be executed, even if
+ // there are the config_num_max_total_jobs count of foreground jobs pending.
+ optional int32 config_num_min_bg_jobs = 3;
+
+ // Number of running foreground jobs.
+ optional int32 num_running_fg_jobs = 4;
+ // Number of running background jobs.
+ optional int32 num_running_bg_jobs = 5;
+
+ // Number of pending foreground jobs.
+ optional int32 num_pending_fg_jobs = 6;
+ // Number of pending background jobs.
+ optional int32 num_pending_bg_jobs = 7;
+}
diff --git a/core/proto/android/server/powermanagerservice.proto b/core/proto/android/server/powermanagerservice.proto
index cee556a7b67f..af0a942dea2f 100644
--- a/core/proto/android/server/powermanagerservice.proto
+++ b/core/proto/android/server/powermanagerservice.proto
@@ -350,4 +350,12 @@ message BatterySaverStateMachineProto {
// The value of Global.LOW_POWER_MODE_TRIGGER_LEVEL. This is a cached value, so it could
// be slightly different from what's in GlobalSettingsProto.LowPowerMode.
optional int32 setting_battery_saver_trigger_threshold = 11;
+
+ // The value of Global.LOW_POWER_MODE_STICKY_AUTO_DISABLE_ENABLED. This is a cached value, so
+ // it could be slightly different from what's in GlobalSettingsProto.LowPowerMode.
+ optional bool setting_battery_saver_sticky_auto_disable_enabled = 12;
+
+ // The value of Global.LOW_POWER_MODE_STICKY_AUTO_DISABLE_LEVEL. This is a cached value, so it
+ // could be slightly different from what's in GlobalSettingsProto.LowPowerMode.
+ optional int32 setting_battery_saver_sticky_auto_disable_threshold = 13;
}
diff --git a/core/proto/android/service/usb.proto b/core/proto/android/service/usb.proto
index f7dcee282a57..367c54086ade 100644
--- a/core/proto/android/service/usb.proto
+++ b/core/proto/android/service/usb.proto
@@ -228,6 +228,15 @@ message UsbPortProto {
repeated Mode supported_modes = 2;
}
+/* Same as android.hardware.usb.V1_2.Constants.ContaminantPresenceStatus */
+enum ContaminantPresenceStatus {
+ CONTAMINANT_STATUS_UNKNOWN = 0;
+ CONTAMINANT_STATUS_NOT_SUPPORTED = 1;
+ CONTAMINANT_STATUS_DISABLED = 2;
+ CONTAMINANT_STATUS_NOT_DETECTED = 3;
+ CONTAMINANT_STATUS_DETECTED = 4;
+}
+
message UsbPortStatusProto {
option (android.msg_privacy).dest = DEST_AUTOMATIC;
@@ -250,6 +259,7 @@ message UsbPortStatusProto {
optional PowerRole power_role = 3;
optional DataRole data_role = 4;
repeated UsbPortStatusRoleCombinationProto role_combinations = 5;
+ optional ContaminantPresenceStatus contaminant_presence_status = 6;
}
message UsbPortStatusRoleCombinationProto {
diff --git a/core/proto/android/stats/devicepolicy/device_policy_enums.proto b/core/proto/android/stats/devicepolicy/device_policy_enums.proto
index 82460ec4ed8b..a8e64c6d8324 100644
--- a/core/proto/android/stats/devicepolicy/device_policy_enums.proto
+++ b/core/proto/android/stats/devicepolicy/device_policy_enums.proto
@@ -92,8 +92,7 @@ enum EventId {
SET_UNINSTALL_BLOCKED = 67;
SET_PACKAGES_SUSPENDED = 68;
ON_LOCK_TASK_MODE_ENTERING = 69;
- ADD_CROSS_PROFILE_CALENDAR_PACKAGE = 70;
- REMOVE_CROSS_PROFILE_CALENDAR_PACKAGE = 71;
+ SET_CROSS_PROFILE_CALENDAR_PACKAGES = 70;
GET_USER_PASSWORD_COMPLEXITY_LEVEL = 72;
INSTALL_SYSTEM_UPDATE = 73;
INSTALL_SYSTEM_UPDATE_ERROR = 74;
diff --git a/core/proto/android/view/remote_animation_target.proto b/core/proto/android/view/remote_animation_target.proto
index fb4d5bdd1a7f..808c5143fe8e 100644
--- a/core/proto/android/view/remote_animation_target.proto
+++ b/core/proto/android/view/remote_animation_target.proto
@@ -42,4 +42,6 @@ message RemoteAnimationTargetProto {
optional .android.graphics.PointProto position = 8;
optional .android.graphics.RectProto source_container_bounds = 9;
optional .android.app.WindowConfigurationProto window_configuration = 10;
+ optional .android.view.SurfaceControlProto start_leash = 11;
+ optional .android.graphics.RectProto start_bounds = 12;
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 96b8dc222e7e..6f7312f79720 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -487,6 +487,7 @@
<protected-broadcast android:name="android.security.action.TRUST_STORE_CHANGED" />
<protected-broadcast android:name="android.security.action.KEYCHAIN_CHANGED" />
<protected-broadcast android:name="android.security.action.KEY_ACCESS_CHANGED" />
+ <protected-broadcast android:name="android.telecom.action.NUISANCE_CALL_STATUS_CHANGED" />
<protected-broadcast android:name="android.telecom.action.PHONE_ACCOUNT_REGISTERED" />
<protected-broadcast android:name="android.telecom.action.PHONE_ACCOUNT_UNREGISTERED" />
<protected-broadcast android:name="android.telecom.action.SHOW_MISSED_CALLS_NOTIFICATION" />
@@ -1653,6 +1654,11 @@
<permission android:name="android.permission.WIFI_SET_DEVICE_MOBILITY_STATE"
android:protectionLevel="signature|privileged" />
+ <!-- #SystemApi @hide Allows privileged system APK to update Wifi usability stats and score.
+ <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.WIFI_UPDATE_USABILITY_STATS_SCORE"
+ android:protectionLevel="signature|privileged" />
+
<!-- ======================================= -->
<!-- Permissions for short range, peripheral networks -->
<!-- ======================================= -->
@@ -3522,6 +3528,12 @@
android:protectionLevel="signature|privileged" />
<uses-permission android:name="android.permission.CONTROL_VPN" />
+ <!-- Allows an application to access and modify always-on VPN configuration.
+ <p>Not for use by third-party or privileged applications.
+ @hide -->
+ <permission android:name="android.permission.CONTROL_ALWAYS_ON_VPN"
+ android:protectionLevel="signature" />
+
<!-- Allows an application to capture audio output.
<p>Not for use by third-party applications.</p> -->
<permission android:name="android.permission.CAPTURE_AUDIO_OUTPUT"
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 8ef264a84efb..de6468dbb72a 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -950,6 +950,17 @@
<p>The default value of this attribute is <code>false</code>. -->
<attr name="allowEmbedded" format="boolean" />
+ <!-- @hide @SystemApi Specifies whether this {@link android.app.Activity} should be shown on
+ top of the lock screen whenever the lockscreen is up and this activity has another
+ activity behind it with the {@link android.R.attr#showWhenLocked} attribute set. That
+ is, this activity is only visible on the lock screen if there is another activity with
+ the {@link android.R.attr#showWhenLocked} attribute visible at the same time on the
+ lock screen. A use case for this is permission dialogs, that should only be visible on
+ the lock screen if their requesting activity is also visible.
+
+ <p>The default value of this attribute is <code>false</code>. -->
+ <attr name="inheritShowWhenLocked" format="boolean" />
+
<!-- Descriptive text for the associated data. -->
<attr name="description" format="reference" />
@@ -2415,18 +2426,6 @@
<attr name="showForAllUsers" />
<attr name="showWhenLocked" />
- <!-- @hide @SystemApi Specifies whether this {@link android.app.Activity} should be shown on
- top of the lock screen whenever the lockscreen is up and this activity has another
- activity behind it with the {@link android.R.attr#showWhenLocked} attribute set. That
- is, this activity is only visible on the lock screen if there is another activity with
- the {@link android.R.attr#showWhenLocked} attribute visible at the same time on the
- lock screen. A use case for this is permission dialogs, that should only be visible on
- the lock screen if their requesting activity is also visible.
-
- The default value of this attribute is <code>false</code>. -->
- <attr name="inheritShowWhenLocked" format="boolean" />
-
-
<attr name="inheritShowWhenLocked" />
<attr name="turnScreenOn" />
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 16d1d1a9b90a..49f2c84335c5 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -126,6 +126,9 @@
be sent during a change to the audio output device. -->
<bool name="config_sendAudioBecomingNoisy">true</bool>
+ <!-- Whether Hearing Aid profile is supported -->
+ <bool name="config_hearing_aid_profile_supported">true</bool>
+
<!-- Flag to disable all transition animations -->
<bool name="config_disableTransitionAnimation">false</bool>
@@ -530,6 +533,9 @@
<!-- Boolean indicating whether the wifi chipset has dual frequency band support -->
<bool translatable="false" name="config_wifi_dual_band_support">false</bool>
+ <!-- Maximum number of concurrent WiFi interfaces in AP mode -->
+ <integer translatable="false" name="config_wifi_max_ap_interfaces">1</integer>
+
<!-- Boolean indicating whether the wifi chipset requires the softap band be -->
<!-- converted from 5GHz to ANY due to hardware restrictions -->
<bool translatable="false" name="config_wifi_convert_apband_5ghz_to_any">false</bool>
@@ -951,7 +957,7 @@
<!-- Default mode to control how Night display is automatically activated.
One of the following values (see ColorDisplayController.java):
0 - AUTO_MODE_DISABLED
- 1 - AUTO_MODE_CUSTOM
+ 1 - AUTO_MODE_CUSTOM_TIME
2 - AUTO_MODE_TWILIGHT
-->
<integer name="config_defaultNightDisplayAutoMode">0</integer>
@@ -3723,9 +3729,6 @@
<!-- Whether or not the "SMS app service" feature is enabled -->
<bool name="config_useSmsAppService">true</bool>
- <!-- Component name for default assistant on this device -->
- <string name="config_defaultAssistantComponentName">#+UNSET</string>
-
<!-- Class name for the InputEvent compatibility processor override.
Empty string means use the default compatibility processor
(android.view.InputEventCompatProcessor). -->
@@ -3756,4 +3759,7 @@
<!-- Whether cbrs is supported on the device or not -->
<bool translatable="false" name="config_cbrs_supported">false</bool>
+
+ <!-- Whether or not aware is enabled by default -->
+ <bool name="config_awareSettingAvailable">false</bool>
</resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 777886a9911c..ec1bac1a41d6 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2944,6 +2944,8 @@
</public-group>
<public-group type="style" first-id="0x010302e2">
+ <!-- @hide @SystemApi -->
+ <public name="Theme.DeviceDefault.DocumentsUI" />
</public-group>
<public-group type="id" first-id="0x01020046">
@@ -2986,7 +2988,7 @@
</public-group>
<public-group type="array" first-id="0x01070006">
- <!-- @hide @SystemApi -->
+ <!-- @hide @TestApi @SystemApi -->
<public name="config_defaultRoleHolders" />
</public-group>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index f2b4b9ccfce8..a761baf95b0d 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -3387,6 +3387,9 @@
<!-- A notification is shown when the user connects to a Wi-Fi network and the system detects that that network has no Internet access. This is the notification's message. -->
<string name="wifi_no_internet_detailed">Tap for options</string>
+ <!-- A notification is shown after the user logs in to a captive portal network, to indicate that the network should now have internet connectivity. This is the message of notification. [CHAR LIMIT=50] -->
+ <string name="captive_portal_logged_in_detailed">Connected</string>
+
<!-- A notification is shown when the user's softap config has been changed due to underlying
hardware restrictions. This is the notifications's title.
[CHAR_LIMIT=NONE] -->
@@ -3573,6 +3576,15 @@
<string name="adb_active_notification_message">Tap to turn off USB debugging</string>
<string name="adb_active_notification_message" product="tv">Select to disable USB debugging.</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] -->
+ <string name="usb_contaminant_detected_message">USB port is automatically disabled. Tap to learn more.</string>
+ <!-- Title of notification shown when contaminant is no longer detected on the USB port. [CHAR LIMIT=NONE] -->
+ <string name="usb_contaminant_not_detected_title">Safe to use USB port</string>
+ <!-- Message of notification shown when contaminant is no longer detected on the USB port. [CHAR LIMIT=NONE] -->
+ <string name="usb_contaminant_not_detected_message">Phone no longer detects liquid or debris.</string>
+
<!-- Title of notification shown to indicate that bug report is being collected. -->
<string name="taking_remote_bugreport_notification_title">Taking bug report\u2026</string>
<!-- Title of notification shown to ask for user consent for sharing a bugreport that was requested remotely by the IT administrator. -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 751721860441..d5561302fdc6 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -437,6 +437,7 @@
<java-symbol type="integer" name="config_bluetooth_operating_voltage_mv" />
<java-symbol type="bool" name="config_bluetooth_pan_enable_autoconnect" />
<java-symbol type="bool" name="config_bluetooth_reload_supported_profiles_when_enabled" />
+ <java-symbol type="bool" name="config_hearing_aid_profile_supported" />
<java-symbol type="integer" name="config_cursorWindowSize" />
<java-symbol type="integer" name="config_drawLockTimeoutMillis" />
<java-symbol type="integer" name="config_doublePressOnPowerBehavior" />
@@ -694,6 +695,7 @@
<java-symbol type="string" name="capability_title_canControlMagnification" />
<java-symbol type="string" name="capability_desc_canPerformGestures" />
<java-symbol type="string" name="capability_title_canPerformGestures" />
+ <java-symbol type="string" name="captive_portal_logged_in_detailed" />
<java-symbol type="string" name="cfTemplateForwarded" />
<java-symbol type="string" name="cfTemplateForwardedTime" />
<java-symbol type="string" name="cfTemplateNotForwarded" />
@@ -1879,6 +1881,7 @@
<java-symbol type="bool" name="config_supportLongPressPowerWhenNonInteractive" />
<java-symbol type="bool" name="config_wifi_background_scan_support" />
<java-symbol type="bool" name="config_wifi_dual_band_support" />
+ <java-symbol type="integer" name="config_wifi_max_ap_interfaces" />
<java-symbol type="bool" name="config_wifi_convert_apband_5ghz_to_any" />
<java-symbol type="bool" name="config_wifi_local_only_hotspot_5ghz" />
<java-symbol type="bool" name="config_wifi_connected_mac_randomization_supported" />
@@ -2103,6 +2106,10 @@
<java-symbol type="string" name="usb_supplying_notification_title" />
<java-symbol type="string" name="usb_unsupported_audio_accessory_title" />
<java-symbol type="string" name="usb_unsupported_audio_accessory_message" />
+ <java-symbol type="string" name="usb_contaminant_detected_title" />
+ <java-symbol type="string" name="usb_contaminant_detected_message" />
+ <java-symbol type="string" name="usb_contaminant_not_detected_title" />
+ <java-symbol type="string" name="usb_contaminant_not_detected_message" />
<java-symbol type="string" name="config_UsbDeviceConnectionHandling_component" />
<java-symbol type="string" name="vpn_text" />
<java-symbol type="string" name="vpn_text_long" />
@@ -3521,8 +3528,6 @@
<java-symbol type="bool" name="config_useSmsAppService" />
- <java-symbol type="string" name="config_defaultAssistantComponentName" />
-
<java-symbol type="id" name="transition_overlay_view_tag" />
<java-symbol type="dimen" name="rounded_corner_radius" />
@@ -3547,4 +3552,6 @@
<!-- For CBRS -->
<java-symbol type="bool" name="config_cbrs_supported" />
+
+ <java-symbol type="bool" name="config_awareSettingAvailable" />
</resources>
diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml
index 75a727bdcfdf..160350878349 100644
--- a/core/res/res/values/themes_device_defaults.xml
+++ b/core/res/res/values/themes_device_defaults.xml
@@ -1711,4 +1711,6 @@ easier.
<item name="notificationHeaderTextAppearance">@style/TextAppearance.DeviceDefault.Notification.Info</item>
</style>
+ <!-- @hide DeviceDefault theme for the DocumentsUI app. -->
+ <style name="Theme.DeviceDefault.DocumentsUI" parent="Theme.DeviceDefault.DayNight" />
</resources>
diff --git a/core/tests/BroadcastRadioTests/src/android/hardware/radio/tests/functional/RadioTunerTest.java b/core/tests/BroadcastRadioTests/src/android/hardware/radio/tests/functional/RadioTunerTest.java
index d2bd1e172ee2..11eb158317e9 100644
--- a/core/tests/BroadcastRadioTests/src/android/hardware/radio/tests/functional/RadioTunerTest.java
+++ b/core/tests/BroadcastRadioTests/src/android/hardware/radio/tests/functional/RadioTunerTest.java
@@ -18,13 +18,13 @@ package android.hardware.radio.tests.functional;
import static org.junit.Assert.*;
import static org.junit.Assume.*;
import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyBoolean;
+import static org.mockito.Matchers.anyInt;
import static org.mockito.Mockito.after;
-import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.atMost;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.testng.Assert.assertThrows;
import android.Manifest;
@@ -119,10 +119,9 @@ public class RadioTunerTest {
}
private void resetCallback() {
- verify(mCallback, atLeast(0)).onMetadataChanged(any());
- verify(mCallback, atLeast(0)).onProgramInfoChanged(any());
- verify(mCallback, atLeast(0)).onProgramListChanged();
- verifyNoMoreInteractions(mCallback);
+ verify(mCallback, never()).onError(anyInt());
+ verify(mCallback, never()).onTuneFailed(anyInt(), any());
+ verify(mCallback, never()).onControlChanged(anyBoolean());
Mockito.reset(mCallback);
}
diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
index 8604b0c48476..bdf3aa2563db 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
@@ -38,7 +38,6 @@ import android.content.pm.ProviderInfo;
import android.content.pm.ServiceInfo;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
-import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
import android.os.Debug;
@@ -501,7 +500,7 @@ public class TransactionParcelTests {
}
@Override
- public void setHttpProxy(String s, String s1, String s2, Uri uri) throws RemoteException {
+ public void updateHttpProxy() throws RemoteException {
}
@Override
diff --git a/core/tests/coretests/src/android/content/pm/PackageBackwardCompatibilityTest.java b/core/tests/coretests/src/android/content/pm/PackageBackwardCompatibilityTest.java
index 3d7aab001227..ad9814bd01b1 100644
--- a/core/tests/coretests/src/android/content/pm/PackageBackwardCompatibilityTest.java
+++ b/core/tests/coretests/src/android/content/pm/PackageBackwardCompatibilityTest.java
@@ -48,21 +48,11 @@ public class PackageBackwardCompatibilityTest extends PackageSharedLibraryUpdate
}
/**
- * Detect when the org.apache.http.legacy is not on the bootclasspath.
- *
- * <p>This test will be ignored when org.apache.http.legacy is not on the bootclasspath and
- * succeed otherwise. This allows a developer to ensure that the tests are being
- */
- @Test
- public void detectWhenOAHLisOnBCP() {
- Assume.assumeTrue(PackageBackwardCompatibility.bootClassPathContainsOAHL());
- }
-
- /**
* Detect when the android.test.base is not on the bootclasspath.
*
* <p>This test will be ignored when org.apache.http.legacy is not on the bootclasspath and
- * succeed otherwise. This allows a developer to ensure that the tests are being
+ * succeed otherwise. This allows a developer to ensure that the tests are being run in the
+ * correct environment.
*/
@Test
public void detectWhenATBisOnBCP() {
@@ -85,9 +75,7 @@ public class PackageBackwardCompatibilityTest extends PackageSharedLibraryUpdate
if (!PackageBackwardCompatibility.bootClassPathContainsATB()) {
expected.add(ANDROID_TEST_BASE);
}
- if (!PackageBackwardCompatibility.bootClassPathContainsOAHL()) {
- expected.add(ORG_APACHE_HTTP_LEGACY);
- }
+ expected.add(ORG_APACHE_HTTP_LEGACY);
PackageBuilder after = builder()
.targetSdkVersion(Build.VERSION_CODES.O)
@@ -98,30 +86,6 @@ public class PackageBackwardCompatibilityTest extends PackageSharedLibraryUpdate
/**
* Ensures that the {@link PackageBackwardCompatibility} uses
- * {@link RemoveUnnecessaryOrgApacheHttpLegacyLibraryTest}
- * when necessary.
- *
- * <p>More comprehensive tests for that class can be found in
- * {@link RemoveUnnecessaryOrgApacheHttpLegacyLibraryTest}.
- */
- @Test
- public void org_apache_http_legacy_in_usesLibraries() {
- Assume.assumeTrue("Test requires that "
- + ORG_APACHE_HTTP_LEGACY + " is on the bootclasspath",
- PackageBackwardCompatibility.bootClassPathContainsOAHL());
-
- PackageBuilder before = builder()
- .requiredLibraries(ORG_APACHE_HTTP_LEGACY);
-
- // org.apache.http.legacy should be removed from the libraries because it is provided
- // on the bootclasspath and providing both increases start up cost unnecessarily.
- PackageBuilder after = builder();
-
- checkBackwardsCompatibility(before, after);
- }
-
- /**
- * Ensures that the {@link PackageBackwardCompatibility} uses
* {@link RemoveUnnecessaryAndroidTestBaseLibrary}
* when necessary.
*
diff --git a/core/tests/coretests/src/android/content/pm/PackageParserTest.java b/core/tests/coretests/src/android/content/pm/PackageParserTest.java
index c5454a649e73..300394d426e4 100644
--- a/core/tests/coretests/src/android/content/pm/PackageParserTest.java
+++ b/core/tests/coretests/src/android/content/pm/PackageParserTest.java
@@ -527,12 +527,16 @@ public class PackageParserTest {
R.raw.com_android_tzdata);
PackageInfo pi = PackageParser.generatePackageInfoFromApex(apexFile, false);
assertEquals("com.google.android.tzdata", pi.packageName);
+ assertEquals("com.google.android.tzdata", pi.applicationInfo.packageName);
assertEquals(1, pi.getLongVersionCode());
+ assertEquals(1, pi.applicationInfo.longVersionCode);
assertNull(pi.signingInfo);
pi = PackageParser.generatePackageInfoFromApex(apexFile, true);
assertEquals("com.google.android.tzdata", pi.packageName);
+ assertEquals("com.google.android.tzdata", pi.applicationInfo.packageName);
assertEquals(1, pi.getLongVersionCode());
+ assertEquals(1, pi.applicationInfo.longVersionCode);
assertNotNull(pi.signingInfo);
assertTrue(pi.signingInfo.getApkContentsSigners().length > 0);
}
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 143174125fe4..9b79e85ae531 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -298,6 +298,7 @@ public class SettingsBackupTest {
Settings.Global.LOCATION_BACKGROUND_THROTTLE_INTERVAL_MS,
Settings.Global.LOCATION_BACKGROUND_THROTTLE_PROXIMITY_ALERT_INTERVAL_MS,
Settings.Global.LOCATION_BACKGROUND_THROTTLE_PACKAGE_WHITELIST,
+ Settings.Global.LOCATION_IGNORE_SETTINGS_PACKAGE_WHITELIST,
Settings.Global.LOCATION_DISABLE_STATUS_CALLBACKS,
Settings.Global.LOCATION_LAST_LOCATION_MAX_AGE_MILLIS,
Settings.Global.LOCATION_GLOBAL_KILL_SWITCH,
@@ -388,12 +389,9 @@ public class SettingsBackupTest {
Settings.Global.POLICY_CONTROL,
Settings.Global.POWER_MANAGER_CONSTANTS,
Settings.Global.PREFERRED_NETWORK_MODE,
- Settings.Global.PRIV_APP_OOB_ENABLED,
- Settings.Global.PRIV_APP_OOB_LIST,
Settings.Global.PRIVATE_DNS_DEFAULT_MODE,
- Settings.Global.PRIVILEGED_DEVICE_IDENTIFIER_CHECK_ENABLED,
Settings.Global.PRIVILEGED_DEVICE_IDENTIFIER_NON_PRIV_CHECK_RELAXED,
- Settings.Global.PRIVILEGED_DEVICE_IDENTIFIER_TARGET_Q_BEHAVIOR_ENABLED,
+ Settings.Global.PRIVILEGED_DEVICE_IDENTIFIER_PRIV_CHECK_RELAXED,
Settings.Global.PRIVILEGED_DEVICE_IDENTIFIER_3P_CHECK_RELAXED,
Settings.Global.PROVISIONING_APN_ALARM_DELAY_IN_MS,
Settings.Global.RADIO_BLUETOOTH,
@@ -483,10 +481,12 @@ public class SettingsBackupTest {
Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_ALL_ANGLE,
Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_PKGS,
Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_VALUES,
+ Settings.Global.GLOBAL_SETTINGS_ANGLE_WHITELIST,
Settings.Global.GUP_DEV_ALL_APPS,
Settings.Global.GUP_DEV_OPT_IN_APPS,
Settings.Global.GUP_DEV_OPT_OUT_APPS,
Settings.Global.GUP_BLACKLIST,
+ Settings.Global.GAME_DRIVER_WHITELIST,
Settings.Global.GPU_DEBUG_LAYER_APP,
Settings.Global.ENABLE_GNSS_RAW_MEAS_FULL_TRACKING,
Settings.Global.INSTALL_CARRIER_APP_NOTIFICATION_PERSISTENT,
@@ -564,7 +564,9 @@ public class SettingsBackupTest {
Settings.Global.APPOP_HISTORY_BASE_INTERVAL_MILLIS,
Settings.Global.ENABLE_RADIO_BUG_DETECTION,
Settings.Global.RADIO_BUG_WAKELOCK_TIMEOUT_COUNT_THRESHOLD,
- Settings.Global.RADIO_BUG_SYSTEM_ERROR_COUNT_THRESHOLD);
+ Settings.Global.RADIO_BUG_SYSTEM_ERROR_COUNT_THRESHOLD,
+ Settings.Global.ENABLED_SUBSCRIPTION_FOR_SLOT,
+ Settings.Global.MODEM_STACK_ENABLED_FOR_SLOT);
private static final Set<String> BACKUP_BLACKLISTED_SECURE_SETTINGS =
newHashSet(
Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
@@ -572,6 +574,7 @@ public class SettingsBackupTest {
Settings.Secure.ALLOWED_GEOLOCATION_ORIGINS,
Settings.Secure.ALWAYS_ON_VPN_APP,
Settings.Secure.ALWAYS_ON_VPN_LOCKDOWN,
+ Settings.Secure.ALWAYS_ON_VPN_LOCKDOWN_WHITELIST,
Settings.Secure.ANDROID_ID,
Settings.Secure.ANR_SHOW_BACKGROUND,
Settings.Secure.ASSISTANT,
diff --git a/core/tests/coretests/src/android/util/LongArrayQueueTest.java b/core/tests/coretests/src/android/util/LongArrayQueueTest.java
new file mode 100644
index 000000000000..77e8d608810f
--- /dev/null
+++ b/core/tests/coretests/src/android/util/LongArrayQueueTest.java
@@ -0,0 +1,238 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.util;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.fail;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.NoSuchElementException;
+
+/**
+ * Internal tests for {@link LongArrayQueue}.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class LongArrayQueueTest {
+
+ private LongArrayQueue mQueueUnderTest;
+
+ @Before
+ public void setUp() {
+ mQueueUnderTest = new LongArrayQueue();
+ }
+
+ @Test
+ public void removeFirstOnEmptyQueue() {
+ try {
+ mQueueUnderTest.removeFirst();
+ fail("removeFirst() succeeded on an empty queue!");
+ } catch (NoSuchElementException e) {
+ }
+ mQueueUnderTest.addLast(5);
+ mQueueUnderTest.removeFirst();
+ try {
+ mQueueUnderTest.removeFirst();
+ fail("removeFirst() succeeded on an empty queue!");
+ } catch (NoSuchElementException e) {
+ }
+ }
+
+ @Test
+ public void addLastRemoveFirstFifo() {
+ mQueueUnderTest.addLast(1);
+ assertEquals(1, mQueueUnderTest.removeFirst());
+ int n = 890;
+ int removes = 0;
+ for (int i = 0; i < n; i++) {
+ mQueueUnderTest.addLast(i);
+ if ((i % 3) == 0) {
+ assertEquals(removes++, mQueueUnderTest.removeFirst());
+ }
+ }
+ while (removes < n) {
+ assertEquals(removes++, mQueueUnderTest.removeFirst());
+ }
+ }
+
+ @Test
+ public void peekFirstOnEmptyQueue() {
+ try {
+ mQueueUnderTest.peekFirst();
+ fail("peekFirst() succeeded on an empty queue!");
+ } catch (NoSuchElementException e) {
+ }
+ mQueueUnderTest.addLast(5);
+ mQueueUnderTest.removeFirst();
+ try {
+ mQueueUnderTest.peekFirst();
+ fail("peekFirst() succeeded on an empty queue!");
+ } catch (NoSuchElementException e) {
+ }
+ }
+
+ @Test
+ public void peekFirstChanges() {
+ mQueueUnderTest.addLast(1);
+ assertEquals(1, mQueueUnderTest.peekFirst());
+ mQueueUnderTest.addLast(2);
+ mQueueUnderTest.addLast(3);
+ mQueueUnderTest.addLast(4);
+ // addLast() has no effect on peekFirst().
+ assertEquals(1, mQueueUnderTest.peekFirst());
+ mQueueUnderTest.removeFirst();
+ mQueueUnderTest.removeFirst();
+ assertEquals(3, mQueueUnderTest.peekFirst());
+ }
+
+ @Test
+ public void peekLastOnEmptyQueue() {
+ try {
+ mQueueUnderTest.peekLast();
+ fail("peekLast() succeeded on an empty queue!");
+ } catch (NoSuchElementException e) {
+ }
+ mQueueUnderTest.addLast(5);
+ mQueueUnderTest.removeFirst();
+ try {
+ mQueueUnderTest.peekLast();
+ fail("peekLast() succeeded on an empty queue!");
+ } catch (NoSuchElementException e) {
+ }
+ }
+
+ @Test
+ public void peekLastChanges() {
+ mQueueUnderTest.addLast(1);
+ assertEquals(1, mQueueUnderTest.peekLast());
+ mQueueUnderTest.addLast(2);
+ mQueueUnderTest.addLast(3);
+ mQueueUnderTest.addLast(4);
+ assertEquals(4, mQueueUnderTest.peekLast());
+ mQueueUnderTest.removeFirst();
+ mQueueUnderTest.removeFirst();
+ // removeFirst() has no effect on peekLast().
+ assertEquals(4, mQueueUnderTest.peekLast());
+ }
+
+ @Test
+ public void peekFirstVsPeekLast() {
+ mQueueUnderTest.addLast(2);
+ assertEquals(mQueueUnderTest.peekFirst(), mQueueUnderTest.peekLast());
+ mQueueUnderTest.addLast(3);
+ assertNotEquals(mQueueUnderTest.peekFirst(), mQueueUnderTest.peekLast());
+ mQueueUnderTest.removeFirst();
+ assertEquals(mQueueUnderTest.peekFirst(), mQueueUnderTest.peekLast());
+ }
+
+ @Test
+ public void peekFirstVsRemoveFirst() {
+ int n = 25;
+ for (int i = 0; i < n; i++) {
+ mQueueUnderTest.addLast(i + 1);
+ }
+ for (int i = 0; i < n; i++) {
+ long peekVal = mQueueUnderTest.peekFirst();
+ assertEquals(peekVal, mQueueUnderTest.removeFirst());
+ }
+ }
+
+ @Test
+ public void sizeOfEmptyQueue() {
+ assertEquals(0, mQueueUnderTest.size());
+ mQueueUnderTest = new LongArrayQueue(1000);
+ // capacity doesn't affect size.
+ assertEquals(0, mQueueUnderTest.size());
+ }
+
+ @Test
+ public void sizeAfterOperations() {
+ final int added = 1200;
+ for (int i = 0; i < added; i++) {
+ mQueueUnderTest.addLast(i);
+ }
+ // each add increments the size by 1.
+ assertEquals(added, mQueueUnderTest.size());
+ mQueueUnderTest.peekLast();
+ mQueueUnderTest.peekFirst();
+ // peeks don't change the size.
+ assertEquals(added, mQueueUnderTest.size());
+ final int removed = 345;
+ for (int i = 0; i < removed; i++) {
+ mQueueUnderTest.removeFirst();
+ }
+ // each remove decrements the size by 1.
+ assertEquals(added - removed, mQueueUnderTest.size());
+ mQueueUnderTest.clear();
+ // clear reduces the size to 0.
+ assertEquals(0, mQueueUnderTest.size());
+ }
+
+ @Test
+ public void getInvalidPositions() {
+ try {
+ mQueueUnderTest.get(0);
+ fail("get(0) succeeded on an empty queue!");
+ } catch (IndexOutOfBoundsException e) {
+ }
+ int n = 520;
+ for (int i = 0; i < 2 * n; i++) {
+ mQueueUnderTest.addLast(i + 1);
+ }
+ for (int i = 0; i < n; i++) {
+ mQueueUnderTest.removeFirst();
+ }
+ try {
+ mQueueUnderTest.get(-3);
+ fail("get(-3) succeeded");
+ } catch (IndexOutOfBoundsException e) {
+ }
+ assertEquals(n, mQueueUnderTest.size());
+ try {
+ mQueueUnderTest.get(n);
+ fail("get(" + n + ") succeeded on a queue with " + n + " elements");
+ } catch (IndexOutOfBoundsException e) {
+ }
+ try {
+ mQueueUnderTest.get(n + 3);
+ fail("get(" + (n + 3) + ") succeeded on a queue with " + n + " elements");
+ } catch (IndexOutOfBoundsException e) {
+ }
+ }
+
+ @Test
+ public void getValidPositions() {
+ int added = 423;
+ int removed = 212;
+ for (int i = 0; i < added; i++) {
+ mQueueUnderTest.addLast(i);
+ }
+ for (int i = 0; i < removed; i++) {
+ mQueueUnderTest.removeFirst();
+ }
+ for (int i = 0; i < (added - removed); i++) {
+ assertEquals(removed + i, mQueueUnderTest.get(i));
+ }
+ }
+}
diff --git a/core/tests/coretests/src/android/view/autofill/AutofillIdTest.java b/core/tests/coretests/src/android/view/autofill/AutofillIdTest.java
index 33bc59349230..2f17b32370f4 100644
--- a/core/tests/coretests/src/android/view/autofill/AutofillIdTest.java
+++ b/core/tests/coretests/src/android/view/autofill/AutofillIdTest.java
@@ -34,26 +34,57 @@ public class AutofillIdTest {
public void testNonVirtual() {
final AutofillId id = new AutofillId(42);
assertThat(id.getViewId()).isEqualTo(42);
- assertThat(id.isVirtual()).isFalse();
- assertThat(id.getVirtualChildId()).isEqualTo(View.NO_ID);
+ assertThat(id.isNonVirtual()).isTrue();
+ assertThat(id.isVirtualInt()).isFalse();
+ assertThat(id.isVirtualLong()).isFalse();
+ assertThat(id.getVirtualChildIntId()).isEqualTo(View.NO_ID);
+ assertThat(id.getVirtualChildLongId()).isEqualTo(View.NO_ID);
final AutofillId clone = cloneThroughParcel(id);
assertThat(clone.getViewId()).isEqualTo(42);
- assertThat(clone.isVirtual()).isFalse();
- assertThat(clone.getVirtualChildId()).isEqualTo(View.NO_ID);
+ assertThat(clone.isNonVirtual()).isTrue();
+ assertThat(clone.isVirtualInt()).isFalse();
+ assertThat(clone.isVirtualLong()).isFalse();
+ assertThat(clone.getVirtualChildIntId()).isEqualTo(View.NO_ID);
+ assertThat(clone.getVirtualChildLongId()).isEqualTo(View.NO_ID);
}
@Test
- public void testVirtual() {
+ public void testVirtual_int() {
final AutofillId id = new AutofillId(42, 108);
assertThat(id.getViewId()).isEqualTo(42);
- assertThat(id.isVirtual()).isTrue();
- assertThat(id.getVirtualChildId()).isEqualTo(108);
+ assertThat(id.isVirtualInt()).isTrue();
+ assertThat(id.isVirtualLong()).isFalse();
+ assertThat(id.isNonVirtual()).isFalse();
+ assertThat(id.getVirtualChildIntId()).isEqualTo(108);
+ assertThat(id.getVirtualChildLongId()).isEqualTo(View.NO_ID);
final AutofillId clone = cloneThroughParcel(id);
assertThat(clone.getViewId()).isEqualTo(42);
- assertThat(clone.isVirtual()).isTrue();
- assertThat(clone.getVirtualChildId()).isEqualTo(108);
+ assertThat(clone.isVirtualLong()).isFalse();
+ assertThat(clone.isVirtualInt()).isTrue();
+ assertThat(clone.isNonVirtual()).isFalse();
+ assertThat(clone.getVirtualChildIntId()).isEqualTo(108);
+ assertThat(clone.getVirtualChildLongId()).isEqualTo(View.NO_ID);
+ }
+
+ @Test
+ public void testVirtual_long() {
+ final AutofillId id = new AutofillId(new AutofillId(42), 4815162342L, 108);
+ assertThat(id.getViewId()).isEqualTo(42);
+ assertThat(id.isVirtualLong()).isTrue();
+ assertThat(id.isVirtualInt()).isFalse();
+ assertThat(id.isNonVirtual()).isFalse();
+ assertThat(id.getVirtualChildIntId()).isEqualTo(View.NO_ID);
+ assertThat(id.getVirtualChildLongId()).isEqualTo(4815162342L);
+
+ final AutofillId clone = cloneThroughParcel(id);
+ assertThat(clone.getViewId()).isEqualTo(42);
+ assertThat(clone.isVirtualLong()).isTrue();
+ assertThat(clone.isVirtualInt()).isFalse();
+ assertThat(clone.isNonVirtual()).isFalse();
+ assertThat(clone.getVirtualChildIntId()).isEqualTo(View.NO_ID);
+ assertThat(clone.getVirtualChildLongId()).isEqualTo(4815162342L);
}
@Test
@@ -62,27 +93,33 @@ public class AutofillIdTest {
final AutofillId id = new AutofillId(new AutofillId(42), 108);
assertThat(id.getViewId()).isEqualTo(42);
- assertThat(id.isVirtual()).isTrue();
- assertThat(id.getVirtualChildId()).isEqualTo(108);
+ assertThat(id.isVirtualInt()).isTrue();
+ assertThat(id.getVirtualChildIntId()).isEqualTo(108);
final AutofillId clone = cloneThroughParcel(id);
assertThat(clone.getViewId()).isEqualTo(42);
- assertThat(clone.isVirtual()).isTrue();
- assertThat(clone.getVirtualChildId()).isEqualTo(108);
+ assertThat(clone.isVirtualInt()).isTrue();
+ assertThat(clone.getVirtualChildIntId()).isEqualTo(108);
}
@Test
public void testVirtual_withSession() {
- final AutofillId id = new AutofillId(new AutofillId(42), 108, 666);
+ final AutofillId id = new AutofillId(new AutofillId(42), 108L, 666);
assertThat(id.getViewId()).isEqualTo(42);
- assertThat(id.isVirtual()).isTrue();
- assertThat(id.getVirtualChildId()).isEqualTo(108);
+ assertThat(id.isVirtualLong()).isTrue();
+ assertThat(id.isVirtualInt()).isFalse();
+ assertThat(id.isNonVirtual()).isFalse();
+ assertThat(id.getVirtualChildLongId()).isEqualTo(108L);
+ assertThat(id.getVirtualChildIntId()).isEqualTo(View.NO_ID);
assertThat(id.getSessionId()).isEqualTo(666);
final AutofillId clone = cloneThroughParcel(id);
assertThat(clone.getViewId()).isEqualTo(42);
- assertThat(clone.isVirtual()).isTrue();
- assertThat(clone.getVirtualChildId()).isEqualTo(108);
+ assertThat(clone.isVirtualLong()).isTrue();
+ assertThat(clone.isVirtualInt()).isFalse();
+ assertThat(clone.isNonVirtual()).isFalse();
+ assertThat(clone.getVirtualChildLongId()).isEqualTo(108L);
+ assertThat(clone.getVirtualChildIntId()).isEqualTo(View.NO_ID);
assertThat(clone.getSessionId()).isEqualTo(666);
}
@@ -118,13 +155,14 @@ public class AutofillIdTest {
assertThat(virtualIdDifferentParent).isNotEqualTo(virtualIdDifferentChild);
assertThat(virtualIdDifferentChild).isNotEqualTo(virtualIdDifferentParent);
- final AutofillId virtualIdDifferentSession = new AutofillId(new AutofillId(42), 1, 108);
+ final AutofillId virtualIdDifferentSession = new AutofillId(new AutofillId(42), 1L, 108);
assertThat(virtualIdDifferentSession).isNotEqualTo(virtualId);
assertThat(virtualId).isNotEqualTo(virtualIdDifferentSession);
assertThat(virtualIdDifferentSession).isNotEqualTo(realId);
assertThat(realId).isNotEqualTo(virtualIdDifferentSession);
- final AutofillId sameVirtualIdDifferentSession = new AutofillId(new AutofillId(42), 1, 108);
+ final AutofillId sameVirtualIdDifferentSession =
+ new AutofillId(new AutofillId(42), 1L, 108);
assertThat(sameVirtualIdDifferentSession).isEqualTo(virtualIdDifferentSession);
assertThat(virtualIdDifferentSession).isEqualTo(sameVirtualIdDifferentSession);
assertThat(sameVirtualIdDifferentSession.hashCode())
diff --git a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureManagerTest.java b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureManagerTest.java
new file mode 100644
index 000000000000..312e0e0d8268
--- /dev/null
+++ b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureManagerTest.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.view.contentcapture;
+
+import static org.testng.Assert.assertThrows;
+
+import android.content.Context;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+/**
+ * Unit test for {@link ContentCaptureManager}.
+ *
+ * <p>To run it:
+ * {@code atest FrameworksCoreTests:android.view.contentcapture.ContentCaptureManagerTest}
+ */
+@RunWith(MockitoJUnitRunner.class)
+public class ContentCaptureManagerTest {
+
+ @Mock
+ private Context mMockContext;
+
+ private ContentCaptureManager mManager;
+
+ @Before
+ public void before() {
+ mManager = new ContentCaptureManager(mMockContext, null);
+ }
+
+ @Test
+ public void testRemoveUserData_invalid() {
+ assertThrows(NullPointerException.class, () -> mManager.removeUserData(null));
+ }
+}
diff --git a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java
index ff97aa1d3914..bfa6e062b08d 100644
--- a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java
+++ b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java
@@ -31,7 +31,7 @@ import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
/**
- * Unit test for {@link ContentCaptureSessionTest}.
+ * Unit tests for {@link ContentCaptureSession}.
*
* <p>To run it:
* {@code atest FrameworksCoreTests:android.view.contentcapture.ContentCaptureSessionTest}
@@ -48,17 +48,18 @@ public class ContentCaptureSessionTest {
@Test
public void testNewAutofillId_invalid() {
- assertThrows(NullPointerException.class, () -> mSession1.newAutofillId(null, 42));
+ assertThrows(NullPointerException.class, () -> mSession1.newAutofillId(null, 42L));
assertThrows(IllegalArgumentException.class,
- () -> mSession1.newAutofillId(new AutofillId(42, 42), 42));
+ () -> mSession1.newAutofillId(new AutofillId(42, 42), 42L));
}
@Test
public void testNewAutofillId_valid() {
final AutofillId parentId = new AutofillId(42);
- final AutofillId childId = mSession1.newAutofillId(parentId, 108);
+ final AutofillId childId = mSession1.newAutofillId(parentId, 108L);
assertThat(childId.getViewId()).isEqualTo(42);
- assertThat(childId.getVirtualChildId()).isEqualTo(108);
+ assertThat(childId.getVirtualChildLongId()).isEqualTo(108L);
+ assertThat(childId.getVirtualChildIntId()).isEqualTo(View.NO_ID);
assertThat(childId.getSessionId()).isEqualTo(mSession1.getIdAsInt());
}
@@ -66,8 +67,8 @@ public class ContentCaptureSessionTest {
public void testNewAutofillId_differentSessions() {
assertThat(mSession1.getIdAsInt()).isNotSameAs(mSession2.getIdAsInt()); //sanity check
final AutofillId parentId = new AutofillId(42);
- final AutofillId childId1 = mSession1.newAutofillId(parentId, 108);
- final AutofillId childId2 = mSession2.newAutofillId(parentId, 108);
+ final AutofillId childId1 = mSession1.newAutofillId(parentId, 108L);
+ final AutofillId childId2 = mSession2.newAutofillId(parentId, 108L);
assertThat(childId1).isNotEqualTo(childId2);
assertThat(childId2).isNotEqualTo(childId1);
}
@@ -91,9 +92,9 @@ public class ContentCaptureSessionTest {
@Test
public void testNewVirtualViewStructure() {
final AutofillId parentId = new AutofillId(42);
- final ViewStructure structure = mSession1.newVirtualViewStructure(parentId, 108);
+ final ViewStructure structure = mSession1.newVirtualViewStructure(parentId, 108L);
assertThat(structure).isNotNull();
- final AutofillId childId = mSession1.newAutofillId(parentId, 108);
+ final AutofillId childId = mSession1.newAutofillId(parentId, 108L);
assertThat(structure.getAutofillId()).isEqualTo(childId);
}
@@ -101,16 +102,16 @@ public class ContentCaptureSessionTest {
public void testNotifyViewsDisappeared_invalid() {
// Null parent
assertThrows(NullPointerException.class,
- () -> mSession1.notifyViewsDisappeared(null, new int[] {42}));
+ () -> mSession1.notifyViewsDisappeared(null, new long[] {42}));
// Null child
assertThrows(IllegalArgumentException.class,
() -> mSession1.notifyViewsDisappeared(new AutofillId(42), null));
// Empty child
assertThrows(IllegalArgumentException.class,
- () -> mSession1.notifyViewsDisappeared(new AutofillId(42), new int[] {}));
+ () -> mSession1.notifyViewsDisappeared(new AutofillId(42), new long[] {}));
// Virtual parent
assertThrows(IllegalArgumentException.class,
- () -> mSession1.notifyViewsDisappeared(new AutofillId(42, 108), new int[] {666}));
+ () -> mSession1.notifyViewsDisappeared(new AutofillId(42, 108), new long[] {666}));
}
// Cannot use @Spy because we need to pass the session id on constructor
diff --git a/core/tests/coretests/src/android/view/contentcapture/UserDataRemovalRequestTest.java b/core/tests/coretests/src/android/view/contentcapture/UserDataRemovalRequestTest.java
new file mode 100644
index 000000000000..bebb2a89f480
--- /dev/null
+++ b/core/tests/coretests/src/android/view/contentcapture/UserDataRemovalRequestTest.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.view.contentcapture;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.testng.Assert.assertThrows;
+
+import android.net.Uri;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+/**
+ * Unit test for {@link UserDataRemovalRequest}.
+ *
+ * <p>To run it:
+ * {@code atest FrameworksCoreTests:android.view.contentcapture.UserDataRemovalRequestTest}
+ */
+@RunWith(MockitoJUnitRunner.class)
+public class UserDataRemovalRequestTest {
+
+ @Mock
+ private final Uri mUri = Uri.parse("content://com.example/");
+
+ private UserDataRemovalRequest.Builder mBuilder = new UserDataRemovalRequest.Builder();
+
+ @Test
+ public void testBuilder_addUri_invalid() {
+ assertThrows(NullPointerException.class, () -> mBuilder.addUri(null, false));
+ }
+
+ @Test
+ public void testBuilder_addUri_valid() {
+ assertThat(mBuilder.addUri(mUri, false)).isNotNull();
+ assertThat(mBuilder.addUri(Uri.parse("content://com.example2"), true)).isNotNull();
+ }
+
+ @Test
+ public void testBuilder_addUriAfterForEverything() {
+ assertThat(mBuilder.forEverything()).isNotNull();
+ assertThrows(IllegalStateException.class, () -> mBuilder.addUri(mUri, false));
+ }
+
+ @Test
+ public void testBuilder_forEverythingAfterAddingUri() {
+ assertThat(mBuilder.addUri(mUri, false)).isNotNull();
+ assertThrows(IllegalStateException.class, () -> mBuilder.forEverything());
+ }
+
+ @Test
+ public void testBuild_invalid() {
+ assertThrows(IllegalStateException.class, () -> mBuilder.build());
+ }
+
+ @Test
+ public void testBuild_valid() {
+ assertThat(new UserDataRemovalRequest.Builder().forEverything().build())
+ .isNotNull();
+ assertThat(new UserDataRemovalRequest.Builder().addUri(mUri, false).build())
+ .isNotNull();
+ }
+
+ @Test
+ public void testNoMoreInteractionsAfterBuild() {
+ assertThat(mBuilder.forEverything().build()).isNotNull();
+
+ assertThrows(IllegalStateException.class, () -> mBuilder.addUri(mUri, false));
+ assertThrows(IllegalStateException.class, () -> mBuilder.forEverything());
+ assertThrows(IllegalStateException.class, () -> mBuilder.build());
+
+ }
+}
diff --git a/core/tests/coretests/src/android/view/contentcapture/ViewNodeTest.java b/core/tests/coretests/src/android/view/contentcapture/ViewNodeTest.java
index eadde6280848..b84a098574c2 100644
--- a/core/tests/coretests/src/android/view/contentcapture/ViewNodeTest.java
+++ b/core/tests/coretests/src/android/view/contentcapture/ViewNodeTest.java
@@ -42,7 +42,7 @@ import org.mockito.junit.MockitoJUnitRunner;
import java.util.Locale;
/**
- * Unit test for {@link ViewNode}.
+ * Unit tests for {@link ViewNode}.
*
* <p>To run it: {@code atest FrameworksCoreTests:android.view.contentcapture.ViewNodeTest}
*/
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java
index 7009fb2ea758..4d78e4036e74 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java
@@ -262,6 +262,9 @@ public class TextClassifierTest {
assertEquals(
context.getString(com.android.internal.R.string.translate),
classification.getActions().get(0).getTitle());
+ Intent intent = (Intent) classification.getExtras()
+ .getParcelableArrayList(TextClassifierImpl.ACTIONS_INTENTS).get(0);
+ assertEquals(Intent.ACTION_TRANSLATE, intent.getAction());
LocaleList.setDefault(originalLocales);
}
diff --git a/core/tests/coretests/src/android/view/textclassifier/logging/TextClassifierEventTronLoggerTest.java b/core/tests/coretests/src/android/view/textclassifier/logging/TextClassifierEventTronLoggerTest.java
index b1b74160ecd5..73af56743b5f 100644
--- a/core/tests/coretests/src/android/view/textclassifier/logging/TextClassifierEventTronLoggerTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/logging/TextClassifierEventTronLoggerTest.java
@@ -18,9 +18,10 @@ package android.view.textclassifier.logging;
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.ACTION_TEXT_SELECTION_SMART_SHARE;
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.CONVERSATION_ACTIONS;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_SELECTION_ENTITY_TYPE;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_SELECTION_WIDGET_TYPE;
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TEXT_CLASSIFIER_EVENT_TIME;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TEXT_CLASSIFIER_FIRST_ENTITY_TYPE;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TEXT_CLASSIFIER_SCORE;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TEXT_CLASSIFIER_WIDGET_TYPE;
import static com.google.common.truth.Truth.assertThat;
@@ -71,7 +72,8 @@ public class TextClassifierEventTronLoggerTest {
new TextClassifierEvent.Builder(
TextClassifierEvent.CATEGORY_CONVERSATION_ACTIONS,
TextClassifierEvent.TYPE_SMART_ACTION)
- .setEntityType(ConversationAction.TYPE_CALL_PHONE)
+ .setEntityTypes(ConversationAction.TYPE_CALL_PHONE)
+ .setScore(0.5f)
.setEventTime(EVENT_TIME)
.setEventContext(textClassificationContext)
.build();
@@ -83,15 +85,18 @@ public class TextClassifierEventTronLoggerTest {
LogMaker logMaker = captor.getValue();
assertThat(logMaker.getCategory()).isEqualTo(
CONVERSATION_ACTIONS);
- assertThat(logMaker.getType()).isEqualTo(
+ assertThat(logMaker.getSubtype()).isEqualTo(
ACTION_TEXT_SELECTION_SMART_SHARE);
- assertThat(logMaker.getTaggedData(FIELD_SELECTION_ENTITY_TYPE))
+ assertThat(logMaker.getTaggedData(FIELD_TEXT_CLASSIFIER_FIRST_ENTITY_TYPE))
.isEqualTo(ConversationAction.TYPE_CALL_PHONE);
+ assertThat((float) logMaker.getTaggedData(FIELD_TEXT_CLASSIFIER_SCORE))
+ .isWithin(0.00001f).of(0.5f);
assertThat(logMaker.getTaggedData(FIELD_TEXT_CLASSIFIER_EVENT_TIME))
.isEqualTo(EVENT_TIME);
assertThat(logMaker.getPackageName()).isEqualTo(PACKAGE_NAME);
- assertThat(logMaker.getTaggedData(FIELD_SELECTION_WIDGET_TYPE))
+ assertThat(logMaker.getTaggedData(FIELD_TEXT_CLASSIFIER_WIDGET_TYPE))
.isEqualTo(WIDGET_TYPE);
+
}
@Test
diff --git a/core/tests/coretests/src/com/android/internal/statusbar/NotificationVisibilityTest.java b/core/tests/coretests/src/com/android/internal/statusbar/NotificationVisibilityTest.java
new file mode 100644
index 000000000000..b740eccfb794
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/statusbar/NotificationVisibilityTest.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.statusbar;
+
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
+
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.lang.reflect.Field;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public final class NotificationVisibilityTest {
+
+ @Test
+ public void testNotificationLocation_sameValuesAsMetricsProto() throws Exception {
+ for (NotificationVisibility.NotificationLocation location :
+ NotificationVisibility.NotificationLocation.values()) {
+ Field locationField = MetricsEvent.class.getField(location.name());
+ int metricsValue = locationField.getInt(null);
+ assertThat(metricsValue, is(location.toMetricsEventEnum()));
+ }
+ }
+}
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index bfbdbc585e08..8636949943b7 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -1711,20 +1711,22 @@ public final class Bitmap implements Parcelable {
*/
@Nullable
public final ColorSpace getColorSpace() {
- // A reconfigure can change the configuration and rgba16f is
- // always linear scRGB at this time
- if (getConfig() == Config.RGBA_F16) {
- // Reset the color space for potential future reconfigurations
- mColorSpace = null;
- return ColorSpace.get(ColorSpace.Named.LINEAR_EXTENDED_SRGB);
- }
-
+ checkRecycled("getColorSpace called on a recycled bitmap");
// Cache the color space retrieval since it can be fairly expensive
if (mColorSpace == null) {
- if (nativeIsSRGB(mNativePtr)) {
+ if (nativeIsConfigF16(mNativePtr)) {
+ // an F16 bitmaps is intended to always be linear extended, but due to
+ // inconsistencies in Bitmap.create() functions it is possible to have
+ // rendered into a bitmap in non-linear sRGB.
+ if (nativeIsSRGB(mNativePtr)) {
+ mColorSpace = ColorSpace.get(ColorSpace.Named.EXTENDED_SRGB);
+ } else {
+ mColorSpace = ColorSpace.get(ColorSpace.Named.LINEAR_EXTENDED_SRGB);
+ }
+ } else if (nativeIsSRGB(mNativePtr)) {
mColorSpace = ColorSpace.get(ColorSpace.Named.SRGB);
- } else if (getConfig() == Config.HARDWARE && nativeIsSRGBLinear(mNativePtr)) {
- mColorSpace = ColorSpace.get(ColorSpace.Named.LINEAR_EXTENDED_SRGB);
+ } else if (nativeIsSRGBLinear(mNativePtr)) {
+ mColorSpace = ColorSpace.get(ColorSpace.Named.LINEAR_SRGB);
} else {
float[] xyz = new float[9];
float[] params = new float[7];
@@ -2127,6 +2129,7 @@ public final class Bitmap implements Parcelable {
private static native void nativeErase(long nativeBitmap, long colorSpacePtr, long color);
private static native int nativeRowBytes(long nativeBitmap);
private static native int nativeConfig(long nativeBitmap);
+ private static native boolean nativeIsConfigF16(long nativeBitmap);
private static native int nativeGetPixel(long nativeBitmap, int x, int y);
private static native void nativeGetPixels(long nativeBitmap, int[] pixels,
diff --git a/jarjar_rules_hidl.txt b/jarjar_rules_hidl.txt
new file mode 100644
index 000000000000..4b2331db7c34
--- /dev/null
+++ b/jarjar_rules_hidl.txt
@@ -0,0 +1 @@
+rule android.hidl.** android.internal.hidl.@1
diff --git a/libs/hwui/DeviceInfo.cpp b/libs/hwui/DeviceInfo.cpp
index 56b1885de820..4c675133a6c1 100644
--- a/libs/hwui/DeviceInfo.cpp
+++ b/libs/hwui/DeviceInfo.cpp
@@ -62,10 +62,8 @@ DisplayInfo QueryDisplayInfo() {
return displayInfo;
}
-static void queryWideColorGamutPreference(SkColorSpace::Gamut* colorGamut,
- sk_sp<SkColorSpace>* colorSpace, SkColorType* colorType) {
+static void queryWideColorGamutPreference(sk_sp<SkColorSpace>* colorSpace, SkColorType* colorType) {
if (Properties::isolatedProcess) {
- *colorGamut = SkColorSpace::Gamut::kSRGB_Gamut;
*colorSpace = SkColorSpace::MakeSRGB();
*colorType = SkColorType::kN32_SkColorType;
return;
@@ -78,16 +76,13 @@ static void queryWideColorGamutPreference(SkColorSpace::Gamut* colorGamut,
LOG_ALWAYS_FATAL_IF(status, "Failed to get composition preference, error %d", status);
switch (wcgDataspace) {
case ui::Dataspace::DISPLAY_P3:
- *colorGamut = SkColorSpace::Gamut::kDCIP3_D65_Gamut;
*colorSpace = SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, SkNamedGamut::kDCIP3);
break;
case ui::Dataspace::V0_SCRGB:
- *colorGamut = SkColorSpace::Gamut::kSRGB_Gamut;
*colorSpace = SkColorSpace::MakeSRGB();
break;
case ui::Dataspace::V0_SRGB:
// when sRGB is returned, it means wide color gamut is not supported.
- *colorGamut = SkColorSpace::Gamut::kSRGB_Gamut;
*colorSpace = SkColorSpace::MakeSRGB();
break;
default:
@@ -112,7 +107,7 @@ DeviceInfo::DeviceInfo() {
mMaxTextureSize = -1;
#endif
mDisplayInfo = QueryDisplayInfo();
- queryWideColorGamutPreference(&mWideColorGamut, &mWideColorSpace, &mWideColorType);
+ queryWideColorGamutPreference(&mWideColorSpace, &mWideColorType);
}
int DeviceInfo::maxTextureSize() const {
diff --git a/libs/hwui/DeviceInfo.h b/libs/hwui/DeviceInfo.h
index 9bcc8e8a3dbe..2bab5d3596cf 100644
--- a/libs/hwui/DeviceInfo.h
+++ b/libs/hwui/DeviceInfo.h
@@ -38,7 +38,6 @@ public:
// context or if you are using the HWUI_NULL_GPU
int maxTextureSize() const;
const DisplayInfo& displayInfo() const { return mDisplayInfo; }
- SkColorSpace::Gamut getWideColorGamut() const { return mWideColorGamut; }
sk_sp<SkColorSpace> getWideColorSpace() const { return mWideColorSpace; }
SkColorType getWideColorType() const { return mWideColorType; }
@@ -50,7 +49,6 @@ private:
int mMaxTextureSize;
DisplayInfo mDisplayInfo;
- SkColorSpace::Gamut mWideColorGamut;
sk_sp<SkColorSpace> mWideColorSpace;
SkColorType mWideColorType;
};
diff --git a/libs/hwui/HardwareBitmapUploader.cpp b/libs/hwui/HardwareBitmapUploader.cpp
index 635d0ec66673..39bfcdd944a4 100644
--- a/libs/hwui/HardwareBitmapUploader.cpp
+++ b/libs/hwui/HardwareBitmapUploader.cpp
@@ -164,15 +164,11 @@ static SkBitmap makeHwCompatible(const FormatInfo& format, const SkBitmap& sourc
const SkImageInfo& info = source.info();
bitmap.allocPixels(
SkImageInfo::MakeN32(info.width(), info.height(), info.alphaType(), nullptr));
- bitmap.eraseColor(0);
- if (info.colorType() == kRGBA_F16_SkColorType) {
- // Drawing RGBA_F16 onto ARGB_8888 is not supported
- source.readPixels(bitmap.info().makeColorSpace(SkColorSpace::MakeSRGB()),
- bitmap.getPixels(), bitmap.rowBytes(), 0, 0);
- } else {
- SkCanvas canvas(bitmap);
- canvas.drawBitmap(source, 0.0f, 0.0f, nullptr);
- }
+
+ SkCanvas canvas(bitmap);
+ canvas.drawColor(0);
+ canvas.drawBitmap(source, 0.0f, 0.0f, nullptr);
+
return bitmap;
}
}
@@ -253,8 +249,8 @@ sk_sp<Bitmap> HardwareBitmapUploader::allocateHardwareBitmap(const SkBitmap& sou
eglDestroySyncKHR(display, fence);
}
- return Bitmap::createFrom(buffer.get(), bitmap.refColorSpace(), bitmap.alphaType(),
- Bitmap::computePalette(bitmap));
+ return Bitmap::createFrom(buffer.get(), bitmap.colorType(), bitmap.refColorSpace(),
+ bitmap.alphaType(), Bitmap::computePalette(bitmap));
}
void HardwareBitmapUploader::terminate() {
diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp
index 6e0258c9ecb2..3bbee18c6dd1 100644
--- a/libs/hwui/hwui/Bitmap.cpp
+++ b/libs/hwui/hwui/Bitmap.cpp
@@ -133,12 +133,11 @@ sk_sp<Bitmap> Bitmap::createFrom(const SkImageInfo& info, SkPixelRef& pixelRef)
}
-sk_sp<Bitmap> Bitmap::createFrom(sp<GraphicBuffer> graphicBuffer, sk_sp<SkColorSpace> colorSpace,
- SkAlphaType alphaType, BitmapPalette palette) {
- // As we will be effectively texture-sampling the buffer (using either EGL or Vulkan), we can
- // view the format as RGBA8888.
+sk_sp<Bitmap> Bitmap::createFrom(sp<GraphicBuffer> graphicBuffer, SkColorType colorType,
+ sk_sp<SkColorSpace> colorSpace, SkAlphaType alphaType,
+ BitmapPalette palette) {
SkImageInfo info = SkImageInfo::Make(graphicBuffer->getWidth(), graphicBuffer->getHeight(),
- kRGBA_8888_SkColorType, alphaType, colorSpace);
+ colorType, alphaType, colorSpace);
return sk_sp<Bitmap>(new Bitmap(graphicBuffer.get(), info, palette));
}
diff --git a/libs/hwui/hwui/Bitmap.h b/libs/hwui/hwui/Bitmap.h
index 2138040d9690..01e45166e0a3 100644
--- a/libs/hwui/hwui/Bitmap.h
+++ b/libs/hwui/hwui/Bitmap.h
@@ -72,6 +72,7 @@ public:
* memory that is provided as an input param.
*/
static sk_sp<Bitmap> createFrom(sp<GraphicBuffer> graphicBuffer,
+ SkColorType colorType,
sk_sp<SkColorSpace> colorSpace,
SkAlphaType alphaType = kPremul_SkAlphaType,
BitmapPalette palette = BitmapPalette::Unknown);
diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
index cfbb9956d3dc..570e895a012d 100644
--- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
@@ -167,7 +167,7 @@ bool SkiaOpenGLPipeline::setSurface(ANativeWindow* surface, SwapBehavior swapBeh
if (surface) {
mRenderThread.requireGlContext();
- auto newSurface = mEglManager.createSurface(surface, colorMode, mSurfaceColorGamut);
+ auto newSurface = mEglManager.createSurface(surface, colorMode, mSurfaceColorSpace);
if (!newSurface) {
return false;
}
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
index 47c90948bbbe..a00a36f93501 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
@@ -476,11 +476,9 @@ void SkiaPipeline::dumpResourceCacheUsage() const {
void SkiaPipeline::setSurfaceColorProperties(ColorMode colorMode) {
if (colorMode == ColorMode::SRGB) {
mSurfaceColorType = SkColorType::kN32_SkColorType;
- mSurfaceColorGamut = SkColorSpace::Gamut::kSRGB_Gamut;
mSurfaceColorSpace = SkColorSpace::MakeSRGB();
} else if (colorMode == ColorMode::WideColorGamut) {
mSurfaceColorType = DeviceInfo::get()->getWideColorType();
- mSurfaceColorGamut = DeviceInfo::get()->getWideColorGamut();
mSurfaceColorSpace = DeviceInfo::get()->getWideColorSpace();
} else {
LOG_ALWAYS_FATAL("Unreachable: unsupported color mode.");
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.h b/libs/hwui/pipeline/skia/SkiaPipeline.h
index e9957df95f10..7381e0417a2d 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.h
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.h
@@ -116,7 +116,6 @@ protected:
renderthread::RenderThread& mRenderThread;
SkColorType mSurfaceColorType;
- SkColorSpace::Gamut mSurfaceColorGamut;
sk_sp<SkColorSpace> mSurfaceColorSpace;
private:
diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
index 53495a7d62c0..d0fe022616c5 100644
--- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
@@ -129,7 +129,7 @@ bool SkiaVulkanPipeline::setSurface(ANativeWindow* surface, SwapBehavior swapBeh
setSurfaceColorProperties(colorMode);
if (surface) {
mVkSurface = mVkManager.createSurface(surface, colorMode, mSurfaceColorSpace,
- mSurfaceColorGamut, mSurfaceColorType);
+ mSurfaceColorType);
}
return mVkSurface != nullptr;
@@ -149,20 +149,8 @@ void SkiaVulkanPipeline::invokeFunctor(const RenderThread& thread, Functor* func
sk_sp<Bitmap> SkiaVulkanPipeline::allocateHardwareBitmap(renderthread::RenderThread& renderThread,
SkBitmap& skBitmap) {
- // TODO: implement this function for Vulkan pipeline
- // code below is a hack to avoid crashing because of missing HW Bitmap support
- sp<GraphicBuffer> buffer = new GraphicBuffer(
- skBitmap.info().width(), skBitmap.info().height(), PIXEL_FORMAT_RGBA_8888,
- GraphicBuffer::USAGE_HW_TEXTURE | GraphicBuffer::USAGE_SW_WRITE_NEVER |
- GraphicBuffer::USAGE_SW_READ_NEVER,
- std::string("SkiaVulkanPipeline::allocateHardwareBitmap pid [") +
- std::to_string(getpid()) + "]");
- status_t error = buffer->initCheck();
- if (error < 0) {
- ALOGW("SkiaVulkanPipeline::allocateHardwareBitmap() failed in GraphicBuffer.create()");
- return nullptr;
- }
- return Bitmap::createFrom(buffer, skBitmap.refColorSpace());
+ LOG_ALWAYS_FATAL("Unimplemented");
+ return nullptr;
}
} /* namespace skiapipeline */
diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp
index 8cd97ed20215..2cc3f362e172 100644
--- a/libs/hwui/renderthread/EglManager.cpp
+++ b/libs/hwui/renderthread/EglManager.cpp
@@ -132,11 +132,13 @@ void EglManager::initialize() {
createPBufferSurface();
makeCurrent(mPBufferSurface, nullptr, /* force */ true);
- SkColorSpace::Gamut wideColorGamut = DeviceInfo::get()->getWideColorGamut();
+ skcms_Matrix3x3 wideColorGamut;
+ LOG_ALWAYS_FATAL_IF(!DeviceInfo::get()->getWideColorSpace()->toXYZD50(&wideColorGamut),
+ "Could not get gamut matrix from wideColorSpace");
bool hasWideColorSpaceExtension = false;
- if (wideColorGamut == SkColorSpace::Gamut::kDCIP3_D65_Gamut) {
+ if (memcmp(&wideColorGamut, &SkNamedGamut::kDCIP3, sizeof(wideColorGamut)) == 0) {
hasWideColorSpaceExtension = EglExtensions.displayP3;
- } else if (wideColorGamut == SkColorSpace::Gamut::kSRGB_Gamut) {
+ } else if (memcmp(&wideColorGamut, &SkNamedGamut::kSRGB, sizeof(wideColorGamut)) == 0) {
hasWideColorSpaceExtension = EglExtensions.scRGB;
} else {
LOG_ALWAYS_FATAL("Unsupported wide color space.");
@@ -297,7 +299,7 @@ void EglManager::createPBufferSurface() {
Result<EGLSurface, EGLint> EglManager::createSurface(EGLNativeWindowType window,
ColorMode colorMode,
- SkColorSpace::Gamut colorGamut) {
+ sk_sp<SkColorSpace> colorSpace) {
LOG_ALWAYS_FATAL_IF(!hasEglContext(), "Not initialized");
bool wideColorGamut = colorMode == ColorMode::WideColorGamut && mHasWideColorGamutSupport &&
@@ -330,15 +332,15 @@ Result<EGLSurface, EGLint> EglManager::createSurface(EGLNativeWindowType window,
if (EglExtensions.glColorSpace) {
attribs[0] = EGL_GL_COLORSPACE_KHR;
if (wideColorGamut) {
- switch (colorGamut) {
- case SkColorSpace::Gamut::kDCIP3_D65_Gamut:
- attribs[1] = EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT;
- break;
- case SkColorSpace::Gamut::kSRGB_Gamut:
- attribs[1] = EGL_GL_COLORSPACE_SCRGB_EXT;
- break;
- default:
- LOG_ALWAYS_FATAL("Unreachable: unsupported wide color space.");
+ skcms_Matrix3x3 colorGamut;
+ LOG_ALWAYS_FATAL_IF(!colorSpace->toXYZD50(&colorGamut),
+ "Could not get gamut matrix from color space");
+ if (memcmp(&colorGamut, &SkNamedGamut::kDCIP3, sizeof(colorGamut)) == 0) {
+ attribs[1] = EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT;
+ } else if (memcmp(&colorGamut, &SkNamedGamut::kSRGB, sizeof(colorGamut)) == 0) {
+ attribs[1] = EGL_GL_COLORSPACE_SCRGB_EXT;
+ } else {
+ LOG_ALWAYS_FATAL("Unreachable: unsupported wide color space.");
}
} else {
attribs[1] = EGL_GL_COLORSPACE_LINEAR_KHR;
diff --git a/libs/hwui/renderthread/EglManager.h b/libs/hwui/renderthread/EglManager.h
index 4dd90961b4f7..27d41d26a73a 100644
--- a/libs/hwui/renderthread/EglManager.h
+++ b/libs/hwui/renderthread/EglManager.h
@@ -49,7 +49,7 @@ public:
bool hasEglContext();
Result<EGLSurface, EGLint> createSurface(EGLNativeWindowType window, ColorMode colorMode,
- SkColorSpace::Gamut colorGamut);
+ sk_sp<SkColorSpace> colorSpace);
void destroySurface(EGLSurface surface);
void destroy();
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index ab59af71d344..720c60362a55 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -241,8 +241,10 @@ uint32_t RenderProxy::frameTimePercentile(int percentile) {
}
void RenderProxy::dumpGraphicsMemory(int fd) {
- auto& thread = RenderThread::getInstance();
- thread.queue().runSync([&]() { thread.dumpGraphicsMemory(fd); });
+ if (RenderThread::hasInstance()) {
+ auto& thread = RenderThread::getInstance();
+ thread.queue().runSync([&]() { thread.dumpGraphicsMemory(fd); });
+ }
}
void RenderProxy::setProcessStatsBuffer(int fd) {
diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp
index 5c6cb9ad43db..1e7520216d66 100644
--- a/libs/hwui/renderthread/VulkanManager.cpp
+++ b/libs/hwui/renderthread/VulkanManager.cpp
@@ -516,10 +516,9 @@ SkSurface* VulkanManager::getBackbufferSurface(VulkanSurface** surfaceOut) {
if (windowWidth != surface->mWindowWidth || windowHeight != surface->mWindowHeight) {
ColorMode colorMode = surface->mColorMode;
sk_sp<SkColorSpace> colorSpace = surface->mColorSpace;
- SkColorSpace::Gamut colorGamut = surface->mColorGamut;
SkColorType colorType = surface->mColorType;
destroySurface(surface);
- *surfaceOut = createSurface(window, colorMode, colorSpace, colorGamut, colorType);
+ *surfaceOut = createSurface(window, colorMode, colorSpace, colorType);
surface = *surfaceOut;
}
@@ -841,9 +840,12 @@ bool VulkanManager::createSwapchain(VulkanSurface* surface) {
}
if (surface->mColorMode == ColorMode::WideColorGamut) {
- if (surface->mColorGamut == SkColorSpace::Gamut::kSRGB_Gamut) {
+ skcms_Matrix3x3 surfaceGamut;
+ LOG_ALWAYS_FATAL_IF(!surface->mColorSpace->toXYZD50(&surfaceGamut),
+ "Could not get gamut matrix from color space");
+ if (memcmp(&surfaceGamut, &SkNamedGamut::kSRGB, sizeof(surfaceGamut)) == 0) {
colorSpace = VK_COLOR_SPACE_EXTENDED_SRGB_NONLINEAR_EXT;
- } else if (surface->mColorGamut == SkColorSpace::Gamut::kDCIP3_D65_Gamut) {
+ } else if (memcmp(&surfaceGamut, &SkNamedGamut::kDCIP3, sizeof(surfaceGamut)) == 0) {
colorSpace = VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT;
} else {
LOG_ALWAYS_FATAL("Unreachable: unsupported wide color space.");
@@ -922,7 +924,6 @@ bool VulkanManager::createSwapchain(VulkanSurface* surface) {
VulkanSurface* VulkanManager::createSurface(ANativeWindow* window, ColorMode colorMode,
sk_sp<SkColorSpace> surfaceColorSpace,
- SkColorSpace::Gamut surfaceColorGamut,
SkColorType surfaceColorType) {
initialize();
@@ -931,7 +932,7 @@ VulkanSurface* VulkanManager::createSurface(ANativeWindow* window, ColorMode col
}
VulkanSurface* surface = new VulkanSurface(colorMode, window, surfaceColorSpace,
- surfaceColorGamut, surfaceColorType);
+ surfaceColorType);
VkAndroidSurfaceCreateInfoKHR surfaceCreateInfo;
memset(&surfaceCreateInfo, 0, sizeof(VkAndroidSurfaceCreateInfoKHR));
diff --git a/libs/hwui/renderthread/VulkanManager.h b/libs/hwui/renderthread/VulkanManager.h
index b06eb82dac79..abe78efc9174 100644
--- a/libs/hwui/renderthread/VulkanManager.h
+++ b/libs/hwui/renderthread/VulkanManager.h
@@ -39,9 +39,9 @@ class RenderThread;
class VulkanSurface {
public:
VulkanSurface(ColorMode colorMode, ANativeWindow* window, sk_sp<SkColorSpace> colorSpace,
- SkColorSpace::Gamut colorGamut, SkColorType colorType)
+ SkColorType colorType)
: mColorMode(colorMode), mNativeWindow(window), mColorSpace(colorSpace),
- mColorGamut(colorGamut), mColorType(colorType) {}
+ mColorType(colorType) {}
sk_sp<SkSurface> getBackBufferSurface() { return mBackbuffer; }
@@ -90,7 +90,6 @@ private:
int mWindowWidth = 0;
int mWindowHeight = 0;
sk_sp<SkColorSpace> mColorSpace;
- SkColorSpace::Gamut mColorGamut;
SkColorType mColorType;
VkSurfaceTransformFlagBitsKHR mTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
SkMatrix mPreTransform;
@@ -113,7 +112,6 @@ public:
// VulkanSurface object which is returned.
VulkanSurface* createSurface(ANativeWindow* window, ColorMode colorMode,
sk_sp<SkColorSpace> surfaceColorSpace,
- SkColorSpace::Gamut surfaceColorGamut,
SkColorType surfaceColorType);
// Destroy the VulkanSurface and all associated vulkan objects.
diff --git a/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp b/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp
index ec81f629ee45..2af955fbb711 100644
--- a/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp
+++ b/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp
@@ -50,7 +50,8 @@ public:
pixels[4000 + 4 * i + 3] = 255;
}
buffer->unlock();
- sk_sp<Bitmap> hardwareBitmap(Bitmap::createFrom(buffer, SkColorSpace::MakeSRGB()));
+ sk_sp<Bitmap> hardwareBitmap(Bitmap::createFrom(buffer, kRGBA_8888_SkColorType,
+ SkColorSpace::MakeSRGB()));
sk_sp<SkShader> hardwareShader(createBitmapShader(*hardwareBitmap));
SkPoint center;
diff --git a/libs/hwui/utils/Color.cpp b/libs/hwui/utils/Color.cpp
index 4415a593f6ee..d14116f7b555 100644
--- a/libs/hwui/utils/Color.cpp
+++ b/libs/hwui/utils/Color.cpp
@@ -45,6 +45,20 @@ android::PixelFormat ColorTypeToPixelFormat(SkColorType colorType) {
}
}
+SkColorType PixelFormatToColorType(android::PixelFormat format) {
+ switch (format) {
+ case PIXEL_FORMAT_RGBX_8888: return kRGB_888x_SkColorType;
+ case PIXEL_FORMAT_RGBA_8888: return kRGBA_8888_SkColorType;
+ case PIXEL_FORMAT_RGBA_FP16: return kRGBA_F16_SkColorType;
+ case PIXEL_FORMAT_RGB_565: return kRGB_565_SkColorType;
+ case PIXEL_FORMAT_RGBA_1010102: return kRGBA_1010102_SkColorType;
+ case PIXEL_FORMAT_RGBA_4444: return kARGB_4444_SkColorType;
+ default:
+ ALOGW("Unsupported PixelFormat: %d, return kUnknown_SkColorType by default", format);
+ return kUnknown_SkColorType;
+ }
+}
+
sk_sp<SkColorSpace> DataSpaceToColorSpace(android_dataspace dataspace) {
skcms_Matrix3x3 gamut;
diff --git a/libs/hwui/utils/Color.h b/libs/hwui/utils/Color.h
index 388025207ed6..b67d10d4249c 100644
--- a/libs/hwui/utils/Color.h
+++ b/libs/hwui/utils/Color.h
@@ -112,6 +112,7 @@ static constexpr float EOCF(float srgb) {
}
android::PixelFormat ColorTypeToPixelFormat(SkColorType colorType);
+ANDROID_API SkColorType PixelFormatToColorType(android::PixelFormat format);
ANDROID_API sk_sp<SkColorSpace> DataSpaceToColorSpace(android_dataspace dataspace);
diff --git a/media/Android.bp b/media/Android.bp
index 0eb86acf9ecf..88ed9c6a05a9 100644
--- a/media/Android.bp
+++ b/media/Android.bp
@@ -1,25 +1,59 @@
java_library {
- name: "media1",
+ name: "updatable-media1",
srcs: [
":media1-srcs",
+ ":framework-media-annotation-srcs",
],
+ aidl: {
+ export_include_dirs: [
+ "apex/java",
+ ],
+
+ // TODO: find out a way to include only the necessary aidl files instead of dirs.
+ include_dirs: [
+ "frameworks/base/core/java",
+ "frameworks/base/media/java",
+ ],
+ },
+
+ installable: true,
+
+ // Make sure that the implementaion only relies on SDK or system APIs.
sdk_version: "system_current",
}
-filegroup {
- name: "media1-srcs",
+java_library {
+ name: "updatable-mediasession2",
+
srcs: [
- "java/android/media/session/MediaSessionProviderService.java",
+ ":mediasession2-srcs",
+ ":framework-media-annotation-srcs",
],
+
+ aidl: {
+ export_include_dirs: [
+ "apex/java",
+ ],
+
+ // TODO: find out a way to include only the necessary aidl files instead of dirs.
+ include_dirs: [
+ "frameworks/base/core/java",
+ ],
+ },
+
+ installable: true,
+
+ // Make sure that the implementaion only relies on SDK or system APIs.
+ sdk_version: "system_current",
}
java_library {
name: "updatable-media",
srcs: [
- ":media2-srcs",
+ ":mediaplayer2-srcs",
":framework-media-annotation-srcs",
],
@@ -34,7 +68,88 @@ java_library {
}
filegroup {
- name: "media2-srcs",
+ name: "media-srcs-without-aidls",
+ srcs : [
+ ":media1-srcs-without-aidls",
+ ":mediasession2-srcs-without-aidls",
+ ":mediaplayer2-srcs",
+ ],
+}
+
+filegroup {
+ name: "media1-srcs",
+ srcs: [
+ "apex/java/android/media/MediaMetadata.java",
+ "apex/java/android/media/MediaParceledListSlice.java",
+ "apex/java/android/media/VolumeProvider.java",
+ "apex/java/android/media/browse/MediaBrowser.java",
+ "apex/java/android/media/browse/MediaBrowserUtils.java",
+ "apex/java/android/media/session/ControllerCallbackLink.java",
+ "apex/java/android/media/session/ControllerLink.java",
+ "apex/java/android/media/session/ISession.aidl",
+ "apex/java/android/media/session/ISessionCallback.aidl",
+ "apex/java/android/media/session/ISessionController.aidl",
+ "apex/java/android/media/session/ISessionControllerCallback.aidl",
+ "apex/java/android/media/session/MediaController.java",
+ "apex/java/android/media/session/MediaSessionEngine.java",
+ "apex/java/android/media/session/MediaSessionProviderService.java",
+ "apex/java/android/media/session/PlaybackState.java",
+ "apex/java/android/media/session/SessionCallbackLink.java",
+ "apex/java/android/media/session/SessionLink.java",
+ "apex/java/android/service/media/IMediaBrowserService.aidl",
+ "apex/java/android/service/media/IMediaBrowserServiceCallbacks.aidl",
+ "apex/java/android/service/media/MediaBrowserService.java",
+ ],
+}
+
+filegroup {
+ name: "media1-srcs-without-aidls",
+ srcs: [
+ ":media1-srcs",
+ ],
+ exclude_srcs: [
+ "apex/java/android/media/session/ISession.aidl",
+ "apex/java/android/media/session/ISessionCallback.aidl",
+ "apex/java/android/media/session/ISessionController.aidl",
+ "apex/java/android/media/session/ISessionControllerCallback.aidl",
+ "apex/java/android/service/media/IMediaBrowserService.aidl",
+ "apex/java/android/service/media/IMediaBrowserServiceCallbacks.aidl",
+ ],
+}
+
+filegroup {
+ name: "mediasession2-srcs",
+ srcs: [
+ "apex/java/android/media/Controller2Link.java",
+ "apex/java/android/media/IMediaController2.aidl",
+ "apex/java/android/media/IMediaSession2.aidl",
+ "apex/java/android/media/IMediaSession2Service.aidl",
+ "apex/java/android/media/MediaConstants.java",
+ "apex/java/android/media/MediaController2.java",
+ "apex/java/android/media/MediaItem2.java",
+ "apex/java/android/media/MediaSession2.java",
+ "apex/java/android/media/MediaSession2Service.java",
+ "apex/java/android/media/Session2Command.java",
+ "apex/java/android/media/Session2CommandGroup.java",
+ "apex/java/android/media/Session2Link.java",
+ "apex/java/android/media/Session2Token.java",
+ ],
+}
+
+filegroup {
+ name: "mediasession2-srcs-without-aidls",
+ srcs: [
+ ":mediasession2-srcs",
+ ],
+ exclude_srcs: [
+ "apex/java/android/media/IMediaController2.aidl",
+ "apex/java/android/media/IMediaSession2.aidl",
+ "apex/java/android/media/IMediaSession2Service.aidl",
+ ],
+}
+
+filegroup {
+ name: "mediaplayer2-srcs",
srcs: [
"apex/java/android/media/CloseGuard.java",
"apex/java/android/media/DataSourceCallback.java",
diff --git a/media/java/android/media/Controller2Link.aidl b/media/apex/java/android/media/Controller2Link.aidl
index 64edafcb11fc..64edafcb11fc 100644
--- a/media/java/android/media/Controller2Link.aidl
+++ b/media/apex/java/android/media/Controller2Link.aidl
diff --git a/media/java/android/media/Controller2Link.java b/media/apex/java/android/media/Controller2Link.java
index d11f7769ee5e..d11f7769ee5e 100644
--- a/media/java/android/media/Controller2Link.java
+++ b/media/apex/java/android/media/Controller2Link.java
diff --git a/media/java/android/media/IMediaController2.aidl b/media/apex/java/android/media/IMediaController2.aidl
index 42c6e70529ec..42c6e70529ec 100644
--- a/media/java/android/media/IMediaController2.aidl
+++ b/media/apex/java/android/media/IMediaController2.aidl
diff --git a/media/java/android/media/IMediaSession2.aidl b/media/apex/java/android/media/IMediaSession2.aidl
index 26e717b39afc..26e717b39afc 100644
--- a/media/java/android/media/IMediaSession2.aidl
+++ b/media/apex/java/android/media/IMediaSession2.aidl
diff --git a/media/java/android/media/IMediaSession2Service.aidl b/media/apex/java/android/media/IMediaSession2Service.aidl
index 10ac1be0a36e..10ac1be0a36e 100644
--- a/media/java/android/media/IMediaSession2Service.aidl
+++ b/media/apex/java/android/media/IMediaSession2Service.aidl
diff --git a/media/java/android/media/MediaConstants.java b/media/apex/java/android/media/MediaConstants.java
index 65b6f55a068a..65b6f55a068a 100644
--- a/media/java/android/media/MediaConstants.java
+++ b/media/apex/java/android/media/MediaConstants.java
diff --git a/media/java/android/media/MediaController2.java b/media/apex/java/android/media/MediaController2.java
index 887b4475a4d1..887b4475a4d1 100644
--- a/media/java/android/media/MediaController2.java
+++ b/media/apex/java/android/media/MediaController2.java
diff --git a/media/java/android/media/MediaItem2.java b/media/apex/java/android/media/MediaItem2.java
index c496cf75995e..c496cf75995e 100644
--- a/media/java/android/media/MediaItem2.java
+++ b/media/apex/java/android/media/MediaItem2.java
diff --git a/media/java/android/media/MediaMetadata.aidl b/media/apex/java/android/media/MediaMetadata.aidl
index 66ee48304168..66ee48304168 100644
--- a/media/java/android/media/MediaMetadata.aidl
+++ b/media/apex/java/android/media/MediaMetadata.aidl
diff --git a/media/java/android/media/MediaMetadata.java b/media/apex/java/android/media/MediaMetadata.java
index a3d75a30c2b7..dea98d591db1 100644
--- a/media/java/android/media/MediaMetadata.java
+++ b/media/apex/java/android/media/MediaMetadata.java
@@ -250,16 +250,16 @@ public final class MediaMetadata implements Parcelable {
* second line for media described by this metadata this should be preferred
* to other fields if present.
*/
- public static final String METADATA_KEY_DISPLAY_SUBTITLE
- = "android.media.metadata.DISPLAY_SUBTITLE";
+ public static final String METADATA_KEY_DISPLAY_SUBTITLE =
+ "android.media.metadata.DISPLAY_SUBTITLE";
/**
* A description that is suitable for display to the user. When displaying
* more information for media described by this metadata this should be
* preferred to other fields if present.
*/
- public static final String METADATA_KEY_DISPLAY_DESCRIPTION
- = "android.media.metadata.DISPLAY_DESCRIPTION";
+ public static final String METADATA_KEY_DISPLAY_DESCRIPTION =
+ "android.media.metadata.DISPLAY_DESCRIPTION";
/**
* An icon or thumbnail that is suitable for display to the user. When
@@ -270,8 +270,8 @@ public final class MediaMetadata implements Parcelable {
* if it is too large. For higher resolution artwork
* {@link #METADATA_KEY_DISPLAY_ICON_URI} should be used instead.
*/
- public static final String METADATA_KEY_DISPLAY_ICON
- = "android.media.metadata.DISPLAY_ICON";
+ public static final String METADATA_KEY_DISPLAY_ICON =
+ "android.media.metadata.DISPLAY_ICON";
/**
* A Uri formatted String for an icon or thumbnail that is suitable for
@@ -285,8 +285,8 @@ public final class MediaMetadata implements Parcelable {
* {@link ContentResolver#EXTRA_SIZE} for retrieving scaled artwork through
* {@link ContentResolver#openTypedAssetFileDescriptor(Uri, String, Bundle)}.
*/
- public static final String METADATA_KEY_DISPLAY_ICON_URI
- = "android.media.metadata.DISPLAY_ICON_URI";
+ public static final String METADATA_KEY_DISPLAY_ICON_URI =
+ "android.media.metadata.DISPLAY_ICON_URI";
/**
* A String key for identifying the content. This value is specific to the
@@ -320,8 +320,8 @@ public final class MediaMetadata implements Parcelable {
* <li>{@link MediaDescription#BT_FOLDER_TYPE_YEARS}</li>
* </ul>
*/
- public static final String METADATA_KEY_BT_FOLDER_TYPE
- = "android.media.metadata.BT_FOLDER_TYPE";
+ public static final String METADATA_KEY_BT_FOLDER_TYPE =
+ "android.media.metadata.BT_FOLDER_TYPE";
private static final @TextKey String[] PREFERRED_DESCRIPTION_ORDER = {
METADATA_KEY_TITLE,
diff --git a/media/java/android/media/MediaParceledListSlice.aidl b/media/apex/java/android/media/MediaParceledListSlice.aidl
index 5c0e5bc84720..5c0e5bc84720 100644
--- a/media/java/android/media/MediaParceledListSlice.aidl
+++ b/media/apex/java/android/media/MediaParceledListSlice.aidl
diff --git a/media/java/android/media/MediaParceledListSlice.java b/media/apex/java/android/media/MediaParceledListSlice.java
index 16a37d99fb86..16a37d99fb86 100644
--- a/media/java/android/media/MediaParceledListSlice.java
+++ b/media/apex/java/android/media/MediaParceledListSlice.java
diff --git a/media/apex/java/android/media/MediaPlayer2.java b/media/apex/java/android/media/MediaPlayer2.java
index aa79c417922c..a2feec2e9faf 100644
--- a/media/apex/java/android/media/MediaPlayer2.java
+++ b/media/apex/java/android/media/MediaPlayer2.java
@@ -26,6 +26,7 @@ import android.content.Context;
import android.content.res.AssetFileDescriptor;
import android.graphics.Rect;
import android.graphics.SurfaceTexture;
+import android.media.MediaDrm.KeyRequest;
import android.media.MediaPlayer2.DrmInfo;
import android.media.MediaPlayer2Proto.PlayerMessage;
import android.media.MediaPlayer2Proto.Value;
@@ -76,6 +77,8 @@ import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
@@ -298,7 +301,7 @@ public class MediaPlayer2 implements AutoCloseable
private volatile float mVolume = 1.0f;
private VideoSize mVideoSize = new VideoSize(0, 0);
- private ExecutorService mDrmThreadPool = Executors.newCachedThreadPool();
+ private static ExecutorService sDrmThreadPool = Executors.newCachedThreadPool();
// Creating a dummy audio track, used for keeping session id alive
private final Object mSessionIdLock = new Object();
@@ -402,7 +405,7 @@ public class MediaPlayer2 implements AutoCloseable
// Modular DRM clean up
synchronized (mDrmEventCallbackLock) {
- mDrmEventCallbackRecords.clear();
+ mDrmEventCallback = null;
}
native_release();
@@ -2293,6 +2296,7 @@ public class MediaPlayer2 implements AutoCloseable
private static final int MEDIA_PAUSED = 7;
private static final int MEDIA_STOPPED = 8;
private static final int MEDIA_SKIPPED = 9;
+ private static final int MEDIA_DRM_PREPARED = 10;
private static final int MEDIA_NOTIFY_TIME = 98;
private static final int MEDIA_TIMED_TEXT = 99;
private static final int MEDIA_ERROR = 100;
@@ -2330,7 +2334,16 @@ public class MediaPlayer2 implements AutoCloseable
switch(msg.what) {
case MEDIA_PREPARED:
+ case MEDIA_DRM_PREPARED:
{
+ sourceInfo.mPrepareBarrier--;
+ if (sourceInfo.mPrepareBarrier > 0) {
+ break;
+ } else if (sourceInfo.mPrepareBarrier < 0) {
+ Log.w(TAG, "duplicated (drm) prepared events");
+ break;
+ }
+
if (dsd != null) {
sendEvent(new EventNotifier() {
@Override
@@ -2387,14 +2400,42 @@ public class MediaPlayer2 implements AutoCloseable
}
// notifying the client outside the lock
+ DrmPreparationInfo drmPrepareInfo = null;
if (drmInfo != null) {
- sendDrmEvent(new DrmEventNotifier() {
+ try {
+ drmPrepareInfo = sendDrmEventWait(
+ new DrmEventNotifier<DrmPreparationInfo>() {
+ @Override
+ public DrmPreparationInfo notifyWait(
+ DrmEventCallback callback) {
+ return callback.onDrmInfo(mMediaPlayer, dsd,
+ drmInfo);
+ }
+ });
+ } catch (InterruptedException | ExecutionException
+ | TimeoutException e) {
+ Log.w(TAG, "Exception while waiting for DrmPreparationInfo", e);
+ }
+ }
+ if (sourceInfo.mDrmHandle.setPreparationInfo(drmPrepareInfo)) {
+ sourceInfo.mPrepareBarrier++;
+ final Task prepareDrmTask;
+ prepareDrmTask = newPrepareDrmTask(dsd, drmPrepareInfo.mUUID);
+ mTaskHandler.post(new Runnable() {
@Override
- public void notify(DrmEventCallback callback) {
- callback.onDrmInfo(
- mMediaPlayer, dsd, drmInfo);
+ public void run() {
+ // Run as simple Runnable, not Task
+ try {
+ prepareDrmTask.process();
+ } catch (NoDrmSchemeException | IOException e) {
+ final String errMsg;
+ errMsg = "Unexpected Exception during prepareDrm";
+ throw new RuntimeException(errMsg, e);
+ }
}
});
+ } else {
+ Log.w(TAG, "No valid DrmPreparationInfo set");
}
} else {
Log.w(TAG, "MEDIA_DRM_INFO msg.obj of unexpected type " + msg.obj);
@@ -2892,7 +2933,8 @@ public class MediaPlayer2 implements AutoCloseable
private void sendDrmEvent(final DrmEventNotifier notifier) {
synchronized (mDrmEventCallbackLock) {
try {
- for (Pair<Executor, DrmEventCallback> cb : mDrmEventCallbackRecords) {
+ Pair<Executor, DrmEventCallback> cb = mDrmEventCallback;
+ if (cb != null) {
cb.first.execute(() -> notifier.notify(cb.second));
}
} catch (RejectedExecutionException e) {
@@ -2903,13 +2945,18 @@ public class MediaPlayer2 implements AutoCloseable
}
private <T> T sendDrmEventWait(final DrmEventNotifier<T> notifier)
- throws InterruptedException, ExecutionException {
+ throws InterruptedException, ExecutionException, TimeoutException {
+ return sendDrmEventWait(notifier, 0);
+ }
+
+ private <T> T sendDrmEventWait(final DrmEventNotifier<T> notifier, final long timeoutMs)
+ throws InterruptedException, ExecutionException, TimeoutException {
synchronized (mDrmEventCallbackLock) {
- mDrmEventCallbackRecords.get(0);
- for (Pair<Executor, DrmEventCallback> cb : mDrmEventCallbackRecords) {
+ Pair<Executor, DrmEventCallback> cb = mDrmEventCallback;
+ if (cb != null) {
CompletableFuture<T> ret = new CompletableFuture<>();
cb.first.execute(() -> ret.complete(notifier.notifyWait(cb.second)));
- return ret.get();
+ return timeoutMs <= 0 ? ret.get() : ret.get(timeoutMs, TimeUnit.MILLISECONDS);
}
}
return null;
@@ -3388,8 +3435,8 @@ public class MediaPlayer2 implements AutoCloseable
private Map<String, String> mOptionalParameters;
/**
- * Set UUID of the crypto scheme selected to decrypt content. An UUID can be retrieved from
- * the source listening to {@link MediaPlayer2.DrmEventCallback#onDrmInfo}.
+ * Set UUID of the crypto scheme selected to decrypt content. An UUID can be retrieved
+ * from the source listening to {@link MediaPlayer2.DrmEventCallback#onDrmInfo}.
*
* @param uuid of selected crypto scheme
* @return this
@@ -3401,11 +3448,12 @@ public class MediaPlayer2 implements AutoCloseable
/**
* Set identifier of a persisted offline key obtained from
- * {@link MediaPlayer2.DrmEventCallback#onDrmPrepared(MediaPlayer2, DataSourceDesc, int, byte[])}.
+ * {@link MediaPlayer2.DrmEventCallback#onDrmPrepared}.
*
* A {@code keySetId} can be used to restore persisted offline keys into a new playback
- * session of a DRM protected data source. When {@code keySetId} is set, {@code initData},
- * {@code mimeType}, {@code keyType}, {@code optionalParameters} are ignored.
+ * session of a DRM protected data source. When {@code keySetId} is set,
+ * {@code initData}, {@code mimeType}, {@code keyType}, {@code optionalParameters} are
+ * ignored.
*
* @param keySetId identifier of a persisted offline key
* @return this
@@ -3455,24 +3503,24 @@ public class MediaPlayer2 implements AutoCloseable
}
/**
- * Set optional parameters to be included in a {@link MediaDrm.KeyRequest} message sent to
- * the license server.
+ * Set optional parameters to be included in a {@link MediaDrm.KeyRequest} message sent
+ * to the license server.
*
* @param optionalParameters optional parameters to be included in a key request
* @return this
*/
- public Builder setOptionalParameters(
- @Nullable Map<String, String> optionalParameters) {
+ public Builder setOptionalParameters(@Nullable Map<String, String> optionalParameters) {
this.mOptionalParameters = optionalParameters;
return this;
}
/**
- * @return an immutable {@link MediaPlayer2.DrmPreparationInfo} representing the settings of this builder
+ * @return an immutable {@link MediaPlayer2.DrmPreparationInfo} representing the
+ * settings of this builder
*/
public MediaPlayer2.DrmPreparationInfo build() {
- return new MediaPlayer2.DrmPreparationInfo(mUUID, mKeySetId, mInitData, mMimeType, mKeyType,
- mOptionalParameters);
+ return new MediaPlayer2.DrmPreparationInfo(mUUID, mKeySetId, mInitData, mMimeType,
+ mKeyType, mOptionalParameters);
}
}
@@ -3494,6 +3542,20 @@ public class MediaPlayer2 implements AutoCloseable
this.mOptionalParameters = optionalParameters;
}
+ boolean isValid() {
+ if (mUUID == null) {
+ return false;
+ }
+ if (mKeySetId != null) {
+ // offline restore case
+ return true;
+ }
+ if (mInitData != null && mMimeType != null) {
+ // new streaming license case
+ return true;
+ }
+ return false;
+ }
}
/**
@@ -3501,6 +3563,7 @@ public class MediaPlayer2 implements AutoCloseable
* DRM events.
*/
public static class DrmEventCallback {
+
/**
* Called to indicate DRM info is available. Return a {@link DrmPreparationInfo} object that
* bundles DRM initialization parameters.
@@ -3517,21 +3580,6 @@ public class MediaPlayer2 implements AutoCloseable
}
/**
- * Called to notify the client that {@code mp} is ready to decrypt DRM protected data source
- * {@code dsd}
- *
- * @param mp the {@code MediaPlayer2} associated with this callback
- * @param dsd the {@link DataSourceDesc} of this data source
- * @param status the result of DRM preparation.
- * @param keySetId optional identifier that can be used to restore DRM playback initiated
- * with a {@link MediaDrm#KEY_TYPE_OFFLINE} key request.
- *
- * @see DrmPreparationInfo.Builder#setKeySetId(byte[])
- */
- public void onDrmPrepared(@NonNull MediaPlayer2 mp, @NonNull DataSourceDesc dsd,
- @PrepareDrmStatusCode int status, @Nullable byte[] keySetId) { }
-
- /**
* Called to give the app the opportunity to configure DRM before the session is created.
*
* This facilitates configuration of the properties, like 'securityLevel', which
@@ -3567,11 +3615,25 @@ public class MediaPlayer2 implements AutoCloseable
return null;
}
+ /**
+ * Called to notify the client that {@code mp} is ready to decrypt DRM protected data source
+ * {@code dsd} or if there is an error during DRM preparation
+ *
+ * @param mp the {@code MediaPlayer2} associated with this callback
+ * @param dsd the {@link DataSourceDesc} of this data source
+ * @param status the result of DRM preparation.
+ * @param keySetId optional identifier that can be used to restore DRM playback initiated
+ * with a {@link MediaDrm#KEY_TYPE_OFFLINE} key request.
+ *
+ * @see DrmPreparationInfo.Builder#setKeySetId(byte[])
+ */
+ public void onDrmPrepared(@NonNull MediaPlayer2 mp, @NonNull DataSourceDesc dsd,
+ @PrepareDrmStatusCode int status, @Nullable byte[] keySetId) { }
+
}
private final Object mDrmEventCallbackLock = new Object();
- private List<Pair<Executor, DrmEventCallback>> mDrmEventCallbackRecords =
- new ArrayList<Pair<Executor, DrmEventCallback>>();
+ private Pair<Executor, DrmEventCallback> mDrmEventCallback;
/**
* Registers the callback to be invoked for various DRM events.
@@ -3590,25 +3652,17 @@ public class MediaPlayer2 implements AutoCloseable
"Illegal null Executor for the EventCallback");
}
synchronized (mDrmEventCallbackLock) {
- mDrmEventCallbackRecords = Collections.singletonList(
- new Pair<Executor, DrmEventCallback>(executor, eventCallback));
+ mDrmEventCallback = new Pair<Executor, DrmEventCallback>(executor, eventCallback);
}
}
/**
- * Unregisters the {@link DrmEventCallback}.
- *
- * @param eventCallback the callback to be unregistered
- * @hide
+ * Clear the {@link DrmEventCallback}.
*/
// This is a synchronous call.
- public void unregisterDrmEventCallback(DrmEventCallback eventCallback) {
+ public void clearDrmEventCallback() {
synchronized (mDrmEventCallbackLock) {
- for (Pair<Executor, DrmEventCallback> cb : mDrmEventCallbackRecords) {
- if (cb.second == eventCallback) {
- mDrmEventCallbackRecords.remove(cb);
- }
- }
+ mDrmEventCallback = null;
}
}
@@ -3651,6 +3705,18 @@ public class MediaPlayer2 implements AutoCloseable
*/
public static final int PREPARE_DRM_STATUS_RESOURCE_BUSY = 5;
+ /**
+ * Restoring persisted offline keys failed.
+ * @hide
+ */
+ public static final int PREPARE_DRM_STATUS_RESTORE_ERROR = 6;
+
+ /**
+ * Error during key request/response exchange with license server.
+ * @hide
+ */
+ public static final int PREPARE_DRM_STATUS_KEY_EXCHANGE_ERROR = 7;
+
/** @hide */
@IntDef(flag = false, prefix = "PREPARE_DRM_STATUS", value = {
PREPARE_DRM_STATUS_SUCCESS,
@@ -3659,6 +3725,8 @@ public class MediaPlayer2 implements AutoCloseable
PREPARE_DRM_STATUS_PREPARATION_ERROR,
PREPARE_DRM_STATUS_UNSUPPORTED_SCHEME,
PREPARE_DRM_STATUS_RESOURCE_BUSY,
+ PREPARE_DRM_STATUS_RESTORE_ERROR,
+ PREPARE_DRM_STATUS_KEY_EXCHANGE_ERROR,
})
@Retention(RetentionPolicy.SOURCE)
public @interface PrepareDrmStatusCode {}
@@ -3747,12 +3815,16 @@ public class MediaPlayer2 implements AutoCloseable
*/
// This is an asynchronous call.
public Object prepareDrm(@NonNull DataSourceDesc dsd, @NonNull UUID uuid) {
- return addTask(new Task(CALL_COMPLETED_PREPARE_DRM, true) {
+ return addTask(newPrepareDrmTask(dsd, uuid));
+ }
+
+ private Task newPrepareDrmTask(DataSourceDesc dsd, UUID uuid) {
+ return new Task(CALL_COMPLETED_PREPARE_DRM, true) {
@Override
void process() {
final SourceInfo sourceInfo = getSourceInfo(dsd);
int status = PREPARE_DRM_STATUS_PREPARATION_ERROR;
- boolean sendEvent = true;
+ boolean finishPrepare = true;
if (sourceInfo == null) {
Log.e(TAG, "prepareDrm(): DataSource not found.");
@@ -3780,8 +3852,8 @@ public class MediaPlayer2 implements AutoCloseable
status = sourceInfo.mDrmHandle.handleProvisioninig(uuid, mTaskId);
if (status == PREPARE_DRM_STATUS_SUCCESS) {
- // DrmEventCallback will be fired in provisioning
- sendEvent = false;
+ // License will be setup in provisioning
+ finishPrepare = false;
} else {
synchronized (sourceInfo.mDrmHandle) {
sourceInfo.mDrmHandle.cleanDrmObj();
@@ -3808,23 +3880,16 @@ public class MediaPlayer2 implements AutoCloseable
status = PREPARE_DRM_STATUS_PREPARATION_ERROR;
}
- if (sendEvent) {
- final int prepareDrmStatus = status;
- sendDrmEvent(new DrmEventNotifier() {
- @Override
- public void notify(DrmEventCallback callback) {
- callback.onDrmPrepared(MediaPlayer2.this, dsd, prepareDrmStatus,
- /* TODO: keySetId */ null);
- }
- });
-
+ if (finishPrepare) {
+ sourceInfo.mDrmHandle.finishPrepare(status);
synchronized (mTaskLock) {
mCurrentTask = null;
processPendingTask_l();
}
}
+
}
- });
+ };
}
/**
@@ -4417,7 +4482,7 @@ public class MediaPlayer2 implements AutoCloseable
};
// Modular DRM
- final class DrmHandle {
+ private class DrmHandle {
static final int PROVISION_TIMEOUT_MS = 60000;
@@ -4432,6 +4497,7 @@ public class MediaPlayer2 implements AutoCloseable
boolean mDrmProvisioningInProgress;
boolean mPrepareDrmInProgress;
Future<?> mProvisionResult;
+ DrmPreparationInfo mPrepareInfo;
//--- guarded by |this| end
DrmHandle(DataSourceDesc dsd, long srcId) {
@@ -4441,7 +4507,7 @@ public class MediaPlayer2 implements AutoCloseable
void prepare(UUID uuid) throws UnsupportedSchemeException,
ResourceBusyException, NotProvisionedException, InterruptedException,
- ExecutionException {
+ ExecutionException, TimeoutException {
Log.v(TAG, "prepareDrm: uuid: " + uuid);
synchronized (this) {
@@ -4580,7 +4646,7 @@ public class MediaPlayer2 implements AutoCloseable
// networking in a background thread
mDrmProvisioningInProgress = true;
- mProvisionResult = mDrmThreadPool.submit(newProvisioningTask(uuid, taskId));
+ mProvisionResult = sDrmThreadPool.submit(newProvisioningTask(uuid, taskId));
return PREPARE_DRM_STATUS_SUCCESS;
}
@@ -4654,14 +4720,7 @@ public class MediaPlayer2 implements AutoCloseable
} // synchronized
// calling the callback outside the lock
- final int finalStatus = status;
- sendDrmEvent(new DrmEventNotifier() {
- @Override
- public void notify(DrmEventCallback callback) {
- callback.onDrmPrepared(
- MediaPlayer2.this, mDSD, finalStatus, /* TODO: keySetId */ null);
- }
- });
+ finishPrepare(status);
synchronized (mTaskLock) {
if (mCurrentTask != null
@@ -4703,6 +4762,93 @@ public class MediaPlayer2 implements AutoCloseable
return success;
}
+ synchronized boolean setPreparationInfo(DrmPreparationInfo prepareInfo) {
+ if (prepareInfo == null || !prepareInfo.isValid() || mPrepareInfo != null) {
+ return false;
+ }
+ mPrepareInfo = prepareInfo;
+ return true;
+ }
+
+ void finishPrepare(int status) {
+ if (status != PREPARE_DRM_STATUS_SUCCESS) {
+ notifyPrepared(status, null);
+ return;
+ }
+
+ if (mPrepareInfo == null) {
+ // Deprecated: this can only happen when using MediaPlayer Version 1 APIs
+ notifyPrepared(status, null);
+ return;
+ }
+
+ final byte[] keySetId = mPrepareInfo.mKeySetId;
+ if (keySetId != null) {
+ try {
+ mDrmObj.restoreKeys(mDrmSessionId, keySetId);
+ notifyPrepared(PREPARE_DRM_STATUS_SUCCESS, keySetId);
+ } catch (Exception e) {
+ notifyPrepared(PREPARE_DRM_STATUS_RESTORE_ERROR, keySetId);
+ }
+ return;
+ }
+
+ sDrmThreadPool.submit(newKeyExchangeTask());
+ }
+
+ Runnable newKeyExchangeTask() {
+ return new Runnable() {
+ @Override
+ public void run() {
+ final byte[] initData = mPrepareInfo.mInitData;
+ final String mimeType = mPrepareInfo.mMimeType;
+ final int keyType = mPrepareInfo.mKeyType;
+ final Map<String, String> optionalParams = mPrepareInfo.mOptionalParameters;
+ byte[] keySetId = null;
+ try {
+ KeyRequest req;
+ req = getDrmKeyRequest(null, initData, mimeType, keyType, optionalParams);
+ byte[] response = sendDrmEventWait(new DrmEventNotifier<byte[]>() {
+ @Override
+ public byte[] notifyWait(DrmEventCallback callback) {
+ final MediaPlayer2 mp = MediaPlayer2.this;
+ return callback.onDrmKeyRequest(mp, mDSD, req);
+ }
+ });
+ keySetId = provideDrmKeyResponse(null, response);
+ } catch (Exception e) {
+ }
+ if (keySetId == null) {
+ notifyPrepared(PREPARE_DRM_STATUS_KEY_EXCHANGE_ERROR, null);
+ } else {
+ notifyPrepared(PREPARE_DRM_STATUS_SUCCESS, keySetId);
+ }
+ }
+ };
+ }
+
+ void notifyPrepared(final int status, byte[] keySetId) {
+
+ Message msg;
+ if (status == PREPARE_DRM_STATUS_SUCCESS) {
+ msg = mTaskHandler.obtainMessage(
+ MEDIA_DRM_PREPARED, 0, 0, null);
+ } else {
+ msg = mTaskHandler.obtainMessage(
+ MEDIA_ERROR, status, MEDIA_ERROR_UNKNOWN, null);
+ }
+ mTaskHandler.sendMessage(msg);
+
+ sendDrmEvent(new DrmEventNotifier() {
+ @Override
+ public void notify(DrmEventCallback callback) {
+ callback.onDrmPrepared(MediaPlayer2.this, mDSD, status,
+ keySetId);
+ }
+ });
+
+ }
+
void cleanDrmObj() {
// the caller holds mDrmLock
Log.v(TAG, "cleanDrmObj: mDrmObj=" + mDrmObj + " mDrmSessionId=" + mDrmSessionId);
@@ -4768,6 +4914,7 @@ public class MediaPlayer2 implements AutoCloseable
// set to false to avoid duplicate release calls
this.mActiveDrmUUID = null;
+ native_releaseDrm(mSrcId);
cleanDrmObj();
} // synchronized
}
@@ -4924,6 +5071,7 @@ public class MediaPlayer2 implements AutoCloseable
final long mId = mSrcIdGenerator.getAndIncrement();
AtomicInteger mBufferedPercentage = new AtomicInteger(0);
boolean mClosed = false;
+ int mPrepareBarrier = 1;
// m*AsNextSource (below) only applies to pending data sources in the playlist;
// the meanings of mCurrentSourceInfo.{mStateAsNextSource,mPlayPendingAsNextSource}
@@ -5022,7 +5170,7 @@ public class MediaPlayer2 implements AutoCloseable
if (sourceInfo != null) {
sourceInfo.close();
Runnable task = sourceInfo.mDrmHandle.newCleanupTask();
- mDrmThreadPool.submit(task);
+ sDrmThreadPool.submit(task);
}
}
diff --git a/media/java/android/media/MediaSession2.java b/media/apex/java/android/media/MediaSession2.java
index fdd07fdd52e3..fdd07fdd52e3 100644
--- a/media/java/android/media/MediaSession2.java
+++ b/media/apex/java/android/media/MediaSession2.java
diff --git a/media/java/android/media/MediaSession2Service.java b/media/apex/java/android/media/MediaSession2Service.java
index 5bb746a7f9e3..5bb746a7f9e3 100644
--- a/media/java/android/media/MediaSession2Service.java
+++ b/media/apex/java/android/media/MediaSession2Service.java
diff --git a/media/java/android/media/Session2Command.aidl b/media/apex/java/android/media/Session2Command.aidl
index 43a7b123ed29..43a7b123ed29 100644
--- a/media/java/android/media/Session2Command.aidl
+++ b/media/apex/java/android/media/Session2Command.aidl
diff --git a/media/java/android/media/Session2Command.java b/media/apex/java/android/media/Session2Command.java
index 8b285f212a8d..8b285f212a8d 100644
--- a/media/java/android/media/Session2Command.java
+++ b/media/apex/java/android/media/Session2Command.java
diff --git a/media/java/android/media/Session2CommandGroup.java b/media/apex/java/android/media/Session2CommandGroup.java
index a189c264b029..2dab697c155d 100644
--- a/media/java/android/media/Session2CommandGroup.java
+++ b/media/apex/java/android/media/Session2CommandGroup.java
@@ -71,11 +71,10 @@ public final class Session2CommandGroup implements Parcelable {
*/
@SuppressWarnings("WeakerAccess") /* synthetic access */
Session2CommandGroup(Parcel in) {
- Session2Command[] commands = in.readParcelableArray(
- Session2Command.class.getClassLoader(), Session2Command.class);
+ Parcelable[] commands = in.readParcelableArray(Session2Command.class.getClassLoader());
if (commands != null) {
- for (Session2Command command : commands) {
- mCommands.add(command);
+ for (Parcelable command : commands) {
+ mCommands.add((Session2Command) command);
}
}
}
diff --git a/media/java/android/media/Session2Link.java b/media/apex/java/android/media/Session2Link.java
index 08664aa3b38f..08664aa3b38f 100644
--- a/media/java/android/media/Session2Link.java
+++ b/media/apex/java/android/media/Session2Link.java
diff --git a/media/java/android/media/Session2Token.aidl b/media/apex/java/android/media/Session2Token.aidl
index c5980e9e77fd..c5980e9e77fd 100644
--- a/media/java/android/media/Session2Token.aidl
+++ b/media/apex/java/android/media/Session2Token.aidl
diff --git a/media/java/android/media/Session2Token.java b/media/apex/java/android/media/Session2Token.java
index 238cc2b8ee7d..238cc2b8ee7d 100644
--- a/media/java/android/media/Session2Token.java
+++ b/media/apex/java/android/media/Session2Token.java
diff --git a/media/java/android/media/VolumeProvider.java b/media/apex/java/android/media/VolumeProvider.java
index 1c017c564b43..49202eecef19 100644
--- a/media/java/android/media/VolumeProvider.java
+++ b/media/apex/java/android/media/VolumeProvider.java
@@ -16,6 +16,7 @@
package android.media;
import android.annotation.IntDef;
+import android.annotation.SystemApi;
import android.media.session.MediaSession;
import java.lang.annotation.Retention;
@@ -147,6 +148,7 @@ public abstract class VolumeProvider {
* Sets a callback to receive volume changes.
* @hide
*/
+ @SystemApi
public void setCallback(Callback callback) {
mCallback = callback;
}
@@ -155,7 +157,11 @@ public abstract class VolumeProvider {
* Listens for changes to the volume.
* @hide
*/
- public static abstract class Callback {
+ @SystemApi
+ public abstract static class Callback {
+ /**
+ * Called when volume changed.
+ */
public abstract void onVolumeChanged(VolumeProvider volumeProvider);
}
}
diff --git a/media/java/android/media/browse/MediaBrowser.aidl b/media/apex/java/android/media/browse/MediaBrowser.aidl
index 782e09471a56..782e09471a56 100644
--- a/media/java/android/media/browse/MediaBrowser.aidl
+++ b/media/apex/java/android/media/browse/MediaBrowser.aidl
diff --git a/media/java/android/media/browse/MediaBrowser.java b/media/apex/java/android/media/browse/MediaBrowser.java
index b1b14c6e4ba5..2dffef9fb40a 100644
--- a/media/java/android/media/browse/MediaBrowser.java
+++ b/media/apex/java/android/media/browse/MediaBrowser.java
@@ -284,8 +284,8 @@ public final class MediaBrowser {
*/
public @NonNull ComponentName getServiceComponent() {
if (!isConnected()) {
- throw new IllegalStateException("getServiceComponent() called while not connected" +
- " (state=" + mState + ")");
+ throw new IllegalStateException("getServiceComponent() called while not connected"
+ + " (state=" + mState + ")");
}
return mServiceComponent;
}
@@ -331,7 +331,7 @@ public final class MediaBrowser {
*
* @throws IllegalStateException if not connected.
*/
- public @NonNull MediaSession.Token getSessionToken() {
+ public @NonNull MediaSession.Token getSessionToken() {
if (!isConnected()) {
throw new IllegalStateException("getSessionToken() called while not connected (state="
+ mState + ")");
@@ -464,7 +464,7 @@ public final class MediaBrowser {
cb.onError(mediaId);
return;
}
- cb.onItemLoaded((MediaItem)item);
+ cb.onItemLoaded((MediaItem) item);
}
};
try {
@@ -575,7 +575,7 @@ public final class MediaBrowser {
}
}
- private final void onServiceConnected(final IMediaBrowserServiceCallbacks callback,
+ private void onServiceConnected(final IMediaBrowserServiceCallbacks callback,
final String root, final MediaSession.Token session, final Bundle extra) {
mHandler.post(new Runnable() {
@Override
@@ -625,7 +625,7 @@ public final class MediaBrowser {
});
}
- private final void onConnectionFailed(final IMediaBrowserServiceCallbacks callback) {
+ private void onConnectionFailed(final IMediaBrowserServiceCallbacks callback) {
mHandler.post(new Runnable() {
@Override
public void run() {
@@ -652,7 +652,7 @@ public final class MediaBrowser {
});
}
- private final void onLoadChildren(final IMediaBrowserServiceCallbacks callback,
+ private void onLoadChildren(final IMediaBrowserServiceCallbacks callback,
final String parentId, final MediaParceledListSlice list, final Bundle options) {
mHandler.post(new Runnable() {
@Override
@@ -745,7 +745,7 @@ public final class MediaBrowser {
/** @hide */
@Retention(RetentionPolicy.SOURCE)
- @IntDef(flag=true, value = { FLAG_BROWSABLE, FLAG_PLAYABLE })
+ @IntDef(flag = true, value = { FLAG_BROWSABLE, FLAG_PLAYABLE })
public @interface Flags { }
/**
@@ -886,7 +886,7 @@ public final class MediaBrowser {
/**
* Callbacks for subscription related events.
*/
- public static abstract class SubscriptionCallback {
+ public abstract static class SubscriptionCallback {
Binder mToken;
public SubscriptionCallback() {
@@ -947,7 +947,7 @@ public final class MediaBrowser {
/**
* Callback for receiving the result of {@link #getItem}.
*/
- public static abstract class ItemCallback {
+ public abstract static class ItemCallback {
/**
* Called when the item has been returned by the connected service.
*
@@ -1078,7 +1078,7 @@ public final class MediaBrowser {
private static class ServiceCallbacks extends IMediaBrowserServiceCallbacks.Stub {
private WeakReference<MediaBrowser> mMediaBrowser;
- public ServiceCallbacks(MediaBrowser mediaBrowser) {
+ ServiceCallbacks(MediaBrowser mediaBrowser) {
mMediaBrowser = new WeakReference<MediaBrowser>(mediaBrowser);
}
@@ -1125,7 +1125,7 @@ public final class MediaBrowser {
private final List<SubscriptionCallback> mCallbacks;
private final List<Bundle> mOptionsList;
- public Subscription() {
+ Subscription() {
mCallbacks = new ArrayList<>();
mOptionsList = new ArrayList<>();
}
diff --git a/media/java/android/media/browse/MediaBrowserUtils.java b/media/apex/java/android/media/browse/MediaBrowserUtils.java
index 2943e60dbbbd..19d9f008d3db 100644
--- a/media/java/android/media/browse/MediaBrowserUtils.java
+++ b/media/apex/java/android/media/browse/MediaBrowserUtils.java
@@ -22,6 +22,9 @@ import android.os.Bundle;
* @hide
*/
public class MediaBrowserUtils {
+ /**
+ * Compares whether two bundles are the same.
+ */
public static boolean areSameOptions(Bundle options1, Bundle options2) {
if (options1 == options2) {
return true;
@@ -39,6 +42,9 @@ public class MediaBrowserUtils {
}
}
+ /**
+ * Returnes true if the page options has duplicated items.
+ */
public static boolean hasDuplicatedItems(Bundle options1, Bundle options2) {
int page1 = options1 == null ? -1 : options1.getInt(MediaBrowser.EXTRA_PAGE, -1);
int page2 = options2 == null ? -1 : options2.getInt(MediaBrowser.EXTRA_PAGE, -1);
diff --git a/media/java/android/media/session/ControllerCallbackLink.aidl b/media/apex/java/android/media/session/ControllerCallbackLink.aidl
index 8ee8c7d00148..8ee8c7d00148 100644
--- a/media/java/android/media/session/ControllerCallbackLink.aidl
+++ b/media/apex/java/android/media/session/ControllerCallbackLink.aidl
diff --git a/media/java/android/media/session/ControllerCallbackLink.java b/media/apex/java/android/media/session/ControllerCallbackLink.java
index 2d59e442abf0..adc14a550b7d 100644
--- a/media/java/android/media/session/ControllerCallbackLink.java
+++ b/media/apex/java/android/media/session/ControllerCallbackLink.java
@@ -315,12 +315,6 @@ public final class ControllerCallbackLink implements Parcelable {
}
private void ensureMediaControlPermission() {
- // Allow API calls from the System UI
- if (mContext.checkCallingPermission(android.Manifest.permission.STATUS_BAR_SERVICE)
- == PackageManager.PERMISSION_GRANTED) {
- return;
- }
-
// Check if it's system server or has MEDIA_CONTENT_CONTROL.
// Note that system server doesn't have MEDIA_CONTENT_CONTROL, so we need extra
// check here.
diff --git a/media/java/android/media/session/ControllerLink.aidl b/media/apex/java/android/media/session/ControllerLink.aidl
index 532df59d16cf..532df59d16cf 100644
--- a/media/java/android/media/session/ControllerLink.aidl
+++ b/media/apex/java/android/media/session/ControllerLink.aidl
diff --git a/media/java/android/media/session/ControllerLink.java b/media/apex/java/android/media/session/ControllerLink.java
index 937df20949f0..937df20949f0 100644
--- a/media/java/android/media/session/ControllerLink.java
+++ b/media/apex/java/android/media/session/ControllerLink.java
diff --git a/media/java/android/media/session/ISession.aidl b/media/apex/java/android/media/session/ISession.aidl
index 9b1ad7bcf77c..9b1ad7bcf77c 100644
--- a/media/java/android/media/session/ISession.aidl
+++ b/media/apex/java/android/media/session/ISession.aidl
diff --git a/media/java/android/media/session/ISessionCallback.aidl b/media/apex/java/android/media/session/ISessionCallback.aidl
index 9b86bfced340..9b86bfced340 100644
--- a/media/java/android/media/session/ISessionCallback.aidl
+++ b/media/apex/java/android/media/session/ISessionCallback.aidl
diff --git a/media/java/android/media/session/ISessionController.aidl b/media/apex/java/android/media/session/ISessionController.aidl
index a3439a1a8deb..a3439a1a8deb 100644
--- a/media/java/android/media/session/ISessionController.aidl
+++ b/media/apex/java/android/media/session/ISessionController.aidl
diff --git a/media/java/android/media/session/ISessionControllerCallback.aidl b/media/apex/java/android/media/session/ISessionControllerCallback.aidl
index 56ae852d6f50..56ae852d6f50 100644
--- a/media/java/android/media/session/ISessionControllerCallback.aidl
+++ b/media/apex/java/android/media/session/ISessionControllerCallback.aidl
diff --git a/media/java/android/media/session/MediaController.aidl b/media/apex/java/android/media/session/MediaController.aidl
index 17167f45d0e3..17167f45d0e3 100644
--- a/media/java/android/media/session/MediaController.aidl
+++ b/media/apex/java/android/media/session/MediaController.aidl
diff --git a/media/java/android/media/session/MediaController.java b/media/apex/java/android/media/session/MediaController.java
index 057c9cb028c1..d43acf47b863 100644
--- a/media/java/android/media/session/MediaController.java
+++ b/media/apex/java/android/media/session/MediaController.java
@@ -206,6 +206,7 @@ public final class MediaController {
} catch (RuntimeException e) {
Log.wtf(TAG, "Error calling adjustVolumeBy", e);
}
+ break;
}
case KeyEvent.ACTION_UP: {
@@ -319,7 +320,7 @@ public final class MediaController {
*
* @return The current set of flags for the session.
*/
- public @MediaSession.SessionFlags long getFlags() {
+ public long getFlags() {
try {
return mSessionBinder.getFlags();
} catch (RuntimeException e) {
@@ -582,7 +583,7 @@ public final class MediaController {
return null;
}
- private final void postMessage(int what, Object obj, Bundle data) {
+ private void postMessage(int what, Object obj, Bundle data) {
synchronized (mLock) {
for (int i = mCallbacks.size() - 1; i >= 0; i--) {
mCallbacks.get(i).post(what, obj, data);
@@ -594,7 +595,7 @@ public final class MediaController {
* Callback for receiving updates from the session. A Callback can be
* registered using {@link #registerCallback}.
*/
- public static abstract class Callback {
+ public abstract static class Callback {
/**
* Override to handle the session being destroyed. The session is no
* longer valid after this call and calls to it will be ignored.
@@ -1191,11 +1192,11 @@ public final class MediaController {
}
}
- private final static class MessageHandler extends Handler {
+ private static final class MessageHandler extends Handler {
private final MediaController.Callback mCallback;
private boolean mRegistered = false;
- public MessageHandler(Looper looper, MediaController.Callback cb) {
+ MessageHandler(Looper looper, MediaController.Callback cb) {
super(looper);
mCallback = cb;
}
diff --git a/media/java/android/media/session/MediaSessionEngine.java b/media/apex/java/android/media/session/MediaSessionEngine.java
index f159a9538835..1f5fa5fe4127 100644
--- a/media/java/android/media/session/MediaSessionEngine.java
+++ b/media/apex/java/android/media/session/MediaSessionEngine.java
@@ -53,7 +53,7 @@ import java.util.Objects;
*/
@SystemApi
public final class MediaSessionEngine implements AutoCloseable {
- private static final String TAG = MediaSession.TAG;
+ private static final String TAG = "MediaSession";
private final Object mLock = new Object();
private final int mMaxBitmapSize;
@@ -172,7 +172,7 @@ public final class MediaSessionEngine implements AutoCloseable {
*
* @param flags The flags to set for this session.
*/
- public void setFlags(@MediaSession.SessionFlags int flags) {
+ public void setFlags(int flags) {
try {
mSessionLink.setFlags(flags);
} catch (RuntimeException e) {
@@ -409,7 +409,7 @@ public final class MediaSessionEngine implements AutoCloseable {
* <li>{@link Rating#RATING_THUMB_UP_DOWN}</li>
* </ul>
*/
- public void setRatingType(@Rating.Style int type) {
+ public void setRatingType(int type) {
try {
mSessionLink.setRatingType(type);
} catch (RuntimeException e) {
diff --git a/media/java/android/media/session/MediaSessionProviderService.java b/media/apex/java/android/media/session/MediaSessionProviderService.java
index 9a346ff4a12e..9a346ff4a12e 100644
--- a/media/java/android/media/session/MediaSessionProviderService.java
+++ b/media/apex/java/android/media/session/MediaSessionProviderService.java
diff --git a/media/java/android/media/session/PlaybackState.aidl b/media/apex/java/android/media/session/PlaybackState.aidl
index 0876ebd2d4d2..0876ebd2d4d2 100644
--- a/media/java/android/media/session/PlaybackState.aidl
+++ b/media/apex/java/android/media/session/PlaybackState.aidl
diff --git a/media/java/android/media/session/PlaybackState.java b/media/apex/java/android/media/session/PlaybackState.java
index 0d0ec4c78394..6b28c976c710 100644
--- a/media/java/android/media/session/PlaybackState.java
+++ b/media/apex/java/android/media/session/PlaybackState.java
@@ -41,7 +41,7 @@ public final class PlaybackState implements Parcelable {
/**
* @hide
*/
- @LongDef(flag=true, value={ACTION_STOP, ACTION_PAUSE, ACTION_PLAY, ACTION_REWIND,
+ @LongDef(flag = true, value = {ACTION_STOP, ACTION_PAUSE, ACTION_PLAY, ACTION_REWIND,
ACTION_SKIP_TO_PREVIOUS, ACTION_SKIP_TO_NEXT, ACTION_FAST_FORWARD, ACTION_SET_RATING,
ACTION_SEEK_TO, ACTION_PLAY_PAUSE, ACTION_PLAY_FROM_MEDIA_ID, ACTION_PLAY_FROM_SEARCH,
ACTION_SKIP_TO_QUEUE_ITEM, ACTION_PLAY_FROM_URI, ACTION_PREPARE,
@@ -191,42 +191,42 @@ public final class PlaybackState implements Parcelable {
* @see Builder#setState(int, long, float)
* @see Builder#setState(int, long, float, long)
*/
- public final static int STATE_NONE = 0;
+ public static final int STATE_NONE = 0;
/**
* State indicating this item is currently stopped.
*
* @see Builder#setState
*/
- public final static int STATE_STOPPED = 1;
+ public static final int STATE_STOPPED = 1;
/**
* State indicating this item is currently paused.
*
* @see Builder#setState
*/
- public final static int STATE_PAUSED = 2;
+ public static final int STATE_PAUSED = 2;
/**
* State indicating this item is currently playing.
*
* @see Builder#setState
*/
- public final static int STATE_PLAYING = 3;
+ public static final int STATE_PLAYING = 3;
/**
* State indicating this item is currently fast forwarding.
*
* @see Builder#setState
*/
- public final static int STATE_FAST_FORWARDING = 4;
+ public static final int STATE_FAST_FORWARDING = 4;
/**
* State indicating this item is currently rewinding.
*
* @see Builder#setState
*/
- public final static int STATE_REWINDING = 5;
+ public static final int STATE_REWINDING = 5;
/**
* State indicating this item is currently buffering and will begin playing
@@ -234,7 +234,7 @@ public final class PlaybackState implements Parcelable {
*
* @see Builder#setState
*/
- public final static int STATE_BUFFERING = 6;
+ public static final int STATE_BUFFERING = 6;
/**
* State indicating this item is currently in an error state. The error
@@ -242,7 +242,7 @@ public final class PlaybackState implements Parcelable {
*
* @see Builder#setState
*/
- public final static int STATE_ERROR = 7;
+ public static final int STATE_ERROR = 7;
/**
* State indicating the class doing playback is currently connecting to a
@@ -252,21 +252,21 @@ public final class PlaybackState implements Parcelable {
*
* @see Builder#setState
*/
- public final static int STATE_CONNECTING = 8;
+ public static final int STATE_CONNECTING = 8;
/**
* State indicating the player is currently skipping to the previous item.
*
* @see Builder#setState
*/
- public final static int STATE_SKIPPING_TO_PREVIOUS = 9;
+ public static final int STATE_SKIPPING_TO_PREVIOUS = 9;
/**
* State indicating the player is currently skipping to the next item.
*
* @see Builder#setState
*/
- public final static int STATE_SKIPPING_TO_NEXT = 10;
+ public static final int STATE_SKIPPING_TO_NEXT = 10;
/**
* State indicating the player is currently skipping to a specific item in
@@ -274,12 +274,12 @@ public final class PlaybackState implements Parcelable {
*
* @see Builder#setState
*/
- public final static int STATE_SKIPPING_TO_QUEUE_ITEM = 11;
+ public static final int STATE_SKIPPING_TO_QUEUE_ITEM = 11;
/**
* Use this value for the position to indicate the position is not known.
*/
- public final static long PLAYBACK_POSITION_UNKNOWN = -1;
+ public static final long PLAYBACK_POSITION_UNKNOWN = -1;
private final int mState;
private final long mPosition;
@@ -534,19 +534,19 @@ public final class PlaybackState implements Parcelable {
return 0;
}
- public static final Parcelable.Creator<PlaybackState.CustomAction> CREATOR
- = new Parcelable.Creator<PlaybackState.CustomAction>() {
+ public static final Parcelable.Creator<PlaybackState.CustomAction> CREATOR =
+ new Parcelable.Creator<PlaybackState.CustomAction>() {
- @Override
- public PlaybackState.CustomAction createFromParcel(Parcel p) {
- return new PlaybackState.CustomAction(p);
- }
+ @Override
+ public PlaybackState.CustomAction createFromParcel(Parcel p) {
+ return new PlaybackState.CustomAction(p);
+ }
- @Override
- public PlaybackState.CustomAction[] newArray(int size) {
- return new PlaybackState.CustomAction[size];
- }
- };
+ @Override
+ public PlaybackState.CustomAction[] newArray(int size) {
+ return new PlaybackState.CustomAction[size];
+ }
+ };
/**
* Returns the action of the {@link CustomAction}.
@@ -588,10 +588,7 @@ public final class PlaybackState implements Parcelable {
@Override
public String toString() {
- return "Action:" +
- "mName='" + mName +
- ", mIcon=" + mIcon +
- ", mExtras=" + mExtras;
+ return "Action:" + "mName='" + mName + ", mIcon=" + mIcon + ", mExtras=" + mExtras;
}
/**
diff --git a/media/java/android/media/session/SessionCallbackLink.aidl b/media/apex/java/android/media/session/SessionCallbackLink.aidl
index c489e5bee6e2..c489e5bee6e2 100644
--- a/media/java/android/media/session/SessionCallbackLink.aidl
+++ b/media/apex/java/android/media/session/SessionCallbackLink.aidl
diff --git a/media/java/android/media/session/SessionCallbackLink.java b/media/apex/java/android/media/session/SessionCallbackLink.java
index 0dbf427b048d..3bcb65c42010 100644
--- a/media/java/android/media/session/SessionCallbackLink.java
+++ b/media/apex/java/android/media/session/SessionCallbackLink.java
@@ -944,12 +944,6 @@ public final class SessionCallbackLink implements Parcelable {
}
private void ensureMediaControlPermission() {
- // Allow API calls from the System UI
- if (mContext.checkCallingPermission(android.Manifest.permission.STATUS_BAR_SERVICE)
- == PackageManager.PERMISSION_GRANTED) {
- return;
- }
-
// Check if it's system server or has MEDIA_CONTENT_CONTROL.
// Note that system server doesn't have MEDIA_CONTENT_CONTROL, so we need extra
// check here.
diff --git a/media/java/android/media/session/SessionLink.aidl b/media/apex/java/android/media/session/SessionLink.aidl
index c3be23e8f6b7..c3be23e8f6b7 100644
--- a/media/java/android/media/session/SessionLink.aidl
+++ b/media/apex/java/android/media/session/SessionLink.aidl
diff --git a/media/java/android/media/session/SessionLink.java b/media/apex/java/android/media/session/SessionLink.java
index 0da0a5ae2fe1..4ea762367010 100644
--- a/media/java/android/media/session/SessionLink.java
+++ b/media/apex/java/android/media/session/SessionLink.java
@@ -23,8 +23,6 @@ import android.app.PendingIntent;
import android.media.AudioAttributes;
import android.media.MediaMetadata;
import android.media.MediaParceledListSlice;
-import android.media.Rating;
-import android.media.VolumeProvider;
import android.media.session.MediaSession.QueueItem;
import android.os.Bundle;
import android.os.IBinder;
@@ -234,7 +232,7 @@ public final class SessionLink implements Parcelable {
*
* @param type the rating type.
*/
- void setRatingType(@Rating.Style int type) {
+ void setRatingType(int type) {
try {
mISession.setRatingType(type);
} catch (RemoteException e) {
@@ -261,7 +259,7 @@ public final class SessionLink implements Parcelable {
* @param control the volume control type
* @param max the max volume
*/
- void setPlaybackToRemote(@VolumeProvider.ControlType int control, int max) {
+ void setPlaybackToRemote(int control, int max) {
try {
mISession.setPlaybackToRemote(control, max);
} catch (RemoteException e) {
diff --git a/media/java/android/service/media/IMediaBrowserService.aidl b/media/apex/java/android/service/media/IMediaBrowserService.aidl
index 84f41f6c3afe..1c50ec7ac421 100644
--- a/media/java/android/service/media/IMediaBrowserService.aidl
+++ b/media/apex/java/android/service/media/IMediaBrowserService.aidl
@@ -2,9 +2,7 @@
package android.service.media;
-import android.content.res.Configuration;
import android.service.media.IMediaBrowserServiceCallbacks;
-import android.net.Uri;
import android.os.Bundle;
import android.os.ResultReceiver;
diff --git a/media/java/android/service/media/IMediaBrowserServiceCallbacks.aidl b/media/apex/java/android/service/media/IMediaBrowserServiceCallbacks.aidl
index 8dc480d6bff7..507a8f72ea57 100644
--- a/media/java/android/service/media/IMediaBrowserServiceCallbacks.aidl
+++ b/media/apex/java/android/service/media/IMediaBrowserServiceCallbacks.aidl
@@ -2,7 +2,6 @@
package android.service.media;
-import android.graphics.Bitmap;
import android.media.MediaParceledListSlice;
import android.media.session.MediaSession;
import android.os.Bundle;
diff --git a/media/java/android/service/media/MediaBrowserService.java b/media/apex/java/android/service/media/MediaBrowserService.java
index 2fbc6998576a..d9ef6ae40dfb 100644
--- a/media/java/android/service/media/MediaBrowserService.java
+++ b/media/apex/java/android/service/media/MediaBrowserService.java
@@ -98,7 +98,7 @@ public abstract class MediaBrowserService extends Service {
/** @hide */
@Retention(RetentionPolicy.SOURCE)
- @IntDef(flag=true, value = { RESULT_FLAG_OPTION_NOT_HANDLED,
+ @IntDef(flag = true, value = { RESULT_FLAG_OPTION_NOT_HANDLED,
RESULT_FLAG_ON_LOAD_ITEM_NOT_IMPLEMENTED })
private @interface ResultFlags { }
@@ -291,7 +291,7 @@ public abstract class MediaBrowserService extends Service {
final ConnectionRecord connection = mConnections.get(b);
if (connection == null) {
Log.w(TAG, "addSubscription for callback that isn't registered id="
- + id);
+ + id);
return;
}
@@ -301,7 +301,8 @@ public abstract class MediaBrowserService extends Service {
}
@Override
- public void removeSubscriptionDeprecated(String id, IMediaBrowserServiceCallbacks callbacks) {
+ public void removeSubscriptionDeprecated(
+ String id, IMediaBrowserServiceCallbacks callbacks) {
// do-nothing
}
@@ -487,7 +488,7 @@ public abstract class MediaBrowserService extends Service {
@Override
public void run() {
Iterator<ConnectionRecord> iter = mConnections.values().iterator();
- while (iter.hasNext()){
+ while (iter.hasNext()) {
ConnectionRecord connection = iter.next();
try {
connection.callbacks.onConnect(connection.root.getRootId(), token,
@@ -607,7 +608,7 @@ public abstract class MediaBrowserService extends Service {
final PackageManager pm = getPackageManager();
final String[] packages = pm.getPackagesForUid(uid);
final int N = packages.length;
- for (int i=0; i<N; i++) {
+ for (int i = 0; i < N; i++) {
if (packages[i].equals(pkg)) {
return true;
}
@@ -648,7 +649,7 @@ public abstract class MediaBrowserService extends Service {
List<Pair<IBinder, Bundle>> callbackList = connection.subscriptions.get(id);
if (callbackList != null) {
Iterator<Pair<IBinder, Bundle>> iter = callbackList.iterator();
- while (iter.hasNext()){
+ while (iter.hasNext()) {
if (token == iter.next().first) {
removed = true;
iter.remove();
@@ -819,8 +820,8 @@ public abstract class MediaBrowserService extends Service {
*/
public static final String EXTRA_SUGGESTED = "android.service.media.extra.SUGGESTED";
- final private String mRootId;
- final private Bundle mExtras;
+ private final String mRootId;
+ private final Bundle mExtras;
/**
* Constructs a browser root.
@@ -829,8 +830,8 @@ public abstract class MediaBrowserService extends Service {
*/
public BrowserRoot(@NonNull String rootId, @Nullable Bundle extras) {
if (rootId == null) {
- throw new IllegalArgumentException("The root id in BrowserRoot cannot be null. " +
- "Use null for BrowserRoot instead.");
+ throw new IllegalArgumentException("The root id in BrowserRoot cannot be null. "
+ + "Use null for BrowserRoot instead.");
}
mRootId = rootId;
mExtras = extras;
diff --git a/media/java/android/media/MediaDrm.java b/media/java/android/media/MediaDrm.java
index bfc10da5d431..2d2c4a80bbf0 100644
--- a/media/java/android/media/MediaDrm.java
+++ b/media/java/android/media/MediaDrm.java
@@ -176,7 +176,8 @@ public final class MediaDrm implements AutoCloseable {
* @param uuid The UUID of the crypto scheme.
*/
public static final boolean isCryptoSchemeSupported(@NonNull UUID uuid) {
- return isCryptoSchemeSupportedNative(getByteArrayFromUUID(uuid), null);
+ return isCryptoSchemeSupportedNative(getByteArrayFromUUID(uuid), null,
+ SECURITY_LEVEL_UNKNOWN);
}
/**
@@ -189,7 +190,25 @@ public final class MediaDrm implements AutoCloseable {
*/
public static final boolean isCryptoSchemeSupported(
@NonNull UUID uuid, @NonNull String mimeType) {
- return isCryptoSchemeSupportedNative(getByteArrayFromUUID(uuid), mimeType);
+ return isCryptoSchemeSupportedNative(getByteArrayFromUUID(uuid),
+ mimeType, SECURITY_LEVEL_UNKNOWN);
+ }
+
+ /**
+ * Query if the given scheme identified by its UUID is supported on
+ * this device, and whether the DRM plugin is able to handle the
+ * media container format specified by mimeType at the requested
+ * security level.
+ *
+ * @param uuid The UUID of the crypto scheme.
+ * @param mimeType The MIME type of the media container, e.g. "video/mp4"
+ * or "video/webm"
+ * @param securityLevel the security level requested
+ */
+ public static final boolean isCryptoSchemeSupported(
+ @NonNull UUID uuid, @NonNull String mimeType, @SecurityLevel int securityLevel) {
+ return isCryptoSchemeSupportedNative(getByteArrayFromUUID(uuid), mimeType,
+ securityLevel);
}
private static final byte[] getByteArrayFromUUID(@NonNull UUID uuid) {
@@ -206,7 +225,7 @@ public final class MediaDrm implements AutoCloseable {
}
private static final native boolean isCryptoSchemeSupportedNative(
- @NonNull byte[] uuid, @Nullable String mimeType);
+ @NonNull byte[] uuid, @Nullable String mimeType, @SecurityLevel int securityLevel);
private EventHandler createHandler() {
Looper looper;
diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java
index 4896d0803e42..1a185e982cf1 100644
--- a/media/java/android/media/session/MediaSession.java
+++ b/media/java/android/media/session/MediaSession.java
@@ -481,7 +481,7 @@ public final class MediaSession {
* @hide
*/
@SystemApi
- ControllerLink getControllerLink() {
+ public ControllerLink getControllerLink() {
return mControllerLink;
}
diff --git a/media/jni/android_media_MediaDrm.cpp b/media/jni/android_media_MediaDrm.cpp
index 42c5b052d4aa..81fce8a8044b 100644
--- a/media/jni/android_media_MediaDrm.cpp
+++ b/media/jni/android_media_MediaDrm.cpp
@@ -542,14 +542,15 @@ void JDrm::disconnect() {
// static
-bool JDrm::IsCryptoSchemeSupported(const uint8_t uuid[16], const String8 &mimeType) {
+bool JDrm::IsCryptoSchemeSupported(const uint8_t uuid[16], const String8 &mimeType,
+ DrmPlugin::SecurityLevel securityLevel) {
sp<IDrm> drm = MakeDrm();
if (drm == NULL) {
return false;
}
- return drm->isCryptoSchemeSupported(uuid, mimeType);
+ return drm->isCryptoSchemeSupported(uuid, mimeType, securityLevel);
}
status_t JDrm::initCheck() const {
@@ -930,8 +931,30 @@ static void android_media_MediaDrm_native_setup(
setDrm(env, thiz, drm);
}
+DrmPlugin::SecurityLevel jintToSecurityLevel(jint jlevel) {
+ DrmPlugin::SecurityLevel level;
+
+ if (jlevel == gSecurityLevels.kSecurityLevelMax) {
+ level = DrmPlugin::kSecurityLevelMax;
+ } else if (jlevel == gSecurityLevels.kSecurityLevelSwSecureCrypto) {
+ level = DrmPlugin::kSecurityLevelSwSecureCrypto;
+ } else if (jlevel == gSecurityLevels.kSecurityLevelSwSecureDecode) {
+ level = DrmPlugin::kSecurityLevelSwSecureDecode;
+ } else if (jlevel == gSecurityLevels.kSecurityLevelHwSecureCrypto) {
+ level = DrmPlugin::kSecurityLevelHwSecureCrypto;
+ } else if (jlevel == gSecurityLevels.kSecurityLevelHwSecureDecode) {
+ level = DrmPlugin::kSecurityLevelHwSecureDecode;
+ } else if (jlevel == gSecurityLevels.kSecurityLevelHwSecureAll) {
+ level = DrmPlugin::kSecurityLevelHwSecureAll;
+ } else {
+ level = DrmPlugin::kSecurityLevelUnknown;
+ }
+ return level;
+}
+
static jboolean android_media_MediaDrm_isCryptoSchemeSupportedNative(
- JNIEnv *env, jobject /* thiz */, jbyteArray uuidObj, jstring jmimeType) {
+ JNIEnv *env, jobject /* thiz */, jbyteArray uuidObj, jstring jmimeType,
+ jint jSecurityLevel) {
if (uuidObj == NULL) {
jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
@@ -952,8 +975,9 @@ static jboolean android_media_MediaDrm_isCryptoSchemeSupportedNative(
if (jmimeType != NULL) {
mimeType = JStringToString8(env, jmimeType);
}
+ DrmPlugin::SecurityLevel securityLevel = jintToSecurityLevel(jSecurityLevel);
- return JDrm::IsCryptoSchemeSupported(uuid.array(), mimeType);
+ return JDrm::IsCryptoSchemeSupported(uuid.array(), mimeType, securityLevel);
}
static jbyteArray android_media_MediaDrm_openSession(
@@ -965,21 +989,8 @@ static jbyteArray android_media_MediaDrm_openSession(
}
Vector<uint8_t> sessionId;
- DrmPlugin::SecurityLevel level;
-
- if (jlevel == gSecurityLevels.kSecurityLevelMax) {
- level = DrmPlugin::kSecurityLevelMax;
- } else if (jlevel == gSecurityLevels.kSecurityLevelSwSecureCrypto) {
- level = DrmPlugin::kSecurityLevelSwSecureCrypto;
- } else if (jlevel == gSecurityLevels.kSecurityLevelSwSecureDecode) {
- level = DrmPlugin::kSecurityLevelSwSecureDecode;
- } else if (jlevel == gSecurityLevels.kSecurityLevelHwSecureCrypto) {
- level = DrmPlugin::kSecurityLevelHwSecureCrypto;
- } else if (jlevel == gSecurityLevels.kSecurityLevelHwSecureDecode) {
- level = DrmPlugin::kSecurityLevelHwSecureDecode;
- } else if (jlevel == gSecurityLevels.kSecurityLevelHwSecureAll) {
- level = DrmPlugin::kSecurityLevelHwSecureAll;
- } else {
+ DrmPlugin::SecurityLevel level = jintToSecurityLevel(jlevel);
+ if (level == DrmPlugin::kSecurityLevelUnknown) {
jniThrowException(env, "java/lang/IllegalArgumentException", "Invalid security level");
return NULL;
}
@@ -1903,7 +1914,7 @@ static const JNINativeMethod gMethods[] = {
{ "native_setup", "(Ljava/lang/Object;[BLjava/lang/String;)V",
(void *)android_media_MediaDrm_native_setup },
- { "isCryptoSchemeSupportedNative", "([BLjava/lang/String;)Z",
+ { "isCryptoSchemeSupportedNative", "([BLjava/lang/String;I)Z",
(void *)android_media_MediaDrm_isCryptoSchemeSupportedNative },
{ "openSession", "(I)[B",
diff --git a/media/jni/android_media_MediaDrm.h b/media/jni/android_media_MediaDrm.h
index b9356f30bae8..93388612efcf 100644
--- a/media/jni/android_media_MediaDrm.h
+++ b/media/jni/android_media_MediaDrm.h
@@ -37,7 +37,9 @@ public:
};
struct JDrm : public BnDrmClient {
- static bool IsCryptoSchemeSupported(const uint8_t uuid[16], const String8 &mimeType);
+ static bool IsCryptoSchemeSupported(const uint8_t uuid[16],
+ const String8 &mimeType,
+ DrmPlugin::SecurityLevel level);
JDrm(JNIEnv *env, jobject thiz, const uint8_t uuid[16], const String8 &appPackageName);
diff --git a/media/packages/MediaCore/Android.bp b/media/packages/MediaCore/Android.bp.bak
index c7fd58bf933a..c7fd58bf933a 100644
--- a/media/packages/MediaCore/Android.bp
+++ b/media/packages/MediaCore/Android.bp.bak
diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt
index 8b45af0c3450..51afbc7d91b0 100644
--- a/native/android/libandroid.map.txt
+++ b/native/android/libandroid.map.txt
@@ -231,6 +231,7 @@ LIBANDROID {
ASurfaceTransaction_setBuffer; # introduced=29
ASurfaceTransaction_setBufferAlpha; # introduced=29
ASurfaceTransaction_setBufferTransparency; # introduced=29
+ ASurfaceTransaction_setColor; # introduced=29
ASurfaceTransaction_setDamageRegion; # introduced=29
ASurfaceTransaction_setDesiredPresentTime; # introduced=29
ASurfaceTransaction_setGeometry; # introduced=29
diff --git a/native/android/surface_control.cpp b/native/android/surface_control.cpp
index f0100a9fd4f9..5fae9d5a7974 100644
--- a/native/android/surface_control.cpp
+++ b/native/android/surface_control.cpp
@@ -44,6 +44,76 @@ using Transaction = SurfaceComposerClient::Transaction;
LOG_ALWAYS_FATAL_IF(!static_cast<const Rect&>(name).isValid(), \
"invalid arg passed as " #name " argument");
+static bool getWideColorSupport(const sp<SurfaceControl>& surfaceControl) {
+ sp<SurfaceComposerClient> client = surfaceControl->getClient();
+ sp<IBinder> display(client->getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain));
+
+ Vector<ui::ColorMode> colorModes;
+ status_t err = client->getDisplayColorModes(display, &colorModes);
+ if (err) {
+ ALOGE("unable to get wide color support");
+ return false;
+ }
+
+ bool wideColorBoardConfig =
+ getBool<ISurfaceFlingerConfigs,
+ &ISurfaceFlingerConfigs::hasWideColorDisplay>(false);
+
+ for (android::ui::ColorMode colorMode : colorModes) {
+ switch (colorMode) {
+ case ui::ColorMode::DISPLAY_P3:
+ case ui::ColorMode::ADOBE_RGB:
+ case ui::ColorMode::DCI_P3:
+ if (wideColorBoardConfig) {
+ return true;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ return false;
+}
+
+static bool getHdrSupport(const sp<SurfaceControl>& surfaceControl) {
+ sp<SurfaceComposerClient> client = surfaceControl->getClient();
+ sp<IBinder> display(client->getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain));
+
+ HdrCapabilities hdrCapabilities;
+ status_t err = client->getHdrCapabilities(display, &hdrCapabilities);
+ if (err) {
+ ALOGE("unable to get hdr capabilities");
+ return false;
+ }
+
+ return !hdrCapabilities.getSupportedHdrTypes().empty();
+}
+
+static bool isDataSpaceValid(const sp<SurfaceControl>& surfaceControl, ADataSpace dataSpace) {
+ static_assert(static_cast<int>(ADATASPACE_UNKNOWN) == static_cast<int>(HAL_DATASPACE_UNKNOWN));
+ static_assert(static_cast<int>(ADATASPACE_SCRGB_LINEAR) == static_cast<int>(HAL_DATASPACE_V0_SCRGB_LINEAR));
+ static_assert(static_cast<int>(ADATASPACE_SRGB) == static_cast<int>(HAL_DATASPACE_V0_SRGB));
+ static_assert(static_cast<int>(ADATASPACE_SCRGB) == static_cast<int>(HAL_DATASPACE_V0_SCRGB));
+ static_assert(static_cast<int>(ADATASPACE_DISPLAY_P3) == static_cast<int>(HAL_DATASPACE_DISPLAY_P3));
+ static_assert(static_cast<int>(ADATASPACE_BT2020_PQ) == static_cast<int>(HAL_DATASPACE_BT2020_PQ));
+
+ switch (static_cast<android_dataspace_t>(dataSpace)) {
+ case HAL_DATASPACE_UNKNOWN:
+ case HAL_DATASPACE_V0_SRGB:
+ return true;
+ // These data space need wide gamut support.
+ case HAL_DATASPACE_V0_SCRGB_LINEAR:
+ case HAL_DATASPACE_V0_SCRGB:
+ case HAL_DATASPACE_DISPLAY_P3:
+ return getWideColorSupport(surfaceControl);
+ // These data space need HDR support.
+ case HAL_DATASPACE_BT2020_PQ:
+ return getHdrSupport(surfaceControl);
+ default:
+ return false;
+ }
+}
+
Transaction* ASurfaceTransaction_to_Transaction(ASurfaceTransaction* aSurfaceTransaction) {
return reinterpret_cast<Transaction*>(aSurfaceTransaction);
}
@@ -431,3 +501,24 @@ void ASurfaceTransaction_setHdrMetadata_cta861_3(ASurfaceTransaction* aSurfaceTr
transaction->setHdrMetadata(surfaceControl, hdrMetadata);
}
+
+void ASurfaceTransaction_setColor(ASurfaceTransaction* aSurfaceTransaction,
+ ASurfaceControl* aSurfaceControl,
+ float r, float g, float b, float alpha,
+ ADataSpace dataspace) {
+ CHECK_NOT_NULL(aSurfaceTransaction);
+ CHECK_NOT_NULL(aSurfaceControl);
+
+ sp<SurfaceControl> surfaceControl = ASurfaceControl_to_SurfaceControl(aSurfaceControl);
+ LOG_ALWAYS_FATAL_IF(!isDataSpaceValid(surfaceControl, dataspace), "invalid dataspace");
+ Transaction* transaction = ASurfaceTransaction_to_Transaction(aSurfaceTransaction);
+
+ half3 color;
+ color.r = r;
+ color.g = g;
+ color.b = b;
+
+ transaction->setColor(surfaceControl, color)
+ .setColorAlpha(surfaceControl, alpha)
+ .setColorDataspace(surfaceControl, static_cast<ui::Dataspace>(dataspace));
+}
diff --git a/packages/CarSystemUI/Android.bp b/packages/CarSystemUI/Android.bp
index 9b6ad38545b4..9064ebe80da1 100644
--- a/packages/CarSystemUI/Android.bp
+++ b/packages/CarSystemUI/Android.bp
@@ -81,5 +81,5 @@ android_app {
"com.android.keyguard",
],
- annotation_processors: ["dagger2-compiler-2.19"],
+ plugins: ["dagger2-compiler-2.19"],
}
diff --git a/packages/CarSystemUI/res/layout/car_navigation_bar.xml b/packages/CarSystemUI/res/layout/car_navigation_bar.xml
index 052566d67c1b..e591ea90c112 100644
--- a/packages/CarSystemUI/res/layout/car_navigation_bar.xml
+++ b/packages/CarSystemUI/res/layout/car_navigation_bar.xml
@@ -65,8 +65,9 @@
<com.android.systemui.statusbar.car.CarFacetButton
android:id="@+id/music_nav"
style="@style/NavigationBarButton"
+ systemui:categories="android.intent.category.APP_MUSIC"
systemui:icon="@drawable/car_ic_music"
- systemui:intent="intent:#Intent;component=com.android.car.media/.MediaActivity;launchFlags=0x14000000;end"
+ systemui:intent="intent:#Intent;action=android.car.intent.action.MEDIA_TEMPLATE;launchFlags=0x10000000;end"
systemui:packages="com.android.car.media"
systemui:selectedIcon="@drawable/car_ic_music_selected"
systemui:useMoreIcon="false"
diff --git a/packages/ExtServices/src/android/ext/services/notification/AssistantSettings.java b/packages/ExtServices/src/android/ext/services/notification/AssistantSettings.java
index 39a1676b4ec7..406b44d64da2 100644
--- a/packages/ExtServices/src/android/ext/services/notification/AssistantSettings.java
+++ b/packages/ExtServices/src/android/ext/services/notification/AssistantSettings.java
@@ -20,8 +20,11 @@ import android.content.ContentResolver;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.Handler;
+import android.provider.DeviceConfig;
import android.provider.Settings;
+import android.text.TextUtils;
import android.util.KeyValueListParser;
+import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
@@ -29,6 +32,7 @@ import com.android.internal.annotations.VisibleForTesting;
* Observes the settings for {@link Assistant}.
*/
final class AssistantSettings extends ContentObserver {
+ private static final String LOG_TAG = "AssistantSettings";
public static Factory FACTORY = AssistantSettings::createAndRegister;
private static final boolean DEFAULT_GENERATE_REPLIES = true;
private static final boolean DEFAULT_GENERATE_ACTIONS = true;
@@ -39,19 +43,33 @@ final class AssistantSettings extends ContentObserver {
private static final Uri DISMISS_TO_VIEW_RATIO_LIMIT_URI =
Settings.Global.getUriFor(
Settings.Global.BLOCKING_HELPER_DISMISS_TO_VIEW_RATIO_LIMIT);
- private static final Uri SMART_SUGGESTIONS_IN_NOTIFICATIONS_FLAGS_URI =
- Settings.Global.getUriFor(
- Settings.Global.SMART_SUGGESTIONS_IN_NOTIFICATIONS_FLAGS);
private static final Uri NOTIFICATION_NEW_INTERRUPTION_MODEL_URI =
Settings.Secure.getUriFor(Settings.Secure.NOTIFICATION_NEW_INTERRUPTION_MODEL);
- private static final String KEY_GENERATE_REPLIES = "generate_replies";
- private static final String KEY_GENERATE_ACTIONS = "generate_actions";
+ /**
+ * Flag determining whether the Notification Assistant should generate replies for
+ * notifications.
+ * <p>
+ * This flag belongs to the namespace: {@link DeviceConfig#NAMESPACE_NOTIFICATION_ASSISTANT}.
+ */
+ @VisibleForTesting
+ static final String KEY_GENERATE_REPLIES = "notification_assistant_generate_replies";
+
+ /**
+ * Flag determining whether the Notification Assistant should generate contextual actions in
+ * notifications.
+ * <p>
+ * This flag belongs to the namespace: {@link DeviceConfig#NAMESPACE_NOTIFICATION_ASSISTANT}.
+ */
+ @VisibleForTesting
+ static final String KEY_GENERATE_ACTIONS = "notification_assistant_generate_actions";
private final KeyValueListParser mParser = new KeyValueListParser(',');
private final ContentResolver mResolver;
private final int mUserId;
+ private final Handler mHandler;
+
@VisibleForTesting
protected final Runnable mOnUpdateRunnable;
@@ -65,6 +83,7 @@ final class AssistantSettings extends ContentObserver {
private AssistantSettings(Handler handler, ContentResolver resolver, int userId,
Runnable onUpdateRunnable) {
super(handler);
+ mHandler = handler;
mResolver = resolver;
mUserId = userId;
mOnUpdateRunnable = onUpdateRunnable;
@@ -75,6 +94,7 @@ final class AssistantSettings extends ContentObserver {
AssistantSettings assistantSettings =
new AssistantSettings(handler, resolver, userId, onUpdateRunnable);
assistantSettings.register();
+ assistantSettings.registerDeviceConfigs();
return assistantSettings;
}
@@ -91,13 +111,62 @@ final class AssistantSettings extends ContentObserver {
mResolver.registerContentObserver(
DISMISS_TO_VIEW_RATIO_LIMIT_URI, false, this, mUserId);
mResolver.registerContentObserver(STREAK_LIMIT_URI, false, this, mUserId);
- mResolver.registerContentObserver(
- SMART_SUGGESTIONS_IN_NOTIFICATIONS_FLAGS_URI, false, this, mUserId);
// Update all uris on creation.
update(null);
}
+ private void registerDeviceConfigs() {
+ DeviceConfig.addOnPropertyChangedListener(
+ DeviceConfig.NAMESPACE_NOTIFICATION_ASSISTANT,
+ this::postToHandler,
+ this::onDeviceConfigPropertyChanged);
+
+ // Update the fields in this class from the current state of the device config.
+ updateFromDeviceConfigFlags();
+ }
+
+ private void postToHandler(Runnable r) {
+ this.mHandler.post(r);
+ }
+
+ @VisibleForTesting
+ void onDeviceConfigPropertyChanged(String namespace, String name, String value) {
+ if (!DeviceConfig.NAMESPACE_NOTIFICATION_ASSISTANT.equals(namespace)) {
+ Log.e(LOG_TAG, "Received update from DeviceConfig for unrelated namespace: "
+ + namespace + " " + name + "=" + value);
+ return;
+ }
+
+ updateFromDeviceConfigFlags();
+ }
+
+ private void updateFromDeviceConfigFlags() {
+ String generateRepliesFlag = DeviceConfig.getProperty(
+ DeviceConfig.NAMESPACE_NOTIFICATION_ASSISTANT,
+ KEY_GENERATE_REPLIES);
+ if (TextUtils.isEmpty(generateRepliesFlag)) {
+ mGenerateReplies = DEFAULT_GENERATE_REPLIES;
+ } else {
+ // parseBoolean returns false for everything that isn't 'true' so there's no need to
+ // sanitise the flag string here.
+ mGenerateReplies = Boolean.parseBoolean(generateRepliesFlag);
+ }
+
+ String generateActionsFlag = DeviceConfig.getProperty(
+ DeviceConfig.NAMESPACE_NOTIFICATION_ASSISTANT,
+ KEY_GENERATE_ACTIONS);
+ if (TextUtils.isEmpty(generateActionsFlag)) {
+ mGenerateActions = DEFAULT_GENERATE_ACTIONS;
+ } else {
+ // parseBoolean returns false for everything that isn't 'true' so there's no need to
+ // sanitise the flag string here.
+ mGenerateActions = Boolean.parseBoolean(generateActionsFlag);
+ }
+
+ mOnUpdateRunnable.run();
+ }
+
@Override
public void onChange(boolean selfChange, Uri uri) {
update(uri);
@@ -114,15 +183,6 @@ final class AssistantSettings extends ContentObserver {
mResolver, Settings.Global.BLOCKING_HELPER_STREAK_LIMIT,
ChannelImpressions.DEFAULT_STREAK_LIMIT);
}
- if (uri == null || SMART_SUGGESTIONS_IN_NOTIFICATIONS_FLAGS_URI.equals(uri)) {
- mParser.setString(
- Settings.Global.getString(mResolver,
- Settings.Global.SMART_SUGGESTIONS_IN_NOTIFICATIONS_FLAGS));
- mGenerateReplies =
- mParser.getBoolean(KEY_GENERATE_REPLIES, DEFAULT_GENERATE_REPLIES);
- mGenerateActions =
- mParser.getBoolean(KEY_GENERATE_ACTIONS, DEFAULT_GENERATE_ACTIONS);
- }
if (uri == null || NOTIFICATION_NEW_INTERRUPTION_MODEL_URI.equals(uri)) {
int mNewInterruptionModelInt = Settings.Secure.getInt(
mResolver, Settings.Secure.NOTIFICATION_NEW_INTERRUPTION_MODEL,
diff --git a/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java b/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java
index 0d528e7078f8..bc6e2fc7fc48 100644
--- a/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java
+++ b/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java
@@ -202,7 +202,7 @@ public class SmartActionsHelper {
}
TextClassifierEvent textClassifierEvent =
createTextClassifierEventBuilder(TextClassifierEvent.TYPE_SMART_ACTION, resultId)
- .setEntityType(ConversationAction.TYPE_TEXT_REPLY)
+ .setEntityTypes(ConversationAction.TYPE_TEXT_REPLY)
.build();
mTextClassifier.onTextClassifierEvent(textClassifierEvent);
}
@@ -225,7 +225,7 @@ public class SmartActionsHelper {
}
TextClassifierEvent textClassifierEvent =
createTextClassifierEventBuilder(TextClassifierEvent.TYPE_SMART_ACTION, resultId)
- .setEntityType(actionType)
+ .setEntityTypes(actionType)
.build();
mTextClassifier.onTextClassifierEvent(textClassifierEvent);
}
diff --git a/packages/ExtServices/tests/src/android/ext/services/notification/AssistantSettingsTest.java b/packages/ExtServices/tests/src/android/ext/services/notification/AssistantSettingsTest.java
index fd23f2b78b42..51b723d0950c 100644
--- a/packages/ExtServices/tests/src/android/ext/services/notification/AssistantSettingsTest.java
+++ b/packages/ExtServices/tests/src/android/ext/services/notification/AssistantSettingsTest.java
@@ -26,6 +26,7 @@ import static org.mockito.Mockito.verify;
import android.content.ContentResolver;
import android.os.Handler;
import android.os.Looper;
+import android.provider.DeviceConfig;
import android.provider.Settings;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
@@ -62,9 +63,6 @@ public class AssistantSettingsTest {
Settings.Global.putFloat(mResolver,
Settings.Global.BLOCKING_HELPER_DISMISS_TO_VIEW_RATIO_LIMIT, 0.8f);
Settings.Global.putInt(mResolver, Settings.Global.BLOCKING_HELPER_STREAK_LIMIT, 2);
- Settings.Global.putString(mResolver,
- Settings.Global.SMART_SUGGESTIONS_IN_NOTIFICATIONS_FLAGS,
- "generate_replies=true,generate_actions=true");
Settings.Secure.putInt(mResolver, Settings.Secure.NOTIFICATION_NEW_INTERRUPTION_MODEL, 1);
mAssistantSettings = AssistantSettings.createForTesting(
@@ -73,56 +71,78 @@ public class AssistantSettingsTest {
@Test
public void testGenerateRepliesDisabled() {
- Settings.Global.putString(mResolver,
- Settings.Global.SMART_SUGGESTIONS_IN_NOTIFICATIONS_FLAGS,
- "generate_replies=false");
-
- // Notify for the settings values we updated.
- mAssistantSettings.onChange(false,
- Settings.Global.getUriFor(
- Settings.Global.SMART_SUGGESTIONS_IN_NOTIFICATIONS_FLAGS));
-
+ mAssistantSettings.onDeviceConfigPropertyChanged(
+ DeviceConfig.NAMESPACE_NOTIFICATION_ASSISTANT,
+ AssistantSettings.KEY_GENERATE_REPLIES,
+ "false");
assertFalse(mAssistantSettings.mGenerateReplies);
}
@Test
public void testGenerateRepliesEnabled() {
- Settings.Global.putString(mResolver,
- Settings.Global.SMART_SUGGESTIONS_IN_NOTIFICATIONS_FLAGS, "generate_replies=true");
-
- // Notify for the settings values we updated.
- mAssistantSettings.onChange(false,
- Settings.Global.getUriFor(
- Settings.Global.SMART_SUGGESTIONS_IN_NOTIFICATIONS_FLAGS));
+ mAssistantSettings.onDeviceConfigPropertyChanged(
+ DeviceConfig.NAMESPACE_NOTIFICATION_ASSISTANT,
+ AssistantSettings.KEY_GENERATE_REPLIES,
+ "true");
assertTrue(mAssistantSettings.mGenerateReplies);
}
@Test
- public void testGenerateActionsDisabled() {
- Settings.Global.putString(mResolver,
- Settings.Global.SMART_SUGGESTIONS_IN_NOTIFICATIONS_FLAGS, "generate_actions=false");
+ public void testGenerateRepliesEmptyFlag() {
+ mAssistantSettings.onDeviceConfigPropertyChanged(
+ DeviceConfig.NAMESPACE_NOTIFICATION_ASSISTANT,
+ AssistantSettings.KEY_GENERATE_REPLIES,
+ "false");
- // Notify for the settings values we updated.
- mAssistantSettings.onChange(false,
- Settings.Global.getUriFor(
- Settings.Global.SMART_SUGGESTIONS_IN_NOTIFICATIONS_FLAGS));
+ assertFalse(mAssistantSettings.mGenerateReplies);
+ mAssistantSettings.onDeviceConfigPropertyChanged(
+ DeviceConfig.NAMESPACE_NOTIFICATION_ASSISTANT,
+ AssistantSettings.KEY_GENERATE_REPLIES,
+ "");
+
+ // Go back to the default value.
assertTrue(mAssistantSettings.mGenerateReplies);
}
@Test
+ public void testGenerateActionsDisabled() {
+ mAssistantSettings.onDeviceConfigPropertyChanged(
+ DeviceConfig.NAMESPACE_NOTIFICATION_ASSISTANT,
+ AssistantSettings.KEY_GENERATE_ACTIONS,
+ "false");
+
+ assertFalse(mAssistantSettings.mGenerateActions);
+ }
+
+ @Test
public void testGenerateActionsEnabled() {
- Settings.Global.putString(mResolver,
- Settings.Global.SMART_SUGGESTIONS_IN_NOTIFICATIONS_FLAGS, "generate_actions=true");
+ mAssistantSettings.onDeviceConfigPropertyChanged(
+ DeviceConfig.NAMESPACE_NOTIFICATION_ASSISTANT,
+ AssistantSettings.KEY_GENERATE_ACTIONS,
+ "true");
- // Notify for the settings values we updated.
- mAssistantSettings.onChange(false,
- Settings.Global.getUriFor(
- Settings.Global.SMART_SUGGESTIONS_IN_NOTIFICATIONS_FLAGS));
+ assertTrue(mAssistantSettings.mGenerateActions);
+ }
- assertTrue(mAssistantSettings.mGenerateReplies);
+ @Test
+ public void testGenerateActionsEmptyFlag() {
+ mAssistantSettings.onDeviceConfigPropertyChanged(
+ DeviceConfig.NAMESPACE_NOTIFICATION_ASSISTANT,
+ AssistantSettings.KEY_GENERATE_ACTIONS,
+ "false");
+
+ assertFalse(mAssistantSettings.mGenerateActions);
+
+ mAssistantSettings.onDeviceConfigPropertyChanged(
+ DeviceConfig.NAMESPACE_NOTIFICATION_ASSISTANT,
+ AssistantSettings.KEY_GENERATE_ACTIONS,
+ "");
+
+ // Go back to the default value.
+ assertTrue(mAssistantSettings.mGenerateActions);
}
@Test
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java
index 74aaf3c26aba..93f6a94dcf49 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java
@@ -47,6 +47,7 @@ import androidx.annotation.VisibleForTesting;
import com.android.internal.widget.LockPatternUtils;
import java.util.List;
+import java.util.Set;
/**
* Utility class to host methods usable in adding a restricted padlock icon and showing admin
@@ -325,7 +326,8 @@ public class RestrictedLockUtilsInternal extends RestrictedLockUtils {
if (admin == null) {
return null;
}
- if (dpm.getCrossProfileCalendarPackages().isEmpty()) {
+ final Set<String> packages = dpm.getCrossProfileCalendarPackages();
+ if (packages != null && packages.isEmpty()) {
return admin;
}
return null;
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/AppUtils.java b/packages/SettingsLib/src/com/android/settingslib/applications/AppUtils.java
index 7357fe63d9b0..42afb69a27f4 100644
--- a/packages/SettingsLib/src/com/android/settingslib/applications/AppUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/AppUtils.java
@@ -16,6 +16,7 @@
package com.android.settingslib.applications;
+import android.app.Application;
import android.content.ComponentName;
import android.content.Context;
import android.content.IntentFilter;
@@ -123,4 +124,12 @@ public class AppUtils {
return null;
}
+ /**
+ * Returns a boolean indicating whether the given package is a hidden system module
+ */
+ public static boolean isHiddenSystemModule(Context context, String packageName) {
+ return ApplicationsState.getInstance((Application) context.getApplicationContext())
+ .isHiddenModule(packageName);
+ }
+
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
index a936df2bf2eb..c9fbc7ba9f05 100644
--- a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
@@ -29,6 +29,7 @@ import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.content.pm.IPackageStatsObserver;
+import android.content.pm.ModuleInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PackageStats;
@@ -71,6 +72,7 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.UUID;
@@ -95,9 +97,14 @@ public class ApplicationsState {
static ApplicationsState sInstance;
public static ApplicationsState getInstance(Application app) {
+ return getInstance(app, AppGlobals.getPackageManager());
+ }
+
+ @VisibleForTesting
+ static ApplicationsState getInstance(Application app, IPackageManager iPackageManager) {
synchronized (sLock) {
if (sInstance == null) {
- sInstance = new ApplicationsState(app);
+ sInstance = new ApplicationsState(app, iPackageManager);
}
return sInstance;
}
@@ -132,6 +139,7 @@ public class ApplicationsState {
String mCurComputingSizePkg;
int mCurComputingSizeUserId;
boolean mSessionsChanged;
+ final HashSet<String> mHiddenModules = new HashSet<>();
// Temporary for dispatching session callbacks. Only touched by main thread.
final ArrayList<WeakReference<Session>> mActiveSessions = new ArrayList<>();
@@ -172,11 +180,11 @@ public class ApplicationsState {
FLAG_SESSION_REQUEST_HOME_APP | FLAG_SESSION_REQUEST_ICONS |
FLAG_SESSION_REQUEST_SIZES | FLAG_SESSION_REQUEST_LAUNCHER;
- private ApplicationsState(Application app) {
+ private ApplicationsState(Application app, IPackageManager iPackageManager) {
mContext = app;
mPm = mContext.getPackageManager();
mDrawableFactory = IconDrawableFactory.newInstance(mContext);
- mIpm = AppGlobals.getPackageManager();
+ mIpm = iPackageManager;
mUm = mContext.getSystemService(UserManager.class);
mStats = mContext.getSystemService(StorageStatsManager.class);
for (int userId : mUm.getProfileIdsWithDisabled(UserHandle.myUserId())) {
@@ -194,6 +202,13 @@ public class ApplicationsState {
mRetrieveFlags = PackageManager.MATCH_DISABLED_COMPONENTS |
PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS;
+ final List<ModuleInfo> moduleInfos = mPm.getInstalledModules(0 /* flags */);
+ for (ModuleInfo info : moduleInfos) {
+ if (info.isHidden()) {
+ mHiddenModules.add(info.getPackageName());
+ }
+ }
+
/**
* This is a trick to prevent the foreground thread from being delayed.
* The problem is that Dalvik monitors are initially spin locks, to keep
@@ -283,6 +298,10 @@ public class ApplicationsState {
}
mHaveDisabledApps = true;
}
+ if (isHiddenModule(info.packageName)) {
+ mApplications.remove(i--);
+ continue;
+ }
if (!mHaveInstantApps && AppUtils.isInstant(info)) {
mHaveInstantApps = true;
}
@@ -314,10 +333,15 @@ public class ApplicationsState {
public boolean haveDisabledApps() {
return mHaveDisabledApps;
}
+
public boolean haveInstantApps() {
return mHaveInstantApps;
}
+ boolean isHiddenModule(String packageName) {
+ return mHiddenModules.contains(packageName);
+ }
+
void doPauseIfNeededLocked() {
if (!mResumed) {
return;
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 ccec175aefad..a098ecc17b3d 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
@@ -18,7 +18,9 @@ package com.android.settingslib.applications;
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.robolectric.shadow.api.Shadow.extract;
@@ -33,12 +35,16 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageManager;
+import android.content.pm.ModuleInfo;
import android.content.pm.PackageManager;
+import android.content.pm.ParceledListSlice;
import android.content.pm.ResolveInfo;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.os.UserHandle;
+import android.text.TextUtils;
import android.util.IconDrawableFactory;
import com.android.settingslib.applications.ApplicationsState.AppEntry;
@@ -46,11 +52,11 @@ import com.android.settingslib.applications.ApplicationsState.Callbacks;
import com.android.settingslib.applications.ApplicationsState.Session;
import com.android.settingslib.testutils.shadow.ShadowUserManager;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
-import org.mockito.ArgumentMatchers;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -78,6 +84,8 @@ public class ApplicationsStateRoboTest {
/** Class under test */
private ApplicationsState mApplicationsState;
+ private Session mSession;
+
@Mock
private Callbacks mCallbacks;
@@ -85,6 +93,8 @@ public class ApplicationsStateRoboTest {
private ArgumentCaptor<ArrayList<AppEntry>> mAppEntriesCaptor;
@Mock
private StorageStatsManager mStorageStatsManager;
+ @Mock
+ private IPackageManager mPackageManagerService;
@Implements(value = IconDrawableFactory.class)
public static class ShadowIconDrawableFactory {
@@ -99,6 +109,11 @@ public class ApplicationsStateRoboTest {
public static class ShadowPackageManager extends
org.robolectric.shadows.ShadowApplicationPackageManager {
+ // test installed modules, 2 regular, 2 hidden
+ private final String[] mModuleNames = {
+ "test.module.1", "test.hidden.module.2", "test.hidden.module.3", "test.module.4"};
+ private final List<ModuleInfo> mInstalledModules = new ArrayList<>();
+
@Implementation
protected ComponentName getHomeActivities(List<ResolveInfo> outActivities) {
ResolveInfo resolveInfo = new ResolveInfo();
@@ -109,6 +124,16 @@ public class ApplicationsStateRoboTest {
return ComponentName.createRelative(resolveInfo.activityInfo.packageName, "foo");
}
+ @Implementation
+ public List<ModuleInfo> getInstalledModules(int flags) {
+ if (mInstalledModules.isEmpty()) {
+ for (String moduleName : mModuleNames) {
+ mInstalledModules.add(createModuleInfo(moduleName));
+ }
+ }
+ return mInstalledModules;
+ }
+
public List<ResolveInfo> queryIntentActivitiesAsUser(Intent intent,
@PackageManager.ResolveInfoFlags int flags, @UserIdInt int userId) {
List<ResolveInfo> resolveInfos = new ArrayList<>();
@@ -121,6 +146,15 @@ public class ApplicationsStateRoboTest {
resolveInfos.add(resolveInfo);
return resolveInfos;
}
+
+ private ModuleInfo createModuleInfo(String packageName) {
+ final ModuleInfo info = new ModuleInfo();
+ info.setName(packageName);
+ info.setPackageName(packageName);
+ // will treat any app with package name that contains "hidden" as hidden module
+ info.setHidden(!TextUtils.isEmpty(packageName) && packageName.contains("hidden"));
+ return info;
+ }
}
@Before
@@ -136,12 +170,28 @@ public class ApplicationsStateRoboTest {
storageStats.codeBytes = 10;
storageStats.dataBytes = 20;
storageStats.cacheBytes = 30;
- when(mStorageStatsManager.queryStatsForPackage(ArgumentMatchers.any(UUID.class),
- anyString(), ArgumentMatchers.any(UserHandle.class))).thenReturn(storageStats);
+ when(mStorageStatsManager.queryStatsForPackage(any(UUID.class),
+ anyString(), any(UserHandle.class))).thenReturn(storageStats);
+
+ // Set up 3 installed apps, in which 1 is hidden module
+ final List<ApplicationInfo> infos = new ArrayList<>();
+ infos.add(createApplicationInfo("test.package.1"));
+ infos.add(createApplicationInfo("test.hidden.module.2"));
+ infos.add(createApplicationInfo("test.package.3"));
+ when(mPackageManagerService.getInstalledApplications(
+ anyInt() /* flags */, anyInt() /* userId */)).thenReturn(new ParceledListSlice(infos));
ApplicationsState.sInstance = null;
- mApplicationsState = ApplicationsState.getInstance(RuntimeEnvironment.application);
+ mApplicationsState =
+ ApplicationsState.getInstance(RuntimeEnvironment.application, mPackageManagerService);
mApplicationsState.clearEntries();
+
+ mSession = mApplicationsState.newSession(mCallbacks);
+ }
+
+ @After
+ public void tearDown() {
+ mSession.onDestroy();
}
private ApplicationInfo createApplicationInfo(String packageName) {
@@ -187,12 +237,11 @@ public class ApplicationsStateRoboTest {
@Test
public void testDefaultSessionLoadsAll() {
- Session session = mApplicationsState.newSession(mCallbacks);
- session.onResume();
+ mSession.onResume();
addApp(HOME_PACKAGE_NAME, 1);
addApp(LAUNCHABLE_PACKAGE_NAME, 2);
- session.rebuild(ApplicationsState.FILTER_EVERYTHING, ApplicationsState.SIZE_COMPARATOR);
+ mSession.rebuild(ApplicationsState.FILTER_EVERYTHING, ApplicationsState.SIZE_COMPARATOR);
processAllMessages();
verify(mCallbacks).onRebuildComplete(mAppEntriesCaptor.capture());
@@ -211,17 +260,15 @@ public class ApplicationsStateRoboTest {
AppEntry launchableEntry = findAppEntry(appEntries, 2);
assertThat(launchableEntry.hasLauncherEntry).isTrue();
assertThat(launchableEntry.launcherEntryEnabled).isTrue();
- session.onDestroy();
}
@Test
public void testCustomSessionLoadsIconsOnly() {
- Session session = mApplicationsState.newSession(mCallbacks);
- session.setSessionFlags(ApplicationsState.FLAG_SESSION_REQUEST_ICONS);
- session.onResume();
+ mSession.setSessionFlags(ApplicationsState.FLAG_SESSION_REQUEST_ICONS);
+ mSession.onResume();
addApp(LAUNCHABLE_PACKAGE_NAME, 1);
- session.rebuild(ApplicationsState.FILTER_EVERYTHING, ApplicationsState.SIZE_COMPARATOR);
+ mSession.rebuild(ApplicationsState.FILTER_EVERYTHING, ApplicationsState.SIZE_COMPARATOR);
processAllMessages();
verify(mCallbacks).onRebuildComplete(mAppEntriesCaptor.capture());
@@ -232,17 +279,15 @@ public class ApplicationsStateRoboTest {
assertThat(launchableEntry.icon).isNotNull();
assertThat(launchableEntry.size).isEqualTo(-1);
assertThat(launchableEntry.hasLauncherEntry).isFalse();
- session.onDestroy();
}
@Test
public void testCustomSessionLoadsSizesOnly() {
- Session session = mApplicationsState.newSession(mCallbacks);
- session.setSessionFlags(ApplicationsState.FLAG_SESSION_REQUEST_SIZES);
- session.onResume();
+ mSession.setSessionFlags(ApplicationsState.FLAG_SESSION_REQUEST_SIZES);
+ mSession.onResume();
addApp(LAUNCHABLE_PACKAGE_NAME, 1);
- session.rebuild(ApplicationsState.FILTER_EVERYTHING, ApplicationsState.SIZE_COMPARATOR);
+ mSession.rebuild(ApplicationsState.FILTER_EVERYTHING, ApplicationsState.SIZE_COMPARATOR);
processAllMessages();
verify(mCallbacks).onRebuildComplete(mAppEntriesCaptor.capture());
@@ -253,17 +298,15 @@ public class ApplicationsStateRoboTest {
assertThat(launchableEntry.icon).isNull();
assertThat(launchableEntry.hasLauncherEntry).isFalse();
assertThat(launchableEntry.size).isGreaterThan(0L);
- session.onDestroy();
}
@Test
public void testCustomSessionLoadsHomeOnly() {
- Session session = mApplicationsState.newSession(mCallbacks);
- session.setSessionFlags(ApplicationsState.FLAG_SESSION_REQUEST_HOME_APP);
- session.onResume();
+ mSession.setSessionFlags(ApplicationsState.FLAG_SESSION_REQUEST_HOME_APP);
+ mSession.onResume();
addApp(HOME_PACKAGE_NAME, 1);
- session.rebuild(ApplicationsState.FILTER_EVERYTHING, ApplicationsState.SIZE_COMPARATOR);
+ mSession.rebuild(ApplicationsState.FILTER_EVERYTHING, ApplicationsState.SIZE_COMPARATOR);
processAllMessages();
verify(mCallbacks).onRebuildComplete(mAppEntriesCaptor.capture());
@@ -275,17 +318,15 @@ public class ApplicationsStateRoboTest {
assertThat(launchableEntry.hasLauncherEntry).isFalse();
assertThat(launchableEntry.size).isEqualTo(-1);
assertThat(launchableEntry.isHomeApp).isTrue();
- session.onDestroy();
}
@Test
public void testCustomSessionLoadsLeanbackOnly() {
- Session session = mApplicationsState.newSession(mCallbacks);
- session.setSessionFlags(ApplicationsState.FLAG_SESSION_REQUEST_LEANBACK_LAUNCHER);
- session.onResume();
+ mSession.setSessionFlags(ApplicationsState.FLAG_SESSION_REQUEST_LEANBACK_LAUNCHER);
+ mSession.onResume();
addApp(LAUNCHABLE_PACKAGE_NAME, 1);
- session.rebuild(ApplicationsState.FILTER_EVERYTHING, ApplicationsState.SIZE_COMPARATOR);
+ mSession.rebuild(ApplicationsState.FILTER_EVERYTHING, ApplicationsState.SIZE_COMPARATOR);
processAllMessages();
verify(mCallbacks).onRebuildComplete(mAppEntriesCaptor.capture());
@@ -298,6 +339,16 @@ public class ApplicationsStateRoboTest {
assertThat(launchableEntry.isHomeApp).isFalse();
assertThat(launchableEntry.hasLauncherEntry).isTrue();
assertThat(launchableEntry.launcherEntryEnabled).isTrue();
- session.onDestroy();
}
+
+ @Test
+ public void onResume_shouldNotIncludeSystemHiddenModule() {
+ mSession.onResume();
+
+ final List<ApplicationInfo> mApplications = mApplicationsState.mApplications;
+ assertThat(mApplications).hasSize(2);
+ assertThat(mApplications.get(0).packageName).isEqualTo("test.package.1");
+ assertThat(mApplications.get(1).packageName).isEqualTo("test.package.3");
+ }
+
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index e843eb43a3a6..aff6f0452533 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -19,7 +19,6 @@ package com.android.providers.settings;
import android.annotation.NonNull;
import android.os.UserHandle;
import android.provider.Settings;
-import android.provider.Settings.Global;
import android.providers.settings.GlobalSettingsProto;
import android.providers.settings.SecureSettingsProto;
import android.providers.settings.SettingProto;
@@ -397,7 +396,7 @@ class SettingsProtoDumpUtil {
p.end(certPinToken);
dumpSetting(s, p,
- Global.CHAINED_BATTERY_ATTRIBUTION_ENABLED,
+ Settings.Global.CHAINED_BATTERY_ATTRIBUTION_ENABLED,
GlobalSettingsProto.CHAINED_BATTERY_ATTRIBUTION_ENABLED);
dumpSetting(s, p,
Settings.Global.COMPATIBILITY_MODE,
@@ -699,6 +698,9 @@ class SettingsProtoDumpUtil {
Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_VALUES,
GlobalSettingsProto.Gpu.ANGLE_GL_DRIVER_SELECTION_VALUES);
dumpSetting(s, p,
+ Settings.Global.GLOBAL_SETTINGS_ANGLE_WHITELIST,
+ GlobalSettingsProto.Gpu.ANGLE_WHITELIST);
+ dumpSetting(s, p,
Settings.Global.GPU_DEBUG_LAYER_APP,
GlobalSettingsProto.Gpu.DEBUG_LAYER_APP);
dumpSetting(s, p,
@@ -716,6 +718,9 @@ class SettingsProtoDumpUtil {
dumpSetting(s, p,
Settings.Global.GUP_BLACKLIST,
GlobalSettingsProto.Gpu.GUP_BLACKLIST);
+ dumpSetting(s, p,
+ Settings.Global.GAME_DRIVER_WHITELIST,
+ GlobalSettingsProto.Gpu.GAME_DRIVER_WHITELIST);
p.end(gpuToken);
final long hdmiToken = p.start(GlobalSettingsProto.HDMI);
@@ -737,7 +742,7 @@ class SettingsProtoDumpUtil {
Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED,
GlobalSettingsProto.HEADS_UP_NOTIFICATIONS_ENABLED);
dumpSetting(s, p,
- Global.HIDDEN_API_BLACKLIST_EXEMPTIONS,
+ Settings.Global.HIDDEN_API_BLACKLIST_EXEMPTIONS,
GlobalSettingsProto.HIDDEN_API_BLACKLIST_EXEMPTIONS);
final long inetCondToken = p.start(GlobalSettingsProto.INET_CONDITION);
@@ -832,6 +837,15 @@ class SettingsProtoDumpUtil {
dumpSetting(s, p,
Settings.Global.AUTOMATIC_POWER_SAVER_MODE,
GlobalSettingsProto.LowPowerMode.AUTOMATIC_POWER_SAVER_MODE);
+ dumpSetting(s, p,
+ Settings.Global.LOW_POWER_MODE_STICKY,
+ GlobalSettingsProto.LowPowerMode.STICKY_ENABLED);
+ dumpSetting(s, p,
+ Settings.Global.LOW_POWER_MODE_STICKY_AUTO_DISABLE_ENABLED,
+ GlobalSettingsProto.LowPowerMode.STICKY_AUTO_DISABLE_ENABLED);
+ dumpSetting(s, p,
+ Settings.Global.LOW_POWER_MODE_STICKY_AUTO_DISABLE_LEVEL,
+ GlobalSettingsProto.LowPowerMode.STICKY_AUTO_DISABLE_LEVEL);
p.end(lpmToken);
dumpSetting(s, p,
@@ -882,7 +896,7 @@ class SettingsProtoDumpUtil {
p.end(multiSimToken);
dumpSetting(s, p,
- Global.NATIVE_FLAGS_HEALTH_CHECK_ENABLED,
+ Settings.Global.NATIVE_FLAGS_HEALTH_CHECK_ENABLED,
GlobalSettingsProto.NATIVE_FLAGS_HEALTH_CHECK_ENABLED);
final long netstatsToken = p.start(GlobalSettingsProto.NETSTATS);
@@ -1109,9 +1123,6 @@ class SettingsProtoDumpUtil {
dumpSetting(s, p,
Settings.Global.POWER_MANAGER_CONSTANTS,
GlobalSettingsProto.POWER_MANAGER_CONSTANTS);
- dumpSetting(s, p,
- Settings.Global.PRIV_APP_OOB_ENABLED,
- GlobalSettingsProto.PRIV_APP_OOB_ENABLED);
final long prepaidSetupToken = p.start(GlobalSettingsProto.PREPAID_SETUP);
dumpSetting(s, p,
@@ -1262,10 +1273,10 @@ class SettingsProtoDumpUtil {
final long soundTriggerToken = p.start(GlobalSettingsProto.SOUND_TRIGGER);
dumpSetting(s, p,
- Global.MAX_SOUND_TRIGGER_DETECTION_SERVICE_OPS_PER_DAY,
+ Settings.Global.MAX_SOUND_TRIGGER_DETECTION_SERVICE_OPS_PER_DAY,
GlobalSettingsProto.SoundTrigger.MAX_SOUND_TRIGGER_DETECTION_SERVICE_OPS_PER_DAY);
dumpSetting(s, p,
- Global.SOUND_TRIGGER_DETECTION_SERVICE_OP_TIMEOUT,
+ Settings.Global.SOUND_TRIGGER_DETECTION_SERVICE_OP_TIMEOUT,
GlobalSettingsProto.SoundTrigger.DETECTION_SERVICE_OP_TIMEOUT_MS);
p.end(soundTriggerToken);
@@ -1561,7 +1572,7 @@ class SettingsProtoDumpUtil {
GlobalSettingsProto.ZRAM_ENABLED);
dumpSetting(s, p,
- Global.APP_OPS_CONSTANTS,
+ Settings.Global.APP_OPS_CONSTANTS,
GlobalSettingsProto.APP_OPS_CONSTANTS);
p.end(token);
@@ -2377,6 +2388,14 @@ class SettingsProtoDumpUtil {
Settings.Secure.SILENCE_GESTURE,
SecureSettingsProto.SILENCE_GESTURE_ENABLED);
+ dumpSetting(s, p,
+ Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES,
+ SecureSettingsProto.THEME_CUSTOMIZATION_OVERLAY_PACKAGES);
+
+ dumpSetting(s, p,
+ Settings.Secure.AWARE_ENABLED,
+ SecureSettingsProto.AWARE_ENABLED);
+
// Please insert new settings using the same order as in SecureSettingsProto.
p.end(token);
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index 8be67d9a7a51..0a62b7c1bde2 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -73,7 +73,7 @@ android_library {
"com.android.keyguard",
],
- annotation_processors: ["dagger2-compiler-2.19"],
+ plugins: ["dagger2-compiler-2.19"],
}
android_library {
@@ -127,7 +127,7 @@ android_library {
"--extra-packages",
"com.android.keyguard:com.android.systemui",
],
- annotation_processors: ["dagger2-compiler-2.19"],
+ plugins: ["dagger2-compiler-2.19"],
}
android_app {
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index b4f2711ef9d2..3453e798c7ae 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -386,6 +386,15 @@
android:excludeFromRecents="true">
</activity>
+ <!-- started from UsbPortManager -->
+ <activity android:name=".usb.UsbContaminantActivity"
+ android:exported="true"
+ android:permission="android.permission.MANAGE_USB"
+ android:theme="@style/Theme.SystemUI.Dialog.Alert"
+ android:finishOnCloseSystemDialogs="true"
+ android:excludeFromRecents="true">
+ </activity>
+
<!-- started from AdbDebuggingManager -->
<activity android:name=".usb.UsbDebuggingActivity"
android:permission="android.permission.MANAGE_DEBUGGING"
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationMenuRowPlugin.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationMenuRowPlugin.java
index 0b1dab1c3bca..fc84332151ec 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationMenuRowPlugin.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationMenuRowPlugin.java
@@ -20,14 +20,14 @@ import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
-import java.util.ArrayList;
-
import com.android.systemui.plugins.Plugin;
import com.android.systemui.plugins.annotations.DependsOn;
import com.android.systemui.plugins.annotations.ProvidesInterface;
+import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.MenuItem;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.OnMenuEventListener;
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption;
-import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.MenuItem;
+
+import java.util.ArrayList;
@ProvidesInterface(action = NotificationMenuRowPlugin.ACTION,
version = NotificationMenuRowPlugin.VERSION)
@@ -149,6 +149,12 @@ public interface NotificationMenuRowPlugin extends Plugin {
public boolean canBeDismissed();
/**
+ * Informs the menu whether dismiss gestures are left-to-right or right-to-left.
+ */
+ default void setDismissRtl(boolean dismissRtl) {
+ }
+
+ /**
* Determines whether the menu should remain open given its current state, or snap closed.
* @return true if the menu should remain open, false otherwise.
*/
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index d64e2f9d21ce..190bd7ae91a2 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -159,6 +159,12 @@
<!-- 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 USB contaminant presence dialog [CHAR LIMIT=NONE] -->
+ <string name="usb_contaminant_title">USB port disabled</string>
+
+ <!-- Message of USB contaminant presence dialog [CHAR LIMIT=NONE] -->
+ <string name="usb_contaminant_message">To protect your device from liquid or debris, the USB port is disabled and won\u2019t detect any accessories.\n\nYou\u2019ll be notified when it\u2019s safe to use the USB port again.</string>
+
<!-- Checkbox label for application compatibility mode ON (zooming app to look like it's running
on a phone). [CHAR LIMIT=25] -->
<string name="compat_mode_on">Zoom to fill screen</string>
@@ -2279,7 +2285,7 @@
app for debugging. Will not be seen by users. [CHAR LIMIT=20] -->
<string name="heap_dump_tile_name">Dump SysUI Heap</string>
- <!-- Text on chip for multiple apps using a single app op [CHAR LIMIT=10] -->
+ <!-- Text on chip for multiple apps using a single app op [CHAR LIMIT=12] -->
<plurals name="ongoing_privacy_chip_multiple_apps">
<item quantity="one"><xliff:g id="num_apps" example="1">%d</xliff:g> app</item>
<item quantity="few"><xliff:g id="num_apps" example="3">%d</xliff:g> apps</item>
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
index 53cdee549536..4d708908975e 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
@@ -47,6 +47,10 @@ import com.android.systemui.statusbar.policy.DeviceProvisionedController;
public class AssistManager implements ConfigurationChangedReceiver {
private static final String TAG = "AssistManager";
+
+ // Note that VERBOSE logging may leak PII (e.g. transcription contents).
+ private static final boolean VERBOSE = false;
+
private static final String ASSIST_ICON_METADATA_NAME =
"com.android.systemui.action_assist_icon";
@@ -103,16 +107,41 @@ public class AssistManager implements ConfigurationChangedReceiver {
protected void registerVoiceInteractionSessionListener() {
mAssistUtils.registerVoiceInteractionSessionListener(
new IVoiceInteractionSessionListener.Stub() {
- @Override
- public void onVoiceSessionShown() throws RemoteException {
- Log.v(TAG, "Voice open");
- }
+ @Override
+ public void onVoiceSessionShown() throws RemoteException {
+ if (VERBOSE) {
+ Log.v(TAG, "Voice open");
+ }
+ }
- @Override
- public void onVoiceSessionHidden() throws RemoteException {
- Log.v(TAG, "Voice closed");
- }
- });
+ @Override
+ public void onVoiceSessionHidden() throws RemoteException {
+ if (VERBOSE) {
+ Log.v(TAG, "Voice closed");
+ }
+ }
+
+ @Override
+ public void onTranscriptionUpdate(String transcription) {
+ if (VERBOSE) {
+ Log.v(TAG, "Transcription Updated: \"" + transcription + "\"");
+ }
+ }
+
+ @Override
+ public void onTranscriptionComplete(boolean immediate) {
+ if (VERBOSE) {
+ Log.v(TAG, "Transcription complete (immediate=" + immediate + ")");
+ }
+ }
+
+ @Override
+ public void onVoiceStateChange(int state) {
+ if (VERBOSE) {
+ Log.v(TAG, "Voice state is now " + state);
+ }
+ }
+ });
}
public void onConfigurationChanged(Configuration newConfiguration) {
@@ -291,8 +320,10 @@ public class AssistManager implements ConfigurationChangedReceiver {
}
}
} catch (PackageManager.NameNotFoundException e) {
- Log.v(TAG, "Assistant component "
- + component.flattenToShortString() + " not found");
+ if (VERBOSE) {
+ Log.v(TAG, "Assistant component "
+ + component.flattenToShortString() + " not found");
+ }
} catch (Resources.NotFoundException nfe) {
Log.w(TAG, "Failed to swap drawable from "
+ component.flattenToShortString(), nfe);
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java
index 7b6e79be64db..e8432b9a7017 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java
@@ -33,6 +33,7 @@ import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
+import android.widget.LinearLayout;
import android.widget.TextView;
import com.android.internal.graphics.ColorUtils;
@@ -251,7 +252,7 @@ public class BubbleView extends FrameLayout implements BubbleTouchHandler.Floati
// HACK: release() will crash if the view is not attached.
mActivityView.setVisibility(View.GONE);
- tmpParent.addView(mActivityView, new ViewGroup.LayoutParams(
+ tmpParent.addView(mActivityView, new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT));
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
index 1155a414b870..e1becdbb42a6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
@@ -188,7 +188,7 @@ public class CellularTile extends QSTileImpl<SignalState> {
state.secondaryLabel = r.getString(R.string.status_bar_airplane);
} else if (mobileDataEnabled) {
state.state = Tile.STATE_ACTIVE;
- state.secondaryLabel = getMobileDataDescription(cb);
+ state.secondaryLabel = getMobileDataSubscriptionName(cb);
} else {
state.state = Tile.STATE_INACTIVE;
state.secondaryLabel = r.getString(R.string.cell_data_off);
@@ -207,16 +207,16 @@ public class CellularTile extends QSTileImpl<SignalState> {
state.contentDescription = state.label + ", " + contentDescriptionSuffix;
}
- private CharSequence getMobileDataDescription(CallbackInfo cb) {
- if (cb.roaming && !TextUtils.isEmpty(cb.dataContentDescription)) {
+ private CharSequence getMobileDataSubscriptionName(CallbackInfo cb) {
+ if (cb.roaming && !TextUtils.isEmpty(cb.dataSubscriptionName)) {
String roaming = mContext.getString(R.string.data_connection_roaming);
- String dataDescription = cb.dataContentDescription;
+ String dataDescription = cb.dataSubscriptionName.toString();
return mContext.getString(R.string.mobile_data_text_format, roaming, dataDescription);
}
if (cb.roaming) {
return mContext.getString(R.string.data_connection_roaming);
}
- return cb.dataContentDescription;
+ return cb.dataSubscriptionName;
}
@Override
@@ -231,7 +231,7 @@ public class CellularTile extends QSTileImpl<SignalState> {
private static final class CallbackInfo {
boolean airplaneModeEnabled;
- String dataContentDescription;
+ CharSequence dataSubscriptionName;
boolean activityIn;
boolean activityOut;
boolean noSim;
@@ -249,7 +249,7 @@ public class CellularTile extends QSTileImpl<SignalState> {
// Not data sim, don't display.
return;
}
- mInfo.dataContentDescription = typeContentDescription;
+ mInfo.dataSubscriptionName = mController.getMobileDataNetworkName();
mInfo.activityIn = activityIn;
mInfo.activityOut = activityOut;
mInfo.roaming = roaming;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java
index b04132d09b7c..43ce0b50e03d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java
@@ -82,7 +82,7 @@ public class NightDisplayTile extends QSTileImpl<BooleanState>
if ("1".equals(Settings.Global.getString(mContext.getContentResolver(),
Settings.Global.NIGHT_DISPLAY_FORCED_AUTO_MODE_AVAILABLE))
&& mController.getAutoModeRaw() == -1) {
- mController.setAutoMode(ColorDisplayController.AUTO_MODE_CUSTOM);
+ mController.setAutoMode(ColorDisplayManager.AUTO_MODE_CUSTOM_TIME);
Log.i("NightDisplayTile", "Enrolled in forced night display auto mode");
}
@@ -127,7 +127,7 @@ public class NightDisplayTile extends QSTileImpl<BooleanState>
@Nullable
private String getSecondaryLabel(boolean isNightLightActivated) {
switch(mController.getAutoMode()) {
- case ColorDisplayController.AUTO_MODE_TWILIGHT:
+ case ColorDisplayManager.AUTO_MODE_TWILIGHT:
// Auto mode related to sunrise & sunset. If the light is on, it's guaranteed to be
// turned off at sunrise. If it's off, it's guaranteed to be turned on at sunset.
return isNightLightActivated
@@ -136,7 +136,7 @@ public class NightDisplayTile extends QSTileImpl<BooleanState>
: mContext.getString(
R.string.quick_settings_night_secondary_label_on_at_sunset);
- case ColorDisplayController.AUTO_MODE_CUSTOM:
+ case ColorDisplayManager.AUTO_MODE_CUSTOM_TIME:
// User-specified time, approximated to the nearest hour.
final @StringRes int toggleTimeStringRes;
final LocalTime toggleTime;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/MediaArtworkProcessor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/MediaArtworkProcessor.kt
new file mode 100644
index 000000000000..494473242a90
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/MediaArtworkProcessor.kt
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar
+
+import android.annotation.ColorInt
+import android.content.Context
+import android.graphics.Bitmap
+import android.graphics.Canvas
+import android.graphics.Point
+import android.graphics.Rect
+import android.renderscript.Allocation
+import android.renderscript.Element
+import android.renderscript.RenderScript
+import android.renderscript.ScriptIntrinsicBlur
+import android.util.MathUtils
+import com.android.internal.graphics.ColorUtils
+
+import javax.inject.Inject
+import javax.inject.Singleton
+
+private const val COLOR_ALPHA = (255 * 0.7f).toInt()
+private const val BLUR_RADIUS = 25f
+private const val DOWNSAMPLE = 6
+
+@Singleton
+class MediaArtworkProcessor @Inject constructor() {
+
+ private val mTmpSize = Point()
+ private var mArtworkCache: Bitmap? = null
+
+ fun processArtwork(context: Context, artwork: Bitmap, @ColorInt color: Int): Bitmap {
+ if (mArtworkCache != null) {
+ return mArtworkCache!!
+ }
+
+ context.display.getSize(mTmpSize)
+ val renderScript = RenderScript.create(context)
+ val rect = Rect(0, 0,artwork.width, artwork.height)
+ MathUtils.fitRect(rect, Math.max(mTmpSize.x / DOWNSAMPLE, mTmpSize.y / DOWNSAMPLE))
+ val inBitmap = Bitmap.createScaledBitmap(artwork, rect.width(), rect.height(),
+ true /* filter */)
+ val input = Allocation.createFromBitmap(renderScript, inBitmap,
+ Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_GRAPHICS_TEXTURE)
+ val outBitmap = Bitmap.createBitmap(inBitmap.width, inBitmap.height,
+ Bitmap.Config.ARGB_8888)
+ val output = Allocation.createFromBitmap(renderScript, outBitmap)
+ val blur = ScriptIntrinsicBlur.create(renderScript, Element.U8_4(renderScript))
+ blur.setRadius(BLUR_RADIUS)
+ blur.setInput(input)
+ blur.forEach(output)
+ output.copyTo(outBitmap)
+
+ input.destroy()
+ output.destroy()
+ inBitmap.recycle()
+
+ val canvas = Canvas(outBitmap)
+ canvas.drawColor(ColorUtils.setAlphaComponent(color, COLOR_ALPHA))
+ return outBitmap
+ }
+
+ fun clearCache() {
+ mArtworkCache?.recycle()
+ mArtworkCache = null
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java
index f46ded4d61d8..c25b7cfd804c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java
@@ -39,6 +39,9 @@ public interface NotificationLockscreenUserManager {
boolean isCurrentProfile(int userId);
+ /** Adds a listener to be notified when the current user changes. */
+ void addUserChangedListener(UserChangedListener listener);
+
void destroy();
SparseArray<UserInfo> getCurrentProfiles();
@@ -58,4 +61,9 @@ public interface NotificationLockscreenUserManager {
boolean needsRedaction(NotificationEntry entry);
boolean userAllowsPrivateNotificationsInPublic(int currentUserId);
+
+ /** Notified when the current user changes. */
+ interface UserChangedListener {
+ void onUserChanged(int userId);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
index d5f4d0461ba4..4f9d4282dae8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
@@ -49,11 +49,14 @@ import com.android.systemui.statusbar.StatusBarStateController.StateListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.NotificationUtils;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.KeyguardMonitor;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
/**
* Handles keeping track of the current user, profiles, and various things related to hiding
@@ -77,6 +80,7 @@ public class NotificationLockscreenUserManagerImpl implements
private final SparseBooleanArray mUsersAllowingNotifications = new SparseBooleanArray();
private final UserManager mUserManager;
private final IStatusBarService mBarService;
+ private final List<UserChangedListener> mListeners = new ArrayList<>();
private boolean mShowLockscreenNotifications;
private boolean mAllowLockscreenRemoteInput;
@@ -111,6 +115,10 @@ public class NotificationLockscreenUserManagerImpl implements
updatePublicMode();
mPresenter.onUserSwitched(mCurrentUserId);
getEntryManager().getNotificationData().filterAndSort();
+
+ for (UserChangedListener listener : mListeners) {
+ listener.onUserChanged(mCurrentUserId);
+ }
} else if (Intent.ACTION_USER_ADDED.equals(action)) {
updateCurrentProfilesCache();
} else if (Intent.ACTION_USER_UNLOCKED.equals(action)) {
@@ -130,8 +138,11 @@ public class NotificationLockscreenUserManagerImpl implements
final int count =
getEntryManager().getNotificationData().getActiveNotifications().size();
final int rank = getEntryManager().getNotificationData().getRank(notificationKey);
+ NotificationVisibility.NotificationLocation location =
+ NotificationLogger.getNotificationLocation(
+ getEntryManager().getNotificationData().get(notificationKey));
final NotificationVisibility nv = NotificationVisibility.obtain(notificationKey,
- rank, count, true);
+ rank, count, true, location);
try {
mBarService.onNotificationClick(notificationKey, nv);
} catch (RemoteException e) {
@@ -498,6 +509,10 @@ public class NotificationLockscreenUserManagerImpl implements
}
}
+ @Override
+ public void addUserChangedListener(UserChangedListener listener) {
+ mListeners.add(listener);
+ }
// public void updatePublicMode() {
// //TODO: I think there may be a race condition where mKeyguardViewManager.isShowing() returns
@@ -537,6 +552,7 @@ public class NotificationLockscreenUserManagerImpl implements
public void destroy() {
mContext.unregisterReceiver(mBaseBroadcastReceiver);
mContext.unregisterReceiver(mAllUsersReceiver);
+ mListeners.clear();
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
index 7412702abfea..98a3a547ed2c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
@@ -25,6 +25,7 @@ import android.annotation.Nullable;
import android.app.Notification;
import android.content.Context;
import android.graphics.Bitmap;
+import android.graphics.Color;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
@@ -102,6 +103,7 @@ public class NotificationMediaManager implements Dumpable {
private final Context mContext;
private final MediaSessionManager mMediaSessionManager;
private final ArrayList<MediaListener> mMediaListeners;
+ private final MediaArtworkProcessor mMediaArtworkProcessor;
protected NotificationPresenter mPresenter;
private MediaController mMediaController;
@@ -133,6 +135,7 @@ public class NotificationMediaManager implements Dumpable {
if (DEBUG_MEDIA) {
Log.v(TAG, "DEBUG_MEDIA: onMetadataChanged: " + metadata);
}
+ mMediaArtworkProcessor.clearCache();
mMediaMetadata = metadata;
dispatchUpdateMediaMetaData(true /* changed */, true /* allowAnimation */);
}
@@ -143,8 +146,10 @@ public class NotificationMediaManager implements Dumpable {
Context context,
Lazy<ShadeController> shadeController,
Lazy<StatusBarWindowController> statusBarWindowController,
- NotificationEntryManager notificationEntryManager) {
+ NotificationEntryManager notificationEntryManager,
+ MediaArtworkProcessor mediaArtworkProcessor) {
mContext = context;
+ mMediaArtworkProcessor = mediaArtworkProcessor;
mMediaListeners = new ArrayList<>();
mMediaSessionManager
= (MediaSessionManager) mContext.getSystemService(Context.MEDIA_SESSION_SERVICE);
@@ -366,6 +371,7 @@ public class NotificationMediaManager implements Dumpable {
}
private void clearCurrentMediaNotificationSession() {
+ mMediaArtworkProcessor.clearCache();
mMediaMetadata = null;
if (mMediaController != null) {
if (DEBUG_MEDIA) {
@@ -418,7 +424,19 @@ public class NotificationMediaManager implements Dumpable {
// might still be null
}
if (artworkBitmap != null) {
- artworkDrawable = new BitmapDrawable(mBackdropBack.getResources(), artworkBitmap);
+ int notificationColor;
+ synchronized (mEntryManager.getNotificationData()) {
+ NotificationEntry entry = mEntryManager.getNotificationData()
+ .get(mMediaNotificationKey);
+ if (entry == null || entry.getRow() == null) {
+ notificationColor = Color.TRANSPARENT;
+ } else {
+ notificationColor = entry.getRow().calculateBgColor();
+ }
+ }
+ Bitmap bmp = mMediaArtworkProcessor.processArtwork(mContext, artworkBitmap,
+ notificationColor);
+ artworkDrawable = new BitmapDrawable(mBackdropBack.getResources(), bmp);
}
}
boolean allowWhenShade = false;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
index 7d6231f27885..31d16211f521 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
@@ -54,6 +54,7 @@ import com.android.systemui.Dumpable;
import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.policy.RemoteInputView;
@@ -181,7 +182,11 @@ public class NotificationRemoteInputManager implements Dumpable {
final int rank = mEntryManager.getNotificationData().getRank(key);
final Notification.Action action =
statusBarNotification.getNotification().actions[actionIndex];
- final NotificationVisibility nv = NotificationVisibility.obtain(key, rank, count, true);
+ NotificationVisibility.NotificationLocation location =
+ NotificationLogger.getNotificationLocation(
+ mEntryManager.getNotificationData().get(key));
+ final NotificationVisibility nv =
+ NotificationVisibility.obtain(key, rank, count, true, location);
try {
mBarService.onNotificationActionClick(key, buttonIndex, action, nv, false);
} catch (RemoteException e) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java b/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java
index 573c1f8ee509..a2abcd2f9c8f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java
@@ -23,6 +23,7 @@ import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.statusbar.NotificationVisibility;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import java.util.Set;
@@ -74,8 +75,10 @@ public class SmartReplyController {
boolean generatedByAssistant) {
final int count = mEntryManager.getNotificationData().getActiveNotifications().size();
final int rank = mEntryManager.getNotificationData().getRank(entry.key);
- final NotificationVisibility nv =
- NotificationVisibility.obtain(entry.key, rank, count, true);
+ NotificationVisibility.NotificationLocation location =
+ NotificationLogger.getNotificationLocation(entry);
+ final NotificationVisibility nv = NotificationVisibility.obtain(
+ entry.key, rank, count, true, location);
try {
mBarService.onNotificationActionClick(
entry.key, actionIndex, action, nv, generatedByAssistant);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
index 989e781ab5ba..ef5e936f9532 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -36,6 +36,7 @@ import com.android.systemui.statusbar.NotificationUpdateHandler;
import com.android.systemui.statusbar.notification.collection.NotificationData;
import com.android.systemui.statusbar.notification.collection.NotificationData.KeyguardEnvironment;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.NotificationInflater;
import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
@@ -160,8 +161,10 @@ public class NotificationEntryManager implements
public void performRemoveNotification(StatusBarNotification n) {
final int rank = mNotificationData.getRank(n.getKey());
final int count = mNotificationData.getActiveNotifications().size();
+ NotificationVisibility.NotificationLocation location =
+ NotificationLogger.getNotificationLocation(getNotificationData().get(n.getKey()));
final NotificationVisibility nv = NotificationVisibility.obtain(n.getKey(), rank, count,
- true);
+ true, location);
removeNotificationInternal(
n.getKey(), null, nv, false /* forceRemove */, true /* removedByUser */);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java
index 8c29fb50f00a..54ed0d9cd8ce 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java
@@ -333,6 +333,7 @@ public class NotificationData {
mGroupManager.onEntryUpdated(entry, oldSbn);
}
entry.populateFromRanking(mTmpRanking);
+ entry.setIsHighPriority(isHighPriority(entry.notification));
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
index ee551ee96e7b..2e93c3822737 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -153,6 +153,12 @@ public final class NotificationEntry {
*/
private boolean mUserDismissedBubble;
+ /**
+ * Whether this notification is shown to the user as a high priority notification: visible on
+ * the lock screen/status bar and in the top section in the shade.
+ */
+ private boolean mHighPriority;
+
public NotificationEntry(StatusBarNotification n) {
this(n, null);
}
@@ -191,6 +197,14 @@ public final class NotificationEntry {
return interruption;
}
+ public boolean isHighPriority() {
+ return mHighPriority;
+ }
+
+ public void setIsHighPriority(boolean highPriority) {
+ this.mHighPriority = highPriority;
+ }
+
public void setIsBubble(boolean bubbleable) {
mIsBubble = bubbleable;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
index 76d394d197eb..7b94c7450637 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
@@ -32,7 +32,6 @@ import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.statusbar.NotificationVisibility;
-import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
import com.android.systemui.UiOffloadThread;
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.StatusBarStateController;
@@ -40,6 +39,8 @@ import com.android.systemui.statusbar.StatusBarStateController.StateListener;
import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.policy.HeadsUpManager;
@@ -128,7 +129,8 @@ public class NotificationLogger implements StateListener {
NotificationEntry entry = activeNotifications.get(i);
String key = entry.notification.getKey();
boolean isVisible = mListContainer.isInVisibleLocation(entry);
- NotificationVisibility visObj = NotificationVisibility.obtain(key, i, N, isVisible);
+ NotificationVisibility visObj = NotificationVisibility.obtain(key, i, N, isVisible,
+ getNotificationLocation(entry));
boolean previouslyVisible = mCurrentlyVisibleNotifications.contains(visObj);
if (isVisible) {
// Build new set of visible notifications.
@@ -160,6 +162,40 @@ public class NotificationLogger implements StateListener {
}
};
+ /**
+ * Returns the location of the notification referenced by the given {@link NotificationEntry}.
+ */
+ public static NotificationVisibility.NotificationLocation getNotificationLocation(
+ NotificationEntry entry) {
+ ExpandableNotificationRow row = entry.getRow();
+ ExpandableViewState childViewState = row.getViewState();
+
+ if (childViewState == null) {
+ return NotificationVisibility.NotificationLocation.LOCATION_UNKNOWN;
+ }
+ return convertNotificationLocation(childViewState.location);
+ }
+
+ private static NotificationVisibility.NotificationLocation convertNotificationLocation(
+ int location) {
+ switch (location) {
+ case ExpandableViewState.LOCATION_FIRST_HUN:
+ return NotificationVisibility.NotificationLocation.LOCATION_FIRST_HEADS_UP;
+ case ExpandableViewState.LOCATION_HIDDEN_TOP:
+ return NotificationVisibility.NotificationLocation.LOCATION_HIDDEN_TOP;
+ case ExpandableViewState.LOCATION_MAIN_AREA:
+ return NotificationVisibility.NotificationLocation.LOCATION_MAIN_AREA;
+ case ExpandableViewState.LOCATION_BOTTOM_STACK_PEEKING:
+ return NotificationVisibility.NotificationLocation.LOCATION_BOTTOM_STACK_PEEKING;
+ case ExpandableViewState.LOCATION_BOTTOM_STACK_HIDDEN:
+ return NotificationVisibility.NotificationLocation.LOCATION_BOTTOM_STACK_HIDDEN;
+ case ExpandableViewState.LOCATION_GONE:
+ return NotificationVisibility.NotificationLocation.LOCATION_GONE;
+ default:
+ return NotificationVisibility.NotificationLocation.LOCATION_UNKNOWN;
+ }
+ }
+
@Inject
public NotificationLogger(NotificationListener notificationListener,
UiOffloadThread uiOffloadThread,
@@ -363,7 +399,9 @@ public class NotificationLogger implements StateListener {
* Called when the notification is expanded / collapsed.
*/
public void onExpansionChanged(String key, boolean isUserAction, boolean isExpanded) {
- mExpansionStateLogger.onExpansionChanged(key, isUserAction, isExpanded);
+ NotificationVisibility.NotificationLocation location =
+ getNotificationLocation(mEntryManager.getNotificationData().get(key));
+ mExpansionStateLogger.onExpansionChanged(key, isUserAction, isExpanded, location);
}
/**
@@ -397,10 +435,12 @@ public class NotificationLogger implements StateListener {
}
@VisibleForTesting
- void onExpansionChanged(String key, boolean isUserAction, boolean isExpanded) {
+ void onExpansionChanged(String key, boolean isUserAction, boolean isExpanded,
+ NotificationVisibility.NotificationLocation location) {
State state = getState(key);
state.mIsUserAction = isUserAction;
state.mIsExpanded = isExpanded;
+ state.mLocation = location;
maybeNotifyOnNotificationExpansionChanged(key, state);
}
@@ -416,6 +456,7 @@ public class NotificationLogger implements StateListener {
for (NotificationVisibility nv : newlyVisibleAr) {
State state = getState(nv.key);
state.mIsVisible = true;
+ state.mLocation = nv.location;
maybeNotifyOnNotificationExpansionChanged(nv.key, state);
}
for (NotificationVisibility nv : noLongerVisibleAr) {
@@ -460,10 +501,8 @@ public class NotificationLogger implements StateListener {
final State stateToBeLogged = new State(state);
mUiOffloadThread.submit(() -> {
try {
- mBarService.onNotificationExpansionChanged(
- key, stateToBeLogged.mIsUserAction, stateToBeLogged.mIsExpanded,
- // TODO (b/120767764): fill in location
- ExpandableViewState.LOCATION_UNKNOWN /* notificationLocation */);
+ mBarService.onNotificationExpansionChanged(key, stateToBeLogged.mIsUserAction,
+ stateToBeLogged.mIsExpanded, stateToBeLogged.mLocation.ordinal());
} catch (RemoteException e) {
Log.e(TAG, "Failed to call onNotificationExpansionChanged: ", e);
}
@@ -477,6 +516,8 @@ public class NotificationLogger implements StateListener {
Boolean mIsExpanded;
@Nullable
Boolean mIsVisible;
+ @Nullable
+ NotificationVisibility.NotificationLocation mLocation;
private State() {}
@@ -484,10 +525,12 @@ public class NotificationLogger implements StateListener {
this.mIsUserAction = state.mIsUserAction;
this.mIsExpanded = state.mIsExpanded;
this.mIsVisible = state.mIsVisible;
+ this.mLocation = state.mLocation;
}
private boolean isFullySet() {
- return mIsUserAction != null && mIsExpanded != null && mIsVisible != null;
+ return mIsUserAction != null && mIsExpanded != null && mIsVisible != null
+ && mLocation != null;
}
}
}
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 296c061459bc..bed2426021a1 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
@@ -3092,6 +3092,11 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
}
}
+ /** Sets whether dismiss gestures are right-to-left (instead of left-to-right). */
+ public void setDismissRtl(boolean dismissRtl) {
+ mMenuRow.setDismissRtl(dismissRtl);
+ }
+
private static class NotificationViewState extends ExpandableViewState {
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
index bd1dfb181afe..cb1384cacec8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
@@ -25,6 +25,7 @@ import android.app.NotificationChannel;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
+import android.metrics.LogMaker;
import android.net.Uri;
import android.os.ServiceManager;
import android.os.UserHandle;
@@ -159,7 +160,8 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx
return bindGuts(row, mGutsMenuItem);
}
- private boolean bindGuts(final ExpandableNotificationRow row,
+ @VisibleForTesting
+ protected boolean bindGuts(final ExpandableNotificationRow row,
NotificationMenuRowPlugin.MenuItem item) {
StatusBarNotification sbn = row.getStatusBarNotification();
@@ -298,7 +300,8 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx
row.getIsNonblockable(),
isForBlockingHelper,
row.getEntry().userSentiment == USER_SENTIMENT_NEGATIVE,
- row.getEntry().importance);
+ row.getEntry().importance,
+ row.getEntry().isHighPriority());
}
@@ -389,7 +392,11 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx
return false;
}
- mMetricsLogger.action(MetricsProto.MetricsEvent.ACTION_NOTE_CONTROLS);
+ LogMaker logMaker = (row.getStatusBarNotification() == null)
+ ? new LogMaker(MetricsProto.MetricsEvent.ACTION_NOTE_CONTROLS)
+ : row.getStatusBarNotification().getLogMaker();
+ mMetricsLogger.write(logMaker.setCategory(MetricsProto.MetricsEvent.ACTION_NOTE_CONTROLS)
+ .setType(MetricsProto.MetricsEvent.TYPE_ACTION));
// ensure that it's laid but not visible until actually laid out
guts.setVisibility(View.INVISIBLE);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
index 5253e38fd675..2a9a815e12d1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
@@ -21,7 +21,6 @@ import static android.app.NotificationManager.IMPORTANCE_HIGH;
import static android.app.NotificationManager.IMPORTANCE_LOW;
import static android.app.NotificationManager.IMPORTANCE_MIN;
import static android.app.NotificationManager.IMPORTANCE_NONE;
-import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -97,8 +96,12 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
private int mNumUniqueChannelsInRow;
private NotificationChannel mSingleNotificationChannel;
private int mStartingChannelImportance;
- private int mStartingChannelOrNotificationImportance;
- private int mChosenImportance;
+ private boolean mWasShownHighPriority;
+ /**
+ * The last importance level chosen by the user. Null if the user has not chosen an importance
+ * level; non-null once the user takes an action which indicates an explicit preference.
+ */
+ @Nullable private Integer mChosenImportance;
private boolean mIsSingleDefaultChannel;
private boolean mIsNonblockable;
private StatusBarNotification mSbn;
@@ -195,13 +198,14 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
final OnAppSettingsClickListener onAppSettingsClick,
boolean isDeviceProvisioned,
boolean isNonblockable,
- int importance)
+ int importance,
+ boolean wasShownHighPriority)
throws RemoteException {
bindNotification(pm, iNotificationManager, pkg, notificationChannel,
numUniqueChannelsInRow, sbn, checkSaveListener, onSettingsClick,
onAppSettingsClick, isDeviceProvisioned, isNonblockable,
false /* isBlockingHelper */, false /* isUserSentimentNegative */,
- importance);
+ importance, wasShownHighPriority);
}
public void bindNotification(
@@ -218,7 +222,8 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
boolean isNonblockable,
boolean isForBlockingHelper,
boolean isUserSentimentNegative,
- int importance)
+ int importance,
+ boolean wasShownHighPriority)
throws RemoteException {
mINotificationManager = iNotificationManager;
mMetricsLogger = Dependency.get(MetricsLogger.class);
@@ -231,10 +236,8 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
mCheckSaveListener = checkSaveListener;
mOnSettingsClickListener = onSettingsClick;
mSingleNotificationChannel = notificationChannel;
- int channelImportance = mSingleNotificationChannel.getImportance();
- mStartingChannelImportance = mChosenImportance = channelImportance;
- mStartingChannelOrNotificationImportance =
- channelImportance == IMPORTANCE_UNSPECIFIED ? importance : channelImportance;
+ mStartingChannelImportance = mSingleNotificationChannel.getImportance();
+ mWasShownHighPriority = wasShownHighPriority;
mNegativeUserSentiment = isUserSentimentNegative;
mIsNonblockable = isNonblockable;
mIsForeground =
@@ -400,19 +403,27 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
* @return new LogMaker
*/
private LogMaker importanceChangeLogMaker() {
+ Integer chosenImportance =
+ mChosenImportance != null ? mChosenImportance : mStartingChannelImportance;
return new LogMaker(MetricsEvent.ACTION_SAVE_IMPORTANCE)
.setType(MetricsEvent.TYPE_ACTION)
- .setSubtype(mChosenImportance - mStartingChannelImportance);
+ .setSubtype(chosenImportance - mStartingChannelImportance);
}
private boolean hasImportanceChanged() {
return mSingleNotificationChannel != null
- && mStartingChannelImportance != mChosenImportance;
+ && mChosenImportance != null
+ && (mStartingChannelImportance != mChosenImportance
+ || (mWasShownHighPriority && mChosenImportance < IMPORTANCE_DEFAULT)
+ || (!mWasShownHighPriority && mChosenImportance >= IMPORTANCE_DEFAULT));
}
private void saveImportance() {
if (!mIsNonblockable
|| mExitReason != NotificationCounters.BLOCKING_HELPER_STOP_NOTIFICATIONS) {
+ if (mChosenImportance == null) {
+ mChosenImportance = mStartingChannelImportance;
+ }
updateImportance();
}
}
@@ -421,12 +432,15 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
* Commits the updated importance values on the background thread.
*/
private void updateImportance() {
- mMetricsLogger.write(importanceChangeLogMaker());
-
- Handler bgHandler = new Handler(Dependency.get(Dependency.BG_LOOPER));
- bgHandler.post(new UpdateImportanceRunnable(mINotificationManager, mPackageName, mAppUid,
- mNumUniqueChannelsInRow == 1 ? mSingleNotificationChannel : null,
- mStartingChannelImportance, mChosenImportance));
+ if (mChosenImportance != null) {
+ mMetricsLogger.write(importanceChangeLogMaker());
+
+ Handler bgHandler = new Handler(Dependency.get(Dependency.BG_LOOPER));
+ bgHandler.post(
+ new UpdateImportanceRunnable(mINotificationManager, mPackageName, mAppUid,
+ mNumUniqueChannelsInRow == 1 ? mSingleNotificationChannel : null,
+ mStartingChannelImportance, mChosenImportance));
+ }
}
private void bindButtons() {
@@ -444,11 +458,8 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
TextView silent = findViewById(R.id.int_silent);
TextView alert = findViewById(R.id.int_alert);
- boolean isCurrentlyAlerting =
- mStartingChannelOrNotificationImportance >= IMPORTANCE_DEFAULT;
-
block.setOnClickListener(mOnStopOrMinimizeNotifications);
- if (isCurrentlyAlerting) {
+ if (mWasShownHighPriority) {
silent.setOnClickListener(mOnToggleSilent);
silent.setText(R.string.inline_silent_button_silent);
alert.setOnClickListener(mOnKeepShowing);
@@ -517,7 +528,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
break;
case ACTION_TOGGLE_SILENT:
mExitReason = NotificationCounters.BLOCKING_HELPER_TOGGLE_SILENT;
- if (mStartingChannelOrNotificationImportance >= IMPORTANCE_DEFAULT) {
+ if (mWasShownHighPriority) {
mChosenImportance = IMPORTANCE_LOW;
confirmationText.setText(R.string.notification_channel_silenced);
} else {
@@ -584,9 +595,8 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
@Override
public void onFinishedClosing() {
- mStartingChannelImportance = mChosenImportance;
- if (mChosenImportance != IMPORTANCE_UNSPECIFIED) {
- mStartingChannelOrNotificationImportance = mChosenImportance;
+ if (mChosenImportance != null) {
+ mStartingChannelImportance = mChosenImportance;
}
mExitReason = NotificationCounters.BLOCKING_HELPER_DISMISSED;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
index d97162c2ef1e..d83a158b319f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
@@ -23,7 +23,6 @@ import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.annotation.Nullable;
import android.app.Notification;
-import android.app.NotificationManager;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
@@ -78,6 +77,8 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl
private ArrayList<MenuItem> mRightMenuItems;
private final Map<View, MenuItem> mMenuItemsByView = new ArrayMap<>();
private OnMenuEventListener mMenuListener;
+ private boolean mDismissRtl;
+ private boolean mIsForeground;
private ValueAnimator mFadeAnimator;
private boolean mAnimating;
@@ -238,6 +239,8 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl
}
private void createMenuViews(boolean resetState, final boolean isForeground) {
+ mIsForeground = isForeground;
+
final Resources res = mContext.getResources();
mHorizSpaceForIcon = res.getDimensionPixelSize(R.dimen.notification_menu_icon_size);
mVertSpaceForIcons = res.getDimensionPixelSize(R.dimen.notification_min_height);
@@ -250,12 +253,7 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl
}
mAppOpsItem = createAppOpsItem(mContext);
if (NotificationUtils.useNewInterruptionModel(mContext)) {
- int channelImportance = mParent.getEntry().channel.getImportance();
- int effectiveImportance =
- channelImportance == NotificationManager.IMPORTANCE_UNSPECIFIED
- ? mParent.getEntry().importance : channelImportance;
- mInfoItem = createInfoItem(mContext,
- effectiveImportance < NotificationManager.IMPORTANCE_DEFAULT);
+ mInfoItem = createInfoItem(mContext, !mParent.getEntry().isHighPriority());
} else {
mInfoItem = createInfoItem(mContext);
}
@@ -268,10 +266,11 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl
mRightMenuItems.add(mAppOpsItem);
mLeftMenuItems.addAll(mRightMenuItems);
} else {
- mRightMenuItems.add(mInfoItem);
- mRightMenuItems.add(mAppOpsItem);
+ ArrayList<MenuItem> menuItems = mDismissRtl ? mLeftMenuItems : mRightMenuItems;
+ menuItems.add(mInfoItem);
+ menuItems.add(mAppOpsItem);
if (!isForeground) {
- mRightMenuItems.add(mSnoozeItem);
+ menuItems.add(mSnoozeItem);
}
}
@@ -729,6 +728,14 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl
return getParent().canViewBeDismissed();
}
+ @Override
+ public void setDismissRtl(boolean dismissRtl) {
+ mDismissRtl = dismissRtl;
+ if (mMenuContainer != null) {
+ createMenuViews(true, mIsForeground);
+ }
+ }
+
public static class NotificationMenuItem implements MenuItem {
View mMenuView;
GutsContent mGutsContent;
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 2129b81b1448..63b34d185c8d 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
@@ -174,6 +174,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
private final boolean mShouldDrawNotificationBackground;
private boolean mLowPriorityBeforeSpeedBump;
private final boolean mAllowLongPress;
+ private boolean mDismissRtl;
private float mExpandedHeight;
private int mOwnScrollY;
@@ -533,8 +534,10 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
tunerService.addTunable((key, newValue) -> {
if (key.equals(LOW_PRIORITY)) {
mLowPriorityBeforeSpeedBump = "1".equals(newValue);
+ } else if (key.equals(Settings.Secure.NOTIFICATION_DISMISS_RTL)) {
+ updateDismissRtlSetting("1".equals(newValue));
}
- }, LOW_PRIORITY);
+ }, LOW_PRIORITY, Settings.Secure.NOTIFICATION_DISMISS_RTL);
mEntryManager.addNotificationEntryListener(new NotificationEntryListener() {
@Override
@@ -548,6 +551,16 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
});
}
+ private void updateDismissRtlSetting(boolean dismissRtl) {
+ mDismissRtl = dismissRtl;
+ for (int i = 0; i < getChildCount(); i++) {
+ View child = getChildAt(i);
+ if (child instanceof ExpandableNotificationRow) {
+ ((ExpandableNotificationRow) child).setDismissRtl(dismissRtl);
+ }
+ }
+ }
+
@Override
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
protected void onFinishInflate() {
@@ -2600,8 +2613,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
View child = getChildAt(i);
if (child.getVisibility() != View.GONE && child instanceof ExpandableNotificationRow) {
ExpandableNotificationRow row = (ExpandableNotificationRow) child;
- if (!mEntryManager.getNotificationData().isHighPriority(
- row.getStatusBarNotification())) {
+ if (!row.getEntry().isHighPriority()) {
break;
} else {
lastChildBeforeGap = row;
@@ -2619,8 +2631,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
View child = getChildAt(i);
if (child.getVisibility() != View.GONE && child instanceof ExpandableNotificationRow) {
ExpandableNotificationRow row = (ExpandableNotificationRow) child;
- if (!mEntryManager.getNotificationData().isHighPriority(
- row.getStatusBarNotification())) {
+ if (!row.getEntry().isHighPriority()) {
return row;
}
}
@@ -3255,6 +3266,9 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
generateAddAnimation(child, false /* fromMoreCard */);
updateAnimationState(child);
updateChronometerForChild(child);
+ if (child instanceof ExpandableNotificationRow) {
+ ((ExpandableNotificationRow) child).setDismissRtl(mDismissRtl);
+ }
if (ANCHOR_SCROLLING) {
// TODO: once we're recycling this will need to check the adapter position of the child
if (child == getFirstChildNotGone() && (isScrolledToTop() || !mIsExpanded)) {
@@ -5756,11 +5770,9 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
currentIndex++;
boolean beforeSpeedBump;
if (mLowPriorityBeforeSpeedBump) {
- beforeSpeedBump = !mEntryManager.getNotificationData().isAmbient(
- row.getStatusBarNotification().getKey());
+ beforeSpeedBump = !row.getEntry().ambient;
} else {
- beforeSpeedBump = mEntryManager.getNotificationData().isHighPriority(
- row.getStatusBarNotification());
+ beforeSpeedBump = row.getEntry().isHighPriority();
}
if (beforeSpeedBump) {
speedBumpIndex = currentIndex;
@@ -5784,8 +5796,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
continue;
}
ExpandableNotificationRow row = (ExpandableNotificationRow) view;
- if (!mEntryManager.getNotificationData().isHighPriority(
- row.getStatusBarNotification())) {
+ if (!row.getEntry().isHighPriority()) {
if (currentIndex > 0) {
gapIndex = currentIndex;
}
@@ -6315,7 +6326,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
public boolean canChildBeDismissedInDirection(View v, boolean isRightOrDown) {
boolean isValidDirection;
if (NotificationUtils.useNewInterruptionModel(mContext)) {
- isValidDirection = isLayoutRtl() ? !isRightOrDown : isRightOrDown;
+ isValidDirection = mDismissRtl ? !isRightOrDown : isRightOrDown;
} else {
isValidDirection = true;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
index fac4dbbe735a..b96c55b37c48 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
@@ -168,8 +168,8 @@ public class AutoTileManager {
@Override
public void onAutoModeChanged(int autoMode) {
- if (autoMode == ColorDisplayController.AUTO_MODE_CUSTOM
- || autoMode == ColorDisplayController.AUTO_MODE_TWILIGHT) {
+ if (autoMode == ColorDisplayManager.AUTO_MODE_CUSTOM_TIME
+ || autoMode == ColorDisplayManager.AUTO_MODE_TWILIGHT) {
addNightTile();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
index d3c6a1d8ee74..ee047e41ec78 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -243,6 +243,9 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback
mDisabledFlags2 = savedInstanceState.getInt(EXTRA_DISABLE2_STATE, 0);
}
mAccessibilityManagerWrapper.addCallback(mAccessibilityListener);
+
+ // Respect the latest disabled-flags.
+ mCommandQueue.recomputeDisableFlags(mDisplayId, false);
}
@Override
@@ -799,7 +802,9 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback
}
private void onAccessibilityClick(View v) {
- mAccessibilityManager.notifyAccessibilityButtonClicked();
+ final Display display = v.getDisplay();
+ mAccessibilityManager.notifyAccessibilityButtonClicked(
+ display != null ? display.getDisplayId() : Display.DEFAULT_DISPLAY);
}
private boolean onAccessibilityLongClick(View v) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationGestureAction.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationGestureAction.java
index a5d938216f5c..39fbbb17c498 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationGestureAction.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationGestureAction.java
@@ -20,6 +20,7 @@ import static android.view.WindowManagerPolicyConstants.NAV_BAR_LEFT;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_RIGHT;
import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_NONE;
+import static com.android.systemui.statusbar.phone.NavigationPrototypeController.PROTOTYPE_ENABLED;
import android.annotation.NonNull;
import android.content.Context;
@@ -84,7 +85,7 @@ public abstract class NavigationGestureAction {
// Tell launcher that this action requires a stable task list or not
boolean flag = requiresStableTaskList();
- if (flag != sLastTaskStabilizationFlag) {
+ if (getGlobalBoolean(PROTOTYPE_ENABLED) && flag != sLastTaskStabilizationFlag) {
Settings.Global.putInt(mNavigationBarView.getContext().getContentResolver(),
ENABLE_TASK_STABILIZER_FLAG, flag ? 1 : 0);
sLastTaskStabilizationFlag = flag;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationPrototypeController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationPrototypeController.java
index a09e5858d576..f762a6a68ad6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationPrototypeController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationPrototypeController.java
@@ -37,6 +37,7 @@ public class NavigationPrototypeController extends ContentObserver {
private final String GESTURE_MATCH_SETTING = "quickstepcontroller_gesture_match_map";
public static final String NAV_COLOR_ADAPT_ENABLE_SETTING = "navbar_color_adapt_enable";
+ public static final String PROTOTYPE_ENABLED = "prototype_enabled";
@Retention(RetentionPolicy.SOURCE)
@IntDef({ACTION_DEFAULT, ACTION_QUICKSTEP, ACTION_QUICKSCRUB, ACTION_BACK,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
index 077fcda70f0c..e86996a81bcd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
@@ -194,8 +194,7 @@ public class NotificationIconAreaController implements DarkReceiver,
if (mEntryManager.getNotificationData().isAmbient(entry.key) && !showAmbient) {
return false;
}
- if (!showLowPriority
- && !mEntryManager.getNotificationData().isHighPriority(entry.notification)) {
+ if (!showLowPriority && !entry.isHighPriority()) {
return false;
}
if (!entry.isTopLevelChild()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
index 4f61009095c7..86326beb8b00 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
@@ -64,6 +64,7 @@ import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.policy.HeadsUpUtil;
import com.android.systemui.statusbar.policy.KeyguardMonitor;
@@ -304,8 +305,11 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
final int count =
mEntryManager.getNotificationData().getActiveNotifications().size();
final int rank = mEntryManager.getNotificationData().getRank(notificationKey);
+ NotificationVisibility.NotificationLocation location =
+ NotificationLogger.getNotificationLocation(
+ mEntryManager.getNotificationData().get(notificationKey));
final NotificationVisibility nv = NotificationVisibility.obtain(notificationKey,
- rank, count, true);
+ rank, count, true, location);
try {
mBarService.onNotificationClick(notificationKey, nv);
} catch (RemoteException ex) {
diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbContaminantActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbContaminantActivity.java
new file mode 100644
index 000000000000..fa4b3fe4be18
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/usb/UsbContaminantActivity.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.usb;
+
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.hardware.usb.ParcelableUsbPort;
+import android.hardware.usb.UsbManager;
+import android.hardware.usb.UsbPort;
+import android.os.Bundle;
+import android.view.Window;
+import android.view.WindowManager;
+
+import com.android.internal.app.AlertActivity;
+import com.android.internal.app.AlertController;
+import com.android.systemui.R;
+
+/**
+ * Activity that alerts the user when contaminant is detected on USB port.
+ */
+public class UsbContaminantActivity extends AlertActivity
+ implements DialogInterface.OnClickListener {
+ private static final String TAG = "UsbContaminantActivity";
+
+ private UsbDisconnectedReceiver mDisconnectedReceiver;
+ private UsbPort mUsbPort;
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ Window window = getWindow();
+ window.addSystemFlags(WindowManager.LayoutParams
+ .SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
+ window.setType(WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG);
+
+ super.onCreate(icicle);
+
+ Intent intent = getIntent();
+ ParcelableUsbPort port = intent.getParcelableExtra(UsbManager.EXTRA_PORT);
+ mUsbPort = port.getUsbPort(getSystemService(UsbManager.class));
+
+ final AlertController.AlertParams ap = mAlertParams;
+ ap.mTitle = getString(R.string.usb_contaminant_title);
+ ap.mMessage = getString(R.string.usb_contaminant_message);
+ ap.mPositiveButtonText = getString(android.R.string.ok);
+ ap.mPositiveButtonListener = this;
+
+ setupAlert();
+ }
+
+ @Override
+ public void onWindowAttributesChanged(WindowManager.LayoutParams params) {
+ super.onWindowAttributesChanged(params);
+ }
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ finish();
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/ExpansionStateLoggerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/ExpansionStateLoggerTest.java
index 2f6b22117a10..5ff9d1490f3a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/ExpansionStateLoggerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/ExpansionStateLoggerTest.java
@@ -28,7 +28,6 @@ import android.testing.TestableLooper;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.statusbar.NotificationVisibility;
-import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
import com.android.systemui.Dependency;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.UiOffloadThread;
@@ -73,7 +72,8 @@ public class ExpansionStateLoggerTest extends SysuiTestCase {
@Test
public void testExpanded() throws RemoteException {
- mLogger.onExpansionChanged(NOTIFICATION_KEY, false, true);
+ mLogger.onExpansionChanged(NOTIFICATION_KEY, false, true,
+ NotificationVisibility.NotificationLocation.LOCATION_UNKNOWN);
waitForUiOffloadThread();
verify(mBarService, Mockito.never()).onNotificationExpansionChanged(
@@ -82,7 +82,8 @@ public class ExpansionStateLoggerTest extends SysuiTestCase {
@Test
public void testVisibleAndNotExpanded() throws RemoteException {
- mLogger.onExpansionChanged(NOTIFICATION_KEY, true, false);
+ mLogger.onExpansionChanged(NOTIFICATION_KEY, true, false,
+ NotificationVisibility.NotificationLocation.LOCATION_UNKNOWN);
mLogger.onVisibilityChanged(
Collections.singletonList(createNotificationVisibility(NOTIFICATION_KEY, true)),
Collections.emptyList());
@@ -94,26 +95,33 @@ public class ExpansionStateLoggerTest extends SysuiTestCase {
@Test
public void testVisibleAndExpanded() throws RemoteException {
- mLogger.onExpansionChanged(NOTIFICATION_KEY, true, true);
+ mLogger.onExpansionChanged(NOTIFICATION_KEY, true, true,
+ NotificationVisibility.NotificationLocation.LOCATION_UNKNOWN);
mLogger.onVisibilityChanged(
Collections.singletonList(createNotificationVisibility(NOTIFICATION_KEY, true)),
Collections.emptyList());
waitForUiOffloadThread();
verify(mBarService).onNotificationExpansionChanged(
- NOTIFICATION_KEY, true, true, ExpandableViewState.LOCATION_UNKNOWN);
+ NOTIFICATION_KEY, true, true,
+ NotificationVisibility.NotificationLocation.LOCATION_UNKNOWN.toMetricsEventEnum());
}
@Test
public void testExpandedAndVisible_expandedBeforeVisible() throws RemoteException {
- mLogger.onExpansionChanged(NOTIFICATION_KEY, false, true);
+ mLogger.onExpansionChanged(NOTIFICATION_KEY, false, true,
+ NotificationVisibility.NotificationLocation.LOCATION_UNKNOWN);
mLogger.onVisibilityChanged(
- Collections.singletonList(createNotificationVisibility(NOTIFICATION_KEY, true)),
+ Collections.singletonList(createNotificationVisibility(NOTIFICATION_KEY, true,
+ NotificationVisibility.NotificationLocation.LOCATION_MAIN_AREA)),
Collections.emptyList());
waitForUiOffloadThread();
verify(mBarService).onNotificationExpansionChanged(
- NOTIFICATION_KEY, false, true, ExpandableViewState.LOCATION_UNKNOWN);
+ NOTIFICATION_KEY, false, true,
+ // The last location seen should be logged (the one passed to onVisibilityChanged).
+ NotificationVisibility.NotificationLocation.LOCATION_MAIN_AREA.toMetricsEventEnum()
+ );
}
@Test
@@ -121,11 +129,14 @@ public class ExpansionStateLoggerTest extends SysuiTestCase {
mLogger.onVisibilityChanged(
Collections.singletonList(createNotificationVisibility(NOTIFICATION_KEY, true)),
Collections.emptyList());
- mLogger.onExpansionChanged(NOTIFICATION_KEY, false, true);
+ mLogger.onExpansionChanged(NOTIFICATION_KEY, false, true,
+ NotificationVisibility.NotificationLocation.LOCATION_FIRST_HEADS_UP);
waitForUiOffloadThread();
verify(mBarService).onNotificationExpansionChanged(
- NOTIFICATION_KEY, false, true, ExpandableViewState.LOCATION_UNKNOWN);
+ NOTIFICATION_KEY, false, true,
+ // The last location seen should be logged (the one passed to onExpansionChanged).
+ NotificationVisibility.NotificationLocation.LOCATION_FIRST_HEADS_UP.toMetricsEventEnum());
}
@Test
@@ -133,15 +144,24 @@ public class ExpansionStateLoggerTest extends SysuiTestCase {
mLogger.onVisibilityChanged(
Collections.singletonList(createNotificationVisibility(NOTIFICATION_KEY, true)),
Collections.emptyList());
- mLogger.onExpansionChanged(NOTIFICATION_KEY, false, true);
- mLogger.onExpansionChanged(NOTIFICATION_KEY, false, true);
+ mLogger.onExpansionChanged(NOTIFICATION_KEY, false, true,
+ NotificationVisibility.NotificationLocation.LOCATION_UNKNOWN);
+ mLogger.onExpansionChanged(NOTIFICATION_KEY, false, true,
+ NotificationVisibility.NotificationLocation.LOCATION_UNKNOWN);
waitForUiOffloadThread();
verify(mBarService).onNotificationExpansionChanged(
- NOTIFICATION_KEY, false, true, ExpandableViewState.LOCATION_UNKNOWN);
+ NOTIFICATION_KEY, false, true,
+ NotificationVisibility.NotificationLocation.LOCATION_UNKNOWN.toMetricsEventEnum());
}
private NotificationVisibility createNotificationVisibility(String key, boolean visibility) {
- return NotificationVisibility.obtain(key, 0, 0, visibility);
+ return createNotificationVisibility(key, visibility,
+ NotificationVisibility.NotificationLocation.LOCATION_UNKNOWN);
+ }
+
+ private NotificationVisibility createNotificationVisibility(String key, boolean visibility,
+ NotificationVisibility.NotificationLocation location) {
+ return NotificationVisibility.obtain(key, 0, 0, visibility, location);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
index 0899c73ab07b..fdc9e0c4d7e4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
@@ -45,6 +45,7 @@ import android.app.Notification;
import android.app.NotificationChannel;
import android.content.Intent;
import android.content.pm.PackageManager;
+import android.metrics.LogMaker;
import android.os.Binder;
import android.os.Handler;
import android.provider.Settings;
@@ -55,6 +56,8 @@ import android.testing.TestableLooper;
import android.util.ArraySet;
import android.view.View;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.statusbar.NotificationPresenter;
@@ -92,6 +95,7 @@ public class NotificationGutsManagerTest extends SysuiTestCase {
private NotificationGutsManager mGutsManager;
@Rule public MockitoRule mockito = MockitoJUnit.rule();
+ @Mock private MetricsLogger mMetricsLogger;
@Mock private NotificationPresenter mPresenter;
@Mock private NotificationActivityStarter mNotificationActivityStarter;
@Mock private NotificationStackScrollLayout mStackScroller;
@@ -105,6 +109,7 @@ public class NotificationGutsManagerTest extends SysuiTestCase {
Assert.sMainLooper = TestableLooper.get(this).getLooper();
mDependency.injectTestDependency(DeviceProvisionedController.class,
mDeviceProvisionedController);
+ mDependency.injectTestDependency(MetricsLogger.class, mMetricsLogger);
mHandler = Handler.createAsync(mTestableLooper.getLooper());
mHelper = new NotificationTestHelper(mContext);
@@ -141,7 +146,7 @@ public class NotificationGutsManagerTest extends SysuiTestCase {
when(row.getWindowToken()).thenReturn(new Binder());
when(row.getGuts()).thenReturn(guts);
- mGutsManager.openGuts(row, 0, 0, menuItem);
+ assertTrue(mGutsManager.openGuts(row, 0, 0, menuItem));
assertEquals(View.INVISIBLE, guts.getVisibility());
mTestableLooper.processAllMessages();
verify(guts).openControls(
@@ -189,7 +194,7 @@ public class NotificationGutsManagerTest extends SysuiTestCase {
when(entry.getRow()).thenReturn(row);
when(entry.getGuts()).thenReturn(guts);
- mGutsManager.openGuts(row, 0, 0, menuItem);
+ assertTrue(mGutsManager.openGuts(row, 0, 0, menuItem));
mTestableLooper.processAllMessages();
verify(guts).openControls(
eq(true),
@@ -215,6 +220,34 @@ public class NotificationGutsManagerTest extends SysuiTestCase {
}
@Test
+ public void testOpenGutsLogging() {
+ NotificationGutsManager gutsManager = spy(mGutsManager);
+ doReturn(true).when(gutsManager).bindGuts(any(), any());
+
+ NotificationGuts guts = spy(new NotificationGuts(mContext));
+ doReturn(true).when(guts).post(any());
+
+ ExpandableNotificationRow realRow = createTestNotificationRow();
+ NotificationMenuRowPlugin.MenuItem menuItem = createTestMenuItem(realRow);
+
+ ExpandableNotificationRow row = spy(realRow);
+ when(row.getWindowToken()).thenReturn(new Binder());
+ when(row.getGuts()).thenReturn(guts);
+ StatusBarNotification notification = spy(realRow.getStatusBarNotification());
+ when(row.getStatusBarNotification()).thenReturn(notification);
+
+ assertTrue(gutsManager.openGuts(row, 0, 0, menuItem));
+
+ ArgumentCaptor<LogMaker> logMakerCaptor = ArgumentCaptor.forClass(LogMaker.class);
+ verify(notification).getLogMaker();
+ verify(mMetricsLogger).write(logMakerCaptor.capture());
+ assertEquals(MetricsProto.MetricsEvent.ACTION_NOTE_CONTROLS,
+ logMakerCaptor.getValue().getCategory());
+ assertEquals(MetricsProto.MetricsEvent.TYPE_ACTION,
+ logMakerCaptor.getValue().getType());
+ }
+
+ @Test
public void testAppOpsSettingsIntent_camera() {
ArraySet<Integer> ops = new ArraySet<>();
ops.add(OP_CAMERA);
@@ -321,7 +354,8 @@ public class NotificationGutsManagerTest extends SysuiTestCase {
eq(false),
eq(true) /* isForBlockingHelper */,
eq(true) /* isUserSentimentNegative */,
- eq(0));
+ eq(0),
+ eq(false) /* wasShownHighPriority */);
}
@Test
@@ -349,16 +383,18 @@ public class NotificationGutsManagerTest extends SysuiTestCase {
eq(false),
eq(false) /* isForBlockingHelper */,
eq(true) /* isUserSentimentNegative */,
- eq(0));
+ eq(0),
+ eq(false) /* wasShownHighPriority */);
}
@Test
- public void testInitializeNotificationInfoView_importance() throws Exception {
+ public void testInitializeNotificationInfoView_highPriority() throws Exception {
NotificationInfo notificationInfoView = mock(NotificationInfo.class);
ExpandableNotificationRow row = spy(mHelper.createRow());
row.setBlockingHelperShowing(true);
row.getEntry().userSentiment = USER_SENTIMENT_NEGATIVE;
row.getEntry().importance = IMPORTANCE_DEFAULT;
+ row.getEntry().setIsHighPriority(true);
when(row.getIsNonblockable()).thenReturn(false);
StatusBarNotification statusBarNotification = row.getStatusBarNotification();
@@ -378,7 +414,8 @@ public class NotificationGutsManagerTest extends SysuiTestCase {
eq(false),
eq(true) /* isForBlockingHelper */,
eq(true) /* isUserSentimentNegative */,
- eq(IMPORTANCE_DEFAULT));
+ eq(IMPORTANCE_DEFAULT),
+ eq(true) /* wasShownHighPriority */);
}
@Test
@@ -407,7 +444,8 @@ public class NotificationGutsManagerTest extends SysuiTestCase {
eq(false),
eq(false) /* isForBlockingHelper */,
eq(true) /* isUserSentimentNegative */,
- eq(0));
+ eq(0),
+ eq(false) /* wasShownHighPriority */);
}
@Test
@@ -435,7 +473,8 @@ public class NotificationGutsManagerTest extends SysuiTestCase {
eq(false),
eq(true) /* isForBlockingHelper */,
eq(true) /* isUserSentimentNegative */,
- eq(0));
+ eq(0),
+ eq(false) /* wasShownHighPriority */);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
index f6791dd5778c..08955e3c4d5a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
@@ -210,7 +210,7 @@ public class NotificationInfoTest extends SysuiTestCase {
when(mMockPackageManager.getApplicationLabel(any())).thenReturn("App Name");
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- IMPORTANCE_DEFAULT);
+ IMPORTANCE_DEFAULT, true);
final TextView textView = mNotificationInfo.findViewById(R.id.pkgname);
assertTrue(textView.getText().toString().contains("App Name"));
assertEquals(VISIBLE, mNotificationInfo.findViewById(R.id.header).getVisibility());
@@ -223,7 +223,7 @@ public class NotificationInfoTest extends SysuiTestCase {
.thenReturn(iconDrawable);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- IMPORTANCE_DEFAULT);
+ IMPORTANCE_DEFAULT, true);
final ImageView iconView = mNotificationInfo.findViewById(R.id.pkgicon);
assertEquals(iconDrawable, iconView.getDrawable());
}
@@ -232,7 +232,7 @@ public class NotificationInfoTest extends SysuiTestCase {
public void testBindNotification_noDelegate() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- IMPORTANCE_DEFAULT);
+ IMPORTANCE_DEFAULT, true);
final TextView nameView = mNotificationInfo.findViewById(R.id.delegate_name);
assertEquals(GONE, nameView.getVisibility());
final TextView dividerView = mNotificationInfo.findViewById(R.id.pkg_divider);
@@ -251,7 +251,7 @@ public class NotificationInfoTest extends SysuiTestCase {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- IMPORTANCE_DEFAULT);
+ IMPORTANCE_DEFAULT, true);
final TextView nameView = mNotificationInfo.findViewById(R.id.delegate_name);
assertEquals(VISIBLE, nameView.getVisibility());
assertTrue(nameView.getText().toString().contains("Other"));
@@ -263,7 +263,7 @@ public class NotificationInfoTest extends SysuiTestCase {
public void testBindNotification_GroupNameHiddenIfNoGroup() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- IMPORTANCE_DEFAULT);
+ IMPORTANCE_DEFAULT, true);
final TextView groupNameView = mNotificationInfo.findViewById(R.id.group_name);
assertEquals(GONE, groupNameView.getVisibility());
final TextView groupDividerView = mNotificationInfo.findViewById(R.id.pkg_group_divider);
@@ -280,7 +280,7 @@ public class NotificationInfoTest extends SysuiTestCase {
.thenReturn(notificationChannelGroup);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- IMPORTANCE_DEFAULT);
+ IMPORTANCE_DEFAULT, true);
final TextView groupNameView = mNotificationInfo.findViewById(R.id.group_name);
assertEquals(View.VISIBLE, groupNameView.getVisibility());
assertEquals("Test Group Name", groupNameView.getText());
@@ -292,7 +292,7 @@ public class NotificationInfoTest extends SysuiTestCase {
public void testBindNotification_SetsTextChannelName() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- IMPORTANCE_DEFAULT);
+ IMPORTANCE_DEFAULT, true);
final TextView textView = mNotificationInfo.findViewById(R.id.channel_name);
assertEquals(TEST_CHANNEL_NAME, textView.getText());
}
@@ -301,7 +301,7 @@ public class NotificationInfoTest extends SysuiTestCase {
public void testBindNotification_DefaultChannelDoesNotUseChannelName() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mDefaultNotificationChannel, 1, mSbn, null, null, null, true,
- false, IMPORTANCE_DEFAULT);
+ false, IMPORTANCE_DEFAULT, true);
final TextView textView = mNotificationInfo.findViewById(R.id.channel_name);
assertEquals(GONE, textView.getVisibility());
}
@@ -314,7 +314,7 @@ public class NotificationInfoTest extends SysuiTestCase {
eq(TEST_PACKAGE_NAME), eq(TEST_UID), anyBoolean())).thenReturn(10);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mDefaultNotificationChannel, 1, mSbn, null, null, null, true,
- false, IMPORTANCE_DEFAULT);
+ false, IMPORTANCE_DEFAULT, true);
final TextView textView = mNotificationInfo.findViewById(R.id.channel_name);
assertEquals(VISIBLE, textView.getVisibility());
}
@@ -323,7 +323,7 @@ public class NotificationInfoTest extends SysuiTestCase {
public void testBindNotification_UnblockablePackageUsesChannelName() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true,
- IMPORTANCE_DEFAULT);
+ IMPORTANCE_DEFAULT, true);
final TextView textView = mNotificationInfo.findViewById(R.id.channel_name);
assertEquals(VISIBLE, textView.getVisibility());
}
@@ -332,7 +332,7 @@ public class NotificationInfoTest extends SysuiTestCase {
public void testBindNotification_BlockButton() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- IMPORTANCE_DEFAULT);
+ IMPORTANCE_DEFAULT, true);
final View block = mNotificationInfo.findViewById(R.id.int_block);
final View minimize = mNotificationInfo.findViewById(R.id.block_or_minimize);
assertEquals(VISIBLE, block.getVisibility());
@@ -343,7 +343,7 @@ public class NotificationInfoTest extends SysuiTestCase {
public void testBindNotification_BlockButton_BlockHelper() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- true /* isBlockingHelper */, false, IMPORTANCE_DEFAULT);
+ true /* isBlockingHelper */, false, IMPORTANCE_DEFAULT, true);
final View block = mNotificationInfo.findViewById(R.id.block);
final View interruptivenessSettings = mNotificationInfo.findViewById(
R.id.interruptiveness_settings);
@@ -356,7 +356,7 @@ public class NotificationInfoTest extends SysuiTestCase {
mNotificationChannel.setImportance(IMPORTANCE_DEFAULT);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- IMPORTANCE_DEFAULT);
+ IMPORTANCE_DEFAULT, true);
final TextView silent = mNotificationInfo.findViewById(R.id.int_silent);
assertEquals(VISIBLE, silent.getVisibility());
assertEquals(
@@ -368,7 +368,7 @@ public class NotificationInfoTest extends SysuiTestCase {
mNotificationChannel.setImportance(IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- IMPORTANCE_LOW);
+ IMPORTANCE_LOW, false);
final TextView silent = mNotificationInfo.findViewById(R.id.int_silent);
assertEquals(VISIBLE, silent.getVisibility());
assertEquals(
@@ -381,7 +381,7 @@ public class NotificationInfoTest extends SysuiTestCase {
mNotificationChannel.setImportance(IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- IMPORTANCE_LOW);
+ IMPORTANCE_LOW, false);
final TextView alert = mNotificationInfo.findViewById(R.id.int_alert);
assertEquals(VISIBLE, alert.getVisibility());
assertEquals(
@@ -393,7 +393,7 @@ public class NotificationInfoTest extends SysuiTestCase {
mNotificationChannel.setImportance(IMPORTANCE_DEFAULT);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- IMPORTANCE_DEFAULT);
+ IMPORTANCE_DEFAULT, true);
final TextView alert = mNotificationInfo.findViewById(R.id.int_alert);
assertEquals(VISIBLE, alert.getVisibility());
assertEquals(
@@ -405,7 +405,7 @@ public class NotificationInfoTest extends SysuiTestCase {
mNotificationChannel.setImportance(IMPORTANCE_UNSPECIFIED);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- IMPORTANCE_DEFAULT);
+ IMPORTANCE_DEFAULT, true);
final TextView silent = mNotificationInfo.findViewById(R.id.int_silent);
final TextView alert = mNotificationInfo.findViewById(R.id.int_alert);
assertEquals(VISIBLE, silent.getVisibility());
@@ -421,7 +421,7 @@ public class NotificationInfoTest extends SysuiTestCase {
mNotificationChannel.setImportance(IMPORTANCE_UNSPECIFIED);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- IMPORTANCE_LOW);
+ IMPORTANCE_LOW, false);
final TextView silent = mNotificationInfo.findViewById(R.id.int_silent);
final TextView alert = mNotificationInfo.findViewById(R.id.int_alert);
assertEquals(VISIBLE, silent.getVisibility());
@@ -437,7 +437,7 @@ public class NotificationInfoTest extends SysuiTestCase {
mSbn.getNotification().flags = Notification.FLAG_FOREGROUND_SERVICE;
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- IMPORTANCE_DEFAULT);
+ IMPORTANCE_DEFAULT, true);
final View block = mNotificationInfo.findViewById(R.id.block);
final View interruptivenessSettings = mNotificationInfo.findViewById(
R.id.interruptiveness_settings);
@@ -455,7 +455,7 @@ public class NotificationInfoTest extends SysuiTestCase {
(View v, NotificationChannel c, int appUid) -> {
assertEquals(mNotificationChannel, c);
latch.countDown();
- }, null, true, false, IMPORTANCE_DEFAULT);
+ }, null, true, false, IMPORTANCE_DEFAULT, true);
final View settingsButton = mNotificationInfo.findViewById(R.id.info);
settingsButton.performClick();
@@ -467,7 +467,7 @@ public class NotificationInfoTest extends SysuiTestCase {
public void testBindNotification_SettingsButtonInvisibleWhenNoClickListener() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- IMPORTANCE_DEFAULT);
+ IMPORTANCE_DEFAULT, true);
final View settingsButton = mNotificationInfo.findViewById(R.id.info);
assertTrue(settingsButton.getVisibility() != View.VISIBLE);
}
@@ -479,7 +479,7 @@ public class NotificationInfoTest extends SysuiTestCase {
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null,
(View v, NotificationChannel c, int appUid) -> {
assertEquals(mNotificationChannel, c);
- }, null, false, false, IMPORTANCE_DEFAULT);
+ }, null, false, false, IMPORTANCE_DEFAULT, true);
final View settingsButton = mNotificationInfo.findViewById(R.id.info);
assertTrue(settingsButton.getVisibility() != View.VISIBLE);
}
@@ -488,11 +488,11 @@ public class NotificationInfoTest extends SysuiTestCase {
public void testBindNotification_SettingsButtonReappearsAfterSecondBind() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- IMPORTANCE_DEFAULT);
+ IMPORTANCE_DEFAULT, true);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null,
(View v, NotificationChannel c, int appUid) -> {
- }, null, true, false, IMPORTANCE_DEFAULT);
+ }, null, true, false, IMPORTANCE_DEFAULT, true);
final View settingsButton = mNotificationInfo.findViewById(R.id.info);
assertEquals(View.VISIBLE, settingsButton.getVisibility());
}
@@ -501,7 +501,7 @@ public class NotificationInfoTest extends SysuiTestCase {
public void testLogBlockingHelperCounter_logGutsViewDisplayed() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- IMPORTANCE_DEFAULT);
+ IMPORTANCE_DEFAULT, true);
mNotificationInfo.logBlockingHelperCounter("HowCanNotifsBeRealIfAppsArent");
verify(mMetricsLogger).write(argThat(logMaker ->
logMaker.getType() == MetricsEvent.NOTIFICATION_BLOCKING_HELPER
@@ -513,7 +513,7 @@ public class NotificationInfoTest extends SysuiTestCase {
public void testLogBlockingHelperCounter_logsForBlockingHelper() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false, true,
- true, true, IMPORTANCE_DEFAULT);
+ true, true, IMPORTANCE_DEFAULT, true);
mNotificationInfo.logBlockingHelperCounter("HowCanNotifsBeRealIfAppsArent");
verify(mMetricsLogger).count(eq("HowCanNotifsBeRealIfAppsArent"), eq(1));
}
@@ -526,7 +526,7 @@ public class NotificationInfoTest extends SysuiTestCase {
(View v, NotificationChannel c, int appUid) -> {
assertEquals(null, c);
latch.countDown();
- }, null, true, true, IMPORTANCE_DEFAULT);
+ }, null, true, true, IMPORTANCE_DEFAULT, true);
mNotificationInfo.findViewById(R.id.info).performClick();
// Verify that listener was triggered.
@@ -539,7 +539,7 @@ public class NotificationInfoTest extends SysuiTestCase {
throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, MULTIPLE_CHANNEL_COUNT, mSbn, null, null,
- null, true, true, IMPORTANCE_DEFAULT);
+ null, true, true, IMPORTANCE_DEFAULT, true);
final TextView channelNameView =
mNotificationInfo.findViewById(R.id.channel_name);
assertEquals(GONE, channelNameView.getVisibility());
@@ -550,7 +550,7 @@ public class NotificationInfoTest extends SysuiTestCase {
public void testStopInvisibleIfBundleFromDifferentChannels() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, MULTIPLE_CHANNEL_COUNT, mSbn, null, null,
- null, true, true, IMPORTANCE_DEFAULT);
+ null, true, true, IMPORTANCE_DEFAULT, true);
final TextView blockView = mNotificationInfo.findViewById(R.id.block);
assertEquals(GONE, blockView.getVisibility());
}
@@ -559,7 +559,7 @@ public class NotificationInfoTest extends SysuiTestCase {
public void testbindNotification_BlockingHelper() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false, false,
- true, true, IMPORTANCE_DEFAULT);
+ true, true, IMPORTANCE_DEFAULT, true);
final TextView view = mNotificationInfo.findViewById(R.id.block_prompt);
assertEquals(View.VISIBLE, view.getVisibility());
assertEquals(mContext.getString(R.string.inline_blocking_helper), view.getText());
@@ -569,7 +569,7 @@ public class NotificationInfoTest extends SysuiTestCase {
public void testbindNotification_UnblockableTextVisibleWhenAppUnblockable() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true,
- IMPORTANCE_DEFAULT);
+ IMPORTANCE_DEFAULT, true);
final TextView view = mNotificationInfo.findViewById(R.id.block_prompt);
assertEquals(View.VISIBLE, view.getVisibility());
assertEquals(mContext.getString(R.string.notification_unblockable_desc),
@@ -580,7 +580,7 @@ public class NotificationInfoTest extends SysuiTestCase {
public void testBindNotification_DoesNotUpdateNotificationChannel() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- IMPORTANCE_DEFAULT);
+ IMPORTANCE_DEFAULT, true);
mTestableLooper.processAllMessages();
verify(mMockINotificationManager, never()).updateNotificationChannelForPackage(
anyString(), eq(TEST_UID), any());
@@ -591,7 +591,7 @@ public class NotificationInfoTest extends SysuiTestCase {
mNotificationChannel.setImportance(IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- IMPORTANCE_DEFAULT);
+ IMPORTANCE_DEFAULT, false);
mNotificationInfo.findViewById(R.id.int_block).performClick();
mTestableLooper.processAllMessages();
@@ -605,7 +605,7 @@ public class NotificationInfoTest extends SysuiTestCase {
mNotificationChannel.setImportance(IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- IMPORTANCE_DEFAULT);
+ IMPORTANCE_DEFAULT, false);
mNotificationInfo.findViewById(R.id.minimize).performClick();
mTestableLooper.processAllMessages();
@@ -619,7 +619,7 @@ public class NotificationInfoTest extends SysuiTestCase {
mNotificationChannel.setImportance(IMPORTANCE_DEFAULT);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- IMPORTANCE_DEFAULT);
+ IMPORTANCE_DEFAULT, true);
mNotificationInfo.findViewById(R.id.int_silent).performClick();
mTestableLooper.processAllMessages();
@@ -633,7 +633,7 @@ public class NotificationInfoTest extends SysuiTestCase {
mNotificationChannel.setImportance(IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- IMPORTANCE_DEFAULT);
+ IMPORTANCE_DEFAULT, false);
mNotificationInfo.findViewById(R.id.int_alert).performClick();
mTestableLooper.processAllMessages();
@@ -647,7 +647,7 @@ public class NotificationInfoTest extends SysuiTestCase {
int originalImportance = mNotificationChannel.getImportance();
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- IMPORTANCE_DEFAULT);
+ IMPORTANCE_DEFAULT, true);
mNotificationInfo.handleCloseControls(true, false);
mTestableLooper.processAllMessages();
@@ -662,7 +662,7 @@ public class NotificationInfoTest extends SysuiTestCase {
mNotificationChannel.setImportance(IMPORTANCE_UNSPECIFIED);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- IMPORTANCE_DEFAULT);
+ IMPORTANCE_DEFAULT, true);
mNotificationInfo.handleCloseControls(true, false);
@@ -680,7 +680,7 @@ public class NotificationInfoTest extends SysuiTestCase {
TEST_PACKAGE_NAME, mNotificationChannel /* notificationChannel */,
10 /* numUniqueChannelsInRow */, mSbn, null /* checkSaveListener */,
null /* onSettingsClick */, null /* onAppSettingsClick */ ,
- true, false /* isNonblockable */, IMPORTANCE_DEFAULT
+ true, false /* isNonblockable */, IMPORTANCE_DEFAULT, false
);
mNotificationInfo.findViewById(R.id.int_block).performClick();
@@ -702,7 +702,7 @@ public class NotificationInfoTest extends SysuiTestCase {
TEST_PACKAGE_NAME, mNotificationChannel /* notificationChannel */,
10 /* numUniqueChannelsInRow */, mSbn, null /* checkSaveListener */,
null /* onSettingsClick */, null /* onAppSettingsClick */,
- true, false /* isNonblockable */, IMPORTANCE_DEFAULT
+ true, false /* isNonblockable */, IMPORTANCE_DEFAULT, false
);
mNotificationInfo.findViewById(R.id.int_block).performClick();
@@ -724,7 +724,7 @@ public class NotificationInfoTest extends SysuiTestCase {
null /* onSettingsClick */, null /* onAppSettingsClick */ ,
true /* provisioned */,
false /* isNonblockable */, true /* isForBlockingHelper */,
- true /* isUserSentimentNegative */, IMPORTANCE_DEFAULT);
+ true /* isUserSentimentNegative */, IMPORTANCE_DEFAULT, true);
NotificationGuts guts = spy(new NotificationGuts(mContext, null));
when(guts.getWindowToken()).thenReturn(mock(IBinder.class));
@@ -752,7 +752,7 @@ public class NotificationInfoTest extends SysuiTestCase {
10 /* numUniqueChannelsInRow */, mSbn, listener /* checkSaveListener */,
null /* onSettingsClick */, null /* onAppSettingsClick */ , true /* provisioned */,
false /* isNonblockable */, true /* isForBlockingHelper */,
- true /* isUserSentimentNegative */, IMPORTANCE_DEFAULT);
+ true /* isUserSentimentNegative */, IMPORTANCE_DEFAULT, true);
NotificationGuts guts = spy(new NotificationGuts(mContext, null));
when(guts.getWindowToken()).thenReturn(mock(IBinder.class));
@@ -781,7 +781,7 @@ public class NotificationInfoTest extends SysuiTestCase {
null /* onSettingsClick */, null /* onAppSettingsClick */ ,
false /* isNonblockable */, true /* isForBlockingHelper */,
true, true /* isUserSentimentNegative */, /* isNoisy */
- IMPORTANCE_DEFAULT);
+ IMPORTANCE_DEFAULT, true);
mNotificationInfo.handleCloseControls(true /* save */, false /* force */);
@@ -800,7 +800,7 @@ public class NotificationInfoTest extends SysuiTestCase {
null /* onSettingsClick */, null /* onAppSettingsClick */,
true /* provisioned */,
false /* isNonblockable */, true /* isForBlockingHelper */,
- true /* isUserSentimentNegative */, IMPORTANCE_DEFAULT);
+ true /* isUserSentimentNegative */, IMPORTANCE_DEFAULT, true);
mNotificationInfo.findViewById(R.id.block).performClick();
mTestableLooper.processAllMessages();
@@ -823,7 +823,7 @@ public class NotificationInfoTest extends SysuiTestCase {
true /* isForBlockingHelper */,
true,
false /* isUserSentimentNegative */,
- IMPORTANCE_DEFAULT);
+ IMPORTANCE_DEFAULT, true);
NotificationGuts guts = mock(NotificationGuts.class);
doCallRealMethod().when(guts).closeControls(anyInt(), anyInt(), anyBoolean(), anyBoolean());
mNotificationInfo.setGutsParent(guts);
@@ -838,7 +838,7 @@ public class NotificationInfoTest extends SysuiTestCase {
mNotificationChannel.setImportance(IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true,
- IMPORTANCE_DEFAULT);
+ IMPORTANCE_DEFAULT, false);
mNotificationInfo.findViewById(R.id.block).performClick();
waitForUndoButton();
@@ -852,7 +852,7 @@ public class NotificationInfoTest extends SysuiTestCase {
mNotificationChannel.setImportance(IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- IMPORTANCE_DEFAULT);
+ IMPORTANCE_DEFAULT, false);
mNotificationInfo.findViewById(R.id.int_block).performClick();
waitForUndoButton();
@@ -888,7 +888,8 @@ public class NotificationInfoTest extends SysuiTestCase {
false /* isNonblockable */,
true /* isForBlockingHelper */,
true /* isUserSentimentNegative */,
- IMPORTANCE_DEFAULT);
+ IMPORTANCE_DEFAULT,
+ false);
mNotificationInfo.findViewById(R.id.block).performClick();
waitForUndoButton();
@@ -913,7 +914,7 @@ public class NotificationInfoTest extends SysuiTestCase {
mNotificationChannel.setImportance(IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true,
- IMPORTANCE_DEFAULT);
+ IMPORTANCE_DEFAULT, false);
mNotificationInfo.findViewById(R.id.minimize).performClick();
waitForUndoButton();
@@ -928,7 +929,7 @@ public class NotificationInfoTest extends SysuiTestCase {
mSbn.getNotification().flags = Notification.FLAG_FOREGROUND_SERVICE;
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- IMPORTANCE_DEFAULT);
+ IMPORTANCE_DEFAULT, false);
mNotificationInfo.findViewById(R.id.minimize).performClick();
waitForUndoButton();
@@ -949,7 +950,7 @@ public class NotificationInfoTest extends SysuiTestCase {
mNotificationChannel.setImportance(IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- IMPORTANCE_DEFAULT);
+ IMPORTANCE_DEFAULT, false);
mNotificationInfo.handleCloseControls(true, false);
@@ -967,7 +968,7 @@ public class NotificationInfoTest extends SysuiTestCase {
mNotificationChannel.setImportance(IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- IMPORTANCE_DEFAULT);
+ IMPORTANCE_DEFAULT, false);
mNotificationInfo.findViewById(R.id.int_block).performClick();
waitForUndoButton();
@@ -988,7 +989,7 @@ public class NotificationInfoTest extends SysuiTestCase {
mNotificationChannel.setImportance(IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true,
- IMPORTANCE_DEFAULT);
+ IMPORTANCE_DEFAULT, false);
mNotificationInfo.findViewById(R.id.minimize).performClick();
waitForUndoButton();
@@ -1006,7 +1007,7 @@ public class NotificationInfoTest extends SysuiTestCase {
mNotificationChannel.setImportance(IMPORTANCE_DEFAULT);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- IMPORTANCE_DEFAULT);
+ IMPORTANCE_DEFAULT, true);
mNotificationInfo.findViewById(R.id.int_silent).performClick();
waitForUndoButton();
@@ -1027,7 +1028,7 @@ public class NotificationInfoTest extends SysuiTestCase {
mNotificationChannel.setImportance(IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- IMPORTANCE_DEFAULT);
+ IMPORTANCE_DEFAULT, false);
mNotificationInfo.findViewById(R.id.int_alert).performClick();
waitForUndoButton();
@@ -1049,7 +1050,7 @@ public class NotificationInfoTest extends SysuiTestCase {
mNotificationChannel.setImportance(IMPORTANCE_UNSPECIFIED);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- IMPORTANCE_DEFAULT);
+ IMPORTANCE_DEFAULT, true);
mNotificationInfo.findViewById(R.id.int_silent).performClick();
waitForUndoButton();
@@ -1071,7 +1072,7 @@ public class NotificationInfoTest extends SysuiTestCase {
mNotificationChannel.setImportance(IMPORTANCE_UNSPECIFIED);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- IMPORTANCE_LOW);
+ IMPORTANCE_LOW, false);
mNotificationInfo.findViewById(R.id.int_alert).performClick();
waitForUndoButton();
@@ -1092,7 +1093,7 @@ public class NotificationInfoTest extends SysuiTestCase {
mNotificationChannel.setImportance(IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true,
- IMPORTANCE_DEFAULT);
+ IMPORTANCE_DEFAULT, false);
mNotificationInfo.findViewById(R.id.minimize).performClick();
waitForUndoButton();
@@ -1108,7 +1109,7 @@ public class NotificationInfoTest extends SysuiTestCase {
mNotificationChannel.setImportance(IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- IMPORTANCE_DEFAULT);
+ IMPORTANCE_DEFAULT, false);
mNotificationInfo.findViewById(R.id.int_block).performClick();
waitForUndoButton();
@@ -1125,7 +1126,7 @@ public class NotificationInfoTest extends SysuiTestCase {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn,
(Runnable saveImportance, StatusBarNotification sbn) -> {
- }, null, null, true, true, IMPORTANCE_DEFAULT);
+ }, null, null, true, true, IMPORTANCE_DEFAULT, false);
mNotificationInfo.findViewById(R.id.int_block).performClick();
mTestableLooper.processAllMessages();
@@ -1143,7 +1144,7 @@ public class NotificationInfoTest extends SysuiTestCase {
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn,
(Runnable saveImportance, StatusBarNotification sbn) -> {
saveImportance.run();
- }, null, null, true, false, IMPORTANCE_DEFAULT
+ }, null, null, true, false, IMPORTANCE_DEFAULT, false
);
mNotificationInfo.findViewById(R.id.int_block).performClick();
@@ -1170,7 +1171,7 @@ public class NotificationInfoTest extends SysuiTestCase {
mNotificationChannel.setImportance(IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true,
- IMPORTANCE_DEFAULT);
+ IMPORTANCE_DEFAULT, false);
mNotificationInfo.findViewById(R.id.minimize).performClick();
waitForUndoButton();
@@ -1183,7 +1184,7 @@ public class NotificationInfoTest extends SysuiTestCase {
mNotificationChannel.setImportance(IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- IMPORTANCE_DEFAULT);
+ IMPORTANCE_DEFAULT, false);
mNotificationInfo.findViewById(R.id.int_block).performClick();
waitForUndoButton();
@@ -1196,7 +1197,7 @@ public class NotificationInfoTest extends SysuiTestCase {
mNotificationChannel.setImportance(IMPORTANCE_DEFAULT);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- IMPORTANCE_DEFAULT);
+ IMPORTANCE_DEFAULT, true);
mNotificationInfo.findViewById(R.id.int_silent).performClick();
waitForUndoButton();
@@ -1210,7 +1211,7 @@ public class NotificationInfoTest extends SysuiTestCase {
mNotificationChannel.setImportance(IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- IMPORTANCE_DEFAULT);
+ IMPORTANCE_DEFAULT, false);
mNotificationInfo.findViewById(R.id.int_alert).performClick();
waitForUndoButton();
@@ -1224,7 +1225,7 @@ public class NotificationInfoTest extends SysuiTestCase {
mNotificationChannel.setImportance(IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- IMPORTANCE_DEFAULT);
+ IMPORTANCE_DEFAULT, false);
mNotificationInfo.findViewById(R.id.int_block).performClick();
waitForUndoButton();
@@ -1236,7 +1237,7 @@ public class NotificationInfoTest extends SysuiTestCase {
mNotificationChannel.setImportance(IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- IMPORTANCE_DEFAULT);
+ IMPORTANCE_DEFAULT, false);
mNotificationInfo.findViewById(R.id.int_block).performClick();
waitForUndoButton();
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 736f3840b91a..ae70b01cd35c 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
@@ -352,7 +352,7 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
RETURNS_DEEP_STUBS);
String key = Integer.toString(i);
when(row.getStatusBarNotification().getKey()).thenReturn(key);
- when(mNotificationData.isHighPriority(row.getStatusBarNotification())).thenReturn(true);
+ when(row.getEntry().isHighPriority()).thenReturn(true);
when(mStackScroller.getChildAt(i)).thenReturn(row);
}
@@ -368,8 +368,7 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
RETURNS_DEEP_STUBS);
String key = Integer.toString(i);
when(row.getStatusBarNotification().getKey()).thenReturn(key);
- when(mNotificationData.isHighPriority(row.getStatusBarNotification()))
- .thenReturn(false);
+ when(row.getEntry().isHighPriority()).thenReturn(false);
when(mStackScroller.getChildAt(i)).thenReturn(row);
}
@@ -385,8 +384,7 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
RETURNS_DEEP_STUBS);
String key = Integer.toString(i);
when(row.getStatusBarNotification().getKey()).thenReturn(key);
- when(mNotificationData.isHighPriority(row.getStatusBarNotification()))
- .thenReturn(i < 3);
+ when(row.getEntry().isHighPriority()).thenReturn(i < 3);
when(mStackScroller.getChildAt(i)).thenReturn(row);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
index c0f7f0ce217f..1ded835e9651 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
@@ -85,7 +85,7 @@ public class AutoTileManagerTest extends SysuiTestCase {
return;
}
mAutoTileManager.mColorDisplayCallback.onAutoModeChanged(
- ColorDisplayController.AUTO_MODE_TWILIGHT);
+ ColorDisplayManager.AUTO_MODE_TWILIGHT);
verify(mQsTileHost).addTile("night");
}
@@ -95,7 +95,7 @@ public class AutoTileManagerTest extends SysuiTestCase {
return;
}
mAutoTileManager.mColorDisplayCallback.onAutoModeChanged(
- ColorDisplayController.AUTO_MODE_CUSTOM);
+ ColorDisplayManager.AUTO_MODE_CUSTOM_TIME);
verify(mQsTileHost).addTile("night");
}
@@ -105,7 +105,7 @@ public class AutoTileManagerTest extends SysuiTestCase {
return;
}
mAutoTileManager.mColorDisplayCallback.onAutoModeChanged(
- ColorDisplayController.AUTO_MODE_DISABLED);
+ ColorDisplayManager.AUTO_MODE_DISABLED);
verify(mQsTileHost, never()).addTile("night");
}
}
diff --git a/proto/src/metrics_constants/metrics_constants.proto b/proto/src/metrics_constants/metrics_constants.proto
index efa4e79cc318..73fcb0150a9e 100644
--- a/proto/src/metrics_constants/metrics_constants.proto
+++ b/proto/src/metrics_constants/metrics_constants.proto
@@ -6780,22 +6780,22 @@ message MetricsEvent {
CONVERSATION_ACTIONS = 1615;
// ACTION: Actions from a text classifier are shown to user.
- // CATEGORY: CONVERSATION_ACTIONS
+ // CATEGORY: LANGUAGE_DETECTION, CONVERSATION_ACTIONS
// OS: Q
ACTION_TEXT_CLASSIFIER_ACTIONS_SHOWN = 1616;
- // ACTION: Event time of a text classifier event in unix timestamp.
- // CATEGORY: CONVERSATION_ACTIONS, LANGUAGE_DETECTION
+ // FIELD: Event time of a text classifier event in unix timestamp.
+ // CATEGORY: LANGUAGE_DETECTION, CONVERSATION_ACTIONS
// OS: Q
FIELD_TEXT_CLASSIFIER_EVENT_TIME = 1617;
// ACTION: Users compose their own replies instead of using suggested ones.
- // CATEGORY: CONVERSATION_ACTIONS
+ // CATEGORY: LANGUAGE_DETECTION, CONVERSATION_ACTIONS
// OS: Q
ACTION_TEXT_CLASSIFIER_MANUAL_REPLY = 1618;
// ACTION: Text classifier generates an action.
- // CATEGORY: CONVERSATION_ACTIONS
+ // CATEGORY: LANGUAGE_DETECTION, CONVERSATION_ACTIONS
// OS: Q
ACTION_TEXT_CLASSIFIER_ACTIONS_GENERATED = 1619;
@@ -6840,7 +6840,7 @@ message MetricsEvent {
// OPEN: Settings > Display > Adaptive sleep
// OS: Q
SETTINGS_ADAPTIVE_SLEEP = 1628;
-
+
// Tagged data for SMART_REPLY_VISIBLE and NOTIFICATION_ITEM_ACTION.
// The UI location of the notification containing the smart suggestions.
// This is a NotificationLocation object (see the NotificationLocation
@@ -6862,6 +6862,48 @@ message MetricsEvent {
// OS: Q
FIELD_AUTOFILL_NUMBER_AUGMENTED_REQUESTS = 1631;
+ // OPEN: Settings > System > Aware
+ // OS: Q
+ SETTINGS_AWARE = 1632;
+
+ // OPEN: Settings > System > Aware > Disable > Dialog
+ // OS: Q
+ DIALOG_AWARE_DISABLE = 1633;
+
+ // FIELD: Session ID of TextClassifierEvent.
+ // CATEGORY: LANGUAGE_DETECTION, CONVERSATION_ACTIONS
+ // OS: Q
+ FIELD_TEXT_CLASSIFIER_SESSION_ID = 1634;
+
+ // FIELD: First entity type.
+ // CATEGORY: LANGUAGE_DETECTION, CONVERSATION_ACTIONS
+ // OS: Q
+ FIELD_TEXT_CLASSIFIER_FIRST_ENTITY_TYPE = 1635;
+ // FIELD: Second entity type.
+ // CATEGORY: LANGUAGE_DETECTION, CONVERSATION_ACTIONS
+ // OS: Q
+ FIELD_TEXT_CLASSIFIER_SECOND_ENTITY_TYPE = 1636;
+
+ // FIELD: Third entity type.
+ // CATEGORY: LANGUAGE_DETECTION, CONVERSATION_ACTIONS
+ // OS: Q
+ FIELD_TEXT_CLASSIFIER_THIRD_ENTITY_TYPE = 1637;
+
+ // FIELD: Score of the suggestion.
+ // CATEGORY: LANGUAGE_DETECTION, CONVERSATION_ACTIONS
+ // OS: Q
+ FIELD_TEXT_CLASSIFIER_SCORE = 1638;
+
+ // FIELD: widget type, e.g: notification, textview
+ // CATEGORY: LANGUAGE_DETECTION, CONVERSATION_ACTIONS
+ // OS: Q
+ FIELD_TEXT_CLASSIFIER_WIDGET_TYPE = 1639;
+
+ // FIELD: version of the widget.
+ // CATEGORY: LANGUAGE_DETECTION, CONVERSATION_ACTIONS
+ // OS: Q
+ FIELD_TEXT_CLASSIFIER_WIDGET_VERSION = 1640;
+
// ---- End Q Constants, all Q constants go above this line ----
// Add new aosp constants above this line.
// END OF AOSP CONSTANTS
diff --git a/proto/src/system_messages.proto b/proto/src/system_messages.proto
index 6ff2b35d49a0..3a8931630338 100644
--- a/proto/src/system_messages.proto
+++ b/proto/src/system_messages.proto
@@ -220,6 +220,12 @@ message SystemMessage {
// Package: android
NOTE_NETWORK_SUGGESTION_AVAILABLE = 51;
+ // Inform the user that the contaminant is detected on the USB port
+ NOTE_USB_CONTAMINANT_DETECTED = 52;
+
+ // Inform that user that the USB port is free of contaminants.
+ NOTE_USB_CONTAMINANT_NOT_DETECTED = 53;
+
// 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
@@ -235,6 +241,8 @@ message SystemMessage {
NOTE_NETWORK_LOST_INTERNET = 742;
// The system default network switched to a different network
NOTE_NETWORK_SWITCH = 743;
+ // Device logged-in captive portal network successfully
+ NOTE_NETWORK_LOGGED_IN = 744;
// Notify the user that their work profile has been deleted
// Package: android
diff --git a/proto/src/wifi.proto b/proto/src/wifi.proto
index 79b63bc55102..c063e82766ed 100644
--- a/proto/src/wifi.proto
+++ b/proto/src/wifi.proto
@@ -491,6 +491,9 @@ message WifiLog {
// List of PNO scan stats, one element for each mobility state
repeated DeviceMobilityStatePnoScanStats mobility_state_pno_stats_list = 128;
+
+ // Wifi p2p statistics
+ optional WifiP2pStats wifi_p2p_stats = 129;
}
// Information that gets logged for every WiFi connection.
@@ -962,6 +965,10 @@ message StaEvent {
// NetworkAgent Wifi usability score of connected wifi
optional int32 last_wifi_usability_score = 15 [default = -1];
+
+ // Prediction horizon (in second) of Wifi usability score provided by external
+ // system app
+ optional int32 last_prediction_horizon_sec = 16 [default = -1];
}
// Wi-Fi Aware metrics
@@ -1679,6 +1686,10 @@ message WifiIsUnusableEvent {
// NetworkAgent wifi usability score of connected wifi.
// Defaults to -1 if the score was never set.
optional int32 last_wifi_usability_score = 11 [default = -1];
+
+ // Prediction horizon (in second) of Wifi usability score provided by external
+ // system app
+ optional int32 last_prediction_horizon_sec = 12 [default = -1];
}
message PasspointProfileTypeCount {
@@ -1800,6 +1811,21 @@ message WifiUsabilityStatsEntry {
// Sequence number from external system app to framework
optional int32 seq_num_to_framework = 19;
+
+ // The total time CCA is on busy status on the current frequency in ms
+ // counted from the last radio chip reset
+ optional int64 total_cca_busy_freq_time_ms = 20;
+
+ // The total radio on time of the current frequency from the last radio
+ // chip reset
+ optional int64 total_radio_on_freq_time_ms = 21;
+
+ // The total number of beacons received from the last radio chip reset
+ optional int64 total_beacon_rx = 22;
+
+ // Prediction horizon (in second) of Wifi usability score provided by external
+ // system app
+ optional int32 prediction_horizon_sec = 23;
}
message WifiUsabilityStats {
@@ -1849,3 +1875,135 @@ message DeviceMobilityStatePnoScanStats {
// the total duration elapsed while in this mobility state with PNO scans running, in ms
optional int64 pno_duration_ms = 4;
}
+
+// The information about the Wifi P2p events.
+message WifiP2pStats {
+
+ // Group event list tracking sessions and client counts in tethered mode.
+ repeated GroupEvent group_event = 1;
+
+ // Session information that gets logged for every Wifi P2p connection.
+ repeated P2pConnectionEvent connection_event = 2;
+
+ // Number of persistent group in the user profile.
+ optional int32 num_persistent_group = 3;
+
+ // Number of peer scan.
+ optional int32 num_total_peer_scans = 4;
+
+ // Number of service scan.
+ optional int32 num_total_service_scans = 5;
+}
+
+message P2pConnectionEvent {
+
+ enum ConnectionType {
+
+ // fresh new connection.
+ CONNECTION_FRESH = 0;
+
+ // reinvoke a group.
+ CONNECTION_REINVOKE = 1;
+
+ // create a group with the current device as the group owner locally.
+ CONNECTION_LOCAL = 2;
+
+ // create a group or join a group with config.
+ CONNECTION_FAST = 3;
+ }
+
+ enum ConnectivityLevelFailure {
+
+ // Failure is unknown.
+ CLF_UNKNOWN = 0;
+
+ // No failure.
+ CLF_NONE = 1;
+
+ // Timeout for current connecting request.
+ CLF_TIMEOUT = 2;
+
+ // The connecting request is canceled by the user.
+ CLF_CANCEL = 3;
+
+ // Provision discovery failure, e.g. no pin code, timeout, rejected by the peer.
+ CLF_PROV_DISC_FAIL = 4;
+
+ // Invitation failure, e.g. rejected by the peer.
+ CLF_INVITATION_FAIL = 5;
+
+ // Incoming request is rejected by the user.
+ CLF_USER_REJECT = 6;
+
+ // New connection request is issued before ending previous connecting request.
+ CLF_NEW_CONNECTION_ATTEMPT = 7;
+ }
+
+ // WPS method.
+ enum WpsMethod {
+ // WPS is skipped for Group Reinvoke.
+ WPS_NA = -1;
+
+ // Push button configuration.
+ WPS_PBC = 0;
+
+ // Display pin method configuration - pin is generated and displayed on device.
+ WPS_DISPLAY = 1;
+
+ // Keypad pin method configuration - pin is entered on device.
+ WPS_KEYPAD = 2;
+
+ // Label pin method configuration - pin is labelled on device.
+ WPS_LABEL = 3;
+ }
+
+ // Start time of the connection.
+ optional int64 start_time_millis = 1;
+
+ // Type of the connection.
+ optional ConnectionType connection_type = 2;
+
+ // WPS method.
+ optional WpsMethod wps_method = 3 [default = WPS_NA];
+
+ // Duration to connect.
+ optional int32 duration_taken_to_connect_millis = 4;
+
+ // Failures that happen at the connectivity layer.
+ optional ConnectivityLevelFailure connectivity_level_failure_code = 5;
+}
+
+// GroupEvent tracking group information from GroupStarted to GroupRemoved.
+message GroupEvent {
+
+ enum GroupRole {
+
+ GROUP_OWNER = 0;
+
+ GROUP_CLIENT = 1;
+ }
+
+ // The ID of network in supplicant for this group.
+ optional int32 net_id = 1;
+
+ // Start time of the group.
+ optional int64 start_time_millis = 2;
+
+ // Channel frequency used for Group.
+ optional int32 channel_frequency = 3;
+
+ // Is group owner or group client.
+ optional GroupRole group_role = 5;
+
+ // Number of connected clients.
+ optional int32 num_connected_clients = 6;
+
+ // Cumulative number of connected clients.
+ optional int32 num_cumulative_clients = 7;
+
+ // The session duration.
+ optional int32 session_duration_millis = 8;
+
+ // The idle duration. A group without any client is in idle.
+ optional int32 idle_duration_millis = 9;
+}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
index cf086812cb76..bcff4e0a90f1 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
@@ -17,13 +17,11 @@
package com.android.server.accessibility;
import android.content.Context;
-import android.os.Handler;
import android.os.PowerManager;
-import android.util.DebugUtils;
-import android.util.ExceptionUtils;
-import android.util.Log;
import android.util.Slog;
+import android.util.SparseArray;
import android.util.SparseBooleanArray;
+import android.view.Display;
import android.view.InputDevice;
import android.view.InputEvent;
import android.view.InputFilter;
@@ -31,10 +29,11 @@ import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.accessibility.AccessibilityEvent;
-import com.android.internal.util.BitUtils;
import com.android.server.LocalServices;
import com.android.server.policy.WindowManagerPolicy;
+import java.util.ArrayList;
+
/**
* This class is an input filter for implementing accessibility features such
* as display magnification and explore by touch.
@@ -108,23 +107,24 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo
private final AccessibilityManagerService mAms;
- private boolean mInstalled;
-
- private int mUserId;
-
- private int mEnabledFeatures;
+ private final SparseArray<EventStreamTransformation> mEventHandler;
- private TouchExplorer mTouchExplorer;
+ private final SparseArray<TouchExplorer> mTouchExplorer = new SparseArray<>(0);
- private MagnificationGestureHandler mMagnificationGestureHandler;
+ private final SparseArray<MagnificationGestureHandler> mMagnificationGestureHandler =
+ new SparseArray<>(0);
- private MotionEventInjector mMotionEventInjector;
+ private final SparseArray<MotionEventInjector> mMotionEventInjector = new SparseArray<>(0);
private AutoclickController mAutoclickController;
private KeyboardInterceptor mKeyboardInterceptor;
- private EventStreamTransformation mEventHandler;
+ private boolean mInstalled;
+
+ private int mUserId;
+
+ private int mEnabledFeatures;
private EventStreamState mMouseStreamState;
@@ -133,10 +133,16 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo
private EventStreamState mKeyboardStreamState;
AccessibilityInputFilter(Context context, AccessibilityManagerService service) {
+ this(context, service, new SparseArray<>(0));
+ }
+
+ AccessibilityInputFilter(Context context, AccessibilityManagerService service,
+ SparseArray<EventStreamTransformation> eventHandler) {
super(context.getMainLooper());
mContext = context;
mAms = service;
mPm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
+ mEventHandler = eventHandler;
}
@Override
@@ -160,6 +166,13 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo
super.onUninstalled();
}
+ void onDisplayChanged() {
+ if (mInstalled) {
+ disableFeatures();
+ enableFeatures();
+ }
+ }
+
@Override
public void onInputEvent(InputEvent event, int policyFlags) {
if (DEBUG) {
@@ -167,8 +180,8 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo
+ Integer.toHexString(policyFlags));
}
- if (mEventHandler == null) {
- if (DEBUG) Slog.d(TAG, "mEventHandler == null for event " + event);
+ if (mEventHandler.size() == 0) {
+ if (DEBUG) Slog.d(TAG, "No mEventHandler for event " + event);
super.onInputEvent(event, policyFlags);
return;
}
@@ -182,16 +195,16 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo
int eventSource = event.getSource();
if ((policyFlags & WindowManagerPolicy.FLAG_PASS_TO_USER) == 0) {
state.reset();
- mEventHandler.clearEvents(eventSource);
+ clearEventsForAllEventHandlers(eventSource);
super.onInputEvent(event, policyFlags);
return;
}
- if (state.updateDeviceId(event.getDeviceId())) {
- mEventHandler.clearEvents(eventSource);
+ if (state.updateInputSource(event.getSource())) {
+ clearEventsForAllEventHandlers(eventSource);
}
- if (!state.deviceIdValid()) {
+ if (!state.inputSourceValid()) {
super.onInputEvent(event, policyFlags);
return;
}
@@ -240,6 +253,15 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo
return null;
}
+ private void clearEventsForAllEventHandlers(int eventSource) {
+ for (int i = 0; i < mEventHandler.size(); i++) {
+ final EventStreamTransformation eventHandler = mEventHandler.valueAt(i);
+ if (eventHandler != null) {
+ eventHandler.clearEvents(eventSource);
+ }
+ }
+ }
+
private void processMotionEvent(EventStreamState state, MotionEvent event, int policyFlags) {
if (!state.shouldProcessScroll() && event.getActionMasked() == MotionEvent.ACTION_SCROLL) {
super.onInputEvent(event, policyFlags);
@@ -258,7 +280,10 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo
super.onInputEvent(event, policyFlags);
return;
}
- mEventHandler.onKeyEvent(event, policyFlags);
+ // Since the display id of KeyEvent always would be -1 and there is only one
+ // KeyboardInterceptor for all display, pass KeyEvent to the mEventHandler of
+ // DEFAULT_DISPLAY to handle.
+ mEventHandler.get(Display.DEFAULT_DISPLAY).onKeyEvent(event, policyFlags);
}
private void handleMotionEvent(MotionEvent event, int policyFlags) {
@@ -267,10 +292,16 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo
}
mPm.userActivity(event.getEventTime(), false);
MotionEvent transformedEvent = MotionEvent.obtain(event);
- mEventHandler.onMotionEvent(transformedEvent, event, policyFlags);
+ final int displayId = event.getDisplayId();
+ mEventHandler.get(isDisplayIdValid(displayId) ? displayId : Display.DEFAULT_DISPLAY)
+ .onMotionEvent(transformedEvent, event, policyFlags);
transformedEvent.recycle();
}
+ private boolean isDisplayIdValid(int displayId) {
+ return mEventHandler.get(displayId) != null;
+ }
+
@Override
public void onMotionEvent(MotionEvent transformedEvent, MotionEvent rawEvent,
int policyFlags) {
@@ -323,14 +354,20 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo
}
void notifyAccessibilityEvent(AccessibilityEvent event) {
- if (mEventHandler != null) {
- mEventHandler.onAccessibilityEvent(event);
+ for (int i = 0; i < mEventHandler.size(); i++) {
+ final EventStreamTransformation eventHandler = mEventHandler.valueAt(i);
+ if (eventHandler != null) {
+ eventHandler.onAccessibilityEvent(event);
+ }
}
}
- void notifyAccessibilityButtonClicked() {
- if (mMagnificationGestureHandler != null) {
- mMagnificationGestureHandler.notifyShortcutTriggered();
+ void notifyAccessibilityButtonClicked(int displayId) {
+ if (mMagnificationGestureHandler.size() != 0) {
+ final MagnificationGestureHandler handler = mMagnificationGestureHandler.get(displayId);
+ if (handler != null) {
+ handler.notifyShortcutTriggered();
+ }
}
}
@@ -339,81 +376,124 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo
resetStreamState();
+ final ArrayList<Display> displaysList = mAms.getValidDisplayList();
+
if ((mEnabledFeatures & FLAG_FEATURE_AUTOCLICK) != 0) {
mAutoclickController = new AutoclickController(mContext, mUserId);
- addFirstEventHandler(mAutoclickController);
+ addFirstEventHandlerForAllDisplays(displaysList, mAutoclickController);
}
- if ((mEnabledFeatures & FLAG_FEATURE_TOUCH_EXPLORATION) != 0) {
- mTouchExplorer = new TouchExplorer(mContext, mAms);
- addFirstEventHandler(mTouchExplorer);
- }
+ for (int i = displaysList.size() - 1; i >= 0; i--) {
+ final int displayId = displaysList.get(i).getDisplayId();
- if ((mEnabledFeatures & FLAG_FEATURE_CONTROL_SCREEN_MAGNIFIER) != 0
- || ((mEnabledFeatures & FLAG_FEATURE_SCREEN_MAGNIFIER) != 0)
- || ((mEnabledFeatures & FLAG_FEATURE_TRIGGERED_SCREEN_MAGNIFIER) != 0)) {
- final boolean detectControlGestures = (mEnabledFeatures
- & FLAG_FEATURE_SCREEN_MAGNIFIER) != 0;
- final boolean triggerable = (mEnabledFeatures
- & FLAG_FEATURE_TRIGGERED_SCREEN_MAGNIFIER) != 0;
- mMagnificationGestureHandler = new MagnificationGestureHandler(
- mContext, mAms.getMagnificationController(),
- detectControlGestures, triggerable);
- addFirstEventHandler(mMagnificationGestureHandler);
- }
+ if ((mEnabledFeatures & FLAG_FEATURE_TOUCH_EXPLORATION) != 0) {
+ TouchExplorer explorer = new TouchExplorer(mContext, mAms);
+ addFirstEventHandler(displayId, explorer);
+ mTouchExplorer.put(displayId, explorer);
+ }
- if ((mEnabledFeatures & FLAG_FEATURE_INJECT_MOTION_EVENTS) != 0) {
- mMotionEventInjector = new MotionEventInjector(mContext.getMainLooper());
- addFirstEventHandler(mMotionEventInjector);
- mAms.setMotionEventInjector(mMotionEventInjector);
+ if ((mEnabledFeatures & FLAG_FEATURE_CONTROL_SCREEN_MAGNIFIER) != 0
+ || ((mEnabledFeatures & FLAG_FEATURE_SCREEN_MAGNIFIER) != 0)
+ || ((mEnabledFeatures & FLAG_FEATURE_TRIGGERED_SCREEN_MAGNIFIER) != 0)) {
+ final boolean detectControlGestures = (mEnabledFeatures
+ & FLAG_FEATURE_SCREEN_MAGNIFIER) != 0;
+ final boolean triggerable = (mEnabledFeatures
+ & FLAG_FEATURE_TRIGGERED_SCREEN_MAGNIFIER) != 0;
+ MagnificationGestureHandler magnificationGestureHandler =
+ new MagnificationGestureHandler(mContext,
+ mAms.getMagnificationController(),
+ detectControlGestures, triggerable);
+ addFirstEventHandler(displayId, magnificationGestureHandler);
+ mMagnificationGestureHandler.put(displayId, magnificationGestureHandler);
+ }
+
+ if ((mEnabledFeatures & FLAG_FEATURE_INJECT_MOTION_EVENTS) != 0) {
+ MotionEventInjector injector = new MotionEventInjector(
+ mContext.getMainLooper());
+ addFirstEventHandler(displayId, injector);
+ // TODO: Need to set MotionEventInjector per display.
+ mAms.setMotionEventInjector(injector);
+ mMotionEventInjector.put(displayId, injector);
+ }
}
if ((mEnabledFeatures & FLAG_FEATURE_FILTER_KEY_EVENTS) != 0) {
mKeyboardInterceptor = new KeyboardInterceptor(mAms,
LocalServices.getService(WindowManagerPolicy.class));
- addFirstEventHandler(mKeyboardInterceptor);
+ // Since the display id of KeyEvent always would be -1 and it would be dispatched to
+ // the display with input focus directly, we only need one KeyboardInterceptor for
+ // default display.
+ addFirstEventHandler(Display.DEFAULT_DISPLAY, mKeyboardInterceptor);
}
}
/**
- * Adds an event handler to the event handler chain. The handler is added at the beginning of
- * the chain.
+ * Adds an event handler to the event handler chain for giving display. The handler is added at
+ * the beginning of the chain.
*
+ * @param displayId The logical display id.
* @param handler The handler to be added to the event handlers list.
*/
- private void addFirstEventHandler(EventStreamTransformation handler) {
- if (mEventHandler != null) {
- handler.setNext(mEventHandler);
+ private void addFirstEventHandler(int displayId, EventStreamTransformation handler) {
+ EventStreamTransformation eventHandler = mEventHandler.get(displayId);
+ if (eventHandler != null) {
+ handler.setNext(eventHandler);
} else {
handler.setNext(this);
}
- mEventHandler = handler;
+ eventHandler = handler;
+ mEventHandler.put(displayId, eventHandler);
+ }
+
+ /**
+ * Adds an event handler to the event handler chain for all displays. The handler is added at
+ * the beginning of the chain.
+ *
+ * @param displayList The list of displays
+ * @param handler The handler to be added to the event handlers list.
+ */
+ private void addFirstEventHandlerForAllDisplays(ArrayList<Display> displayList,
+ EventStreamTransformation handler) {
+ for (int i = 0; i < displayList.size(); i++) {
+ final int displayId = displayList.get(i).getDisplayId();
+ addFirstEventHandler(displayId, handler);
+ }
}
private void disableFeatures() {
- if (mMotionEventInjector != null) {
+ for (int i = mMotionEventInjector.size() - 1; i >= 0; i--) {
+ final MotionEventInjector injector = mMotionEventInjector.valueAt(i);
+ // TODO: Need to set MotionEventInjector per display.
mAms.setMotionEventInjector(null);
- mMotionEventInjector.onDestroy();
- mMotionEventInjector = null;
+ if (injector != null) {
+ injector.onDestroy();
+ }
}
+ mMotionEventInjector.clear();
if (mAutoclickController != null) {
mAutoclickController.onDestroy();
mAutoclickController = null;
}
- if (mTouchExplorer != null) {
- mTouchExplorer.onDestroy();
- mTouchExplorer = null;
+ for (int i = mTouchExplorer.size() - 1; i >= 0; i--) {
+ final TouchExplorer explorer = mTouchExplorer.valueAt(i);
+ if (explorer != null) {
+ explorer.onDestroy();
+ }
}
- if (mMagnificationGestureHandler != null) {
- mMagnificationGestureHandler.onDestroy();
- mMagnificationGestureHandler = null;
+ mTouchExplorer.clear();
+ for (int i = mMagnificationGestureHandler.size() - 1; i >= 0; i--) {
+ final MagnificationGestureHandler handler = mMagnificationGestureHandler.valueAt(i);
+ if (handler != null) {
+ handler.onDestroy();
+ }
}
+ mMagnificationGestureHandler.clear();
if (mKeyboardInterceptor != null) {
mKeyboardInterceptor.onDestroy();
mKeyboardInterceptor = null;
}
- mEventHandler = null;
+ mEventHandler.clear();
resetStreamState();
}
@@ -441,41 +521,41 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo
* whose events should not be handled by a11y event stream transformations.
*/
private static class EventStreamState {
- private int mDeviceId;
+ private int mSource;
EventStreamState() {
- mDeviceId = -1;
+ mSource = -1;
}
/**
- * Updates the ID of the device associated with the state. If the ID changes, resets
- * internal state.
+ * Updates the input source of the device associated with the state. If the source changes,
+ * resets internal state.
*
- * @param deviceId Updated input device ID.
- * @return Whether the device ID has changed.
+ * @param source Updated input source.
+ * @return Whether the input source has changed.
*/
- public boolean updateDeviceId(int deviceId) {
- if (mDeviceId == deviceId) {
+ public boolean updateInputSource(int source) {
+ if (mSource == source) {
return false;
}
- // Reset clears internal state, so make sure it's called before |mDeviceId| is updated.
+ // Reset clears internal state, so make sure it's called before |mSource| is updated.
reset();
- mDeviceId = deviceId;
+ mSource = source;
return true;
}
/**
- * @return Whether device ID is valid.
+ * @return Whether input source is valid.
*/
- public boolean deviceIdValid() {
- return mDeviceId >= 0;
+ public boolean inputSourceValid() {
+ return mSource >= 0;
}
/**
* Resets the event stream state.
*/
public void reset() {
- mDeviceId = -1;
+ mSource = -1;
}
/**
@@ -592,20 +672,19 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo
/*
* Key events from different devices may be interleaved. For example, the volume up and
- * down keys can come from different device IDs.
+ * down keys can come from different input sources.
*/
@Override
- public boolean updateDeviceId(int deviceId) {
+ public boolean updateInputSource(int deviceId) {
return false;
}
- // We manage all device ids simultaneously; there is no concept of validity.
+ // We manage all input source simultaneously; there is no concept of validity.
@Override
- public boolean deviceIdValid() {
+ public boolean inputSourceValid() {
return true;
}
-
@Override
final public boolean shouldProcessKeyEvent(KeyEvent event) {
// For each keyboard device, wait for a down event from a device to start processing
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index dbe86c15c7a4..305c53e1e26a 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -38,6 +38,7 @@ import android.accessibilityservice.AccessibilityServiceInfo;
import android.accessibilityservice.IAccessibilityServiceClient;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.ActivityOptions;
import android.app.AlertDialog;
import android.app.AppOpsManager;
import android.app.PendingIntent;
@@ -208,12 +209,12 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
private final WindowManagerInternal mWindowManagerService;
- private final DisplayManager mDisplayManager;
-
private AppWidgetManagerInternal mAppWidgetService;
private final SecurityPolicy mSecurityPolicy;
+ private final AccessibilityDisplayListener mA11yDisplayListener;
+
private final AppOpsManager mAppOpsManager;
private final MainHandler mMainHandler;
@@ -306,12 +307,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
mMainHandler = new MainHandler(mContext.getMainLooper());
mGlobalActionPerformer = new GlobalActionPerformer(mContext, mWindowManagerService);
- mDisplayManager = mContext.getSystemService(DisplayManager.class);
+ mA11yDisplayListener = new AccessibilityDisplayListener(mContext, mMainHandler);
registerBroadcastReceivers();
new AccessibilityContentObserver(mMainHandler).register(
context.getContentResolver());
- registerDisplayListener(mMainHandler);
}
@Override
@@ -527,30 +527,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}, UserHandle.ALL, intentFilter, null, null);
}
- private void registerDisplayListener(Handler handler) {
- mDisplayManager.registerDisplayListener(new DisplayManager.DisplayListener() {
- @Override
- public void onDisplayAdded(int displayId) {
- synchronized (mLock) {
- UserState userState = getCurrentUserStateLocked();
- updateMagnificationLocked(userState);
- }
- }
-
- @Override
- public void onDisplayRemoved(int displayId) {
- if (mMagnificationController != null) {
- mMagnificationController.onDisplayRemoved(displayId);
- }
- }
-
- @Override
- public void onDisplayChanged(int displayId) {
- // do nothing
- }
- }, handler);
- }
-
@Override
public long addClient(IAccessibilityManagerClient callback, int userId) {
synchronized (mLock) {
@@ -937,16 +913,18 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
/**
* Invoked remotely over AIDL by SysUi when the accessibility button within the system's
* navigation area has been clicked.
+ *
+ * @param displayId The logical display id.
*/
@Override
- public void notifyAccessibilityButtonClicked() {
+ public void notifyAccessibilityButtonClicked(int displayId) {
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.STATUS_BAR_SERVICE)
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Caller does not hold permission "
+ android.Manifest.permission.STATUS_BAR_SERVICE);
}
synchronized (mLock) {
- notifyAccessibilityButtonClickedLocked();
+ notifyAccessibilityButtonClickedLocked(displayId);
}
}
@@ -1249,7 +1227,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
}
- private void notifyAccessibilityButtonClickedLocked() {
+ private void notifyAccessibilityButtonClickedLocked(int displayId) {
final UserState state = getCurrentUserStateLocked();
int potentialTargets = state.mIsNavBarMagnificationEnabled ? 1 : 0;
@@ -1266,12 +1244,15 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
if (potentialTargets == 1) {
if (state.mIsNavBarMagnificationEnabled) {
mMainHandler.sendMessage(obtainMessage(
- AccessibilityManagerService::sendAccessibilityButtonToInputFilter, this));
+ AccessibilityManagerService::sendAccessibilityButtonToInputFilter, this,
+ displayId));
return;
} else {
for (int i = state.mBoundServices.size() - 1; i >= 0; i--) {
final AccessibilityServiceConnection service = state.mBoundServices.get(i);
if (service.mRequestAccessibilityButton) {
+ // TODO(b/120762691): Need to notify each accessibility service if
+ // accessibility button is clicked per display.
service.notifyAccessibilityButtonClickedLocked();
return;
}
@@ -1281,17 +1262,21 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
if (state.mServiceAssignedToAccessibilityButton == null
&& !state.mIsNavBarMagnificationAssignedToAccessibilityButton) {
mMainHandler.sendMessage(obtainMessage(
- AccessibilityManagerService::showAccessibilityButtonTargetSelection, this));
+ AccessibilityManagerService::showAccessibilityButtonTargetSelection, this,
+ displayId));
} else if (state.mIsNavBarMagnificationEnabled
&& state.mIsNavBarMagnificationAssignedToAccessibilityButton) {
mMainHandler.sendMessage(obtainMessage(
- AccessibilityManagerService::sendAccessibilityButtonToInputFilter, this));
+ AccessibilityManagerService::sendAccessibilityButtonToInputFilter, this,
+ displayId));
return;
} else {
for (int i = state.mBoundServices.size() - 1; i >= 0; i--) {
final AccessibilityServiceConnection service = state.mBoundServices.get(i);
if (service.mRequestAccessibilityButton && (service.mComponentName.equals(
state.mServiceAssignedToAccessibilityButton))) {
+ // TODO(b/120762691): Need to notify each accessibility service if
+ // accessibility button is clicked per display.
service.notifyAccessibilityButtonClickedLocked();
return;
}
@@ -1299,22 +1284,24 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
// The user may have turned off the assigned service or feature
mMainHandler.sendMessage(obtainMessage(
- AccessibilityManagerService::showAccessibilityButtonTargetSelection, this));
+ AccessibilityManagerService::showAccessibilityButtonTargetSelection, this,
+ displayId));
}
}
- private void sendAccessibilityButtonToInputFilter() {
+ private void sendAccessibilityButtonToInputFilter(int displayId) {
synchronized (mLock) {
if (mHasInputFilter && mInputFilter != null) {
- mInputFilter.notifyAccessibilityButtonClicked();
+ mInputFilter.notifyAccessibilityButtonClicked(displayId);
}
}
}
- private void showAccessibilityButtonTargetSelection() {
+ private void showAccessibilityButtonTargetSelection(int displayId) {
Intent intent = new Intent(AccessibilityManager.ACTION_CHOOSE_ACCESSIBILITY_BUTTON);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
- mContext.startActivityAsUser(intent, UserHandle.of(mCurrentUserId));
+ final Bundle bundle = ActivityOptions.makeBasic().setLaunchDisplayId(displayId).toBundle();
+ mContext.startActivityAsUser(intent, bundle, UserHandle.of(mCurrentUserId));
}
private void notifyAccessibilityButtonVisibilityChangedLocked(boolean available) {
@@ -2226,32 +2213,22 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
return;
}
- // register all display if global magnification is enabled.
- final Display[] displays = mDisplayManager.getDisplays();
+ // Get all valid displays and register them if global magnification is enabled.
+ // We would skip overlay display because it uses overlay window to simulate secondary
+ // displays in one display. It's not a real display and there's no input events for it.
+ final ArrayList<Display> displays = getValidDisplayList();
if (userState.mIsDisplayMagnificationEnabled
|| userState.mIsNavBarMagnificationEnabled) {
- for (int i = 0; i < displays.length; i++) {
- final Display display = displays[i];
- // Overlay display uses overlay window to simulate secondary displays in
- // one display. It's not a real display and there's no input events for it.
- // We should ignore it.
- if (display.getType() == Display.TYPE_OVERLAY) {
- continue;
- }
+ for (int i = 0; i < displays.size(); i++) {
+ final Display display = displays.get(i);
getMagnificationController().register(display.getDisplayId());
}
return;
}
- // register if display has listening magnification services.
- for (int i = 0; i < displays.length; i++) {
- final Display display = displays[i];
- // Overlay display uses overlay window to simulate secondary displays in
- // one display. It's not a real display and there's no input events for it.
- // We should ignore it.
- if (display.getType() == Display.TYPE_OVERLAY) {
- continue;
- }
+ // Register if display has listening magnification services.
+ for (int i = 0; i < displays.size(); i++) {
+ final Display display = displays.get(i);
final int displayId = display.getDisplayId();
if (userHasListeningMagnificationServicesLocked(userState, displayId)) {
getMagnificationController().register(displayId);
@@ -3812,6 +3789,92 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
}
+ /**
+ * Gets all currently valid logical displays.
+ *
+ * @return An array list containing all valid logical displays.
+ */
+ public ArrayList<Display> getValidDisplayList() {
+ return mA11yDisplayListener.getValidDisplayList();
+ }
+
+ /**
+ * A Utility class to handle display state.
+ */
+ public class AccessibilityDisplayListener implements DisplayManager.DisplayListener {
+ private final DisplayManager mDisplayManager;
+ private final ArrayList<Display> mDisplaysList = new ArrayList<>();
+
+ AccessibilityDisplayListener(Context context, MainHandler handler) {
+ mDisplayManager = (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE);
+ mDisplayManager.registerDisplayListener(this, handler);
+ initializeDisplayList();
+ }
+
+ ArrayList<Display> getValidDisplayList() {
+ synchronized (mLock) {
+ return mDisplaysList;
+ }
+ }
+
+ private void initializeDisplayList() {
+ final Display[] displays = mDisplayManager.getDisplays();
+ synchronized (mLock) {
+ mDisplaysList.clear();
+ for (int i = 0; i < displays.length; i++) {
+ // Exclude overlay virtual displays. The display list is for A11yInputFilter
+ // to create event handler per display. The events should be handled by the
+ // display which is overlaid by it.
+ final Display display = displays[i];
+ if (display.getType() == Display.TYPE_OVERLAY) {
+ continue;
+ }
+ mDisplaysList.add(display);
+ }
+ }
+ }
+
+ @Override
+ public void onDisplayAdded(int displayId) {
+ final Display display = mDisplayManager.getDisplay(displayId);
+ if (display == null || display.getType() == Display.TYPE_OVERLAY) {
+ return;
+ }
+
+ synchronized (mLock) {
+ mDisplaysList.add(display);
+ if (mInputFilter != null) {
+ mInputFilter.onDisplayChanged();
+ }
+ UserState userState = getCurrentUserStateLocked();
+ updateMagnificationLocked(userState);
+ }
+ }
+
+ @Override
+ public void onDisplayRemoved(int displayId) {
+ synchronized (mLock) {
+ for (int i = 0; i < mDisplaysList.size(); i++) {
+ if (mDisplaysList.get(i).getDisplayId() == displayId) {
+ mDisplaysList.remove(i);
+ break;
+ }
+ }
+ if (mInputFilter != null) {
+ mInputFilter.onDisplayChanged();
+ }
+ }
+ if (mMagnificationController != null) {
+ mMagnificationController.onDisplayRemoved(displayId);
+ }
+ }
+
+ @Override
+ public void onDisplayChanged(int displayId) {
+ /* do nothing */
+ }
+ }
+
/** Represents an {@link AccessibilityManager} */
class Client {
final IAccessibilityManagerClient mCallback;
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index c992da43fc07..2e45fa72eaac 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -18,14 +18,13 @@ package com.android.server.autofill;
import static android.Manifest.permission.MANAGE_AUTO_FILL;
import static android.content.Context.AUTOFILL_MANAGER_SERVICE;
-import static android.util.DebugUtils.flagsToString;
import static android.view.autofill.AutofillManager.MAX_TEMP_AUGMENTED_SERVICE_DURATION_MS;
+import static android.view.autofill.AutofillManager.getSmartSuggestionModeToString;
import static com.android.server.autofill.Helper.sDebug;
import static com.android.server.autofill.Helper.sFullScreenMode;
import static com.android.server.autofill.Helper.sVerbose;
-import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
@@ -61,6 +60,7 @@ import android.util.Slog;
import android.util.SparseArray;
import android.view.autofill.AutofillId;
import android.view.autofill.AutofillManager;
+import android.view.autofill.AutofillManager.SmartSuggestionMode;
import android.view.autofill.AutofillManagerInternal;
import android.view.autofill.AutofillValue;
import android.view.autofill.IAutoFillManager;
@@ -80,8 +80,6 @@ import com.android.server.infra.SecureSettingsServiceNameResolver;
import java.io.FileDescriptor;
import java.io.PrintWriter;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -102,26 +100,6 @@ public final class AutofillManagerService
private static final Object sLock = AutofillManagerService.class;
- /**
- * IME supports Smart Suggestions.
- */
- // NOTE: must be public because of flagsToString()
- public static final int FLAG_SMART_SUGGESTION_IME = 0x1;
-
- /**
- * System supports Smarts Suggestions (as a popup-window similar to standard Autofill).
- */
- // NOTE: must be public because of flagsToString()
- public static final int FLAG_SMART_SUGGESTION_SYSTEM = 0x2;
-
- /** @hide */
- @IntDef(flag = true, prefix = { "FLAG_SMART_SUGGESTION_" }, value = {
- FLAG_SMART_SUGGESTION_IME,
- FLAG_SMART_SUGGESTION_SYSTEM
- })
- @Retention(RetentionPolicy.SOURCE)
- @interface SmartSuggestionMode {}
-
static final String RECEIVER_BUNDLE_EXTRA_SESSIONS = "sessions";
private static final char COMPAT_PACKAGE_DELIMITER = ':';
@@ -484,7 +462,7 @@ public final class AutofillManagerService
Settings.Global.AUTOFILL_SMART_SUGGESTION_EMULATION_FLAGS, 0);
if (sDebug) {
Slog.d(TAG, "setSmartSuggestionEmulationFromSettings(): "
- + smartSuggestionFlagsToString(flags));
+ + getSmartSuggestionModeToString(flags));
}
synchronized (mLock) {
@@ -698,10 +676,6 @@ public final class AutofillManagerService
}
}
- static String smartSuggestionFlagsToString(int flags) {
- return flagsToString(AutofillManagerService.class, "FLAG_SMART_SUGGESTION_", flags);
- }
-
private final class LocalService extends AutofillManagerInternal {
@Override
public void onBackKeyPressed() {
@@ -1251,7 +1225,7 @@ public final class AutofillManagerService
pw.println(getWhitelistedCompatModePackagesFromSettings());
if (mSupportedSmartSuggestionModes != 0) {
pw.print("Smart Suggestion modes: ");
- pw.println(smartSuggestionFlagsToString(mSupportedSmartSuggestionModes));
+ pw.println(getSmartSuggestionModeToString(mSupportedSmartSuggestionModes));
}
if (showHistory) {
pw.println(); pw.println("Requests history:"); pw.println();
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index 954b67e4e2dc..8886ee2365c0 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -63,6 +63,7 @@ import android.util.SparseArray;
import android.util.TimeUtils;
import android.view.autofill.AutofillId;
import android.view.autofill.AutofillManager;
+import android.view.autofill.AutofillManager.SmartSuggestionMode;
import android.view.autofill.AutofillValue;
import android.view.autofill.IAutoFillManagerClient;
@@ -72,7 +73,6 @@ import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.server.LocalServices;
import com.android.server.autofill.AutofillManagerService.AutofillCompatState;
-import com.android.server.autofill.AutofillManagerService.SmartSuggestionMode;
import com.android.server.autofill.RemoteAugmentedAutofillService.RemoteAugmentedAutofillServiceCallbacks;
import com.android.server.autofill.ui.AutoFillUI;
import com.android.server.infra.AbstractPerUserSystemService;
@@ -855,7 +855,6 @@ final class AutofillManagerServiceImpl
@GuardedBy("mLock")
@SmartSuggestionMode int getSupportedSmartSuggestionModesLocked() {
- // TODO(b/111330312): once we support IME, we need to set it per-user (OR'ed with master)
return mMaster.getSupportedSmartSuggestionModesLocked();
}
@@ -1049,7 +1048,7 @@ final class AutofillManagerServiceImpl
componentName, mUserId, new RemoteAugmentedAutofillServiceCallbacks() {
@Override
public void onServiceDied(@NonNull RemoteAugmentedAutofillService service) {
- // TODO(b/111330312): properly implement
+ // TODO(b/123100811): properly implement
Slog.w(TAG, "remote augmented autofill service died");
}
}, mMaster.isInstantServiceAllowed(), mMaster.verbose);
diff --git a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
index 5d8d8fa46d3f..9b863a9f2d26 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
@@ -113,7 +113,7 @@ final class RemoteAugmentedAutofillService
scheduleAsyncRequest((s) -> s.onDestroyAllFillWindowsRequest());
}
- // TODO(b/111330312): inline into PendingAutofillRequest if it doesn't have any other subclass
+ // TODO(b/123100811): inline into PendingAutofillRequest if it doesn't have any other subclass
private abstract static class MyPendingRequest
extends PendingRequest<RemoteAugmentedAutofillService, IAugmentedAutofillService> {
protected final int mSessionId;
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 9f7d33f6796a..194332ab8451 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -23,11 +23,10 @@ import static android.view.autofill.AutofillManager.ACTION_START_SESSION;
import static android.view.autofill.AutofillManager.ACTION_VALUE_CHANGED;
import static android.view.autofill.AutofillManager.ACTION_VIEW_ENTERED;
import static android.view.autofill.AutofillManager.ACTION_VIEW_EXITED;
+import static android.view.autofill.AutofillManager.FLAG_SMART_SUGGESTION_SYSTEM;
+import static android.view.autofill.AutofillManager.getSmartSuggestionModeToString;
import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
-import static com.android.server.autofill.AutofillManagerService.FLAG_SMART_SUGGESTION_IME;
-import static com.android.server.autofill.AutofillManagerService.FLAG_SMART_SUGGESTION_SYSTEM;
-import static com.android.server.autofill.AutofillManagerService.smartSuggestionFlagsToString;
import static com.android.server.autofill.Helper.getNumericValue;
import static com.android.server.autofill.Helper.sDebug;
import static com.android.server.autofill.Helper.sVerbose;
@@ -88,6 +87,7 @@ import android.util.TimeUtils;
import android.view.KeyEvent;
import android.view.autofill.AutofillId;
import android.view.autofill.AutofillManager;
+import android.view.autofill.AutofillManager.SmartSuggestionMode;
import android.view.autofill.AutofillValue;
import android.view.autofill.IAutoFillManagerClient;
import android.view.autofill.IAutofillWindowPresenter;
@@ -97,7 +97,6 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.util.ArrayUtils;
-import com.android.server.autofill.AutofillManagerService.SmartSuggestionMode;
import com.android.server.autofill.ui.AutoFillUI;
import com.android.server.autofill.ui.PendingUi;
@@ -250,7 +249,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
/**
* Destroys the augmented Autofill UI.
*/
- // TODO(b/111330312): this runnable is called when the Autofill session is destroyed, the
+ // TODO(b/123099468): this runnable is called when the Autofill session is destroyed, the
// main reason being the cases where user tap HOME.
// Right now it's completely destroying the UI, but we need to decide whether / how to
// properly recover it later (for example, if the user switches back to the activity,
@@ -2559,7 +2558,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
notifyUnavailableToClient(AutofillManager.STATE_FINISHED);
removeSelf();
} else {
- // TODO(b/111330312, b/119638958): must set internal state so when user focus other
+ // TODO(b/123099468, b/119638958): must set internal state so when user focus other
// fields it does not generate a new call to the standard autofill service (right now
// it does). Must also add CTS tests to exercise this scenario.
if (sVerbose) {
@@ -2574,7 +2573,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
*
* @return callback to destroy the autofill UI, or {@code null} if not supported.
*/
- // TODO(b/111330312): might need to call it in other places, like when the service returns a
+ // TODO(b/123099468): might need to call it in other places, like when the service returns a
// non-null response but without datasets (for example, just SaveInfo)
@GuardedBy("mLock")
private Runnable triggerAugmentedAutofillLocked() {
@@ -2594,14 +2593,12 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
// Define which mode will be used
final int mode;
- if ((supportedModes & FLAG_SMART_SUGGESTION_IME) != 0) {
- // TODO(b/111330312): support it :-)
- Slog.w(TAG, "Smart Suggestions on IME not supported yet");
- return null;
- } else if ((supportedModes & FLAG_SMART_SUGGESTION_SYSTEM) != 0) {
+ if ((supportedModes & FLAG_SMART_SUGGESTION_SYSTEM) != 0) {
mode = FLAG_SMART_SUGGESTION_SYSTEM;
+ } else if ((supportedModes & AutofillManager.FLAG_SMART_SUGGESTION_LEGACY) != 0) {
+ mode = AutofillManager.FLAG_SMART_SUGGESTION_LEGACY;
} else {
- Slog.w(TAG, "Unsupported Smart Suggestion Mode: " + supportedModes);
+ Slog.w(TAG, "Unsupported Smart Suggestion mode: " + supportedModes);
return null;
}
@@ -2614,7 +2611,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
Slog.v(TAG, "calling Augmented Autofill Service ("
+ remoteService.getComponentName().toShortString() + ") on view "
+ mCurrentViewId + " using suggestion mode "
- + smartSuggestionFlagsToString(mode)
+ + getSmartSuggestionModeToString(mode)
+ " when server returned null for session " + this.id);
}
diff --git a/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java b/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java
index 4a1e5b9910bf..2241569afe18 100644
--- a/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java
+++ b/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java
@@ -164,7 +164,7 @@ public class PackageManagerBackupAgent extends BackupAgent {
int N = pkgs.size();
for (int a = N-1; a >= 0; a--) {
PackageInfo pkg = pkgs.get(a);
- if (!AppBackupUtils.appIsEligibleForBackup(pkg.applicationInfo, pm)) {
+ if (!AppBackupUtils.appIsEligibleForBackup(pkg.applicationInfo, userId)) {
pkgs.remove(a);
}
}
diff --git a/services/backup/java/com/android/server/backup/Trampoline.java b/services/backup/java/com/android/server/backup/Trampoline.java
index b9a6f3c08cc4..303734a4043c 100644
--- a/services/backup/java/com/android/server/backup/Trampoline.java
+++ b/services/backup/java/com/android/server/backup/Trampoline.java
@@ -18,6 +18,7 @@ package com.android.server.backup;
import static com.android.server.backup.BackupManagerService.TAG;
+import android.Manifest;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.admin.DevicePolicyManager;
@@ -41,9 +42,10 @@ import android.os.RemoteException;
import android.os.SystemProperties;
import android.os.Trace;
import android.os.UserHandle;
-import android.provider.Settings;
+import android.os.UserManager;
import android.util.Slog;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.DumpUtils;
import java.io.File;
@@ -75,19 +77,25 @@ import java.io.PrintWriter;
* system user is unlocked before any other users.
*/
public class Trampoline extends IBackupManager.Stub {
- // When this file is present, the backup service is inactive.
+ /**
+ * Name of file that disables the backup service. If this file exists, then backup is disabled
+ * for all users.
+ */
private static final String BACKUP_SUPPRESS_FILENAME = "backup-suppress";
+ /**
+ * Name of file for non-system users that enables the backup service for the user. Backup is
+ * disabled by default in non-system users.
+ */
+ private static final String BACKUP_ACTIVATED_FILENAME = "backup-activated";
+
// Product-level suppression of backup/restore.
private static final String BACKUP_DISABLE_PROPERTY = "ro.backup.disable";
private static final String BACKUP_THREAD = "backup";
- /** Values for setting {@link Settings.Global#BACKUP_MULTI_USER_ENABLED} */
- private static final int MULTI_USER_DISABLED = 0;
- private static final int MULTI_USER_ENABLED = 1;
-
private final Context mContext;
+ private final UserManager mUserManager;
private final boolean mGlobalDisable;
// Lock to write backup suppress files.
@@ -104,20 +112,13 @@ public class Trampoline extends IBackupManager.Stub {
mHandlerThread = new HandlerThread(BACKUP_THREAD, Process.THREAD_PRIORITY_BACKGROUND);
mHandlerThread.start();
mHandler = new Handler(mHandlerThread.getLooper());
+ mUserManager = UserManager.get(context);
}
protected boolean isBackupDisabled() {
return SystemProperties.getBoolean(BACKUP_DISABLE_PROPERTY, false);
}
- private boolean isMultiUserEnabled() {
- return Settings.Global.getInt(
- mContext.getContentResolver(),
- Settings.Global.BACKUP_MULTI_USER_ENABLED,
- MULTI_USER_DISABLED)
- == MULTI_USER_ENABLED;
- }
-
protected int binderGetCallingUserId() {
return Binder.getCallingUserHandle().getIdentifier();
}
@@ -126,21 +127,65 @@ public class Trampoline extends IBackupManager.Stub {
return Binder.getCallingUid();
}
- protected File getSuppressFileForUser(int userId) {
- return new File(UserBackupManagerFiles.getBaseStateDir(userId),
+ /** Stored in the system user's directory. */
+ protected File getSuppressFileForSystemUser() {
+ return new File(UserBackupManagerFiles.getBaseStateDir(UserHandle.USER_SYSTEM),
BACKUP_SUPPRESS_FILENAME);
}
- protected void createBackupSuppressFileForUser(int userId) throws IOException {
- synchronized (mStateLock) {
- getSuppressFileForUser(userId).getParentFile().mkdirs();
- getSuppressFileForUser(userId).createNewFile();
+ /** Stored in the system user's directory and the file is indexed by the user it refers to. */
+ protected File getActivatedFileForNonSystemUser(int userId) {
+ return new File(UserBackupManagerFiles.getBaseStateDir(UserHandle.USER_SYSTEM),
+ BACKUP_ACTIVATED_FILENAME + "-" + userId);
+ }
+
+ private void createFile(File file) throws IOException {
+ if (file.exists()) {
+ return;
+ }
+
+ file.getParentFile().mkdirs();
+ if (!file.createNewFile()) {
+ Slog.w(TAG, "Failed to create file " + file.getPath());
+ }
+ }
+
+ private void deleteFile(File file) {
+ if (!file.exists()) {
+ return;
+ }
+
+ if (!file.delete()) {
+ Slog.w(TAG, "Failed to delete file " + file.getPath());
}
}
- private void deleteBackupSuppressFileForUser(int userId) {
- if (!getSuppressFileForUser(userId).delete()) {
- Slog.w(TAG, "Failed deleting backup suppressed file for user: " + userId);
+ /**
+ * Deactivates the backup service for user {@code userId}. If this is the system user, it
+ * creates a suppress file which disables backup for all users. If this is a non-system user, it
+ * only deactivates backup for that user by deleting its activate file.
+ */
+ @GuardedBy("mStateLock")
+ private void deactivateBackupForUserLocked(int userId) throws IOException {
+ if (userId == UserHandle.USER_SYSTEM) {
+ createFile(getSuppressFileForSystemUser());
+ } else {
+ deleteFile(getActivatedFileForNonSystemUser(userId));
+ }
+ }
+
+ /**
+ * Enables the backup service for user {@code userId}. If this is the system user, it deletes
+ * the suppress file. If this is a non-system user, it creates the user's activate file. Note,
+ * deleting the suppress file does not automatically enable backup for non-system users, they
+ * need their own activate file in order to participate in the service.
+ */
+ @GuardedBy("mStateLock")
+ private void activateBackupForUserLocked(int userId) throws IOException {
+ if (userId == UserHandle.USER_SYSTEM) {
+ deleteFile(getSuppressFileForSystemUser());
+ } else {
+ createFile(getActivatedFileForNonSystemUser(userId));
}
}
@@ -148,24 +193,31 @@ public class Trampoline extends IBackupManager.Stub {
// admin (device owner or profile owner).
private boolean isUserReadyForBackup(int userId) {
return mService != null && mService.getServiceUsers().get(userId) != null
- && !isBackupSuppressedForUser(userId);
+ && isBackupActivatedForUser(userId);
}
- private boolean isBackupSuppressedForUser(int userId) {
- // If backup is disabled for system user, it's disabled for all other users on device.
- if (getSuppressFileForUser(UserHandle.USER_SYSTEM).exists()) {
- return true;
- }
- if (userId != UserHandle.USER_SYSTEM) {
- return getSuppressFileForUser(userId).exists();
+ /**
+ * Backup is activated for the system user if the suppress file does not exist. Backup is
+ * activated for non-system users if the suppress file does not exist AND the user's activated
+ * file exists.
+ */
+ private boolean isBackupActivatedForUser(int userId) {
+ if (getSuppressFileForSystemUser().exists()) {
+ return false;
}
- return false;
+
+ return userId == UserHandle.USER_SYSTEM
+ || getActivatedFileForNonSystemUser(userId).exists();
}
protected Context getContext() {
return mContext;
}
+ protected UserManager getUserManager() {
+ return mUserManager;
+ }
+
protected BackupManagerService createBackupManagerService() {
return new BackupManagerService(mContext, this, mHandlerThread);
}
@@ -198,23 +250,17 @@ public class Trampoline extends IBackupManager.Stub {
/**
* Called from {@link BackupManagerService.Lifecycle} when a user {@code userId} is unlocked.
- * Starts the backup service for this user if it's the system user or if the service supports
- * multi-user. Offloads work onto the handler thread {@link #mHandlerThread} to keep unlock time
- * low.
+ * Starts the backup service for this user if backup is active for this user. Offloads work onto
+ * the handler thread {@link #mHandlerThread} to keep unlock time low.
*/
void unlockUser(int userId) {
- if (userId != UserHandle.USER_SYSTEM && !isMultiUserEnabled()) {
- Slog.i(TAG, "Multi-user disabled, cannot start service for user: " + userId);
- return;
- }
-
postToHandler(() -> startServiceForUser(userId));
}
private void startServiceForUser(int userId) {
// We know that the user is unlocked here because it is called from setBackupServiceActive
// and unlockUser which have these guarantees. So we can check if the file exists.
- if (mService != null && !isBackupSuppressedForUser(userId)) {
+ if (mService != null && isBackupActivatedForUser(userId)) {
Slog.i(TAG, "Starting service for user: " + userId);
mService.startServiceForUser(userId);
}
@@ -225,11 +271,6 @@ public class Trampoline extends IBackupManager.Stub {
* Offloads work onto the handler thread {@link #mHandlerThread} to keep stopping time low.
*/
void stopUser(int userId) {
- if (userId != UserHandle.USER_SYSTEM && !isMultiUserEnabled()) {
- Slog.i(TAG, "Multi-user disabled, cannot stop service for user: " + userId);
- return;
- }
-
postToHandler(
() -> {
if (mService != null) {
@@ -240,45 +281,63 @@ public class Trampoline extends IBackupManager.Stub {
}
/**
- * Only privileged callers should be changing the backup state. This method only acts on {@link
- * UserHandle#USER_SYSTEM} and is a no-op if passed non-system users. Deactivating backup in the
- * system user also deactivates backup in all users.
- *
- * This call will only work if the calling {@code userID} is unlocked.
+ * The system user and managed profiles can only be acted on by callers in the system or root
+ * processes. Other users can be acted on by callers who have both android.permission.BACKUP and
+ * android.permission.INTERACT_ACROSS_USERS_FULL permissions.
*/
- public void setBackupServiceActive(int userId, boolean makeActive) {
- int caller = binderGetCallingUid();
- if (caller != Process.SYSTEM_UID && caller != Process.ROOT_UID) {
- throw new SecurityException("No permission to configure backup activity");
+ private void enforcePermissionsOnUser(int userId) throws SecurityException {
+ boolean isRestrictedUser =
+ userId == UserHandle.USER_SYSTEM
+ || getUserManager().getUserInfo(userId).isManagedProfile();
+
+ if (isRestrictedUser) {
+ int caller = binderGetCallingUid();
+ if (caller != Process.SYSTEM_UID && caller != Process.ROOT_UID) {
+ throw new SecurityException("No permission to configure backup activity");
+ }
+ } else {
+ mContext.enforceCallingOrSelfPermission(
+ Manifest.permission.BACKUP, "No permission to configure backup activity");
+ mContext.enforceCallingOrSelfPermission(
+ Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+ "No permission to configure backup activity");
}
+ }
+
+ /**
+ * Only privileged callers should be changing the backup state. Deactivating backup in the
+ * system user also deactivates backup in all users. We are not guaranteed that {@code userId}
+ * is unlocked at this point yet, so handle both cases.
+ */
+ public void setBackupServiceActive(int userId, boolean makeActive) {
+ enforcePermissionsOnUser(userId);
if (mGlobalDisable) {
Slog.i(TAG, "Backup service not supported");
return;
}
- if (userId != UserHandle.USER_SYSTEM) {
- Slog.i(TAG, "Cannot set backup service activity for non-system user: " + userId);
- return;
- }
-
- if (makeActive == isBackupServiceActive(userId)) {
- Slog.i(TAG, "No change in backup service activity");
- return;
- }
-
synchronized (mStateLock) {
Slog.i(TAG, "Making backup " + (makeActive ? "" : "in") + "active");
if (makeActive) {
if (mService == null) {
mService = createBackupManagerService();
}
- deleteBackupSuppressFileForUser(userId);
- startServiceForUser(userId);
+ try {
+ activateBackupForUserLocked(userId);
+ } catch (IOException e) {
+ Slog.e(TAG, "Unable to persist backup service activity");
+ }
+
+ // If the user is unlocked, we can start the backup service for it. Otherwise we
+ // will start the service when the user is unlocked as part of its unlock callback.
+ if (getUserManager().isUserUnlocked(userId)) {
+ startServiceForUser(userId);
+ }
} else {
try {
//TODO(b/121198006): what if this throws an exception?
- createBackupSuppressFileForUser(userId);
+ deactivateBackupForUserLocked(userId);
} catch (IOException e) {
Slog.e(TAG, "Unable to persist backup service inactivity");
}
@@ -636,14 +695,18 @@ public class Trampoline extends IBackupManager.Stub {
}
@Override
- public void opComplete(int token, long result) throws RemoteException {
- int userId = binderGetCallingUserId();
+ public void opCompleteForUser(int userId, int token, long result) throws RemoteException {
if (isUserReadyForBackup(userId)) {
- mService.opComplete(binderGetCallingUserId(), token, result);
+ mService.opComplete(userId, token, result);
}
}
@Override
+ public void opComplete(int token, long result) throws RemoteException {
+ opCompleteForUser(binderGetCallingUserId(), token, result);
+ }
+
+ @Override
public long getAvailableRestoreTokenForUser(int userId, String packageName) {
return isUserReadyForBackup(userId) ? mService.getAvailableRestoreToken(userId,
packageName) : 0;
diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
index 79f8a7e4e9ae..115e9240c6fe 100644
--- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
@@ -804,7 +804,7 @@ public class UserBackupManagerService {
public BackupAgent makeMetadataAgent() {
PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent(mPackageManager, mUserId);
pmAgent.attach(mContext);
- pmAgent.onCreate();
+ pmAgent.onCreate(UserHandle.of(mUserId));
return pmAgent;
}
@@ -815,7 +815,7 @@ public class UserBackupManagerService {
PackageManagerBackupAgent pmAgent =
new PackageManagerBackupAgent(mPackageManager, packages, mUserId);
pmAgent.attach(mContext);
- pmAgent.onCreate();
+ pmAgent.onCreate(UserHandle.of(mUserId));
return pmAgent;
}
@@ -910,10 +910,10 @@ public class UserBackupManagerService {
long lastBackup = in.readLong();
foundApps.add(pkgName); // all apps that we've addressed already
try {
- PackageInfo pkg = mPackageManager.getPackageInfo(pkgName, 0);
+ PackageInfo pkg = mPackageManager.getPackageInfoAsUser(pkgName, 0, mUserId);
if (AppBackupUtils.appGetsFullBackup(pkg)
- && AppBackupUtils.appIsEligibleForBackup(
- pkg.applicationInfo, mPackageManager)) {
+ && AppBackupUtils.appIsEligibleForBackup(pkg.applicationInfo,
+ mUserId)) {
schedule.add(new FullBackupEntry(pkgName, lastBackup));
} else {
if (DEBUG) {
@@ -933,8 +933,8 @@ public class UserBackupManagerService {
// scan to make sure that we're tracking all full-backup candidates properly
for (PackageInfo app : apps) {
if (AppBackupUtils.appGetsFullBackup(app)
- && AppBackupUtils.appIsEligibleForBackup(
- app.applicationInfo, mPackageManager)) {
+ && AppBackupUtils.appIsEligibleForBackup(app.applicationInfo,
+ mUserId)) {
if (!foundApps.contains(app.packageName)) {
if (MORE_DEBUG) {
Slog.i(TAG, "New full backup app " + app.packageName + " found");
@@ -960,7 +960,7 @@ public class UserBackupManagerService {
schedule = new ArrayList<>(apps.size());
for (PackageInfo info : apps) {
if (AppBackupUtils.appGetsFullBackup(info) && AppBackupUtils.appIsEligibleForBackup(
- info.applicationInfo, mPackageManager)) {
+ info.applicationInfo, mUserId)) {
schedule.add(new FullBackupEntry(info.packageName, 0));
}
}
@@ -1222,8 +1222,8 @@ public class UserBackupManagerService {
mPackageManager.getPackageInfoAsUser(
packageName, /* flags */ 0, mUserId);
if (AppBackupUtils.appGetsFullBackup(app)
- && AppBackupUtils.appIsEligibleForBackup(
- app.applicationInfo, mPackageManager)) {
+ && AppBackupUtils.appIsEligibleForBackup(app.applicationInfo,
+ mUserId)) {
enqueueFullBackup(packageName, now);
scheduleNextFullBackupJob(0);
} else {
@@ -1618,8 +1618,7 @@ public class UserBackupManagerService {
try {
PackageInfo packageInfo = mPackageManager.getPackageInfoAsUser(packageName,
PackageManager.GET_SIGNING_CERTIFICATES, mUserId);
- if (!AppBackupUtils.appIsEligibleForBackup(packageInfo.applicationInfo,
- mPackageManager)) {
+ if (!AppBackupUtils.appIsEligibleForBackup(packageInfo.applicationInfo, mUserId)) {
BackupObserverUtils.sendBackupOnPackageResult(observer, packageName,
BackupManager.ERROR_BACKUP_NOT_ALLOWED);
continue;
@@ -2095,7 +2094,8 @@ public class UserBackupManagerService {
}
try {
- PackageInfo appInfo = mPackageManager.getPackageInfo(entry.packageName, 0);
+ PackageInfo appInfo = mPackageManager.getPackageInfoAsUser(
+ entry.packageName, 0, mUserId);
if (!AppBackupUtils.appGetsFullBackup(appInfo)) {
// The head app isn't supposed to get full-data backups [any more];
// so we cull it and force a loop around to consider the new head
diff --git a/services/backup/java/com/android/server/backup/fullbackup/PerformAdbBackupTask.java b/services/backup/java/com/android/server/backup/fullbackup/PerformAdbBackupTask.java
index 31786d749f0a..0a7159bfe1b7 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/PerformAdbBackupTask.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/PerformAdbBackupTask.java
@@ -286,7 +286,8 @@ public class PerformAdbBackupTask extends FullBackupTask implements BackupRestor
Iterator<Entry<String, PackageInfo>> iter = packagesToBackup.entrySet().iterator();
while (iter.hasNext()) {
PackageInfo pkg = iter.next().getValue();
- if (!AppBackupUtils.appIsEligibleForBackup(pkg.applicationInfo, pm)
+ if (!AppBackupUtils.appIsEligibleForBackup(pkg.applicationInfo,
+ mUserBackupManagerService.getUserId())
|| AppBackupUtils.appIsStopped(pkg.applicationInfo)) {
iter.remove();
if (DEBUG) {
diff --git a/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java b/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
index 0fb4f93e542b..86e679f16f66 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
@@ -143,6 +143,7 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba
private final int mBackupRunnerOpToken;
private final OnTaskFinishedListener mListener;
private final TransportClient mTransportClient;
+ private final int mUserId;
// This is true when a backup operation for some package is in progress.
private volatile boolean mIsDoingBackup;
@@ -173,6 +174,7 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba
mAgentTimeoutParameters = Preconditions.checkNotNull(
backupManagerService.getAgentTimeoutParameters(),
"Timeout parameters cannot be null");
+ mUserId = backupManagerService.getUserId();
if (backupManagerService.isBackupOperationInProgress()) {
if (DEBUG) {
@@ -187,9 +189,10 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba
for (String pkg : whichPackages) {
try {
PackageManager pm = backupManagerService.getPackageManager();
- PackageInfo info = pm.getPackageInfo(pkg, PackageManager.GET_SIGNING_CERTIFICATES);
+ PackageInfo info = pm.getPackageInfoAsUser(pkg,
+ PackageManager.GET_SIGNING_CERTIFICATES, mUserId);
mCurrentPackage = info;
- if (!AppBackupUtils.appIsEligibleForBackup(info.applicationInfo, pm)) {
+ if (!AppBackupUtils.appIsEligibleForBackup(info.applicationInfo, mUserId)) {
// Cull any packages that have indicated that backups are not permitted,
// that run as system-domain uids but do not define their own backup agents,
// as well as any explicit mention of the 'special' shared-storage agent
@@ -633,7 +636,7 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba
unregisterTask();
if (mJob != null) {
- mJob.finishBackupPass(backupManagerService.getUserId());
+ mJob.finishBackupPass(mUserId);
}
synchronized (backupManagerService.getQueueLock()) {
diff --git a/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java b/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java
index cfc129e11c6e..294eb0128b2c 100644
--- a/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java
+++ b/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java
@@ -489,7 +489,7 @@ public class KeyValueBackupTask implements BackupRestoreTask, Runnable {
throw AgentException.permanent(e);
}
ApplicationInfo applicationInfo = packageInfo.applicationInfo;
- if (!AppBackupUtils.appIsEligibleForBackup(applicationInfo, mPackageManager)) {
+ if (!AppBackupUtils.appIsEligibleForBackup(applicationInfo, mUserId)) {
mReporter.onPackageNotEligibleForBackup(packageName);
throw AgentException.permanent();
}
diff --git a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
index b3d9fbcb88d3..c5389fa5f878 100644
--- a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
+++ b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
@@ -230,7 +230,7 @@ public class FullRestoreEngine extends RestoreEngine {
PackageManagerInternal.class);
RestorePolicy restorePolicy = tarBackupReader.chooseRestorePolicy(
mBackupManagerService.getPackageManager(), allowApks, info, signatures,
- pmi);
+ pmi, mUserId);
mManifestSignatures.put(info.packageName, signatures);
mPackagePolicies.put(pkg, restorePolicy);
mPackageInstallers.put(pkg, info.installerPackageName);
@@ -332,8 +332,9 @@ public class FullRestoreEngine extends RestoreEngine {
}
try {
- mTargetApp = mBackupManagerService.getPackageManager()
- .getApplicationInfoAsUser(pkg, 0, mUserId);
+ mTargetApp =
+ mBackupManagerService.getPackageManager()
+ .getApplicationInfoAsUser(pkg, 0, mUserId);
// If we haven't sent any data to this app yet, we probably
// need to clear it first. Check that.
diff --git a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
index d01f77bfd84c..7763d7b9adc0 100644
--- a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
+++ b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
@@ -232,7 +232,7 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask {
continue;
}
- if (AppBackupUtils.appIsEligibleForBackup(info.applicationInfo, pm)) {
+ if (AppBackupUtils.appIsEligibleForBackup(info.applicationInfo, mUserId)) {
mAcceptSet.add(info);
}
} catch (NameNotFoundException e) {
diff --git a/services/backup/java/com/android/server/backup/utils/AppBackupUtils.java b/services/backup/java/com/android/server/backup/utils/AppBackupUtils.java
index 054879b077ad..2db89289e4fc 100644
--- a/services/backup/java/com/android/server/backup/utils/AppBackupUtils.java
+++ b/services/backup/java/com/android/server/backup/utils/AppBackupUtils.java
@@ -18,19 +18,25 @@ package com.android.server.backup.utils;
import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
import static com.android.server.backup.BackupManagerService.TAG;
+import static com.android.server.backup.UserBackupManagerService.PACKAGE_MANAGER_SENTINEL;
import static com.android.server.backup.UserBackupManagerService.SHARED_BACKUP_AGENT_PACKAGE;
import android.annotation.Nullable;
+import android.app.AppGlobals;
import android.app.backup.BackupTransport;
import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageManager;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.Signature;
import android.content.pm.SigningInfo;
import android.os.Process;
+import android.os.RemoteException;
+import android.os.UserHandle;
import android.util.Slog;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.backup.IBackupTransport;
import com.android.internal.util.ArrayUtils;
import com.android.server.backup.transport.TransportClient;
@@ -39,7 +45,6 @@ import com.android.server.backup.transport.TransportClient;
* Utility methods wrapping operations on ApplicationInfo and PackageInfo.
*/
public class AppBackupUtils {
-
private static final boolean DEBUG = false;
/**
@@ -54,15 +59,30 @@ public class AppBackupUtils {
* <li>it is the special shared-storage backup package used for 'adb backup'
* </ol>
*/
- public static boolean appIsEligibleForBackup(ApplicationInfo app, PackageManager pm) {
+ public static boolean appIsEligibleForBackup(ApplicationInfo app, int userId) {
+ return appIsEligibleForBackup(app, AppGlobals.getPackageManager(), userId);
+ }
+
+ @VisibleForTesting
+ static boolean appIsEligibleForBackup(ApplicationInfo app,
+ IPackageManager packageManager, int userId) {
// 1. their manifest states android:allowBackup="false"
if ((app.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) == 0) {
return false;
}
- // 2. they run as a system-level uid but do not supply their own backup agent
- if ((app.uid < Process.FIRST_APPLICATION_UID) && (app.backupAgentName == null)) {
- return false;
+ // 2. they run as a system-level uid
+ if ((app.uid < Process.FIRST_APPLICATION_UID)) {
+ // and the backup is happening for non-system user
+ if (userId != UserHandle.USER_SYSTEM && !app.packageName.equals(
+ PACKAGE_MANAGER_SENTINEL)) {
+ return false;
+ }
+
+ // or do not supply their own backup agent
+ if (app.backupAgentName == null) {
+ return false;
+ }
}
// 3. it is the special shared-storage backup package used for 'adb backup'
@@ -75,9 +95,7 @@ public class AppBackupUtils {
return false;
}
- // Everything else checks out; the only remaining roadblock would be if the
- // package were disabled
- return !appIsDisabled(app, pm);
+ return !appIsDisabled(app, packageManager, userId);
}
/**
@@ -99,9 +117,9 @@ public class AppBackupUtils {
PackageInfo packageInfo = pm.getPackageInfoAsUser(packageName,
PackageManager.GET_SIGNING_CERTIFICATES, userId);
ApplicationInfo applicationInfo = packageInfo.applicationInfo;
- if (!appIsEligibleForBackup(applicationInfo, pm)
+ if (!appIsEligibleForBackup(applicationInfo, userId)
|| appIsStopped(applicationInfo)
- || appIsDisabled(applicationInfo, pm)) {
+ || appIsDisabled(applicationInfo, userId)) {
return false;
}
if (transportClient != null) {
@@ -123,8 +141,22 @@ public class AppBackupUtils {
}
/** Avoid backups of 'disabled' apps. */
- public static boolean appIsDisabled(ApplicationInfo app, PackageManager pm) {
- switch (pm.getApplicationEnabledSetting(app.packageName)) {
+ static boolean appIsDisabled(ApplicationInfo app, int userId) {
+ return appIsDisabled(app, AppGlobals.getPackageManager(), userId);
+ }
+
+ @VisibleForTesting
+ static boolean appIsDisabled(ApplicationInfo app,
+ IPackageManager packageManager, int userId) {
+ int enabledSetting;
+ try {
+ enabledSetting = packageManager.getApplicationEnabledSetting(app.packageName, userId);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to get application enabled setting: " + e);
+ return false;
+ }
+
+ switch (enabledSetting) {
case PackageManager.COMPONENT_ENABLED_STATE_DISABLED:
case PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER:
case PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED:
diff --git a/services/backup/java/com/android/server/backup/utils/TarBackupReader.java b/services/backup/java/com/android/server/backup/utils/TarBackupReader.java
index 0f4b6810f15b..f4b235a3f3e1 100644
--- a/services/backup/java/com/android/server/backup/utils/TarBackupReader.java
+++ b/services/backup/java/com/android/server/backup/utils/TarBackupReader.java
@@ -383,11 +383,12 @@ public class TarBackupReader {
* @param allowApks - allow restore set to include apks.
* @param info - file metadata.
* @param signatures - array of signatures parsed from backup file.
+ * @param userId - ID of the user for which restore is performed.
* @return a restore policy constant.
*/
public RestorePolicy chooseRestorePolicy(PackageManager packageManager,
boolean allowApks, FileMetadata info, Signature[] signatures,
- PackageManagerInternal pmi) {
+ PackageManagerInternal pmi, int userId) {
if (signatures == null) {
return RestorePolicy.IGNORE;
}
@@ -396,8 +397,8 @@ public class TarBackupReader {
// Okay, got the manifest info we need...
try {
- PackageInfo pkgInfo = packageManager.getPackageInfo(
- info.packageName, PackageManager.GET_SIGNING_CERTIFICATES);
+ PackageInfo pkgInfo = packageManager.getPackageInfoAsUser(
+ info.packageName, PackageManager.GET_SIGNING_CERTIFICATES, userId);
// Fall through to IGNORE if the app explicitly disallows backup
final int flags = pkgInfo.applicationInfo.flags;
if ((flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0) {
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
index e4bbcd67d4df..844096d9d717 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
@@ -35,6 +35,7 @@ import android.os.UserManager;
import android.util.LocalLog;
import android.util.Slog;
import android.view.contentcapture.IContentCaptureManager;
+import android.view.contentcapture.UserDataRemovalRequest;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.os.IResultReceiver;
@@ -224,6 +225,15 @@ public final class ContentCaptureManagerService extends
}
@Override
+ public void removeUserData(@UserIdInt int userId, @NonNull UserDataRemovalRequest request) {
+ Preconditions.checkNotNull(request);
+ synchronized (mLock) {
+ final ContentCapturePerUserService service = getServiceForUserLocked(userId);
+ service.removeUserDataLocked(request);
+ }
+ }
+
+ @Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) return;
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
index 8d2c79bd9923..bc0e19a69040 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
@@ -28,6 +28,7 @@ import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_STRUC
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.UserIdInt;
+import android.app.ActivityManagerInternal;
import android.app.AppGlobals;
import android.app.assist.AssistContent;
import android.app.assist.AssistStructure;
@@ -35,18 +36,22 @@ import android.content.ComponentName;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ServiceInfo;
+import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.service.contentcapture.ContentCaptureService;
import android.service.contentcapture.IContentCaptureServiceCallback;
import android.service.contentcapture.SnapshotData;
import android.util.ArrayMap;
import android.util.Log;
import android.util.Slog;
+import android.view.contentcapture.UserDataRemovalRequest;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.os.IResultReceiver;
+import com.android.server.LocalServices;
import com.android.server.contentcapture.RemoteContentCaptureService.ContentCaptureServiceCallbacks;
import com.android.server.infra.AbstractPerUserSystemService;
@@ -249,6 +254,39 @@ final class ContentCapturePerUserService
}
@GuardedBy("mLock")
+ public void removeUserDataLocked(@NonNull UserDataRemovalRequest request) {
+ if (!isEnabledLocked()) {
+ return;
+ }
+ assertCallerLocked(request.getPackageName());
+ mRemoteService.onUserDataRemovalRequest(request);
+ }
+
+ /**
+ * Asserts the component is owned by the caller.
+ */
+ @GuardedBy("mLock")
+ private void assertCallerLocked(@NonNull String packageName) {
+ final PackageManager pm = getContext().getPackageManager();
+ final int callingUid = Binder.getCallingUid();
+ final int packageUid;
+ try {
+ packageUid = pm.getPackageUidAsUser(packageName, UserHandle.getCallingUserId());
+ } catch (NameNotFoundException e) {
+ throw new SecurityException("Could not verify UID for " + packageName);
+ }
+ if (callingUid != packageUid && !LocalServices.getService(ActivityManagerInternal.class)
+ .hasRunningActivity(callingUid, packageName)) {
+ final String[] packages = pm.getPackagesForUid(callingUid);
+ final String callingPackage = packages != null ? packages[0] : "uid-" + callingUid;
+ Slog.w(TAG, "App (package=" + callingPackage + ", UID=" + callingUid
+ + ") passed package (" + packageName + ") owned by UID " + packageUid);
+
+ throw new SecurityException("Invalid package: " + packageName);
+ }
+ }
+
+ @GuardedBy("mLock")
public boolean sendActivityAssistDataLocked(@NonNull IBinder activityToken,
@NonNull Bundle data) {
final String id = getSessionId(activityToken);
diff --git a/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java b/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java
index 12742ca0a46f..54eea5d8591c 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java
@@ -26,6 +26,7 @@ import android.service.contentcapture.SnapshotData;
import android.text.format.DateUtils;
import android.util.Slog;
import android.view.contentcapture.ContentCaptureContext;
+import android.view.contentcapture.UserDataRemovalRequest;
import com.android.internal.infra.AbstractMultiplePendingRequestsRemoteService;
import com.android.internal.os.IResultReceiver;
@@ -108,6 +109,13 @@ final class RemoteContentCaptureService
scheduleAsyncRequest((s) -> s.onActivitySnapshot(sessionId, snapshotData));
}
+ /**
+ * Called by {@link ContentCaptureServerSession} to request removal of user data.
+ */
+ public void onUserDataRemovalRequest(@NonNull UserDataRemovalRequest request) {
+ scheduleAsyncRequest((s) -> s.onUserDataRemovalRequest(request));
+ }
+
public interface ContentCaptureServiceCallbacks
extends VultureCallback<RemoteContentCaptureService> {
// NOTE: so far we don't need to notify the callback implementation
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index fcd136c65169..e3dcb7d331cf 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -77,6 +77,7 @@ import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.KeyValueListParser;
import android.util.Log;
+import android.util.LongArrayQueue;
import android.util.NtpTrustedTime;
import android.util.Pair;
import android.util.Slog;
@@ -91,6 +92,7 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
+import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.LocalLog;
import com.android.internal.util.StatLogger;
import com.android.server.AppStateTracker.Listener;
@@ -145,6 +147,7 @@ class AlarmManagerService extends SystemService {
static final String TIMEZONE_PROPERTY = "persist.sys.timezone";
static final int TICK_HISTORY_DEPTH = 10;
+ static final long MILLIS_IN_DAY = 24 * 60 * 60 * 1000;
// Indices into the APP_STANDBY_MIN_DELAYS and KEYS_APP_STANDBY_DELAY arrays
static final int ACTIVE_INDEX = 0;
@@ -195,6 +198,7 @@ class AlarmManagerService extends SystemService {
ArrayList<Alarm> mPendingNonWakeupAlarms = new ArrayList<>();
ArrayList<InFlight> mInFlight = new ArrayList<>();
AlarmHandler mHandler;
+ AppWakeupHistory mAppWakeupHistory;
ClockReceiver mClockReceiver;
final DeliveryTracker mDeliveryTracker = new DeliveryTracker();
Intent mTimeTickIntent;
@@ -277,7 +281,91 @@ class AlarmManagerService extends SystemService {
private AppStateTracker mAppStateTracker;
private boolean mAppStandbyParole;
- private ArrayMap<Pair<String, Integer>, Long> mLastAlarmDeliveredForPackage = new ArrayMap<>();
+
+ /**
+ * A rolling window history of previous times when an alarm was sent to a package.
+ */
+ private static class AppWakeupHistory {
+ private ArrayMap<Pair<String, Integer>, LongArrayQueue> mPackageHistory =
+ new ArrayMap<>();
+ private long mWindowSize;
+
+ AppWakeupHistory(long windowSize) {
+ mWindowSize = windowSize;
+ }
+
+ void recordAlarmForPackage(String packageName, int userId, long nowElapsed) {
+ final Pair<String, Integer> packageUser = Pair.create(packageName, userId);
+ LongArrayQueue history = mPackageHistory.get(packageUser);
+ if (history == null) {
+ history = new LongArrayQueue();
+ mPackageHistory.put(packageUser, history);
+ }
+ if (history.size() == 0 || history.peekLast() < nowElapsed) {
+ history.addLast(nowElapsed);
+ }
+ snapToWindow(history);
+ }
+
+ void removeForUser(int userId) {
+ for (int i = mPackageHistory.size() - 1; i >= 0; i--) {
+ final Pair<String, Integer> packageUserKey = mPackageHistory.keyAt(i);
+ if (packageUserKey.second == userId) {
+ mPackageHistory.removeAt(i);
+ }
+ }
+ }
+
+ void removeForPackage(String packageName, int userId) {
+ final Pair<String, Integer> packageUser = Pair.create(packageName, userId);
+ mPackageHistory.remove(packageUser);
+ }
+
+ private void snapToWindow(LongArrayQueue history) {
+ while (history.peekFirst() + mWindowSize < history.peekLast()) {
+ history.removeFirst();
+ }
+ }
+
+ int getTotalWakeupsInWindow(String packageName, int userId) {
+ final LongArrayQueue history = mPackageHistory.get(Pair.create(packageName, userId));
+ return (history == null) ? 0 : history.size();
+ }
+
+ long getLastWakeupForPackage(String packageName, int userId, int positionFromEnd) {
+ final LongArrayQueue history = mPackageHistory.get(Pair.create(packageName, userId));
+ if (history == null) {
+ return 0;
+ }
+ final int i = history.size() - positionFromEnd;
+ return (i < 0) ? 0 : history.get(i);
+ }
+
+ void dump(PrintWriter pw, String prefix, long nowElapsed) {
+ dump(new IndentingPrintWriter(pw, " ").setIndent(prefix), nowElapsed);
+ }
+
+ void dump(IndentingPrintWriter pw, long nowElapsed) {
+ pw.println("App Alarm history:");
+ pw.increaseIndent();
+ for (int i = 0; i < mPackageHistory.size(); i++) {
+ final Pair<String, Integer> packageUser = mPackageHistory.keyAt(i);
+ final LongArrayQueue timestamps = mPackageHistory.valueAt(i);
+ pw.print(packageUser.first);
+ pw.print(", u");
+ pw.print(packageUser.second);
+ pw.print(": ");
+ // limit dumping to a max of 100 values
+ final int lastIdx = Math.max(0, timestamps.size() - 100);
+ for (int j = timestamps.size() - 1; j >= lastIdx; j--) {
+ TimeUtils.formatDuration(timestamps.get(j), nowElapsed, pw);
+ pw.print(", ");
+ }
+ pw.println();
+ }
+ pw.decreaseIndent();
+ }
+ }
/**
* All times are in milliseconds. These constants are kept synchronized with the system
@@ -302,6 +390,17 @@ class AlarmManagerService extends SystemService {
= "allow_while_idle_whitelist_duration";
@VisibleForTesting
static final String KEY_LISTENER_TIMEOUT = "listener_timeout";
+ @VisibleForTesting
+ static final String KEY_APP_STANDBY_QUOTAS_ENABLED = "app_standby_quotas_enabled";
+ private static final String KEY_APP_STANDBY_WINDOW = "app_standby_window";
+ @VisibleForTesting
+ final String[] KEYS_APP_STANDBY_QUOTAS = {
+ "standby_active_quota",
+ "standby_working_quota",
+ "standby_frequent_quota",
+ "standby_rare_quota",
+ "standby_never_quota",
+ };
// Keys for specifying throttling delay based on app standby bucketing
private final String[] KEYS_APP_STANDBY_DELAY = {
@@ -319,6 +418,18 @@ class AlarmManagerService extends SystemService {
private static final long DEFAULT_ALLOW_WHILE_IDLE_LONG_TIME = 9*60*1000;
private static final long DEFAULT_ALLOW_WHILE_IDLE_WHITELIST_DURATION = 10*1000;
private static final long DEFAULT_LISTENER_TIMEOUT = 5 * 1000;
+ private static final boolean DEFAULT_APP_STANDBY_QUOTAS_ENABLED = true;
+ private static final long DEFAULT_APP_STANDBY_WINDOW = 60 * 60 * 1000; // 1 hr
+ /**
+ * Max number of times an app can receive alarms in {@link #APP_STANDBY_WINDOW}
+ */
+ private final int[] DEFAULT_APP_STANDBY_QUOTAS = {
+ 720, // Active
+ 10, // Working
+ 2, // Frequent
+ 1, // Rare
+ 0 // Never
+ };
private final long[] DEFAULT_APP_STANDBY_DELAYS = {
0, // Active
6 * 60_000, // Working
@@ -348,8 +459,11 @@ class AlarmManagerService extends SystemService {
// Direct alarm listener callback timeout
public long LISTENER_TIMEOUT = DEFAULT_LISTENER_TIMEOUT;
+ public boolean APP_STANDBY_QUOTAS_ENABLED = DEFAULT_APP_STANDBY_QUOTAS_ENABLED;
+ public long APP_STANDBY_WINDOW = DEFAULT_APP_STANDBY_WINDOW;
public long[] APP_STANDBY_MIN_DELAYS = new long[DEFAULT_APP_STANDBY_DELAYS.length];
+ public int[] APP_STANDBY_QUOTAS = new int[DEFAULT_APP_STANDBY_QUOTAS.length];
private ContentResolver mResolver;
private final KeyValueListParser mParser = new KeyValueListParser(',');
@@ -409,48 +523,90 @@ class AlarmManagerService extends SystemService {
DEFAULT_APP_STANDBY_DELAYS[ACTIVE_INDEX]);
for (int i = WORKING_INDEX; i < KEYS_APP_STANDBY_DELAY.length; i++) {
APP_STANDBY_MIN_DELAYS[i] = mParser.getDurationMillis(KEYS_APP_STANDBY_DELAY[i],
- Math.max(APP_STANDBY_MIN_DELAYS[i-1], DEFAULT_APP_STANDBY_DELAYS[i]));
+ Math.max(APP_STANDBY_MIN_DELAYS[i - 1], DEFAULT_APP_STANDBY_DELAYS[i]));
+ }
+
+ APP_STANDBY_QUOTAS_ENABLED = mParser.getBoolean(KEY_APP_STANDBY_QUOTAS_ENABLED,
+ DEFAULT_APP_STANDBY_QUOTAS_ENABLED);
+
+ APP_STANDBY_WINDOW = mParser.getLong(KEY_APP_STANDBY_WINDOW,
+ DEFAULT_APP_STANDBY_WINDOW);
+ if (APP_STANDBY_WINDOW > DEFAULT_APP_STANDBY_WINDOW) {
+ Slog.w(TAG, "Cannot exceed the app_standby_window size of "
+ + DEFAULT_APP_STANDBY_WINDOW);
+ APP_STANDBY_WINDOW = DEFAULT_APP_STANDBY_WINDOW;
+ } else if (APP_STANDBY_WINDOW < DEFAULT_APP_STANDBY_WINDOW) {
+ // Not recommended outside of testing.
+ Slog.w(TAG, "Using a non-default app_standby_window of " + APP_STANDBY_WINDOW);
+ }
+
+ APP_STANDBY_QUOTAS[ACTIVE_INDEX] = mParser.getInt(
+ KEYS_APP_STANDBY_QUOTAS[ACTIVE_INDEX],
+ DEFAULT_APP_STANDBY_QUOTAS[ACTIVE_INDEX]);
+ for (int i = WORKING_INDEX; i < KEYS_APP_STANDBY_QUOTAS.length; i++) {
+ APP_STANDBY_QUOTAS[i] = mParser.getInt(KEYS_APP_STANDBY_QUOTAS[i],
+ Math.min(APP_STANDBY_QUOTAS[i - 1], DEFAULT_APP_STANDBY_QUOTAS[i]));
}
updateAllowWhileIdleWhitelistDurationLocked();
}
}
- void dump(PrintWriter pw) {
- pw.println(" Settings:");
+ void dump(PrintWriter pw, String prefix) {
+ dump(new IndentingPrintWriter(pw, " ").setIndent(prefix));
+ }
+
+ void dump(IndentingPrintWriter pw) {
+ pw.println("Settings:");
- pw.print(" "); pw.print(KEY_MIN_FUTURITY); pw.print("=");
+ pw.increaseIndent();
+
+ pw.print(KEY_MIN_FUTURITY); pw.print("=");
TimeUtils.formatDuration(MIN_FUTURITY, pw);
pw.println();
- pw.print(" "); pw.print(KEY_MIN_INTERVAL); pw.print("=");
+ pw.print(KEY_MIN_INTERVAL); pw.print("=");
TimeUtils.formatDuration(MIN_INTERVAL, pw);
pw.println();
- pw.print(" "); pw.print(KEY_MAX_INTERVAL); pw.print("=");
+ pw.print(KEY_MAX_INTERVAL); pw.print("=");
TimeUtils.formatDuration(MAX_INTERVAL, pw);
pw.println();
- pw.print(" "); pw.print(KEY_LISTENER_TIMEOUT); pw.print("=");
+ pw.print(KEY_LISTENER_TIMEOUT); pw.print("=");
TimeUtils.formatDuration(LISTENER_TIMEOUT, pw);
pw.println();
- pw.print(" "); pw.print(KEY_ALLOW_WHILE_IDLE_SHORT_TIME); pw.print("=");
+ pw.print(KEY_ALLOW_WHILE_IDLE_SHORT_TIME); pw.print("=");
TimeUtils.formatDuration(ALLOW_WHILE_IDLE_SHORT_TIME, pw);
pw.println();
- pw.print(" "); pw.print(KEY_ALLOW_WHILE_IDLE_LONG_TIME); pw.print("=");
+ pw.print(KEY_ALLOW_WHILE_IDLE_LONG_TIME); pw.print("=");
TimeUtils.formatDuration(ALLOW_WHILE_IDLE_LONG_TIME, pw);
pw.println();
- pw.print(" "); pw.print(KEY_ALLOW_WHILE_IDLE_WHITELIST_DURATION); pw.print("=");
+ pw.print(KEY_ALLOW_WHILE_IDLE_WHITELIST_DURATION); pw.print("=");
TimeUtils.formatDuration(ALLOW_WHILE_IDLE_WHITELIST_DURATION, pw);
pw.println();
for (int i = 0; i < KEYS_APP_STANDBY_DELAY.length; i++) {
- pw.print(" "); pw.print(KEYS_APP_STANDBY_DELAY[i]); pw.print("=");
+ pw.print(KEYS_APP_STANDBY_DELAY[i]); pw.print("=");
TimeUtils.formatDuration(APP_STANDBY_MIN_DELAYS[i], pw);
pw.println();
}
+
+ pw.print(KEY_APP_STANDBY_QUOTAS_ENABLED); pw.print("=");
+ pw.println(APP_STANDBY_QUOTAS_ENABLED);
+
+ pw.print(KEY_APP_STANDBY_WINDOW); pw.print("=");
+ TimeUtils.formatDuration(APP_STANDBY_WINDOW, pw);
+ pw.println();
+
+ for (int i = 0; i < KEYS_APP_STANDBY_QUOTAS.length; i++) {
+ pw.print(KEYS_APP_STANDBY_QUOTAS[i]); pw.print("=");
+ pw.println(APP_STANDBY_QUOTAS[i]);
+ }
+
+ pw.decreaseIndent();
}
void dumpProto(ProtoOutputStream proto, long fieldId) {
@@ -925,7 +1081,7 @@ class AlarmManagerService extends SystemService {
if (targetPackages != null && !targetPackages.contains(packageUser)) {
continue;
}
- if (adjustDeliveryTimeBasedOnStandbyBucketLocked(alarm)) {
+ if (adjustDeliveryTimeBasedOnBucketLocked(alarm)) {
batch.remove(alarm);
rescheduledAlarms.add(alarm);
}
@@ -1300,6 +1456,7 @@ class AlarmManagerService extends SystemService {
synchronized (mLock) {
mHandler = new AlarmHandler();
mConstants = new Constants(mHandler);
+ mAppWakeupHistory = new AppWakeupHistory(Constants.DEFAULT_APP_STANDBY_WINDOW);
mNextWakeup = mNextNonWakeup = 0;
@@ -1583,6 +1740,27 @@ class AlarmManagerService extends SystemService {
}
/**
+ * Returns the maximum alarms that an app in the specified bucket can receive in a rolling time
+ * window given by {@link Constants#APP_STANDBY_WINDOW}
+ */
+ @VisibleForTesting
+ int getQuotaForBucketLocked(int bucket) {
+ final int index;
+ if (bucket <= UsageStatsManager.STANDBY_BUCKET_ACTIVE) {
+ index = ACTIVE_INDEX;
+ } else if (bucket <= UsageStatsManager.STANDBY_BUCKET_WORKING_SET) {
+ index = WORKING_INDEX;
+ } else if (bucket <= UsageStatsManager.STANDBY_BUCKET_FREQUENT) {
+ index = FREQUENT_INDEX;
+ } else if (bucket < UsageStatsManager.STANDBY_BUCKET_NEVER) {
+ index = RARE_INDEX;
+ } else {
+ index = NEVER_INDEX;
+ }
+ return mConstants.APP_STANDBY_QUOTAS[index];
+ }
+
+ /**
* Return the minimum time that should elapse before an app in the specified bucket
* can receive alarms again
*/
@@ -1608,7 +1786,7 @@ class AlarmManagerService extends SystemService {
* @param alarm The alarm to adjust
* @return true if the alarm delivery time was updated.
*/
- private boolean adjustDeliveryTimeBasedOnStandbyBucketLocked(Alarm alarm) {
+ private boolean adjustDeliveryTimeBasedOnBucketLocked(Alarm alarm) {
if (isExemptFromAppStandby(alarm)) {
return false;
}
@@ -1629,18 +1807,49 @@ class AlarmManagerService extends SystemService {
final int standbyBucket = mUsageStatsManagerInternal.getAppStandbyBucket(
sourcePackage, sourceUserId, mInjector.getElapsedRealtime());
- final Pair<String, Integer> packageUser = Pair.create(sourcePackage, sourceUserId);
- final long lastElapsed = mLastAlarmDeliveredForPackage.getOrDefault(packageUser, 0L);
- if (lastElapsed > 0) {
- final long minElapsed = lastElapsed + getMinDelayForBucketLocked(standbyBucket);
- if (alarm.expectedWhenElapsed < minElapsed) {
- alarm.whenElapsed = alarm.maxWhenElapsed = minElapsed;
- } else {
- // app is now eligible to run alarms at the originally requested window.
+ if (mConstants.APP_STANDBY_QUOTAS_ENABLED) {
+ // Quota deferring implementation:
+ final int wakeupsInWindow = mAppWakeupHistory.getTotalWakeupsInWindow(sourcePackage,
+ sourceUserId);
+ final int quotaForBucket = getQuotaForBucketLocked(standbyBucket);
+ boolean deferred = false;
+ if (wakeupsInWindow >= quotaForBucket) {
+ final long minElapsed;
+ if (quotaForBucket <= 0) {
+ // Just keep deferring for a day till the quota changes
+ minElapsed = mInjector.getElapsedRealtime() + MILLIS_IN_DAY;
+ } else {
+ // Suppose the quota for window was q, and the qth last delivery time for this
+ // package was t(q) then the next delivery must be after t(q) + <window_size>
+ final long t = mAppWakeupHistory.getLastWakeupForPackage(sourcePackage,
+ sourceUserId, quotaForBucket);
+ minElapsed = t + 1 + mConstants.APP_STANDBY_WINDOW;
+ }
+ if (alarm.expectedWhenElapsed < minElapsed) {
+ alarm.whenElapsed = alarm.maxWhenElapsed = minElapsed;
+ deferred = true;
+ }
+ }
+ if (!deferred) {
// Restore original requirements in case they were changed earlier.
alarm.whenElapsed = alarm.expectedWhenElapsed;
alarm.maxWhenElapsed = alarm.expectedMaxWhenElapsed;
}
+ } else {
+ // Minimum delay deferring implementation:
+ final long lastElapsed = mAppWakeupHistory.getLastWakeupForPackage(sourcePackage,
+ sourceUserId, 1);
+ if (lastElapsed > 0) {
+ final long minElapsed = lastElapsed + getMinDelayForBucketLocked(standbyBucket);
+ if (alarm.expectedWhenElapsed < minElapsed) {
+ alarm.whenElapsed = alarm.maxWhenElapsed = minElapsed;
+ } else {
+ // app is now eligible to run alarms at the originally requested window.
+ // Restore original requirements in case they were changed earlier.
+ alarm.whenElapsed = alarm.expectedWhenElapsed;
+ alarm.maxWhenElapsed = alarm.expectedMaxWhenElapsed;
+ }
+ }
}
return (oldWhenElapsed != alarm.whenElapsed || oldMaxWhenElapsed != alarm.maxWhenElapsed);
}
@@ -1696,7 +1905,7 @@ class AlarmManagerService extends SystemService {
mAllowWhileIdleDispatches.add(ent);
}
}
- adjustDeliveryTimeBasedOnStandbyBucketLocked(a);
+ adjustDeliveryTimeBasedOnBucketLocked(a);
insertAndBatchAlarmLocked(a);
if (a.alarmClock != null) {
@@ -1915,7 +2124,7 @@ class AlarmManagerService extends SystemService {
void dumpImpl(PrintWriter pw) {
synchronized (mLock) {
pw.println("Current Alarm Manager state:");
- mConstants.dump(pw);
+ mConstants.dump(pw, " ");
pw.println();
if (mAppStateTracker != null) {
@@ -2065,14 +2274,7 @@ class AlarmManagerService extends SystemService {
pw.println(" none");
}
- pw.println(" mLastAlarmDeliveredForPackage:");
- for (int i = 0; i < mLastAlarmDeliveredForPackage.size(); i++) {
- Pair<String, Integer> packageUser = mLastAlarmDeliveredForPackage.keyAt(i);
- pw.print(" Package " + packageUser.first + ", User " + packageUser.second + ":");
- TimeUtils.formatDuration(mLastAlarmDeliveredForPackage.valueAt(i), nowELAPSED, pw);
- pw.println();
- }
- pw.println();
+ mAppWakeupHistory.dump(pw, " ", nowELAPSED);
if (mPendingIdleUntil != null || mPendingWhileIdleAlarms.size() > 0) {
pw.println();
@@ -3862,6 +4064,7 @@ class AlarmManagerService extends SystemService {
obtainMessage(REMOVE_FOR_STOPPED, uid, 0).sendToTarget();
}
+ @Override
public void handleMessage(Message msg) {
switch (msg.what) {
case ALARM_EVENT: {
@@ -4030,64 +4233,57 @@ class AlarmManagerService extends SystemService {
public void onReceive(Context context, Intent intent) {
final int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
synchronized (mLock) {
- String action = intent.getAction();
String pkgList[] = null;
- if (Intent.ACTION_QUERY_PACKAGE_RESTART.equals(action)) {
- pkgList = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES);
- for (String packageName : pkgList) {
- if (lookForPackageLocked(packageName)) {
- setResultCode(Activity.RESULT_OK);
- return;
- }
- }
- return;
- } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
- pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
- } else if (Intent.ACTION_USER_STOPPED.equals(action)) {
- int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
- if (userHandle >= 0) {
- removeUserLocked(userHandle);
- for (int i = mLastAlarmDeliveredForPackage.size() - 1; i >= 0; i--) {
- final Pair<String, Integer> packageUser =
- mLastAlarmDeliveredForPackage.keyAt(i);
- if (packageUser.second == userHandle) {
- mLastAlarmDeliveredForPackage.removeAt(i);
+ switch (intent.getAction()) {
+ case Intent.ACTION_QUERY_PACKAGE_RESTART:
+ pkgList = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES);
+ for (String packageName : pkgList) {
+ if (lookForPackageLocked(packageName)) {
+ setResultCode(Activity.RESULT_OK);
+ return;
}
}
- }
- } else if (Intent.ACTION_UID_REMOVED.equals(action)) {
- if (uid >= 0) {
- mLastAllowWhileIdleDispatch.delete(uid);
- mUseAllowWhileIdleShortTime.delete(uid);
- }
- } else {
- if (Intent.ACTION_PACKAGE_REMOVED.equals(action)
- && intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
- // This package is being updated; don't kill its alarms.
return;
- }
- Uri data = intent.getData();
- if (data != null) {
- String pkg = data.getSchemeSpecificPart();
- if (pkg != null) {
- pkgList = new String[]{pkg};
+ case Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE:
+ pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
+ break;
+ case Intent.ACTION_USER_STOPPED:
+ final int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
+ if (userHandle >= 0) {
+ removeUserLocked(userHandle);
+ mAppWakeupHistory.removeForUser(userHandle);
}
- }
+ return;
+ case Intent.ACTION_UID_REMOVED:
+ if (uid >= 0) {
+ mLastAllowWhileIdleDispatch.delete(uid);
+ mUseAllowWhileIdleShortTime.delete(uid);
+ }
+ return;
+ case Intent.ACTION_PACKAGE_REMOVED:
+ if (intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
+ // This package is being updated; don't kill its alarms.
+ return;
+ }
+ // Intentional fall-through.
+ case Intent.ACTION_PACKAGE_RESTARTED:
+ final Uri data = intent.getData();
+ if (data != null) {
+ final String pkg = data.getSchemeSpecificPart();
+ if (pkg != null) {
+ pkgList = new String[]{pkg};
+ }
+ }
+ break;
}
if (pkgList != null && (pkgList.length > 0)) {
- for (int i = mLastAlarmDeliveredForPackage.size() - 1; i >= 0; i--) {
- Pair<String, Integer> packageUser = mLastAlarmDeliveredForPackage.keyAt(i);
- if (ArrayUtils.contains(pkgList, packageUser.first)
- && packageUser.second == UserHandle.getUserId(uid)) {
- mLastAlarmDeliveredForPackage.removeAt(i);
- }
- }
for (String pkg : pkgList) {
if (uid >= 0) {
- // package-removed case
+ // package-removed and package-restarted case
+ mAppWakeupHistory.removeForPackage(pkg, UserHandle.getUserId(uid));
removeLocked(uid);
} else {
- // external-applications-unavailable etc case
+ // external-applications-unavailable case
removeLocked(pkg);
}
mPriorities.remove(pkg);
@@ -4131,7 +4327,8 @@ class AlarmManagerService extends SystemService {
/**
* Tracking of app assignments to standby buckets
*/
- final class AppStandbyTracker extends UsageStatsManagerInternal.AppIdleStateChangeListener {
+ private final class AppStandbyTracker extends
+ UsageStatsManagerInternal.AppIdleStateChangeListener {
@Override
public void onAppIdleStateChanged(final String packageName, final @UserIdInt int userId,
boolean idle, int bucket, int reason) {
@@ -4474,7 +4671,8 @@ class AlarmManagerService extends SystemService {
if (!isExemptFromAppStandby(alarm)) {
final Pair<String, Integer> packageUser = Pair.create(alarm.sourcePackage,
UserHandle.getUserId(alarm.creatorUid));
- mLastAlarmDeliveredForPackage.put(packageUser, nowELAPSED);
+ mAppWakeupHistory.recordAlarmForPackage(alarm.sourcePackage,
+ UserHandle.getUserId(alarm.creatorUid), nowELAPSED);
}
final BroadcastStats bs = inflight.mBroadcastStats;
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index a33338164caf..c4bc52c6f4de 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -208,6 +208,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
private int mErrorRecoveryRetryCounter;
private final int mSystemUiUid;
+ private boolean mIsHearingAidProfileSupported;
+
// Save a ProfileServiceConnections object for each of the bound
// bluetooth profile services
private final Map<Integer, ProfileServiceConnections> mProfileServices = new HashMap<>();
@@ -391,13 +393,19 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
mCallbacks = new RemoteCallbackList<IBluetoothManagerCallback>();
mStateChangeCallbacks = new RemoteCallbackList<IBluetoothStateChangeCallback>();
+ mIsHearingAidProfileSupported = context.getResources()
+ .getBoolean(com.android.internal.R.bool.config_hearing_aid_profile_supported);
+
// TODO: We need a more generic way to initialize the persist keys of FeatureFlagUtils
- boolean isHearingAidEnabled;
String value = SystemProperties.get(FeatureFlagUtils.PERSIST_PREFIX + FeatureFlagUtils.HEARING_AID_SETTINGS);
if (!TextUtils.isEmpty(value)) {
- isHearingAidEnabled = Boolean.parseBoolean(value);
+ boolean isHearingAidEnabled = Boolean.parseBoolean(value);
Log.v(TAG, "set feature flag HEARING_AID_SETTINGS to " + isHearingAidEnabled);
FeatureFlagUtils.setEnabled(context, FeatureFlagUtils.HEARING_AID_SETTINGS, isHearingAidEnabled);
+ if (isHearingAidEnabled && !mIsHearingAidProfileSupported) {
+ // Overwrite to enable support by FeatureFlag
+ mIsHearingAidProfileSupported = true;
+ }
}
IntentFilter filter = new IntentFilter();
@@ -679,6 +687,11 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
return false;
}
+ @Override
+ public boolean isHearingAidProfileSupported() {
+ return mIsHearingAidProfileSupported;
+ }
+
// Monitor change of BLE scan only mode settings.
private void registerForBleScanModeChange() {
ContentObserver contentObserver = new ContentObserver(null) {
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index aa74600cc527..1519c1785070 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -73,6 +73,7 @@ import android.net.INetworkStatsService;
import android.net.LinkProperties;
import android.net.LinkProperties.CompareResult;
import android.net.MatchAllNetworkSpecifier;
+import android.net.NattSocketKeepalive;
import android.net.Network;
import android.net.NetworkAgent;
import android.net.NetworkCapabilities;
@@ -97,10 +98,10 @@ import android.net.VpnService;
import android.net.metrics.IpConnectivityLog;
import android.net.metrics.NetworkEvent;
import android.net.netlink.InetDiagMessage;
+import android.net.shared.NetdService;
import android.net.shared.NetworkMonitorUtils;
import android.net.shared.PrivateDnsConfig;
import android.net.util.MultinetworkPolicyTracker;
-import android.net.shared.NetdService;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
@@ -237,6 +238,9 @@ public class ConnectivityService extends IConnectivityManager.Stub
// connect anyway?" dialog after the user selects a network that doesn't validate.
private static final int PROMPT_UNVALIDATED_DELAY_MS = 8 * 1000;
+ // How long to dismiss network notification.
+ private static final int TIMEOUT_NOTIFICATION_DELAY_MS = 20 * 1000;
+
// Default to 30s linger time-out. Modifiable only for testing.
private static final String LINGER_DELAY_PROPERTY = "persist.netmon.linger";
private static final int DEFAULT_LINGER_DELAY_MS = 30_000;
@@ -473,6 +477,11 @@ public class ConnectivityService extends IConnectivityManager.Stub
public static final int EVENT_PROVISIONING_NOTIFICATION = 43;
/**
+ * This event can handle dismissing notification by given network id.
+ */
+ public static final int EVENT_TIMEOUT_NOTIFICATION = 44;
+
+ /**
* Argument for {@link #EVENT_PROVISIONING_NOTIFICATION} to indicate that the notification
* should be shown.
*/
@@ -506,7 +515,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
// A helper object to track the current default HTTP proxy. ConnectivityService needs to tell
// the world when it changes.
- private final ProxyTracker mProxyTracker;
+ @VisibleForTesting
+ protected final ProxyTracker mProxyTracker;
final private SettingsObserver mSettingsObserver;
@@ -815,7 +825,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
mPolicyManagerInternal = checkNotNull(
LocalServices.getService(NetworkPolicyManagerInternal.class),
"missing NetworkPolicyManagerInternal");
- mProxyTracker = new ProxyTracker(context, mHandler, EVENT_PROXY_HAS_CHANGED);
+ mProxyTracker = makeProxyTracker();
mNetd = NetdService.getInstance();
mKeyStore = KeyStore.getInstance();
@@ -981,6 +991,11 @@ public class ConnectivityService extends IConnectivityManager.Stub
deps);
}
+ @VisibleForTesting
+ protected ProxyTracker makeProxyTracker() {
+ return new ProxyTracker(mContext, mHandler, EVENT_PROXY_HAS_CHANGED);
+ }
+
private static NetworkCapabilities createDefaultNetworkCapabilitiesForUid(int uid) {
final NetworkCapabilities netCap = new NetworkCapabilities();
netCap.addCapability(NET_CAPABILITY_INTERNET);
@@ -1869,6 +1884,12 @@ public class ConnectivityService extends IConnectivityManager.Stub
"ConnectivityService");
}
+ private void enforceControlAlwaysOnVpnPermission() {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.CONTROL_ALWAYS_ON_VPN,
+ "ConnectivityService");
+ }
+
private void enforceNetworkStackSettingsOrSetup() {
enforceAnyPermissionOf(
android.Manifest.permission.NETWORK_SETTINGS,
@@ -1876,6 +1897,12 @@ public class ConnectivityService extends IConnectivityManager.Stub
android.Manifest.permission.NETWORK_STACK);
}
+ private void enforceNetworkStackPermission() {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.NETWORK_STACK,
+ "ConnectivityService");
+ }
+
private boolean checkNetworkStackPermission() {
return PERMISSION_GRANTED == mContext.checkCallingOrSelfPermission(
android.Manifest.permission.NETWORK_STACK);
@@ -2476,6 +2503,11 @@ public class ConnectivityService extends IConnectivityManager.Stub
final boolean valid = (msg.arg1 == NETWORK_TEST_RESULT_VALID);
final boolean wasValidated = nai.lastValidated;
final boolean wasDefault = isDefaultNetwork(nai);
+ if (nai.everCaptivePortalDetected && !nai.captivePortalLoginNotified
+ && valid) {
+ nai.captivePortalLoginNotified = true;
+ showNetworkNotification(nai, NotificationType.LOGGED_IN);
+ }
final String redirectUrl = (msg.obj instanceof String) ? (String) msg.obj : "";
@@ -2496,7 +2528,15 @@ public class ConnectivityService extends IConnectivityManager.Stub
updateCapabilities(oldScore, nai, nai.networkCapabilities);
// If score has changed, rebroadcast to NetworkFactories. b/17726566
if (oldScore != nai.getCurrentScore()) sendUpdatedScoreToFactories(nai);
- if (valid) handleFreshlyValidatedNetwork(nai);
+ if (valid) {
+ handleFreshlyValidatedNetwork(nai);
+ // Clear NO_INTERNET and LOST_INTERNET notifications if network becomes
+ // valid.
+ mNotifier.clearNotification(nai.network.netId,
+ NotificationType.NO_INTERNET);
+ mNotifier.clearNotification(nai.network.netId,
+ NotificationType.LOST_INTERNET);
+ }
}
updateInetCondition(nai);
// Let the NetworkAgent know the state of its network
@@ -2520,6 +2560,9 @@ public class ConnectivityService extends IConnectivityManager.Stub
final int oldScore = nai.getCurrentScore();
nai.lastCaptivePortalDetected = visible;
nai.everCaptivePortalDetected |= visible;
+ if (visible) {
+ nai.captivePortalLoginNotified = false;
+ }
if (nai.lastCaptivePortalDetected &&
Settings.Global.CAPTIVE_PORTAL_MODE_AVOID == getCaptivePortalMode()) {
if (DBG) log("Avoiding captive portal network: " + nai.name());
@@ -2531,7 +2574,10 @@ public class ConnectivityService extends IConnectivityManager.Stub
updateCapabilities(oldScore, nai, nai.networkCapabilities);
}
if (!visible) {
- mNotifier.clearNotification(netId);
+ // Only clear SIGN_IN and NETWORK_SWITCH notifications here, or else other
+ // notifications belong to the same network may be cleared unexpected.
+ mNotifier.clearNotification(netId, NotificationType.SIGN_IN);
+ mNotifier.clearNotification(netId, NotificationType.NETWORK_SWITCH);
} else {
if (nai == null) {
loge("EVENT_PROVISIONING_NOTIFICATION from unknown NetworkMonitor");
@@ -3237,9 +3283,15 @@ public class ConnectivityService extends IConnectivityManager.Stub
pw.decreaseIndent();
}
- private void showValidationNotification(NetworkAgentInfo nai, NotificationType type) {
+ private void showNetworkNotification(NetworkAgentInfo nai, NotificationType type) {
final String action;
switch (type) {
+ case LOGGED_IN:
+ action = Settings.ACTION_WIFI_SETTINGS;
+ mHandler.removeMessages(EVENT_TIMEOUT_NOTIFICATION);
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(EVENT_TIMEOUT_NOTIFICATION,
+ nai.network.netId, 0), TIMEOUT_NOTIFICATION_DELAY_MS);
+ break;
case NO_INTERNET:
action = ConnectivityManager.ACTION_PROMPT_UNVALIDATED;
break;
@@ -3252,10 +3304,12 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
Intent intent = new Intent(action);
- intent.setData(Uri.fromParts("netId", Integer.toString(nai.network.netId), null));
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- intent.setClassName("com.android.settings",
- "com.android.settings.wifi.WifiNoInternetDialog");
+ if (type != NotificationType.LOGGED_IN) {
+ intent.setData(Uri.fromParts("netId", Integer.toString(nai.network.netId), null));
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ intent.setClassName("com.android.settings",
+ "com.android.settings.wifi.WifiNoInternetDialog");
+ }
PendingIntent pendingIntent = PendingIntent.getActivityAsUser(
mContext, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT, null, UserHandle.CURRENT);
@@ -3273,7 +3327,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
!nai.networkMisc.explicitlySelected || nai.networkMisc.acceptUnvalidated) {
return;
}
- showValidationNotification(nai, NotificationType.NO_INTERNET);
+ showNetworkNotification(nai, NotificationType.NO_INTERNET);
}
private void handleNetworkUnvalidated(NetworkAgentInfo nai) {
@@ -3282,7 +3336,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
if (nc.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) &&
mMultinetworkPolicyTracker.shouldNotifyWifiUnvalidated()) {
- showValidationNotification(nai, NotificationType.LOST_INTERNET);
+ showNetworkNotification(nai, NotificationType.LOST_INTERNET);
}
}
@@ -3428,6 +3482,9 @@ public class ConnectivityService extends IConnectivityManager.Stub
case EVENT_DATA_SAVER_CHANGED:
handleRestrictBackgroundChanged(toBool(msg.arg1));
break;
+ case EVENT_TIMEOUT_NOTIFICATION:
+ mNotifier.clearNotification(msg.arg1, NotificationType.LOGGED_IN);
+ break;
}
}
}
@@ -3685,20 +3742,46 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
}
+ /**
+ * Returns information about the proxy a certain network is using. If given a null network, it
+ * it will return the proxy for the bound network for the caller app or the default proxy if
+ * none.
+ *
+ * @param network the network we want to get the proxy information for.
+ * @return Proxy information if a network has a proxy configured, or otherwise null.
+ */
@Override
public ProxyInfo getProxyForNetwork(Network network) {
- if (network == null) return mProxyTracker.getDefaultProxy();
final ProxyInfo globalProxy = mProxyTracker.getGlobalProxy();
if (globalProxy != null) return globalProxy;
- if (!NetworkUtils.queryUserAccess(Binder.getCallingUid(), network.netId)) return null;
- // Don't call getLinkProperties() as it requires ACCESS_NETWORK_STATE permission, which
- // caller may not have.
+ if (network == null) {
+ // Get the network associated with the calling UID.
+ final Network activeNetwork = getActiveNetworkForUidInternal(Binder.getCallingUid(),
+ true);
+ if (activeNetwork == null) {
+ return null;
+ }
+ return getLinkPropertiesProxyInfo(activeNetwork);
+ } else if (queryUserAccess(Binder.getCallingUid(), network.netId)) {
+ // Don't call getLinkProperties() as it requires ACCESS_NETWORK_STATE permission, which
+ // caller may not have.
+ return getLinkPropertiesProxyInfo(network);
+ }
+ // No proxy info available if the calling UID does not have network access.
+ return null;
+ }
+
+ @VisibleForTesting
+ protected boolean queryUserAccess(int uid, int netId) {
+ return NetworkUtils.queryUserAccess(uid, netId);
+ }
+
+ private ProxyInfo getLinkPropertiesProxyInfo(Network network) {
final NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network);
if (nai == null) return null;
synchronized (nai) {
- final ProxyInfo proxyInfo = nai.linkProperties.getHttpProxy();
- if (proxyInfo == null) return null;
- return new ProxyInfo(proxyInfo);
+ final ProxyInfo linkHttpProxy = nai.linkProperties.getHttpProxy();
+ return linkHttpProxy == null ? null : new ProxyInfo(linkHttpProxy);
}
}
@@ -3722,11 +3805,10 @@ public class ConnectivityService extends IConnectivityManager.Stub
mProxyTracker.setDefaultProxy(proxy);
}
- // If the proxy has changed from oldLp to newLp, resend proxy broadcast with default proxy.
- // This method gets called when any network changes proxy, but the broadcast only ever contains
- // the default proxy (even if it hasn't changed).
- // TODO: Deprecate the broadcast extras as they aren't necessarily applicable in a multi-network
- // world where an app might be bound to a non-default network.
+ // If the proxy has changed from oldLp to newLp, resend proxy broadcast. This method gets called
+ // when any network changes proxy.
+ // TODO: Remove usage of broadcast extras as they are deprecated and not applicable in a
+ // multi-network world where an app might be bound to a non-default network.
private void updateProxy(LinkProperties newLp, LinkProperties oldLp) {
ProxyInfo newProxyInfo = newLp == null ? null : newLp.getHttpProxy();
ProxyInfo oldProxyInfo = oldLp == null ? null : oldLp.getHttpProxy();
@@ -4077,8 +4159,9 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
@Override
- public boolean setAlwaysOnVpnPackage(int userId, String packageName, boolean lockdown) {
- enforceConnectivityInternalPermission();
+ public boolean setAlwaysOnVpnPackage(
+ int userId, String packageName, boolean lockdown, List<String> lockdownWhitelist) {
+ enforceControlAlwaysOnVpnPermission();
enforceCrossUserPermission(userId);
synchronized (mVpns) {
@@ -4092,11 +4175,11 @@ public class ConnectivityService extends IConnectivityManager.Stub
Slog.w(TAG, "User " + userId + " has no Vpn configuration");
return false;
}
- if (!vpn.setAlwaysOnPackage(packageName, lockdown)) {
+ if (!vpn.setAlwaysOnPackage(packageName, lockdown, lockdownWhitelist)) {
return false;
}
if (!startAlwaysOnVpn(userId)) {
- vpn.setAlwaysOnPackage(null, false);
+ vpn.setAlwaysOnPackage(null, false, null);
return false;
}
}
@@ -4105,7 +4188,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
@Override
public String getAlwaysOnVpnPackage(int userId) {
- enforceConnectivityInternalPermission();
+ enforceControlAlwaysOnVpnPermission();
enforceCrossUserPermission(userId);
synchronized (mVpns) {
@@ -4119,6 +4202,36 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
@Override
+ public boolean isVpnLockdownEnabled(int userId) {
+ enforceControlAlwaysOnVpnPermission();
+ enforceCrossUserPermission(userId);
+
+ synchronized (mVpns) {
+ Vpn vpn = mVpns.get(userId);
+ if (vpn == null) {
+ Slog.w(TAG, "User " + userId + " has no Vpn configuration");
+ return false;
+ }
+ return vpn.getLockdown();
+ }
+ }
+
+ @Override
+ public List<String> getVpnLockdownWhitelist(int userId) {
+ enforceControlAlwaysOnVpnPermission();
+ enforceCrossUserPermission(userId);
+
+ synchronized (mVpns) {
+ Vpn vpn = mVpns.get(userId);
+ if (vpn == null) {
+ Slog.w(TAG, "User " + userId + " has no Vpn configuration");
+ return null;
+ }
+ return vpn.getLockdownWhitelist();
+ }
+ }
+
+ @Override
public int checkMobileProvisioning(int suggestedTimeOutMs) {
// TODO: Remove? Any reason to trigger a provisioning check?
return -1;
@@ -4347,7 +4460,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
if (TextUtils.equals(vpn.getAlwaysOnPackage(), packageName) && !isReplacing) {
Slog.d(TAG, "Removing always-on VPN package " + packageName + " for user "
+ userId);
- vpn.setAlwaysOnPackage(null, false);
+ vpn.setAlwaysOnPackage(null, false, null);
}
}
}
@@ -5893,12 +6006,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
scheduleUnvalidatedPrompt(networkAgent);
- if (networkAgent.isVPN()) {
- // Temporarily disable the default proxy (not global).
- mProxyTracker.setDefaultProxyEnabled(false);
- // TODO: support proxy per network.
- }
-
// Whether a particular NetworkRequest listen should cause signal strength thresholds to
// be communicated to a particular NetworkAgent depends only on the network's immutable,
// capabilities, so it only needs to be done once on initial connect, not every time the
@@ -5917,10 +6024,16 @@ public class ConnectivityService extends IConnectivityManager.Stub
} else if (state == NetworkInfo.State.DISCONNECTED) {
networkAgent.asyncChannel.disconnect();
if (networkAgent.isVPN()) {
- mProxyTracker.setDefaultProxyEnabled(true);
updateUids(networkAgent, networkAgent.networkCapabilities, null);
}
disconnectAndDestroyNetwork(networkAgent);
+ if (networkAgent.isVPN()) {
+ // As the active or bound network changes for apps, broadcast the default proxy, as
+ // apps may need to update their proxy data. This is called after disconnecting from
+ // VPN to make sure we do not broadcast the old proxy data.
+ // TODO(b/122649188): send the broadcast only to VPN users.
+ mProxyTracker.sendProxyBroadcast();
+ }
} else if ((oldInfo != null && oldInfo.getState() == NetworkInfo.State.SUSPENDED) ||
state == NetworkInfo.State.SUSPENDED) {
// going into or coming out of SUSPEND: re-score and notify
@@ -6185,6 +6298,17 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
@Override
+ public void startNattKeepaliveWithFd(Network network, FileDescriptor fd, int resourceId,
+ int intervalSeconds, Messenger messenger, IBinder binder, String srcAddr,
+ String dstAddr) {
+ enforceKeepalivePermission();
+ mKeepaliveTracker.startNattKeepalive(
+ getNetworkAgentInfoForNetwork(network), fd, resourceId,
+ intervalSeconds, messenger, binder,
+ srcAddr, dstAddr, NattSocketKeepalive.NATT_PORT);
+ }
+
+ @Override
public void stopKeepalive(Network network, int slot) {
mHandler.sendMessage(mHandler.obtainMessage(
NetworkAgent.CMD_STOP_PACKET_KEEPALIVE, slot, PacketKeepalive.SUCCESS, network));
@@ -6216,7 +6340,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
synchronized (mVpns) {
final String alwaysOnPackage = getAlwaysOnVpnPackage(userId);
if (alwaysOnPackage != null) {
- setAlwaysOnVpnPackage(userId, null, false);
+ setAlwaysOnVpnPackage(userId, null, false, null);
setVpnPackageAuthorization(alwaysOnPackage, userId, false);
}
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index 8dcc1d57c572..d2c6354e91b8 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -220,6 +220,8 @@ public class LocationManagerService extends ILocationManager.Stub {
private final ArraySet<String> mBackgroundThrottlePackageWhitelist = new ArraySet<>();
+ private final ArraySet<String> mIgnoreSettingsPackageWhitelist = new ArraySet<>();
+
@GuardedBy("mLock")
private final ArrayMap<IBinder, Identity> mGnssMeasurementsListeners = new ArrayMap<>();
@@ -353,6 +355,18 @@ public class LocationManagerService extends ILocationManager.Stub {
}
}
}, UserHandle.USER_ALL);
+ mContext.getContentResolver().registerContentObserver(
+ Settings.Global.getUriFor(
+ Settings.Global.LOCATION_IGNORE_SETTINGS_PACKAGE_WHITELIST),
+ true,
+ new ContentObserver(mHandler) {
+ @Override
+ public void onChange(boolean selfChange) {
+ synchronized (mLock) {
+ onIgnoreSettingsWhitelistChangedLocked();
+ }
+ }
+ }, UserHandle.USER_ALL);
new PackageMonitor() {
@Override
@@ -550,6 +564,25 @@ public class LocationManagerService extends ILocationManager.Stub {
}
}
+ @GuardedBy("lock")
+ private void onIgnoreSettingsWhitelistChangedLocked() {
+ String setting = Settings.Global.getString(
+ mContext.getContentResolver(),
+ Settings.Global.LOCATION_IGNORE_SETTINGS_PACKAGE_WHITELIST);
+ if (setting == null) {
+ setting = "";
+ }
+
+ mIgnoreSettingsPackageWhitelist.clear();
+ mIgnoreSettingsPackageWhitelist.addAll(
+ SystemConfig.getInstance().getAllowIgnoreLocationSettings());
+ mIgnoreSettingsPackageWhitelist.addAll(Arrays.asList(setting.split(",")));
+
+ for (LocationProvider p : mProviders) {
+ applyRequirementsLocked(p);
+ }
+ }
+
@GuardedBy("mLock")
private void onUserProfilesChangedLocked() {
mCurrentUserProfiles = mUserManager.getProfileIdsWithDisabled(mCurrentUserId);
@@ -1299,8 +1332,7 @@ public class LocationManagerService extends ILocationManager.Stub {
if (provider == null) {
continue;
}
- if (!provider.isUseableLocked()
- && !updateRecord.mRealRequest.isLocationSettingsIgnored()) {
+ if (!provider.isUseableLocked() && !isSettingsExemptLocked(updateRecord)) {
continue;
}
@@ -1988,7 +2020,7 @@ public class LocationManagerService extends ILocationManager.Stub {
}
// requests that ignore location settings will never provider notifications
- if (record.mRealRequest.isLocationSettingsIgnored()) {
+ if (isSettingsExemptLocked(record)) {
continue;
}
@@ -2052,8 +2084,7 @@ public class LocationManagerService extends ILocationManager.Stub {
record.mReceiver.mAllowedResolutionLevel)) {
continue;
}
- if (!provider.isUseableLocked()
- && !record.mRealRequest.isLocationSettingsIgnored()) {
+ if (!provider.isUseableLocked() && !isSettingsExemptLocked(record)) {
continue;
}
@@ -2163,6 +2194,25 @@ public class LocationManagerService extends ILocationManager.Stub {
return false;
}
+ @GuardedBy("mLock")
+ private boolean isSettingsExemptLocked(UpdateRecord record) {
+ if (!record.mRealRequest.isLocationSettingsIgnored()) {
+ return false;
+ }
+
+ if (mBackgroundThrottlePackageWhitelist.contains(record.mReceiver.mIdentity.mPackageName)) {
+ return true;
+ }
+
+ for (LocationProvider provider : mProviders) {
+ if (record.mReceiver.mIdentity.mPackageName.equals(provider.getPackageLocked())) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
private class UpdateRecord {
final String mProvider;
private final LocationRequest mRealRequest; // original request from client
@@ -2306,7 +2356,7 @@ public class LocationManagerService extends ILocationManager.Stub {
}
// make getFastestInterval() the minimum of interval and fastest interval
if (sanitizedRequest.getFastestInterval() > sanitizedRequest.getInterval()) {
- request.setFastestInterval(request.getInterval());
+ sanitizedRequest.setFastestInterval(request.getInterval());
}
return sanitizedRequest;
}
@@ -2418,7 +2468,7 @@ public class LocationManagerService extends ILocationManager.Stub {
oldRecord.disposeLocked(false);
}
- if (!provider.isUseableLocked() && !request.isLocationSettingsIgnored()) {
+ if (!provider.isUseableLocked() && !isSettingsExemptLocked(record)) {
// Notify the listener that updates are currently disabled - but only if the request
// does not ignore location settings
receiver.callProviderEnabledLocked(name, false);
@@ -3056,7 +3106,7 @@ public class LocationManagerService extends ILocationManager.Stub {
Receiver receiver = r.mReceiver;
boolean receiverDead = false;
- if (!provider.isUseableLocked() && !r.mRealRequest.isLocationSettingsIgnored()) {
+ if (!provider.isUseableLocked() && !isSettingsExemptLocked(r)) {
continue;
}
diff --git a/services/core/java/com/android/server/TEST_MAPPING b/services/core/java/com/android/server/TEST_MAPPING
index 16b12f1f1d68..1870f8d95977 100644
--- a/services/core/java/com/android/server/TEST_MAPPING
+++ b/services/core/java/com/android/server/TEST_MAPPING
@@ -2,6 +2,7 @@
"presubmit": [
{
"name": "FrameworksMockingServicesTests",
+ "file_patterns": ["AlarmManagerService\\.java"],
"options": [
{
"include-annotation": "android.platform.test.annotations.Presubmit"
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index aa2389056c13..e5436178217d 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -2115,6 +2115,16 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
android.Manifest.permission.READ_PRECISE_PHONE_STATE, null);
}
+ if ((events & PhoneStateListener.LISTEN_RADIO_POWER_STATE_CHANGED) != 0) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, null);
+ }
+
+ if ((events & PhoneStateListener.LISTEN_VOICE_ACTIVATION_STATE) != 0) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, null);
+ }
+
return true;
}
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index e879efd644d1..c826df06c925 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -5202,7 +5202,7 @@ public class AccountManagerService
Process.SYSTEM_UID, null /* packageName */, false);
fout.println("Accounts: " + accounts.length);
for (Account account : accounts) {
- fout.println(" " + account.toSafeString());
+ fout.println(" " + account.toString());
}
// Add debug information.
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index dd2b33ab1179..d025d739055b 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -16,13 +16,19 @@
package com.android.server.am;
+import static android.provider.DeviceConfig.ActivityManager.KEY_MAX_CACHED_PROCESSES;
+
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_POWER_QUICK;
+import android.app.ActivityThread;
import android.content.ContentResolver;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.Handler;
+import android.provider.DeviceConfig;
+import android.provider.DeviceConfig.OnPropertyChangedListener;
import android.provider.Settings;
+import android.text.TextUtils;
import android.util.KeyValueListParser;
import android.util.Slog;
@@ -34,7 +40,6 @@ import java.io.PrintWriter;
final class ActivityManagerConstants extends ContentObserver {
// Key names stored in the settings value.
- private static final String KEY_MAX_CACHED_PROCESSES = "max_cached_processes";
private static final String KEY_BACKGROUND_SETTLE_TIME = "background_settle_time";
private static final String KEY_FGSERVICE_MIN_SHOWN_TIME
= "fgservice_min_shown_time";
@@ -69,13 +74,6 @@ final class ActivityManagerConstants extends ContentObserver {
static final String KEY_PROCESS_START_ASYNC = "process_start_async";
static final String KEY_MEMORY_INFO_THROTTLE_TIME = "memory_info_throttle_time";
static final String KEY_TOP_TO_FGS_GRACE_DURATION = "top_to_fgs_grace_duration";
- static final String KEY_USE_COMPACTION = "use_compaction";
- static final String KEY_COMPACT_ACTION_1 = "compact_action_1";
- static final String KEY_COMPACT_ACTION_2 = "compact_action_2";
- static final String KEY_COMPACT_THROTTLE_1 = "compact_throttle_1";
- static final String KEY_COMPACT_THROTTLE_2 = "compact_throttle_2";
- static final String KEY_COMPACT_THROTTLE_3 = "compact_throttle_3";
- static final String KEY_COMPACT_THROTTLE_4 = "compact_throttle_4";
private static final int DEFAULT_MAX_CACHED_PROCESSES = 32;
private static final long DEFAULT_BACKGROUND_SETTLE_TIME = 60*1000;
@@ -106,13 +104,6 @@ final class ActivityManagerConstants extends ContentObserver {
private static final boolean DEFAULT_PROCESS_START_ASYNC = true;
private static final long DEFAULT_MEMORY_INFO_THROTTLE_TIME = 5*60*1000;
private static final long DEFAULT_TOP_TO_FGS_GRACE_DURATION = 15 * 1000;
- private static final boolean DEFAULT_USE_COMPACTION = false;
- public static final int DEFAULT_COMPACT_ACTION_1 = 1;
- public static final int DEFAULT_COMPACT_ACTION_2 = 3;
- public static final long DEFAULT_COMPACT_THROTTLE_1 = 5000;
- public static final long DEFAULT_COMPACT_THROTTLE_2 = 10000;
- public static final long DEFAULT_COMPACT_THROTTLE_3 = 500;
- public static final long DEFAULT_COMPACT_THROTTLE_4 = 10000;
// Maximum number of cached processes we will allow.
public int MAX_CACHED_PROCESSES = DEFAULT_MAX_CACHED_PROCESSES;
@@ -232,23 +223,6 @@ final class ActivityManagerConstants extends ContentObserver {
// this long.
public long TOP_TO_FGS_GRACE_DURATION = DEFAULT_TOP_TO_FGS_GRACE_DURATION;
- // Use compaction for background apps.
- public boolean USE_COMPACTION = DEFAULT_USE_COMPACTION;
-
- // Action for compactAppSome.
- public int COMPACT_ACTION_1 = DEFAULT_COMPACT_ACTION_1;
- // Action for compactAppFull;
- public int COMPACT_ACTION_2 = DEFAULT_COMPACT_ACTION_2;
-
- // How long we'll skip second compactAppSome after first compactAppSome
- public long COMPACT_THROTTLE_1 = DEFAULT_COMPACT_THROTTLE_1;
- // How long we'll skip compactAppSome after compactAppFull
- public long COMPACT_THROTTLE_2 = DEFAULT_COMPACT_THROTTLE_2;
- // How long we'll skip compactAppFull after compactAppSome
- public long COMPACT_THROTTLE_3 = DEFAULT_COMPACT_THROTTLE_3;
- // How long we'll skip second compactAppFull after first compactAppFull
- public long COMPACT_THROTTLE_4 = DEFAULT_COMPACT_THROTTLE_4;
-
// Indicates whether the activity starts logging is enabled.
// Controlled by Settings.Global.ACTIVITY_STARTS_LOGGING_ENABLED
volatile boolean mFlagActivityStartsLoggingEnabled;
@@ -295,10 +269,19 @@ final class ActivityManagerConstants extends ContentObserver {
Settings.Global.getUriFor(
Settings.Global.BACKGROUND_ACTIVITY_STARTS_ENABLED);
+ private final OnPropertyChangedListener mOnDeviceConfigChangedListener =
+ new OnPropertyChangedListener() {
+ @Override
+ public void onPropertyChanged(String namespace, String name, String value) {
+ if (KEY_MAX_CACHED_PROCESSES.equals(name)) {
+ updateMaxCachedProcesses();
+ }
+ }
+ };
+
public ActivityManagerConstants(ActivityManagerService service, Handler handler) {
super(handler);
mService = service;
- updateMaxCachedProcesses();
}
public void start(ContentResolver resolver) {
@@ -309,6 +292,11 @@ final class ActivityManagerConstants extends ContentObserver {
updateConstants();
updateActivityStartsLoggingEnabled();
updateBackgroundActivityStartsEnabled();
+ DeviceConfig.addOnPropertyChangedListener(DeviceConfig.ActivityManager.NAMESPACE,
+ ActivityThread.currentApplication().getMainExecutor(),
+ mOnDeviceConfigChangedListener);
+ updateMaxCachedProcesses();
+
}
public void setOverrideMaxCachedProcesses(int value) {
@@ -347,8 +335,6 @@ final class ActivityManagerConstants extends ContentObserver {
// with defaults.
Slog.e("ActivityManagerConstants", "Bad activity manager config settings", e);
}
- MAX_CACHED_PROCESSES = mParser.getInt(KEY_MAX_CACHED_PROCESSES,
- DEFAULT_MAX_CACHED_PROCESSES);
BACKGROUND_SETTLE_TIME = mParser.getLong(KEY_BACKGROUND_SETTLE_TIME,
DEFAULT_BACKGROUND_SETTLE_TIME);
FGSERVICE_MIN_SHOWN_TIME = mParser.getLong(KEY_FGSERVICE_MIN_SHOWN_TIME,
@@ -406,13 +392,9 @@ final class ActivityManagerConstants extends ContentObserver {
DEFAULT_MEMORY_INFO_THROTTLE_TIME);
TOP_TO_FGS_GRACE_DURATION = mParser.getDurationMillis(KEY_TOP_TO_FGS_GRACE_DURATION,
DEFAULT_TOP_TO_FGS_GRACE_DURATION);
- USE_COMPACTION = mParser.getBoolean(KEY_USE_COMPACTION, DEFAULT_USE_COMPACTION);
- COMPACT_ACTION_1 = mParser.getInt(KEY_COMPACT_ACTION_1, DEFAULT_COMPACT_ACTION_1);
- COMPACT_ACTION_2 = mParser.getInt(KEY_COMPACT_ACTION_2, DEFAULT_COMPACT_ACTION_2);
- COMPACT_THROTTLE_1 = mParser.getLong(KEY_COMPACT_THROTTLE_1, DEFAULT_COMPACT_THROTTLE_1);
- COMPACT_THROTTLE_2 = mParser.getLong(KEY_COMPACT_THROTTLE_2, DEFAULT_COMPACT_THROTTLE_2);
- COMPACT_THROTTLE_3 = mParser.getLong(KEY_COMPACT_THROTTLE_3, DEFAULT_COMPACT_THROTTLE_3);
- COMPACT_THROTTLE_4 = mParser.getLong(KEY_COMPACT_THROTTLE_4, DEFAULT_COMPACT_THROTTLE_4);
+
+ // For new flags that are intended for server-side experiments, please use the new
+ // DeviceConfig package.
updateMaxCachedProcesses();
}
@@ -429,8 +411,19 @@ final class ActivityManagerConstants extends ContentObserver {
}
private void updateMaxCachedProcesses() {
- CUR_MAX_CACHED_PROCESSES = mOverrideMaxCachedProcesses < 0
- ? MAX_CACHED_PROCESSES : mOverrideMaxCachedProcesses;
+ String maxCachedProcessesFlag = DeviceConfig.getProperty(
+ DeviceConfig.ActivityManager.NAMESPACE, KEY_MAX_CACHED_PROCESSES);
+ try {
+ CUR_MAX_CACHED_PROCESSES = mOverrideMaxCachedProcesses < 0
+ ? (TextUtils.isEmpty(maxCachedProcessesFlag)
+ ? DEFAULT_MAX_CACHED_PROCESSES : Integer.parseInt(maxCachedProcessesFlag))
+ : mOverrideMaxCachedProcesses;
+ } catch (NumberFormatException e) {
+ // Bad flag value from Phenotype, revert to default.
+ Slog.e("ActivityManagerConstants",
+ "Unable to parse flag for max_cached_processes: " + maxCachedProcessesFlag, e);
+ CUR_MAX_CACHED_PROCESSES = DEFAULT_MAX_CACHED_PROCESSES;
+ }
CUR_MAX_EMPTY_PROCESSES = computeEmptyProcessLimit(CUR_MAX_CACHED_PROCESSES);
// Note the trim levels do NOT depend on the override process limit, we want
@@ -503,8 +496,6 @@ final class ActivityManagerConstants extends ContentObserver {
pw.println(MEMORY_INFO_THROTTLE_TIME);
pw.print(" "); pw.print(KEY_TOP_TO_FGS_GRACE_DURATION); pw.print("=");
pw.println(TOP_TO_FGS_GRACE_DURATION);
- pw.print(" "); pw.print(KEY_USE_COMPACTION); pw.print("=");
- pw.println(USE_COMPACTION);
pw.println();
if (mOverrideMaxCachedProcesses >= 0) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 2f77ed64c670..eb643b670fcd 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -64,6 +64,7 @@ import static android.os.Process.SHELL_UID;
import static android.os.Process.SIGNAL_USR1;
import static android.os.Process.SYSTEM_UID;
import static android.os.Process.THREAD_PRIORITY_FOREGROUND;
+import static android.os.Process.ZYGOTE_PROCESS;
import static android.os.Process.getTotalMemory;
import static android.os.Process.isThreadInProcess;
import static android.os.Process.killProcess;
@@ -75,7 +76,6 @@ import static android.os.Process.removeAllProcessGroups;
import static android.os.Process.sendSignal;
import static android.os.Process.setThreadPriority;
import static android.os.Process.setThreadScheduler;
-import static android.os.Process.zygoteProcess;
import static android.provider.Settings.Global.ALWAYS_FINISH_ACTIVITIES;
import static android.provider.Settings.Global.DEBUG_APP;
import static android.provider.Settings.Global.NETWORK_ACCESS_TIMEOUT_MS;
@@ -1607,19 +1607,8 @@ public class ActivityManagerService extends IActivityManager.Stub
}
} break;
case UPDATE_HTTP_PROXY_MSG: {
- ProxyInfo proxy = (ProxyInfo)msg.obj;
- String host = "";
- String port = "";
- String exclList = "";
- Uri pacFileUrl = Uri.EMPTY;
- if (proxy != null) {
- host = proxy.getHost();
- port = Integer.toString(proxy.getPort());
- exclList = proxy.getExclusionListAsString();
- pacFileUrl = proxy.getPacFileUrl();
- }
synchronized (ActivityManagerService.this) {
- mProcessList.setAllHttpProxyLocked(host, port, exclList, pacFileUrl);
+ mProcessList.setAllHttpProxyLocked();
}
} break;
case PROC_START_TIMEOUT_MSG: {
@@ -2162,7 +2151,7 @@ public class ActivityManagerService extends IActivityManager.Stub
? Collections.emptyList()
: Arrays.asList(exemptions.split(","));
}
- if (!zygoteProcess.setApiBlacklistExemptions(mExemptions)) {
+ if (!ZYGOTE_PROCESS.setApiBlacklistExemptions(mExemptions)) {
Slog.e(TAG, "Failed to set API blacklist exemptions!");
// leave mExemptionsStr as is, so we don't try to send the same list again.
mExemptions = Collections.emptyList();
@@ -2175,7 +2164,7 @@ public class ActivityManagerService extends IActivityManager.Stub
}
if (logSampleRate != -1 && logSampleRate != mLogSampleRate) {
mLogSampleRate = logSampleRate;
- zygoteProcess.setHiddenApiAccessLogSampleRate(mLogSampleRate);
+ ZYGOTE_PROCESS.setHiddenApiAccessLogSampleRate(mLogSampleRate);
}
mPolicy = getValidEnforcementPolicy(Settings.Global.HIDDEN_API_POLICY);
}
@@ -4882,7 +4871,7 @@ public class ActivityManagerService extends IActivityManager.Stub
ArraySet<String> completedIsas = new ArraySet<String>();
for (String abi : Build.SUPPORTED_ABIS) {
- zygoteProcess.establishZygoteConnectionForAbi(abi);
+ ZYGOTE_PROCESS.establishZygoteConnectionForAbi(abi);
final String instructionSet = VMRuntime.getInstructionSet(abi);
if (!completedIsas.contains(instructionSet)) {
try {
@@ -7228,6 +7217,7 @@ public class ActivityManagerService extends IActivityManager.Stub
mActivityTaskManager.installSystemProviders();
mDevelopmentSettingsObserver = new DevelopmentSettingsObserver();
SettingsToPropertiesMapper.start(mContext.getContentResolver());
+ mOomAdjuster.initSettings();
// Now that the settings provider is published we can consider sending
// in a rescue party.
@@ -9340,6 +9330,7 @@ public class ActivityManagerService extends IActivityManager.Stub
synchronized(this) {
mConstants.dump(pw);
+ mOomAdjuster.dumpAppCompactorSettings(pw);
pw.println();
if (dumpAll) {
pw.println("-------------------------------------------------------------------------------");
@@ -9739,6 +9730,7 @@ public class ActivityManagerService extends IActivityManager.Stub
} else if ("settings".equals(cmd)) {
synchronized (this) {
mConstants.dump(pw);
+ mOomAdjuster.dumpAppCompactorSettings(pw);
}
} else if ("services".equals(cmd) || "s".equals(cmd)) {
if (dumpClient) {
@@ -14656,8 +14648,7 @@ public class ActivityManagerService extends IActivityManager.Stub
mHandler.sendEmptyMessage(CLEAR_DNS_CACHE_MSG);
break;
case Proxy.PROXY_CHANGE_ACTION:
- ProxyInfo proxy = intent.getParcelableExtra(Proxy.EXTRA_PROXY_INFO);
- mHandler.sendMessage(mHandler.obtainMessage(UPDATE_HTTP_PROXY_MSG, proxy));
+ mHandler.sendMessage(mHandler.obtainMessage(UPDATE_HTTP_PROXY_MSG));
break;
case android.hardware.Camera.ACTION_NEW_PICTURE:
case android.hardware.Camera.ACTION_NEW_VIDEO:
diff --git a/services/core/java/com/android/server/am/AppCompactor.java b/services/core/java/com/android/server/am/AppCompactor.java
index fd402cc08c0c..bb55ec3100c0 100644
--- a/services/core/java/com/android/server/am/AppCompactor.java
+++ b/services/core/java/com/android/server/am/AppCompactor.java
@@ -16,34 +16,63 @@
package com.android.server.am;
-import com.android.internal.annotations.GuardedBy;
+import static android.os.Process.THREAD_PRIORITY_FOREGROUND;
+import static android.provider.DeviceConfig.ActivityManager.KEY_COMPACT_ACTION_1;
+import static android.provider.DeviceConfig.ActivityManager.KEY_COMPACT_ACTION_2;
+import static android.provider.DeviceConfig.ActivityManager.KEY_COMPACT_THROTTLE_1;
+import static android.provider.DeviceConfig.ActivityManager.KEY_COMPACT_THROTTLE_2;
+import static android.provider.DeviceConfig.ActivityManager.KEY_COMPACT_THROTTLE_3;
+import static android.provider.DeviceConfig.ActivityManager.KEY_COMPACT_THROTTLE_4;
+import static android.provider.DeviceConfig.ActivityManager.KEY_USE_COMPACTION;
import android.app.ActivityManager;
-
+import android.app.ActivityThread;
import android.os.Handler;
-import android.os.Looper;
import android.os.Message;
import android.os.Process;
import android.os.SystemClock;
import android.os.Trace;
-
+import android.provider.DeviceConfig;
+import android.provider.DeviceConfig.OnPropertyChangedListener;
+import android.text.TextUtils;
import android.util.EventLog;
import android.util.StatsLog;
-import static android.os.Process.THREAD_PRIORITY_FOREGROUND;
-
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.server.ServiceThread;
import java.io.FileOutputStream;
-import java.io.IOException;
+import java.io.PrintWriter;
import java.util.ArrayList;
public final class AppCompactor {
- /**
- * Processes to compact.
- */
- final ArrayList<ProcessRecord> mPendingCompactionProcesses = new ArrayList<ProcessRecord>();
+ // Phenotype sends int configurations and we map them to the strings we'll use on device,
+ // preventing a weird string value entering the kernel.
+ private static final int COMPACT_ACTION_FILE_FLAG = 1;
+ private static final int COMPACT_ACTION_ANON_FLAG = 2;
+ private static final int COMPACT_ACTION_FULL_FLAG = 3;
+ private static final String COMPACT_ACTION_FILE = "file";
+ private static final String COMPACT_ACTION_ANON = "anon";
+ private static final String COMPACT_ACTION_FULL = "all";
+
+ // Defaults for phenotype flags.
+ @VisibleForTesting static final Boolean DEFAULT_USE_COMPACTION = false;
+ @VisibleForTesting static final int DEFAULT_COMPACT_ACTION_1 = COMPACT_ACTION_FILE_FLAG;
+ @VisibleForTesting static final int DEFAULT_COMPACT_ACTION_2 = COMPACT_ACTION_FULL_FLAG;
+ @VisibleForTesting static final long DEFAULT_COMPACT_THROTTLE_1 = 5_000;
+ @VisibleForTesting static final long DEFAULT_COMPACT_THROTTLE_2 = 10_000;
+ @VisibleForTesting static final long DEFAULT_COMPACT_THROTTLE_3 = 500;
+ @VisibleForTesting static final long DEFAULT_COMPACT_THROTTLE_4 = 10_000;
+
+ @VisibleForTesting
+ interface PropertyChangedCallbackForTest {
+ void onPropertyChanged();
+ }
+ private PropertyChangedCallbackForTest mTestCallback;
+
+ // Handler constants.
static final int COMPACT_PROCESS_SOME = 1;
static final int COMPACT_PROCESS_FULL = 2;
static final int COMPACT_PROCESS_MSG = 1;
@@ -57,70 +86,106 @@ public final class AppCompactor {
*/
final ServiceThread mCompactionThread;
- final private Handler mCompactionHandler;
-
- final private ActivityManagerService mAm;
- final private ActivityManagerConstants mConstants;
-
- final private String COMPACT_ACTION_FILE = "file";
- final private String COMPACT_ACTION_ANON = "anon";
- final private String COMPACT_ACTION_FULL = "all";
-
- final private String compactActionSome;
- final private String compactActionFull;
-
- final private long throttleSomeSome;
- final private long throttleSomeFull;
- final private long throttleFullSome;
- final private long throttleFullFull;
+ private final ArrayList<ProcessRecord> mPendingCompactionProcesses =
+ new ArrayList<ProcessRecord>();
+ private final ActivityManagerService mAm;
+ private final OnPropertyChangedListener mOnFlagsChangedListener =
+ new OnPropertyChangedListener() {
+ @Override
+ public void onPropertyChanged(String namespace, String name, String value) {
+ synchronized (mPhenotypeFlagLock) {
+ if (KEY_USE_COMPACTION.equals(name)) {
+ updateUseCompaction();
+ } else if (KEY_COMPACT_ACTION_1.equals(name)
+ || KEY_COMPACT_ACTION_2.equals(name)) {
+ updateCompactionActions();
+ } else if (KEY_COMPACT_THROTTLE_1.equals(name)
+ || KEY_COMPACT_THROTTLE_2.equals(name)
+ || KEY_COMPACT_THROTTLE_3.equals(name)
+ || KEY_COMPACT_THROTTLE_4.equals(name)) {
+ updateCompactionThrottles();
+ }
+ }
+ if (mTestCallback != null) {
+ mTestCallback.onPropertyChanged();
+ }
+ }
+ };
+
+ private final Object mPhenotypeFlagLock = new Object();
+
+ // Configured by phenotype. Updates from the server take effect immediately.
+ @GuardedBy("mPhenotypeFlagLock")
+ @VisibleForTesting String mCompactActionSome =
+ compactActionIntToString(DEFAULT_COMPACT_ACTION_1);
+ @GuardedBy("mPhenotypeFlagLock")
+ @VisibleForTesting String mCompactActionFull =
+ compactActionIntToString(DEFAULT_COMPACT_ACTION_2);
+ @GuardedBy("mPhenotypeFlagLock")
+ @VisibleForTesting long mCompactThrottleSomeSome = DEFAULT_COMPACT_THROTTLE_1;
+ @GuardedBy("mPhenotypeFlagLock")
+ @VisibleForTesting long mCompactThrottleSomeFull = DEFAULT_COMPACT_THROTTLE_2;
+ @GuardedBy("mPhenotypeFlagLock")
+ @VisibleForTesting long mCompactThrottleFullSome = DEFAULT_COMPACT_THROTTLE_3;
+ @GuardedBy("mPhenotypeFlagLock")
+ @VisibleForTesting long mCompactThrottleFullFull = DEFAULT_COMPACT_THROTTLE_4;
+ @GuardedBy("mPhenotypeFlagLock")
+ private boolean mUseCompaction = DEFAULT_USE_COMPACTION;
+
+ // Handler on which compaction runs.
+ private Handler mCompactionHandler;
public AppCompactor(ActivityManagerService am) {
mAm = am;
- mConstants = am.mConstants;
-
mCompactionThread = new ServiceThread("CompactionThread",
THREAD_PRIORITY_FOREGROUND, true);
- mCompactionThread.start();
- mCompactionHandler = new MemCompactionHandler(this);
-
- switch(mConstants.COMPACT_ACTION_1) {
- case 1:
- compactActionSome = COMPACT_ACTION_FILE;
- break;
- case 2:
- compactActionSome = COMPACT_ACTION_ANON;
- break;
- case 3:
- compactActionSome = COMPACT_ACTION_FULL;
- break;
- default:
- compactActionSome = COMPACT_ACTION_FILE;
- break;
+ }
+
+ @VisibleForTesting
+ AppCompactor(ActivityManagerService am, PropertyChangedCallbackForTest callback) {
+ this(am);
+ mTestCallback = callback;
+ }
+
+ /**
+ * Reads phenotype config to determine whether app compaction is enabled or not and
+ * starts the background thread if necessary.
+ */
+ public void init() {
+ DeviceConfig.addOnPropertyChangedListener(DeviceConfig.ActivityManager.NAMESPACE,
+ ActivityThread.currentApplication().getMainExecutor(), mOnFlagsChangedListener);
+ synchronized (mPhenotypeFlagLock) {
+ updateUseCompaction();
+ updateCompactionActions();
+ updateCompactionThrottles();
}
+ }
- switch(mConstants.COMPACT_ACTION_2) {
- case 1:
- compactActionFull = COMPACT_ACTION_FILE;
- break;
- case 2:
- compactActionFull = COMPACT_ACTION_ANON;
- break;
- case 3:
- compactActionFull = COMPACT_ACTION_FULL;
- break;
- default:
- compactActionFull = COMPACT_ACTION_FULL;
- break;
+ /**
+ * Returns whether compaction is enabled.
+ */
+ public boolean useCompaction() {
+ synchronized (mPhenotypeFlagLock) {
+ return mUseCompaction;
}
+ }
- throttleSomeSome = mConstants.COMPACT_THROTTLE_1;
- throttleSomeFull = mConstants.COMPACT_THROTTLE_2;
- throttleFullSome = mConstants.COMPACT_THROTTLE_3;
- throttleFullFull = mConstants.COMPACT_THROTTLE_4;
+ @GuardedBy("mAm")
+ void dump(PrintWriter pw) {
+ pw.println("AppCompactor settings");
+ synchronized (mPhenotypeFlagLock) {
+ pw.println(" " + KEY_USE_COMPACTION + "=" + mUseCompaction);
+ pw.println(" " + KEY_COMPACT_ACTION_1 + "=" + mCompactActionSome);
+ pw.println(" " + KEY_COMPACT_ACTION_2 + "=" + mCompactActionFull);
+ pw.println(" " + KEY_COMPACT_THROTTLE_1 + "=" + mCompactThrottleSomeSome);
+ pw.println(" " + KEY_COMPACT_THROTTLE_2 + "=" + mCompactThrottleSomeFull);
+ pw.println(" " + KEY_COMPACT_THROTTLE_3 + "=" + mCompactThrottleFullSome);
+ pw.println(" " + KEY_COMPACT_THROTTLE_4 + "=" + mCompactThrottleFullFull);
+ }
}
- // Must be called while holding AMS lock.
- final void compactAppSome(ProcessRecord app) {
+ @GuardedBy("mAm")
+ void compactAppSome(ProcessRecord app) {
app.reqCompactAction = COMPACT_PROCESS_SOME;
mPendingCompactionProcesses.add(app);
mCompactionHandler.sendMessage(
@@ -128,8 +193,8 @@ public final class AppCompactor {
COMPACT_PROCESS_MSG, app.curAdj, app.setProcState));
}
- // Must be called while holding AMS lock.
- final void compactAppFull(ProcessRecord app) {
+ @GuardedBy("mAm")
+ void compactAppFull(ProcessRecord app) {
app.reqCompactAction = COMPACT_PROCESS_FULL;
mPendingCompactionProcesses.add(app);
mCompactionHandler.sendMessage(
@@ -137,97 +202,205 @@ public final class AppCompactor {
COMPACT_PROCESS_MSG, app.curAdj, app.setProcState));
}
- final class MemCompactionHandler extends Handler {
- AppCompactor mAc;
- private MemCompactionHandler(AppCompactor ac) {
- super(ac.mCompactionThread.getLooper());
- mAc = ac;
+ /**
+ * Reads the flag value from DeviceConfig to determine whether app compaction
+ * should be enabled, and starts/stops the compaction thread as needed.
+ */
+ @GuardedBy("mPhenotypeFlagLock")
+ private void updateUseCompaction() {
+ String useCompactionFlag =
+ DeviceConfig.getProperty(DeviceConfig.ActivityManager.NAMESPACE,
+ KEY_USE_COMPACTION);
+ mUseCompaction = TextUtils.isEmpty(useCompactionFlag)
+ ? DEFAULT_USE_COMPACTION : Boolean.parseBoolean(useCompactionFlag);
+ if (mUseCompaction && !mCompactionThread.isAlive()) {
+ mCompactionThread.start();
+ mCompactionHandler = new MemCompactionHandler();
+ }
+ }
+
+ @GuardedBy("mPhenotypeFlagLock")
+ private void updateCompactionActions() {
+ String compactAction1Flag =
+ DeviceConfig.getProperty(DeviceConfig.ActivityManager.NAMESPACE,
+ KEY_COMPACT_ACTION_1);
+ String compactAction2Flag =
+ DeviceConfig.getProperty(DeviceConfig.ActivityManager.NAMESPACE,
+ KEY_COMPACT_ACTION_2);
+
+ int compactAction1 = DEFAULT_COMPACT_ACTION_1;
+ try {
+ compactAction1 = TextUtils.isEmpty(compactAction1Flag)
+ ? DEFAULT_COMPACT_ACTION_1 : Integer.parseInt(compactAction1Flag);
+ } catch (NumberFormatException e) {
+ // Do nothing, leave default.
+ }
+
+ int compactAction2 = DEFAULT_COMPACT_ACTION_2;
+ try {
+ compactAction2 = TextUtils.isEmpty(compactAction2Flag)
+ ? DEFAULT_COMPACT_ACTION_2 : Integer.parseInt(compactAction2Flag);
+ } catch (NumberFormatException e) {
+ // Do nothing, leave default.
+ }
+
+ mCompactActionSome = compactActionIntToString(compactAction1);
+ mCompactActionFull = compactActionIntToString(compactAction2);
+ }
+
+ @GuardedBy("mPhenotypeFlagLock")
+ private void updateCompactionThrottles() {
+ boolean useThrottleDefaults = false;
+ String throttleSomeSomeFlag =
+ DeviceConfig.getProperty(DeviceConfig.ActivityManager.NAMESPACE,
+ KEY_COMPACT_THROTTLE_1);
+ String throttleSomeFullFlag =
+ DeviceConfig.getProperty(DeviceConfig.ActivityManager.NAMESPACE,
+ KEY_COMPACT_THROTTLE_2);
+ String throttleFullSomeFlag =
+ DeviceConfig.getProperty(DeviceConfig.ActivityManager.NAMESPACE,
+ KEY_COMPACT_THROTTLE_3);
+ String throttleFullFullFlag =
+ DeviceConfig.getProperty(DeviceConfig.ActivityManager.NAMESPACE,
+ KEY_COMPACT_THROTTLE_4);
+
+ if (TextUtils.isEmpty(throttleSomeSomeFlag) || TextUtils.isEmpty(throttleSomeFullFlag)
+ || TextUtils.isEmpty(throttleFullSomeFlag)
+ || TextUtils.isEmpty(throttleFullFullFlag)) {
+ // Set defaults for all if any are not set.
+ useThrottleDefaults = true;
+ } else {
+ try {
+ mCompactThrottleSomeSome = Integer.parseInt(throttleSomeSomeFlag);
+ mCompactThrottleSomeFull = Integer.parseInt(throttleSomeFullFlag);
+ mCompactThrottleFullSome = Integer.parseInt(throttleFullSomeFlag);
+ mCompactThrottleFullFull = Integer.parseInt(throttleFullFullFlag);
+ } catch (NumberFormatException e) {
+ useThrottleDefaults = true;
+ }
+ }
+
+ if (useThrottleDefaults) {
+ mCompactThrottleSomeSome = DEFAULT_COMPACT_THROTTLE_1;
+ mCompactThrottleSomeFull = DEFAULT_COMPACT_THROTTLE_2;
+ mCompactThrottleFullSome = DEFAULT_COMPACT_THROTTLE_3;
+ mCompactThrottleFullFull = DEFAULT_COMPACT_THROTTLE_4;
+ }
+ }
+
+ @VisibleForTesting
+ static String compactActionIntToString(int action) {
+ switch(action) {
+ case COMPACT_ACTION_FILE_FLAG:
+ return COMPACT_ACTION_FILE;
+ case COMPACT_ACTION_ANON_FLAG:
+ return COMPACT_ACTION_ANON;
+ case COMPACT_ACTION_FULL_FLAG:
+ return COMPACT_ACTION_FULL;
+ default:
+ return COMPACT_ACTION_FILE;
+ }
+ }
+
+ private final class MemCompactionHandler extends Handler {
+ private MemCompactionHandler() {
+ super(mCompactionThread.getLooper());
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
- case COMPACT_PROCESS_MSG: {
- long start = SystemClock.uptimeMillis();
- ProcessRecord proc;
- int pid;
- String action;
- final String name;
- int pendingAction, lastCompactAction;
- long lastCompactTime;
- synchronized(mAc.mAm) {
- proc = mAc.mPendingCompactionProcesses.remove(0);
-
- // don't compact if the process has returned to perceptible
- if (proc.setAdj <= ProcessList.PERCEPTIBLE_APP_ADJ) {
- return;
+ case COMPACT_PROCESS_MSG: {
+ long start = SystemClock.uptimeMillis();
+ ProcessRecord proc;
+ int pid;
+ String action;
+ final String name;
+ int pendingAction, lastCompactAction;
+ long lastCompactTime;
+ synchronized (mAm) {
+ proc = mPendingCompactionProcesses.remove(0);
+
+ // don't compact if the process has returned to perceptible
+ if (proc.setAdj <= ProcessList.PERCEPTIBLE_APP_ADJ) {
+ return;
+ }
+
+ pid = proc.pid;
+ name = proc.processName;
+ pendingAction = proc.reqCompactAction;
+ lastCompactAction = proc.lastCompactAction;
+ lastCompactTime = proc.lastCompactTime;
}
- pid = proc.pid;
- name = proc.processName;
- pendingAction = proc.reqCompactAction;
- lastCompactAction = proc.lastCompactAction;
- lastCompactTime = proc.lastCompactTime;
- }
- if (pid == 0) {
- // not a real process, either one being launched or one being killed
- return;
- }
-
- // basic throttling
- // use the ActivityManagerConstants knobs to determine whether current/prevous
- // compaction combo should be throtted or not
- if (pendingAction == COMPACT_PROCESS_SOME) {
- if ((lastCompactAction == COMPACT_PROCESS_SOME && (start - lastCompactTime < throttleSomeSome)) ||
- (lastCompactAction == COMPACT_PROCESS_FULL && (start - lastCompactTime < throttleSomeFull))) {
+ if (pid == 0) {
+ // not a real process, either one being launched or one being killed
return;
}
- } else {
- if ((lastCompactAction == COMPACT_PROCESS_SOME && (start - lastCompactTime < throttleFullSome)) ||
- (lastCompactAction == COMPACT_PROCESS_FULL && (start - lastCompactTime < throttleFullFull))) {
- return;
+
+ // basic throttling
+ // use the Phenotype flag knobs to determine whether current/prevous
+ // compaction combo should be throtted or not
+
+ // Note that we explicitly don't take mPhenotypeFlagLock here as the flags
+ // should very seldom change, and taking the risk of using the wrong action is
+ // preferable to taking the lock for every single compaction action.
+ if (pendingAction == COMPACT_PROCESS_SOME) {
+ if ((lastCompactAction == COMPACT_PROCESS_SOME
+ && (start - lastCompactTime < mCompactThrottleSomeSome))
+ || (lastCompactAction == COMPACT_PROCESS_FULL
+ && (start - lastCompactTime
+ < mCompactThrottleSomeFull))) {
+ return;
+ }
+ } else {
+ if ((lastCompactAction == COMPACT_PROCESS_SOME
+ && (start - lastCompactTime < mCompactThrottleFullSome))
+ || (lastCompactAction == COMPACT_PROCESS_FULL
+ && (start - lastCompactTime
+ < mCompactThrottleFullFull))) {
+ return;
+ }
}
- }
- try {
- Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Compact " +
- ((pendingAction == COMPACT_PROCESS_SOME) ? "some" : "full") +
- ": " + name);
- long[] rssBefore = Process.getRss(pid);
- FileOutputStream fos = new FileOutputStream("/proc/" + pid + "/reclaim");
if (pendingAction == COMPACT_PROCESS_SOME) {
- action = compactActionSome;
+ action = mCompactActionSome;
} else {
- action = compactActionFull;
+ action = mCompactActionFull;
}
- fos.write(action.getBytes());
- fos.close();
- long[] rssAfter = Process.getRss(pid);
- long end = SystemClock.uptimeMillis();
- long time = end - start;
- EventLog.writeEvent(EventLogTags.AM_COMPACT, pid, name, action,
- rssBefore[0], rssBefore[1], rssBefore[2], rssBefore[3],
- rssAfter[0], rssAfter[1], rssAfter[2], rssAfter[3], time,
- lastCompactAction, lastCompactTime, msg.arg1, msg.arg2);
- StatsLog.write(StatsLog.APP_COMPACTED, pid, name, pendingAction,
- rssBefore[0], rssBefore[1], rssBefore[2], rssBefore[3],
- rssAfter[0], rssAfter[1], rssAfter[2], rssAfter[3], time,
- lastCompactAction, lastCompactTime, msg.arg1,
- ActivityManager.processStateAmToProto(msg.arg2));
- synchronized(mAc.mAm) {
- proc.lastCompactTime = end;
- proc.lastCompactAction = pendingAction;
+
+ try {
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Compact "
+ + ((pendingAction == COMPACT_PROCESS_SOME) ? "some" : "full")
+ + ": " + name);
+ long[] rssBefore = Process.getRss(pid);
+ FileOutputStream fos = new FileOutputStream("/proc/" + pid + "/reclaim");
+ fos.write(action.getBytes());
+ fos.close();
+ long[] rssAfter = Process.getRss(pid);
+ long end = SystemClock.uptimeMillis();
+ long time = end - start;
+ EventLog.writeEvent(EventLogTags.AM_COMPACT, pid, name, action,
+ rssBefore[0], rssBefore[1], rssBefore[2], rssBefore[3],
+ rssAfter[0], rssAfter[1], rssAfter[2], rssAfter[3], time,
+ lastCompactAction, lastCompactTime, msg.arg1, msg.arg2);
+ StatsLog.write(StatsLog.APP_COMPACTED, pid, name, pendingAction,
+ rssBefore[0], rssBefore[1], rssBefore[2], rssBefore[3],
+ rssAfter[0], rssAfter[1], rssAfter[2], rssAfter[3], time,
+ lastCompactAction, lastCompactTime, msg.arg1,
+ ActivityManager.processStateAmToProto(msg.arg2));
+ synchronized (mAm) {
+ proc.lastCompactTime = end;
+ proc.lastCompactAction = pendingAction;
+ }
+ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+ } catch (Exception e) {
+ // nothing to do, presumably the process died
+ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
- Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
- } catch (Exception e) {
- // nothing to do, presumably the process died
- Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
}
- }
}
}
-
-
}
diff --git a/services/core/java/com/android/server/am/BaseErrorDialog.java b/services/core/java/com/android/server/am/BaseErrorDialog.java
index aabb5877764e..dc9a4bf1ad94 100644
--- a/services/core/java/com/android/server/am/BaseErrorDialog.java
+++ b/services/core/java/com/android/server/am/BaseErrorDialog.java
@@ -16,8 +16,6 @@
package com.android.server.am;
-import com.android.internal.R;
-
import android.app.AlertDialog;
import android.content.Context;
import android.os.Handler;
@@ -26,6 +24,8 @@ import android.view.KeyEvent;
import android.view.WindowManager;
import android.widget.Button;
+import com.android.internal.R;
+
public class BaseErrorDialog extends AlertDialog {
private static final int ENABLE_BUTTONS = 0;
private static final int DISABLE_BUTTONS = 1;
@@ -36,7 +36,7 @@ public class BaseErrorDialog extends AlertDialog {
super(context, com.android.internal.R.style.Theme_DeviceDefault_Dialog_AppError);
context.assertRuntimeOverlayThemable();
- getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
+ getWindow().setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
WindowManager.LayoutParams attrs = getWindow().getAttributes();
diff --git a/services/core/java/com/android/server/am/CoreSettingsObserver.java b/services/core/java/com/android/server/am/CoreSettingsObserver.java
index d3953b58296c..3d69aa8088f5 100644
--- a/services/core/java/com/android/server/am/CoreSettingsObserver.java
+++ b/services/core/java/com/android/server/am/CoreSettingsObserver.java
@@ -61,6 +61,8 @@ final class CoreSettingsObserver extends ContentObserver {
Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_PKGS, String.class);
sGlobalSettingToTypeMap.put(
Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_VALUES, String.class);
+ sGlobalSettingToTypeMap.put(
+ Settings.Global.GLOBAL_SETTINGS_ANGLE_WHITELIST, String.class);
sGlobalSettingToTypeMap.put(Settings.Global.ENABLE_GPU_DEBUG_LAYERS, int.class);
sGlobalSettingToTypeMap.put(Settings.Global.GPU_DEBUG_APP, String.class);
sGlobalSettingToTypeMap.put(Settings.Global.GPU_DEBUG_LAYERS, String.class);
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index be910d46a8ad..1e03f6c3ba5f 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -134,10 +134,11 @@ public final class OomAdjuster {
mLocalPowerManager = LocalServices.getService(PowerManagerInternal.class);
mConstants = mService.mConstants;
- // mConstants can be null under test, which causes AppCompactor to crash
- if (mConstants != null) {
- mAppCompact = new AppCompactor(mService);
- }
+ mAppCompact = new AppCompactor(mService);
+ }
+
+ void initSettings() {
+ mAppCompact.init();
}
/**
@@ -1679,7 +1680,7 @@ public final class OomAdjuster {
if (app.curAdj != app.setAdj) {
// don't compact during bootup
- if (mConstants.USE_COMPACTION && mService.mBooted) {
+ if (mAppCompact.useCompaction() && mService.mBooted) {
// Perform a minor compaction when a perceptible app becomes the prev/home app
// Perform a major compaction when any app enters cached
// reminder: here, setAdj is previous state, curAdj is upcoming state
@@ -2104,4 +2105,8 @@ public final class OomAdjuster {
+ " mNewNumServiceProcs=" + mNewNumServiceProcs);
}
+ @GuardedBy("mService")
+ void dumpAppCompactorSettings(PrintWriter pw) {
+ mAppCompact.dump(pw);
+ }
}
diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java
index 55cca950a213..0b27a8ad77dc 100644
--- a/services/core/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/core/java/com/android/server/am/PendingIntentRecord.java
@@ -379,6 +379,10 @@ public final class PendingIntentRecord extends IIntentSender.Stub {
if (userId == UserHandle.USER_CURRENT) {
userId = controller.mUserController.getCurrentOrTargetUserId();
}
+ // temporarily allow receivers and services to open activities from background if the
+ // PendingIntent.send() caller was foreground at the time of sendInner() call
+ final boolean allowTrampoline = uid != callingUid
+ && controller.mAtmInternal.isUidForeground(callingUid);
switch (key.type) {
case ActivityManager.INTENT_SENDER_ACTIVITY:
@@ -419,7 +423,8 @@ public final class PendingIntentRecord extends IIntentSender.Stub {
uid, finalIntent, resolvedType, finishedReceiver, code, null, null,
requiredPermission, options, (finishedReceiver != null),
false, userId,
- mAllowBgActivityStartsForBroadcastSender.contains(whitelistToken));
+ mAllowBgActivityStartsForBroadcastSender.contains(whitelistToken)
+ || allowTrampoline);
if (sent == ActivityManager.BROADCAST_SUCCESS) {
sendFinish = false;
}
@@ -433,7 +438,8 @@ public final class PendingIntentRecord extends IIntentSender.Stub {
controller.mAmInternal.startServiceInPackage(uid, finalIntent, resolvedType,
key.type == ActivityManager.INTENT_SENDER_FOREGROUND_SERVICE,
key.packageName, userId,
- mAllowBgActivityStartsForServiceSender.contains(whitelistToken));
+ mAllowBgActivityStartsForServiceSender.contains(whitelistToken)
+ || allowTrampoline);
} catch (RuntimeException e) {
Slog.w(TAG, "Unable to send startService intent", e);
} catch (TransactionTooLargeException e) {
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index c1be3872e031..003ddd13f152 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -145,6 +145,11 @@ public final class ProcessList {
static final int CACHED_APP_MAX_ADJ = 999;
static final int CACHED_APP_MIN_ADJ = 900;
+ // This is the oom_adj level that we allow to die first. This cannot be equal to
+ // CACHED_APP_MAX_ADJ unless processes are actively being assigned an oom_score_adj of
+ // CACHED_APP_MAX_ADJ.
+ static final int CACHED_APP_LMK_FIRST_ADJ = 950;
+
// Number of levels we have available for different service connection group importance
// levels.
static final int CACHED_APP_IMPORTANCE_LEVELS = 5;
@@ -266,7 +271,7 @@ public final class ProcessList {
// can't give it a different value for every possible kind of process.
private final int[] mOomAdj = new int[] {
FOREGROUND_APP_ADJ, VISIBLE_APP_ADJ, PERCEPTIBLE_APP_ADJ,
- BACKUP_APP_ADJ, CACHED_APP_MIN_ADJ, CACHED_APP_MAX_ADJ
+ BACKUP_APP_ADJ, CACHED_APP_MIN_ADJ, CACHED_APP_LMK_FIRST_ADJ
};
// These are the low-end OOM level limits. This is appropriate for an
// HVGA or smaller phone with less than 512MB. Values are in KB.
@@ -1497,7 +1502,7 @@ public final class ProcessList {
mService.mNativeDebuggingApp = null;
}
- if ((app.info.privateFlags & ApplicationInfo.PRIVATE_FLAG_PREFER_CODE_INTEGRITY) != 0
+ if (app.info.isCodeIntegrityPreferred()
|| (app.info.isPrivilegedApp()
&& DexManager.isPackageSelectedToRunOob(app.pkgList.mPkgList.keySet()))) {
runtimeFlags |= Zygote.ONLY_USE_SYSTEM_OAT_FILES;
@@ -1730,7 +1735,7 @@ public final class ProcessList {
app.processName, uid, uid, gids, runtimeFlags, mountExternal,
app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
app.info.dataDir, null, app.info.packageName,
- packageNames, visibleVolIds,
+ packageNames, visibleVolIds, /*useBlastulaPool=*/ false,
new String[] {PROC_START_SEQ_IDENT + app.startSeq});
} else {
startResult = Process.start(entryPoint,
@@ -2379,14 +2384,14 @@ public final class ProcessList {
}
@GuardedBy("mService")
- void setAllHttpProxyLocked(String host, String port, String exclList, Uri pacFileUrl) {
+ void setAllHttpProxyLocked() {
for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
ProcessRecord r = mLruProcesses.get(i);
// Don't dispatch to isolated processes as they can't access
// ConnectivityManager and don't have network privileges anyway.
if (r.thread != null && !r.isolated) {
try {
- r.thread.setHttpProxy(host, port, exclList, pacFileUrl);
+ r.thread.updateHttpProxy();
} catch (RemoteException ex) {
Slog.w(TAG, "Failed to update http proxy for: " +
r.info.processName);
diff --git a/services/core/java/com/android/server/connectivity/KeepaliveTracker.java b/services/core/java/com/android/server/connectivity/KeepaliveTracker.java
index 8a3cdcad0230..1559ba8ba883 100644
--- a/services/core/java/com/android/server/connectivity/KeepaliveTracker.java
+++ b/services/core/java/com/android/server/connectivity/KeepaliveTracker.java
@@ -16,40 +16,49 @@
package com.android.server.connectivity;
-import com.android.internal.util.HexDump;
-import com.android.internal.util.IndentingPrintWriter;
-import com.android.server.connectivity.NetworkAgentInfo;
-import android.net.ConnectivityManager;
+// TODO: Clean up imports and remove references of PacketKeepalive constants.
+
+import static android.net.ConnectivityManager.PacketKeepalive.ERROR_INVALID_INTERVAL;
+import static android.net.ConnectivityManager.PacketKeepalive.ERROR_INVALID_IP_ADDRESS;
+import static android.net.ConnectivityManager.PacketKeepalive.ERROR_INVALID_NETWORK;
+import static android.net.ConnectivityManager.PacketKeepalive.MIN_INTERVAL;
+import static android.net.ConnectivityManager.PacketKeepalive.NATT_PORT;
+import static android.net.ConnectivityManager.PacketKeepalive.NO_KEEPALIVE;
+import static android.net.ConnectivityManager.PacketKeepalive.SUCCESS;
+import static android.net.NetworkAgent.CMD_START_PACKET_KEEPALIVE;
+import static android.net.NetworkAgent.CMD_STOP_PACKET_KEEPALIVE;
+import static android.net.NetworkAgent.EVENT_PACKET_KEEPALIVE;
+import static android.net.SocketKeepalive.ERROR_INVALID_SOCKET;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.net.ConnectivityManager.PacketKeepalive;
import android.net.KeepalivePacketData;
-import android.net.LinkAddress;
import android.net.NetworkAgent;
import android.net.NetworkUtils;
import android.net.util.IpUtils;
import android.os.Binder;
-import android.os.IBinder;
import android.os.Handler;
+import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.Process;
import android.os.RemoteException;
-import android.system.OsConstants;
+import android.system.ErrnoException;
+import android.system.Os;
import android.util.Log;
import android.util.Pair;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.net.Inet4Address;
-import java.net.Inet6Address;
+import com.android.internal.util.HexDump;
+import com.android.internal.util.IndentingPrintWriter;
+
+import java.io.FileDescriptor;
import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
import java.util.ArrayList;
import java.util.HashMap;
-import static android.net.ConnectivityManager.PacketKeepalive.*;
-import static android.net.NetworkAgent.CMD_START_PACKET_KEEPALIVE;
-import static android.net.NetworkAgent.CMD_STOP_PACKET_KEEPALIVE;
-import static android.net.NetworkAgent.EVENT_PACKET_KEEPALIVE;
-
/**
* Manages packet keepalive requests.
*
@@ -300,7 +309,9 @@ public class KeepaliveTracker {
}
}
- public void handleEventPacketKeepalive(NetworkAgentInfo nai, Message message) {
+ /** Handle keepalive events from lower layer. */
+ public void handleEventPacketKeepalive(@NonNull NetworkAgentInfo nai,
+ @NonNull Message message) {
int slot = message.arg1;
int reason = message.arg2;
@@ -330,8 +341,18 @@ public class KeepaliveTracker {
}
}
- public void startNattKeepalive(NetworkAgentInfo nai, int intervalSeconds, Messenger messenger,
- IBinder binder, String srcAddrString, int srcPort, String dstAddrString, int dstPort) {
+ /**
+ * Called when requesting that keepalives be started on a IPsec NAT-T socket. See
+ * {@link android.net.SocketKeepalive}.
+ **/
+ public void startNattKeepalive(@Nullable NetworkAgentInfo nai,
+ int intervalSeconds,
+ @NonNull Messenger messenger,
+ @NonNull IBinder binder,
+ @NonNull String srcAddrString,
+ int srcPort,
+ @NonNull String dstAddrString,
+ int dstPort) {
if (nai == null) {
notifyMessenger(messenger, NO_KEEPALIVE, ERROR_INVALID_NETWORK);
return;
@@ -360,6 +381,56 @@ public class KeepaliveTracker {
NetworkAgent.CMD_START_PACKET_KEEPALIVE, ki).sendToTarget();
}
+ /**
+ * Called when requesting that keepalives be started on a IPsec NAT-T socket. This function is
+ * identical to {@link #startNattKeepalive}, but also takes a {@code resourceId}, which is the
+ * resource index bound to the {@link UdpEncapsulationSocket} when creating by
+ * {@link com.android.server.IpSecService} to verify whether the given
+ * {@link UdpEncapsulationSocket} is legitimate.
+ **/
+ public void startNattKeepalive(@Nullable NetworkAgentInfo nai,
+ @Nullable FileDescriptor fd,
+ int resourceId,
+ int intervalSeconds,
+ @NonNull Messenger messenger,
+ @NonNull IBinder binder,
+ @NonNull String srcAddrString,
+ @NonNull String dstAddrString,
+ int dstPort) {
+ // Ensure that the socket is created by IpSecService.
+ if (!isNattKeepaliveSocketValid(fd, resourceId)) {
+ notifyMessenger(messenger, NO_KEEPALIVE, ERROR_INVALID_SOCKET);
+ }
+
+ // Get src port to adopt old API.
+ int srcPort = 0;
+ try {
+ final SocketAddress srcSockAddr = Os.getsockname(fd);
+ srcPort = ((InetSocketAddress) srcSockAddr).getPort();
+ } catch (ErrnoException e) {
+ notifyMessenger(messenger, NO_KEEPALIVE, ERROR_INVALID_SOCKET);
+ }
+
+ // Forward request to old API.
+ startNattKeepalive(nai, intervalSeconds, messenger, binder, srcAddrString, srcPort,
+ dstAddrString, dstPort);
+ }
+
+ /**
+ * Verify if the IPsec NAT-T file descriptor and resource Id hold for IPsec keepalive is valid.
+ **/
+ public static boolean isNattKeepaliveSocketValid(@Nullable FileDescriptor fd, int resourceId) {
+ // TODO: 1. confirm whether the fd is called from system api or created by IpSecService.
+ // 2. If the fd is created from the system api, check that it's bounded. And
+ // call dup to keep the fd open.
+ // 3. If the fd is created from IpSecService, check if the resource ID is valid. And
+ // hold the resource needed in IpSecService.
+ if (null == fd) {
+ return false;
+ }
+ return true;
+ }
+
public void dump(IndentingPrintWriter pw) {
pw.println("Packet keepalives:");
pw.increaseIndent();
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index 9ea73fbb1882..d0cff25dbf05 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -152,6 +152,10 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> {
// Whether a captive portal was found during the last network validation attempt.
public boolean lastCaptivePortalDetected;
+ // Indicates the user was notified of a successful captive portal login since a portal was
+ // last detected.
+ public boolean captivePortalLoginNotified;
+
// Networks are lingered when they become unneeded as a result of their NetworkRequests being
// satisfied by a higher-scoring network. so as to allow communication to wrap up before the
// network is taken down. This usually only happens to the default network. Lingering ends with
@@ -618,18 +622,19 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> {
}
public String toString() {
- return "NetworkAgentInfo{ ni{" + networkInfo + "} " +
- "network{" + network + "} nethandle{" + network.getNetworkHandle() + "} " +
- "lp{" + linkProperties + "} " +
- "nc{" + networkCapabilities + "} Score{" + getCurrentScore() + "} " +
- "everValidated{" + everValidated + "} lastValidated{" + lastValidated + "} " +
- "created{" + created + "} lingering{" + isLingering() + "} " +
- "explicitlySelected{" + networkMisc.explicitlySelected + "} " +
- "acceptUnvalidated{" + networkMisc.acceptUnvalidated + "} " +
- "everCaptivePortalDetected{" + everCaptivePortalDetected + "} " +
- "lastCaptivePortalDetected{" + lastCaptivePortalDetected + "} " +
- "clat{" + clatd + "} " +
- "}";
+ return "NetworkAgentInfo{ ni{" + networkInfo + "} "
+ + "network{" + network + "} nethandle{" + network.getNetworkHandle() + "} "
+ + "lp{" + linkProperties + "} "
+ + "nc{" + networkCapabilities + "} Score{" + getCurrentScore() + "} "
+ + "everValidated{" + everValidated + "} lastValidated{" + lastValidated + "} "
+ + "created{" + created + "} lingering{" + isLingering() + "} "
+ + "explicitlySelected{" + networkMisc.explicitlySelected + "} "
+ + "acceptUnvalidated{" + networkMisc.acceptUnvalidated + "} "
+ + "everCaptivePortalDetected{" + everCaptivePortalDetected + "} "
+ + "lastCaptivePortalDetected{" + lastCaptivePortalDetected + "} "
+ + "captivePortalLoginNotified{" + captivePortalLoginNotified + "} "
+ + "clat{" + clatd + "} "
+ + "}";
}
public String name() {
diff --git a/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java b/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java
index 36a2476d2ceb..b50477bc120f 100644
--- a/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java
+++ b/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java
@@ -16,13 +16,16 @@
package com.android.server.connectivity;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
+
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
-import android.net.NetworkCapabilities;
import android.net.wifi.WifiInfo;
import android.os.UserHandle;
import android.telephony.TelephonyManager;
@@ -31,15 +34,12 @@ import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseIntArray;
import android.widget.Toast;
+
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.internal.notification.SystemNotificationChannels;
-import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
-import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
-import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
-
public class NetworkNotificationManager {
@@ -47,7 +47,8 @@ public class NetworkNotificationManager {
LOST_INTERNET(SystemMessage.NOTE_NETWORK_LOST_INTERNET),
NETWORK_SWITCH(SystemMessage.NOTE_NETWORK_SWITCH),
NO_INTERNET(SystemMessage.NOTE_NETWORK_NO_INTERNET),
- SIGN_IN(SystemMessage.NOTE_NETWORK_SIGN_IN);
+ SIGN_IN(SystemMessage.NOTE_NETWORK_SIGN_IN),
+ LOGGED_IN(SystemMessage.NOTE_NETWORK_LOGGED_IN);
public final int eventId;
@@ -192,6 +193,9 @@ public class NetworkNotificationManager {
details = r.getString(R.string.network_available_sign_in_detailed, name);
break;
}
+ } else if (notifyType == NotificationType.LOGGED_IN) {
+ title = WifiInfo.removeDoubleQuotes(nai.networkCapabilities.getSSID());
+ details = r.getString(R.string.captive_portal_logged_in_detailed);
} else if (notifyType == NotificationType.NETWORK_SWITCH) {
String fromTransport = getTransportName(transportType);
String toTransport = getTransportName(getFirstTransportType(switchToNai));
@@ -239,6 +243,18 @@ public class NetworkNotificationManager {
}
}
+ /**
+ * Clear the notification with the given id, only if it matches the given type.
+ */
+ public void clearNotification(int id, NotificationType notifyType) {
+ final int previousEventId = mNotificationTypeMap.get(id);
+ final NotificationType previousNotifyType = NotificationType.getFromId(previousEventId);
+ if (notifyType != previousNotifyType) {
+ return;
+ }
+ clearNotification(id);
+ }
+
public void clearNotification(int id) {
if (mNotificationTypeMap.indexOfKey(id) < 0) {
return;
@@ -290,6 +306,10 @@ public class NetworkNotificationManager {
return (t != null) ? t.name() : "UNKNOWN";
}
+ /**
+ * A notification with a higher number will take priority over a notification with a lower
+ * number.
+ */
private static int priority(NotificationType t) {
if (t == null) {
return 0;
@@ -302,6 +322,7 @@ public class NetworkNotificationManager {
case NETWORK_SWITCH:
return 2;
case LOST_INTERNET:
+ case LOGGED_IN:
return 1;
default:
return 0;
diff --git a/services/core/java/com/android/server/connectivity/ProxyTracker.java b/services/core/java/com/android/server/connectivity/ProxyTracker.java
index fdddccd20714..a671287324af 100644
--- a/services/core/java/com/android/server/connectivity/ProxyTracker.java
+++ b/services/core/java/com/android/server/connectivity/ProxyTracker.java
@@ -309,22 +309,4 @@ public class ProxyTracker {
}
}
}
-
- /**
- * Enable or disable the default proxy.
- *
- * This sets the flag for enabling/disabling the default proxy and sends the broadcast
- * if applicable.
- * @param enabled whether the default proxy should be enabled.
- */
- public void setDefaultProxyEnabled(final boolean enabled) {
- synchronized (mProxyLock) {
- if (mDefaultProxyEnabled != enabled) {
- mDefaultProxyEnabled = enabled;
- if (mGlobalProxy == null && mDefaultProxy != null) {
- sendProxyBroadcast();
- }
- }
- }
- }
}
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index c72c9ddf3f7a..250884431440 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -151,7 +151,7 @@ public class Vpn {
.divide(BigInteger.valueOf(100));
}
// How many routes to evaluate before bailing and declaring this Vpn should provide
- // the INTERNET capability. This is necessary because computing the adress space is
+ // the INTERNET capability. This is necessary because computing the address space is
// O(n²) and this is running in the system service, so a limit is needed to alleviate
// the risk of attack.
// This is taken as a total of IPv4 + IPV6 routes for simplicity, but the algorithm
@@ -194,6 +194,12 @@ public class Vpn {
private boolean mLockdown = false;
/**
+ * Set of packages in addition to the VPN app itself that can access the network directly when
+ * VPN is not connected even if {@code mLockdown} is set.
+ */
+ private @NonNull List<String> mLockdownWhitelist = Collections.emptyList();
+
+ /**
* List of UIDs for which networking should be blocked until VPN is ready, during brief periods
* when VPN is not running. For example, during system startup or after a crash.
* @see mLockdown
@@ -320,9 +326,9 @@ public class Vpn {
*
* Used to enable/disable legacy VPN lockdown.
*
- * This uses the same ip rule mechanism as {@link #setAlwaysOnPackage(String, boolean)};
- * previous settings from calling that function will be replaced and saved with the
- * always-on state.
+ * This uses the same ip rule mechanism as
+ * {@link #setAlwaysOnPackage(String, boolean, List<String>)}; previous settings from calling
+ * that function will be replaced and saved with the always-on state.
*
* @param lockdown whether to prevent all traffic outside of a VPN.
*/
@@ -419,12 +425,14 @@ public class Vpn {
*
* @param packageName the package to designate as always-on VPN supplier.
* @param lockdown whether to prevent traffic outside of a VPN, for example while connecting.
+ * @param lockdownWhitelist packages to be whitelisted from lockdown.
* @return {@code true} if the package has been set as always-on, {@code false} otherwise.
*/
- public synchronized boolean setAlwaysOnPackage(String packageName, boolean lockdown) {
+ public synchronized boolean setAlwaysOnPackage(
+ String packageName, boolean lockdown, List<String> lockdownWhitelist) {
enforceControlPermissionOrInternalCaller();
- if (setAlwaysOnPackageInternal(packageName, lockdown)) {
+ if (setAlwaysOnPackageInternal(packageName, lockdown, lockdownWhitelist)) {
saveAlwaysOnPackage();
return true;
}
@@ -439,15 +447,27 @@ public class Vpn {
*
* @param packageName the package to designate as always-on VPN supplier.
* @param lockdown whether to prevent traffic outside of a VPN, for example while connecting.
+ * @param lockdownWhitelist packages to be whitelisted from lockdown. This is only used if
+ * {@code lockdown} is {@code true}. Packages must not contain commas.
* @return {@code true} if the package has been set as always-on, {@code false} otherwise.
*/
@GuardedBy("this")
- private boolean setAlwaysOnPackageInternal(String packageName, boolean lockdown) {
+ private boolean setAlwaysOnPackageInternal(
+ String packageName, boolean lockdown, List<String> lockdownWhitelist) {
if (VpnConfig.LEGACY_VPN.equals(packageName)) {
Log.w(TAG, "Not setting legacy VPN \"" + packageName + "\" as always-on.");
return false;
}
+ if (lockdownWhitelist != null) {
+ for (String pkg : lockdownWhitelist) {
+ if (pkg.contains(",")) {
+ Log.w(TAG, "Not setting always-on vpn, invalid whitelisted package: " + pkg);
+ return false;
+ }
+ }
+ }
+
if (packageName != null) {
// Pre-authorize new always-on VPN package.
if (!setPackageAuthorization(packageName, true)) {
@@ -460,13 +480,18 @@ public class Vpn {
}
mLockdown = (mAlwaysOn && lockdown);
+ mLockdownWhitelist = (mLockdown && lockdownWhitelist != null)
+ ? Collections.unmodifiableList(new ArrayList<>(lockdownWhitelist))
+ : Collections.emptyList();
+
if (isCurrentPreparedPackage(packageName)) {
updateAlwaysOnNotification(mNetworkInfo.getDetailedState());
+ setVpnForcedLocked(mLockdown);
} else {
// Prepare this app. The notification will update as a side-effect of updateState().
+ // It also calls setVpnForcedLocked().
prepareInternal(packageName);
}
- setVpnForcedLocked(mLockdown);
return true;
}
@@ -478,7 +503,6 @@ public class Vpn {
* @return the package name of the VPN controller responsible for always-on VPN,
* or {@code null} if none is set or always-on VPN is controlled through
* lockdown instead.
- * @hide
*/
public synchronized String getAlwaysOnPackage() {
enforceControlPermissionOrInternalCaller();
@@ -486,6 +510,13 @@ public class Vpn {
}
/**
+ * @return an immutable list of packages whitelisted from always-on VPN lockdown.
+ */
+ public synchronized List<String> getLockdownWhitelist() {
+ return mLockdown ? mLockdownWhitelist : null;
+ }
+
+ /**
* Save the always-on package and lockdown config into Settings.Secure
*/
@GuardedBy("this")
@@ -496,6 +527,9 @@ public class Vpn {
getAlwaysOnPackage(), mUserHandle);
mSystemServices.settingsSecurePutIntForUser(Settings.Secure.ALWAYS_ON_VPN_LOCKDOWN,
(mAlwaysOn && mLockdown ? 1 : 0), mUserHandle);
+ mSystemServices.settingsSecurePutStringForUser(
+ Settings.Secure.ALWAYS_ON_VPN_LOCKDOWN_WHITELIST,
+ String.join(",", mLockdownWhitelist), mUserHandle);
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -512,7 +546,11 @@ public class Vpn {
Settings.Secure.ALWAYS_ON_VPN_APP, mUserHandle);
final boolean alwaysOnLockdown = mSystemServices.settingsSecureGetIntForUser(
Settings.Secure.ALWAYS_ON_VPN_LOCKDOWN, 0 /*default*/, mUserHandle) != 0;
- setAlwaysOnPackageInternal(alwaysOnPackage, alwaysOnLockdown);
+ final String whitelistString = mSystemServices.settingsSecureGetStringForUser(
+ Settings.Secure.ALWAYS_ON_VPN_LOCKDOWN_WHITELIST, mUserHandle);
+ final List<String> whitelistedPackages = TextUtils.isEmpty(whitelistString)
+ ? Collections.emptyList() : Arrays.asList(whitelistString.split(","));
+ setAlwaysOnPackageInternal(alwaysOnPackage, alwaysOnLockdown, whitelistedPackages);
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -532,7 +570,7 @@ public class Vpn {
}
// Remove always-on VPN if it's not supported.
if (!isAlwaysOnPackageSupported(alwaysOnPackage)) {
- setAlwaysOnPackage(null, false);
+ setAlwaysOnPackage(null, false, null);
return false;
}
// Skip if the service is already established. This isn't bulletproof: it's not bound
@@ -793,6 +831,8 @@ public class Vpn {
}
}
+ lp.setHttpProxy(mConfig.proxyInfo);
+
if (!allowIPv4) {
lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), RTN_UNREACHABLE));
}
@@ -1247,9 +1287,10 @@ public class Vpn {
}
/**
- * Restrict network access from all UIDs affected by this {@link Vpn}, apart from the VPN
- * service app itself, to only sockets that have had {@code protect()} called on them. All
- * non-VPN traffic is blocked via a {@code PROHIBIT} response from the kernel.
+ * Restricts network access from all UIDs affected by this {@link Vpn}, apart from the VPN
+ * service app itself and whitelisted packages, to only sockets that have had {@code protect()}
+ * called on them. All non-VPN traffic is blocked via a {@code PROHIBIT} response from the
+ * kernel.
*
* The exception for the VPN UID isn't technically necessary -- setup should use protected
* sockets -- but in practice it saves apps that don't protect their sockets from breaking.
@@ -1265,8 +1306,13 @@ public class Vpn {
*/
@GuardedBy("this")
private void setVpnForcedLocked(boolean enforce) {
- final List<String> exemptedPackages =
- isNullOrLegacyVpn(mPackage) ? null : Collections.singletonList(mPackage);
+ final List<String> exemptedPackages;
+ if (isNullOrLegacyVpn(mPackage)) {
+ exemptedPackages = null;
+ } else {
+ exemptedPackages = new ArrayList<>(mLockdownWhitelist);
+ exemptedPackages.add(mPackage);
+ }
final Set<UidRange> removedRanges = new ArraySet<>(mBlockedUsers);
Set<UidRange> addedRanges = Collections.emptySet();
diff --git a/services/core/java/com/android/server/display/ColorDisplayService.java b/services/core/java/com/android/server/display/ColorDisplayService.java
index 3a58160cae8d..58c88b36f09d 100644
--- a/services/core/java/com/android/server/display/ColorDisplayService.java
+++ b/services/core/java/com/android/server/display/ColorDisplayService.java
@@ -16,6 +16,10 @@
package com.android.server.display;
+import static android.hardware.display.ColorDisplayManager.AUTO_MODE_CUSTOM_TIME;
+import static android.hardware.display.ColorDisplayManager.AUTO_MODE_DISABLED;
+import static android.hardware.display.ColorDisplayManager.AUTO_MODE_TWILIGHT;
+
import static com.android.server.display.DisplayTransformManager.LEVEL_COLOR_MATRIX_DISPLAY_WHITE_BALANCE;
import static com.android.server.display.DisplayTransformManager.LEVEL_COLOR_MATRIX_NIGHT_DISPLAY;
import static com.android.server.display.DisplayTransformManager.LEVEL_COLOR_MATRIX_SATURATION;
@@ -40,7 +44,9 @@ import android.content.res.Resources;
import android.database.ContentObserver;
import android.graphics.ColorSpace;
import android.hardware.display.ColorDisplayManager;
+import android.hardware.display.ColorDisplayManager.AutoMode;
import android.hardware.display.IColorDisplayManager;
+import android.hardware.display.Time;
import android.net.Uri;
import android.opengl.Matrix;
import android.os.Binder;
@@ -103,59 +109,17 @@ public final class ColorDisplayService extends SystemService {
private static final int MSG_APPLY_GLOBAL_SATURATION = 2;
/**
+ * Return value if a setting has not been set.
+ */
+ private static final int NOT_SET = -1;
+
+ /**
* Evaluator used to animate color matrix transitions.
*/
private static final ColorMatrixEvaluator COLOR_MATRIX_EVALUATOR = new ColorMatrixEvaluator();
- private final TintController mNightDisplayTintController = new TintController() {
-
- private float[] mMatrixNightDisplay = new float[16];
- private final float[] mColorTempCoefficients = new float[9];
-
- /**
- * Set coefficients based on whether the color matrix is linear or not.
- */
- @Override
- public void setUp(Context context, boolean needsLinear) {
- final String[] coefficients = context.getResources().getStringArray(needsLinear
- ? R.array.config_nightDisplayColorTemperatureCoefficients
- : R.array.config_nightDisplayColorTemperatureCoefficientsNative);
- for (int i = 0; i < 9 && i < coefficients.length; i++) {
- mColorTempCoefficients[i] = Float.parseFloat(coefficients[i]);
- }
- }
-
- @Override
- public void setMatrix(int cct) {
- if (mMatrixNightDisplay.length != 16) {
- Slog.d(TAG, "The display transformation matrix must be 4x4");
- return;
- }
-
- Matrix.setIdentityM(mMatrixNightDisplay, 0);
-
- final float squareTemperature = cct * cct;
- final float red = squareTemperature * mColorTempCoefficients[0]
- + cct * mColorTempCoefficients[1] + mColorTempCoefficients[2];
- final float green = squareTemperature * mColorTempCoefficients[3]
- + cct * mColorTempCoefficients[4] + mColorTempCoefficients[5];
- final float blue = squareTemperature * mColorTempCoefficients[6]
- + cct * mColorTempCoefficients[7] + mColorTempCoefficients[8];
- mMatrixNightDisplay[0] = red;
- mMatrixNightDisplay[5] = green;
- mMatrixNightDisplay[10] = blue;
- }
-
- @Override
- public float[] getMatrix() {
- return isActivated() ? mMatrixNightDisplay : MATRIX_IDENTITY;
- }
-
- @Override
- public int getLevel() {
- return LEVEL_COLOR_MATRIX_NIGHT_DISPLAY;
- }
- };
+ private final NightDisplayTintController mNightDisplayTintController =
+ new NightDisplayTintController();
private final TintController mDisplayWhiteBalanceTintController = new TintController() {
// Three chromaticity coordinates per color: X, Y, and Z
@@ -386,7 +350,7 @@ public final class ColorDisplayService extends SystemService {
float saturation = saturationLevel * 0.1f;
float desaturation = 1.0f - saturation;
float[] luminance = {0.231f * desaturation, 0.715f * desaturation,
- 0.072f * desaturation};
+ 0.072f * desaturation};
mMatrixGlobalSaturation[0] = luminance[0] + saturation;
mMatrixGlobalSaturation[1] = luminance[0];
mMatrixGlobalSaturation[2] = luminance[0];
@@ -445,7 +409,7 @@ public final class ColorDisplayService extends SystemService {
public ColorDisplayService(Context context) {
super(context);
- mHandler = new TintHandler(Looper.getMainLooper());
+ mHandler = new TintHandler(DisplayThread.get().getLooper());
}
@Override
@@ -548,23 +512,30 @@ public final class ColorDisplayService extends SystemService {
if (setting != null) {
switch (setting) {
case Secure.NIGHT_DISPLAY_ACTIVATED:
- onNightDisplayActivated(mNightDisplayController.isActivated());
+ final boolean activated = isNightDisplayActivatedSetting();
+ if (mNightDisplayTintController.isActivatedStateNotSet()
+ || mNightDisplayTintController.isActivated() != activated) {
+ mNightDisplayTintController.onActivated(activated);
+ }
break;
case Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE:
- onNightDisplayColorTemperatureChanged(
- mNightDisplayController.getColorTemperature());
+ final int temperature = getNightDisplayColorTemperatureSetting();
+ if (mNightDisplayTintController.getColorTemperature()
+ != temperature) {
+ mNightDisplayTintController
+ .onColorTemperatureChanged(temperature);
+ }
break;
case Secure.NIGHT_DISPLAY_AUTO_MODE:
- onNightDisplayAutoModeChanged(
- mNightDisplayController.getAutoMode());
+ onNightDisplayAutoModeChanged(getNightDisplayAutoModeInternal());
break;
case Secure.NIGHT_DISPLAY_CUSTOM_START_TIME:
onNightDisplayCustomStartTimeChanged(
- mNightDisplayController.getCustomStartTime());
+ getNightDisplayCustomStartTimeInternal().getLocalTime());
break;
case Secure.NIGHT_DISPLAY_CUSTOM_END_TIME:
onNightDisplayCustomEndTimeChanged(
- mNightDisplayController.getCustomEndTime());
+ getNightDisplayCustomEndTimeInternal().getLocalTime());
break;
case System.DISPLAY_COLOR_MODE:
onDisplayColorModeChanged(mNightDisplayController.getColorMode());
@@ -624,14 +595,14 @@ public final class ColorDisplayService extends SystemService {
// Prepare the night display color transformation matrix.
mNightDisplayTintController
.setUp(getContext(), DisplayTransformManager.needsLinearColorMatrix());
- mNightDisplayTintController.setMatrix(mNightDisplayController.getColorTemperature());
+ mNightDisplayTintController.setMatrix(getNightDisplayColorTemperatureSetting());
// Initialize the current auto mode.
- onNightDisplayAutoModeChanged(mNightDisplayController.getAutoMode());
+ onNightDisplayAutoModeChanged(getNightDisplayAutoModeInternal());
- // Force the initialization current activated state.
+ // Force the initialization of the current saved activation state.
if (mNightDisplayTintController.isActivatedStateNotSet()) {
- onNightDisplayActivated(mNightDisplayController.isActivated());
+ mNightDisplayTintController.onActivated(isNightDisplayActivatedSetting());
}
}
@@ -665,23 +636,6 @@ public final class ColorDisplayService extends SystemService {
}
}
- private void onNightDisplayActivated(boolean activated) {
- if (mNightDisplayTintController.isActivatedStateNotSet()
- || mNightDisplayTintController.isActivated() != activated) {
- Slog.i(TAG, activated ? "Turning on night display" : "Turning off night display");
-
- mNightDisplayTintController.setActivated(activated);
-
- if (mNightDisplayAutoMode != null) {
- mNightDisplayAutoMode.onActivated(activated);
- }
-
- updateDisplayWhiteBalanceStatus();
-
- applyTint(mNightDisplayTintController, false);
- }
- }
-
private void onNightDisplayAutoModeChanged(int autoMode) {
Slog.d(TAG, "onNightDisplayAutoModeChanged: autoMode=" + autoMode);
@@ -690,9 +644,9 @@ public final class ColorDisplayService extends SystemService {
mNightDisplayAutoMode = null;
}
- if (autoMode == ColorDisplayController.AUTO_MODE_CUSTOM) {
+ if (autoMode == AUTO_MODE_CUSTOM_TIME) {
mNightDisplayAutoMode = new CustomNightDisplayAutoMode();
- } else if (autoMode == ColorDisplayController.AUTO_MODE_TWILIGHT) {
+ } else if (autoMode == AUTO_MODE_TWILIGHT) {
mNightDisplayAutoMode = new TwilightNightDisplayAutoMode();
}
@@ -717,13 +671,8 @@ public final class ColorDisplayService extends SystemService {
}
}
- private void onNightDisplayColorTemperatureChanged(int colorTemperature) {
- mNightDisplayTintController.setMatrix(colorTemperature);
- applyTint(mNightDisplayTintController, true);
- }
-
private void onDisplayColorModeChanged(int mode) {
- if (mode == -1) {
+ if (mode == NOT_SET) {
return;
}
@@ -732,7 +681,7 @@ public final class ColorDisplayService extends SystemService {
mNightDisplayTintController
.setUp(getContext(), DisplayTransformManager.needsLinearColorMatrix(mode));
- mNightDisplayTintController.setMatrix(mNightDisplayController.getColorTemperature());
+ mNightDisplayTintController.setMatrix(getNightDisplayColorTemperatureSetting());
updateDisplayWhiteBalanceStatus();
@@ -901,6 +850,71 @@ public final class ColorDisplayService extends SystemService {
return availabilityFlags;
}
+ private boolean setNightDisplayAutoModeInternal(@AutoMode int autoMode) {
+ if (getNightDisplayAutoModeInternal() != autoMode) {
+ Secure.putStringForUser(getContext().getContentResolver(),
+ Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME,
+ null,
+ mCurrentUser);
+ }
+ return Secure.putIntForUser(getContext().getContentResolver(),
+ Secure.NIGHT_DISPLAY_AUTO_MODE, autoMode, mCurrentUser);
+ }
+
+ private int getNightDisplayAutoModeInternal() {
+ int autoMode = getNightDisplayAutoModeRawInternal();
+ if (autoMode == NOT_SET) {
+ autoMode = getContext().getResources().getInteger(
+ R.integer.config_defaultNightDisplayAutoMode);
+ }
+ if (autoMode != AUTO_MODE_DISABLED
+ && autoMode != AUTO_MODE_CUSTOM_TIME
+ && autoMode != AUTO_MODE_TWILIGHT) {
+ Slog.e(TAG, "Invalid autoMode: " + autoMode);
+ autoMode = AUTO_MODE_DISABLED;
+ }
+ return autoMode;
+ }
+
+ private int getNightDisplayAutoModeRawInternal() {
+ return Secure
+ .getIntForUser(getContext().getContentResolver(), Secure.NIGHT_DISPLAY_AUTO_MODE,
+ NOT_SET, mCurrentUser);
+ }
+
+ private Time getNightDisplayCustomStartTimeInternal() {
+ int startTimeValue = Secure.getIntForUser(getContext().getContentResolver(),
+ Secure.NIGHT_DISPLAY_CUSTOM_START_TIME, NOT_SET, mCurrentUser);
+ if (startTimeValue == NOT_SET) {
+ startTimeValue = getContext().getResources().getInteger(
+ R.integer.config_defaultNightDisplayCustomStartTime);
+ }
+ return new Time(LocalTime.ofSecondOfDay(startTimeValue / 1000));
+ }
+
+ private boolean setNightDisplayCustomStartTimeInternal(Time startTime) {
+ return Secure.putIntForUser(getContext().getContentResolver(),
+ Secure.NIGHT_DISPLAY_CUSTOM_START_TIME,
+ startTime.getLocalTime().toSecondOfDay() * 1000,
+ mCurrentUser);
+ }
+
+ private Time getNightDisplayCustomEndTimeInternal() {
+ int endTimeValue = Secure.getIntForUser(getContext().getContentResolver(),
+ Secure.NIGHT_DISPLAY_CUSTOM_END_TIME, NOT_SET, mCurrentUser);
+ if (endTimeValue == NOT_SET) {
+ endTimeValue = getContext().getResources().getInteger(
+ R.integer.config_defaultNightDisplayCustomEndTime);
+ }
+ return new Time(LocalTime.ofSecondOfDay(endTimeValue / 1000));
+ }
+
+ private boolean setNightDisplayCustomEndTimeInternal(Time endTime) {
+ return Secure.putIntForUser(getContext().getContentResolver(),
+ Secure.NIGHT_DISPLAY_CUSTOM_END_TIME, endTime.getLocalTime().toSecondOfDay() * 1000,
+ mCurrentUser);
+ }
+
/**
* Returns the last time the night display transform activation state was changed, or {@link
* LocalDateTime#MIN} if night display has never been activated.
@@ -941,6 +955,33 @@ public final class ColorDisplayService extends SystemService {
mAppSaturationController.dump(pw);
}
+ private boolean isNightDisplayActivatedSetting() {
+ return Secure.getIntForUser(getContext().getContentResolver(),
+ Secure.NIGHT_DISPLAY_ACTIVATED, 0, mCurrentUser) == 1;
+ }
+
+ private int getNightDisplayColorTemperatureSetting() {
+ return clampNightDisplayColorTemperature(Secure.getIntForUser(
+ getContext().getContentResolver(), Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE, NOT_SET,
+ mCurrentUser));
+ }
+
+ private int clampNightDisplayColorTemperature(int colorTemperature) {
+ if (colorTemperature == NOT_SET) {
+ colorTemperature = getContext().getResources().getInteger(
+ R.integer.config_nightDisplayColorTemperatureDefault);
+ }
+ final int minimumTemperature = ColorDisplayManager.getMinimumColorTemperature(getContext());
+ final int maximumTemperature = ColorDisplayManager.getMaximumColorTemperature(getContext());
+ if (colorTemperature < minimumTemperature) {
+ colorTemperature = minimumTemperature;
+ } else if (colorTemperature > maximumTemperature) {
+ colorTemperature = maximumTemperature;
+ }
+
+ return colorTemperature;
+ }
+
private abstract class NightDisplayAutoMode {
public abstract void onActivated(boolean activated);
@@ -987,13 +1028,13 @@ public final class ColorDisplayService extends SystemService {
// Maintain the existing activated state if within the current period.
if (mLastActivatedTime.isBefore(now) && mLastActivatedTime.isAfter(start)
&& (mLastActivatedTime.isAfter(end) || now.isBefore(end))) {
- activate = mNightDisplayController.isActivated();
+ activate = isNightDisplayActivatedSetting();
}
}
if (mNightDisplayTintController.isActivatedStateNotSet() || (
mNightDisplayTintController.isActivated() != activate)) {
- mNightDisplayController.setActivated(activate);
+ mNightDisplayTintController.setActivated(activate);
}
updateNextAlarm(mNightDisplayTintController.isActivated(), now);
@@ -1014,8 +1055,8 @@ public final class ColorDisplayService extends SystemService {
intentFilter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
getContext().registerReceiver(mTimeChangedReceiver, intentFilter);
- mStartTime = mNightDisplayController.getCustomStartTime();
- mEndTime = mNightDisplayController.getCustomEndTime();
+ mStartTime = getNightDisplayCustomStartTimeInternal().getLocalTime();
+ mEndTime = getNightDisplayCustomEndTimeInternal().getLocalTime();
mLastActivatedTime = getNightDisplayLastActivatedTimeSetting();
@@ -1083,13 +1124,13 @@ public final class ColorDisplayService extends SystemService {
// Maintain the existing activated state if within the current period.
if (mLastActivatedTime.isBefore(now) && (mLastActivatedTime.isBefore(sunrise)
^ mLastActivatedTime.isBefore(sunset))) {
- activate = mNightDisplayController.isActivated();
+ activate = isNightDisplayActivatedSetting();
}
}
if (mNightDisplayTintController.isActivatedStateNotSet() || (
mNightDisplayTintController.isActivated() != activate)) {
- mNightDisplayController.setActivated(activate);
+ mNightDisplayTintController.setActivated(activate);
}
}
@@ -1211,6 +1252,114 @@ public final class ColorDisplayService extends SystemService {
public abstract int getLevel();
}
+ private final class NightDisplayTintController extends TintController {
+
+ private float[] mMatrix = new float[16];
+ private final float[] mColorTempCoefficients = new float[9];
+ private Integer mColorTemp;
+
+ /**
+ * Set coefficients based on whether the color matrix is linear or not.
+ */
+ @Override
+ public void setUp(Context context, boolean needsLinear) {
+ final String[] coefficients = context.getResources().getStringArray(needsLinear
+ ? R.array.config_nightDisplayColorTemperatureCoefficients
+ : R.array.config_nightDisplayColorTemperatureCoefficientsNative);
+ for (int i = 0; i < 9 && i < coefficients.length; i++) {
+ mColorTempCoefficients[i] = Float.parseFloat(coefficients[i]);
+ }
+ }
+
+ @Override
+ public void setMatrix(int cct) {
+ if (mMatrix.length != 16) {
+ Slog.d(TAG, "The display transformation matrix must be 4x4");
+ return;
+ }
+
+ Matrix.setIdentityM(mMatrix, 0);
+
+ final float squareTemperature = cct * cct;
+ final float red = squareTemperature * mColorTempCoefficients[0]
+ + cct * mColorTempCoefficients[1] + mColorTempCoefficients[2];
+ final float green = squareTemperature * mColorTempCoefficients[3]
+ + cct * mColorTempCoefficients[4] + mColorTempCoefficients[5];
+ final float blue = squareTemperature * mColorTempCoefficients[6]
+ + cct * mColorTempCoefficients[7] + mColorTempCoefficients[8];
+ mMatrix[0] = red;
+ mMatrix[5] = green;
+ mMatrix[10] = blue;
+ }
+
+ @Override
+ public float[] getMatrix() {
+ return isActivated() ? mMatrix : MATRIX_IDENTITY;
+ }
+
+ @Override
+ public void setActivated(Boolean activated) {
+ if (activated == null) {
+ super.setActivated(null);
+ return;
+ }
+
+ boolean activationStateChanged = activated != isActivated();
+
+ if (!isActivatedStateNotSet() && activationStateChanged) {
+ // This is a true state change, so set this as the last activation time.
+ Secure.putStringForUser(getContext().getContentResolver(),
+ Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME,
+ LocalDateTime.now().toString(),
+ mCurrentUser);
+ }
+
+ if (isActivatedStateNotSet() || activationStateChanged) {
+ super.setActivated(activated);
+ Secure.putIntForUser(getContext().getContentResolver(),
+ Secure.NIGHT_DISPLAY_ACTIVATED,
+ activated ? 1 : 0, mCurrentUser);
+ onActivated(activated);
+ }
+ }
+
+ @Override
+ public int getLevel() {
+ return LEVEL_COLOR_MATRIX_NIGHT_DISPLAY;
+ }
+
+ void onActivated(boolean activated) {
+ Slog.i(TAG, activated ? "Turning on night display" : "Turning off night display");
+ if (mNightDisplayAutoMode != null) {
+ mNightDisplayAutoMode.onActivated(activated);
+ }
+
+ if (ColorDisplayManager.isDisplayWhiteBalanceAvailable(getContext())) {
+ updateDisplayWhiteBalanceStatus();
+ }
+
+ mHandler.sendEmptyMessage(MSG_APPLY_NIGHT_DISPLAY_ANIMATED);
+ }
+
+ int getColorTemperature() {
+ return mColorTemp != null ? clampNightDisplayColorTemperature(mColorTemp)
+ : getNightDisplayColorTemperatureSetting();
+ }
+
+ boolean setColorTemperature(int temperature) {
+ mColorTemp = temperature;
+ final boolean success = Secure.putIntForUser(getContext().getContentResolver(),
+ Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE, temperature, mCurrentUser);
+ onColorTemperatureChanged(temperature);
+ return success;
+ }
+
+ void onColorTemperatureChanged(int temperature) {
+ setMatrix(temperature);
+ mHandler.sendEmptyMessage(MSG_APPLY_NIGHT_DISPLAY_IMMEDIATE);
+ }
+ }
+
/**
* Local service that allows color transforms to be enabled from other system services.
*/
@@ -1270,7 +1419,7 @@ public final class ColorDisplayService extends SystemService {
private final class TintHandler extends Handler {
- TintHandler(Looper looper) {
+ private TintHandler(Looper looper) {
super(looper, null, true /* async */);
}
@@ -1281,6 +1430,12 @@ public final class ColorDisplayService extends SystemService {
mGlobalSaturationTintController.setMatrix(msg.arg1);
applyTint(mGlobalSaturationTintController, false);
break;
+ case MSG_APPLY_NIGHT_DISPLAY_IMMEDIATE:
+ applyTint(mNightDisplayTintController, true);
+ break;
+ case MSG_APPLY_NIGHT_DISPLAY_ANIMATED:
+ applyTint(mNightDisplayTintController, false);
+ break;
}
}
}
@@ -1294,7 +1449,8 @@ public final class ColorDisplayService extends SystemService {
void applyAppSaturation(@Size(9) float[] matrix, @Size(3) float[] translation);
}
- private final class BinderService extends IColorDisplayManager.Stub {
+ @VisibleForTesting
+ final class BinderService extends IColorDisplayManager.Stub {
@Override
public boolean isDeviceColorManaged() {
@@ -1354,6 +1510,135 @@ public final class ColorDisplayService extends SystemService {
}
@Override
+ public boolean setNightDisplayActivated(boolean activated) {
+ getContext().enforceCallingOrSelfPermission(
+ Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS,
+ "Permission required to set night display activated");
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mNightDisplayTintController.setActivated(activated);
+ return true;
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public boolean isNightDisplayActivated() {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ return mNightDisplayTintController.isActivated();
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public boolean setNightDisplayColorTemperature(int temperature) {
+ getContext().enforceCallingOrSelfPermission(
+ Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS,
+ "Permission required to set night display temperature");
+ final long token = Binder.clearCallingIdentity();
+ try {
+ return mNightDisplayTintController.setColorTemperature(temperature);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public int getNightDisplayColorTemperature() {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ return mNightDisplayTintController.getColorTemperature();
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public boolean setNightDisplayAutoMode(int autoMode) {
+ getContext().enforceCallingOrSelfPermission(
+ Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS,
+ "Permission required to set night display auto mode");
+ final long token = Binder.clearCallingIdentity();
+ try {
+ return setNightDisplayAutoModeInternal(autoMode);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public int getNightDisplayAutoMode() {
+ getContext().enforceCallingOrSelfPermission(
+ Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS,
+ "Permission required to get night display auto mode");
+ final long token = Binder.clearCallingIdentity();
+ try {
+ return getNightDisplayAutoModeInternal();
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public int getNightDisplayAutoModeRaw() {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ return getNightDisplayAutoModeRawInternal();
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public boolean setNightDisplayCustomStartTime(Time startTime) {
+ getContext().enforceCallingOrSelfPermission(
+ Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS,
+ "Permission required to set night display custom start time");
+ final long token = Binder.clearCallingIdentity();
+ try {
+ return setNightDisplayCustomStartTimeInternal(startTime);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public Time getNightDisplayCustomStartTime() {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ return getNightDisplayCustomStartTimeInternal();
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public boolean setNightDisplayCustomEndTime(Time endTime) {
+ getContext().enforceCallingOrSelfPermission(
+ Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS,
+ "Permission required to set night display custom end time");
+ final long token = Binder.clearCallingIdentity();
+ try {
+ return setNightDisplayCustomEndTimeInternal(endTime);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public Time getNightDisplayCustomEndTime() {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ return getNightDisplayCustomEndTimeInternal();
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) return;
diff --git a/services/core/java/com/android/server/job/JobConcurrencyManager.java b/services/core/java/com/android/server/job/JobConcurrencyManager.java
index 4d9b5f5d01c6..bec1947df228 100644
--- a/services/core/java/com/android/server/job/JobConcurrencyManager.java
+++ b/services/core/java/com/android/server/job/JobConcurrencyManager.java
@@ -36,6 +36,7 @@ import com.android.internal.os.BackgroundThread;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.StatLogger;
import com.android.server.job.JobSchedulerService.Constants;
+import com.android.server.job.JobSchedulerService.MaxJobCountsPerMemoryTrimLevel;
import com.android.server.job.controllers.JobStatus;
import com.android.server.job.controllers.StateController;
@@ -148,14 +149,14 @@ class JobConcurrencyManager {
Slog.d(TAG, "Interactive: " + interactive);
}
- final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+ final long nowRealtime = JobSchedulerService.sElapsedRealtimeClock.millis();
if (interactive) {
- mLastScreenOnRealtime = now;
+ mLastScreenOnRealtime = nowRealtime;
mEffectiveInteractiveState = true;
mHandler.removeCallbacks(mRampUpForScreenOff);
} else {
- mLastScreenOffRealtime = now;
+ mLastScreenOffRealtime = nowRealtime;
// Set mEffectiveInteractiveState to false after the delay, when we may increase
// the concurrency.
@@ -232,38 +233,24 @@ class JobConcurrencyManager {
private void updateMaxCountsLocked() {
refreshSystemStateLocked();
- if (mEffectiveInteractiveState) {
- // Screen on
- switch (mLastMemoryTrimLevel) {
- case ProcessStats.ADJ_MEM_FACTOR_MODERATE:
- mMaxJobCounts = mConstants.MAX_JOB_COUNTS_ON_MODERATE;
- break;
- case ProcessStats.ADJ_MEM_FACTOR_LOW:
- mMaxJobCounts = mConstants.MAX_JOB_COUNTS_ON_LOW;
- break;
- case ProcessStats.ADJ_MEM_FACTOR_CRITICAL:
- mMaxJobCounts = mConstants.MAX_JOB_COUNTS_ON_CRITICAL;
- break;
- default:
- mMaxJobCounts = mConstants.MAX_JOB_COUNTS_ON_NORMAL;
- break;
- }
- } else {
- // Screen off
- switch (mLastMemoryTrimLevel) {
- case ProcessStats.ADJ_MEM_FACTOR_MODERATE:
- mMaxJobCounts = mConstants.MAX_JOB_COUNTS_OFF_MODERATE;
- break;
- case ProcessStats.ADJ_MEM_FACTOR_LOW:
- mMaxJobCounts = mConstants.MAX_JOB_COUNTS_OFF_LOW;
- break;
- case ProcessStats.ADJ_MEM_FACTOR_CRITICAL:
- mMaxJobCounts = mConstants.MAX_JOB_COUNTS_OFF_CRITICAL;
- break;
- default:
- mMaxJobCounts = mConstants.MAX_JOB_COUNTS_OFF_NORMAL;
- break;
- }
+ final MaxJobCountsPerMemoryTrimLevel jobCounts = mEffectiveInteractiveState
+ ? mConstants.MAX_JOB_COUNTS_SCREEN_ON
+ : mConstants.MAX_JOB_COUNTS_SCREEN_OFF;
+
+
+ switch (mLastMemoryTrimLevel) {
+ case ProcessStats.ADJ_MEM_FACTOR_MODERATE:
+ mMaxJobCounts = jobCounts.moderate;
+ break;
+ case ProcessStats.ADJ_MEM_FACTOR_LOW:
+ mMaxJobCounts = jobCounts.low;
+ break;
+ case ProcessStats.ADJ_MEM_FACTOR_CRITICAL:
+ mMaxJobCounts = jobCounts.critical;
+ break;
+ default:
+ mMaxJobCounts = jobCounts.normal;
+ break;
}
}
@@ -303,7 +290,7 @@ class JobConcurrencyManager {
// Initialize the work variables and also count running jobs.
mJobCountTracker.reset(
- mMaxJobCounts.getTotalMax(),
+ mMaxJobCounts.getMaxTotal(),
mMaxJobCounts.getMaxBg(),
mMaxJobCounts.getMinBg());
@@ -482,10 +469,7 @@ class JobConcurrencyManager {
}
- public void dumpLocked(IndentingPrintWriter pw) {
- final long now = System.currentTimeMillis();
- final long nowRealtime = JobSchedulerService.sElapsedRealtimeClock.millis();
-
+ public void dumpLocked(IndentingPrintWriter pw, long now, long nowRealtime) {
pw.println("Concurrency:");
pw.increaseIndent();
@@ -522,19 +506,36 @@ class JobConcurrencyManager {
}
}
- public void dumpProtoLocked(ProtoOutputStream proto) {
- // TODO Implement it.
+ public void dumpProtoLocked(ProtoOutputStream proto, long tag, long now, long nowRealtime) {
+ final long token = proto.start(tag);
+
+ proto.write(JobConcurrencyManagerProto.CURRENT_INTERACTIVE,
+ mCurrentInteractiveState);
+ proto.write(JobConcurrencyManagerProto.EFFECTIVE_INTERACTIVE,
+ mEffectiveInteractiveState);
+
+ proto.write(JobConcurrencyManagerProto.TIME_SINCE_LAST_SCREEN_ON_MS,
+ nowRealtime - mLastScreenOnRealtime);
+ proto.write(JobConcurrencyManagerProto.TIME_SINCE_LAST_SCREEN_OFF_MS,
+ nowRealtime - mLastScreenOffRealtime);
+
+ mJobCountTracker.dumpProto(proto, JobConcurrencyManagerProto.JOB_COUNT_TRACKER);
+
+ proto.write(JobConcurrencyManagerProto.MEMORY_TRIM_LEVEL,
+ mLastMemoryTrimLevel);
+
+ proto.end(token);
}
/**
- * This class decides, taking into account {@link #mMaxJobCounts} and how many jos are running /
+ * This class decides, taking into account {@link #mMaxJobCounts} and how mny jos are running /
* pending, how many more job can start.
*
* Extracted for testing and logging.
*/
@VisibleForTesting
static class JobCountTracker {
- private int mConfigNumTotalMaxJobs;
+ private int mConfigNumMaxTotalJobs;
private int mConfigNumMaxBgJobs;
private int mConfigNumMinBgJobs;
@@ -552,7 +553,7 @@ class JobConcurrencyManager {
private int mNumActualMaxBgJobs;
void reset(int numTotalMaxJobs, int numMaxBgJobs, int numMinBgJobs) {
- mConfigNumTotalMaxJobs = numTotalMaxJobs;
+ mConfigNumMaxTotalJobs = numTotalMaxJobs;
mConfigNumMaxBgJobs = numMaxBgJobs;
mConfigNumMinBgJobs = numMinBgJobs;
@@ -607,12 +608,12 @@ class JobConcurrencyManager {
// However, if there are FG jobs already running, we have to adjust it.
mNumReservedForBg = Math.min(reservedForBg,
- mConfigNumTotalMaxJobs - mNumRunningFgJobs);
+ mConfigNumMaxTotalJobs - mNumRunningFgJobs);
// Max FG is [total - [number needed for BG jobs]]
// [number needed for BG jobs] is the bigger one of [running BG] or [reserved BG]
final int maxFg =
- mConfigNumTotalMaxJobs - Math.max(mNumRunningBgJobs, mNumReservedForBg);
+ mConfigNumMaxTotalJobs - Math.max(mNumRunningBgJobs, mNumReservedForBg);
// The above maxFg is the theoretical max. If there are less FG jobs, the actual
// max FG will be lower accordingly.
@@ -623,7 +624,7 @@ class JobConcurrencyManager {
// Max BG is [total - actual max FG], but cap at [config max BG].
final int maxBg = Math.min(
mConfigNumMaxBgJobs,
- mConfigNumTotalMaxJobs - mNumActualMaxFgJobs);
+ mConfigNumMaxTotalJobs - mNumActualMaxFgJobs);
// If there are less BG jobs than maxBg, then reduce the actual max BG accordingly.
// This isn't needed for the logic to work, but this will give consistent output
@@ -669,12 +670,13 @@ class JobConcurrencyManager {
final int totalBg = mNumRunningBgJobs + mNumStartingBgJobs;
return String.format(
"Config={tot=%d bg min/max=%d/%d}"
- + " Running: %d / %d (%d)"
+ + " Running[FG/BG (total)]: %d / %d (%d)"
+ " Pending: %d / %d (%d)"
+ " Actual max: %d%s / %d%s (%d%s)"
+ + " Res BG: %d"
+ " Starting: %d / %d (%d)"
+ " Total: %d%s / %d%s (%d%s)",
- mConfigNumTotalMaxJobs,
+ mConfigNumMaxTotalJobs,
mConfigNumMinBgJobs,
mConfigNumMaxBgJobs,
@@ -684,19 +686,37 @@ class JobConcurrencyManager {
mNumPendingFgJobs, mNumPendingBgJobs,
mNumPendingFgJobs + mNumPendingBgJobs,
- mNumActualMaxFgJobs, (totalFg <= mConfigNumTotalMaxJobs) ? "" : "*",
+ mNumActualMaxFgJobs, (totalFg <= mConfigNumMaxTotalJobs) ? "" : "*",
mNumActualMaxBgJobs, (totalBg <= mConfigNumMaxBgJobs) ? "" : "*",
mNumActualMaxFgJobs + mNumActualMaxBgJobs,
- (mNumActualMaxFgJobs + mNumActualMaxBgJobs <= mConfigNumTotalMaxJobs)
+ (mNumActualMaxFgJobs + mNumActualMaxBgJobs <= mConfigNumMaxTotalJobs)
? "" : "*",
+ mNumReservedForBg,
+
mNumStartingFgJobs, mNumStartingBgJobs, mNumStartingFgJobs + mNumStartingBgJobs,
totalFg, (totalFg <= mNumActualMaxFgJobs) ? "" : "*",
totalBg, (totalBg <= mNumActualMaxBgJobs) ? "" : "*",
- totalFg + totalBg, (totalFg + totalBg <= mConfigNumTotalMaxJobs) ? "" : "*"
+ totalFg + totalBg, (totalFg + totalBg <= mConfigNumMaxTotalJobs) ? "" : "*"
);
}
+
+ public void dumpProto(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+
+ proto.write(JobCountTrackerProto.CONFIG_NUM_MAX_TOTAL_JOBS, mConfigNumMaxTotalJobs);
+ proto.write(JobCountTrackerProto.CONFIG_NUM_MAX_BG_JOBS, mConfigNumMaxBgJobs);
+ proto.write(JobCountTrackerProto.CONFIG_NUM_MIN_BG_JOBS, mConfigNumMinBgJobs);
+
+ proto.write(JobCountTrackerProto.NUM_RUNNING_FG_JOBS, mNumRunningFgJobs);
+ proto.write(JobCountTrackerProto.NUM_RUNNING_BG_JOBS, mNumRunningBgJobs);
+
+ proto.write(JobCountTrackerProto.NUM_PENDING_FG_JOBS, mNumPendingFgJobs);
+ proto.write(JobCountTrackerProto.NUM_PENDING_BG_JOBS, mNumPendingBgJobs);
+
+ proto.end(token);
+ }
}
}
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index bd12075fdad3..7625aafd0907 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -366,14 +366,20 @@ public class JobSchedulerService extends com.android.server.SystemService
}
}
- public int getTotalMax() {
+ /** Total number of jobs to run simultaneously. */
+ public int getMaxTotal() {
return mTotal.getValue();
}
+ /** Max number of BG (== owned by non-TOP apps) jobs to run simultaneously. */
public int getMaxBg() {
return mMaxBg.getValue();
}
+ /**
+ * We try to run at least this many BG (== owned by non-TOP apps) jobs, when there are any
+ * pending, rather than always running the TOTAL number of FG jobs.
+ */
public int getMinBg() {
return mMinBg.getValue();
}
@@ -384,10 +390,39 @@ public class JobSchedulerService extends com.android.server.SystemService
mMinBg.dump(pw, prefix);
}
- public void dumpProto(ProtoOutputStream proto, long tagTotal, long tagBg) {
- mTotal.dumpProto(proto, tagTotal);
- mMaxBg.dumpProto(proto, tagBg);
- mMinBg.dumpProto(proto, tagBg);
+ public void dumpProto(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+ mTotal.dumpProto(proto, MaxJobCountsProto.TOTAL_JOBS);
+ mMaxBg.dumpProto(proto, MaxJobCountsProto.MAX_BG);
+ mMinBg.dumpProto(proto, MaxJobCountsProto.MIN_BG);
+ proto.end(token);
+ }
+ }
+
+ /** {@link MaxJobCounts} for each memory trim level. */
+ static class MaxJobCountsPerMemoryTrimLevel {
+ public final MaxJobCounts normal;
+ public final MaxJobCounts moderate;
+ public final MaxJobCounts low;
+ public final MaxJobCounts critical;
+
+ MaxJobCountsPerMemoryTrimLevel(
+ MaxJobCounts normal,
+ MaxJobCounts moderate, MaxJobCounts low,
+ MaxJobCounts critical) {
+ this.normal = normal;
+ this.moderate = moderate;
+ this.low = low;
+ this.critical = critical;
+ }
+
+ public void dumpProto(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+ normal.dumpProto(proto, MaxJobCountsPerMemoryTrimLevelProto.NORMAL);
+ moderate.dumpProto(proto, MaxJobCountsPerMemoryTrimLevelProto.MODERATE);
+ low.dumpProto(proto, MaxJobCountsPerMemoryTrimLevelProto.LOW);
+ critical.dumpProto(proto, MaxJobCountsPerMemoryTrimLevelProto.CRITICAL);
+ proto.end(token);
}
}
@@ -546,45 +581,44 @@ public class JobSchedulerService extends com.android.server.SystemService
float MODERATE_USE_FACTOR = DEFAULT_MODERATE_USE_FACTOR;
// Max job counts for screen on / off, for each memory trim level.
- final MaxJobCounts MAX_JOB_COUNTS_ON_NORMAL = new MaxJobCounts(
- 8, "max_job_total_on_normal",
- 6, "max_job_max_bg_on_normal",
- 2, "max_job_min_bg_on_normal");
-
- final MaxJobCounts MAX_JOB_COUNTS_ON_MODERATE = new MaxJobCounts(
- 8, "max_job_total_on_moderate",
- 4, "max_job_max_bg_on_moderate",
- 2, "max_job_min_bg_on_moderate");
-
- final MaxJobCounts MAX_JOB_COUNTS_ON_LOW = new MaxJobCounts(
- 5, "max_job_total_on_low",
- 1, "max_job_max_bg_on_low",
- 1, "max_job_min_bg_on_low");
-
- final MaxJobCounts MAX_JOB_COUNTS_ON_CRITICAL = new MaxJobCounts(
- 5, "max_job_total_on_critical",
- 1, "max_job_max_bg_on_critical",
- 1, "max_job_min_bg_on_critical");
-
- final MaxJobCounts MAX_JOB_COUNTS_OFF_NORMAL = new MaxJobCounts(
- 10, "max_job_total_off_normal",
- 6, "max_job_max_bg_off_normal",
- 2, "max_job_min_bg_off_normal");
-
- final MaxJobCounts MAX_JOB_COUNTS_OFF_MODERATE = new MaxJobCounts(
- 10, "max_job_total_off_moderate",
- 4, "max_job_max_bg_off_moderate",
- 2, "max_job_min_bg_off_moderate");
-
- final MaxJobCounts MAX_JOB_COUNTS_OFF_LOW = new MaxJobCounts(
- 5, "max_job_total_off_low",
- 1, "max_job_max_bg_off_low",
- 1, "max_job_min_bg_off_low");
-
- final MaxJobCounts MAX_JOB_COUNTS_OFF_CRITICAL = new MaxJobCounts(
- 5, "max_job_total_off_critical",
- 1, "max_job_max_bg_off_critical",
- 1, "max_job_min_bg_off_critical");
+ final MaxJobCountsPerMemoryTrimLevel MAX_JOB_COUNTS_SCREEN_ON =
+ new MaxJobCountsPerMemoryTrimLevel(
+ new MaxJobCounts(
+ 8, "max_job_total_on_normal",
+ 6, "max_job_max_bg_on_normal",
+ 2, "max_job_min_bg_on_normal"),
+ new MaxJobCounts(
+ 8, "max_job_total_on_moderate",
+ 4, "max_job_max_bg_on_moderate",
+ 2, "max_job_min_bg_on_moderate"),
+ new MaxJobCounts(
+ 5, "max_job_total_on_low",
+ 1, "max_job_max_bg_on_low",
+ 1, "max_job_min_bg_on_low"),
+ new MaxJobCounts(
+ 5, "max_job_total_on_critical",
+ 1, "max_job_max_bg_on_critical",
+ 1, "max_job_min_bg_on_critical"));
+
+ final MaxJobCountsPerMemoryTrimLevel MAX_JOB_COUNTS_SCREEN_OFF =
+ new MaxJobCountsPerMemoryTrimLevel(
+ new MaxJobCounts(
+ 10, "max_job_total_off_normal",
+ 6, "max_job_max_bg_off_normal",
+ 2, "max_job_min_bg_off_normal"),
+ new MaxJobCounts(
+ 10, "max_job_total_off_moderate",
+ 4, "max_job_max_bg_off_moderate",
+ 2, "max_job_min_bg_off_moderate"),
+ new MaxJobCounts(
+ 5, "max_job_total_off_low",
+ 1, "max_job_max_bg_off_low",
+ 1, "max_job_min_bg_off_low"),
+ new MaxJobCounts(
+ 5, "max_job_total_off_critical",
+ 1, "max_job_max_bg_off_critical",
+ 1, "max_job_min_bg_off_critical"));
+
/** Wait for this long after screen off before increasing the job concurrency. */
final KeyValueListParser.IntValue SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS =
@@ -766,15 +800,15 @@ public class JobSchedulerService extends com.android.server.SystemService
MODERATE_USE_FACTOR = mParser.getFloat(KEY_MODERATE_USE_FACTOR,
DEFAULT_MODERATE_USE_FACTOR);
- MAX_JOB_COUNTS_ON_NORMAL.parse(mParser);
- MAX_JOB_COUNTS_ON_MODERATE.parse(mParser);
- MAX_JOB_COUNTS_ON_LOW.parse(mParser);
- MAX_JOB_COUNTS_ON_CRITICAL.parse(mParser);
+ MAX_JOB_COUNTS_SCREEN_ON.normal.parse(mParser);
+ MAX_JOB_COUNTS_SCREEN_ON.moderate.parse(mParser);
+ MAX_JOB_COUNTS_SCREEN_ON.low.parse(mParser);
+ MAX_JOB_COUNTS_SCREEN_ON.critical.parse(mParser);
- MAX_JOB_COUNTS_OFF_NORMAL.parse(mParser);
- MAX_JOB_COUNTS_OFF_MODERATE.parse(mParser);
- MAX_JOB_COUNTS_OFF_LOW.parse(mParser);
- MAX_JOB_COUNTS_OFF_CRITICAL.parse(mParser);
+ MAX_JOB_COUNTS_SCREEN_OFF.normal.parse(mParser);
+ MAX_JOB_COUNTS_SCREEN_OFF.moderate.parse(mParser);
+ MAX_JOB_COUNTS_SCREEN_OFF.low.parse(mParser);
+ MAX_JOB_COUNTS_SCREEN_OFF.critical.parse(mParser);
MAX_STANDARD_RESCHEDULE_COUNT = mParser.getInt(KEY_MAX_STANDARD_RESCHEDULE_COUNT,
DEFAULT_MAX_STANDARD_RESCHEDULE_COUNT);
@@ -851,15 +885,17 @@ public class JobSchedulerService extends com.android.server.SystemService
pw.printPair(KEY_HEAVY_USE_FACTOR, HEAVY_USE_FACTOR).println();
pw.printPair(KEY_MODERATE_USE_FACTOR, MODERATE_USE_FACTOR).println();
- MAX_JOB_COUNTS_ON_NORMAL.dump(pw, "");
- MAX_JOB_COUNTS_ON_MODERATE.dump(pw, "");
- MAX_JOB_COUNTS_ON_LOW.dump(pw, "");
- MAX_JOB_COUNTS_ON_CRITICAL.dump(pw, "");
+ MAX_JOB_COUNTS_SCREEN_ON.normal.dump(pw, "");
+ MAX_JOB_COUNTS_SCREEN_ON.moderate.dump(pw, "");
+ MAX_JOB_COUNTS_SCREEN_ON.low.dump(pw, "");
+ MAX_JOB_COUNTS_SCREEN_ON.critical.dump(pw, "");
+
+ MAX_JOB_COUNTS_SCREEN_OFF.normal.dump(pw, "");
+ MAX_JOB_COUNTS_SCREEN_OFF.moderate.dump(pw, "");
+ MAX_JOB_COUNTS_SCREEN_OFF.low.dump(pw, "");
+ MAX_JOB_COUNTS_SCREEN_OFF.critical.dump(pw, "");
- MAX_JOB_COUNTS_OFF_NORMAL.dump(pw, "");
- MAX_JOB_COUNTS_OFF_MODERATE.dump(pw, "");
- MAX_JOB_COUNTS_OFF_LOW.dump(pw, "");
- MAX_JOB_COUNTS_OFF_CRITICAL.dump(pw, "");
+ SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS.dump(pw, "");
pw.printPair(KEY_MAX_STANDARD_RESCHEDULE_COUNT, MAX_STANDARD_RESCHEDULE_COUNT).println();
pw.printPair(KEY_MAX_WORK_RESCHEDULE_COUNT, MAX_WORK_RESCHEDULE_COUNT).println();
@@ -917,7 +953,11 @@ public class JobSchedulerService extends com.android.server.SystemService
proto.write(ConstantsProto.HEAVY_USE_FACTOR, HEAVY_USE_FACTOR);
proto.write(ConstantsProto.MODERATE_USE_FACTOR, MODERATE_USE_FACTOR);
- // TODO Dump max job counts.
+ MAX_JOB_COUNTS_SCREEN_ON.dumpProto(proto, ConstantsProto.MAX_JOB_COUNTS_SCREEN_ON);
+ MAX_JOB_COUNTS_SCREEN_OFF.dumpProto(proto, ConstantsProto.MAX_JOB_COUNTS_SCREEN_OFF);
+
+ SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS.dumpProto(proto,
+ ConstantsProto.SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS);
proto.write(ConstantsProto.MAX_STANDARD_RESCHEDULE_COUNT, MAX_STANDARD_RESCHEDULE_COUNT);
proto.write(ConstantsProto.MAX_WORK_RESCHEDULE_COUNT, MAX_WORK_RESCHEDULE_COUNT);
@@ -3371,8 +3411,10 @@ public class JobSchedulerService extends com.android.server.SystemService
void dumpInternal(final IndentingPrintWriter pw, int filterUid) {
final int filterUidFinal = UserHandle.getAppId(filterUid);
+ final long now = sSystemClock.millis();
final long nowElapsed = sElapsedRealtimeClock.millis();
final long nowUptime = sUptimeMillisClock.millis();
+
final Predicate<JobStatus> predicate = (js) -> {
return filterUidFinal == -1 || UserHandle.getAppId(js.getUid()) == filterUidFinal
|| UserHandle.getAppId(js.getSourceUid()) == filterUidFinal;
@@ -3548,7 +3590,7 @@ public class JobSchedulerService extends com.android.server.SystemService
}
pw.println();
- mConcurrencyManager.dumpLocked(pw);
+ mConcurrencyManager.dumpLocked(pw, now, nowElapsed);
pw.println();
pw.print("PersistStats: ");
@@ -3560,6 +3602,7 @@ public class JobSchedulerService extends com.android.server.SystemService
void dumpInternalProto(final FileDescriptor fd, int filterUid) {
ProtoOutputStream proto = new ProtoOutputStream(fd);
final int filterUidFinal = UserHandle.getAppId(filterUid);
+ final long now = sSystemClock.millis();
final long nowElapsed = sElapsedRealtimeClock.millis();
final long nowUptime = sUptimeMillisClock.millis();
final Predicate<JobStatus> predicate = (js) -> {
@@ -3703,7 +3746,8 @@ public class JobSchedulerService extends com.android.server.SystemService
proto.write(JobSchedulerServiceDumpProto.IS_READY_TO_ROCK, mReadyToRock);
proto.write(JobSchedulerServiceDumpProto.REPORTED_ACTIVE, mReportedActive);
}
- mConcurrencyManager.dumpProtoLocked(proto);
+ mConcurrencyManager.dumpProtoLocked(proto,
+ JobSchedulerServiceDumpProto.CONCURRENCY_MANAGER, now, nowElapsed);
}
proto.flush();
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 47a55971b9c7..a1646862de9f 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -845,10 +845,12 @@ public class NotificationManagerService extends SystemService {
reportSeen(r);
}
r.setVisibility(true, nv.rank, nv.count);
+ boolean isHun = (nv.location
+ == NotificationVisibility.NotificationLocation.LOCATION_FIRST_HEADS_UP);
// hasBeenVisiblyExpanded must be called after updating the expansion state of
// the NotificationRecord to ensure the expansion state is up-to-date.
- if (r.hasBeenVisiblyExpanded()) {
- logSmartSuggestionsVisible(r);
+ if (isHun || r.hasBeenVisiblyExpanded()) {
+ logSmartSuggestionsVisible(r, nv.location.toMetricsEventEnum());
}
maybeRecordInterruptionLocked(r);
nv.recycle();
@@ -876,7 +878,7 @@ public class NotificationManagerService extends SystemService {
// hasBeenVisiblyExpanded must be called after updating the expansion state of
// the NotificationRecord to ensure the expansion state is up-to-date.
if (r.hasBeenVisiblyExpanded()) {
- logSmartSuggestionsVisible(r);
+ logSmartSuggestionsVisible(r, notificationLocation);
}
if (userAction) {
MetricsLogger.action(r.getItemLogMaker()
@@ -952,7 +954,7 @@ public class NotificationManagerService extends SystemService {
};
@VisibleForTesting
- void logSmartSuggestionsVisible(NotificationRecord r) {
+ void logSmartSuggestionsVisible(NotificationRecord r, int notificationLocation) {
// If the newly visible notification has smart suggestions
// then log that the user has seen them.
if ((r.getNumSmartRepliesAdded() > 0 || r.getNumSmartActionsAdded() > 0)
@@ -966,7 +968,10 @@ public class NotificationManagerService extends SystemService {
r.getNumSmartActionsAdded())
.addTaggedData(
MetricsEvent.NOTIFICATION_SMART_SUGGESTION_ASSISTANT_GENERATED,
- r.getSuggestionsGeneratedByAssistant() ? 1 : 0);
+ r.getSuggestionsGeneratedByAssistant() ? 1 : 0)
+ // The fields in the NotificationVisibility.NotificationLocation enum map
+ // directly to the fields in the MetricsEvent.NotificationLocation enum.
+ .addTaggedData(MetricsEvent.NOTIFICATION_LOCATION, notificationLocation);
mMetricsLogger.write(logMaker);
}
}
diff --git a/services/core/java/com/android/server/pm/BackgroundDexOptService.java b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
index 65fc9824c76e..ad9ac1232437 100644
--- a/services/core/java/com/android/server/pm/BackgroundDexOptService.java
+++ b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
@@ -50,6 +50,7 @@ import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.function.Supplier;
/**
* {@hide}
@@ -57,7 +58,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
public class BackgroundDexOptService extends JobService {
private static final String TAG = "BackgroundDexOptService";
- private static final boolean DEBUG = false;
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private static final int JOB_IDLE_OPTIMIZE = 800;
private static final int JOB_POST_BOOT_UPDATE = 801;
@@ -102,7 +103,6 @@ public class BackgroundDexOptService extends JobService {
private final AtomicBoolean mExitPostBootUpdate = new AtomicBoolean(false);
private final File mDataDir = Environment.getDataDirectory();
-
private static final long mDowngradeUnusedAppsThresholdInMillis =
getDowngradeUnusedAppsThresholdInMillis();
@@ -275,21 +275,18 @@ public class BackgroundDexOptService extends JobService {
long lowStorageThreshold = getLowStorageThreshold(context);
// Optimize primary apks.
- int result = optimizePackages(pm, pkgs, lowStorageThreshold, /*is_for_primary_dex*/ true,
- sFailedPackageNamesPrimary);
-
+ int result = optimizePackages(pm, pkgs, lowStorageThreshold,
+ /*isForPrimaryDex=*/ true);
if (result == OPTIMIZE_ABORT_BY_JOB_SCHEDULER) {
return result;
}
-
- if (SystemProperties.getBoolean("dalvik.vm.dexopt.secondary", false)) {
+ if (supportSecondaryDex()) {
result = reconcileSecondaryDexFiles(pm.getDexManager());
if (result == OPTIMIZE_ABORT_BY_JOB_SCHEDULER) {
return result;
}
-
- result = optimizePackages(pm, pkgs, lowStorageThreshold, /*is_for_primary_dex*/ false,
- sFailedPackageNamesSecondary);
+ result = optimizePackages(pm, pkgs, lowStorageThreshold,
+ /*isForPrimaryDex=*/ false);
}
return result;
}
@@ -339,92 +336,84 @@ public class BackgroundDexOptService extends JobService {
}
private int optimizePackages(PackageManagerService pm, ArraySet<String> pkgs,
- long lowStorageThreshold, boolean is_for_primary_dex,
- ArraySet<String> failedPackageNames) {
+ long lowStorageThreshold, boolean isForPrimaryDex) {
ArraySet<String> updatedPackages = new ArraySet<>();
Set<String> unusedPackages = pm.getUnusedPackages(mDowngradeUnusedAppsThresholdInMillis);
+ Log.d(TAG, "Unsused Packages " + String.join(",", unusedPackages));
// Only downgrade apps when space is low on device.
// Threshold is selected above the lowStorageThreshold so that we can pro-actively clean
// up disk before user hits the actual lowStorageThreshold.
final long lowStorageThresholdForDowngrade = LOW_THRESHOLD_MULTIPLIER_FOR_DOWNGRADE *
lowStorageThreshold;
boolean shouldDowngrade = shouldDowngrade(lowStorageThresholdForDowngrade);
+ Log.d(TAG, "Should Downgrade " + shouldDowngrade);
+ boolean dex_opt_performed = false;
for (String pkg : pkgs) {
int abort_code = abortIdleOptimizations(lowStorageThreshold);
if (abort_code == OPTIMIZE_ABORT_BY_JOB_SCHEDULER) {
return abort_code;
}
-
- synchronized (failedPackageNames) {
- if (failedPackageNames.contains(pkg)) {
- // Skip previously failing package
- continue;
- }
- }
-
- int reason;
- boolean downgrade;
- long package_size_before = 0; //used when the app is downgraded
// Downgrade unused packages.
if (unusedPackages.contains(pkg) && shouldDowngrade) {
- package_size_before = getPackageSize(pm, pkg);
- // This applies for system apps or if packages location is not a directory, i.e.
- // monolithic install.
- if (is_for_primary_dex && !pm.canHaveOatDir(pkg)) {
- // For apps that don't have the oat directory, instead of downgrading,
- // remove their compiler artifacts from dalvik cache.
- pm.deleteOatArtifactsOfPackage(pkg);
+ dex_opt_performed = downgradePackage(pm, pkg, isForPrimaryDex);
+ } else {
+ if (abort_code == OPTIMIZE_ABORT_NO_SPACE_LEFT) {
+ // can't dexopt because of low space.
continue;
- } else {
- reason = PackageManagerService.REASON_INACTIVE_PACKAGE_DOWNGRADE;
- downgrade = true;
}
- } else if (abort_code != OPTIMIZE_ABORT_NO_SPACE_LEFT) {
- reason = PackageManagerService.REASON_BACKGROUND_DEXOPT;
- downgrade = false;
- } else {
- // can't dexopt because of low space.
- continue;
+ dex_opt_performed = optimizePackage(pm, pkg, isForPrimaryDex);
}
-
- synchronized (failedPackageNames) {
- // Conservatively add package to the list of failing ones in case
- // performDexOpt never returns.
- failedPackageNames.add(pkg);
+ if (dex_opt_performed) {
+ updatedPackages.add(pkg);
}
+ }
- // Optimize package if needed. Note that there can be no race between
- // concurrent jobs because PackageDexOptimizer.performDexOpt is synchronized.
- boolean success;
- int dexoptFlags =
- DexoptOptions.DEXOPT_CHECK_FOR_PROFILES_UPDATES |
- DexoptOptions.DEXOPT_BOOT_COMPLETE |
- (downgrade ? DexoptOptions.DEXOPT_DOWNGRADE : 0) |
- DexoptOptions.DEXOPT_IDLE_BACKGROUND_JOB;
- if (is_for_primary_dex) {
- int result = pm.performDexOptWithStatus(new DexoptOptions(pkg, reason,
- dexoptFlags));
- success = result != PackageDexOptimizer.DEX_OPT_FAILED;
- if (result == PackageDexOptimizer.DEX_OPT_PERFORMED) {
- updatedPackages.add(pkg);
- }
+ notifyPinService(updatedPackages);
+ return OPTIMIZE_PROCESSED;
+ }
+
+
+ /**
+ * Try to downgrade the package to a smaller compilation filter.
+ * eg. if the package is in speed-profile the package will be downgraded to verify.
+ * @param pm PackageManagerService
+ * @param pkg The package to be downgraded.
+ * @param isForPrimaryDex. Apps can have several dex file, primary and secondary.
+ * @return true if the package was downgraded.
+ */
+ private boolean downgradePackage(PackageManagerService pm, String pkg,
+ boolean isForPrimaryDex) {
+ Log.d(TAG, "Downgrading " + pkg);
+ boolean dex_opt_performed = false;
+ int reason = PackageManagerService.REASON_INACTIVE_PACKAGE_DOWNGRADE;
+ int dexoptFlags = DexoptOptions.DEXOPT_BOOT_COMPLETE
+ | DexoptOptions.DEXOPT_IDLE_BACKGROUND_JOB
+ | DexoptOptions.DEXOPT_DOWNGRADE;
+ long package_size_before = getPackageSize(pm, pkg);
+
+ if (isForPrimaryDex) {
+ // This applies for system apps or if packages location is not a directory, i.e.
+ // monolithic install.
+ if (!pm.canHaveOatDir(pkg)) {
+ // For apps that don't have the oat directory, instead of downgrading,
+ // remove their compiler artifacts from dalvik cache.
+ pm.deleteOatArtifactsOfPackage(pkg);
} else {
- success = pm.performDexOpt(new DexoptOptions(pkg,
- reason, dexoptFlags | DexoptOptions.DEXOPT_ONLY_SECONDARY_DEX));
- }
- if (success) {
- // Dexopt succeeded, remove package from the list of failing ones.
- synchronized (failedPackageNames) {
- failedPackageNames.remove(pkg);
- }
- if (downgrade) {
- StatsLog.write(StatsLog.APP_DOWNGRADED, pkg, package_size_before,
- getPackageSize(pm, pkg), /*aggressive=*/ false);
- }
+ dex_opt_performed = performDexOptPrimary(pm, pkg, reason, dexoptFlags);
}
+ } else {
+ dex_opt_performed = performDexOptSecondary(pm, pkg, reason, dexoptFlags);
}
- notifyPinService(updatedPackages);
- return OPTIMIZE_PROCESSED;
+
+ if (dex_opt_performed) {
+ StatsLog.write(StatsLog.APP_DOWNGRADED, pkg, package_size_before,
+ getPackageSize(pm, pkg), /*aggressive=*/ false);
+ }
+ return dex_opt_performed;
+ }
+
+ private boolean supportSecondaryDex() {
+ return (SystemProperties.getBoolean("dalvik.vm.dexopt.secondary", false));
}
private int reconcileSecondaryDexFiles(DexManager dm) {
@@ -438,6 +427,73 @@ public class BackgroundDexOptService extends JobService {
return OPTIMIZE_PROCESSED;
}
+ /**
+ *
+ * Optimize package if needed. Note that there can be no race between
+ * concurrent jobs because PackageDexOptimizer.performDexOpt is synchronized.
+ * @param pm An instance of PackageManagerService
+ * @param pkg The package to be downgraded.
+ * @param isForPrimaryDex. Apps can have several dex file, primary and secondary.
+ * @return true if the package was downgraded.
+ */
+ private boolean optimizePackage(PackageManagerService pm, String pkg,
+ boolean isForPrimaryDex) {
+ int reason = PackageManagerService.REASON_BACKGROUND_DEXOPT;
+ int dexoptFlags = DexoptOptions.DEXOPT_CHECK_FOR_PROFILES_UPDATES
+ | DexoptOptions.DEXOPT_BOOT_COMPLETE
+ | DexoptOptions.DEXOPT_IDLE_BACKGROUND_JOB;
+
+ return isForPrimaryDex
+ ? performDexOptPrimary(pm, pkg, reason, dexoptFlags)
+ : performDexOptSecondary(pm, pkg, reason, dexoptFlags);
+ }
+
+ private boolean performDexOptPrimary(PackageManagerService pm, String pkg, int reason,
+ int dexoptFlags) {
+ int result = trackPerformDexOpt(pkg, /*isForPrimaryDex=*/ false,
+ () -> pm.performDexOptWithStatus(new DexoptOptions(pkg, reason, dexoptFlags)));
+ return result == PackageDexOptimizer.DEX_OPT_PERFORMED;
+ }
+
+ private boolean performDexOptSecondary(PackageManagerService pm, String pkg, int reason,
+ int dexoptFlags) {
+ DexoptOptions dexoptOptions = new DexoptOptions(pkg, reason,
+ dexoptFlags | DexoptOptions.DEXOPT_ONLY_SECONDARY_DEX);
+ int result = trackPerformDexOpt(pkg, /*isForPrimaryDex=*/ true,
+ () -> pm.performDexOpt(dexoptOptions)
+ ? PackageDexOptimizer.DEX_OPT_PERFORMED : PackageDexOptimizer.DEX_OPT_FAILED
+ );
+ return result == PackageDexOptimizer.DEX_OPT_PERFORMED;
+ }
+
+ /**
+ * Execute the dexopt wrapper and make sure that if performDexOpt wrapper fails
+ * the package is added to the list of failed packages.
+ * Return one of following result:
+ * {@link PackageDexOptimizer#DEX_OPT_SKIPPED}
+ * {@link PackageDexOptimizer#DEX_OPT_PERFORMED}
+ * {@link PackageDexOptimizer#DEX_OPT_FAILED}
+ */
+ private int trackPerformDexOpt(String pkg, boolean isForPrimaryDex,
+ Supplier<Integer> performDexOptWrapper) {
+ ArraySet<String> sFailedPackageNames =
+ isForPrimaryDex ? sFailedPackageNamesPrimary : sFailedPackageNamesSecondary;
+ synchronized (sFailedPackageNames) {
+ if (sFailedPackageNames.contains(pkg)) {
+ // Skip previously failing package
+ return PackageDexOptimizer.DEX_OPT_SKIPPED;
+ }
+ sFailedPackageNames.add(pkg);
+ }
+ int result = performDexOptWrapper.get();
+ if (result != PackageDexOptimizer.DEX_OPT_FAILED) {
+ synchronized (sFailedPackageNames) {
+ sFailedPackageNames.remove(pkg);
+ }
+ }
+ return result;
+ }
+
// Evaluate whether or not idle optimizations should continue.
private int abortIdleOptimizations(long lowStorageThreshold) {
if (mAbortIdleOptimization.get()) {
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index a33f14bab4b1..d0ef4f1523d4 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -25,6 +25,7 @@ import android.app.AppGlobals;
import android.app.IApplicationThread;
import android.app.PendingIntent;
import android.app.admin.DevicePolicyManager;
+import android.app.usage.UsageStatsManagerInternal;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -135,6 +136,7 @@ public class LauncherAppsService extends SystemService {
private final Context mContext;
private final UserManager mUm;
private final UserManagerInternal mUserManagerInternal;
+ private final UsageStatsManagerInternal mUsageStatsManagerInternal;
private final ActivityManagerInternal mActivityManagerInternal;
private final ActivityTaskManagerInternal mActivityTaskManagerInternal;
private final ShortcutServiceInternal mShortcutServiceInternal;
@@ -156,6 +158,8 @@ public class LauncherAppsService extends SystemService {
mUm = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
mUserManagerInternal = Preconditions.checkNotNull(
LocalServices.getService(UserManagerInternal.class));
+ mUsageStatsManagerInternal = Preconditions.checkNotNull(
+ LocalServices.getService(UsageStatsManagerInternal.class));
mActivityManagerInternal = Preconditions.checkNotNull(
LocalServices.getService(ActivityManagerInternal.class));
mActivityTaskManagerInternal = Preconditions.checkNotNull(
@@ -671,6 +675,30 @@ public class LauncherAppsService extends SystemService {
}
}
+ @Override
+ public LauncherApps.AppUsageLimit getAppUsageLimit(String callingPackage,
+ String packageName, UserHandle user) {
+ verifyCallingPackage(callingPackage);
+ if (!canAccessProfile(user.getIdentifier(), "Cannot access usage limit")) {
+ return null;
+ }
+
+ final PackageManagerInternal pmi =
+ LocalServices.getService(PackageManagerInternal.class);
+ final ComponentName cn = pmi.getDefaultHomeActivity(user.getIdentifier());
+ if (!cn.getPackageName().equals(callingPackage)) {
+ throw new SecurityException("Caller is not the active launcher");
+ }
+
+ final UsageStatsManagerInternal.AppUsageLimitData data =
+ mUsageStatsManagerInternal.getAppUsageLimit(packageName, user);
+ if (data == null) {
+ return null;
+ }
+ return new LauncherApps.AppUsageLimit(
+ data.isGroupLimit(), data.getTotalUsageLimit(), data.getUsageRemaining());
+ }
+
private void ensureShortcutPermission(@NonNull String callingPackage) {
verifyCallingPackage(callingPackage);
if (!mShortcutServiceInternal.hasShortcutHostPermission(getCallingUserId(),
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index 5412e9425adf..94b1b362f43a 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -506,8 +506,10 @@ public class PackageDexOptimizer {
boolean isUsedByOtherApps) {
int flags = info.flags;
boolean vmSafeMode = (flags & ApplicationInfo.FLAG_VM_SAFE_MODE) != 0;
- // When a priv app is configured to run out of box, only verify it.
- if (info.isPrivilegedApp() && DexManager.isPackageSelectedToRunOob(info.packageName)) {
+ // When an app or priv app is configured to run out of box, only verify it.
+ if (info.isCodeIntegrityPreferred()
+ || (info.isPrivilegedApp()
+ && DexManager.isPackageSelectedToRunOob(info.packageName))) {
return "verify";
}
if (vmSafeMode) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index b8342cf8f81e..9100f6aec6d9 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -10574,8 +10574,6 @@ public class PackageManagerService extends IPackageManager.Stub
Log.d(TAG, "Scanning package " + pkg.packageName);
}
- DexManager.maybeLogUnexpectedPackageDetails(pkg);
-
// Initialize package source and resource directories
final File scanFile = new File(pkg.codePath);
final File destCodeFile = new File(pkg.applicationInfo.getCodePath());
@@ -20672,7 +20670,6 @@ public class PackageManagerService extends IPackageManager.Stub
storage.registerListener(mStorageListener);
mInstallerService.systemReady();
- mDexManager.systemReady();
mPackageDexOptimizer.systemReady();
getStorageManagerInternal().addExternalStoragePolicy(
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 022c1aad8113..692c032b1f70 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -40,10 +40,10 @@ import android.content.pm.InstrumentationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageInstaller.SessionParams;
-import android.content.pm.PackageManagerInternal;
import android.content.pm.PackageItemInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.PackageManagerInternal;
import android.content.pm.PackageParser;
import android.content.pm.PackageParser.ApkLite;
import android.content.pm.PackageParser.PackageLite;
@@ -634,9 +634,9 @@ class PackageManagerShellCommand extends ShellCommand {
if (showVersionCode) {
pw.print(" versionCode:");
if (info.applicationInfo != null) {
- pw.print(info.applicationInfo.versionCode);
+ pw.print(info.applicationInfo.longVersionCode);
} else {
- pw.print(info.versionCode);
+ pw.print(info.getLongVersionCode());
}
}
if (listInstaller && !isApex) {
@@ -2307,7 +2307,7 @@ class PackageManagerShellCommand extends ShellCommand {
sessionParams.installFlags |= PackageManager.INSTALL_FORCE_SDK;
break;
case "--apex":
- sessionParams.installFlags |= PackageManager.INSTALL_APEX;
+ sessionParams.setInstallAsApex();
sessionParams.setStaged();
break;
case "--multi-package":
diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
index aaa187468f8d..8d64b810b407 100644
--- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
+++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
@@ -666,6 +666,7 @@ public class UserRestrictionsUtils {
case android.provider.Settings.Secure.ALWAYS_ON_VPN_APP:
case android.provider.Settings.Secure.ALWAYS_ON_VPN_LOCKDOWN:
+ case android.provider.Settings.Secure.ALWAYS_ON_VPN_LOCKDOWN_WHITELIST:
// Whitelist system uid (ConnectivityService) and root uid to change always-on vpn
final int appId = UserHandle.getAppId(callingUid);
if (appId == Process.SYSTEM_UID || appId == Process.ROOT_UID) {
diff --git a/services/core/java/com/android/server/pm/dex/DexManager.java b/services/core/java/com/android/server/pm/dex/DexManager.java
index 1a2b11559446..7ac7395e1f99 100644
--- a/services/core/java/com/android/server/pm/dex/DexManager.java
+++ b/services/core/java/com/android/server/pm/dex/DexManager.java
@@ -16,31 +16,28 @@
package com.android.server.pm.dex;
+import static android.provider.DeviceConfig.FsiBoot;
+
import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
import static com.android.server.pm.dex.PackageDexUsage.DexUseInfo;
import static com.android.server.pm.dex.PackageDexUsage.PackageUseInfo;
-import android.content.ContentResolver;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.content.pm.PackageInfo;
-import android.content.pm.PackageParser;
-import android.database.ContentObserver;
-import android.os.Build;
import android.os.FileUtils;
import android.os.RemoteException;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.storage.StorageManager;
-import android.provider.Settings.Global;
+import android.provider.DeviceConfig;
import android.util.Log;
import android.util.Slog;
import android.util.jar.StrictJarFile;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.ArrayUtils;
import com.android.server.pm.Installer;
import com.android.server.pm.Installer.InstallerException;
import com.android.server.pm.PackageDexOptimizer;
@@ -136,10 +133,6 @@ public class DexManager {
return mDexLogger;
}
- public void systemReady() {
- registerSettingObserver();
- }
-
/**
* Notify about dex files loads.
* Note that this method is invoked when apps load dex files and it should
@@ -699,47 +692,10 @@ public class DexManager {
mDexLogger.writeNow();
}
- private void registerSettingObserver() {
- final ContentResolver resolver = mContext.getContentResolver();
-
- // This observer provides a one directional mapping from Global.PRIV_APP_OOB_ENABLED to
- // pm.dexopt.priv-apps-oob property. This is only for experiment and should be removed once
- // it is done.
- ContentObserver privAppOobObserver = new ContentObserver(null) {
- @Override
- public void onChange(boolean selfChange) {
- int oobEnabled = Global.getInt(resolver, Global.PRIV_APP_OOB_ENABLED, 0);
- SystemProperties.set(PROPERTY_NAME_PM_DEXOPT_PRIV_APPS_OOB,
- oobEnabled == 1 ? "true" : "false");
- }
- };
- resolver.registerContentObserver(
- Global.getUriFor(Global.PRIV_APP_OOB_ENABLED), false, privAppOobObserver,
- UserHandle.USER_SYSTEM);
- // At boot, restore the value from the setting, which persists across reboot.
- privAppOobObserver.onChange(true);
-
- ContentObserver privAppOobListObserver = new ContentObserver(null) {
- @Override
- public void onChange(boolean selfChange) {
- String oobList = Global.getString(resolver, Global.PRIV_APP_OOB_LIST);
- if (oobList == null) {
- oobList = "ALL";
- }
- SystemProperties.set(PROPERTY_NAME_PM_DEXOPT_PRIV_APPS_OOB_LIST, oobList);
- }
- };
- resolver.registerContentObserver(
- Global.getUriFor(Global.PRIV_APP_OOB_LIST), false, privAppOobListObserver,
- UserHandle.USER_SYSTEM);
- // At boot, restore the value from the setting, which persists across reboot.
- privAppOobListObserver.onChange(true);
- }
-
/**
* Returns whether the given package is in the list of privilaged apps that should run out of
- * box. This only makes sense if PROPERTY_NAME_PM_DEXOPT_PRIV_APPS_OOB is true. Note that when
- * the the OOB list is empty, all priv apps will run in OOB mode.
+ * box. This only makes sense if the feature is enabled. Note that when the the OOB list is
+ * empty, all priv apps will run in OOB mode.
*/
public static boolean isPackageSelectedToRunOob(String packageName) {
return isPackageSelectedToRunOob(Arrays.asList(packageName));
@@ -747,19 +703,35 @@ public class DexManager {
/**
* Returns whether any of the given packages are in the list of privilaged apps that should run
- * out of box. This only makes sense if PROPERTY_NAME_PM_DEXOPT_PRIV_APPS_OOB is true. Note that
- * when the the OOB list is empty, all priv apps will run in OOB mode.
+ * out of box. This only makes sense if the feature is enabled. Note that when the the OOB list
+ * is empty, all priv apps will run in OOB mode.
*/
public static boolean isPackageSelectedToRunOob(Collection<String> packageNamesInSameProcess) {
- if (!SystemProperties.getBoolean(PROPERTY_NAME_PM_DEXOPT_PRIV_APPS_OOB, false)) {
+ return isPackageSelectedToRunOobInternal(
+ SystemProperties.getBoolean(PROPERTY_NAME_PM_DEXOPT_PRIV_APPS_OOB, false),
+ SystemProperties.get(PROPERTY_NAME_PM_DEXOPT_PRIV_APPS_OOB_LIST, "ALL"),
+ DeviceConfig.getProperty(FsiBoot.NAMESPACE, FsiBoot.OOB_ENABLED),
+ DeviceConfig.getProperty(FsiBoot.NAMESPACE, FsiBoot.OOB_WHITELIST),
+ packageNamesInSameProcess);
+ }
+
+ @VisibleForTesting
+ /* package */ static boolean isPackageSelectedToRunOobInternal(
+ boolean isDefaultEnabled, String defaultWhitelist, String overrideEnabled,
+ String overrideWhitelist, Collection<String> packageNamesInSameProcess) {
+ // Allow experiment (if exists) to override device configuration.
+ boolean enabled = overrideEnabled != null ? overrideEnabled.equals("true")
+ : isDefaultEnabled;
+ if (!enabled) {
return false;
}
- String oobListProperty = SystemProperties.get(
- PROPERTY_NAME_PM_DEXOPT_PRIV_APPS_OOB_LIST, "ALL");
- if ("ALL".equals(oobListProperty)) {
+
+ // Similarly, experiment flag can override the whitelist.
+ String whitelist = overrideWhitelist != null ? overrideWhitelist : defaultWhitelist;
+ if ("ALL".equals(whitelist)) {
return true;
}
- for (String oobPkgName : oobListProperty.split(",")) {
+ for (String oobPkgName : whitelist.split(",")) {
if (packageNamesInSameProcess.contains(oobPkgName)) {
return true;
}
@@ -768,32 +740,6 @@ public class DexManager {
}
/**
- * Generates package related log if the package has code stored in unexpected way.
- */
- public static void maybeLogUnexpectedPackageDetails(PackageParser.Package pkg) {
- if (!Build.IS_DEBUGGABLE) {
- return;
- }
-
- if (pkg.isPrivileged() && isPackageSelectedToRunOob(pkg.packageName)) {
- logIfPackageHasUncompressedCode(pkg);
- }
- }
-
- /**
- * Generates log if the APKs in the given package have uncompressed dex file and so
- * files that can be direclty mapped.
- */
- private static void logIfPackageHasUncompressedCode(PackageParser.Package pkg) {
- auditUncompressedCodeInApk(pkg.baseCodePath);
- if (!ArrayUtils.isEmpty(pkg.splitCodePaths)) {
- for (int i = 0; i < pkg.splitCodePaths.length; i++) {
- auditUncompressedCodeInApk(pkg.splitCodePaths[i]);
- }
- }
- }
-
- /**
* Generates log if the archive located at {@code fileName} has uncompressed dex file and so
* files that can be direclty mapped.
*/
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 41cab2d7ebd3..13c4d886e7b1 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -1901,9 +1901,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
mWindowManagerInternal.registerAppTransitionListener(new AppTransitionListener() {
@Override
- public int onAppTransitionStartingLocked(int transit, IBinder openToken,
- IBinder closeToken, long duration, long statusBarAnimationStartTime,
- long statusBarAnimationDuration) {
+ public int onAppTransitionStartingLocked(int transit, long duration,
+ long statusBarAnimationStartTime, long statusBarAnimationDuration) {
return handleStartTransitionForKeyguardLw(transit, duration);
}
@@ -2570,6 +2569,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
private static final int[] WINDOW_TYPES_WHERE_HOME_DOESNT_WORK = {
+ WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
WindowManager.LayoutParams.TYPE_SYSTEM_ALERT,
WindowManager.LayoutParams.TYPE_SYSTEM_ERROR,
};
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index e1a911e8ada5..1d829707f180 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -825,16 +825,16 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants {
// like the ANR / app crashed dialogs
return canAddInternalSystemWindow ? 11 : 10;
case TYPE_APPLICATION_OVERLAY:
- return 12;
+ return canAddInternalSystemWindow ? 13 : 12;
case TYPE_DREAM:
// used for Dreams (screensavers with TYPE_DREAM windows)
- return 13;
+ return 14;
case TYPE_INPUT_METHOD:
// on-screen keyboards and other such input method user interfaces go here.
- return 14;
+ return 15;
case TYPE_INPUT_METHOD_DIALOG:
// on-screen keyboards and other such input method user interfaces go here.
- return 15;
+ return 16;
case TYPE_STATUS_BAR:
return 17;
case TYPE_STATUS_BAR_PANEL:
diff --git a/services/core/java/com/android/server/policy/role/LegacyRoleResolutionPolicy.java b/services/core/java/com/android/server/policy/role/LegacyRoleResolutionPolicy.java
index 055c941f8b0a..7f2dedb70514 100644
--- a/services/core/java/com/android/server/policy/role/LegacyRoleResolutionPolicy.java
+++ b/services/core/java/com/android/server/policy/role/LegacyRoleResolutionPolicy.java
@@ -18,6 +18,7 @@ package com.android.server.policy.role;
import android.annotation.NonNull;
import android.app.role.RoleManager;
+import android.content.ComponentName;
import android.content.Context;
import android.os.Debug;
import android.provider.Settings;
@@ -90,6 +91,17 @@ public class LegacyRoleResolutionPolicy implements RoleManagerService.RoleHolder
return CollectionUtils.singletonOrEmpty(result);
}
+ case RoleManager.ROLE_ASSISTANT: {
+ String legacyAssistant = Settings.Secure.getStringForUser(
+ mContext.getContentResolver(), Settings.Secure.ASSISTANT, userId);
+
+ if (legacyAssistant == null || legacyAssistant.isEmpty()) {
+ return Collections.emptyList();
+ } else {
+ return Collections.singletonList(
+ ComponentName.unflattenFromString(legacyAssistant).getPackageName());
+ }
+ }
default: {
Slog.e(LOG_TAG, "Don't know how to find legacy role holders for " + roleName);
return Collections.emptyList();
diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java
index 1d74350de978..ab2807a9a8a8 100644
--- a/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java
+++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java
@@ -104,6 +104,7 @@ public class BatterySaverController implements BatterySaverPolicyListener {
public static final int REASON_SETTING_CHANGED = 8;
public static final int REASON_DYNAMIC_POWER_SAVINGS_AUTOMATIC_ON = 9;
public static final int REASON_DYNAMIC_POWER_SAVINGS_AUTOMATIC_OFF = 10;
+ public static final int REASON_STICKY_RESTORE_OFF = 13;
/**
* Plugin interface. All methods are guaranteed to be called on the same (handler) thread.
diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java
index b7f28da499e9..404e450ccf14 100644
--- a/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java
+++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java
@@ -28,7 +28,6 @@ import android.os.Handler;
import android.os.PowerManager;
import android.os.UserHandle;
import android.provider.Settings;
-import android.provider.Settings.Global;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
@@ -85,42 +84,56 @@ public class BatterySaverStateMachine {
@GuardedBy("mLock")
private boolean mIsBatteryLevelLow;
- /** Previously known value of Global.LOW_POWER_MODE. */
+ /** Previously known value of Settings.Global.LOW_POWER_MODE. */
@GuardedBy("mLock")
private boolean mSettingBatterySaverEnabled;
- /** Previously known value of Global.LOW_POWER_MODE_STICKY. */
+ /** Previously known value of Settings.Global.LOW_POWER_MODE_STICKY. */
@GuardedBy("mLock")
private boolean mSettingBatterySaverEnabledSticky;
/** Config flag to track if battery saver's sticky behaviour is disabled. */
private final boolean mBatterySaverStickyBehaviourDisabled;
+ /**
+ * Whether or not to end sticky battery saver upon reaching a level specified by
+ * {@link #mSettingBatterySaverStickyAutoDisableThreshold}.
+ */
+ @GuardedBy("mLock")
+ private boolean mSettingBatterySaverStickyAutoDisableEnabled;
+
+ /**
+ * The battery level at which to end sticky battery saver. Only useful if
+ * {@link #mSettingBatterySaverStickyAutoDisableEnabled} is {@code true}.
+ */
+ @GuardedBy("mLock")
+ private int mSettingBatterySaverStickyAutoDisableThreshold;
+
/** Config flag to track default disable threshold for Dynamic Power Savings enabled battery
* saver. */
@GuardedBy("mLock")
private final int mDynamicPowerSavingsDefaultDisableThreshold;
/**
- * Previously known value of Global.LOW_POWER_MODE_TRIGGER_LEVEL.
+ * Previously known value of Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL.
* (Currently only used in dumpsys.)
*/
@GuardedBy("mLock")
private int mSettingBatterySaverTriggerThreshold;
- /** Previously known value of Global.AUTOMATIC_POWER_SAVER_MODE. */
+ /** Previously known value of Settings.Global.AUTOMATIC_POWER_SAVER_MODE. */
@GuardedBy("mLock")
private int mSettingAutomaticBatterySaver;
/** When to disable battery saver again if it was enabled due to an external suggestion.
- * Corresponds to Global.DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD.
+ * Corresponds to Settings.Global.DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD.
*/
@GuardedBy("mLock")
private int mDynamicPowerSavingsDisableThreshold;
/**
* Whether we've received a suggestion that battery saver should be on from an external app.
- * Updates when Global.DYNAMIC_POWER_SAVINGS_ENABLED changes.
+ * Updates when Settings.Global.DYNAMIC_POWER_SAVINGS_ENABLED changes.
*/
@GuardedBy("mLock")
private boolean mDynamicPowerSavingsBatterySaver;
@@ -181,7 +194,7 @@ public class BatterySaverStateMachine {
Slog.d(TAG, "onBootCompleted");
}
// Just booted. We don't want LOW_POWER_MODE to be persisted, so just always clear it.
- putGlobalSetting(Global.LOW_POWER_MODE, 0);
+ putGlobalSetting(Settings.Global.LOW_POWER_MODE, 0);
// This is called with the power manager lock held. Don't do anything that may call to
// upper services. (e.g. don't call into AM directly)
@@ -199,13 +212,19 @@ public class BatterySaverStateMachine {
Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL),
false, mSettingsObserver, UserHandle.USER_SYSTEM);
cr.registerContentObserver(Settings.Global.getUriFor(
- Global.AUTOMATIC_POWER_SAVER_MODE),
+ Settings.Global.AUTOMATIC_POWER_SAVER_MODE),
+ false, mSettingsObserver, UserHandle.USER_SYSTEM);
+ cr.registerContentObserver(Settings.Global.getUriFor(
+ Settings.Global.DYNAMIC_POWER_SAVINGS_ENABLED),
+ false, mSettingsObserver, UserHandle.USER_SYSTEM);
+ cr.registerContentObserver(Settings.Global.getUriFor(
+ Settings.Global.DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD),
false, mSettingsObserver, UserHandle.USER_SYSTEM);
cr.registerContentObserver(Settings.Global.getUriFor(
- Global.DYNAMIC_POWER_SAVINGS_ENABLED),
+ Settings.Global.LOW_POWER_MODE_STICKY_AUTO_DISABLE_ENABLED),
false, mSettingsObserver, UserHandle.USER_SYSTEM);
cr.registerContentObserver(Settings.Global.getUriFor(
- Global.DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD),
+ Settings.Global.LOW_POWER_MODE_STICKY_AUTO_DISABLE_LEVEL),
false, mSettingsObserver, UserHandle.USER_SYSTEM);
synchronized (mLock) {
@@ -239,25 +258,31 @@ public class BatterySaverStateMachine {
}
@GuardedBy("mLock")
- void refreshSettingsLocked() {
+ private void refreshSettingsLocked() {
final boolean lowPowerModeEnabled = getGlobalSetting(
Settings.Global.LOW_POWER_MODE, 0) != 0;
final boolean lowPowerModeEnabledSticky = getGlobalSetting(
Settings.Global.LOW_POWER_MODE_STICKY, 0) != 0;
final boolean dynamicPowerSavingsBatterySaver = getGlobalSetting(
- Global.DYNAMIC_POWER_SAVINGS_ENABLED, 0) != 0;
+ Settings.Global.DYNAMIC_POWER_SAVINGS_ENABLED, 0) != 0;
final int lowPowerModeTriggerLevel = getGlobalSetting(
Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL, 0);
- final int automaticBatterySaver = getGlobalSetting(
- Global.AUTOMATIC_POWER_SAVER_MODE,
+ final int automaticBatterySaverMode = getGlobalSetting(
+ Settings.Global.AUTOMATIC_POWER_SAVER_MODE,
PowerManager.POWER_SAVER_MODE_PERCENTAGE);
final int dynamicPowerSavingsDisableThreshold = getGlobalSetting(
- Global.DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD,
+ Settings.Global.DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD,
mDynamicPowerSavingsDefaultDisableThreshold);
+ final boolean isStickyAutoDisableEnabled = getGlobalSetting(
+ Settings.Global.LOW_POWER_MODE_STICKY_AUTO_DISABLE_ENABLED, 0) != 0;
+ final int stickyAutoDisableThreshold = getGlobalSetting(
+ Settings.Global.LOW_POWER_MODE_STICKY_AUTO_DISABLE_LEVEL, 90);
setSettingsLocked(lowPowerModeEnabled, lowPowerModeEnabledSticky,
- lowPowerModeTriggerLevel, automaticBatterySaver, dynamicPowerSavingsBatterySaver,
- dynamicPowerSavingsDisableThreshold);
+ lowPowerModeTriggerLevel,
+ isStickyAutoDisableEnabled, stickyAutoDisableThreshold,
+ automaticBatterySaverMode,
+ dynamicPowerSavingsBatterySaver, dynamicPowerSavingsDisableThreshold);
}
/**
@@ -269,12 +294,16 @@ public class BatterySaverStateMachine {
@GuardedBy("mLock")
@VisibleForTesting
void setSettingsLocked(boolean batterySaverEnabled, boolean batterySaverEnabledSticky,
- int batterySaverTriggerThreshold, int automaticBatterySaver,
+ int batterySaverTriggerThreshold,
+ boolean isStickyAutoDisableEnabled, int stickyAutoDisableThreshold,
+ int automaticBatterySaver,
boolean dynamicPowerSavingsBatterySaver, int dynamicPowerSavingsDisableThreshold) {
if (DEBUG) {
Slog.d(TAG, "setSettings: enabled=" + batterySaverEnabled
+ " sticky=" + batterySaverEnabledSticky
+ " threshold=" + batterySaverTriggerThreshold
+ + " stickyAutoDisableEnabled=" + isStickyAutoDisableEnabled
+ + " stickyAutoDisableThreshold=" + stickyAutoDisableThreshold
+ " automaticBatterySaver=" + automaticBatterySaver
+ " dynamicPowerSavingsBatterySaver=" + dynamicPowerSavingsBatterySaver
+ " dynamicPowerSavingsDisableThreshold="
@@ -283,11 +312,19 @@ public class BatterySaverStateMachine {
mSettingsLoaded = true;
+ // Set sensible limits.
+ stickyAutoDisableThreshold = Math.max(stickyAutoDisableThreshold,
+ batterySaverTriggerThreshold);
+
final boolean enabledChanged = mSettingBatterySaverEnabled != batterySaverEnabled;
final boolean stickyChanged =
mSettingBatterySaverEnabledSticky != batterySaverEnabledSticky;
final boolean thresholdChanged
= mSettingBatterySaverTriggerThreshold != batterySaverTriggerThreshold;
+ final boolean stickyAutoDisableEnabledChanged =
+ mSettingBatterySaverStickyAutoDisableEnabled != isStickyAutoDisableEnabled;
+ final boolean stickyAutoDisableThresholdChanged =
+ mSettingBatterySaverStickyAutoDisableThreshold != stickyAutoDisableThreshold;
final boolean automaticModeChanged = mSettingAutomaticBatterySaver != automaticBatterySaver;
final boolean dynamicPowerSavingsThresholdChanged =
mDynamicPowerSavingsDisableThreshold != dynamicPowerSavingsDisableThreshold;
@@ -295,6 +332,7 @@ public class BatterySaverStateMachine {
mDynamicPowerSavingsBatterySaver != dynamicPowerSavingsBatterySaver;
if (!(enabledChanged || stickyChanged || thresholdChanged || automaticModeChanged
+ || stickyAutoDisableEnabledChanged || stickyAutoDisableThresholdChanged
|| dynamicPowerSavingsThresholdChanged || dynamicPowerSavingsBatterySaverChanged)) {
return;
}
@@ -302,6 +340,8 @@ public class BatterySaverStateMachine {
mSettingBatterySaverEnabled = batterySaverEnabled;
mSettingBatterySaverEnabledSticky = batterySaverEnabledSticky;
mSettingBatterySaverTriggerThreshold = batterySaverTriggerThreshold;
+ mSettingBatterySaverStickyAutoDisableEnabled = isStickyAutoDisableEnabled;
+ mSettingBatterySaverStickyAutoDisableThreshold = stickyAutoDisableThreshold;
mSettingAutomaticBatterySaver = automaticBatterySaver;
mDynamicPowerSavingsDisableThreshold = dynamicPowerSavingsDisableThreshold;
mDynamicPowerSavingsBatterySaver = dynamicPowerSavingsBatterySaver;
@@ -376,7 +416,9 @@ public class BatterySaverStateMachine {
+ " mBatterySaverSnoozing=" + mBatterySaverSnoozing
+ " mIsPowered=" + mIsPowered
+ " mSettingAutomaticBatterySaver=" + mSettingAutomaticBatterySaver
- + " mSettingBatterySaverEnabledSticky=" + mSettingBatterySaverEnabledSticky);
+ + " mSettingBatterySaverEnabledSticky=" + mSettingBatterySaverEnabledSticky
+ + " mSettingBatterySaverStickyAutoDisableEnabled="
+ + mSettingBatterySaverStickyAutoDisableEnabled);
}
if (!(mBootCompleted && mSettingsLoaded && mBatteryStatusSet)) {
return; // Not fully initialized yet.
@@ -392,10 +434,15 @@ public class BatterySaverStateMachine {
"Plugged in");
} else if (mSettingBatterySaverEnabledSticky && !mBatterySaverStickyBehaviourDisabled) {
- // Re-enable BS.
- enableBatterySaverLocked(/*enable=*/ true, /*manual=*/ true,
- BatterySaverController.REASON_STICKY_RESTORE,
- "Sticky restore");
+ if (mSettingBatterySaverStickyAutoDisableEnabled
+ && mBatteryLevel >= mSettingBatterySaverStickyAutoDisableThreshold) {
+ setStickyActive(false);
+ } else {
+ // Re-enable BS.
+ enableBatterySaverLocked(/*enable=*/ true, /*manual=*/ true,
+ BatterySaverController.REASON_STICKY_RESTORE,
+ "Sticky restore");
+ }
} else if (mSettingAutomaticBatterySaver
== PowerManager.POWER_SAVER_MODE_PERCENTAGE
@@ -483,12 +530,10 @@ public class BatterySaverStateMachine {
}
mSettingBatterySaverEnabled = enable;
- putGlobalSetting(Global.LOW_POWER_MODE, enable ? 1 : 0);
+ putGlobalSetting(Settings.Global.LOW_POWER_MODE, enable ? 1 : 0);
if (manual) {
- mSettingBatterySaverEnabledSticky = !mBatterySaverStickyBehaviourDisabled && enable;
- putGlobalSetting(Global.LOW_POWER_MODE_STICKY,
- mSettingBatterySaverEnabledSticky ? 1 : 0);
+ setStickyActive(!mBatterySaverStickyBehaviourDisabled && enable);
}
mBatterySaverController.enableBatterySaver(enable, intReason);
@@ -506,7 +551,8 @@ public class BatterySaverStateMachine {
}
}
- private void triggerDynamicModeNotification() {
+ @VisibleForTesting
+ void triggerDynamicModeNotification() {
NotificationManager manager = mContext.getSystemService(NotificationManager.class);
ensureNotificationChannelExists(manager);
@@ -553,14 +599,20 @@ public class BatterySaverStateMachine {
mBatterySaverSnoozing = snoozing;
}
+ private void setStickyActive(boolean active) {
+ mSettingBatterySaverEnabledSticky = active;
+ putGlobalSetting(Settings.Global.LOW_POWER_MODE_STICKY,
+ mSettingBatterySaverEnabledSticky ? 1 : 0);
+ }
+
@VisibleForTesting
protected void putGlobalSetting(String key, int value) {
- Global.putInt(mContext.getContentResolver(), key, value);
+ Settings.Global.putInt(mContext.getContentResolver(), key, value);
}
@VisibleForTesting
protected int getGlobalSetting(String key, int defValue) {
- return Global.getInt(mContext.getContentResolver(), key, defValue);
+ return Settings.Global.getInt(mContext.getContentResolver(), key, defValue);
}
public void dump(PrintWriter pw) {
@@ -597,6 +649,10 @@ public class BatterySaverStateMachine {
pw.println(mSettingBatterySaverEnabled);
pw.print(" mSettingBatterySaverEnabledSticky=");
pw.println(mSettingBatterySaverEnabledSticky);
+ pw.print(" mSettingBatterySaverStickyAutoDisableEnabled=");
+ pw.println(mSettingBatterySaverStickyAutoDisableEnabled);
+ pw.print(" mSettingBatterySaverStickyAutoDisableThreshold=");
+ pw.println(mSettingBatterySaverStickyAutoDisableThreshold);
pw.print(" mSettingBatterySaverTriggerThreshold=");
pw.println(mSettingBatterySaverTriggerThreshold);
pw.print(" mBatterySaverStickyBehaviourDisabled=");
@@ -628,6 +684,13 @@ public class BatterySaverStateMachine {
mSettingBatterySaverEnabledSticky);
proto.write(BatterySaverStateMachineProto.SETTING_BATTERY_SAVER_TRIGGER_THRESHOLD,
mSettingBatterySaverTriggerThreshold);
+ proto.write(
+ BatterySaverStateMachineProto.SETTING_BATTERY_SAVER_STICKY_AUTO_DISABLE_ENABLED,
+ mSettingBatterySaverStickyAutoDisableEnabled);
+ proto.write(
+ BatterySaverStateMachineProto
+ .SETTING_BATTERY_SAVER_STICKY_AUTO_DISABLE_THRESHOLD,
+ mSettingBatterySaverStickyAutoDisableThreshold);
proto.end(token);
}
diff --git a/services/core/java/com/android/server/role/RoleManagerService.java b/services/core/java/com/android/server/role/RoleManagerService.java
index c0517fdc99d0..1c7596b80fd7 100644
--- a/services/core/java/com/android/server/role/RoleManagerService.java
+++ b/services/core/java/com/android/server/role/RoleManagerService.java
@@ -198,6 +198,7 @@ public class RoleManagerService extends SystemService implements RoleUserState.C
// Make sure to implement LegacyRoleResolutionPolicy#getRoleHolders
// for a given role before adding a migration statement for it here
migrateRoleIfNecessary(RoleManager.ROLE_SMS, userId);
+ migrateRoleIfNecessary(RoleManager.ROLE_ASSISTANT, userId);
// Some vital packages state has changed since last role grant
// Run grants again
diff --git a/services/core/java/com/android/server/rollback/LocalIntentReceiver.java b/services/core/java/com/android/server/rollback/LocalIntentReceiver.java
new file mode 100644
index 000000000000..504a3496147c
--- /dev/null
+++ b/services/core/java/com/android/server/rollback/LocalIntentReceiver.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2018 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.rollback;
+
+import android.content.IIntentReceiver;
+import android.content.IIntentSender;
+import android.content.Intent;
+import android.content.IntentSender;
+import android.os.Bundle;
+import android.os.IBinder;
+
+import java.util.function.Consumer;
+
+/** {@code IntentSender} implementation for RollbackManager internal use. */
+class LocalIntentReceiver {
+ final Consumer<Intent> mConsumer;
+
+ LocalIntentReceiver(Consumer<Intent> consumer) {
+ mConsumer = consumer;
+ }
+
+ private IIntentSender.Stub mLocalSender = new IIntentSender.Stub() {
+ @Override
+ public void send(int code, Intent intent, String resolvedType, IBinder whitelistToken,
+ IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) {
+ mConsumer.accept(intent);
+ }
+ };
+
+ public IntentSender getIntentSender() {
+ return new IntentSender((IIntentSender) mLocalSender);
+ }
+}
diff --git a/services/core/java/com/android/server/rollback/RollbackData.java b/services/core/java/com/android/server/rollback/RollbackData.java
index 4015c4f7b391..a4f306489c60 100644
--- a/services/core/java/com/android/server/rollback/RollbackData.java
+++ b/services/core/java/com/android/server/rollback/RollbackData.java
@@ -49,6 +49,14 @@ class RollbackData {
*/
public Instant timestamp;
+ /**
+ * Whether this Rollback is currently in progress. This field is true from the point
+ * we commit a {@code PackageInstaller} session containing these packages to the point the
+ * {@code PackageInstaller} calls into the {@code onFinished} callback.
+ */
+ // NOTE: All accesses to this field are from the RollbackManager handler thread.
+ public boolean inProgress = false;
+
RollbackData(int rollbackId, File backupDir) {
this.rollbackId = rollbackId;
this.backupDir = backupDir;
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
index 7f515bf63bc1..8b4c410000bb 100644
--- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
+++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
@@ -19,8 +19,6 @@ package com.android.server.rollback;
import android.app.AppOpsManager;
import android.content.BroadcastReceiver;
import android.content.Context;
-import android.content.IIntentReceiver;
-import android.content.IIntentSender;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.IntentSender;
@@ -36,11 +34,9 @@ import android.content.rollback.IRollbackManager;
import android.content.rollback.PackageRollbackInfo;
import android.content.rollback.RollbackInfo;
import android.os.Binder;
-import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.HandlerThread;
-import android.os.IBinder;
import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.os.storage.StorageManager;
@@ -66,7 +62,6 @@ import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
-import java.util.function.Consumer;
/**
* Implementation of service that manages APK level rollbacks.
@@ -116,6 +111,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
private final Context mContext;
private final HandlerThread mHandlerThread;
private final Installer mInstaller;
+ private final RollbackPackageHealthObserver mPackageHealthObserver;
RollbackManagerServiceImpl(Context context) {
mContext = context;
@@ -128,6 +124,8 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
mRollbackStore = new RollbackStore(new File(Environment.getDataDirectory(), "rollback"));
+ mPackageHealthObserver = new RollbackPackageHealthObserver(mContext);
+
// Kick off loading of the rollback data from strorage in a background
// thread.
// TODO: Consider loading the rollback data directly here instead, to
@@ -285,14 +283,19 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
Log.i(TAG, "Initiating rollback of " + targetPackageName);
// Get the latest RollbackData for the target package.
- RollbackData data = getRollbackForPackage(targetPackageName);
+ final RollbackData data = getRollbackForPackage(targetPackageName);
if (data == null) {
sendFailure(statusReceiver, "No rollback available for package.");
return;
}
if (data.rollbackId != rollback.getRollbackId()) {
- sendFailure(statusReceiver, "Rollback for package is out of date");
+ sendFailure(statusReceiver, "Rollback for package is out of date.");
+ return;
+ }
+
+ if (data.inProgress) {
+ sendFailure(statusReceiver, "Rollback for package is already in progress.");
return;
}
@@ -371,27 +374,36 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
final LocalIntentReceiver receiver = new LocalIntentReceiver(
(Intent result) -> {
- int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
- PackageInstaller.STATUS_FAILURE);
- if (status != PackageInstaller.STATUS_SUCCESS) {
- sendFailure(statusReceiver, "Rollback downgrade install failed: "
- + result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE));
- return;
- }
-
- addRecentlyExecutedRollback(rollback);
- sendSuccess(statusReceiver);
-
- Intent broadcast = new Intent(Intent.ACTION_PACKAGE_ROLLBACK_EXECUTED);
-
- // TODO: This call emits the warning "Calling a method in the
- // system process without a qualified user". Fix that.
- // TODO: Limit this to receivers holding the
- // MANAGE_ROLLBACKS permission?
- mContext.sendBroadcast(broadcast);
+ getHandler().post(() -> {
+ // We've now completed the rollback, so we mark it as no longer in
+ // progress.
+ data.inProgress = false;
+
+ int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
+ PackageInstaller.STATUS_FAILURE);
+ if (status != PackageInstaller.STATUS_SUCCESS) {
+ sendFailure(statusReceiver,
+ "Rollback downgrade install failed: "
+ + result.getStringExtra(
+ PackageInstaller.EXTRA_STATUS_MESSAGE));
+ return;
+ }
+
+ addRecentlyExecutedRollback(rollback);
+ sendSuccess(statusReceiver);
+
+ Intent broadcast = new Intent(Intent.ACTION_PACKAGE_ROLLBACK_EXECUTED);
+
+ // TODO: This call emits the warning "Calling a method in the
+ // system process without a qualified user". Fix that.
+ // TODO: Limit this to receivers holding the
+ // MANAGE_ROLLBACKS permission?
+ mContext.sendBroadcast(broadcast);
+ });
}
);
+ data.inProgress = true;
parentSession.commit(receiver.getIntentSender());
} catch (IOException e) {
Log.e(TAG, "Unable to roll back " + targetPackageName, e);
@@ -774,10 +786,15 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
getHandler().post(() -> {
PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class);
- // TODO(narayan): Should we make sure we're in the middle of a session commit for a
- // a package with this package name ? Otherwise it's possible we may roll back data
- // for some other downgrade.
- if (getRollbackForPackage(packageName) == null) {
+ final RollbackData rollbackData = getRollbackForPackage(packageName);
+ if (rollbackData == null) {
+ pmi.finishPackageInstall(token, false);
+ return;
+ }
+
+ if (!rollbackData.inProgress) {
+ Log.e(TAG, "Request to restore userData for: " + packageName
+ + ", but no rollback in progress.");
pmi.finishPackageInstall(token, false);
return;
}
@@ -805,26 +822,6 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
});
}
- private class LocalIntentReceiver {
- final Consumer<Intent> mConsumer;
-
- LocalIntentReceiver(Consumer<Intent> consumer) {
- mConsumer = consumer;
- }
-
- private IIntentSender.Stub mLocalSender = new IIntentSender.Stub() {
- @Override
- public void send(int code, Intent intent, String resolvedType, IBinder whitelistToken,
- IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) {
- getHandler().post(() -> mConsumer.accept(intent));
- }
- };
-
- public IntentSender getIntentSender() {
- return new IntentSender((IIntentSender) mLocalSender);
- }
- }
-
/**
* Gets the version of the package currently installed.
* Returns null if the package is not currently installed.
@@ -891,7 +888,17 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
ensureRollbackDataLoadedLocked();
mAvailableRollbacks.add(data);
}
-
+ // TODO(zezeozue): Provide API to explicitly start observing instead
+ // of doing this for all rollbacks. If we do this for all rollbacks,
+ // should document in PackageInstaller.SessionParams#setEnableRollback
+ // After enabling and commiting any rollback, observe packages and
+ // prepare to rollback if packages crashes too frequently.
+ List<String> packages = new ArrayList<>();
+ for (int i = 0; i < data.packages.size(); i++) {
+ packages.add(data.packages.get(i).getPackageName());
+ }
+ mPackageHealthObserver.startObservingHealth(packages,
+ ROLLBACK_LIFETIME_DURATION_MILLIS);
scheduleExpiration(ROLLBACK_LIFETIME_DURATION_MILLIS);
} catch (IOException e) {
Log.e(TAG, "Unable to enable rollback", e);
diff --git a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
new file mode 100644
index 000000000000..1f2f1ccd7383
--- /dev/null
+++ b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.rollback;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageInstaller;
+import android.content.rollback.RollbackInfo;
+import android.content.rollback.RollbackManager;
+import android.os.Handler;
+import android.os.HandlerThread;
+
+import com.android.server.PackageWatchdog;
+import com.android.server.PackageWatchdog.PackageHealthObserver;
+
+import java.util.List;
+
+/**
+ * {@code PackageHealthObserver} for {@code RollbackManagerService}.
+ *
+ * @hide
+ */
+public final class RollbackPackageHealthObserver implements PackageHealthObserver {
+ private static final String TAG = "RollbackPackageHealthObserver";
+ private static final String NAME = "rollback-observer";
+ private Context mContext;
+ private Handler mHandler;
+
+ RollbackPackageHealthObserver(Context context) {
+ mContext = context;
+ HandlerThread handlerThread = new HandlerThread("RollbackPackageHealthObserver");
+ handlerThread.start();
+ mHandler = handlerThread.getThreadHandler();
+ PackageWatchdog.getInstance(mContext).registerHealthObserver(this);
+ }
+
+ @Override
+ public boolean onHealthCheckFailed(String packageName) {
+ RollbackManager rollbackManager = mContext.getSystemService(RollbackManager.class);
+ RollbackInfo rollback = rollbackManager.getAvailableRollback(packageName);
+ if (rollback != null) {
+ // TODO(zezeozue): Only rollback if rollback version == failed package version
+ mHandler.post(() -> executeRollback(rollbackManager, rollback));
+ return true;
+ }
+ // Don't handle the notification, no rollbacks available
+ return false;
+ }
+
+ /**
+ * Start observing health of {@code packages} for {@code durationMs}.
+ * This may cause {@code packages} to be rolled back if they crash too freqeuntly.
+ */
+ public void startObservingHealth(List<String> packages, long durationMs) {
+ PackageWatchdog.getInstance(mContext).startObservingHealth(this, packages, durationMs);
+ }
+
+ private void executeRollback(RollbackManager manager, RollbackInfo rollback) {
+ // TODO(zezeozue): Log initiated metrics
+ LocalIntentReceiver rollbackReceiver = new LocalIntentReceiver((Intent result) -> {
+ mHandler.post(() -> {
+ int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
+ PackageInstaller.STATUS_FAILURE);
+ if (status == PackageInstaller.STATUS_SUCCESS) {
+ // TODO(zezeozue); Log success metrics
+ // Rolledback successfully, no action required by other observers
+ } else {
+ // TODO(zezeozue); Log failure metrics
+ // Rollback failed other observers should have a shot
+ }
+ });
+ });
+ manager.executeRollback(rollback, rollbackReceiver.getIntentSender());
+ }
+
+ @Override
+ public String getName() {
+ return NAME;
+ }
+}
diff --git a/services/core/java/com/android/server/signedconfig/SignatureVerifier.java b/services/core/java/com/android/server/signedconfig/SignatureVerifier.java
index 56db32a3071d..146c51688531 100644
--- a/services/core/java/com/android/server/signedconfig/SignatureVerifier.java
+++ b/services/core/java/com/android/server/signedconfig/SignatureVerifier.java
@@ -41,8 +41,8 @@ public class SignatureVerifier {
private static final boolean DBG = false;
private static final String DEBUG_KEY =
- "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEaAn2XVifsLTHg616nTsOMVmlhBoECGbTEBTKKvdd2hO60"
- + "pj1pnU8SMkhYfaNxZuKgw9LNvOwlFwStboIYeZ3lQ==";
+ "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEmJKs4lSn+XRhMQmMid+Zbhbu13YrU1haIhVC5296InRu1"
+ + "x7A8PV1ejQyisBODGgRY6pqkAHRncBCYcgg5wIIJg==";
private static final String PROD_KEY =
"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE+lky6wKyGL6lE1VrD0YTMHwb0Xwc+tzC8MvnrzVxodvTp"
+ "VY/jV7V+Zktcx+pry43XPABFRXtbhTo+qykhyBA1g==";
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index 40664fef2825..acede7d4fa90 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -1705,6 +1705,27 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
}
}
+ private void pullTemperature(int tagId, long elapsedNanos, long wallClockNanos,
+ List<StatsLogEventWrapper> pulledData) {
+ long callingToken = Binder.clearCallingIdentity();
+ try {
+ List<Temperature> temperatures = sThermalService.getCurrentTemperatures();
+ for (Temperature temp : temperatures) {
+ StatsLogEventWrapper e =
+ new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
+ e.writeInt(temp.getType());
+ e.writeString(temp.getName());
+ e.writeInt((int) (temp.getValue() * 10));
+ pulledData.add(e);
+ }
+ } catch (RemoteException e) {
+ // Should not happen.
+ Slog.e(TAG, "Disconnected from thermal service. Cannot pull temperatures.");
+ } finally {
+ Binder.restoreCallingIdentity(callingToken);
+ }
+ }
+
/**
* Pulls various data.
*/
@@ -1867,6 +1888,10 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
pullDeviceCalculatedPowerBlameOther(tagId, elapsedNanos, wallClockNanos, ret);
break;
}
+ case StatsLog.TEMPERATURE: {
+ pullTemperature(tagId, elapsedNanos, wallClockNanos, ret);
+ break;
+ }
default:
Slog.w(TAG, "No such tagId data as " + tagId);
return null;
diff --git a/services/core/java/com/android/server/uri/UriGrantsManagerService.java b/services/core/java/com/android/server/uri/UriGrantsManagerService.java
index 9a7e75e548af..744efab7d78d 100644
--- a/services/core/java/com/android/server/uri/UriGrantsManagerService.java
+++ b/services/core/java/com/android/server/uri/UriGrantsManagerService.java
@@ -1129,7 +1129,8 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub {
* In this case, we grant a uri permission, even if the ContentProvider does not normally
* grant uri permissions.
*/
- boolean specialCrossUserGrant = UserHandle.getUserId(targetUid) != grantUri.sourceUserId
+ boolean specialCrossUserGrant = targetUid >= 0
+ && UserHandle.getUserId(targetUid) != grantUri.sourceUserId
&& checkHoldingPermissionsInternal(pm, pi, grantUri, callingUid,
modeFlags, false /*without considering the uid permissions*/);
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index d36e545aa74f..3a077b86b89a 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -747,12 +747,23 @@ class ActivityStarter {
abort |= !mService.mIntentFirewall.checkStartActivity(intent, callingUid,
callingPid, resolvedType, aInfo.applicationInfo);
- // not sure if we need to create START_ABORTED_BACKGROUND so for now piggybacking
- // on START_ABORTED
+ boolean abortBackgroundStart = false;
if (!abort) {
- abort |= shouldAbortBackgroundActivityStart(callingUid, callingPid, callingPackage,
- realCallingUid, callerApp, originatingPendingIntent,
+ abortBackgroundStart = shouldAbortBackgroundActivityStart(callingUid, callingPid,
+ callingPackage, realCallingUid, callerApp, originatingPendingIntent,
allowBackgroundActivityStart, intent);
+ abort |= (abortBackgroundStart && !mService.isBackgroundActivityStartsEnabled());
+ // TODO: remove this toast after feature development is done
+ if (abortBackgroundStart) {
+ final String toastMsg = abort
+ ? "Background activity start from " + callingPackage
+ + " blocked. See go/q-bg-block."
+ : "This background activity start from " + callingPackage
+ + " will be blocked in future Q builds. See go/q-bg-block.";
+ mService.mUiHandler.post(() -> {
+ Toast.makeText(mService.mContext, toastMsg, Toast.LENGTH_LONG).show();
+ });
+ }
}
// Merge the two options bundles, while realCallerOptions takes precedence.
@@ -798,8 +809,6 @@ class ActivityStarter {
// We pretend to the caller that it was really started, but
// they will just get a cancel result.
ActivityOptions.abort(checkedOptions);
- maybeLogActivityStart(callingUid, callingPackage, realCallingUid, intent, callerApp,
- null /*r*/, originatingPendingIntent, true /*abortedStart*/);
return START_ABORTED;
}
@@ -892,8 +901,11 @@ class ActivityStarter {
mService.onStartActivitySetDidAppSwitch();
mController.doPendingActivityLaunches(false);
- maybeLogActivityStart(callingUid, callingPackage, realCallingUid, intent, callerApp, r,
- originatingPendingIntent, false /*abortedStart*/);
+ // maybe log to TRON, but only if we haven't already in shouldAbortBackgroundActivityStart()
+ if (!abortBackgroundStart) {
+ maybeLogActivityStart(callingUid, callingPackage, realCallingUid, intent, callerApp, r,
+ originatingPendingIntent, false /*abortedStart*/);
+ }
return startActivity(r, sourceRecord, voiceSession, voiceInteractor, startFlags,
true /* doResume */, checkedOptions, inTask, outActivity);
@@ -903,11 +915,9 @@ class ActivityStarter {
final String callingPackage, int realCallingUid, WindowProcessController callerApp,
PendingIntentRecord originatingPendingIntent, boolean allowBackgroundActivityStart,
Intent intent) {
- if (mService.isBackgroundActivityStartsEnabled()) {
- return false;
- }
// don't abort for the most important UIDs
- if (callingUid == Process.ROOT_UID || callingUid == Process.SYSTEM_UID) {
+ if (callingUid == Process.ROOT_UID || callingUid == Process.SYSTEM_UID
+ || callingUid == Process.NFC_UID) {
return false;
}
// don't abort if the callerApp has any visible activity
@@ -915,14 +925,15 @@ class ActivityStarter {
return false;
}
// don't abort if the callingUid is in the foreground or is a persistent system process
- final boolean isCallingUidForeground = isUidForeground(callingUid);
+ final boolean isCallingUidForeground = mService.isUidForeground(callingUid);
final boolean isCallingUidPersistentSystemProcess = isUidPersistentSystemProcess(
callingUid);
if (isCallingUidForeground || isCallingUidPersistentSystemProcess) {
return false;
}
// take realCallingUid into consideration
- final boolean isRealCallingUidForeground = isUidForeground(realCallingUid);
+ final boolean isRealCallingUidForeground = mService.isUidForeground(
+ realCallingUid);
final boolean isRealCallingUidPersistentSystemProcess = isUidPersistentSystemProcess(
realCallingUid);
if (realCallingUid != callingUid) {
@@ -949,8 +960,8 @@ class ActivityStarter {
if (mSupervisor.mRecentTasks.isCallerRecents(callingUid)) {
return false;
}
- // anything that has fallen through will currently be aborted
- Slog.w(TAG, "Blocking background activity start [callingPackage: " + callingPackage
+ // anything that has fallen through would currently be aborted
+ Slog.w(TAG, "Background activity start [callingPackage: " + callingPackage
+ "; callingUid: " + callingUid
+ "; isCallingUidForeground: " + isCallingUidForeground
+ "; isCallingUidPersistentSystemProcess: " + isCallingUidPersistentSystemProcess
@@ -962,21 +973,11 @@ class ActivityStarter {
+ "; isBgStartWhitelisted: " + allowBackgroundActivityStart
+ "; intent: " + intent
+ "]");
- // TODO: remove this toast after feature development is done
- mService.mUiHandler.post(() -> {
- Toast.makeText(mService.mContext,
- "Blocking background activity start for " + callingPackage,
- Toast.LENGTH_SHORT).show();
- });
+ maybeLogActivityStart(callingUid, callingPackage, realCallingUid, intent, callerApp,
+ null /*r*/, originatingPendingIntent, true /*abortedStart*/);
return true;
}
- /** Returns true if uid has a visible window or its process is in a top state. */
- private boolean isUidForeground(int uid) {
- return (mService.getUidStateLocked(uid) == ActivityManager.PROCESS_STATE_TOP)
- || mService.mWindowManager.mRoot.isAnyNonToastWindowVisibleForUid(uid);
- }
-
/** Returns true if uid is in a persistent state. */
private boolean isUidPersistentSystemProcess(int uid) {
return (mService.getUidStateLocked(uid) <= ActivityManager.PROCESS_STATE_PERSISTENT_UI);
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index 67b00b2cfbf1..1a5e6a14e733 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -489,4 +489,7 @@ public abstract class ActivityTaskManagerInternal {
*/
public abstract ActivityManager.TaskSnapshot getTaskSnapshot(int taskId,
boolean reducedResolution);
+
+ /** Returns true if uid has a visible window or its process is in a top state. */
+ public abstract boolean isUidForeground(int uid);
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 2affa974e999..e5a66cb4238f 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -5632,6 +5632,11 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
return mActiveUids.get(uid, PROCESS_STATE_NONEXISTENT);
}
+ boolean isUidForeground(int uid) {
+ return (getUidStateLocked(uid) == ActivityManager.PROCESS_STATE_TOP)
+ || mWindowManager.mRoot.isAnyNonToastWindowVisibleForUid(uid);
+ }
+
/**
* @return whitelist tag for a uid from mPendingTempWhitelist, null if not currently on
* the whitelist
@@ -6222,30 +6227,27 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
return;
}
- mH.post(() -> {
- synchronized (mGlobalLock) {
- final ActivityDisplay activityDisplay =
- mRootActivityContainer.getActivityDisplay(displayId);
- if (activityDisplay == null) {
- // Call might come when display is not yet added or has been removed.
- if (DEBUG_CONFIGURATION) {
- Slog.w(TAG, "Trying to update display configuration for non-existing "
- + "displayId=" + displayId);
- }
- return;
+ synchronized (mGlobalLock) {
+ final ActivityDisplay activityDisplay =
+ mRootActivityContainer.getActivityDisplay(displayId);
+ if (activityDisplay == null) {
+ // Call might come when display is not yet added or has been removed.
+ if (DEBUG_CONFIGURATION) {
+ Slog.w(TAG, "Trying to update display configuration for non-existing "
+ + "displayId=" + displayId);
}
- final WindowProcessController process = mPidMap.get(pid);
- if (process == null) {
- if (DEBUG_CONFIGURATION) {
- Slog.w(TAG, "Trying to update display configuration for invalid "
- + "process, pid=" + pid);
- }
- return;
+ return;
+ }
+ final WindowProcessController process = mPidMap.get(pid);
+ if (process == null) {
+ if (DEBUG_CONFIGURATION) {
+ Slog.w(TAG, "Trying to update display configuration for invalid "
+ + "process, pid=" + pid);
}
- process.registerDisplayConfigurationListenerLocked(activityDisplay);
+ return;
}
- });
-
+ process.registerDisplayConfigurationListenerLocked(activityDisplay);
+ }
}
@Override
@@ -7044,5 +7046,12 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
return ActivityTaskManagerService.this.getTaskSnapshot(taskId, reducedResolution);
}
}
+
+ @Override
+ public boolean isUidForeground(int uid) {
+ synchronized (mGlobalLock) {
+ return ActivityTaskManagerService.this.isUidForeground(uid);
+ }
+ }
}
}
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index 5f393ef59057..f3a363a30cf8 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -174,6 +174,7 @@ public class AppTransition implements Dump {
private int mLastUsedAppTransition = TRANSIT_UNSET;
private String mLastOpeningApp;
private String mLastClosingApp;
+ private String mLastChangingApp;
private static final int NEXT_TRANSIT_TYPE_NONE = 0;
private static final int NEXT_TRANSIT_TYPE_CUSTOM = 1;
@@ -318,14 +319,16 @@ public class AppTransition implements Dump {
private void setAppTransition(int transit, int flags) {
mNextAppTransition = transit;
mNextAppTransitionFlags |= flags;
- setLastAppTransition(TRANSIT_UNSET, null, null);
+ setLastAppTransition(TRANSIT_UNSET, null, null, null);
updateBooster();
}
- void setLastAppTransition(int transit, AppWindowToken openingApp, AppWindowToken closingApp) {
+ void setLastAppTransition(int transit, AppWindowToken openingApp, AppWindowToken closingApp,
+ AppWindowToken changingApp) {
mLastUsedAppTransition = transit;
mLastOpeningApp = "" + openingApp;
mLastClosingApp = "" + closingApp;
+ mLastChangingApp = "" + changingApp;
}
boolean isReady() {
@@ -412,9 +415,7 @@ public class AppTransition implements Dump {
* @return bit-map of WindowManagerPolicy#FINISH_LAYOUT_REDO_* to indicate whether another
* layout pass needs to be done
*/
- int goodToGo(int transit, AppWindowToken topOpeningApp,
- AppWindowToken topClosingApp, ArraySet<AppWindowToken> openingApps,
- ArraySet<AppWindowToken> closingApps) {
+ int goodToGo(int transit, AppWindowToken topOpeningApp, ArraySet<AppWindowToken> openingApps) {
mNextAppTransition = TRANSIT_UNSET;
mNextAppTransitionFlags = 0;
setAppTransitionState(APP_STATE_RUNNING);
@@ -422,8 +423,6 @@ public class AppTransition implements Dump {
? topOpeningApp.getAnimation()
: null;
int redoLayout = notifyAppTransitionStartingLocked(transit,
- topOpeningApp != null ? topOpeningApp.token : null,
- topClosingApp != null ? topClosingApp.token : null,
topOpeningAnim != null ? topOpeningAnim.getDurationHint() : 0,
topOpeningAnim != null
? topOpeningAnim.getStatusBarTransitionsStartTime()
@@ -500,13 +499,12 @@ public class AppTransition implements Dump {
}
}
- private int notifyAppTransitionStartingLocked(int transit, IBinder openToken,
- IBinder closeToken, long duration, long statusBarAnimationStartTime,
- long statusBarAnimationDuration) {
+ private int notifyAppTransitionStartingLocked(int transit, long duration,
+ long statusBarAnimationStartTime, long statusBarAnimationDuration) {
int redoLayout = 0;
for (int i = 0; i < mListeners.size(); i++) {
- redoLayout |= mListeners.get(i).onAppTransitionStartingLocked(transit, openToken,
- closeToken, duration, statusBarAnimationStartTime, statusBarAnimationDuration);
+ redoLayout |= mListeners.get(i).onAppTransitionStartingLocked(transit, duration,
+ statusBarAnimationStartTime, statusBarAnimationDuration);
}
return redoLayout;
}
@@ -2128,6 +2126,8 @@ public class AppTransition implements Dump {
pw.println(mLastOpeningApp);
pw.print(prefix); pw.print("mLastClosingApp=");
pw.println(mLastClosingApp);
+ pw.print(prefix); pw.print("mLastChangingApp=");
+ pw.println(mLastChangingApp);
}
}
@@ -2226,14 +2226,16 @@ public class AppTransition implements Dump {
if (dc == null) {
return;
}
- if (isTransitionSet() || !dc.mOpeningApps.isEmpty() || !dc.mClosingApps.isEmpty()) {
+ if (isTransitionSet() || !dc.mOpeningApps.isEmpty() || !dc.mClosingApps.isEmpty()
+ || !dc.mChangingApps.isEmpty()) {
if (DEBUG_APP_TRANSITIONS) {
Slog.v(TAG_WM, "*** APP TRANSITION TIMEOUT."
+ " displayId=" + dc.getDisplayId()
+ " isTransitionSet()="
+ dc.mAppTransition.isTransitionSet()
+ " mOpeningApps.size()=" + dc.mOpeningApps.size()
- + " mClosingApps.size()=" + dc.mClosingApps.size());
+ + " mClosingApps.size()=" + dc.mClosingApps.size()
+ + " mChangingApps.size()=" + dc.mChangingApps.size());
}
setTimeout();
mService.mWindowPlacerLocked.performSurfacePlacement();
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index bf00ffb30222..49308b8f92b4 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -49,8 +49,8 @@ import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIO
import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-import static com.android.server.wm.WindowManagerService.H.NOTIFY_APP_TRANSITION_STARTING;
+import android.os.SystemClock;
import android.os.Trace;
import android.util.ArraySet;
import android.util.Slog;
@@ -89,8 +89,9 @@ public class AppTransitionController {
* Handle application transition for given display.
*/
void handleAppTransitionReady() {
- final int appsCount = mDisplayContent.mOpeningApps.size();
- if (!transitionGoodToGo(appsCount, mTempTransitionReasons)) {
+ mTempTransitionReasons.clear();
+ if (!transitionGoodToGo(mDisplayContent.mOpeningApps, mTempTransitionReasons)
+ || !transitionGoodToGo(mDisplayContent.mChangingApps, mTempTransitionReasons)) {
return;
}
Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "AppTransitionReady");
@@ -108,21 +109,25 @@ public class AppTransitionController {
mDisplayContent.mWallpaperMayChange = false;
- int i;
- for (i = 0; i < appsCount; i++) {
- final AppWindowToken wtoken = mDisplayContent.mOpeningApps.valueAt(i);
+ int appCount = mDisplayContent.mOpeningApps.size();
+ for (int i = 0; i < appCount; ++i) {
// Clearing the mAnimatingExit flag before entering animation. It's set to true if app
// window is removed, or window relayout to invisible. This also affects window
// visibility. We need to clear it *before* maybeUpdateTransitToWallpaper() as the
// transition selection depends on wallpaper target visibility.
- wtoken.clearAnimatingFlags();
+ mDisplayContent.mOpeningApps.valueAtUnchecked(i).clearAnimatingFlags();
+ }
+ appCount = mDisplayContent.mChangingApps.size();
+ for (int i = 0; i < appCount; ++i) {
+ // Clearing for same reason as above.
+ mDisplayContent.mChangingApps.valueAtUnchecked(i).clearAnimatingFlags();
}
// Adjust wallpaper before we pull the lower/upper target, since pending changes
// (like the clearAnimatingFlags() above) might affect wallpaper target result.
// Or, the opening app window should be a wallpaper target.
mWallpaperControllerLocked.adjustWallpaperWindowsForAppTransitionIfNeeded(
- mDisplayContent.mOpeningApps);
+ mDisplayContent.mOpeningApps, mDisplayContent.mChangingApps);
// Determine if closing and opening app token sets are wallpaper targets, in which case
// special animations are needed.
@@ -141,7 +146,7 @@ public class AppTransitionController {
// no need to do an animation. This is the case, for example, when this transition is being
// done behind a dream window.
final ArraySet<Integer> activityTypes = collectActivityTypes(mDisplayContent.mOpeningApps,
- mDisplayContent.mClosingApps);
+ mDisplayContent.mClosingApps, mDisplayContent.mChangingApps);
final boolean allowAnimations = mDisplayContent.getDisplayPolicy().allowAppAnimationsLw();
final AppWindowToken animLpToken = allowAnimations
? findAnimLayoutParamsToken(transit, activityTypes)
@@ -152,11 +157,15 @@ public class AppTransitionController {
final AppWindowToken topClosingApp = allowAnimations
? getTopApp(mDisplayContent.mClosingApps, false /* ignoreHidden */)
: null;
+ final AppWindowToken topChangingApp = allowAnimations
+ ? getTopApp(mDisplayContent.mChangingApps, false /* ignoreHidden */)
+ : null;
final WindowManager.LayoutParams animLp = getAnimLp(animLpToken);
overrideWithRemoteAnimationIfSet(animLpToken, transit, activityTypes);
final boolean voiceInteraction = containsVoiceInteraction(mDisplayContent.mOpeningApps)
- || containsVoiceInteraction(mDisplayContent.mOpeningApps);
+ || containsVoiceInteraction(mDisplayContent.mOpeningApps)
+ || containsVoiceInteraction(mDisplayContent.mChangingApps);
final int layoutRedo;
mService.mSurfaceAnimationRunner.deferStartingAnimations();
@@ -165,13 +174,14 @@ public class AppTransitionController {
handleClosingApps(transit, animLp, voiceInteraction);
handleOpeningApps(transit, animLp, voiceInteraction);
+ handleChangingApps(transit, animLp, voiceInteraction);
appTransition.setLastAppTransition(transit, topOpeningApp,
- topClosingApp);
+ topClosingApp, topChangingApp);
final int flags = appTransition.getTransitFlags();
layoutRedo = appTransition.goodToGo(transit, topOpeningApp,
- topClosingApp, mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps);
+ mDisplayContent.mOpeningApps);
handleNonAppWindowsInTransition(transit, flags);
appTransition.postAnimationCallback();
appTransition.clear();
@@ -183,6 +193,7 @@ public class AppTransitionController {
mDisplayContent.mOpeningApps.clear();
mDisplayContent.mClosingApps.clear();
+ mDisplayContent.mChangingApps.clear();
mDisplayContent.mUnknownAppVisibilityController.clear();
// This has changed the visibility of windows, so perform
@@ -191,8 +202,8 @@ public class AppTransitionController {
mDisplayContent.computeImeTarget(true /* updateImeTarget */);
- mService.mH.obtainMessage(NOTIFY_APP_TRANSITION_STARTING,
- mTempTransitionReasons.clone()).sendToTarget();
+ mService.mAtmInternal.notifyAppTransitionStarting(mTempTransitionReasons.clone(),
+ SystemClock.uptimeMillis());
Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
@@ -237,29 +248,30 @@ public class AppTransitionController {
AppWindowToken result;
final ArraySet<AppWindowToken> closingApps = mDisplayContent.mClosingApps;
final ArraySet<AppWindowToken> openingApps = mDisplayContent.mOpeningApps;
+ final ArraySet<AppWindowToken> changingApps = mDisplayContent.mChangingApps;
// Remote animations always win, but fullscreen tokens override non-fullscreen tokens.
- result = lookForHighestTokenWithFilter(closingApps, openingApps,
+ result = lookForHighestTokenWithFilter(closingApps, openingApps, changingApps,
w -> w.getRemoteAnimationDefinition() != null
&& w.getRemoteAnimationDefinition().hasTransition(transit, activityTypes));
if (result != null) {
return result;
}
- result = lookForHighestTokenWithFilter(closingApps, openingApps,
+ result = lookForHighestTokenWithFilter(closingApps, openingApps, changingApps,
w -> w.fillsParent() && w.findMainWindow() != null);
if (result != null) {
return result;
}
- return lookForHighestTokenWithFilter(closingApps, openingApps,
+ return lookForHighestTokenWithFilter(closingApps, openingApps, changingApps,
w -> w.findMainWindow() != null);
}
/**
* @return The set of {@link android.app.WindowConfiguration.ActivityType}s contained in the set
- * of apps in {@code array1} and {@code array2}.
+ * of apps in {@code array1}, {@code array2}, and {@code array3}.
*/
private static ArraySet<Integer> collectActivityTypes(ArraySet<AppWindowToken> array1,
- ArraySet<AppWindowToken> array2) {
+ ArraySet<AppWindowToken> array2, ArraySet<AppWindowToken> array3) {
final ArraySet<Integer> result = new ArraySet<>();
for (int i = array1.size() - 1; i >= 0; i--) {
result.add(array1.valueAt(i).getActivityType());
@@ -267,19 +279,26 @@ public class AppTransitionController {
for (int i = array2.size() - 1; i >= 0; i--) {
result.add(array2.valueAt(i).getActivityType());
}
+ for (int i = array3.size() - 1; i >= 0; i--) {
+ result.add(array3.valueAt(i).getActivityType());
+ }
return result;
}
private static AppWindowToken lookForHighestTokenWithFilter(ArraySet<AppWindowToken> array1,
- ArraySet<AppWindowToken> array2, Predicate<AppWindowToken> filter) {
- final int array1count = array1.size();
- final int count = array1count + array2.size();
+ ArraySet<AppWindowToken> array2, ArraySet<AppWindowToken> array3,
+ Predicate<AppWindowToken> filter) {
+ final int array2base = array1.size();
+ final int array3base = array2.size() + array2base;
+ final int count = array3base + array3.size();
int bestPrefixOrderIndex = Integer.MIN_VALUE;
AppWindowToken bestToken = null;
for (int i = 0; i < count; i++) {
- final AppWindowToken wtoken = i < array1count
+ final AppWindowToken wtoken = i < array2base
? array1.valueAt(i)
- : array2.valueAt(i - array1count);
+ : (i < array3base
+ ? array2.valueAt(i - array2base)
+ : array3.valueAt(i - array3base));
final int prefixOrderIndex = wtoken.getPrefixOrderIndex();
if (filter.test(wtoken) && prefixOrderIndex > bestPrefixOrderIndex) {
bestPrefixOrderIndex = prefixOrderIndex;
@@ -360,6 +379,24 @@ public class AppTransitionController {
}
}
+ private void handleChangingApps(int transit, LayoutParams animLp, boolean voiceInteraction) {
+ final ArraySet<AppWindowToken> apps = mDisplayContent.mChangingApps;
+ final int appsCount = apps.size();
+ for (int i = 0; i < appsCount; i++) {
+ AppWindowToken wtoken = apps.valueAt(i);
+ if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Now changing app" + wtoken);
+ wtoken.cancelAnimationOnly();
+ wtoken.applyAnimationLocked(null, transit, true, false);
+ wtoken.updateReportedVisibilityLocked();
+ mService.openSurfaceTransaction();
+ try {
+ wtoken.showAllWindowsLocked();
+ } finally {
+ mService.closeSurfaceTransaction("handleChangingApps");
+ }
+ }
+ }
+
private void handleNonAppWindowsInTransition(int transit, int flags) {
if (transit == TRANSIT_KEYGUARD_GOING_AWAY) {
if ((flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER) != 0
@@ -379,16 +416,15 @@ public class AppTransitionController {
}
}
- private boolean transitionGoodToGo(int appsCount, SparseIntArray outReasons) {
+ private boolean transitionGoodToGo(ArraySet<AppWindowToken> apps, SparseIntArray outReasons) {
if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
- "Checking " + appsCount + " opening apps (frozen="
+ "Checking " + apps.size() + " opening apps (frozen="
+ mService.mDisplayFrozen + " timeout="
+ mDisplayContent.mAppTransition.isTimeout() + ")...");
final ScreenRotationAnimation screenRotationAnimation =
mService.mAnimator.getScreenRotationAnimationLocked(
Display.DEFAULT_DISPLAY);
- outReasons.clear();
if (!mDisplayContent.mAppTransition.isTimeout()) {
// Imagine the case where we are changing orientation due to an app transition, but a
// previous orientation change is still in progress. We won't process the orientation
@@ -404,8 +440,8 @@ public class AppTransitionController {
}
return false;
}
- for (int i = 0; i < appsCount; i++) {
- AppWindowToken wtoken = mDisplayContent.mOpeningApps.valueAt(i);
+ for (int i = 0; i < apps.size(); i++) {
+ AppWindowToken wtoken = apps.valueAt(i);
if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
"Check opening app=" + wtoken + ": allDrawn="
+ wtoken.allDrawn + " startingDisplayed="
diff --git a/services/core/java/com/android/server/wm/AppWindowThumbnail.java b/services/core/java/com/android/server/wm/AppWindowThumbnail.java
index bb38f3035a6c..29645f68b2fb 100644
--- a/services/core/java/com/android/server/wm/AppWindowThumbnail.java
+++ b/services/core/java/com/android/server/wm/AppWindowThumbnail.java
@@ -50,9 +50,23 @@ class AppWindowThumbnail implements Animatable {
private final SurfaceAnimator mSurfaceAnimator;
private final int mWidth;
private final int mHeight;
+ private final boolean mRelative;
AppWindowThumbnail(Transaction t, AppWindowToken appToken, GraphicBuffer thumbnailHeader) {
+ this(t, appToken, thumbnailHeader, false /* relative */);
+ }
+
+ /**
+ * @param t Transaction to create the thumbnail in.
+ * @param appToken {@link AppWindowToken} to associate this thumbnail with.
+ * @param thumbnailHeader A thumbnail or placeholder for thumbnail to initialize with.
+ * @param relative Whether this thumbnail will be a child of appToken (and thus positioned
+ * relative to it) or not.
+ */
+ AppWindowThumbnail(Transaction t, AppWindowToken appToken, GraphicBuffer thumbnailHeader,
+ boolean relative) {
mAppToken = appToken;
+ mRelative = relative;
mSurfaceAnimator =
new SurfaceAnimator(this, this::onAnimationFinished, appToken.mWmService);
mWidth = thumbnailHeader.getWidth();
@@ -86,6 +100,9 @@ class AppWindowThumbnail implements Animatable {
// We parent the thumbnail to the task, and just place it on top of anything else in the
// task.
t.setLayer(mSurfaceControl, Integer.MAX_VALUE);
+ if (relative) {
+ t.reparent(mSurfaceControl, appToken.getSurfaceControl());
+ }
}
void startAnimation(Transaction t, Animation anim) {
@@ -101,6 +118,13 @@ class AppWindowThumbnail implements Animatable {
mAppToken.mWmService.mSurfaceAnimationRunner), false /* hidden */);
}
+ /**
+ * Start animation with existing adapter.
+ */
+ void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden) {
+ mSurfaceAnimator.startAnimation(t, anim, hidden);
+ }
+
private void onAnimationFinished() {
}
@@ -147,6 +171,9 @@ class AppWindowThumbnail implements Animatable {
@Override
public void onAnimationLeashCreated(Transaction t, SurfaceControl leash) {
t.setLayer(leash, Integer.MAX_VALUE);
+ if (mRelative) {
+ t.reparent(leash, mAppToken.getSurfaceControl());
+ }
}
@Override
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 750c5ca5922e..65b36a092228 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -16,6 +16,7 @@
package com.android.server.wm;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
@@ -33,6 +34,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static android.view.WindowManager.TRANSIT_DOCK_TASK_FROM_RECENTS;
+import static android.view.WindowManager.TRANSIT_TASK_CHANGE_WINDOWING_MODE;
import static android.view.WindowManager.TRANSIT_UNSET;
import static android.view.WindowManager.TRANSIT_WALLPAPER_OPEN;
@@ -97,6 +99,7 @@ import android.os.IBinder;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.Trace;
+import android.util.ArraySet;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
import android.view.DisplayInfo;
@@ -117,6 +120,7 @@ import com.android.server.LocalServices;
import com.android.server.display.ColorDisplayService;
import com.android.server.policy.WindowManagerPolicy;
import com.android.server.policy.WindowManagerPolicy.StartingSurface;
+import com.android.server.wm.RemoteAnimationController.RemoteAnimationRecord;
import com.android.server.wm.WindowManagerService.H;
import java.io.PrintWriter;
@@ -261,7 +265,18 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
/** Whether our surface was set to be showing in the last call to {@link #prepareSurfaces} */
private boolean mLastSurfaceShowing = true;
+ /**
+ * This gets used during some open/close transitions as well as during a change transition
+ * where it represents the starting-state snapshot.
+ */
private AppWindowThumbnail mThumbnail;
+ private final Rect mTransitStartRect = new Rect();
+
+ /**
+ * This leash is used to "freeze" the app surface in place after the state change, but before
+ * the animation is ready to start.
+ */
+ private SurfaceControl mTransitChangeLeash = null;
/** Have we been asked to have this token keep the screen frozen? */
private boolean mFreezingScreen;
@@ -272,6 +287,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
private final Point mTmpPoint = new Point();
private final Rect mTmpRect = new Rect();
+ private final Rect mTmpPrevBounds = new Rect();
private RemoteAnimationDefinition mRemoteAnimationDefinition;
private AnimatingAppWindowTokenRegistry mAnimatingAppWindowTokenRegistry;
@@ -810,6 +826,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
boolean delayed = commitVisibility(null, false, TRANSIT_UNSET, true, mVoiceInteraction);
getDisplayContent().mOpeningApps.remove(this);
+ getDisplayContent().mChangingApps.remove(this);
getDisplayContent().mUnknownAppVisibilityController.appRemovedOrHidden(this);
mWmService.mTaskSnapshotController.onAppRemoved(this);
waitingToShow = false;
@@ -1528,6 +1545,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
@Override
public void onConfigurationChanged(Configuration newParentConfig) {
final int prevWinMode = getWindowingMode();
+ mTmpPrevBounds.set(getBounds());
super.onConfigurationChanged(newParentConfig);
final int winMode = getWindowingMode();
@@ -1559,9 +1577,68 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
mDisplayContent.mPinnedStackControllerLocked.saveReentrySnapFraction(this,
stackBounds);
}
+ } else if (shouldStartChangeTransition(prevWinMode, winMode)) {
+ initializeChangeTransition(mTmpPrevBounds);
+ }
+ }
+
+ private boolean shouldStartChangeTransition(int prevWinMode, int newWinMode) {
+ if (!isVisible() || getDisplayContent().mAppTransition.isTransitionSet()) {
+ return false;
+ }
+ // Only do an animation into and out-of freeform mode for now. Other mode
+ // transition animations are currently handled by system-ui.
+ return (prevWinMode == WINDOWING_MODE_FREEFORM) != (newWinMode == WINDOWING_MODE_FREEFORM);
+ }
+
+ /**
+ * Initializes a change transition. Because the app is visible already, there is a small period
+ * of time where the user can see the app content/window update before the transition starts.
+ * To prevent this, we immediately take a snapshot and place the app/snapshot into a leash which
+ * "freezes" the location/crop until the transition starts.
+ * <p>
+ * Here's a walk-through of the process:
+ * 1. Create a temporary leash ("interim-change-leash") and reparent the app to it.
+ * 2. Set the temporary leash's position/crop to the current state.
+ * 3. Create a snapshot and place that at the top of the leash to cover up content changes.
+ * 4. Once the transition is ready, it will reparent the app to the animation leash.
+ * 5. Detach the interim-change-leash.
+ */
+ private void initializeChangeTransition(Rect startBounds) {
+ mDisplayContent.prepareAppTransition(TRANSIT_TASK_CHANGE_WINDOWING_MODE,
+ false /* alwaysKeepCurrent */, 0, false /* forceOverride */);
+ mDisplayContent.mChangingApps.add(this);
+ mTransitStartRect.set(startBounds);
+
+ final SurfaceControl.Builder builder = makeAnimationLeash()
+ .setParent(getAnimationLeashParent())
+ .setName(getSurfaceControl() + " - interim-change-leash");
+ mTransitChangeLeash = builder.build();
+ Transaction t = getPendingTransaction();
+ t.setWindowCrop(mTransitChangeLeash, startBounds.width(), startBounds.height());
+ t.setPosition(mTransitChangeLeash, startBounds.left, startBounds.top);
+ t.show(mTransitChangeLeash);
+ t.reparent(getSurfaceControl(), mTransitChangeLeash);
+ onAnimationLeashCreated(t, mTransitChangeLeash);
+
+ if (mThumbnail == null && getTask() != null) {
+ final TaskSnapshotController snapshotCtrl = mWmService.mTaskSnapshotController;
+ final ArraySet<Task> tasks = new ArraySet<>();
+ tasks.add(getTask());
+ snapshotCtrl.snapshotTasks(tasks);
+ snapshotCtrl.addSkipClosingAppSnapshotTasks(tasks);
+ final ActivityManager.TaskSnapshot snapshot = snapshotCtrl.getSnapshot(
+ getTask().mTaskId, getTask().mUserId, false /* restoreFromDisk */,
+ false /* reducedResolution */);
+ mThumbnail = new AppWindowThumbnail(t, this, snapshot.getSnapshot(),
+ true /* relative */);
}
}
+ boolean isInChangeTransition() {
+ return mTransitChangeLeash != null || isChangeTransition(mTransit);
+ }
+
@Override
void checkAppWindowsReadyToShow() {
if (allDrawn == mLastAllDrawn) {
@@ -2242,6 +2319,15 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
return getBounds();
}
+ private static boolean isChangeTransition(int transit) {
+ return transit == TRANSIT_TASK_CHANGE_WINDOWING_MODE;
+ }
+
+ private int getDefaultChangeTransitionDuration() {
+ return (int) (AppTransition.DEFAULT_APP_TRANSITION_DURATION
+ * mWmService.getTransitionAnimationScaleLocked());
+ }
+
boolean applyAnimationLocked(WindowManager.LayoutParams lp, int transit, boolean enter,
boolean isVoiceInteraction) {
@@ -2260,13 +2346,35 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "AWT#applyAnimationLocked");
if (okToAnimate()) {
final AnimationAdapter adapter;
+ AnimationAdapter thumbnailAdapter = null;
getAnimationBounds(mTmpPoint, mTmpRect);
+ boolean isChanging = isChangeTransition(transit) && mThumbnail != null;
+
// Delaying animation start isn't compatible with remote animations at all.
if (getDisplayContent().mAppTransition.getRemoteAnimationController() != null
&& !mSurfaceAnimator.isAnimationStartDelayed()) {
- adapter = getDisplayContent().mAppTransition.getRemoteAnimationController()
- .createAnimationAdapter(this, mTmpPoint, mTmpRect);
+ RemoteAnimationRecord adapters =
+ getDisplayContent().mAppTransition.getRemoteAnimationController()
+ .createRemoteAnimationRecord(this, mTmpPoint, mTmpRect,
+ (isChanging ? mTransitStartRect : null));
+ adapter = adapters.mAdapter;
+ thumbnailAdapter = adapters.mThumbnailAdapter;
+ } else if (isChanging) {
+ int duration = getDefaultChangeTransitionDuration();
+ mTmpRect.offsetTo(mTmpPoint.x, mTmpPoint.y);
+ adapter = new LocalAnimationAdapter(
+ new WindowChangeAnimationSpec(mTransitStartRect, mTmpRect,
+ getDisplayContent().getDisplayInfo(), duration,
+ true /* isAppAnimation */, false /* isThumbnail */),
+ mWmService.mSurfaceAnimationRunner);
+ thumbnailAdapter = new LocalAnimationAdapter(
+ new WindowChangeAnimationSpec(mTransitStartRect, mTmpRect,
+ getDisplayContent().getDisplayInfo(), duration,
+ true /* isAppAnimation */, true /* isThumbnail */),
+ mWmService.mSurfaceAnimationRunner);
+ mTransit = transit;
+ mTransitFlags = getDisplayContent().mAppTransition.getTransitFlags();
} else {
final int appStackClipMode =
getDisplayContent().mAppTransition.getAppStackClipMode();
@@ -2294,6 +2402,10 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
if (adapter.getShowWallpaper()) {
mDisplayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
}
+ if (thumbnailAdapter != null) {
+ mThumbnail.startAnimation(
+ getPendingTransaction(), thumbnailAdapter, !isVisible());
+ }
}
} else {
cancelAnimation();
@@ -2429,6 +2541,17 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
final DisplayContent dc = getDisplayContent();
dc.assignStackOrdering();
+
+ if (leash == mTransitChangeLeash) {
+ // This is a temporary state so skip any animation notifications
+ return;
+ } else if (mTransitChangeLeash != null) {
+ // unparent mTransitChangeLeash for clean-up
+ t.hide(mTransitChangeLeash);
+ t.reparent(mTransitChangeLeash, null);
+ mTransitChangeLeash = null;
+ }
+
if (mAnimatingAppWindowTokenRegistry != null) {
mAnimatingAppWindowTokenRegistry.notifyStarting(this);
}
@@ -2487,6 +2610,12 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
+ " okToAnimate=" + okToAnimate()
+ " startingDisplayed=" + startingDisplayed);
+ // clean up thumbnail window
+ if (mThumbnail != null) {
+ mThumbnail.destroy();
+ mThumbnail = null;
+ }
+
// WindowState.onExitAnimationDone might modify the children list, so make a copy and then
// traverse the copy.
final ArrayList<WindowState> children = new ArrayList<>(mChildren);
@@ -2518,14 +2647,30 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
@Override
void cancelAnimation() {
- super.cancelAnimation();
+ cancelAnimationOnly();
clearThumbnail();
+ if (mTransitChangeLeash != null) {
+ getPendingTransaction().hide(mTransitChangeLeash);
+ getPendingTransaction().reparent(mTransitChangeLeash, null);
+ mTransitChangeLeash = null;
+ }
+ }
+
+ /**
+ * This only cancels the animation. It doesn't do other teardown like cleaning-up thumbnail
+ * or interim leashes.
+ * <p>
+ * Used when canceling in preparation for starting a new animation.
+ */
+ void cancelAnimationOnly() {
+ super.cancelAnimation();
}
boolean isWaitingForTransitionStart() {
return getDisplayContent().mAppTransition.isTransitionSet()
&& (getDisplayContent().mOpeningApps.contains(this)
- || getDisplayContent().mClosingApps.contains(this));
+ || getDisplayContent().mClosingApps.contains(this)
+ || getDisplayContent().mChangingApps.contains(this));
}
public int getTransit() {
@@ -2835,6 +2980,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
void removeFromPendingTransition() {
if (isWaitingForTransitionStart() && mDisplayContent != null) {
mDisplayContent.mOpeningApps.remove(this);
+ mDisplayContent.mChangingApps.remove(this);
mDisplayContent.mClosingApps.remove(this);
}
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 8fefd352e027..8f976e74670d 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -250,6 +250,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
final ArraySet<AppWindowToken> mOpeningApps = new ArraySet<>();
final ArraySet<AppWindowToken> mClosingApps = new ArraySet<>();
+ final ArraySet<AppWindowToken> mChangingApps = new ArraySet<>();
final UnknownAppVisibilityController mUnknownAppVisibilityController;
BoundsAnimationController mBoundsAnimationController;
@@ -2454,6 +2455,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
// Clear all transitions & screen frozen states when removing display.
mOpeningApps.clear();
mClosingApps.clear();
+ mChangingApps.clear();
mUnknownAppVisibilityController.clear();
mAppTransition.removeAppTransitionTimeoutCallbacks();
handleAnimatingStoppedAndTransition();
@@ -3284,7 +3286,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
}
}
- if (!mOpeningApps.isEmpty() || !mClosingApps.isEmpty()) {
+ if (!mOpeningApps.isEmpty() || !mClosingApps.isEmpty() || !mChangingApps.isEmpty()) {
pw.println();
if (mOpeningApps.size() > 0) {
pw.print(" mOpeningApps="); pw.println(mOpeningApps);
@@ -3292,6 +3294,9 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
if (mClosingApps.size() > 0) {
pw.print(" mClosingApps="); pw.println(mClosingApps);
}
+ if (mChangingApps.size() > 0) {
+ pw.print(" mChangingApps="); pw.println(mChangingApps);
+ }
}
mUnknownAppVisibilityController.dump(pw, " ");
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 6d3c69385a09..40063326e76e 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -58,6 +58,7 @@ import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLES
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_BOOT_PROGRESS;
import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
@@ -2029,7 +2030,8 @@ public class DisplayPolicy {
of.set(displayFrames.mRestricted);
df.set(displayFrames.mRestricted);
pf.set(displayFrames.mRestricted);
- } else if (type == TYPE_TOAST || type == TYPE_SYSTEM_ALERT) {
+ } else if (type == TYPE_TOAST || type == TYPE_SYSTEM_ALERT
+ || type == TYPE_APPLICATION_OVERLAY) {
// These dialogs are stable to interim decor changes.
cf.set(displayFrames.mStable);
of.set(displayFrames.mStable);
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index 7ea88bbdaec8..ea65dd99077a 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -36,7 +36,6 @@ import static com.android.server.wm.AppTransition.TOUCH_RESPONSE_INTERPOLATOR;
import static com.android.server.wm.DockedStackDividerControllerProto.MINIMIZED_DOCK;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-import static com.android.server.wm.WindowManagerService.H.NOTIFY_DOCKED_STACK_MINIMIZED_CHANGED;
import static com.android.server.wm.WindowManagerService.LAYER_OFFSET_DIM;
import android.content.Context;
@@ -566,9 +565,7 @@ public class DockedStackDividerController {
mMaximizeMeetFraction = getClipRevealMeetFraction(stack);
animDuration = (long) (mAnimationDuration * mMaximizeMeetFraction);
}
- mService.mH.removeMessages(NOTIFY_DOCKED_STACK_MINIMIZED_CHANGED);
- mService.mH.obtainMessage(NOTIFY_DOCKED_STACK_MINIMIZED_CHANGED,
- minimizedDock ? 1 : 0, 0).sendToTarget();
+ mService.mAtmInternal.notifyDockedStackMinimizedChanged(minimizedDock);
final int size = mDockedStackListeners.beginBroadcast();
for (int i = 0; i < size; ++i) {
final IDockedStackListener listener = mDockedStackListeners.getBroadcastItem(i);
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index 632db3842839..3c5d911903e7 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -46,7 +46,6 @@ import android.view.InputChannel;
import android.view.InputEventReceiver;
import android.view.InputWindowHandle;
import android.view.SurfaceControl;
-import android.view.animation.Animation;
import com.android.server.AnimationThread;
import com.android.server.policy.WindowManagerPolicy;
@@ -70,8 +69,7 @@ final class InputMonitor {
private boolean mDisableWallpaperTouchEvents;
private final Rect mTmpRect = new Rect();
- private final UpdateInputForAllWindowsConsumer mUpdateInputForAllWindowsConsumer =
- new UpdateInputForAllWindowsConsumer();
+ private final UpdateInputForAllWindowsConsumer mUpdateInputForAllWindowsConsumer;
private final int mDisplayId;
private final DisplayContent mDisplayContent;
@@ -165,6 +163,7 @@ final class InputMonitor {
mDisplayId = displayId;
mInputTransaction = mDisplayContent.getPendingTransaction();
mHandler = AnimationThread.getHandler();
+ mUpdateInputForAllWindowsConsumer = new UpdateInputForAllWindowsConsumer();
}
void onDisplayRemoved() {
@@ -407,6 +406,9 @@ final class InputMonitor {
boolean inDrag;
WallpaperController wallpaperController;
+ // An invalid window handle that tells SurfaceFlinger not update the input info.
+ final InputWindowHandle mInvalidInputWindow = new InputWindowHandle(null, null, mDisplayId);
+
private void updateInputWindows(boolean inDrag) {
Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "updateInputWindows");
@@ -445,6 +447,10 @@ final class InputMonitor {
final InputWindowHandle inputWindowHandle = w.mInputWindowHandle;
if (inputChannel == null || inputWindowHandle == null || w.mRemoved
|| w.cantReceiveTouchInput()) {
+ if (w.mWinAnimator.hasSurface()) {
+ mInputTransaction.setInputWindowInfo(
+ w.mWinAnimator.mSurfaceController.mSurfaceControl, mInvalidInputWindow);
+ }
// Skip this window because it cannot possibly receive input.
return;
}
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index 83ba384c9069..105ff0674ef0 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -29,7 +29,6 @@ import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_R
import static com.android.server.wm.AnimationAdapterProto.REMOTE;
import static com.android.server.wm.RemoteAnimationAdapterWrapperProto.TARGET;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_RECENTS_ANIMATIONS;
-import static com.android.server.wm.WindowManagerService.H.NOTIFY_APP_TRANSITION_STARTING;
import android.annotation.IntDef;
import android.app.ActivityManager.TaskSnapshot;
@@ -413,8 +412,7 @@ public class RecentsAnimationController implements DeathRecipient {
}
final SparseIntArray reasons = new SparseIntArray();
reasons.put(WINDOWING_MODE_FULLSCREEN, APP_TRANSITION_RECENTS_ANIM);
- mService.mH.obtainMessage(NOTIFY_APP_TRANSITION_STARTING,
- reasons).sendToTarget();
+ mService.mAtmInternal.notifyAppTransitionStarting(reasons, SystemClock.uptimeMillis());
}
void cancelAnimation(@ReorderMode int reorderMode, String reason) {
@@ -626,7 +624,7 @@ public class RecentsAnimationController implements DeathRecipient {
mTarget = new RemoteAnimationTarget(mTask.mTaskId, mode, mCapturedLeash,
!topApp.fillsParent(), mainWindow.mWinAnimator.mLastClipRect,
insets, mTask.getPrefixOrderIndex(), mPosition, mBounds,
- mTask.getWindowConfiguration(), mIsRecentTaskInvisible);
+ mTask.getWindowConfiguration(), mIsRecentTaskInvisible, null, null);
return mTarget;
}
diff --git a/services/core/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java
index f5acdccf07f4..f760b39c5332 100644
--- a/services/core/java/com/android/server/wm/RemoteAnimationController.java
+++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java
@@ -57,7 +57,7 @@ class RemoteAnimationController implements DeathRecipient {
private final WindowManagerService mService;
private final RemoteAnimationAdapter mRemoteAnimationAdapter;
- private final ArrayList<RemoteAnimationAdapterWrapper> mPendingAnimations = new ArrayList<>();
+ private final ArrayList<RemoteAnimationRecord> mPendingAnimations = new ArrayList<>();
private final Rect mTmpRect = new Rect();
private final Handler mHandler;
private final Runnable mTimeoutRunnable = () -> cancelAnimation("timeoutRunnable");
@@ -74,21 +74,22 @@ class RemoteAnimationController implements DeathRecipient {
}
/**
- * Creates an animation for each individual {@link AppWindowToken}.
+ * Creates an animation record for each individual {@link AppWindowToken}.
*
* @param appWindowToken The app to animate.
* @param position The position app bounds, in screen coordinates.
- * @param stackBounds The stack bounds of the app.
- * @return The adapter to be run on the app.
+ * @param stackBounds The stack bounds of the app relative to position.
+ * @param startBounds The stack bounds before the transition, in screen coordinates
+ * @return The record representing animation(s) to run on the app.
*/
- AnimationAdapter createAnimationAdapter(AppWindowToken appWindowToken, Point position,
- Rect stackBounds) {
+ RemoteAnimationRecord createRemoteAnimationRecord(AppWindowToken appWindowToken,
+ Point position, Rect stackBounds, Rect startBounds) {
if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "createAnimationAdapter(): token="
+ appWindowToken);
- final RemoteAnimationAdapterWrapper adapter = new RemoteAnimationAdapterWrapper(
- appWindowToken, position, stackBounds);
- mPendingAnimations.add(adapter);
- return adapter;
+ final RemoteAnimationRecord adapters =
+ new RemoteAnimationRecord(appWindowToken, position, stackBounds, startBounds);
+ mPendingAnimations.add(adapters);
+ return adapters;
}
/**
@@ -148,7 +149,7 @@ class RemoteAnimationController implements DeathRecipient {
final StringWriter sw = new StringWriter();
final FastPrintWriter pw = new FastPrintWriter(sw);
for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
- mPendingAnimations.get(i).dump(pw, "");
+ mPendingAnimations.get(i).mAdapter.dump(pw, "");
}
pw.close();
Slog.i(TAG, sw.toString());
@@ -158,19 +159,26 @@ class RemoteAnimationController implements DeathRecipient {
if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "createAnimations()");
final ArrayList<RemoteAnimationTarget> targets = new ArrayList<>();
for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
- final RemoteAnimationAdapterWrapper wrapper = mPendingAnimations.get(i);
- final RemoteAnimationTarget target = wrapper.createRemoteAppAnimation();
+ final RemoteAnimationRecord wrappers = mPendingAnimations.get(i);
+ final RemoteAnimationTarget target = wrappers.createRemoteAnimationTarget();
if (target != null) {
- if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "\tAdd token=" + wrapper.mAppWindowToken);
+ if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "\tAdd token=" + wrappers.mAppWindowToken);
targets.add(target);
} else {
if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "\tRemove token="
- + wrapper.mAppWindowToken);
+ + wrappers.mAppWindowToken);
// We can't really start an animation but we still need to make sure to finish the
// pending animation that was started by SurfaceAnimator
- if (wrapper.mCapturedFinishCallback != null) {
- wrapper.mCapturedFinishCallback.onAnimationFinished(wrapper);
+ if (wrappers.mAdapter != null
+ && wrappers.mAdapter.mCapturedFinishCallback != null) {
+ wrappers.mAdapter.mCapturedFinishCallback
+ .onAnimationFinished(wrappers.mAdapter);
+ }
+ if (wrappers.mThumbnailAdapter != null
+ && wrappers.mThumbnailAdapter.mCapturedFinishCallback != null) {
+ wrappers.mThumbnailAdapter.mCapturedFinishCallback
+ .onAnimationFinished(wrappers.mThumbnailAdapter);
}
mPendingAnimations.remove(i);
}
@@ -190,9 +198,16 @@ class RemoteAnimationController implements DeathRecipient {
if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG,
"onAnimationFinished(): Notify animation finished:");
for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
- final RemoteAnimationAdapterWrapper adapter = mPendingAnimations.get(i);
- adapter.mCapturedFinishCallback.onAnimationFinished(adapter);
- if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "\t" + adapter.mAppWindowToken);
+ final RemoteAnimationRecord adapters = mPendingAnimations.get(i);
+ if (adapters.mAdapter != null) {
+ adapters.mAdapter.mCapturedFinishCallback
+ .onAnimationFinished(adapters.mAdapter);
+ }
+ if (adapters.mThumbnailAdapter != null) {
+ adapters.mThumbnailAdapter.mCapturedFinishCallback
+ .onAnimationFinished(adapters.mThumbnailAdapter);
+ }
+ if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "\t" + adapters.mAppWindowToken);
}
} catch (Exception e) {
Slog.e(TAG, "Failed to finish remote animation", e);
@@ -282,47 +297,84 @@ class RemoteAnimationController implements DeathRecipient {
}
};
- private class RemoteAnimationAdapterWrapper implements AnimationAdapter {
-
- private final AppWindowToken mAppWindowToken;
- private SurfaceControl mCapturedLeash;
- private OnAnimationFinishedCallback mCapturedFinishCallback;
- private final Point mPosition = new Point();
- private final Rect mStackBounds = new Rect();
- private RemoteAnimationTarget mTarget;
-
- RemoteAnimationAdapterWrapper(AppWindowToken appWindowToken, Point position,
- Rect stackBounds) {
+ /**
+ * Contains information about a remote-animation for one AppWindowToken. This keeps track of,
+ * potentially, multiple animating surfaces (AdapterWrappers) associated with one
+ * Window/Transition. For example, a change transition has an adapter controller for the
+ * main window and an adapter controlling the start-state snapshot.
+ * <p>
+ * This can be thought of as a bridge between the information that the remote animator sees (via
+ * {@link RemoteAnimationTarget}) and what the server sees (the
+ * {@link RemoteAnimationAdapterWrapper}(s) interfacing with the moving surfaces).
+ */
+ public class RemoteAnimationRecord {
+ RemoteAnimationAdapterWrapper mAdapter;
+ RemoteAnimationAdapterWrapper mThumbnailAdapter = null;
+ RemoteAnimationTarget mTarget;
+ final AppWindowToken mAppWindowToken;
+ final Rect mStartBounds;
+
+ RemoteAnimationRecord(AppWindowToken appWindowToken, Point endPos, Rect endBounds,
+ Rect startBounds) {
mAppWindowToken = appWindowToken;
- mPosition.set(position.x, position.y);
- mStackBounds.set(stackBounds);
+ mAdapter = new RemoteAnimationAdapterWrapper(this, endPos, endBounds);
+ if (startBounds != null) {
+ mStartBounds = new Rect(startBounds);
+ mTmpRect.set(startBounds);
+ mTmpRect.offsetTo(0, 0);
+ mThumbnailAdapter =
+ new RemoteAnimationAdapterWrapper(this, new Point(0, 0), mTmpRect);
+ } else {
+ mStartBounds = null;
+ }
}
- RemoteAnimationTarget createRemoteAppAnimation() {
+ RemoteAnimationTarget createRemoteAnimationTarget() {
final Task task = mAppWindowToken.getTask();
final WindowState mainWindow = mAppWindowToken.findMainWindow();
- if (task == null || mainWindow == null || mCapturedFinishCallback == null
- || mCapturedLeash == null) {
+ if (task == null || mainWindow == null || mAdapter == null
+ || mAdapter.mCapturedFinishCallback == null
+ || mAdapter.mCapturedLeash == null) {
return null;
}
final Rect insets = new Rect();
mainWindow.getContentInsets(insets);
InsetUtils.addInsets(insets, mAppWindowToken.getLetterboxInsets());
mTarget = new RemoteAnimationTarget(task.mTaskId, getMode(),
- mCapturedLeash, !mAppWindowToken.fillsParent(),
+ mAdapter.mCapturedLeash, !mAppWindowToken.fillsParent(),
mainWindow.mWinAnimator.mLastClipRect, insets,
- mAppWindowToken.getPrefixOrderIndex(), mPosition, mStackBounds,
- task.getWindowConfiguration(), false /*isNotInRecents*/);
+ mAppWindowToken.getPrefixOrderIndex(), mAdapter.mPosition,
+ mAdapter.mStackBounds, task.getWindowConfiguration(), false /*isNotInRecents*/,
+ mThumbnailAdapter != null ? mThumbnailAdapter.mCapturedLeash : null,
+ mStartBounds);
return mTarget;
}
private int getMode() {
- if (mAppWindowToken.getDisplayContent().mOpeningApps.contains(mAppWindowToken)) {
+ final DisplayContent dc = mAppWindowToken.getDisplayContent();
+ if (dc.mOpeningApps.contains(mAppWindowToken)) {
return RemoteAnimationTarget.MODE_OPENING;
+ } else if (dc.mChangingApps.contains(mAppWindowToken)) {
+ return RemoteAnimationTarget.MODE_CHANGING;
} else {
return RemoteAnimationTarget.MODE_CLOSING;
}
}
+ }
+
+ private class RemoteAnimationAdapterWrapper implements AnimationAdapter {
+ private final RemoteAnimationRecord mRecord;
+ SurfaceControl mCapturedLeash;
+ private OnAnimationFinishedCallback mCapturedFinishCallback;
+ private final Point mPosition = new Point();
+ private final Rect mStackBounds = new Rect();
+
+ RemoteAnimationAdapterWrapper(RemoteAnimationRecord record, Point position,
+ Rect stackBounds) {
+ mRecord = record;
+ mPosition.set(position.x, position.y);
+ mStackBounds.set(stackBounds);
+ }
@Override
public boolean getShowWallpaper() {
@@ -340,7 +392,7 @@ class RemoteAnimationController implements DeathRecipient {
if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "startAnimation");
// Restore z-layering, position and stack crop until client has a chance to modify it.
- t.setLayer(animationLeash, mAppWindowToken.getPrefixOrderIndex());
+ t.setLayer(animationLeash, mRecord.mAppWindowToken.getPrefixOrderIndex());
t.setPosition(animationLeash, mPosition.x, mPosition.y);
mTmpRect.set(mStackBounds);
mTmpRect.offsetTo(0, 0);
@@ -351,7 +403,14 @@ class RemoteAnimationController implements DeathRecipient {
@Override
public void onAnimationCancelled(SurfaceControl animationLeash) {
- mPendingAnimations.remove(this);
+ if (mRecord.mAdapter == this) {
+ mRecord.mAdapter = null;
+ } else {
+ mRecord.mThumbnailAdapter = null;
+ }
+ if (mRecord.mAdapter == null && mRecord.mThumbnailAdapter == null) {
+ mPendingAnimations.remove(mRecord);
+ }
if (mPendingAnimations.isEmpty()) {
mHandler.removeCallbacks(mTimeoutRunnable);
releaseFinishedCallback();
@@ -373,10 +432,10 @@ class RemoteAnimationController implements DeathRecipient {
@Override
public void dump(PrintWriter pw, String prefix) {
- pw.print(prefix); pw.print("token="); pw.println(mAppWindowToken);
- if (mTarget != null) {
+ pw.print(prefix); pw.print("token="); pw.println(mRecord.mAppWindowToken);
+ if (mRecord.mTarget != null) {
pw.print(prefix); pw.println("Target:");
- mTarget.dump(pw, prefix + " ");
+ mRecord.mTarget.dump(pw, prefix + " ");
} else {
pw.print(prefix); pw.println("Target: null");
}
@@ -385,8 +444,8 @@ class RemoteAnimationController implements DeathRecipient {
@Override
public void writeToProto(ProtoOutputStream proto) {
final long token = proto.start(REMOTE);
- if (mTarget != null) {
- mTarget.writeToProto(proto, TARGET);
+ if (mRecord.mTarget != null) {
+ mRecord.mTarget.writeToProto(proto, TARGET);
}
proto.end(token);
}
diff --git a/services/core/java/com/android/server/wm/StatusBarController.java b/services/core/java/com/android/server/wm/StatusBarController.java
index b4de75b287fa..6db606d2a30b 100644
--- a/services/core/java/com/android/server/wm/StatusBarController.java
+++ b/services/core/java/com/android/server/wm/StatusBarController.java
@@ -61,9 +61,8 @@ public class StatusBarController extends BarController {
}
@Override
- public int onAppTransitionStartingLocked(int transit, IBinder openToken,
- IBinder closeToken, long duration, long statusBarAnimationStartTime,
- long statusBarAnimationDuration) {
+ public int onAppTransitionStartingLocked(int transit, long duration,
+ long statusBarAnimationStartTime, long statusBarAnimationDuration) {
mHandler.post(() -> {
StatusBarManagerInternal statusBar = getStatusBarInternal();
if (statusBar != null && mWin != null) {
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index 15239c7e57e1..c15afc547085 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -661,7 +661,8 @@ class WallpaperController {
* Adjusts the wallpaper windows if the input display has a pending wallpaper layout or one of
* the opening apps should be a wallpaper target.
*/
- void adjustWallpaperWindowsForAppTransitionIfNeeded(ArraySet<AppWindowToken> openingApps) {
+ void adjustWallpaperWindowsForAppTransitionIfNeeded(ArraySet<AppWindowToken> openingApps,
+ ArraySet<AppWindowToken> changingApps) {
boolean adjust = false;
if ((mDisplayContent.pendingLayoutChanges & FINISH_LAYOUT_REDO_WALLPAPER) != 0) {
adjust = true;
@@ -673,6 +674,15 @@ class WallpaperController {
break;
}
}
+ if (!adjust) {
+ for (int i = changingApps.size() - 1; i >= 0; --i) {
+ final AppWindowToken token = changingApps.valueAt(i);
+ if (token.windowsCanBeWallpaperTarget()) {
+ adjust = true;
+ break;
+ }
+ }
+ }
}
if (adjust) {
diff --git a/services/core/java/com/android/server/wm/WindowChangeAnimationSpec.java b/services/core/java/com/android/server/wm/WindowChangeAnimationSpec.java
new file mode 100644
index 000000000000..7dd7c4f5c958
--- /dev/null
+++ b/services/core/java/com/android/server/wm/WindowChangeAnimationSpec.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2018 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.wm;
+
+import static com.android.server.wm.AnimationAdapter.STATUS_BAR_TRANSITION_DURATION;
+import static com.android.server.wm.AnimationSpecProto.WINDOW;
+import static com.android.server.wm.WindowAnimationSpecProto.ANIMATION;
+
+import android.graphics.Matrix;
+import android.graphics.Rect;
+import android.os.SystemClock;
+import android.util.proto.ProtoOutputStream;
+import android.view.DisplayInfo;
+import android.view.SurfaceControl;
+import android.view.SurfaceControl.Transaction;
+import android.view.animation.AlphaAnimation;
+import android.view.animation.Animation;
+import android.view.animation.AnimationSet;
+import android.view.animation.ClipRectAnimation;
+import android.view.animation.ScaleAnimation;
+import android.view.animation.Transformation;
+import android.view.animation.TranslateAnimation;
+
+import com.android.server.wm.LocalAnimationAdapter.AnimationSpec;
+
+import java.io.PrintWriter;
+
+/**
+ * Animation spec for changing window animations.
+ */
+public class WindowChangeAnimationSpec implements AnimationSpec {
+
+ private final ThreadLocal<TmpValues> mThreadLocalTmps = ThreadLocal.withInitial(TmpValues::new);
+ private final boolean mIsAppAnimation;
+ private final Rect mStartBounds;
+ private final Rect mEndBounds;
+ private final Rect mTmpRect = new Rect();
+
+ private Animation mAnimation;
+ private final boolean mIsThumbnail;
+
+ public WindowChangeAnimationSpec(Rect startBounds, Rect endBounds, DisplayInfo displayInfo,
+ long duration, boolean isAppAnimation, boolean isThumbnail) {
+ mStartBounds = new Rect(startBounds);
+ mEndBounds = new Rect(endBounds);
+ mIsAppAnimation = isAppAnimation;
+ mIsThumbnail = isThumbnail;
+ createBoundsInterpolator(duration, displayInfo);
+ }
+
+ @Override
+ public boolean getShowWallpaper() {
+ return false;
+ }
+
+ @Override
+ public int getBackgroundColor() {
+ return 0;
+ }
+
+ @Override
+ public long getDuration() {
+ return mAnimation.getDuration();
+ }
+
+ /**
+ * This animator behaves slightly differently depending on whether the window is growing
+ * or shrinking:
+ * If growing, it will do a clip-reveal after quicker fade-out/scale of the smaller (old)
+ * snapshot.
+ * If shrinking, it will do an opposite clip-reveal on the old snapshot followed by a quicker
+ * fade-out of the bigger (old) snapshot while simultaneously shrinking the new window into
+ * place.
+ * @param duration
+ * @param displayInfo
+ */
+ private void createBoundsInterpolator(long duration, DisplayInfo displayInfo) {
+ boolean growing = mEndBounds.width() - mStartBounds.width()
+ + mEndBounds.height() - mStartBounds.height() >= 0;
+ float scalePart = 0.7f;
+ long scalePeriod = (long) (duration * scalePart);
+ float startScaleX = scalePart * ((float) mStartBounds.width()) / mEndBounds.width()
+ + (1.f - scalePart);
+ float startScaleY = scalePart * ((float) mStartBounds.height()) / mEndBounds.height()
+ + (1.f - scalePart);
+ if (mIsThumbnail) {
+ AnimationSet animSet = new AnimationSet(true);
+ Animation anim = new AlphaAnimation(1.f, 0.f);
+ anim.setDuration(scalePeriod);
+ if (!growing) {
+ anim.setStartOffset(duration - scalePeriod);
+ }
+ animSet.addAnimation(anim);
+ float endScaleX = 1.f / startScaleX;
+ float endScaleY = 1.f / startScaleY;
+ anim = new ScaleAnimation(endScaleX, endScaleX, endScaleY, endScaleY);
+ anim.setDuration(duration);
+ animSet.addAnimation(anim);
+ mAnimation = animSet;
+ mAnimation.initialize(mStartBounds.width(), mStartBounds.height(),
+ mEndBounds.width(), mEndBounds.height());
+ } else {
+ AnimationSet animSet = new AnimationSet(true);
+ final Animation scaleAnim = new ScaleAnimation(startScaleX, 1, startScaleY, 1);
+ scaleAnim.setDuration(scalePeriod);
+ if (!growing) {
+ scaleAnim.setStartOffset(duration - scalePeriod);
+ }
+ animSet.addAnimation(scaleAnim);
+ final Animation translateAnim = new TranslateAnimation(mStartBounds.left,
+ mEndBounds.left, mStartBounds.top, mEndBounds.top);
+ translateAnim.setDuration(duration);
+ animSet.addAnimation(translateAnim);
+ Rect startClip = new Rect(mStartBounds);
+ Rect endClip = new Rect(mEndBounds);
+ startClip.offsetTo(0, 0);
+ endClip.offsetTo(0, 0);
+ final Animation clipAnim = new ClipRectAnimation(startClip, endClip);
+ clipAnim.setDuration(duration);
+ animSet.addAnimation(clipAnim);
+ mAnimation = animSet;
+ mAnimation.initialize(mStartBounds.width(), mStartBounds.height(),
+ displayInfo.appWidth, displayInfo.appHeight);
+ }
+ }
+
+ @Override
+ public void apply(Transaction t, SurfaceControl leash, long currentPlayTime) {
+ final TmpValues tmp = mThreadLocalTmps.get();
+ if (mIsThumbnail) {
+ mAnimation.getTransformation(currentPlayTime, tmp.mTransformation);
+ t.setMatrix(leash, tmp.mTransformation.getMatrix(), tmp.mFloats);
+ t.setAlpha(leash, tmp.mTransformation.getAlpha());
+ } else {
+ mAnimation.getTransformation(currentPlayTime, tmp.mTransformation);
+ final Matrix matrix = tmp.mTransformation.getMatrix();
+ t.setMatrix(leash, matrix, tmp.mFloats);
+
+ // The following applies an inverse scale to the clip-rect so that it crops "after" the
+ // scale instead of before.
+ tmp.mVecs[1] = tmp.mVecs[2] = 0;
+ tmp.mVecs[0] = tmp.mVecs[3] = 1;
+ matrix.mapVectors(tmp.mVecs);
+ tmp.mVecs[0] = 1.f / tmp.mVecs[0];
+ tmp.mVecs[3] = 1.f / tmp.mVecs[3];
+ final Rect clipRect = tmp.mTransformation.getClipRect();
+ mTmpRect.left = (int) (clipRect.left * tmp.mVecs[0] + 0.5f);
+ mTmpRect.right = (int) (clipRect.right * tmp.mVecs[0] + 0.5f);
+ mTmpRect.top = (int) (clipRect.top * tmp.mVecs[3] + 0.5f);
+ mTmpRect.bottom = (int) (clipRect.bottom * tmp.mVecs[3] + 0.5f);
+ t.setWindowCrop(leash, mTmpRect);
+ }
+ }
+
+ @Override
+ public long calculateStatusBarTransitionStartTime() {
+ long uptime = SystemClock.uptimeMillis();
+ return Math.max(uptime, uptime + ((long) (((float) mAnimation.getDuration()) * 0.99f))
+ - STATUS_BAR_TRANSITION_DURATION);
+ }
+
+ @Override
+ public boolean canSkipFirstFrame() {
+ return false;
+ }
+
+ @Override
+ public boolean needsEarlyWakeup() {
+ return mIsAppAnimation;
+ }
+
+ @Override
+ public void dump(PrintWriter pw, String prefix) {
+ pw.print(prefix); pw.println(mAnimation.getDuration());
+ }
+
+ @Override
+ public void writeToProtoInner(ProtoOutputStream proto) {
+ final long token = proto.start(WINDOW);
+ proto.write(ANIMATION, mAnimation.toString());
+ proto.end(token);
+ }
+
+ private static class TmpValues {
+ final Transformation mTransformation = new Transformation();
+ final float[] mFloats = new float[9];
+ final float[] mVecs = new float[4];
+ }
+}
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index 5267e7e55793..e204697e46cf 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -118,8 +118,6 @@ public abstract class WindowManagerInternal {
*
* @param transit transition type indicating what kind of transition gets run, must be one
* of AppTransition.TRANSIT_* values
- * @param openToken the token for the opening app
- * @param closeToken the token for the closing app
* @param duration the total duration of the transition
* @param statusBarAnimationStartTime the desired start time for all visual animations in
* the status bar caused by this app transition in uptime millis
@@ -131,8 +129,8 @@ public abstract class WindowManagerInternal {
* {@link WindowManagerPolicy#FINISH_LAYOUT_REDO_WALLPAPER},
* or {@link WindowManagerPolicy#FINISH_LAYOUT_REDO_ANIM}.
*/
- public int onAppTransitionStartingLocked(int transit, IBinder openToken, IBinder closeToken,
- long duration, long statusBarAnimationStartTime, long statusBarAnimationDuration) {
+ public int onAppTransitionStartingLocked(int transit, long duration,
+ long statusBarAnimationStartTime, long statusBarAnimationDuration) {
return 0;
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index c6679a9ad0d7..5eff7d8a8553 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -184,7 +184,6 @@ import android.util.MergedConfiguration;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
-import android.util.SparseIntArray;
import android.util.TimeUtils;
import android.util.TypedValue;
import android.util.proto.ProtoOutputStream;
@@ -857,12 +856,12 @@ public class WindowManagerService extends IWindowManager.Stub
@Override
public void onAppTransitionCancelledLocked(int transit) {
- mH.sendEmptyMessage(H.NOTIFY_APP_TRANSITION_CANCELLED);
+ mAtmInternal.notifyAppTransitionCancelled();
}
@Override
public void onAppTransitionFinishedLocked(IBinder token) {
- mH.sendEmptyMessage(H.NOTIFY_APP_TRANSITION_FINISHED);
+ mAtmInternal.notifyAppTransitionFinished();
final AppWindowToken atoken = mRoot.getAppWindowToken(token);
if (atoken == null) {
return;
@@ -2602,7 +2601,7 @@ public class WindowManagerService extends IWindowManager.Stub
@Override
public void notifyKeyguardTrustedChanged() {
- mH.sendEmptyMessage(H.NOTIFY_KEYGUARD_TRUSTED_CHANGED);
+ mAtmInternal.notifyKeyguardTrustedChanged();
}
@Override
@@ -2667,11 +2666,7 @@ public class WindowManagerService extends IWindowManager.Stub
* @param callback Runnable to be called when activity manager is done reevaluating visibilities
*/
void notifyKeyguardFlagsChanged(@Nullable Runnable callback, int displayId) {
- final Runnable wrappedCallback = callback != null
- ? () -> { synchronized (mGlobalLock) { callback.run(); } }
- : null;
- mH.obtainMessage(H.NOTIFY_KEYGUARD_FLAGS_CHANGED, displayId, 0,
- wrappedCallback).sendToTarget();
+ mAtmInternal.notifyKeyguardFlagsChanged(callback, displayId);
}
public boolean isKeyguardTrusted() {
@@ -4363,16 +4358,10 @@ public class WindowManagerService extends IWindowManager.Stub
public static final int WINDOW_REPLACEMENT_TIMEOUT = 46;
- public static final int NOTIFY_APP_TRANSITION_STARTING = 47;
- public static final int NOTIFY_APP_TRANSITION_CANCELLED = 48;
- public static final int NOTIFY_APP_TRANSITION_FINISHED = 49;
public static final int UPDATE_ANIMATION_SCALE = 51;
public static final int WINDOW_HIDE_TIMEOUT = 52;
- public static final int NOTIFY_DOCKED_STACK_MINIMIZED_CHANGED = 53;
public static final int SEAMLESS_ROTATION_TIMEOUT = 54;
public static final int RESTORE_POINTER_ICON = 55;
- public static final int NOTIFY_KEYGUARD_FLAGS_CHANGED = 56;
- public static final int NOTIFY_KEYGUARD_TRUSTED_CHANGED = 57;
public static final int SET_HAS_OVERLAY_UI = 58;
public static final int SET_RUNNING_REMOTE_ANIMATION = 59;
public static final int ANIMATION_FAILSAFE = 60;
@@ -4708,19 +4697,6 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
break;
- case NOTIFY_APP_TRANSITION_STARTING: {
- mAtmInternal.notifyAppTransitionStarting((SparseIntArray) msg.obj,
- msg.getWhen());
- }
- break;
- case NOTIFY_APP_TRANSITION_CANCELLED: {
- mAtmInternal.notifyAppTransitionCancelled();
- }
- break;
- case NOTIFY_APP_TRANSITION_FINISHED: {
- mAtmInternal.notifyAppTransitionFinished();
- }
- break;
case WINDOW_HIDE_TIMEOUT: {
final WindowState window = (WindowState) msg.obj;
synchronized (mGlobalLock) {
@@ -4742,10 +4718,6 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
break;
- case NOTIFY_DOCKED_STACK_MINIMIZED_CHANGED: {
- mAtmInternal.notifyDockedStackMinimizedChanged(msg.arg1 == 1);
- }
- break;
case RESTORE_POINTER_ICON: {
synchronized (mGlobalLock) {
restorePointerIconLocked((DisplayContent)msg.obj, msg.arg1, msg.arg2);
@@ -4759,14 +4731,6 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
break;
- case NOTIFY_KEYGUARD_FLAGS_CHANGED: {
- mAtmInternal.notifyKeyguardFlagsChanged((Runnable) msg.obj, msg.arg1);
- }
- break;
- case NOTIFY_KEYGUARD_TRUSTED_CHANGED: {
- mAtmInternal.notifyKeyguardTrustedChanged();
- }
- break;
case SET_HAS_OVERLAY_UI: {
mAmInternal.setHasOverlayUi(msg.arg1, msg.arg2 == 1);
}
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 90c9cc2b0a35..ff0b0d6f6eaf 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -782,7 +782,7 @@ void NativeInputManager::setInputWindows(JNIEnv* env, jobjectArray windowHandleO
}
sp<InputWindowHandle> windowHandle =
- android_server_InputWindowHandle_getHandle(env, windowHandleObj);
+ android_view_InputWindowHandle_getHandle(env, windowHandleObj);
if (windowHandle != nullptr) {
windowHandles.push(windowHandle);
}
@@ -822,7 +822,7 @@ void NativeInputManager::setInputWindows(JNIEnv* env, jobjectArray windowHandleO
void NativeInputManager::setFocusedApplication(JNIEnv* env, int32_t displayId,
jobject applicationHandleObj) {
sp<InputApplicationHandle> applicationHandle =
- android_server_InputApplicationHandle_getHandle(env, applicationHandleObj);
+ android_view_InputApplicationHandle_getHandle(env, applicationHandleObj);
mInputManager->getDispatcher()->setFocusedApplication(displayId, applicationHandle);
}
diff --git a/services/core/jni/com_android_server_lights_LightsService.cpp b/services/core/jni/com_android_server_lights_LightsService.cpp
index c90113f4b44e..26f6d7428fcc 100644
--- a/services/core/jni/com_android_server_lights_LightsService.cpp
+++ b/services/core/jni/com_android_server_lights_LightsService.cpp
@@ -39,37 +39,7 @@ using Type = ::android::hardware::light::V2_0::Type;
template<typename T>
using Return = ::android::hardware::Return<T>;
-class LightHal {
-private:
- static sp<ILight> sLight;
- static bool sLightInit;
-
- LightHal() {}
-
-public:
- static void disassociate() {
- sLightInit = false;
- sLight = nullptr;
- }
-
- static sp<ILight> associate() {
- if ((sLight == nullptr && !sLightInit) ||
- (sLight != nullptr && !sLight->ping().isOk())) {
- // will return the hal if it exists the first time.
- sLight = ILight::getService();
- sLightInit = true;
-
- if (sLight == nullptr) {
- ALOGE("Unable to get ILight interface.");
- }
- }
-
- return sLight;
- }
-};
-
-sp<ILight> LightHal::sLight = nullptr;
-bool LightHal::sLightInit = false;
+static bool sLightSupported = true;
static bool validate(jint light, jint flash, jint brightness) {
bool valid = true;
@@ -134,7 +104,6 @@ static void processReturn(
const LightState &state) {
if (!ret.isOk()) {
ALOGE("Failed to issue set light command.");
- LightHal::disassociate();
return;
}
@@ -164,13 +133,11 @@ static void setLight_native(
jint offMS,
jint brightnessMode) {
- if (!validate(light, flashMode, brightnessMode)) {
+ if (!sLightSupported) {
return;
}
- sp<ILight> hal = LightHal::associate();
-
- if (hal == nullptr) {
+ if (!validate(light, flashMode, brightnessMode)) {
return;
}
@@ -180,6 +147,11 @@ static void setLight_native(
{
android::base::Timer t;
+ sp<ILight> hal = ILight::getService();
+ if (hal == nullptr) {
+ sLightSupported = false;
+ return;
+ }
Return<Status> ret = hal->setLight(type, state);
processReturn(ret, type, state);
if (t.duration() > 50ms) ALOGD("Excessive delay setting light");
diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp
index 918f57e2945e..6e31aedd3f76 100644
--- a/services/core/jni/onload.cpp
+++ b/services/core/jni/onload.cpp
@@ -26,8 +26,6 @@ namespace android {
int register_android_server_AlarmManagerService(JNIEnv* env);
int register_android_server_BatteryStatsService(JNIEnv* env);
int register_android_server_ConsumerIrService(JNIEnv *env);
-int register_android_server_InputApplicationHandle(JNIEnv* env);
-int register_android_server_InputWindowHandle(JNIEnv* env);
int register_android_server_InputManager(JNIEnv* env);
int register_android_server_LightsService(JNIEnv* env);
int register_android_server_PowerManagerService(JNIEnv* env);
@@ -74,8 +72,6 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */)
register_android_server_broadcastradio_Tuner(vm, env);
register_android_server_PowerManagerService(env);
register_android_server_SerialService(env);
- register_android_server_InputApplicationHandle(env);
- register_android_server_InputWindowHandle(env);
register_android_server_InputManager(env);
register_android_server_LightsService(env);
register_android_server_AlarmManagerService(env);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
index d8225b38487c..2bf6f357bec8 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
@@ -108,12 +108,7 @@ abstract class BaseIDevicePolicyManager extends IDevicePolicyManager.Stub {
ParcelFileDescriptor updateFileDescriptor, StartInstallingUpdateCallback listener) {}
@Override
- public void addCrossProfileCalendarPackage(ComponentName admin, String packageName) {
- }
-
- @Override
- public boolean removeCrossProfileCalendarPackage(ComponentName admin, String packageName) {
- return false;
+ public void setCrossProfileCalendarPackages(ComponentName admin, List<String> packageNames) {
}
@Override
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 54053a896df9..90888201b89a 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -70,6 +70,7 @@ import static android.app.admin.DevicePolicyManager.PROFILE_KEYGUARD_FEATURES_AF
import static android.app.admin.DevicePolicyManager.WIPE_EUICC;
import static android.app.admin.DevicePolicyManager.WIPE_EXTERNAL_STORAGE;
import static android.app.admin.DevicePolicyManager.WIPE_RESET_PROTECTION_DATA;
+import static android.app.admin.DevicePolicyManager.WIPE_SILENTLY;
import static android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES;
import static android.provider.Settings.Global.PRIVATE_DNS_MODE;
import static android.provider.Settings.Global.PRIVATE_DNS_SPECIFIER;
@@ -77,14 +78,10 @@ import static android.provider.Telephony.Carriers.DPC_URI;
import static android.provider.Telephony.Carriers.ENFORCE_KEY;
import static android.provider.Telephony.Carriers.ENFORCE_MANAGED_URI;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent
- .PROVISIONING_ENTRY_POINT_ADB;
-import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker
- .STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW;
-import static com.android.server.devicepolicy.TransferOwnershipMetadataManager
- .ADMIN_TYPE_DEVICE_OWNER;
-import static com.android.server.devicepolicy.TransferOwnershipMetadataManager
- .ADMIN_TYPE_PROFILE_OWNER;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.PROVISIONING_ENTRY_POINT_ADB;
+import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW;
+import static com.android.server.devicepolicy.TransferOwnershipMetadataManager.ADMIN_TYPE_DEVICE_OWNER;
+import static com.android.server.devicepolicy.TransferOwnershipMetadataManager.ADMIN_TYPE_PROFILE_OWNER;
import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
@@ -949,8 +946,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
"metered_data_disabled_packages";
private static final String TAG_CROSS_PROFILE_CALENDAR_PACKAGES =
"cross-profile-calendar-packages";
- private static final String TAG_PACKAGE = "package";
-
+ private static final String TAG_CROSS_PROFILE_CALENDAR_PACKAGES_NULL =
+ "cross-profile-calendar-packages-null";
DeviceAdminInfo info;
@@ -1072,7 +1069,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
String endUserSessionMessage = null;
// The whitelist of packages that can access cross profile calendar APIs.
- final Set<String> mCrossProfileCalendarPackages = new ArraySet<>();
+ // This whitelist should be in default an empty list, which indicates that no package
+ // is whitelisted.
+ List<String> mCrossProfileCalendarPackages = Collections.emptyList();
ActiveAdmin(DeviceAdminInfo _info, boolean parent) {
info = _info;
@@ -1343,11 +1342,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
out.text(endUserSessionMessage);
out.endTag(null, TAG_END_USER_SESSION_MESSAGE);
}
- if (!mCrossProfileCalendarPackages.isEmpty()) {
- out.startTag(null, TAG_CROSS_PROFILE_CALENDAR_PACKAGES);
- writeAttributeValuesToXml(
- out, TAG_PACKAGE, mCrossProfileCalendarPackages);
- out.endTag(null, TAG_CROSS_PROFILE_CALENDAR_PACKAGES);
+ if (mCrossProfileCalendarPackages == null) {
+ out.startTag(null, TAG_CROSS_PROFILE_CALENDAR_PACKAGES_NULL);
+ out.endTag(null, TAG_CROSS_PROFILE_CALENDAR_PACKAGES_NULL);
+ } else {
+ writePackageListToXml(out, TAG_CROSS_PROFILE_CALENDAR_PACKAGES,
+ mCrossProfileCalendarPackages);
}
}
@@ -1542,8 +1542,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Log.w(LOG_TAG, "Missing text when loading end session message");
}
} else if (TAG_CROSS_PROFILE_CALENDAR_PACKAGES.equals(tag)) {
- readAttributeValues(
- parser, TAG_PACKAGE, mCrossProfileCalendarPackages);
+ mCrossProfileCalendarPackages = readPackageList(parser, tag);
+ } else if (TAG_CROSS_PROFILE_CALENDAR_PACKAGES_NULL.equals(tag)) {
+ mCrossProfileCalendarPackages = null;
} else {
Slog.w(LOG_TAG, "Unknown admin tag: " + tag);
XmlUtils.skipCurrentTag(parser);
@@ -1759,8 +1760,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
pw.print(prefix); pw.println("parentAdmin:");
parentAdmin.dump(prefix + " ", pw);
}
- pw.print(prefix); pw.print("mCrossProfileCalendarPackages=");
- pw.println(mCrossProfileCalendarPackages);
+ if (mCrossProfileCalendarPackages != null) {
+ pw.print(prefix); pw.print("mCrossProfileCalendarPackages=");
+ pw.println(mCrossProfileCalendarPackages);
+ }
}
}
@@ -1916,7 +1919,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
AlarmManager getAlarmManager() {
- return (AlarmManager) mContext.getSystemService(AlarmManager.class);
+ return mContext.getSystemService(AlarmManager.class);
+ }
+
+ ConnectivityManager getConnectivityManager() {
+ return mContext.getSystemService(ConnectivityManager.class);
}
IWindowManager getIWindowManager() {
@@ -6299,7 +6306,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
* @throws UnsupportedOperationException if the package does not support being set as always-on.
*/
@Override
- public boolean setAlwaysOnVpnPackage(ComponentName admin, String vpnPackage, boolean lockdown)
+ public boolean setAlwaysOnVpnPackage(ComponentName admin, String vpnPackage, boolean lockdown,
+ List<String> lockdownWhitelist)
throws SecurityException {
enforceProfileOrDeviceOwner(admin);
@@ -6307,11 +6315,23 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
final long token = mInjector.binderClearCallingIdentity();
try {
if (vpnPackage != null && !isPackageInstalledForUser(vpnPackage, userId)) {
- return false;
+ Slog.w(LOG_TAG, "Non-existent VPN package specified: " + vpnPackage);
+ throw new ServiceSpecificException(
+ DevicePolicyManager.ERROR_VPN_PACKAGE_NOT_FOUND, vpnPackage);
}
- ConnectivityManager connectivityManager = (ConnectivityManager)
- mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
- if (!connectivityManager.setAlwaysOnVpnPackageForUser(userId, vpnPackage, lockdown)) {
+
+ if (vpnPackage != null && lockdown && lockdownWhitelist != null) {
+ for (String packageName : lockdownWhitelist) {
+ if (!isPackageInstalledForUser(packageName, userId)) {
+ Slog.w(LOG_TAG, "Non-existent package in VPN whitelist: " + packageName);
+ throw new ServiceSpecificException(
+ DevicePolicyManager.ERROR_VPN_PACKAGE_NOT_FOUND, packageName);
+ }
+ }
+ }
+ // If some package is uninstalled after the check above, it will be ignored by CM.
+ if (!mInjector.getConnectivityManager().setAlwaysOnVpnPackageForUser(
+ userId, vpnPackage, lockdown, lockdownWhitelist)) {
throw new UnsupportedOperationException();
}
DevicePolicyEventLogger
@@ -6328,16 +6348,40 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
@Override
- public String getAlwaysOnVpnPackage(ComponentName admin)
+ public String getAlwaysOnVpnPackage(ComponentName admin) throws SecurityException {
+ enforceProfileOrDeviceOwner(admin);
+
+ final int userId = mInjector.userHandleGetCallingUserId();
+ final long token = mInjector.binderClearCallingIdentity();
+ try {
+ return mInjector.getConnectivityManager().getAlwaysOnVpnPackageForUser(userId);
+ } finally {
+ mInjector.binderRestoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public boolean isAlwaysOnVpnLockdownEnabled(ComponentName admin) throws SecurityException {
+ enforceProfileOrDeviceOwner(admin);
+
+ final int userId = mInjector.userHandleGetCallingUserId();
+ final long token = mInjector.binderClearCallingIdentity();
+ try {
+ return mInjector.getConnectivityManager().isVpnLockdownEnabled(userId);
+ } finally {
+ mInjector.binderRestoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public List<String> getAlwaysOnVpnLockdownWhitelist(ComponentName admin)
throws SecurityException {
enforceProfileOrDeviceOwner(admin);
final int userId = mInjector.userHandleGetCallingUserId();
final long token = mInjector.binderClearCallingIdentity();
- try{
- ConnectivityManager connectivityManager = (ConnectivityManager)
- mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
- return connectivityManager.getAlwaysOnVpnPackageForUser(userId);
+ try {
+ return mInjector.getConnectivityManager().getVpnLockdownWhitelist(userId);
} finally {
mInjector.binderRestoreCallingIdentity(token);
}
@@ -6362,7 +6406,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
}
- private void forceWipeUser(int userId, String wipeReasonForUser) {
+ private void forceWipeUser(int userId, String wipeReasonForUser, boolean wipeSilently) {
boolean success = false;
try {
IActivityManager am = mInjector.getIActivityManager();
@@ -6373,7 +6417,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
success = mUserManagerInternal.removeUserEvenWhenDisallowed(userId);
if (!success) {
Slog.w(LOG_TAG, "Couldn't remove user " + userId);
- } else if (isManagedProfile(userId) && !TextUtils.isEmpty(wipeReasonForUser)) {
+ } else if (isManagedProfile(userId) && !wipeSilently) {
sendWipeProfileNotification(wipeReasonForUser);
}
} catch (RemoteException re) {
@@ -6388,6 +6432,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (!mHasFeature) {
return;
}
+ Preconditions.checkStringNotEmpty(wipeReasonForUser, "wipeReasonForUser is null or empty");
enforceFullCrossUsersPermission(mInjector.userHandleGetCallingUserId());
final ActiveAdmin admin;
@@ -6447,7 +6492,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
internalReason,
/*wipeEuicc=*/ (flags & WIPE_EUICC) != 0);
} else {
- forceWipeUser(userId, wipeReasonForUser);
+ forceWipeUser(userId, wipeReasonForUser, (flags & WIPE_SILENTLY) != 0);
}
} finally {
mInjector.binderRestoreCallingIdentity(ident);
@@ -6809,9 +6854,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
enforceDeviceOwner(who);
long token = mInjector.binderClearCallingIdentity();
try {
- ConnectivityManager connectivityManager = (ConnectivityManager)
- mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
- connectivityManager.setGlobalProxy(proxyInfo);
+ mInjector.getConnectivityManager().setGlobalProxy(proxyInfo);
} finally {
mInjector.binderRestoreCallingIdentity(token);
}
@@ -13988,55 +14031,27 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
@Override
- public void addCrossProfileCalendarPackage(ComponentName who, String packageName) {
+ public void setCrossProfileCalendarPackages(ComponentName who, List<String> packageNames) {
if (!mHasFeature) {
return;
}
Preconditions.checkNotNull(who, "ComponentName is null");
- Preconditions.checkStringNotEmpty(packageName, "Package name is null or empty");
synchronized (getLockObject()) {
final ActiveAdmin admin = getActiveAdminForCallerLocked(
who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
- if (admin.mCrossProfileCalendarPackages.add(packageName)) {
- saveSettingsLocked(mInjector.userHandleGetCallingUserId());
- }
+ admin.mCrossProfileCalendarPackages = packageNames;
+ saveSettingsLocked(mInjector.userHandleGetCallingUserId());
}
DevicePolicyEventLogger
- .createEvent(DevicePolicyEnums.ADD_CROSS_PROFILE_CALENDAR_PACKAGE)
+ .createEvent(DevicePolicyEnums.SET_CROSS_PROFILE_CALENDAR_PACKAGES)
.setAdmin(who)
- .setStrings(packageName)
+ .setStrings(packageNames == null ? null
+ : packageNames.toArray(new String[packageNames.size()]))
.write();
}
@Override
- public boolean removeCrossProfileCalendarPackage(ComponentName who, String packageName) {
- if (!mHasFeature) {
- return false;
- }
- Preconditions.checkNotNull(who, "ComponentName is null");
- Preconditions.checkStringNotEmpty(packageName, "Package name is null or empty");
-
- boolean isRemoved = false;
- synchronized (getLockObject()) {
- final ActiveAdmin admin = getActiveAdminForCallerLocked(
- who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
- isRemoved = admin.mCrossProfileCalendarPackages.remove(packageName);
- if (isRemoved) {
- saveSettingsLocked(mInjector.userHandleGetCallingUserId());
- }
- }
- if (isRemoved) {
- DevicePolicyEventLogger
- .createEvent(DevicePolicyEnums.REMOVE_CROSS_PROFILE_CALENDAR_PACKAGE)
- .setAdmin(who)
- .setStrings(packageName)
- .write();
- }
- return isRemoved;
- }
-
- @Override
public List<String> getCrossProfileCalendarPackages(ComponentName who) {
if (!mHasFeature) {
return Collections.emptyList();
@@ -14046,7 +14061,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
synchronized (getLockObject()) {
final ActiveAdmin admin = getActiveAdminForCallerLocked(
who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
- return new ArrayList<String>(admin.mCrossProfileCalendarPackages);
+ return admin.mCrossProfileCalendarPackages;
}
}
@@ -14062,6 +14077,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
synchronized (getLockObject()) {
final ActiveAdmin admin = getProfileOwnerAdminLocked(userHandle);
if (admin != null) {
+ if (admin.mCrossProfileCalendarPackages == null) {
+ return true;
+ }
return admin.mCrossProfileCalendarPackages.contains(packageName);
}
}
@@ -14077,7 +14095,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
synchronized (getLockObject()) {
final ActiveAdmin admin = getProfileOwnerAdminLocked(userHandle);
if (admin != null) {
- return new ArrayList<String>(admin.mCrossProfileCalendarPackages);
+ return admin.mCrossProfileCalendarPackages;
}
}
return Collections.emptyList();
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 623990ba211a..586136802619 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -873,7 +873,7 @@ public final class SystemServer {
TimingsTraceLog traceLog = new TimingsTraceLog(
SYSTEM_SERVER_TIMING_ASYNC_TAG, Trace.TRACE_TAG_SYSTEM_SERVER);
traceLog.traceBegin(SECONDARY_ZYGOTE_PRELOAD);
- if (!Process.zygoteProcess.preloadDefault(Build.SUPPORTED_32_BIT_ABIS[0])) {
+ if (!Process.ZYGOTE_PROCESS.preloadDefault(Build.SUPPORTED_32_BIT_ABIS[0])) {
Slog.e(TAG, "Unable to preload default resources");
}
traceLog.traceEnd();
@@ -1561,6 +1561,12 @@ public final class SystemServer {
traceEnd();
}
+ // Grants default permissions and defines roles
+ traceBeginAndSlog("StartRoleManagerService");
+ mSystemServiceManager.startService(new RoleManagerService(
+ mSystemContext, new LegacyRoleResolutionPolicy(mSystemContext)));
+ traceEnd();
+
// We need to always start this service, regardless of whether the
// FEATURE_VOICE_RECOGNIZERS feature is set, because it needs to take care
// of initializing various settings. It will internally modify its behavior
@@ -2011,12 +2017,6 @@ public final class SystemServer {
}
traceEnd();
- // Grants default permissions and defines roles
- traceBeginAndSlog("StartRoleManagerService");
- mSystemServiceManager.startService(new RoleManagerService(
- mSystemContext, new LegacyRoleResolutionPolicy(mSystemContext)));
- traceEnd();
-
// No dependency on Webview preparation in system server. But this should
// be completed before allowing 3rd party
final String WEBVIEW_PREPARATION = "WebViewFactoryPreparation";
diff --git a/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java b/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
index 4811523b22f9..164570a84cb0 100644
--- a/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
@@ -116,6 +116,7 @@ import com.android.server.backup.testing.TransportTestUtils;
import com.android.server.backup.testing.TransportTestUtils.TransportMock;
import com.android.server.testing.shadows.FrameworkShadowLooper;
import com.android.server.testing.shadows.ShadowApplicationPackageManager;
+import com.android.server.testing.shadows.ShadowBackupActivityThread;
import com.android.server.testing.shadows.ShadowBackupDataInput;
import com.android.server.testing.shadows.ShadowBackupDataOutput;
import com.android.server.testing.shadows.ShadowEventLog;
@@ -163,6 +164,7 @@ import java.util.stream.Stream;
ShadowBackupDataOutput.class,
ShadowEventLog.class,
ShadowQueuedWork.class,
+ ShadowBackupActivityThread.class,
})
@Presubmit
public class KeyValueBackupTaskTest {
diff --git a/services/robotests/src/com/android/server/testing/shadows/ShadowAppBackupUtils.java b/services/robotests/src/com/android/server/testing/shadows/ShadowAppBackupUtils.java
index aefc871d2639..33b8aa73d293 100644
--- a/services/robotests/src/com/android/server/testing/shadows/ShadowAppBackupUtils.java
+++ b/services/robotests/src/com/android/server/testing/shadows/ShadowAppBackupUtils.java
@@ -62,7 +62,7 @@ public class ShadowAppBackupUtils {
}
@Implementation
- protected static boolean appIsEligibleForBackup(ApplicationInfo app, PackageManager pm) {
+ protected static boolean appIsEligibleForBackup(ApplicationInfo app, int userId) {
return sAppsEligibleForBackup.contains(app.packageName);
}
diff --git a/services/robotests/src/com/android/server/testing/shadows/ShadowBackupActivityThread.java b/services/robotests/src/com/android/server/testing/shadows/ShadowBackupActivityThread.java
new file mode 100644
index 000000000000..ca2e3b6dafef
--- /dev/null
+++ b/services/robotests/src/com/android/server/testing/shadows/ShadowBackupActivityThread.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.testing.shadows;
+
+import android.app.ActivityThread;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageManager;
+import android.os.RemoteException;
+
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.shadows.ShadowActivityThread;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+
+import javax.annotation.Nonnull;
+
+/**
+ * Extends the existing {@link ShadowActivityThread} to add support for
+ * {@link PackageManager#getApplicationEnabledSetting(String)} in the shadow {@link PackageManager}
+ * returned by {@link ShadowBackupActivityThread#getPackageManager()}.
+ */
+@Implements(value = ActivityThread.class, isInAndroidSdk = false, looseSignatures = true)
+public class ShadowBackupActivityThread extends ShadowActivityThread {
+ @Implementation
+ public static Object getPackageManager() {
+ ClassLoader classLoader = ShadowActivityThread.class.getClassLoader();
+ Class<?> iPackageManagerClass;
+ try {
+ iPackageManagerClass = classLoader.loadClass("android.content.pm.IPackageManager");
+ } catch (ClassNotFoundException e) {
+ throw new RuntimeException(e);
+ }
+
+ return Proxy.newProxyInstance(
+ classLoader,
+ new Class[] {iPackageManagerClass},
+ new InvocationHandler() {
+ @Override
+ public Object invoke(Object proxy, @Nonnull Method method, Object[] args)
+ throws Exception {
+ if (method.getName().equals("getApplicationInfo")) {
+ String packageName = (String) args[0];
+ int flags = (Integer) args[1];
+
+ try {
+ return RuntimeEnvironment.application
+ .getPackageManager()
+ .getApplicationInfo(packageName, flags);
+ } catch (PackageManager.NameNotFoundException e) {
+ throw new RemoteException(e.getMessage());
+ }
+ } else if (method.getName().equals("getApplicationEnabledSetting")) {
+ return 0;
+ } else {
+ return null;
+ }
+ }
+ });
+ }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java
index 6a153d5346ed..6386b3b396ae 100644
--- a/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java
@@ -28,14 +28,18 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSess
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
+import static com.android.server.AlarmManagerService.ACTIVE_INDEX;
+import static com.android.server.AlarmManagerService.AlarmHandler.APP_STANDBY_BUCKET_CHANGED;
+import static com.android.server.AlarmManagerService.AlarmHandler.APP_STANDBY_PAROLE_CHANGED;
import static com.android.server.AlarmManagerService.Constants.KEY_ALLOW_WHILE_IDLE_LONG_TIME;
import static com.android.server.AlarmManagerService.Constants.KEY_ALLOW_WHILE_IDLE_SHORT_TIME;
-import static com.android.server.AlarmManagerService.Constants
- .KEY_ALLOW_WHILE_IDLE_WHITELIST_DURATION;
+import static com.android.server.AlarmManagerService.Constants.KEY_ALLOW_WHILE_IDLE_WHITELIST_DURATION;
+import static com.android.server.AlarmManagerService.Constants.KEY_APP_STANDBY_QUOTAS_ENABLED;
import static com.android.server.AlarmManagerService.Constants.KEY_LISTENER_TIMEOUT;
import static com.android.server.AlarmManagerService.Constants.KEY_MAX_INTERVAL;
import static com.android.server.AlarmManagerService.Constants.KEY_MIN_FUTURITY;
import static com.android.server.AlarmManagerService.Constants.KEY_MIN_INTERVAL;
+import static com.android.server.AlarmManagerService.WORKING_INDEX;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@@ -58,6 +62,7 @@ import android.content.Context;
import android.content.Intent;
import android.os.Handler;
import android.os.Looper;
+import android.os.Message;
import android.os.PowerManager;
import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
@@ -65,7 +70,6 @@ import android.provider.Settings;
import android.util.Log;
import android.util.SparseArray;
-import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.annotations.GuardedBy;
@@ -83,7 +87,6 @@ import org.mockito.quality.Strictness;
import java.util.ArrayList;
@Presubmit
-@SmallTest
@RunWith(AndroidJUnit4.class)
public class AlarmManagerServiceTest {
private static final String TAG = AlarmManagerServiceTest.class.getSimpleName();
@@ -91,7 +94,9 @@ public class AlarmManagerServiceTest {
private static final int SYSTEM_UI_UID = 123456789;
private static final int TEST_CALLING_UID = 12345;
+ private long mAppStandbyWindow;
private AlarmManagerService mService;
+ private UsageStatsManagerInternal.AppIdleStateChangeListener mAppStandbyListener;
@Mock
private ContentResolver mMockResolver;
@Mock
@@ -229,16 +234,23 @@ public class AlarmManagerServiceTest {
mService = new AlarmManagerService(mMockContext, mInjector);
spyOn(mService);
doNothing().when(mService).publishBinderService(any(), any());
+
mService.onStart();
- mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
spyOn(mService.mHandler);
-
- assertEquals(0, mService.mConstants.MIN_FUTURITY);
assertEquals(mService.mSystemUiUid, SYSTEM_UI_UID);
assertEquals(mService.mClockReceiver, mClockReceiver);
assertEquals(mService.mWakeLock, mWakeLock);
verify(mIActivityManager).registerUidObserver(any(IUidObserver.class), anyInt(), anyInt(),
isNull());
+
+ // Other boot phases don't matter
+ mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
+ assertEquals(0, mService.mConstants.MIN_FUTURITY);
+ mAppStandbyWindow = mService.mConstants.APP_STANDBY_WINDOW;
+ ArgumentCaptor<UsageStatsManagerInternal.AppIdleStateChangeListener> captor =
+ ArgumentCaptor.forClass(UsageStatsManagerInternal.AppIdleStateChangeListener.class);
+ verify(mUsageStatsManagerInternal).addAppIdleStateChangeListener(captor.capture());
+ mAppStandbyListener = captor.getValue();
}
private void setTestAlarm(int type, long triggerTime, PendingIntent operation) {
@@ -254,6 +266,28 @@ public class AlarmManagerServiceTest {
return mockPi;
}
+ /**
+ * Careful while calling as this will replace any existing settings for the calling test.
+ */
+ private void setQuotasEnabled(boolean enabled) {
+ final StringBuilder constantsBuilder = new StringBuilder();
+ constantsBuilder.append(KEY_MIN_FUTURITY);
+ constantsBuilder.append("=0,");
+ // Capping active and working quotas to make testing feasible.
+ constantsBuilder.append(mService.mConstants.KEYS_APP_STANDBY_QUOTAS[ACTIVE_INDEX]);
+ constantsBuilder.append("=8,");
+ constantsBuilder.append(mService.mConstants.KEYS_APP_STANDBY_QUOTAS[WORKING_INDEX]);
+ constantsBuilder.append("=5,");
+ if (!enabled) {
+ constantsBuilder.append(KEY_APP_STANDBY_QUOTAS_ENABLED);
+ constantsBuilder.append("=false,");
+ }
+ doReturn(constantsBuilder.toString()).when(() -> Settings.Global.getString(mMockResolver,
+ Settings.Global.ALARM_MANAGER_CONSTANTS));
+ mService.mConstants.onChange(false, null);
+ assertEquals(mService.mConstants.APP_STANDBY_QUOTAS_ENABLED, enabled);
+ }
+
@Test
public void testSingleAlarmSet() {
final long triggerTime = mNowElapsedTest + 5000;
@@ -346,6 +380,7 @@ public class AlarmManagerServiceTest {
@Test
public void testStandbyBucketDelay_workingSet() throws Exception {
+ setQuotasEnabled(false);
setTestAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 5, getNewMockPendingIntent());
setTestAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 6, getNewMockPendingIntent());
assertEquals(mNowElapsedTest + 5, mTestTimer.getElapsed());
@@ -366,6 +401,7 @@ public class AlarmManagerServiceTest {
@Test
public void testStandbyBucketDelay_frequent() throws Exception {
+ setQuotasEnabled(false);
setTestAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 5, getNewMockPendingIntent());
setTestAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 6, getNewMockPendingIntent());
assertEquals(mNowElapsedTest + 5, mTestTimer.getElapsed());
@@ -385,6 +421,7 @@ public class AlarmManagerServiceTest {
@Test
public void testStandbyBucketDelay_rare() throws Exception {
+ setQuotasEnabled(false);
setTestAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 5, getNewMockPendingIntent());
setTestAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 6, getNewMockPendingIntent());
assertEquals(mNowElapsedTest + 5, mTestTimer.getElapsed());
@@ -402,6 +439,253 @@ public class AlarmManagerServiceTest {
assertEquals("Incorrect next alarm trigger.", expectedNextTrigger, mTestTimer.getElapsed());
}
+ private void testQuotasDeferralOnSet(int standbyBucket) throws Exception {
+ final int quota = mService.getQuotaForBucketLocked(standbyBucket);
+ when(mUsageStatsManagerInternal.getAppStandbyBucket(eq(TEST_CALLING_PACKAGE), anyInt(),
+ anyLong())).thenReturn(standbyBucket);
+ final long firstTrigger = mNowElapsedTest + 10;
+ for (int i = 0; i < quota; i++) {
+ setTestAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 10 + i,
+ getNewMockPendingIntent());
+ mNowElapsedTest = mTestTimer.getElapsed();
+ mTestTimer.expire();
+ }
+ // This one should get deferred on set
+ setTestAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + quota + 10,
+ getNewMockPendingIntent());
+ final long expectedNextTrigger = firstTrigger + 1 + mAppStandbyWindow;
+ assertEquals("Incorrect next alarm trigger", expectedNextTrigger, mTestTimer.getElapsed());
+ }
+
+ private void testQuotasDeferralOnExpiration(int standbyBucket) throws Exception {
+ final int quota = mService.getQuotaForBucketLocked(standbyBucket);
+ when(mUsageStatsManagerInternal.getAppStandbyBucket(eq(TEST_CALLING_PACKAGE), anyInt(),
+ anyLong())).thenReturn(standbyBucket);
+ final long firstTrigger = mNowElapsedTest + 10;
+ for (int i = 0; i < quota; i++) {
+ setTestAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 10 + i,
+ getNewMockPendingIntent());
+ }
+ // This one should get deferred after the latest alarm expires
+ setTestAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + quota + 10,
+ getNewMockPendingIntent());
+ for (int i = 0; i < quota; i++) {
+ mNowElapsedTest = mTestTimer.getElapsed();
+ mTestTimer.expire();
+ }
+ final long expectedNextTrigger = firstTrigger + 1 + mAppStandbyWindow;
+ assertEquals("Incorrect next alarm trigger", expectedNextTrigger, mTestTimer.getElapsed());
+ }
+
+ private void testQuotasNoDeferral(int standbyBucket) throws Exception {
+ final int quota = mService.getQuotaForBucketLocked(standbyBucket);
+ when(mUsageStatsManagerInternal.getAppStandbyBucket(eq(TEST_CALLING_PACKAGE), anyInt(),
+ anyLong())).thenReturn(standbyBucket);
+ final long firstTrigger = mNowElapsedTest + 10;
+ for (int i = 0; i < quota; i++) {
+ setTestAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 10 + i,
+ getNewMockPendingIntent());
+ }
+ // This delivery time maintains the quota invariant. Should not be deferred.
+ final long expectedNextTrigger = firstTrigger + mAppStandbyWindow + 5;
+ setTestAlarm(ELAPSED_REALTIME_WAKEUP, expectedNextTrigger, getNewMockPendingIntent());
+ for (int i = 0; i < quota; i++) {
+ mNowElapsedTest = mTestTimer.getElapsed();
+ mTestTimer.expire();
+ }
+ assertEquals("Incorrect next alarm trigger", expectedNextTrigger, mTestTimer.getElapsed());
+ }
+
+ @Test
+ public void testActiveQuota_deferredOnSet() throws Exception {
+ setQuotasEnabled(true);
+ testQuotasDeferralOnSet(STANDBY_BUCKET_ACTIVE);
+ }
+
+ @Test
+ public void testActiveQuota_deferredOnExpiration() throws Exception {
+ setQuotasEnabled(true);
+ testQuotasDeferralOnExpiration(STANDBY_BUCKET_ACTIVE);
+ }
+
+ @Test
+ public void testActiveQuota_notDeferred() throws Exception {
+ setQuotasEnabled(true);
+ testQuotasNoDeferral(STANDBY_BUCKET_ACTIVE);
+ }
+
+ @Test
+ public void testWorkingQuota_deferredOnSet() throws Exception {
+ setQuotasEnabled(true);
+ testQuotasDeferralOnSet(STANDBY_BUCKET_WORKING_SET);
+ }
+
+ @Test
+ public void testWorkingQuota_deferredOnExpiration() throws Exception {
+ setQuotasEnabled(true);
+ testQuotasDeferralOnExpiration(STANDBY_BUCKET_WORKING_SET);
+ }
+
+ @Test
+ public void testWorkingQuota_notDeferred() throws Exception {
+ setQuotasEnabled(true);
+ testQuotasNoDeferral(STANDBY_BUCKET_WORKING_SET);
+ }
+
+ @Test
+ public void testFrequentQuota_deferredOnSet() throws Exception {
+ setQuotasEnabled(true);
+ testQuotasDeferralOnSet(STANDBY_BUCKET_FREQUENT);
+ }
+
+ @Test
+ public void testFrequentQuota_deferredOnExpiration() throws Exception {
+ setQuotasEnabled(true);
+ testQuotasDeferralOnExpiration(STANDBY_BUCKET_FREQUENT);
+ }
+
+ @Test
+ public void testFrequentQuota_notDeferred() throws Exception {
+ setQuotasEnabled(true);
+ testQuotasNoDeferral(STANDBY_BUCKET_FREQUENT);
+ }
+
+ @Test
+ public void testRareQuota_deferredOnSet() throws Exception {
+ setQuotasEnabled(true);
+ testQuotasDeferralOnSet(STANDBY_BUCKET_RARE);
+ }
+
+ @Test
+ public void testRareQuota_deferredOnExpiration() throws Exception {
+ setQuotasEnabled(true);
+ testQuotasDeferralOnExpiration(STANDBY_BUCKET_RARE);
+ }
+
+ @Test
+ public void testRareQuota_notDeferred() throws Exception {
+ setQuotasEnabled(true);
+ testQuotasNoDeferral(STANDBY_BUCKET_RARE);
+ }
+
+ private void sendAndHandleBucketChanged(int bucket) {
+ when(mUsageStatsManagerInternal.getAppStandbyBucket(eq(TEST_CALLING_PACKAGE), anyInt(),
+ anyLong())).thenReturn(bucket);
+ // Stubbing the handler call to simulate it synchronously here.
+ doReturn(true).when(mService.mHandler).sendMessage(any(Message.class));
+ mAppStandbyListener.onAppIdleStateChanged(TEST_CALLING_PACKAGE,
+ UserHandle.getUserId(TEST_CALLING_UID), false, bucket, 0);
+ final ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
+ verify(mService.mHandler, atLeastOnce()).sendMessage(messageCaptor.capture());
+ final Message lastMessage = messageCaptor.getValue();
+ assertEquals("Unexpected message send to handler", lastMessage.what,
+ APP_STANDBY_BUCKET_CHANGED);
+ mService.mHandler.handleMessage(lastMessage);
+ }
+
+ @Test
+ public void testQuotaDowngrade() throws Exception {
+ setQuotasEnabled(true);
+ final int workingQuota = mService.getQuotaForBucketLocked(STANDBY_BUCKET_WORKING_SET);
+ when(mUsageStatsManagerInternal.getAppStandbyBucket(eq(TEST_CALLING_PACKAGE), anyInt(),
+ anyLong())).thenReturn(STANDBY_BUCKET_WORKING_SET);
+
+ final long firstTrigger = mNowElapsedTest + 10;
+ for (int i = 0; i < workingQuota; i++) {
+ setTestAlarm(ELAPSED_REALTIME_WAKEUP, firstTrigger + i, getNewMockPendingIntent());
+ }
+ // No deferrals now.
+ for (int i = 0; i < workingQuota - 1; i++) {
+ mNowElapsedTest = mTestTimer.getElapsed();
+ assertEquals(firstTrigger + i, mNowElapsedTest);
+ mTestTimer.expire();
+ }
+ // The next upcoming alarm in queue should also be set as expected.
+ assertEquals(firstTrigger + workingQuota - 1, mTestTimer.getElapsed());
+ // Downgrading the bucket now
+ sendAndHandleBucketChanged(STANDBY_BUCKET_RARE);
+ final int rareQuota = mService.getQuotaForBucketLocked(STANDBY_BUCKET_RARE);
+ // The last alarm should now be deferred.
+ final long expectedNextTrigger = (firstTrigger + workingQuota - 1 - rareQuota)
+ + mAppStandbyWindow + 1;
+ assertEquals("Incorrect next alarm trigger", expectedNextTrigger, mTestTimer.getElapsed());
+ }
+
+ @Test
+ public void testQuotaUpgrade() throws Exception {
+ setQuotasEnabled(true);
+ final int frequentQuota = mService.getQuotaForBucketLocked(STANDBY_BUCKET_FREQUENT);
+ when(mUsageStatsManagerInternal.getAppStandbyBucket(eq(TEST_CALLING_PACKAGE), anyInt(),
+ anyLong())).thenReturn(STANDBY_BUCKET_FREQUENT);
+
+ final long firstTrigger = mNowElapsedTest + 10;
+ for (int i = 0; i < frequentQuota + 1; i++) {
+ setTestAlarm(ELAPSED_REALTIME_WAKEUP, firstTrigger + i, getNewMockPendingIntent());
+ if (i < frequentQuota) {
+ mNowElapsedTest = mTestTimer.getElapsed();
+ mTestTimer.expire();
+ }
+ }
+ // The last alarm should be deferred due to exceeding the quota
+ final long deferredTrigger = firstTrigger + 1 + mAppStandbyWindow;
+ assertEquals(deferredTrigger, mTestTimer.getElapsed());
+
+ // Upgrading the bucket now
+ sendAndHandleBucketChanged(STANDBY_BUCKET_ACTIVE);
+ // The last alarm should now be rescheduled to go as per original expectations
+ final long originalTrigger = firstTrigger + frequentQuota;
+ assertEquals("Incorrect next alarm trigger", originalTrigger, mTestTimer.getElapsed());
+ }
+
+ private void sendAndHandleParoleChanged(boolean parole) {
+ // Stubbing the handler call to simulate it synchronously here.
+ doReturn(true).when(mService.mHandler).sendMessage(any(Message.class));
+ mAppStandbyListener.onParoleStateChanged(parole);
+ final ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
+ verify(mService.mHandler, atLeastOnce()).sendMessage(messageCaptor.capture());
+ final Message lastMessage = messageCaptor.getValue();
+ assertEquals("Unexpected message send to handler", lastMessage.what,
+ APP_STANDBY_PAROLE_CHANGED);
+ mService.mHandler.handleMessage(lastMessage);
+ }
+
+ @Test
+ public void testParole() throws Exception {
+ setQuotasEnabled(true);
+ final int workingQuota = mService.getQuotaForBucketLocked(STANDBY_BUCKET_WORKING_SET);
+ when(mUsageStatsManagerInternal.getAppStandbyBucket(eq(TEST_CALLING_PACKAGE), anyInt(),
+ anyLong())).thenReturn(STANDBY_BUCKET_WORKING_SET);
+
+ final long firstTrigger = mNowElapsedTest + 10;
+ final int totalAlarms = workingQuota + 10;
+ for (int i = 0; i < totalAlarms; i++) {
+ setTestAlarm(ELAPSED_REALTIME_WAKEUP, firstTrigger + i, getNewMockPendingIntent());
+ }
+ // Use up the quota, no deferrals expected.
+ for (int i = 0; i < workingQuota; i++) {
+ mNowElapsedTest = mTestTimer.getElapsed();
+ assertEquals(firstTrigger + i, mNowElapsedTest);
+ mTestTimer.expire();
+ }
+ // Any subsequent alarms in queue should all be deferred
+ assertEquals(firstTrigger + mAppStandbyWindow + 1, mTestTimer.getElapsed());
+ // Paroling now
+ sendAndHandleParoleChanged(true);
+
+ // Subsequent alarms should now go off as per original expectations.
+ for (int i = 0; i < 5; i++) {
+ mNowElapsedTest = mTestTimer.getElapsed();
+ assertEquals(firstTrigger + workingQuota + i, mNowElapsedTest);
+ mTestTimer.expire();
+ }
+ // Come out of parole
+ sendAndHandleParoleChanged(false);
+
+ // Subsequent alarms should again get deferred
+ final long expectedNextTrigger = (firstTrigger + 5) + 1 + mAppStandbyWindow;
+ assertEquals("Incorrect next alarm trigger", expectedNextTrigger, mTestTimer.getElapsed());
+ }
+
@Test
public void testAlarmRestrictedInBatterSaver() throws Exception {
final ArgumentCaptor<AppStateTracker.Listener> listenerArgumentCaptor =
diff --git a/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverStateMachineTest.java b/services/tests/mockingservicestests/src/com/android/server/power/batterysaver/BatterySaverStateMachineTest.java
index f31ca55d422e..5009d64b94d4 100644
--- a/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverStateMachineTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/power/batterysaver/BatterySaverStateMachineTest.java
@@ -19,51 +19,41 @@ import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
+import android.app.NotificationManager;
import android.content.ContentResolver;
+import android.content.Context;
import android.content.res.Resources;
import android.provider.Settings.Global;
-import android.test.mock.MockContext;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
-import com.google.common.base.Objects;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.HashMap;
+import java.util.Objects;
/**
- atest $ANDROID_BUILD_TOP/frameworks/base/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverStateMachineTest.java
+ * atest com.android.server.power.batterysaver.BatterySaverStateMachineTest
*/
@SmallTest
@RunWith(AndroidJUnit4.class)
public class BatterySaverStateMachineTest {
- private MyMockContext mMockContext;
+ private Context mMockContext;
private ContentResolver mMockContextResolver;
private BatterySaverController mMockBatterySaverController;
+ private NotificationManager mMockNotificationManager;
private Device mDevice;
private TestableBatterySaverStateMachine mTarget;
private Resources mMockResources;
- private class MyMockContext extends MockContext {
- @Override
- public ContentResolver getContentResolver() {
- return mMockContextResolver;
- }
-
- @Override
- public Resources getResources() {
- return mMockResources;
- }
- }
-
private DevicePersistedState mPersistedState;
private class DevicePersistedState {
@@ -116,6 +106,10 @@ public class BatterySaverStateMachineTest {
mPersistedState.global.getOrDefault(Global.LOW_POWER_MODE, 0) != 0,
mPersistedState.global.getOrDefault(Global.LOW_POWER_MODE_STICKY, 0) != 0,
mDevice.getLowPowerModeTriggerLevel(),
+ mPersistedState.global.getOrDefault(
+ Global.LOW_POWER_MODE_STICKY_AUTO_DISABLE_ENABLED, 0) != 0,
+ mPersistedState.global.getOrDefault(
+ Global.LOW_POWER_MODE_STICKY_AUTO_DISABLE_LEVEL, 90),
mPersistedState.global.getOrDefault(Global.AUTOMATIC_POWER_SAVER_MODE, 0),
mPersistedState.global.getOrDefault(
Global.DYNAMIC_POWER_SAVINGS_ENABLED, 0) != 0,
@@ -137,13 +131,13 @@ public class BatterySaverStateMachineTest {
* Test target class.
*/
private class TestableBatterySaverStateMachine extends BatterySaverStateMachine {
- public TestableBatterySaverStateMachine() {
+ TestableBatterySaverStateMachine() {
super(new Object(), mMockContext, mMockBatterySaverController);
}
@Override
protected void putGlobalSetting(String key, int value) {
- if (Objects.equal(mPersistedState.global.get(key), value)) {
+ if (Objects.equals(mPersistedState.global.get(key), value)) {
return;
}
mDevice.putGlobalSetting(key, value);
@@ -163,15 +157,25 @@ public class BatterySaverStateMachineTest {
void runOnBgThreadLazy(Runnable r, int delayMillis) {
r.run();
}
+
+ @Override
+ void triggerDynamicModeNotification() {
+ // Do nothing
+ }
}
@Before
public void setUp() {
- mMockContext = new MyMockContext();
+ mMockContext = mock(Context.class);
mMockContextResolver = mock(ContentResolver.class);
mMockBatterySaverController = mock(BatterySaverController.class);
+ mMockNotificationManager = mock(NotificationManager.class);
mMockResources = mock(Resources.class);
+ doReturn(mMockContextResolver).when(mMockContext).getContentResolver();
+ doReturn(mMockResources).when(mMockContext).getResources();
+ doReturn(mMockNotificationManager).when(mMockContext)
+ .getSystemService(NotificationManager.class);
doAnswer((inv) -> mDevice.batterySaverEnabled = inv.getArgument(0))
.when(mMockBatterySaverController).enableBatterySaver(anyBoolean(), anyInt());
when(mMockBatterySaverController.isEnabled())
@@ -446,8 +450,9 @@ public class BatterySaverStateMachineTest {
}
@Test
- public void testAutoBatterySaver_withSticky() {
+ public void testAutoBatterySaver_withSticky_withAutoOffDisabled() {
mDevice.putGlobalSetting(Global.LOW_POWER_MODE_TRIGGER_LEVEL, 50);
+ mDevice.putGlobalSetting(Global.LOW_POWER_MODE_STICKY_AUTO_DISABLE_ENABLED, 0);
mTarget.setBatterySaverEnabledManually(true);
@@ -518,6 +523,197 @@ public class BatterySaverStateMachineTest {
}
@Test
+ public void testAutoBatterySaver_withSticky_withAutoOffEnabled() {
+ mDevice.putGlobalSetting(Global.LOW_POWER_MODE_TRIGGER_LEVEL, 50);
+ mDevice.putGlobalSetting(Global.LOW_POWER_MODE_STICKY_AUTO_DISABLE_ENABLED, 1);
+ mDevice.putGlobalSetting(Global.LOW_POWER_MODE_STICKY_AUTO_DISABLE_LEVEL, 90);
+
+ // Scenario 1: User turns BS on manually above the threshold, it shouldn't turn off even
+ // with battery level change above threshold.
+ mDevice.setBatteryLevel(100);
+ mTarget.setBatterySaverEnabledManually(true);
+
+ assertEquals(true, mDevice.batterySaverEnabled);
+ assertEquals(100, mPersistedState.batteryLevel);
+ assertEquals(false, mPersistedState.batteryLow);
+
+ mDevice.setBatteryLevel(95);
+
+ assertEquals(true, mDevice.batterySaverEnabled); // Stays on.
+ assertEquals(95, mPersistedState.batteryLevel);
+ assertEquals(false, mPersistedState.batteryLow);
+
+ // Scenario 2: User turns BS on manually above the threshold then charges device. BS
+ // shouldn't turn back on.
+ mDevice.setPowered(true);
+
+ assertEquals(false, mDevice.batterySaverEnabled);
+ assertEquals(95, mPersistedState.batteryLevel);
+ assertEquals(false, mPersistedState.batteryLow);
+
+ mDevice.setBatteryLevel(97);
+ mDevice.setPowered(false);
+
+ assertEquals(false, mDevice.batterySaverEnabled); // Sticky BS no longer enabled.
+ assertEquals(97, mPersistedState.batteryLevel);
+ assertEquals(false, mPersistedState.batteryLow);
+
+ // Scenario 3: User turns BS on manually above the threshold. Device drains below
+ // threshold and then charged to below threshold. Sticky BS should activate.
+ mTarget.setBatterySaverEnabledManually(true);
+ mDevice.setBatteryLevel(30);
+
+ assertEquals(true, mDevice.batterySaverEnabled);
+ assertEquals(30, mPersistedState.batteryLevel);
+ assertEquals(true, mPersistedState.batteryLow);
+
+ mDevice.setPowered(true);
+ mDevice.setBatteryLevel(80);
+
+ assertEquals(false, mDevice.batterySaverEnabled);
+ assertEquals(80, mPersistedState.batteryLevel);
+ assertEquals(false, mPersistedState.batteryLow);
+
+ mDevice.setPowered(false);
+
+ assertEquals(true, mDevice.batterySaverEnabled); // Still enabled.
+ assertEquals(80, mPersistedState.batteryLevel);
+ assertEquals(false, mPersistedState.batteryLow);
+
+ mDevice.setBatteryLevel(30);
+
+ assertEquals(true, mDevice.batterySaverEnabled);
+ assertEquals(30, mPersistedState.batteryLevel);
+ assertEquals(true, mPersistedState.batteryLow);
+
+ // Scenario 4: User turns BS on manually above the threshold. Device drains below
+ // threshold and is eventually charged to above threshold. Sticky BS should turn off.
+ mDevice.setPowered(true);
+ mDevice.setBatteryLevel(90);
+
+ assertEquals(false, mDevice.batterySaverEnabled);
+ assertEquals(90, mPersistedState.batteryLevel);
+ assertEquals(false, mPersistedState.batteryLow);
+
+ mDevice.setPowered(false);
+
+ assertEquals(false, mDevice.batterySaverEnabled); // Sticky BS no longer enabled.
+ assertEquals(90, mPersistedState.batteryLevel);
+ assertEquals(false, mPersistedState.batteryLow);
+
+ // Scenario 5: User turns BS on manually below threshold and charges to below threshold.
+ // Sticky BS should activate.
+ mDevice.setBatteryLevel(70);
+
+ assertEquals(false, mDevice.batterySaverEnabled);
+ assertEquals(70, mPersistedState.batteryLevel);
+ assertEquals(false, mPersistedState.batteryLow);
+
+ mTarget.setBatterySaverEnabledManually(true);
+
+ assertEquals(true, mDevice.batterySaverEnabled);
+ assertEquals(70, mPersistedState.batteryLevel);
+ assertEquals(false, mPersistedState.batteryLow);
+
+ mDevice.setPowered(true);
+ mDevice.setBatteryLevel(80);
+
+ assertEquals(false, mDevice.batterySaverEnabled);
+ assertEquals(80, mPersistedState.batteryLevel);
+ assertEquals(false, mPersistedState.batteryLow);
+
+ mDevice.setPowered(false);
+
+ assertEquals(true, mDevice.batterySaverEnabled); // Sticky BS still on.
+ assertEquals(80, mPersistedState.batteryLevel);
+ assertEquals(false, mPersistedState.batteryLow);
+
+ // Scenario 6: User turns BS on manually below threshold and eventually charges to above
+ // threshold. Sticky BS should turn off.
+
+ mDevice.setPowered(true);
+ mDevice.setBatteryLevel(95);
+ mDevice.setPowered(false);
+
+ assertEquals(false, mDevice.batterySaverEnabled);
+ assertEquals(95, mPersistedState.batteryLevel);
+ assertEquals(false, mPersistedState.batteryLow);
+
+ // Scenario 7: User turns BS on above threshold and then reboots device. Sticky BS
+ // shouldn't activate.
+ mTarget.setBatterySaverEnabledManually(true);
+ mPersistedState.batteryLevel = 93;
+
+ initDevice();
+
+ assertEquals(false, mDevice.batterySaverEnabled);
+ assertEquals(93, mPersistedState.batteryLevel);
+ assertEquals(false, mPersistedState.batteryLow);
+
+ // Scenario 8: User turns BS on below threshold and then reboots device without charging.
+ // Sticky BS should activate.
+ mDevice.setBatteryLevel(75);
+ mTarget.setBatterySaverEnabledManually(true);
+ assertEquals(true, mDevice.batterySaverEnabled);
+ assertEquals(75, mPersistedState.batteryLevel);
+ assertEquals(false, mPersistedState.batteryLow);
+
+ initDevice();
+
+ assertEquals(true, mDevice.batterySaverEnabled);
+ assertEquals(75, mPersistedState.batteryLevel);
+ assertEquals(false, mPersistedState.batteryLow);
+
+ // Scenario 9: User turns BS on below threshold and then reboots device after charging
+ // above threshold. Sticky BS shouldn't activate.
+ mDevice.setBatteryLevel(80);
+ mTarget.setBatterySaverEnabledManually(true);
+ mPersistedState.batteryLevel = 100;
+
+ initDevice();
+
+ assertEquals(false, mDevice.batterySaverEnabled);
+ assertEquals(100, mPersistedState.batteryLevel);
+ assertEquals(false, mPersistedState.batteryLow);
+
+ // Scenario 10: Somehow autoDisableLevel is set to a value below lowPowerModeTriggerLevel
+ // and then user enables manually above both thresholds, discharges below
+ // autoDisableLevel and then charges up to between autoDisableLevel and
+ // lowPowerModeTriggerLevel. Sticky BS shouldn't activate, but BS should still be on
+ // because the level is below lowPowerModeTriggerLevel.
+ mDevice.putGlobalSetting(Global.LOW_POWER_MODE_TRIGGER_LEVEL, 75);
+ mDevice.putGlobalSetting(Global.LOW_POWER_MODE_STICKY_AUTO_DISABLE_ENABLED, 1);
+ mDevice.putGlobalSetting(Global.LOW_POWER_MODE_STICKY_AUTO_DISABLE_LEVEL, 60);
+ initDevice();
+
+ mDevice.setBatteryLevel(90);
+ mTarget.setBatterySaverEnabledManually(true);
+
+ assertEquals(true, mDevice.batterySaverEnabled);
+ assertEquals(90, mPersistedState.batteryLevel);
+ assertEquals(false, mPersistedState.batteryLow);
+
+ mDevice.setBatteryLevel(50);
+
+ assertEquals(true, mDevice.batterySaverEnabled);
+ assertEquals(50, mPersistedState.batteryLevel);
+ assertEquals(true, mPersistedState.batteryLow);
+
+ mDevice.setPowered(true);
+ mDevice.setBatteryLevel(65);
+
+ assertEquals(false, mDevice.batterySaverEnabled);
+ assertEquals(65, mPersistedState.batteryLevel);
+ assertEquals(true, mPersistedState.batteryLow);
+
+ mDevice.setPowered(false);
+
+ assertEquals(true, mDevice.batterySaverEnabled);
+ assertEquals(65, mPersistedState.batteryLevel);
+ assertEquals(true, mPersistedState.batteryLow);
+ }
+
+ @Test
public void testAutoBatterySaver_withStickyDisabled() {
when(mMockResources.getBoolean(
com.android.internal.R.bool.config_batterySaverStickyBehaviourDisabled))
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInputFilterTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInputFilterTest.java
new file mode 100644
index 000000000000..782dc3e2ad45
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInputFilterTest.java
@@ -0,0 +1,275 @@
+/*
+ * Copyright (C) 2018 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.accessibility;
+
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS;
+import static android.view.WindowManagerPolicyConstants.FLAG_PASS_TO_USER;
+
+import static com.android.server.accessibility.AccessibilityInputFilter.FLAG_FEATURE_AUTOCLICK;
+import static com.android.server.accessibility.AccessibilityInputFilter.FLAG_FEATURE_CONTROL_SCREEN_MAGNIFIER;
+import static com.android.server.accessibility.AccessibilityInputFilter.FLAG_FEATURE_FILTER_KEY_EVENTS;
+import static com.android.server.accessibility.AccessibilityInputFilter.FLAG_FEATURE_INJECT_MOTION_EVENTS;
+import static com.android.server.accessibility.AccessibilityInputFilter.FLAG_FEATURE_TOUCH_EXPLORATION;
+import static com.android.server.accessibility.AccessibilityInputFilter.FLAG_FEATURE_TRIGGERED_SCREEN_MAGNIFIER;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.hardware.display.DisplayManagerGlobal;
+import android.os.Looper;
+import android.os.SystemClock;
+import android.util.SparseArray;
+import android.view.Display;
+import android.view.DisplayInfo;
+import android.view.InputDevice;
+import android.view.InputEvent;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Tests for AccessibilityInputFilterTest
+ */
+@RunWith(AndroidJUnit4.class)
+public class AccessibilityInputFilterTest {
+ private static int sNextDisplayId = DEFAULT_DISPLAY;
+ private static final int SECOND_DISPLAY = DEFAULT_DISPLAY + 1;
+ private static final float DEFAULT_X = 100f;
+ private static final float DEFAULT_Y = 100f;
+
+ private final SparseArray<EventStreamTransformation> mEventHandler = new SparseArray<>(0);
+ private final ArrayList<Display> mDisplayList = new ArrayList<>();
+ private final int mFeatures = FLAG_FEATURE_AUTOCLICK
+ | FLAG_FEATURE_TOUCH_EXPLORATION
+ | FLAG_FEATURE_CONTROL_SCREEN_MAGNIFIER
+ | FLAG_FEATURE_TRIGGERED_SCREEN_MAGNIFIER
+ | FLAG_FEATURE_INJECT_MOTION_EVENTS
+ | FLAG_FEATURE_FILTER_KEY_EVENTS;
+
+ // The expected order of EventStreamTransformations.
+ private final Class[] mExpectedEventHandlerTypes =
+ {KeyboardInterceptor.class, MotionEventInjector.class,
+ MagnificationGestureHandler.class, TouchExplorer.class,
+ AutoclickController.class, AccessibilityInputFilter.class};
+
+ private MagnificationController mMockMagnificationController;
+ private AccessibilityManagerService mAms;
+ private AccessibilityInputFilter mA11yInputFilter;
+ private EventCaptor mCaptor1;
+ private EventCaptor mCaptor2;
+ private long mLastDownTime = Integer.MIN_VALUE;
+
+ private class EventCaptor implements EventStreamTransformation {
+ List<InputEvent> mEvents = new ArrayList<>();
+
+ @Override
+ public void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+ mEvents.add(event.copy());
+ }
+
+ @Override
+ public void onKeyEvent(KeyEvent event, int policyFlags) {
+ mEvents.add(event.copy());
+ }
+
+ @Override
+ public void setNext(EventStreamTransformation next) {
+ }
+
+ @Override
+ public EventStreamTransformation getNext() {
+ return null;
+ }
+
+ @Override
+ public void clearEvents(int inputSource) {
+ clear();
+ }
+
+ private void clear() {
+ mEvents.clear();
+ }
+ }
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ Context context = InstrumentationRegistry.getContext();
+
+ setDisplayCount(1);
+ mAms = spy(new AccessibilityManagerService(context));
+ mMockMagnificationController = mock(MagnificationController.class);
+ mA11yInputFilter = new AccessibilityInputFilter(context, mAms, mEventHandler);
+ mA11yInputFilter.onInstalled();
+
+ when(mAms.getValidDisplayList()).thenReturn(mDisplayList);
+ when(mAms.getMagnificationController()).thenReturn(mMockMagnificationController);
+ }
+
+ @After
+ public void tearDown() {
+ mA11yInputFilter.onUninstalled();
+ }
+
+ @Test
+ public void testEventHandler_shouldChangeAfterSetUserAndEnabledFeatures() {
+ prepareLooper();
+
+ // Check if there is no mEventHandler when no feature is set.
+ assertEquals(0, mEventHandler.size());
+
+ // Check if mEventHandler is added/removed after setting a11y features.
+ mA11yInputFilter.setUserAndEnabledFeatures(0, mFeatures);
+ assertEquals(1, mEventHandler.size());
+
+ mA11yInputFilter.setUserAndEnabledFeatures(0, 0);
+ assertEquals(0, mEventHandler.size());
+ }
+
+ @Test
+ public void testEventHandler_shouldChangeAfterOnDisplayChanged() {
+ prepareLooper();
+
+ // Check if there is only one mEventHandler when there is one default display.
+ mA11yInputFilter.setUserAndEnabledFeatures(0, mFeatures);
+ assertEquals(1, mEventHandler.size());
+
+ // Check if it has correct numbers of mEventHandler for corresponding displays.
+ setDisplayCount(4);
+ mA11yInputFilter.onDisplayChanged();
+ assertEquals(4, mEventHandler.size());
+
+ setDisplayCount(2);
+ mA11yInputFilter.onDisplayChanged();
+ assertEquals(2, mEventHandler.size());
+ }
+
+ @Test
+ public void testEventHandler_shouldHaveCorrectOrderForEventStreamTransformation() {
+ prepareLooper();
+
+ setDisplayCount(2);
+ mA11yInputFilter.setUserAndEnabledFeatures(0, mFeatures);
+ assertEquals(2, mEventHandler.size());
+
+ // Check if mEventHandler for each display has correct order of the
+ // EventStreamTransformations.
+ EventStreamTransformation next = mEventHandler.get(DEFAULT_DISPLAY);
+ for (int i = 0; next != null; i++) {
+ assertEquals(next.getClass(), mExpectedEventHandlerTypes[i]);
+ next = next.getNext();
+ }
+
+ next = mEventHandler.get(SECOND_DISPLAY);
+ // Start from index 1 because KeyboardInterceptor only exists in EventHandler for
+ // DEFAULT_DISPLAY.
+ for (int i = 1; next != null; i++) {
+ assertEquals(next.getClass(), mExpectedEventHandlerTypes[i]);
+ next = next.getNext();
+ }
+ }
+
+ @Test
+ public void testInputEvent_shouldDispatchToCorrespondingEventHandlers() {
+ prepareLooper();
+
+ setDisplayCount(2);
+ mA11yInputFilter.setUserAndEnabledFeatures(0, mFeatures);
+ assertEquals(2, mEventHandler.size());
+
+ mCaptor1 = new EventCaptor();
+ mCaptor2 = new EventCaptor();
+ mEventHandler.put(DEFAULT_DISPLAY, mCaptor1);
+ mEventHandler.put(SECOND_DISPLAY, mCaptor2);
+
+ // InputEvent with different displayId should be dispatched to corresponding EventHandler.
+ send(downEvent(DEFAULT_DISPLAY, InputDevice.SOURCE_TOUCHSCREEN));
+ send(downEvent(SECOND_DISPLAY, InputDevice.SOURCE_TOUCHSCREEN));
+
+ assertEquals(1, mCaptor1.mEvents.size());
+ assertEquals(1, mCaptor2.mEvents.size());
+ }
+
+ @Test
+ public void testInputEvent_shouldClearEventsForAllEventHandlers() {
+ prepareLooper();
+
+ mA11yInputFilter.setUserAndEnabledFeatures(0, mFeatures);
+ assertEquals(1, mEventHandler.size());
+
+ mCaptor1 = new EventCaptor();
+ mEventHandler.put(DEFAULT_DISPLAY, mCaptor1);
+
+ send(downEvent(DEFAULT_DISPLAY, InputDevice.SOURCE_TOUCHSCREEN));
+ send(downEvent(DEFAULT_DISPLAY, InputDevice.SOURCE_TOUCHSCREEN));
+ assertEquals(2, mCaptor1.mEvents.size());
+
+ // InputEvent with different input source should trigger clearEvents() for each
+ // EventStreamTransformation in EventHandler.
+ send(downEvent(DEFAULT_DISPLAY, InputDevice.SOURCE_MOUSE));
+ assertEquals(1, mCaptor1.mEvents.size());
+ }
+
+ private static void prepareLooper() {
+ if (Looper.myLooper() == null) {
+ Looper.prepare();
+ }
+ }
+
+ private Display createStubDisplay(DisplayInfo displayInfo) {
+ final int displayId = sNextDisplayId++;
+ final Display display = new Display(DisplayManagerGlobal.getInstance(), displayId,
+ displayInfo, DEFAULT_DISPLAY_ADJUSTMENTS);
+ return display;
+ }
+
+ private void setDisplayCount(int count) {
+ sNextDisplayId = DEFAULT_DISPLAY;
+ mDisplayList.clear();
+ for (int i = 0; i < count; i++) {
+ mDisplayList.add(createStubDisplay(new DisplayInfo()));
+ }
+ }
+
+ private void send(InputEvent event) {
+ mA11yInputFilter.onInputEvent(event, /* policyFlags */ FLAG_PASS_TO_USER);
+ }
+
+ private MotionEvent downEvent(int displayId, int source) {
+ mLastDownTime = SystemClock.uptimeMillis();
+ final MotionEvent ev = MotionEvent.obtain(mLastDownTime, mLastDownTime,
+ MotionEvent.ACTION_DOWN, DEFAULT_X, DEFAULT_Y, 0);
+ ev.setDisplayId(displayId);
+ ev.setSource(source);
+ return ev;
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/am/AppCompactorTest.java b/services/tests/servicestests/src/com/android/server/am/AppCompactorTest.java
new file mode 100644
index 000000000000..1a231cf56921
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/am/AppCompactorTest.java
@@ -0,0 +1,379 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.am;
+
+import static android.provider.DeviceConfig.ActivityManager.KEY_COMPACT_ACTION_1;
+import static android.provider.DeviceConfig.ActivityManager.KEY_COMPACT_ACTION_2;
+import static android.provider.DeviceConfig.ActivityManager.KEY_COMPACT_THROTTLE_1;
+import static android.provider.DeviceConfig.ActivityManager.KEY_COMPACT_THROTTLE_2;
+import static android.provider.DeviceConfig.ActivityManager.KEY_COMPACT_THROTTLE_3;
+import static android.provider.DeviceConfig.ActivityManager.KEY_COMPACT_THROTTLE_4;
+import static android.provider.DeviceConfig.ActivityManager.KEY_USE_COMPACTION;
+
+import static com.android.server.am.ActivityManagerService.Injector;
+import static com.android.server.am.AppCompactor.compactActionIntToString;
+
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
+
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.provider.DeviceConfig;
+import android.support.test.uiautomator.UiDevice;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.appop.AppOpsService;
+
+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 java.io.File;
+import java.io.IOException;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Tests for {@link AppCompactor}.
+ *
+ * Build/Install/Run:
+ * atest FrameworksServicesTests:AppCompactorTest
+ */
+@RunWith(AndroidJUnit4.class)
+public final class AppCompactorTest {
+
+ @Mock private AppOpsService mAppOpsService;
+ private AppCompactor mCompactorUnderTest;
+ private HandlerThread mHandlerThread;
+ private Handler mHandler;
+ private CountDownLatch mCountDown;
+
+ private static void clearDeviceConfig() throws IOException {
+ UiDevice uiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+ uiDevice.executeShellCommand(
+ "device_config delete activity_manager " + KEY_USE_COMPACTION);
+ uiDevice.executeShellCommand(
+ "device_config delete activity_manager " + KEY_COMPACT_ACTION_1);
+ uiDevice.executeShellCommand(
+ "device_config delete activity_manager " + KEY_COMPACT_ACTION_2);
+ uiDevice.executeShellCommand(
+ "device_config delete activity_manager " + KEY_COMPACT_THROTTLE_1);
+ uiDevice.executeShellCommand(
+ "device_config delete activity_manager " + KEY_COMPACT_THROTTLE_2);
+ uiDevice.executeShellCommand(
+ "device_config delete activity_manager " + KEY_COMPACT_THROTTLE_3);
+ uiDevice.executeShellCommand(
+ "device_config delete activity_manager " + KEY_COMPACT_THROTTLE_4);
+ }
+
+ @Before
+ public void setUp() throws IOException {
+ MockitoAnnotations.initMocks(this);
+ clearDeviceConfig();
+ mHandlerThread = new HandlerThread("");
+ mHandlerThread.start();
+ mHandler = new Handler(mHandlerThread.getLooper());
+ ActivityManagerService ams = new ActivityManagerService(new TestInjector());
+ mCompactorUnderTest = new AppCompactor(ams,
+ new AppCompactor.PropertyChangedCallbackForTest() {
+ @Override
+ public void onPropertyChanged() {
+ if (mCountDown != null) {
+ mCountDown.countDown();
+ }
+ }
+ });
+ }
+
+ @After
+ public void tearDown() throws IOException {
+ mHandlerThread.quit();
+ mCountDown = null;
+ clearDeviceConfig();
+ }
+
+ @Test
+ public void init_setsDefaults() {
+ mCompactorUnderTest.init();
+ assertThat(mCompactorUnderTest.useCompaction(),
+ is(mCompactorUnderTest.DEFAULT_USE_COMPACTION));
+ assertThat(mCompactorUnderTest.mCompactActionSome, is(
+ compactActionIntToString(mCompactorUnderTest.DEFAULT_COMPACT_ACTION_1)));
+ assertThat(mCompactorUnderTest.mCompactActionFull, is(
+ compactActionIntToString(mCompactorUnderTest.DEFAULT_COMPACT_ACTION_2)));
+ assertThat(mCompactorUnderTest.mCompactThrottleSomeSome,
+ is(AppCompactor.DEFAULT_COMPACT_THROTTLE_1));
+ assertThat(mCompactorUnderTest.mCompactThrottleSomeFull,
+ is(AppCompactor.DEFAULT_COMPACT_THROTTLE_2));
+ assertThat(mCompactorUnderTest.mCompactThrottleFullSome,
+ is(AppCompactor.DEFAULT_COMPACT_THROTTLE_3));
+ assertThat(mCompactorUnderTest.mCompactThrottleFullFull,
+ is(AppCompactor.DEFAULT_COMPACT_THROTTLE_4));
+ }
+
+ @Test
+ public void init_withDeviceConfigSetsParameters() {
+ // When the DeviceConfig already has a flag value stored (note this test will need to
+ // change if the default value changes from false).
+ assertThat(mCompactorUnderTest.DEFAULT_USE_COMPACTION, is(false));
+ DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
+ KEY_USE_COMPACTION, "true", false);
+ DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
+ KEY_COMPACT_ACTION_1,
+ Integer.toString((AppCompactor.DEFAULT_COMPACT_ACTION_1 + 1 % 3) + 1), false);
+ DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
+ KEY_COMPACT_ACTION_2,
+ Integer.toString((AppCompactor.DEFAULT_COMPACT_ACTION_2 + 1 % 3) + 1), false);
+ DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
+ KEY_COMPACT_THROTTLE_1,
+ Long.toString(AppCompactor.DEFAULT_COMPACT_THROTTLE_1 + 1), false);
+ DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
+ KEY_COMPACT_THROTTLE_2,
+ Long.toString(AppCompactor.DEFAULT_COMPACT_THROTTLE_2 + 1), false);
+ DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
+ KEY_COMPACT_THROTTLE_3,
+ Long.toString(AppCompactor.DEFAULT_COMPACT_THROTTLE_3 + 1), false);
+ DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
+ KEY_COMPACT_THROTTLE_4,
+ Long.toString(AppCompactor.DEFAULT_COMPACT_THROTTLE_4 + 1), false);
+
+ // Then calling init will read and set that flag.
+ mCompactorUnderTest.init();
+ assertThat(mCompactorUnderTest.useCompaction(), is(true));
+ assertThat(mCompactorUnderTest.mCompactionThread.isAlive(), is(true));
+
+ assertThat(mCompactorUnderTest.mCompactActionSome,
+ is(compactActionIntToString((AppCompactor.DEFAULT_COMPACT_ACTION_1 + 1 % 3) + 1)));
+ assertThat(mCompactorUnderTest.mCompactActionFull,
+ is(compactActionIntToString((AppCompactor.DEFAULT_COMPACT_ACTION_2 + 1 % 3) + 1)));
+ assertThat(mCompactorUnderTest.mCompactThrottleSomeSome,
+ is(AppCompactor.DEFAULT_COMPACT_THROTTLE_1 + 1));
+ assertThat(mCompactorUnderTest.mCompactThrottleSomeFull,
+ is(AppCompactor.DEFAULT_COMPACT_THROTTLE_2 + 1));
+ assertThat(mCompactorUnderTest.mCompactThrottleFullSome,
+ is(AppCompactor.DEFAULT_COMPACT_THROTTLE_3 + 1));
+ assertThat(mCompactorUnderTest.mCompactThrottleFullFull,
+ is(AppCompactor.DEFAULT_COMPACT_THROTTLE_4 + 1));
+ }
+
+ @Test
+ public void useCompaction_listensToDeviceConfigChanges() throws InterruptedException {
+ assertThat(mCompactorUnderTest.useCompaction(),
+ is(mCompactorUnderTest.DEFAULT_USE_COMPACTION));
+ // When we call init and change some the flag value...
+ mCompactorUnderTest.init();
+ mCountDown = new CountDownLatch(1);
+ DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
+ KEY_USE_COMPACTION, "true", false);
+ assertThat(mCountDown.await(5, TimeUnit.SECONDS), is(true));
+
+ // Then that new flag value is updated in the implementation.
+ assertThat(mCompactorUnderTest.useCompaction(), is(true));
+ assertThat(mCompactorUnderTest.mCompactionThread.isAlive(), is(true));
+
+ // And again, setting the flag the other way.
+ mCountDown = new CountDownLatch(1);
+ DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
+ KEY_USE_COMPACTION, "false", false);
+ assertThat(mCountDown.await(5, TimeUnit.SECONDS), is(true));
+ assertThat(mCompactorUnderTest.useCompaction(), is(false));
+ }
+
+ @Test
+ public void useCompaction_listensToDeviceConfigChangesBadValues() throws InterruptedException {
+ assertThat(mCompactorUnderTest.useCompaction(),
+ is(mCompactorUnderTest.DEFAULT_USE_COMPACTION));
+ mCompactorUnderTest.init();
+
+ // When we push an invalid flag value...
+ mCountDown = new CountDownLatch(1);
+ DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
+ KEY_USE_COMPACTION, "foobar", false);
+ assertThat(mCountDown.await(5, TimeUnit.SECONDS), is(true));
+
+ // Then we set the default.
+ assertThat(mCompactorUnderTest.useCompaction(), is(AppCompactor.DEFAULT_USE_COMPACTION));
+ }
+
+ @Test
+ public void compactAction_listensToDeviceConfigChanges() throws InterruptedException {
+ mCompactorUnderTest.init();
+
+ // When we override new values for the compaction action with reasonable values...
+
+ // There are three possible values for compactAction[Some|Full].
+ for (int i = 1; i < 4; i++) {
+ mCountDown = new CountDownLatch(2);
+ int expectedSome = (mCompactorUnderTest.DEFAULT_COMPACT_ACTION_1 + i) % 3 + 1;
+ DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
+ KEY_COMPACT_ACTION_1, Integer.toString(expectedSome), false);
+ int expectedFull = (mCompactorUnderTest.DEFAULT_COMPACT_ACTION_2 + i) % 3 + 1;
+ DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
+ KEY_COMPACT_ACTION_2, Integer.toString(expectedFull), false);
+ assertThat(mCountDown.await(5, TimeUnit.SECONDS), is(true));
+
+ // Then the updates are reflected in the flags.
+ assertThat(mCompactorUnderTest.mCompactActionSome,
+ is(compactActionIntToString(expectedSome)));
+ assertThat(mCompactorUnderTest.mCompactActionFull,
+ is(compactActionIntToString(expectedFull)));
+ }
+ }
+
+ @Test
+ public void compactAction_listensToDeviceConfigChangesBadValues() throws InterruptedException {
+ mCompactorUnderTest.init();
+
+ // When we override new values for the compaction action with bad values ...
+ mCountDown = new CountDownLatch(2);
+ DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
+ KEY_COMPACT_ACTION_1, "foo", false);
+ DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
+ KEY_COMPACT_ACTION_2, "foo", false);
+ assertThat(mCountDown.await(5, TimeUnit.SECONDS), is(true));
+
+ // Then the default values are reflected in the flag
+ assertThat(mCompactorUnderTest.mCompactActionSome,
+ is(compactActionIntToString(AppCompactor.DEFAULT_COMPACT_ACTION_1)));
+ assertThat(mCompactorUnderTest.mCompactActionFull,
+ is(compactActionIntToString(AppCompactor.DEFAULT_COMPACT_ACTION_2)));
+
+ mCountDown = new CountDownLatch(2);
+ DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
+ KEY_COMPACT_ACTION_1, "", false);
+ DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
+ KEY_COMPACT_ACTION_2, "", false);
+ assertThat(mCountDown.await(5, TimeUnit.SECONDS), is(true));
+
+ assertThat(mCompactorUnderTest.mCompactActionSome,
+ is(compactActionIntToString(AppCompactor.DEFAULT_COMPACT_ACTION_1)));
+ assertThat(mCompactorUnderTest.mCompactActionFull,
+ is(compactActionIntToString(AppCompactor.DEFAULT_COMPACT_ACTION_2)));
+ }
+
+ @Test
+ public void compactThrottle_listensToDeviceConfigChanges() throws InterruptedException {
+ mCompactorUnderTest.init();
+
+ // When we override new reasonable throttle values after init...
+ mCountDown = new CountDownLatch(4);
+ DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
+ KEY_COMPACT_THROTTLE_1,
+ Long.toString(AppCompactor.DEFAULT_COMPACT_THROTTLE_1 + 1), false);
+ DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
+ KEY_COMPACT_THROTTLE_2,
+ Long.toString(AppCompactor.DEFAULT_COMPACT_THROTTLE_2 + 1), false);
+ DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
+ KEY_COMPACT_THROTTLE_3,
+ Long.toString(AppCompactor.DEFAULT_COMPACT_THROTTLE_3 + 1), false);
+ DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
+ KEY_COMPACT_THROTTLE_4,
+ Long.toString(AppCompactor.DEFAULT_COMPACT_THROTTLE_4 + 1), false);
+ assertThat(mCountDown.await(5, TimeUnit.SECONDS), is(true));
+
+ // Then those flags values are reflected in the compactor.
+ assertThat(mCompactorUnderTest.mCompactThrottleSomeSome,
+ is(AppCompactor.DEFAULT_COMPACT_THROTTLE_1 + 1));
+ assertThat(mCompactorUnderTest.mCompactThrottleSomeFull,
+ is(AppCompactor.DEFAULT_COMPACT_THROTTLE_2 + 1));
+ assertThat(mCompactorUnderTest.mCompactThrottleFullSome,
+ is(AppCompactor.DEFAULT_COMPACT_THROTTLE_3 + 1));
+ assertThat(mCompactorUnderTest.mCompactThrottleFullFull,
+ is(AppCompactor.DEFAULT_COMPACT_THROTTLE_4 + 1));
+ }
+
+ @Test
+ public void compactThrottle_listensToDeviceConfigChangesBadValues()
+ throws IOException, InterruptedException {
+ mCompactorUnderTest.init();
+
+ // When one of the throttles is overridden with a bad value...
+ mCountDown = new CountDownLatch(1);
+ DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
+ KEY_COMPACT_THROTTLE_1, "foo", false);
+ // Then all the throttles have the defaults set.
+ assertThat(mCountDown.await(5, TimeUnit.SECONDS), is(true));
+ assertThat(mCompactorUnderTest.mCompactThrottleSomeSome,
+ is(AppCompactor.DEFAULT_COMPACT_THROTTLE_1));
+ assertThat(mCompactorUnderTest.mCompactThrottleSomeFull,
+ is(AppCompactor.DEFAULT_COMPACT_THROTTLE_2));
+ assertThat(mCompactorUnderTest.mCompactThrottleFullSome,
+ is(AppCompactor.DEFAULT_COMPACT_THROTTLE_3));
+ assertThat(mCompactorUnderTest.mCompactThrottleFullFull,
+ is(AppCompactor.DEFAULT_COMPACT_THROTTLE_4));
+ clearDeviceConfig();
+
+ // Repeat for each of the throttle keys.
+ mCountDown = new CountDownLatch(1);
+ DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
+ KEY_COMPACT_THROTTLE_2, "foo", false);
+ assertThat(mCountDown.await(5, TimeUnit.SECONDS), is(true));
+ assertThat(mCompactorUnderTest.mCompactThrottleSomeSome,
+ is(AppCompactor.DEFAULT_COMPACT_THROTTLE_1));
+ assertThat(mCompactorUnderTest.mCompactThrottleSomeFull,
+ is(AppCompactor.DEFAULT_COMPACT_THROTTLE_2));
+ assertThat(mCompactorUnderTest.mCompactThrottleFullSome,
+ is(AppCompactor.DEFAULT_COMPACT_THROTTLE_3));
+ assertThat(mCompactorUnderTest.mCompactThrottleFullFull,
+ is(AppCompactor.DEFAULT_COMPACT_THROTTLE_4));
+ clearDeviceConfig();
+
+ mCountDown = new CountDownLatch(1);
+ DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
+ KEY_COMPACT_THROTTLE_3, "foo", false);
+ assertThat(mCountDown.await(5, TimeUnit.SECONDS), is(true));
+ assertThat(mCompactorUnderTest.mCompactThrottleSomeSome,
+ is(AppCompactor.DEFAULT_COMPACT_THROTTLE_1));
+ assertThat(mCompactorUnderTest.mCompactThrottleSomeFull,
+ is(AppCompactor.DEFAULT_COMPACT_THROTTLE_2));
+ assertThat(mCompactorUnderTest.mCompactThrottleFullSome,
+ is(AppCompactor.DEFAULT_COMPACT_THROTTLE_3));
+ assertThat(mCompactorUnderTest.mCompactThrottleFullFull,
+ is(AppCompactor.DEFAULT_COMPACT_THROTTLE_4));
+ clearDeviceConfig();
+
+ mCountDown = new CountDownLatch(1);
+ DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
+ KEY_COMPACT_THROTTLE_4, "foo", false);
+ assertThat(mCountDown.await(5, TimeUnit.SECONDS), is(true));
+ assertThat(mCompactorUnderTest.mCompactThrottleSomeSome,
+ is(AppCompactor.DEFAULT_COMPACT_THROTTLE_1));
+ assertThat(mCompactorUnderTest.mCompactThrottleSomeFull,
+ is(AppCompactor.DEFAULT_COMPACT_THROTTLE_2));
+ assertThat(mCompactorUnderTest.mCompactThrottleFullSome,
+ is(AppCompactor.DEFAULT_COMPACT_THROTTLE_3));
+ assertThat(mCompactorUnderTest.mCompactThrottleFullFull,
+ is(AppCompactor.DEFAULT_COMPACT_THROTTLE_4));
+ }
+
+ private class TestInjector extends Injector {
+ @Override
+ public AppOpsService getAppOpsService(File file, Handler handler) {
+ return mAppOpsService;
+ }
+
+ @Override
+ public Handler getUiHandler(ActivityManagerService service) {
+ return mHandler;
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java b/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java
index ac4a5fe90c06..a3f36b720398 100644
--- a/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java
+++ b/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java
@@ -24,11 +24,15 @@ import static junit.framework.Assert.assertTrue;
import static junit.framework.Assert.fail;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
+import android.Manifest;
import android.annotation.UserIdInt;
import android.app.backup.BackupManager;
import android.app.backup.IBackupManagerMonitor;
@@ -39,21 +43,21 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.os.RemoteException;
import android.os.UserHandle;
+import android.os.UserManager;
import android.platform.test.annotations.Presubmit;
-import android.provider.Settings;
-import android.test.mock.MockContentResolver;
import android.util.SparseArray;
+import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
-import com.android.internal.util.test.FakeSettingsProvider;
-
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -99,10 +103,6 @@ public class TrampolineTest {
@Mock
private Context mContextMock;
@Mock
- private File mSuppressFileMock;
- @Mock
- private File mSuppressFileParentMock;
- @Mock
private IBinder mAgentMock;
@Mock
private ParcelFileDescriptor mParcelFileDescriptorMock;
@@ -114,181 +114,232 @@ public class TrampolineTest {
private IBackupManagerMonitor mBackupManagerMonitorMock;
@Mock
private PrintWriter mPrintWriterMock;
+ @Mock
+ private UserManager mUserManagerMock;
+ @Mock
+ private UserInfo mUserInfoMock;
private FileDescriptor mFileDescriptorStub = new FileDescriptor();
private TrampolineTestable mTrampoline;
- private MockContentResolver mContentResolver;
+ private File mTestDir;
+ private File mSuppressFile;
+ private File mActivatedFile;
@Before
- public void setUp() {
+ public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- mUserId = NON_USER_SYSTEM;
+ mUserId = UserHandle.USER_SYSTEM;
SparseArray<UserBackupManagerService> serviceUsers = new SparseArray<>();
- serviceUsers.append(UserHandle.SYSTEM.getIdentifier(), mUserBackupManagerService);
+ serviceUsers.append(UserHandle.USER_SYSTEM, mUserBackupManagerService);
serviceUsers.append(NON_USER_SYSTEM, mUserBackupManagerService);
when(mBackupManagerServiceMock.getServiceUsers()).thenReturn(serviceUsers);
+ when(mUserManagerMock.getUserInfo(UserHandle.USER_SYSTEM)).thenReturn(mUserInfoMock);
+ when(mUserManagerMock.getUserInfo(NON_USER_SYSTEM)).thenReturn(mUserInfoMock);
+
TrampolineTestable.sBackupManagerServiceMock = mBackupManagerServiceMock;
TrampolineTestable.sCallingUserId = UserHandle.USER_SYSTEM;
TrampolineTestable.sCallingUid = Process.SYSTEM_UID;
TrampolineTestable.sBackupDisabled = false;
+ TrampolineTestable.sUserManagerMock = mUserManagerMock;
+
+ mTestDir = InstrumentationRegistry.getContext().getFilesDir();
+ mTestDir.mkdirs();
- when(mSuppressFileMock.getParentFile()).thenReturn(mSuppressFileParentMock);
+ mSuppressFile = new File(mTestDir, "suppress");
+ TrampolineTestable.sSuppressFile = mSuppressFile;
+
+ mActivatedFile = new File(mTestDir, "activate-" + NON_USER_SYSTEM);
+ TrampolineTestable.sActivatedFiles.append(NON_USER_SYSTEM, mActivatedFile);
mTrampoline = new TrampolineTestable(mContextMock);
+ }
- mContentResolver = new MockContentResolver();
- mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
- when(mContextMock.getContentResolver()).thenReturn(mContentResolver);
+ @After
+ public void tearDown() throws Exception {
+ mSuppressFile.delete();
+ mActivatedFile.delete();
}
@Test
- public void unlockUser_whenMultiUserSettingDisabled_callsBackupManagerServiceForSystemUser() {
- Settings.Global.putInt(mContentResolver, Settings.Global.BACKUP_MULTI_USER_ENABLED, 0);
+ public void initializeService_successfullyInitializesBackupService() {
mTrampoline.initializeService();
- mTrampoline.unlockUser(UserHandle.USER_SYSTEM);
-
- verify(mBackupManagerServiceMock).startServiceForUser(UserHandle.USER_SYSTEM);
+ assertTrue(mTrampoline.isBackupServiceActive(UserHandle.USER_SYSTEM));
}
@Test
- public void unlockUser_whenMultiUserSettingDisabled_isIgnoredForNonSystemUser() {
- Settings.Global.putInt(mContentResolver, Settings.Global.BACKUP_MULTI_USER_ENABLED, 0);
- mTrampoline.initializeService();
+ public void initializeService_globallyDisabled_nonInitialized() {
+ TrampolineTestable.sBackupDisabled = true;
+ TrampolineTestable trampoline = new TrampolineTestable(mContextMock);
- mTrampoline.unlockUser(NON_USER_SYSTEM);
+ trampoline.initializeService();
- verify(mBackupManagerServiceMock, never()).startServiceForUser(NON_USER_SYSTEM);
+ assertFalse(trampoline.isBackupServiceActive(UserHandle.USER_SYSTEM));
}
@Test
- public void unlockUser_whenMultiUserSettingEnabled_callsBackupManagerServiceForNonSystemUser() {
- Settings.Global.putInt(mContentResolver, Settings.Global.BACKUP_MULTI_USER_ENABLED, 1);
+ public void initializeService_doesNotStartServiceForUsers() {
mTrampoline.initializeService();
- mTrampoline.unlockUser(NON_USER_SYSTEM);
+ verify(mBackupManagerServiceMock, never()).startServiceForUser(anyInt());
+ }
- verify(mBackupManagerServiceMock).startServiceForUser(NON_USER_SYSTEM);
+ @Test
+ public void isBackupServiceActive_calledBeforeInitialize_returnsFalse() {
+ assertFalse(mTrampoline.isBackupServiceActive(UserHandle.USER_SYSTEM));
}
@Test
- public void stopUser_whenMultiUserSettingDisabled_callsBackupManagerServiceForSystemUser() {
- Settings.Global.putInt(mContentResolver, Settings.Global.BACKUP_MULTI_USER_ENABLED, 0);
+ public void isBackupServiceActive_forSystemUser_returnsTrueWhenActivated() throws Exception {
mTrampoline.initializeService();
+ mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
- mTrampoline.stopUser(UserHandle.USER_SYSTEM);
-
- verify(mBackupManagerServiceMock).stopServiceForUser(UserHandle.USER_SYSTEM);
+ assertTrue(mTrampoline.isBackupServiceActive(UserHandle.USER_SYSTEM));
}
@Test
- public void stopUser_whenMultiUserSettingDisabled_isIgnoredForNonSystemUser() {
- Settings.Global.putInt(mContentResolver, Settings.Global.BACKUP_MULTI_USER_ENABLED, 0);
+ public void isBackupServiceActive_forSystemUser_returnsFalseWhenDeactivated() throws Exception {
mTrampoline.initializeService();
+ mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, false);
- mTrampoline.stopUser(NON_USER_SYSTEM);
-
- verify(mBackupManagerServiceMock, never()).stopServiceForUser(NON_USER_SYSTEM);
+ assertFalse(mTrampoline.isBackupServiceActive(UserHandle.USER_SYSTEM));
}
@Test
- public void stopUser_whenMultiUserSettingEnabled_callsBackupManagerServiceForNonSystemUser() {
- Settings.Global.putInt(mContentResolver, Settings.Global.BACKUP_MULTI_USER_ENABLED, 1);
-
+ public void isBackupServiceActive_forNonSystemUser_returnsFalseWhenSystemUserDeactivated()
+ throws Exception {
mTrampoline.initializeService();
+ mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, false);
+ mTrampoline.setBackupServiceActive(NON_USER_SYSTEM, true);
- mTrampoline.stopUser(NON_USER_SYSTEM);
-
- verify(mBackupManagerServiceMock).stopServiceForUser(NON_USER_SYSTEM);
+ assertFalse(mTrampoline.isBackupServiceActive(NON_USER_SYSTEM));
}
@Test
- public void initializeService_successfullyInitializesBackupService() {
+ public void isBackupServiceActive_forNonSystemUser_returnsFalseWhenNonSystemUserDeactivated()
+ throws Exception {
mTrampoline.initializeService();
+ mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
+ // Don't activate non-system user.
- assertTrue(mTrampoline.isBackupServiceActive(UserHandle.USER_SYSTEM));
+ assertFalse(mTrampoline.isBackupServiceActive(NON_USER_SYSTEM));
}
@Test
- public void initializeService_globallyDisabled_nonInitialized() {
- TrampolineTestable.sBackupDisabled = true;
- TrampolineTestable trampoline = new TrampolineTestable(mContextMock);
-
- trampoline.initializeService();
+ public void
+ isBackupServiceActive_forNonSystemUser_returnsTrueWhenSystemAndNonSystemUserActivated()
+ throws Exception {
+ mTrampoline.initializeService();
+ mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
+ mTrampoline.setBackupServiceActive(NON_USER_SYSTEM, true);
- assertFalse(trampoline.isBackupServiceActive(UserHandle.USER_SYSTEM));
+ assertTrue(mTrampoline.isBackupServiceActive(NON_USER_SYSTEM));
}
- // Verify that BackupManagerService is not initialized if suppress file exists.
@Test
- public void initializeService_suppressFileExists_nonInitialized() throws Exception {
- TrampolineTestable trampoline = new TrampolineTestable(mContextMock);
- trampoline.createBackupSuppressFileForUser(UserHandle.USER_SYSTEM);
-
+ public void setBackupServiceActive_forSystemUserAndCallerSystemUid_serviceCreated() {
+ mTrampoline.initializeService();
+ TrampolineTestable.sCallingUid = Process.SYSTEM_UID;
- trampoline.initializeService();
+ mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
- assertFalse(trampoline.isBackupServiceActive(UserHandle.USER_SYSTEM));
+ assertTrue(mTrampoline.isBackupServiceActive(UserHandle.USER_SYSTEM));
}
@Test
- public void initializeService_doesNotStartServiceForUsers() {
+ public void setBackupServiceActive_forSystemUserAndCallerRootUid_serviceCreated() {
mTrampoline.initializeService();
+ TrampolineTestable.sCallingUid = Process.ROOT_UID;
- verify(mBackupManagerServiceMock, never()).startServiceForUser(anyInt());
- }
+ mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
- @Test
- public void isBackupServiceActive_calledBeforeInitialize_returnsFalse() {
- assertFalse(mTrampoline.isBackupServiceActive(UserHandle.USER_SYSTEM));
+ assertTrue(mTrampoline.isBackupServiceActive(UserHandle.USER_SYSTEM));
}
@Test
- public void isBackupServiceActive_forNonSysUser_whenSysUserIsDeactivated_returnsFalse() {
- mTrampoline.setBackupServiceActive(NON_USER_SYSTEM, true);
- mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, false);
+ public void setBackupServiceActive_forSystemUserAndCallerNonRootNonSystem_throws() {
+ mTrampoline.initializeService();
+ TrampolineTestable.sCallingUid = Process.FIRST_APPLICATION_UID;
- assertFalse(mTrampoline.isBackupServiceActive(NON_USER_SYSTEM));
+ try {
+ mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
+ fail();
+ } catch (SecurityException expected) {
+ }
}
@Test
- public void setBackupServiceActive_callerSystemUid_serviceCreated() {
+ public void setBackupServiceActive_forManagedProfileAndCallerSystemUid_serviceCreated() {
+ when(mUserInfoMock.isManagedProfile()).thenReturn(true);
+ mTrampoline.initializeService();
TrampolineTestable.sCallingUid = Process.SYSTEM_UID;
- mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
+ mTrampoline.setBackupServiceActive(NON_USER_SYSTEM, true);
- assertTrue(mTrampoline.isBackupServiceActive(UserHandle.USER_SYSTEM));
+ assertTrue(mTrampoline.isBackupServiceActive(NON_USER_SYSTEM));
}
@Test
- public void setBackupServiceActive_callerRootUid_serviceCreated() {
+ public void setBackupServiceActive_forManagedProfileAndCallerRootUid_serviceCreated() {
+ when(mUserInfoMock.isManagedProfile()).thenReturn(true);
+ mTrampoline.initializeService();
TrampolineTestable.sCallingUid = Process.ROOT_UID;
- mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
+ mTrampoline.setBackupServiceActive(NON_USER_SYSTEM, true);
- assertTrue(mTrampoline.isBackupServiceActive(UserHandle.USER_SYSTEM));
+ assertTrue(mTrampoline.isBackupServiceActive(NON_USER_SYSTEM));
}
@Test
- public void setBackupServiceActive_callerNonRootNonSystem_securityExceptionThrown() {
+ public void setBackupServiceActive_forManagedProfileAndCallerNonRootNonSystem_throws() {
+ when(mUserInfoMock.isManagedProfile()).thenReturn(true);
+ mTrampoline.initializeService();
TrampolineTestable.sCallingUid = Process.FIRST_APPLICATION_UID;
try {
- mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
+ mTrampoline.setBackupServiceActive(NON_USER_SYSTEM, true);
fail();
} catch (SecurityException expected) {
}
+ }
- assertFalse(mTrampoline.isBackupServiceActive(UserHandle.USER_SYSTEM));
+ @Test
+ public void setBackupServiceActive_forNonSystemUserAndCallerWithoutBackupPermission_throws() {
+ doThrow(new SecurityException())
+ .when(mContextMock)
+ .enforceCallingOrSelfPermission(eq(Manifest.permission.BACKUP), anyString());
+ mTrampoline.initializeService();
+
+ try {
+ mTrampoline.setBackupServiceActive(NON_USER_SYSTEM, true);
+ fail();
+ } catch (SecurityException expected) {
+ }
+ }
+
+ @Test
+ public void setBackupServiceActive_forNonSystemUserAndCallerWithoutUserPermission_throws() {
+ doThrow(new SecurityException())
+ .when(mContextMock)
+ .enforceCallingOrSelfPermission(
+ eq(Manifest.permission.INTERACT_ACROSS_USERS_FULL), anyString());
+ mTrampoline.initializeService();
+
+ try {
+ mTrampoline.setBackupServiceActive(NON_USER_SYSTEM, true);
+ fail();
+ } catch (SecurityException expected) {
+ }
}
@Test
public void setBackupServiceActive_backupDisabled_ignored() {
TrampolineTestable.sBackupDisabled = true;
TrampolineTestable trampoline = new TrampolineTestable(mContextMock);
+ trampoline.initializeService();
trampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
@@ -296,14 +347,8 @@ public class TrampolineTest {
}
@Test
- public void setBackupServiceActive_nonUserSystem_ignored() {
- mTrampoline.setBackupServiceActive(NON_USER_SYSTEM, true);
-
- assertFalse(mTrampoline.isBackupServiceActive(NON_USER_SYSTEM));
- }
-
- @Test
public void setBackupServiceActive_alreadyActive_ignored() {
+ mTrampoline.initializeService();
mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
assertTrue(mTrampoline.isBackupServiceActive(UserHandle.USER_SYSTEM));
assertEquals(1, mTrampoline.getCreateServiceCallsCount());
@@ -315,6 +360,7 @@ public class TrampolineTest {
@Test
public void setBackupServiceActive_makeNonActive_alreadyNonActive_ignored() {
+ mTrampoline.initializeService();
mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, false);
mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, false);
@@ -323,6 +369,7 @@ public class TrampolineTest {
@Test
public void setBackupServiceActive_makeActive_serviceCreatedAndSuppressFileDeleted() {
+ mTrampoline.initializeService();
mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
assertTrue(mTrampoline.isBackupServiceActive(UserHandle.USER_SYSTEM));
@@ -349,6 +396,21 @@ public class TrampolineTest {
}
@Test
+ public void setBackupServiceActive_forOneNonSystemUser_doesNotActivateForAllNonSystemUsers() {
+ mTrampoline.initializeService();
+ int otherUser = NON_USER_SYSTEM + 1;
+ File activateFile = new File(mTestDir, "activate-" + otherUser);
+ TrampolineTestable.sActivatedFiles.append(otherUser, activateFile);
+ mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
+
+ mTrampoline.setBackupServiceActive(NON_USER_SYSTEM, true);
+
+ assertTrue(mTrampoline.isBackupServiceActive(NON_USER_SYSTEM));
+ assertFalse(mTrampoline.isBackupServiceActive(otherUser));
+ activateFile.delete();
+ }
+
+ @Test
public void dataChanged_calledBeforeInitialize_ignored() throws Exception {
mTrampoline.dataChanged(PACKAGE_NAME);
verifyNoMoreInteractions(mBackupManagerServiceMock);
@@ -1123,7 +1185,7 @@ public class TrampolineTest {
@Test
public void requestBackup_forwardedToCallingUserId() throws Exception {
TrampolineTestable.sCallingUserId = mUserId;
- when(mBackupManagerServiceMock.requestBackup(NON_USER_SYSTEM, PACKAGE_NAMES,
+ when(mBackupManagerServiceMock.requestBackup(mUserId, PACKAGE_NAMES,
mBackupObserverMock, mBackupManagerMonitorMock, 123)).thenReturn(456);
mTrampoline.initializeService();
@@ -1227,50 +1289,33 @@ public class TrampolineTest {
static int sCallingUserId = -1;
static int sCallingUid = -1;
static BackupManagerService sBackupManagerServiceMock = null;
+ static File sSuppressFile = null;
+ static SparseArray<File> sActivatedFiles = new SparseArray<>();
+ static UserManager sUserManagerMock = null;
private int mCreateServiceCallsCount = 0;
- private SparseArray<FakeFile> mSuppressFiles = new SparseArray<>();
-
- private static class FakeFile extends File {
- private boolean mExists;
-
- FakeFile(String pathname) {
- super(pathname);
- }
-
- @Override
- public boolean exists() {
- return mExists;
- }
-
- @Override
- public boolean delete() {
- mExists = false;
- return true;
- }
-
- @Override
- public boolean createNewFile() throws IOException {
- mExists = true;
- return true;
- }
- }
TrampolineTestable(Context context) {
super(context);
}
@Override
+ protected UserManager getUserManager() {
+ return sUserManagerMock;
+ }
+
+ @Override
public boolean isBackupDisabled() {
return sBackupDisabled;
}
@Override
- public File getSuppressFileForUser(int userId) {
- if (mSuppressFiles.get(userId) == null) {
- FakeFile file = new FakeFile(Integer.toString(userId));
- mSuppressFiles.append(userId, file);
- }
- return mSuppressFiles.get(userId);
+ protected File getSuppressFileForSystemUser() {
+ return sSuppressFile;
+ }
+
+ @Override
+ protected File getActivatedFileForNonSystemUser(int userId) {
+ return sActivatedFiles.get(userId);
}
protected int binderGetCallingUserId() {
@@ -1289,11 +1334,6 @@ public class TrampolineTest {
}
@Override
- protected void createBackupSuppressFileForUser(int userId) throws IOException {
- getSuppressFileForUser(userId).createNewFile();
- }
-
- @Override
protected void postToHandler(Runnable runnable) {
runnable.run();
}
diff --git a/services/tests/servicestests/src/com/android/server/backup/testutils/IPackageManagerStub.java b/services/tests/servicestests/src/com/android/server/backup/testutils/IPackageManagerStub.java
new file mode 100644
index 000000000000..095a1deac912
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/backup/testutils/IPackageManagerStub.java
@@ -0,0 +1,1172 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.backup.testutils;
+
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.IntentSender;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.ChangedPackages;
+import android.content.pm.IDexModuleRegisterCallback;
+import android.content.pm.IOnPermissionsChangeListener;
+import android.content.pm.IPackageDataObserver;
+import android.content.pm.IPackageDeleteObserver;
+import android.content.pm.IPackageDeleteObserver2;
+import android.content.pm.IPackageInstaller;
+import android.content.pm.IPackageManager;
+import android.content.pm.IPackageMoveObserver;
+import android.content.pm.IPackageStatsObserver;
+import android.content.pm.InstrumentationInfo;
+import android.content.pm.KeySet;
+import android.content.pm.ModuleInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ParceledListSlice;
+import android.content.pm.PermissionGroupInfo;
+import android.content.pm.PermissionInfo;
+import android.content.pm.ProviderInfo;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.content.pm.SuspendDialogInfo;
+import android.content.pm.VerifierDeviceIdentity;
+import android.content.pm.VersionedPackage;
+import android.content.pm.dex.IArtManager;
+import android.graphics.Bitmap;
+import android.os.IBinder;
+import android.os.PersistableBundle;
+import android.os.RemoteException;
+import java.util.List;
+
+/**
+ * Stub for IPackageManager to use in tests.
+ */
+public class IPackageManagerStub implements IPackageManager {
+ public static PackageInfo sPackageInfo;
+ public static int sApplicationEnabledSetting = PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
+
+ @Override
+ public PackageInfo getPackageInfo(String packageName, int flags, int userId)
+ throws RemoteException {
+ return sPackageInfo;
+ }
+
+ @Override
+ public int getApplicationEnabledSetting(String packageName, int userId) throws RemoteException {
+ return sApplicationEnabledSetting;
+ }
+
+ @Override
+ public void checkPackageStartable(String packageName, int userId) throws RemoteException {
+
+ }
+
+ @Override
+ public boolean isPackageAvailable(String packageName, int userId) throws RemoteException {
+ return false;
+ }
+
+ @Override
+ public PackageInfo getPackageInfoVersioned(VersionedPackage versionedPackage, int flags,
+ int userId) throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public int getPackageUid(String packageName, int flags, int userId) throws RemoteException {
+ return 0;
+ }
+
+ @Override
+ public int[] getPackageGids(String packageName, int flags, int userId) throws RemoteException {
+ return new int[0];
+ }
+
+ @Override
+ public String[] currentToCanonicalPackageNames(String[] names) throws RemoteException {
+ return new String[0];
+ }
+
+ @Override
+ public String[] canonicalToCurrentPackageNames(String[] names) throws RemoteException {
+ return new String[0];
+ }
+
+ @Override
+ public PermissionInfo getPermissionInfo(String name, String packageName, int flags)
+ throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public ParceledListSlice queryPermissionsByGroup(String group, int flags)
+ throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public PermissionGroupInfo getPermissionGroupInfo(String name, int flags)
+ throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public ParceledListSlice getAllPermissionGroups(int flags) throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public ApplicationInfo getApplicationInfo(String packageName, int flags, int userId)
+ throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public ActivityInfo getActivityInfo(ComponentName className, int flags, int userId)
+ throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public boolean activitySupportsIntent(ComponentName className, Intent intent,
+ String resolvedType)
+ throws RemoteException {
+ return false;
+ }
+
+ @Override
+ public ActivityInfo getReceiverInfo(ComponentName className, int flags, int userId)
+ throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public ServiceInfo getServiceInfo(ComponentName className, int flags, int userId)
+ throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public ProviderInfo getProviderInfo(ComponentName className, int flags, int userId)
+ throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public int checkPermission(String permName, String pkgName, int userId) throws RemoteException {
+ return 0;
+ }
+
+ @Override
+ public int checkUidPermission(String permName, int uid) throws RemoteException {
+ return 0;
+ }
+
+ @Override
+ public boolean addPermission(PermissionInfo info) throws RemoteException {
+ return false;
+ }
+
+ @Override
+ public void removePermission(String name) throws RemoteException {
+
+ }
+
+ @Override
+ public void grantRuntimePermission(String packageName, String permissionName, int userId)
+ throws RemoteException {
+
+ }
+
+ @Override
+ public void revokeRuntimePermission(String packageName, String permissionName, int userId)
+ throws RemoteException {
+
+ }
+
+ @Override
+ public void resetRuntimePermissions() throws RemoteException {
+
+ }
+
+ @Override
+ public int getPermissionFlags(String permissionName, String packageName, int userId)
+ throws RemoteException {
+ return 0;
+ }
+
+ @Override
+ public void updatePermissionFlags(String permissionName, String packageName, int flagMask,
+ int flagValues, int userId) throws RemoteException {
+
+ }
+
+ @Override
+ public void updatePermissionFlagsForAllApps(int flagMask, int flagValues, int userId)
+ throws RemoteException {
+
+ }
+
+ @Override
+ public boolean shouldShowRequestPermissionRationale(String permissionName, String packageName,
+ int userId) throws RemoteException {
+ return false;
+ }
+
+ @Override
+ public boolean isProtectedBroadcast(String actionName) throws RemoteException {
+ return false;
+ }
+
+ @Override
+ public int checkSignatures(String pkg1, String pkg2) throws RemoteException {
+ return 0;
+ }
+
+ @Override
+ public int checkUidSignatures(int uid1, int uid2) throws RemoteException {
+ return 0;
+ }
+
+ @Override
+ public List<String> getAllPackages() throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public String[] getPackagesForUid(int uid) throws RemoteException {
+ return new String[0];
+ }
+
+ @Override
+ public String getNameForUid(int uid) throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public String[] getNamesForUids(int[] uids) throws RemoteException {
+ return new String[0];
+ }
+
+ @Override
+ public int getUidForSharedUser(String sharedUserName) throws RemoteException {
+ return 0;
+ }
+
+ @Override
+ public int getFlagsForUid(int uid) throws RemoteException {
+ return 0;
+ }
+
+ @Override
+ public int getPrivateFlagsForUid(int uid) throws RemoteException {
+ return 0;
+ }
+
+ @Override
+ public boolean isUidPrivileged(int uid) throws RemoteException {
+ return false;
+ }
+
+ @Override
+ public String[] getAppOpPermissionPackages(String permissionName) throws RemoteException {
+ return new String[0];
+ }
+
+ @Override
+ public ResolveInfo resolveIntent(Intent intent, String resolvedType, int flags, int userId)
+ throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public ResolveInfo findPersistentPreferredActivity(Intent intent, int userId)
+ throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public boolean canForwardTo(Intent intent, String resolvedType, int sourceUserId,
+ int targetUserId) throws RemoteException {
+ return false;
+ }
+
+ @Override
+ public ParceledListSlice queryIntentActivities(Intent intent, String resolvedType, int flags,
+ int userId) throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public ParceledListSlice queryIntentActivityOptions(ComponentName caller, Intent[] specifics,
+ String[] specificTypes, Intent intent, String resolvedType, int flags, int userId)
+ throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public ParceledListSlice queryIntentReceivers(Intent intent, String resolvedType, int flags,
+ int userId) throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public ResolveInfo resolveService(Intent intent, String resolvedType, int flags, int userId)
+ throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public ParceledListSlice queryIntentServices(Intent intent, String resolvedType, int flags,
+ int userId) throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public ParceledListSlice queryIntentContentProviders(Intent intent, String resolvedType,
+ int flags, int userId) throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public ParceledListSlice getInstalledPackages(int flags, int userId) throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public ParceledListSlice getPackagesHoldingPermissions(String[] permissions, int flags,
+ int userId) throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public ParceledListSlice getInstalledApplications(int flags, int userId)
+ throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public ParceledListSlice getPersistentApplications(int flags) throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public ProviderInfo resolveContentProvider(String name, int flags, int userId)
+ throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public void querySyncProviders(List<String> outNames, List<ProviderInfo> outInfo)
+ throws RemoteException {
+
+ }
+
+ @Override
+ public ParceledListSlice queryContentProviders(String processName, int uid, int flags,
+ String metaDataKey) throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public InstrumentationInfo getInstrumentationInfo(ComponentName className, int flags)
+ throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public ParceledListSlice queryInstrumentation(String targetPackage, int flags)
+ throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public void finishPackageInstall(int token, boolean didLaunch) throws RemoteException {
+
+ }
+
+ @Override
+ public void setInstallerPackageName(String targetPackage, String installerPackageName)
+ throws RemoteException {
+
+ }
+
+ @Override
+ public void setApplicationCategoryHint(String packageName, int categoryHint,
+ String callerPackageName) throws RemoteException {
+
+ }
+
+ @Override
+ public void deletePackageAsUser(String packageName, int versionCode,
+ IPackageDeleteObserver observer, int userId, int flags) throws RemoteException {
+
+ }
+
+ @Override
+ public void deletePackageVersioned(VersionedPackage versionedPackage,
+ IPackageDeleteObserver2 observer, int userId, int flags) throws RemoteException {
+
+ }
+
+ @Override
+ public String getInstallerPackageName(String packageName) throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public void resetApplicationPreferences(int userId) throws RemoteException {
+
+ }
+
+ @Override
+ public ResolveInfo getLastChosenActivity(Intent intent, String resolvedType, int flags)
+ throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public void setLastChosenActivity(Intent intent, String resolvedType, int flags,
+ IntentFilter filter, int match, ComponentName activity) throws RemoteException {
+
+ }
+
+ @Override
+ public void addPreferredActivity(IntentFilter filter, int match, ComponentName[] set,
+ ComponentName activity, int userId) throws RemoteException {
+
+ }
+
+ @Override
+ public void replacePreferredActivity(IntentFilter filter, int match, ComponentName[] set,
+ ComponentName activity, int userId) throws RemoteException {
+
+ }
+
+ @Override
+ public void clearPackagePreferredActivities(String packageName) throws RemoteException {
+
+ }
+
+ @Override
+ public int getPreferredActivities(List<IntentFilter> outFilters,
+ List<ComponentName> outActivities, String packageName) throws RemoteException {
+ return 0;
+ }
+
+ @Override
+ public void addPersistentPreferredActivity(IntentFilter filter, ComponentName activity,
+ int userId) throws RemoteException {
+
+ }
+
+ @Override
+ public void clearPackagePersistentPreferredActivities(String packageName, int userId)
+ throws RemoteException {
+
+ }
+
+ @Override
+ public void addCrossProfileIntentFilter(IntentFilter intentFilter, String ownerPackage,
+ int sourceUserId, int targetUserId, int flags) throws RemoteException {
+
+ }
+
+ @Override
+ public void clearCrossProfileIntentFilters(int sourceUserId, String ownerPackage)
+ throws RemoteException {
+
+ }
+
+ @Override
+ public String[] setDistractingPackageRestrictionsAsUser(String[] packageNames,
+ int restrictionFlags, int userId) throws RemoteException {
+ return new String[0];
+ }
+
+ @Override
+ public String[] setPackagesSuspendedAsUser(String[] packageNames, boolean suspended,
+ PersistableBundle appExtras, PersistableBundle launcherExtras, SuspendDialogInfo dialogInfo,
+ String callingPackage, int userId) throws RemoteException {
+ return new String[0];
+ }
+
+ @Override
+ public String[] getUnsuspendablePackagesForUser(String[] packageNames, int userId)
+ throws RemoteException {
+ return new String[0];
+ }
+
+ @Override
+ public boolean isPackageSuspendedForUser(String packageName, int userId)
+ throws RemoteException {
+ return false;
+ }
+
+ @Override
+ public PersistableBundle getSuspendedPackageAppExtras(String packageName, int userId)
+ throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public byte[] getPreferredActivityBackup(int userId) throws RemoteException {
+ return new byte[0];
+ }
+
+ @Override
+ public void restorePreferredActivities(byte[] backup, int userId) throws RemoteException {
+
+ }
+
+ @Override
+ public byte[] getDefaultAppsBackup(int userId) throws RemoteException {
+ return new byte[0];
+ }
+
+ @Override
+ public void restoreDefaultApps(byte[] backup, int userId) throws RemoteException {
+
+ }
+
+ @Override
+ public byte[] getIntentFilterVerificationBackup(int userId) throws RemoteException {
+ return new byte[0];
+ }
+
+ @Override
+ public void restoreIntentFilterVerification(byte[] backup, int userId) throws RemoteException {
+
+ }
+
+ @Override
+ public byte[] getPermissionGrantBackup(int userId) throws RemoteException {
+ return new byte[0];
+ }
+
+ @Override
+ public void restorePermissionGrants(byte[] backup, int userId) throws RemoteException {
+
+ }
+
+ @Override
+ public ComponentName getHomeActivities(List<ResolveInfo> outHomeCandidates)
+ throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public void setHomeActivity(ComponentName className, int userId) throws RemoteException {
+
+ }
+
+ @Override
+ public void setComponentEnabledSetting(ComponentName componentName, int newState, int flags,
+ int userId) throws RemoteException {
+
+ }
+
+ @Override
+ public int getComponentEnabledSetting(ComponentName componentName, int userId)
+ throws RemoteException {
+ return 0;
+ }
+
+ @Override
+ public void setApplicationEnabledSetting(String packageName, int newState, int flags,
+ int userId,
+ String callingPackage) throws RemoteException {
+
+ }
+
+ @Override
+ public void logAppProcessStartIfNeeded(String processName, int uid, String seinfo,
+ String apkFile,
+ int pid) throws RemoteException {
+
+ }
+
+ @Override
+ public void flushPackageRestrictionsAsUser(int userId) throws RemoteException {
+
+ }
+
+ @Override
+ public void setPackageStoppedState(String packageName, boolean stopped, int userId)
+ throws RemoteException {
+
+ }
+
+ @Override
+ public void freeStorageAndNotify(String volumeUuid, long freeStorageSize, int storageFlags,
+ IPackageDataObserver observer) throws RemoteException {
+
+ }
+
+ @Override
+ public void freeStorage(String volumeUuid, long freeStorageSize, int storageFlags,
+ IntentSender pi) throws RemoteException {
+
+ }
+
+ @Override
+ public void deleteApplicationCacheFiles(String packageName, IPackageDataObserver observer)
+ throws RemoteException {
+
+ }
+
+ @Override
+ public void deleteApplicationCacheFilesAsUser(String packageName, int userId,
+ IPackageDataObserver observer) throws RemoteException {
+
+ }
+
+ @Override
+ public void clearApplicationUserData(String packageName, IPackageDataObserver observer,
+ int userId) throws RemoteException {
+
+ }
+
+ @Override
+ public void clearApplicationProfileData(String packageName) throws RemoteException {
+
+ }
+
+ @Override
+ public void getPackageSizeInfo(String packageName, int userHandle,
+ IPackageStatsObserver observer)
+ throws RemoteException {
+
+ }
+
+ @Override
+ public String[] getSystemSharedLibraryNames() throws RemoteException {
+ return new String[0];
+ }
+
+ @Override
+ public ParceledListSlice getSystemAvailableFeatures() throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public boolean hasSystemFeature(String name, int version) throws RemoteException {
+ return false;
+ }
+
+ @Override
+ public void enterSafeMode() throws RemoteException {
+
+ }
+
+ @Override
+ public boolean isSafeMode() throws RemoteException {
+ return false;
+ }
+
+ @Override
+ public void systemReady() throws RemoteException {
+
+ }
+
+ @Override
+ public boolean hasSystemUidErrors() throws RemoteException {
+ return false;
+ }
+
+ @Override
+ public void performFstrimIfNeeded() throws RemoteException {
+
+ }
+
+ @Override
+ public void updatePackagesIfNeeded() throws RemoteException {
+
+ }
+
+ @Override
+ public void notifyPackageUse(String packageName, int reason) throws RemoteException {
+
+ }
+
+ @Override
+ public void notifyDexLoad(String loadingPackageName, List<String> classLoadersNames,
+ List<String> classPaths, String loaderIsa) throws RemoteException {
+
+ }
+
+ @Override
+ public void registerDexModule(String packageName, String dexModulePath, boolean isSharedModule,
+ IDexModuleRegisterCallback callback) throws RemoteException {
+
+ }
+
+ @Override
+ public boolean performDexOptMode(String packageName, boolean checkProfiles,
+ String targetCompilerFilter, boolean force, boolean bootComplete, String splitName)
+ throws RemoteException {
+ return false;
+ }
+
+ @Override
+ public boolean performDexOptSecondary(String packageName, String targetCompilerFilter,
+ boolean force) throws RemoteException {
+ return false;
+ }
+
+ @Override
+ public boolean compileLayouts(String packageName) throws RemoteException {
+ return false;
+ }
+
+ @Override
+ public void dumpProfiles(String packageName) throws RemoteException {
+
+ }
+
+ @Override
+ public void forceDexOpt(String packageName) throws RemoteException {
+
+ }
+
+ @Override
+ public boolean runBackgroundDexoptJob(List<String> packageNames) throws RemoteException {
+ return false;
+ }
+
+ @Override
+ public void reconcileSecondaryDexFiles(String packageName) throws RemoteException {
+
+ }
+
+ @Override
+ public int getMoveStatus(int moveId) throws RemoteException {
+ return 0;
+ }
+
+ @Override
+ public void registerMoveCallback(IPackageMoveObserver callback) throws RemoteException {
+
+ }
+
+ @Override
+ public void unregisterMoveCallback(IPackageMoveObserver callback) throws RemoteException {
+
+ }
+
+ @Override
+ public int movePackage(String packageName, String volumeUuid) throws RemoteException {
+ return 0;
+ }
+
+ @Override
+ public int movePrimaryStorage(String volumeUuid) throws RemoteException {
+ return 0;
+ }
+
+ @Override
+ public boolean addPermissionAsync(PermissionInfo info) throws RemoteException {
+ return false;
+ }
+
+ @Override
+ public boolean setInstallLocation(int loc) throws RemoteException {
+ return false;
+ }
+
+ @Override
+ public int getInstallLocation() throws RemoteException {
+ return 0;
+ }
+
+ @Override
+ public int installExistingPackageAsUser(String packageName, int userId, int installFlags,
+ int installReason) throws RemoteException {
+ return 0;
+ }
+
+ @Override
+ public void verifyPendingInstall(int id, int verificationCode) throws RemoteException {
+
+ }
+
+ @Override
+ public void extendVerificationTimeout(int id, int verificationCodeAtTimeout,
+ long millisecondsToDelay) throws RemoteException {
+
+ }
+
+ @Override
+ public void verifyIntentFilter(int id, int verificationCode, List<String> failedDomains)
+ throws RemoteException {
+
+ }
+
+ @Override
+ public int getIntentVerificationStatus(String packageName, int userId) throws RemoteException {
+ return 0;
+ }
+
+ @Override
+ public boolean updateIntentVerificationStatus(String packageName, int status, int userId)
+ throws RemoteException {
+ return false;
+ }
+
+ @Override
+ public ParceledListSlice getIntentFilterVerifications(String packageName)
+ throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public ParceledListSlice getAllIntentFilters(String packageName) throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public boolean setDefaultBrowserPackageName(String packageName, int userId)
+ throws RemoteException {
+ return false;
+ }
+
+ @Override
+ public String getDefaultBrowserPackageName(int userId) throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public VerifierDeviceIdentity getVerifierDeviceIdentity() throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public boolean isFirstBoot() throws RemoteException {
+ return false;
+ }
+
+ @Override
+ public boolean isOnlyCoreApps() throws RemoteException {
+ return false;
+ }
+
+ @Override
+ public boolean isUpgrade() throws RemoteException {
+ return false;
+ }
+
+ @Override
+ public void setPermissionEnforced(String permission, boolean enforced) throws RemoteException {
+
+ }
+
+ @Override
+ public boolean isPermissionEnforced(String permission) throws RemoteException {
+ return false;
+ }
+
+ @Override
+ public boolean isStorageLow() throws RemoteException {
+ return false;
+ }
+
+ @Override
+ public boolean setApplicationHiddenSettingAsUser(String packageName, boolean hidden, int userId)
+ throws RemoteException {
+ return false;
+ }
+
+ @Override
+ public boolean getApplicationHiddenSettingAsUser(String packageName, int userId)
+ throws RemoteException {
+ return false;
+ }
+
+ @Override
+ public void setSystemAppHiddenUntilInstalled(String packageName, boolean hidden)
+ throws RemoteException {
+
+ }
+
+ @Override
+ public boolean setSystemAppInstallState(String packageName, boolean installed, int userId)
+ throws RemoteException {
+ return false;
+ }
+
+ @Override
+ public IPackageInstaller getPackageInstaller() throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public boolean setBlockUninstallForUser(String packageName, boolean blockUninstall, int userId)
+ throws RemoteException {
+ return false;
+ }
+
+ @Override
+ public boolean getBlockUninstallForUser(String packageName, int userId) throws RemoteException {
+ return false;
+ }
+
+ @Override
+ public KeySet getKeySetByAlias(String packageName, String alias) throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public KeySet getSigningKeySet(String packageName) throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public boolean isPackageSignedByKeySet(String packageName, KeySet ks) throws RemoteException {
+ return false;
+ }
+
+ @Override
+ public boolean isPackageSignedByKeySetExactly(String packageName, KeySet ks)
+ throws RemoteException {
+ return false;
+ }
+
+ @Override
+ public void addOnPermissionsChangeListener(IOnPermissionsChangeListener listener)
+ throws RemoteException {
+
+ }
+
+ @Override
+ public void removeOnPermissionsChangeListener(IOnPermissionsChangeListener listener)
+ throws RemoteException {
+
+ }
+
+ @Override
+ public void grantDefaultPermissionsToEnabledCarrierApps(String[] packageNames, int userId)
+ throws RemoteException {
+
+ }
+
+ @Override
+ public void grantDefaultPermissionsToEnabledImsServices(String[] packageNames, int userId)
+ throws RemoteException {
+
+ }
+
+ @Override
+ public void grantDefaultPermissionsToEnabledTelephonyDataServices(String[] packageNames,
+ int userId) throws RemoteException {
+
+ }
+
+ @Override
+ public void revokeDefaultPermissionsFromDisabledTelephonyDataServices(String[] packageNames,
+ int userId) throws RemoteException {
+
+ }
+
+ @Override
+ public void grantDefaultPermissionsToActiveLuiApp(String packageName, int userId)
+ throws RemoteException {
+
+ }
+
+ @Override
+ public void revokeDefaultPermissionsFromLuiApps(String[] packageNames, int userId)
+ throws RemoteException {
+
+ }
+
+ @Override
+ public boolean isPermissionRevokedByPolicy(String permission, String packageName, int userId)
+ throws RemoteException {
+ return false;
+ }
+
+ @Override
+ public String getPermissionControllerPackageName() throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public ParceledListSlice getInstantApps(int userId) throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public byte[] getInstantAppCookie(String packageName, int userId) throws RemoteException {
+ return new byte[0];
+ }
+
+ @Override
+ public boolean setInstantAppCookie(String packageName, byte[] cookie, int userId)
+ throws RemoteException {
+ return false;
+ }
+
+ @Override
+ public Bitmap getInstantAppIcon(String packageName, int userId) throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public boolean isInstantApp(String packageName, int userId) throws RemoteException {
+ return false;
+ }
+
+ @Override
+ public boolean setRequiredForSystemUser(String packageName, boolean systemUserApp)
+ throws RemoteException {
+ return false;
+ }
+
+ @Override
+ public void setUpdateAvailable(String packageName, boolean updateAvaialble)
+ throws RemoteException {
+
+ }
+
+ @Override
+ public String getServicesSystemSharedLibraryPackageName() throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public String getSharedSystemSharedLibraryPackageName() throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public ChangedPackages getChangedPackages(int sequenceNumber, int userId)
+ throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public boolean isPackageDeviceAdminOnAnyUser(String packageName) throws RemoteException {
+ return false;
+ }
+
+ @Override
+ public int getInstallReason(String packageName, int userId) throws RemoteException {
+ return 0;
+ }
+
+ @Override
+ public ParceledListSlice getSharedLibraries(String packageName, int flags, int userId)
+ throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public boolean canRequestPackageInstalls(String packageName, int userId)
+ throws RemoteException {
+ return false;
+ }
+
+ @Override
+ public void deletePreloadsFileCache() throws RemoteException {
+
+ }
+
+ @Override
+ public ComponentName getInstantAppResolverComponent() throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public ComponentName getInstantAppResolverSettingsComponent() throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public ComponentName getInstantAppInstallerComponent() throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public String getInstantAppAndroidId(String packageName, int userId) throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public IArtManager getArtManager() throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public void setHarmfulAppWarning(String packageName, CharSequence warning, int userId)
+ throws RemoteException {
+
+ }
+
+ @Override
+ public CharSequence getHarmfulAppWarning(String packageName, int userId)
+ throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public boolean hasSigningCertificate(String packageName, byte[] signingCertificate, int flags)
+ throws RemoteException {
+ return false;
+ }
+
+ @Override
+ public boolean hasUidSigningCertificate(int uid, byte[] signingCertificate, int flags)
+ throws RemoteException {
+ return false;
+ }
+
+ @Override
+ public String getSystemTextClassifierPackageName() throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public String getWellbeingPackageName() throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public boolean isPackageStateProtected(String packageName, int userId) throws RemoteException {
+ return false;
+ }
+
+ @Override
+ public void sendDeviceCustomizationReadyBroadcast() throws RemoteException {
+
+ }
+
+ @Override
+ public List<ModuleInfo> getInstalledModules(int flags) throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public ModuleInfo getModuleInfo(String packageName, int flags) throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public IBinder asBinder() {
+ return null;
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/backup/testutils/PackageManagerStub.java b/services/tests/servicestests/src/com/android/server/backup/testutils/PackageManagerStub.java
index 525135c6fc5a..717275232a81 100644
--- a/services/tests/servicestests/src/com/android/server/backup/testutils/PackageManagerStub.java
+++ b/services/tests/servicestests/src/com/android/server/backup/testutils/PackageManagerStub.java
@@ -48,11 +48,7 @@ public class PackageManagerStub extends PackageManager {
@Override
public PackageInfo getPackageInfo(String packageName, int flags)
throws NameNotFoundException {
- if (sPackageInfo == null) {
- throw new NameNotFoundException();
- }
-
- return sPackageInfo;
+ return getPackageInfoAsUser(packageName, flags, UserHandle.USER_SYSTEM);
}
@Override
@@ -64,7 +60,11 @@ public class PackageManagerStub extends PackageManager {
@Override
public PackageInfo getPackageInfoAsUser(String packageName, int flags, int userId)
throws NameNotFoundException {
- return null;
+ if (sPackageInfo == null) {
+ throw new NameNotFoundException();
+ }
+
+ return sPackageInfo;
}
@Override
diff --git a/services/tests/servicestests/src/com/android/server/backup/utils/AppBackupUtilsTest.java b/services/tests/servicestests/src/com/android/server/backup/utils/AppBackupUtilsTest.java
index 479a19b93ca7..a92b576e5d0f 100644
--- a/services/tests/servicestests/src/com/android/server/backup/utils/AppBackupUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/backup/utils/AppBackupUtilsTest.java
@@ -29,13 +29,14 @@ import android.content.pm.PackageParser;
import android.content.pm.Signature;
import android.content.pm.SigningInfo;
import android.os.Process;
+import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.server.backup.UserBackupManagerService;
-import com.android.server.backup.testutils.PackageManagerStub;
+import com.android.server.backup.testutils.IPackageManagerStub;
import org.junit.Before;
import org.junit.Test;
@@ -53,13 +54,17 @@ public class AppBackupUtilsTest {
private static final Signature SIGNATURE_3 = generateSignature((byte) 3);
private static final Signature SIGNATURE_4 = generateSignature((byte) 4);
- private PackageManagerStub mPackageManagerStub;
+ private IPackageManagerStub mPackageManagerStub;
private PackageManagerInternal mMockPackageManagerInternal;
+ private int mUserId;
+
@Before
public void setUp() throws Exception {
- mPackageManagerStub = new PackageManagerStub();
+ mPackageManagerStub = new IPackageManagerStub();
mMockPackageManagerInternal = mock(PackageManagerInternal.class);
+
+ mUserId = UserHandle.USER_SYSTEM;
}
@Test
@@ -71,7 +76,7 @@ public class AppBackupUtilsTest {
applicationInfo.packageName = TEST_PACKAGE_NAME;
boolean isEligible = AppBackupUtils.appIsEligibleForBackup(applicationInfo,
- mPackageManagerStub);
+ mPackageManagerStub, mUserId);
assertThat(isEligible).isFalse();
}
@@ -86,7 +91,7 @@ public class AppBackupUtilsTest {
applicationInfo.packageName = TEST_PACKAGE_NAME;
boolean isEligible = AppBackupUtils.appIsEligibleForBackup(applicationInfo,
- mPackageManagerStub);
+ mPackageManagerStub, mUserId);
assertThat(isEligible).isFalse();
}
@@ -100,7 +105,7 @@ public class AppBackupUtilsTest {
applicationInfo.packageName = UserBackupManagerService.SHARED_BACKUP_AGENT_PACKAGE;
boolean isEligible = AppBackupUtils.appIsEligibleForBackup(applicationInfo,
- mPackageManagerStub);
+ mPackageManagerStub, mUserId);
assertThat(isEligible).isFalse();
}
@@ -114,11 +119,11 @@ public class AppBackupUtilsTest {
applicationInfo.backupAgentName = CUSTOM_BACKUP_AGENT_NAME;
applicationInfo.packageName = TEST_PACKAGE_NAME;
- PackageManagerStub.sApplicationEnabledSetting =
+ IPackageManagerStub.sApplicationEnabledSetting =
PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
boolean isEligible = AppBackupUtils.appIsEligibleForBackup(applicationInfo,
- mPackageManagerStub);
+ mPackageManagerStub, mUserId);
assertThat(isEligible).isTrue();
}
@@ -132,11 +137,11 @@ public class AppBackupUtilsTest {
applicationInfo.backupAgentName = null;
applicationInfo.packageName = TEST_PACKAGE_NAME;
- PackageManagerStub.sApplicationEnabledSetting =
+ IPackageManagerStub.sApplicationEnabledSetting =
PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
boolean isEligible = AppBackupUtils.appIsEligibleForBackup(applicationInfo,
- mPackageManagerStub);
+ mPackageManagerStub, mUserId);
assertThat(isEligible).isTrue();
}
@@ -150,11 +155,11 @@ public class AppBackupUtilsTest {
applicationInfo.backupAgentName = CUSTOM_BACKUP_AGENT_NAME;
applicationInfo.packageName = TEST_PACKAGE_NAME;
- PackageManagerStub.sApplicationEnabledSetting =
+ IPackageManagerStub.sApplicationEnabledSetting =
PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
boolean isEligible = AppBackupUtils.appIsEligibleForBackup(applicationInfo,
- mPackageManagerStub);
+ mPackageManagerStub, mUserId);
assertThat(isEligible).isTrue();
}
@@ -168,11 +173,11 @@ public class AppBackupUtilsTest {
applicationInfo.backupAgentName = CUSTOM_BACKUP_AGENT_NAME;
applicationInfo.packageName = TEST_PACKAGE_NAME;
- PackageManagerStub.sApplicationEnabledSetting =
+ IPackageManagerStub.sApplicationEnabledSetting =
PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
boolean isEligible = AppBackupUtils.appIsEligibleForBackup(applicationInfo,
- mPackageManagerStub);
+ mPackageManagerStub, mUserId);
assertThat(isEligible).isFalse();
}
@@ -186,11 +191,11 @@ public class AppBackupUtilsTest {
applicationInfo.backupAgentName = null;
applicationInfo.packageName = TEST_PACKAGE_NAME;
- PackageManagerStub.sApplicationEnabledSetting =
+ IPackageManagerStub.sApplicationEnabledSetting =
PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
boolean isEligible = AppBackupUtils.appIsEligibleForBackup(applicationInfo,
- mPackageManagerStub);
+ mPackageManagerStub, mUserId);
assertThat(isEligible).isFalse();
}
@@ -204,11 +209,11 @@ public class AppBackupUtilsTest {
applicationInfo.backupAgentName = CUSTOM_BACKUP_AGENT_NAME;
applicationInfo.packageName = TEST_PACKAGE_NAME;
- PackageManagerStub.sApplicationEnabledSetting =
+ IPackageManagerStub.sApplicationEnabledSetting =
PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
boolean isEligible = AppBackupUtils.appIsEligibleForBackup(applicationInfo,
- mPackageManagerStub);
+ mPackageManagerStub, mUserId);
assertThat(isEligible).isFalse();
}
@@ -222,10 +227,11 @@ public class AppBackupUtilsTest {
applicationInfo.packageName = TEST_PACKAGE_NAME;
applicationInfo.enabled = true;
- PackageManagerStub.sApplicationEnabledSetting =
+ IPackageManagerStub.sApplicationEnabledSetting =
PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
- boolean isDisabled = AppBackupUtils.appIsDisabled(applicationInfo, mPackageManagerStub);
+ boolean isDisabled = AppBackupUtils.appIsDisabled(applicationInfo, mPackageManagerStub,
+ mUserId);
assertThat(isDisabled).isFalse();
}
@@ -239,10 +245,11 @@ public class AppBackupUtilsTest {
applicationInfo.packageName = TEST_PACKAGE_NAME;
applicationInfo.enabled = false;
- PackageManagerStub.sApplicationEnabledSetting =
+ IPackageManagerStub.sApplicationEnabledSetting =
PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
- boolean isDisabled = AppBackupUtils.appIsDisabled(applicationInfo, mPackageManagerStub);
+ boolean isDisabled = AppBackupUtils.appIsDisabled(applicationInfo, mPackageManagerStub,
+ mUserId);
assertThat(isDisabled).isTrue();
}
@@ -255,10 +262,11 @@ public class AppBackupUtilsTest {
applicationInfo.backupAgentName = CUSTOM_BACKUP_AGENT_NAME;
applicationInfo.packageName = TEST_PACKAGE_NAME;
- PackageManagerStub.sApplicationEnabledSetting =
+ IPackageManagerStub.sApplicationEnabledSetting =
PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
- boolean isDisabled = AppBackupUtils.appIsDisabled(applicationInfo, mPackageManagerStub);
+ boolean isDisabled = AppBackupUtils.appIsDisabled(applicationInfo, mPackageManagerStub,
+ mUserId);
assertThat(isDisabled).isFalse();
}
@@ -271,10 +279,11 @@ public class AppBackupUtilsTest {
applicationInfo.backupAgentName = CUSTOM_BACKUP_AGENT_NAME;
applicationInfo.packageName = TEST_PACKAGE_NAME;
- PackageManagerStub.sApplicationEnabledSetting =
+ IPackageManagerStub.sApplicationEnabledSetting =
PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
- boolean isDisabled = AppBackupUtils.appIsDisabled(applicationInfo, mPackageManagerStub);
+ boolean isDisabled = AppBackupUtils.appIsDisabled(applicationInfo, mPackageManagerStub,
+ mUserId);
assertThat(isDisabled).isTrue();
}
@@ -287,10 +296,11 @@ public class AppBackupUtilsTest {
applicationInfo.backupAgentName = CUSTOM_BACKUP_AGENT_NAME;
applicationInfo.packageName = TEST_PACKAGE_NAME;
- PackageManagerStub.sApplicationEnabledSetting =
+ IPackageManagerStub.sApplicationEnabledSetting =
PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER;
- boolean isDisabled = AppBackupUtils.appIsDisabled(applicationInfo, mPackageManagerStub);
+ boolean isDisabled = AppBackupUtils.appIsDisabled(applicationInfo, mPackageManagerStub,
+ mUserId);
assertThat(isDisabled).isTrue();
}
@@ -303,10 +313,11 @@ public class AppBackupUtilsTest {
applicationInfo.backupAgentName = CUSTOM_BACKUP_AGENT_NAME;
applicationInfo.packageName = TEST_PACKAGE_NAME;
- PackageManagerStub.sApplicationEnabledSetting =
+ IPackageManagerStub.sApplicationEnabledSetting =
PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED;
- boolean isDisabled = AppBackupUtils.appIsDisabled(applicationInfo, mPackageManagerStub);
+ boolean isDisabled = AppBackupUtils.appIsDisabled(applicationInfo, mPackageManagerStub,
+ mUserId);
assertThat(isDisabled).isTrue();
}
diff --git a/services/tests/servicestests/src/com/android/server/backup/utils/TarBackupReaderTest.java b/services/tests/servicestests/src/com/android/server/backup/utils/TarBackupReaderTest.java
index d43b677d0e50..5fcce671db1e 100644
--- a/services/tests/servicestests/src/com/android/server/backup/utils/TarBackupReaderTest.java
+++ b/services/tests/servicestests/src/com/android/server/backup/utils/TarBackupReaderTest.java
@@ -44,6 +44,7 @@ import android.content.pm.Signature;
import android.content.pm.SigningInfo;
import android.os.Bundle;
import android.os.Process;
+import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
import androidx.test.InstrumentationRegistry;
@@ -88,12 +89,14 @@ public class TarBackupReaderTest {
private final PackageManagerStub mPackageManagerStub = new PackageManagerStub();
private Context mContext;
+ private int mUserId;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mContext = InstrumentationRegistry.getContext();
+ mUserId = UserHandle.USER_SYSTEM;
}
@Test
@@ -146,7 +149,7 @@ public class TarBackupReaderTest {
fileMetadata);
RestorePolicy restorePolicy = tarBackupReader.chooseRestorePolicy(
mPackageManagerStub, false /* allowApks */, fileMetadata, signatures,
- mMockPackageManagerInternal);
+ mMockPackageManagerInternal, mUserId);
assertThat(restorePolicy).isEqualTo(RestorePolicy.IGNORE);
assertThat(fileMetadata.packageName).isEqualTo(TEST_PACKAGE_NAME);
@@ -160,7 +163,7 @@ public class TarBackupReaderTest {
fileMetadata);
restorePolicy = tarBackupReader.chooseRestorePolicy(
mPackageManagerStub, false /* allowApks */, fileMetadata, signatures,
- mMockPackageManagerInternal);
+ mMockPackageManagerInternal, mUserId);
assertThat(restorePolicy).isEqualTo(RestorePolicy.IGNORE);
assertThat(fileMetadata.packageName).isEqualTo(TEST_PACKAGE_NAME);
@@ -223,7 +226,7 @@ public class TarBackupReaderTest {
RestorePolicy policy = tarBackupReader.chooseRestorePolicy(mPackageManagerStub,
true /* allowApks */, new FileMetadata(), null /* signatures */,
- mMockPackageManagerInternal);
+ mMockPackageManagerInternal, mUserId);
assertThat(policy).isEqualTo(RestorePolicy.IGNORE);
verifyZeroInteractions(mBackupManagerMonitorMock);
@@ -244,7 +247,7 @@ public class TarBackupReaderTest {
RestorePolicy policy = tarBackupReader.chooseRestorePolicy(mPackageManagerStub,
true /* allowApks */, info, new Signature[0] /* signatures */,
- mMockPackageManagerInternal);
+ mMockPackageManagerInternal, mUserId);
assertThat(policy).isEqualTo(RestorePolicy.ACCEPT_IF_APK);
ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
@@ -269,7 +272,7 @@ public class TarBackupReaderTest {
RestorePolicy policy = tarBackupReader.chooseRestorePolicy(mPackageManagerStub,
true /* allowApks */, info, new Signature[0] /* signatures */,
- mMockPackageManagerInternal);
+ mMockPackageManagerInternal, mUserId);
assertThat(policy).isEqualTo(RestorePolicy.ACCEPT_IF_APK);
ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
@@ -295,7 +298,7 @@ public class TarBackupReaderTest {
RestorePolicy policy = tarBackupReader.chooseRestorePolicy(mPackageManagerStub,
false /* allowApks */, new FileMetadata(), new Signature[0] /* signatures */,
- mMockPackageManagerInternal);
+ mMockPackageManagerInternal, mUserId);
assertThat(policy).isEqualTo(RestorePolicy.IGNORE);
ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
@@ -320,7 +323,7 @@ public class TarBackupReaderTest {
RestorePolicy policy = tarBackupReader.chooseRestorePolicy(mPackageManagerStub,
false /* allowApks */, new FileMetadata(), new Signature[0] /* signatures */,
- mMockPackageManagerInternal);
+ mMockPackageManagerInternal, mUserId);
assertThat(policy).isEqualTo(RestorePolicy.IGNORE);
ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
@@ -347,7 +350,7 @@ public class TarBackupReaderTest {
RestorePolicy policy = tarBackupReader.chooseRestorePolicy(mPackageManagerStub,
false /* allowApks */, new FileMetadata(), new Signature[0] /* signatures */,
- mMockPackageManagerInternal);
+ mMockPackageManagerInternal, mUserId);
assertThat(policy).isEqualTo(RestorePolicy.IGNORE);
ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
@@ -381,7 +384,8 @@ public class TarBackupReaderTest {
PackageManagerStub.sPackageInfo = packageInfo;
RestorePolicy policy = tarBackupReader.chooseRestorePolicy(mPackageManagerStub,
- false /* allowApks */, new FileMetadata(), signatures, mMockPackageManagerInternal);
+ false /* allowApks */, new FileMetadata(), signatures,
+ mMockPackageManagerInternal, mUserId);
assertThat(policy).isEqualTo(RestorePolicy.IGNORE);
ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
@@ -419,7 +423,8 @@ public class TarBackupReaderTest {
doReturn(true).when(mMockPackageManagerInternal).isDataRestoreSafe(FAKE_SIGNATURE_1,
packageInfo.packageName);
RestorePolicy policy = tarBackupReader.chooseRestorePolicy(mPackageManagerStub,
- false /* allowApks */, new FileMetadata(), signatures, mMockPackageManagerInternal);
+ false /* allowApks */, new FileMetadata(), signatures,
+ mMockPackageManagerInternal, mUserId);
assertThat(policy).isEqualTo(RestorePolicy.ACCEPT);
ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
@@ -456,7 +461,8 @@ public class TarBackupReaderTest {
doReturn(true).when(mMockPackageManagerInternal).isDataRestoreSafe(FAKE_SIGNATURE_1,
packageInfo.packageName);
RestorePolicy policy = tarBackupReader.chooseRestorePolicy(mPackageManagerStub,
- false /* allowApks */, new FileMetadata(), signatures, mMockPackageManagerInternal);
+ false /* allowApks */, new FileMetadata(), signatures,
+ mMockPackageManagerInternal, mUserId);
assertThat(policy).isEqualTo(RestorePolicy.ACCEPT);
ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
@@ -497,7 +503,8 @@ public class TarBackupReaderTest {
doReturn(true).when(mMockPackageManagerInternal).isDataRestoreSafe(FAKE_SIGNATURE_1,
packageInfo.packageName);
RestorePolicy policy = tarBackupReader.chooseRestorePolicy(mPackageManagerStub,
- false /* allowApks */, info, signatures, mMockPackageManagerInternal);
+ false /* allowApks */, info, signatures, mMockPackageManagerInternal,
+ mUserId);
assertThat(policy).isEqualTo(RestorePolicy.ACCEPT);
ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
@@ -540,7 +547,8 @@ public class TarBackupReaderTest {
doReturn(true).when(mMockPackageManagerInternal).isDataRestoreSafe(FAKE_SIGNATURE_1,
packageInfo.packageName);
RestorePolicy policy = tarBackupReader.chooseRestorePolicy(mPackageManagerStub,
- true /* allowApks */, info, signatures, mMockPackageManagerInternal);
+ true /* allowApks */, info, signatures, mMockPackageManagerInternal,
+ mUserId);
assertThat(policy).isEqualTo(RestorePolicy.ACCEPT_IF_APK);
verifyNoMoreInteractions(mBackupManagerMonitorMock);
@@ -579,7 +587,8 @@ public class TarBackupReaderTest {
doReturn(true).when(mMockPackageManagerInternal).isDataRestoreSafe(FAKE_SIGNATURE_1,
packageInfo.packageName);
RestorePolicy policy = tarBackupReader.chooseRestorePolicy(mPackageManagerStub,
- false /* allowApks */, info, signatures, mMockPackageManagerInternal);
+ false /* allowApks */, info, signatures, mMockPackageManagerInternal,
+ mUserId);
assertThat(policy).isEqualTo(RestorePolicy.IGNORE);
ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
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 0813e6fa0252..535198b3dbfa 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -5239,6 +5239,45 @@ public class DevicePolicyManagerTest extends DpmTestBase {
assertEquals(PASSWORD_COMPLEXITY_HIGH, dpm.getPasswordComplexity());
}
+ public void testCrossProfileCalendarPackages_initiallyEmpty() {
+ setAsProfileOwner(admin1);
+ final Set<String> packages = dpm.getCrossProfileCalendarPackages(admin1);
+ assertCrossProfileCalendarPackagesEqual(packages, Collections.emptySet());
+ }
+
+ public void testCrossProfileCalendarPackages_reopenDpms() {
+ setAsProfileOwner(admin1);
+ dpm.setCrossProfileCalendarPackages(admin1, null);
+ Set<String> packages = dpm.getCrossProfileCalendarPackages(admin1);
+ assertTrue(packages == null);
+ initializeDpms();
+ packages = dpm.getCrossProfileCalendarPackages(admin1);
+ assertTrue(packages == null);
+
+ dpm.setCrossProfileCalendarPackages(admin1, Collections.emptySet());
+ packages = dpm.getCrossProfileCalendarPackages(admin1);
+ assertCrossProfileCalendarPackagesEqual(packages, Collections.emptySet());
+ initializeDpms();
+ packages = dpm.getCrossProfileCalendarPackages(admin1);
+ assertCrossProfileCalendarPackagesEqual(packages, Collections.emptySet());
+
+ final String dummyPackageName = "test";
+ final Set<String> testPackages = new ArraySet<String>(Arrays.asList(dummyPackageName));
+ dpm.setCrossProfileCalendarPackages(admin1, testPackages);
+ packages = dpm.getCrossProfileCalendarPackages(admin1);
+ assertCrossProfileCalendarPackagesEqual(packages, testPackages);
+ initializeDpms();
+ packages = dpm.getCrossProfileCalendarPackages(admin1);
+ assertCrossProfileCalendarPackagesEqual(packages, testPackages);
+ }
+
+ private void assertCrossProfileCalendarPackagesEqual(Set<String> expected, Set<String> actual) {
+ assertTrue(expected != null);
+ assertTrue(actual != null);
+ assertTrue(expected.containsAll(actual));
+ assertTrue(actual.containsAll(expected));
+ }
+
private void configureProfileOwnerForDeviceIdAccess(ComponentName who, int userId) {
final long ident = mServiceContext.binder.clearCallingIdentity();
mServiceContext.binder.callingUid =
diff --git a/services/tests/servicestests/src/com/android/server/display/ColorDisplayServiceTest.java b/services/tests/servicestests/src/com/android/server/display/ColorDisplayServiceTest.java
index e0ecd3ee24f0..ca39a7f98f79 100644
--- a/services/tests/servicestests/src/com/android/server/display/ColorDisplayServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/ColorDisplayServiceTest.java
@@ -25,6 +25,7 @@ import android.app.ActivityManager;
import android.app.AlarmManager;
import android.content.Context;
import android.content.ContextWrapper;
+import android.hardware.display.ColorDisplayManager;
import android.os.Handler;
import android.os.UserHandle;
import android.provider.Settings;
@@ -45,7 +46,9 @@ import com.android.server.twilight.TwilightManager;
import com.android.server.twilight.TwilightState;
import org.junit.After;
+import org.junit.AfterClass;
import org.junit.Before;
+import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
@@ -67,16 +70,23 @@ public class ColorDisplayServiceTest {
private MockTwilightManager mTwilightManager;
- private ColorDisplayController mColorDisplayController;
private ColorDisplayService mColorDisplayService;
+ private ColorDisplayController mColorDisplayController;
+ private ColorDisplayService.BinderService mBinderService;
+
+ @BeforeClass
+ public static void setDtm() {
+ final DisplayTransformManager dtm = Mockito.mock(DisplayTransformManager.class);
+ LocalServices.addService(DisplayTransformManager.class, dtm);
+ }
@Before
public void setUp() {
mContext = Mockito.spy(new ContextWrapper(InstrumentationRegistry.getTargetContext()));
- mUserId = ActivityManager.getCurrentUser();
-
doReturn(mContext).when(mContext).getApplicationContext();
+ mUserId = ActivityManager.getCurrentUser();
+
final MockContentResolver cr = new MockContentResolver(mContext);
cr.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
doReturn(cr).when(mContext).getContentResolver();
@@ -84,23 +94,19 @@ public class ColorDisplayServiceTest {
final AlarmManager am = Mockito.mock(AlarmManager.class);
doReturn(am).when(mContext).getSystemService(Context.ALARM_SERVICE);
- final DisplayTransformManager dtm = Mockito.mock(DisplayTransformManager.class);
- LocalServices.addService(DisplayTransformManager.class, dtm);
-
mTwilightManager = new MockTwilightManager();
LocalServices.addService(TwilightManager.class, mTwilightManager);
mColorDisplayController = new ColorDisplayController(mContext, mUserId);
mColorDisplayService = new ColorDisplayService(mContext);
+ mBinderService = mColorDisplayService.new BinderService();
}
@After
public void tearDown() {
- LocalServices.removeServiceForTest(DisplayTransformManager.class);
LocalServices.removeServiceForTest(TwilightManager.class);
mColorDisplayService = null;
- mColorDisplayController = null;
mTwilightManager = null;
@@ -108,6 +114,11 @@ public class ColorDisplayServiceTest {
mContext = null;
}
+ @AfterClass
+ public static void removeDtm() {
+ LocalServices.removeServiceForTest(DisplayTransformManager.class);
+ }
+
@Test
public void customSchedule_whenStartedAfterNight_ifOffAfterNight_turnsOff() {
setAutoModeCustom(-120 /* startTimeOffset */, -60 /* endTimeOffset */);
@@ -981,7 +992,7 @@ public class ColorDisplayServiceTest {
* @param endTimeOffset the offset relative to now to deactivate Night display (in minutes)
*/
private void setAutoModeCustom(int startTimeOffset, int endTimeOffset) {
- mColorDisplayController.setAutoMode(ColorDisplayController.AUTO_MODE_CUSTOM);
+ mColorDisplayController.setAutoMode(ColorDisplayManager.AUTO_MODE_CUSTOM_TIME);
mColorDisplayController.setCustomStartTime(getLocalTimeRelativeToNow(startTimeOffset));
mColorDisplayController.setCustomEndTime(getLocalTimeRelativeToNow(endTimeOffset));
}
@@ -993,7 +1004,7 @@ public class ColorDisplayServiceTest {
* @param sunriseOffset the offset relative to now for sunrise (in minutes)
*/
private void setAutoModeTwilight(int sunsetOffset, int sunriseOffset) {
- mColorDisplayController.setAutoMode(ColorDisplayController.AUTO_MODE_TWILIGHT);
+ mColorDisplayController.setAutoMode(ColorDisplayManager.AUTO_MODE_TWILIGHT);
mTwilightManager.setTwilightState(
getTwilightStateRelativeToNow(sunsetOffset, sunriseOffset));
}
@@ -1006,7 +1017,7 @@ public class ColorDisplayServiceTest {
* activated (in minutes)
*/
private void setActivated(boolean activated, int lastActivatedTimeOffset) {
- mColorDisplayController.setActivated(activated);
+ mBinderService.setNightDisplayActivated(activated);
Secure.putStringForUser(mContext.getContentResolver(),
Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME,
LocalDateTime.now().plusMinutes(lastActivatedTimeOffset).toString(),
@@ -1036,10 +1047,10 @@ public class ColorDisplayServiceTest {
/**
* Configures color mode via ColorDisplayController.
*
- * @param mode the color mode to set
+ * @param colorMode the color mode to set
*/
- private void setColorMode(int mode) {
- mColorDisplayController.setColorMode(mode);
+ private void setColorMode(int colorMode) {
+ mColorDisplayController.setColorMode(colorMode);
}
/**
@@ -1081,8 +1092,8 @@ public class ColorDisplayServiceTest {
* @param activated the expected activated state of Night display
*/
private void assertActivated(boolean activated) {
- assertWithMessage("Invalid Night display activated state")
- .that(mColorDisplayController.isActivated())
+ assertWithMessage("Incorrect Night display activated state")
+ .that(mBinderService.isNightDisplayActivated())
.isEqualTo(activated);
}
diff --git a/services/tests/servicestests/src/com/android/server/job/MaxJobCountsTest.java b/services/tests/servicestests/src/com/android/server/job/MaxJobCountsTest.java
index 01199a36ff5b..0219f2201675 100644
--- a/services/tests/servicestests/src/com/android/server/job/MaxJobCountsTest.java
+++ b/services/tests/servicestests/src/com/android/server/job/MaxJobCountsTest.java
@@ -17,15 +17,15 @@ package com.android.server.job;
import android.util.KeyValueListParser;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
import com.android.server.job.JobSchedulerService.MaxJobCounts;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
@RunWith(AndroidJUnit4.class)
@SmallTest
public class MaxJobCountsTest {
@@ -43,13 +43,14 @@ public class MaxJobCountsTest {
counts.parse(parser);
- Assert.assertEquals(expectedTotal, counts.getTotalMax());
+ Assert.assertEquals(expectedTotal, counts.getMaxTotal());
Assert.assertEquals(expectedMaxBg, counts.getMaxBg());
Assert.assertEquals(expectedMinBg, counts.getMinBg());
}
@Test
public void test() {
+ // Tests with various combinations.
check("", /*default*/ 5, 1, 0, /*expected*/ 5, 1, 0);
check("", /*default*/ 5, 0, 0, /*expected*/ 5, 1, 0);
check("", /*default*/ 0, 0, 0, /*expected*/ 1, 1, 0);
@@ -58,7 +59,11 @@ public class MaxJobCountsTest {
check("", /*default*/ 6, 5, 6, /*expected*/ 6, 5, 5);
check("", /*default*/ 4, 5, 6, /*expected*/ 4, 4, 3);
check("", /*default*/ 5, 1, 1, /*expected*/ 5, 1, 1);
+ check("", /*default*/ 15, 15, 15, /*expected*/ 15, 15, 14);
+ check("", /*default*/ 16, 16, 16, /*expected*/ 16, 16, 15);
+ check("", /*default*/ 20, 20, 20, /*expected*/ 16, 16, 15);
+ // Test for overriding with a setting string.
check("total=5,maxbg=4,minbg=3", /*default*/ 9, 9, 9, /*expected*/ 5, 4, 3);
check("total=5", /*default*/ 9, 9, 9, /*expected*/ 5, 5, 4);
check("maxbg=4", /*default*/ 9, 9, 9, /*expected*/ 9, 4, 4);
diff --git a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
index bc1f7981258d..6845f15f6a28 100644
--- a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
@@ -43,6 +43,7 @@ import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
import android.app.IUidObserver;
import android.app.Person;
+import android.app.admin.DevicePolicyManager;
import android.app.usage.UsageStatsManagerInternal;
import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
@@ -143,8 +144,10 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase {
switch (name) {
case Context.USER_SERVICE:
return mMockUserManager;
+ case Context.DEVICE_POLICY_SERVICE:
+ return mMockDevicePolicyManager;
}
- throw new UnsupportedOperationException();
+ throw new UnsupportedOperationException("Couldn't find system service: " + name);
}
@Override
@@ -610,6 +613,7 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase {
protected PackageManager mMockPackageManager;
protected PackageManagerInternal mMockPackageManagerInternal;
protected UserManager mMockUserManager;
+ protected DevicePolicyManager mMockDevicePolicyManager;
protected UserManagerInternal mMockUserManagerInternal;
protected UsageStatsManagerInternal mMockUsageStatsManagerInternal;
protected ActivityManagerInternal mMockActivityManagerInternal;
@@ -750,6 +754,7 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase {
mMockPackageManager = mock(PackageManager.class);
mMockPackageManagerInternal = mock(PackageManagerInternal.class);
mMockUserManager = mock(UserManager.class);
+ mMockDevicePolicyManager = mock(DevicePolicyManager.class);
mMockUserManagerInternal = mock(UserManagerInternal.class);
mMockUsageStatsManagerInternal = mock(UsageStatsManagerInternal.class);
mMockActivityManagerInternal = mock(ActivityManagerInternal.class);
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java
index 7cd8ceddfd23..2ddc71f570b8 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java
@@ -55,6 +55,7 @@ import org.mockito.quality.Strictness;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
@@ -685,4 +686,68 @@ public class DexManagerTests {
return mPackageInfo.applicationInfo.splitSourceDirs[length - 1];
}
}
+
+ private boolean shouldPackageRunOob(
+ boolean isDefaultEnabled, String defaultWhitelist, String overrideEnabled,
+ String overrideWhitelist, Collection<String> packageNamesInSameProcess) {
+ return DexManager.isPackageSelectedToRunOobInternal(
+ isDefaultEnabled, defaultWhitelist, overrideEnabled, overrideWhitelist,
+ packageNamesInSameProcess);
+ }
+
+ @Test
+ public void testOobPackageSelectionSwitch() {
+ // Feature is off by default, not overriden
+ assertFalse(shouldPackageRunOob(false, "ALL", null, null, null));
+
+ // Feature is off by default, overriden
+ assertTrue(shouldPackageRunOob(false, "ALL", "true", "ALL", null));
+ assertFalse(shouldPackageRunOob(false, "ALL", "false", null, null));
+ assertFalse(shouldPackageRunOob(false, "ALL", "false", "ALL", null));
+ assertFalse(shouldPackageRunOob(false, "ALL", "false", null, null));
+
+ // Feature is on by default, not overriden
+ assertTrue(shouldPackageRunOob(true, "ALL", null, null, null));
+ assertTrue(shouldPackageRunOob(true, "ALL", null, null, null));
+ assertTrue(shouldPackageRunOob(true, "ALL", null, "ALL", null));
+
+ // Feature is on by default, overriden
+ assertTrue(shouldPackageRunOob(true, "ALL", "true", null, null));
+ assertTrue(shouldPackageRunOob(true, "ALL", "true", "ALL", null));
+ assertFalse(shouldPackageRunOob(true, "ALL", "false", null, null));
+ assertFalse(shouldPackageRunOob(true, "ALL", "false", "ALL", null));
+ }
+
+ @Test
+ public void testOobPackageSelectionWhitelist() {
+ // Various whitelist of apps to run in OOB mode.
+ final String kWhitelistApp0 = "com.priv.app0";
+ final String kWhitelistApp1 = "com.priv.app1";
+ final String kWhitelistApp2 = "com.priv.app2";
+ final String kWhitelistApp1AndApp2 = "com.priv.app1,com.priv.app2";
+
+ // Packages that shares the targeting process.
+ final Collection<String> runningPackages = Arrays.asList("com.priv.app1", "com.priv.app2");
+
+ // Feature is off, whitelist does not matter
+ assertFalse(shouldPackageRunOob(false, kWhitelistApp0, null, null, runningPackages));
+ assertFalse(shouldPackageRunOob(false, kWhitelistApp1, null, null, runningPackages));
+ assertFalse(shouldPackageRunOob(false, "", null, kWhitelistApp1, runningPackages));
+ assertFalse(shouldPackageRunOob(false, "", null, "ALL", runningPackages));
+ assertFalse(shouldPackageRunOob(false, "ALL", null, "ALL", runningPackages));
+ assertFalse(shouldPackageRunOob(false, "ALL", null, "", runningPackages));
+
+ // Feature is on, app not in default or overridden whitelist
+ assertFalse(shouldPackageRunOob(true, kWhitelistApp0, null, null, runningPackages));
+ assertFalse(shouldPackageRunOob(true, "", null, kWhitelistApp0, runningPackages));
+ assertFalse(shouldPackageRunOob(true, "ALL", null, kWhitelistApp0, runningPackages));
+
+ // Feature is on, app in default or overridden whitelist
+ assertTrue(shouldPackageRunOob(true, kWhitelistApp1, null, null, runningPackages));
+ assertTrue(shouldPackageRunOob(true, kWhitelistApp2, null, null, runningPackages));
+ assertTrue(shouldPackageRunOob(true, kWhitelistApp1AndApp2, null, null, runningPackages));
+ assertTrue(shouldPackageRunOob(true, kWhitelistApp1, null, "ALL", runningPackages));
+ assertTrue(shouldPackageRunOob(true, "", null, kWhitelistApp1, runningPackages));
+ assertTrue(shouldPackageRunOob(true, "ALL", null, kWhitelistApp1, runningPackages));
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/usage/AppTimeLimitControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppTimeLimitControllerTests.java
index b348aeef802e..5d69bbdcf0c7 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppTimeLimitControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppTimeLimitControllerTests.java
@@ -18,12 +18,15 @@ package com.android.server.usage;
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.junit.Assert.fail;
import android.app.PendingIntent;
+import android.app.usage.UsageStatsManagerInternal;
import android.os.HandlerThread;
import android.os.Looper;
+import android.os.UserHandle;
import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
@@ -64,7 +67,7 @@ public class AppTimeLimitControllerTests {
private static final long TIME_30_MIN = 30 * 60_000L;
private static final long TIME_10_MIN = 10 * 60_000L;
- private static final long TIME_1_MIN = 10 * 60_000L;
+ private static final long TIME_1_MIN = 1 * 60_000L;
private static final long MAX_OBSERVER_PER_UID = 10;
private static final long MIN_TIME_LIMIT = 4_000L;
@@ -128,6 +131,11 @@ public class AppTimeLimitControllerTests {
}
@Override
+ protected long getAppUsageLimitObserverPerUidLimit() {
+ return MAX_OBSERVER_PER_UID;
+ }
+
+ @Override
protected long getMinTimeLimit() {
return MIN_TIME_LIMIT;
}
@@ -164,6 +172,16 @@ public class AppTimeLimitControllerTests {
assertTrue("Observer wasn't added", hasUsageSessionObserver(UID, OBS_ID2));
}
+ /** Verify app usage limit observer is added */
+ @Test
+ public void testAppUsageLimitObserver_AddObserver() {
+ addAppUsageLimitObserver(OBS_ID1, GROUP1, TIME_30_MIN);
+ assertTrue("Observer wasn't added", hasAppUsageLimitObserver(UID, OBS_ID1));
+ addAppUsageLimitObserver(OBS_ID2, GROUP_GAME, TIME_30_MIN);
+ assertTrue("Observer wasn't added", hasAppUsageLimitObserver(UID, OBS_ID2));
+ assertTrue("Observer wasn't added", hasAppUsageLimitObserver(UID, OBS_ID1));
+ }
+
/** Verify app usage observer is removed */
@Test
public void testAppUsageObserver_RemoveObserver() {
@@ -182,6 +200,15 @@ public class AppTimeLimitControllerTests {
assertFalse("Observer wasn't removed", hasUsageSessionObserver(UID, OBS_ID1));
}
+ /** Verify app usage limit observer is removed */
+ @Test
+ public void testAppUsageLimitObserver_RemoveObserver() {
+ addAppUsageLimitObserver(OBS_ID1, GROUP1, TIME_30_MIN);
+ assertTrue("Observer wasn't added", hasAppUsageLimitObserver(UID, OBS_ID1));
+ mController.removeAppUsageLimitObserver(UID, OBS_ID1, USER_ID);
+ assertFalse("Observer wasn't removed", hasAppUsageLimitObserver(UID, OBS_ID1));
+ }
+
/** Verify nothing happens when a nonexistent app usage observer is removed */
@Test
public void testAppUsageObserver_RemoveMissingObserver() {
@@ -218,6 +245,24 @@ public class AppTimeLimitControllerTests {
assertFalse("Observer should not exist", hasUsageSessionObserver(UID, OBS_ID1));
}
+ /** Verify nothing happens when a nonexistent app usage limit observer is removed */
+ @Test
+ public void testAppUsageLimitObserver_RemoveMissingObserver() {
+ assertFalse("Observer should not exist", hasAppUsageLimitObserver(UID, OBS_ID1));
+ try {
+ mController.removeAppUsageLimitObserver(UID, OBS_ID1, USER_ID);
+ } catch (Exception e) {
+ StringWriter sw = new StringWriter();
+ sw.write("Hit exception trying to remove nonexistent observer:\n");
+ sw.write(e.toString());
+ PrintWriter pw = new PrintWriter(sw);
+ e.printStackTrace(pw);
+ sw.write("\nTest Failed!");
+ fail(sw.toString());
+ }
+ assertFalse("Observer should not exist", hasAppUsageLimitObserver(UID, OBS_ID1));
+ }
+
/** Re-adding an observer should result in only one copy */
@Test
public void testAppUsageObserver_ObserverReAdd() {
@@ -242,22 +287,39 @@ public class AppTimeLimitControllerTests {
assertFalse("Observer wasn't removed", hasUsageSessionObserver(UID, OBS_ID1));
}
+ /** Re-adding an observer should result in only one copy */
+ @Test
+ public void testAppUsageLimitObserver_ObserverReAdd() {
+ addAppUsageLimitObserver(OBS_ID1, GROUP1, TIME_30_MIN);
+ assertTrue("Observer wasn't added", hasAppUsageLimitObserver(UID, OBS_ID1));
+ addAppUsageLimitObserver(OBS_ID1, GROUP1, TIME_10_MIN);
+ assertTrue("Observer wasn't added",
+ getAppUsageLimitObserver(UID, OBS_ID1).getTimeLimitMs() == TIME_10_MIN);
+ mController.removeAppUsageLimitObserver(UID, OBS_ID1, USER_ID);
+ assertFalse("Observer wasn't removed", hasAppUsageLimitObserver(UID, OBS_ID1));
+ }
+
/** Different type observers can be registered to the same observerId value */
@Test
public void testAllObservers_ExclusiveObserverIds() {
addAppUsageObserver(OBS_ID1, GROUP1, TIME_10_MIN);
addUsageSessionObserver(OBS_ID1, GROUP1, TIME_30_MIN, TIME_1_MIN);
+ addAppUsageLimitObserver(OBS_ID1, GROUP1, TIME_10_MIN);
assertTrue("Observer wasn't added", hasAppUsageObserver(UID, OBS_ID1));
assertTrue("Observer wasn't added", hasUsageSessionObserver(UID, OBS_ID1));
+ assertTrue("Observer wasn't added", hasAppUsageLimitObserver(UID, OBS_ID1));
AppTimeLimitController.UsageGroup appUsageGroup = mController.getAppUsageGroup(UID,
OBS_ID1);
AppTimeLimitController.UsageGroup sessionUsageGroup = mController.getSessionUsageGroup(UID,
OBS_ID1);
+ AppTimeLimitController.UsageGroup appUsageLimitGroup = getAppUsageLimitObserver(
+ UID, OBS_ID1);
// Verify data still intact
assertEquals(TIME_10_MIN, appUsageGroup.getTimeLimitMs());
assertEquals(TIME_30_MIN, sessionUsageGroup.getTimeLimitMs());
+ assertEquals(TIME_10_MIN, appUsageLimitGroup.getTimeLimitMs());
}
/** Verify that usage across different apps within a group are added up */
@@ -299,7 +361,7 @@ public class AppTimeLimitControllerTests {
@Test
public void testUsageSessionObserver_Accumulation() throws Exception {
setTime(0L);
- addUsageSessionObserver(OBS_ID1, GROUP1, TIME_30_MIN, TIME_1_MIN);
+ addUsageSessionObserver(OBS_ID1, GROUP1, TIME_30_MIN, TIME_10_MIN);
startUsage(PKG_SOC1);
// Add 10 mins
setTime(TIME_10_MIN);
@@ -330,6 +392,41 @@ public class AppTimeLimitControllerTests {
assertTrue(mLimitReachedLatch.await(100L, TimeUnit.MILLISECONDS));
}
+ /** Verify that usage across different apps within a group are added up */
+ @Test
+ public void testAppUsageLimitObserver_Accumulation() throws Exception {
+ setTime(0L);
+ addAppUsageLimitObserver(OBS_ID1, GROUP1, TIME_30_MIN);
+ startUsage(PKG_SOC1);
+ // Add 10 mins
+ setTime(TIME_10_MIN);
+ stopUsage(PKG_SOC1);
+
+ AppTimeLimitController.UsageGroup group = getAppUsageLimitObserver(UID, OBS_ID1);
+
+ long timeRemaining = group.getTimeLimitMs() - group.getUsageTimeMs();
+ assertEquals(TIME_10_MIN * 2, timeRemaining);
+
+ startUsage(PKG_SOC1);
+ setTime(TIME_10_MIN * 2);
+ stopUsage(PKG_SOC1);
+
+ timeRemaining = group.getTimeLimitMs() - group.getUsageTimeMs();
+ assertEquals(TIME_10_MIN, timeRemaining);
+
+ setTime(TIME_30_MIN);
+
+ assertFalse(mLimitReachedLatch.await(100L, TimeUnit.MILLISECONDS));
+
+ // Add a different package in the group
+ startUsage(PKG_GAME1);
+ setTime(TIME_30_MIN + TIME_10_MIN);
+ stopUsage(PKG_GAME1);
+
+ assertEquals(0, group.getTimeLimitMs() - group.getUsageTimeMs());
+ assertTrue(mLimitReachedLatch.await(100L, TimeUnit.MILLISECONDS));
+ }
+
/** Verify that time limit does not get triggered due to a different app */
@Test
public void testAppUsageObserver_TimeoutOtherApp() throws Exception {
@@ -355,6 +452,18 @@ public class AppTimeLimitControllerTests {
}
+ /** Verify that time limit does not get triggered due to a different app */
+ @Test
+ public void testAppUsageLimitObserver_TimeoutOtherApp() throws Exception {
+ setTime(0L);
+ addAppUsageLimitObserver(OBS_ID1, GROUP1, 4_000L);
+ startUsage(PKG_SOC2);
+ assertFalse(mLimitReachedLatch.await(6_000L, TimeUnit.MILLISECONDS));
+ setTime(6_000L);
+ stopUsage(PKG_SOC2);
+ assertFalse(mLimitReachedLatch.await(100L, TimeUnit.MILLISECONDS));
+ }
+
/** Verify the timeout message is delivered at the right time */
@Test
public void testAppUsageObserver_Timeout() throws Exception {
@@ -385,6 +494,19 @@ public class AppTimeLimitControllerTests {
assertTrue(hasUsageSessionObserver(UID, OBS_ID1));
}
+ /** Verify the timeout message is delivered at the right time */
+ @Test
+ public void testAppUsageLimitObserver_Timeout() throws Exception {
+ setTime(0L);
+ addAppUsageLimitObserver(OBS_ID1, GROUP1, 4_000L);
+ startUsage(PKG_SOC1);
+ setTime(6_000L);
+ assertTrue(mLimitReachedLatch.await(6_000L, TimeUnit.MILLISECONDS));
+ stopUsage(PKG_SOC1);
+ // Verify that the observer was not removed
+ assertTrue(hasAppUsageLimitObserver(UID, OBS_ID1));
+ }
+
/** If an app was already running, make sure it is partially counted towards the time limit */
@Test
public void testAppUsageObserver_AlreadyRunning() throws Exception {
@@ -423,6 +545,25 @@ public class AppTimeLimitControllerTests {
assertTrue(hasUsageSessionObserver(UID, OBS_ID2));
}
+ /** If an app was already running, make sure it is partially counted towards the time limit */
+ @Test
+ public void testAppUsageLimitObserver_AlreadyRunning() throws Exception {
+ setTime(TIME_10_MIN);
+ startUsage(PKG_GAME1);
+ setTime(TIME_30_MIN);
+ addAppUsageLimitObserver(OBS_ID2, GROUP_GAME, TIME_30_MIN);
+ setTime(TIME_30_MIN + TIME_10_MIN);
+ stopUsage(PKG_GAME1);
+ assertFalse(mLimitReachedLatch.await(1_000L, TimeUnit.MILLISECONDS));
+
+ startUsage(PKG_GAME2);
+ setTime(TIME_30_MIN + TIME_30_MIN);
+ stopUsage(PKG_GAME2);
+ assertTrue(mLimitReachedLatch.await(1_000L, TimeUnit.MILLISECONDS));
+ // Verify that the observer was not removed
+ assertTrue(hasAppUsageLimitObserver(UID, OBS_ID2));
+ }
+
/** If watched app is already running, verify the timeout callback happens at the right time */
@Test
public void testAppUsageObserver_AlreadyRunningTimeout() throws Exception {
@@ -464,6 +605,24 @@ public class AppTimeLimitControllerTests {
assertTrue(hasUsageSessionObserver(UID, OBS_ID1));
}
+ /** If watched app is already running, verify the timeout callback happens at the right time */
+ @Test
+ public void testAppUsageLimitObserver_AlreadyRunningTimeout() throws Exception {
+ setTime(0);
+ startUsage(PKG_SOC1);
+ setTime(TIME_10_MIN);
+ // 10 second time limit
+ addAppUsageLimitObserver(OBS_ID1, GROUP_SOC, 10_000L);
+ setTime(TIME_10_MIN + 5_000L);
+ // Shouldn't call back in 6 seconds
+ assertFalse(mLimitReachedLatch.await(6_000L, TimeUnit.MILLISECONDS));
+ setTime(TIME_10_MIN + 10_000L);
+ // Should call back by 11 seconds (6 earlier + 5 now)
+ assertTrue(mLimitReachedLatch.await(5_000L, TimeUnit.MILLISECONDS));
+ // Verify that the observer was not removed
+ assertTrue(hasAppUsageLimitObserver(UID, OBS_ID1));
+ }
+
/**
* Verify that App Time Limit Controller will limit the number of observerIds for app usage
* observers
@@ -525,6 +684,37 @@ public class AppTimeLimitControllerTests {
assertTrue("Should have caused an IllegalStateException", receivedException);
}
+ /**
+ * Verify that App Time Limit Controller will limit the number of observerIds for app usage
+ * limit observers
+ */
+ @Test
+ public void testAppUsageLimitObserver_MaxObserverLimit() throws Exception {
+ boolean receivedException = false;
+ int ANOTHER_UID = UID + 1;
+ addAppUsageLimitObserver(OBS_ID1, GROUP1, TIME_30_MIN);
+ addAppUsageLimitObserver(OBS_ID2, GROUP1, TIME_30_MIN);
+ addAppUsageLimitObserver(OBS_ID3, GROUP1, TIME_30_MIN);
+ addAppUsageLimitObserver(OBS_ID4, GROUP1, TIME_30_MIN);
+ addAppUsageLimitObserver(OBS_ID5, GROUP1, TIME_30_MIN);
+ addAppUsageLimitObserver(OBS_ID6, GROUP1, TIME_30_MIN);
+ addAppUsageLimitObserver(OBS_ID7, GROUP1, TIME_30_MIN);
+ addAppUsageLimitObserver(OBS_ID8, GROUP1, TIME_30_MIN);
+ addAppUsageLimitObserver(OBS_ID9, GROUP1, TIME_30_MIN);
+ addAppUsageLimitObserver(OBS_ID10, GROUP1, TIME_30_MIN);
+ // Readding an observer should not cause an IllegalStateException
+ addAppUsageLimitObserver(OBS_ID5, GROUP1, TIME_30_MIN);
+ // Adding an observer for a different uid shouldn't cause an IllegalStateException
+ mController.addAppUsageLimitObserver(
+ ANOTHER_UID, OBS_ID11, GROUP1, TIME_30_MIN, null, USER_ID);
+ try {
+ addAppUsageLimitObserver(OBS_ID11, GROUP1, TIME_30_MIN);
+ } catch (IllegalStateException ise) {
+ receivedException = true;
+ }
+ assertTrue("Should have caused an IllegalStateException", receivedException);
+ }
+
/** Verify that addAppUsageObserver minimum time limit is one minute */
@Test
public void testAppUsageObserver_MinimumTimeLimit() throws Exception {
@@ -553,6 +743,20 @@ public class AppTimeLimitControllerTests {
assertTrue("Should have caused an IllegalArgumentException", receivedException);
}
+ /** Verify that addAppUsageLimitObserver minimum time limit is one minute */
+ @Test
+ public void testAppUsageLimitObserver_MinimumTimeLimit() throws Exception {
+ boolean receivedException = false;
+ // adding an observer with a one minute time limit should not cause an exception
+ addAppUsageLimitObserver(OBS_ID1, GROUP1, MIN_TIME_LIMIT);
+ try {
+ addAppUsageLimitObserver(OBS_ID1, GROUP1, MIN_TIME_LIMIT - 1);
+ } catch (IllegalArgumentException iae) {
+ receivedException = true;
+ }
+ assertTrue("Should have caused an IllegalArgumentException", receivedException);
+ }
+
/** Verify that concurrent usage from multiple apps in the same group will counted correctly */
@Test
public void testAppUsageObserver_ConcurrentUsage() throws Exception {
@@ -599,6 +803,29 @@ public class AppTimeLimitControllerTests {
assertTrue(mLimitReachedLatch.await(100L, TimeUnit.MILLISECONDS));
}
+ /** Verify that concurrent usage from multiple apps in the same group will counted correctly */
+ @Test
+ public void testAppUsageLimitObserver_ConcurrentUsage() throws Exception {
+ setTime(0L);
+ addAppUsageLimitObserver(OBS_ID1, GROUP1, TIME_30_MIN);
+ AppTimeLimitController.UsageGroup group = getAppUsageLimitObserver(UID, OBS_ID1);
+ startUsage(PKG_SOC1);
+ // Add 10 mins
+ setTime(TIME_10_MIN);
+
+ // Add a different package in the group will first package is still in use
+ startUsage(PKG_GAME1);
+ setTime(TIME_10_MIN * 2);
+ // Stop first package usage
+ stopUsage(PKG_SOC1);
+
+ setTime(TIME_30_MIN);
+ stopUsage(PKG_GAME1);
+
+ assertEquals(TIME_30_MIN, group.getUsageTimeMs());
+ assertTrue(mLimitReachedLatch.await(100L, TimeUnit.MILLISECONDS));
+ }
+
/** Verify that a session will continue if usage starts again within the session threshold */
@Test
public void testUsageSessionObserver_ContinueSession() throws Exception {
@@ -737,6 +964,97 @@ public class AppTimeLimitControllerTests {
assertFalse(hasAppUsageObserver(UID, OBS_ID1));
}
+ /** Verify app usage limit observer added correctly reports it being a group limit */
+ @Test
+ public void testAppUsageLimitObserver_IsGroupLimit() {
+ addAppUsageLimitObserver(OBS_ID1, GROUP1, TIME_30_MIN);
+ AppTimeLimitController.AppUsageLimitGroup group = getAppUsageLimitObserver(UID, OBS_ID1);
+ assertNotNull("Observer wasn't added", group);
+ assertTrue("Observer didn't correctly report being a group limit",
+ group.isGroupLimit());
+ }
+
+ /** Verify app usage limit observer added correctly reports it being not a group limit */
+ @Test
+ public void testAppUsageLimitObserver_IsNotGroupLimit() {
+ addAppUsageLimitObserver(OBS_ID1, new String[]{PKG_PROD}, TIME_30_MIN);
+ AppTimeLimitController.AppUsageLimitGroup group = getAppUsageLimitObserver(UID, OBS_ID1);
+ assertNotNull("Observer wasn't added", group);
+ assertFalse("Observer didn't correctly report not being a group limit",
+ group.isGroupLimit());
+ }
+
+ /** Verify app usage limit observer added correctly reports its total usage limit */
+ @Test
+ public void testAppUsageLimitObserver_GetTotalUsageLimit() {
+ addAppUsageLimitObserver(OBS_ID1, GROUP1, TIME_30_MIN);
+ AppTimeLimitController.AppUsageLimitGroup group = getAppUsageLimitObserver(UID, OBS_ID1);
+ assertNotNull("Observer wasn't added", group);
+ assertEquals("Observer didn't correctly report total usage limit",
+ TIME_30_MIN, group.getTotaUsageLimit());
+ }
+
+ /** Verify app usage limit observer added correctly reports its total usage limit */
+ @Test
+ public void testAppUsageLimitObserver_GetUsageRemaining() {
+ setTime(0L);
+ addAppUsageLimitObserver(OBS_ID1, GROUP1, TIME_30_MIN);
+ startUsage(PKG_SOC1);
+ setTime(TIME_10_MIN);
+ stopUsage(PKG_SOC1);
+ AppTimeLimitController.AppUsageLimitGroup group = getAppUsageLimitObserver(UID, OBS_ID1);
+ assertNotNull("Observer wasn't added", group);
+ assertEquals("Observer didn't correctly report total usage limit",
+ TIME_10_MIN * 2, group.getUsageRemaining());
+ }
+
+ /** Verify the app usage limit observer with the smallest usage limit remaining is returned
+ * when querying the getAppUsageLimit API.
+ */
+ @Test
+ public void testAppUsageLimitObserver_GetAppUsageLimit() {
+ addAppUsageLimitObserver(OBS_ID1, GROUP1, TIME_30_MIN);
+ addAppUsageLimitObserver(OBS_ID2, GROUP_SOC, TIME_10_MIN);
+ UsageStatsManagerInternal.AppUsageLimitData group = getAppUsageLimit(PKG_SOC1);
+ assertEquals("Observer with the smallest usage limit remaining wasn't returned",
+ TIME_10_MIN, group.getTotalUsageLimit());
+ }
+
+ /** Verify the app usage limit observer with the smallest usage limit remaining is returned
+ * when querying the getAppUsageLimit API.
+ */
+ @Test
+ public void testAppUsageLimitObserver_GetAppUsageLimitUsed() {
+ setTime(0L);
+ addAppUsageLimitObserver(OBS_ID1, GROUP1, TIME_30_MIN);
+ addAppUsageLimitObserver(OBS_ID2, GROUP_SOC, TIME_10_MIN);
+ startUsage(PKG_GAME1);
+ setTime(TIME_10_MIN * 2 + TIME_1_MIN);
+ stopUsage(PKG_GAME1);
+ // PKG_GAME1 is only in GROUP1 but since we're querying for PCK_SOC1 which is
+ // in both groups, GROUP1 should be returned since it has a smaller time remaining
+ UsageStatsManagerInternal.AppUsageLimitData group = getAppUsageLimit(PKG_SOC1);
+ assertEquals("Observer with the smallest usage limit remaining wasn't returned",
+ TIME_1_MIN * 9, group.getUsageRemaining());
+ }
+
+ /** Verify the app usage limit observer with the smallest usage limit remaining is returned
+ * when querying the getAppUsageLimit API.
+ */
+ @Test
+ public void testAppUsageLimitObserver_GetAppUsageLimitAllUsed() {
+ setTime(0L);
+ addAppUsageLimitObserver(OBS_ID1, GROUP1, TIME_30_MIN);
+ addAppUsageLimitObserver(OBS_ID2, GROUP_SOC, TIME_10_MIN);
+ startUsage(PKG_SOC1);
+ setTime(TIME_10_MIN);
+ stopUsage(PKG_SOC1);
+ // GROUP_SOC should be returned since it should be completely used up (0ms remaining)
+ UsageStatsManagerInternal.AppUsageLimitData group = getAppUsageLimit(PKG_SOC1);
+ assertEquals("Observer with the smallest usage limit remaining wasn't returned",
+ 0L, group.getUsageRemaining());
+ }
+
private void startUsage(String packageName) {
mController.noteUsageStart(packageName, USER_ID);
}
@@ -759,6 +1077,10 @@ public class AppTimeLimitControllerTests {
null, null, USER_ID);
}
+ private void addAppUsageLimitObserver(int observerId, String[] packages, long timeLimit) {
+ mController.addAppUsageLimitObserver(UID, observerId, packages, timeLimit, null, USER_ID);
+ }
+
/** Is there still an app usage observer by that id */
private boolean hasAppUsageObserver(int uid, int observerId) {
return mController.getAppUsageGroup(uid, observerId) != null;
@@ -769,6 +1091,20 @@ public class AppTimeLimitControllerTests {
return mController.getSessionUsageGroup(uid, observerId) != null;
}
+ /** Is there still an app usage limit observer by that id */
+ private boolean hasAppUsageLimitObserver(int uid, int observerId) {
+ return mController.getAppUsageLimitGroup(uid, observerId) != null;
+ }
+
+ private AppTimeLimitController.AppUsageLimitGroup getAppUsageLimitObserver(
+ int uid, int observerId) {
+ return mController.getAppUsageLimitGroup(uid, observerId);
+ }
+
+ private UsageStatsManagerInternal.AppUsageLimitData getAppUsageLimit(String packageName) {
+ return mController.getAppUsageLimit(packageName, UserHandle.of(USER_ID));
+ }
+
private void setTime(long time) {
mUptimeMillis = time;
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index c0f9b80b2e08..62229235a026 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -245,8 +245,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
@Override
- void logSmartSuggestionsVisible(NotificationRecord r) {
- super.logSmartSuggestionsVisible(r);
+ void logSmartSuggestionsVisible(NotificationRecord r, int notificationLocation) {
+ super.logSmartSuggestionsVisible(r, notificationLocation);
countLogSmartSuggestionsVisible++;
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index a381023590c3..056568a0de64 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -549,8 +549,7 @@ public class ActivityStarterTests extends ActivityTestsBase {
verify(mActivityMetricsLogger, times(1)).logActivityStart(any(), any(), any(),
eq(FAKE_CALLING_UID), eq(FAKE_CALLING_PACKAGE), anyInt(), anyBoolean(),
eq(FAKE_REAL_CALLING_UID), anyInt(), anyBoolean(), anyInt(),
- eq(ActivityBuilder.getDefaultComponent().getPackageName()), anyInt(), anyBoolean(),
- any(), eq(false));
+ any(), anyInt(), anyBoolean(), any(), eq(false));
}
/**
@@ -599,6 +598,10 @@ public class ActivityStarterTests extends ActivityTestsBase {
Process.SYSTEM_UID, false, PROCESS_STATE_TOP + 1,
UNIMPORTANT_UID2, false, PROCESS_STATE_TOP + 1,
false, false, false);
+ runAndVerifyBackgroundActivityStartsSubtest("disallowed_nfcUid_notAborted", false,
+ Process.NFC_UID, false, PROCESS_STATE_TOP + 1,
+ UNIMPORTANT_UID2, false, PROCESS_STATE_TOP + 1,
+ false, false, false);
runAndVerifyBackgroundActivityStartsSubtest(
"disallowed_callingUidHasVisibleWindow_notAborted", false,
UNIMPORTANT_UID, true, PROCESS_STATE_TOP + 1,
diff --git a/services/tests/wmtests/src/com/android/server/wm/BoundsAnimationControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/BoundsAnimationControllerTests.java
index 1c5391ed3a6c..9ce579512eda 100644
--- a/services/tests/wmtests/src/com/android/server/wm/BoundsAnimationControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/BoundsAnimationControllerTests.java
@@ -106,7 +106,7 @@ public class BoundsAnimationControllerTests extends WindowTestsBase {
}
public void notifyTransitionStarting(int transit) {
- mListener.onAppTransitionStartingLocked(transit, null, null, 0, 0, 0);
+ mListener.onAppTransitionStartingLocked(transit, 0, 0, 0);
}
public void notifyTransitionFinished() {
diff --git a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
index 413b6f4b3905..9478be90b5c3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
@@ -25,6 +25,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.verifyNoMor
import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
import static org.mockito.ArgumentMatchers.eq;
import android.graphics.Point;
@@ -43,6 +44,7 @@ import androidx.test.filters.SmallTest;
import com.android.server.testutils.OffsettableClock;
import com.android.server.testutils.TestHandler;
+import com.android.server.wm.RemoteAnimationController.RemoteAnimationRecord;
import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
import org.junit.Before;
@@ -60,8 +62,10 @@ import org.mockito.MockitoAnnotations;
public class RemoteAnimationControllerTest extends WindowTestsBase {
@Mock SurfaceControl mMockLeash;
+ @Mock SurfaceControl mMockThumbnailLeash;
@Mock Transaction mMockTransaction;
@Mock OnAnimationFinishedCallback mFinishedCallback;
+ @Mock OnAnimationFinishedCallback mThumbnailFinishedCallback;
@Mock IRemoteAnimationRunner mMockRunner;
private RemoteAnimationAdapter mAdapter;
private RemoteAnimationController mController;
@@ -84,8 +88,8 @@ public class RemoteAnimationControllerTest extends WindowTestsBase {
final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
mDisplayContent.mOpeningApps.add(win.mAppToken);
try {
- final AnimationAdapter adapter = mController.createAnimationAdapter(win.mAppToken,
- new Point(50, 100), new Rect(50, 100, 150, 150));
+ final AnimationAdapter adapter = mController.createRemoteAnimationRecord(win.mAppToken,
+ new Point(50, 100), new Rect(50, 100, 150, 150), null).mAdapter;
adapter.startAnimation(mMockLeash, mMockTransaction, mFinishedCallback);
mController.goodToGo();
mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
@@ -117,8 +121,8 @@ public class RemoteAnimationControllerTest extends WindowTestsBase {
@Test
public void testCancel() throws Exception {
final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
- final AnimationAdapter adapter = mController.createAnimationAdapter(win.mAppToken,
- new Point(50, 100), new Rect(50, 100, 150, 150));
+ final AnimationAdapter adapter = mController.createRemoteAnimationRecord(win.mAppToken,
+ new Point(50, 100), new Rect(50, 100, 150, 150), null).mAdapter;
adapter.startAnimation(mMockLeash, mMockTransaction, mFinishedCallback);
mController.goodToGo();
@@ -129,8 +133,8 @@ public class RemoteAnimationControllerTest extends WindowTestsBase {
@Test
public void testTimeout() throws Exception {
final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
- final AnimationAdapter adapter = mController.createAnimationAdapter(win.mAppToken,
- new Point(50, 100), new Rect(50, 100, 150, 150));
+ final AnimationAdapter adapter = mController.createRemoteAnimationRecord(win.mAppToken,
+ new Point(50, 100), new Rect(50, 100, 150, 150), null).mAdapter;
adapter.startAnimation(mMockLeash, mMockTransaction, mFinishedCallback);
mController.goodToGo();
@@ -147,8 +151,8 @@ public class RemoteAnimationControllerTest extends WindowTestsBase {
mWm.setAnimationScale(2, 5.0f);
final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION,
"testWin");
- final AnimationAdapter adapter = mController.createAnimationAdapter(win.mAppToken,
- new Point(50, 100), new Rect(50, 100, 150, 150));
+ final AnimationAdapter adapter = mController.createRemoteAnimationRecord(
+ win.mAppToken, new Point(50, 100), new Rect(50, 100, 150, 150), null).mAdapter;
adapter.startAnimation(mMockLeash, mMockTransaction, mFinishedCallback);
mController.goodToGo();
@@ -176,8 +180,8 @@ public class RemoteAnimationControllerTest extends WindowTestsBase {
@Test
public void testNotReallyStarted() {
final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
- mController.createAnimationAdapter(win.mAppToken,
- new Point(50, 100), new Rect(50, 100, 150, 150));
+ mController.createRemoteAnimationRecord(win.mAppToken,
+ new Point(50, 100), new Rect(50, 100, 150, 150), null);
mController.goodToGo();
verifyNoMoreInteractionsExceptAsBinder(mMockRunner);
}
@@ -186,10 +190,10 @@ public class RemoteAnimationControllerTest extends WindowTestsBase {
public void testOneNotStarted() throws Exception {
final WindowState win1 = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin1");
final WindowState win2 = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin2");
- mController.createAnimationAdapter(win1.mAppToken,
- new Point(50, 100), new Rect(50, 100, 150, 150));
- final AnimationAdapter adapter = mController.createAnimationAdapter(win2.mAppToken,
- new Point(50, 100), new Rect(50, 100, 150, 150));
+ mController.createRemoteAnimationRecord(win1.mAppToken,
+ new Point(50, 100), new Rect(50, 100, 150, 150), null);
+ final AnimationAdapter adapter = mController.createRemoteAnimationRecord(win2.mAppToken,
+ new Point(50, 100), new Rect(50, 100, 150, 150), null).mAdapter;
adapter.startAnimation(mMockLeash, mMockTransaction, mFinishedCallback);
mController.goodToGo();
mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
@@ -205,8 +209,8 @@ public class RemoteAnimationControllerTest extends WindowTestsBase {
@Test
public void testRemovedBeforeStarted() {
final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
- final AnimationAdapter adapter = mController.createAnimationAdapter(win.mAppToken,
- new Point(50, 100), new Rect(50, 100, 150, 150));
+ final AnimationAdapter adapter = mController.createRemoteAnimationRecord(win.mAppToken,
+ new Point(50, 100), new Rect(50, 100, 150, 150), null).mAdapter;
adapter.startAnimation(mMockLeash, mMockTransaction, mFinishedCallback);
win.mAppToken.removeImmediately();
mController.goodToGo();
@@ -214,6 +218,49 @@ public class RemoteAnimationControllerTest extends WindowTestsBase {
verify(mFinishedCallback).onAnimationFinished(eq(adapter));
}
+ @Test
+ public void testChange() throws Exception {
+ final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
+ mDisplayContent.mChangingApps.add(win.mAppToken);
+ try {
+ final RemoteAnimationRecord record = mController.createRemoteAnimationRecord(
+ win.mAppToken, new Point(50, 100), new Rect(50, 100, 150, 150),
+ new Rect(0, 0, 200, 200));
+ assertNotNull(record.mThumbnailAdapter);
+ ((AnimationAdapter) record.mAdapter)
+ .startAnimation(mMockLeash, mMockTransaction, mFinishedCallback);
+ ((AnimationAdapter) record.mThumbnailAdapter).startAnimation(mMockThumbnailLeash,
+ mMockTransaction, mThumbnailFinishedCallback);
+ mController.goodToGo();
+ mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+ final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
+ ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
+ final ArgumentCaptor<IRemoteAnimationFinishedCallback> finishedCaptor =
+ ArgumentCaptor.forClass(IRemoteAnimationFinishedCallback.class);
+ verify(mMockRunner).onAnimationStart(appsCaptor.capture(), finishedCaptor.capture());
+ assertEquals(1, appsCaptor.getValue().length);
+ final RemoteAnimationTarget app = appsCaptor.getValue()[0];
+ assertEquals(RemoteAnimationTarget.MODE_CHANGING, app.mode);
+ assertEquals(new Point(50, 100), app.position);
+ assertEquals(new Rect(50, 100, 150, 150), app.sourceContainerBounds);
+ assertEquals(new Rect(0, 0, 200, 200), app.startBounds);
+ assertEquals(mMockLeash, app.leash);
+ assertEquals(mMockThumbnailLeash, app.startLeash);
+ assertEquals(win.mWinAnimator.mLastClipRect, app.clipRect);
+ assertEquals(false, app.isTranslucent);
+ verify(mMockTransaction).setLayer(mMockLeash, app.prefixOrderIndex);
+ verify(mMockTransaction).setPosition(mMockLeash, app.position.x, app.position.y);
+ verify(mMockTransaction).setWindowCrop(mMockLeash, new Rect(0, 0, 200, 200));
+ verify(mMockTransaction).setPosition(mMockThumbnailLeash, 0, 0);
+
+ finishedCaptor.getValue().onAnimationFinished();
+ verify(mFinishedCallback).onAnimationFinished(eq(record.mAdapter));
+ verify(mThumbnailFinishedCallback).onAnimationFinished(eq(record.mThumbnailAdapter));
+ } finally {
+ mDisplayContent.mChangingApps.clear();
+ }
+ }
+
private static void verifyNoMoreInteractionsExceptAsBinder(IInterface binder) {
verify(binder, atLeast(0)).asBinder();
verifyNoMoreInteractions(binder);
diff --git a/services/usage/java/com/android/server/usage/AppTimeLimitController.java b/services/usage/java/com/android/server/usage/AppTimeLimitController.java
index 2ed11fe92e15..fa472e2575f0 100644
--- a/services/usage/java/com/android/server/usage/AppTimeLimitController.java
+++ b/services/usage/java/com/android/server/usage/AppTimeLimitController.java
@@ -18,11 +18,14 @@ package com.android.server.usage;
import android.annotation.UserIdInt;
import android.app.PendingIntent;
+import android.app.usage.UsageStatsManagerInternal;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.SystemClock;
+import android.os.UserHandle;
import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.Slog;
import android.util.SparseArray;
@@ -163,6 +166,9 @@ public class AppTimeLimitController {
/** Map of observerId to details of the time limit group */
SparseArray<SessionUsageGroup> sessionUsageGroups = new SparseArray<>();
+ /** Map of observerId to details of the app usage limit group */
+ SparseArray<AppUsageLimitGroup> appUsageLimitGroups = new SparseArray<>();
+
private ObserverAppData(int uid) {
this.uid = uid;
}
@@ -177,6 +183,10 @@ public class AppTimeLimitController {
sessionUsageGroups.remove(observerId);
}
+ @GuardedBy("mLock")
+ void removeAppUsageLimitGroup(int observerId) {
+ appUsageLimitGroups.remove(observerId);
+ }
@GuardedBy("mLock")
void dump(PrintWriter pw) {
@@ -194,6 +204,12 @@ public class AppTimeLimitController {
sessionUsageGroups.valueAt(i).dump(pw);
pw.println();
}
+ pw.println(" App Usage Limit Groups:");
+ final int nAppUsageLimitGroups = appUsageLimitGroups.size();
+ for (int i = 0; i < nAppUsageLimitGroups; i++) {
+ appUsageLimitGroups.valueAt(i).dump(pw);
+ pw.println();
+ }
}
}
@@ -493,6 +509,54 @@ public class AppTimeLimitController {
}
}
+ class AppUsageLimitGroup extends UsageGroup {
+ private boolean mGroupLimit;
+
+ public AppUsageLimitGroup(UserData user, ObserverAppData observerApp, int observerId,
+ String[] observed, long timeLimitMs, PendingIntent limitReachedCallback) {
+ super(user, observerApp, observerId, observed, timeLimitMs, limitReachedCallback);
+ mGroupLimit = observed.length > 1;
+ }
+
+ @Override
+ @GuardedBy("mLock")
+ public void remove() {
+ super.remove();
+ ObserverAppData observerApp = mObserverAppRef.get();
+ if (observerApp != null) {
+ observerApp.removeAppUsageLimitGroup(mObserverId);
+ }
+ }
+
+ @GuardedBy("mLock")
+ boolean isGroupLimit() {
+ return mGroupLimit;
+ }
+
+ @GuardedBy("mLock")
+ long getTotaUsageLimit() {
+ return mTimeLimitMs;
+ }
+
+ @GuardedBy("mLock")
+ long getUsageRemaining() {
+ // If there is currently an active session, account for its usage
+ if (mActives > 0) {
+ return mTimeLimitMs - mUsageTimeMs - (getUptimeMillis() - mLastKnownUsageTimeMs);
+ } else {
+ return mTimeLimitMs - mUsageTimeMs;
+ }
+ }
+
+ @Override
+ @GuardedBy("mLock")
+ void dump(PrintWriter pw) {
+ super.dump(pw);
+ pw.print(" groupLimit=");
+ pw.print(mGroupLimit);
+ }
+ }
+
private class MyHandler extends Handler {
static final int MSG_CHECK_TIMEOUT = 1;
@@ -553,6 +617,12 @@ public class AppTimeLimitController {
/** Overrideable for testing purposes */
@VisibleForTesting
+ protected long getAppUsageLimitObserverPerUidLimit() {
+ return MAX_OBSERVER_PER_UID;
+ }
+
+ /** Overrideable for testing purposes */
+ @VisibleForTesting
protected long getMinTimeLimit() {
return ONE_MINUTE;
}
@@ -572,6 +642,61 @@ public class AppTimeLimitController {
}
}
+ @VisibleForTesting
+ AppUsageLimitGroup getAppUsageLimitGroup(int observerAppUid, int observerId) {
+ synchronized (mLock) {
+ return getOrCreateObserverAppDataLocked(observerAppUid).appUsageLimitGroups.get(
+ observerId);
+ }
+ }
+
+ /**
+ * Returns an object describing the app usage limit for the given package which was set via
+ * {@link #addAppUsageLimitObserver).
+ * If there are multiple limits that apply to the package, the one with the smallest
+ * time remaining will be returned.
+ */
+ public UsageStatsManagerInternal.AppUsageLimitData getAppUsageLimit(
+ String packageName, UserHandle user) {
+ synchronized (mLock) {
+ final UserData userData = getOrCreateUserDataLocked(user.getIdentifier());
+ if (userData == null) {
+ return null;
+ }
+
+ final ArrayList<UsageGroup> usageGroups = userData.observedMap.get(packageName);
+ if (usageGroups == null || usageGroups.isEmpty()) {
+ return null;
+ }
+
+ final ArraySet<AppUsageLimitGroup> usageLimitGroups = new ArraySet<>();
+ for (int i = 0; i < usageGroups.size(); i++) {
+ if (usageGroups.get(i) instanceof AppUsageLimitGroup) {
+ final AppUsageLimitGroup group = (AppUsageLimitGroup) usageGroups.get(i);
+ for (int j = 0; j < group.mObserved.length; j++) {
+ if (group.mObserved[j].equals(packageName)) {
+ usageLimitGroups.add(group);
+ break;
+ }
+ }
+ }
+ }
+ if (usageLimitGroups.isEmpty()) {
+ return null;
+ }
+
+ AppUsageLimitGroup smallestGroup = usageLimitGroups.valueAt(0);
+ for (int i = 1; i < usageLimitGroups.size(); i++) {
+ final AppUsageLimitGroup otherGroup = usageLimitGroups.valueAt(i);
+ if (otherGroup.getUsageRemaining() < smallestGroup.getUsageRemaining()) {
+ smallestGroup = otherGroup;
+ }
+ }
+ return new UsageStatsManagerInternal.AppUsageLimitData(smallestGroup.isGroupLimit(),
+ smallestGroup.getTotaUsageLimit(), smallestGroup.getUsageRemaining());
+ }
+ }
+
/** Returns an existing UserData object for the given userId, or creates one */
@GuardedBy("mLock")
private UserData getOrCreateUserDataLocked(int userId) {
@@ -726,6 +851,61 @@ public class AppTimeLimitController {
}
/**
+ * Registers an app usage limit observer with the given details.
+ * Existing app usage limit observer with the same observerId will be removed.
+ */
+ public void addAppUsageLimitObserver(int requestingUid, int observerId, String[] observed,
+ long timeLimit, PendingIntent callbackIntent, @UserIdInt int userId) {
+ if (timeLimit < getMinTimeLimit()) {
+ throw new IllegalArgumentException("Time limit must be >= " + getMinTimeLimit());
+ }
+ synchronized (mLock) {
+ UserData user = getOrCreateUserDataLocked(userId);
+ ObserverAppData observerApp = getOrCreateObserverAppDataLocked(requestingUid);
+ AppUsageLimitGroup group = observerApp.appUsageLimitGroups.get(observerId);
+ if (group != null) {
+ // Remove previous app usage group associated with observerId
+ group.remove();
+ }
+
+ final int observerIdCount = observerApp.appUsageLimitGroups.size();
+ if (observerIdCount >= getAppUsageLimitObserverPerUidLimit()) {
+ throw new IllegalStateException(
+ "Too many app usage observers added by uid " + requestingUid);
+ }
+ group = new AppUsageLimitGroup(user, observerApp, observerId, observed, timeLimit,
+ callbackIntent);
+ observerApp.appUsageLimitGroups.append(observerId, group);
+
+ if (DEBUG) {
+ Slog.d(TAG, "addObserver " + observed + " for " + timeLimit);
+ }
+
+ user.addUsageGroup(group);
+ noteActiveLocked(user, group, getUptimeMillis());
+ }
+ }
+
+ /**
+ * Remove a registered observer by observerId and calling uid.
+ *
+ * @param requestingUid The calling uid
+ * @param observerId The unique observer id for this user
+ * @param userId The user id of the observer
+ */
+ public void removeAppUsageLimitObserver(int requestingUid, int observerId,
+ @UserIdInt int userId) {
+ synchronized (mLock) {
+ final ObserverAppData observerApp = getOrCreateObserverAppDataLocked(requestingUid);
+ final AppUsageLimitGroup group = observerApp.appUsageLimitGroups.get(observerId);
+ if (group != null) {
+ // Remove previous app usage group associated with observerId
+ group.remove();
+ }
+ }
+ }
+
+ /**
* Called when an entity becomes active.
*
* @param name The entity that became active
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 6ad698b39763..85939d498755 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -855,6 +855,22 @@ public class UsageStatsService extends SystemService implements
== PackageManager.PERMISSION_GRANTED;
}
+ private boolean hasPermissions(String callingPackage, String... permissions) {
+ final int callingUid = Binder.getCallingUid();
+ if (callingUid == Process.SYSTEM_UID) {
+ // Caller is the system, so proceed.
+ return true;
+ }
+
+ boolean hasPermissions = true;
+ final Context context = getContext();
+ for (int i = 0; i < permissions.length; i++) {
+ hasPermissions = hasPermissions && (context.checkCallingPermission(permissions[i])
+ == PackageManager.PERMISSION_GRANTED);
+ }
+ return hasPermissions;
+ }
+
private void checkCallerIsSystemOrSameApp(String pkg) {
if (isCallingUidSystem()) {
return;
@@ -1346,6 +1362,51 @@ public class UsageStatsService extends SystemService implements
}
@Override
+ public void registerAppUsageLimitObserver(int observerId, String[] packages,
+ long timeLimitMs, PendingIntent callbackIntent, String callingPackage) {
+ if (!hasPermissions(callingPackage,
+ Manifest.permission.SUSPEND_APPS, Manifest.permission.OBSERVE_APP_USAGE)) {
+ throw new SecurityException("Caller doesn't have both SUSPEND_APPS and "
+ + "OBSERVE_APP_USAGE permissions");
+ }
+
+ if (packages == null || packages.length == 0) {
+ throw new IllegalArgumentException("Must specify at least one package");
+ }
+ if (callbackIntent == null) {
+ throw new NullPointerException("callbackIntent can't be null");
+ }
+ final int callingUid = Binder.getCallingUid();
+ final int userId = UserHandle.getUserId(callingUid);
+ final long token = Binder.clearCallingIdentity();
+ try {
+ UsageStatsService.this.registerAppUsageLimitObserver(callingUid, observerId,
+ packages, timeLimitMs, callbackIntent, userId);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public void unregisterAppUsageLimitObserver(int observerId, String callingPackage) {
+ if (!hasPermissions(callingPackage,
+ Manifest.permission.SUSPEND_APPS, Manifest.permission.OBSERVE_APP_USAGE)) {
+ throw new SecurityException("Caller doesn't have both SUSPEND_APPS and "
+ + "OBSERVE_APP_USAGE permissions");
+ }
+
+ final int callingUid = Binder.getCallingUid();
+ final int userId = UserHandle.getUserId(callingUid);
+ final long token = Binder.clearCallingIdentity();
+ try {
+ UsageStatsService.this.unregisterAppUsageLimitObserver(
+ callingUid, observerId, userId);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
public void reportUsageStart(IBinder activity, String token, String callingPackage) {
reportPastUsageStart(activity, token, 0, callingPackage);
}
@@ -1447,6 +1508,16 @@ public class UsageStatsService extends SystemService implements
mAppTimeLimit.removeUsageSessionObserver(callingUid, sessionObserverId, userId);
}
+ void registerAppUsageLimitObserver(int callingUid, int observerId, String[] packages,
+ long timeLimitMs, PendingIntent callbackIntent, int userId) {
+ mAppTimeLimit.addAppUsageLimitObserver(callingUid, observerId, packages, timeLimitMs,
+ callbackIntent, userId);
+ }
+
+ void unregisterAppUsageLimitObserver(int callingUid, int observerId, int userId) {
+ mAppTimeLimit.removeAppUsageLimitObserver(callingUid, observerId, userId);
+ }
+
/**
* This local service implementation is primarily used by ActivityManagerService.
* ActivityManagerService will call these methods holding the 'am' lock, which means we
@@ -1652,5 +1723,10 @@ public class UsageStatsService extends SystemService implements
public void reportExemptedSyncStart(String packageName, int userId) {
mAppStandby.postReportExemptedSyncStart(packageName, userId);
}
+
+ @Override
+ public AppUsageLimitData getAppUsageLimit(String packageName, UserHandle user) {
+ return mAppTimeLimit.getAppUsageLimit(packageName, user);
+ }
}
}
diff --git a/services/usb/Android.bp b/services/usb/Android.bp
index feb7b76ae119..20855b70cabc 100644
--- a/services/usb/Android.bp
+++ b/services/usb/Android.bp
@@ -10,6 +10,7 @@ java_library_static {
static_libs: [
"android.hardware.usb-V1.0-java",
"android.hardware.usb-V1.1-java",
+ "android.hardware.usb-V1.2-java",
"android.hardware.usb.gadget-V1.0-java",
],
}
diff --git a/services/usb/java/com/android/server/usb/UsbPortManager.java b/services/usb/java/com/android/server/usb/UsbPortManager.java
index 6f210e37d6d1..50e4faab76e3 100644
--- a/services/usb/java/com/android/server/usb/UsbPortManager.java
+++ b/services/usb/java/com/android/server/usb/UsbPortManager.java
@@ -16,6 +16,8 @@
package com.android.server.usb;
+import static android.hardware.usb.UsbPortStatus.CONTAMINANT_DETECTION_NOT_SUPPORTED;
+import static android.hardware.usb.UsbPortStatus.CONTAMINANT_PROTECTION_NONE;
import static android.hardware.usb.UsbPortStatus.DATA_ROLE_DEVICE;
import static android.hardware.usb.UsbPortStatus.DATA_ROLE_HOST;
import static android.hardware.usb.UsbPortStatus.MODE_DFP;
@@ -29,19 +31,23 @@ import static com.android.internal.usb.DumpUtils.writePortStatus;
import android.Manifest;
import android.annotation.NonNull;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
+import android.content.res.Resources;
import android.hardware.usb.ParcelableUsbPort;
import android.hardware.usb.UsbManager;
import android.hardware.usb.UsbPort;
import android.hardware.usb.UsbPortStatus;
-import android.hardware.usb.V1_0.IUsb;
import android.hardware.usb.V1_0.PortRole;
import android.hardware.usb.V1_0.PortRoleType;
-import android.hardware.usb.V1_0.PortStatus;
import android.hardware.usb.V1_0.Status;
-import android.hardware.usb.V1_1.IUsbCallback;
import android.hardware.usb.V1_1.PortStatus_1_1;
+import android.hardware.usb.V1_2.IUsb;
+import android.hardware.usb.V1_2.IUsbCallback;
+import android.hardware.usb.V1_2.PortStatus;
import android.hidl.manager.V1_0.IServiceManager;
import android.hidl.manager.V1_0.IServiceNotification;
import android.os.Bundle;
@@ -55,18 +61,20 @@ import android.os.SystemClock;
import android.os.UserHandle;
import android.service.usb.UsbPortInfoProto;
import android.service.usb.UsbPortManagerProto;
+import android.service.usb.UsbServiceProto;
import android.util.ArrayMap;
import android.util.Log;
import android.util.Slog;
import android.util.StatsLog;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
+import com.android.internal.notification.SystemNotificationChannels;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.dump.DualDumpOutputStream;
import com.android.server.FgThread;
import java.util.ArrayList;
-import java.util.HashMap;
import java.util.NoSuchElementException;
/**
@@ -85,6 +93,7 @@ public class UsbPortManager {
private static final String TAG = "UsbPortManager";
private static final int MSG_UPDATE_PORTS = 1;
+ private static final int MSG_SYSTEM_READY = 2;
// All non-trivial role combinations.
private static final int COMBO_SOURCE_HOST =
@@ -132,7 +141,19 @@ public class UsbPortManager {
// Maintains the current connected status of the port.
// Uploads logs only when the connection status is changes.
- private final HashMap<String, Boolean> mConnected = new HashMap<>();
+ private final ArrayMap<String, Boolean> mConnected = new ArrayMap<>();
+
+ // Maintains the USB contaminant status that was previously logged.
+ // Logs get uploaded only when contaminant presence status changes.
+ private final ArrayMap<String, Integer> mContaminantStatus = new ArrayMap<>();
+
+ private NotificationManager mNotificationManager;
+
+ /**
+ * If there currently is a notification about contaminated USB port shown the id of the
+ * notification, or 0 if there is none.
+ */
+ private int mIsPortContaminatedNotificationId;
public UsbPortManager(Context context) {
mContext = context;
@@ -164,6 +185,90 @@ public class UsbPortManager {
"ServiceStart: Failed to query port status", e);
}
}
+ mHandler.sendEmptyMessage(MSG_SYSTEM_READY);
+ }
+
+ private void updateContaminantNotification() {
+ PortInfo currentPortInfo = null;
+ Resources r = mContext.getResources();
+
+ // Not handling multiple ports here. Showing the notification
+ // for the first port that returns CONTAMINANT_PRESENCE_DETECTED.
+ for (PortInfo portInfo : mPorts.values()) {
+ if (portInfo.mUsbPortStatus.getContaminantDetectionStatus()
+ == UsbPortStatus.CONTAMINANT_DETECTION_DETECTED) {
+ currentPortInfo = portInfo;
+ break;
+ }
+ }
+
+ if (currentPortInfo != null && mIsPortContaminatedNotificationId
+ != SystemMessage.NOTE_USB_CONTAMINANT_DETECTED) {
+ if (mIsPortContaminatedNotificationId
+ == SystemMessage.NOTE_USB_CONTAMINANT_NOT_DETECTED) {
+ mNotificationManager.cancelAsUser(null, mIsPortContaminatedNotificationId,
+ UserHandle.ALL);
+ }
+
+ mIsPortContaminatedNotificationId = SystemMessage.NOTE_USB_CONTAMINANT_DETECTED;
+ int titleRes = com.android.internal.R.string.usb_contaminant_detected_title;
+ CharSequence title = r.getText(titleRes);
+ String channel = SystemNotificationChannels.ALERTS;
+ CharSequence message = r.getText(
+ com.android.internal.R.string.usb_contaminant_detected_message);
+
+ Intent intent = new Intent();
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ intent.setClassName("com.android.systemui",
+ "com.android.systemui.usb.UsbContaminantActivity");
+ intent.putExtra(UsbManager.EXTRA_PORT, ParcelableUsbPort.of(currentPortInfo.mUsbPort));
+
+ PendingIntent pi = PendingIntent.getActivityAsUser(mContext, 0,
+ intent, 0, null, UserHandle.CURRENT);
+
+ Notification.Builder builder = new Notification.Builder(mContext, channel)
+ .setOngoing(true)
+ .setTicker(title)
+ .setColor(mContext.getColor(
+ com.android.internal.R.color
+ .system_notification_accent_color))
+ .setContentIntent(pi)
+ .setContentTitle(title)
+ .setContentText(message)
+ .setVisibility(Notification.VISIBILITY_PUBLIC)
+ .setSmallIcon(android.R.drawable.stat_sys_warning)
+ .setStyle(new Notification.BigTextStyle()
+ .bigText(message));
+ Notification notification = builder.build();
+ mNotificationManager.notifyAsUser(null, mIsPortContaminatedNotificationId, notification,
+ UserHandle.ALL);
+ } else if (currentPortInfo == null && mIsPortContaminatedNotificationId
+ == SystemMessage.NOTE_USB_CONTAMINANT_DETECTED) {
+ mNotificationManager.cancelAsUser(null, mIsPortContaminatedNotificationId,
+ UserHandle.ALL);
+
+ mIsPortContaminatedNotificationId = SystemMessage.NOTE_USB_CONTAMINANT_NOT_DETECTED;
+ int titleRes = com.android.internal.R.string.usb_contaminant_not_detected_title;
+ CharSequence title = r.getText(titleRes);
+ String channel = SystemNotificationChannels.ALERTS;
+ CharSequence message = r.getText(
+ com.android.internal.R.string.usb_contaminant_not_detected_message);
+
+ Notification.Builder builder = new Notification.Builder(mContext, channel)
+ .setSmallIcon(com.android.internal.R.drawable.ic_usb_48dp)
+ .setTicker(title)
+ .setColor(mContext.getColor(
+ com.android.internal.R.color
+ .system_notification_accent_color))
+ .setContentTitle(title)
+ .setContentText(message)
+ .setVisibility(Notification.VISIBILITY_PUBLIC)
+ .setStyle(new Notification.BigTextStyle()
+ .bigText(message));
+ Notification notification = builder.build();
+ mNotificationManager.notifyAsUser(null, mIsPortContaminatedNotificationId, notification,
+ UserHandle.ALL);
+ }
}
public UsbPort[] getPorts() {
@@ -184,6 +289,43 @@ public class UsbPortManager {
}
}
+ /**
+ * Enables/disables contaminant detection.
+ *
+ * @param portId port identifier.
+ * @param enable enable contaminant detection when set to true.
+ */
+ public void enableContaminantDetection(@NonNull String portId, boolean enable,
+ @NonNull IndentingPrintWriter pw) {
+ final PortInfo portInfo = mPorts.get(portId);
+ if (portInfo == null) {
+ if (pw != null) {
+ pw.println("No such USB port: " + portId);
+ }
+ return;
+ }
+
+ if (!portInfo.mUsbPort.supportsEnableContaminantPresenceDetection()) {
+ return;
+ }
+
+ if ((enable && portInfo.mUsbPortStatus.getContaminantDetectionStatus()
+ != UsbPortStatus.CONTAMINANT_DETECTION_DISABLED) || (!enable
+ && portInfo.mUsbPortStatus.getContaminantDetectionStatus()
+ == UsbPortStatus.CONTAMINANT_DETECTION_DISABLED)
+ || (portInfo.mUsbPortStatus.getContaminantDetectionStatus()
+ == UsbPortStatus.CONTAMINANT_DETECTION_NOT_SUPPORTED)) {
+ return;
+ }
+
+ try {
+ // Oneway call into the hal
+ mProxy.enableContaminantPresenceDetection(portId, enable);
+ } catch (RemoteException e) {
+ logAndPrintException(null, "Failed to set contaminant detection", e);
+ }
+ }
+
public void setPortRoles(String portId, int newPowerRole, int newDataRole,
IndentingPrintWriter pw) {
synchronized (mLock) {
@@ -371,6 +513,27 @@ public class UsbPortManager {
}
}
+ /**
+ * Sets contaminant status for simulated USB port objects.
+ */
+ public void simulateContaminantStatus(String portId, boolean detected,
+ IndentingPrintWriter pw) {
+ synchronized (mLock) {
+ final RawPortInfo portInfo = mSimulatedPorts.get(portId);
+ if (portInfo == null) {
+ pw.println("Simulated port not found.");
+ return;
+ }
+
+ pw.println("Simulating wet port: portId=" + portId
+ + ", wet=" + detected);
+ portInfo.contaminantDetectionStatus = detected
+ ? UsbPortStatus.CONTAMINANT_DETECTION_DETECTED
+ : UsbPortStatus.CONTAMINANT_DETECTION_NOT_DETECTED;
+ updatePortsLocked(pw, null);
+ }
+ }
+
public void disconnectSimulatedPort(String portId, IndentingPrintWriter pw) {
synchronized (mLock) {
final RawPortInfo portInfo = mSimulatedPorts.get(portId);
@@ -441,7 +604,8 @@ public class UsbPortManager {
this.portManager = portManager;
}
- public void notifyPortStatusChange(ArrayList<PortStatus> currentPortStatus, int retval) {
+ public void notifyPortStatusChange(
+ ArrayList<android.hardware.usb.V1_0.PortStatus> currentPortStatus, int retval) {
if (!portManager.mSystemReady) {
return;
}
@@ -453,14 +617,17 @@ public class UsbPortManager {
ArrayList<RawPortInfo> newPortInfo = new ArrayList<>();
- for (PortStatus current : currentPortStatus) {
+ for (android.hardware.usb.V1_0.PortStatus current : currentPortStatus) {
RawPortInfo temp = new RawPortInfo(current.portName,
- current.supportedModes, current.currentMode,
+ current.supportedModes, CONTAMINANT_PROTECTION_NONE,
+ current.currentMode,
current.canChangeMode, current.currentPowerRole,
current.canChangePowerRole,
- current.currentDataRole, current.canChangeDataRole);
+ current.currentDataRole, current.canChangeDataRole,
+ false, CONTAMINANT_PROTECTION_NONE,
+ false, CONTAMINANT_DETECTION_NOT_SUPPORTED);
newPortInfo.add(temp);
- logAndPrint(Log.INFO, pw, "ClientCallback: " + current.portName);
+ logAndPrint(Log.INFO, pw, "ClientCallback V1_0: " + current.portName);
}
Message message = portManager.mHandler.obtainMessage();
@@ -485,14 +652,61 @@ public class UsbPortManager {
ArrayList<RawPortInfo> newPortInfo = new ArrayList<>();
- for (PortStatus_1_1 current : currentPortStatus) {
+ int numStatus = currentPortStatus.size();
+ for (int i = 0; i < numStatus; i++) {
+ PortStatus_1_1 current = currentPortStatus.get(i);
RawPortInfo temp = new RawPortInfo(current.status.portName,
- current.supportedModes, current.currentMode,
+ current.supportedModes, CONTAMINANT_PROTECTION_NONE,
+ current.currentMode,
current.status.canChangeMode, current.status.currentPowerRole,
current.status.canChangePowerRole,
- current.status.currentDataRole, current.status.canChangeDataRole);
+ current.status.currentDataRole, current.status.canChangeDataRole,
+ false, CONTAMINANT_PROTECTION_NONE,
+ false, CONTAMINANT_DETECTION_NOT_SUPPORTED);
+ newPortInfo.add(temp);
+ logAndPrint(Log.INFO, pw, "ClientCallback V1_1: " + current.status.portName);
+ }
+
+ Message message = portManager.mHandler.obtainMessage();
+ Bundle bundle = new Bundle();
+ bundle.putParcelableArrayList(PORT_INFO, newPortInfo);
+ message.what = MSG_UPDATE_PORTS;
+ message.setData(bundle);
+ portManager.mHandler.sendMessage(message);
+ }
+
+ public void notifyPortStatusChange_1_2(
+ ArrayList<PortStatus> currentPortStatus, int retval) {
+ if (!portManager.mSystemReady) {
+ return;
+ }
+
+ if (retval != Status.SUCCESS) {
+ logAndPrint(Log.ERROR, pw, "port status enquiry failed");
+ return;
+ }
+
+ ArrayList<RawPortInfo> newPortInfo = new ArrayList<>();
+
+ int numStatus = currentPortStatus.size();
+ for (int i = 0; i < numStatus; i++) {
+ PortStatus current = currentPortStatus.get(i);
+ RawPortInfo temp = new RawPortInfo(current.status_1_1.status.portName,
+ current.status_1_1.supportedModes,
+ current.supportedContaminantProtectionModes,
+ current.status_1_1.currentMode,
+ current.status_1_1.status.canChangeMode,
+ current.status_1_1.status.currentPowerRole,
+ current.status_1_1.status.canChangePowerRole,
+ current.status_1_1.status.currentDataRole,
+ current.status_1_1.status.canChangeDataRole,
+ current.supportsEnableContaminantPresenceProtection,
+ current.contaminantProtectionStatus,
+ current.supportsEnableContaminantPresenceDetection,
+ current.contaminantDetectionStatus);
newPortInfo.add(temp);
- logAndPrint(Log.INFO, pw, "ClientCallback: " + current.status.portName);
+ logAndPrint(Log.INFO, pw, "ClientCallback V1_2: "
+ + current.status_1_1.status.portName);
}
Message message = portManager.mHandler.obtainMessage();
@@ -573,16 +787,26 @@ public class UsbPortManager {
for (int i = 0; i < count; i++) {
final RawPortInfo portInfo = mSimulatedPorts.valueAt(i);
addOrUpdatePortLocked(portInfo.portId, portInfo.supportedModes,
+ portInfo.supportedContaminantProtectionModes,
portInfo.currentMode, portInfo.canChangeMode,
portInfo.currentPowerRole, portInfo.canChangePowerRole,
- portInfo.currentDataRole, portInfo.canChangeDataRole, pw);
+ portInfo.currentDataRole, portInfo.canChangeDataRole,
+ portInfo.supportsEnableContaminantPresenceProtection,
+ portInfo.contaminantProtectionStatus,
+ portInfo.supportsEnableContaminantPresenceDetection,
+ portInfo.contaminantDetectionStatus, pw);
}
} else {
for (RawPortInfo currentPortInfo : newPortInfo) {
addOrUpdatePortLocked(currentPortInfo.portId, currentPortInfo.supportedModes,
+ currentPortInfo.supportedContaminantProtectionModes,
currentPortInfo.currentMode, currentPortInfo.canChangeMode,
currentPortInfo.currentPowerRole, currentPortInfo.canChangePowerRole,
- currentPortInfo.currentDataRole, currentPortInfo.canChangeDataRole, pw);
+ currentPortInfo.currentDataRole, currentPortInfo.canChangeDataRole,
+ currentPortInfo.supportsEnableContaminantPresenceProtection,
+ currentPortInfo.contaminantProtectionStatus,
+ currentPortInfo.supportsEnableContaminantPresenceDetection,
+ currentPortInfo.contaminantDetectionStatus, pw);
}
}
@@ -608,12 +832,16 @@ public class UsbPortManager {
}
}
-
// Must only be called by updatePortsLocked.
private void addOrUpdatePortLocked(String portId, int supportedModes,
+ int supportedContaminantProtectionModes,
int currentMode, boolean canChangeMode,
int currentPowerRole, boolean canChangePowerRole,
int currentDataRole, boolean canChangeDataRole,
+ boolean supportsEnableContaminantPresenceProtection,
+ int contaminantProtectionStatus,
+ boolean supportsEnableContaminantPresenceDetection,
+ int contaminantDetectionStatus,
IndentingPrintWriter pw) {
// Only allow mode switch capability for dual role ports.
// Validate that the current mode matches the supported modes we expect.
@@ -664,12 +892,15 @@ public class UsbPortManager {
// Update the port data structures.
PortInfo portInfo = mPorts.get(portId);
if (portInfo == null) {
- portInfo = new PortInfo(mContext.getSystemService(UsbManager.class), portId,
- supportedModes);
+ portInfo = new PortInfo(mContext.getSystemService(UsbManager.class),
+ portId, supportedModes, supportedContaminantProtectionModes,
+ supportsEnableContaminantPresenceProtection,
+ supportsEnableContaminantPresenceDetection);
portInfo.setStatus(currentMode, canChangeMode,
currentPowerRole, canChangePowerRole,
currentDataRole, canChangeDataRole,
- supportedRoleCombinations);
+ supportedRoleCombinations, contaminantProtectionStatus,
+ contaminantDetectionStatus);
mPorts.put(portId, portInfo);
} else {
// Sanity check that ports aren't changing definition out from under us.
@@ -681,10 +912,32 @@ public class UsbPortManager {
+ ", current=" + UsbPort.modeToString(supportedModes));
}
+ if (supportsEnableContaminantPresenceProtection
+ != portInfo.mUsbPort.supportsEnableContaminantPresenceProtection()) {
+ logAndPrint(Log.WARN, pw,
+ "Ignoring inconsistent supportsEnableContaminantPresenceProtection"
+ + "USB port driver (should be immutable): "
+ + "previous="
+ + portInfo.mUsbPort.supportsEnableContaminantPresenceProtection()
+ + ", current=" + supportsEnableContaminantPresenceProtection);
+ }
+
+ if (supportsEnableContaminantPresenceDetection
+ != portInfo.mUsbPort.supportsEnableContaminantPresenceDetection()) {
+ logAndPrint(Log.WARN, pw,
+ "Ignoring inconsistent supportsEnableContaminantPresenceDetection "
+ + "USB port driver (should be immutable): "
+ + "previous="
+ + portInfo.mUsbPort.supportsEnableContaminantPresenceDetection()
+ + ", current=" + supportsEnableContaminantPresenceDetection);
+ }
+
+
if (portInfo.setStatus(currentMode, canChangeMode,
currentPowerRole, canChangePowerRole,
currentDataRole, canChangeDataRole,
- supportedRoleCombinations)) {
+ supportedRoleCombinations, contaminantProtectionStatus,
+ contaminantDetectionStatus)) {
portInfo.mDisposition = PortInfo.DISPOSITION_CHANGED;
} else {
portInfo.mDisposition = PortInfo.DISPOSITION_READY;
@@ -695,16 +948,37 @@ public class UsbPortManager {
private void handlePortAddedLocked(PortInfo portInfo, IndentingPrintWriter pw) {
logAndPrint(Log.INFO, pw, "USB port added: " + portInfo);
sendPortChangedBroadcastLocked(portInfo);
+ updateContaminantNotification();
}
private void handlePortChangedLocked(PortInfo portInfo, IndentingPrintWriter pw) {
logAndPrint(Log.INFO, pw, "USB port changed: " + portInfo);
sendPortChangedBroadcastLocked(portInfo);
+ updateContaminantNotification();
}
private void handlePortRemovedLocked(PortInfo portInfo, IndentingPrintWriter pw) {
logAndPrint(Log.INFO, pw, "USB port removed: " + portInfo);
sendPortChangedBroadcastLocked(portInfo);
+ updateContaminantNotification();
+ }
+
+ // Constants have to be converted between USB HAL V1.2 ContaminantDetectionStatus
+ // to usb.proto as proto guidelines recommends 0 to be UNKNOWN/UNSUPPORTTED
+ // whereas HAL policy is against a loosely defined constant.
+ private static int convertContaminantDetectionStatusToProto(int contaminantDetectionStatus) {
+ switch (contaminantDetectionStatus) {
+ case UsbPortStatus.CONTAMINANT_DETECTION_NOT_SUPPORTED:
+ return UsbServiceProto.CONTAMINANT_STATUS_NOT_SUPPORTED;
+ case UsbPortStatus.CONTAMINANT_DETECTION_DISABLED:
+ return UsbServiceProto.CONTAMINANT_STATUS_DISABLED;
+ case UsbPortStatus.CONTAMINANT_DETECTION_NOT_DETECTED:
+ return UsbServiceProto.CONTAMINANT_STATUS_NOT_DETECTED;
+ case UsbPortStatus.CONTAMINANT_DETECTION_DETECTED:
+ return UsbServiceProto.CONTAMINANT_STATUS_DETECTED;
+ default:
+ return UsbServiceProto.CONTAMINANT_STATUS_UNKNOWN;
+ }
}
private void sendPortChangedBroadcastLocked(PortInfo portInfo) {
@@ -721,6 +995,33 @@ public class UsbPortManager {
Manifest.permission.MANAGE_USB));
// Log to statsd
+
+ // Port is removed
+ if (portInfo.mUsbPortStatus == null) {
+ if (mConnected.containsKey(portInfo.mUsbPort.getId())) {
+ //Previous logged a connected. Set it to disconnected.
+ if (mConnected.get(portInfo.mUsbPort.getId())) {
+ StatsLog.write(StatsLog.USB_CONNECTOR_STATE_CHANGED,
+ StatsLog.USB_CONNECTOR_STATE_CHANGED__STATE__STATE_DISCONNECTED,
+ portInfo.mUsbPort.getId(), portInfo.mLastConnectDurationMillis);
+ }
+ mConnected.remove(portInfo.mUsbPort.getId());
+ }
+
+ if (mContaminantStatus.containsKey(portInfo.mUsbPort.getId())) {
+ //Previous logged a contaminant detected. Set it to not detected.
+ if ((mContaminantStatus.get(portInfo.mUsbPort.getId())
+ == UsbPortStatus.CONTAMINANT_DETECTION_DETECTED)) {
+ StatsLog.write(StatsLog.USB_CONTAMINANT_REPORTED,
+ portInfo.mUsbPort.getId(),
+ convertContaminantDetectionStatusToProto(
+ UsbPortStatus.CONTAMINANT_DETECTION_NOT_DETECTED));
+ }
+ mContaminantStatus.remove(portInfo.mUsbPort.getId());
+ }
+ return;
+ }
+
if (!mConnected.containsKey(portInfo.mUsbPort.getId())
|| (mConnected.get(portInfo.mUsbPort.getId())
!= portInfo.mUsbPortStatus.isConnected())) {
@@ -731,6 +1032,17 @@ public class UsbPortManager {
StatsLog.USB_CONNECTOR_STATE_CHANGED__STATE__STATE_DISCONNECTED,
portInfo.mUsbPort.getId(), portInfo.mLastConnectDurationMillis);
}
+
+ if (!mContaminantStatus.containsKey(portInfo.mUsbPort.getId())
+ || (mContaminantStatus.get(portInfo.mUsbPort.getId())
+ != portInfo.mUsbPortStatus.getContaminantDetectionStatus())) {
+ mContaminantStatus.put(portInfo.mUsbPort.getId(),
+ portInfo.mUsbPortStatus.getContaminantDetectionStatus());
+ StatsLog.write(StatsLog.USB_CONTAMINANT_REPORTED,
+ portInfo.mUsbPort.getId(),
+ convertContaminantDetectionStatusToProto(
+ portInfo.mUsbPortStatus.getContaminantDetectionStatus()));
+ }
}
private static void logAndPrint(int priority, IndentingPrintWriter pw, String msg) {
@@ -759,6 +1071,11 @@ public class UsbPortManager {
}
break;
}
+ case MSG_SYSTEM_READY: {
+ mNotificationManager = (NotificationManager)
+ mContext.getSystemService(Context.NOTIFICATION_SERVICE);
+ break;
+ }
}
}
};
@@ -784,8 +1101,14 @@ public class UsbPortManager {
// 0 when port is connected. Else reports the last connected duration
public long mLastConnectDurationMillis;
- PortInfo(@NonNull UsbManager usbManager, @NonNull String portId, int supportedModes) {
- mUsbPort = new UsbPort(usbManager, portId, supportedModes);
+ PortInfo(@NonNull UsbManager usbManager, @NonNull String portId, int supportedModes,
+ int supportedContaminantProtectionModes,
+ boolean supportsEnableContaminantPresenceDetection,
+ boolean supportsEnableContaminantPresenceProtection) {
+ mUsbPort = new UsbPort(usbManager, portId, supportedModes,
+ supportedContaminantProtectionModes,
+ supportsEnableContaminantPresenceDetection,
+ supportsEnableContaminantPresenceProtection);
}
public boolean setStatus(int currentMode, boolean canChangeMode,
@@ -804,7 +1127,45 @@ public class UsbPortManager {
|| mUsbPortStatus.getSupportedRoleCombinations()
!= supportedRoleCombinations) {
mUsbPortStatus = new UsbPortStatus(currentMode, currentPowerRole, currentDataRole,
- supportedRoleCombinations);
+ supportedRoleCombinations, UsbPortStatus.CONTAMINANT_PROTECTION_NONE,
+ UsbPortStatus.CONTAMINANT_DETECTION_NOT_SUPPORTED);
+ dispositionChanged = true;
+ }
+
+ if (mUsbPortStatus.isConnected() && mConnectedAtMillis == 0) {
+ mConnectedAtMillis = SystemClock.elapsedRealtime();
+ mLastConnectDurationMillis = 0;
+ } else if (!mUsbPortStatus.isConnected() && mConnectedAtMillis != 0) {
+ mLastConnectDurationMillis = SystemClock.elapsedRealtime() - mConnectedAtMillis;
+ mConnectedAtMillis = 0;
+ }
+
+ return dispositionChanged;
+ }
+
+ public boolean setStatus(int currentMode, boolean canChangeMode,
+ int currentPowerRole, boolean canChangePowerRole,
+ int currentDataRole, boolean canChangeDataRole,
+ int supportedRoleCombinations, int contaminantProtectionStatus,
+ int contaminantDetectionStatus) {
+ boolean dispositionChanged = false;
+
+ mCanChangeMode = canChangeMode;
+ mCanChangePowerRole = canChangePowerRole;
+ mCanChangeDataRole = canChangeDataRole;
+ if (mUsbPortStatus == null
+ || mUsbPortStatus.getCurrentMode() != currentMode
+ || mUsbPortStatus.getCurrentPowerRole() != currentPowerRole
+ || mUsbPortStatus.getCurrentDataRole() != currentDataRole
+ || mUsbPortStatus.getSupportedRoleCombinations()
+ != supportedRoleCombinations
+ || mUsbPortStatus.getContaminantProtectionStatus()
+ != contaminantProtectionStatus
+ || mUsbPortStatus.getContaminantDetectionStatus()
+ != contaminantDetectionStatus) {
+ mUsbPortStatus = new UsbPortStatus(currentMode, currentPowerRole, currentDataRole,
+ supportedRoleCombinations, contaminantProtectionStatus,
+ contaminantDetectionStatus);
dispositionChanged = true;
}
@@ -855,32 +1216,54 @@ public class UsbPortManager {
private static final class RawPortInfo implements Parcelable {
public final String portId;
public final int supportedModes;
+ public final int supportedContaminantProtectionModes;
public int currentMode;
public boolean canChangeMode;
public int currentPowerRole;
public boolean canChangePowerRole;
public int currentDataRole;
public boolean canChangeDataRole;
+ public boolean supportsEnableContaminantPresenceProtection;
+ public int contaminantProtectionStatus;
+ public boolean supportsEnableContaminantPresenceDetection;
+ public int contaminantDetectionStatus;
RawPortInfo(String portId, int supportedModes) {
this.portId = portId;
this.supportedModes = supportedModes;
+ this.supportedContaminantProtectionModes = UsbPortStatus.CONTAMINANT_PROTECTION_NONE;
+ this.supportsEnableContaminantPresenceProtection = false;
+ this.contaminantProtectionStatus = UsbPortStatus.CONTAMINANT_PROTECTION_NONE;
+ this.supportsEnableContaminantPresenceDetection = false;
+ this.contaminantDetectionStatus = UsbPortStatus.CONTAMINANT_DETECTION_NOT_SUPPORTED;
}
- RawPortInfo(String portId, int supportedModes,
+ RawPortInfo(String portId, int supportedModes, int supportedContaminantProtectionModes,
int currentMode, boolean canChangeMode,
int currentPowerRole, boolean canChangePowerRole,
- int currentDataRole, boolean canChangeDataRole) {
+ int currentDataRole, boolean canChangeDataRole,
+ boolean supportsEnableContaminantPresenceProtection,
+ int contaminantProtectionStatus,
+ boolean supportsEnableContaminantPresenceDetection,
+ int contaminantDetectionStatus) {
this.portId = portId;
this.supportedModes = supportedModes;
+ this.supportedContaminantProtectionModes = supportedContaminantProtectionModes;
this.currentMode = currentMode;
this.canChangeMode = canChangeMode;
this.currentPowerRole = currentPowerRole;
this.canChangePowerRole = canChangePowerRole;
this.currentDataRole = currentDataRole;
this.canChangeDataRole = canChangeDataRole;
+ this.supportsEnableContaminantPresenceProtection =
+ supportsEnableContaminantPresenceProtection;
+ this.contaminantProtectionStatus = contaminantProtectionStatus;
+ this.supportsEnableContaminantPresenceDetection =
+ supportsEnableContaminantPresenceDetection;
+ this.contaminantDetectionStatus = contaminantDetectionStatus;
}
+
@Override
public int describeContents() {
return 0;
@@ -890,35 +1273,50 @@ public class UsbPortManager {
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(portId);
dest.writeInt(supportedModes);
+ dest.writeInt(supportedContaminantProtectionModes);
dest.writeInt(currentMode);
dest.writeByte((byte) (canChangeMode ? 1 : 0));
dest.writeInt(currentPowerRole);
dest.writeByte((byte) (canChangePowerRole ? 1 : 0));
dest.writeInt(currentDataRole);
dest.writeByte((byte) (canChangeDataRole ? 1 : 0));
+ dest.writeBoolean(supportsEnableContaminantPresenceProtection);
+ dest.writeInt(contaminantProtectionStatus);
+ dest.writeBoolean(supportsEnableContaminantPresenceDetection);
+ dest.writeInt(contaminantDetectionStatus);
}
public static final Parcelable.Creator<RawPortInfo> CREATOR =
new Parcelable.Creator<RawPortInfo>() {
- @Override
- public RawPortInfo createFromParcel(Parcel in) {
- String id = in.readString();
- int supportedModes = in.readInt();
- int currentMode = in.readInt();
- boolean canChangeMode = in.readByte() != 0;
- int currentPowerRole = in.readInt();
- boolean canChangePowerRole = in.readByte() != 0;
- int currentDataRole = in.readInt();
- boolean canChangeDataRole = in.readByte() != 0;
- return new RawPortInfo(id, supportedModes, currentMode, canChangeMode,
- currentPowerRole, canChangePowerRole,
- currentDataRole, canChangeDataRole);
- }
+ @Override
+ public RawPortInfo createFromParcel(Parcel in) {
+ String id = in.readString();
+ int supportedModes = in.readInt();
+ int supportedContaminantProtectionModes = in.readInt();
+ int currentMode = in.readInt();
+ boolean canChangeMode = in.readByte() != 0;
+ int currentPowerRole = in.readInt();
+ boolean canChangePowerRole = in.readByte() != 0;
+ int currentDataRole = in.readInt();
+ boolean canChangeDataRole = in.readByte() != 0;
+ boolean supportsEnableContaminantPresenceProtection = in.readBoolean();
+ int contaminantProtectionStatus = in.readInt();
+ boolean supportsEnableContaminantPresenceDetection = in.readBoolean();
+ int contaminantDetectionStatus = in.readInt();
+ return new RawPortInfo(id, supportedModes,
+ supportedContaminantProtectionModes, currentMode, canChangeMode,
+ currentPowerRole, canChangePowerRole,
+ currentDataRole, canChangeDataRole,
+ supportsEnableContaminantPresenceProtection,
+ contaminantProtectionStatus,
+ supportsEnableContaminantPresenceDetection,
+ contaminantDetectionStatus);
+ }
- @Override
- public RawPortInfo[] newArray(int size) {
- return new RawPortInfo[size];
- }
- };
+ @Override
+ public RawPortInfo[] newArray(int size) {
+ return new RawPortInfo[size];
+ }
+ };
}
}
diff --git a/services/usb/java/com/android/server/usb/UsbService.java b/services/usb/java/com/android/server/usb/UsbService.java
index 911547720a8f..4be68b83dbcb 100644
--- a/services/usb/java/com/android/server/usb/UsbService.java
+++ b/services/usb/java/com/android/server/usb/UsbService.java
@@ -554,6 +554,21 @@ public class UsbService extends IUsbManager.Stub {
}
@Override
+ public void enableContaminantDetection(String portId, boolean enable) {
+ Preconditions.checkNotNull(portId, "portId must not be null");
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
+
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ if (mPortManager != null) {
+ mPortManager.enableContaminantDetection(portId, enable, null);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ @Override
public void setUsbDeviceConnectionHandler(ComponentName usbDeviceConnectionHandler) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
synchronized (mLock) {
@@ -747,6 +762,15 @@ public class UsbService extends IUsbManager.Stub {
mPortManager.dump(new DualDumpOutputStream(new IndentingPrintWriter(pw, " ")),
"", 0);
}
+ } else if ("set-contaminant-status".equals(args[0]) && args.length == 3) {
+ final String portId = args[1];
+ final Boolean wet = Boolean.parseBoolean(args[2]);
+ if (mPortManager != null) {
+ mPortManager.simulateContaminantStatus(portId, wet, pw);
+ pw.println();
+ mPortManager.dump(new DualDumpOutputStream(new IndentingPrintWriter(pw, " ")),
+ "", 0);
+ }
} else if ("ports".equals(args[0]) && args.length == 1) {
if (mPortManager != null) {
mPortManager.dump(new DualDumpOutputStream(new IndentingPrintWriter(pw, " ")),
@@ -791,6 +815,11 @@ public class UsbService extends IUsbManager.Stub {
pw.println(" dumpsys usb connect-port \"matrix\" ufp sink device");
pw.println(" dumpsys usb reset");
pw.println();
+ pw.println("Example simulate contaminant status:");
+ pw.println(" dumpsys usb add-port \"matrix\" ufp");
+ pw.println(" dumpsys usb set-contaminant-status \"matrix\" true");
+ pw.println(" dumpsys usb set-contaminant-status \"matrix\" false");
+ pw.println();
pw.println("Example USB device descriptors:");
pw.println(" dumpsys usb dump-descriptors -dump-short");
pw.println(" dumpsys usb dump-descriptors -dump-tree");
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
index 8c82cc835ed9..697469a3c680 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
@@ -939,11 +939,7 @@ public class SoundTriggerService extends SystemService {
runOrAddOperation(new Operation(
// always execute:
() -> {
- // Don't remove the callback if multiple triggers are allowed or
- // if this event was triggered by a getModelState request
- if (!mRecognitionConfig.allowMultipleTriggers
- && event.status
- != SoundTrigger.RECOGNITION_STATUS_GET_STATE_RESPONSE) {
+ if (!mRecognitionConfig.allowMultipleTriggers) {
// Unregister this remoteService once op is done
synchronized (mCallbacksLock) {
mCallbacks.remove(mPuuid.getUuid());
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index 613c4ffceffc..718f2d379883 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -17,16 +17,18 @@
package com.android.server.voiceinteraction;
import android.Manifest;
+import android.annotation.CallbackExecutor;
+import android.annotation.NonNull;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
-
-import com.android.internal.app.IVoiceActionCheckCallback;
-import com.android.server.wm.ActivityTaskManagerInternal;
import android.app.AppGlobals;
+import android.app.role.OnRoleHoldersChangedListener;
+import android.app.role.RoleManager;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
@@ -62,8 +64,9 @@ import android.util.ArraySet;
import android.util.Log;
import android.util.Slog;
-import com.android.internal.app.IVoiceInteractionSessionListener;
+import com.android.internal.app.IVoiceActionCheckCallback;
import com.android.internal.app.IVoiceInteractionManagerService;
+import com.android.internal.app.IVoiceInteractionSessionListener;
import com.android.internal.app.IVoiceInteractionSessionShowCallback;
import com.android.internal.app.IVoiceInteractor;
import com.android.internal.content.PackageMonitor;
@@ -75,10 +78,12 @@ import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.UiThread;
import com.android.server.soundtrigger.SoundTriggerInternal;
+import com.android.server.wm.ActivityTaskManagerInternal;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.List;
+import java.util.concurrent.Executor;
/**
* SystemService that publishes an IVoiceInteractionManagerService.
@@ -201,6 +206,7 @@ public class VoiceInteractionManagerService extends SystemService {
VoiceInteractionManagerServiceStub() {
mEnableService = shouldEnableService(mContext);
+ new RoleObserver(mContext.getMainExecutor());
}
// TODO: VI Make sure the caller is the current user or profile
@@ -1205,6 +1211,57 @@ public class VoiceInteractionManagerService extends SystemService {
mSoundTriggerInternal.dump(fd, pw, args);
}
+ @Override
+ public void setTranscription(String transcription) {
+ synchronized (this) {
+ final int size = mVoiceInteractionSessionListeners.beginBroadcast();
+ for (int i = 0; i < size; ++i) {
+ final IVoiceInteractionSessionListener listener =
+ mVoiceInteractionSessionListeners.getBroadcastItem(i);
+ try {
+ listener.onTranscriptionUpdate(transcription);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error delivering voice transcription.", e);
+ }
+ }
+ mVoiceInteractionSessionListeners.finishBroadcast();
+ }
+ }
+
+ @Override
+ public void clearTranscription(boolean immediate) {
+ synchronized (this) {
+ final int size = mVoiceInteractionSessionListeners.beginBroadcast();
+ for (int i = 0; i < size; ++i) {
+ final IVoiceInteractionSessionListener listener =
+ mVoiceInteractionSessionListeners.getBroadcastItem(i);
+ try {
+ listener.onTranscriptionComplete(immediate);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error delivering transcription complete event.", e);
+ }
+ }
+ mVoiceInteractionSessionListeners.finishBroadcast();
+ }
+ }
+
+ @Override
+ public void setVoiceState(int state) {
+ synchronized (this) {
+ final int size = mVoiceInteractionSessionListeners.beginBroadcast();
+ for (int i = 0; i < size; ++i) {
+ final IVoiceInteractionSessionListener listener =
+ mVoiceInteractionSessionListeners.getBroadcastItem(i);
+ try {
+ listener.onVoiceStateChange(state);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error delivering voice state change.", e);
+ }
+ }
+ mVoiceInteractionSessionListeners.finishBroadcast();
+ }
+ }
+
private void enforceCallingPermission(String permission) {
if (mContext.checkCallingOrSelfPermission(permission)
!= PackageManager.PERMISSION_GRANTED) {
@@ -1218,6 +1275,106 @@ public class VoiceInteractionManagerService extends SystemService {
getActiveServiceComponentName());
}
+ class RoleObserver implements OnRoleHoldersChangedListener {
+ private PackageManager mPm = mContext.getPackageManager();
+ private RoleManager mRm = mContext.getSystemService(RoleManager.class);
+
+ RoleObserver(@NonNull @CallbackExecutor Executor executor) {
+ mRm.addOnRoleHoldersChangedListenerAsUser(executor, this, UserHandle.ALL);
+ }
+
+ private @NonNull String getDefaultRecognizer(@NonNull UserHandle user) {
+ ResolveInfo resolveInfo = mPm.resolveServiceAsUser(
+ new Intent(RecognitionService.SERVICE_INTERFACE),
+ PackageManager.GET_META_DATA, user.getIdentifier());
+
+ if (resolveInfo == null || resolveInfo.serviceInfo == null) {
+ Log.w(TAG, "Unable to resolve default voice recognition service.");
+ return "";
+ }
+
+ return new ComponentName(resolveInfo.serviceInfo.packageName,
+ resolveInfo.serviceInfo.name).flattenToShortString();
+ }
+
+ /**
+ * Convert the assistant-role holder into settings. The rest of the system uses the
+ * settings.
+ *
+ * @param roleName the name of the role whose holders are changed
+ * @param user the user for this role holder change
+ */
+ @Override
+ public void onRoleHoldersChanged(@NonNull String roleName, @NonNull UserHandle user) {
+ if (!roleName.equals(RoleManager.ROLE_ASSISTANT)) {
+ return;
+ }
+
+ List<String> roleHolders = mRm.getRoleHoldersAsUser(roleName, user);
+
+ if (roleHolders.isEmpty()) {
+ Settings.Secure.putString(getContext().getContentResolver(),
+ Settings.Secure.ASSISTANT, "");
+ Settings.Secure.putString(getContext().getContentResolver(),
+ Settings.Secure.VOICE_INTERACTION_SERVICE, "");
+ Settings.Secure.putString(getContext().getContentResolver(),
+ Settings.Secure.VOICE_RECOGNITION_SERVICE, getDefaultRecognizer(user));
+ } else {
+ // Assistant is singleton role
+ String pkg = roleHolders.get(0);
+
+ // Try to set role holder as VoiceInteractionService
+ List<ResolveInfo> services = mPm.queryIntentServicesAsUser(
+ new Intent(VoiceInteractionService.SERVICE_INTERFACE).setPackage(pkg),
+ PackageManager.GET_META_DATA, user.getIdentifier());
+
+ for (ResolveInfo resolveInfo : services) {
+ ServiceInfo serviceInfo = resolveInfo.serviceInfo;
+
+ VoiceInteractionServiceInfo voiceInteractionServiceInfo =
+ new VoiceInteractionServiceInfo(mPm, serviceInfo);
+ if (!voiceInteractionServiceInfo.getSupportsAssist()) {
+ continue;
+ }
+
+ String serviceComponentName = serviceInfo.getComponentName()
+ .flattenToShortString();
+
+ String serviceRecognizerName = new ComponentName(pkg,
+ voiceInteractionServiceInfo.getRecognitionService())
+ .flattenToShortString();
+
+ Settings.Secure.putString(getContext().getContentResolver(),
+ Settings.Secure.ASSISTANT, serviceComponentName);
+ Settings.Secure.putString(getContext().getContentResolver(),
+ Settings.Secure.VOICE_INTERACTION_SERVICE, serviceComponentName);
+ Settings.Secure.putString(getContext().getContentResolver(),
+ Settings.Secure.VOICE_RECOGNITION_SERVICE, serviceRecognizerName);
+
+ return;
+ }
+
+ // If no service could be found try to set assist activity
+ final List<ResolveInfo> activities = mPm.queryIntentActivitiesAsUser(
+ new Intent(Intent.ACTION_ASSIST).setPackage(pkg),
+ PackageManager.MATCH_DEFAULT_ONLY, user.getIdentifier());
+
+ for (ResolveInfo resolveInfo : activities) {
+ ActivityInfo activityInfo = resolveInfo.activityInfo;
+
+ Settings.Secure.putString(getContext().getContentResolver(),
+ Settings.Secure.ASSISTANT,
+ activityInfo.getComponentName().flattenToShortString());
+ Settings.Secure.putString(getContext().getContentResolver(),
+ Settings.Secure.VOICE_INTERACTION_SERVICE, "");
+ Settings.Secure.putString(getContext().getContentResolver(),
+ Settings.Secure.VOICE_RECOGNITION_SERVICE,
+ getDefaultRecognizer(user));
+ }
+ }
+ }
+ }
+
class SettingsObserver extends ContentObserver {
SettingsObserver(Handler handler) {
super(handler);
diff --git a/startop/OWNERS b/startop/OWNERS
index 762cd8e48be4..5cf95825d11b 100644
--- a/startop/OWNERS
+++ b/startop/OWNERS
@@ -2,5 +2,5 @@
chriswailes@google.com
eholk@google.com
iam@google.com
-sehr@google.com
mathieuc@google.com
+sehr@google.com
diff --git a/telecomm/java/android/telecom/CallScreeningService.java b/telecomm/java/android/telecom/CallScreeningService.java
index 826ad82dfbb2..818ebd998f50 100644
--- a/telecomm/java/android/telecom/CallScreeningService.java
+++ b/telecomm/java/android/telecom/CallScreeningService.java
@@ -16,6 +16,7 @@
package android.telecom;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.SdkConstant;
import android.app.Service;
@@ -32,6 +33,9 @@ import com.android.internal.os.SomeArgs;
import com.android.internal.telecom.ICallScreeningAdapter;
import com.android.internal.telecom.ICallScreeningService;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* This service can be implemented by the default dialer (see
* {@link TelecomManager#getDefaultDialerPackage()}) or a third party app to allow or disallow
@@ -88,6 +92,128 @@ import com.android.internal.telecom.ICallScreeningService;
* </pre>
*/
public abstract class CallScreeningService extends Service {
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(
+ prefix = { "CALL_DURATION_" },
+ value = {CALL_DURATION_VERY_SHORT, CALL_DURATION_SHORT, CALL_DURATION_MEDIUM,
+ CALL_DURATION_LONG})
+ public @interface CallDuration {}
+
+ /**
+ * Call duration reported with {@link #EXTRA_CALL_DURATION} to indicate to the
+ * {@link CallScreeningService} the duration of a call for which the user reported the nuisance
+ * status (see {@link TelecomManager#reportNuisanceCallStatus(Uri, boolean)}). The
+ * {@link CallScreeningService} can use this as a signal for training nuisance detection
+ * algorithms. The call duration is reported in coarse grained buckets to minimize exposure of
+ * identifying call log information to the {@link CallScreeningService}.
+ * <p>
+ * Indicates the call was < 3 seconds in duration.
+ */
+ public static final int CALL_DURATION_VERY_SHORT = 1;
+
+ /**
+ * Call duration reported with {@link #EXTRA_CALL_DURATION} to indicate to the
+ * {@link CallScreeningService} the duration of a call for which the user reported the nuisance
+ * status (see {@link TelecomManager#reportNuisanceCallStatus(Uri, boolean)}). The
+ * {@link CallScreeningService} can use this as a signal for training nuisance detection
+ * algorithms. The call duration is reported in coarse grained buckets to minimize exposure of
+ * identifying call log information to the {@link CallScreeningService}.
+ * <p>
+ * Indicates the call was greater than 3 seconds, but less than 60 seconds in duration.
+ */
+ public static final int CALL_DURATION_SHORT = 2;
+
+ /**
+ * Call duration reported with {@link #EXTRA_CALL_DURATION} to indicate to the
+ * {@link CallScreeningService} the duration of a call for which the user reported the nuisance
+ * status (see {@link TelecomManager#reportNuisanceCallStatus(Uri, boolean)}). The
+ * {@link CallScreeningService} can use this as a signal for training nuisance detection
+ * algorithms. The call duration is reported in coarse grained buckets to minimize exposure of
+ * identifying call log information to the {@link CallScreeningService}.
+ * <p>
+ * Indicates the call was greater than 60 seconds, but less than 120 seconds in duration.
+ */
+ public static final int CALL_DURATION_MEDIUM = 3;
+
+ /**
+ * Call duration reported with {@link #EXTRA_CALL_DURATION} to indicate to the
+ * {@link CallScreeningService} the duration of a call for which the user reported the nuisance
+ * status (see {@link TelecomManager#reportNuisanceCallStatus(Uri, boolean)}). The
+ * {@link CallScreeningService} can use this as a signal for training nuisance detection
+ * algorithms. The call duration is reported in coarse grained buckets to minimize exposure of
+ * identifying call log information to the {@link CallScreeningService}.
+ * <p>
+ * Indicates the call was greater than 120 seconds.
+ */
+ public static final int CALL_DURATION_LONG = 4;
+
+ /**
+ * Telecom sends this intent to the {@link CallScreeningService} which the user has chosen to
+ * fill the call screening role when the user indicates through the default dialer whether a
+ * call is a nuisance call or not (see
+ * {@link TelecomManager#reportNuisanceCallStatus(Uri, boolean)}).
+ * <p>
+ * The following extra values are provided for the call:
+ * <ol>
+ * <li>{@link #EXTRA_CALL_HANDLE} - the handle of the call.</li>
+ * <li>{@link #EXTRA_IS_NUISANCE} - {@code true} if the user reported the call as a nuisance
+ * call, {@code false} otherwise.</li>
+ * <li>{@link #EXTRA_CALL_TYPE} - reports the type of call (incoming, rejected, missed,
+ * blocked).</li>
+ * <li>{@link #EXTRA_CALL_DURATION} - the duration of the call (see
+ * {@link #EXTRA_CALL_DURATION} for valid values).</li>
+ * </ol>
+ * <p>
+ * {@link CallScreeningService} implementations which want to track whether the user reports
+ * calls are nuisance calls should use declare a broadcast receiver in their manifest for this
+ * intent.
+ * <p>
+ * Note: Only {@link CallScreeningService} implementations which have provided
+ * {@link CallIdentification} information for calls at some point will receive this intent.
+ */
+ public static final String ACTION_NUISANCE_CALL_STATUS_CHANGED =
+ "android.telecom.action.NUISANCE_CALL_STATUS_CHANGED";
+
+ /**
+ * Extra used to provide the handle of the call for
+ * {@link #ACTION_NUISANCE_CALL_STATUS_CHANGED}. The call handle is reported as a
+ * {@link Uri}.
+ */
+ public static final String EXTRA_CALL_HANDLE = "android.telecom.extra.CALL_HANDLE";
+
+ /**
+ * Boolean extra used to indicate whether the user reported a call as a nuisance call.
+ * When {@code true}, the user reported that a call was a nuisance call, {@code false}
+ * otherwise. Sent with {@link #ACTION_NUISANCE_CALL_STATUS_CHANGED}.
+ */
+ public static final String EXTRA_IS_NUISANCE = "android.telecom.extra.IS_NUISANCE";
+
+ /**
+ * Integer extra used with {@link #ACTION_NUISANCE_CALL_STATUS_CHANGED} to report the type of
+ * call. Valid values are:
+ * <UL>
+ * <li>{@link android.provider.CallLog.Calls#MISSED_TYPE}</li>
+ * <li>{@link android.provider.CallLog.Calls#INCOMING_TYPE}</li>
+ * <li>{@link android.provider.CallLog.Calls#BLOCKED_TYPE}</li>
+ * <li>{@link android.provider.CallLog.Calls#REJECTED_TYPE}</li>
+ * </UL>
+ */
+ public static final String EXTRA_CALL_TYPE = "android.telecom.extra.CALL_TYPE";
+
+ /**
+ * Integer extra used to with {@link #ACTION_NUISANCE_CALL_STATUS_CHANGED} to report how long
+ * the call lasted. Valid values are:
+ * <UL>
+ * <LI>{@link #CALL_DURATION_VERY_SHORT}</LI>
+ * <LI>{@link #CALL_DURATION_SHORT}</LI>
+ * <LI>{@link #CALL_DURATION_MEDIUM}</LI>
+ * <LI>{@link #CALL_DURATION_LONG}</LI>
+ * </UL>
+ */
+ public static final String EXTRA_CALL_DURATION = "android.telecom.extra.CALL_DURATION";
+
/**
* The {@link Intent} that must be declared as handled by the service.
*/
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 0fe5e080d1f8..12a534418663 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -1970,6 +1970,33 @@ public class TelecomManager {
}
/**
+ * Called by the default dialer to report to Telecom when the user has marked a previous
+ * incoming call as a nuisance call or not.
+ * <p>
+ * Where the user has chosen a {@link CallScreeningService} to fill the call screening role,
+ * Telecom will notify that {@link CallScreeningService} of the user's report.
+ * <p>
+ * Requires that the caller is the default dialer app.
+ *
+ * @param handle The phone number of an incoming call which the user is reporting as either a
+ * nuisance of non-nuisance call.
+ * @param isNuisanceCall {@code true} if the user is reporting the call as a nuisance call,
+ * {@code false} if the user is reporting the call as a non-nuisance call.
+ */
+ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+ public void reportNuisanceCallStatus(@NonNull Uri handle, boolean isNuisanceCall) {
+ ITelecomService service = getTelecomService();
+ if (service != null) {
+ try {
+ service.reportNuisanceCallStatus(handle, isNuisanceCall,
+ mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelecomService#showCallScreen", e);
+ }
+ }
+ }
+
+ /**
* Handles {@link Intent#ACTION_CALL} intents trampolined from UserCallActivity.
* @param intent The {@link Intent#ACTION_CALL} intent to handle.
* @hide
diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
index e1d5c17d5e3a..5030f90afd3e 100644
--- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
+++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
@@ -285,6 +285,8 @@ interface ITelecomService {
*/
boolean isInEmergencyCall();
+ oneway void reportNuisanceCallStatus(in Uri address, boolean isNuisance, String callingPackage);
+
/**
* @see TelecomServiceImpl#handleCallIntent
*/
@@ -299,4 +301,5 @@ interface ITelecomService {
void addOrRemoveTestCallCompanionApp(String packageName, boolean isAdded);
void setTestAutoModeApp(String packageName);
+
}
diff --git a/telephony/java/android/provider/Telephony.java b/telephony/java/android/provider/Telephony.java
index 15abdb78b214..548227067b67 100644
--- a/telephony/java/android/provider/Telephony.java
+++ b/telephony/java/android/provider/Telephony.java
@@ -2375,6 +2375,9 @@ public final class Telephony {
/**
* Contains message parts.
+ *
+ * To avoid issues where applications might cache a part ID, the ID of a deleted part must
+ * not be reused to point at a new part.
*/
public static final class Part implements BaseColumns {
@@ -2386,6 +2389,12 @@ public final class Telephony {
}
/**
+ * The {@code content://} style URL for this table. Can be appended with a part ID to
+ * address individual parts.
+ */
+ public static final Uri CONTENT_URI = Uri.withAppendedPath(Mms.CONTENT_URI, "part");
+
+ /**
* The identifier of the message which this part belongs to.
* <P>Type: INTEGER</P>
*/
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index a33b44c454b4..349880d5ea3c 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -608,11 +608,41 @@ public class CarrierConfigManager {
public static final String KEY_CARRIER_PROMOTE_WFC_ON_CALL_FAIL_BOOL =
"carrier_promote_wfc_on_call_fail_bool";
- /** Flag specifying whether provisioning is required for VOLTE. */
+ /**
+ * Flag specifying whether provisioning is required for VoLTE, Video Telephony, and WiFi
+ * Calling.
+ */
public static final String KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL
= "carrier_volte_provisioning_required_bool";
/**
+ * Flag indicating whether or not the IMS MmTel UT capability requires carrier provisioning
+ * before it can be set as enabled.
+ *
+ * If true, the UT capability will be set to false for the newly loaded subscription
+ * and will require the carrier provisioning app to set the persistent provisioning result.
+ * If false, the platform will not wait for provisioning status updates for the UT capability
+ * and enable the UT over IMS capability for the subscription when the subscription is loaded.
+ *
+ * The default value for this key is {@code false}.
+ */
+ public static final String KEY_CARRIER_UT_PROVISIONING_REQUIRED_BOOL =
+ "carrier_ut_provisioning_required_bool";
+
+ /**
+ * Flag indicating whether or not the carrier supports Supplementary Services over the UT
+ * interface for this subscription.
+ *
+ * If true, the device will use Supplementary Services over UT when provisioned (see
+ * {@link #KEY_CARRIER_UT_PROVISIONING_REQUIRED_BOOL}). If false, this device will fallback to
+ * circuit switch for supplementary services and will disable this capability for IMS entirely.
+ *
+ * The default value for this key is {@code true}.
+ */
+ public static final String KEY_CARRIER_SUPPORTS_SS_OVER_UT_BOOL =
+ "carrier_supports_ss_over_ut_bool";
+
+ /**
* Flag specifying if WFC provisioning depends on VoLTE provisioning.
*
* {@code false}: default value; honor actual WFC provisioning state.
@@ -2575,6 +2605,8 @@ public class CarrierConfigManager {
sDefaults.putInt(KEY_CARRIER_DEFAULT_WFC_IMS_ROAMING_MODE_INT, 2);
sDefaults.putBoolean(KEY_CARRIER_FORCE_DISABLE_ETWS_CMAS_TEST_BOOL, false);
sDefaults.putBoolean(KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL, false);
+ sDefaults.putBoolean(KEY_CARRIER_UT_PROVISIONING_REQUIRED_BOOL, false);
+ sDefaults.putBoolean(KEY_CARRIER_SUPPORTS_SS_OVER_UT_BOOL, true);
sDefaults.putBoolean(KEY_CARRIER_VOLTE_OVERRIDE_WFC_PROVISIONING_BOOL, false);
sDefaults.putBoolean(KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL, true);
sDefaults.putBoolean(KEY_CARRIER_ALLOW_TURNOFF_IMS_BOOL, true);
diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java
index 1378bb004696..d777bf123b67 100644
--- a/telephony/java/android/telephony/SmsManager.java
+++ b/telephony/java/android/telephony/SmsManager.java
@@ -22,6 +22,10 @@ import android.annotation.SystemApi;
import android.annotation.UnsupportedAppUsage;
import android.app.ActivityThread;
import android.app.PendingIntent;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothMapClient;
+import android.bluetooth.BluetoothProfile;
import android.content.ActivityNotFoundException;
import android.content.ContentValues;
import android.content.Context;
@@ -32,6 +36,7 @@ import android.os.Build;
import android.os.Bundle;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.telecom.PhoneAccount;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;
@@ -61,6 +66,8 @@ import java.util.Map;
*/
public final class SmsManager {
private static final String TAG = "SmsManager";
+ private static final boolean DBG = false;
+
/**
* A psuedo-subId that represents the default subId at any given time. The actual subId it
* represents changes as the default subId is changed.
@@ -339,6 +346,44 @@ public final class SmsManager {
throw new IllegalArgumentException("Invalid message body");
}
+ // A Manager code accessing another manager is *not* acceptable, in Android.
+ // In this particular case, it is unavoidable because of the following:
+ // If the subscription for this SmsManager instance belongs to a remote SIM
+ // then a listener to get BluetoothMapClient proxy needs to be started up.
+ // Doing that is possible only in a foreground thread or as a system user.
+ // i.e., Can't be done in ISms service.
+ // For that reason, SubscriptionManager needs to be accessed here to determine
+ // if the subscription belongs to a remote SIM.
+ // Ideally, there should be another API in ISms to service messages going thru
+ // remote SIM subscriptions (and ISms should be tweaked to be able to access
+ // BluetoothMapClient proxy)
+ Context context = ActivityThread.currentApplication().getApplicationContext();
+ SubscriptionManager manager = (SubscriptionManager) context
+ .getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE);
+ int subId = getSubscriptionId();
+ SubscriptionInfo info = manager.getActiveSubscriptionInfo(subId);
+ if (DBG) {
+ Log.d(TAG, "for subId: " + subId + ", subscription-info: " + info);
+ }
+ if (info == null) {
+ // There is no subscription for the given subId. That can only mean one thing:
+ // the caller is using a SmsManager instance with an obsolete subscription id.
+ // That is most probably because caller didn't invalidate SmsManager instance
+ // for an already deleted subscription id.
+ Log.e(TAG, "subId: " + subId + " for this SmsManager instance is obsolete.");
+ sendErrorInPendingIntent(sentIntent, SmsManager.RESULT_ERROR_NO_SERVICE);
+ }
+
+ /* If the Subscription associated with this SmsManager instance belongs to a remote-sim,
+ * then send the message thru the remote-sim subscription.
+ */
+ if (info.getSubscriptionType() == SubscriptionManager.SUBSCRIPTION_TYPE_REMOTE_SIM) {
+ if (DBG) Log.d(TAG, "sending message thru bluetooth");
+ sendTextMessageBluetooth(destinationAddress, scAddress, text, sentIntent,
+ deliveryIntent, info);
+ return;
+ }
+
try {
ISms iccISms = getISmsServiceOrThrow();
iccISms.sendTextForSubscriber(getSubscriptionId(), ActivityThread.currentPackageName(),
@@ -350,6 +395,79 @@ public final class SmsManager {
}
}
+ private void sendTextMessageBluetooth(String destAddr, String scAddress,
+ String text, PendingIntent sentIntent, PendingIntent deliveryIntent,
+ SubscriptionInfo info) {
+ BluetoothAdapter btAdapter = BluetoothAdapter.getDefaultAdapter();
+ if (btAdapter == null) {
+ // No bluetooth service on this platform?
+ sendErrorInPendingIntent(sentIntent, SmsManager.RESULT_ERROR_NO_SERVICE);
+ return;
+ }
+ BluetoothDevice device = btAdapter.getRemoteDevice(info.getIccId());
+ if (device == null) {
+ if (DBG) Log.d(TAG, "Bluetooth device addr invalid: " + info.getIccId());
+ sendErrorInPendingIntent(sentIntent, SmsManager.RESULT_ERROR_NO_SERVICE);
+ return;
+ }
+ btAdapter.getProfileProxy(ActivityThread.currentApplication().getApplicationContext(),
+ new MapMessageSender(destAddr, text, device, sentIntent, deliveryIntent),
+ BluetoothProfile.MAP_CLIENT);
+ }
+
+ private class MapMessageSender implements BluetoothProfile.ServiceListener {
+ final Uri[] mDestAddr;
+ private String mMessage;
+ final BluetoothDevice mDevice;
+ final PendingIntent mSentIntent;
+ final PendingIntent mDeliveryIntent;
+ MapMessageSender(final String destAddr, final String message, final BluetoothDevice device,
+ final PendingIntent sentIntent, final PendingIntent deliveryIntent) {
+ super();
+ mDestAddr = new Uri[] {new Uri.Builder()
+ .appendPath(destAddr)
+ .scheme(PhoneAccount.SCHEME_TEL)
+ .build()};
+ mMessage = message;
+ mDevice = device;
+ mSentIntent = sentIntent;
+ mDeliveryIntent = deliveryIntent;
+ }
+
+ @Override
+ public void onServiceConnected(int profile, BluetoothProfile proxy) {
+ if (DBG) Log.d(TAG, "Service connected");
+ if (profile != BluetoothProfile.MAP_CLIENT) return;
+ BluetoothMapClient mapProfile = (BluetoothMapClient) proxy;
+ if (mMessage != null) {
+ if (DBG) Log.d(TAG, "Sending message thru bluetooth");
+ mapProfile.sendMessage(mDevice, mDestAddr, mMessage, mSentIntent, mDeliveryIntent);
+ mMessage = null;
+ }
+ BluetoothAdapter.getDefaultAdapter()
+ .closeProfileProxy(BluetoothProfile.MAP_CLIENT, mapProfile);
+ }
+
+ @Override
+ public void onServiceDisconnected(int profile) {
+ if (mMessage != null) {
+ if (DBG) Log.d(TAG, "Bluetooth disconnected before sending the message");
+ sendErrorInPendingIntent(mSentIntent, SmsManager.RESULT_ERROR_NO_SERVICE);
+ mMessage = null;
+ }
+ }
+ }
+
+ private void sendErrorInPendingIntent(PendingIntent intent, int errorCode) {
+ try {
+ intent.send(errorCode);
+ } catch (PendingIntent.CanceledException e) {
+ // PendingIntent is cancelled. ignore sending this error code back to
+ // caller.
+ if (DBG) Log.d(TAG, "PendingIntent.CanceledException: " + e.getMessage());
+ }
+ }
+
/**
* Send a text based SMS without writing it into the SMS Provider.
*
@@ -888,8 +1006,6 @@ public final class SmsManager {
}
}
-
-
/**
* Get the SmsManager associated with the default subscription id. The instance will always be
* associated with the default subscription id, even if the default subscription id is changed.
diff --git a/telephony/java/android/telephony/SubscriptionInfo.java b/telephony/java/android/telephony/SubscriptionInfo.java
index 51d5ab17ee16..a3b3374b183c 100644
--- a/telephony/java/android/telephony/SubscriptionInfo.java
+++ b/telephony/java/android/telephony/SubscriptionInfo.java
@@ -184,6 +184,11 @@ public class SubscriptionInfo implements Parcelable {
private int mProfileClass;
/**
+ * Type of subscription
+ */
+ private int mSubscriptionType;
+
+ /**
* @hide
*/
public SubscriptionInfo(int id, String iccId, int simSlotIndex, CharSequence displayName,
@@ -206,7 +211,8 @@ public class SubscriptionInfo implements Parcelable {
@Nullable String groupUUID, boolean isMetered, int carrierId, int profileClass) {
this(id, iccId, simSlotIndex, displayName, carrierName, nameSource, iconTint, number,
roaming, icon, mcc, mnc, countryIso, isEmbedded, accessRules, cardString, -1,
- isOpportunistic, groupUUID, isMetered, false, carrierId, profileClass);
+ isOpportunistic, groupUUID, isMetered, false, carrierId, profileClass,
+ SubscriptionManager.SUBSCRIPTION_TYPE_LOCAL_SIM);
}
/**
@@ -217,7 +223,7 @@ public class SubscriptionInfo implements Parcelable {
Bitmap icon, String mcc, String mnc, String countryIso, boolean isEmbedded,
@Nullable UiccAccessRule[] accessRules, String cardString, int cardId,
boolean isOpportunistic, @Nullable String groupUUID, boolean isMetered,
- boolean isGroupDisabled, int carrierid, int profileClass) {
+ boolean isGroupDisabled, int carrierId, int profileClass, int subType) {
this.mId = id;
this.mIccId = iccId;
this.mSimSlotIndex = simSlotIndex;
@@ -239,11 +245,11 @@ public class SubscriptionInfo implements Parcelable {
this.mGroupUUID = groupUUID;
this.mIsMetered = isMetered;
this.mIsGroupDisabled = isGroupDisabled;
- this.mCarrierId = carrierid;
+ this.mCarrierId = carrierId;
this.mProfileClass = profileClass;
+ this.mSubscriptionType = subType;
}
-
/**
* @return the subscription ID.
*/
@@ -487,6 +493,16 @@ public class SubscriptionInfo implements Parcelable {
}
/**
+ * This method returns the type of a subscription. It can be
+ * {@link SubscriptionManager#SUBSCRIPTION_TYPE_LOCAL_SIM} or
+ * {@link SubscriptionManager#SUBSCRIPTION_TYPE_REMOTE_SIM}.
+ * @return the type of subscription
+ */
+ public @SubscriptionManager.SubscriptionType int getSubscriptionType() {
+ return mSubscriptionType;
+ }
+
+ /**
* Checks whether the app with the given context is authorized to manage this subscription
* according to its metadata. Only supported for embedded subscriptions (if {@link #isEmbedded}
* returns true).
@@ -612,11 +628,12 @@ public class SubscriptionInfo implements Parcelable {
boolean isGroupDisabled = source.readBoolean();
int carrierid = source.readInt();
int profileClass = source.readInt();
+ int subType = source.readInt();
return new SubscriptionInfo(id, iccId, simSlotIndex, displayName, carrierName,
nameSource, iconTint, number, dataRoaming, iconBitmap, mcc, mnc, countryIso,
isEmbedded, accessRules, cardString, cardId, isOpportunistic, groupUUID,
- isMetered, isGroupDisabled, carrierid, profileClass);
+ isMetered, isGroupDisabled, carrierid, profileClass, subType);
}
@Override
@@ -650,6 +667,7 @@ public class SubscriptionInfo implements Parcelable {
dest.writeBoolean(mIsGroupDisabled);
dest.writeInt(mCarrierId);
dest.writeInt(mProfileClass);
+ dest.writeInt(mSubscriptionType);
}
@Override
@@ -686,7 +704,8 @@ public class SubscriptionInfo implements Parcelable {
+ " cardString=" + cardStringToPrint + " cardId=" + mCardId
+ " isOpportunistic " + mIsOpportunistic + " mGroupUUID=" + mGroupUUID
+ " isMetered=" + mIsMetered + " mIsGroupDisabled=" + mIsGroupDisabled
- + " profileClass=" + mProfileClass + "}";
+ + " profileClass=" + mProfileClass
+ + " subscriptionType=" + mSubscriptionType + "}";
}
@Override
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 9fa4c3ce899f..869cf1cf9e14 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -247,7 +247,9 @@ public class SubscriptionManager {
public static final String UNIQUE_KEY_SUBSCRIPTION_ID = "_id";
/**
- * TelephonyProvider column name for SIM ICC Identifier
+ * TelephonyProvider column name for a unique identifier for the subscription within the
+ * specific subscription type. For example, it contains SIM ICC Identifier subscriptions
+ * on Local SIMs. and Mac-address for Remote-SIM Subscriptions for Bluetooth devices.
* <P>Type: TEXT (String)</P>
*/
/** @hide */
@@ -265,6 +267,63 @@ public class SubscriptionManager {
public static final int SIM_NOT_INSERTED = -1;
/**
+ * The slot-index for Bluetooth Remote-SIM subscriptions
+ * @hide
+ */
+ public static final int SLOT_INDEX_FOR_REMOTE_SIM_SUB = INVALID_SIM_SLOT_INDEX;
+
+ /**
+ * TelephonyProvider column name Subscription-type.
+ * <P>Type: INTEGER (int)</P> {@link #SUBSCRIPTION_TYPE_LOCAL_SIM} for Local-SIM Subscriptions,
+ * {@link #SUBSCRIPTION_TYPE_REMOTE_SIM} for Remote-SIM Subscriptions.
+ * Default value is 0.
+ */
+ /** @hide */
+ public static final String SUBSCRIPTION_TYPE = "subscription_type";
+
+ /**
+ * This constant is to designate a subscription as a Local-SIM Subscription.
+ * <p> A Local-SIM can be a physical SIM inserted into a sim-slot in the device, or eSIM on the
+ * device.
+ * </p>
+ */
+ public static final int SUBSCRIPTION_TYPE_LOCAL_SIM = 0;
+
+ /**
+ * This constant is to designate a subscription as a Remote-SIM Subscription.
+ * <p>
+ * A Remote-SIM subscription is for a SIM on a phone connected to this device via some
+ * connectivity mechanism, for example bluetooth. Similar to Local SIM, this subscription can
+ * be used for SMS, Voice and data by proxying data through the connected device.
+ * Certain data of the SIM, such as IMEI, are not accessible for Remote SIMs.
+ * </p>
+ *
+ * <p>
+ * A Remote-SIM is available only as long the phone stays connected to this device.
+ * When the phone disconnects, Remote-SIM subscription is removed from this device and is
+ * no longer known. All data associated with the subscription, such as stored SMS, call logs,
+ * contacts etc, are removed from this device.
+ * </p>
+ *
+ * <p>
+ * If the phone re-connects to this device, a new Remote-SIM subscription is created for
+ * the phone. The Subscription Id associated with the new subscription is different from
+ * the Subscription Id of the previous Remote-SIM subscription created (and removed) for the
+ * phone; i.e., new Remote-SIM subscription treats the reconnected phone as a Remote-SIM that
+ * was never seen before.
+ * </p>
+ */
+ public static final int SUBSCRIPTION_TYPE_REMOTE_SIM = 1;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"SUBSCRIPTION_TYPE_"},
+ value = {
+ SUBSCRIPTION_TYPE_LOCAL_SIM,
+ SUBSCRIPTION_TYPE_REMOTE_SIM})
+ public @interface SubscriptionType {}
+
+ /**
* TelephonyProvider column name for user displayed name.
* <P>Type: TEXT (String)</P>
*/
@@ -1145,7 +1204,7 @@ public class SubscriptionManager {
}
/**
- * Get the SubscriptionInfo(s) of the currently inserted SIM(s). The records will be sorted
+ * Get the SubscriptionInfo(s) of the currently active SIM(s). The records will be sorted
* by {@link SubscriptionInfo#getSimSlotIndex} then by {@link SubscriptionInfo#getSubscriptionId}.
*
* <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
@@ -1427,21 +1486,84 @@ public class SubscriptionManager {
logd("[addSubscriptionInfoRecord]- invalid slotIndex");
}
+ addSubscriptionInfoRecord(iccId, null, slotIndex, SUBSCRIPTION_TYPE_LOCAL_SIM);
+
+ // FIXME: Always returns null?
+ return null;
+
+ }
+
+ /**
+ * Add a new SubscriptionInfo to SubscriptionInfo database if needed
+ * @param uniqueId This is the unique identifier for the subscription within the
+ * specific subscription type.
+ * @param displayName human-readable name of the device the subscription corresponds to.
+ * @param slotIndex the slot assigned to this subscription. It is ignored for subscriptionType
+ * of {@link #SUBSCRIPTION_TYPE_REMOTE_SIM}.
+ * @param subscriptionType the {@link #SUBSCRIPTION_TYPE}
+ * @hide
+ */
+ public void addSubscriptionInfoRecord(String uniqueId, String displayName, int slotIndex,
+ int subscriptionType) {
+ if (VDBG) {
+ logd("[addSubscriptionInfoRecord]+ uniqueId:" + uniqueId
+ + ", displayName:" + displayName + ", slotIndex:" + slotIndex
+ + ", subscriptionType: " + subscriptionType);
+ }
+ if (uniqueId == null) {
+ Log.e(LOG_TAG, "[addSubscriptionInfoRecord]- uniqueId is null");
+ return;
+ }
+
try {
ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
- if (iSub != null) {
- // FIXME: This returns 1 on success, 0 on error should should we return it?
- iSub.addSubInfoRecord(iccId, slotIndex);
+ if (iSub == null) {
+ Log.e(LOG_TAG, "[addSubscriptionInfoRecord]- ISub service is null");
+ return;
+ }
+ int result = iSub.addSubInfo(uniqueId, displayName, slotIndex, subscriptionType);
+ if (result < 0) {
+ Log.e(LOG_TAG, "Adding of subscription didn't succeed: error = " + result);
} else {
- logd("[addSubscriptionInfoRecord]- ISub service is null");
+ logd("successfully added new subscription");
}
} catch (RemoteException ex) {
// ignore it
}
+ }
- // FIXME: Always returns null?
- return null;
+ /**
+ * Remove SubscriptionInfo record from the SubscriptionInfo database
+ * @param uniqueId This is the unique identifier for the subscription within the specific
+ * subscription type.
+ * @param subscriptionType the {@link #SUBSCRIPTION_TYPE}
+ * @hide
+ */
+ public void removeSubscriptionInfoRecord(String uniqueId, int subscriptionType) {
+ if (VDBG) {
+ logd("[removeSubscriptionInfoRecord]+ uniqueId:" + uniqueId
+ + ", subscriptionType: " + subscriptionType);
+ }
+ if (uniqueId == null) {
+ Log.e(LOG_TAG, "[addSubscriptionInfoRecord]- uniqueId is null");
+ return;
+ }
+ try {
+ ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+ if (iSub == null) {
+ Log.e(LOG_TAG, "[removeSubscriptionInfoRecord]- ISub service is null");
+ return;
+ }
+ int result = iSub.removeSubInfo(uniqueId, subscriptionType);
+ if (result < 0) {
+ Log.e(LOG_TAG, "Removal of subscription didn't succeed: error = " + result);
+ } else {
+ logd("successfully removed subscription");
+ }
+ } catch (RemoteException ex) {
+ // ignore it
+ }
}
/**
@@ -2737,6 +2859,95 @@ public class SubscriptionManager {
}
}
+ /**
+ * Enabled or disable a subscription. This is currently used in the settings page.
+ *
+ * <p>
+ * Permissions android.Manifest.permission.MODIFY_PHONE_STATE is required
+ *
+ * @param enable whether user is turning it on or off.
+ * @param subscriptionId Subscription to be enabled or disabled.
+ * It could be a eSIM or pSIM subscription.
+ *
+ * @return whether the operation is successful.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ public boolean setSubscriptionEnabled(int subscriptionId, boolean enable) {
+ if (VDBG) {
+ logd("setSubscriptionActivated subId= " + subscriptionId + " enable " + enable);
+ }
+ try {
+ ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+ if (iSub != null) {
+ return iSub.setSubscriptionEnabled(enable, subscriptionId);
+ }
+ } catch (RemoteException ex) {
+ // ignore it
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns whether the subscription is enabled or not. This is different from activated
+ * or deactivated for two aspects. 1) For when user disables a physical subscription, we
+ * actually disable the modem because we can't switch off the subscription. 2) For eSIM,
+ * user may enable one subscription but the system may activate another temporarily. In this
+ * case, user enabled one is different from current active one.
+
+ * @param subscriptionId The subscription it asks about.
+ * @return whether it's enabled or not. {@code true} if user set this subscription enabled
+ * earlier, or user never set subscription enable / disable on this slot explicitly, and
+ * this subscription is currently active. Otherwise, it returns {@code false}.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public boolean isSubscriptionEnabled(int subscriptionId) {
+ try {
+ ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+ if (iSub != null) {
+ return iSub.isSubscriptionEnabled(subscriptionId);
+ }
+ } catch (RemoteException ex) {
+ // ignore it
+ }
+
+ return false;
+ }
+
+ /**
+ * Get which subscription is enabled on this slot. See {@link #isSubscriptionEnabled(int)}
+ * for more details.
+ *
+ * @param slotIndex which slot it asks about.
+ * @return which subscription is enabled on this slot. If there's no enabled subscription
+ * in this slot, it will return {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID}.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public int getEnabledSubscriptionId(int slotIndex) {
+ int subId = INVALID_SUBSCRIPTION_ID;
+
+ try {
+ ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+ if (iSub != null) {
+ subId = iSub.getEnabledSubscriptionId(slotIndex);
+ }
+ } catch (RemoteException ex) {
+ // ignore it
+ }
+
+ if (VDBG) logd("getEnabledSubscriptionId, subId = " + subId);
+ return subId;
+ }
+
private interface CallISubMethodHelper {
int callMethod(ISub iSub) throws RemoteException;
}
diff --git a/telephony/java/android/telephony/ims/ImsMmTelManager.java b/telephony/java/android/telephony/ims/ImsMmTelManager.java
index 9414abd98b1c..5b2e635b179f 100644
--- a/telephony/java/android/telephony/ims/ImsMmTelManager.java
+++ b/telephony/java/android/telephony/ims/ImsMmTelManager.java
@@ -86,9 +86,7 @@ public class ImsMmTelManager {
/**
* Prefer registering for IMS over IWLAN if possible if WiFi signal quality is high enough.
- * @hide
*/
- @SystemApi
public static final int WIFI_MODE_WIFI_PREFERRED = 2;
/**
diff --git a/telephony/java/android/telephony/ims/ProvisioningManager.java b/telephony/java/android/telephony/ims/ProvisioningManager.java
index d37198a3e25d..086a76546b2d 100644
--- a/telephony/java/android/telephony/ims/ProvisioningManager.java
+++ b/telephony/java/android/telephony/ims/ProvisioningManager.java
@@ -21,13 +21,17 @@ import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
+import android.annotation.WorkerThread;
import android.content.Context;
import android.os.Binder;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.telephony.CarrierConfigManager;
import android.telephony.SubscriptionManager;
import android.telephony.ims.aidl.IImsConfigCallback;
+import android.telephony.ims.feature.MmTelFeature;
import android.telephony.ims.stub.ImsConfigImplBase;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
import com.android.internal.telephony.ITelephony;
@@ -38,13 +42,68 @@ import java.util.concurrent.Executor;
* to changes in these configurations.
*
* Note: IMS provisioning keys are defined per carrier or OEM using OMA-DM or other provisioning
- * applications and may vary.
+ * applications and may vary. For compatibility purposes, the first 100 integer values used in
+ * {@link #setProvisioningIntValue(int, int)} have been reserved for existing provisioning keys
+ * previously defined in the Android framework. Some common constants have been defined in this
+ * class to make integrating with other system apps easier. USE WITH CARE!
+ *
+ * To avoid collisions, please use String based configurations when possible:
+ * {@link #setProvisioningStringValue(int, String)} and {@link #getProvisioningStringValue(int)}.
* @hide
*/
@SystemApi
public class ProvisioningManager {
/**
+ * The query from {@link #getProvisioningStringValue(int)} has resulted in an unspecified error.
+ */
+ public static final String STRING_QUERY_RESULT_ERROR_GENERIC =
+ "STRING_QUERY_RESULT_ERROR_GENERIC";
+
+ /**
+ * The query from {@link #getProvisioningStringValue(int)} has resulted in an error because the
+ * ImsService implementation was not ready for provisioning queries.
+ */
+ public static final String STRING_QUERY_RESULT_ERROR_NOT_READY =
+ "STRING_QUERY_RESULT_ERROR_NOT_READY";
+
+ /**
+ * The integer result of provisioning for the queried key is disabled.
+ */
+ public static final int PROVISIONING_VALUE_DISABLED = 0;
+
+ /**
+ * The integer result of provisioning for the queried key is enabled.
+ */
+ public static final int PROVISIONING_VALUE_ENABLED = 1;
+
+
+ /**
+ * Override the user-defined WiFi Roaming enabled setting for this subscription, defined in
+ * {@link SubscriptionManager#WFC_ROAMING_ENABLED_CONTENT_URI}, for the purposes of provisioning
+ * the subscription for WiFi Calling.
+ *
+ * @see #getProvisioningIntValue(int)
+ * @see #setProvisioningIntValue(int, int)
+ */
+ public static final int KEY_VOICE_OVER_WIFI_ROAMING_ENABLED_OVERRIDE = 26;
+
+ /**
+ * Override the user-defined WiFi mode for this subscription, defined in
+ * {@link SubscriptionManager#WFC_MODE_CONTENT_URI}, for the purposes of provisioning
+ * this subscription for WiFi Calling.
+ *
+ * Valid values for this key are:
+ * {@link ImsMmTelManager#WIFI_MODE_WIFI_ONLY},
+ * {@link ImsMmTelManager#WIFI_MODE_CELLULAR_PREFERRED}, or
+ * {@link ImsMmTelManager#WIFI_MODE_WIFI_PREFERRED}.
+ *
+ * @see #getProvisioningIntValue(int)
+ * @see #setProvisioningIntValue(int, int)
+ */
+ public static final int KEY_VOICE_OVER_WIFI_MODE_OVERRIDE = 27;
+
+ /**
* Callback for IMS provisioning changes.
*/
public static class Callback {
@@ -180,10 +239,15 @@ public class ProvisioningManager {
/**
* Query for the integer value associated with the provided key.
+ *
+ * This operation is blocking and should not be performed on the UI thread.
+ *
* @param key An integer that represents the provisioning key, which is defined by the OEM.
- * @return an integer value for the provided key.
+ * @return an integer value for the provided key, or
+ * {@link ImsConfigImplBase#CONFIG_RESULT_UNKNOWN} if the key doesn't exist.
* @throws IllegalArgumentException if the key provided was invalid.
*/
+ @WorkerThread
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public int getProvisioningIntValue(int key) {
try {
@@ -195,10 +259,16 @@ public class ProvisioningManager {
/**
* Query for the String value associated with the provided key.
- * @param key An integer that represents the provisioning key, which is defined by the OEM.
- * @return a String value for the provided key, or {@code null} if the key doesn't exist.
+ *
+ * This operation is blocking and should not be performed on the UI thread.
+ *
+ * @param key A String that represents the provisioning key, which is defined by the OEM.
+ * @return a String value for the provided key, {@code null} if the key doesn't exist, or one
+ * of the following error codes: {@link #STRING_QUERY_RESULT_ERROR_GENERIC},
+ * {@link #STRING_QUERY_RESULT_ERROR_NOT_READY}.
* @throws IllegalArgumentException if the key provided was invalid.
*/
+ @WorkerThread
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public String getProvisioningStringValue(int key) {
try {
@@ -210,10 +280,16 @@ public class ProvisioningManager {
/**
* Set the integer value associated with the provided key.
+ *
+ * This operation is blocking and should not be performed on the UI thread.
+ *
+ * Use {@link #setProvisioningStringValue(int, String)} with proper namespacing (to be defined
+ * per OEM or carrier) when possible instead to avoid key collision if needed.
* @param key An integer that represents the provisioning key, which is defined by the OEM.
* @param value a integer value for the provided key.
* @return the result of setting the configuration value.
*/
+ @WorkerThread
@RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
public @ImsConfigImplBase.SetConfigResult int setProvisioningIntValue(int key, int value) {
try {
@@ -226,10 +302,14 @@ public class ProvisioningManager {
/**
* Set the String value associated with the provided key.
*
- * @param key An integer that represents the provisioning key, which is defined by the OEM.
+ * This operation is blocking and should not be performed on the UI thread.
+ *
+ * @param key A String that represents the provisioning key, which is defined by the OEM and
+ * should be appropriately namespaced to avoid collision.
* @param value a String value for the provided key.
* @return the result of setting the configuration value.
*/
+ @WorkerThread
@RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
public @ImsConfigImplBase.SetConfigResult int setProvisioningStringValue(int key,
String value) {
@@ -240,6 +320,55 @@ public class ProvisioningManager {
}
}
+ /**
+ * Set the provisioning status for the IMS MmTel capability using the specified subscription.
+ *
+ * Provisioning may or may not be required, depending on the carrier configuration. If
+ * provisioning is not required for the carrier associated with this subscription or the device
+ * does not support the capability/technology combination specified, this operation will be a
+ * no-op.
+ *
+ * @see CarrierConfigManager#KEY_CARRIER_UT_PROVISIONING_REQUIRED_BOOL
+ * @see CarrierConfigManager#KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL
+ * @param isProvisioned true if the device is provisioned for UT over IMS, false otherwise.
+ */
+ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ public void setProvisioningStatusForCapability(
+ @MmTelFeature.MmTelCapabilities.MmTelCapability int capability,
+ @ImsRegistrationImplBase.ImsRegistrationTech int tech, boolean isProvisioned) {
+ try {
+ getITelephony().setImsProvisioningStatusForCapability(mSubId, capability, tech,
+ isProvisioned);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * Get the provisioning status for the IMS MmTel capability specified.
+ *
+ * If provisioning is not required for the queried
+ * {@link MmTelFeature.MmTelCapabilities.MmTelCapability} and
+ * {@link ImsRegistrationImplBase.ImsRegistrationTech} combination specified, this method will
+ * always return {@code true}.
+ *
+ * @see CarrierConfigManager#KEY_CARRIER_UT_PROVISIONING_REQUIRED_BOOL
+ * @see CarrierConfigManager#KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL
+ * @return true if the device is provisioned for the capability or does not require
+ * provisioning, false if the capability does require provisioning and has not been
+ * provisioned yet.
+ */
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public boolean getProvisioningStatusForCapability(
+ @MmTelFeature.MmTelCapabilities.MmTelCapability int capability,
+ @ImsRegistrationImplBase.ImsRegistrationTech int tech) {
+ try {
+ return getITelephony().getImsProvisioningStatusForCapability(mSubId, capability, tech);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
private static SubscriptionManager getSubscriptionManager(Context context) {
SubscriptionManager manager = context.getSystemService(SubscriptionManager.class);
if (manager == null) {
diff --git a/telephony/java/android/telephony/ims/feature/CapabilityChangeRequest.java b/telephony/java/android/telephony/ims/feature/CapabilityChangeRequest.java
index 7c793a5c18ac..1ee85633c6dc 100644
--- a/telephony/java/android/telephony/ims/feature/CapabilityChangeRequest.java
+++ b/telephony/java/android/telephony/ims/feature/CapabilityChangeRequest.java
@@ -97,6 +97,13 @@ public final class CapabilityChangeRequest implements Parcelable {
public @ImsRegistrationImplBase.ImsRegistrationTech int getRadioTech() {
return radioTech;
}
+
+ @Override
+ public String toString() {
+ return "CapabilityPair{"
+ + "mCapability=" + mCapability
+ + ", radioTech=" + radioTech + '}';
+ }
}
// Pair contains <radio tech, mCapability>
@@ -212,6 +219,13 @@ public final class CapabilityChangeRequest implements Parcelable {
}
}
+ @Override
+ public String toString() {
+ return "CapabilityChangeRequest{"
+ + "mCapabilitiesToEnable=" + mCapabilitiesToEnable
+ + ", mCapabilitiesToDisable=" + mCapabilitiesToDisable + '}';
+ }
+
/**
* @hide
*/
diff --git a/telephony/java/com/android/ims/ImsConfig.java b/telephony/java/com/android/ims/ImsConfig.java
index 71a21743a449..4fc6a19d1f38 100644
--- a/telephony/java/com/android/ims/ImsConfig.java
+++ b/telephony/java/com/android/ims/ImsConfig.java
@@ -277,12 +277,14 @@ public class ImsConfig {
* Wi-Fi calling roaming status.
* Value is in Integer format. ON (1), OFF(0).
*/
- public static final int VOICE_OVER_WIFI_ROAMING = 26;
+ public static final int VOICE_OVER_WIFI_ROAMING =
+ ProvisioningManager.KEY_VOICE_OVER_WIFI_ROAMING_ENABLED_OVERRIDE;
/**
* Wi-Fi calling modem - WfcModeFeatureValueConstants.
* Value is in Integer format.
*/
- public static final int VOICE_OVER_WIFI_MODE = 27;
+ public static final int VOICE_OVER_WIFI_MODE =
+ ProvisioningManager.KEY_VOICE_OVER_WIFI_MODE_OVERRIDE;
/**
* VOLTE Status for voice over wifi status of Enabled (1), or Disabled (0).
* Value is in Integer format.
diff --git a/telephony/java/com/android/internal/telephony/ISub.aidl b/telephony/java/com/android/internal/telephony/ISub.aidl
index 577ddbda50fa..a49d2d976d16 100755
--- a/telephony/java/com/android/internal/telephony/ISub.aidl
+++ b/telephony/java/com/android/internal/telephony/ISub.aidl
@@ -52,8 +52,8 @@ interface ISub {
/**
* Get the active SubscriptionInfo associated with the slotIndex
* @param slotIndex the slot which the subscription is inserted
- * @param callingPackage The package maing the call.
- * @return SubscriptionInfo, maybe null if its not active
+ * @param callingPackage The package making the call.
+ * @return SubscriptionInfo, null for Remote-SIMs or non-active slotIndex.
*/
SubscriptionInfo getActiveSubscriptionInfoForSimSlotIndex(int slotIndex, String callingPackage);
@@ -115,6 +115,26 @@ interface ISub {
int addSubInfoRecord(String iccId, int slotIndex);
/**
+ * Add a new subscription info record, if needed
+ * @param uniqueId This is the unique identifier for the subscription within the specific
+ * subscription type.
+ * @param displayName human-readable name of the device the subscription corresponds to.
+ * @param slotIndex the slot assigned to this device
+ * @param subscriptionType the type of subscription to be added.
+ * @return 0 if success, < 0 on error.
+ */
+ int addSubInfo(String uniqueId, String displayName, int slotIndex, int subscriptionType);
+
+ /**
+ * Remove subscription info record for the given device.
+ * @param uniqueId This is the unique identifier for the subscription within the specific
+ * subscription type.
+ * @param subscriptionType the type of subscription to be removed
+ * @return 0 if success, < 0 on error.
+ */
+ int removeSubInfo(String uniqueId, int subscriptionType);
+
+ /**
* Set SIM icon tint color by simInfo index
* @param tint the icon tint color of the SIM
* @param subId the unique SubscriptionInfo index in database
@@ -256,6 +276,11 @@ interface ISub {
String getSubscriptionProperty(int subId, String propKey, String callingPackage);
+ boolean setSubscriptionEnabled(boolean enable, int subId);
+
+ boolean isSubscriptionEnabled(int subId);
+
+ int getEnabledSubscriptionId(int slotIndex);
/**
* Get the SIM state for the slot index
* @return SIM state as the ordinal of IccCardConstants.State
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 9cc173cfcdd6..8237d39c4c3d 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -1765,6 +1765,24 @@ interface ITelephony {
void unregisterImsProvisioningChangedCallback(int subId, IImsConfigCallback callback);
/**
+ * Set the provisioning status for the IMS MmTel capability using the specified subscription.
+ */
+ void setImsProvisioningStatusForCapability(int subId, int capability, int tech,
+ boolean isProvisioned);
+
+ /**
+ * Get the provisioning status for the IMS MmTel capability specified.
+ */
+ boolean getImsProvisioningStatusForCapability(int subId, int capability, int tech);
+
+ /** Is the capability and tech flagged as provisioned in the cache */
+ boolean isMmTelCapabilityProvisionedInCache(int subId, int capability, int tech);
+
+ /** Set the provisioning for the capability and tech in the cache */
+ void cacheMmTelCapabilityProvisioning(int subId, int capability, int tech,
+ boolean isProvisioned);
+
+ /**
* Return an integer containing the provisioning value for the specified provisioning key.
*/
int getImsProvisioningInt(int subId, int key);
diff --git a/telephony/java/com/android/internal/telephony/TelephonyPermissions.java b/telephony/java/com/android/internal/telephony/TelephonyPermissions.java
index 0edc0026722b..c76d153c6112 100644
--- a/telephony/java/com/android/internal/telephony/TelephonyPermissions.java
+++ b/telephony/java/com/android/internal/telephony/TelephonyPermissions.java
@@ -284,10 +284,6 @@ public final class TelephonyPermissions {
*/
private static boolean reportAccessDeniedToReadIdentifiers(Context context, int subId, int pid,
int uid, String callingPackage, String message) {
- // If the device identifier check is enabled then enforce the new access requirements for
- // both 1P and 3P apps.
- boolean enableDeviceIdentifierCheck = Settings.Global.getInt(context.getContentResolver(),
- Settings.Global.PRIVILEGED_DEVICE_IDENTIFIER_CHECK_ENABLED, 0) == 1;
// Check if the application is a 3P app; if so then a separate setting is required to relax
// the check to begin flagging problems with 3P apps early.
boolean relax3PDeviceIdentifierCheck = Settings.Global.getInt(context.getContentResolver(),
@@ -300,6 +296,11 @@ public final class TelephonyPermissions {
context.getContentResolver(),
Settings.Global.PRIVILEGED_DEVICE_IDENTIFIER_NON_PRIV_CHECK_RELAXED, 0) == 1;
boolean isNonPrivApp = false;
+ // Similar to above support relaxing the check for privileged apps while still enforcing it
+ // for non-privileged and 3P apps.
+ boolean relaxPrivDeviceIdentifierCheck = Settings.Global.getInt(
+ context.getContentResolver(),
+ Settings.Global.PRIVILEGED_DEVICE_IDENTIFIER_PRIV_CHECK_RELAXED, 0) == 1;
ApplicationInfo callingPackageInfo = null;
try {
callingPackageInfo = context.getPackageManager().getApplicationInfo(callingPackage, 0);
@@ -315,37 +316,28 @@ public final class TelephonyPermissions {
Log.e(LOG_TAG, "Exception caught obtaining package info for package " + callingPackage,
e);
}
- Log.wtf(LOG_TAG, "reportAccessDeniedToReadIdentifiers:" + callingPackage + ":" + message
- + ":is3PApp=" + is3PApp + ":isNonPrivApp=" + isNonPrivApp);
- // The new Q restrictions for device identifier access will be enforced if any of the
- // following are true:
- // - The PRIVILEGED_DEVICE_IDENTIFIER_CHECK_ENABLED setting has been set.
- // - The app requesting a device identifier is not a preloaded app (3P), and the
- // PRIVILEGED_DEVICE_IDENTIFIER_3P_CHECK_RELAXED setting has not been set.
- // - The app requesting a device identifier is a preloaded app but is not a privileged app,
- // and the PRIVILEGED_DEVICE_IDENTIFIER_NON_PRIV_CHECK_RELAXED setting has not been set.
- if (enableDeviceIdentifierCheck
+ // The new Q restrictions for device identifier access will be enforced for all apps with
+ // settings to individually disable the new restrictions for privileged, preloaded
+ // non-privileged, and 3P apps.
+ if ((!is3PApp && !isNonPrivApp && !relaxPrivDeviceIdentifierCheck)
|| (is3PApp && !relax3PDeviceIdentifierCheck)
|| (isNonPrivApp && !relaxNonPrivDeviceIdentifierCheck)) {
- boolean targetQBehaviorDisabled = Settings.Global.getInt(context.getContentResolver(),
- Settings.Global.PRIVILEGED_DEVICE_IDENTIFIER_TARGET_Q_BEHAVIOR_ENABLED, 0) == 0;
- if (callingPackage != null) {
- // if the target SDK is pre-Q or the target Q behavior is disabled then check if
- // the calling package would have previously had access to device identifiers.
- if (callingPackageInfo != null && (
- callingPackageInfo.targetSdkVersion < Build.VERSION_CODES.Q
- || targetQBehaviorDisabled)) {
- if (context.checkPermission(
- android.Manifest.permission.READ_PHONE_STATE,
- pid,
- uid) == PackageManager.PERMISSION_GRANTED) {
- return false;
- }
- if (SubscriptionManager.isValidSubscriptionId(subId)
- && getCarrierPrivilegeStatus(TELEPHONY_SUPPLIER, subId, uid)
- == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) {
- return false;
- }
+ Log.wtf(LOG_TAG, "reportAccessDeniedToReadIdentifiers:" + callingPackage + ":" + message
+ + ":is3PApp=" + is3PApp + ":isNonPrivApp=" + isNonPrivApp);
+ // if the target SDK is pre-Q then check if the calling package would have previously
+ // had access to device identifiers.
+ if (callingPackageInfo != null && (
+ callingPackageInfo.targetSdkVersion < Build.VERSION_CODES.Q)) {
+ if (context.checkPermission(
+ android.Manifest.permission.READ_PHONE_STATE,
+ pid,
+ uid) == PackageManager.PERMISSION_GRANTED) {
+ return false;
+ }
+ if (SubscriptionManager.isValidSubscriptionId(subId)
+ && getCarrierPrivilegeStatus(TELEPHONY_SUPPLIER, subId, uid)
+ == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) {
+ return false;
}
}
throw new SecurityException(message + ": The user " + uid
diff --git a/tests/RollbackTest/Android.mk b/tests/RollbackTest/Android.mk
index 34aa258bf465..780bb24e437b 100644
--- a/tests/RollbackTest/Android.mk
+++ b/tests/RollbackTest/Android.mk
@@ -36,6 +36,17 @@ LOCAL_PACKAGE_NAME := RollbackTestAppAv2
include $(BUILD_PACKAGE)
ROLLBACK_TEST_APP_AV2 := $(LOCAL_INSTALLED_MODULE)
+# RollbackTestAppACrashingV2.apk
+include $(CLEAR_VARS)
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+LOCAL_SDK_VERSION := current
+LOCAL_SRC_FILES := $(call all-java-files-under, TestApp/src)
+LOCAL_MANIFEST_FILE := TestApp/ACrashingV2.xml
+LOCAL_PACKAGE_NAME := RollbackTestAppACrashingV2
+include $(BUILD_PACKAGE)
+ROLLBACK_TEST_APP_A_CRASHING_V2 := $(LOCAL_INSTALLED_MODULE)
+
# RollbackTestAppBv1.apk
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
@@ -68,6 +79,7 @@ LOCAL_COMPATIBILITY_SUITE := general-tests
LOCAL_JAVA_RESOURCE_FILES := \
$(ROLLBACK_TEST_APP_AV1) \
$(ROLLBACK_TEST_APP_AV2) \
+ $(ROLLBACK_TEST_APP_A_CRASHING_V2) \
$(ROLLBACK_TEST_APP_BV1) \
$(ROLLBACK_TEST_APP_BV2)
LOCAL_SDK_VERSION := system_current
@@ -77,5 +89,6 @@ include $(BUILD_PACKAGE)
# Clean up local variables
ROLLBACK_TEST_APP_AV1 :=
ROLLBACK_TEST_APP_AV2 :=
+ROLLBACK_TEST_APP_A_CRASHING_V2 :=
ROLLBACK_TEST_APP_BV1 :=
ROLLBACK_TEST_APP_BV2 :=
diff --git a/tests/RollbackTest/TestApp/ACrashingV2.xml b/tests/RollbackTest/TestApp/ACrashingV2.xml
new file mode 100644
index 000000000000..5708d2385f01
--- /dev/null
+++ b/tests/RollbackTest/TestApp/ACrashingV2.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.tests.rollback.testapp.A"
+ android:versionCode="2"
+ android:versionName="2.0" >
+
+
+ <uses-sdk android:minSdkVersion="19" />
+
+ <application android:label="Rollback Test App A v2">
+ <meta-data android:name="version" android:value="2" />
+ <receiver android:name="com.android.tests.rollback.testapp.ProcessUserData"
+ android:exported="true" />
+ <activity android:name="com.android.tests.rollback.testapp.CrashingMainActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.DEFAULT"/>
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
diff --git a/cmds/statsd/src/external/ResourceThermalManagerPuller.h b/tests/RollbackTest/TestApp/src/com/android/tests/rollback/testapp/CrashingMainActivity.java
index 531379226bb0..02a439b5dd69 100644
--- a/cmds/statsd/src/external/ResourceThermalManagerPuller.h
+++ b/tests/RollbackTest/TestApp/src/com/android/tests/rollback/testapp/CrashingMainActivity.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,26 +14,20 @@
* limitations under the License.
*/
-#pragma once
+package com.android.tests.rollback.testapp;
-#include <utils/String16.h>
-#include "StatsPuller.h"
-
-namespace android {
-namespace os {
-namespace statsd {
+import android.app.Activity;
+import android.os.Bundle;
/**
- * Reads IThermal.hal
+ * A crashing test app for testing apk rollback support.
*/
-class ResourceThermalManagerPuller : public StatsPuller {
-public:
- ResourceThermalManagerPuller();
+public class CrashingMainActivity extends Activity {
-private:
- bool PullInternal(vector<std::shared_ptr<LogEvent>>* data) override;
-};
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
-} // namespace statsd
-} // namespace os
-} // namespace android \ No newline at end of file
+ throw new RuntimeException("Intended force crash");
+ }
+}
diff --git a/tests/RollbackTest/src/com/android/tests/rollback/RollbackTest.java b/tests/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
index 9d67cea05fc8..13ac4f09dd86 100644
--- a/tests/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
+++ b/tests/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
@@ -38,6 +38,7 @@ import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@@ -57,6 +58,7 @@ public class RollbackTest {
private static final String TEST_APP_A = "com.android.tests.rollback.testapp.A";
private static final String TEST_APP_B = "com.android.tests.rollback.testapp.B";
+ private static final String INSTRUMENTED_APP = "com.android.tests.rollback";
/**
* Test basic rollbacks.
@@ -663,4 +665,63 @@ public class RollbackTest {
assertEquals(packageName, info.getVersionRolledBackTo().getPackageName());
assertEquals(versionRolledBackTo, info.getVersionRolledBackTo().getLongVersionCode());
}
+
+ // TODO(zezeozue): Stop ignoring after fixing race between rolling back and testing version
+ /**
+ * Test bad update automatic rollback.
+ */
+ @Ignore("Flaky")
+ @Test
+ public void testBadUpdateRollback() throws Exception {
+ try {
+ RollbackTestUtils.adoptShellPermissionIdentity(
+ Manifest.permission.INSTALL_PACKAGES,
+ Manifest.permission.DELETE_PACKAGES,
+ Manifest.permission.MANAGE_ROLLBACKS);
+ RollbackManager rm = RollbackTestUtils.getRollbackManager();
+
+ // Prep installation of the test apps.
+ RollbackTestUtils.uninstall(TEST_APP_A);
+ RollbackTestUtils.install("RollbackTestAppAv1.apk", false);
+ RollbackTestUtils.install("RollbackTestAppACrashingV2.apk", true);
+ assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
+
+ RollbackTestUtils.uninstall(TEST_APP_B);
+ RollbackTestUtils.install("RollbackTestAppBv1.apk", false);
+ RollbackTestUtils.install("RollbackTestAppBv2.apk", true);
+ assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_B));
+
+ // Both test apps should now be available for rollback, and the
+ // targetPackage returned for rollback should be correct.
+ // TODO: See if there is a way to remove this race condition
+ // between when the app is installed and when the rollback
+ // is made available.
+ Thread.sleep(1000);
+ RollbackInfo rollbackA = rm.getAvailableRollback(TEST_APP_A);
+ assertNotNull(rollbackA);
+ assertEquals(TEST_APP_A, rollbackA.targetPackage.getPackageName());
+
+ RollbackInfo rollbackB = rm.getAvailableRollback(TEST_APP_B);
+ assertNotNull(rollbackB);
+ assertEquals(TEST_APP_B, rollbackB.targetPackage.getPackageName());
+
+ // Start apps PackageWatchdog#TRIGGER_FAILURE_COUNT times so TEST_APP_A crashes
+ for (int i = 0; i < 5; i++) {
+ RollbackTestUtils.launchPackage(TEST_APP_A);
+ Thread.sleep(1000);
+ }
+ Thread.sleep(1000);
+
+ // TEST_APP_A is automatically rolled back by the RollbackPackageHealthObserver
+ assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
+ // Instrumented app is still the package installer
+ Context context = InstrumentationRegistry.getContext();
+ String installer = context.getPackageManager().getInstallerPackageName(TEST_APP_A);
+ assertEquals(INSTRUMENTED_APP, installer);
+ // TEST_APP_B is untouched
+ assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_B));
+ } finally {
+ RollbackTestUtils.dropShellPermissionIdentity();
+ }
+ }
}
diff --git a/tests/RollbackTest/src/com/android/tests/rollback/RollbackTestUtils.java b/tests/RollbackTest/src/com/android/tests/rollback/RollbackTestUtils.java
index fbc3d8f1cd34..edb13556b8fc 100644
--- a/tests/RollbackTest/src/com/android/tests/rollback/RollbackTestUtils.java
+++ b/tests/RollbackTest/src/com/android/tests/rollback/RollbackTestUtils.java
@@ -135,6 +135,17 @@ class RollbackTestUtils {
assertStatusSuccess(LocalIntentSender.getIntentSenderResult());
}
+ /** Launches {@code packageName} with {@link Intent#ACTION_MAIN}. */
+ static void launchPackage(String packageName)
+ throws InterruptedException, IOException {
+ Context context = InstrumentationRegistry.getContext();
+ Intent intent = new Intent(Intent.ACTION_MAIN);
+ intent.setPackage(packageName);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ intent.addCategory(Intent.CATEGORY_LAUNCHER);
+ context.startActivity(intent);
+ }
+
/**
* Installs the apks with the given resource names as an atomic set.
*
diff --git a/tests/UsageStatsTest/AndroidManifest.xml b/tests/UsageStatsTest/AndroidManifest.xml
index 4b1c1bd69920..fefd99394a87 100644
--- a/tests/UsageStatsTest/AndroidManifest.xml
+++ b/tests/UsageStatsTest/AndroidManifest.xml
@@ -11,6 +11,7 @@
<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />
<uses-permission android:name="android.permission.OBSERVE_APP_USAGE" />
+ <uses-permission android:name="android.permission.SUSPEND_APPS" />
<application android:label="Usage Access Test">
<activity android:name=".UsageStatsActivity"
diff --git a/tests/UsageStatsTest/res/menu/main.xml b/tests/UsageStatsTest/res/menu/main.xml
index 612267c85b1b..272e0f4e1f54 100644
--- a/tests/UsageStatsTest/res/menu/main.xml
+++ b/tests/UsageStatsTest/res/menu/main.xml
@@ -6,4 +6,6 @@
android:title="Call isAppInactive()"/>
<item android:id="@+id/set_app_limit"
android:title="Set App Limit" />
+ <item android:id="@+id/set_app_usage_limit"
+ android:title="Set App Usage Limit" />
</menu>
diff --git a/tests/UsageStatsTest/src/com/android/tests/usagestats/UsageStatsActivity.java b/tests/UsageStatsTest/src/com/android/tests/usagestats/UsageStatsActivity.java
index 3c628f6e0013..0105893adf9e 100644
--- a/tests/UsageStatsTest/src/com/android/tests/usagestats/UsageStatsActivity.java
+++ b/tests/UsageStatsTest/src/com/android/tests/usagestats/UsageStatsActivity.java
@@ -21,6 +21,8 @@ import android.app.ListActivity;
import android.app.PendingIntent;
import android.app.usage.UsageStats;
import android.app.usage.UsageStatsManager;
+import android.content.ClipData;
+import android.content.ClipboardManager;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
@@ -49,6 +51,8 @@ public class UsageStatsActivity extends ListActivity {
private static final long USAGE_STATS_PERIOD = 1000 * 60 * 60 * 24 * 14;
private static final String EXTRA_KEY_TIMEOUT = "com.android.tests.usagestats.extra.TIMEOUT";
private UsageStatsManager mUsageStatsManager;
+ private ClipboardManager mClipboard;
+ private ClipData mClip;
private Adapter mAdapter;
private Comparator<UsageStats> mComparator = new Comparator<UsageStats>() {
@Override
@@ -61,6 +65,7 @@ public class UsageStatsActivity extends ListActivity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mUsageStatsManager = (UsageStatsManager) getSystemService(Context.USAGE_STATS_SERVICE);
+ mClipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
mAdapter = new Adapter();
setListAdapter(mAdapter);
Bundle extras = getIntent().getExtras();
@@ -98,6 +103,8 @@ public class UsageStatsActivity extends ListActivity {
case R.id.set_app_limit:
callSetAppLimit();
return true;
+ case R.id.set_app_usage_limit:
+ callSetAppUsageLimit();
default:
return super.onOptionsItemSelected(item);
}
@@ -170,6 +177,40 @@ public class UsageStatsActivity extends ListActivity {
builder.show();
}
+ private void callSetAppUsageLimit() {
+ final AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ builder.setTitle("Enter package name");
+ final EditText input = new EditText(this);
+ input.setInputType(InputType.TYPE_CLASS_TEXT);
+ input.setHint("com.android.tests.usagestats");
+ builder.setView(input);
+
+ builder.setPositiveButton("OK", new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ final String packageName = input.getText().toString().trim();
+ if (!TextUtils.isEmpty(packageName)) {
+ String[] packages = packageName.split(",");
+ Intent intent = new Intent(Intent.ACTION_MAIN);
+ intent.setClass(UsageStatsActivity.this, UsageStatsActivity.class);
+ intent.setPackage(getPackageName());
+ intent.putExtra(EXTRA_KEY_TIMEOUT, true);
+ mUsageStatsManager.registerAppUsageLimitObserver(1, packages,
+ 60, TimeUnit.SECONDS, PendingIntent.getActivity(UsageStatsActivity.this,
+ 1, intent, 0));
+ }
+ }
+ });
+ builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ dialog.cancel();
+ }
+ });
+
+ builder.show();
+ }
+
private void showInactive(String packageName) {
final AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setMessage(
@@ -232,6 +273,21 @@ public class UsageStatsActivity extends ListActivity {
holder.packageName.setText(mStats.get(position).getPackageName());
holder.usageTime.setText(DateUtils.formatDuration(
mStats.get(position).getTotalTimeInForeground()));
+
+ //copy package name to the clipboard for convenience
+ holder.packageName.setOnLongClickListener(new View.OnLongClickListener() {
+ @Override
+ public boolean onLongClick(View v) {
+ String text = holder.packageName.getText().toString();
+ mClip = ClipData.newPlainText("package_name", text);
+ mClipboard.setPrimaryClip(mClip);
+
+ Toast.makeText(getApplicationContext(), "package name copied to clipboard",
+ Toast.LENGTH_SHORT).show();
+ return true;
+ }
+ });
+
return convertView;
}
}
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index dda44819e664..923c7dd5fb94 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -107,6 +107,8 @@ import android.net.INetworkPolicyManager;
import android.net.INetworkStatsService;
import android.net.InterfaceConfiguration;
import android.net.IpPrefix;
+import android.net.IpSecManager;
+import android.net.IpSecManager.UdpEncapsulationSocket;
import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.MatchAllNetworkSpecifier;
@@ -121,7 +123,9 @@ import android.net.NetworkRequest;
import android.net.NetworkSpecifier;
import android.net.NetworkStack;
import android.net.NetworkUtils;
+import android.net.ProxyInfo;
import android.net.RouteInfo;
+import android.net.SocketKeepalive;
import android.net.UidRange;
import android.net.metrics.IpConnectivityLog;
import android.net.shared.NetworkMonitorUtils;
@@ -158,6 +162,7 @@ import com.android.server.connectivity.DefaultNetworkMetrics;
import com.android.server.connectivity.IpConnectivityMetrics;
import com.android.server.connectivity.MockableSystemProperties;
import com.android.server.connectivity.Nat464Xlat;
+import com.android.server.connectivity.ProxyTracker;
import com.android.server.connectivity.Tethering;
import com.android.server.connectivity.Vpn;
import com.android.server.net.NetworkPinner;
@@ -186,6 +191,8 @@ import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -402,8 +409,8 @@ public class ConnectivityServiceTest {
private final ConditionVariable mPreventReconnectReceived = new ConditionVariable();
private int mScore;
private NetworkAgent mNetworkAgent;
- private int mStartKeepaliveError = PacketKeepalive.ERROR_HARDWARE_UNSUPPORTED;
- private int mStopKeepaliveError = PacketKeepalive.NO_KEEPALIVE;
+ private int mStartKeepaliveError = SocketKeepalive.ERROR_HARDWARE_UNSUPPORTED;
+ private int mStopKeepaliveError = SocketKeepalive.NO_KEEPALIVE;
private Integer mExpectedKeepaliveSlot = null;
// Contains the redirectUrl from networkStatus(). Before reading, wait for
// mNetworkStatusReceived.
@@ -1002,6 +1009,11 @@ public class ConnectivityServiceTest {
}
@Override
+ protected ProxyTracker makeProxyTracker() {
+ return mock(ProxyTracker.class);
+ }
+
+ @Override
protected int reserveNetId() {
while (true) {
final int netId = super.reserveNetId();
@@ -1023,6 +1035,11 @@ public class ConnectivityServiceTest {
}
}
+ @Override
+ protected boolean queryUserAccess(int uid, int netId) {
+ return true;
+ }
+
public Nat464Xlat getNat464Xlat(MockNetworkAgent mna) {
return getNetworkAgentInfoForNetwork(mna.getNetwork()).clatd;
}
@@ -3548,6 +3565,80 @@ public class ConnectivityServiceTest {
}
}
+ private static class TestSocketKeepaliveCallback extends SocketKeepalive.Callback {
+
+ public enum CallbackType { ON_STARTED, ON_STOPPED, ON_ERROR };
+
+ private class CallbackValue {
+ public CallbackType callbackType;
+ public int error;
+
+ CallbackValue(CallbackType type) {
+ this.callbackType = type;
+ this.error = SocketKeepalive.SUCCESS;
+ assertTrue("onError callback must have error", type != CallbackType.ON_ERROR);
+ }
+
+ CallbackValue(CallbackType type, int error) {
+ this.callbackType = type;
+ this.error = error;
+ assertEquals("error can only be set for onError", type, CallbackType.ON_ERROR);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ return o instanceof CallbackValue
+ && this.callbackType == ((CallbackValue) o).callbackType
+ && this.error == ((CallbackValue) o).error;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("%s(%s, %d)", getClass().getSimpleName(), callbackType,
+ error);
+ }
+ }
+
+ private LinkedBlockingQueue<CallbackValue> mCallbacks = new LinkedBlockingQueue<>();
+
+ @Override
+ public void onStarted() {
+ mCallbacks.add(new CallbackValue(CallbackType.ON_STARTED));
+ }
+
+ @Override
+ public void onStopped() {
+ mCallbacks.add(new CallbackValue(CallbackType.ON_STOPPED));
+ }
+
+ @Override
+ public void onError(int error) {
+ mCallbacks.add(new CallbackValue(CallbackType.ON_ERROR, error));
+ }
+
+ private void expectCallback(CallbackValue callbackValue) {
+ try {
+ assertEquals(
+ callbackValue,
+ mCallbacks.poll(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ } catch (InterruptedException e) {
+ fail(callbackValue.callbackType + " callback not seen after " + TIMEOUT_MS + " ms");
+ }
+ }
+
+ public void expectStarted() {
+ expectCallback(new CallbackValue(CallbackType.ON_STARTED));
+ }
+
+ public void expectStopped() {
+ expectCallback(new CallbackValue(CallbackType.ON_STOPPED));
+ }
+
+ public void expectError(int error) {
+ expectCallback(new CallbackValue(CallbackType.ON_ERROR, error));
+ }
+ }
+
private Network connectKeepaliveNetwork(LinkProperties lp) {
// Ensure the network is disconnected before we do anything.
if (mWiFiNetworkAgent != null) {
@@ -3695,6 +3786,145 @@ public class ConnectivityServiceTest {
}
@Test
+ public void testNattSocketKeepalives() throws Exception {
+ // TODO: 1. Move this outside of ConnectivityServiceTest.
+ // 2. Add helper function to test against newSingleThreadExecutor as well as inline
+ // executor.
+ // 3. Make test to verify that Nat-T keepalive socket is created by IpSecService.
+ final int srcPort = 12345;
+ final InetAddress myIPv4 = InetAddress.getByName("192.0.2.129");
+ final InetAddress notMyIPv4 = InetAddress.getByName("192.0.2.35");
+ final InetAddress myIPv6 = InetAddress.getByName("2001:db8::1");
+ final InetAddress dstIPv4 = InetAddress.getByName("8.8.8.8");
+ final InetAddress dstIPv6 = InetAddress.getByName("2001:4860:4860::8888");
+
+ final int validKaInterval = 15;
+ final int invalidKaInterval = 9;
+
+ final IpSecManager mIpSec = (IpSecManager) mContext.getSystemService(Context.IPSEC_SERVICE);
+ final UdpEncapsulationSocket testSocket = mIpSec.openUdpEncapsulationSocket(srcPort);
+
+ final Executor executor = Executors.newSingleThreadExecutor();
+
+ LinkProperties lp = new LinkProperties();
+ lp.setInterfaceName("wlan12");
+ lp.addLinkAddress(new LinkAddress(myIPv6, 64));
+ lp.addLinkAddress(new LinkAddress(myIPv4, 25));
+ lp.addRoute(new RouteInfo(InetAddress.getByName("fe80::1234")));
+ lp.addRoute(new RouteInfo(InetAddress.getByName("192.0.2.254")));
+
+ Network notMyNet = new Network(61234);
+ Network myNet = connectKeepaliveNetwork(lp);
+
+ TestSocketKeepaliveCallback callback = new TestSocketKeepaliveCallback();
+ SocketKeepalive ka;
+
+ // Attempt to start keepalives with invalid parameters and check for errors.
+ // Invalid network.
+ ka = mCm.createSocketKeepalive(notMyNet, testSocket, myIPv4, dstIPv4, executor, callback);
+ ka.start(validKaInterval);
+ callback.expectError(SocketKeepalive.ERROR_INVALID_NETWORK);
+
+ // Invalid interval.
+ ka = mCm.createSocketKeepalive(myNet, testSocket, myIPv4, dstIPv4, executor, callback);
+ ka.start(invalidKaInterval);
+ callback.expectError(SocketKeepalive.ERROR_INVALID_INTERVAL);
+
+ // Invalid destination.
+ ka = mCm.createSocketKeepalive(myNet, testSocket, myIPv4, dstIPv6, executor, callback);
+ ka.start(validKaInterval);
+ callback.expectError(SocketKeepalive.ERROR_INVALID_IP_ADDRESS);
+
+ // Invalid source;
+ ka = mCm.createSocketKeepalive(myNet, testSocket, myIPv6, dstIPv4, executor, callback);
+ ka.start(validKaInterval);
+ callback.expectError(SocketKeepalive.ERROR_INVALID_IP_ADDRESS);
+
+ // NAT-T is only supported for IPv4.
+ ka = mCm.createSocketKeepalive(myNet, testSocket, myIPv6, dstIPv6, executor, callback);
+ ka.start(validKaInterval);
+ callback.expectError(SocketKeepalive.ERROR_INVALID_IP_ADDRESS);
+
+ // Sanity check before testing started keepalive.
+ ka = mCm.createSocketKeepalive(myNet, testSocket, myIPv4, dstIPv4, executor, callback);
+ ka.start(validKaInterval);
+ callback.expectError(SocketKeepalive.ERROR_HARDWARE_UNSUPPORTED);
+
+ // Check that a started keepalive can be stopped.
+ mWiFiNetworkAgent.setStartKeepaliveError(SocketKeepalive.SUCCESS);
+ ka = mCm.createSocketKeepalive(myNet, testSocket, myIPv4, dstIPv4, executor, callback);
+ ka.start(validKaInterval);
+ callback.expectStarted();
+ mWiFiNetworkAgent.setStopKeepaliveError(SocketKeepalive.SUCCESS);
+ ka.stop();
+ callback.expectStopped();
+
+ // Check that deleting the IP address stops the keepalive.
+ LinkProperties bogusLp = new LinkProperties(lp);
+ ka = mCm.createSocketKeepalive(myNet, testSocket, myIPv4, dstIPv4, executor, callback);
+ ka.start(validKaInterval);
+ callback.expectStarted();
+ bogusLp.removeLinkAddress(new LinkAddress(myIPv4, 25));
+ bogusLp.addLinkAddress(new LinkAddress(notMyIPv4, 25));
+ mWiFiNetworkAgent.sendLinkProperties(bogusLp);
+ callback.expectError(SocketKeepalive.ERROR_INVALID_IP_ADDRESS);
+ mWiFiNetworkAgent.sendLinkProperties(lp);
+
+ // Check that a started keepalive is stopped correctly when the network disconnects.
+ ka = mCm.createSocketKeepalive(myNet, testSocket, myIPv4, dstIPv4, executor, callback);
+ ka.start(validKaInterval);
+ callback.expectStarted();
+ mWiFiNetworkAgent.disconnect();
+ waitFor(mWiFiNetworkAgent.getDisconnectedCV());
+ callback.expectError(SocketKeepalive.ERROR_INVALID_NETWORK);
+
+ // ... and that stopping it after that has no adverse effects.
+ waitForIdle();
+ final Network myNetAlias = myNet;
+ assertNull(mCm.getNetworkCapabilities(myNetAlias));
+ ka.stop();
+
+ // Reconnect.
+ myNet = connectKeepaliveNetwork(lp);
+ mWiFiNetworkAgent.setStartKeepaliveError(SocketKeepalive.SUCCESS);
+
+ // Check things work as expected when the keepalive is stopped and the network disconnects.
+ ka = mCm.createSocketKeepalive(myNet, testSocket, myIPv4, dstIPv4, executor, callback);
+ ka.start(validKaInterval);
+ callback.expectStarted();
+ ka.stop();
+ mWiFiNetworkAgent.disconnect();
+ waitFor(mWiFiNetworkAgent.getDisconnectedCV());
+ waitForIdle();
+ callback.expectStopped();
+
+ // Reconnect.
+ myNet = connectKeepaliveNetwork(lp);
+ mWiFiNetworkAgent.setStartKeepaliveError(SocketKeepalive.SUCCESS);
+
+ // Check that keepalive slots start from 1 and increment. The first one gets slot 1.
+ mWiFiNetworkAgent.setExpectedKeepaliveSlot(1);
+ ka = mCm.createSocketKeepalive(myNet, testSocket, myIPv4, dstIPv4, executor, callback);
+ ka.start(validKaInterval);
+ callback.expectStarted();
+
+ // The second one gets slot 2.
+ mWiFiNetworkAgent.setExpectedKeepaliveSlot(2);
+ final UdpEncapsulationSocket testSocket2 = mIpSec.openUdpEncapsulationSocket(6789);
+ TestSocketKeepaliveCallback callback2 = new TestSocketKeepaliveCallback();
+ SocketKeepalive ka2 =
+ mCm.createSocketKeepalive(myNet, testSocket2, myIPv4, dstIPv4, executor, callback2);
+ ka2.start(validKaInterval);
+ callback2.expectStarted();
+
+ ka.stop();
+ callback.expectStopped();
+
+ ka2.stop();
+ callback2.expectStopped();
+ }
+
+ @Test
public void testGetCaptivePortalServerUrl() throws Exception {
String url = mCm.getCaptivePortalServerUrl();
assertEquals("http://connectivitycheck.gstatic.com/generate_204", url);
@@ -4914,4 +5144,84 @@ public class ConnectivityServiceTest {
mCellNetworkAgent.sendLinkProperties(lp);
verifyTcpBufferSizeChange(TEST_TCP_BUFFER_SIZES);
}
+
+ @Test
+ public void testGetGlobalProxyForNetwork() {
+ final ProxyInfo testProxyInfo = ProxyInfo.buildDirectProxy("test", 8888);
+ mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ final Network wifiNetwork = mWiFiNetworkAgent.getNetwork();
+ when(mService.mProxyTracker.getGlobalProxy()).thenReturn(testProxyInfo);
+ assertEquals(testProxyInfo, mService.getProxyForNetwork(wifiNetwork));
+ }
+
+ @Test
+ public void testGetProxyForActiveNetwork() {
+ final ProxyInfo testProxyInfo = ProxyInfo.buildDirectProxy("test", 8888);
+ mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent.connect(true);
+ waitForIdle();
+ assertNull(mService.getProxyForNetwork(null));
+
+ final LinkProperties testLinkProperties = new LinkProperties();
+ testLinkProperties.setHttpProxy(testProxyInfo);
+
+ mWiFiNetworkAgent.sendLinkProperties(testLinkProperties);
+ waitForIdle();
+
+ assertEquals(testProxyInfo, mService.getProxyForNetwork(null));
+ }
+
+ @Test
+ public void testGetProxyForVPN() {
+ final ProxyInfo testProxyInfo = ProxyInfo.buildDirectProxy("test", 8888);
+
+ // Set up a WiFi network with no proxy
+ mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent.connect(true);
+ waitForIdle();
+ assertNull(mService.getProxyForNetwork(null));
+
+ // Set up a VPN network with a proxy
+ final int uid = Process.myUid();
+ final MockNetworkAgent vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN);
+ final ArraySet<UidRange> ranges = new ArraySet<>();
+ ranges.add(new UidRange(uid, uid));
+ mMockVpn.setUids(ranges);
+ LinkProperties testLinkProperties = new LinkProperties();
+ testLinkProperties.setHttpProxy(testProxyInfo);
+ vpnNetworkAgent.sendLinkProperties(testLinkProperties);
+ waitForIdle();
+
+ // Connect to VPN with proxy
+ mMockVpn.setNetworkAgent(vpnNetworkAgent);
+ vpnNetworkAgent.connect(true);
+ mMockVpn.connect();
+ waitForIdle();
+
+ // Test that the VPN network returns a proxy, and the WiFi does not.
+ assertEquals(testProxyInfo, mService.getProxyForNetwork(vpnNetworkAgent.getNetwork()));
+ assertEquals(testProxyInfo, mService.getProxyForNetwork(null));
+ assertNull(mService.getProxyForNetwork(mWiFiNetworkAgent.getNetwork()));
+
+ // Test that the VPN network returns no proxy when it is set to null.
+ testLinkProperties.setHttpProxy(null);
+ vpnNetworkAgent.sendLinkProperties(testLinkProperties);
+ waitForIdle();
+ assertNull(mService.getProxyForNetwork(vpnNetworkAgent.getNetwork()));
+ assertNull(mService.getProxyForNetwork(null));
+
+ // Set WiFi proxy and check that the vpn proxy is still null.
+ testLinkProperties.setHttpProxy(testProxyInfo);
+ mWiFiNetworkAgent.sendLinkProperties(testLinkProperties);
+ waitForIdle();
+ assertNull(mService.getProxyForNetwork(null));
+
+ // Disconnect from VPN and check that the active network, which is now the WiFi, has the
+ // correct proxy setting.
+ vpnNetworkAgent.disconnect();
+ waitForIdle();
+ assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
+ assertEquals(testProxyInfo, mService.getProxyForNetwork(mWiFiNetworkAgent.getNetwork()));
+ assertEquals(testProxyInfo, mService.getProxyForNetwork(null));
+ }
}
diff --git a/tests/net/java/com/android/server/connectivity/NetworkNotificationManagerTest.java b/tests/net/java/com/android/server/connectivity/NetworkNotificationManagerTest.java
index 125fe7258e94..273b8fc3773b 100644
--- a/tests/net/java/com/android/server/connectivity/NetworkNotificationManagerTest.java
+++ b/tests/net/java/com/android/server/connectivity/NetworkNotificationManagerTest.java
@@ -17,6 +17,7 @@
package com.android.server.connectivity;
import static com.android.server.connectivity.NetworkNotificationManager.NotificationType.*;
+
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.eq;
@@ -34,25 +35,23 @@ import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
-import android.support.test.runner.AndroidJUnit4;
import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
import android.telephony.TelephonyManager;
import com.android.server.connectivity.NetworkNotificationManager.NotificationType;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-
-import org.junit.runner.RunWith;
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.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
@RunWith(AndroidJUnit4.class)
@SmallTest
@@ -194,4 +193,54 @@ public class NetworkNotificationManagerTest {
mManager.clearNotification(id);
verify(mNotificationManager, times(1)).cancelAsUser(eq(tag), eq(SIGN_IN.eventId), any());
}
+
+ @Test
+ public void testSameLevelNotifications() {
+ final int id = 101;
+ final String tag = NetworkNotificationManager.tagFor(id);
+
+ mManager.showNotification(id, LOGGED_IN, mWifiNai, mCellNai, null, false);
+ verify(mNotificationManager, times(1))
+ .notifyAsUser(eq(tag), eq(LOGGED_IN.eventId), any(), any());
+
+ mManager.showNotification(id, LOST_INTERNET, mWifiNai, mCellNai, null, false);
+ verify(mNotificationManager, times(1))
+ .notifyAsUser(eq(tag), eq(LOST_INTERNET.eventId), any(), any());
+ }
+
+ @Test
+ public void testClearNotificationByType() {
+ final int id = 101;
+ final String tag = NetworkNotificationManager.tagFor(id);
+
+ // clearNotification(int id, NotificationType notifyType) will check if given type is equal
+ // to previous type or not. If they are equal then clear the notification; if they are not
+ // equal then return.
+
+ mManager.showNotification(id, LOGGED_IN, mWifiNai, mCellNai, null, false);
+ verify(mNotificationManager, times(1))
+ .notifyAsUser(eq(tag), eq(LOGGED_IN.eventId), any(), any());
+
+ // Previous notification is LOGGED_IN and given type is LOGGED_IN too. The notification
+ // should be cleared.
+ mManager.clearNotification(id, LOGGED_IN);
+ verify(mNotificationManager, times(1))
+ .cancelAsUser(eq(tag), eq(LOGGED_IN.eventId), any());
+
+ mManager.showNotification(id, LOGGED_IN, mWifiNai, mCellNai, null, false);
+ verify(mNotificationManager, times(2))
+ .notifyAsUser(eq(tag), eq(LOGGED_IN.eventId), any(), any());
+
+ // LOST_INTERNET notification popup after LOGGED_IN notification.
+ mManager.showNotification(id, LOST_INTERNET, mWifiNai, mCellNai, null, false);
+ verify(mNotificationManager, times(1))
+ .notifyAsUser(eq(tag), eq(LOST_INTERNET.eventId), any(), any());
+
+ // Previous notification is LOST_INTERNET and given type is LOGGED_IN. The notification
+ // shouldn't be cleared.
+ mManager.clearNotification(id, LOGGED_IN);
+ // LOST_INTERNET shouldn't be cleared.
+ verify(mNotificationManager, never())
+ .cancelAsUser(eq(tag), eq(LOST_INTERNET.eventId), any());
+ }
}
diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java
index 0b74d878f069..5b17224e41e5 100644
--- a/tests/net/java/com/android/server/connectivity/VpnTest.java
+++ b/tests/net/java/com/android/server/connectivity/VpnTest.java
@@ -246,17 +246,17 @@ public class VpnTest {
assertFalse(vpn.getLockdown());
// Set always-on without lockdown.
- assertTrue(vpn.setAlwaysOnPackage(PKGS[1], false));
+ assertTrue(vpn.setAlwaysOnPackage(PKGS[1], false, Collections.emptyList()));
assertTrue(vpn.getAlwaysOn());
assertFalse(vpn.getLockdown());
// Set always-on with lockdown.
- assertTrue(vpn.setAlwaysOnPackage(PKGS[1], true));
+ assertTrue(vpn.setAlwaysOnPackage(PKGS[1], true, Collections.emptyList()));
assertTrue(vpn.getAlwaysOn());
assertTrue(vpn.getLockdown());
// Remove always-on configuration.
- assertTrue(vpn.setAlwaysOnPackage(null, false));
+ assertTrue(vpn.setAlwaysOnPackage(null, false, Collections.emptyList()));
assertFalse(vpn.getAlwaysOn());
assertFalse(vpn.getLockdown());
}
@@ -270,11 +270,11 @@ public class VpnTest {
assertUnblocked(vpn, user.start + PKG_UIDS[0], user.start + PKG_UIDS[1], user.start + PKG_UIDS[2], user.start + PKG_UIDS[3]);
// Set always-on without lockdown.
- assertTrue(vpn.setAlwaysOnPackage(PKGS[1], false));
+ assertTrue(vpn.setAlwaysOnPackage(PKGS[1], false, null));
assertUnblocked(vpn, user.start + PKG_UIDS[0], user.start + PKG_UIDS[1], user.start + PKG_UIDS[2], user.start + PKG_UIDS[3]);
// Set always-on with lockdown.
- assertTrue(vpn.setAlwaysOnPackage(PKGS[1], true));
+ assertTrue(vpn.setAlwaysOnPackage(PKGS[1], true, null));
verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(new UidRange[] {
new UidRange(user.start, user.start + PKG_UIDS[1] - 1),
new UidRange(user.start + PKG_UIDS[1] + 1, user.stop)
@@ -283,7 +283,7 @@ public class VpnTest {
assertUnblocked(vpn, user.start + PKG_UIDS[1]);
// Switch to another app.
- assertTrue(vpn.setAlwaysOnPackage(PKGS[3], true));
+ assertTrue(vpn.setAlwaysOnPackage(PKGS[3], true, null));
verify(mNetService).setAllowOnlyVpnForUids(eq(false), aryEq(new UidRange[] {
new UidRange(user.start, user.start + PKG_UIDS[1] - 1),
new UidRange(user.start + PKG_UIDS[1] + 1, user.stop)
@@ -297,6 +297,87 @@ public class VpnTest {
}
@Test
+ public void testLockdownWhitelist() throws Exception {
+ final Vpn vpn = createVpn(primaryUser.id);
+ final UidRange user = UidRange.createForUser(primaryUser.id);
+
+ // Set always-on with lockdown and whitelist app PKGS[2] from lockdown.
+ assertTrue(vpn.setAlwaysOnPackage(PKGS[1], true, Collections.singletonList(PKGS[2])));
+ verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(new UidRange[] {
+ new UidRange(user.start, user.start + PKG_UIDS[1] - 1),
+ new UidRange(user.start + PKG_UIDS[2] + 1, user.stop)
+ }));
+ assertBlocked(vpn, user.start + PKG_UIDS[0], user.start + PKG_UIDS[3]);
+ assertUnblocked(vpn, user.start + PKG_UIDS[1], user.start + PKG_UIDS[2]);
+
+ // Change whitelisted app to PKGS[3].
+ assertTrue(vpn.setAlwaysOnPackage(PKGS[1], true, Collections.singletonList(PKGS[3])));
+ verify(mNetService).setAllowOnlyVpnForUids(eq(false), aryEq(new UidRange[] {
+ new UidRange(user.start + PKG_UIDS[2] + 1, user.stop)
+ }));
+ verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(new UidRange[] {
+ new UidRange(user.start + PKG_UIDS[1] + 1, user.start + PKG_UIDS[3] - 1),
+ new UidRange(user.start + PKG_UIDS[3] + 1, user.stop)
+ }));
+ assertBlocked(vpn, user.start + PKG_UIDS[0], user.start + PKG_UIDS[2]);
+ assertUnblocked(vpn, user.start + PKG_UIDS[1], user.start + PKG_UIDS[3]);
+
+ // Change the VPN app.
+ assertTrue(vpn.setAlwaysOnPackage(PKGS[0], true, Collections.singletonList(PKGS[3])));
+ verify(mNetService).setAllowOnlyVpnForUids(eq(false), aryEq(new UidRange[] {
+ new UidRange(user.start, user.start + PKG_UIDS[1] - 1),
+ new UidRange(user.start + PKG_UIDS[1] + 1, user.start + PKG_UIDS[3] - 1)
+ }));
+ verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(new UidRange[] {
+ new UidRange(user.start, user.start + PKG_UIDS[0] - 1),
+ new UidRange(user.start + PKG_UIDS[0] + 1, user.start + PKG_UIDS[3] - 1)
+ }));
+ assertBlocked(vpn, user.start + PKG_UIDS[1], user.start + PKG_UIDS[2]);
+ assertUnblocked(vpn, user.start + PKG_UIDS[0], user.start + PKG_UIDS[3]);
+
+ // Remove the whitelist.
+ assertTrue(vpn.setAlwaysOnPackage(PKGS[0], true, null));
+ verify(mNetService).setAllowOnlyVpnForUids(eq(false), aryEq(new UidRange[] {
+ new UidRange(user.start + PKG_UIDS[0] + 1, user.start + PKG_UIDS[3] - 1),
+ new UidRange(user.start + PKG_UIDS[3] + 1, user.stop)
+ }));
+ verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(new UidRange[] {
+ new UidRange(user.start + PKG_UIDS[0] + 1, user.stop),
+ }));
+ assertBlocked(vpn, user.start + PKG_UIDS[1], user.start + PKG_UIDS[2],
+ user.start + PKG_UIDS[3]);
+ assertUnblocked(vpn, user.start + PKG_UIDS[0]);
+
+ // Add the whitelist.
+ assertTrue(vpn.setAlwaysOnPackage(PKGS[0], true, Collections.singletonList(PKGS[1])));
+ verify(mNetService).setAllowOnlyVpnForUids(eq(false), aryEq(new UidRange[] {
+ new UidRange(user.start + PKG_UIDS[0] + 1, user.stop)
+ }));
+ verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(new UidRange[] {
+ new UidRange(user.start + PKG_UIDS[0] + 1, user.start + PKG_UIDS[1] - 1),
+ new UidRange(user.start + PKG_UIDS[1] + 1, user.stop)
+ }));
+ assertBlocked(vpn, user.start + PKG_UIDS[2], user.start + PKG_UIDS[3]);
+ assertUnblocked(vpn, user.start + PKG_UIDS[0], user.start + PKG_UIDS[1]);
+
+ // Try whitelisting a package with a comma, should be rejected.
+ assertFalse(vpn.setAlwaysOnPackage(PKGS[0], true, Collections.singletonList("a.b,c.d")));
+
+ // Pass a non-existent packages in the whitelist, they (and only they) should be ignored.
+ // Whitelisted package should change from PGKS[1] to PKGS[2].
+ assertTrue(vpn.setAlwaysOnPackage(PKGS[0], true,
+ Arrays.asList("com.foo.app", PKGS[2], "com.bar.app")));
+ verify(mNetService).setAllowOnlyVpnForUids(eq(false), aryEq(new UidRange[]{
+ new UidRange(user.start + PKG_UIDS[0] + 1, user.start + PKG_UIDS[1] - 1),
+ new UidRange(user.start + PKG_UIDS[1] + 1, user.stop)
+ }));
+ verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(new UidRange[]{
+ new UidRange(user.start + PKG_UIDS[0] + 1, user.start + PKG_UIDS[2] - 1),
+ new UidRange(user.start + PKG_UIDS[2] + 1, user.stop)
+ }));
+ }
+
+ @Test
public void testLockdownAddingAProfile() throws Exception {
final Vpn vpn = createVpn(primaryUser.id);
setMockedUsers(primaryUser);
@@ -310,7 +391,7 @@ public class VpnTest {
final UidRange profile = UidRange.createForUser(tempProfile.id);
// Set lockdown.
- assertTrue(vpn.setAlwaysOnPackage(PKGS[3], true));
+ assertTrue(vpn.setAlwaysOnPackage(PKGS[3], true, null));
verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(new UidRange[] {
new UidRange(user.start, user.start + PKG_UIDS[3] - 1),
new UidRange(user.start + PKG_UIDS[3] + 1, user.stop)
@@ -436,7 +517,7 @@ public class VpnTest {
.cancelAsUser(anyString(), anyInt(), eq(userHandle));
// Start showing a notification for disconnected once always-on.
- vpn.setAlwaysOnPackage(PKGS[0], false);
+ vpn.setAlwaysOnPackage(PKGS[0], false, null);
order.verify(mNotificationManager)
.notifyAsUser(anyString(), anyInt(), any(), eq(userHandle));
@@ -450,7 +531,7 @@ public class VpnTest {
.notifyAsUser(anyString(), anyInt(), any(), eq(userHandle));
// Notification should be cleared after unsetting always-on package.
- vpn.setAlwaysOnPackage(null, false);
+ vpn.setAlwaysOnPackage(null, false, null);
order.verify(mNotificationManager).cancelAsUser(anyString(), anyInt(), eq(userHandle));
}
@@ -583,7 +664,9 @@ public class VpnTest {
doAnswer(invocation -> {
final String appName = (String) invocation.getArguments()[0];
final int userId = (int) invocation.getArguments()[1];
- return UserHandle.getUid(userId, packages.get(appName));
+ Integer appId = packages.get(appName);
+ if (appId == null) throw new PackageManager.NameNotFoundException(appName);
+ return UserHandle.getUid(userId, appId);
}).when(mPackageManager).getPackageUidAsUser(anyString(), anyInt());
} catch (Exception e) {
}
diff --git a/tests/utils/testutils/java/com/android/test/filters/SelectTestTests.java b/tests/utils/testutils/java/com/android/test/filters/SelectTestTests.java
index a6b0102a511f..163b00abafcd 100644
--- a/tests/utils/testutils/java/com/android/test/filters/SelectTestTests.java
+++ b/tests/utils/testutils/java/com/android/test/filters/SelectTestTests.java
@@ -190,21 +190,22 @@ public class SelectTestTests {
@Test
public void testSelectClassAndSamePackage() {
- final Filter filter = mBuilder.withSelectTest(CLASS_A1, CLASS_A2, PACKAGE_A,
- CLASS_C5, CLASS_C6, PACKAGE_C).build();
- acceptTests(filter, TEST_PACKAGE_A, TEST_PACKAGE_C);
+ final Filter filter = mBuilder.withSelectTest(
+ CLASS_A1, PACKAGE_A, CLASS_B3, PACKAGE_C, CLASS_C5).build();
+ acceptTests(filter, TEST_PACKAGE_A, TEST_CLASS_B3, TEST_PACKAGE_C);
}
@Test
public void testSelectMethodAndSameClass() {
- final Filter filter = mBuilder.withSelectTest(METHOD_A1K, METHOD_A1L, METHOD_A2M, CLASS_A1,
- CLASS_B3, METHOD_B3P, METHOD_B3Q, METHOD_B4R).build();
+ final Filter filter = mBuilder.withSelectTest(
+ METHOD_A1K, METHOD_A2M, CLASS_A1, CLASS_B3, METHOD_B3P, METHOD_B4R).build();
acceptTests(filter, TEST_CLASS_A1, TEST_METHOD_A2M, TEST_CLASS_B3, TEST_METHOD_B4R);
}
@Test
public void testSelectMethodAndSamePackage() {
- final Filter filter = mBuilder.withSelectTest(METHOD_A1K, METHOD_A1L, METHOD_A2M, PACKAGE_A,
+ final Filter filter = mBuilder.withSelectTest(
+ METHOD_A1K, METHOD_A1L, METHOD_A2M, PACKAGE_A,
PACKAGE_C, METHOD_C5W, METHOD_C5X, METHOD_C6Y).build();
acceptTests(filter, TEST_PACKAGE_A, TEST_PACKAGE_C);
}
diff --git a/tools/aapt2/cmd/Convert_test.cpp b/tools/aapt2/cmd/Convert_test.cpp
index 2e4315086105..8da5bb8d5dd6 100644
--- a/tools/aapt2/cmd/Convert_test.cpp
+++ b/tools/aapt2/cmd/Convert_test.cpp
@@ -53,7 +53,11 @@ TEST_F(ConvertTest, RemoveRawXmlStrings) {
// Load the binary xml tree
android::ResXMLTree tree;
std::unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(out_convert_apk, &diag);
- AssertLoadXml(apk.get(), "res/xml/test.xml", &tree);
+
+ std::unique_ptr<io::IData> data = OpenFileAsData(apk.get(), "res/xml/test.xml");
+ ASSERT_THAT(data, Ne(nullptr));
+
+ AssertLoadXml(apk.get(), data.get(), &tree);
// Check that the raw string index has not been assigned
EXPECT_THAT(tree.getAttributeValueStringID(0), Eq(-1));
@@ -87,7 +91,11 @@ TEST_F(ConvertTest, KeepRawXmlStrings) {
// Load the binary xml tree
android::ResXMLTree tree;
std::unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(out_convert_apk, &diag);
- AssertLoadXml(apk.get(), "res/xml/test.xml", &tree);
+
+ std::unique_ptr<io::IData> data = OpenFileAsData(apk.get(), "res/xml/test.xml");
+ ASSERT_THAT(data, Ne(nullptr));
+
+ AssertLoadXml(apk.get(), data.get(), &tree);
// Check that the raw string index has been set to the correct string pool entry
int32_t raw_index = tree.getAttributeValueStringID(0);
diff --git a/tools/aapt2/cmd/Link_test.cpp b/tools/aapt2/cmd/Link_test.cpp
index 3c8b72d3cb2c..9ea93f638aff 100644
--- a/tools/aapt2/cmd/Link_test.cpp
+++ b/tools/aapt2/cmd/Link_test.cpp
@@ -43,7 +43,11 @@ TEST_F(LinkTest, RemoveRawXmlStrings) {
// Load the binary xml tree
android::ResXMLTree tree;
std::unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(out_apk, &diag);
- AssertLoadXml(apk.get(), "res/xml/test.xml", &tree);
+
+ std::unique_ptr<io::IData> data = OpenFileAsData(apk.get(), "res/xml/test.xml");
+ ASSERT_THAT(data, Ne(nullptr));
+
+ AssertLoadXml(apk.get(), data.get(), &tree);
// Check that the raw string index has not been assigned
EXPECT_THAT(tree.getAttributeValueStringID(0), Eq(-1));
@@ -67,7 +71,11 @@ TEST_F(LinkTest, KeepRawXmlStrings) {
// Load the binary xml tree
android::ResXMLTree tree;
std::unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(out_apk, &diag);
- AssertLoadXml(apk.get(), "res/xml/test.xml", &tree);
+
+ std::unique_ptr<io::IData> data = OpenFileAsData(apk.get(), "res/xml/test.xml");
+ ASSERT_THAT(data, Ne(nullptr));
+
+ AssertLoadXml(apk.get(), data.get(), &tree);
// Check that the raw string index has been set to the correct string pool entry
int32_t raw_index = tree.getAttributeValueStringID(0);
diff --git a/tools/aapt2/test/Fixture.cpp b/tools/aapt2/test/Fixture.cpp
index aae79fafc0a6..3fcdfb70a524 100644
--- a/tools/aapt2/test/Fixture.cpp
+++ b/tools/aapt2/test/Fixture.cpp
@@ -133,16 +133,18 @@ std::string CommandTestFixture::GetDefaultManifest() {
return manifest_file;
}
-void CommandTestFixture::AssertLoadXml(LoadedApk *apk, const android::StringPiece &xml_path,
+std::unique_ptr<io::IData> CommandTestFixture::OpenFileAsData(LoadedApk* apk,
+ const android::StringPiece& path) {
+ return apk
+ ->GetFileCollection()
+ ->FindFile(path)
+ ->OpenAsData();
+}
+
+void CommandTestFixture::AssertLoadXml(LoadedApk* apk, const io::IData* data,
android::ResXMLTree *out_tree) {
ASSERT_THAT(apk, Ne(nullptr));
- io::IFile* file = apk->GetFileCollection()->FindFile(xml_path);
- ASSERT_THAT(file, Ne(nullptr));
-
- std::unique_ptr<io::IData> data = file->OpenAsData();
- ASSERT_THAT(data, Ne(nullptr));
-
out_tree->setTo(data->data(), data->size());
ASSERT_THAT(out_tree->getError(), Eq(android::OK));
while (out_tree->next() != android::ResXMLTree::START_TAG) {
diff --git a/tools/aapt2/test/Fixture.h b/tools/aapt2/test/Fixture.h
index 89d3b7b751a0..3079c757f61a 100644
--- a/tools/aapt2/test/Fixture.h
+++ b/tools/aapt2/test/Fixture.h
@@ -83,8 +83,12 @@ class CommandTestFixture : public TestDirectoryFixture {
// Creates a minimal android manifest within the test directory and returns the file path.
std::string GetDefaultManifest();
+ // Returns pointer to data inside APK files
+ std::unique_ptr<io::IData> OpenFileAsData(LoadedApk* apk,
+ const android::StringPiece& path);
+
// Asserts that loading the tree from the specified file in the apk succeeds.
- void AssertLoadXml(LoadedApk* apk, const android::StringPiece& xml_path,
+ void AssertLoadXml(LoadedApk* apk, const io::IData* data,
android::ResXMLTree* out_tree);
private:
diff --git a/tools/processors/unsupportedappusage/Android.bp b/tools/processors/unsupportedappusage/Android.bp
index 1aca3edfab88..0e33fddcde07 100644
--- a/tools/processors/unsupportedappusage/Android.bp
+++ b/tools/processors/unsupportedappusage/Android.bp
@@ -1,6 +1,8 @@
-java_library_host {
+java_plugin {
name: "unsupportedappusage-annotation-processor",
+ processor_class: "android.processor.unsupportedappusage.UnsupportedAppUsageProcessor",
+
java_resources: [
"META-INF/**/*",
],
diff --git a/tools/processors/view_inspector/Android.bp b/tools/processors/view_inspector/Android.bp
index 9b5df56e3987..06ff05e5d755 100644
--- a/tools/processors/view_inspector/Android.bp
+++ b/tools/processors/view_inspector/Android.bp
@@ -1,6 +1,8 @@
-java_library_host {
+java_plugin {
name: "view-inspector-annotation-processor",
+ processor_class: "android.processor.view.inspector.PlatformInspectableProcessor",
+
srcs: ["src/java/**/*.java"],
java_resource_dirs: ["src/resources"],
diff --git a/tools/signedconfig/debug_key.pem b/tools/signedconfig/debug_key.pem
index 0af577bf81e1..17a1dff71707 100644
--- a/tools/signedconfig/debug_key.pem
+++ b/tools/signedconfig/debug_key.pem
@@ -1,5 +1,5 @@
-----BEGIN EC PRIVATE KEY-----
-MHcCAQEEIEfgtO+KPOoqJqTnqkDDKkAcOzyvtovsUO/ShLE6y4XRoAoGCCqGSM49
-AwEHoUQDQgAEaAn2XVifsLTHg616nTsOMVmlhBoECGbTEBTKKvdd2hO60pj1pnU8
-SMkhYfaNxZuKgw9LNvOwlFwStboIYeZ3lQ==
+MHcCAQEEIFbNNr1/TsFlvnmH1z6e0xyact9t7PDs+VFWc7QFtoRcoAoGCCqGSM49
+AwEHoUQDQgAEmJKs4lSn+XRhMQmMid+Zbhbu13YrU1haIhVC5296InRu1x7A8PV1
+ejQyisBODGgRY6pqkAHRncBCYcgg5wIIJg==
-----END EC PRIVATE KEY-----
diff --git a/tools/signedconfig/debug_public.pem b/tools/signedconfig/debug_public.pem
index f61f81322b94..d9f0d387b823 100644
--- a/tools/signedconfig/debug_public.pem
+++ b/tools/signedconfig/debug_public.pem
@@ -1,4 +1,4 @@
-----BEGIN PUBLIC KEY-----
-MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEaAn2XVifsLTHg616nTsOMVmlhBoE
-CGbTEBTKKvdd2hO60pj1pnU8SMkhYfaNxZuKgw9LNvOwlFwStboIYeZ3lQ==
+MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEmJKs4lSn+XRhMQmMid+Zbhbu13Yr
+U1haIhVC5296InRu1x7A8PV1ejQyisBODGgRY6pqkAHRncBCYcgg5wIIJg==
-----END PUBLIC KEY-----
diff --git a/tools/signedconfig/debug_sign.sh b/tools/signedconfig/debug_sign.sh
index 28e54289f8f8..3a2814a62aff 100755
--- a/tools/signedconfig/debug_sign.sh
+++ b/tools/signedconfig/debug_sign.sh
@@ -2,5 +2,5 @@
# Script to sign data with the debug keys. Outputs base64 for embedding into
# APK metadata.
-openssl dgst -sha256 -sign $(dirname $0)/debug_key.pem $1 | base64 -w 0
+openssl dgst -sha256 -sign $(dirname $0)/debug_key.pem <(echo -n "$1" | base64 -d) | base64 -w 0
echo
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index 46c419130233..d5497990aefd 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -28,6 +28,7 @@ import android.net.wifi.IDppCallback;
import android.net.wifi.INetworkRequestMatchCallback;
import android.net.wifi.ISoftApCallback;
import android.net.wifi.ITrafficStateCallback;
+import android.net.wifi.IWifiUsabilityStatsListener;
import android.net.wifi.PasspointManagementObjectDefinition;
import android.net.wifi.ScanResult;
import android.net.wifi.WifiActivityEnergyInfo;
@@ -186,6 +187,10 @@ interface IWifiManager
void unregisterSoftApCallback(int callbackIdentifier);
+ void addWifiUsabilityStatsListener(in IBinder binder, in IWifiUsabilityStatsListener listener, int listenerIdentifier);
+
+ void removeWifiUsabilityStatsListener(int listenerIdentifier);
+
void registerTrafficStateCallback(in IBinder binder, in ITrafficStateCallback callback, int callbackIdentifier);
void unregisterTrafficStateCallback(int callbackIdentifier);
@@ -209,5 +214,6 @@ interface IWifiManager
in IDppCallback callback);
void stopDppSession();
-}
+ void updateWifiUsabilityScore(int seqNum, int score, int predictionHorizonSec);
+}
diff --git a/wifi/java/android/net/wifi/IWifiUsabilityStatsListener.aidl b/wifi/java/android/net/wifi/IWifiUsabilityStatsListener.aidl
new file mode 100644
index 000000000000..284ffaa18257
--- /dev/null
+++ b/wifi/java/android/net/wifi/IWifiUsabilityStatsListener.aidl
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi;
+
+import android.net.wifi.WifiUsabilityStatsEntry;
+
+/**
+ * Interface for Wi-Fi usability stats listener.
+ *
+ * @hide
+ */
+oneway interface IWifiUsabilityStatsListener
+{
+ /**
+ * Service to manager callback providing current Wi-Fi usability stats.
+ *
+ * @param seqNum The sequence number of stats, used to derive the timing of updated Wi-Fi
+ * usability statistics, set by framework and shall be incremented by one
+ * after each update.
+ * @param isSameBssidAndFreq The flag to indicate whether the BSSID and the frequency of
+ * network stays the same or not relative to the last update of
+ * Wi-Fi usability stats.
+ * @param stats The updated Wi-Fi usability statistics.
+ */
+ void onStatsUpdated(int seqNum, boolean isSameBssidAndFreq,
+ in WifiUsabilityStatsEntry stats);
+}
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index d2d711f10944..96493de69673 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -787,6 +787,18 @@ public class WifiConfiguration implements Parcelable {
public boolean trusted;
/**
+ * This Wifi configuration is created from a {@link WifiNetworkSuggestion}
+ * @hide
+ */
+ public boolean fromWifiNetworkSuggestion;
+
+ /**
+ * This Wifi configuration is created from a {@link WifiNetworkSpecifier}
+ * @hide
+ */
+ public boolean fromWifiNetworkSpecifier;
+
+ /**
* Indicates if the creator of this configuration has expressed that it
* should be considered metered.
*
@@ -1668,6 +1680,8 @@ public class WifiConfiguration implements Parcelable {
ephemeral = false;
osu = false;
trusted = true; // Networks are considered trusted by default.
+ fromWifiNetworkSuggestion = false;
+ fromWifiNetworkSpecifier = false;
meteredHint = false;
meteredOverride = METERED_OVERRIDE_NONE;
useExternalScores = false;
@@ -1779,10 +1793,13 @@ public class WifiConfiguration implements Parcelable {
if (this.ephemeral) sbuf.append(" ephemeral");
if (this.osu) sbuf.append(" osu");
if (this.trusted) sbuf.append(" trusted");
+ if (this.fromWifiNetworkSuggestion) sbuf.append(" fromWifiNetworkSuggestion");
+ if (this.fromWifiNetworkSpecifier) sbuf.append(" fromWifiNetworkSpecifier");
if (this.meteredHint) sbuf.append(" meteredHint");
if (this.useExternalScores) sbuf.append(" useExternalScores");
if (this.didSelfAdd || this.selfAdded || this.validatedInternetAccess
- || this.ephemeral || this.trusted || this.meteredHint || this.useExternalScores) {
+ || this.ephemeral || this.trusted || this.fromWifiNetworkSuggestion
+ || this.fromWifiNetworkSpecifier || this.meteredHint || this.useExternalScores) {
sbuf.append("\n");
}
if (this.meteredOverride != METERED_OVERRIDE_NONE) {
@@ -2270,6 +2287,8 @@ public class WifiConfiguration implements Parcelable {
ephemeral = source.ephemeral;
osu = source.osu;
trusted = source.trusted;
+ fromWifiNetworkSuggestion = source.fromWifiNetworkSuggestion;
+ fromWifiNetworkSpecifier = source.fromWifiNetworkSpecifier;
meteredHint = source.meteredHint;
meteredOverride = source.meteredOverride;
useExternalScores = source.useExternalScores;
@@ -2347,6 +2366,8 @@ public class WifiConfiguration implements Parcelable {
dest.writeInt(isLegacyPasspointConfig ? 1 : 0);
dest.writeInt(ephemeral ? 1 : 0);
dest.writeInt(trusted ? 1 : 0);
+ dest.writeInt(fromWifiNetworkSuggestion ? 1 : 0);
+ dest.writeInt(fromWifiNetworkSpecifier ? 1 : 0);
dest.writeInt(meteredHint ? 1 : 0);
dest.writeInt(meteredOverride);
dest.writeInt(useExternalScores ? 1 : 0);
@@ -2418,6 +2439,8 @@ public class WifiConfiguration implements Parcelable {
config.isLegacyPasspointConfig = in.readInt() != 0;
config.ephemeral = in.readInt() != 0;
config.trusted = in.readInt() != 0;
+ config.fromWifiNetworkSuggestion = in.readInt() != 0;
+ config.fromWifiNetworkSpecifier = in.readInt() != 0;
config.meteredHint = in.readInt() != 0;
config.meteredOverride = in.readInt();
config.useExternalScores = in.readInt() != 0;
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 1fd45e72f1e8..40077e111654 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -4777,4 +4777,114 @@ public class WifiManager {
});
}
}
-}
+
+ /**
+ * Interface for Wi-Fi usability statistics listener. Should be implemented by applications and
+ * set when calling {@link WifiManager#addWifiUsabilityStatsListener(Executor,
+ * WifiUsabilityStatsListener)}.
+ *
+ * @hide
+ */
+ @SystemApi
+ public interface WifiUsabilityStatsListener {
+ /**
+ * Called when Wi-Fi usability statistics is updated.
+ *
+ * @param seqNum The sequence number of statistics, used to derive the timing of updated
+ * Wi-Fi usability statistics, set by framework and incremented by one after
+ * each update.
+ * @param isSameBssidAndFreq The flag to indicate whether the BSSID and the frequency of
+ * network stays the same or not relative to the last update of
+ * Wi-Fi usability stats.
+ * @param stats The updated Wi-Fi usability statistics.
+ */
+ void onStatsUpdated(int seqNum, boolean isSameBssidAndFreq,
+ WifiUsabilityStatsEntry stats);
+ }
+
+ /**
+ * Adds a listener for Wi-Fi usability statistics. See {@link WifiUsabilityStatsListener}.
+ * Multiple listeners can be added. Callers will be invoked periodically by framework to
+ * inform clients about the current Wi-Fi usability statistics. Callers can remove a previously
+ * added listener using {@link removeWifiUsabilityStatsListener}.
+ *
+ * @param executor The executor on which callback will be invoked.
+ * @param listener Listener for Wifi usability statistics.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.WIFI_UPDATE_USABILITY_STATS_SCORE)
+ public void addWifiUsabilityStatsListener(@NonNull @CallbackExecutor Executor executor,
+ @NonNull WifiUsabilityStatsListener listener) {
+ if (executor == null) throw new IllegalArgumentException("executor cannot be null");
+ if (listener == null) throw new IllegalArgumentException("listener cannot be null");
+ if (mVerboseLoggingEnabled) {
+ Log.v(TAG, "addWifiUsabilityStatsListener: listener=" + listener);
+ }
+ try {
+ mService.addWifiUsabilityStatsListener(new Binder(),
+ new IWifiUsabilityStatsListener.Stub() {
+ @Override
+ public void onStatsUpdated(int seqNum, boolean isSameBssidAndFreq,
+ WifiUsabilityStatsEntry stats) {
+ if (mVerboseLoggingEnabled) {
+ Log.v(TAG, "WifiUsabilityStatsListener: onStatsUpdated: seqNum="
+ + seqNum);
+ }
+ Binder.withCleanCallingIdentity(() ->
+ executor.execute(() -> listener.onStatsUpdated(seqNum,
+ isSameBssidAndFreq, stats)));
+ }
+ },
+ listener.hashCode()
+ );
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Allow callers to remove a previously registered listener. After calling this method,
+ * applications will no longer receive Wi-Fi usability statistics.
+ *
+ * @param listener Listener to remove the Wi-Fi usability statistics.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.WIFI_UPDATE_USABILITY_STATS_SCORE)
+ public void removeWifiUsabilityStatsListener(@NonNull WifiUsabilityStatsListener listener) {
+ if (listener == null) throw new IllegalArgumentException("listener cannot be null");
+ if (mVerboseLoggingEnabled) {
+ Log.v(TAG, "removeWifiUsabilityStatsListener: listener=" + listener);
+ }
+ try {
+ mService.removeWifiUsabilityStatsListener(listener.hashCode());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Provide a Wi-Fi usability score information to be recorded (but not acted upon) by the
+ * framework. The Wi-Fi usability score is derived from {@link WifiUsabilityStatsListener}
+ * where a score is matched to Wi-Fi usability statistics using the sequence number. The score
+ * is used to quantify whether Wi-Fi is usable in a future time.
+ *
+ * @param seqNum Sequence number of the Wi-Fi usability score.
+ * @param score The Wi-Fi usability score.
+ * @param predictionHorizonSec Prediction horizon of the Wi-Fi usability score.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.WIFI_UPDATE_USABILITY_STATS_SCORE)
+ public void updateWifiUsabilityScore(int seqNum, int score, int predictionHorizonSec) {
+ try {
+ mService.updateWifiUsabilityScore(seqNum, score, predictionHorizonSec);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+} \ No newline at end of file
diff --git a/wifi/java/android/net/wifi/WifiUsabilityStatsEntry.aidl b/wifi/java/android/net/wifi/WifiUsabilityStatsEntry.aidl
new file mode 100644
index 000000000000..839af54b81ba
--- /dev/null
+++ b/wifi/java/android/net/wifi/WifiUsabilityStatsEntry.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi;
+
+parcelable WifiUsabilityStatsEntry;
diff --git a/wifi/java/android/net/wifi/WifiUsabilityStatsEntry.java b/wifi/java/android/net/wifi/WifiUsabilityStatsEntry.java
new file mode 100644
index 000000000000..c796e29e4e1a
--- /dev/null
+++ b/wifi/java/android/net/wifi/WifiUsabilityStatsEntry.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi;
+
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * This class makes a subset of
+ * com.android.server.wifi.nano.WifiMetricsProto.WifiUsabilityStatsEntry parcelable.
+ *
+ * @hide
+ */
+@SystemApi
+public final class WifiUsabilityStatsEntry implements Parcelable {
+ /** Absolute milliseconds from device boot when these stats were sampled */
+ public final long timeStampMs;
+ /** The RSSI (in dBm) at the sample time */
+ public final int rssi;
+ /** Link speed at the sample time in Mbps */
+ public final int linkSpeedMbps;
+ /** The total number of tx success counted from the last radio chip reset */
+ public final long totalTxSuccess;
+ /** The total number of MPDU data packet retries counted from the last radio chip reset */
+ public final long totalTxRetries;
+ /** The total number of tx bad counted from the last radio chip reset */
+ public final long totalTxBad;
+ /** The total number of rx success counted from the last radio chip reset */
+ public final long totalRxSuccess;
+ /** The total time the wifi radio is on in ms counted from the last radio chip reset */
+ public final long totalRadioOnTimeMs;
+ /** The total time the wifi radio is doing tx in ms counted from the last radio chip reset */
+ public final long totalRadioTxTimeMs;
+ /** The total time the wifi radio is doing rx in ms counted from the last radio chip reset */
+ public final long totalRadioRxTimeMs;
+ /** The total time spent on all types of scans in ms counted from the last radio chip reset */
+ public final long totalScanTimeMs;
+ /** The total time spent on nan scans in ms counted from the last radio chip reset */
+ public final long totalNanScanTimeMs;
+ /** The total time spent on background scans in ms counted from the last radio chip reset */
+ public final long totalBackgroundScanTimeMs;
+ /** The total time spent on roam scans in ms counted from the last radio chip reset */
+ public final long totalRoamScanTimeMs;
+ /** The total time spent on pno scans in ms counted from the last radio chip reset */
+ public final long totalPnoScanTimeMs;
+ /** The total time spent on hotspot2.0 scans and GAS exchange in ms counted from the last radio
+ * chip reset */
+ public final long totalHotspot2ScanTimeMs;
+ /** The total time CCA is on busy status on the current frequency in ms counted from the last
+ * radio chip reset */
+ public final long totalCcaBusyFreqTimeMs;
+ /** The total radio on time of the current frequency from the last radio chip reset */
+ public final long totalRadioOnFreqTimeMs;
+ /** The total number of beacons received from the last radio chip reset */
+ public final long totalBeaconRx;
+
+ /** Constructor function {@hide} */
+ public WifiUsabilityStatsEntry(long timeStampMs, int rssi,
+ int linkSpeedMbps, long totalTxSuccess, long totalTxRetries,
+ long totalTxBad, long totalRxSuccess, long totalRadioOnTimeMs,
+ long totalRadioTxTimeMs, long totalRadioRxTimeMs, long totalScanTimeMs,
+ long totalNanScanTimeMs, long totalBackgroundScanTimeMs, long totalRoamScanTimeMs,
+ long totalPnoScanTimeMs, long totalHotspot2ScanTimeMs, long totalCcaBusyFreqTimeMs,
+ long totalRadioOnFreqTimeMs, long totalBeaconRx) {
+ this.timeStampMs = timeStampMs;
+ this.rssi = rssi;
+ this.linkSpeedMbps = linkSpeedMbps;
+ this.totalTxSuccess = totalTxSuccess;
+ this.totalTxRetries = totalTxRetries;
+ this.totalTxBad = totalTxBad;
+ this.totalRxSuccess = totalRxSuccess;
+ this.totalRadioOnTimeMs = totalRadioOnTimeMs;
+ this.totalRadioTxTimeMs = totalRadioTxTimeMs;
+ this.totalRadioRxTimeMs = totalRadioRxTimeMs;
+ this.totalScanTimeMs = totalScanTimeMs;
+ this.totalNanScanTimeMs = totalNanScanTimeMs;
+ this.totalBackgroundScanTimeMs = totalBackgroundScanTimeMs;
+ this.totalRoamScanTimeMs = totalRoamScanTimeMs;
+ this.totalPnoScanTimeMs = totalPnoScanTimeMs;
+ this.totalHotspot2ScanTimeMs = totalHotspot2ScanTimeMs;
+ this.totalCcaBusyFreqTimeMs = totalCcaBusyFreqTimeMs;
+ this.totalRadioOnFreqTimeMs = totalRadioOnFreqTimeMs;
+ this.totalBeaconRx = totalBeaconRx;
+ }
+
+ /** Implement the Parcelable interface */
+ public int describeContents() {
+ return 0;
+ }
+
+ /** Implement the Parcelable interface */
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeLong(timeStampMs);
+ dest.writeInt(rssi);
+ dest.writeInt(linkSpeedMbps);
+ dest.writeLong(totalTxSuccess);
+ dest.writeLong(totalTxRetries);
+ dest.writeLong(totalTxBad);
+ dest.writeLong(totalRxSuccess);
+ dest.writeLong(totalRadioOnTimeMs);
+ dest.writeLong(totalRadioTxTimeMs);
+ dest.writeLong(totalRadioRxTimeMs);
+ dest.writeLong(totalScanTimeMs);
+ dest.writeLong(totalNanScanTimeMs);
+ dest.writeLong(totalBackgroundScanTimeMs);
+ dest.writeLong(totalRoamScanTimeMs);
+ dest.writeLong(totalPnoScanTimeMs);
+ dest.writeLong(totalHotspot2ScanTimeMs);
+ dest.writeLong(totalCcaBusyFreqTimeMs);
+ dest.writeLong(totalRadioOnFreqTimeMs);
+ dest.writeLong(totalBeaconRx);
+ }
+
+ /** Implement the Parcelable interface */
+ public static final Creator<WifiUsabilityStatsEntry> CREATOR =
+ new Creator<WifiUsabilityStatsEntry>() {
+ public WifiUsabilityStatsEntry createFromParcel(Parcel in) {
+ return new WifiUsabilityStatsEntry(
+ in.readLong(), in.readInt(),
+ in.readInt(), in.readLong(), in.readLong(),
+ in.readLong(), in.readLong(), in.readLong(),
+ in.readLong(), in.readLong(), in.readLong(),
+ in.readLong(), in.readLong(), in.readLong(),
+ in.readLong(), in.readLong(), in.readLong(),
+ in.readLong(), in.readLong()
+ );
+ }
+
+ public WifiUsabilityStatsEntry[] newArray(int size) {
+ return new WifiUsabilityStatsEntry[size];
+ }
+ };
+}
diff --git a/wifi/java/com/android/server/wifi/BaseWifiService.java b/wifi/java/com/android/server/wifi/BaseWifiService.java
index b3ac9f15eb9a..c236c7a05488 100644
--- a/wifi/java/com/android/server/wifi/BaseWifiService.java
+++ b/wifi/java/com/android/server/wifi/BaseWifiService.java
@@ -26,6 +26,7 @@ import android.net.wifi.INetworkRequestMatchCallback;
import android.net.wifi.ISoftApCallback;
import android.net.wifi.ITrafficStateCallback;
import android.net.wifi.IWifiManager;
+import android.net.wifi.IWifiUsabilityStatsListener;
import android.net.wifi.ScanResult;
import android.net.wifi.WifiActivityEnergyInfo;
import android.net.wifi.WifiConfiguration;
@@ -464,4 +465,20 @@ public class BaseWifiService extends IWifiManager.Stub {
public void stopDppSession() throws RemoteException {
throw new UnsupportedOperationException();
}
+
+ @Override
+ public void addWifiUsabilityStatsListener(
+ IBinder binder, IWifiUsabilityStatsListener listener, int listenerIdentifier) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void removeWifiUsabilityStatsListener(int listenerIdentifier) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void updateWifiUsabilityScore(int seqNum, int score, int predictionHorizonSec) {
+ throw new UnsupportedOperationException();
+ }
}
diff --git a/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java b/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
index 7bff68aaaa97..449423f44a35 100644
--- a/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
@@ -60,6 +60,8 @@ public class WifiConfigurationTest {
config.setPasspointManagementObjectTree(cookie);
config.trusted = false;
config.updateIdentifier = "1234";
+ config.fromWifiNetworkSpecifier = true;
+ config.fromWifiNetworkSuggestion = true;
MacAddress macBeforeParcel = config.getOrCreateRandomizedMacAddress();
Parcel parcelW = Parcel.obtain();
config.writeToParcel(parcelW, 0);
@@ -76,6 +78,8 @@ public class WifiConfigurationTest {
assertEquals(macBeforeParcel, reconfig.getOrCreateRandomizedMacAddress());
assertEquals(config.updateIdentifier, reconfig.updateIdentifier);
assertFalse(reconfig.trusted);
+ assertTrue(config.fromWifiNetworkSpecifier);
+ assertTrue(config.fromWifiNetworkSuggestion);
Parcel parcelWW = Parcel.obtain();
reconfig.writeToParcel(parcelWW, 0);
diff --git a/wifi/tests/src/android/net/wifi/WifiManagerTest.java b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
index 4fbef5a8493d..5c2f626a24cc 100644
--- a/wifi/tests/src/android/net/wifi/WifiManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
@@ -61,6 +61,7 @@ import android.net.wifi.WifiManager.NetworkRequestMatchCallback;
import android.net.wifi.WifiManager.NetworkRequestUserSelectionCallback;
import android.net.wifi.WifiManager.SoftApCallback;
import android.net.wifi.WifiManager.TrafficStateCallback;
+import android.net.wifi.WifiManager.WifiUsabilityStatsListener;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
@@ -80,6 +81,7 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.concurrent.Executor;
/**
* Unit tests for {@link android.net.wifi.WifiManager}.
@@ -103,7 +105,9 @@ public class WifiManagerTest {
@Mock SoftApCallback mSoftApCallback;
@Mock TrafficStateCallback mTrafficStateCallback;
@Mock NetworkRequestMatchCallback mNetworkRequestMatchCallback;
+ @Mock WifiUsabilityStatsListener mWifiUsabilityStatsListener;
+ private Executor mExecutor;
private Handler mHandler;
private TestLooper mLooper;
private WifiManager mWifiManager;
@@ -1342,4 +1346,40 @@ i * Verify that a call to cancel WPS immediately returns a failure.
assertArrayEquals(TEST_MAC_ADDRESSES, mWifiManager.getFactoryMacAddresses());
verify(mWifiService).getFactoryMacAddresses();
}
+
+ /**
+ * Verify the call to addWifiUsabilityStatsListener goes to WifiServiceImpl.
+ */
+ @Test
+ public void addWifiUsabilityStatsListeneroesToWifiServiceImpl() throws Exception {
+ mExecutor = new SynchronousExecutor();
+ mWifiManager.addWifiUsabilityStatsListener(mExecutor, mWifiUsabilityStatsListener);
+ verify(mWifiService).addWifiUsabilityStatsListener(any(IBinder.class),
+ any(IWifiUsabilityStatsListener.Stub.class), anyInt());
+ }
+
+ /**
+ * Verify the call to removeWifiUsabilityStatsListener goes to WifiServiceImpl.
+ */
+ @Test
+ public void removeWifiUsabilityListenerGoesToWifiServiceImpl() throws Exception {
+ ArgumentCaptor<Integer> listenerIdentifier = ArgumentCaptor.forClass(Integer.class);
+ mExecutor = new SynchronousExecutor();
+ mWifiManager.addWifiUsabilityStatsListener(mExecutor, mWifiUsabilityStatsListener);
+ verify(mWifiService).addWifiUsabilityStatsListener(any(IBinder.class),
+ any(IWifiUsabilityStatsListener.Stub.class), listenerIdentifier.capture());
+
+ mWifiManager.removeWifiUsabilityStatsListener(mWifiUsabilityStatsListener);
+ verify(mWifiService).removeWifiUsabilityStatsListener(
+ eq((int) listenerIdentifier.getValue()));
+ }
+
+ /**
+ * Defined for testing purpose.
+ */
+ class SynchronousExecutor implements Executor {
+ public void execute(Runnable r) {
+ r.run();
+ }
+ }
}
diff --git a/wifi/tests/src/android/net/wifi/WifiUsabilityStatsEntryTest.java b/wifi/tests/src/android/net/wifi/WifiUsabilityStatsEntryTest.java
new file mode 100644
index 000000000000..a947b5568a16
--- /dev/null
+++ b/wifi/tests/src/android/net/wifi/WifiUsabilityStatsEntryTest.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.validateMockitoUsage;
+
+import android.os.Parcel;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.MockitoAnnotations;
+
+
+/**
+ * Unit tests for {@link android.net.wifi.WifiUsabilityStatsEntry}.
+ */
+@SmallTest
+public class WifiUsabilityStatsEntryTest {
+
+ /**
+ * Setup before tests.
+ */
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ /**
+ * Clean up after tests.
+ */
+ @After
+ public void cleanup() {
+ validateMockitoUsage();
+ }
+
+ /**
+ * Verify parcel read/write for Wifi usability stats result.
+ */
+ @Test
+ public void verifyStatsResultWriteAndThenRead() throws Exception {
+ WifiUsabilityStatsEntry writeResult = createResult();
+ WifiUsabilityStatsEntry readResult = parcelWriteRead(writeResult);
+ assertWifiUsabilityStatsEntryEquals(writeResult, readResult);
+ }
+
+ /**
+ * Write the provided {@link WifiUsabilityStatsEntry} to a parcel and deserialize it.
+ */
+ private static WifiUsabilityStatsEntry parcelWriteRead(
+ WifiUsabilityStatsEntry writeResult) throws Exception {
+ Parcel parcel = Parcel.obtain();
+ writeResult.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0); // Rewind data position back to the beginning for read.
+ return WifiUsabilityStatsEntry.CREATOR.createFromParcel(parcel);
+ }
+
+ private static WifiUsabilityStatsEntry createResult() {
+ return new WifiUsabilityStatsEntry(
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18
+ );
+ }
+
+ private static void assertWifiUsabilityStatsEntryEquals(
+ WifiUsabilityStatsEntry expected,
+ WifiUsabilityStatsEntry actual) {
+ assertEquals(expected.timeStampMs, actual.timeStampMs);
+ assertEquals(expected.rssi, actual.rssi);
+ assertEquals(expected.linkSpeedMbps, actual.linkSpeedMbps);
+ assertEquals(expected.totalTxSuccess, actual.totalTxSuccess);
+ assertEquals(expected.totalTxRetries, actual.totalTxRetries);
+ assertEquals(expected.totalTxBad, actual.totalTxBad);
+ assertEquals(expected.totalRxSuccess, actual.totalRxSuccess);
+ assertEquals(expected.totalRadioOnTimeMs, actual.totalRadioOnTimeMs);
+ assertEquals(expected.totalRadioTxTimeMs, actual.totalRadioTxTimeMs);
+ assertEquals(expected.totalRadioRxTimeMs, actual.totalRadioRxTimeMs);
+ assertEquals(expected.totalScanTimeMs, actual.totalScanTimeMs);
+ assertEquals(expected.totalNanScanTimeMs, actual.totalNanScanTimeMs);
+ assertEquals(expected.totalBackgroundScanTimeMs, actual.totalBackgroundScanTimeMs);
+ assertEquals(expected.totalRoamScanTimeMs, actual.totalRoamScanTimeMs);
+ assertEquals(expected.totalPnoScanTimeMs, actual.totalPnoScanTimeMs);
+ assertEquals(expected.totalHotspot2ScanTimeMs, actual.totalHotspot2ScanTimeMs);
+ assertEquals(expected.totalCcaBusyFreqTimeMs, actual.totalCcaBusyFreqTimeMs);
+ assertEquals(expected.totalRadioOnFreqTimeMs, actual.totalRadioOnFreqTimeMs);
+ assertEquals(expected.totalBeaconRx, actual.totalBeaconRx);
+ }
+}