summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.bp1
-rw-r--r--apex/blobstore/framework/java/android/app/blob/BlobHandle.java47
-rw-r--r--apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java141
-rw-r--r--apex/blobstore/framework/java/android/app/blob/IBlobStoreManager.aidl3
-rw-r--r--apex/blobstore/framework/java/android/app/blob/IBlobStoreSession.aidl1
-rw-r--r--apex/blobstore/framework/java/android/app/blob/XmlTags.java55
-rw-r--r--apex/blobstore/service/java/com/android/server/blob/BlobAccessMode.java74
-rw-r--r--apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java259
-rw-r--r--apex/blobstore/service/java/com/android/server/blob/BlobStoreConfig.java20
-rw-r--r--apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java474
-rw-r--r--apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java120
-rw-r--r--apex/statsd/aidl/Android.bp2
-rw-r--r--apex/statsd/framework/Android.bp11
-rw-r--r--apex/statsd/framework/java/android/app/StatsManager.java (renamed from core/java/android/app/StatsManager.java)7
-rw-r--r--apex/statsd/framework/java/android/os/StatsFrameworkInitializer.java77
-rw-r--r--apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java264
-rw-r--r--api/current.txt434
-rwxr-xr-xapi/system-current.txt410
-rw-r--r--api/system-lint-baseline.txt6
-rw-r--r--api/test-current.txt92
-rw-r--r--api/test-lint-baseline.txt18
-rw-r--r--cmds/statsd/Android.bp2
-rw-r--r--cmds/statsd/src/atoms.proto46
-rw-r--r--cmds/statsd/src/external/CarStatsPuller.cpp96
-rw-r--r--cmds/statsd/src/external/CarStatsPuller.h36
-rw-r--r--cmds/statsd/src/external/Perfetto.h4
-rw-r--r--cmds/statsd/src/external/StatsPullerManager.cpp6
-rw-r--r--cmds/statsd/src/logd/LogEvent.cpp64
-rw-r--r--cmds/statsd/src/logd/LogEvent.h12
-rw-r--r--cmds/statsd/tests/LogEvent_test.cpp162
-rw-r--r--cmds/statsd/tests/storage/StorageManager_test.cpp1
-rw-r--r--config/boot-image-profile.txt1
-rw-r--r--core/java/Android.bp5
-rw-r--r--core/java/android/app/Activity.java34
-rw-r--r--core/java/android/app/ActivityManager.java26
-rw-r--r--core/java/android/app/ActivityManagerInternal.java10
-rw-r--r--core/java/android/app/ActivityThread.java48
-rw-r--r--core/java/android/app/ApplicationPackageManager.java58
-rw-r--r--core/java/android/app/IActivityManager.aidl10
-rw-r--r--core/java/android/app/IActivityTaskManager.aidl1
-rw-r--r--core/java/android/app/IApplicationThread.aidl1
-rw-r--r--core/java/android/app/SystemServiceRegistry.java19
-rw-r--r--core/java/android/app/admin/DevicePolicyManager.java112
-rw-r--r--core/java/android/app/admin/IDevicePolicyManager.aidl4
-rw-r--r--core/java/android/app/usage/UsageEvents.java38
-rwxr-xr-xcore/java/android/bluetooth/BluetoothA2dpSink.java5
-rw-r--r--core/java/android/bluetooth/BluetoothAdapter.java25
-rw-r--r--core/java/android/bluetooth/BluetoothDevice.java22
-rw-r--r--core/java/android/bluetooth/BluetoothHeadsetClient.java5
-rw-r--r--core/java/android/bluetooth/BluetoothHidHost.java5
-rw-r--r--core/java/android/bluetooth/BluetoothMap.java5
-rw-r--r--core/java/android/bluetooth/BluetoothMapClient.java5
-rw-r--r--core/java/android/bluetooth/BluetoothPbapClient.java5
-rw-r--r--core/java/android/bluetooth/BluetoothSap.java5
-rw-r--r--core/java/android/content/Context.java9
-rw-r--r--core/java/android/content/Intent.java39
-rw-r--r--core/java/android/content/LocusId.aidl (renamed from core/java/android/service/controls/templates/RangeTemplate.aidl)8
-rw-r--r--core/java/android/content/integrity/AppInstallMetadata.java22
-rw-r--r--core/java/android/content/integrity/AtomicFormula.java322
-rw-r--r--core/java/android/content/integrity/CompoundFormula.java56
-rw-r--r--core/java/android/content/integrity/Formula.java103
-rw-r--r--core/java/android/content/integrity/IntegrityFormula.java277
-rw-r--r--core/java/android/content/integrity/IntegrityUtils.java (renamed from services/core/java/com/android/server/integrity/IntegrityUtils.java)8
-rw-r--r--core/java/android/content/integrity/Rule.java13
-rw-r--r--core/java/android/content/om/OverlayManager.java48
-rw-r--r--core/java/android/content/pm/PackageManager.java61
-rw-r--r--core/java/android/content/pm/PackageParser.java2
-rw-r--r--core/java/android/content/pm/SuspendDialogInfo.java102
-rw-r--r--core/java/android/hardware/CameraStatus.java3
-rw-r--r--core/java/android/hardware/Sensor.java19
-rw-r--r--core/java/android/hardware/SensorEvent.java10
-rw-r--r--core/java/android/hardware/camera2/CameraCharacteristics.java17
-rw-r--r--core/java/android/hardware/camera2/CameraManager.java152
-rw-r--r--core/java/android/hardware/camera2/CameraMetadata.java70
-rw-r--r--core/java/android/hardware/camera2/CaptureRequest.java93
-rw-r--r--core/java/android/hardware/camera2/CaptureResult.java93
-rw-r--r--core/java/android/hardware/location/ContextHubClient.java5
-rw-r--r--core/java/android/hardware/location/ContextHubManager.java87
-rw-r--r--core/java/android/hardware/usb/UsbManager.java22
-rw-r--r--core/java/android/net/ConnectivityDiagnosticsManager.java219
-rw-r--r--core/java/android/net/ConnectivityManager.java46
-rw-r--r--core/java/android/net/IConnectivityDiagnosticsCallback.aidl (renamed from core/java/com/android/internal/car/ICarStatsService.aidl)27
-rw-r--r--core/java/android/net/IConnectivityManager.aidl5
-rw-r--r--core/java/android/net/Ikev2VpnProfile.java728
-rw-r--r--core/java/android/net/LinkAddress.java174
-rw-r--r--core/java/android/net/LinkProperties.java3
-rw-r--r--core/java/android/net/NetworkCapabilities.java107
-rw-r--r--core/java/android/net/PlatformVpnProfile.java107
-rw-r--r--core/java/android/net/VpnManager.java88
-rw-r--r--core/java/android/os/BatteryStatsManager.java26
-rw-r--r--core/java/android/os/Process.java6
-rw-r--r--core/java/android/os/StatsLogEventWrapper.java267
-rw-r--r--core/java/android/os/StatsServiceManager.java124
-rw-r--r--core/java/android/os/TelephonyServiceManager.java35
-rw-r--r--core/java/android/os/connectivity/WifiBatteryStats.java241
-rw-r--r--core/java/android/permission/PermissionManager.java24
-rw-r--r--core/java/android/provider/Settings.java20
-rw-r--r--core/java/android/provider/Telephony.java6
-rw-r--r--core/java/android/se/omapi/ISecureElementReader.aidl6
-rw-r--r--core/java/android/se/omapi/Reader.java23
-rw-r--r--core/java/android/service/autofill/FillResponse.java42
-rw-r--r--core/java/android/service/autofill/InlinePresentation.java57
-rw-r--r--core/java/android/service/contentcapture/ContentCaptureService.java113
-rw-r--r--core/java/android/service/contentcapture/DataShareCallback.java47
-rw-r--r--core/java/android/service/contentcapture/DataShareReadAdapter.java47
-rw-r--r--core/java/android/service/contentcapture/IContentCaptureService.aidl3
-rw-r--r--core/java/android/service/contentcapture/IDataShareCallback.aidl (renamed from core/java/android/os/StatsLogEventWrapper.aidl)11
-rw-r--r--core/java/android/service/contentcapture/IDataShareReadAdapter.aidl (renamed from core/java/android/service/controls/actions/BooleanAction.aidl)12
-rw-r--r--core/java/android/service/controls/Control.java222
-rw-r--r--core/java/android/service/controls/ControlsProviderService.java282
-rw-r--r--core/java/android/service/controls/DeviceTypes.java3
-rw-r--r--core/java/android/service/controls/IControlsActionCallback.aidl (renamed from core/java/android/service/controls/templates/ToggleTemplate.aidl)11
-rw-r--r--core/java/android/service/controls/IControlsLoadCallback.aidl26
-rw-r--r--core/java/android/service/controls/IControlsProvider.aidl19
-rw-r--r--core/java/android/service/controls/IControlsSubscriber.aidl (renamed from core/java/android/service/controls/IControlsProviderCallback.aidl)18
-rw-r--r--core/java/android/service/controls/IControlsSubscription.aidl (renamed from core/java/android/service/controls/templates/ThumbnailTemplate.aidl)12
-rw-r--r--core/java/android/service/controls/actions/BooleanAction.java27
-rw-r--r--core/java/android/service/controls/actions/CommandAction.aidl18
-rw-r--r--core/java/android/service/controls/actions/CommandAction.java24
-rw-r--r--core/java/android/service/controls/actions/ControlAction.java112
-rw-r--r--core/java/android/service/controls/actions/ControlActionWrapper.aidl (renamed from core/java/android/service/controls/actions/ModeAction.aidl)4
-rw-r--r--core/java/android/service/controls/actions/ControlActionWrapper.java67
-rw-r--r--core/java/android/service/controls/actions/FloatAction.java29
-rw-r--r--core/java/android/service/controls/actions/ModeAction.java29
-rw-r--r--core/java/android/service/controls/actions/MultiFloatAction.aidl18
-rw-r--r--core/java/android/service/controls/actions/MultiFloatAction.java29
-rw-r--r--core/java/android/service/controls/templates/ControlButton.java6
-rw-r--r--core/java/android/service/controls/templates/ControlTemplate.java141
-rw-r--r--core/java/android/service/controls/templates/ControlTemplateWrapper.aidl (renamed from core/java/android/service/controls/templates/StatelessTemplate.aidl)4
-rw-r--r--core/java/android/service/controls/templates/ControlTemplateWrapper.java66
-rw-r--r--core/java/android/service/controls/templates/CoordinatedRangeTemplate.aidl19
-rw-r--r--core/java/android/service/controls/templates/CoordinatedRangeTemplate.java37
-rw-r--r--core/java/android/service/controls/templates/DiscreteToggleTemplate.aidl18
-rw-r--r--core/java/android/service/controls/templates/DiscreteToggleTemplate.java39
-rw-r--r--core/java/android/service/controls/templates/RangeTemplate.java22
-rw-r--r--core/java/android/service/controls/templates/StatelessTemplate.java22
-rw-r--r--core/java/android/service/controls/templates/TemperatureControlTemplate.aidl19
-rw-r--r--core/java/android/service/controls/templates/TemperatureControlTemplate.java36
-rw-r--r--core/java/android/service/controls/templates/ThumbnailTemplate.java28
-rw-r--r--core/java/android/service/controls/templates/ToggleRangeTemplate.aidl19
-rw-r--r--core/java/android/service/controls/templates/ToggleRangeTemplate.java35
-rw-r--r--core/java/android/service/controls/templates/ToggleTemplate.java31
-rw-r--r--core/java/android/service/notification/StatusBarNotification.java20
-rw-r--r--core/java/android/telephony/TelephonyRegistryManager.java76
-rw-r--r--core/java/android/view/IWindowManager.aidl5
-rw-r--r--core/java/android/view/SurfaceControl.java1
-rw-r--r--core/java/android/view/SurfaceView.java9
-rw-r--r--core/java/android/view/SyncRtSurfaceTransactionApplier.java8
-rw-r--r--core/java/android/view/View.java8
-rw-r--r--core/java/android/view/ViewRootImpl.java12
-rw-r--r--core/java/android/view/WindowManager.java39
-rw-r--r--core/java/android/view/contentcapture/ContentCaptureManager.java86
-rw-r--r--core/java/android/view/contentcapture/DataShareRequest.aidl (renamed from core/java/android/service/controls/actions/FloatAction.aidl)7
-rw-r--r--core/java/android/view/contentcapture/DataShareRequest.java207
-rw-r--r--core/java/android/view/contentcapture/DataShareWriteAdapter.java58
-rw-r--r--core/java/android/view/contentcapture/IContentCaptureManager.aidl7
-rw-r--r--core/java/android/view/contentcapture/IDataShareWriteAdapter.aidl (renamed from core/java/android/service/controls/actions/ControlAction.aidl)15
-rw-r--r--core/java/android/view/inputmethod/InlineSuggestionInfo.java77
-rw-r--r--core/java/android/widget/Editor.java2
-rw-r--r--core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java24
-rw-r--r--core/java/com/android/internal/app/BlockedAppActivity.java86
-rw-r--r--core/java/com/android/internal/app/ChooserActivity.java276
-rw-r--r--core/java/com/android/internal/app/ChooserListAdapter.java39
-rw-r--r--core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java13
-rw-r--r--core/java/com/android/internal/app/ResolverActivity.java6
-rw-r--r--core/java/com/android/internal/app/ResolverListAdapter.java7
-rw-r--r--core/java/com/android/internal/app/ResolverMultiProfilePagerAdapter.java13
-rw-r--r--core/java/com/android/internal/app/SuspendedAppActivity.java119
-rw-r--r--core/java/com/android/internal/app/chooser/SelectableTargetInfo.java34
-rw-r--r--core/java/com/android/internal/net/VpnProfile.java293
-rw-r--r--core/java/com/android/internal/os/BatteryStatsImpl.java29
-rw-r--r--core/java/com/android/internal/os/RuntimeInit.java28
-rw-r--r--core/java/com/android/internal/policy/DecorView.java14
-rw-r--r--core/java/com/android/internal/policy/PhoneWindow.java8
-rw-r--r--core/java/com/android/internal/util/ScreenshotHelper.java51
-rw-r--r--core/jni/AndroidRuntime.cpp19
-rwxr-xr-xcore/jni/android/graphics/Bitmap.cpp3
-rw-r--r--core/jni/android/graphics/apex/android_bitmap.cpp10
-rw-r--r--core/jni/android_media_AudioEffectDescriptor.cpp26
-rw-r--r--core/proto/OWNERS1
-rw-r--r--core/proto/android/server/usagestatsservice_v2.proto2
-rw-r--r--core/res/AndroidManifest.xml15
-rw-r--r--core/res/res/layout/autofill_inline_suggestion.xml36
-rw-r--r--core/res/res/values/arrays.xml2
-rw-r--r--core/res/res/values/attrs.xml8
-rw-r--r--core/res/res/values/config.xml7
-rw-r--r--core/res/res/values/public.xml20
-rw-r--r--core/res/res/values/strings.xml12
-rw-r--r--core/res/res/values/styles.xml16
-rw-r--r--core/res/res/values/symbols.xml8
-rw-r--r--core/res/res/values/themes.xml7
-rw-r--r--core/tests/coretests/src/android/app/activity/ActivityThreadTest.java10
-rw-r--r--core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java4
-rw-r--r--core/tests/coretests/src/android/content/integrity/AtomicFormulaTest.java323
-rw-r--r--core/tests/coretests/src/android/content/integrity/CompoundFormulaTest.java149
-rw-r--r--core/tests/coretests/src/android/content/integrity/IntegrityFormulaTest.java214
-rw-r--r--core/tests/coretests/src/android/content/integrity/IntegrityUtilsTest.java (renamed from services/tests/servicestests/src/com/android/server/integrity/IntegrityUtilsTest.java)32
-rw-r--r--core/tests/coretests/src/android/content/integrity/RuleTest.java12
-rw-r--r--core/tests/coretests/src/android/content/pm/PackageParserTest.java4
-rw-r--r--core/tests/coretests/src/android/service/controls/ControlProviderServiceTest.java242
-rw-r--r--core/tests/coretests/src/android/service/controls/actions/ControlActionTest.java (renamed from core/tests/coretests/src/android/service/controls/ControlActionTest.java)14
-rw-r--r--core/tests/coretests/src/android/service/controls/templates/ControlTemplateTest.java (renamed from core/tests/coretests/src/android/service/controls/ControlTemplateTest.java)100
-rw-r--r--core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java17
-rw-r--r--core/tests/screenshothelpertests/src/com/android/internal/util/ScreenshotHelperTest.java10
-rw-r--r--data/etc/privapp-permissions-platform.xml6
-rw-r--r--docs/html/reference/images/camera2/metadata/android.scaler.rotateAndCrop/crop-region-rotate-90-43-ratio.pngbin0 -> 28061 bytes
-rw-r--r--libs/hwui/hwui/Bitmap.cpp34
-rw-r--r--libs/hwui/hwui/Bitmap.h12
-rw-r--r--libs/input/PointerController.h2
-rw-r--r--libs/services/Android.bp1
-rw-r--r--libs/services/include/android/os/StatsLogEventWrapper.h127
-rw-r--r--libs/services/src/os/StatsLogEventWrapper.cpp127
-rw-r--r--location/java/android/location/GnssClock.java217
-rw-r--r--location/java/android/location/GnssMeasurement.java320
-rw-r--r--location/java/android/location/LocationManager.java2
-rw-r--r--location/java/android/location/LocationManagerInternal.java44
-rw-r--r--location/java/com/android/internal/location/ILocationProvider.aidl2
-rw-r--r--location/java/com/android/internal/location/ILocationProviderManager.aidl2
-rw-r--r--location/lib/api/current.txt7
-rw-r--r--location/lib/java/com/android/location/provider/LocationProviderBase.java75
-rw-r--r--media/java/android/media/MediaCodecInfo.java8
-rw-r--r--media/java/android/media/MediaFormat.java12
-rw-r--r--media/java/android/media/MediaMetadataRetriever.java272
-rw-r--r--media/java/android/media/audiofx/AudioEffect.java8
-rw-r--r--media/java/android/media/tv/DvbDeviceInfo.java19
-rw-r--r--media/java/android/media/tv/TvContentRating.java7
-rw-r--r--media/java/android/media/tv/TvInputManager.java15
-rw-r--r--media/jni/android_media_MediaCodec.cpp140
-rw-r--r--media/jni/android_media_MediaMetadataRetriever.cpp2
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java6
-rw-r--r--packages/CarSystemUI/res/layout/super_notification_shade.xml18
-rw-r--r--packages/CarSystemUI/res/layout/super_status_bar.xml19
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBar.java2
-rw-r--r--packages/FusedLocation/test/src/com/android/location/fused/tests/FusedLocationServiceTest.java2
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java2
-rw-r--r--packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java4
-rw-r--r--packages/Shell/AndroidManifest.xml7
-rw-r--r--packages/SystemUI/res/drawable/ic_bubble_overflow_button.xml23
-rw-r--r--packages/SystemUI/res/layout/bubble_overflow_activity.xml7
-rw-r--r--packages/SystemUI/res/layout/bubble_overflow_button.xml24
-rw-r--r--packages/SystemUI/res/values/config.xml1
-rw-r--r--packages/SystemUI/res/values/dimens.xml3
-rw-r--r--packages/SystemUI/res/values/integers.xml6
-rw-r--r--packages/SystemUI/res/values/styles.xml2
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl9
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/ThumbnailData.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/DumpController.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java393
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BadgedImageView.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java30
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java35
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java49
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java43
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleExperimentConfig.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java107
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java148
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java43
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt87
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt59
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/controller/ServiceWrapper.kt (renamed from packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderServiceWrapper.kt)39
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingControllerImpl.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java65
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshotLegacy.java70
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java19
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationModeController.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java20
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayManager.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt9
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManagerTest.kt45
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsProviderServiceWrapperTest.kt127
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/controls/controller/ServiceWrapperTest.kt157
-rw-r--r--packages/Tethering/apex/AndroidManifest.xml4
-rw-r--r--packages/Tethering/common/TetheringLib/Android.bp14
-rw-r--r--packages/Tethering/common/TetheringLib/src/android/net/ITetheringConnector.aidl5
-rw-r--r--packages/Tethering/common/TetheringLib/src/android/net/TetheredClient.aidl (renamed from core/java/android/service/controls/templates/ControlTemplate.aidl)9
-rw-r--r--packages/Tethering/common/TetheringLib/src/android/net/TetheredClient.java212
-rw-r--r--packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java195
-rw-r--r--packages/Tethering/common/TetheringLib/src/android/net/TetheringRequestParcel.aidl30
-rw-r--r--packages/Tethering/res/values/config.xml6
-rw-r--r--packages/Tethering/res/values/overlayable.xml1
-rw-r--r--packages/Tethering/src/android/net/ip/IpServer.java3
-rw-r--r--packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java55
-rw-r--r--packages/Tethering/src/com/android/server/connectivity/tethering/TetheringConfiguration.java8
-rw-r--r--packages/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java9
-rw-r--r--packages/Tethering/tests/unit/src/android/net/TetheredClientTest.kt95
-rw-r--r--packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java80
-rw-r--r--proto/src/task_snapshot.proto1
-rw-r--r--services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java5
-rw-r--r--services/autofill/java/com/android/server/autofill/InlineSuggestionFactory.java17
-rw-r--r--services/autofill/java/com/android/server/autofill/ui/InlineSuggestionUi.java125
-rw-r--r--services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java229
-rw-r--r--services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java12
-rw-r--r--services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java7
-rw-r--r--services/core/java/android/app/usage/UsageStatsManagerInternal.java18
-rw-r--r--services/core/java/android/content/pm/PackageManagerInternal.java14
-rw-r--r--services/core/java/com/android/server/ConnectivityService.java50
-rw-r--r--services/core/java/com/android/server/LocationManagerService.java122
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java30
-rw-r--r--services/core/java/com/android/server/am/UserController.java9
-rw-r--r--services/core/java/com/android/server/appop/AppOpsService.java16
-rw-r--r--services/core/java/com/android/server/connectivity/Vpn.java2
-rw-r--r--services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java57
-rw-r--r--services/core/java/com/android/server/integrity/engine/RuleEvaluationEngine.java15
-rw-r--r--services/core/java/com/android/server/integrity/engine/RuleEvaluator.java8
-rw-r--r--services/core/java/com/android/server/integrity/model/BitOutputStream.java2
-rw-r--r--services/core/java/com/android/server/integrity/parser/BinaryFileOperations.java3
-rw-r--r--services/core/java/com/android/server/integrity/parser/RuleBinaryParser.java17
-rw-r--r--services/core/java/com/android/server/integrity/parser/RuleXmlParser.java14
-rw-r--r--services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java21
-rw-r--r--services/core/java/com/android/server/integrity/serializer/RuleIndexingDetailsIdentifier.java14
-rw-r--r--services/core/java/com/android/server/integrity/serializer/RuleXmlSerializer.java13
-rw-r--r--services/core/java/com/android/server/location/AbstractLocationProvider.java47
-rw-r--r--services/core/java/com/android/server/location/ContextHubClientBroker.java19
-rw-r--r--services/core/java/com/android/server/location/ContextHubServiceUtil.java14
-rw-r--r--services/core/java/com/android/server/location/GnssLocationProvider.java2
-rw-r--r--services/core/java/com/android/server/location/LocationProviderProxy.java12
-rw-r--r--services/core/java/com/android/server/location/MockProvider.java17
-rw-r--r--services/core/java/com/android/server/location/MockableLocationProvider.java6
-rw-r--r--services/core/java/com/android/server/location/PassiveProvider.java2
-rwxr-xr-xservices/core/java/com/android/server/notification/NotificationManagerService.java30
-rw-r--r--services/core/java/com/android/server/notification/NotificationRecord.java16
-rw-r--r--services/core/java/com/android/server/notification/NotificationRecordLogger.java142
-rw-r--r--services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java69
-rw-r--r--services/core/java/com/android/server/om/OverlayActorEnforcer.java5
-rw-r--r--services/core/java/com/android/server/pm/AppsFilter.java2
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java155
-rw-r--r--services/core/java/com/android/server/pm/ParallelPackageParser.java56
-rw-r--r--services/core/java/com/android/server/rollback/Rollback.java40
-rw-r--r--services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java54
-rw-r--r--services/core/java/com/android/server/rollback/RollbackStore.java18
-rw-r--r--services/core/java/com/android/server/stats/pull/StatsPullAtomService.java66
-rw-r--r--services/core/java/com/android/server/wallpaper/WallpaperManagerService.java3
-rw-r--r--services/core/java/com/android/server/webkit/WebViewUpdateService.java32
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java107
-rw-r--r--services/core/java/com/android/server/wm/ActivityStack.java49
-rw-r--r--services/core/java/com/android/server/wm/ActivityStackSupervisor.java24
-rw-r--r--services/core/java/com/android/server/wm/ActivityStartInterceptor.java39
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerService.java70
-rw-r--r--services/core/java/com/android/server/wm/AppTaskImpl.java9
-rw-r--r--services/core/java/com/android/server/wm/ConfigurationContainer.java61
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java16
-rw-r--r--services/core/java/com/android/server/wm/LockTaskController.java16
-rw-r--r--services/core/java/com/android/server/wm/RootWindowContainer.java16
-rw-r--r--services/core/java/com/android/server/wm/Task.java94
-rw-r--r--services/core/java/com/android/server/wm/TaskSnapshotController.java4
-rw-r--r--services/core/java/com/android/server/wm/TaskSnapshotLoader.java4
-rw-r--r--services/core/java/com/android/server/wm/TaskSnapshotPersister.java11
-rw-r--r--services/core/java/com/android/server/wm/WindowContainer.java53
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java30
-rw-r--r--services/core/jni/com_android_server_input_InputManagerService.cpp32
-rw-r--r--services/core/jni/com_android_server_location_GnssLocationProvider.cpp43
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java7
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java327
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/appop/AppOpsServiceTest.java3
-rw-r--r--services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java22
-rw-r--r--services/tests/servicestests/src/com/android/server/integrity/IntegrityFileManagerTest.java16
-rw-r--r--services/tests/servicestests/src/com/android/server/integrity/engine/RuleEvaluationEngineTest.java113
-rw-r--r--services/tests/servicestests/src/com/android/server/integrity/engine/RuleEvaluatorTest.java41
-rw-r--r--services/tests/servicestests/src/com/android/server/integrity/parser/BinaryFileOperationsTest.java3
-rw-r--r--services/tests/servicestests/src/com/android/server/integrity/parser/RuleBinaryParserTest.java14
-rw-r--r--services/tests/servicestests/src/com/android/server/integrity/parser/RuleXmlParserTest.java241
-rw-r--r--services/tests/servicestests/src/com/android/server/integrity/serializer/RuleBinarySerializerTest.java31
-rw-r--r--services/tests/servicestests/src/com/android/server/integrity/serializer/RuleIndexingDetailsIdentifierTest.java20
-rw-r--r--services/tests/servicestests/src/com/android/server/integrity/serializer/RuleXmlSerializerTest.java18
-rw-r--r--services/tests/servicestests/src/com/android/server/integrity/utils/TestUtils.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java5
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/ParallelPackageParserTest.java13
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/SuspendDialogInfoTest.java25
-rw-r--r--services/tests/servicestests/src/com/android/server/usage/IntervalStatsTests.java3
-rw-r--r--services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java18
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java3
-rwxr-xr-xservices/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java65
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerFake.java53
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java17
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java23
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java35
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java17
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java10
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java4
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java11
-rw-r--r--services/usage/java/com/android/server/usage/IntervalStats.java17
-rw-r--r--services/usage/java/com/android/server/usage/UsageStatsProtoV2.java9
-rw-r--r--services/usage/java/com/android/server/usage/UsageStatsService.java15
-rw-r--r--telecomm/java/android/telecom/DisconnectCause.java3
-rw-r--r--telecomm/java/android/telecom/ParcelableCallAnalytics.java23
-rw-r--r--telecomm/java/android/telecom/PhoneAccount.java13
-rw-r--r--telecomm/java/android/telecom/TelecomManager.java69
-rw-r--r--telephony/OWNERS12
-rw-r--r--telephony/common/com/android/internal/telephony/CarrierAppUtils.java206
-rw-r--r--telephony/common/com/android/internal/telephony/GsmAlphabet.java10
-rw-r--r--telephony/common/com/google/android/mms/util/SqliteWrapper.java3
-rw-r--r--telephony/java/android/service/euicc/IEuiccServiceDumpResultCallback.aidl (renamed from core/java/android/service/euicc/IEuiccServiceDumpResultCallback.aidl)0
-rwxr-xr-xtelephony/java/android/telephony/CarrierConfigManager.java24
-rw-r--r--telephony/java/android/telephony/CellSignalStrengthNr.java24
-rw-r--r--telephony/java/android/telephony/ModemActivityInfo.java16
-rw-r--r--telephony/java/android/telephony/ServiceState.java12
-rw-r--r--telephony/java/android/telephony/SubscriptionManager.java20
-rw-r--r--telephony/java/android/telephony/TelephonyManager.java77
-rw-r--r--telephony/java/android/telephony/euicc/EuiccManager.java160
-rw-r--r--telephony/java/android/telephony/ims/RcsUceAdapter.java11
-rw-r--r--telephony/java/android/telephony/ims/feature/MmTelFeature.java1
-rw-r--r--telephony/java/com/android/internal/telephony/ITelephony.aidl5
-rw-r--r--telephony/java/com/android/internal/telephony/RILConstants.java1
-rw-r--r--telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl4
-rw-r--r--tests/net/common/java/android/net/LinkAddressTest.java84
-rw-r--r--tests/net/common/java/android/net/NetworkCapabilitiesTest.java3
-rw-r--r--tests/net/java/android/net/ConnectivityDiagnosticsManagerTest.java52
-rw-r--r--tests/net/java/android/net/Ikev2VpnProfileTest.java360
-rw-r--r--tests/net/java/android/net/VpnManagerTest.java83
-rw-r--r--tests/net/java/com/android/internal/net/VpnProfileTest.java185
-rw-r--r--tests/net/java/com/android/server/ConnectivityServiceTest.java18
-rw-r--r--wifi/java/android/net/wifi/IWifiManager.aidl2
-rw-r--r--wifi/java/android/net/wifi/WifiConfiguration.java3
-rw-r--r--wifi/java/android/net/wifi/WifiManager.java55
-rw-r--r--wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java45
-rw-r--r--wifi/java/com/android/server/wifi/BaseWifiService.java5
-rw-r--r--wifi/tests/src/android/net/wifi/WifiManagerTest.java39
-rw-r--r--wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java3
427 files changed, 15819 insertions, 5419 deletions
diff --git a/Android.bp b/Android.bp
index 3ecb5a9340d2..9411eeca834c 100644
--- a/Android.bp
+++ b/Android.bp
@@ -340,6 +340,7 @@ java_library {
"android.hardware.cas-V1.2-java",
"android.hardware.contexthub-V1.0-java",
"android.hardware.gnss-V1.0-java",
+ "android.hardware.gnss-V2.1-java",
"android.hardware.health-V1.0-java-constants",
"android.hardware.radio-V1.0-java",
"android.hardware.radio-V1.1-java",
diff --git a/apex/blobstore/framework/java/android/app/blob/BlobHandle.java b/apex/blobstore/framework/java/android/app/blob/BlobHandle.java
index 60c313683240..f7e6a987ded3 100644
--- a/apex/blobstore/framework/java/android/app/blob/BlobHandle.java
+++ b/apex/blobstore/framework/java/android/app/blob/BlobHandle.java
@@ -15,13 +15,26 @@
*/
package android.app.blob;
+import static android.app.blob.XmlTags.ATTR_ALGO;
+import static android.app.blob.XmlTags.ATTR_DIGEST;
+import static android.app.blob.XmlTags.ATTR_EXPIRY_TIME;
+import static android.app.blob.XmlTags.ATTR_LABEL;
+import static android.app.blob.XmlTags.ATTR_TAG;
+
import android.annotation.CurrentTimeMillisLong;
import android.annotation.NonNull;
import android.os.Parcel;
import android.os.Parcelable;
+import android.util.Base64;
+import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
+import com.android.internal.util.XmlUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlSerializer;
+import java.io.IOException;
import java.util.Arrays;
import java.util.Objects;
@@ -41,17 +54,20 @@ public final class BlobHandle implements Parcelable {
* @hide
*/
@NonNull public final String algorithm;
+
/**
* Hash of the blob this handle is representing using {@link #algorithm}.
*
* @hide
*/
@NonNull public final byte[] digest;
+
/**
* Label of the blob that can be surfaced to the user.
* @hide
*/
@NonNull public final CharSequence label;
+
/**
* Time in milliseconds after which the blob should be invalidated and not
* allowed to be accessed by any other app, in {@link System#currentTimeMillis()} timebase.
@@ -59,6 +75,7 @@ public final class BlobHandle implements Parcelable {
* @hide
*/
@CurrentTimeMillisLong public final long expiryTimeMillis;
+
/**
* An opaque {@link String} associated with the blob.
*
@@ -197,6 +214,15 @@ public final class BlobHandle implements Parcelable {
return Objects.hash(algorithm, Arrays.hashCode(digest), label, expiryTimeMillis, tag);
}
+ /** @hide */
+ public void dump(IndentingPrintWriter fout) {
+ fout.println("algo: " + algorithm);
+ fout.println("digest: " + Base64.encodeToString(digest, Base64.NO_WRAP));
+ fout.println("label: " + label);
+ fout.println("expiryMs: " + expiryTimeMillis);
+ fout.println("tag: " + tag);
+ }
+
public static final @NonNull Creator<BlobHandle> CREATOR = new Creator<BlobHandle>() {
@Override
public @NonNull BlobHandle createFromParcel(@NonNull Parcel source) {
@@ -208,4 +234,25 @@ public final class BlobHandle implements Parcelable {
return new BlobHandle[size];
}
};
+
+ /** @hide */
+ public void writeToXml(@NonNull XmlSerializer out) throws IOException {
+ XmlUtils.writeStringAttribute(out, ATTR_ALGO, algorithm);
+ XmlUtils.writeByteArrayAttribute(out, ATTR_DIGEST, digest);
+ XmlUtils.writeStringAttribute(out, ATTR_LABEL, label);
+ XmlUtils.writeLongAttribute(out, ATTR_EXPIRY_TIME, expiryTimeMillis);
+ XmlUtils.writeStringAttribute(out, ATTR_TAG, tag);
+ }
+
+ /** @hide */
+ @NonNull
+ public static BlobHandle createFromXml(@NonNull XmlPullParser in) throws IOException {
+ final String algo = XmlUtils.readStringAttribute(in, ATTR_ALGO);
+ final byte[] digest = XmlUtils.readByteArrayAttribute(in, ATTR_DIGEST);
+ final CharSequence label = XmlUtils.readStringAttribute(in, ATTR_LABEL);
+ final long expiryTimeMs = XmlUtils.readLongAttribute(in, ATTR_EXPIRY_TIME);
+ final String tag = XmlUtils.readStringAttribute(in, ATTR_TAG);
+
+ return BlobHandle.create(algo, digest, label, expiryTimeMs, tag);
+ }
}
diff --git a/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java b/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java
index 47af7c0a5ed9..8cea645f4e71 100644
--- a/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java
+++ b/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java
@@ -25,23 +25,115 @@ import android.annotation.SystemService;
import android.content.Context;
import android.os.ParcelFileDescriptor;
import android.os.ParcelableException;
+import android.os.RemoteCallback;
import android.os.RemoteException;
import com.android.internal.util.function.pooled.PooledLambda;
import java.io.Closeable;
import java.io.IOException;
+import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
import java.util.function.Consumer;
/**
* This class provides access to the blob store managed by the system.
*
- * Apps can publish data blobs which might be useful for other apps on the device to be
- * managed by the system and apps that would like to access these data blobs can do so
- * by addressing them via their cryptographically secure hashes.
+ * <p> Apps can publish and access a data blob using a {@link BlobHandle} object which can
+ * be created with {@link BlobHandle#createWithSha256(byte[], CharSequence, long, String)}.
+ * This {@link BlobHandle} object encapsulates the following pieces of information used for
+ * identifying the blobs:
+ * <ul>
+ * <li> {@link BlobHandle#getSha256Digest()}
+ * <li> {@link BlobHandle#getLabel()}
+ * <li> {@link BlobHandle#getExpiryTimeMillis()}
+ * <li> {@link BlobHandle#getTag()}
+ * </ul>
+ * For two {@link BlobHandle} objects to be considered identical, all these pieces of information
+ * must be equal.
*
- * TODO: More documentation.
+ * <p> For contributing a new data blob, an app needs to create a session using
+ * {@link BlobStoreManager#createSession(BlobHandle)} and then open this session for writing using
+ * {@link BlobStoreManager#openSession(long)}.
+ *
+ * <p> The following code snippet shows how to create and open a session for writing:
+ * <pre class="prettyprint">
+ * final long sessionId = blobStoreManager.createSession(blobHandle);
+ * try (BlobStoreManager.Session session = blobStoreManager.openSession(sessionId)) {
+ * try (ParcelFileDescriptor pfd = new ParcelFileDescriptor.AutoCloseOutputStream(
+ * session.openWrite(offsetBytes, lengthBytes))) {
+ * writeData(pfd);
+ * }
+ * }
+ * </pre>
+ *
+ * <p> If all the data could not be written in a single attempt, apps can close this session
+ * and re-open it again using the session id obtained via
+ * {@link BlobStoreManager#createSession(BlobHandle)}. Note that the session data is persisted
+ * and can be re-opened for completing the data contribution, even across device reboots.
+ *
+ * <p> After the data is written to the session, it can be committed using
+ * {@link Session#commit(Executor, Consumer)}. Until the session is committed, data written
+ * to the session will not be shared with any app.
+ *
+ * <p class="note"> Once a session is committed using {@link Session#commit(Executor, Consumer)},
+ * any data written as part of this session is sealed and cannot be modified anymore.
+ *
+ * <p> Before committing the session, apps can indicate which apps are allowed to access the
+ * contributed data using one or more of the following access modes:
+ * <ul>
+ * <li> {@link Session#allowPackageAccess(String, byte[])} which will allow whitelisting
+ * specific packages to access the blobs.
+ * <li> {@link Session#allowSameSignatureAccess()} which will allow only apps which are signed
+ * with the same certificate as the app which contributed the blob to access it.
+ * <li> {@link Session#allowPublicAccess()} which will allow any app on the device to access
+ * the blob.
+ * </ul>
+ *
+ * <p> The following code snippet shows how to specify the access mode and commit the session:
+ * <pre class="prettyprint">
+ * try (BlobStoreManager.Session session = blobStoreManager.openSession(sessionId)) {
+ * try (ParcelFileDescriptor pfd = new ParcelFileDescriptor.AutoCloseOutputStream(
+ * session.openWrite(offsetBytes, lengthBytes))) {
+ * writeData(pfd);
+ * }
+ * session.allowSameSignatureAccess();
+ * session.allowPackageAccess(packageName, certificate);
+ * session.commit(executor, callback);
+ * }
+ * </pre>
+ *
+ * <p> Apps that satisfy at least one of the access mode constraints specified by the publisher
+ * of the data blob will be able to access it.
+ *
+ * <p> A data blob published without specifying any of
+ * these access modes will be considered private and only the app that contributed the data
+ * blob will be allowed to access it. This is still useful for overall device system health as
+ * the System can try to keep one copy of data blob on disk when multiple apps contribute the
+ * same data.
+ *
+ * <p class="note"> It is strongly recommended that apps use one of
+ * {@link Session#allowPackageAccess(String, byte[])} or {@link Session#allowSameSignatureAccess()}
+ * when they know, ahead of time, the set of apps they would like to share the blobs with.
+ * {@link Session#allowPublicAccess()} is meant for publicly available data committed from
+ * libraries and SDKs.
+ *
+ * <p> Once a data blob is committed with {@link Session#commit(Executor, Consumer)}, it
+ * can be accessed using {@link BlobStoreManager#openBlob(BlobHandle)}, assuming the caller
+ * satisfies constraints of any of the access modes associated with that data blob. An app may
+ * acquire a lease on a blob with {@link BlobStoreManager#acquireLease(BlobHandle, int)} and
+ * release the lease with {@link BlobStoreManager#releaseLease(BlobHandle)}. A blob will not be
+ * deleted from the system while there is at least one app leasing it.
+ *
+ * <p> The following code snippet shows how to access the data blob:
+ * <pre class="prettyprint">
+ * try (ParcelFileDescriptor pfd = new ParcelFileDescriptor.AutoCloseInputStream(
+ * blobStoreManager.openBlob(blobHandle)) {
+ * useData(pfd);
+ * }
+ * </pre>
*/
@SystemService(Context.BLOB_STORE_SERVICE)
public class BlobStoreManager {
@@ -265,6 +357,25 @@ public class BlobStoreManager {
}
/**
+ * Wait until any pending tasks (like persisting data to disk) have finished.
+ *
+ * @hide
+ */
+ public void waitForIdle(long timeoutMillis) throws InterruptedException, TimeoutException {
+ try {
+ final CountDownLatch countDownLatch = new CountDownLatch(1);
+ mService.waitForIdle(new RemoteCallback((result) -> countDownLatch.countDown()));
+ if (!countDownLatch.await(timeoutMillis, TimeUnit.MILLISECONDS)) {
+ throw new TimeoutException("Timed out waiting for service to become idle");
+ }
+ } catch (ParcelableException e) {
+ throw new RuntimeException(e);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Represents an ongoing session of a blob's contribution to the blob store managed by the
* system.
*
@@ -323,6 +434,28 @@ public class BlobStoreManager {
}
/**
+ * Opens a file descriptor to read the blob content already written into this session.
+ *
+ * @return a {@link ParcelFileDescriptor} for reading from the blob file.
+ *
+ * @throws IOException when there is an I/O error while opening the file to read.
+ * @throws SecurityException when the caller is not the owner of the session.
+ * @throws IllegalStateException when the caller tries to read the file after it is
+ * abandoned (using {@link #abandon()})
+ * or closed (using {@link #close()}).
+ */
+ public @NonNull ParcelFileDescriptor openRead() throws IOException {
+ try {
+ return mSession.openRead();
+ } catch (ParcelableException e) {
+ e.maybeRethrow(IOException.class);
+ throw new RuntimeException(e);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Gets the size of the blob file that was written to the session so far.
*
* @return the size of the blob file so far.
diff --git a/apex/blobstore/framework/java/android/app/blob/IBlobStoreManager.aidl b/apex/blobstore/framework/java/android/app/blob/IBlobStoreManager.aidl
index dfbf78f4009b..e2128b421746 100644
--- a/apex/blobstore/framework/java/android/app/blob/IBlobStoreManager.aidl
+++ b/apex/blobstore/framework/java/android/app/blob/IBlobStoreManager.aidl
@@ -17,6 +17,7 @@ package android.app.blob;
import android.app.blob.BlobHandle;
import android.app.blob.IBlobStoreSession;
+import android.os.RemoteCallback;
/** {@hide} */
interface IBlobStoreManager {
@@ -28,4 +29,6 @@ interface IBlobStoreManager {
void acquireLease(in BlobHandle handle, int descriptionResId, long leaseTimeout,
in String packageName);
void releaseLease(in BlobHandle handle, in String packageName);
+
+ void waitForIdle(in RemoteCallback callback);
} \ No newline at end of file
diff --git a/apex/blobstore/framework/java/android/app/blob/IBlobStoreSession.aidl b/apex/blobstore/framework/java/android/app/blob/IBlobStoreSession.aidl
index 4ae919bfedab..4035b96938d9 100644
--- a/apex/blobstore/framework/java/android/app/blob/IBlobStoreSession.aidl
+++ b/apex/blobstore/framework/java/android/app/blob/IBlobStoreSession.aidl
@@ -21,6 +21,7 @@ import android.os.ParcelFileDescriptor;
/** {@hide} */
interface IBlobStoreSession {
ParcelFileDescriptor openWrite(long offsetBytes, long lengthBytes);
+ ParcelFileDescriptor openRead();
void allowPackageAccess(in String packageName, in byte[] certificate);
void allowSameSignatureAccess();
diff --git a/apex/blobstore/framework/java/android/app/blob/XmlTags.java b/apex/blobstore/framework/java/android/app/blob/XmlTags.java
new file mode 100644
index 000000000000..803c9a40e5ea
--- /dev/null
+++ b/apex/blobstore/framework/java/android/app/blob/XmlTags.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.app.blob;
+
+/** @hide */
+public final class XmlTags {
+ public static final String ATTR_VERSION = "v";
+
+ public static final String TAG_SESSIONS = "ss";
+ public static final String TAG_BLOBS = "bs";
+
+ // For BlobStoreSession
+ public static final String TAG_SESSION = "s";
+ public static final String ATTR_ID = "id";
+ public static final String ATTR_PACKAGE = "p";
+ public static final String ATTR_UID = "u";
+
+ // For BlobMetadata
+ public static final String TAG_BLOB = "b";
+ public static final String ATTR_USER_ID = "us";
+
+ // For BlobAccessMode
+ public static final String TAG_ACCESS_MODE = "am";
+ public static final String ATTR_TYPE = "t";
+ public static final String TAG_WHITELISTED_PACKAGE = "wl";
+ public static final String ATTR_CERTIFICATE = "ct";
+
+ // For BlobHandle
+ public static final String TAG_BLOB_HANDLE = "bh";
+ public static final String ATTR_ALGO = "al";
+ public static final String ATTR_DIGEST = "dg";
+ public static final String ATTR_LABEL = "lbl";
+ public static final String ATTR_EXPIRY_TIME = "ex";
+ public static final String ATTR_TAG = "tg";
+
+ // For committer
+ public static final String TAG_COMMITTER = "c";
+
+ // For leasee
+ public static final String TAG_LEASEE = "l";
+ public static final String ATTR_DESCRIPTION_RES_ID = "rid";
+}
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobAccessMode.java b/apex/blobstore/service/java/com/android/server/blob/BlobAccessMode.java
index 357250a3244c..ec7ba287acec 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobAccessMode.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobAccessMode.java
@@ -15,12 +15,27 @@
*/
package com.android.server.blob;
+import static android.app.blob.XmlTags.ATTR_CERTIFICATE;
+import static android.app.blob.XmlTags.ATTR_PACKAGE;
+import static android.app.blob.XmlTags.ATTR_TYPE;
+import static android.app.blob.XmlTags.TAG_WHITELISTED_PACKAGE;
+
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.content.Context;
import android.content.pm.PackageManager;
import android.util.ArraySet;
+import android.util.Base64;
+import android.util.DebugUtils;
+
+import com.android.internal.util.IndentingPrintWriter;
+import com.android.internal.util.XmlUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Arrays;
@@ -40,10 +55,10 @@ class BlobAccessMode {
ACCESS_TYPE_WHITELIST,
})
@interface AccessType {}
- static final int ACCESS_TYPE_PRIVATE = 1 << 0;
- static final int ACCESS_TYPE_PUBLIC = 1 << 1;
- static final int ACCESS_TYPE_SAME_SIGNATURE = 1 << 2;
- static final int ACCESS_TYPE_WHITELIST = 1 << 3;
+ public static final int ACCESS_TYPE_PRIVATE = 1 << 0;
+ public static final int ACCESS_TYPE_PUBLIC = 1 << 1;
+ public static final int ACCESS_TYPE_SAME_SIGNATURE = 1 << 2;
+ public static final int ACCESS_TYPE_WHITELIST = 1 << 3;
private int mAccessType = ACCESS_TYPE_PRIVATE;
@@ -112,6 +127,51 @@ class BlobAccessMode {
return false;
}
+ void dump(IndentingPrintWriter fout) {
+ fout.println("accessType: " + DebugUtils.flagsToString(
+ BlobAccessMode.class, "ACCESS_TYPE_", mAccessType));
+ fout.print("Whitelisted pkgs:");
+ if (mWhitelistedPackages.isEmpty()) {
+ fout.println(" (Empty)");
+ } else {
+ fout.increaseIndent();
+ for (int i = 0, count = mWhitelistedPackages.size(); i < count; ++i) {
+ fout.println(mWhitelistedPackages.valueAt(i).toString());
+ }
+ fout.decreaseIndent();
+ }
+ }
+
+ void writeToXml(@NonNull XmlSerializer out) throws IOException {
+ XmlUtils.writeIntAttribute(out, ATTR_TYPE, mAccessType);
+ for (int i = 0, count = mWhitelistedPackages.size(); i < count; ++i) {
+ out.startTag(null, TAG_WHITELISTED_PACKAGE);
+ final PackageIdentifier packageIdentifier = mWhitelistedPackages.valueAt(i);
+ XmlUtils.writeStringAttribute(out, ATTR_PACKAGE, packageIdentifier.packageName);
+ XmlUtils.writeByteArrayAttribute(out, ATTR_CERTIFICATE, packageIdentifier.certificate);
+ out.endTag(null, TAG_WHITELISTED_PACKAGE);
+ }
+ }
+
+ @NonNull
+ static BlobAccessMode createFromXml(@NonNull XmlPullParser in)
+ throws IOException, XmlPullParserException {
+ final BlobAccessMode blobAccessMode = new BlobAccessMode();
+
+ final int accessType = XmlUtils.readIntAttribute(in, ATTR_TYPE);
+ blobAccessMode.mAccessType = accessType;
+
+ final int depth = in.getDepth();
+ while (XmlUtils.nextElementWithin(in, depth)) {
+ if (TAG_WHITELISTED_PACKAGE.equals(in.getName())) {
+ final String packageName = XmlUtils.readStringAttribute(in, ATTR_PACKAGE);
+ final byte[] certificate = XmlUtils.readByteArrayAttribute(in, ATTR_CERTIFICATE);
+ blobAccessMode.allowPackageAccess(packageName, certificate);
+ }
+ }
+ return blobAccessMode;
+ }
+
private static final class PackageIdentifier {
public final String packageName;
public final byte[] certificate;
@@ -143,5 +203,11 @@ class BlobAccessMode {
public int hashCode() {
return Objects.hash(packageName, Arrays.hashCode(certificate));
}
+
+ @Override
+ public String toString() {
+ return "[" + packageName + ", "
+ + Base64.encodeToString(certificate, Base64.NO_WRAP) + "]";
+ }
}
}
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java b/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java
index d3a227152627..e9838d6b9712 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java
@@ -15,20 +15,45 @@
*/
package com.android.server.blob;
+import static android.app.blob.XmlTags.ATTR_DESCRIPTION_RES_ID;
+import static android.app.blob.XmlTags.ATTR_EXPIRY_TIME;
+import static android.app.blob.XmlTags.ATTR_ID;
+import static android.app.blob.XmlTags.ATTR_PACKAGE;
+import static android.app.blob.XmlTags.ATTR_UID;
+import static android.app.blob.XmlTags.ATTR_USER_ID;
+import static android.app.blob.XmlTags.TAG_ACCESS_MODE;
+import static android.app.blob.XmlTags.TAG_BLOB_HANDLE;
+import static android.app.blob.XmlTags.TAG_COMMITTER;
+import static android.app.blob.XmlTags.TAG_LEASEE;
import static android.system.OsConstants.O_RDONLY;
+import static com.android.server.blob.BlobStoreConfig.TAG;
+
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.blob.BlobHandle;
import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
import android.os.ParcelFileDescriptor;
import android.os.RevocableFileDescriptor;
+import android.os.UserHandle;
import android.system.ErrnoException;
import android.system.Os;
import android.util.ArrayMap;
import android.util.ArraySet;
+import android.util.Slog;
+import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.IndentingPrintWriter;
+import com.android.internal.util.XmlUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+import java.io.File;
import java.io.FileDescriptor;
import java.io.IOException;
import java.util.Objects;
@@ -37,8 +62,10 @@ class BlobMetadata {
private final Object mMetadataLock = new Object();
private final Context mContext;
- private final long mBlobId;
- private final BlobHandle mBlobHandle;
+
+ public final long blobId;
+ public final BlobHandle blobHandle;
+ public final int userId;
@GuardedBy("mMetadataLock")
private final ArraySet<Committer> mCommitters = new ArraySet<>();
@@ -57,15 +84,47 @@ class BlobMetadata {
private final ArrayMap<String, ArraySet<RevocableFileDescriptor>> mRevocableFds =
new ArrayMap<>();
- BlobMetadata(Context context, long blobId, BlobHandle blobHandle) {
+ // Do not access this directly, instead use getSessionFile().
+ private File mBlobFile;
+
+ BlobMetadata(Context context, long blobId, BlobHandle blobHandle, int userId) {
mContext = context;
- mBlobId = blobId;
- mBlobHandle = blobHandle;
+ this.blobId = blobId;
+ this.blobHandle = blobHandle;
+ this.userId = userId;
+ }
+
+ void addCommitter(@NonNull Committer committer) {
+ synchronized (mMetadataLock) {
+ mCommitters.add(committer);
+ }
+ }
+
+ void addCommitters(ArraySet<Committer> committers) {
+ synchronized (mMetadataLock) {
+ mCommitters.addAll(committers);
+ }
+ }
+
+ void removeCommitter(@NonNull String packageName, int uid) {
+ synchronized (mMetadataLock) {
+ mCommitters.removeIf((committer) ->
+ committer.uid == uid && committer.packageName.equals(packageName));
+ }
}
- void addCommitter(String packageName, int uid, BlobAccessMode blobAccessMode) {
+ void removeInvalidCommitters(SparseArray<String> packages) {
synchronized (mMetadataLock) {
- mCommitters.add(new Committer(packageName, uid, blobAccessMode));
+ mCommitters.removeIf(committer ->
+ !committer.packageName.equals(packages.get(committer.uid)));
+ }
+ }
+
+ @Nullable
+ Committer getExistingCommitter(@NonNull Committer newCommitter) {
+ synchronized (mCommitters) {
+ final int index = mCommitters.indexOf(newCommitter);
+ return index >= 0 ? mCommitters.valueAt(index) : null;
}
}
@@ -77,9 +136,23 @@ class BlobMetadata {
}
}
+ void addLeasees(ArraySet<Leasee> leasees) {
+ synchronized (mMetadataLock) {
+ mLeasees.addAll(leasees);
+ }
+ }
+
void removeLeasee(String packageName, int uid) {
synchronized (mMetadataLock) {
- mLeasees.remove(new Accessor(packageName, uid));
+ mLeasees.removeIf((leasee) ->
+ leasee.uid == uid && leasee.packageName.equals(packageName));
+ }
+ }
+
+ void removeInvalidLeasees(SparseArray<String> packages) {
+ synchronized (mMetadataLock) {
+ mLeasees.removeIf(leasee ->
+ !leasee.packageName.equals(packages.get(leasee.uid)));
}
}
@@ -114,11 +187,18 @@ class BlobMetadata {
return false;
}
+ File getBlobFile() {
+ if (mBlobFile == null) {
+ mBlobFile = BlobStoreConfig.getBlobFile(blobId);
+ }
+ return mBlobFile;
+ }
+
ParcelFileDescriptor openForRead(String callingPackage) throws IOException {
// TODO: Add limit on opened fds
FileDescriptor fd;
try {
- fd = Os.open(BlobStoreConfig.getBlobFile(mBlobId).getPath(), O_RDONLY, 0);
+ fd = Os.open(getBlobFile().getPath(), O_RDONLY, 0);
} catch (ErrnoException e) {
throw e.rethrowAsIOException();
}
@@ -154,6 +234,94 @@ class BlobMetadata {
return revocableFd.getRevocableFileDescriptor();
}
+ void dump(IndentingPrintWriter fout) {
+ fout.println("blobHandle:");
+ fout.increaseIndent();
+ blobHandle.dump(fout);
+ fout.decreaseIndent();
+
+ fout.println("Committers:");
+ fout.increaseIndent();
+ for (int i = 0, count = mCommitters.size(); i < count; ++i) {
+ final Committer committer = mCommitters.valueAt(i);
+ fout.println("committer " + committer.toString());
+ fout.increaseIndent();
+ committer.dump(fout);
+ fout.decreaseIndent();
+ }
+ fout.decreaseIndent();
+
+ fout.println("Leasees:");
+ fout.increaseIndent();
+ for (int i = 0, count = mLeasees.size(); i < count; ++i) {
+ final Leasee leasee = mLeasees.valueAt(i);
+ fout.println("leasee " + leasee.toString());
+ fout.increaseIndent();
+ leasee.dump(mContext, fout);
+ fout.decreaseIndent();
+ }
+ fout.decreaseIndent();
+
+ fout.println("Open fds: #" + mRevocableFds.size());
+ }
+
+ void writeToXml(XmlSerializer out) throws IOException {
+ synchronized (mMetadataLock) {
+ XmlUtils.writeLongAttribute(out, ATTR_ID, blobId);
+ XmlUtils.writeIntAttribute(out, ATTR_USER_ID, userId);
+
+ out.startTag(null, TAG_BLOB_HANDLE);
+ blobHandle.writeToXml(out);
+ out.endTag(null, TAG_BLOB_HANDLE);
+
+ for (int i = 0, count = mCommitters.size(); i < count; ++i) {
+ out.startTag(null, TAG_COMMITTER);
+ mCommitters.valueAt(i).writeToXml(out);
+ out.endTag(null, TAG_COMMITTER);
+ }
+
+ for (int i = 0, count = mLeasees.size(); i < count; ++i) {
+ out.startTag(null, TAG_LEASEE);
+ mLeasees.valueAt(i).writeToXml(out);
+ out.endTag(null, TAG_LEASEE);
+ }
+ }
+ }
+
+ @Nullable
+ static BlobMetadata createFromXml(Context context, XmlPullParser in)
+ throws XmlPullParserException, IOException {
+ final long blobId = XmlUtils.readLongAttribute(in, ATTR_ID);
+ final int userId = XmlUtils.readIntAttribute(in, ATTR_USER_ID);
+
+ BlobHandle blobHandle = null;
+ final ArraySet<Committer> committers = new ArraySet<>();
+ final ArraySet<Leasee> leasees = new ArraySet<>();
+ final int depth = in.getDepth();
+ while (XmlUtils.nextElementWithin(in, depth)) {
+ if (TAG_BLOB_HANDLE.equals(in.getName())) {
+ blobHandle = BlobHandle.createFromXml(in);
+ } else if (TAG_COMMITTER.equals(in.getName())) {
+ final Committer committer = Committer.createFromXml(in);
+ if (committer != null) {
+ committers.add(committer);
+ }
+ } else if (TAG_LEASEE.equals(in.getName())) {
+ leasees.add(Leasee.createFromXml(in));
+ }
+ }
+
+ if (blobHandle == null) {
+ Slog.wtf(TAG, "blobHandle should be available");
+ return null;
+ }
+
+ final BlobMetadata blobMetadata = new BlobMetadata(context, blobId, blobHandle, userId);
+ blobMetadata.addCommitters(committers);
+ blobMetadata.addLeasees(leasees);
+ return blobMetadata;
+ }
+
static final class Committer extends Accessor {
public final BlobAccessMode blobAccessMode;
@@ -161,6 +329,42 @@ class BlobMetadata {
super(packageName, uid);
this.blobAccessMode = blobAccessMode;
}
+
+ void dump(IndentingPrintWriter fout) {
+ fout.println("accessMode:");
+ fout.increaseIndent();
+ blobAccessMode.dump(fout);
+ fout.decreaseIndent();
+ }
+
+ void writeToXml(@NonNull XmlSerializer out) throws IOException {
+ XmlUtils.writeStringAttribute(out, ATTR_PACKAGE, packageName);
+ XmlUtils.writeIntAttribute(out, ATTR_UID, uid);
+
+ out.startTag(null, TAG_ACCESS_MODE);
+ blobAccessMode.writeToXml(out);
+ out.endTag(null, TAG_ACCESS_MODE);
+ }
+
+ @Nullable
+ static Committer createFromXml(@NonNull XmlPullParser in)
+ throws XmlPullParserException, IOException {
+ final String packageName = XmlUtils.readStringAttribute(in, ATTR_PACKAGE);
+ final int uid = XmlUtils.readIntAttribute(in, ATTR_UID);
+
+ final int depth = in.getDepth();
+ BlobAccessMode blobAccessMode = null;
+ while (XmlUtils.nextElementWithin(in, depth)) {
+ if (TAG_ACCESS_MODE.equals(in.getName())) {
+ blobAccessMode = BlobAccessMode.createFromXml(in);
+ }
+ }
+ if (blobAccessMode == null) {
+ Slog.wtf(TAG, "blobAccessMode should be available");
+ return null;
+ }
+ return new Committer(packageName, uid, blobAccessMode);
+ }
}
static final class Leasee extends Accessor {
@@ -176,6 +380,38 @@ class BlobMetadata {
boolean isStillValid() {
return expiryTimeMillis == 0 || expiryTimeMillis <= System.currentTimeMillis();
}
+
+ void dump(Context context, IndentingPrintWriter fout) {
+ String desc = null;
+ try {
+ final Resources leaseeRes = context.getPackageManager()
+ .getResourcesForApplicationAsUser(packageName, UserHandle.getUserId(uid));
+ desc = leaseeRes.getString(descriptionResId);
+ } catch (PackageManager.NameNotFoundException e) {
+ Slog.d(TAG, "Unknown package in user " + UserHandle.getUserId(uid) + ": "
+ + packageName, e);
+ desc = "<none>";
+ }
+ fout.println("desc: " + desc);
+ fout.println("expiryMs: " + expiryTimeMillis);
+ }
+
+ void writeToXml(@NonNull XmlSerializer out) throws IOException {
+ XmlUtils.writeStringAttribute(out, ATTR_PACKAGE, packageName);
+ XmlUtils.writeIntAttribute(out, ATTR_UID, uid);
+ XmlUtils.writeIntAttribute(out, ATTR_DESCRIPTION_RES_ID, descriptionResId);
+ XmlUtils.writeLongAttribute(out, ATTR_EXPIRY_TIME, expiryTimeMillis);
+ }
+
+ @NonNull
+ static Leasee createFromXml(@NonNull XmlPullParser in) throws IOException {
+ final String packageName = XmlUtils.readStringAttribute(in, ATTR_PACKAGE);
+ final int uid = XmlUtils.readIntAttribute(in, ATTR_UID);
+ final int descriptionResId = XmlUtils.readIntAttribute(in, ATTR_DESCRIPTION_RES_ID);
+ final long expiryTimeMillis = XmlUtils.readLongAttribute(in, ATTR_EXPIRY_TIME);
+
+ return new Leasee(packageName, uid, descriptionResId, expiryTimeMillis);
+ }
}
static class Accessor {
@@ -207,5 +443,10 @@ class BlobMetadata {
public int hashCode() {
return Objects.hash(packageName, uid);
}
+
+ @Override
+ public String toString() {
+ return "[" + packageName + ", " + uid + "]";
+ }
}
}
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobStoreConfig.java b/apex/blobstore/service/java/com/android/server/blob/BlobStoreConfig.java
index b9a4b17c53cc..20661c6f0833 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreConfig.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreConfig.java
@@ -25,6 +25,8 @@ import java.io.File;
class BlobStoreConfig {
public static final String TAG = "BlobStore";
+ public static final int CURRENT_XML_VERSION = 1;
+
@Nullable
public static File prepareBlobFile(long sessionId) {
final File blobsDir = prepareBlobsDir();
@@ -62,6 +64,24 @@ class BlobStoreConfig {
}
@Nullable
+ public static File prepareSessionIndexFile() {
+ final File blobStoreRootDir = prepareBlobStoreRootDir();
+ if (blobStoreRootDir == null) {
+ return null;
+ }
+ return new File(blobStoreRootDir, "sessions_index.xml");
+ }
+
+ @Nullable
+ public static File prepareBlobsIndexFile() {
+ final File blobsStoreRootDir = prepareBlobStoreRootDir();
+ if (blobsStoreRootDir == null) {
+ return null;
+ }
+ return new File(blobsStoreRootDir, "blobs_index.xml");
+ }
+
+ @Nullable
public static File prepareBlobStoreRootDir() {
final File blobStoreRootDir = getBlobStoreRootDir();
if (!blobStoreRootDir.exists() && !blobStoreRootDir.mkdir()) {
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java b/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
index 9d60f861e8eb..fcc30e30dfaa 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
@@ -15,8 +15,19 @@
*/
package com.android.server.blob;
+import static android.app.blob.BlobStoreManager.COMMIT_RESULT_ERROR;
import static android.app.blob.BlobStoreManager.COMMIT_RESULT_SUCCESS;
-
+import static android.app.blob.XmlTags.ATTR_VERSION;
+import static android.app.blob.XmlTags.TAG_BLOB;
+import static android.app.blob.XmlTags.TAG_BLOBS;
+import static android.app.blob.XmlTags.TAG_SESSION;
+import static android.app.blob.XmlTags.TAG_SESSIONS;
+import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
+import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
+import static android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES;
+import static android.os.UserHandle.USER_NULL;
+
+import static com.android.server.blob.BlobStoreConfig.CURRENT_XML_VERSION;
import static com.android.server.blob.BlobStoreConfig.TAG;
import static com.android.server.blob.BlobStoreSession.STATE_ABANDONED;
import static com.android.server.blob.BlobStoreSession.STATE_COMMITTED;
@@ -28,32 +39,58 @@ import android.annotation.CurrentTimeSecondsLong;
import android.annotation.IdRes;
import android.annotation.IntRange;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.blob.BlobHandle;
import android.app.blob.IBlobStoreManager;
import android.app.blob.IBlobStoreSession;
+import android.content.BroadcastReceiver;
import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManagerInternal;
import android.os.Binder;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.ParcelFileDescriptor;
import android.os.Process;
+import android.os.RemoteCallback;
+import android.os.SystemClock;
import android.os.UserHandle;
+import android.os.UserManagerInternal;
import android.util.ArrayMap;
+import android.util.AtomicFile;
import android.util.ExceptionUtils;
import android.util.LongSparseArray;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.Xml;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.DumpUtils;
+import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
+import com.android.internal.util.XmlUtils;
import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.LocalServices;
import com.android.server.ServiceThread;
import com.android.server.SystemService;
import com.android.server.Watchdog;
+import com.android.server.blob.BlobMetadata.Committer;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlSerializer;
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
import java.io.IOException;
+import java.io.PrintWriter;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.List;
/**
* Service responsible for maintaining and facilitating access to data blobs published by apps.
@@ -96,14 +133,34 @@ public class BlobStoreManagerService extends SystemService {
publishBinderService(Context.BLOB_STORE_SERVICE, new Stub());
mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
+ registerReceivers();
}
+ @Override
+ public void onBootPhase(int phase) {
+ if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
+ synchronized (mBlobsLock) {
+ final SparseArray<SparseArray<String>> allPackages = getAllPackages();
+ readBlobSessionsLocked(allPackages);
+ readBlobsInfoLocked(allPackages);
+ }
+ }
+ }
@GuardedBy("mBlobsLock")
private long generateNextSessionIdLocked() {
return ++mCurrentMaxSessionId;
}
+ private void registerReceivers() {
+ final IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction(Intent.ACTION_PACKAGE_FULLY_REMOVED);
+ intentFilter.addAction(Intent.ACTION_PACKAGE_DATA_CLEARED);
+ intentFilter.addAction(Intent.ACTION_USER_REMOVED);
+ mContext.registerReceiverAsUser(new PackageChangedReceiver(), UserHandle.ALL,
+ intentFilter, null, mHandler);
+ }
+
@GuardedBy("mBlobsLock")
private LongSparseArray<BlobStoreSession> getUserSessionsLocked(int userId) {
LongSparseArray<BlobStoreSession> userSessions = mSessions.get(userId);
@@ -133,7 +190,7 @@ public class BlobStoreManagerService extends SystemService {
sessionId, blobHandle, callingUid, callingPackage,
mSessionStateChangeListener);
getUserSessionsLocked(UserHandle.getUserId(callingUid)).put(sessionId, session);
- // TODO: persist sessions data
+ writeBlobSessionsAsync();
return sessionId;
}
}
@@ -160,7 +217,8 @@ public class BlobStoreManagerService extends SystemService {
callingUid, callingPackage);
session.open();
session.abandon();
- // TODO: persist sessions data
+
+ writeBlobSessionsAsync();
}
}
@@ -194,7 +252,7 @@ public class BlobStoreManagerService extends SystemService {
}
blobMetadata.addLeasee(callingPackage, callingUid,
descriptionResId, leaseExpiryTimeMillis);
- // TODO: persist blobs data
+ writeBlobsInfoAsync();
}
}
@@ -209,6 +267,7 @@ public class BlobStoreManagerService extends SystemService {
+ "; callingUid=" + callingUid + ", callingPackage=" + callingPackage);
}
blobMetadata.removeLeasee(callingPackage, callingUid);
+ writeBlobsInfoAsync();
}
}
@@ -241,18 +300,25 @@ public class BlobStoreManagerService extends SystemService {
session.verifyBlobData();
break;
case STATE_VERIFIED_VALID:
- final ArrayMap<BlobHandle, BlobMetadata> userBlobs =
- getUserBlobsLocked(UserHandle.getUserId(session.ownerUid));
+ final int userId = UserHandle.getUserId(session.ownerUid);
+ final ArrayMap<BlobHandle, BlobMetadata> userBlobs = getUserBlobsLocked(userId);
BlobMetadata blob = userBlobs.get(session.blobHandle);
if (blob == null) {
blob = new BlobMetadata(mContext,
- session.sessionId, session.blobHandle);
+ session.sessionId, session.blobHandle, userId);
userBlobs.put(session.blobHandle, blob);
}
- blob.addCommitter(session.ownerPackageName, session.ownerUid,
- session.getBlobAccessMode());
- // TODO: Persist blobs data.
- session.sendCommitCallbackResult(COMMIT_RESULT_SUCCESS);
+ final Committer newCommitter = new Committer(session.ownerPackageName,
+ session.ownerUid, session.getBlobAccessMode());
+ final Committer existingCommitter = blob.getExistingCommitter(newCommitter);
+ blob.addCommitter(newCommitter);
+ try {
+ writeBlobsInfoLocked();
+ session.sendCommitCallbackResult(COMMIT_RESULT_SUCCESS);
+ } catch (Exception e) {
+ blob.addCommitter(existingCommitter);
+ session.sendCommitCallbackResult(COMMIT_RESULT_ERROR);
+ }
getUserSessionsLocked(UserHandle.getUserId(session.ownerUid))
.remove(session.sessionId);
break;
@@ -260,7 +326,330 @@ public class BlobStoreManagerService extends SystemService {
Slog.wtf(TAG, "Invalid session state: "
+ stateToString(session.getState()));
}
- // TODO: Persist sessions data.
+ try {
+ writeBlobSessionsLocked();
+ } catch (Exception e) {
+ // already logged, ignore.
+ }
+ }
+ }
+
+ @GuardedBy("mBlobsLock")
+ private void writeBlobSessionsLocked() throws Exception {
+ final AtomicFile sessionsIndexFile = prepareSessionsIndexFile();
+ if (sessionsIndexFile == null) {
+ Slog.wtf(TAG, "Error creating sessions index file");
+ return;
+ }
+ FileOutputStream fos = null;
+ try {
+ fos = sessionsIndexFile.startWrite(SystemClock.uptimeMillis());
+ final XmlSerializer out = new FastXmlSerializer();
+ out.setOutput(fos, StandardCharsets.UTF_8.name());
+ out.startDocument(null, true);
+ out.startTag(null, TAG_SESSIONS);
+ XmlUtils.writeIntAttribute(out, ATTR_VERSION, CURRENT_XML_VERSION);
+
+ for (int i = 0, userCount = mSessions.size(); i < userCount; ++i) {
+ final LongSparseArray<BlobStoreSession> userSessions =
+ mSessions.valueAt(i);
+ for (int j = 0, sessionsCount = userSessions.size(); j < sessionsCount; ++j) {
+ out.startTag(null, TAG_SESSION);
+ userSessions.valueAt(j).writeToXml(out);
+ out.endTag(null, TAG_SESSION);
+ }
+ }
+
+ out.endTag(null, TAG_SESSIONS);
+ out.endDocument();
+ sessionsIndexFile.finishWrite(fos);
+ } catch (Exception e) {
+ sessionsIndexFile.failWrite(fos);
+ Slog.wtf(TAG, "Error writing sessions data", e);
+ throw e;
+ }
+ }
+
+ @GuardedBy("mBlobsLock")
+ private void readBlobSessionsLocked(SparseArray<SparseArray<String>> allPackages) {
+ if (!BlobStoreConfig.getBlobStoreRootDir().exists()) {
+ return;
+ }
+ final AtomicFile sessionsIndexFile = prepareSessionsIndexFile();
+ if (sessionsIndexFile == null) {
+ Slog.wtf(TAG, "Error creating sessions index file");
+ return;
+ }
+
+ mSessions.clear();
+ try (FileInputStream fis = sessionsIndexFile.openRead()) {
+ final XmlPullParser in = Xml.newPullParser();
+ in.setInput(fis, StandardCharsets.UTF_8.name());
+ XmlUtils.beginDocument(in, TAG_SESSIONS);
+ while (true) {
+ XmlUtils.nextElement(in);
+ if (in.getEventType() == XmlPullParser.END_DOCUMENT) {
+ break;
+ }
+
+ if (TAG_SESSION.equals(in.getName())) {
+ final BlobStoreSession session = BlobStoreSession.createFromXml(
+ in, mContext, mSessionStateChangeListener);
+ if (session == null) {
+ continue;
+ }
+ final SparseArray<String> userPackages = allPackages.get(
+ UserHandle.getUserId(session.ownerUid));
+ if (userPackages != null
+ && session.ownerPackageName.equals(
+ userPackages.get(session.ownerUid))) {
+ getUserSessionsLocked(UserHandle.getUserId(session.ownerUid)).put(
+ session.sessionId, session);
+ } else {
+ // Unknown package or the session data does not belong to this package.
+ session.getSessionFile().delete();
+ }
+ mCurrentMaxSessionId = Math.max(mCurrentMaxSessionId, session.sessionId);
+ }
+ }
+ } catch (Exception e) {
+ Slog.wtf(TAG, "Error reading sessions data", e);
+ }
+ }
+
+ @GuardedBy("mBlobsLock")
+ private void writeBlobsInfoLocked() throws Exception {
+ final AtomicFile blobsIndexFile = prepareBlobsIndexFile();
+ if (blobsIndexFile == null) {
+ Slog.wtf(TAG, "Error creating blobs index file");
+ return;
+ }
+ FileOutputStream fos = null;
+ try {
+ fos = blobsIndexFile.startWrite(SystemClock.uptimeMillis());
+ final XmlSerializer out = new FastXmlSerializer();
+ out.setOutput(fos, StandardCharsets.UTF_8.name());
+ out.startDocument(null, true);
+ out.startTag(null, TAG_BLOBS);
+ XmlUtils.writeIntAttribute(out, ATTR_VERSION, CURRENT_XML_VERSION);
+
+ for (int i = 0, userCount = mBlobsMap.size(); i < userCount; ++i) {
+ final ArrayMap<BlobHandle, BlobMetadata> userBlobs = mBlobsMap.valueAt(i);
+ for (int j = 0, blobsCount = userBlobs.size(); j < blobsCount; ++j) {
+ out.startTag(null, TAG_BLOB);
+ userBlobs.valueAt(j).writeToXml(out);
+ out.endTag(null, TAG_BLOB);
+ }
+ }
+
+ out.endTag(null, TAG_BLOBS);
+ out.endDocument();
+ blobsIndexFile.finishWrite(fos);
+ } catch (Exception e) {
+ blobsIndexFile.failWrite(fos);
+ Slog.wtf(TAG, "Error writing blobs data", e);
+ throw e;
+ }
+ }
+
+ @GuardedBy("mBlobsLock")
+ private void readBlobsInfoLocked(SparseArray<SparseArray<String>> allPackages) {
+ if (!BlobStoreConfig.getBlobStoreRootDir().exists()) {
+ return;
+ }
+ final AtomicFile blobsIndexFile = prepareBlobsIndexFile();
+ if (blobsIndexFile == null) {
+ Slog.wtf(TAG, "Error creating blobs index file");
+ return;
+ }
+
+ mBlobsMap.clear();
+ try (FileInputStream fis = blobsIndexFile.openRead()) {
+ final XmlPullParser in = Xml.newPullParser();
+ in.setInput(fis, StandardCharsets.UTF_8.name());
+ XmlUtils.beginDocument(in, TAG_BLOBS);
+ while (true) {
+ XmlUtils.nextElement(in);
+ if (in.getEventType() == XmlPullParser.END_DOCUMENT) {
+ break;
+ }
+
+ if (TAG_BLOB.equals(in.getName())) {
+ final BlobMetadata blobMetadata = BlobMetadata.createFromXml(mContext, in);
+ final SparseArray<String> userPackages = allPackages.get(blobMetadata.userId);
+ if (userPackages == null) {
+ blobMetadata.getBlobFile().delete();
+ } else {
+ getUserBlobsLocked(blobMetadata.userId).put(
+ blobMetadata.blobHandle, blobMetadata);
+ blobMetadata.removeInvalidCommitters(userPackages);
+ blobMetadata.removeInvalidLeasees(userPackages);
+ }
+ mCurrentMaxSessionId = Math.max(mCurrentMaxSessionId, blobMetadata.blobId);
+ }
+ }
+ } catch (Exception e) {
+ Slog.wtf(TAG, "Error reading blobs data", e);
+ }
+ }
+
+ private void writeBlobsInfo() {
+ synchronized (mBlobsLock) {
+ try {
+ writeBlobsInfoLocked();
+ } catch (Exception e) {
+ // Already logged, ignore
+ }
+ }
+ }
+
+ private void writeBlobsInfoAsync() {
+ mHandler.post(PooledLambda.obtainRunnable(
+ BlobStoreManagerService::writeBlobsInfo,
+ BlobStoreManagerService.this).recycleOnUse());
+ }
+
+ private void writeBlobSessions() {
+ synchronized (mBlobsLock) {
+ try {
+ writeBlobSessionsLocked();
+ } catch (Exception e) {
+ // Already logged, ignore
+ }
+ }
+ }
+
+ private void writeBlobSessionsAsync() {
+ mHandler.post(PooledLambda.obtainRunnable(
+ BlobStoreManagerService::writeBlobSessions,
+ BlobStoreManagerService.this).recycleOnUse());
+ }
+
+ private int getPackageUid(String packageName, int userId) {
+ final int uid = mPackageManagerInternal.getPackageUid(
+ packageName,
+ MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE | MATCH_UNINSTALLED_PACKAGES,
+ userId);
+ return uid;
+ }
+
+ private SparseArray<SparseArray<String>> getAllPackages() {
+ final SparseArray<SparseArray<String>> allPackages = new SparseArray<>();
+ final int[] allUsers = LocalServices.getService(UserManagerInternal.class).getUserIds();
+ for (int userId : allUsers) {
+ final SparseArray<String> userPackages = new SparseArray<>();
+ allPackages.put(userId, userPackages);
+ final List<ApplicationInfo> applicationInfos = mPackageManagerInternal
+ .getInstalledApplications(
+ MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE
+ | MATCH_UNINSTALLED_PACKAGES,
+ userId, Process.myUid());
+ for (int i = 0, count = applicationInfos.size(); i < count; ++i) {
+ final ApplicationInfo applicationInfo = applicationInfos.get(i);
+ userPackages.put(applicationInfo.uid, applicationInfo.packageName);
+ }
+ }
+ return allPackages;
+ }
+
+ AtomicFile prepareSessionsIndexFile() {
+ final File file = BlobStoreConfig.prepareSessionIndexFile();
+ if (file == null) {
+ return null;
+ }
+ return new AtomicFile(file, "session_index" /* commitLogTag */);
+ }
+
+ AtomicFile prepareBlobsIndexFile() {
+ final File file = BlobStoreConfig.prepareBlobsIndexFile();
+ if (file == null) {
+ return null;
+ }
+ return new AtomicFile(file, "blobs_index" /* commitLogTag */);
+ }
+
+ private void handlePackageRemoved(String packageName, int uid) {
+ synchronized (mBlobsLock) {
+ // Clean up any pending sessions
+ final LongSparseArray<BlobStoreSession> userSessions =
+ getUserSessionsLocked(UserHandle.getUserId(uid));
+ final ArrayList<Integer> indicesToRemove = new ArrayList<>();
+ for (int i = 0, count = userSessions.size(); i < count; ++i) {
+ final BlobStoreSession session = userSessions.valueAt(i);
+ if (session.ownerUid == uid
+ && session.ownerPackageName.equals(packageName)) {
+ session.getSessionFile().delete();
+ indicesToRemove.add(i);
+ }
+ }
+ for (int i = 0, count = indicesToRemove.size(); i < count; ++i) {
+ userSessions.removeAt(i);
+ }
+
+ // Remove the package from the committer and leasee list
+ final ArrayMap<BlobHandle, BlobMetadata> userBlobs =
+ getUserBlobsLocked(UserHandle.getUserId(uid));
+ for (int i = 0, count = userBlobs.size(); i < count; ++i) {
+ final BlobMetadata blobMetadata = userBlobs.valueAt(i);
+ blobMetadata.removeCommitter(packageName, uid);
+ blobMetadata.removeLeasee(packageName, uid);
+ }
+ // TODO: clean-up blobs which doesn't have any active leases.
+ }
+ }
+
+ private void handleUserRemoved(int userId) {
+ synchronized (mBlobsLock) {
+ final LongSparseArray<BlobStoreSession> userSessions =
+ mSessions.removeReturnOld(userId);
+ if (userSessions != null) {
+ for (int i = 0, count = userSessions.size(); i < count; ++i) {
+ final BlobStoreSession session = userSessions.valueAt(i);
+ session.getSessionFile().delete();
+ }
+ }
+
+ final ArrayMap<BlobHandle, BlobMetadata> userBlobs =
+ mBlobsMap.removeReturnOld(userId);
+ if (userBlobs != null) {
+ for (int i = 0, count = userBlobs.size(); i < count; ++i) {
+ final BlobMetadata blobMetadata = userBlobs.valueAt(i);
+ blobMetadata.getBlobFile().delete();
+ }
+ }
+ }
+ }
+
+ private class PackageChangedReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ switch (intent.getAction()) {
+ case Intent.ACTION_PACKAGE_FULLY_REMOVED:
+ case Intent.ACTION_PACKAGE_DATA_CLEARED:
+ final String packageName = intent.getData().getSchemeSpecificPart();
+ if (packageName == null) {
+ Slog.wtf(TAG, "Package name is missing in the intent: " + intent);
+ return;
+ }
+ final int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
+ if (uid == -1) {
+ Slog.wtf(TAG, "uid is missing in the intent: " + intent);
+ return;
+ }
+ handlePackageRemoved(packageName, uid);
+ break;
+ case Intent.ACTION_USER_REMOVED:
+ final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
+ USER_NULL);
+ if (userId == USER_NULL) {
+ Slog.wtf(TAG, "userId is missing in the intent: " + intent);
+ return;
+ }
+ handleUserRemoved(userId);
+ break;
+ default:
+ Slog.wtf(TAG, "Received unknown intent: " + intent);
+ }
}
}
@@ -341,6 +730,8 @@ public class BlobStoreManagerService extends SystemService {
@CurrentTimeSecondsLong long leaseTimeoutSecs, @NonNull String packageName) {
Preconditions.checkNotNull(blobHandle, "blobHandle must not be null");
Preconditions.checkNotNull(packageName, "packageName must not be null");
+ Preconditions.checkArgumentPositive(descriptionResId,
+ "descriptionResId must be positive; value=" + descriptionResId);
final int callingUid = Binder.getCallingUid();
verifyCallingPackage(callingUid, packageName);
@@ -360,5 +751,62 @@ public class BlobStoreManagerService extends SystemService {
releaseLeaseInternal(blobHandle, callingUid, packageName);
}
+
+ @Override
+ public void waitForIdle(@NonNull RemoteCallback remoteCallback) {
+ Preconditions.checkNotNull(remoteCallback, "remoteCallback must not be null");
+
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP,
+ "Caller is not allowed to call this; caller=" + Binder.getCallingUid());
+ mHandler.post(PooledLambda.obtainRunnable(remoteCallback::sendResult, null)
+ .recycleOnUse());
+ }
+
+ @Override
+ public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter writer,
+ @Nullable String[] args) {
+ // TODO: add proto-based version of this.
+ if (!DumpUtils.checkDumpPermission(mContext, TAG, writer)) return;
+
+ final IndentingPrintWriter fout = new IndentingPrintWriter(writer, " ");
+ synchronized (mBlobsLock) {
+ fout.println("mCurrentMaxSessionId: " + mCurrentMaxSessionId);
+ fout.println();
+ for (int i = 0, userCount = mSessions.size(); i < userCount; ++i) {
+ final int userId = mSessions.keyAt(i);
+ final LongSparseArray<BlobStoreSession> userSessions = mSessions.valueAt(i);
+ fout.println("List of sessions in user #"
+ + userId + " (" + userSessions.size() + "):");
+ fout.increaseIndent();
+ for (int j = 0, sessionsCount = userSessions.size(); j < sessionsCount; ++j) {
+ final long sessionId = userSessions.keyAt(j);
+ final BlobStoreSession session = userSessions.valueAt(j);
+ fout.println("Session #" + sessionId);
+ fout.increaseIndent();
+ session.dump(fout);
+ fout.decreaseIndent();
+ }
+ fout.decreaseIndent();
+ }
+
+ fout.print("\n\n");
+
+ for (int i = 0, userCount = mBlobsMap.size(); i < userCount; ++i) {
+ final int userId = mBlobsMap.keyAt(i);
+ final ArrayMap<BlobHandle, BlobMetadata> userBlobs = mBlobsMap.valueAt(i);
+ fout.println("List of blobs in user #"
+ + userId + " (" + userBlobs.size() + "):");
+ fout.increaseIndent();
+ for (int j = 0, blobsCount = userBlobs.size(); j < blobsCount; ++j) {
+ final BlobMetadata blobMetadata = userBlobs.valueAt(j);
+ fout.println("Blob #" + blobMetadata.blobId);
+ fout.increaseIndent();
+ blobMetadata.dump(fout);
+ fout.decreaseIndent();
+ }
+ fout.decreaseIndent();
+ }
+ }
+ }
}
-}
+} \ No newline at end of file
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java b/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java
index 29092b327fc7..7d1c16653383 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java
@@ -16,7 +16,13 @@
package com.android.server.blob;
import static android.app.blob.BlobStoreManager.COMMIT_RESULT_ERROR;
+import static android.app.blob.XmlTags.ATTR_ID;
+import static android.app.blob.XmlTags.ATTR_PACKAGE;
+import static android.app.blob.XmlTags.ATTR_UID;
+import static android.app.blob.XmlTags.TAG_ACCESS_MODE;
+import static android.app.blob.XmlTags.TAG_BLOB_HANDLE;
import static android.system.OsConstants.O_CREAT;
+import static android.system.OsConstants.O_RDONLY;
import static android.system.OsConstants.O_RDWR;
import static android.system.OsConstants.SEEK_SET;
@@ -41,9 +47,15 @@ import android.util.ExceptionUtils;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
+import com.android.internal.util.XmlUtils;
import com.android.server.blob.BlobStoreManagerService.SessionStateChangeListener;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
import java.io.File;
import java.io.FileDescriptor;
import java.io.IOException;
@@ -187,9 +199,43 @@ public class BlobStoreSession extends IBlobStoreSession.Stub {
}
@Override
+ @NonNull
+ public ParcelFileDescriptor openRead() {
+ assertCallerIsOwner();
+ synchronized (mSessionLock) {
+ if (mState != STATE_OPENED) {
+ throw new IllegalStateException("Not allowed to read in state: "
+ + stateToString(mState));
+ }
+
+ try {
+ return openReadLocked();
+ } catch (IOException e) {
+ throw ExceptionUtils.wrap(e);
+ }
+ }
+ }
+
+ @GuardedBy("mSessionLock")
+ @NonNull
+ private ParcelFileDescriptor openReadLocked() throws IOException {
+ FileDescriptor fd = null;
+ try {
+ final File sessionFile = getSessionFile();
+ if (sessionFile == null) {
+ throw new IllegalStateException("Couldn't get the file for this session");
+ }
+ fd = Os.open(sessionFile.getPath(), O_RDONLY, 0);
+ } catch (ErrnoException e) {
+ e.rethrowAsIOException();
+ }
+ return createRevocableFdLocked(fd);
+ }
+
+ @Override
@BytesLong
public long getSize() {
- return 0;
+ return getSessionFile().length();
}
@Override
@@ -331,8 +377,8 @@ public class BlobStoreSession extends IBlobStoreSession.Stub {
private void revokeAllFdsLocked() {
for (int i = mRevocableFds.size() - 1; i >= 0; --i) {
mRevocableFds.get(i).revoke();
- mRevocableFds.remove(i);
}
+ mRevocableFds.clear();
}
@GuardedBy("mSessionLock")
@@ -383,4 +429,74 @@ public class BlobStoreSession extends IBlobStoreSession.Stub {
throw new SecurityException(ownerUid + " is not the session owner");
}
}
+
+ void dump(IndentingPrintWriter fout) {
+ synchronized (mSessionLock) {
+ fout.println("state: " + stateToString(mState));
+ fout.println("ownerUid: " + ownerUid);
+ fout.println("ownerPkg: " + ownerPackageName);
+
+ fout.println("blobHandle:");
+ fout.increaseIndent();
+ blobHandle.dump(fout);
+ fout.decreaseIndent();
+
+ fout.println("accessMode:");
+ fout.increaseIndent();
+ mBlobAccessMode.dump(fout);
+ fout.decreaseIndent();
+
+ fout.println("Open fds: #" + mRevocableFds.size());
+ }
+ }
+
+ void writeToXml(@NonNull XmlSerializer out) throws IOException {
+ synchronized (mSessionLock) {
+ XmlUtils.writeLongAttribute(out, ATTR_ID, sessionId);
+ XmlUtils.writeStringAttribute(out, ATTR_PACKAGE, ownerPackageName);
+ XmlUtils.writeIntAttribute(out, ATTR_UID, ownerUid);
+
+ out.startTag(null, TAG_BLOB_HANDLE);
+ blobHandle.writeToXml(out);
+ out.endTag(null, TAG_BLOB_HANDLE);
+
+ out.startTag(null, TAG_ACCESS_MODE);
+ mBlobAccessMode.writeToXml(out);
+ out.endTag(null, TAG_ACCESS_MODE);
+ }
+ }
+
+ @Nullable
+ static BlobStoreSession createFromXml(@NonNull XmlPullParser in,
+ @NonNull Context context, @NonNull SessionStateChangeListener stateChangeListener)
+ throws IOException, XmlPullParserException {
+ final int sessionId = XmlUtils.readIntAttribute(in, ATTR_ID);
+ final String ownerPackageName = XmlUtils.readStringAttribute(in, ATTR_PACKAGE);
+ final int ownerUid = XmlUtils.readIntAttribute(in, ATTR_UID);
+
+ final int depth = in.getDepth();
+ BlobHandle blobHandle = null;
+ BlobAccessMode blobAccessMode = null;
+ while (XmlUtils.nextElementWithin(in, depth)) {
+ if (TAG_BLOB_HANDLE.equals(in.getName())) {
+ blobHandle = BlobHandle.createFromXml(in);
+ } else if (TAG_ACCESS_MODE.equals(in.getName())) {
+ blobAccessMode = BlobAccessMode.createFromXml(in);
+ }
+ }
+
+ if (blobHandle == null) {
+ Slog.wtf(TAG, "blobHandle should be available");
+ return null;
+ }
+ if (blobAccessMode == null) {
+ Slog.wtf(TAG, "blobAccessMode should be available");
+ return null;
+ }
+
+ final BlobStoreSession blobStoreSession = new BlobStoreSession(context, sessionId,
+ blobHandle, ownerUid, ownerPackageName, stateChangeListener);
+ blobStoreSession.mBlobAccessMode.allow(blobAccessMode);
+ return blobStoreSession;
+ }
}
diff --git a/apex/statsd/aidl/Android.bp b/apex/statsd/aidl/Android.bp
index cc5172c6018a..6d639fddd043 100644
--- a/apex/statsd/aidl/Android.bp
+++ b/apex/statsd/aidl/Android.bp
@@ -14,7 +14,7 @@
// limitations under the License.
//
-// TODO(b/145815909): move StatsDimensionsValue.aidl and StatsLogEventWrapper.aidl here
+// TODO(b/145815909): move StatsDimensionsValue.aidl here
filegroup {
name: "statsd_aidl",
srcs: [
diff --git a/apex/statsd/framework/Android.bp b/apex/statsd/framework/Android.bp
index 0b46645ad06f..f66f0340edab 100644
--- a/apex/statsd/framework/Android.bp
+++ b/apex/statsd/framework/Android.bp
@@ -24,7 +24,7 @@ java_library {
name: "framework-statsd",
installable: true,
// TODO(b/146209659): Use system_current instead.
- sdk_version: "core_current",
+ sdk_version: "core_platform",
srcs: [
":framework-statsd-sources",
],
@@ -35,7 +35,9 @@ java_library {
libs: [
"framework-annotations-lib",
// TODO(b/146230220): Use framework-system-stubs instead.
- "android_system_stubs_current",
+ //"android_system_stubs_current",
+ //"framework_module_lib_stubs_current",
+ "framework-all",
],
hostdex: true, // for hiddenapi check
visibility: [
@@ -52,12 +54,14 @@ java_library {
droidstubs {
name: "framework-statsd-stubs-docs",
defaults: [
- "framework-module-stubs-defaults-publicapi"
+ "framework-module-stubs-defaults-systemapi"
],
srcs: [
+ ":framework-annotations",
":framework-statsd-sources",
],
libs: [
+ // TODO(b/148218250): Change to android_system_stubs_current
"framework-all",
],
sdk_version: "core_platform",
@@ -70,6 +74,7 @@ java_library {
":framework-statsd-stubs-docs",
],
libs: [
+ // TODO(b/148218250): Change to android_system_stubs_current
"framework-all",
],
sdk_version: "core_platform",
diff --git a/core/java/android/app/StatsManager.java b/apex/statsd/framework/java/android/app/StatsManager.java
index 0ea05d8f683c..ad1ac95d667c 100644
--- a/core/java/android/app/StatsManager.java
+++ b/apex/statsd/framework/java/android/app/StatsManager.java
@@ -30,7 +30,7 @@ import android.os.IPullAtomResultReceiver;
import android.os.IStatsManagerService;
import android.os.IStatsd;
import android.os.RemoteException;
-import android.os.ServiceManager;
+import android.os.StatsFrameworkInitializer;
import android.util.AndroidException;
import android.util.Slog;
import android.util.StatsEvent;
@@ -702,7 +702,10 @@ public final class StatsManager {
return mStatsManagerService;
}
mStatsManagerService = IStatsManagerService.Stub.asInterface(
- ServiceManager.getService(Context.STATS_MANAGER_SERVICE));
+ StatsFrameworkInitializer
+ .getStatsServiceManager()
+ .getStatsManagerServiceRegisterer()
+ .get());
return mStatsManagerService;
}
diff --git a/apex/statsd/framework/java/android/os/StatsFrameworkInitializer.java b/apex/statsd/framework/java/android/os/StatsFrameworkInitializer.java
new file mode 100644
index 000000000000..3d955336b45c
--- /dev/null
+++ b/apex/statsd/framework/java/android/os/StatsFrameworkInitializer.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.os;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.app.StatsManager;
+import android.app.SystemServiceRegistry;
+import android.content.Context;
+
+/**
+ * Class for performing registration for all stats services
+ *
+ * TODO(b/148225705) Change to @SystemApi(client=MODULE_LIBRARIES) when the build system is ready.
+ * @hide
+ */
+@SystemApi
+public class StatsFrameworkInitializer {
+ private StatsFrameworkInitializer() {
+ }
+
+ private static volatile StatsServiceManager sStatsServiceManager;
+
+ /**
+ * Sets an instance of {@link StatsServiceManager} that allows
+ * the statsd mainline module to register/obtain stats binder services. This is called
+ * by the platform during the system initialization.
+ *
+ * @param statsServiceManager instance of {@link StatsServiceManager} that allows
+ * the statsd mainline module to register/obtain statsd binder services.
+ */
+ public static void setStatsServiceManager(
+ @NonNull StatsServiceManager statsServiceManager) {
+ if (sStatsServiceManager != null) {
+ throw new IllegalStateException("setStatsServiceManager called twice!");
+ }
+
+ if (statsServiceManager == null) {
+ throw new NullPointerException("statsServiceManager is null");
+ }
+
+ sStatsServiceManager = statsServiceManager;
+ }
+
+ /** @hide */
+ public static StatsServiceManager getStatsServiceManager() {
+ return sStatsServiceManager;
+ }
+
+ /**
+ * Called by {@link SystemServiceRegistry}'s static initializer and registers all statsd
+ * services to {@link Context}, so that {@link Context#getSystemService} can return them.
+ *
+ * @throws IllegalStateException if this is called from anywhere besides
+ * {@link SystemServiceRegistry}
+ */
+ public static void registerServiceWrappers() {
+ SystemServiceRegistry.registerContextAwareService(
+ Context.STATS_MANAGER,
+ StatsManager.class,
+ context -> new StatsManager(context)
+ );
+ }
+}
diff --git a/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java b/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java
index bcbb5a1407f6..1e92826ee8a0 100644
--- a/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java
+++ b/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java
@@ -15,160 +15,51 @@
*/
package com.android.server.stats;
-import static android.app.AppOpsManager.OP_FLAGS_ALL_TRUSTED;
-import static android.content.pm.PackageInfo.REQUESTED_PERMISSION_GRANTED;
-import static android.content.pm.PermissionInfo.PROTECTION_DANGEROUS;
import static android.os.Process.THREAD_PRIORITY_BACKGROUND;
-import static android.os.Process.getUidForPid;
-import static android.os.storage.VolumeInfo.TYPE_PRIVATE;
-import static android.os.storage.VolumeInfo.TYPE_PUBLIC;
-
-import static com.android.server.am.MemoryStatUtil.readMemoryStatFromFilesystem;
-import static com.android.server.stats.pull.IonMemoryUtil.readProcessSystemIonHeapSizesFromDebugfs;
-import static com.android.server.stats.pull.IonMemoryUtil.readSystemIonHeapSizeFromDebugfs;
-import static com.android.server.stats.pull.ProcfsMemoryUtil.forEachPid;
-import static com.android.server.stats.pull.ProcfsMemoryUtil.readCmdlineFromProcfs;
-import static com.android.server.stats.pull.ProcfsMemoryUtil.readMemorySnapshotFromProcfs;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.app.ActivityManagerInternal;
+
import android.app.AlarmManager;
import android.app.AlarmManager.OnAlarmListener;
-import android.app.AppOpsManager;
-import android.app.AppOpsManager.HistoricalOps;
-import android.app.AppOpsManager.HistoricalOpsRequest;
-import android.app.AppOpsManager.HistoricalPackageOps;
-import android.app.AppOpsManager.HistoricalUidOps;
-import android.app.INotificationManager;
-import android.app.ProcessMemoryState;
import android.app.StatsManager;
-import android.bluetooth.BluetoothActivityEnergyInfo;
-import android.bluetooth.BluetoothAdapter;
-import android.bluetooth.UidTraffic;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
-import android.content.pm.PermissionInfo;
-import android.content.pm.UserInfo;
-import android.hardware.biometrics.BiometricsProtoEnums;
-import android.hardware.face.FaceManager;
-import android.hardware.fingerprint.FingerprintManager;
-import android.net.ConnectivityManager;
-import android.net.INetworkStatsService;
-import android.net.Network;
-import android.net.NetworkRequest;
-import android.net.NetworkStats;
-import android.net.wifi.WifiManager;
-import android.os.BatteryStats;
-import android.os.BatteryStatsInternal;
import android.os.Binder;
-import android.os.Build;
import android.os.Bundle;
-import android.os.CoolingDevice;
-import android.os.Environment;
-import android.os.FileUtils;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.IStatsCompanionService;
import android.os.IStatsd;
-import android.os.IStoraged;
-import android.os.IThermalEventListener;
-import android.os.IThermalService;
import android.os.Looper;
import android.os.ParcelFileDescriptor;
-import android.os.Parcelable;
import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.StatFs;
-import android.os.StatsLogEventWrapper;
-import android.os.SynchronousResultReceiver;
+import android.os.StatsFrameworkInitializer;
import android.os.SystemClock;
-import android.os.SystemProperties;
-import android.os.Temperature;
import android.os.UserHandle;
import android.os.UserManager;
-import android.os.connectivity.WifiActivityEnergyInfo;
-import android.os.storage.DiskInfo;
-import android.os.storage.StorageManager;
-import android.os.storage.VolumeInfo;
-import android.provider.Settings;
-import android.stats.storage.StorageEnums;
-import android.telephony.ModemActivityInfo;
-import android.telephony.TelephonyManager;
-import android.util.ArrayMap;
-import android.util.ArraySet;
-import android.util.Log;
import android.util.Slog;
-import android.util.StatsLog;
import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.GuardedBy;
-import com.android.internal.app.procstats.IProcessStats;
-import com.android.internal.app.procstats.ProcessStats;
-import com.android.internal.os.BatterySipper;
-import com.android.internal.os.BatteryStatsHelper;
-import com.android.internal.os.BinderCallsStats.ExportedCallStat;
-import com.android.internal.os.KernelCpuSpeedReader;
-import com.android.internal.os.KernelCpuThreadReader;
-import com.android.internal.os.KernelCpuThreadReaderDiff;
-import com.android.internal.os.KernelCpuThreadReaderSettingsObserver;
-import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidActiveTimeReader;
-import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidClusterTimeReader;
-import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidFreqTimeReader;
-import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidUserSysTimeReader;
-import com.android.internal.os.KernelWakelockReader;
-import com.android.internal.os.KernelWakelockStats;
import com.android.internal.os.LooperStats;
-import com.android.internal.os.PowerProfile;
-import com.android.internal.os.ProcessCpuTracker;
-import com.android.internal.os.StoragedUidIoStatsReader;
import com.android.internal.util.DumpUtils;
import com.android.server.BinderCallsStatsService;
import com.android.server.LocalServices;
-import com.android.server.SystemServiceManager;
-import com.android.server.am.MemoryStatUtil.MemoryStat;
-import com.android.server.notification.NotificationManagerService;
-import com.android.server.role.RoleManagerInternal;
-import com.android.server.stats.pull.IonMemoryUtil.IonAllocations;
-import com.android.server.stats.pull.ProcfsMemoryUtil.MemorySnapshot;
-import com.android.server.storage.DiskStatsFileLogger;
-import com.android.server.storage.DiskStatsLoggingService;
-
-import com.google.android.collect.Sets;
import libcore.io.IoUtils;
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
-
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileOutputStream;
import java.io.IOException;
-import java.io.InputStream;
import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Objects;
-import java.util.Set;
-import java.util.UUID;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.Executor;
-import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
/**
* Helper service for statsd (the native stats management service in cmds/statsd/).
@@ -177,10 +68,7 @@ import java.util.concurrent.TimeoutException;
* @hide
*/
public class StatsCompanionService extends IStatsCompanionService.Stub {
- /**
- * How long to wait on an individual subsystem to return its stats.
- */
- private static final long EXTERNAL_STATS_SYNC_TIMEOUT_MILLIS = 2000;
+
private static final long MILLIS_IN_A_DAY = TimeUnit.DAYS.toMillis(1);
public static final String RESULT_RECEIVER_CONTROLLER_KEY = "controller_activity";
@@ -200,45 +88,6 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
private static final int INSTALLER_FIELD_ID = 5;
public static final int DEATH_THRESHOLD = 10;
- /**
- * Which native processes to snapshot memory for.
- *
- * <p>Processes are matched by their cmdline in procfs. Example: cat /proc/pid/cmdline returns
- * /system/bin/statsd for the stats daemon.
- */
- private static final Set<String> MEMORY_INTERESTING_NATIVE_PROCESSES = Sets.newHashSet(
- "/system/bin/statsd", // Stats daemon.
- "/system/bin/surfaceflinger",
- "/system/bin/apexd", // APEX daemon.
- "/system/bin/audioserver",
- "/system/bin/cameraserver",
- "/system/bin/drmserver",
- "/system/bin/healthd",
- "/system/bin/incidentd",
- "/system/bin/installd",
- "/system/bin/lmkd", // Low memory killer daemon.
- "/system/bin/logd",
- "media.codec",
- "media.extractor",
- "media.metrics",
- "/system/bin/mediadrmserver",
- "/system/bin/mediaserver",
- "/system/bin/performanced",
- "/system/bin/tombstoned",
- "/system/bin/traced", // Perfetto.
- "/system/bin/traced_probes", // Perfetto.
- "webview_zygote",
- "zygote",
- "zygote64");
- /**
- * Lowest available uid for apps.
- *
- * <p>Used to quickly discard memory snapshots of the zygote forks from native process
- * measurements.
- */
- private static final int MIN_APP_UID = 10_000;
-
- private static final int CPU_TIME_PER_THREAD_FREQ_MAX_NUM_FREQUENCIES = 8;
static final class CompanionHandler extends Handler {
CompanionHandler(Looper looper) {
@@ -248,7 +97,6 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
private final Context mContext;
private final AlarmManager mAlarmManager;
- private final INetworkStatsService mNetworkStatsService;
@GuardedBy("sStatsdLock")
private static IStatsd sStatsd;
private static final Object sStatsdLock = new Object();
@@ -262,52 +110,16 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
private StatsManagerService mStatsManagerService;
- private final KernelWakelockReader mKernelWakelockReader = new KernelWakelockReader();
- private final KernelWakelockStats mTmpWakelockStats = new KernelWakelockStats();
- private WifiManager mWifiManager = null;
- private TelephonyManager mTelephony = null;
@GuardedBy("sStatsdLock")
private final HashSet<Long> mDeathTimeMillis = new HashSet<>();
@GuardedBy("sStatsdLock")
private final HashMap<Long, String> mDeletedFiles = new HashMap<>();
private final CompanionHandler mHandler;
- // Disables throttler on CPU time readers.
- private KernelCpuUidUserSysTimeReader mCpuUidUserSysTimeReader =
- new KernelCpuUidUserSysTimeReader(false);
- private KernelCpuSpeedReader[] mKernelCpuSpeedReaders;
- private KernelCpuUidFreqTimeReader mCpuUidFreqTimeReader =
- new KernelCpuUidFreqTimeReader(false);
- private KernelCpuUidActiveTimeReader mCpuUidActiveTimeReader =
- new KernelCpuUidActiveTimeReader(false);
- private KernelCpuUidClusterTimeReader mCpuUidClusterTimeReader =
- new KernelCpuUidClusterTimeReader(false);
- private StoragedUidIoStatsReader mStoragedUidIoStatsReader =
- new StoragedUidIoStatsReader();
- @Nullable
- private final KernelCpuThreadReaderDiff mKernelCpuThreadReader;
-
- private long mDebugElapsedClockPreviousValue = 0;
- private long mDebugElapsedClockPullCount = 0;
- private long mDebugFailingElapsedClockPreviousValue = 0;
- private long mDebugFailingElapsedClockPullCount = 0;
- private BatteryStatsHelper mBatteryStatsHelper = null;
- private static final int MAX_BATTERY_STATS_HELPER_FREQUENCY_MS = 1000;
- private long mBatteryStatsHelperTimestampMs = -MAX_BATTERY_STATS_HELPER_FREQUENCY_MS;
-
- private static IThermalService sThermalService;
- private File mBaseDir =
- new File(SystemServiceManager.ensureSystemDir(), "stats_companion");
- @GuardedBy("this")
- ProcessCpuTracker mProcessCpuTracker = null;
-
public StatsCompanionService(Context context) {
super();
mContext = context;
mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
- mNetworkStatsService = INetworkStatsService.Stub.asInterface(
- ServiceManager.getService(Context.NETWORK_STATS_SERVICE));
- mBaseDir.mkdirs();
mAppUpdateReceiver = new AppUpdateReceiver();
mUserUpdateReceiver = new BroadcastReceiver() {
@Override
@@ -330,47 +142,10 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
};
mShutdownEventReceiver = new ShutdownEventReceiver();
if (DEBUG) Slog.d(TAG, "Registered receiver for ACTION_PACKAGE_REPLACED and ADDED.");
- PowerProfile powerProfile = new PowerProfile(context);
- final int numClusters = powerProfile.getNumCpuClusters();
- mKernelCpuSpeedReaders = new KernelCpuSpeedReader[numClusters];
- int firstCpuOfCluster = 0;
- for (int i = 0; i < numClusters; i++) {
- final int numSpeedSteps = powerProfile.getNumSpeedStepsInCpuCluster(i);
- mKernelCpuSpeedReaders[i] = new KernelCpuSpeedReader(firstCpuOfCluster,
- numSpeedSteps);
- firstCpuOfCluster += powerProfile.getNumCoresInCpuCluster(i);
- }
-
- // Enable push notifications of throttling from vendor thermal
- // management subsystem via thermalservice.
- IBinder b = ServiceManager.getService("thermalservice");
-
- if (b != null) {
- sThermalService = IThermalService.Stub.asInterface(b);
- try {
- sThermalService.registerThermalEventListener(
- new ThermalEventListener());
- Slog.i(TAG, "register thermal listener successfully");
- } catch (RemoteException e) {
- // Should never happen.
- Slog.e(TAG, "register thermal listener error");
- }
- } else {
- Slog.e(TAG, "cannot find thermalservice, no throttling push notifications");
- }
-
- // Default NetworkRequest should cover all transport types.
- final NetworkRequest request = new NetworkRequest.Builder().build();
- final ConnectivityManager connectivityManager =
- (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
- connectivityManager.registerNetworkCallback(request, new ConnectivityStatsCallback());
-
HandlerThread handlerThread = new HandlerThread(TAG);
handlerThread.start();
mHandler = new CompanionHandler(handlerThread.getLooper());
- mKernelCpuThreadReader =
- KernelCpuThreadReaderSettingsObserver.getSettingsModifiedReader(mContext);
}
private final static int[] toIntArray(List<Integer> list) {
@@ -750,7 +525,10 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
* sStatsd with a null check.
*/
private static IStatsd fetchStatsdService() {
- return IStatsd.Stub.asInterface(ServiceManager.getService("stats"));
+ return IStatsd.Stub.asInterface(StatsFrameworkInitializer
+ .getStatsServiceManager()
+ .getStatsdServiceRegisterer()
+ .get());
}
/**
@@ -851,8 +629,8 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
mDeathTimeMillis.add(now);
if (mDeathTimeMillis.size() >= DEATH_THRESHOLD) {
mDeathTimeMillis.clear();
- File[] configs = FileUtils.listFilesOrEmpty(new File(CONFIG_DIR));
- if (configs.length > 0) {
+ File[] configs = new File(CONFIG_DIR).listFiles();
+ if (configs != null && configs.length > 0) {
String fileName = configs[0].getName();
if (configs[0].delete()) {
mDeletedFiles.put(now, fileName);
@@ -905,28 +683,4 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
}
}
}
-
- // Thermal event received from vendor thermal management subsystem
- private static final class ThermalEventListener extends IThermalEventListener.Stub {
- @Override
- public void notifyThrottling(Temperature temp) {
- StatsLog.write(StatsLog.THERMAL_THROTTLING_SEVERITY_STATE_CHANGED, temp.getType(),
- temp.getName(), (int) (temp.getValue() * 10), temp.getStatus());
- }
- }
-
- private static final class ConnectivityStatsCallback extends
- ConnectivityManager.NetworkCallback {
- @Override
- public void onAvailable(Network network) {
- StatsLog.write(StatsLog.CONNECTIVITY_STATE_CHANGED, network.netId,
- StatsLog.CONNECTIVITY_STATE_CHANGED__STATE__CONNECTED);
- }
-
- @Override
- public void onLost(Network network) {
- StatsLog.write(StatsLog.CONNECTIVITY_STATE_CHANGED, network.netId,
- StatsLog.CONNECTIVITY_STATE_CHANGED__STATE__DISCONNECTED);
- }
- }
}
diff --git a/api/current.txt b/api/current.txt
index 6eb65824c5ab..2e99edd2ea50 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -333,6 +333,9 @@ package android {
field public static final int autoUrlDetect = 16843404; // 0x101028c
field public static final int autoVerify = 16844014; // 0x10104ee
field public static final int autofillHints = 16844118; // 0x1010556
+ field public static final int autofillInlineSuggestionChip = 16844307; // 0x1010613
+ field public static final int autofillInlineSuggestionSubtitle = 16844309; // 0x1010615
+ field public static final int autofillInlineSuggestionTitle = 16844308; // 0x1010614
field public static final int autofilledHighlight = 16844136; // 0x1010568
field public static final int background = 16842964; // 0x10100d4
field public static final int backgroundDimAmount = 16842802; // 0x1010032
@@ -2253,6 +2256,7 @@ package android {
field public static final int ThemeOverlay_Material_Dialog = 16974550; // 0x10302d6
field public static final int ThemeOverlay_Material_Dialog_Alert = 16974551; // 0x10302d7
field public static final int ThemeOverlay_Material_Light = 16974410; // 0x103024a
+ field public static final int Theme_AutofillInlineSuggestion = 16974565; // 0x10302e5
field public static final int Theme_Black = 16973832; // 0x1030008
field public static final int Theme_Black_NoTitleBar = 16973833; // 0x1030009
field public static final int Theme_Black_NoTitleBar_Fullscreen = 16973834; // 0x103000a
@@ -3897,6 +3901,7 @@ package android.app {
method public void setImmersive(boolean);
method public void setInheritShowWhenLocked(boolean);
method public void setIntent(android.content.Intent);
+ method public void setLocusContext(@Nullable android.content.LocusId, @Nullable android.os.Bundle);
method public final void setMediaController(android.media.session.MediaController);
method public void setPictureInPictureParams(@NonNull android.app.PictureInPictureParams);
method @Deprecated public final void setProgress(int);
@@ -6827,6 +6832,7 @@ package android.app.admin {
method public int getLockTaskFeatures(@NonNull android.content.ComponentName);
method @NonNull public String[] getLockTaskPackages(@NonNull android.content.ComponentName);
method @Nullable public CharSequence getLongSupportMessage(@NonNull android.content.ComponentName);
+ method public long getManagedProfileMaximumTimeOff(@NonNull android.content.ComponentName);
method public int getMaximumFailedPasswordsForWipe(@Nullable android.content.ComponentName);
method public long getMaximumTimeToLock(@Nullable android.content.ComponentName);
method @NonNull public java.util.List<java.lang.String> getMeteredDataDisabledPackages(@NonNull android.content.ComponentName);
@@ -6913,6 +6919,7 @@ package android.app.admin {
method public boolean removeOverrideApn(@NonNull android.content.ComponentName, int);
method public boolean removeUser(@NonNull android.content.ComponentName, @NonNull android.os.UserHandle);
method public boolean requestBugreport(@NonNull android.content.ComponentName);
+ method public void requestSetLocationProviderAllowed(@NonNull android.content.ComponentName, @NonNull String, boolean);
method @Deprecated public boolean resetPassword(String, int);
method public boolean resetPasswordWithToken(@NonNull android.content.ComponentName, String, byte[], int);
method @Nullable public java.util.List<android.app.admin.NetworkEvent> retrieveNetworkLogs(@Nullable android.content.ComponentName, long);
@@ -6956,6 +6963,7 @@ package android.app.admin {
method public void setLockdownAdminConfiguredNetworks(@NonNull android.content.ComponentName, boolean);
method public void setLogoutEnabled(@NonNull android.content.ComponentName, boolean);
method public void setLongSupportMessage(@NonNull android.content.ComponentName, @Nullable CharSequence);
+ method public void setManagedProfileMaximumTimeOff(@NonNull android.content.ComponentName, long);
method public void setMasterVolumeMuted(@NonNull android.content.ComponentName, boolean);
method public void setMaximumFailedPasswordsForWipe(@NonNull android.content.ComponentName, int);
method public void setMaximumTimeToLock(@NonNull android.content.ComponentName, long);
@@ -7142,6 +7150,7 @@ package android.app.admin {
field public static final int PERMISSION_POLICY_PROMPT = 0; // 0x0
field public static final int PERSONAL_APPS_NOT_SUSPENDED = 0; // 0x0
field public static final int PERSONAL_APPS_SUSPENDED_EXPLICITLY = 1; // 0x1
+ field public static final int PERSONAL_APPS_SUSPENDED_PROFILE_TIMEOUT = 2; // 0x2
field public static final String POLICY_DISABLE_CAMERA = "policy_disable_camera";
field public static final String POLICY_DISABLE_SCREEN_CAPTURE = "policy_disable_screen_capture";
field public static final int PRIVATE_DNS_MODE_OFF = 1; // 0x1
@@ -7543,6 +7552,7 @@ package android.app.blob {
method public boolean isPackageAccessAllowed(@NonNull String, @NonNull byte[]) throws java.io.IOException;
method public boolean isPublicAccessAllowed() throws java.io.IOException;
method public boolean isSameSignatureAccessAllowed() throws java.io.IOException;
+ method @NonNull public android.os.ParcelFileDescriptor openRead() throws java.io.IOException;
method @NonNull public android.os.ParcelFileDescriptor openWrite(long, long) throws java.io.IOException;
}
@@ -10169,6 +10179,7 @@ package android.content {
field public static final String USB_SERVICE = "usb";
field public static final String USER_SERVICE = "user";
field public static final String VIBRATOR_SERVICE = "vibrator";
+ field public static final String VPN_MANAGEMENT_SERVICE = "vpn_management";
field public static final String WALLPAPER_SERVICE = "wallpaper";
field public static final String WIFI_AWARE_SERVICE = "wifiaware";
field public static final String WIFI_P2P_SERVICE = "wifip2p";
@@ -16728,6 +16739,7 @@ package android.hardware {
field public static final String STRING_TYPE_GYROSCOPE_UNCALIBRATED = "android.sensor.gyroscope_uncalibrated";
field public static final String STRING_TYPE_HEART_BEAT = "android.sensor.heart_beat";
field public static final String STRING_TYPE_HEART_RATE = "android.sensor.heart_rate";
+ field public static final String STRING_TYPE_HINGE_ANGLE = "android.sensor.hinge_angle";
field public static final String STRING_TYPE_LIGHT = "android.sensor.light";
field public static final String STRING_TYPE_LINEAR_ACCELERATION = "android.sensor.linear_acceleration";
field public static final String STRING_TYPE_LOW_LATENCY_OFFBODY_DETECT = "android.sensor.low_latency_offbody_detect";
@@ -16757,6 +16769,7 @@ package android.hardware {
field public static final int TYPE_GYROSCOPE_UNCALIBRATED = 16; // 0x10
field public static final int TYPE_HEART_BEAT = 31; // 0x1f
field public static final int TYPE_HEART_RATE = 21; // 0x15
+ field public static final int TYPE_HINGE_ANGLE = 36; // 0x24
field public static final int TYPE_LIGHT = 5; // 0x5
field public static final int TYPE_LINEAR_ACCELERATION = 10; // 0xa
field public static final int TYPE_LOW_LATENCY_OFFBODY_DETECT = 34; // 0x22
@@ -17153,6 +17166,7 @@ package android.hardware.camera2 {
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> REQUEST_PARTIAL_RESULT_COUNT;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Byte> REQUEST_PIPELINE_MAX_DEPTH;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Float> SCALER_AVAILABLE_MAX_DIGITAL_ZOOM;
+ field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> SCALER_AVAILABLE_ROTATE_AND_CROP_MODES;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> SCALER_CROPPING_TYPE;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.MandatoryStreamCombination[]> SCALER_MANDATORY_STREAM_COMBINATIONS;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.StreamConfigurationMap> SCALER_STREAM_CONFIGURATION_MAP;
@@ -17259,6 +17273,8 @@ package android.hardware.camera2 {
method public void onCameraAccessPrioritiesChanged();
method public void onCameraAvailable(@NonNull String);
method public void onCameraUnavailable(@NonNull String);
+ method public void onPhysicalCameraAvailable(@NonNull String, @NonNull String);
+ method public void onPhysicalCameraUnavailable(@NonNull String, @NonNull String);
}
public abstract static class CameraManager.TorchCallback {
@@ -17431,6 +17447,11 @@ package android.hardware.camera2 {
field public static final int REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING = 7; // 0x7
field public static final int SCALER_CROPPING_TYPE_CENTER_ONLY = 0; // 0x0
field public static final int SCALER_CROPPING_TYPE_FREEFORM = 1; // 0x1
+ field public static final int SCALER_ROTATE_AND_CROP_180 = 2; // 0x2
+ field public static final int SCALER_ROTATE_AND_CROP_270 = 3; // 0x3
+ field public static final int SCALER_ROTATE_AND_CROP_90 = 1; // 0x1
+ field public static final int SCALER_ROTATE_AND_CROP_AUTO = 4; // 0x4
+ field public static final int SCALER_ROTATE_AND_CROP_NONE = 0; // 0x0
field public static final int SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_BGGR = 3; // 0x3
field public static final int SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GBRG = 2; // 0x2
field public static final int SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GRBG = 1; // 0x1
@@ -17566,6 +17587,7 @@ package android.hardware.camera2 {
field @NonNull public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Integer> NOISE_REDUCTION_MODE;
field @NonNull public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Float> REPROCESS_EFFECTIVE_EXPOSURE_FACTOR;
field @NonNull public static final android.hardware.camera2.CaptureRequest.Key<android.graphics.Rect> SCALER_CROP_REGION;
+ field @NonNull public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Integer> SCALER_ROTATE_AND_CROP;
field @NonNull public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Long> SENSOR_EXPOSURE_TIME;
field @NonNull public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Long> SENSOR_FRAME_DURATION;
field @NonNull public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Integer> SENSOR_SENSITIVITY;
@@ -17662,6 +17684,7 @@ package android.hardware.camera2 {
field @NonNull public static final android.hardware.camera2.CaptureResult.Key<java.lang.Float> REPROCESS_EFFECTIVE_EXPOSURE_FACTOR;
field @NonNull public static final android.hardware.camera2.CaptureResult.Key<java.lang.Byte> REQUEST_PIPELINE_DEPTH;
field @NonNull public static final android.hardware.camera2.CaptureResult.Key<android.graphics.Rect> SCALER_CROP_REGION;
+ field @NonNull public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> SCALER_ROTATE_AND_CROP;
field @NonNull public static final android.hardware.camera2.CaptureResult.Key<float[]> SENSOR_DYNAMIC_BLACK_LEVEL;
field @NonNull public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> SENSOR_DYNAMIC_WHITE_LEVEL;
field @NonNull public static final android.hardware.camera2.CaptureResult.Key<java.lang.Long> SENSOR_EXPOSURE_TIME;
@@ -23390,6 +23413,9 @@ package android.location {
method public long getFullBiasNanos();
method public int getHardwareClockDiscontinuityCount();
method public int getLeapSecond();
+ method @FloatRange(from=0.0) public double getReferenceCarrierFrequencyHzForIsb();
+ method @NonNull public String getReferenceCodeTypeForIsb();
+ method public int getReferenceConstellationTypeForIsb();
method public long getTimeNanos();
method @FloatRange(from=0.0f) public double getTimeUncertaintyNanos();
method public boolean hasBiasNanos();
@@ -23400,6 +23426,9 @@ package android.location {
method public boolean hasElapsedRealtimeUncertaintyNanos();
method public boolean hasFullBiasNanos();
method public boolean hasLeapSecond();
+ method public boolean hasReferenceCarrierFrequencyHzForIsb();
+ method public boolean hasReferenceCodeTypeForIsb();
+ method public boolean hasReferenceConstellationTypeForIsb();
method public boolean hasTimeUncertaintyNanos();
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.location.GnssClock> CREATOR;
@@ -23424,6 +23453,10 @@ package android.location {
method public double getPseudorangeRateUncertaintyMetersPerSecond();
method public long getReceivedSvTimeNanos();
method public long getReceivedSvTimeUncertaintyNanos();
+ method public double getReceiverInterSignalBiasNanos();
+ method @FloatRange(from=0.0) public double getReceiverInterSignalBiasUncertaintyNanos();
+ method public double getSatelliteInterSignalBiasNanos();
+ method @FloatRange(from=0.0) public double getSatelliteInterSignalBiasUncertaintyNanos();
method public double getSnrInDb();
method public int getState();
method public int getSvid();
@@ -23435,6 +23468,10 @@ package android.location {
method @Deprecated public boolean hasCarrierPhase();
method @Deprecated public boolean hasCarrierPhaseUncertainty();
method public boolean hasCodeType();
+ method public boolean hasReceiverInterSignalBiasNanos();
+ method public boolean hasReceiverInterSignalBiasUncertaintyNanos();
+ method public boolean hasSatelliteInterSignalBiasNanos();
+ method public boolean hasSatelliteInterSignalBiasUncertaintyNanos();
method public boolean hasSnrInDb();
method public void writeToParcel(android.os.Parcel, int);
field public static final int ADR_STATE_CYCLE_SLIP = 4; // 0x4
@@ -25953,6 +25990,7 @@ package android.media {
field public static final String KEY_CAPTURE_RATE = "capture-rate";
field public static final String KEY_CHANNEL_COUNT = "channel-count";
field public static final String KEY_CHANNEL_MASK = "channel-mask";
+ field public static final String KEY_CODECS_STRING = "codecs-string";
field public static final String KEY_COLOR_FORMAT = "color-format";
field public static final String KEY_COLOR_RANGE = "color-range";
field public static final String KEY_COLOR_STANDARD = "color-standard";
@@ -27634,6 +27672,8 @@ package android.media.audiofx {
field public static final int CONTENT_TYPE_VOICE = 3; // 0x3
field public static final String EFFECT_AUXILIARY = "Auxiliary";
field public static final String EFFECT_INSERT = "Insert";
+ field public static final String EFFECT_POST_PROCESSING = "Post Processing";
+ field public static final String EFFECT_PRE_PROCESSING = "Pre Processing";
field public static final java.util.UUID EFFECT_TYPE_AEC;
field public static final java.util.UUID EFFECT_TYPE_AGC;
field public static final java.util.UUID EFFECT_TYPE_BASS_BOOST;
@@ -29584,6 +29624,18 @@ package android.net {
method public long getReportTimestamp();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.net.ConnectivityDiagnosticsManager.ConnectivityReport> CREATOR;
+ field public static final String KEY_NETWORK_PROBES_ATTEMPTED_BITMASK = "networkProbesAttemped";
+ field public static final String KEY_NETWORK_PROBES_SUCCEEDED_BITMASK = "networkProbesSucceeded";
+ field public static final String KEY_NETWORK_VALIDATION_RESULT = "networkValidationResult";
+ field public static final int NETWORK_PROBE_DNS = 4; // 0x4
+ field public static final int NETWORK_PROBE_FALLBACK = 32; // 0x20
+ field public static final int NETWORK_PROBE_HTTP = 8; // 0x8
+ field public static final int NETWORK_PROBE_HTTPS = 16; // 0x10
+ field public static final int NETWORK_PROBE_PRIVATE_DNS = 64; // 0x40
+ field public static final int NETWORK_VALIDATION_RESULT_INVALID = 0; // 0x0
+ field public static final int NETWORK_VALIDATION_RESULT_PARTIALLY_VALID = 2; // 0x2
+ field public static final int NETWORK_VALIDATION_RESULT_SKIPPED = 3; // 0x3
+ field public static final int NETWORK_VALIDATION_RESULT_VALID = 1; // 0x1
}
public static final class ConnectivityDiagnosticsManager.DataStallReport implements android.os.Parcelable {
@@ -29597,6 +29649,9 @@ package android.net {
field @NonNull public static final android.os.Parcelable.Creator<android.net.ConnectivityDiagnosticsManager.DataStallReport> CREATOR;
field public static final int DETECTION_METHOD_DNS_EVENTS = 1; // 0x1
field public static final int DETECTION_METHOD_TCP_METRICS = 2; // 0x2
+ field public static final String KEY_DNS_CONSECUTIVE_TIMEOUTS = "dnsConsecutiveTimeouts";
+ field public static final String KEY_TCP_METRICS_COLLECTION_PERIOD_MILLIS = "tcpMetricsCollectionPeriodMillis";
+ field public static final String KEY_TCP_PACKET_FAIL_RATE = "tcpPacketFailRate";
}
public class ConnectivityManager {
@@ -29739,6 +29794,35 @@ package android.net {
field public final int code;
}
+ public final class Ikev2VpnProfile extends android.net.PlatformVpnProfile {
+ method @NonNull public java.util.List<java.lang.String> getAllowedAlgorithms();
+ method public int getMaxMtu();
+ method @Nullable public String getPassword();
+ method @Nullable public byte[] getPresharedKey();
+ method @Nullable public android.net.ProxyInfo getProxyInfo();
+ method @Nullable public java.security.PrivateKey getRsaPrivateKey();
+ method @NonNull public String getServerAddr();
+ method @Nullable public java.security.cert.X509Certificate getServerRootCaCert();
+ method @Nullable public java.security.cert.X509Certificate getUserCert();
+ method @NonNull public String getUserIdentity();
+ method @Nullable public String getUsername();
+ method public boolean isBypassable();
+ method public boolean isMetered();
+ }
+
+ public static final class Ikev2VpnProfile.Builder {
+ ctor public Ikev2VpnProfile.Builder(@NonNull String, @NonNull String);
+ method @NonNull public android.net.Ikev2VpnProfile build();
+ method @NonNull public android.net.Ikev2VpnProfile.Builder setAllowedAlgorithms(@NonNull java.util.List<java.lang.String>);
+ method @NonNull public android.net.Ikev2VpnProfile.Builder setAuthDigitalSignature(@NonNull java.security.cert.X509Certificate, @NonNull java.security.PrivateKey, @Nullable java.security.cert.X509Certificate);
+ method @NonNull public android.net.Ikev2VpnProfile.Builder setAuthPsk(@NonNull byte[]);
+ method @NonNull public android.net.Ikev2VpnProfile.Builder setAuthUsernamePassword(@NonNull String, @NonNull String, @Nullable java.security.cert.X509Certificate);
+ method @NonNull public android.net.Ikev2VpnProfile.Builder setBypassable(boolean);
+ method @NonNull public android.net.Ikev2VpnProfile.Builder setMaxMtu(int);
+ method @NonNull public android.net.Ikev2VpnProfile.Builder setMetered(boolean);
+ method @NonNull public android.net.Ikev2VpnProfile.Builder setProxy(@Nullable android.net.ProxyInfo);
+ }
+
public class InetAddresses {
method public static boolean isNumericAddress(@NonNull String);
method @NonNull public static java.net.InetAddress parseNumericAddress(@NonNull String);
@@ -29967,6 +30051,7 @@ package android.net {
method public int getLinkDownstreamBandwidthKbps();
method public int getLinkUpstreamBandwidthKbps();
method @Nullable public android.net.NetworkSpecifier getNetworkSpecifier();
+ method public int getOwnerUid();
method public int getSignalStrength();
method @Nullable public android.net.TransportInfo getTransportInfo();
method public boolean hasCapability(int);
@@ -29976,6 +30061,7 @@ package android.net {
method @NonNull public android.net.NetworkCapabilities setLinkDownstreamBandwidthKbps(int);
method @NonNull public android.net.NetworkCapabilities setLinkUpstreamBandwidthKbps(int);
method @NonNull public android.net.NetworkCapabilities setNetworkSpecifier(@NonNull android.net.NetworkSpecifier);
+ method public void setOwnerUid(int);
method @NonNull public android.net.NetworkCapabilities setSignalStrength(int);
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.net.NetworkCapabilities> CREATOR;
@@ -30087,6 +30173,14 @@ package android.net {
field public String response;
}
+ public abstract class PlatformVpnProfile {
+ method public final int getType();
+ method @NonNull public final String getTypeString();
+ field public static final int TYPE_IKEV2_IPSEC_PSK = 7; // 0x7
+ field public static final int TYPE_IKEV2_IPSEC_RSA = 8; // 0x8
+ field public static final int TYPE_IKEV2_IPSEC_USER_PASS = 6; // 0x6
+ }
+
public final class Proxy {
ctor public Proxy();
method @Deprecated public static String getDefaultHost();
@@ -30367,6 +30461,13 @@ package android.net {
method public String sanitize(String);
}
+ public class VpnManager {
+ method public void deleteProvisionedVpnProfile();
+ method @Nullable public android.content.Intent provisionVpnProfile(@NonNull android.net.PlatformVpnProfile);
+ method public void startProvisionedVpnProfile();
+ method public void stopProvisionedVpnProfile();
+ }
+
public class VpnService extends android.app.Service {
ctor public VpnService();
method public final boolean isAlwaysOn();
@@ -42749,6 +42850,7 @@ package android.service.autofill {
public static final class FillResponse.Builder {
ctor public FillResponse.Builder();
method @NonNull public android.service.autofill.FillResponse.Builder addDataset(@Nullable android.service.autofill.Dataset);
+ method @NonNull public android.service.autofill.FillResponse.Builder addInlineAction(@NonNull android.service.autofill.InlinePresentation);
method @NonNull public android.service.autofill.FillResponse build();
method @NonNull public android.service.autofill.FillResponse.Builder disableAutofill(long);
method @NonNull public android.service.autofill.FillResponse.Builder setAuthentication(@NonNull android.view.autofill.AutofillId[], @Nullable android.content.IntentSender, @Nullable android.widget.RemoteViews);
@@ -42778,10 +42880,11 @@ package android.service.autofill {
}
public final class InlinePresentation implements android.os.Parcelable {
- ctor public InlinePresentation(@NonNull android.app.slice.Slice, @NonNull android.view.inline.InlinePresentationSpec);
+ ctor public InlinePresentation(@NonNull android.app.slice.Slice, @NonNull android.view.inline.InlinePresentationSpec, boolean);
method public int describeContents();
method @NonNull public android.view.inline.InlinePresentationSpec getInlinePresentationSpec();
method @NonNull public android.app.slice.Slice getSlice();
+ method public boolean isPinned();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.service.autofill.InlinePresentation> CREATOR;
}
@@ -43029,6 +43132,307 @@ package android.service.chooser {
}
+package android.service.controls {
+
+ public final class Control implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public android.app.PendingIntent getAppIntent();
+ method @NonNull public String getControlId();
+ method @NonNull public android.service.controls.templates.ControlTemplate getControlTemplate();
+ method @Nullable public android.content.res.ColorStateList getCustomColor();
+ method @Nullable public android.graphics.drawable.Icon getCustomIcon();
+ method public int getDeviceType();
+ method public int getStatus();
+ method @NonNull public CharSequence getStatusText();
+ method @Nullable public CharSequence getStructure();
+ method @NonNull public CharSequence getSubtitle();
+ method @NonNull public CharSequence getTitle();
+ method @Nullable public CharSequence getZone();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.service.controls.Control> CREATOR;
+ field public static final int STATUS_DISABLED = 4; // 0x4
+ field public static final int STATUS_ERROR = 3; // 0x3
+ field public static final int STATUS_NOT_FOUND = 2; // 0x2
+ field public static final int STATUS_OK = 1; // 0x1
+ field public static final int STATUS_UNKNOWN = 0; // 0x0
+ }
+
+ public static final class Control.StatefulBuilder {
+ ctor public Control.StatefulBuilder(@NonNull String, @NonNull android.app.PendingIntent);
+ ctor public Control.StatefulBuilder(@NonNull android.service.controls.Control);
+ method @NonNull public android.service.controls.Control build();
+ method @NonNull public android.service.controls.Control.StatefulBuilder setAppIntent(@NonNull android.app.PendingIntent);
+ method @NonNull public android.service.controls.Control.StatefulBuilder setControlId(@NonNull String);
+ method @NonNull public android.service.controls.Control.StatefulBuilder setControlTemplate(@NonNull android.service.controls.templates.ControlTemplate);
+ method @NonNull public android.service.controls.Control.StatefulBuilder setCustomColor(@Nullable android.content.res.ColorStateList);
+ method @NonNull public android.service.controls.Control.StatefulBuilder setCustomIcon(@Nullable android.graphics.drawable.Icon);
+ method @NonNull public android.service.controls.Control.StatefulBuilder setDeviceType(int);
+ method @NonNull public android.service.controls.Control.StatefulBuilder setStatus(int);
+ method @NonNull public android.service.controls.Control.StatefulBuilder setStatusText(@NonNull CharSequence);
+ method @NonNull public android.service.controls.Control.StatefulBuilder setStructure(@Nullable CharSequence);
+ method @NonNull public android.service.controls.Control.StatefulBuilder setSubtitle(@NonNull CharSequence);
+ method @NonNull public android.service.controls.Control.StatefulBuilder setTitle(@NonNull CharSequence);
+ method @NonNull public android.service.controls.Control.StatefulBuilder setZone(@Nullable CharSequence);
+ }
+
+ public static final class Control.StatelessBuilder {
+ ctor public Control.StatelessBuilder(@NonNull String, @NonNull android.app.PendingIntent);
+ ctor public Control.StatelessBuilder(@NonNull android.service.controls.Control);
+ method @NonNull public android.service.controls.Control build();
+ method @NonNull public android.service.controls.Control.StatelessBuilder setAppIntent(@NonNull android.app.PendingIntent);
+ method @NonNull public android.service.controls.Control.StatelessBuilder setControlId(@NonNull String);
+ method @NonNull public android.service.controls.Control.StatelessBuilder setCustomColor(@Nullable android.content.res.ColorStateList);
+ method @NonNull public android.service.controls.Control.StatelessBuilder setCustomIcon(@Nullable android.graphics.drawable.Icon);
+ method @NonNull public android.service.controls.Control.StatelessBuilder setDeviceType(int);
+ method @NonNull public android.service.controls.Control.StatelessBuilder setStructure(@Nullable CharSequence);
+ method @NonNull public android.service.controls.Control.StatelessBuilder setSubtitle(@NonNull CharSequence);
+ method @NonNull public android.service.controls.Control.StatelessBuilder setTitle(@NonNull CharSequence);
+ method @NonNull public android.service.controls.Control.StatelessBuilder setZone(@Nullable CharSequence);
+ }
+
+ public abstract class ControlsProviderService extends android.app.Service {
+ ctor public ControlsProviderService();
+ method public abstract void loadAvailableControls(@NonNull java.util.function.Consumer<java.util.List<android.service.controls.Control>>);
+ method @NonNull public final android.os.IBinder onBind(@NonNull android.content.Intent);
+ method public abstract void performControlAction(@NonNull String, @NonNull android.service.controls.actions.ControlAction, @NonNull java.util.function.Consumer<java.lang.Integer>);
+ method @NonNull public abstract java.util.concurrent.Flow.Publisher<android.service.controls.Control> publisherFor(@NonNull java.util.List<java.lang.String>);
+ field public static final String SERVICE_CONTROLS = "android.service.controls.ControlsProviderService";
+ field @NonNull public static final String TAG = "ControlsProviderService";
+ }
+
+ public class DeviceTypes {
+ method public static boolean validDeviceType(int);
+ field public static final int TYPE_AC_HEATER = 1; // 0x1
+ field public static final int TYPE_AC_UNIT = 2; // 0x2
+ field public static final int TYPE_AIR_FRESHENER = 3; // 0x3
+ field public static final int TYPE_AIR_PURIFIER = 4; // 0x4
+ field public static final int TYPE_AWNING = 33; // 0x21
+ field public static final int TYPE_BLINDS = 34; // 0x22
+ field public static final int TYPE_CAMERA = 50; // 0x32
+ field public static final int TYPE_CLOSET = 35; // 0x23
+ field public static final int TYPE_COFFEE_MAKER = 5; // 0x5
+ field public static final int TYPE_CURTAIN = 36; // 0x24
+ field public static final int TYPE_DEHUMIDIFIER = 6; // 0x6
+ field public static final int TYPE_DISHWASHER = 24; // 0x18
+ field public static final int TYPE_DISPLAY = 7; // 0x7
+ field public static final int TYPE_DOOR = 37; // 0x25
+ field public static final int TYPE_DOORBELL = 51; // 0x33
+ field public static final int TYPE_DRAWER = 38; // 0x26
+ field public static final int TYPE_DRYER = 25; // 0x19
+ field public static final int TYPE_FAN = 8; // 0x8
+ field public static final int TYPE_GARAGE = 39; // 0x27
+ field public static final int TYPE_GATE = 40; // 0x28
+ field public static final int TYPE_GENERIC_ARM_DISARM = -5; // 0xfffffffb
+ field public static final int TYPE_GENERIC_LOCK_UNLOCK = -4; // 0xfffffffc
+ field public static final int TYPE_GENERIC_ON_OFF = -1; // 0xffffffff
+ field public static final int TYPE_GENERIC_OPEN_CLOSE = -3; // 0xfffffffd
+ field public static final int TYPE_GENERIC_START_STOP = -2; // 0xfffffffe
+ field public static final int TYPE_GENERIC_TEMP_SETTING = -6; // 0xfffffffa
+ field public static final int TYPE_GENERIC_VIEWSTREAM = -7; // 0xfffffff9
+ field public static final int TYPE_HEATER = 47; // 0x2f
+ field public static final int TYPE_HOOD = 10; // 0xa
+ field public static final int TYPE_HUMIDIFIER = 11; // 0xb
+ field public static final int TYPE_KETTLE = 12; // 0xc
+ field public static final int TYPE_LIGHT = 13; // 0xd
+ field public static final int TYPE_LOCK = 45; // 0x2d
+ field public static final int TYPE_MICROWAVE = 14; // 0xe
+ field public static final int TYPE_MOP = 26; // 0x1a
+ field public static final int TYPE_MOWER = 27; // 0x1b
+ field public static final int TYPE_MULTICOOKER = 28; // 0x1c
+ field public static final int TYPE_OUTLET = 15; // 0xf
+ field public static final int TYPE_PERGOLA = 41; // 0x29
+ field public static final int TYPE_RADIATOR = 16; // 0x10
+ field public static final int TYPE_REFRIGERATOR = 48; // 0x30
+ field public static final int TYPE_REMOTE_CONTROL = 17; // 0x11
+ field public static final int TYPE_SECURITY_SYSTEM = 46; // 0x2e
+ field public static final int TYPE_SET_TOP = 18; // 0x12
+ field public static final int TYPE_SHOWER = 29; // 0x1d
+ field public static final int TYPE_SHUTTER = 42; // 0x2a
+ field public static final int TYPE_SPRINKLER = 30; // 0x1e
+ field public static final int TYPE_STANDMIXER = 19; // 0x13
+ field public static final int TYPE_STYLER = 20; // 0x14
+ field public static final int TYPE_SWITCH = 21; // 0x15
+ field public static final int TYPE_THERMOSTAT = 49; // 0x31
+ field public static final int TYPE_TV = 22; // 0x16
+ field public static final int TYPE_UNKNOWN = 0; // 0x0
+ field public static final int TYPE_VACUUM = 32; // 0x20
+ field public static final int TYPE_VALVE = 44; // 0x2c
+ field public static final int TYPE_WASHER = 31; // 0x1f
+ field public static final int TYPE_WATER_HEATER = 23; // 0x17
+ field public static final int TYPE_WINDOW = 43; // 0x2b
+ }
+
+}
+
+package android.service.controls.actions {
+
+ public final class BooleanAction extends android.service.controls.actions.ControlAction {
+ ctor public BooleanAction(@NonNull String, boolean);
+ ctor public BooleanAction(@NonNull String, boolean, @Nullable String);
+ method public int getActionType();
+ method public boolean getNewState();
+ }
+
+ public final class CommandAction extends android.service.controls.actions.ControlAction {
+ ctor public CommandAction(@NonNull String, @Nullable String);
+ ctor public CommandAction(@NonNull String);
+ method public int getActionType();
+ }
+
+ public abstract class ControlAction {
+ method public abstract int getActionType();
+ method @Nullable public String getChallengeValue();
+ method @NonNull public String getTemplateId();
+ method public static final boolean isValidResponse(int);
+ field @NonNull public static final android.service.controls.actions.ControlAction ERROR_ACTION;
+ field public static final int RESPONSE_CHALLENGE_ACK = 3; // 0x3
+ field public static final int RESPONSE_CHALLENGE_PASSPHRASE = 5; // 0x5
+ field public static final int RESPONSE_CHALLENGE_PIN = 4; // 0x4
+ field public static final int RESPONSE_FAIL = 2; // 0x2
+ field public static final int RESPONSE_OK = 1; // 0x1
+ field public static final int RESPONSE_UNKNOWN = 0; // 0x0
+ field public static final int TYPE_BOOLEAN = 1; // 0x1
+ field public static final int TYPE_COMMAND = 5; // 0x5
+ field public static final int TYPE_ERROR = -1; // 0xffffffff
+ field public static final int TYPE_FLOAT = 2; // 0x2
+ field public static final int TYPE_MODE = 4; // 0x4
+ field public static final int TYPE_MULTI_FLOAT = 3; // 0x3
+ }
+
+ public final class FloatAction extends android.service.controls.actions.ControlAction {
+ ctor public FloatAction(@NonNull String, float);
+ ctor public FloatAction(@NonNull String, float, @Nullable String);
+ method public int getActionType();
+ method public float getNewValue();
+ }
+
+ public final class ModeAction extends android.service.controls.actions.ControlAction {
+ ctor public ModeAction(@NonNull String, int, @Nullable String);
+ ctor public ModeAction(@NonNull String, int);
+ method public int getActionType();
+ method public int getNewMode();
+ }
+
+ public final class MultiFloatAction extends android.service.controls.actions.ControlAction {
+ ctor public MultiFloatAction(@NonNull String, @NonNull float[], @Nullable String);
+ ctor public MultiFloatAction(@NonNull String, @NonNull float[]);
+ method public int getActionType();
+ method @NonNull public float[] getNewValues();
+ }
+
+}
+
+package android.service.controls.templates {
+
+ public final class ControlButton implements android.os.Parcelable {
+ ctor public ControlButton(boolean, @NonNull CharSequence);
+ method public int describeContents();
+ method @NonNull public CharSequence getActionDescription();
+ method public boolean isChecked();
+ method @NonNull public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.service.controls.templates.ControlButton> CREATOR;
+ }
+
+ public abstract class ControlTemplate {
+ method @NonNull public String getTemplateId();
+ method public abstract int getTemplateType();
+ field @NonNull public static final android.service.controls.templates.ControlTemplate ERROR_TEMPLATE;
+ field @NonNull public static final android.service.controls.templates.ControlTemplate NO_TEMPLATE;
+ field public static final int TYPE_DISCRETE_TOGGLE = 4; // 0x4
+ field public static final int TYPE_ERROR = -1; // 0xffffffff
+ field public static final int TYPE_NONE = 0; // 0x0
+ field public static final int TYPE_RANGE = 2; // 0x2
+ field public static final int TYPE_STATELESS = 8; // 0x8
+ field public static final int TYPE_TEMPERATURE = 7; // 0x7
+ field public static final int TYPE_THUMBNAIL = 3; // 0x3
+ field public static final int TYPE_TOGGLE = 1; // 0x1
+ field public static final int TYPE_TOGGLE_RANGE = 6; // 0x6
+ }
+
+ public final class CoordinatedRangeTemplate extends android.service.controls.templates.ControlTemplate {
+ ctor public CoordinatedRangeTemplate(@NonNull String, float, @NonNull android.service.controls.templates.RangeTemplate, @NonNull android.service.controls.templates.RangeTemplate);
+ ctor public CoordinatedRangeTemplate(@NonNull String, float, float, float, float, float, float, float, float, @Nullable CharSequence);
+ method public float getCurrentValueHigh();
+ method public float getCurrentValueLow();
+ method @NonNull public CharSequence getFormatString();
+ method public float getMaxValueHigh();
+ method public float getMaxValueLow();
+ method public float getMinGap();
+ method public float getMinValueHigh();
+ method public float getMinValueLow();
+ method @NonNull public android.service.controls.templates.RangeTemplate getRangeHigh();
+ method @NonNull public android.service.controls.templates.RangeTemplate getRangeLow();
+ method public float getStepValue();
+ method public int getTemplateType();
+ }
+
+ public final class DiscreteToggleTemplate extends android.service.controls.templates.ControlTemplate {
+ ctor public DiscreteToggleTemplate(@NonNull String, @NonNull android.service.controls.templates.ControlButton, @NonNull android.service.controls.templates.ControlButton);
+ method @NonNull public android.service.controls.templates.ControlButton getNegativeButton();
+ method @NonNull public android.service.controls.templates.ControlButton getPositiveButton();
+ method public int getTemplateType();
+ }
+
+ public final class RangeTemplate extends android.service.controls.templates.ControlTemplate {
+ ctor public RangeTemplate(@NonNull String, float, float, float, float, @Nullable CharSequence);
+ method public float getCurrentValue();
+ method @NonNull public CharSequence getFormatString();
+ method public float getMaxValue();
+ method public float getMinValue();
+ method public float getStepValue();
+ method public int getTemplateType();
+ }
+
+ public final class StatelessTemplate extends android.service.controls.templates.ControlTemplate {
+ ctor public StatelessTemplate(@NonNull String);
+ method public int getTemplateType();
+ }
+
+ public final class TemperatureControlTemplate extends android.service.controls.templates.ControlTemplate {
+ ctor public TemperatureControlTemplate(@NonNull String, @NonNull android.service.controls.templates.ControlTemplate, int, int, int);
+ method public int getCurrentActiveMode();
+ method public int getCurrentMode();
+ method public int getModes();
+ method @NonNull public android.service.controls.templates.ControlTemplate getTemplate();
+ method public int getTemplateType();
+ field public static final int FLAG_MODE_COOL = 8; // 0x8
+ field public static final int FLAG_MODE_ECO = 32; // 0x20
+ field public static final int FLAG_MODE_HEAT = 4; // 0x4
+ field public static final int FLAG_MODE_HEAT_COOL = 16; // 0x10
+ field public static final int FLAG_MODE_OFF = 2; // 0x2
+ field public static final int MODE_COOL = 3; // 0x3
+ field public static final int MODE_ECO = 5; // 0x5
+ field public static final int MODE_HEAT = 2; // 0x2
+ field public static final int MODE_HEAT_COOL = 4; // 0x4
+ field public static final int MODE_OFF = 1; // 0x1
+ field public static final int MODE_UNKNOWN = 0; // 0x0
+ }
+
+ public final class ThumbnailTemplate extends android.service.controls.templates.ControlTemplate {
+ ctor public ThumbnailTemplate(@NonNull String, @NonNull android.graphics.drawable.Icon, @NonNull CharSequence);
+ method @NonNull public CharSequence getContentDescription();
+ method public int getTemplateType();
+ method @NonNull public android.graphics.drawable.Icon getThumbnail();
+ }
+
+ public final class ToggleRangeTemplate extends android.service.controls.templates.ControlTemplate {
+ ctor public ToggleRangeTemplate(@NonNull String, @NonNull android.service.controls.templates.ControlButton, @NonNull android.service.controls.templates.RangeTemplate);
+ ctor public ToggleRangeTemplate(@NonNull String, boolean, @NonNull CharSequence, @NonNull android.service.controls.templates.RangeTemplate);
+ method @NonNull public CharSequence getActionDescription();
+ method @NonNull public android.service.controls.templates.RangeTemplate getRange();
+ method public int getTemplateType();
+ method public boolean isChecked();
+ }
+
+ public final class ToggleTemplate extends android.service.controls.templates.ControlTemplate {
+ ctor public ToggleTemplate(@NonNull String, @NonNull android.service.controls.templates.ControlButton);
+ method @NonNull public CharSequence getContentDescription();
+ method public int getTemplateType();
+ method public boolean isChecked();
+ }
+
+}
+
package android.service.dreams {
public class DreamService extends android.app.Service implements android.view.Window.Callback {
@@ -45328,6 +45732,9 @@ package android.telecom {
field public static final int MISSED = 5; // 0x5
field public static final int OTHER = 9; // 0x9
field public static final String REASON_EMERGENCY_CALL_PLACED = "REASON_EMERGENCY_CALL_PLACED";
+ field public static final String REASON_EMULATING_SINGLE_CALL = "EMULATING_SINGLE_CALL";
+ field public static final String REASON_IMS_ACCESS_BLOCKED = "REASON_IMS_ACCESS_BLOCKED";
+ field public static final String REASON_WIFI_ON_BUT_WFC_OFF = "REASON_WIFI_ON_BUT_WFC_OFF";
field public static final int REJECTED = 6; // 0x6
field public static final int REMOTE = 3; // 0x3
field public static final int RESTRICTED = 8; // 0x8
@@ -45656,6 +46063,7 @@ package android.telecom {
field public static final int DURATION_SHORT = 1; // 0x1
field public static final int DURATION_VERY_SHORT = 0; // 0x0
field public static final String EXTRA_CALL_BACK_NUMBER = "android.telecom.extra.CALL_BACK_NUMBER";
+ field public static final String EXTRA_CALL_CREATED_TIME_MILLIS = "android.telecom.extra.CALL_CREATED_TIME_MILLIS";
field public static final String EXTRA_CALL_DISCONNECT_CAUSE = "android.telecom.extra.CALL_DISCONNECT_CAUSE";
field public static final String EXTRA_CALL_DISCONNECT_MESSAGE = "android.telecom.extra.CALL_DISCONNECT_MESSAGE";
field public static final String EXTRA_CALL_DURATION = "android.telecom.extra.CALL_DURATION";
@@ -47714,10 +48122,10 @@ package android.telephony.euicc {
field public static final int ERROR_CERTIFICATE_ERROR = 10012; // 0x271c
field public static final int ERROR_CONNECTION_ERROR = 10014; // 0x271e
field public static final int ERROR_DISALLOWED_BY_PPR = 10010; // 0x271a
- field public static final int ERROR_EUICC_GSMA_INSTALL_ERROR = 10009; // 0x2719
field public static final int ERROR_EUICC_INSUFFICIENT_MEMORY = 10004; // 0x2714
field public static final int ERROR_EUICC_MISSING = 10006; // 0x2716
field public static final int ERROR_INCOMPATIBLE_CARRIER = 10003; // 0x2713
+ field public static final int ERROR_INSTALL_PROFILE = 10009; // 0x2719
field public static final int ERROR_INVALID_ACTIVATION_CODE = 10001; // 0x2711
field public static final int ERROR_INVALID_CONFIRMATION_CODE = 10002; // 0x2712
field public static final int ERROR_INVALID_RESPONSE = 10015; // 0x271f
@@ -55744,6 +56152,7 @@ package android.view.contentcapture {
method public boolean isContentCaptureEnabled();
method public void removeData(@NonNull android.view.contentcapture.DataRemovalRequest);
method public void setContentCaptureEnabled(boolean);
+ method public void shareData(@NonNull android.view.contentcapture.DataShareRequest, @NonNull java.util.concurrent.Executor, @NonNull android.view.contentcapture.DataShareWriteAdapter);
}
public abstract class ContentCaptureSession implements java.lang.AutoCloseable {
@@ -55792,6 +56201,24 @@ package android.view.contentcapture {
method @NonNull public android.content.LocusId getLocusId();
}
+ public final class DataShareRequest implements android.os.Parcelable {
+ ctor public DataShareRequest(@Nullable android.content.LocusId, @NonNull String);
+ method public int describeContents();
+ method @Nullable public android.content.LocusId getLocusId();
+ method @NonNull public String getMimeType();
+ method @NonNull public String getPackageName();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.view.contentcapture.DataShareRequest> CREATOR;
+ }
+
+ public interface DataShareWriteAdapter {
+ method public default void onError(int);
+ method public void onRejected();
+ method public void onWrite(@NonNull android.os.ParcelFileDescriptor, @NonNull android.os.CancellationSignal);
+ field public static final int ERROR_CONCURRENT_REQUEST = 1; // 0x1
+ field public static final int ERROR_UNKNOWN = 2; // 0x2
+ }
+
}
package android.view.inline {
@@ -55993,10 +56420,13 @@ package android.view.inputmethod {
method @Nullable public String[] getAutofillHints();
method @NonNull public android.view.inline.InlinePresentationSpec getPresentationSpec();
method @NonNull public String getSource();
+ method @NonNull public String getType();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.view.inputmethod.InlineSuggestionInfo> CREATOR;
field public static final String SOURCE_AUTOFILL = "android:autofill";
field public static final String SOURCE_PLATFORM = "android:platform";
+ field public static final String TYPE_ACTION = "android:autofill:action";
+ field public static final String TYPE_SUGGESTION = "android:autofill:suggestion";
}
public final class InlineSuggestionsRequest implements android.os.Parcelable {
diff --git a/api/system-current.txt b/api/system-current.txt
index 0cc1af4a4108..a8d11ac05130 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -5,6 +5,7 @@ package android {
field public static final String ACCESS_AMBIENT_LIGHT_STATS = "android.permission.ACCESS_AMBIENT_LIGHT_STATS";
field public static final String ACCESS_BROADCAST_RADIO = "android.permission.ACCESS_BROADCAST_RADIO";
field public static final String ACCESS_CACHE_FILESYSTEM = "android.permission.ACCESS_CACHE_FILESYSTEM";
+ field public static final String ACCESS_CONTEXT_HUB = "android.permission.ACCESS_CONTEXT_HUB";
field public static final String ACCESS_DRM_CERTIFICATES = "android.permission.ACCESS_DRM_CERTIFICATES";
field @Deprecated public static final String ACCESS_FM_RADIO = "android.permission.ACCESS_FM_RADIO";
field public static final String ACCESS_INSTANT_APPS = "android.permission.ACCESS_INSTANT_APPS";
@@ -246,10 +247,15 @@ package android {
public static final class R.array {
field public static final int config_keySystemUuidMapping = 17235973; // 0x1070005
+ field public static final int config_restrictedPreinstalledCarrierApps = 17235975; // 0x1070007
+ field public static final int config_sms_enabled_locking_shift_tables = 17235977; // 0x1070009
+ field public static final int config_sms_enabled_single_shift_tables = 17235976; // 0x1070008
+ field public static final int simColors = 17235974; // 0x1070006
}
public static final class R.attr {
field public static final int allowClearUserDataOnFailedRestore = 16844288; // 0x1010600
+ field public static final int isAutofillInlineSuggestionTheme = 16844310; // 0x1010616
field public static final int isVrOnly = 16844152; // 0x1010578
field public static final int minExtensionVersion = 16844306; // 0x1010612
field public static final int requiredSystemPropertyName = 16844133; // 0x1010565
@@ -297,6 +303,7 @@ package android {
field public static final int config_helpPackageNameKey = 17039387; // 0x104001b
field public static final int config_helpPackageNameValue = 17039388; // 0x104001c
field public static final int config_systemGallery = 17039402; // 0x104002a
+ field public static final int low_memory = 17039403; // 0x104002b
}
public static final class R.style {
@@ -1824,6 +1831,7 @@ package android.content {
field @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public static final String ACTION_MANAGE_SPECIAL_APP_ACCESSES = "android.intent.action.MANAGE_SPECIAL_APP_ACCESSES";
field public static final String ACTION_MASTER_CLEAR_NOTIFICATION = "android.intent.action.MASTER_CLEAR_NOTIFICATION";
field public static final String ACTION_PACKAGE_NEEDS_INTEGRITY_VERIFICATION = "android.intent.action.PACKAGE_NEEDS_INTEGRITY_VERIFICATION";
+ field public static final String ACTION_PACKAGE_UNSUSPENDED_MANUALLY = "android.intent.action.PACKAGE_UNSUSPENDED_MANUALLY";
field public static final String ACTION_PENDING_INCIDENT_REPORTS_CHANGED = "android.intent.action.PENDING_INCIDENT_REPORTS_CHANGED";
field public static final String ACTION_PRE_BOOT_COMPLETED = "android.intent.action.PRE_BOOT_COMPLETED";
field public static final String ACTION_QUERY_PACKAGE_RESTART = "android.intent.action.QUERY_PACKAGE_RESTART";
@@ -1839,6 +1847,7 @@ package android.content {
field public static final String ACTION_UPGRADE_SETUP = "android.intent.action.UPGRADE_SETUP";
field public static final String ACTION_USER_ADDED = "android.intent.action.USER_ADDED";
field public static final String ACTION_USER_REMOVED = "android.intent.action.USER_REMOVED";
+ field @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public static final String ACTION_USER_SWITCHED = "android.intent.action.USER_SWITCHED";
field public static final String ACTION_VOICE_ASSIST = "android.intent.action.VOICE_ASSIST";
field public static final String CATEGORY_LEANBACK_SETTINGS = "android.intent.category.LEANBACK_SETTINGS";
field public static final String EXTRA_CALLING_PACKAGE = "android.intent.extra.CALLING_PACKAGE";
@@ -1891,26 +1900,6 @@ package android.content {
package android.content.integrity {
- public final class AppInstallMetadata {
- method @NonNull public String getAppCertificate();
- method @Nullable public String getInstallerCertificate();
- method @Nullable public String getInstallerName();
- method @NonNull public String getPackageName();
- method public int getVersionCode();
- method public boolean isPreInstalled();
- }
-
- public static final class AppInstallMetadata.Builder {
- ctor public AppInstallMetadata.Builder();
- method @NonNull public android.content.integrity.AppInstallMetadata build();
- method @NonNull public android.content.integrity.AppInstallMetadata.Builder setAppCertificate(@NonNull String);
- method @NonNull public android.content.integrity.AppInstallMetadata.Builder setInstallerCertificate(@NonNull String);
- method @NonNull public android.content.integrity.AppInstallMetadata.Builder setInstallerName(@NonNull String);
- method @NonNull public android.content.integrity.AppInstallMetadata.Builder setIsPreInstalled(boolean);
- method @NonNull public android.content.integrity.AppInstallMetadata.Builder setPackageName(@NonNull String);
- method @NonNull public android.content.integrity.AppInstallMetadata.Builder setVersionCode(int);
- }
-
public class AppIntegrityManager {
method @NonNull public String getCurrentRuleSetProvider();
method @NonNull public String getCurrentRuleSetVersion();
@@ -1920,84 +1909,28 @@ package android.content.integrity {
field public static final int STATUS_SUCCESS = 0; // 0x0
}
- public abstract class AtomicFormula implements android.content.integrity.Formula {
- ctor public AtomicFormula(int);
- method public int getKey();
- field public static final int APP_CERTIFICATE = 1; // 0x1
- field public static final int EQ = 0; // 0x0
- field public static final int GE = 4; // 0x4
- field public static final int GT = 3; // 0x3
- field public static final int INSTALLER_CERTIFICATE = 3; // 0x3
- field public static final int INSTALLER_NAME = 2; // 0x2
- field public static final int LE = 2; // 0x2
- field public static final int LT = 1; // 0x1
- field public static final int PACKAGE_NAME = 0; // 0x0
- field public static final int PRE_INSTALLED = 5; // 0x5
- field public static final int VERSION_CODE = 4; // 0x4
- }
-
- public static final class AtomicFormula.BooleanAtomicFormula extends android.content.integrity.AtomicFormula implements android.os.Parcelable {
- ctor public AtomicFormula.BooleanAtomicFormula(int, boolean);
- method public int describeContents();
- method public int getTag();
- method public boolean getValue();
- method public boolean isSatisfied(@NonNull android.content.integrity.AppInstallMetadata);
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.content.integrity.AtomicFormula.BooleanAtomicFormula> CREATOR;
- }
-
- public static final class AtomicFormula.IntAtomicFormula extends android.content.integrity.AtomicFormula implements android.os.Parcelable {
- ctor public AtomicFormula.IntAtomicFormula(int, int, int);
- method public int describeContents();
- method public int getOperator();
- method public int getTag();
- method public int getValue();
- method public boolean isSatisfied(@NonNull android.content.integrity.AppInstallMetadata);
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.content.integrity.AtomicFormula.IntAtomicFormula> CREATOR;
- }
-
- public static final class AtomicFormula.StringAtomicFormula extends android.content.integrity.AtomicFormula implements android.os.Parcelable {
- ctor public AtomicFormula.StringAtomicFormula(int, @NonNull String, boolean);
- method public int describeContents();
- method public boolean getIsHashedValue();
- method public int getTag();
- method @NonNull public String getValue();
- method public boolean isSatisfied(@NonNull android.content.integrity.AppInstallMetadata);
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.content.integrity.AtomicFormula.StringAtomicFormula> CREATOR;
- }
-
- public final class CompoundFormula implements android.content.integrity.Formula android.os.Parcelable {
- ctor public CompoundFormula(int, @NonNull java.util.List<android.content.integrity.Formula>);
- method public int describeContents();
- method public int getConnector();
- method @NonNull public java.util.List<android.content.integrity.Formula> getFormulas();
- method public int getTag();
- method public boolean isSatisfied(@NonNull android.content.integrity.AppInstallMetadata);
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field public static final int AND = 0; // 0x0
- field @NonNull public static final android.os.Parcelable.Creator<android.content.integrity.CompoundFormula> CREATOR;
- field public static final int NOT = 2; // 0x2
- field public static final int OR = 1; // 0x1
- }
-
- public interface Formula {
- method public int getTag();
- method public boolean isSatisfied(@NonNull android.content.integrity.AppInstallMetadata);
- method @NonNull public static android.content.integrity.Formula readFromParcel(@NonNull android.os.Parcel);
- method public static void writeToParcel(@NonNull android.content.integrity.Formula, @NonNull android.os.Parcel, int);
- field public static final int BOOLEAN_ATOMIC_FORMULA_TAG = 3; // 0x3
- field public static final int COMPOUND_FORMULA_TAG = 0; // 0x0
- field public static final int INT_ATOMIC_FORMULA_TAG = 2; // 0x2
- field public static final int STRING_ATOMIC_FORMULA_TAG = 1; // 0x1
+ public abstract class IntegrityFormula {
+ method @NonNull public static android.content.integrity.IntegrityFormula all(@NonNull android.content.integrity.IntegrityFormula...);
+ method @NonNull public static android.content.integrity.IntegrityFormula any(@NonNull android.content.integrity.IntegrityFormula...);
+ method @NonNull public android.content.integrity.IntegrityFormula equalTo(@NonNull String);
+ method @NonNull public android.content.integrity.IntegrityFormula equalTo(boolean);
+ method @NonNull public android.content.integrity.IntegrityFormula equalTo(long);
+ method @NonNull public android.content.integrity.IntegrityFormula greaterThan(long);
+ method @NonNull public android.content.integrity.IntegrityFormula greaterThanOrEquals(long);
+ method @NonNull public static android.content.integrity.IntegrityFormula not(@NonNull android.content.integrity.IntegrityFormula);
+ field @NonNull public static final android.content.integrity.IntegrityFormula APP_CERTIFICATE;
+ field @NonNull public static final android.content.integrity.IntegrityFormula INSTALLER_CERTIFICATE;
+ field @NonNull public static final android.content.integrity.IntegrityFormula INSTALLER_NAME;
+ field @NonNull public static final android.content.integrity.IntegrityFormula PACKAGE_NAME;
+ field @NonNull public static final android.content.integrity.IntegrityFormula PRE_INSTALLED;
+ field @NonNull public static final android.content.integrity.IntegrityFormula VERSION_CODE;
}
public final class Rule implements android.os.Parcelable {
- ctor public Rule(@NonNull android.content.integrity.Formula, int);
+ ctor public Rule(@NonNull android.content.integrity.IntegrityFormula, int);
method public int describeContents();
method public int getEffect();
- method @NonNull public android.content.integrity.Formula getFormula();
+ method @NonNull public android.content.integrity.IntegrityFormula getFormula();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.content.integrity.Rule> CREATOR;
field public static final int DENY = 0; // 0x0
@@ -2035,8 +1968,8 @@ package android.content.om {
public class OverlayManager {
method @Nullable public android.content.om.OverlayInfo getOverlayInfo(@NonNull String, @NonNull android.os.UserHandle);
method @NonNull @RequiresPermission(anyOf={"android.permission.INTERACT_ACROSS_USERS", "android.permission.INTERACT_ACROSS_USERS_FULL"}) public java.util.List<android.content.om.OverlayInfo> getOverlayInfosForTarget(@NonNull String, @NonNull android.os.UserHandle);
- method @RequiresPermission(anyOf={"android.permission.INTERACT_ACROSS_USERS", "android.permission.INTERACT_ACROSS_USERS_FULL"}) public void setEnabled(@NonNull String, boolean, @NonNull android.os.UserHandle);
- method @RequiresPermission(anyOf={"android.permission.INTERACT_ACROSS_USERS", "android.permission.INTERACT_ACROSS_USERS_FULL"}) public void setEnabledExclusiveInCategory(@NonNull String, @NonNull android.os.UserHandle);
+ method @RequiresPermission(anyOf={"android.permission.INTERACT_ACROSS_USERS", "android.permission.INTERACT_ACROSS_USERS_FULL"}) public void setEnabled(@NonNull String, boolean, @NonNull android.os.UserHandle) throws java.lang.IllegalStateException, java.lang.SecurityException;
+ method @RequiresPermission(anyOf={"android.permission.INTERACT_ACROSS_USERS", "android.permission.INTERACT_ACROSS_USERS_FULL"}) public void setEnabledExclusiveInCategory(@NonNull String, @NonNull android.os.UserHandle) throws java.lang.IllegalStateException, java.lang.SecurityException;
}
}
@@ -2252,6 +2185,7 @@ package android.content.pm {
method @Deprecated @Nullable @RequiresPermission(android.Manifest.permission.SUSPEND_APPS) public String[] setPackagesSuspended(@Nullable String[], boolean, @Nullable android.os.PersistableBundle, @Nullable android.os.PersistableBundle, @Nullable String);
method @Nullable @RequiresPermission(android.Manifest.permission.SUSPEND_APPS) public String[] setPackagesSuspended(@Nullable String[], boolean, @Nullable android.os.PersistableBundle, @Nullable android.os.PersistableBundle, @Nullable android.content.pm.SuspendDialogInfo);
method @RequiresPermission(value=android.Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE, conditional=true) public void setSyntheticAppDetailsActivityEnabled(@NonNull String, boolean);
+ method public void setSystemAppState(@NonNull String, int);
method @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public abstract void setUpdateAvailable(@NonNull String, boolean);
method @RequiresPermission(android.Manifest.permission.SET_PREFERRED_APPLICATIONS) public abstract boolean updateIntentVerificationStatusAsUser(@NonNull String, int, int);
method @RequiresPermission(anyOf={android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS, android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS}) public abstract void updatePermissionFlags(@NonNull String, @NonNull String, @android.content.pm.PackageManager.PermissionFlags int, @android.content.pm.PackageManager.PermissionFlags int, @NonNull android.os.UserHandle);
@@ -2262,6 +2196,7 @@ package android.content.pm {
field public static final String FEATURE_BROADCAST_RADIO = "android.hardware.broadcastradio";
field public static final String FEATURE_REBOOT_ESCROW = "android.hardware.reboot_escrow";
field public static final String FEATURE_TELEPHONY_CARRIERLOCK = "android.hardware.telephony.carrierlock";
+ field public static final int FLAGS_PERMISSION_RESERVED_PERMISSIONCONTROLLER = -268435456; // 0xf0000000
field public static final int FLAG_PERMISSION_APPLY_RESTRICTION = 16384; // 0x4000
field public static final int FLAG_PERMISSION_GRANTED_BY_DEFAULT = 32; // 0x20
field public static final int FLAG_PERMISSION_GRANTED_BY_ROLE = 32768; // 0x8000
@@ -2325,11 +2260,16 @@ package android.content.pm {
field @Deprecated public static final int MASK_PERMISSION_FLAGS = 255; // 0xff
field public static final int MATCH_ANY_USER = 4194304; // 0x400000
field public static final int MATCH_FACTORY_ONLY = 2097152; // 0x200000
+ field public static final int MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS = 536870912; // 0x20000000
field public static final int MATCH_INSTANT = 8388608; // 0x800000
field public static final int MODULE_APEX_NAME = 1; // 0x1
field public static final int RESTRICTION_HIDE_FROM_SUGGESTIONS = 1; // 0x1
field public static final int RESTRICTION_HIDE_NOTIFICATIONS = 2; // 0x2
field public static final int RESTRICTION_NONE = 0; // 0x0
+ field public static final int SYSTEM_APP_STATE_HIDDEN_UNTIL_INSTALLED_HIDDEN = 0; // 0x0
+ field public static final int SYSTEM_APP_STATE_HIDDEN_UNTIL_INSTALLED_VISIBLE = 1; // 0x1
+ field public static final int SYSTEM_APP_STATE_INSTALLED = 2; // 0x2
+ field public static final int SYSTEM_APP_STATE_UNINSTALLED = 3; // 0x3
}
public abstract static class PackageManager.DexModuleRegisterCallback {
@@ -2390,6 +2330,8 @@ package android.content.pm {
public final class SuspendDialogInfo implements android.os.Parcelable {
method public int describeContents();
method public void writeToParcel(android.os.Parcel, int);
+ field public static final int BUTTON_ACTION_MORE_DETAILS = 0; // 0x0
+ field public static final int BUTTON_ACTION_UNSUSPEND = 1; // 0x1
field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.SuspendDialogInfo> CREATOR;
}
@@ -2399,6 +2341,7 @@ package android.content.pm {
method @NonNull public android.content.pm.SuspendDialogInfo.Builder setIcon(@DrawableRes int);
method @NonNull public android.content.pm.SuspendDialogInfo.Builder setMessage(@NonNull String);
method @NonNull public android.content.pm.SuspendDialogInfo.Builder setMessage(@StringRes int);
+ method @NonNull public android.content.pm.SuspendDialogInfo.Builder setNeutralButtonAction(int);
method @NonNull public android.content.pm.SuspendDialogInfo.Builder setNeutralButtonText(@StringRes int);
method @NonNull public android.content.pm.SuspendDialogInfo.Builder setTitle(@StringRes int);
}
@@ -2995,7 +2938,7 @@ package android.hardware.location {
public class ContextHubClient implements java.io.Closeable {
method public void close();
method @NonNull public android.hardware.location.ContextHubInfo getAttachedHub();
- method @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public int sendMessageToNanoApp(@NonNull android.hardware.location.NanoAppMessage);
+ method @RequiresPermission(anyOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.ACCESS_CONTEXT_HUB}) public int sendMessageToNanoApp(@NonNull android.hardware.location.NanoAppMessage);
}
public class ContextHubClientCallback {
@@ -3044,24 +2987,24 @@ package android.hardware.location {
}
public final class ContextHubManager {
- method @NonNull @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public android.hardware.location.ContextHubClient createClient(@NonNull android.hardware.location.ContextHubInfo, @NonNull android.hardware.location.ContextHubClientCallback, @NonNull java.util.concurrent.Executor);
- method @NonNull @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public android.hardware.location.ContextHubClient createClient(@NonNull android.hardware.location.ContextHubInfo, @NonNull android.hardware.location.ContextHubClientCallback);
- method @NonNull @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public android.hardware.location.ContextHubClient createClient(@NonNull android.hardware.location.ContextHubInfo, @NonNull android.app.PendingIntent, long);
- method @NonNull @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public android.hardware.location.ContextHubTransaction<java.lang.Void> disableNanoApp(@NonNull android.hardware.location.ContextHubInfo, long);
- method @NonNull @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public android.hardware.location.ContextHubTransaction<java.lang.Void> enableNanoApp(@NonNull android.hardware.location.ContextHubInfo, long);
- method @Deprecated @NonNull @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public int[] findNanoAppOnHub(int, @NonNull android.hardware.location.NanoAppFilter);
- method @Deprecated @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public int[] getContextHubHandles();
- method @Deprecated @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public android.hardware.location.ContextHubInfo getContextHubInfo(int);
- method @NonNull @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public java.util.List<android.hardware.location.ContextHubInfo> getContextHubs();
- method @Deprecated @Nullable @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public android.hardware.location.NanoAppInstanceInfo getNanoAppInstanceInfo(int);
- method @Deprecated @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public int loadNanoApp(int, @NonNull android.hardware.location.NanoApp);
- method @NonNull @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public android.hardware.location.ContextHubTransaction<java.lang.Void> loadNanoApp(@NonNull android.hardware.location.ContextHubInfo, @NonNull android.hardware.location.NanoAppBinary);
- method @NonNull @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public android.hardware.location.ContextHubTransaction<java.util.List<android.hardware.location.NanoAppState>> queryNanoApps(@NonNull android.hardware.location.ContextHubInfo);
+ method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.ACCESS_CONTEXT_HUB}) public android.hardware.location.ContextHubClient createClient(@NonNull android.hardware.location.ContextHubInfo, @NonNull android.hardware.location.ContextHubClientCallback, @NonNull java.util.concurrent.Executor);
+ method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.ACCESS_CONTEXT_HUB}) public android.hardware.location.ContextHubClient createClient(@NonNull android.hardware.location.ContextHubInfo, @NonNull android.hardware.location.ContextHubClientCallback);
+ method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.ACCESS_CONTEXT_HUB}) public android.hardware.location.ContextHubClient createClient(@NonNull android.hardware.location.ContextHubInfo, @NonNull android.app.PendingIntent, long);
+ method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.ACCESS_CONTEXT_HUB}) public android.hardware.location.ContextHubTransaction<java.lang.Void> disableNanoApp(@NonNull android.hardware.location.ContextHubInfo, long);
+ method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.ACCESS_CONTEXT_HUB}) public android.hardware.location.ContextHubTransaction<java.lang.Void> enableNanoApp(@NonNull android.hardware.location.ContextHubInfo, long);
+ method @Deprecated @NonNull @RequiresPermission(anyOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.ACCESS_CONTEXT_HUB}) public int[] findNanoAppOnHub(int, @NonNull android.hardware.location.NanoAppFilter);
+ method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.ACCESS_CONTEXT_HUB}) public int[] getContextHubHandles();
+ method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.ACCESS_CONTEXT_HUB}) public android.hardware.location.ContextHubInfo getContextHubInfo(int);
+ method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.ACCESS_CONTEXT_HUB}) public java.util.List<android.hardware.location.ContextHubInfo> getContextHubs();
+ method @Deprecated @Nullable @RequiresPermission(anyOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.ACCESS_CONTEXT_HUB}) public android.hardware.location.NanoAppInstanceInfo getNanoAppInstanceInfo(int);
+ method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.ACCESS_CONTEXT_HUB}) public int loadNanoApp(int, @NonNull android.hardware.location.NanoApp);
+ method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.ACCESS_CONTEXT_HUB}) public android.hardware.location.ContextHubTransaction<java.lang.Void> loadNanoApp(@NonNull android.hardware.location.ContextHubInfo, @NonNull android.hardware.location.NanoAppBinary);
+ method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.ACCESS_CONTEXT_HUB}) public android.hardware.location.ContextHubTransaction<java.util.List<android.hardware.location.NanoAppState>> queryNanoApps(@NonNull android.hardware.location.ContextHubInfo);
method @Deprecated public int registerCallback(@NonNull android.hardware.location.ContextHubManager.Callback);
method @Deprecated public int registerCallback(android.hardware.location.ContextHubManager.Callback, android.os.Handler);
- method @Deprecated @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public int sendMessage(int, int, @NonNull android.hardware.location.ContextHubMessage);
- method @Deprecated @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public int unloadNanoApp(int);
- method @NonNull @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public android.hardware.location.ContextHubTransaction<java.lang.Void> unloadNanoApp(@NonNull android.hardware.location.ContextHubInfo, long);
+ method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.ACCESS_CONTEXT_HUB}) public int sendMessage(int, int, @NonNull android.hardware.location.ContextHubMessage);
+ method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.ACCESS_CONTEXT_HUB}) public int unloadNanoApp(int);
+ method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.ACCESS_CONTEXT_HUB}) public android.hardware.location.ContextHubTransaction<java.lang.Void> unloadNanoApp(@NonNull android.hardware.location.ContextHubInfo, long);
method @Deprecated public int unregisterCallback(@NonNull android.hardware.location.ContextHubManager.Callback);
field public static final int EVENT_HUB_RESET = 6; // 0x6
field public static final int EVENT_NANOAPP_ABORTED = 4; // 0x4
@@ -3794,10 +3737,12 @@ package android.hardware.usb {
method @RequiresPermission(android.Manifest.permission.MANAGE_USB) public void setCurrentFunctions(long);
field @RequiresPermission(android.Manifest.permission.MANAGE_USB) public static final String ACTION_USB_PORT_CHANGED = "android.hardware.usb.action.USB_PORT_CHANGED";
field public static final String ACTION_USB_STATE = "android.hardware.usb.action.USB_STATE";
+ field public static final long FUNCTION_NCM = 1024L; // 0x400L
field public static final long FUNCTION_NONE = 0L; // 0x0L
field public static final long FUNCTION_RNDIS = 32L; // 0x20L
field public static final String USB_CONFIGURED = "configured";
field public static final String USB_CONNECTED = "connected";
+ field public static final String USB_FUNCTION_NCM = "ncm";
field public static final String USB_FUNCTION_RNDIS = "rndis";
}
@@ -4611,8 +4556,8 @@ package android.media.tv {
public final class DvbDeviceInfo implements android.os.Parcelable {
ctor public DvbDeviceInfo(int, int);
method public int describeContents();
- method public int getAdapterId();
- method public int getDeviceId();
+ method @IntRange(from=0) public int getAdapterId();
+ method @IntRange(from=0) public int getDeviceId();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.media.tv.DvbDeviceInfo> CREATOR;
}
@@ -6093,8 +6038,8 @@ package android.net {
method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_AIRPLANE_MODE, android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void setAirplaneMode(boolean);
method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK}) public boolean shouldAvoidBadWifi();
method @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public void startCaptivePortalApp(@NonNull android.net.Network, @NonNull android.os.Bundle);
- method @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void startTethering(int, boolean, android.net.ConnectivityManager.OnStartTetheringCallback);
- method @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void startTethering(int, boolean, android.net.ConnectivityManager.OnStartTetheringCallback, android.os.Handler);
+ method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void startTethering(int, boolean, android.net.ConnectivityManager.OnStartTetheringCallback);
+ method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void startTethering(int, boolean, android.net.ConnectivityManager.OnStartTetheringCallback, android.os.Handler);
method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void stopTethering(int);
method @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public void unregisterNetworkProvider(@NonNull android.net.NetworkProvider);
method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void unregisterTetheringEventCallback(@NonNull android.net.ConnectivityManager.OnTetheringEventCallback);
@@ -6110,10 +6055,10 @@ package android.net {
field @Deprecated public static final int TYPE_WIFI_P2P = 13; // 0xd
}
- public abstract static class ConnectivityManager.OnStartTetheringCallback {
- ctor public ConnectivityManager.OnStartTetheringCallback();
- method public void onTetheringFailed();
- method public void onTetheringStarted();
+ @Deprecated public abstract static class ConnectivityManager.OnStartTetheringCallback {
+ ctor @Deprecated public ConnectivityManager.OnStartTetheringCallback();
+ method @Deprecated public void onTetheringFailed();
+ method @Deprecated public void onTetheringStarted();
}
@Deprecated public static interface ConnectivityManager.OnTetheringEntitlementResultListener {
@@ -6194,13 +6139,18 @@ package android.net {
public class LinkAddress implements android.os.Parcelable {
ctor public LinkAddress(@NonNull java.net.InetAddress, @IntRange(from=0, to=128) int, int, int);
+ ctor public LinkAddress(@NonNull java.net.InetAddress, @IntRange(from=0, to=128) int, int, int, long, long);
ctor public LinkAddress(@NonNull java.net.InetAddress, @IntRange(from=0, to=128) int);
ctor public LinkAddress(@NonNull String);
ctor public LinkAddress(@NonNull String, int, int);
+ method public long getDeprecationTime();
+ method public long getExpirationTime();
method public boolean isGlobalPreferred();
method public boolean isIpv4();
method public boolean isIpv6();
method public boolean isSameAddressAs(@Nullable android.net.LinkAddress);
+ field public static final long LIFETIME_PERMANENT = 9223372036854775807L; // 0x7fffffffffffffffL
+ field public static final long LIFETIME_UNKNOWN = -1L; // 0xffffffffffffffffL
}
public final class LinkProperties implements android.os.Parcelable {
@@ -6317,9 +6267,11 @@ package android.net {
public final class NetworkCapabilities implements android.os.Parcelable {
method public boolean deduceRestrictedCapability();
+ method @NonNull public java.util.List<java.lang.Integer> getAdministratorUids();
method @Nullable public String getSSID();
method @NonNull public int[] getTransportTypes();
method public boolean satisfiedByNetworkCapabilities(@Nullable android.net.NetworkCapabilities);
+ method public void setAdministratorUids(@NonNull java.util.List<java.lang.Integer>);
method @NonNull public android.net.NetworkCapabilities setSSID(@Nullable String);
method @NonNull public android.net.NetworkCapabilities setTransportInfo(@NonNull android.net.TransportInfo);
field public static final int NET_CAPABILITY_OEM_PAID = 22; // 0x16
@@ -6515,12 +6467,32 @@ package android.net {
method public boolean satisfiedBy(android.net.NetworkSpecifier);
}
+ public final class TetheredClient implements android.os.Parcelable {
+ ctor public TetheredClient(@NonNull android.net.MacAddress, @NonNull java.util.Collection<android.net.TetheredClient.AddressInfo>, int);
+ method public int describeContents();
+ method @NonNull public java.util.List<android.net.TetheredClient.AddressInfo> getAddresses();
+ method @NonNull public android.net.MacAddress getMacAddress();
+ method public int getTetheringType();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.TetheredClient> CREATOR;
+ }
+
+ public static final class TetheredClient.AddressInfo implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public android.net.LinkAddress getAddress();
+ method @Nullable public String getHostname();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.TetheredClient.AddressInfo> CREATOR;
+ }
+
public class TetheringManager {
- method public void registerTetheringEventCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.TetheringEventCallback);
- method @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void requestLatestTetheringEntitlementResult(int, boolean, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.OnTetheringEntitlementResultListener);
- method public void stopAllTethering();
- method public void stopTethering(int);
- method public void unregisterTetheringEventCallback(@NonNull android.net.TetheringManager.TetheringEventCallback);
+ method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void registerTetheringEventCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.TetheringEventCallback);
+ method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void requestLatestTetheringEntitlementResult(int, boolean, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.OnTetheringEntitlementResultListener);
+ method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void startTethering(@NonNull android.net.TetheringManager.TetheringRequest, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.StartTetheringCallback);
+ method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void startTethering(int, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.StartTetheringCallback);
+ method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void stopAllTethering();
+ method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void stopTethering(int);
+ method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.ACCESS_NETWORK_STATE}) public void unregisterTetheringEventCallback(@NonNull android.net.TetheringManager.TetheringEventCallback);
field public static final String ACTION_TETHER_STATE_CHANGED = "android.net.conn.TETHER_STATE_CHANGED";
field public static final String EXTRA_ACTIVE_LOCAL_ONLY = "android.net.extra.ACTIVE_LOCAL_ONLY";
field public static final String EXTRA_ACTIVE_TETHER = "tetherArray";
@@ -6528,6 +6500,7 @@ package android.net {
field public static final String EXTRA_ERRORED_TETHER = "erroredArray";
field public static final int TETHERING_BLUETOOTH = 2; // 0x2
field public static final int TETHERING_INVALID = -1; // 0xffffffff
+ field public static final int TETHERING_NCM = 4; // 0x4
field public static final int TETHERING_USB = 1; // 0x1
field public static final int TETHERING_WIFI = 0; // 0x0
field public static final int TETHERING_WIFI_P2P = 3; // 0x3
@@ -6553,8 +6526,15 @@ package android.net {
method public void onTetheringEntitlementResult(int);
}
+ public abstract static class TetheringManager.StartTetheringCallback {
+ ctor public TetheringManager.StartTetheringCallback();
+ method public void onTetheringFailed(int);
+ method public void onTetheringStarted();
+ }
+
public abstract static class TetheringManager.TetheringEventCallback {
ctor public TetheringManager.TetheringEventCallback();
+ method public void onClientsChanged(@NonNull java.util.Collection<android.net.TetheredClient>);
method public void onError(@NonNull String, int);
method @Deprecated public void onTetherableInterfaceRegexpsChanged(@NonNull android.net.TetheringManager.TetheringInterfaceRegexps);
method public void onTetherableInterfacesChanged(@NonNull java.util.List<java.lang.String>);
@@ -6570,6 +6550,17 @@ package android.net {
method @Deprecated @NonNull public java.util.List<java.lang.String> getTetherableWifiRegexs();
}
+ public static class TetheringManager.TetheringRequest {
+ }
+
+ public static class TetheringManager.TetheringRequest.Builder {
+ ctor public TetheringManager.TetheringRequest.Builder(int);
+ method @NonNull public android.net.TetheringManager.TetheringRequest build();
+ method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder setExemptFromEntitlementCheck(boolean);
+ method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder setSilentProvisioning(boolean);
+ method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder useStaticIpv4Addresses(@NonNull android.net.LinkAddress);
+ }
+
public class TrafficStats {
method public static void setThreadStatsTagApp();
method public static void setThreadStatsTagBackup();
@@ -6759,7 +6750,7 @@ package android.net.ipsec.ike {
}
public final class IkeSessionConfiguration {
- ctor public IkeSessionConfiguration();
+ method @NonNull public java.util.List<java.net.InetAddress> getPcscfServers();
method @NonNull public String getRemoteApplicationVersion();
method @NonNull public java.util.List<byte[]> getRemoteVendorIDs();
method public boolean isIkeExtensionEnabled(int);
@@ -6768,6 +6759,7 @@ package android.net.ipsec.ike {
}
public final class IkeSessionParams {
+ method @NonNull public java.util.List<android.net.ipsec.ike.IkeSessionParams.IkeConfigRequest> getConfigurationRequests();
method public long getHardLifetime();
method @NonNull public android.net.ipsec.ike.IkeSessionParams.IkeAuthConfig getLocalAuthConfig();
method @NonNull public android.net.ipsec.ike.IkeIdentification getLocalIdentification();
@@ -6781,6 +6773,8 @@ package android.net.ipsec.ike {
public static final class IkeSessionParams.Builder {
ctor public IkeSessionParams.Builder();
+ method @NonNull public android.net.ipsec.ike.IkeSessionParams.Builder addPcscfServerRequest(@NonNull java.net.InetAddress);
+ method @NonNull public android.net.ipsec.ike.IkeSessionParams.Builder addPcscfServerRequest(int);
method @NonNull public android.net.ipsec.ike.IkeSessionParams.Builder addSaProposal(@NonNull android.net.ipsec.ike.IkeSaProposal);
method @NonNull public android.net.ipsec.ike.IkeSessionParams build();
method @NonNull public android.net.ipsec.ike.IkeSessionParams.Builder setAuthDigitalSignature(@Nullable java.security.cert.X509Certificate, @NonNull java.security.cert.X509Certificate, @NonNull java.security.PrivateKey);
@@ -6794,6 +6788,14 @@ package android.net.ipsec.ike {
method @NonNull public android.net.ipsec.ike.IkeSessionParams.Builder setUdpEncapsulationSocket(@NonNull android.net.IpSecManager.UdpEncapsulationSocket);
}
+ public static interface IkeSessionParams.ConfigRequestIpv4PcscfServer extends android.net.ipsec.ike.IkeSessionParams.IkeConfigRequest {
+ method @Nullable public java.net.Inet4Address getAddress();
+ }
+
+ public static interface IkeSessionParams.ConfigRequestIpv6PcscfServer extends android.net.ipsec.ike.IkeSessionParams.IkeConfigRequest {
+ method @Nullable public java.net.Inet6Address getAddress();
+ }
+
public abstract static class IkeSessionParams.IkeAuthConfig {
}
@@ -6815,6 +6817,9 @@ package android.net.ipsec.ike {
method @NonNull public byte[] getPsk();
}
+ public static interface IkeSessionParams.IkeConfigRequest {
+ }
+
public final class IkeTrafficSelector {
ctor public IkeTrafficSelector(int, int, @NonNull java.net.InetAddress, @NonNull java.net.InetAddress);
field public final int endPort;
@@ -6862,7 +6867,7 @@ package android.net.ipsec.ike {
}
public final class TunnelModeChildSessionParams extends android.net.ipsec.ike.ChildSessionParams {
- method @NonNull public java.util.List<android.net.ipsec.ike.TunnelModeChildSessionParams.ConfigRequest> getConfigurationRequests();
+ method @NonNull public java.util.List<android.net.ipsec.ike.TunnelModeChildSessionParams.TunnelModeChildConfigRequest> getConfigurationRequests();
}
public static final class TunnelModeChildSessionParams.Builder {
@@ -6879,33 +6884,33 @@ package android.net.ipsec.ike {
method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionParams.Builder setLifetime(long, long);
}
- public static interface TunnelModeChildSessionParams.ConfigRequest {
- }
-
- public static interface TunnelModeChildSessionParams.ConfigRequestIpv4Address extends android.net.ipsec.ike.TunnelModeChildSessionParams.ConfigRequest {
+ public static interface TunnelModeChildSessionParams.ConfigRequestIpv4Address extends android.net.ipsec.ike.TunnelModeChildSessionParams.TunnelModeChildConfigRequest {
method @Nullable public java.net.Inet4Address getAddress();
}
- public static interface TunnelModeChildSessionParams.ConfigRequestIpv4DhcpServer extends android.net.ipsec.ike.TunnelModeChildSessionParams.ConfigRequest {
+ public static interface TunnelModeChildSessionParams.ConfigRequestIpv4DhcpServer extends android.net.ipsec.ike.TunnelModeChildSessionParams.TunnelModeChildConfigRequest {
method @Nullable public java.net.Inet4Address getAddress();
}
- public static interface TunnelModeChildSessionParams.ConfigRequestIpv4DnsServer extends android.net.ipsec.ike.TunnelModeChildSessionParams.ConfigRequest {
+ public static interface TunnelModeChildSessionParams.ConfigRequestIpv4DnsServer extends android.net.ipsec.ike.TunnelModeChildSessionParams.TunnelModeChildConfigRequest {
method @Nullable public java.net.Inet4Address getAddress();
}
- public static interface TunnelModeChildSessionParams.ConfigRequestIpv4Netmask extends android.net.ipsec.ike.TunnelModeChildSessionParams.ConfigRequest {
+ public static interface TunnelModeChildSessionParams.ConfigRequestIpv4Netmask extends android.net.ipsec.ike.TunnelModeChildSessionParams.TunnelModeChildConfigRequest {
}
- public static interface TunnelModeChildSessionParams.ConfigRequestIpv6Address extends android.net.ipsec.ike.TunnelModeChildSessionParams.ConfigRequest {
+ public static interface TunnelModeChildSessionParams.ConfigRequestIpv6Address extends android.net.ipsec.ike.TunnelModeChildSessionParams.TunnelModeChildConfigRequest {
method @Nullable public java.net.Inet6Address getAddress();
method public int getPrefixLength();
}
- public static interface TunnelModeChildSessionParams.ConfigRequestIpv6DnsServer extends android.net.ipsec.ike.TunnelModeChildSessionParams.ConfigRequest {
+ public static interface TunnelModeChildSessionParams.ConfigRequestIpv6DnsServer extends android.net.ipsec.ike.TunnelModeChildSessionParams.TunnelModeChildConfigRequest {
method @Nullable public java.net.Inet6Address getAddress();
}
+ public static interface TunnelModeChildSessionParams.TunnelModeChildConfigRequest {
+ }
+
}
package android.net.ipsec.ike.exceptions {
@@ -7601,11 +7606,8 @@ package android.net.wifi {
method public boolean isPortableHotspotSupported();
method @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public boolean isWifiApEnabled();
method public boolean isWifiScannerSupported();
- method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void registerNetworkRequestMatchCallback(@NonNull android.net.wifi.WifiManager.NetworkRequestMatchCallback);
method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void registerNetworkRequestMatchCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.NetworkRequestMatchCallback);
- method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void registerSoftApCallback(@NonNull android.net.wifi.WifiManager.SoftApCallback);
method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void registerSoftApCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.SoftApCallback);
- method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void registerTrafficStateCallback(@NonNull android.net.wifi.WifiManager.TrafficStateCallback);
method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void registerTrafficStateCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.TrafficStateCallback);
method @RequiresPermission(android.Manifest.permission.WIFI_UPDATE_USABILITY_STATS_SCORE) public void removeOnWifiUsabilityStatsListener(@NonNull android.net.wifi.WifiManager.OnWifiUsabilityStatsListener);
method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void restoreBackupData(@NonNull byte[]);
@@ -7616,6 +7618,7 @@ package android.net.wifi {
method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void save(@NonNull android.net.wifi.WifiConfiguration, @Nullable android.net.wifi.WifiManager.ActionListener);
method @RequiresPermission(android.Manifest.permission.WIFI_SET_DEVICE_MOBILITY_STATE) public void setDeviceMobilityState(int);
method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void setMacRandomizationSettingPasspointEnabled(@NonNull String, boolean);
+ method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void setMeteredOverridePasspoint(@NonNull String, int);
method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public boolean setSoftApConfiguration(@NonNull android.net.wifi.SoftApConfiguration);
method @Deprecated @RequiresPermission(android.Manifest.permission.CHANGE_WIFI_STATE) public boolean setWifiApConfiguration(android.net.wifi.WifiConfiguration);
method @RequiresPermission(android.Manifest.permission.WIFI_UPDATE_USABILITY_STATS_SCORE) public boolean setWifiConnectedNetworkScorer(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.WifiConnectedNetworkScorer);
@@ -7981,6 +7984,7 @@ package android.net.wifi.hotspot2 {
}
public final class PasspointConfiguration implements android.os.Parcelable {
+ method public int getMeteredOverride();
method public boolean isAutoJoinEnabled();
method public boolean isMacRandomizationEnabled();
}
@@ -8344,19 +8348,19 @@ package android.os {
public class BatteryStatsManager {
method @NonNull @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public android.os.connectivity.CellularBatteryStats getCellularBatteryStats();
method @NonNull @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public android.os.connectivity.WifiBatteryStats getWifiBatteryStats();
- method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void noteFullWifiLockAcquiredFromSource(@NonNull android.os.WorkSource);
- method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void noteFullWifiLockReleasedFromSource(@NonNull android.os.WorkSource);
- method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void noteWifiBatchedScanStartedFromSource(@NonNull android.os.WorkSource, @IntRange(from=0) int);
- method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void noteWifiBatchedScanStoppedFromSource(@NonNull android.os.WorkSource);
- method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void noteWifiMulticastDisabled(int);
- method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void noteWifiMulticastEnabled(int);
- method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void noteWifiOff();
- method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void noteWifiOn();
- method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void noteWifiRssiChanged(@IntRange(from=0xffffff81, to=0) int);
- method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void noteWifiScanStartedFromSource(@NonNull android.os.WorkSource);
- method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void noteWifiScanStoppedFromSource(@NonNull android.os.WorkSource);
- method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void noteWifiState(int, @Nullable String);
- method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void noteWifiSupplicantStateChanged(int, boolean);
+ method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void reportFullWifiLockAcquiredFromSource(@NonNull android.os.WorkSource);
+ method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void reportFullWifiLockReleasedFromSource(@NonNull android.os.WorkSource);
+ method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void reportWifiBatchedScanStartedFromSource(@NonNull android.os.WorkSource, @IntRange(from=0) int);
+ method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void reportWifiBatchedScanStoppedFromSource(@NonNull android.os.WorkSource);
+ method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void reportWifiMulticastDisabled(int);
+ method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void reportWifiMulticastEnabled(int);
+ method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void reportWifiOff();
+ method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void reportWifiOn();
+ method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void reportWifiRssiChanged(@IntRange(from=0xffffff81, to=0) int);
+ method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void reportWifiScanStartedFromSource(@NonNull android.os.WorkSource);
+ method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void reportWifiScanStoppedFromSource(@NonNull android.os.WorkSource);
+ method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void reportWifiState(int, @Nullable String);
+ method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void reportWifiSupplicantStateChanged(int, boolean);
field public static final int WIFI_STATE_OFF = 0; // 0x0
field public static final int WIFI_STATE_OFF_SCANNING = 1; // 0x1
field public static final int WIFI_STATE_ON_CONNECTED_P2P = 5; // 0x5
@@ -8766,6 +8770,26 @@ package android.os {
field public static final int TUPLE_VALUE_TYPE = 7; // 0x7
}
+ public class StatsFrameworkInitializer {
+ method public static void registerServiceWrappers();
+ method public static void setStatsServiceManager(@NonNull android.os.StatsServiceManager);
+ }
+
+ public class StatsServiceManager {
+ method @NonNull public android.os.StatsServiceManager.ServiceRegisterer getStatsCompanionServiceRegisterer();
+ method @NonNull public android.os.StatsServiceManager.ServiceRegisterer getStatsManagerServiceRegisterer();
+ method @NonNull public android.os.StatsServiceManager.ServiceRegisterer getStatsdServiceRegisterer();
+ }
+
+ public static class StatsServiceManager.ServiceNotFoundException extends java.lang.Exception {
+ ctor public StatsServiceManager.ServiceNotFoundException(@NonNull String);
+ }
+
+ public static final class StatsServiceManager.ServiceRegisterer {
+ method @Nullable public android.os.IBinder get();
+ method @Nullable public android.os.IBinder getOrThrow() throws android.os.StatsServiceManager.ServiceNotFoundException;
+ }
+
public class SystemConfigManager {
method @NonNull @RequiresPermission(android.Manifest.permission.READ_CARRIER_APP_INFO) public java.util.Set<java.lang.String> getDisabledUntilUsedPreinstalledCarrierApps();
method @NonNull @RequiresPermission(android.Manifest.permission.READ_CARRIER_APP_INFO) public java.util.Map<java.lang.String,java.util.List<java.lang.String>> getDisabledUntilUsedPreinstalledCarrierAssociatedApps();
@@ -8800,16 +8824,12 @@ package android.os {
method @NonNull public android.os.TelephonyServiceManager.ServiceRegisterer getEuiccCardControllerServiceRegisterer();
method @NonNull public android.os.TelephonyServiceManager.ServiceRegisterer getEuiccControllerService();
method @NonNull public android.os.TelephonyServiceManager.ServiceRegisterer getIccPhoneBookServiceRegisterer();
- method @NonNull public android.os.TelephonyServiceManager.ServiceRegisterer getNetworkPolicyServiceRegisterer();
method @NonNull public android.os.TelephonyServiceManager.ServiceRegisterer getOpportunisticNetworkServiceRegisterer();
- method @NonNull public android.os.TelephonyServiceManager.ServiceRegisterer getPackageManagerServiceRegisterer();
- method @NonNull public android.os.TelephonyServiceManager.ServiceRegisterer getPermissionManagerServiceRegisterer();
method @NonNull public android.os.TelephonyServiceManager.ServiceRegisterer getPhoneSubServiceRegisterer();
method @NonNull public android.os.TelephonyServiceManager.ServiceRegisterer getSmsServiceRegisterer();
method @NonNull public android.os.TelephonyServiceManager.ServiceRegisterer getSubscriptionServiceRegisterer();
method @NonNull public android.os.TelephonyServiceManager.ServiceRegisterer getTelephonyImsServiceRegisterer();
method @NonNull public android.os.TelephonyServiceManager.ServiceRegisterer getTelephonyRcsMessageServiceRegisterer();
- method @NonNull public android.os.TelephonyServiceManager.ServiceRegisterer getTelephonyRegistryServiceRegisterer();
method @NonNull public android.os.TelephonyServiceManager.ServiceRegisterer getTelephonyServiceRegisterer();
}
@@ -9030,12 +9050,12 @@ package android.os.connectivity {
public final class WifiBatteryStats implements android.os.Parcelable {
method public int describeContents();
+ method public long getAppScanRequestCount();
method public long getEnergyConsumedMaMillis();
method public long getIdleTimeMillis();
method public long getKernelActiveTimeMillis();
method public long getLoggingDurationMillis();
method public long getMonitoredRailChargeConsumedMaMillis();
- method public long getNumAppScanRequest();
method public long getNumBytesRx();
method public long getNumBytesTx();
method public long getNumPacketsRx();
@@ -9145,6 +9165,7 @@ package android.permission {
public final class PermissionManager {
method @IntRange(from=0) @RequiresPermission(android.Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY) public int getRuntimePermissionsVersion();
method @NonNull public java.util.List<android.permission.PermissionManager.SplitPermissionInfo> getSplitPermissions();
+ method @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS_TO_TELEPHONY_DEFAULTS) public void grantDefaultPermissionsToEnabledCarrierApps(@NonNull String[], @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
method @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS_TO_TELEPHONY_DEFAULTS) public void grantDefaultPermissionsToEnabledImsServices(@NonNull String[], @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
method @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS_TO_TELEPHONY_DEFAULTS) public void grantDefaultPermissionsToEnabledTelephonyDataServices(@NonNull String[], @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
method @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS_TO_TELEPHONY_DEFAULTS) public void grantDefaultPermissionsToLuiApp(@NonNull String, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
@@ -9577,6 +9598,7 @@ package android.provider {
field public static final String AUTOFILL_USER_DATA_MAX_USER_DATA_SIZE = "autofill_user_data_max_user_data_size";
field public static final String AUTOFILL_USER_DATA_MAX_VALUE_LENGTH = "autofill_user_data_max_value_length";
field public static final String AUTOFILL_USER_DATA_MIN_VALUE_LENGTH = "autofill_user_data_min_value_length";
+ field public static final String CARRIER_APPS_HANDLED = "carrier_apps_handled";
field public static final String COMPLETED_CATEGORY_PREFIX = "suggested.completed_category.";
field public static final String DOZE_ALWAYS_ON = "doze_always_on";
field public static final String HUSH_GESTURE_USED = "hush_gesture_used";
@@ -9734,6 +9756,7 @@ package android.provider {
field public static final String HPLMNS = "hplmns";
field public static final String ICC_ID = "icc_id";
field public static final String IMSI = "imsi";
+ field public static final String IMS_RCS_UCE_ENABLED = "ims_rcs_uce_enabled";
field public static final String ISO_COUNTRY_CODE = "iso_country_code";
field public static final String IS_EMBEDDED = "is_embedded";
field public static final String IS_OPPORTUNISTIC = "is_opportunistic";
@@ -9791,6 +9814,14 @@ package android.provider {
}
+package android.se.omapi {
+
+ public final class Reader {
+ method @RequiresPermission(android.Manifest.permission.SECURE_ELEMENT_PRIVILEGED) public boolean reset();
+ }
+
+}
+
package android.security.keystore {
public class AndroidKeyStoreProvider extends java.security.Provider {
@@ -10110,6 +10141,7 @@ package android.service.contentcapture {
method public void onContentCaptureEvent(@NonNull android.view.contentcapture.ContentCaptureSessionId, @NonNull android.view.contentcapture.ContentCaptureEvent);
method public void onCreateContentCaptureSession(@NonNull android.view.contentcapture.ContentCaptureContext, @NonNull android.view.contentcapture.ContentCaptureSessionId);
method public void onDataRemovalRequest(@NonNull android.view.contentcapture.DataRemovalRequest);
+ method public void onDataShareRequest(@NonNull android.view.contentcapture.DataShareRequest, @NonNull android.service.contentcapture.DataShareCallback);
method public void onDestroyContentCaptureSession(@NonNull android.view.contentcapture.ContentCaptureSessionId);
method public void onDisconnected();
method public final void setContentCaptureConditions(@NonNull String, @Nullable java.util.Set<android.view.contentcapture.ContentCaptureCondition>);
@@ -10118,6 +10150,16 @@ package android.service.contentcapture {
field public static final String SERVICE_META_DATA = "android.content_capture";
}
+ public interface DataShareCallback {
+ method public void onAccept(@NonNull java.util.concurrent.Executor, @NonNull android.service.contentcapture.DataShareReadAdapter);
+ method public void onReject();
+ }
+
+ public interface DataShareReadAdapter {
+ method public void onError(int);
+ method public void onStart(@NonNull android.os.ParcelFileDescriptor, @NonNull android.os.CancellationSignal);
+ }
+
public final class SnapshotData implements android.os.Parcelable {
method public int describeContents();
method @Nullable public android.app.assist.AssistContent getAssistContent();
@@ -10834,7 +10876,14 @@ package android.telecom {
}
public final class PhoneAccount implements android.os.Parcelable {
+ field public static final int CAPABILITY_EMERGENCY_CALLS_ONLY = 128; // 0x80
+ field public static final int CAPABILITY_EMERGENCY_PREFERRED = 8192; // 0x2000
+ field public static final int CAPABILITY_EMERGENCY_VIDEO_CALLING = 512; // 0x200
field public static final int CAPABILITY_MULTI_USER = 32; // 0x20
+ field public static final String EXTRA_ALWAYS_USE_VOIP_AUDIO_MODE = "android.telecom.extra.ALWAYS_USE_VOIP_AUDIO_MODE";
+ field public static final String EXTRA_PLAY_CALL_RECORDING_TONE = "android.telecom.extra.PLAY_CALL_RECORDING_TONE";
+ field public static final String EXTRA_SORT_ORDER = "android.telecom.extra.SORT_ORDER";
+ field public static final String EXTRA_SUPPORTS_VIDEO_CALLING_FALLBACK = "android.telecom.extra.SUPPORTS_VIDEO_CALLING_FALLBACK";
}
public static class PhoneAccount.Builder {
@@ -10920,10 +10969,20 @@ package android.telecom {
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean isInEmergencyCall();
method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isRinging();
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setUserSelectedOutgoingPhoneAccount(@Nullable android.telecom.PhoneAccountHandle);
+ field public static final String ACTION_CURRENT_TTY_MODE_CHANGED = "android.telecom.action.CURRENT_TTY_MODE_CHANGED";
+ field public static final String ACTION_TTY_PREFERRED_MODE_CHANGED = "android.telecom.action.TTY_PREFERRED_MODE_CHANGED";
+ field public static final int CALL_SOURCE_EMERGENCY_DIALPAD = 1; // 0x1
+ field public static final int CALL_SOURCE_EMERGENCY_SHORTCUT = 2; // 0x2
+ field public static final int CALL_SOURCE_UNSPECIFIED = 0; // 0x0
field public static final String EXTRA_CALL_BACK_INTENT = "android.telecom.extra.CALL_BACK_INTENT";
+ field public static final String EXTRA_CALL_SOURCE = "android.telecom.extra.CALL_SOURCE";
+ field public static final String EXTRA_CALL_TECHNOLOGY_TYPE = "android.telecom.extra.CALL_TECHNOLOGY_TYPE";
field public static final String EXTRA_CLEAR_MISSED_CALLS_INTENT = "android.telecom.extra.CLEAR_MISSED_CALLS_INTENT";
field public static final String EXTRA_CONNECTION_SERVICE = "android.telecom.extra.CONNECTION_SERVICE";
+ field public static final String EXTRA_CURRENT_TTY_MODE = "android.telecom.extra.CURRENT_TTY_MODE";
field public static final String EXTRA_IS_USER_INTENT_EMERGENCY_CALL = "android.telecom.extra.IS_USER_INTENT_EMERGENCY_CALL";
+ field public static final String EXTRA_TTY_PREFERRED_MODE = "android.telecom.extra.TTY_PREFERRED_MODE";
+ field public static final String EXTRA_UNKNOWN_CALL_HANDLE = "android.telecom.extra.UNKNOWN_CALL_HANDLE";
field public static final int TTY_MODE_FULL = 1; // 0x1
field public static final int TTY_MODE_HCO = 2; // 0x2
field public static final int TTY_MODE_OFF = 0; // 0x0
@@ -11581,7 +11640,7 @@ package android.telephony {
}
public final class ModemActivityInfo implements android.os.Parcelable {
- ctor public ModemActivityInfo(long, int, int, @Nullable int[], int);
+ ctor public ModemActivityInfo(long, int, int, @NonNull int[], int);
method public int describeContents();
method public int getIdleTimeMillis();
method public int getReceiveTimeMillis();
@@ -12173,6 +12232,7 @@ package android.telephony {
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void requestNumberVerification(@NonNull android.telephony.PhoneNumberRange, long, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.NumberVerificationCallback);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void resetAllCarrierActions();
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void resetCarrierKeysForImsiEncryption();
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public void resetIms(int);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean resetRadioConfig();
method @RequiresPermission(android.Manifest.permission.CONNECTIVITY_INTERNAL) public void resetSettings();
method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int setAllowedCarriers(int, java.util.List<android.service.carrier.CarrierIdentifier>);
@@ -12320,6 +12380,7 @@ package android.telephony {
public class TelephonyRegistryManager {
method public void addOnOpportunisticSubscriptionsChangedListener(@NonNull android.telephony.SubscriptionManager.OnOpportunisticSubscriptionsChangedListener, @NonNull java.util.concurrent.Executor);
method public void addOnSubscriptionsChangedListener(@NonNull android.telephony.SubscriptionManager.OnSubscriptionsChangedListener, @NonNull java.util.concurrent.Executor);
+ method public void listenForSubscriber(int, @NonNull String, @NonNull String, @NonNull android.telephony.PhoneStateListener, int, boolean);
method public void notifyActiveDataSubIdChanged(int);
method public void notifyBarringInfoChanged(int, int, @NonNull android.telephony.BarringInfo);
method public void notifyCallForwardingChanged(int, boolean);
@@ -12336,6 +12397,9 @@ package android.telephony {
method public void notifyEmergencyNumberList(int, int);
method public void notifyImsDisconnectCause(int, @NonNull android.telephony.ims.ImsReasonInfo);
method public void notifyMessageWaitingChanged(int, int, boolean);
+ method public void notifyOpportunisticSubscriptionInfoChanged();
+ method public void notifyOutgoingEmergencyCall(int, int, @NonNull android.telephony.emergency.EmergencyNumber);
+ method public void notifyOutgoingEmergencySms(int, int, @NonNull android.telephony.emergency.EmergencyNumber);
method public void notifyPhoneCapabilityChanged(@NonNull android.telephony.PhoneCapability);
method public void notifyPreciseCallState(int, int, int, int, int);
method public void notifyPreciseDataConnectionFailed(int, int, int, @Nullable String, int);
@@ -12344,6 +12408,7 @@ package android.telephony {
method public void notifyServiceStateChanged(int, int, @NonNull android.telephony.ServiceState);
method public void notifySignalStrengthChanged(int, int, @NonNull android.telephony.SignalStrength);
method public void notifySrvccStateChanged(int, int);
+ method public void notifySubscriptionInfoChanged();
method public void notifyUserMobileDataStateChanged(int, int, boolean);
method public void notifyVoiceActivationStateChanged(int, int, int);
method public void removeOnOpportunisticSubscriptionsChangedListener(@NonNull android.telephony.SubscriptionManager.OnOpportunisticSubscriptionsChangedListener);
@@ -12649,6 +12714,11 @@ package android.telephony.euicc {
method @RequiresPermission(android.Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) public void getDefaultDownloadableSubscriptionList(android.app.PendingIntent);
method @RequiresPermission(android.Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) public void getDownloadableSubscriptionMetadata(android.telephony.euicc.DownloadableSubscription, android.app.PendingIntent);
method @RequiresPermission(android.Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) public int getOtaStatus();
+ method @NonNull @RequiresPermission(android.Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) public java.util.List<java.lang.String> getSupportedCountries();
+ method @NonNull @RequiresPermission(android.Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) public java.util.List<java.lang.String> getUnsupportedCountries();
+ method @RequiresPermission(android.Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) public boolean isSupportedCountry(@NonNull String);
+ method @RequiresPermission(android.Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) public void setSupportedCountries(@NonNull java.util.List<java.lang.String>);
+ method @RequiresPermission(android.Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) public void setUnsupportedCountries(@NonNull java.util.List<java.lang.String>);
field public static final String ACTION_DELETE_SUBSCRIPTION_PRIVILEGED = "android.telephony.euicc.action.DELETE_SUBSCRIPTION_PRIVILEGED";
field @RequiresPermission(android.Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) public static final String ACTION_OTA_STATUS_CHANGED = "android.telephony.euicc.action.OTA_STATUS_CHANGED";
field public static final String ACTION_PROVISION_EMBEDDED_SUBSCRIPTION = "android.telephony.euicc.action.PROVISION_EMBEDDED_SUBSCRIPTION";
@@ -13343,6 +13413,29 @@ package android.telephony.ims {
method @NonNull public android.telephony.ims.RcsContactUceCapability build();
}
+ public class RcsUceAdapter {
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isUceSettingEnabled() throws android.telephony.ims.ImsException;
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setUceSettingEnabled(boolean) throws android.telephony.ims.ImsException;
+ field public static final int ERROR_ALREADY_IN_QUEUE = 13; // 0xd
+ field public static final int ERROR_FORBIDDEN = 6; // 0x6
+ field public static final int ERROR_GENERIC_FAILURE = 1; // 0x1
+ field public static final int ERROR_INSUFFICIENT_MEMORY = 11; // 0xb
+ field public static final int ERROR_LOST_NETWORK = 12; // 0xc
+ field public static final int ERROR_NOT_AUTHORIZED = 5; // 0x5
+ field public static final int ERROR_NOT_AVAILABLE = 3; // 0x3
+ field public static final int ERROR_NOT_ENABLED = 2; // 0x2
+ field public static final int ERROR_NOT_FOUND = 7; // 0x7
+ field public static final int ERROR_NOT_REGISTERED = 4; // 0x4
+ field public static final int ERROR_REQUEST_TIMEOUT = 10; // 0xa
+ field public static final int ERROR_REQUEST_TOO_LARGE = 8; // 0x8
+ field public static final int PUBLISH_STATE_200_OK = 1; // 0x1
+ field public static final int PUBLISH_STATE_NOT_PUBLISHED = 2; // 0x2
+ field public static final int PUBLISH_STATE_OTHER_ERROR = 6; // 0x6
+ field public static final int PUBLISH_STATE_RCS_PROVISION_ERROR = 4; // 0x4
+ field public static final int PUBLISH_STATE_REQUEST_TIMEOUT = 5; // 0x5
+ field public static final int PUBLISH_STATE_VOLTE_PROVISION_ERROR = 3; // 0x3
+ }
+
}
package android.telephony.ims.feature {
@@ -14274,3 +14367,4 @@ package android.webkit {
}
}
+
diff --git a/api/system-lint-baseline.txt b/api/system-lint-baseline.txt
index 23e1ed75998a..2f1889cea4eb 100644
--- a/api/system-lint-baseline.txt
+++ b/api/system-lint-baseline.txt
@@ -252,6 +252,12 @@ ProtectedMember: android.service.notification.NotificationAssistantService#attac
+ResourceValueFieldName: android.R.array#config_sms_enabled_locking_shift_tables:
+ Expected resource name in `android.R.array` to be in the `fooBarBaz` style, was `config_sms_enabled_locking_shift_tables`
+ResourceValueFieldName: android.R.array#config_sms_enabled_single_shift_tables:
+ Expected resource name in `android.R.array` to be in the `fooBarBaz` style, was `config_sms_enabled_single_shift_tables`
+
+
SamShouldBeLast: android.accounts.AccountManager#addAccount(String, String, String[], android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler):
SamShouldBeLast: android.accounts.AccountManager#addOnAccountsUpdatedListener(android.accounts.OnAccountsUpdateListener, android.os.Handler, boolean):
diff --git a/api/test-current.txt b/api/test-current.txt
index e2407d65ef5c..76af40318fb8 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -1187,6 +1187,9 @@ package android.location {
method public void resetElapsedRealtimeUncertaintyNanos();
method public void resetFullBiasNanos();
method public void resetLeapSecond();
+ method public void resetReferenceCarrierFrequencyHzForIsb();
+ method public void resetReferenceCodeTypeForIsb();
+ method public void resetReferenceConstellationTypeForIsb();
method public void resetTimeUncertaintyNanos();
method public void set(android.location.GnssClock);
method public void setBiasNanos(double);
@@ -1198,6 +1201,9 @@ package android.location {
method public void setFullBiasNanos(long);
method public void setHardwareClockDiscontinuityCount(int);
method public void setLeapSecond(int);
+ method public void setReferenceCarrierFrequencyHzForIsb(@FloatRange(from=0.0) double);
+ method public void setReferenceCodeTypeForIsb(@NonNull String);
+ method public void setReferenceConstellationTypeForIsb(int);
method public void setTimeNanos(long);
method public void setTimeUncertaintyNanos(@FloatRange(from=0.0f) double);
}
@@ -1212,6 +1218,10 @@ package android.location {
method @Deprecated public void resetCarrierPhase();
method @Deprecated public void resetCarrierPhaseUncertainty();
method public void resetCodeType();
+ method public void resetReceiverInterSignalBiasNanos();
+ method public void resetReceiverInterSignalBiasUncertaintyNanos();
+ method public void resetSatelliteInterSignalBiasNanos();
+ method public void resetSatelliteInterSignalBiasUncertaintyNanos();
method public void resetSnrInDb();
method public void set(android.location.GnssMeasurement);
method public void setAccumulatedDeltaRangeMeters(double);
@@ -1231,6 +1241,10 @@ package android.location {
method public void setPseudorangeRateUncertaintyMetersPerSecond(double);
method public void setReceivedSvTimeNanos(long);
method public void setReceivedSvTimeUncertaintyNanos(long);
+ method public void setReceiverInterSignalBiasNanos(double);
+ method public void setReceiverInterSignalBiasUncertaintyNanos(@FloatRange(from=0.0) double);
+ method public void setSatelliteInterSignalBiasNanos(double);
+ method public void setSatelliteInterSignalBiasUncertaintyNanos(@FloatRange(from=0.0) double);
method public void setSnrInDb(double);
method public void setState(int);
method public void setSvid(int);
@@ -1607,9 +1621,12 @@ package android.net {
public class LinkAddress implements android.os.Parcelable {
ctor public LinkAddress(@NonNull java.net.InetAddress, @IntRange(from=0, to=128) int, int, int);
+ ctor public LinkAddress(@NonNull java.net.InetAddress, @IntRange(from=0, to=128) int, int, int, long, long);
ctor public LinkAddress(@NonNull java.net.InetAddress, @IntRange(from=0, to=128) int);
ctor public LinkAddress(@NonNull String);
ctor public LinkAddress(@NonNull String, int, int);
+ method public long getDeprecationTime();
+ method public long getExpirationTime();
method public boolean isGlobalPreferred();
method public boolean isIpv4();
method public boolean isIpv6();
@@ -1709,12 +1726,32 @@ package android.net {
method public void teardownTestNetwork(@NonNull android.net.Network);
}
+ public final class TetheredClient implements android.os.Parcelable {
+ ctor public TetheredClient(@NonNull android.net.MacAddress, @NonNull java.util.Collection<android.net.TetheredClient.AddressInfo>, int);
+ method public int describeContents();
+ method @NonNull public java.util.List<android.net.TetheredClient.AddressInfo> getAddresses();
+ method @NonNull public android.net.MacAddress getMacAddress();
+ method public int getTetheringType();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.TetheredClient> CREATOR;
+ }
+
+ public static final class TetheredClient.AddressInfo implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public android.net.LinkAddress getAddress();
+ method @Nullable public String getHostname();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.TetheredClient.AddressInfo> CREATOR;
+ }
+
public class TetheringManager {
- method public void registerTetheringEventCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.TetheringEventCallback);
- method @RequiresPermission("android.permission.TETHER_PRIVILEGED") public void requestLatestTetheringEntitlementResult(int, boolean, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.OnTetheringEntitlementResultListener);
- method public void stopAllTethering();
- method public void stopTethering(int);
- method public void unregisterTetheringEventCallback(@NonNull android.net.TetheringManager.TetheringEventCallback);
+ method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void registerTetheringEventCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.TetheringEventCallback);
+ method @RequiresPermission(anyOf={"android.permission.TETHER_PRIVILEGED", android.Manifest.permission.WRITE_SETTINGS}) public void requestLatestTetheringEntitlementResult(int, boolean, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.OnTetheringEntitlementResultListener);
+ method @RequiresPermission(anyOf={"android.permission.TETHER_PRIVILEGED", android.Manifest.permission.WRITE_SETTINGS}) public void startTethering(@NonNull android.net.TetheringManager.TetheringRequest, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.StartTetheringCallback);
+ method @RequiresPermission(anyOf={"android.permission.TETHER_PRIVILEGED", android.Manifest.permission.WRITE_SETTINGS}) public void startTethering(int, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.StartTetheringCallback);
+ method @RequiresPermission(anyOf={"android.permission.TETHER_PRIVILEGED", android.Manifest.permission.WRITE_SETTINGS}) public void stopAllTethering();
+ method @RequiresPermission(anyOf={"android.permission.TETHER_PRIVILEGED", android.Manifest.permission.WRITE_SETTINGS}) public void stopTethering(int);
+ method @RequiresPermission(anyOf={"android.permission.TETHER_PRIVILEGED", android.Manifest.permission.ACCESS_NETWORK_STATE}) public void unregisterTetheringEventCallback(@NonNull android.net.TetheringManager.TetheringEventCallback);
field public static final String ACTION_TETHER_STATE_CHANGED = "android.net.conn.TETHER_STATE_CHANGED";
field public static final String EXTRA_ACTIVE_LOCAL_ONLY = "android.net.extra.ACTIVE_LOCAL_ONLY";
field public static final String EXTRA_ACTIVE_TETHER = "tetherArray";
@@ -1722,6 +1759,7 @@ package android.net {
field public static final String EXTRA_ERRORED_TETHER = "erroredArray";
field public static final int TETHERING_BLUETOOTH = 2; // 0x2
field public static final int TETHERING_INVALID = -1; // 0xffffffff
+ field public static final int TETHERING_NCM = 4; // 0x4
field public static final int TETHERING_USB = 1; // 0x1
field public static final int TETHERING_WIFI = 0; // 0x0
field public static final int TETHERING_WIFI_P2P = 3; // 0x3
@@ -1747,8 +1785,15 @@ package android.net {
method public void onTetheringEntitlementResult(int);
}
+ public abstract static class TetheringManager.StartTetheringCallback {
+ ctor public TetheringManager.StartTetheringCallback();
+ method public void onTetheringFailed(int);
+ method public void onTetheringStarted();
+ }
+
public abstract static class TetheringManager.TetheringEventCallback {
ctor public TetheringManager.TetheringEventCallback();
+ method public void onClientsChanged(@NonNull java.util.Collection<android.net.TetheredClient>);
method public void onError(@NonNull String, int);
method @Deprecated public void onTetherableInterfaceRegexpsChanged(@NonNull android.net.TetheringManager.TetheringInterfaceRegexps);
method public void onTetherableInterfacesChanged(@NonNull java.util.List<java.lang.String>);
@@ -1764,6 +1809,17 @@ package android.net {
method @Deprecated @NonNull public java.util.List<java.lang.String> getTetherableWifiRegexs();
}
+ public static class TetheringManager.TetheringRequest {
+ }
+
+ public static class TetheringManager.TetheringRequest.Builder {
+ ctor public TetheringManager.TetheringRequest.Builder(int);
+ method @NonNull public android.net.TetheringManager.TetheringRequest build();
+ method @NonNull @RequiresPermission("android.permission.TETHER_PRIVILEGED") public android.net.TetheringManager.TetheringRequest.Builder setExemptFromEntitlementCheck(boolean);
+ method @NonNull @RequiresPermission("android.permission.TETHER_PRIVILEGED") public android.net.TetheringManager.TetheringRequest.Builder setSilentProvisioning(boolean);
+ method @NonNull @RequiresPermission("android.permission.TETHER_PRIVILEGED") public android.net.TetheringManager.TetheringRequest.Builder useStaticIpv4Addresses(@NonNull android.net.LinkAddress);
+ }
+
public class TrafficStats {
method public static long getLoopbackRxBytes();
method public static long getLoopbackRxPackets();
@@ -3483,6 +3539,7 @@ package android.telephony {
public class TelephonyRegistryManager {
method public void addOnOpportunisticSubscriptionsChangedListener(@NonNull android.telephony.SubscriptionManager.OnOpportunisticSubscriptionsChangedListener, @NonNull java.util.concurrent.Executor);
method public void addOnSubscriptionsChangedListener(@NonNull android.telephony.SubscriptionManager.OnSubscriptionsChangedListener, @NonNull java.util.concurrent.Executor);
+ method public void listenForSubscriber(int, @NonNull String, @NonNull String, @NonNull android.telephony.PhoneStateListener, int, boolean);
method public void notifyActiveDataSubIdChanged(int);
method public void notifyBarringInfoChanged(int, int, @NonNull android.telephony.BarringInfo);
method public void notifyCallForwardingChanged(int, boolean);
@@ -3499,6 +3556,8 @@ package android.telephony {
method public void notifyEmergencyNumberList(int, int);
method public void notifyImsDisconnectCause(int, @NonNull android.telephony.ims.ImsReasonInfo);
method public void notifyMessageWaitingChanged(int, int, boolean);
+ method public void notifyOutgoingEmergencyCall(int, int, @NonNull android.telephony.emergency.EmergencyNumber);
+ method public void notifyOutgoingEmergencySms(int, int, @NonNull android.telephony.emergency.EmergencyNumber);
method public void notifyPhoneCapabilityChanged(@NonNull android.telephony.PhoneCapability);
method public void notifyPreciseCallState(int, int, int, int, int);
method public void notifyPreciseDataConnectionFailed(int, int, int, @Nullable String, int);
@@ -4100,6 +4159,29 @@ package android.telephony.ims {
method public void onProvisioningStringChanged(int, @NonNull String);
}
+ public class RcsUceAdapter {
+ method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public boolean isUceSettingEnabled() throws android.telephony.ims.ImsException;
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setUceSettingEnabled(boolean) throws android.telephony.ims.ImsException;
+ field public static final int ERROR_ALREADY_IN_QUEUE = 13; // 0xd
+ field public static final int ERROR_FORBIDDEN = 6; // 0x6
+ field public static final int ERROR_GENERIC_FAILURE = 1; // 0x1
+ field public static final int ERROR_INSUFFICIENT_MEMORY = 11; // 0xb
+ field public static final int ERROR_LOST_NETWORK = 12; // 0xc
+ field public static final int ERROR_NOT_AUTHORIZED = 5; // 0x5
+ field public static final int ERROR_NOT_AVAILABLE = 3; // 0x3
+ field public static final int ERROR_NOT_ENABLED = 2; // 0x2
+ field public static final int ERROR_NOT_FOUND = 7; // 0x7
+ field public static final int ERROR_NOT_REGISTERED = 4; // 0x4
+ field public static final int ERROR_REQUEST_TIMEOUT = 10; // 0xa
+ field public static final int ERROR_REQUEST_TOO_LARGE = 8; // 0x8
+ field public static final int PUBLISH_STATE_200_OK = 1; // 0x1
+ field public static final int PUBLISH_STATE_NOT_PUBLISHED = 2; // 0x2
+ field public static final int PUBLISH_STATE_OTHER_ERROR = 6; // 0x6
+ field public static final int PUBLISH_STATE_RCS_PROVISION_ERROR = 4; // 0x4
+ field public static final int PUBLISH_STATE_REQUEST_TIMEOUT = 5; // 0x5
+ field public static final int PUBLISH_STATE_VOLTE_PROVISION_ERROR = 3; // 0x3
+ }
+
}
package android.telephony.ims.feature {
diff --git a/api/test-lint-baseline.txt b/api/test-lint-baseline.txt
index 54f7f68d96cb..a9c18363d8b0 100644
--- a/api/test-lint-baseline.txt
+++ b/api/test-lint-baseline.txt
@@ -406,7 +406,13 @@ GetterSetterNames: android.location.GnssClock#setElapsedRealtimeUncertaintyNanos
GetterSetterNames: android.location.GnssClock#setFullBiasNanos(long):
GetterSetterNames: android.location.GnssClock#setLeapSecond(int):
-
+
+GetterSetterNames: android.location.GnssClock#setReferenceConstellationTypeForIsb(int):
+
+GetterSetterNames: android.location.GnssClock#setReferenceCarrierFrequencyHzForIsb(double):
+
+GetterSetterNames: android.location.GnssClock#setReferenceCodeTypeForIsb(String):
+
GetterSetterNames: android.location.GnssClock#setTimeUncertaintyNanos(double):
GetterSetterNames: android.location.GnssMeasurement#setBasebandCn0DbHz(double):
@@ -414,7 +420,15 @@ GetterSetterNames: android.location.GnssMeasurement#setBasebandCn0DbHz(double):
GetterSetterNames: android.location.GnssMeasurement#setCarrierFrequencyHz(float):
GetterSetterNames: android.location.GnssMeasurement#setCodeType(String):
-
+
+GetterSetterNames: android.location.GnssMeasurement#setReceiverInterSignalBiasNanos(double):
+
+GetterSetterNames: android.location.GnssMeasurement#setReceiverInterSignalBiasUncertaintyNanos(double):
+
+GetterSetterNames: android.location.GnssMeasurement#setSatelliteInterSignalBiasNanos(double):
+
+GetterSetterNames: android.location.GnssMeasurement#setSatelliteInterSignalBiasUncertaintyNanos(double):
+
GetterSetterNames: android.location.GnssMeasurement#setSnrInDb(double):
GetterSetterNames: android.location.LocationRequest#isLocationSettingsIgnored():
diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp
index 080b1af35059..ac24a553b555 100644
--- a/cmds/statsd/Android.bp
+++ b/cmds/statsd/Android.bp
@@ -50,7 +50,6 @@ cc_defaults {
srcs: [
":statsd_aidl",
- ":ICarStatsService.aidl",
"src/active_config_list.proto",
"src/anomaly/AlarmMonitor.cpp",
"src/anomaly/AlarmTracker.cpp",
@@ -65,7 +64,6 @@ cc_defaults {
"src/config/ConfigKey.cpp",
"src/config/ConfigListener.cpp",
"src/config/ConfigManager.cpp",
- "src/external/CarStatsPuller.cpp",
"src/external/GpuStatsPuller.cpp",
"src/external/Perfetto.cpp",
"src/external/PowerStatsPuller.cpp",
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index d9b3a6c05c2a..d9d5be6e46ec 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -151,8 +151,8 @@ message Atom {
HardwareFailed hardware_failed = 72;
PhysicalDropDetected physical_drop_detected = 73;
ChargeCyclesReported charge_cycles_reported = 74;
- MobileConnectionStateChanged mobile_connection_state_changed = 75;
- MobileRadioTechnologyChanged mobile_radio_technology_changed = 76;
+ MobileConnectionStateChanged mobile_connection_state_changed = 75 [(module) = "telephony"];
+ MobileRadioTechnologyChanged mobile_radio_technology_changed = 76 [(module) = "telephony"];
UsbDeviceAttached usb_device_attached = 77;
AppCrashOccurred app_crash_occurred = 78;
ANROccurred anr_occurred = 79;
@@ -341,6 +341,8 @@ message Atom {
NotificationReported notification_reported = 244;
NotificationPanelReported notification_panel_reported = 245;
NotificationChannelModified notification_panel_modified = 246;
+ IntegrityCheckResultReported integrity_check_result_reported = 247;
+ IntegrityRulesPushed integrity_rules_pushed = 248;
}
// Pulled events will start at field 10000.
@@ -8070,3 +8072,43 @@ message UserspaceRebootReported {
// State of primary user's encryption storage at the moment boot completed. Always set.
optional UserEncryptionState user_encryption_state = 3;
}
+
+/*
+ * Logs integrity check information during each install.
+ *
+ * Logged from:
+ * frameworks/base/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
+ */
+message IntegrityCheckResultReported {
+ optional string package_name = 1;
+ optional string app_certificate_hash = 2;
+ optional int64 version_code = 3;
+ optional string installer_package_name = 4;
+ enum Response {
+ UNKNOWN = 0;
+ ALLOWED = 1;
+ REJECTED = 2;
+ FORCE_ALLOWED = 3;
+ }
+ optional Response response = 5;
+ // An estimate on the cause of the response. This will only be populated for
+ // REJECTED and FORCE_ALLOWED
+ optional bool caused_by_app_cert_rule = 6;
+ optional bool caused_by_installer_rule = 7;
+}
+
+/**
+ * Logs the information about the rules and the provider whenever rules are
+ * pushed into AppIntegrityManager.
+ *
+ * Logged from:
+ * frameworks/base/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
+ */
+message IntegrityRulesPushed {
+ optional bool success = 1;
+ // Package name of the app that pushed the rules.
+ optional string rule_provider = 2;
+ // Version string of arbitrary format provided by the rule provider to
+ // identify the rules.
+ optional string rule_version = 3;
+}
diff --git a/cmds/statsd/src/external/CarStatsPuller.cpp b/cmds/statsd/src/external/CarStatsPuller.cpp
deleted file mode 100644
index 70c0456b5eb4..000000000000
--- a/cmds/statsd/src/external/CarStatsPuller.cpp
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * 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.
- */
-
-#define DEBUG false
-#include "Log.h"
-
-#include <binder/IServiceManager.h>
-#include <com/android/internal/car/ICarStatsService.h>
-
-#include "CarStatsPuller.h"
-#include "logd/LogEvent.h"
-#include "stats_log_util.h"
-
-using android::binder::Status;
-using com::android::internal::car::ICarStatsService;
-
-namespace android {
-namespace os {
-namespace statsd {
-
-static std::mutex gCarStatsMutex;
-static sp<ICarStatsService> gCarStats = nullptr;
-
-class CarStatsDeathRecipient : public android::IBinder::DeathRecipient {
- public:
- CarStatsDeathRecipient() = default;
- ~CarStatsDeathRecipient() override = default;
-
- // android::IBinder::DeathRecipient override:
- void binderDied(const android::wp<android::IBinder>& /* who */) override {
- ALOGE("Car service has died");
- std::lock_guard<std::mutex> lock(gCarStatsMutex);
- if (gCarStats) {
- sp<IBinder> binder = IInterface::asBinder(gCarStats);
- binder->unlinkToDeath(this);
- gCarStats = nullptr;
- }
- }
-};
-
-static sp<CarStatsDeathRecipient> gDeathRecipient = new CarStatsDeathRecipient();
-
-static sp<ICarStatsService> getCarService() {
- std::lock_guard<std::mutex> lock(gCarStatsMutex);
- if (!gCarStats) {
- const sp<IBinder> binder = defaultServiceManager()->checkService(String16("car_stats"));
- if (!binder) {
- ALOGW("Car service is unavailable");
- return nullptr;
- }
- gCarStats = interface_cast<ICarStatsService>(binder);
- binder->linkToDeath(gDeathRecipient);
- }
- return gCarStats;
-}
-
-CarStatsPuller::CarStatsPuller(const int tagId) : StatsPuller(tagId) {
-}
-
-bool CarStatsPuller::PullInternal(std::vector<std::shared_ptr<LogEvent>>* data) {
- const sp<ICarStatsService> carService = getCarService();
- if (!carService) {
- return false;
- }
-
- vector<StatsLogEventWrapper> returned_value;
- Status status = carService->pullData(mTagId, &returned_value);
- if (!status.isOk()) {
- ALOGW("CarStatsPuller::pull failed for %d", mTagId);
- return false;
- }
-
- data->clear();
- for (const StatsLogEventWrapper& it : returned_value) {
- LogEvent::createLogEvents(it, *data);
- }
- VLOG("CarStatsPuller::pull succeeded for %d", mTagId);
- return true;
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/external/CarStatsPuller.h b/cmds/statsd/src/external/CarStatsPuller.h
deleted file mode 100644
index ca0f1a9c9a17..000000000000
--- a/cmds/statsd/src/external/CarStatsPuller.h
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * 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.
- */
-
-#pragma once
-
-#include "StatsPuller.h"
-
-namespace android {
-namespace os {
-namespace statsd {
-
-/**
- * Pull atoms from CarService.
- */
-class CarStatsPuller : public StatsPuller {
-public:
- explicit CarStatsPuller(const int tagId);
- bool PullInternal(std::vector<std::shared_ptr<LogEvent>>* data) override;
-};
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/external/Perfetto.h b/cmds/statsd/src/external/Perfetto.h
index ab2c1954e2a0..095782a49f9b 100644
--- a/cmds/statsd/src/external/Perfetto.h
+++ b/cmds/statsd/src/external/Perfetto.h
@@ -16,10 +16,6 @@
#pragma once
-#include <android/os/StatsLogEventWrapper.h>
-
-using android::os::StatsLogEventWrapper;
-
namespace android {
namespace os {
namespace statsd {
diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp
index 8d67b5c169f5..5f5dce879bd9 100644
--- a/cmds/statsd/src/external/StatsPullerManager.cpp
+++ b/cmds/statsd/src/external/StatsPullerManager.cpp
@@ -32,7 +32,6 @@
#include "../logd/LogEvent.h"
#include "../stats_log_util.h"
#include "../statscompanion_util.h"
-#include "CarStatsPuller.h"
#include "GpuStatsPuller.h"
#include "PowerStatsPuller.h"
#include "ResourceHealthManagerPuller.h"
@@ -95,11 +94,6 @@ std::map<PullerKey, PullAtomInfo> StatsPullerManager::kAllPullAtomInfo = {
// GpuStatsAppInfo
{{.atomTag = android::util::GPU_STATS_APP_INFO},
{.puller = new GpuStatsPuller(android::util::GPU_STATS_APP_INFO)}},
-
- // VmsClientStats
- {{.atomTag = android::util::VMS_CLIENT_STATS},
- {.additiveFields = {5, 6, 7, 8, 9, 10},
- .puller = new CarStatsPuller(android::util::VMS_CLIENT_STATS)}},
};
StatsPullerManager::StatsPullerManager() : mNextPullTimeNs(NO_ALARM_UPDATE) {
diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp
index 36f4623c4dcb..3827b9e21b70 100644
--- a/cmds/statsd/src/logd/LogEvent.cpp
+++ b/cmds/statsd/src/logd/LogEvent.cpp
@@ -71,70 +71,6 @@ LogEvent::LogEvent(const LogEvent& event) {
mValues = event.mValues;
}
-LogEvent::LogEvent(const StatsLogEventWrapper& statsLogEventWrapper, int workChainIndex) {
- mTagId = statsLogEventWrapper.getTagId();
- mLogdTimestampNs = statsLogEventWrapper.getWallClockTimeNs();
- mElapsedTimestampNs = statsLogEventWrapper.getElapsedRealTimeNs();
- mLogUid = 0;
- int workChainPosOffset = 0;
- if (workChainIndex != -1) {
- const WorkChain& wc = statsLogEventWrapper.getWorkChains()[workChainIndex];
- // chains are at field 1, level 2
- int depth = 2;
- for (int i = 0; i < (int)wc.uids.size(); i++) {
- int pos[] = {1, i + 1, 1};
- mValues.push_back(FieldValue(Field(mTagId, pos, depth), Value(wc.uids[i])));
- pos[2]++;
- mValues.push_back(FieldValue(Field(mTagId, pos, depth), Value(wc.tags[i])));
- mValues.back().mField.decorateLastPos(2);
- }
- mValues.back().mField.decorateLastPos(1);
- workChainPosOffset = 1;
- }
- for (int i = 0; i < (int)statsLogEventWrapper.getElements().size(); i++) {
- Field field(statsLogEventWrapper.getTagId(), getSimpleField(i + 1 + workChainPosOffset));
- switch (statsLogEventWrapper.getElements()[i].type) {
- case android::os::StatsLogValue::STATS_LOG_VALUE_TYPE::INT:
- mValues.push_back(
- FieldValue(field, Value(statsLogEventWrapper.getElements()[i].int_value)));
- break;
- case android::os::StatsLogValue::STATS_LOG_VALUE_TYPE::LONG:
- mValues.push_back(
- FieldValue(field, Value(statsLogEventWrapper.getElements()[i].long_value)));
- break;
- case android::os::StatsLogValue::STATS_LOG_VALUE_TYPE::FLOAT:
- mValues.push_back(FieldValue(
- field, Value(statsLogEventWrapper.getElements()[i].float_value)));
- break;
- case android::os::StatsLogValue::STATS_LOG_VALUE_TYPE::DOUBLE:
- mValues.push_back(FieldValue(
- field, Value(statsLogEventWrapper.getElements()[i].double_value)));
- break;
- case android::os::StatsLogValue::STATS_LOG_VALUE_TYPE::STRING:
- mValues.push_back(
- FieldValue(field, Value(statsLogEventWrapper.getElements()[i].str_value)));
- break;
- case android::os::StatsLogValue::STATS_LOG_VALUE_TYPE::STORAGE:
- mValues.push_back(FieldValue(
- field, Value(statsLogEventWrapper.getElements()[i].storage_value)));
- break;
- default:
- break;
- }
- }
-}
-
-void LogEvent::createLogEvents(const StatsLogEventWrapper& statsLogEventWrapper,
- std::vector<std::shared_ptr<LogEvent>>& logEvents) {
- if (statsLogEventWrapper.getWorkChains().size() == 0) {
- logEvents.push_back(std::make_shared<LogEvent>(statsLogEventWrapper, -1));
- } else {
- for (size_t i = 0; i < statsLogEventWrapper.getWorkChains().size(); i++) {
- logEvents.push_back(std::make_shared<LogEvent>(statsLogEventWrapper, i));
- }
- }
-}
-
LogEvent::LogEvent(int32_t tagId, int64_t wallClockTimestampNs, int64_t elapsedTimestampNs) {
mLogdTimestampNs = wallClockTimestampNs;
mElapsedTimestampNs = elapsedTimestampNs;
diff --git a/cmds/statsd/src/logd/LogEvent.h b/cmds/statsd/src/logd/LogEvent.h
index 596d623debe5..0f33c56be42a 100644
--- a/cmds/statsd/src/logd/LogEvent.h
+++ b/cmds/statsd/src/logd/LogEvent.h
@@ -19,7 +19,6 @@
#include "FieldValue.h"
#include <android/frameworks/stats/1.0/types.h>
-#include <android/os/StatsLogEventWrapper.h>
#include <android/util/ProtoOutputStream.h>
#include <log/log_read.h>
#include <private/android_logger.h>
@@ -80,17 +79,6 @@ public:
explicit LogEvent(uint8_t* msg, uint32_t len, uint32_t uid, bool useNewSchema);
/**
- * Creates LogEvent from StatsLogEventWrapper.
- */
- static void createLogEvents(const StatsLogEventWrapper& statsLogEventWrapper,
- std::vector<std::shared_ptr<LogEvent>>& logEvents);
-
- /**
- * Construct one LogEvent from a StatsLogEventWrapper with the i-th work chain. -1 if no chain.
- */
- explicit LogEvent(const StatsLogEventWrapper& statsLogEventWrapper, int workChainIndex);
-
- /**
* Constructs a LogEvent with synthetic data for testing. Must call init() before reading.
*/
explicit LogEvent(int32_t tagId, int64_t wallClockTimestampNs, int64_t elapsedTimestampNs);
diff --git a/cmds/statsd/tests/LogEvent_test.cpp b/cmds/statsd/tests/LogEvent_test.cpp
index 9f50701d5e9e..1cf9fb681d61 100644
--- a/cmds/statsd/tests/LogEvent_test.cpp
+++ b/cmds/statsd/tests/LogEvent_test.cpp
@@ -665,168 +665,6 @@ TEST(LogEventTest, TestKeyValuePairsEvent) {
EXPECT_EQ(1.1f, item16.mValue.float_value);
}
-TEST(LogEventTest, TestStatsLogEventWrapperNoChain) {
- Parcel parcel;
- // tag id
- parcel.writeInt32(1);
- // elapsed realtime
- parcel.writeInt64(1111L);
- // wallclock time
- parcel.writeInt64(2222L);
- // no chain
- parcel.writeInt32(0);
- // 2 data
- parcel.writeInt32(2);
- // int 6
- parcel.writeInt32(1);
- parcel.writeInt32(6);
- // long 10
- parcel.writeInt32(2);
- parcel.writeInt64(10);
- parcel.setDataPosition(0);
-
- StatsLogEventWrapper statsLogEventWrapper;
- EXPECT_EQ(NO_ERROR, statsLogEventWrapper.readFromParcel(&parcel));
- EXPECT_EQ(1, statsLogEventWrapper.getTagId());
- EXPECT_EQ(1111L, statsLogEventWrapper.getElapsedRealTimeNs());
- EXPECT_EQ(2222L, statsLogEventWrapper.getWallClockTimeNs());
- EXPECT_EQ(0, statsLogEventWrapper.getWorkChains().size());
- EXPECT_EQ(2, statsLogEventWrapper.getElements().size());
- EXPECT_EQ(6, statsLogEventWrapper.getElements()[0].int_value);
- EXPECT_EQ(10L, statsLogEventWrapper.getElements()[1].long_value);
- LogEvent event(statsLogEventWrapper, -1);
- EXPECT_EQ(1, event.GetTagId());
- EXPECT_EQ(1111L, event.GetElapsedTimestampNs());
- EXPECT_EQ(2222L, event.GetLogdTimestampNs());
- EXPECT_EQ(2, event.size());
- EXPECT_EQ(6, event.getValues()[0].mValue.int_value);
- EXPECT_EQ(10, event.getValues()[1].mValue.long_value);
-}
-
-TEST(LogEventTest, TestStatsLogEventWrapperWithChain) {
- Parcel parcel;
- // tag id
- parcel.writeInt32(1);
- // elapsed realtime
- parcel.writeInt64(1111L);
- // wallclock time
- parcel.writeInt64(2222L);
- // 3 chains
- parcel.writeInt32(3);
- // chain1, 2 nodes (1, "tag1") (2, "tag2")
- parcel.writeInt32(2);
- parcel.writeInt32(1);
- parcel.writeString16(String16("tag1"));
- parcel.writeInt32(2);
- parcel.writeString16(String16("tag2"));
- // chain2, 1 node (3, "tag3")
- parcel.writeInt32(1);
- parcel.writeInt32(3);
- parcel.writeString16(String16("tag3"));
- // chain3, 2 nodes (4, "") (5, "")
- parcel.writeInt32(2);
- parcel.writeInt32(4);
- parcel.writeString16(String16(""));
- parcel.writeInt32(5);
- parcel.writeString16(String16(""));
- // 2 data
- parcel.writeInt32(2);
- // int 6
- parcel.writeInt32(1);
- parcel.writeInt32(6);
- // long 10
- parcel.writeInt32(2);
- parcel.writeInt64(10);
- parcel.setDataPosition(0);
-
- StatsLogEventWrapper statsLogEventWrapper;
- EXPECT_EQ(NO_ERROR, statsLogEventWrapper.readFromParcel(&parcel));
- EXPECT_EQ(1, statsLogEventWrapper.getTagId());
- EXPECT_EQ(1111L, statsLogEventWrapper.getElapsedRealTimeNs());
- EXPECT_EQ(2222L, statsLogEventWrapper.getWallClockTimeNs());
- EXPECT_EQ(3, statsLogEventWrapper.getWorkChains().size());
- EXPECT_EQ(2, statsLogEventWrapper.getWorkChains()[0].uids.size());
- EXPECT_EQ(1, statsLogEventWrapper.getWorkChains()[0].uids[0]);
- EXPECT_EQ(2, statsLogEventWrapper.getWorkChains()[0].uids[1]);
- EXPECT_EQ(2, statsLogEventWrapper.getWorkChains()[0].tags.size());
- EXPECT_EQ("tag1", statsLogEventWrapper.getWorkChains()[0].tags[0]);
- EXPECT_EQ("tag2", statsLogEventWrapper.getWorkChains()[0].tags[1]);
- EXPECT_EQ(1, statsLogEventWrapper.getWorkChains()[1].uids.size());
- EXPECT_EQ(3, statsLogEventWrapper.getWorkChains()[1].uids[0]);
- EXPECT_EQ(1, statsLogEventWrapper.getWorkChains()[1].tags.size());
- EXPECT_EQ("tag3", statsLogEventWrapper.getWorkChains()[1].tags[0]);
- EXPECT_EQ(2, statsLogEventWrapper.getElements().size());
- EXPECT_EQ(6, statsLogEventWrapper.getElements()[0].int_value);
- EXPECT_EQ(10L, statsLogEventWrapper.getElements()[1].long_value);
- EXPECT_EQ(2, statsLogEventWrapper.getWorkChains()[2].uids.size());
- EXPECT_EQ(4, statsLogEventWrapper.getWorkChains()[2].uids[0]);
- EXPECT_EQ(5, statsLogEventWrapper.getWorkChains()[2].uids[1]);
- EXPECT_EQ(2, statsLogEventWrapper.getWorkChains()[2].tags.size());
- EXPECT_EQ("", statsLogEventWrapper.getWorkChains()[2].tags[0]);
- EXPECT_EQ("", statsLogEventWrapper.getWorkChains()[2].tags[1]);
-
- LogEvent event(statsLogEventWrapper, -1);
- EXPECT_EQ(1, event.GetTagId());
- EXPECT_EQ(1111L, event.GetElapsedTimestampNs());
- EXPECT_EQ(2222L, event.GetLogdTimestampNs());
- EXPECT_EQ(2, event.size());
- EXPECT_EQ(6, event.getValues()[0].mValue.int_value);
- EXPECT_EQ(10, event.getValues()[1].mValue.long_value);
-
- LogEvent event1(statsLogEventWrapper, 0);
-
- EXPECT_EQ(1, event1.GetTagId());
- EXPECT_EQ(1111L, event1.GetElapsedTimestampNs());
- EXPECT_EQ(2222L, event1.GetLogdTimestampNs());
- EXPECT_EQ(6, event1.size());
- EXPECT_EQ(1, event1.getValues()[0].mValue.int_value);
- EXPECT_EQ(0x2010101, event1.getValues()[0].mField.getField());
- EXPECT_EQ("tag1", event1.getValues()[1].mValue.str_value);
- EXPECT_EQ(0x2010182, event1.getValues()[1].mField.getField());
- EXPECT_EQ(2, event1.getValues()[2].mValue.int_value);
- EXPECT_EQ(0x2010201, event1.getValues()[2].mField.getField());
- EXPECT_EQ("tag2", event1.getValues()[3].mValue.str_value);
- EXPECT_EQ(0x2018282, event1.getValues()[3].mField.getField());
- EXPECT_EQ(6, event1.getValues()[4].mValue.int_value);
- EXPECT_EQ(0x20000, event1.getValues()[4].mField.getField());
- EXPECT_EQ(10, event1.getValues()[5].mValue.long_value);
- EXPECT_EQ(0x30000, event1.getValues()[5].mField.getField());
-
- LogEvent event2(statsLogEventWrapper, 1);
-
- EXPECT_EQ(1, event2.GetTagId());
- EXPECT_EQ(1111L, event2.GetElapsedTimestampNs());
- EXPECT_EQ(2222L, event2.GetLogdTimestampNs());
- EXPECT_EQ(4, event2.size());
- EXPECT_EQ(3, event2.getValues()[0].mValue.int_value);
- EXPECT_EQ(0x2010101, event2.getValues()[0].mField.getField());
- EXPECT_EQ("tag3", event2.getValues()[1].mValue.str_value);
- EXPECT_EQ(0x2018182, event2.getValues()[1].mField.getField());
- EXPECT_EQ(6, event2.getValues()[2].mValue.int_value);
- EXPECT_EQ(0x20000, event2.getValues()[2].mField.getField());
- EXPECT_EQ(10, event2.getValues()[3].mValue.long_value);
- EXPECT_EQ(0x30000, event2.getValues()[3].mField.getField());
-
- LogEvent event3(statsLogEventWrapper, 2);
-
- EXPECT_EQ(1, event3.GetTagId());
- EXPECT_EQ(1111L, event3.GetElapsedTimestampNs());
- EXPECT_EQ(2222L, event3.GetLogdTimestampNs());
- EXPECT_EQ(6, event3.size());
- EXPECT_EQ(4, event3.getValues()[0].mValue.int_value);
- EXPECT_EQ(0x2010101, event3.getValues()[0].mField.getField());
- EXPECT_EQ("", event3.getValues()[1].mValue.str_value);
- EXPECT_EQ(0x2010182, event3.getValues()[1].mField.getField());
- EXPECT_EQ(5, event3.getValues()[2].mValue.int_value);
- EXPECT_EQ(0x2010201, event3.getValues()[2].mField.getField());
- EXPECT_EQ("", event3.getValues()[3].mValue.str_value);
- EXPECT_EQ(0x2018282, event3.getValues()[3].mField.getField());
- EXPECT_EQ(6, event3.getValues()[4].mValue.int_value);
- EXPECT_EQ(0x20000, event3.getValues()[4].mField.getField());
- EXPECT_EQ(10, event3.getValues()[5].mValue.long_value);
- EXPECT_EQ(0x30000, event3.getValues()[5].mField.getField());
-}
-
TEST(LogEventTest, TestBinaryFieldAtom) {
Atom launcherAtom;
auto launcher_event = launcherAtom.mutable_launcher_event();
diff --git a/cmds/statsd/tests/storage/StorageManager_test.cpp b/cmds/statsd/tests/storage/StorageManager_test.cpp
index 9e15e99781d6..b91e5a0ad3a1 100644
--- a/cmds/statsd/tests/storage/StorageManager_test.cpp
+++ b/cmds/statsd/tests/storage/StorageManager_test.cpp
@@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+#include <android-base/unique_fd.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <stdio.h>
diff --git a/config/boot-image-profile.txt b/config/boot-image-profile.txt
index 4aaf72728cbf..e649c303541a 100644
--- a/config/boot-image-profile.txt
+++ b/config/boot-image-profile.txt
@@ -1515,7 +1515,6 @@ HSPLandroid/app/IActivityTaskManager$Stub$Proxy;->activityDestroyed(Landroid/os/
HSPLandroid/app/IActivityTaskManager$Stub$Proxy;->activityIdle(Landroid/os/IBinder;Landroid/content/res/Configuration;Z)V
HSPLandroid/app/IActivityTaskManager$Stub$Proxy;->activityPaused(Landroid/os/IBinder;)V
HSPLandroid/app/IActivityTaskManager$Stub$Proxy;->activityResumed(Landroid/os/IBinder;)V
-HSPLandroid/app/IActivityTaskManager$Stub$Proxy;->activitySlept(Landroid/os/IBinder;)V
HSPLandroid/app/IActivityTaskManager$Stub$Proxy;->activityStopped(Landroid/os/IBinder;Landroid/os/Bundle;Landroid/os/PersistableBundle;Ljava/lang/CharSequence;)V
HSPLandroid/app/IActivityTaskManager$Stub$Proxy;->activityTopResumedStateLost()V
HSPLandroid/app/IActivityTaskManager$Stub$Proxy;->finishActivity(Landroid/os/IBinder;ILandroid/content/Intent;I)Z
diff --git a/core/java/Android.bp b/core/java/Android.bp
index 9a8e130436f8..fb27f74211fb 100644
--- a/core/java/Android.bp
+++ b/core/java/Android.bp
@@ -7,8 +7,3 @@ filegroup {
name: "IDropBoxManagerService.aidl",
srcs: ["com/android/internal/os/IDropBoxManagerService.aidl"],
}
-
-filegroup {
- name: "ICarStatsService.aidl",
- srcs: ["com/android/internal/car/ICarStatsService.aidl"],
-}
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index d952be5218a4..d8b5e7f3b5b0 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -45,6 +45,7 @@ import android.content.CursorLoader;
import android.content.IIntentSender;
import android.content.Intent;
import android.content.IntentSender;
+import android.content.LocusId;
import android.content.SharedPreferences;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
@@ -1025,6 +1026,39 @@ public class Activity extends ContextThemeWrapper
mIntent = newIntent;
}
+ /**
+ * Sets the {@link android.content.LocusId} for this activity. The locus id
+ * helps identify different instances of the same {@code Activity} class.
+ * <p> For example, a locus id based on a specific conversation could be set on a
+ * conversation app's chat {@code Activity}. The system can then use this locus id
+ * along with app's contents to provide ranking signals in various UI surfaces
+ * including sharing, notifications, shortcuts and so on.
+ * <p> It is recommended to set the same locus id in the shortcut's locus id using
+ * {@link android.content.pm.ShortcutInfo.Builder#setLocusId(android.content.LocusId)
+ * setLocusId}
+ * so that the system can learn appropriate ranking signals linking the activity's
+ * locus id with the matching shortcut.
+ *
+ * @param locusId a unique, stable id that identifies this {@code Activity} instance from
+ * others. This can be linked to a shortcut using
+ * {@link android.content.pm.ShortcutInfo.Builder#setLocusId(android.content.LocusId)
+ * setLocusId} with the same locus id string.
+ * @param bundle extras set or updated as part of this locus context. This may help provide
+ * additional metadata such as URLs, conversation participants specific to this
+ * {@code Activity}'s context.
+ *
+ * @see android.view.contentcapture.ContentCaptureManager
+ * @see android.view.contentcapture.ContentCaptureContext
+ */
+ public void setLocusContext(@Nullable LocusId locusId, @Nullable Bundle bundle) {
+ try {
+ ActivityManager.getService().setActivityLocusContext(mComponent, locusId, mToken);
+ } catch (RemoteException re) {
+ re.rethrowFromSystemServer();
+ }
+ // TODO(b/147750355): Pass locusId and bundle to the Content Capture.
+ }
+
/** Return the application that owns this activity. */
public final Application getApplication() {
return mApplication;
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index f7c4d96d0d40..e0a4ae287408 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -73,6 +73,7 @@ import android.util.DisplayMetrics;
import android.util.Singleton;
import android.util.Size;
import android.view.IWindowContainer;
+import android.view.Surface;
import com.android.internal.app.LocalePicker;
import com.android.internal.app.procstats.ProcessStats;
@@ -1928,7 +1929,12 @@ public class ActivityManager {
// Top activity in task when snapshot was taken
private final ComponentName mTopActivityComponent;
private final GraphicBuffer mSnapshot;
+ /** Indicates whether task was in landscape or portrait */
+ @Configuration.Orientation
private final int mOrientation;
+ /** See {@link android.view.Surface.Rotation} */
+ @Surface.Rotation
+ private int mRotation;
private final Rect mContentInsets;
// Whether this snapshot is a down-sampled version of the full resolution, used mainly for
// low-ram devices
@@ -1945,7 +1951,7 @@ public class ActivityManager {
public TaskSnapshot(long id,
@NonNull ComponentName topActivityComponent, GraphicBuffer snapshot,
- @NonNull ColorSpace colorSpace, int orientation, Rect contentInsets,
+ @NonNull ColorSpace colorSpace, int orientation, int rotation, Rect contentInsets,
boolean reducedResolution, float scale, boolean isRealSnapshot, int windowingMode,
int systemUiVisibility, boolean isTranslucent) {
mId = id;
@@ -1954,6 +1960,7 @@ public class ActivityManager {
mColorSpace = colorSpace.getId() < 0
? ColorSpace.get(ColorSpace.Named.SRGB) : colorSpace;
mOrientation = orientation;
+ mRotation = rotation;
mContentInsets = new Rect(contentInsets);
mReducedResolution = reducedResolution;
mScale = scale;
@@ -1972,6 +1979,7 @@ public class ActivityManager {
? ColorSpace.get(ColorSpace.Named.values()[colorSpaceId])
: ColorSpace.get(ColorSpace.Named.SRGB);
mOrientation = source.readInt();
+ mRotation = source.readInt();
mContentInsets = source.readParcelable(null /* classLoader */);
mReducedResolution = source.readBoolean();
mScale = source.readFloat();
@@ -2019,6 +2027,13 @@ public class ActivityManager {
}
/**
+ * @return The screen rotation the screenshot was taken in.
+ */
+ public int getRotation() {
+ return mRotation;
+ }
+
+ /**
* @return The system/content insets on the snapshot. These can be clipped off in order to
* remove any areas behind system bars in the snapshot.
*/
@@ -2087,6 +2102,7 @@ public class ActivityManager {
dest.writeParcelable(mSnapshot, 0);
dest.writeInt(mColorSpace.getId());
dest.writeInt(mOrientation);
+ dest.writeInt(mRotation);
dest.writeParcelable(mContentInsets, 0);
dest.writeBoolean(mReducedResolution);
dest.writeFloat(mScale);
@@ -2106,6 +2122,7 @@ public class ActivityManager {
+ " mSnapshot=" + mSnapshot + " (" + width + "x" + height + ")"
+ " mColorSpace=" + mColorSpace.toString()
+ " mOrientation=" + mOrientation
+ + " mRotation=" + mRotation
+ " mContentInsets=" + mContentInsets.toShortString()
+ " mReducedResolution=" + mReducedResolution + " mScale=" + mScale
+ " mIsRealSnapshot=" + mIsRealSnapshot + " mWindowingMode=" + mWindowingMode
@@ -2129,6 +2146,7 @@ public class ActivityManager {
private GraphicBuffer mSnapshot;
private ColorSpace mColorSpace;
private int mOrientation;
+ private int mRotation;
private Rect mContentInsets;
private boolean mReducedResolution;
private float mScaleFraction;
@@ -2163,6 +2181,11 @@ public class ActivityManager {
return this;
}
+ public Builder setRotation(int rotation) {
+ mRotation = rotation;
+ return this;
+ }
+
public Builder setContentInsets(Rect contentInsets) {
mContentInsets = contentInsets;
return this;
@@ -2218,6 +2241,7 @@ public class ActivityManager {
mSnapshot,
mColorSpace,
mOrientation,
+ mRotation,
mContentInsets,
mReducedResolution,
mScaleFraction,
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 4f3e8ec9fb51..3903856f5aec 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -367,4 +367,14 @@ public abstract class ActivityManagerInternal {
* Unregisters the specified {@code processObserver}.
*/
public abstract void unregisterProcessObserver(IProcessObserver processObserver);
+
+ /**
+ * Checks if there is an unfinished instrumentation that targets the given uid.
+ *
+ * @param uid The uid to be checked for
+ *
+ * @return True, if there is an instrumentation whose target application uid matches the given
+ * uid, false otherwise
+ */
+ public abstract boolean isUidCurrentlyInstrumented(int uid);
}
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 2ca5b1d5c76f..d90e81f09800 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -111,6 +111,8 @@ import android.os.Process;
import android.os.RemoteCallback;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.StatsFrameworkInitializer;
+import android.os.StatsServiceManager;
import android.os.StrictMode;
import android.os.SystemClock;
import android.os.SystemProperties;
@@ -924,10 +926,6 @@ public final class ActivityThread extends ClientTransactionHandler {
private class ApplicationThread extends IApplicationThread.Stub {
private static final String DB_INFO_FORMAT = " %8s %8s %14s %14s %s";
- public final void scheduleSleeping(IBinder token, boolean sleeping) {
- sendMessage(H.SLEEPING, token, sleeping ? 1 : 0);
- }
-
public final void scheduleReceiver(Intent intent, ActivityInfo info,
CompatibilityInfo compatInfo, int resultCode, String data, Bundle extras,
boolean sync, int sendingUser, int processState) {
@@ -1855,7 +1853,6 @@ public final class ActivityThread extends ClientTransactionHandler {
case SCHEDULE_CRASH: return "SCHEDULE_CRASH";
case DUMP_HEAP: return "DUMP_HEAP";
case DUMP_ACTIVITY: return "DUMP_ACTIVITY";
- case SLEEPING: return "SLEEPING";
case SET_CORE_SETTINGS: return "SET_CORE_SETTINGS";
case UPDATE_PACKAGE_COMPATIBILITY_INFO: return "UPDATE_PACKAGE_COMPATIBILITY_INFO";
case DUMP_PROVIDER: return "DUMP_PROVIDER";
@@ -1985,11 +1982,6 @@ public final class ActivityThread extends ClientTransactionHandler {
case DUMP_PROVIDER:
handleDumpProvider((DumpComponentInfo)msg.obj);
break;
- case SLEEPING:
- Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "sleeping");
- handleSleeping((IBinder)msg.obj, msg.arg1 != 0);
- Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
- break;
case SET_CORE_SETTINGS:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "setCoreSettings");
handleSetCoreSettings((Bundle) msg.obj);
@@ -4855,41 +4847,6 @@ public final class ActivityThread extends ClientTransactionHandler {
}
}
- // TODO: This method should be changed to use {@link #performStopActivityInner} to perform to
- // stop operation on the activity to reduce code duplication and the chance of fixing a bug in
- // one place and missing the other.
- private void handleSleeping(IBinder token, boolean sleeping) {
- ActivityClientRecord r = mActivities.get(token);
-
- if (r == null) {
- Log.w(TAG, "handleSleeping: no activity for token " + token);
- return;
- }
-
- if (sleeping) {
- if (!r.stopped && !r.isPreHoneycomb()) {
- callActivityOnStop(r, true /* saveState */, "sleeping");
- }
-
- // Make sure any pending writes are now committed.
- if (!r.isPreHoneycomb()) {
- QueuedWork.waitToFinish();
- }
-
- // Tell activity manager we slept.
- try {
- ActivityTaskManager.getService().activitySlept(r.token);
- } catch (RemoteException ex) {
- throw ex.rethrowFromSystemServer();
- }
- } else {
- if (r.stopped && r.activity.mVisibleFromServer) {
- r.activity.performRestart(true /* start */, "handleSleeping");
- r.setState(ON_START);
- }
- }
- }
-
private void handleSetCoreSettings(Bundle coreSettings) {
synchronized (mResourcesManager) {
mCoreSettings = coreSettings;
@@ -7523,6 +7480,7 @@ public final class ActivityThread extends ClientTransactionHandler {
*/
public static void initializeMainlineModules() {
TelephonyFrameworkInitializer.setTelephonyServiceManager(new TelephonyServiceManager());
+ StatsFrameworkInitializer.setStatsServiceManager(new StatsServiceManager());
}
private void purgePendingResources() {
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 4e6319db97f4..c09aa1ff05a8 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -131,7 +131,7 @@ public class ApplicationPackageManager extends PackageManager {
private static final int DEFAULT_EPHEMERAL_COOKIE_MAX_SIZE_BYTES = 16384; // 16KB
// Default flags to use with PackageManager when no flags are given.
- private final static int sDefaultFlags = PackageManager.GET_SHARED_LIBRARY_FILES;
+ private static final int sDefaultFlags = GET_SHARED_LIBRARY_FILES;
// Name of the resource which provides background permission button string
public static final String APP_PERMISSION_BUTTON_ALLOW_ALWAYS =
@@ -907,7 +907,7 @@ public class ApplicationPackageManager extends PackageManager {
@Override
public boolean hasSigningCertificate(
- String packageName, byte[] certificate, @PackageManager.CertificateInputType int type) {
+ String packageName, byte[] certificate, @CertificateInputType int type) {
try {
return mPM.hasSigningCertificate(packageName, certificate, type);
} catch (RemoteException e) {
@@ -917,7 +917,7 @@ public class ApplicationPackageManager extends PackageManager {
@Override
public boolean hasSigningCertificate(
- int uid, byte[] certificate, @PackageManager.CertificateInputType int type) {
+ int uid, byte[] certificate, @CertificateInputType int type) {
try {
return mPM.hasUidSigningCertificate(uid, certificate, type);
} catch (RemoteException e) {
@@ -1464,8 +1464,7 @@ public class ApplicationPackageManager extends PackageManager {
return getActivityIcon(intent.getComponent());
}
- ResolveInfo info = resolveActivity(
- intent, PackageManager.MATCH_DEFAULT_ONLY);
+ ResolveInfo info = resolveActivity(intent, MATCH_DEFAULT_ONLY);
if (info != null) {
return info.activityInfo.loadIcon(this);
}
@@ -1500,7 +1499,7 @@ public class ApplicationPackageManager extends PackageManager {
}
ResolveInfo info = resolveActivity(
- intent, PackageManager.MATCH_DEFAULT_ONLY);
+ intent, MATCH_DEFAULT_ONLY);
if (info != null) {
return info.activityInfo.loadBanner(this);
}
@@ -1532,8 +1531,7 @@ public class ApplicationPackageManager extends PackageManager {
return getActivityLogo(intent.getComponent());
}
- ResolveInfo info = resolveActivity(
- intent, PackageManager.MATCH_DEFAULT_ONLY);
+ ResolveInfo info = resolveActivity(intent, MATCH_DEFAULT_ONLY);
if (info != null) {
return info.activityInfo.loadLogo(this);
}
@@ -2017,7 +2015,7 @@ public class ApplicationPackageManager extends PackageManager {
@Override
public int installExistingPackage(String packageName) throws NameNotFoundException {
- return installExistingPackage(packageName, PackageManager.INSTALL_REASON_UNKNOWN);
+ return installExistingPackage(packageName, INSTALL_REASON_UNKNOWN);
}
@Override
@@ -2029,7 +2027,7 @@ public class ApplicationPackageManager extends PackageManager {
@Override
public int installExistingPackageAsUser(String packageName, int userId)
throws NameNotFoundException {
- return installExistingPackageAsUser(packageName, PackageManager.INSTALL_REASON_UNKNOWN,
+ return installExistingPackageAsUser(packageName, INSTALL_REASON_UNKNOWN,
userId);
}
@@ -2404,7 +2402,7 @@ public class ApplicationPackageManager extends PackageManager {
public void deletePackageAsUser(String packageName, IPackageDeleteObserver observer,
int flags, int userId) {
try {
- mPM.deletePackageAsUser(packageName, PackageManager.VERSION_CODE_HIGHEST,
+ mPM.deletePackageAsUser(packageName, VERSION_CODE_HIGHEST,
observer, userId, flags);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -2651,11 +2649,11 @@ public class ApplicationPackageManager extends PackageManager {
public void setSyntheticAppDetailsActivityEnabled(String packageName, boolean enabled) {
try {
ComponentName componentName = new ComponentName(packageName,
- PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME);
+ APP_DETAILS_ACTIVITY_CLASS_NAME);
mPM.setComponentEnabledSetting(componentName, enabled
- ? PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
- : PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
- PackageManager.DONT_KILL_APP, getUserId());
+ ? COMPONENT_ENABLED_STATE_DEFAULT
+ : COMPONENT_ENABLED_STATE_DISABLED,
+ DONT_KILL_APP, getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -2665,10 +2663,10 @@ public class ApplicationPackageManager extends PackageManager {
public boolean getSyntheticAppDetailsActivityEnabled(String packageName) {
try {
ComponentName componentName = new ComponentName(packageName,
- PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME);
+ APP_DETAILS_ACTIVITY_CLASS_NAME);
int state = mPM.getComponentEnabledSetting(componentName, getUserId());
- return state == PackageManager.COMPONENT_ENABLED_STATE_ENABLED
- || state == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
+ return state == COMPONENT_ENABLED_STATE_ENABLED
+ || state == COMPONENT_ENABLED_STATE_DEFAULT;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -2744,6 +2742,30 @@ public class ApplicationPackageManager extends PackageManager {
/** @hide */
@Override
+ public void setSystemAppState(String packageName, @SystemAppState int state) {
+ try {
+ switch (state) {
+ case SYSTEM_APP_STATE_HIDDEN_UNTIL_INSTALLED_HIDDEN:
+ mPM.setSystemAppHiddenUntilInstalled(packageName, true);
+ break;
+ case SYSTEM_APP_STATE_HIDDEN_UNTIL_INSTALLED_VISIBLE:
+ mPM.setSystemAppHiddenUntilInstalled(packageName, false);
+ break;
+ case SYSTEM_APP_STATE_INSTALLED:
+ mPM.setSystemAppInstallState(packageName, true, getUserId());
+ break;
+ case SYSTEM_APP_STATE_UNINSTALLED:
+ mPM.setSystemAppInstallState(packageName, false, getUserId());
+ break;
+ default:
+ }
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /** @hide */
+ @Override
public KeySet getKeySetByAlias(String packageName, String alias) {
Objects.requireNonNull(packageName);
Objects.requireNonNull(alias);
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 7d04ca0afe7e..3ffd7c70b40d 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -53,6 +53,7 @@ import android.content.pm.ParceledListSlice;
import android.content.pm.ProviderInfo;
import android.content.pm.UserInfo;
import android.content.res.Configuration;
+import android.content.LocusId;
import android.graphics.Bitmap;
import android.graphics.GraphicBuffer;
import android.graphics.Point;
@@ -637,4 +638,13 @@ interface IActivityManager {
* and the given process is imperceptible.
*/
void killProcessesWhenImperceptible(in int[] pids, String reason);
+
+ /**
+ * Set locus context for a given activity.
+ * @param activity
+ * @param locusId a unique, stable id that identifies this activity instance from others.
+ * @param appToken ActivityRecord's appToken.
+ */
+ void setActivityLocusContext(in ComponentName activity, in LocusId locusId,
+ in IBinder appToken);
}
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index e5c046c2376c..85fa7c1cdb54 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -141,7 +141,6 @@ interface IActivityTaskManager {
in PersistableBundle persistentState, in CharSequence description);
oneway void activityDestroyed(in IBinder token);
void activityRelaunched(in IBinder token);
- oneway void activitySlept(in IBinder token);
int getFrontActivityScreenCompatMode();
void setFrontActivityScreenCompatMode(int mode);
String getCallingPackage(in IBinder token);
diff --git a/core/java/android/app/IApplicationThread.aidl b/core/java/android/app/IApplicationThread.aidl
index 51a64fff7c45..c33c515f062c 100644
--- a/core/java/android/app/IApplicationThread.aidl
+++ b/core/java/android/app/IApplicationThread.aidl
@@ -91,7 +91,6 @@ oneway interface IApplicationThread {
int resultCode, in String data, in Bundle extras, boolean ordered,
boolean sticky, int sendingUser, int processState);
void scheduleLowMemory();
- void scheduleSleeping(IBinder token, boolean sleeping);
void profilerControl(boolean start, in ProfilerInfo profilerInfo, int profileType);
void setSchedulingGroup(int group);
void scheduleCreateBackupAgent(in ApplicationInfo app, in CompatibilityInfo compatInfo,
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 7f698653bef7..f170b5dfbc27 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -122,6 +122,7 @@ import android.net.NetworkScoreManager;
import android.net.NetworkWatchlistManager;
import android.net.TestNetworkManager;
import android.net.TetheringManager;
+import android.net.VpnManager;
import android.net.lowpan.ILowpanManager;
import android.net.lowpan.LowpanManager;
import android.net.nsd.INsdManager;
@@ -150,6 +151,7 @@ import android.os.RecoverySystem;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.ServiceManager.ServiceNotFoundException;
+import android.os.StatsFrameworkInitializer;
import android.os.SystemConfigManager;
import android.os.SystemUpdateManager;
import android.os.SystemVibrator;
@@ -378,6 +380,15 @@ public final class SystemServiceRegistry {
return new IpSecManager(ctx, service);
}});
+ registerService(Context.VPN_MANAGEMENT_SERVICE, VpnManager.class,
+ new CachedServiceFetcher<VpnManager>() {
+ @Override
+ public VpnManager createService(ContextImpl ctx) throws ServiceNotFoundException {
+ IBinder b = ServiceManager.getService(Context.CONNECTIVITY_SERVICE);
+ IConnectivityManager service = IConnectivityManager.Stub.asInterface(b);
+ return new VpnManager(ctx, service);
+ }});
+
registerService(Context.CONNECTIVITY_DIAGNOSTICS_SERVICE,
ConnectivityDiagnosticsManager.class,
new CachedServiceFetcher<ConnectivityDiagnosticsManager>() {
@@ -601,13 +612,6 @@ public final class SystemServiceRegistry {
return SensorPrivacyManager.getInstance(ctx);
}});
- registerService(Context.STATS_MANAGER, StatsManager.class,
- new CachedServiceFetcher<StatsManager>() {
- @Override
- public StatsManager createService(ContextImpl ctx) {
- return new StatsManager(ctx.getOuterContext());
- }});
-
registerService(Context.STATUS_BAR_SERVICE, StatusBarManager.class,
new CachedServiceFetcher<StatusBarManager>() {
@Override
@@ -1327,6 +1331,7 @@ public final class SystemServiceRegistry {
TelephonyFrameworkInitializer.registerServiceWrappers();
AppSearchManagerFrameworkInitializer.initialize();
WifiFrameworkInitializer.registerServiceWrappers();
+ StatsFrameworkInitializer.registerServiceWrappers();
} finally {
// If any of the above code throws, we're in a pretty bad shape and the process
// will likely crash, but we'll reset it just in case there's an exception handler...
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 62557dca9e7e..d1b5a83e7142 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -2403,11 +2403,19 @@ public class DevicePolicyManager {
public static final int PERSONAL_APPS_SUSPENDED_EXPLICITLY = 1 << 0;
/**
+ * Flag for {@link #getPersonalAppsSuspendedReasons} return value. Set when personal apps are
+ * suspended by framework because managed profile was off for longer than allowed by policy.
+ * @see #setManagedProfileMaximumTimeOff
+ */
+ public static final int PERSONAL_APPS_SUSPENDED_PROFILE_TIMEOUT = 1 << 1;
+
+ /**
* @hide
*/
@IntDef(flag = true, prefix = { "PERSONAL_APPS_" }, value = {
PERSONAL_APPS_NOT_SUSPENDED,
- PERSONAL_APPS_SUSPENDED_EXPLICITLY
+ PERSONAL_APPS_SUSPENDED_EXPLICITLY,
+ PERSONAL_APPS_SUSPENDED_PROFILE_TIMEOUT
})
@Retention(RetentionPolicy.SOURCE)
public @interface PersonalAppSuspensionReason {}
@@ -8847,6 +8855,49 @@ public class DevicePolicyManager {
}
/**
+ * Called by device owners to request a location provider to change its allowed state. For a
+ * provider to be enabled requires both that the master location setting is enabled, and that
+ * the provider itself is allowed. Most location providers are always allowed. Some location
+ * providers may have user consents or terms and conditions that must be accepted, or some other
+ * type of blocker before they are allowed however. Every location provider is responsible for
+ * its own allowed state.
+ *
+ * <p>This method requests that a location provider change its allowed state. For providers that
+ * are always allowed and have no state to change, this will have no effect. If the provider
+ * does require some consent, terms and conditions, or other blocking state, using this API
+ * implies that the device owner is agreeing/disagreeing to any consents, terms and conditions,
+ * etc, and the provider should make a best effort to adjust it's allowed state accordingly.
+ *
+ * <p>Location providers are generally only responsible for the current user, and callers must
+ * assume that this method will only affect provider state for the current user. Callers are
+ * responsible for tracking current user changes and re-updating provider state as necessary.
+ *
+ * <p>While providers are expected to make a best effort to honor this request, it is not a
+ * given that all providers will support such a request. If a provider does change its state as
+ * a result of this request, that may happen asynchronously after some delay. Test location
+ * providers set through {@link android.location.LocationManager#addTestProvider} will respond
+ * to this request to aide in testing.
+ *
+ * @param admin Which {@link DeviceAdminReceiver} this request is associated with
+ * @param provider A location provider as listed by
+ * {@link android.location.LocationManager#getAllProviders()}
+ * @param providerAllowed Whether the location provider is being requested to enable or disable
+ * itself
+ * @throws SecurityException if {@code admin} is not a device owner.
+ */
+ public void requestSetLocationProviderAllowed(@NonNull ComponentName admin,
+ @NonNull String provider, boolean providerAllowed) {
+ throwIfParentInstance("requestSetLocationProviderAllowed");
+ if (mService != null) {
+ try {
+ mService.requestSetLocationProviderAllowed(admin, provider, providerAllowed);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ /**
* Called by profile or device owners to update {@link android.provider.Settings.Secure}
* settings. Validation that the value of the setting is in the correct form for the setting
* type should be performed by the caller.
@@ -9109,7 +9160,8 @@ public class DevicePolicyManager {
}
/**
- * Called by device owners to set a local system update policy. When a new policy is set,
+ * Called by device owners or profile owners of an organization-owned managed profile to to set
+ * a local system update policy. When a new policy is set,
* {@link #ACTION_SYSTEM_UPDATE_POLICY_CHANGED} is broadcasted.
* <p>
* If the supplied system update policy has freeze periods set but the freeze periods do not
@@ -9127,7 +9179,8 @@ public class DevicePolicyManager {
* components in the device owner package can set system update policies and the most
* recent policy takes effect.
* @param policy the new policy, or {@code null} to clear the current policy.
- * @throws SecurityException if {@code admin} is not a device owner.
+ * @throws SecurityException if {@code admin} is not a device owner or a profile owner of an
+ * organization-owned managed profile.
* @throws IllegalArgumentException if the policy type or maintenance window is not valid.
* @throws SystemUpdatePolicy.ValidationFailedException if the policy's freeze period does not
* meet the requirement.
@@ -11212,7 +11265,8 @@ public class DevicePolicyManager {
}
/**
- * Called by device owner to install a system update from the given file. The device will be
+ * Called by device owner or profile owner of an organization-owned managed profile to install
+ * a system update from the given file. The device will be
* rebooted in order to finish installing the update. Note that if the device is rebooted, this
* doesn't necessarily mean that the update has been applied successfully. The caller should
* additionally check the system version with {@link android.os.Build#FINGERPRINT} or {@link
@@ -11760,6 +11814,8 @@ public class DevicePolicyManager {
*
* @param admin Which {@link DeviceAdminReceiver} this request is associated with
* @param suspended Whether personal apps should be suspended.
+ * @throws IllegalStateException if the profile owner doesn't have an activity that handles
+ * {@link #ACTION_CHECK_POLICY_COMPLIANCE}
*/
public void setPersonalAppsSuspended(@NonNull ComponentName admin, boolean suspended) {
throwIfParentInstance("setPersonalAppsSuspended");
@@ -11771,4 +11827,52 @@ public class DevicePolicyManager {
}
}
}
+
+ /**
+ * Called by a profile owner of an organization-owned managed profile to set maximum time
+ * the profile is allowed to be turned off. If the profile is turned off for longer, personal
+ * apps are suspended on the device.
+ *
+ * <p>When personal apps are suspended, an ongoing notification about that is shown to the user.
+ * When the user taps the notification, system invokes {@link #ACTION_CHECK_POLICY_COMPLIANCE}
+ * in the profile owner package. Profile owner implementation that uses personal apps suspension
+ * must handle this intent.
+ *
+ * @param admin Which {@link DeviceAdminReceiver} this request is associated with
+ * @param timeoutMs Maximum time the profile is allowed to be off in milliseconds or 0 if
+ * not limited.
+ * @throws IllegalStateException if the profile owner doesn't have an activity that handles
+ * {@link #ACTION_CHECK_POLICY_COMPLIANCE}
+ * @see #setPersonalAppsSuspended
+ */
+ public void setManagedProfileMaximumTimeOff(@NonNull ComponentName admin, long timeoutMs) {
+ throwIfParentInstance("setManagedProfileMaximumTimeOff");
+ if (mService != null) {
+ try {
+ mService.setManagedProfileMaximumTimeOff(admin, timeoutMs);
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ /**
+ * Called by a profile owner of an organization-owned managed profile to get maximum time
+ * the profile is allowed to be turned off.
+ *
+ * @param admin Which {@link DeviceAdminReceiver} this request is associated with
+ * @return Maximum time the profile is allowed to be off in milliseconds or 0 if not limited.
+ * @see #setPersonalAppsSuspended
+ */
+ public long getManagedProfileMaximumTimeOff(@NonNull ComponentName admin) {
+ throwIfParentInstance("getManagedProfileMaximumTimeOff");
+ if (mService != null) {
+ try {
+ return mService.getManagedProfileMaximumTimeOff(admin);
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+ return 0;
+ }
}
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 3d6bf9db5535..e3dba310ab44 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -270,6 +270,7 @@ interface IDevicePolicyManager {
boolean isLockdownAdminConfiguredNetworks(in ComponentName who);
void setLocationEnabled(in ComponentName who, boolean locationEnabled);
+ void requestSetLocationProviderAllowed(in ComponentName who, in String provider, boolean providerAllowed);
boolean setTime(in ComponentName who, long millis);
boolean setTimeZone(in ComponentName who, String timeZone);
@@ -473,4 +474,7 @@ interface IDevicePolicyManager {
int getPersonalAppsSuspendedReasons(in ComponentName admin);
void setPersonalAppsSuspended(in ComponentName admin, boolean suspended);
+
+ long getManagedProfileMaximumTimeOff(in ComponentName admin);
+ void setManagedProfileMaximumTimeOff(in ComponentName admin, long timeoutMs);
}
diff --git a/core/java/android/app/usage/UsageEvents.java b/core/java/android/app/usage/UsageEvents.java
index 6ab880dfb36d..ab71e73fd58c 100644
--- a/core/java/android/app/usage/UsageEvents.java
+++ b/core/java/android/app/usage/UsageEvents.java
@@ -290,10 +290,16 @@ public final class UsageEvents implements Parcelable {
public static final int USER_STOPPED = 29;
/**
+ * An event type denoting that new locusId has been set for a given activity.
+ * @hide
+ */
+ public static final int LOCUS_ID_SET = 30;
+
+ /**
* Keep in sync with the greatest event type value.
* @hide
*/
- public static final int MAX_EVENT_TYPE = 29;
+ public static final int MAX_EVENT_TYPE = 30;
/** @hide */
public static final int FLAG_IS_PACKAGE_INSTANT_APP = 1 << 0;
@@ -436,6 +442,18 @@ public final class UsageEvents implements Parcelable {
*/
public int mNotificationChannelIdToken = UNASSIGNED_TOKEN;
+ /**
+ * LocusId.
+ * Currently LocusId only present for {@link #LOCUS_ID_SET} event types.
+ * {@hide}
+ */
+ public String mLocusId;
+
+ /**
+ * {@hide}
+ */
+ public int mLocusIdToken = UNASSIGNED_TOKEN;
+
/** @hide */
@EventFlags
public int mFlags;
@@ -609,6 +627,16 @@ public final class UsageEvents implements Parcelable {
return ret;
}
+ /**
+ * Returns the locusId for this event if the event is of type {@link #LOCUS_ID_SET},
+ * otherwise it returns null.
+ * @hide
+ */
+ @Nullable
+ public String getLocusId() {
+ return mLocusId;
+ }
+
private void copyFrom(Event orig) {
mPackage = orig.mPackage;
mClass = orig.mClass;
@@ -625,6 +653,7 @@ public final class UsageEvents implements Parcelable {
mFlags = orig.mFlags;
mBucketAndReason = orig.mBucketAndReason;
mNotificationChannelId = orig.mNotificationChannelId;
+ mLocusId = orig.mLocusId;
}
}
@@ -823,6 +852,9 @@ public final class UsageEvents implements Parcelable {
case Event.NOTIFICATION_INTERRUPTION:
p.writeString(event.mNotificationChannelId);
break;
+ case Event.LOCUS_ID_SET:
+ p.writeString(event.mLocusId);
+ break;
}
p.writeInt(event.mFlags);
}
@@ -871,6 +903,7 @@ public final class UsageEvents implements Parcelable {
eventOut.mContentType = null;
eventOut.mContentAnnotations = null;
eventOut.mNotificationChannelId = null;
+ eventOut.mLocusId = null;
switch (eventOut.mEventType) {
case Event.CONFIGURATION_CHANGE:
@@ -891,6 +924,9 @@ public final class UsageEvents implements Parcelable {
case Event.NOTIFICATION_INTERRUPTION:
eventOut.mNotificationChannelId = p.readString();
break;
+ case Event.LOCUS_ID_SET:
+ eventOut.mLocusId = p.readString();
+ break;
}
eventOut.mFlags = p.readInt();
}
diff --git a/core/java/android/bluetooth/BluetoothA2dpSink.java b/core/java/android/bluetooth/BluetoothA2dpSink.java
index 8993de0939e6..ee2cc6d14712 100755
--- a/core/java/android/bluetooth/BluetoothA2dpSink.java
+++ b/core/java/android/bluetooth/BluetoothA2dpSink.java
@@ -297,7 +297,8 @@ public final class BluetoothA2dpSink implements BluetoothProfile {
*/
@SystemApi
@RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
- public boolean setConnectionPolicy(@Nullable BluetoothDevice device, int connectionPolicy) {
+ public boolean setConnectionPolicy(@Nullable BluetoothDevice device,
+ @ConnectionPolicy int connectionPolicy) {
if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
final IBluetoothA2dpSink service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
@@ -345,7 +346,7 @@ public final class BluetoothA2dpSink implements BluetoothProfile {
*/
@SystemApi
@RequiresPermission(Manifest.permission.BLUETOOTH)
- public int getConnectionPolicy(@Nullable BluetoothDevice device) {
+ public @ConnectionPolicy int getConnectionPolicy(@Nullable BluetoothDevice device) {
if (VDBG) log("getConnectionPolicy(" + device + ")");
final IBluetoothA2dpSink service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index e7513544a886..e24b0403cd6a 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -3236,18 +3236,6 @@ public final class BluetoothAdapter {
}
/**
- * TODO: Remove this hidden method once all the SL4A and other tests are updated to use the new
- * API name, listenUsingL2capChannel.
- * @hide
- */
- @RequiresPermission(Manifest.permission.BLUETOOTH)
- public BluetoothServerSocket listenUsingL2capCoc(int transport)
- throws IOException {
- Log.e(TAG, "listenUsingL2capCoc: PLEASE USE THE OFFICIAL API, listenUsingL2capChannel");
- return listenUsingL2capChannel();
- }
-
- /**
* Create an insecure L2CAP Connection-oriented Channel (CoC) {@link BluetoothServerSocket} and
* assign a dynamic PSM value. This socket can be used to listen for incoming connections. The
* supported Bluetooth transport is LE only.
@@ -3294,19 +3282,6 @@ public final class BluetoothAdapter {
}
/**
- * TODO: Remove this hidden method once all the SL4A and other tests are updated to use the new
- * API name, listenUsingInsecureL2capChannel.
- * @hide
- */
- @RequiresPermission(Manifest.permission.BLUETOOTH)
- public BluetoothServerSocket listenUsingInsecureL2capCoc(int transport)
- throws IOException {
- Log.e(TAG, "listenUsingInsecureL2capCoc: PLEASE USE THE OFFICIAL API, "
- + "listenUsingInsecureL2capChannel");
- return listenUsingInsecureL2capChannel();
- }
-
- /**
* Register a {@link #OnMetadataChangedListener} to receive update about metadata
* changes for this {@link BluetoothDevice}.
* Registration must be done when Bluetooth is ON and will last until
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index 9fe4dd66b874..12dc814c3416 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -2172,17 +2172,6 @@ public final class BluetoothDevice implements Parcelable {
}
/**
- * TODO: Remove this hidden method once all the SL4A and other tests are updated to use the new
- * API name, createL2capChannel.
- * @hide
- */
- @RequiresPermission(Manifest.permission.BLUETOOTH)
- public BluetoothSocket createL2capCocSocket(int transport, int psm) throws IOException {
- Log.e(TAG, "createL2capCocSocket: PLEASE USE THE OFFICIAL API, createL2capChannel");
- return createL2capChannel(psm);
- }
-
- /**
* Create a Bluetooth L2CAP Connection-oriented Channel (CoC) {@link BluetoothSocket} that can
* be used to start a secure outgoing connection to the remote device with the same dynamic
* protocol/service multiplexer (PSM) value. The supported Bluetooth transport is LE only.
@@ -2213,17 +2202,6 @@ public final class BluetoothDevice implements Parcelable {
}
/**
- * TODO: Remove this hidden method once all the SL4A and other tests are updated to use the new
- * API name, createInsecureL2capChannel.
- * @hide
- */
- @RequiresPermission(Manifest.permission.BLUETOOTH)
- public BluetoothSocket createInsecureL2capCocSocket(int transport, int psm) throws IOException {
- Log.e(TAG, "createL2capCocSocket: PLEASE USE THE OFFICIAL API, createInsecureL2capChannel");
- return createInsecureL2capChannel(psm);
- }
-
- /**
* Set a keyed metadata of this {@link BluetoothDevice} to a
* {@link String} value.
* Only bonded devices's metadata will be persisted across Bluetooth
diff --git a/core/java/android/bluetooth/BluetoothHeadsetClient.java b/core/java/android/bluetooth/BluetoothHeadsetClient.java
index 6de1ffb6344e..fbda9e9d6dd7 100644
--- a/core/java/android/bluetooth/BluetoothHeadsetClient.java
+++ b/core/java/android/bluetooth/BluetoothHeadsetClient.java
@@ -584,7 +584,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
*/
@SystemApi
@RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
- public boolean setConnectionPolicy(BluetoothDevice device, int connectionPolicy) {
+ public boolean setConnectionPolicy(BluetoothDevice device,
+ @ConnectionPolicy int connectionPolicy) {
if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
final IBluetoothHeadsetClient service =
getService();
@@ -633,7 +634,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
*/
@SystemApi
@RequiresPermission(Manifest.permission.BLUETOOTH)
- public int getConnectionPolicy(BluetoothDevice device) {
+ public @ConnectionPolicy int getConnectionPolicy(BluetoothDevice device) {
if (VDBG) log("getConnectionPolicy(" + device + ")");
final IBluetoothHeadsetClient service =
getService();
diff --git a/core/java/android/bluetooth/BluetoothHidHost.java b/core/java/android/bluetooth/BluetoothHidHost.java
index c1233b800a51..26e3e271bffa 100644
--- a/core/java/android/bluetooth/BluetoothHidHost.java
+++ b/core/java/android/bluetooth/BluetoothHidHost.java
@@ -416,7 +416,8 @@ public final class BluetoothHidHost implements BluetoothProfile {
*/
@SystemApi
@RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
- public boolean setConnectionPolicy(@Nullable BluetoothDevice device, int connectionPolicy) {
+ public boolean setConnectionPolicy(@Nullable BluetoothDevice device,
+ @ConnectionPolicy int connectionPolicy) {
if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
final IBluetoothHidHost service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
@@ -464,7 +465,7 @@ public final class BluetoothHidHost implements BluetoothProfile {
*/
@SystemApi
@RequiresPermission(Manifest.permission.BLUETOOTH)
- public int getConnectionPolicy(@Nullable BluetoothDevice device) {
+ public @ConnectionPolicy int getConnectionPolicy(@Nullable BluetoothDevice device) {
if (VDBG) log("getConnectionPolicy(" + device + ")");
final IBluetoothHidHost service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
diff --git a/core/java/android/bluetooth/BluetoothMap.java b/core/java/android/bluetooth/BluetoothMap.java
index 467470674286..1c62faa97ee6 100644
--- a/core/java/android/bluetooth/BluetoothMap.java
+++ b/core/java/android/bluetooth/BluetoothMap.java
@@ -340,7 +340,8 @@ public final class BluetoothMap implements BluetoothProfile {
*/
@SystemApi
@RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
- public boolean setConnectionPolicy(@Nullable BluetoothDevice device, int connectionPolicy) {
+ public boolean setConnectionPolicy(@Nullable BluetoothDevice device,
+ @ConnectionPolicy int connectionPolicy) {
if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
final IBluetoothMap service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
@@ -388,7 +389,7 @@ public final class BluetoothMap implements BluetoothProfile {
*/
@SystemApi
@RequiresPermission(Manifest.permission.BLUETOOTH)
- public int getConnectionPolicy(@Nullable BluetoothDevice device) {
+ public @ConnectionPolicy int getConnectionPolicy(@Nullable BluetoothDevice device) {
if (VDBG) log("getConnectionPolicy(" + device + ")");
final IBluetoothMap service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
diff --git a/core/java/android/bluetooth/BluetoothMapClient.java b/core/java/android/bluetooth/BluetoothMapClient.java
index 0aa5aac5d8f6..8d2aaddd38d2 100644
--- a/core/java/android/bluetooth/BluetoothMapClient.java
+++ b/core/java/android/bluetooth/BluetoothMapClient.java
@@ -271,7 +271,8 @@ public final class BluetoothMapClient implements BluetoothProfile {
*/
@SystemApi
@RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
- public boolean setConnectionPolicy(BluetoothDevice device, int connectionPolicy) {
+ public boolean setConnectionPolicy(BluetoothDevice device,
+ @ConnectionPolicy int connectionPolicy) {
if (DBG) Log.d(TAG, "setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
final IBluetoothMapClient service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
@@ -319,7 +320,7 @@ public final class BluetoothMapClient implements BluetoothProfile {
*/
@SystemApi
@RequiresPermission(Manifest.permission.BLUETOOTH)
- public int getConnectionPolicy(BluetoothDevice device) {
+ public @ConnectionPolicy int getConnectionPolicy(BluetoothDevice device) {
if (VDBG) Log.d(TAG, "getConnectionPolicy(" + device + ")");
final IBluetoothMapClient service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
diff --git a/core/java/android/bluetooth/BluetoothPbapClient.java b/core/java/android/bluetooth/BluetoothPbapClient.java
index 9618ba01572e..9563c68ce657 100644
--- a/core/java/android/bluetooth/BluetoothPbapClient.java
+++ b/core/java/android/bluetooth/BluetoothPbapClient.java
@@ -271,7 +271,8 @@ public final class BluetoothPbapClient implements BluetoothProfile {
*/
@SystemApi
@RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
- public boolean setConnectionPolicy(BluetoothDevice device, int connectionPolicy) {
+ public boolean setConnectionPolicy(BluetoothDevice device,
+ @ConnectionPolicy int connectionPolicy) {
if (DBG) {
log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
}
@@ -323,7 +324,7 @@ public final class BluetoothPbapClient implements BluetoothProfile {
*/
@SystemApi
@RequiresPermission(Manifest.permission.BLUETOOTH)
- public int getConnectionPolicy(BluetoothDevice device) {
+ public @ConnectionPolicy int getConnectionPolicy(BluetoothDevice device) {
if (VDBG) {
log("getConnectionPolicy(" + device + ")");
}
diff --git a/core/java/android/bluetooth/BluetoothSap.java b/core/java/android/bluetooth/BluetoothSap.java
index 8bf1b588c89a..bfc3a4d25c23 100644
--- a/core/java/android/bluetooth/BluetoothSap.java
+++ b/core/java/android/bluetooth/BluetoothSap.java
@@ -330,7 +330,8 @@ public final class BluetoothSap implements BluetoothProfile {
*/
@SystemApi
@RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
- public boolean setConnectionPolicy(BluetoothDevice device, int connectionPolicy) {
+ public boolean setConnectionPolicy(BluetoothDevice device,
+ @ConnectionPolicy int connectionPolicy) {
if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
final IBluetoothSap service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
@@ -378,7 +379,7 @@ public final class BluetoothSap implements BluetoothProfile {
*/
@SystemApi
@RequiresPermission(Manifest.permission.BLUETOOTH)
- public int getConnectionPolicy(BluetoothDevice device) {
+ public @ConnectionPolicy int getConnectionPolicy(BluetoothDevice device) {
if (VDBG) log("getConnectionPolicy(" + device + ")");
final IBluetoothSap service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index ebc5e0e4aa31..58a0ea56a65f 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3394,6 +3394,7 @@ public abstract class Context {
CONNECTIVITY_SERVICE,
//@hide: IP_MEMORY_STORE_SERVICE,
IPSEC_SERVICE,
+ VPN_MANAGEMENT_SERVICE,
TEST_NETWORK_SERVICE,
//@hide: UPDATE_LOCK_SERVICE,
//@hide: NETWORKMANAGEMENT_SERVICE,
@@ -3984,6 +3985,14 @@ public abstract class Context {
public static final String IPSEC_SERVICE = "ipsec";
/**
+ * Use with {@link #getSystemService(String)} to retrieve a {@link android.net.VpnManager} to
+ * manage profiles for the platform built-in VPN.
+ *
+ * @see #getSystemService(String)
+ */
+ public static final String VPN_MANAGEMENT_SERVICE = "vpn_management";
+
+ /**
* Use with {@link #getSystemService(String)} to retrieve a {@link
* android.net.ConnectivityDiagnosticsManager} for performing network connectivity diagnostics
* as well as receiving network connectivity information from the system.
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index c8c31028b9d2..acffec98b2fc 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -36,6 +36,7 @@ import android.content.pm.ComponentInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ShortcutInfo;
+import android.content.pm.SuspendDialogInfo;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Rect;
@@ -2667,6 +2668,34 @@ public class Intent implements Parcelable, Cloneable {
"android.intent.action.SHOW_SUSPENDED_APP_DETAILS";
/**
+ * Broadcast Action: Sent to indicate that the user unsuspended a package.
+ *
+ * <p>This can happen when the user taps on the neutral button of the
+ * {@linkplain SuspendDialogInfo suspend-dialog} which was created by using
+ * {@link SuspendDialogInfo#BUTTON_ACTION_UNSUSPEND}. This broadcast is only sent to the
+ * suspending app that originally specified this dialog while calling
+ * {@link PackageManager#setPackagesSuspended(String[], boolean, PersistableBundle,
+ * PersistableBundle, SuspendDialogInfo)}.
+ *
+ * <p>Includes an extra {@link #EXTRA_PACKAGE_NAME} which is the name of the package that just
+ * got unsuspended.
+ *
+ * <p class="note">This is a protected intent that can only be sent
+ * by the system. <em>This will be delivered to {@link BroadcastReceiver} components declared in
+ * the manifest.</em>
+ *
+ * @see PackageManager#setPackagesSuspended(String[], boolean, PersistableBundle,
+ * PersistableBundle, SuspendDialogInfo)
+ * @see PackageManager#isPackageSuspended()
+ * @see SuspendDialogInfo#BUTTON_ACTION_MORE_DETAILS
+ * @hide
+ */
+ @SystemApi
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_PACKAGE_UNSUSPENDED_MANUALLY =
+ "android.intent.action.PACKAGE_UNSUSPENDED_MANUALLY";
+
+ /**
* Broadcast Action: Sent to a package that has been unsuspended.
*
* <p class="note">This is a protected intent that can only be sent
@@ -3629,7 +3658,9 @@ public class Intent implements Parcelable, Cloneable {
* {@link android.Manifest.permission#MANAGE_USERS} to receive this broadcast.
* @hide
*/
- @UnsupportedAppUsage
+ @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ @SystemApi
public static final String ACTION_USER_SWITCHED =
"android.intent.action.USER_SWITCHED";
@@ -4531,12 +4562,6 @@ public class Intent implements Parcelable, Cloneable {
public static final String EXTRA_LTE_EARFCN_RSRP_BOOST = "LteEarfcnRsrpBoost";
/**
- * An parcelable extra used with {@link #ACTION_SERVICE_STATE} representing the service state.
- * @hide
- */
- public static final String EXTRA_SERVICE_STATE = "android.intent.extra.SERVICE_STATE";
-
- /**
* The name of the extra used to define the text to be processed, as a
* CharSequence. Note that this may be a styled CharSequence, so you must use
* {@link Bundle#getCharSequence(String) Bundle.getCharSequence()} to retrieve it.
diff --git a/core/java/android/service/controls/templates/RangeTemplate.aidl b/core/java/android/content/LocusId.aidl
index 992881552903..eb98db06ccbf 100644
--- a/core/java/android/service/controls/templates/RangeTemplate.aidl
+++ b/core/java/android/content/LocusId.aidl
@@ -1,5 +1,5 @@
-/*
- * Copyright (c) 2019, The Android Open Source Project
+/**
+ * Copyright (c) 2020, The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,6 +14,6 @@
* limitations under the License.
*/
-package android.service.controls.templates;
+package android.content;
-parcelable RangeTemplate; \ No newline at end of file
+parcelable LocusId;
diff --git a/core/java/android/content/integrity/AppInstallMetadata.java b/core/java/android/content/integrity/AppInstallMetadata.java
index 85af8813fa94..351edc92888d 100644
--- a/core/java/android/content/integrity/AppInstallMetadata.java
+++ b/core/java/android/content/integrity/AppInstallMetadata.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,10 +17,6 @@
package android.content.integrity;
import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.SystemApi;
-
-import com.android.internal.annotations.VisibleForTesting;
import java.util.Objects;
@@ -34,8 +30,6 @@ import java.util.Objects;
*
* @hide
*/
-@SystemApi
-@VisibleForTesting
public final class AppInstallMetadata {
private final String mPackageName;
// Raw string encoding for the SHA-256 hash of the certificate of the app.
@@ -43,7 +37,7 @@ public final class AppInstallMetadata {
private final String mInstallerName;
// Raw string encoding for the SHA-256 hash of the certificate of the installer.
private final String mInstallerCertificate;
- private final int mVersionCode;
+ private final long mVersionCode;
private final boolean mIsPreInstalled;
private AppInstallMetadata(Builder builder) {
@@ -65,18 +59,18 @@ public final class AppInstallMetadata {
return mAppCertificate;
}
- @Nullable
+ @NonNull
public String getInstallerName() {
return mInstallerName;
}
- @Nullable
+ @NonNull
public String getInstallerCertificate() {
return mInstallerCertificate;
}
- /** @see AppInstallMetadata.Builder#setVersionCode(int) */
- public int getVersionCode() {
+ /** @see AppInstallMetadata.Builder#setVersionCode(long) */
+ public long getVersionCode() {
return mVersionCode;
}
@@ -104,7 +98,7 @@ public final class AppInstallMetadata {
private String mAppCertificate;
private String mInstallerName;
private String mInstallerCertificate;
- private int mVersionCode;
+ private long mVersionCode;
private boolean mIsPreInstalled;
/**
@@ -163,7 +157,7 @@ public final class AppInstallMetadata {
* @see AppInstallMetadata#getVersionCode()
*/
@NonNull
- public Builder setVersionCode(int versionCode) {
+ public Builder setVersionCode(long versionCode) {
this.mVersionCode = versionCode;
return this;
}
diff --git a/core/java/android/content/integrity/AtomicFormula.java b/core/java/android/content/integrity/AtomicFormula.java
index 574a93ff9355..4c10a8f7ad38 100644
--- a/core/java/android/content/integrity/AtomicFormula.java
+++ b/core/java/android/content/integrity/AtomicFormula.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,15 +20,16 @@ import static com.android.internal.util.Preconditions.checkArgument;
import android.annotation.IntDef;
import android.annotation.NonNull;
-import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
-import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.nio.charset.StandardCharsets;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
import java.util.Objects;
/**
@@ -38,29 +39,28 @@ import java.util.Objects;
*
* @hide
*/
-@SystemApi
@VisibleForTesting
-public abstract class AtomicFormula implements Formula {
-
- private static final String TAG = "AtomicFormula";
+public abstract class AtomicFormula extends IntegrityFormula {
/** @hide */
@IntDef(
value = {
- PACKAGE_NAME,
- APP_CERTIFICATE,
- INSTALLER_NAME,
- INSTALLER_CERTIFICATE,
- VERSION_CODE,
- PRE_INSTALLED,
+ PACKAGE_NAME,
+ APP_CERTIFICATE,
+ INSTALLER_NAME,
+ INSTALLER_CERTIFICATE,
+ VERSION_CODE,
+ PRE_INSTALLED,
})
@Retention(RetentionPolicy.SOURCE)
- public @interface Key {}
+ public @interface Key {
+ }
/** @hide */
- @IntDef(value = {EQ, LT, LE, GT, GE})
+ @IntDef(value = {EQ, GT, GTE})
@Retention(RetentionPolicy.SOURCE)
- public @interface Operator {}
+ public @interface Operator {
+ }
/**
* Package name of the app.
@@ -94,7 +94,7 @@ public abstract class AtomicFormula implements Formula {
/**
* Version code of the app.
*
- * <p>Can only be used in {@link IntAtomicFormula}.
+ * <p>Can only be used in {@link LongAtomicFormula}.
*/
public static final int VERSION_CODE = 4;
@@ -106,10 +106,8 @@ public abstract class AtomicFormula implements Formula {
public static final int PRE_INSTALLED = 5;
public static final int EQ = 0;
- public static final int LT = 1;
- public static final int LE = 2;
- public static final int GT = 3;
- public static final int GE = 4;
+ public static final int GT = 1;
+ public static final int GTE = 2;
private final @Key int mKey;
@@ -118,78 +116,99 @@ public abstract class AtomicFormula implements Formula {
mKey = key;
}
- /** An {@link AtomicFormula} with an key and int value. */
- public static final class IntAtomicFormula extends AtomicFormula implements Parcelable {
- private final int mValue;
- private final @Operator int mOperator;
+ /** An {@link AtomicFormula} with an key and long value. */
+ public static final class LongAtomicFormula extends AtomicFormula implements Parcelable {
+ private final Long mValue;
+ private final @Operator Integer mOperator;
+
+ /**
+ * Constructs an empty {@link LongAtomicFormula}. This should only be used as a base.
+ *
+ * <p>This formula will always return false.
+ *
+ * @throws IllegalArgumentException if {@code key} cannot be used with long value
+ */
+ public LongAtomicFormula(@Key int key) {
+ super(key);
+ checkArgument(
+ key == VERSION_CODE,
+ String.format(
+ "Key %s cannot be used with LongAtomicFormula", keyToString(key)));
+ mValue = null;
+ mOperator = null;
+ }
/**
- * Constructs a new {@link IntAtomicFormula}.
+ * Constructs a new {@link LongAtomicFormula}.
*
* <p>This formula will hold if and only if the corresponding information of an install
* specified by {@code key} is of the correct relationship to {@code value} as specified by
* {@code operator}.
*
- * @throws IllegalArgumentException if {@code key} cannot be used with integer value
+ * @throws IllegalArgumentException if {@code key} cannot be used with long value
*/
- public IntAtomicFormula(@Key int key, @Operator int operator, int value) {
+ public LongAtomicFormula(@Key int key, @Operator int operator, long value) {
super(key);
checkArgument(
key == VERSION_CODE,
- String.format("Key %s cannot be used with IntAtomicFormula", keyToString(key)));
- checkArgument(isValidOperator(operator),
- String.format("Unknown operator: %d", operator));
+ String.format(
+ "Key %s cannot be used with LongAtomicFormula", keyToString(key)));
+ checkArgument(
+ isValidOperator(operator), String.format("Unknown operator: %d", operator));
mOperator = operator;
mValue = value;
}
- IntAtomicFormula(Parcel in) {
+ LongAtomicFormula(Parcel in) {
super(in.readInt());
- mValue = in.readInt();
+ mValue = in.readLong();
mOperator = in.readInt();
}
@NonNull
- public static final Creator<IntAtomicFormula> CREATOR =
- new Creator<IntAtomicFormula>() {
+ public static final Creator<LongAtomicFormula> CREATOR =
+ new Creator<LongAtomicFormula>() {
@Override
- public IntAtomicFormula createFromParcel(Parcel in) {
- return new IntAtomicFormula(in);
+ public LongAtomicFormula createFromParcel(Parcel in) {
+ return new LongAtomicFormula(in);
}
@Override
- public IntAtomicFormula[] newArray(int size) {
- return new IntAtomicFormula[size];
+ public LongAtomicFormula[] newArray(int size) {
+ return new LongAtomicFormula[size];
}
};
@Override
- public boolean isSatisfied(@NonNull AppInstallMetadata appInstallMetadata) {
- int metadataValue = getMetadataValueByKey(appInstallMetadata);
+ public int getTag() {
+ return IntegrityFormula.LONG_ATOMIC_FORMULA_TAG;
+ }
+
+ @Override
+ public boolean matches(AppInstallMetadata appInstallMetadata) {
+ if (mValue == null || mOperator == null) {
+ return false;
+ }
+
+ long metadataValue = getLongMetadataValue(appInstallMetadata, getKey());
switch (mOperator) {
case EQ:
return metadataValue == mValue;
- case LE:
- return metadataValue <= mValue;
- case LT:
- return metadataValue < mValue;
- case GE:
- return metadataValue >= mValue;
case GT:
return metadataValue > mValue;
+ case GTE:
+ return metadataValue >= mValue;
default:
- Slog.i(TAG, String.format("Unexpected operator %d", mOperator));
- return false;
+ throw new IllegalArgumentException(
+ String.format("Unexpected operator %d", mOperator));
}
}
@Override
- public int getTag() {
- return Formula.INT_ATOMIC_FORMULA_TAG;
- }
-
- @Override
public String toString() {
+ if (mValue == null || mOperator == null) {
+ return String.format("(%s)", keyToString(getKey()));
+ }
return String.format(
"(%s %s %s)", keyToString(getKey()), operatorToString(mOperator), mValue);
}
@@ -202,7 +221,7 @@ public abstract class AtomicFormula implements Formula {
if (o == null || getClass() != o.getClass()) {
return false;
}
- IntAtomicFormula that = (IntAtomicFormula) o;
+ LongAtomicFormula that = (LongAtomicFormula) o;
return getKey() == that.getKey()
&& mValue == that.mValue
&& mOperator == that.mOperator;
@@ -220,35 +239,35 @@ public abstract class AtomicFormula implements Formula {
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
+ if (mValue == null || mOperator == null) {
+ throw new IllegalStateException("Cannot write an empty LongAtomicFormula.");
+ }
dest.writeInt(getKey());
- dest.writeInt(mValue);
+ dest.writeLong(mValue);
dest.writeInt(mOperator);
}
- public int getValue() {
+ public Long getValue() {
return mValue;
}
- public int getOperator() {
+ public Integer getOperator() {
return mOperator;
}
- private int getMetadataValueByKey(AppInstallMetadata appInstallMetadata) {
- switch (getKey()) {
- case VERSION_CODE:
- return appInstallMetadata.getVersionCode();
- default:
- throw new IllegalStateException(
- "Unexpected key in IntAtomicFormula" + getKey());
- }
- }
-
private static boolean isValidOperator(int operator) {
return operator == EQ
- || operator == LT
- || operator == LE
|| operator == GT
- || operator == GE;
+ || operator == GTE;
+ }
+
+ private static long getLongMetadataValue(AppInstallMetadata appInstallMetadata, int key) {
+ switch (key) {
+ case AtomicFormula.VERSION_CODE:
+ return appInstallMetadata.getVersionCode();
+ default:
+ throw new IllegalStateException("Unexpected key in IntAtomicFormula" + key);
+ }
}
}
@@ -256,7 +275,27 @@ public abstract class AtomicFormula implements Formula {
public static final class StringAtomicFormula extends AtomicFormula implements Parcelable {
private final String mValue;
// Indicates whether the value is the actual value or the hashed value.
- private final boolean mIsHashedValue;
+ private final Boolean mIsHashedValue;
+
+ /**
+ * Constructs an empty {@link StringAtomicFormula}. This should only be used as a base.
+ *
+ * <p>An empty formula will always match to false.
+ *
+ * @throws IllegalArgumentException if {@code key} cannot be used with string value
+ */
+ public StringAtomicFormula(@Key int key) {
+ super(key);
+ checkArgument(
+ key == PACKAGE_NAME
+ || key == APP_CERTIFICATE
+ || key == INSTALLER_CERTIFICATE
+ || key == INSTALLER_NAME,
+ String.format(
+ "Key %s cannot be used with StringAtomicFormula", keyToString(key)));
+ mValue = null;
+ mIsHashedValue = null;
+ }
/**
* Constructs a new {@link StringAtomicFormula}.
@@ -266,9 +305,8 @@ public abstract class AtomicFormula implements Formula {
*
* @throws IllegalArgumentException if {@code key} cannot be used with string value
*/
- public StringAtomicFormula(@Key int key, @NonNull String value, boolean isHashedValue) {
+ public StringAtomicFormula(@Key int key, @NonNull String value, boolean isHashed) {
super(key);
- mIsHashedValue = isHashedValue;
checkArgument(
key == PACKAGE_NAME
|| key == APP_CERTIFICATE
@@ -277,6 +315,30 @@ public abstract class AtomicFormula implements Formula {
String.format(
"Key %s cannot be used with StringAtomicFormula", keyToString(key)));
mValue = value;
+ mIsHashedValue = isHashed;
+ }
+
+ /**
+ * Constructs a new {@link StringAtomicFormula} together with handling the necessary
+ * hashing for the given key.
+ *
+ * <p> The value will be hashed with SHA256 and the hex digest will be computed; for
+ * all cases except when the key is PACKAGE_NAME or INSTALLER_NAME and the value
+ * is less than 33 characters.
+ *
+ * @throws IllegalArgumentException if {@code key} cannot be used with string value.
+ */
+ public StringAtomicFormula(@Key int key, @NonNull String value) {
+ super(key);
+ checkArgument(
+ key == PACKAGE_NAME
+ || key == APP_CERTIFICATE
+ || key == INSTALLER_CERTIFICATE
+ || key == INSTALLER_NAME,
+ String.format(
+ "Key %s cannot be used with StringAtomicFormula", keyToString(key)));
+ mValue = hashValue(key, value);
+ mIsHashedValue = !mValue.equals(value);
}
StringAtomicFormula(Parcel in) {
@@ -300,18 +362,23 @@ public abstract class AtomicFormula implements Formula {
};
@Override
- public boolean isSatisfied(@NonNull AppInstallMetadata appInstallMetadata) {
- String metadataValue = getMetadataValueByKey(appInstallMetadata);
- return metadataValue.equals(mValue);
+ public int getTag() {
+ return IntegrityFormula.STRING_ATOMIC_FORMULA_TAG;
}
@Override
- public int getTag() {
- return Formula.STRING_ATOMIC_FORMULA_TAG;
+ public boolean matches(AppInstallMetadata appInstallMetadata) {
+ if (mValue == null || mIsHashedValue == null) {
+ return false;
+ }
+ return getStringMetadataValue(appInstallMetadata, getKey()).equals(mValue);
}
@Override
public String toString() {
+ if (mValue == null || mIsHashedValue == null) {
+ return String.format("(%s)", keyToString(getKey()));
+ }
return String.format("(%s %s %s)", keyToString(getKey()), operatorToString(EQ), mValue);
}
@@ -339,40 +406,80 @@ public abstract class AtomicFormula implements Formula {
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
+ if (mValue == null || mIsHashedValue == null) {
+ throw new IllegalStateException("Cannot write an empty StringAtomicFormula.");
+ }
dest.writeInt(getKey());
dest.writeStringNoHelper(mValue);
dest.writeByte((byte) (mIsHashedValue ? 1 : 0));
}
- @NonNull
public String getValue() {
return mValue;
}
- public boolean getIsHashedValue() {
+ public Boolean getIsHashedValue() {
return mIsHashedValue;
}
- private String getMetadataValueByKey(AppInstallMetadata appInstallMetadata) {
- switch (getKey()) {
- case PACKAGE_NAME:
+ private static String getStringMetadataValue(
+ AppInstallMetadata appInstallMetadata, int key) {
+ switch (key) {
+ case AtomicFormula.PACKAGE_NAME:
return appInstallMetadata.getPackageName();
- case APP_CERTIFICATE:
+ case AtomicFormula.APP_CERTIFICATE:
return appInstallMetadata.getAppCertificate();
- case INSTALLER_CERTIFICATE:
+ case AtomicFormula.INSTALLER_CERTIFICATE:
return appInstallMetadata.getInstallerCertificate();
- case INSTALLER_NAME:
+ case AtomicFormula.INSTALLER_NAME:
return appInstallMetadata.getInstallerName();
default:
throw new IllegalStateException(
- "Unexpected key in StringAtomicFormula: " + getKey());
+ "Unexpected key in StringAtomicFormula: " + key);
+ }
+ }
+
+ private static String hashValue(@Key int key, String value) {
+ // Hash the string value unless it is a PACKAGE_NAME or INSTALLER_NAME and the value is
+ // less than 33 characters.
+ if (value.length() <= 32) {
+ if (key == PACKAGE_NAME || key == INSTALLER_NAME) {
+ return value;
+ }
+ }
+ return hash(value);
+ }
+
+ private static String hash(String value) {
+ try {
+ MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
+ byte[] hashBytes = messageDigest.digest(value.getBytes(StandardCharsets.UTF_8));
+ return IntegrityUtils.getHexDigest(hashBytes);
+ } catch (NoSuchAlgorithmException e) {
+ throw new RuntimeException("SHA-256 algorithm not found", e);
}
}
}
/** An {@link AtomicFormula} with a key and boolean value. */
public static final class BooleanAtomicFormula extends AtomicFormula implements Parcelable {
- private final boolean mValue;
+ private final Boolean mValue;
+
+ /**
+ * Constructs an empty {@link BooleanAtomicFormula}. This should only be used as a base.
+ *
+ * <p>An empty formula will always match to false.
+ *
+ * @throws IllegalArgumentException if {@code key} cannot be used with boolean value
+ */
+ public BooleanAtomicFormula(@Key int key) {
+ super(key);
+ checkArgument(
+ key == PRE_INSTALLED,
+ String.format(
+ "Key %s cannot be used with BooleanAtomicFormula", keyToString(key)));
+ mValue = null;
+ }
/**
* Constructs a new {@link BooleanAtomicFormula}.
@@ -411,18 +518,23 @@ public abstract class AtomicFormula implements Formula {
};
@Override
- public boolean isSatisfied(@NonNull AppInstallMetadata appInstallMetadata) {
- boolean metadataValue = getMetadataValueByKey(appInstallMetadata);
- return metadataValue == mValue;
+ public int getTag() {
+ return IntegrityFormula.BOOLEAN_ATOMIC_FORMULA_TAG;
}
@Override
- public int getTag() {
- return Formula.BOOLEAN_ATOMIC_FORMULA_TAG;
+ public boolean matches(AppInstallMetadata appInstallMetadata) {
+ if (mValue == null) {
+ return false;
+ }
+ return getBooleanMetadataValue(appInstallMetadata, getKey()) == mValue;
}
@Override
public String toString() {
+ if (mValue == null) {
+ return String.format("(%s)", keyToString(getKey()));
+ }
return String.format("(%s %s %s)", keyToString(getKey()), operatorToString(EQ), mValue);
}
@@ -450,21 +562,25 @@ public abstract class AtomicFormula implements Formula {
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
+ if (mValue == null) {
+ throw new IllegalStateException("Cannot write an empty BooleanAtomicFormula.");
+ }
dest.writeInt(getKey());
dest.writeByte((byte) (mValue ? 1 : 0));
}
- public boolean getValue() {
+ public Boolean getValue() {
return mValue;
}
- private boolean getMetadataValueByKey(AppInstallMetadata appInstallMetadata) {
- switch (getKey()) {
- case PRE_INSTALLED:
+ private static boolean getBooleanMetadataValue(
+ AppInstallMetadata appInstallMetadata, int key) {
+ switch (key) {
+ case AtomicFormula.PRE_INSTALLED:
return appInstallMetadata.isPreInstalled();
default:
throw new IllegalStateException(
- "Unexpected key in BooleanAtomicFormula: " + getKey());
+ "Unexpected key in BooleanAtomicFormula: " + key);
}
}
}
@@ -496,14 +612,10 @@ public abstract class AtomicFormula implements Formula {
switch (op) {
case EQ:
return "EQ";
- case LT:
- return "LT";
- case LE:
- return "LE";
case GT:
return "GT";
- case GE:
- return "GE";
+ case GTE:
+ return "GTE";
default:
throw new IllegalArgumentException("Unknown operator " + op);
}
diff --git a/core/java/android/content/integrity/CompoundFormula.java b/core/java/android/content/integrity/CompoundFormula.java
index 2a651d9e90d8..56061df21388 100644
--- a/core/java/android/content/integrity/CompoundFormula.java
+++ b/core/java/android/content/integrity/CompoundFormula.java
@@ -20,10 +20,8 @@ import static com.android.internal.util.Preconditions.checkArgument;
import android.annotation.IntDef;
import android.annotation.NonNull;
-import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
-import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
@@ -42,16 +40,11 @@ import java.util.Objects;
*
* @hide
*/
-@SystemApi
@VisibleForTesting
-public final class CompoundFormula implements Formula, Parcelable {
- private static final String TAG = "OpenFormula";
+public final class CompoundFormula extends IntegrityFormula implements Parcelable {
/** @hide */
- @IntDef(
- value = {
- AND, OR, NOT,
- })
+ @IntDef(value = {AND, OR, NOT})
@Retention(RetentionPolicy.SOURCE)
public @interface Connector {}
@@ -65,7 +58,7 @@ public final class CompoundFormula implements Formula, Parcelable {
public static final int NOT = 2;
private final @Connector int mConnector;
- private final @NonNull List<Formula> mFormulas;
+ private final @NonNull List<IntegrityFormula> mFormulas;
@NonNull
public static final Creator<CompoundFormula> CREATOR =
@@ -85,9 +78,10 @@ public final class CompoundFormula implements Formula, Parcelable {
* Create a new formula from operator and operands.
*
* @throws IllegalArgumentException if the number of operands is not matching the requirements
- * for that operator (at least 2 for {@link #AND} and {@link #OR}, 1 for {@link #NOT}).
+ * for that operator (at least 2 for {@link #AND} and {@link
+ * #OR}, 1 for {@link #NOT}).
*/
- public CompoundFormula(@Connector int connector, @NonNull List<Formula> formulas) {
+ public CompoundFormula(@Connector int connector, List<IntegrityFormula> formulas) {
checkArgument(
isValidConnector(connector), String.format("Unknown connector: %d", connector));
validateFormulas(connector, formulas);
@@ -101,7 +95,7 @@ public final class CompoundFormula implements Formula, Parcelable {
checkArgument(length >= 0, "Must have non-negative length. Got " + length);
mFormulas = new ArrayList<>(length);
for (int i = 0; i < length; i++) {
- mFormulas.add(Formula.readFromParcel(in));
+ mFormulas.add(IntegrityFormula.readFromParcel(in));
}
validateFormulas(mConnector, mFormulas);
}
@@ -111,33 +105,32 @@ public final class CompoundFormula implements Formula, Parcelable {
}
@NonNull
- public List<Formula> getFormulas() {
+ public List<IntegrityFormula> getFormulas() {
return mFormulas;
}
@Override
- public boolean isSatisfied(@NonNull AppInstallMetadata appInstallMetadata) {
- switch (mConnector) {
+ public int getTag() {
+ return IntegrityFormula.COMPOUND_FORMULA_TAG;
+ }
+
+ @Override
+ public boolean matches(AppInstallMetadata appInstallMetadata) {
+ switch (getConnector()) {
case NOT:
- return !mFormulas.get(0).isSatisfied(appInstallMetadata);
+ return !getFormulas().get(0).matches(appInstallMetadata);
case AND:
- return mFormulas.stream()
- .allMatch(formula -> formula.isSatisfied(appInstallMetadata));
+ return getFormulas().stream()
+ .allMatch(formula -> formula.matches(appInstallMetadata));
case OR:
- return mFormulas.stream()
- .anyMatch(formula -> formula.isSatisfied(appInstallMetadata));
+ return getFormulas().stream()
+ .anyMatch(formula -> formula.matches(appInstallMetadata));
default:
- Slog.i(TAG, "Unknown connector " + mConnector);
- return false;
+ throw new IllegalArgumentException("Unknown connector " + getConnector());
}
}
@Override
- public int getTag() {
- return Formula.COMPOUND_FORMULA_TAG;
- }
-
- @Override
public String toString() {
StringBuilder sb = new StringBuilder();
if (mFormulas.size() == 1) {
@@ -180,12 +173,13 @@ public final class CompoundFormula implements Formula, Parcelable {
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeInt(mConnector);
dest.writeInt(mFormulas.size());
- for (Formula formula : mFormulas) {
- Formula.writeToParcel(formula, dest, flags);
+ for (IntegrityFormula formula : mFormulas) {
+ IntegrityFormula.writeToParcel(formula, dest, flags);
}
}
- private static void validateFormulas(@Connector int connector, List<Formula> formulas) {
+ private static void validateFormulas(
+ @Connector int connector, List<IntegrityFormula> formulas) {
switch (connector) {
case AND:
case OR:
diff --git a/core/java/android/content/integrity/Formula.java b/core/java/android/content/integrity/Formula.java
deleted file mode 100644
index b092a22c0191..000000000000
--- a/core/java/android/content/integrity/Formula.java
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * 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.integrity;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.SystemApi;
-import android.content.integrity.AtomicFormula.BooleanAtomicFormula;
-import android.content.integrity.AtomicFormula.IntAtomicFormula;
-import android.content.integrity.AtomicFormula.StringAtomicFormula;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * Represents a rule logic/content.
- *
- * @hide
- */
-@SystemApi
-@VisibleForTesting
-public interface Formula {
- /** @hide */
- @IntDef(
- value = {
- COMPOUND_FORMULA_TAG,
- STRING_ATOMIC_FORMULA_TAG,
- INT_ATOMIC_FORMULA_TAG,
- BOOLEAN_ATOMIC_FORMULA_TAG
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface Tag {}
-
- int COMPOUND_FORMULA_TAG = 0;
- int STRING_ATOMIC_FORMULA_TAG = 1;
- int INT_ATOMIC_FORMULA_TAG = 2;
- int BOOLEAN_ATOMIC_FORMULA_TAG = 3;
-
- /**
- * Returns if this formula can be satisfied by substituting the corresponding information of
- * {@code appInstallMetadata} into the formula.
- */
- boolean isSatisfied(@NonNull AppInstallMetadata appInstallMetadata);
-
- /** Returns the tag that identifies the current class. */
- @Tag int getTag();
-
- /**
- * Write a {@link Formula} to {@link android.os.Parcel}.
- *
- * <p>This helper method is needed because non-final class/interface are not allowed to be
- * {@link Parcelable}.
- *
- * @throws IllegalArgumentException if {@link Formula} is not a recognized subclass
- */
- static void writeToParcel(@NonNull Formula formula, @NonNull Parcel dest, int flags) {
- dest.writeInt(formula.getTag());
- ((Parcelable) formula).writeToParcel(dest, flags);
- }
-
- /**
- * Read a {@link Formula} from a {@link android.os.Parcel}.
- *
- * <p>We need this (hacky) helper method because non-final class/interface cannot be {@link
- * Parcelable} (api lint error).
- *
- * @throws IllegalArgumentException if the parcel cannot be parsed
- */
- @NonNull
- static Formula readFromParcel(@NonNull Parcel in) {
- int tag = in.readInt();
- switch (tag) {
- case COMPOUND_FORMULA_TAG:
- return CompoundFormula.CREATOR.createFromParcel(in);
- case STRING_ATOMIC_FORMULA_TAG:
- return StringAtomicFormula.CREATOR.createFromParcel(in);
- case INT_ATOMIC_FORMULA_TAG:
- return IntAtomicFormula.CREATOR.createFromParcel(in);
- case BOOLEAN_ATOMIC_FORMULA_TAG:
- return BooleanAtomicFormula.CREATOR.createFromParcel(in);
- default:
- throw new IllegalArgumentException("Unknown formula tag " + tag);
- }
- }
-}
diff --git a/core/java/android/content/integrity/IntegrityFormula.java b/core/java/android/content/integrity/IntegrityFormula.java
new file mode 100644
index 000000000000..0660f93e9f01
--- /dev/null
+++ b/core/java/android/content/integrity/IntegrityFormula.java
@@ -0,0 +1,277 @@
+/*
+ * 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.integrity;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.content.integrity.AtomicFormula.BooleanAtomicFormula;
+import android.content.integrity.AtomicFormula.LongAtomicFormula;
+import android.content.integrity.AtomicFormula.StringAtomicFormula;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Arrays;
+
+/**
+ * Represents a rule logic/content.
+ *
+ * @hide
+ */
+@SystemApi
+@VisibleForTesting
+public abstract class IntegrityFormula {
+
+ /**
+ * A static formula base for package name formulas.
+ *
+ * This formulation is incomplete and should always be used with {@code equals} formulation.
+ * Evaluates to false when used directly and cannot be written as a parcel.
+ */
+ @NonNull
+ public static final IntegrityFormula PACKAGE_NAME =
+ new StringAtomicFormula(AtomicFormula.PACKAGE_NAME);
+
+ /**
+ * A static formula base for app certificate formulas.
+ *
+ * This formulation is incomplete and should always be used with {@code equals} formulation.
+ * Evaluates to false when used directly and cannot be written as a parcel.
+ */
+ @NonNull
+ public static final IntegrityFormula APP_CERTIFICATE =
+ new StringAtomicFormula(AtomicFormula.APP_CERTIFICATE);
+
+ /**
+ * A static formula base for installer name formulas.
+ *
+ * This formulation is incomplete and should always be used with {@code equals} formulation.
+ * Evaluates to false when used directly and cannot be written as a parcel.
+ */
+ @NonNull
+ public static final IntegrityFormula INSTALLER_NAME =
+ new StringAtomicFormula(AtomicFormula.INSTALLER_NAME);
+
+ /**
+ * A static formula base for installer certificate formulas.
+ *
+ * This formulation is incomplete and should always be used with {@code equals} formulation.
+ * Evaluates to false when used directly and cannot be written as a parcel.
+ */
+ @NonNull
+ public static final IntegrityFormula INSTALLER_CERTIFICATE =
+ new StringAtomicFormula(AtomicFormula.INSTALLER_CERTIFICATE);
+
+ /**
+ * A static formula base for version code name formulas.
+ *
+ * This formulation is incomplete and should always be used with {@code equals},
+ * {@code greaterThan} and {@code greaterThanEquals} formulation. Evaluates to false when used
+ * directly and cannot be written as a parcel.
+ */
+ @NonNull
+ public static final IntegrityFormula VERSION_CODE =
+ new LongAtomicFormula(AtomicFormula.VERSION_CODE);
+
+ /**
+ * A static formula base for pre-installed status formulas.
+ *
+ * This formulation is incomplete and should always be used with {@code equals} formulation.
+ * Evaluates to false when used directly and cannot be written as a parcel.
+ */
+ @NonNull
+ public static final IntegrityFormula PRE_INSTALLED =
+ new BooleanAtomicFormula(AtomicFormula.PRE_INSTALLED);
+
+ /** @hide */
+ @IntDef(
+ value = {
+ COMPOUND_FORMULA_TAG,
+ STRING_ATOMIC_FORMULA_TAG,
+ LONG_ATOMIC_FORMULA_TAG,
+ BOOLEAN_ATOMIC_FORMULA_TAG
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface Tag {}
+
+ /** @hide */
+ public static final int COMPOUND_FORMULA_TAG = 0;
+ /** @hide */
+ public static final int STRING_ATOMIC_FORMULA_TAG = 1;
+ /** @hide */
+ public static final int LONG_ATOMIC_FORMULA_TAG = 2;
+ /** @hide */
+ public static final int BOOLEAN_ATOMIC_FORMULA_TAG = 3;
+
+ /**
+ * Returns the tag that identifies the current class.
+ *
+ * @hide
+ */
+ public abstract @Tag int getTag();
+
+ /**
+ * Returns true when the integrity formula is satisfied by the {@code appInstallMetadata}.
+ *
+ * @hide
+ */
+ public abstract @Tag boolean matches(AppInstallMetadata appInstallMetadata);
+
+ /**
+ * Write an {@link IntegrityFormula} to {@link android.os.Parcel}.
+ *
+ * <p>This helper method is needed because non-final class/interface are not allowed to be
+ * {@link Parcelable}.
+ *
+ * @throws IllegalArgumentException if {@link IntegrityFormula} is not a recognized subclass
+ *
+ * @hide
+ */
+ public static void writeToParcel(
+ @NonNull IntegrityFormula formula, @NonNull Parcel dest, int flags) {
+ dest.writeInt(formula.getTag());
+ ((Parcelable) formula).writeToParcel(dest, flags);
+ }
+
+ /**
+ * Read a {@link IntegrityFormula} from a {@link android.os.Parcel}.
+ *
+ * <p>We need this (hacky) helper method because non-final class/interface cannot be {@link
+ * Parcelable} (api lint error).
+ *
+ * @throws IllegalArgumentException if the parcel cannot be parsed
+ * @hide
+ */
+ @NonNull
+ public static IntegrityFormula readFromParcel(@NonNull Parcel in) {
+ int tag = in.readInt();
+ switch (tag) {
+ case COMPOUND_FORMULA_TAG:
+ return CompoundFormula.CREATOR.createFromParcel(in);
+ case STRING_ATOMIC_FORMULA_TAG:
+ return StringAtomicFormula.CREATOR.createFromParcel(in);
+ case LONG_ATOMIC_FORMULA_TAG:
+ return LongAtomicFormula.CREATOR.createFromParcel(in);
+ case BOOLEAN_ATOMIC_FORMULA_TAG:
+ return BooleanAtomicFormula.CREATOR.createFromParcel(in);
+ default:
+ throw new IllegalArgumentException("Unknown formula tag " + tag);
+ }
+ }
+
+ /**
+ * Returns an integrity formula that evaluates to true when value of the key matches to the
+ * provided string value.
+ *
+ * <p>The value will be hashed with SHA256 and the hex digest will be computed; for
+ * all cases except when the key is PACKAGE_NAME or INSTALLER_NAME and the value is less than
+ * 32 characters.
+ *
+ * <p>Throws an {@link IllegalArgumentException} if the key is not string typed.
+ */
+ @NonNull
+ public IntegrityFormula equalTo(@NonNull String value) {
+ AtomicFormula baseFormula = (AtomicFormula) this;
+ return new AtomicFormula.StringAtomicFormula(baseFormula.getKey(), value);
+ }
+
+ /**
+ * Returns an integrity formula that evaluates to true when the boolean value of the key matches
+ * the provided boolean value. It can only be used with the boolean comparison keys.
+ *
+ * <p>Throws an {@link IllegalArgumentException} if the key is not boolean typed.
+ */
+ @NonNull
+ public IntegrityFormula equalTo(boolean value) {
+ AtomicFormula baseFormula = (AtomicFormula) this;
+ return new AtomicFormula.BooleanAtomicFormula(baseFormula.getKey(), value);
+ }
+
+ /**
+ * Returns a formula that evaluates to true when the value of the key in the package being
+ * installed is equal to {@code value}.
+ *
+ * <p>Throws an {@link IllegalArgumentException} if the key is not long typed.
+ */
+ @NonNull
+ public IntegrityFormula equalTo(long value) {
+ AtomicFormula baseFormula = (AtomicFormula) this;
+ return new AtomicFormula.LongAtomicFormula(baseFormula.getKey(), AtomicFormula.EQ, value);
+ }
+
+ /**
+ * Returns a formula that evaluates to true when the value of the key in the package being
+ * installed is greater than {@code value}.
+ *
+ * <p>Throws an {@link IllegalArgumentException} if the key is not long typed.
+ */
+ @NonNull
+ public IntegrityFormula greaterThan(long value) {
+ AtomicFormula baseFormula = (AtomicFormula) this;
+ return new AtomicFormula.LongAtomicFormula(baseFormula.getKey(), AtomicFormula.GT, value);
+ }
+
+ /**
+ * Returns a formula that evaluates to true when the value of the key in the package being
+ * installed is greater than or equals to the {@code value}.
+ *
+ * <p>Throws an {@link IllegalArgumentException} if the key is not long typed.
+ */
+ @NonNull
+ public IntegrityFormula greaterThanOrEquals(long value) {
+ AtomicFormula baseFormula = (AtomicFormula) this;
+ return new AtomicFormula.LongAtomicFormula(baseFormula.getKey(), AtomicFormula.GTE, value);
+ }
+
+ /**
+ * Returns a formula that evaluates to true when any formula in {@code formulae} evaluates to
+ * true.
+ *
+ * <p>Throws an {@link IllegalArgumentException} if formulae has less than two elements.
+ */
+ @NonNull
+ public static IntegrityFormula any(@NonNull IntegrityFormula... formulae) {
+ return new CompoundFormula(CompoundFormula.OR, Arrays.asList(formulae));
+ }
+
+ /**
+ * Returns a formula that evaluates to true when all formula in {@code formulae} evaluates to
+ * true.
+ *
+ * <p>Throws an {@link IllegalArgumentException} if formulae has less than two elements.
+ */
+ @NonNull
+ public static IntegrityFormula all(@NonNull IntegrityFormula... formulae) {
+ return new CompoundFormula(CompoundFormula.AND, Arrays.asList(formulae));
+ }
+
+ /**
+ * Returns a formula that evaluates to true when {@code formula} evaluates to false.
+ */
+ @NonNull
+ public static IntegrityFormula not(@NonNull IntegrityFormula formula) {
+ return new CompoundFormula(CompoundFormula.NOT, Arrays.asList(formula));
+ }
+
+ // Constructor is package private so it cannot be inherited outside of this package.
+ IntegrityFormula() {
+ }
+}
diff --git a/services/core/java/com/android/server/integrity/IntegrityUtils.java b/core/java/android/content/integrity/IntegrityUtils.java
index f49c675dbd8d..c3f762469348 100644
--- a/services/core/java/com/android/server/integrity/IntegrityUtils.java
+++ b/core/java/android/content/integrity/IntegrityUtils.java
@@ -14,11 +14,15 @@
* limitations under the License.
*/
-package com.android.server.integrity;
+package android.content.integrity;
import static com.android.internal.util.Preconditions.checkArgument;
-/** Utils class for simple operations used in integrity module. */
+/**
+ * Utils class for simple operations used in integrity module.
+ *
+ * @hide
+ */
public class IntegrityUtils {
private static final char[] HEX_CHARS = "0123456789ABCDEF".toCharArray();
diff --git a/core/java/android/content/integrity/Rule.java b/core/java/android/content/integrity/Rule.java
index 77587851b18c..c421c4076d16 100644
--- a/core/java/android/content/integrity/Rule.java
+++ b/core/java/android/content/integrity/Rule.java
@@ -59,17 +59,17 @@ public final class Rule implements Parcelable {
*/
public static final int FORCE_ALLOW = 1;
- private final @NonNull Formula mFormula;
+ private final @NonNull IntegrityFormula mFormula;
private final @Effect int mEffect;
- public Rule(@NonNull Formula formula, @Effect int effect) {
+ public Rule(@NonNull IntegrityFormula formula, @Effect int effect) {
checkArgument(isValidEffect(effect), String.format("Unknown effect: %d", effect));
this.mFormula = Objects.requireNonNull(formula);
this.mEffect = effect;
}
Rule(Parcel in) {
- mFormula = Formula.readFromParcel(in);
+ mFormula = IntegrityFormula.readFromParcel(in);
mEffect = in.readInt();
}
@@ -94,12 +94,12 @@ public final class Rule implements Parcelable {
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
- Formula.writeToParcel(mFormula, dest, flags);
+ IntegrityFormula.writeToParcel(mFormula, dest, flags);
dest.writeInt(mEffect);
}
@NonNull
- public Formula getFormula() {
+ public IntegrityFormula getFormula() {
return mFormula;
}
@@ -141,7 +141,6 @@ public final class Rule implements Parcelable {
}
private static boolean isValidEffect(int effect) {
- return effect == DENY
- || effect == FORCE_ALLOW;
+ return effect == DENY || effect == FORCE_ALLOW;
}
}
diff --git a/core/java/android/content/om/OverlayManager.java b/core/java/android/content/om/OverlayManager.java
index a2f8886eb7d2..33d17763fb24 100644
--- a/core/java/android/content/om/OverlayManager.java
+++ b/core/java/android/content/om/OverlayManager.java
@@ -22,7 +22,11 @@ import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.TestApi;
+import android.compat.Compatibility;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledAfter;
import android.content.Context;
+import android.os.Build;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
@@ -40,6 +44,10 @@ public class OverlayManager {
private final IOverlayManager mService;
private final Context mContext;
+ @ChangeId
+ @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q)
+ private static final long THROW_SECURITY_EXCEPTIONS = 147340954;
+
/**
* Creates a new instance.
*
@@ -69,6 +77,9 @@ public class OverlayManager {
* @param packageName the name of the overlay package to enable.
* @param user The user for which to change the overlay.
*
+ * @throws SecurityException when caller is not allowed to enable {@param packageName}
+ * @throws IllegalStateException when enabling fails otherwise
+ *
* @hide
*/
@SystemApi
@@ -77,11 +88,13 @@ public class OverlayManager {
"android.permission.INTERACT_ACROSS_USERS_FULL"
})
public void setEnabledExclusiveInCategory(@NonNull final String packageName,
- @NonNull UserHandle user) {
+ @NonNull UserHandle user) throws SecurityException, IllegalStateException {
try {
if (!mService.setEnabledExclusiveInCategory(packageName, user.getIdentifier())) {
throw new IllegalStateException("setEnabledExclusiveInCategory failed");
}
+ } catch (SecurityException e) {
+ rethrowSecurityException(e);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -97,6 +110,9 @@ public class OverlayManager {
* @param enable {@code false} if the overlay should be turned off.
* @param user The user for which to change the overlay.
*
+ * @throws SecurityException when caller is not allowed to enable/disable {@param packageName}
+ * @throws IllegalStateException when enabling/disabling fails otherwise
+ *
* @hide
*/
@SystemApi
@@ -105,15 +121,16 @@ public class OverlayManager {
"android.permission.INTERACT_ACROSS_USERS_FULL"
})
public void setEnabled(@NonNull final String packageName, final boolean enable,
- @NonNull UserHandle user) {
+ @NonNull UserHandle user) throws SecurityException, IllegalStateException {
try {
if (!mService.setEnabled(packageName, enable, user.getIdentifier())) {
throw new IllegalStateException("setEnabled failed");
}
+ } catch (SecurityException e) {
+ rethrowSecurityException(e);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
- return;
}
/**
@@ -187,4 +204,29 @@ public class OverlayManager {
throw e.rethrowFromSystemServer();
}
}
+
+ /**
+ * Starting on R, actor enforcement and app visibility changes introduce additional failure
+ * cases, but the SecurityException thrown with these checks is unexpected for existing
+ * consumers of the API.
+ *
+ * The only prior case it would be thrown is with a permission failure, but the calling
+ * application would be able to verify that themselves, and so they may choose to ignore
+ * catching SecurityException when calling these APIs.
+ *
+ * For R, this no longer holds true, and SecurityExceptions can be thrown for any number of
+ * reasons, none of which are exposed to the caller. So for consumers targeting below R,
+ * transform these SecurityExceptions into IllegalStateExceptions, which are a little more
+ * expected to be thrown by the setEnabled APIs.
+ *
+ * This will mask the prior permission exception if it applies, but it's assumed that apps
+ * wouldn't call the APIs without the permission on prior versions, and so it's safe to ignore.
+ */
+ private void rethrowSecurityException(SecurityException e) {
+ if (!Compatibility.isChangeEnabled(THROW_SECURITY_EXCEPTIONS)) {
+ throw new IllegalStateException(e);
+ } else {
+ throw e;
+ }
+ }
}
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 205617022aa1..66a2b7a3ac66 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -549,9 +549,10 @@ public abstract class PackageManager {
public static final int MATCH_DEBUG_TRIAGED_MISSING = MATCH_DIRECT_BOOT_AUTO;
/**
- * Internal flag used to indicate that a package is a hidden system app.
+ * Internal {@link PackageInfo} flag used to indicate that a package is a hidden system app.
* @hide
*/
+ @SystemApi
public static final int MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS = 0x20000000;
/**
@@ -3328,6 +3329,15 @@ public abstract class PackageManager {
public static final int FLAG_PERMISSION_ONE_TIME = 1 << 16;
/**
+ * Permission flags: Reserved for use by the permission controller.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int FLAGS_PERMISSION_RESERVED_PERMISSIONCONTROLLER = 1 << 28 | 1 << 29
+ | 1 << 30 | 1 << 31;
+
+ /**
* Permission flags: Bitwise or of all permission flags allowing an
* exemption for a restricted permission.
* @hide
@@ -3517,6 +3527,44 @@ public abstract class PackageManager {
public static final long FILTER_APPLICATION_QUERY = 135549675L;
/** {@hide} */
+ @IntDef(prefix = {"SYSTEM_APP_STATE_"}, value = {
+ SYSTEM_APP_STATE_HIDDEN_UNTIL_INSTALLED_HIDDEN,
+ SYSTEM_APP_STATE_HIDDEN_UNTIL_INSTALLED_VISIBLE,
+ SYSTEM_APP_STATE_INSTALLED,
+ SYSTEM_APP_STATE_UNINSTALLED
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface SystemAppState {}
+
+ /**
+ * Constant for noting system app state as hidden before installation
+ * @hide
+ */
+ @SystemApi
+ public static final int SYSTEM_APP_STATE_HIDDEN_UNTIL_INSTALLED_HIDDEN = 0;
+
+ /**
+ * Constant for noting system app state as visible before installation
+ * @hide
+ */
+ @SystemApi
+ public static final int SYSTEM_APP_STATE_HIDDEN_UNTIL_INSTALLED_VISIBLE = 1;
+
+ /**
+ * Constant for noting system app state as installed
+ * @hide
+ */
+ @SystemApi
+ public static final int SYSTEM_APP_STATE_INSTALLED = 2;
+
+ /**
+ * Constant for noting system app state as not installed
+ * @hide
+ */
+ @SystemApi
+ public static final int SYSTEM_APP_STATE_UNINSTALLED = 3;
+
+ /** {@hide} */
public int getUserId() {
return UserHandle.myUserId();
}
@@ -6620,6 +6668,17 @@ public abstract class PackageManager {
@NonNull UserHandle userHandle);
/**
+ * Sets system app state
+ * @param packageName Package name of the app.
+ * @param state State of the app.
+ * @hide
+ */
+ @SystemApi
+ public void setSystemAppState(@NonNull String packageName, @SystemAppState int state) {
+ throw new RuntimeException("Not implemented. Must override in a subclass");
+ }
+
+ /**
* Return whether the device has been booted into safe mode.
*/
public abstract boolean isSafeMode();
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index fd4c26569873..dbfc65066c11 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -41,6 +41,7 @@ import static android.os.Build.VERSION_CODES.O;
import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_UNSPECIFIED;
+import android.annotation.AnyThread;
import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
@@ -1080,6 +1081,7 @@ public class PackageParser {
*
* @see #parsePackage(File, int, boolean)
*/
+ @AnyThread
public ParsedPackage parseParsedPackage(File packageFile, int flags, boolean useCaches)
throws PackageParserException {
ParsedPackage parsed = useCaches ? getCachedResult(packageFile, flags) : null;
diff --git a/core/java/android/content/pm/SuspendDialogInfo.java b/core/java/android/content/pm/SuspendDialogInfo.java
index 73b75df80e5b..851a08116f56 100644
--- a/core/java/android/content/pm/SuspendDialogInfo.java
+++ b/core/java/android/content/pm/SuspendDialogInfo.java
@@ -19,6 +19,7 @@ package android.content.pm;
import static android.content.res.Resources.ID_NULL;
import android.annotation.DrawableRes;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.StringRes;
@@ -36,20 +37,21 @@ import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlSerializer;
import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.Locale;
import java.util.Objects;
/**
* A container to describe the dialog to be shown when the user tries to launch a suspended
- * application.
- * The suspending app can customize the dialog's following attributes:
+ * application. The suspending app can customize the dialog's following attributes:
* <ul>
* <li>The dialog icon, by providing a resource id.
* <li>The title text, by providing a resource id.
* <li>The text of the dialog's body, by providing a resource id or a string.
- * <li>The text on the neutral button which starts the
- * {@link android.content.Intent#ACTION_SHOW_SUSPENDED_APP_DETAILS SHOW_SUSPENDED_APP_DETAILS}
- * activity, by providing a resource id.
+ * <li>The text on the neutral button by providing a resource id.
+ * <li>The action performed on tapping the neutral button. Only {@link #BUTTON_ACTION_UNSUSPEND}
+ * and {@link #BUTTON_ACTION_MORE_DETAILS} are currently supported.
* </ul>
* System defaults are used whenever any of these are not provided, or any of the provided resource
* ids cannot be resolved at the time of displaying the dialog.
@@ -67,12 +69,47 @@ public final class SuspendDialogInfo implements Parcelable {
private static final String XML_ATTR_DIALOG_MESSAGE_RES_ID = "dialogMessageResId";
private static final String XML_ATTR_DIALOG_MESSAGE = "dialogMessage";
private static final String XML_ATTR_BUTTON_TEXT_RES_ID = "buttonTextResId";
+ private static final String XML_ATTR_BUTTON_ACTION = "buttonAction";
private final int mIconResId;
private final int mTitleResId;
private final int mDialogMessageResId;
private final String mDialogMessage;
private final int mNeutralButtonTextResId;
+ private final int mNeutralButtonAction;
+
+ /**
+ * Used with {@link Builder#setNeutralButtonAction(int)} to create a neutral button that
+ * starts the {@link android.content.Intent#ACTION_SHOW_SUSPENDED_APP_DETAILS} activity.
+ * @see Builder#setNeutralButtonAction(int)
+ */
+ public static final int BUTTON_ACTION_MORE_DETAILS = 0;
+
+ /**
+ * Used with {@link Builder#setNeutralButtonAction(int)} to create a neutral button that
+ * unsuspends the app that the user was trying to launch and continues with the launch. The
+ * system also sends the broadcast
+ * {@link android.content.Intent#ACTION_PACKAGE_UNSUSPENDED_MANUALLY} to the suspending app
+ * when this happens.
+ * @see Builder#setNeutralButtonAction(int)
+ * @see android.content.Intent#ACTION_PACKAGE_UNSUSPENDED_MANUALLY
+ */
+ public static final int BUTTON_ACTION_UNSUSPEND = 1;
+
+ /**
+ * Button actions to specify what happens when the user taps on the neutral button.
+ * To be used with {@link Builder#setNeutralButtonAction(int)}.
+ *
+ * @hide
+ * @see Builder#setNeutralButtonAction(int)
+ */
+ @IntDef(flag = true, prefix = {"BUTTON_ACTION_"}, value = {
+ BUTTON_ACTION_MORE_DETAILS,
+ BUTTON_ACTION_UNSUSPEND
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ButtonAction {
+ }
/**
* @return the resource id of the icon to be used with the dialog
@@ -102,8 +139,8 @@ public final class SuspendDialogInfo implements Parcelable {
}
/**
- * @return the text to be shown in the dialog's body. Returns {@code null} if
- * {@link #getDialogMessageResId()} returns a valid resource id.
+ * @return the text to be shown in the dialog's body. Returns {@code null} if {@link
+ * #getDialogMessageResId()} returns a valid resource id
* @hide
*/
@Nullable
@@ -121,6 +158,15 @@ public final class SuspendDialogInfo implements Parcelable {
}
/**
+ * @return The {@link ButtonAction} that happens on tapping this button
+ * @hide
+ */
+ @ButtonAction
+ public int getNeutralButtonAction() {
+ return mNeutralButtonAction;
+ }
+
+ /**
* @hide
*/
public void saveToXml(XmlSerializer out) throws IOException {
@@ -138,6 +184,7 @@ public final class SuspendDialogInfo implements Parcelable {
if (mNeutralButtonTextResId != ID_NULL) {
XmlUtils.writeIntAttribute(out, XML_ATTR_BUTTON_TEXT_RES_ID, mNeutralButtonTextResId);
}
+ XmlUtils.writeIntAttribute(out, XML_ATTR_BUTTON_ACTION, mNeutralButtonAction);
}
/**
@@ -150,6 +197,8 @@ public final class SuspendDialogInfo implements Parcelable {
final int titleId = XmlUtils.readIntAttribute(in, XML_ATTR_TITLE_RES_ID, ID_NULL);
final int buttonTextId = XmlUtils.readIntAttribute(in, XML_ATTR_BUTTON_TEXT_RES_ID,
ID_NULL);
+ final int buttonAction = XmlUtils.readIntAttribute(in, XML_ATTR_BUTTON_ACTION,
+ BUTTON_ACTION_MORE_DETAILS);
final int dialogMessageResId = XmlUtils.readIntAttribute(
in, XML_ATTR_DIALOG_MESSAGE_RES_ID, ID_NULL);
final String dialogMessage = XmlUtils.readStringAttribute(in, XML_ATTR_DIALOG_MESSAGE);
@@ -168,6 +217,7 @@ public final class SuspendDialogInfo implements Parcelable {
} else if (dialogMessage != null) {
dialogInfoBuilder.setMessage(dialogMessage);
}
+ dialogInfoBuilder.setNeutralButtonAction(buttonAction);
} catch (Exception e) {
Slog.e(TAG, "Exception while parsing from xml. Some fields may default", e);
}
@@ -181,6 +231,7 @@ public final class SuspendDialogInfo implements Parcelable {
hashCode = 31 * hashCode + mNeutralButtonTextResId;
hashCode = 31 * hashCode + mDialogMessageResId;
hashCode = 31 * hashCode + Objects.hashCode(mDialogMessage);
+ hashCode = 31 * hashCode + mNeutralButtonAction;
return hashCode;
}
@@ -197,6 +248,7 @@ public final class SuspendDialogInfo implements Parcelable {
&& mTitleResId == otherDialogInfo.mTitleResId
&& mDialogMessageResId == otherDialogInfo.mDialogMessageResId
&& mNeutralButtonTextResId == otherDialogInfo.mNeutralButtonTextResId
+ && mNeutralButtonAction == otherDialogInfo.mNeutralButtonAction
&& Objects.equals(mDialogMessage, otherDialogInfo.mDialogMessage);
}
@@ -228,6 +280,8 @@ public final class SuspendDialogInfo implements Parcelable {
builder.append(mDialogMessage);
builder.append("\" ");
}
+ builder.append("mNeutralButtonAction = ");
+ builder.append(mNeutralButtonAction);
builder.append("}");
return builder.toString();
}
@@ -244,6 +298,7 @@ public final class SuspendDialogInfo implements Parcelable {
dest.writeInt(mDialogMessageResId);
dest.writeString(mDialogMessage);
dest.writeInt(mNeutralButtonTextResId);
+ dest.writeInt(mNeutralButtonAction);
}
private SuspendDialogInfo(Parcel source) {
@@ -252,6 +307,7 @@ public final class SuspendDialogInfo implements Parcelable {
mDialogMessageResId = source.readInt();
mDialogMessage = source.readString();
mNeutralButtonTextResId = source.readInt();
+ mNeutralButtonAction = source.readInt();
}
SuspendDialogInfo(Builder b) {
@@ -260,9 +316,11 @@ public final class SuspendDialogInfo implements Parcelable {
mDialogMessageResId = b.mDialogMessageResId;
mDialogMessage = (mDialogMessageResId == ID_NULL) ? b.mDialogMessage : null;
mNeutralButtonTextResId = b.mNeutralButtonTextResId;
+ mNeutralButtonAction = b.mNeutralButtonAction;
}
- public static final @android.annotation.NonNull Creator<SuspendDialogInfo> CREATOR = new Creator<SuspendDialogInfo>() {
+ public static final @NonNull Creator<SuspendDialogInfo> CREATOR =
+ new Creator<SuspendDialogInfo>() {
@Override
public SuspendDialogInfo createFromParcel(Parcel source) {
return new SuspendDialogInfo(source);
@@ -283,6 +341,7 @@ public final class SuspendDialogInfo implements Parcelable {
private int mTitleResId = ID_NULL;
private int mIconResId = ID_NULL;
private int mNeutralButtonTextResId = ID_NULL;
+ private int mNeutralButtonAction = BUTTON_ACTION_MORE_DETAILS;
/**
* Set the resource id of the icon to be used. If not provided, no icon will be shown.
@@ -333,8 +392,8 @@ public final class SuspendDialogInfo implements Parcelable {
/**
* Set the resource id of the dialog message to be shown. If no dialog message is provided
- * via either this method or {@link #setMessage(String)}, the system will use a
- * default message.
+ * via either this method or {@link #setMessage(String)}, the system will use a default
+ * message.
* <p>
* The system will use {@link android.content.res.Resources#getString(int, Object...)
* getString} to insert the suspended app name into the message, so an example format string
@@ -353,9 +412,10 @@ public final class SuspendDialogInfo implements Parcelable {
}
/**
- * Set the resource id of text to be shown on the neutral button. Tapping this button starts
- * the {@link android.content.Intent#ACTION_SHOW_SUSPENDED_APP_DETAILS} activity. If this is
- * not provided, the system will use a default text.
+ * Set the resource id of text to be shown on the neutral button. Tapping this button would
+ * perform the {@link ButtonAction action} specified through
+ * {@link #setNeutralButtonAction(int)}. If this is not provided, the system will use a
+ * default text.
*
* @param resId The resource id of the button text
* @return this builder object.
@@ -368,6 +428,22 @@ public final class SuspendDialogInfo implements Parcelable {
}
/**
+ * Set the action expected to happen on neutral button tap. Defaults to
+ * {@link #BUTTON_ACTION_MORE_DETAILS} if this is not provided.
+ *
+ * @param buttonAction Either {@link #BUTTON_ACTION_MORE_DETAILS} or
+ * {@link #BUTTON_ACTION_UNSUSPEND}.
+ * @return this builder object
+ */
+ @NonNull
+ public Builder setNeutralButtonAction(@ButtonAction int buttonAction) {
+ Preconditions.checkArgument(buttonAction == BUTTON_ACTION_MORE_DETAILS
+ || buttonAction == BUTTON_ACTION_UNSUSPEND, "Invalid button action");
+ mNeutralButtonAction = buttonAction;
+ return this;
+ }
+
+ /**
* Build the final object based on given inputs.
*
* @return The {@link SuspendDialogInfo} object built using this builder.
diff --git a/core/java/android/hardware/CameraStatus.java b/core/java/android/hardware/CameraStatus.java
index 08b5b776c94e..29802cb33c38 100644
--- a/core/java/android/hardware/CameraStatus.java
+++ b/core/java/android/hardware/CameraStatus.java
@@ -30,6 +30,7 @@ import android.os.Parcelable;
public class CameraStatus implements Parcelable {
public String cameraId;
public int status;
+ public String[] unavailablePhysicalCameras;
@Override
public int describeContents() {
@@ -40,11 +41,13 @@ public class CameraStatus implements Parcelable {
public void writeToParcel(Parcel out, int flags) {
out.writeString(cameraId);
out.writeInt(status);
+ out.writeStringArray(unavailablePhysicalCameras);
}
public void readFromParcel(Parcel in) {
cameraId = in.readString();
status = in.readInt();
+ unavailablePhysicalCameras = in.readStringArray();
}
public static final @android.annotation.NonNull Parcelable.Creator<CameraStatus> CREATOR =
diff --git a/core/java/android/hardware/Sensor.java b/core/java/android/hardware/Sensor.java
index a71a7b63f30e..cc4c45699bd4 100644
--- a/core/java/android/hardware/Sensor.java
+++ b/core/java/android/hardware/Sensor.java
@@ -693,6 +693,22 @@ public final class Sensor {
"android.sensor.accelerometer_uncalibrated";
/**
+ * A constant describing a hinge angle sensor.
+ *
+ * See {@link android.hardware.SensorEvent#values SensorEvent.values} for more details.
+ *
+ */
+ public static final int TYPE_HINGE_ANGLE = 36;
+
+ /**
+ * A constant string describing a hinge angle sensor.
+ *
+ * @see #TYPE_HINGE_ANGLE
+ *
+ */
+ public static final String STRING_TYPE_HINGE_ANGLE = "android.sensor.hinge_angle";
+
+ /**
* A constant describing all sensor types.
*/
@@ -811,6 +827,7 @@ public final class Sensor {
16, // skip over additional sensor info type
1, // SENSOR_TYPE_LOW_LATENCY_OFFBODY_DETECT
6, // SENSOR_TYPE_ACCELEROMETER_UNCALIBRATED
+ 1, // SENSOR_TYPE_HINGE_ANGLE
};
/**
@@ -1226,6 +1243,8 @@ public final class Sensor {
case TYPE_ACCELEROMETER_UNCALIBRATED:
mStringType = STRING_TYPE_ACCELEROMETER_UNCALIBRATED;
return true;
+ case TYPE_HINGE_ANGLE:
+ mStringType = STRING_TYPE_HINGE_ANGLE;
default:
return false;
}
diff --git a/core/java/android/hardware/SensorEvent.java b/core/java/android/hardware/SensorEvent.java
index 64c45bf290ec..5fbf0da8b5a5 100644
--- a/core/java/android/hardware/SensorEvent.java
+++ b/core/java/android/hardware/SensorEvent.java
@@ -630,6 +630,16 @@ public class SensorEvent {
* x_bias, y_bias, z_bias are the estimated biases.
* </p>
*
+ * <h4>{@link android.hardware.Sensor#TYPE_HINGE_ANGLE Sensor.TYPE_HINGE_ANGLE}:</h4>
+ *
+ * A sensor of this type measures the angle, in degrees, between two integral parts of the
+ * device. Movement of a hinge measured by this sensor type is expected to alter the ways in
+ * which the user may interact with the device, for example by unfolding or revealing a display.
+ *
+ * <ul>
+ * <li> values[0]: Measured hinge angle between 0 and 360 degrees inclusive</li>
+ * </ul>
+ *
* @see GeomagneticField
*/
public final float[] values;
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 7bddc1decae1..dfc4f0f6586c 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -2860,6 +2860,23 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
new Key<android.hardware.camera2.params.MandatoryStreamCombination[]>("android.scaler.mandatoryStreamCombinations", android.hardware.camera2.params.MandatoryStreamCombination[].class);
/**
+ * <p>List of rotate-and-crop modes for {@link CaptureRequest#SCALER_ROTATE_AND_CROP android.scaler.rotateAndCrop} that are supported by this camera device.</p>
+ * <p>This entry lists the valid modes for {@link CaptureRequest#SCALER_ROTATE_AND_CROP android.scaler.rotateAndCrop} for this camera device.</p>
+ * <p>Starting with API level 30, all devices will list at least <code>ROTATE_AND_CROP_NONE</code>.
+ * Devices with support for rotate-and-crop will additionally list at least
+ * <code>ROTATE_AND_CROP_AUTO</code> and <code>ROTATE_AND_CROP_90</code>.</p>
+ * <p><b>Range of valid values:</b><br>
+ * Any value listed in {@link CaptureRequest#SCALER_ROTATE_AND_CROP android.scaler.rotateAndCrop}</p>
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ *
+ * @see CaptureRequest#SCALER_ROTATE_AND_CROP
+ */
+ @PublicKey
+ @NonNull
+ public static final Key<int[]> SCALER_AVAILABLE_ROTATE_AND_CROP_MODES =
+ new Key<int[]>("android.scaler.availableRotateAndCropModes", int[].class);
+
+ /**
* <p>The area of the image sensor which corresponds to active pixels after any geometric
* distortion correction has been applied.</p>
* <p>This is the rectangle representing the size of the active region of the sensor (i.e.
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index 9b58578e3811..55025f0411f9 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -718,6 +718,52 @@ public final class CameraManager {
public void onCameraAccessPrioritiesChanged() {
// default empty implementation
}
+
+ /**
+ * A physical camera has become available for use again.
+ *
+ * <p>By default, all of the physical cameras of a logical multi-camera are
+ * available, so {@link #onPhysicalCameraAvailable} is not called for any of the physical
+ * cameras of a logical multi-camera, when {@link #onCameraAvailable} for the logical
+ * multi-camera is invoked. However, if some specific physical cameras are unavailable
+ * to begin with, {@link #onPhysicalCameraUnavailable} may be invoked after
+ * {@link #onCameraAvailable}.</p>
+ *
+ * <p>The default implementation of this method does nothing.</p>
+ *
+ * @param cameraId The unique identifier of the logical multi-camera.
+ * @param physicalCameraId The unique identifier of the physical camera.
+ *
+ * @see #onCameraAvailable
+ * @see #onPhysicalCameraUnavailable
+ */
+ public void onPhysicalCameraAvailable(@NonNull String cameraId,
+ @NonNull String physicalCameraId) {
+ // default empty implementation
+ }
+
+ /**
+ * A previously-available physical camera has become unavailable for use.
+ *
+ * <p>By default, all of the physical cameras of a logical multi-camera are
+ * available, so {@link #onPhysicalCameraAvailable} is not called for any of the physical
+ * cameras of a logical multi-camera, when {@link #onCameraAvailable} for the logical
+ * multi-camera is invoked. If some specific physical cameras are unavailable
+ * to begin with, {@link #onPhysicalCameraUnavailable} may be invoked after
+ * {@link #onCameraAvailable}.</p>
+ *
+ * <p>The default implementation of this method does nothing.</p>
+ *
+ * @param cameraId The unique identifier of the logical multi-camera.
+ * @param physicalCameraId The unique identifier of the physical camera.
+ *
+ * @see #onCameraAvailable
+ * @see #onPhysicalCameraAvailable
+ */
+ public void onPhysicalCameraUnavailable(@NonNull String cameraId,
+ @NonNull String physicalCameraId) {
+ // default empty implementation
+ }
}
/**
@@ -914,6 +960,9 @@ public final class CameraManager {
private final ScheduledExecutorService mScheduler = Executors.newScheduledThreadPool(1);
// Camera ID -> Status map
private final ArrayMap<String, Integer> mDeviceStatus = new ArrayMap<String, Integer>();
+ // Camera ID -> (physical camera ID -> Status map)
+ private final ArrayMap<String, ArrayList<String>> mUnavailablePhysicalDevices =
+ new ArrayMap<String, ArrayList<String>>();
// Registered availablility callbacks and their executors
private final ArrayMap<AvailabilityCallback, Executor> mCallbackMap =
@@ -1003,6 +1052,14 @@ public final class CameraManager {
CameraStatus[] cameraStatuses = cameraService.addListener(this);
for (CameraStatus c : cameraStatuses) {
onStatusChangedLocked(c.status, c.cameraId);
+
+ if (c.unavailablePhysicalCameras != null) {
+ for (String unavailPhysicalCamera : c.unavailablePhysicalCameras) {
+ onPhysicalCameraStatusChangedLocked(
+ ICameraServiceListener.STATUS_NOT_PRESENT,
+ c.cameraId, unavailPhysicalCamera);
+ }
+ }
}
mCameraService = cameraService;
} catch(ServiceSpecificException e) {
@@ -1086,6 +1143,10 @@ public final class CameraManager {
public void onStatusChanged(int status, String id) throws RemoteException {
}
@Override
+ public void onPhysicalCameraStatusChanged(int status,
+ String id, String physicalId) throws RemoteException {
+ }
+ @Override
public void onTorchStatusChanged(int status, String id) throws RemoteException {
}
@Override
@@ -1236,7 +1297,7 @@ public final class CameraManager {
}
private void postSingleUpdate(final AvailabilityCallback callback, final Executor executor,
- final String id, final int status) {
+ final String id, final String physicalId, final int status) {
if (isAvailable(status)) {
final long ident = Binder.clearCallingIdentity();
try {
@@ -1244,7 +1305,11 @@ public final class CameraManager {
new Runnable() {
@Override
public void run() {
- callback.onCameraAvailable(id);
+ if (physicalId == null) {
+ callback.onCameraAvailable(id);
+ } else {
+ callback.onPhysicalCameraAvailable(id, physicalId);
+ }
}
});
} finally {
@@ -1257,7 +1322,11 @@ public final class CameraManager {
new Runnable() {
@Override
public void run() {
- callback.onCameraUnavailable(id);
+ if (physicalId == null) {
+ callback.onCameraUnavailable(id);
+ } else {
+ callback.onPhysicalCameraUnavailable(id, physicalId);
+ }
}
});
} finally {
@@ -1304,7 +1373,16 @@ public final class CameraManager {
for (int i = 0; i < mDeviceStatus.size(); i++) {
String id = mDeviceStatus.keyAt(i);
Integer status = mDeviceStatus.valueAt(i);
- postSingleUpdate(callback, executor, id, status);
+ postSingleUpdate(callback, executor, id, null /*physicalId*/, status);
+
+ // Send the NOT_PRESENT state for unavailable physical cameras
+ if (isAvailable(status) && mUnavailablePhysicalDevices.containsKey(id)) {
+ ArrayList<String> unavailableIds = mUnavailablePhysicalDevices.get(id);
+ for (String unavailableId : unavailableIds) {
+ postSingleUpdate(callback, executor, id, unavailableId,
+ ICameraServiceListener.STATUS_NOT_PRESENT);
+ }
+ }
}
}
@@ -1323,8 +1401,12 @@ public final class CameraManager {
Integer oldStatus;
if (status == ICameraServiceListener.STATUS_NOT_PRESENT) {
oldStatus = mDeviceStatus.remove(id);
+ mUnavailablePhysicalDevices.remove(id);
} else {
oldStatus = mDeviceStatus.put(id, status);
+ if (oldStatus == null) {
+ mUnavailablePhysicalDevices.put(id, new ArrayList<String>());
+ }
}
if (oldStatus != null && oldStatus == status) {
@@ -1366,10 +1448,62 @@ public final class CameraManager {
Executor executor = mCallbackMap.valueAt(i);
final AvailabilityCallback callback = mCallbackMap.keyAt(i);
- postSingleUpdate(callback, executor, id, status);
+ postSingleUpdate(callback, executor, id, null /*physicalId*/, status);
}
} // onStatusChangedLocked
+ private void onPhysicalCameraStatusChangedLocked(int status,
+ String id, String physicalId) {
+ if (DEBUG) {
+ Log.v(TAG,
+ String.format("Camera id %s physical camera id %s has status "
+ + "changed to 0x%x", id, physicalId, status));
+ }
+
+ if (!validStatus(status)) {
+ Log.e(TAG, String.format(
+ "Ignoring invalid device %s physical device %s status 0x%x", id,
+ physicalId, status));
+ return;
+ }
+
+ //TODO: Do we need to treat this as error?
+ if (!mDeviceStatus.containsKey(id) || !isAvailable(mDeviceStatus.get(id))
+ || !mUnavailablePhysicalDevices.containsKey(id)) {
+ Log.e(TAG, String.format("Camera %s is not available. Ignore physical camera "
+ + "status change", id));
+ return;
+ }
+
+ ArrayList<String> unavailablePhysicalDevices = mUnavailablePhysicalDevices.get(id);
+ if (!isAvailable(status)
+ && !unavailablePhysicalDevices.contains(physicalId)) {
+ unavailablePhysicalDevices.add(physicalId);
+ } else if (isAvailable(status)
+ && unavailablePhysicalDevices.contains(physicalId)) {
+ unavailablePhysicalDevices.remove(physicalId);
+ } else {
+ if (DEBUG) {
+ Log.v(TAG,
+ String.format(
+ "Physical camera device status was previously available (%b), "
+ + " and is now again available (%b)"
+ + "so no new client visible update will be sent",
+ !unavailablePhysicalDevices.contains(physicalId),
+ isAvailable(status)));
+ }
+ return;
+ }
+
+ final int callbackCount = mCallbackMap.size();
+ for (int i = 0; i < callbackCount; i++) {
+ Executor executor = mCallbackMap.valueAt(i);
+ final AvailabilityCallback callback = mCallbackMap.keyAt(i);
+
+ postSingleUpdate(callback, executor, id, physicalId, status);
+ }
+ } // onPhysicalCameraStatusChangedLocked
+
private void updateTorchCallbackLocked(TorchCallback callback, Executor executor) {
for (int i = 0; i < mTorchStatus.size(); i++) {
String id = mTorchStatus.keyAt(i);
@@ -1478,6 +1612,14 @@ public final class CameraManager {
}
@Override
+ public void onPhysicalCameraStatusChanged(int status, String cameraId,
+ String physicalCameraId) throws RemoteException {
+ synchronized (mLock) {
+ onPhysicalCameraStatusChangedLocked(status, cameraId, physicalCameraId);
+ }
+ }
+
+ @Override
public void onTorchStatusChanged(int status, String cameraId) throws RemoteException {
synchronized (mLock) {
onTorchStatusChangedLocked(status, cameraId);
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index 89dac2aef68f..9bef2e18db08 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -865,14 +865,20 @@ public abstract class CameraMetadata<TKey> {
* <p>The camera device is a logical camera backed by two or more physical cameras.</p>
* <p>In API level 28, the physical cameras must also be exposed to the application via
* {@link android.hardware.camera2.CameraManager#getCameraIdList }.</p>
- * <p>Starting from API level 29, some or all physical cameras may not be independently
- * exposed to the application, in which case the physical camera IDs will not be
- * available in {@link android.hardware.camera2.CameraManager#getCameraIdList }. But the
+ * <p>Starting from API level 29:</p>
+ * <ul>
+ * <li>Some or all physical cameras may not be independently exposed to the application,
+ * in which case the physical camera IDs will not be available in
+ * {@link android.hardware.camera2.CameraManager#getCameraIdList }. But the
* application can still query the physical cameras' characteristics by calling
- * {@link android.hardware.camera2.CameraManager#getCameraCharacteristics }. Additionally,
- * if a physical camera is hidden from camera ID list, the mandatory stream combinations
- * for that physical camera must be supported through the logical camera using physical
- * streams.</p>
+ * {@link android.hardware.camera2.CameraManager#getCameraCharacteristics }.</li>
+ * <li>If a physical camera is hidden from camera ID list, the mandatory stream
+ * combinations for that physical camera must be supported through the logical camera
+ * using physical streams. One exception is that in API level 30, a physical camera
+ * may become unavailable via
+ * {@link CameraManager.AvailabilityCallback#onPhysicalCameraUnavailable }
+ * callback.</li>
+ * </ul>
* <p>Combinations of logical and physical streams, or physical streams from different
* physical cameras are not guaranteed. However, if the camera device supports
* {@link CameraDevice#isSessionConfigurationSupported },
@@ -2743,6 +2749,56 @@ public abstract class CameraMetadata<TKey> {
public static final int NOISE_REDUCTION_MODE_ZERO_SHUTTER_LAG = 4;
//
+ // Enumeration values for CaptureRequest#SCALER_ROTATE_AND_CROP
+ //
+
+ /**
+ * <p>No rotate and crop is applied. Processed outputs are in the sensor orientation.</p>
+ * @see CaptureRequest#SCALER_ROTATE_AND_CROP
+ */
+ public static final int SCALER_ROTATE_AND_CROP_NONE = 0;
+
+ /**
+ * <p>Processed images are rotated by 90 degrees clockwise, and then cropped
+ * to the original aspect ratio.</p>
+ * @see CaptureRequest#SCALER_ROTATE_AND_CROP
+ */
+ public static final int SCALER_ROTATE_AND_CROP_90 = 1;
+
+ /**
+ * <p>Processed images are rotated by 180 degrees. Since the aspect ratio does not
+ * change, no cropping is performed.</p>
+ * @see CaptureRequest#SCALER_ROTATE_AND_CROP
+ */
+ public static final int SCALER_ROTATE_AND_CROP_180 = 2;
+
+ /**
+ * <p>Processed images are rotated by 270 degrees clockwise, and then cropped
+ * to the original aspect ratio.</p>
+ * @see CaptureRequest#SCALER_ROTATE_AND_CROP
+ */
+ public static final int SCALER_ROTATE_AND_CROP_270 = 3;
+
+ /**
+ * <p>The camera API automatically selects the best concrete value for
+ * rotate-and-crop based on the application's support for resizability and the current
+ * multi-window mode.</p>
+ * <p>If the application does not support resizing but the display mode for its main
+ * Activity is not in a typical orientation, the camera API will set <code>ROTATE_AND_CROP_90</code>
+ * or some other supported rotation value, depending on device configuration,
+ * to ensure preview and captured images are correctly shown to the user. Otherwise,
+ * <code>ROTATE_AND_CROP_NONE</code> will be selected.</p>
+ * <p>When a value other than NONE is selected, several metadata fields will also be parsed
+ * differently to ensure that coordinates are correctly handled for features like drawing
+ * face detection boxes or passing in tap-to-focus coordinates. The camera API will
+ * convert positions in the active array coordinate system to/from the cropped-and-rotated
+ * coordinate system to make the operation transparent for applications.</p>
+ * <p>No coordinate mapping will be done when the application selects a non-AUTO mode.</p>
+ * @see CaptureRequest#SCALER_ROTATE_AND_CROP
+ */
+ public static final int SCALER_ROTATE_AND_CROP_AUTO = 4;
+
+ //
// Enumeration values for CaptureRequest#SENSOR_TEST_PATTERN_MODE
//
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index b9af8f517985..30fbde255201 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -2845,6 +2845,99 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
new Key<android.graphics.Rect>("android.scaler.cropRegion", android.graphics.Rect.class);
/**
+ * <p>Whether a rotation-and-crop operation is applied to processed
+ * outputs from the camera.</p>
+ * <p>This control is primarily intended to help camera applications with no support for
+ * multi-window modes to work correctly on devices where multi-window scenarios are
+ * unavoidable, such as foldables or other devices with variable display geometry or more
+ * free-form window placement (such as laptops, which often place portrait-orientation apps
+ * in landscape with pillarboxing).</p>
+ * <p>If supported, the default value is <code>ROTATE_AND_CROP_AUTO</code>, which allows the camera API
+ * to enable backwards-compatibility support for applications that do not support resizing
+ * / multi-window modes, when the device is in fact in a multi-window mode (such as inset
+ * portrait on laptops, or on a foldable device in some fold states). In addition,
+ * <code>ROTATE_AND_CROP_NONE</code> and <code>ROTATE_AND_CROP_90</code> will always be available if this control
+ * is supported by the device. If not supported, devices API level 30 or higher will always
+ * list only <code>ROTATE_AND_CROP_NONE</code>.</p>
+ * <p>When <code>CROP_AUTO</code> is in use, and the camera API activates backward-compatibility mode,
+ * several metadata fields will also be parsed differently to ensure that coordinates are
+ * correctly handled for features like drawing face detection boxes or passing in
+ * tap-to-focus coordinates. The camera API will convert positions in the active array
+ * coordinate system to/from the cropped-and-rotated coordinate system to make the
+ * operation transparent for applications. The following controls are affected:</p>
+ * <ul>
+ * <li>{@link CaptureRequest#CONTROL_AE_REGIONS android.control.aeRegions}</li>
+ * <li>{@link CaptureRequest#CONTROL_AF_REGIONS android.control.afRegions}</li>
+ * <li>{@link CaptureRequest#CONTROL_AWB_REGIONS android.control.awbRegions}</li>
+ * <li>{@link CaptureResult#STATISTICS_FACES android.statistics.faces}</li>
+ * </ul>
+ * <p>Capture results will contain the actual value selected by the API;
+ * <code>ROTATE_AND_CROP_AUTO</code> will never be seen in a capture result.</p>
+ * <p>Applications can also select their preferred cropping mode, either to opt out of the
+ * backwards-compatibility treatment, or to use the cropping feature themselves as needed.
+ * In this case, no coordinate translation will be done automatically, and all controls
+ * will continue to use the normal active array coordinates.</p>
+ * <p>Cropping and rotating is done after the application of digital zoom (via either
+ * {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} or {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio}), but before each individual
+ * output is further cropped and scaled. It only affects processed outputs such as
+ * YUV, PRIVATE, and JPEG. It has no effect on RAW outputs.</p>
+ * <p>When <code>CROP_90</code> or <code>CROP_270</code> are selected, there is a significant loss to the field of
+ * view. For example, with a 4:3 aspect ratio output of 1600x1200, <code>CROP_90</code> will still
+ * produce 1600x1200 output, but these buffers are cropped from a vertical 3:4 slice at the
+ * center of the 4:3 area, then rotated to be 4:3, and then upscaled to 1600x1200. Only
+ * 56.25% of the original FOV is still visible. In general, for an aspect ratio of <code>w:h</code>,
+ * the crop and rotate operation leaves <code>(h/w)^2</code> of the field of view visible. For 16:9,
+ * this is ~31.6%.</p>
+ * <p>As a visual example, the figure below shows the effect of <code>ROTATE_AND_CROP_90</code> on the
+ * outputs for the following parameters:</p>
+ * <ul>
+ * <li>Sensor active array: <code>2000x1500</code></li>
+ * <li>Crop region: top-left: <code>(500, 375)</code>, size: <code>(1000, 750)</code> (4:3 aspect ratio)</li>
+ * <li>Output streams: YUV <code>640x480</code> and YUV <code>1280x720</code></li>
+ * <li><code>ROTATE_AND_CROP_90</code></li>
+ * </ul>
+ * <p><img alt="Effect of ROTATE_AND_CROP_90" src="/reference/images/camera2/metadata/android.scaler.rotateAndCrop/crop-region-rotate-90-43-ratio.png" /></p>
+ * <p>With these settings, the regions of the active array covered by the output streams are:</p>
+ * <ul>
+ * <li>640x480 stream crop: top-left: <code>(219, 375)</code>, size: <code>(562, 750)</code></li>
+ * <li>1280x720 stream crop: top-left: <code>(289, 375)</code>, size: <code>(422, 750)</code></li>
+ * </ul>
+ * <p>Since the buffers are rotated, the buffers as seen by the application are:</p>
+ * <ul>
+ * <li>640x480 stream: top-left: <code>(781, 375)</code> on active array, size: <code>(640, 480)</code>, downscaled 1.17x from sensor pixels</li>
+ * <li>1280x720 stream: top-left: <code>(711, 375)</code> on active array, size: <code>(1280, 720)</code>, upscaled 1.71x from sensor pixels</li>
+ * </ul>
+ * <p><b>Possible values:</b>
+ * <ul>
+ * <li>{@link #SCALER_ROTATE_AND_CROP_NONE NONE}</li>
+ * <li>{@link #SCALER_ROTATE_AND_CROP_90 90}</li>
+ * <li>{@link #SCALER_ROTATE_AND_CROP_180 180}</li>
+ * <li>{@link #SCALER_ROTATE_AND_CROP_270 270}</li>
+ * <li>{@link #SCALER_ROTATE_AND_CROP_AUTO AUTO}</li>
+ * </ul></p>
+ * <p><b>Available values for this device:</b><br>
+ * {@link CameraCharacteristics#SCALER_AVAILABLE_ROTATE_AND_CROP_MODES android.scaler.availableRotateAndCropModes}</p>
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ *
+ * @see CaptureRequest#CONTROL_AE_REGIONS
+ * @see CaptureRequest#CONTROL_AF_REGIONS
+ * @see CaptureRequest#CONTROL_AWB_REGIONS
+ * @see CaptureRequest#CONTROL_ZOOM_RATIO
+ * @see CameraCharacteristics#SCALER_AVAILABLE_ROTATE_AND_CROP_MODES
+ * @see CaptureRequest#SCALER_CROP_REGION
+ * @see CaptureResult#STATISTICS_FACES
+ * @see #SCALER_ROTATE_AND_CROP_NONE
+ * @see #SCALER_ROTATE_AND_CROP_90
+ * @see #SCALER_ROTATE_AND_CROP_180
+ * @see #SCALER_ROTATE_AND_CROP_270
+ * @see #SCALER_ROTATE_AND_CROP_AUTO
+ */
+ @PublicKey
+ @NonNull
+ public static final Key<Integer> SCALER_ROTATE_AND_CROP =
+ new Key<Integer>("android.scaler.rotateAndCrop", int.class);
+
+ /**
* <p>Duration each pixel is exposed to
* light.</p>
* <p>If the sensor can't expose this exact duration, it will shorten the
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index 6f0d1358a1b7..641a3e1515b3 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -3491,6 +3491,99 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> {
new Key<android.graphics.Rect>("android.scaler.cropRegion", android.graphics.Rect.class);
/**
+ * <p>Whether a rotation-and-crop operation is applied to processed
+ * outputs from the camera.</p>
+ * <p>This control is primarily intended to help camera applications with no support for
+ * multi-window modes to work correctly on devices where multi-window scenarios are
+ * unavoidable, such as foldables or other devices with variable display geometry or more
+ * free-form window placement (such as laptops, which often place portrait-orientation apps
+ * in landscape with pillarboxing).</p>
+ * <p>If supported, the default value is <code>ROTATE_AND_CROP_AUTO</code>, which allows the camera API
+ * to enable backwards-compatibility support for applications that do not support resizing
+ * / multi-window modes, when the device is in fact in a multi-window mode (such as inset
+ * portrait on laptops, or on a foldable device in some fold states). In addition,
+ * <code>ROTATE_AND_CROP_NONE</code> and <code>ROTATE_AND_CROP_90</code> will always be available if this control
+ * is supported by the device. If not supported, devices API level 30 or higher will always
+ * list only <code>ROTATE_AND_CROP_NONE</code>.</p>
+ * <p>When <code>CROP_AUTO</code> is in use, and the camera API activates backward-compatibility mode,
+ * several metadata fields will also be parsed differently to ensure that coordinates are
+ * correctly handled for features like drawing face detection boxes or passing in
+ * tap-to-focus coordinates. The camera API will convert positions in the active array
+ * coordinate system to/from the cropped-and-rotated coordinate system to make the
+ * operation transparent for applications. The following controls are affected:</p>
+ * <ul>
+ * <li>{@link CaptureRequest#CONTROL_AE_REGIONS android.control.aeRegions}</li>
+ * <li>{@link CaptureRequest#CONTROL_AF_REGIONS android.control.afRegions}</li>
+ * <li>{@link CaptureRequest#CONTROL_AWB_REGIONS android.control.awbRegions}</li>
+ * <li>{@link CaptureResult#STATISTICS_FACES android.statistics.faces}</li>
+ * </ul>
+ * <p>Capture results will contain the actual value selected by the API;
+ * <code>ROTATE_AND_CROP_AUTO</code> will never be seen in a capture result.</p>
+ * <p>Applications can also select their preferred cropping mode, either to opt out of the
+ * backwards-compatibility treatment, or to use the cropping feature themselves as needed.
+ * In this case, no coordinate translation will be done automatically, and all controls
+ * will continue to use the normal active array coordinates.</p>
+ * <p>Cropping and rotating is done after the application of digital zoom (via either
+ * {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} or {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio}), but before each individual
+ * output is further cropped and scaled. It only affects processed outputs such as
+ * YUV, PRIVATE, and JPEG. It has no effect on RAW outputs.</p>
+ * <p>When <code>CROP_90</code> or <code>CROP_270</code> are selected, there is a significant loss to the field of
+ * view. For example, with a 4:3 aspect ratio output of 1600x1200, <code>CROP_90</code> will still
+ * produce 1600x1200 output, but these buffers are cropped from a vertical 3:4 slice at the
+ * center of the 4:3 area, then rotated to be 4:3, and then upscaled to 1600x1200. Only
+ * 56.25% of the original FOV is still visible. In general, for an aspect ratio of <code>w:h</code>,
+ * the crop and rotate operation leaves <code>(h/w)^2</code> of the field of view visible. For 16:9,
+ * this is ~31.6%.</p>
+ * <p>As a visual example, the figure below shows the effect of <code>ROTATE_AND_CROP_90</code> on the
+ * outputs for the following parameters:</p>
+ * <ul>
+ * <li>Sensor active array: <code>2000x1500</code></li>
+ * <li>Crop region: top-left: <code>(500, 375)</code>, size: <code>(1000, 750)</code> (4:3 aspect ratio)</li>
+ * <li>Output streams: YUV <code>640x480</code> and YUV <code>1280x720</code></li>
+ * <li><code>ROTATE_AND_CROP_90</code></li>
+ * </ul>
+ * <p><img alt="Effect of ROTATE_AND_CROP_90" src="/reference/images/camera2/metadata/android.scaler.rotateAndCrop/crop-region-rotate-90-43-ratio.png" /></p>
+ * <p>With these settings, the regions of the active array covered by the output streams are:</p>
+ * <ul>
+ * <li>640x480 stream crop: top-left: <code>(219, 375)</code>, size: <code>(562, 750)</code></li>
+ * <li>1280x720 stream crop: top-left: <code>(289, 375)</code>, size: <code>(422, 750)</code></li>
+ * </ul>
+ * <p>Since the buffers are rotated, the buffers as seen by the application are:</p>
+ * <ul>
+ * <li>640x480 stream: top-left: <code>(781, 375)</code> on active array, size: <code>(640, 480)</code>, downscaled 1.17x from sensor pixels</li>
+ * <li>1280x720 stream: top-left: <code>(711, 375)</code> on active array, size: <code>(1280, 720)</code>, upscaled 1.71x from sensor pixels</li>
+ * </ul>
+ * <p><b>Possible values:</b>
+ * <ul>
+ * <li>{@link #SCALER_ROTATE_AND_CROP_NONE NONE}</li>
+ * <li>{@link #SCALER_ROTATE_AND_CROP_90 90}</li>
+ * <li>{@link #SCALER_ROTATE_AND_CROP_180 180}</li>
+ * <li>{@link #SCALER_ROTATE_AND_CROP_270 270}</li>
+ * <li>{@link #SCALER_ROTATE_AND_CROP_AUTO AUTO}</li>
+ * </ul></p>
+ * <p><b>Available values for this device:</b><br>
+ * {@link CameraCharacteristics#SCALER_AVAILABLE_ROTATE_AND_CROP_MODES android.scaler.availableRotateAndCropModes}</p>
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ *
+ * @see CaptureRequest#CONTROL_AE_REGIONS
+ * @see CaptureRequest#CONTROL_AF_REGIONS
+ * @see CaptureRequest#CONTROL_AWB_REGIONS
+ * @see CaptureRequest#CONTROL_ZOOM_RATIO
+ * @see CameraCharacteristics#SCALER_AVAILABLE_ROTATE_AND_CROP_MODES
+ * @see CaptureRequest#SCALER_CROP_REGION
+ * @see CaptureResult#STATISTICS_FACES
+ * @see #SCALER_ROTATE_AND_CROP_NONE
+ * @see #SCALER_ROTATE_AND_CROP_90
+ * @see #SCALER_ROTATE_AND_CROP_180
+ * @see #SCALER_ROTATE_AND_CROP_270
+ * @see #SCALER_ROTATE_AND_CROP_AUTO
+ */
+ @PublicKey
+ @NonNull
+ public static final Key<Integer> SCALER_ROTATE_AND_CROP =
+ new Key<Integer>("android.scaler.rotateAndCrop", int.class);
+
+ /**
* <p>Duration each pixel is exposed to
* light.</p>
* <p>If the sensor can't expose this exact duration, it will shorten the
diff --git a/core/java/android/hardware/location/ContextHubClient.java b/core/java/android/hardware/location/ContextHubClient.java
index c6a5dd0d048d..43480ab9cc44 100644
--- a/core/java/android/hardware/location/ContextHubClient.java
+++ b/core/java/android/hardware/location/ContextHubClient.java
@@ -136,7 +136,10 @@ public class ContextHubClient implements Closeable {
* @see NanoAppMessage
* @see ContextHubTransaction.Result
*/
- @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.LOCATION_HARDWARE,
+ android.Manifest.permission.ACCESS_CONTEXT_HUB
+ })
@ContextHubTransaction.Result
public int sendMessageToNanoApp(@NonNull NanoAppMessage message) {
Objects.requireNonNull(message, "NanoAppMessage cannot be null");
diff --git a/core/java/android/hardware/location/ContextHubManager.java b/core/java/android/hardware/location/ContextHubManager.java
index a51d2c929a2c..1001f800df3c 100644
--- a/core/java/android/hardware/location/ContextHubManager.java
+++ b/core/java/android/hardware/location/ContextHubManager.java
@@ -44,7 +44,9 @@ import java.util.concurrent.Executor;
* A class that exposes the Context hubs on a device to applications.
*
* Please note that this class is not expected to be used by unbundled applications. Also, calling
- * applications are expected to have LOCATION_HARDWARE permissions to use this class.
+ * applications are expected to have LOCATION_HARDWARE or ACCESS_CONTEXT_HUB permissions to use this
+ * class. Use of LOCATION_HARDWARE to enable access to these APIs is deprecated and may be removed
+ * in the future - all applications are recommended to move to the ACCESS_CONTEXT_HUB permission.
*
* @hide
*/
@@ -196,7 +198,10 @@ public final class ContextHubManager {
* new APIs.
*/
@Deprecated
- @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.LOCATION_HARDWARE,
+ android.Manifest.permission.ACCESS_CONTEXT_HUB
+ })
public int[] getContextHubHandles() {
try {
return mService.getContextHubHandles();
@@ -217,7 +222,10 @@ public final class ContextHubManager {
* new APIs.
*/
@Deprecated
- @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.LOCATION_HARDWARE,
+ android.Manifest.permission.ACCESS_CONTEXT_HUB
+ })
public ContextHubInfo getContextHubInfo(int hubHandle) {
try {
return mService.getContextHubInfo(hubHandle);
@@ -248,7 +256,10 @@ public final class ContextHubManager {
* @deprecated Use {@link #loadNanoApp(ContextHubInfo, NanoAppBinary)} instead.
*/
@Deprecated
- @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.LOCATION_HARDWARE,
+ android.Manifest.permission.ACCESS_CONTEXT_HUB
+ })
public int loadNanoApp(int hubHandle, @NonNull NanoApp app) {
try {
return mService.loadNanoApp(hubHandle, app);
@@ -275,7 +286,10 @@ public final class ContextHubManager {
* @deprecated Use {@link #unloadNanoApp(ContextHubInfo, long)} instead.
*/
@Deprecated
- @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.LOCATION_HARDWARE,
+ android.Manifest.permission.ACCESS_CONTEXT_HUB
+ })
public int unloadNanoApp(int nanoAppHandle) {
try {
return mService.unloadNanoApp(nanoAppHandle);
@@ -315,7 +329,10 @@ public final class ContextHubManager {
* for loaded nanoapps.
*/
@Deprecated
- @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.LOCATION_HARDWARE,
+ android.Manifest.permission.ACCESS_CONTEXT_HUB
+ })
@Nullable public NanoAppInstanceInfo getNanoAppInstanceInfo(int nanoAppHandle) {
try {
return mService.getNanoAppInstanceInfo(nanoAppHandle);
@@ -338,7 +355,10 @@ public final class ContextHubManager {
* for loaded nanoapps.
*/
@Deprecated
- @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.LOCATION_HARDWARE,
+ android.Manifest.permission.ACCESS_CONTEXT_HUB
+ })
@NonNull public int[] findNanoAppOnHub(int hubHandle, @NonNull NanoAppFilter filter) {
try {
return mService.findNanoAppOnHub(hubHandle, filter);
@@ -373,7 +393,10 @@ public final class ContextHubManager {
* or {@link #createClient(ContextHubInfo, ContextHubClientCallback)}.
*/
@Deprecated
- @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.LOCATION_HARDWARE,
+ android.Manifest.permission.ACCESS_CONTEXT_HUB
+ })
public int sendMessage(int hubHandle, int nanoAppHandle, @NonNull ContextHubMessage message) {
try {
return mService.sendMessage(hubHandle, nanoAppHandle, message);
@@ -389,7 +412,10 @@ public final class ContextHubManager {
*
* @see ContextHubInfo
*/
- @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.LOCATION_HARDWARE,
+ android.Manifest.permission.ACCESS_CONTEXT_HUB
+ })
@NonNull public List<ContextHubInfo> getContextHubs() {
try {
return mService.getContextHubs();
@@ -466,7 +492,10 @@ public final class ContextHubManager {
*
* @see NanoAppBinary
*/
- @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.LOCATION_HARDWARE,
+ android.Manifest.permission.ACCESS_CONTEXT_HUB
+ })
@NonNull public ContextHubTransaction<Void> loadNanoApp(
@NonNull ContextHubInfo hubInfo, @NonNull NanoAppBinary appBinary) {
Objects.requireNonNull(hubInfo, "ContextHubInfo cannot be null");
@@ -495,7 +524,10 @@ public final class ContextHubManager {
*
* @throws NullPointerException if hubInfo is null
*/
- @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.LOCATION_HARDWARE,
+ android.Manifest.permission.ACCESS_CONTEXT_HUB
+ })
@NonNull public ContextHubTransaction<Void> unloadNanoApp(
@NonNull ContextHubInfo hubInfo, long nanoAppId) {
Objects.requireNonNull(hubInfo, "ContextHubInfo cannot be null");
@@ -523,7 +555,10 @@ public final class ContextHubManager {
*
* @throws NullPointerException if hubInfo is null
*/
- @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.LOCATION_HARDWARE,
+ android.Manifest.permission.ACCESS_CONTEXT_HUB
+ })
@NonNull public ContextHubTransaction<Void> enableNanoApp(
@NonNull ContextHubInfo hubInfo, long nanoAppId) {
Objects.requireNonNull(hubInfo, "ContextHubInfo cannot be null");
@@ -551,7 +586,10 @@ public final class ContextHubManager {
*
* @throws NullPointerException if hubInfo is null
*/
- @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.LOCATION_HARDWARE,
+ android.Manifest.permission.ACCESS_CONTEXT_HUB
+ })
@NonNull public ContextHubTransaction<Void> disableNanoApp(
@NonNull ContextHubInfo hubInfo, long nanoAppId) {
Objects.requireNonNull(hubInfo, "ContextHubInfo cannot be null");
@@ -578,7 +616,10 @@ public final class ContextHubManager {
*
* @throws NullPointerException if hubInfo is null
*/
- @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.LOCATION_HARDWARE,
+ android.Manifest.permission.ACCESS_CONTEXT_HUB
+ })
@NonNull public ContextHubTransaction<List<NanoAppState>> queryNanoApps(
@NonNull ContextHubInfo hubInfo) {
Objects.requireNonNull(hubInfo, "ContextHubInfo cannot be null");
@@ -724,7 +765,10 @@ public final class ContextHubManager {
*
* @see ContextHubClientCallback
*/
- @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.LOCATION_HARDWARE,
+ android.Manifest.permission.ACCESS_CONTEXT_HUB
+ })
@NonNull public ContextHubClient createClient(
@NonNull ContextHubInfo hubInfo, @NonNull ContextHubClientCallback callback,
@NonNull @CallbackExecutor Executor executor) {
@@ -761,7 +805,10 @@ public final class ContextHubManager {
*
* @see ContextHubClientCallback
*/
- @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.LOCATION_HARDWARE,
+ android.Manifest.permission.ACCESS_CONTEXT_HUB
+ })
@NonNull public ContextHubClient createClient(
@NonNull ContextHubInfo hubInfo, @NonNull ContextHubClientCallback callback) {
return createClient(hubInfo, callback, new HandlerExecutor(Handler.getMain()));
@@ -780,6 +827,9 @@ public final class ContextHubManager {
* If a client is regenerated, the host endpoint identifier attached to messages sent to the
* nanoapp remains consistent, even if the original process has exited.
*
+ * To avoid unintentionally spreading data from the Context Hub to external applications, it is
+ * strongly recommended that the PendingIntent supplied to this API is an explicit intent.
+ *
* If registered successfully, intents will be delivered regarding events or messages from the
* specified nanoapp from the attached Context Hub. The intent will have an extra
* {@link ContextHubManager.EXTRA_CONTEXT_HUB_INFO} of type {@link ContextHubInfo}, which
@@ -804,7 +854,10 @@ public final class ContextHubManager {
* @throws IllegalStateException if there were too many registered clients at the service
* @throws NullPointerException if pendingIntent or hubInfo is null
*/
- @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.LOCATION_HARDWARE,
+ android.Manifest.permission.ACCESS_CONTEXT_HUB
+ })
@NonNull public ContextHubClient createClient(
@NonNull ContextHubInfo hubInfo, @NonNull PendingIntent pendingIntent, long nanoAppId) {
Objects.requireNonNull(pendingIntent);
diff --git a/core/java/android/hardware/usb/UsbManager.java b/core/java/android/hardware/usb/UsbManager.java
index 827353b1802c..086db1010f73 100644
--- a/core/java/android/hardware/usb/UsbManager.java
+++ b/core/java/android/hardware/usb/UsbManager.java
@@ -262,6 +262,15 @@ public class UsbManager {
public static final String USB_FUNCTION_ACCESSORY = "accessory";
/**
+ * Name of the NCM USB function.
+ * Used in extras for the {@link #ACTION_USB_STATE} broadcast
+ *
+ * {@hide}
+ */
+ @SystemApi
+ public static final String USB_FUNCTION_NCM = "ncm";
+
+ /**
* Name of extra for {@link #ACTION_USB_PORT_CHANGED}
* containing the {@link UsbPort} object for the port.
*
@@ -367,8 +376,15 @@ public class UsbManager {
*/
public static final long FUNCTION_ADB = GadgetFunction.ADB;
+ /**
+ * Code for the ncm source usb function.
+ * {@hide}
+ */
+ @SystemApi
+ public static final long FUNCTION_NCM = 1 << 10;
+
private static final long SETTABLE_FUNCTIONS = FUNCTION_MTP | FUNCTION_PTP | FUNCTION_RNDIS
- | FUNCTION_MIDI;
+ | FUNCTION_MIDI | FUNCTION_NCM;
private static final Map<String, Long> FUNCTION_NAME_TO_CODE = new HashMap<>();
@@ -380,6 +396,7 @@ public class UsbManager {
FUNCTION_NAME_TO_CODE.put(UsbManager.USB_FUNCTION_ACCESSORY, FUNCTION_ACCESSORY);
FUNCTION_NAME_TO_CODE.put(UsbManager.USB_FUNCTION_AUDIO_SOURCE, FUNCTION_AUDIO_SOURCE);
FUNCTION_NAME_TO_CODE.put(UsbManager.USB_FUNCTION_ADB, FUNCTION_ADB);
+ FUNCTION_NAME_TO_CODE.put(UsbManager.USB_FUNCTION_NCM, FUNCTION_NCM);
}
private final Context mContext;
@@ -954,6 +971,9 @@ public class UsbManager {
if ((functions & FUNCTION_AUDIO_SOURCE) != 0) {
joiner.add(UsbManager.USB_FUNCTION_AUDIO_SOURCE);
}
+ if ((functions & FUNCTION_NCM) != 0) {
+ joiner.add(UsbManager.USB_FUNCTION_NCM);
+ }
if ((functions & FUNCTION_ADB) != 0) {
joiner.add(UsbManager.USB_FUNCTION_ADB);
}
diff --git a/core/java/android/net/ConnectivityDiagnosticsManager.java b/core/java/android/net/ConnectivityDiagnosticsManager.java
index a6f9b96269e1..b13e4b72aa22 100644
--- a/core/java/android/net/ConnectivityDiagnosticsManager.java
+++ b/core/java/android/net/ConnectivityDiagnosticsManager.java
@@ -19,7 +19,9 @@ package android.net;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.StringDef;
import android.content.Context;
+import android.os.Binder;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.PersistableBundle;
@@ -79,6 +81,128 @@ public class ConnectivityDiagnosticsManager {
/** Class that includes connectivity information for a specific Network at a specific time. */
public static final class ConnectivityReport implements Parcelable {
+ /**
+ * The overall status of the network is that it is invalid; it neither provides
+ * connectivity nor has been exempted from validation.
+ */
+ public static final int NETWORK_VALIDATION_RESULT_INVALID = 0;
+
+ /**
+ * The overall status of the network is that it is valid, this may be because it provides
+ * full Internet access (all probes succeeded), or because other properties of the network
+ * caused probes not to be run.
+ */
+ // TODO: link to INetworkMonitor.NETWORK_VALIDATION_RESULT_VALID
+ public static final int NETWORK_VALIDATION_RESULT_VALID = 1;
+
+ /**
+ * The overall status of the network is that it provides partial connectivity; some
+ * probed services succeeded but others failed.
+ */
+ // TODO: link to INetworkMonitor.NETWORK_VALIDATION_RESULT_PARTIAL;
+ public static final int NETWORK_VALIDATION_RESULT_PARTIALLY_VALID = 2;
+
+ /**
+ * Due to the properties of the network, validation was not performed.
+ */
+ public static final int NETWORK_VALIDATION_RESULT_SKIPPED = 3;
+
+ /** @hide */
+ @IntDef(
+ prefix = {"NETWORK_VALIDATION_RESULT_"},
+ value = {
+ NETWORK_VALIDATION_RESULT_INVALID,
+ NETWORK_VALIDATION_RESULT_VALID,
+ NETWORK_VALIDATION_RESULT_PARTIALLY_VALID,
+ NETWORK_VALIDATION_RESULT_SKIPPED
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface NetworkValidationResult {}
+
+ /**
+ * The overall validation result for the Network being reported on.
+ *
+ * <p>The possible values for this key are:
+ * {@link #NETWORK_VALIDATION_RESULT_INVALID},
+ * {@link #NETWORK_VALIDATION_RESULT_VALID},
+ * {@link #NETWORK_VALIDATION_RESULT_PARTIALLY_VALID},
+ * {@link #NETWORK_VALIDATION_RESULT_SKIPPED}.
+ *
+ * @see android.net.NetworkCapabilities#CAPABILITY_VALIDATED
+ */
+ @NetworkValidationResult
+ public static final String KEY_NETWORK_VALIDATION_RESULT = "networkValidationResult";
+
+ /** DNS probe. */
+ // TODO: link to INetworkMonitor.NETWORK_VALIDATION_PROBE_DNS
+ public static final int NETWORK_PROBE_DNS = 0x04;
+
+ /** HTTP probe. */
+ // TODO: link to INetworkMonitor.NETWORK_VALIDATION_PROBE_HTTP
+ public static final int NETWORK_PROBE_HTTP = 0x08;
+
+ /** HTTPS probe. */
+ // TODO: link to INetworkMonitor.NETWORK_VALIDATION_PROBE_HTTPS;
+ public static final int NETWORK_PROBE_HTTPS = 0x10;
+
+ /** Captive portal fallback probe. */
+ // TODO: link to INetworkMonitor.NETWORK_VALIDATION_FALLBACK
+ public static final int NETWORK_PROBE_FALLBACK = 0x20;
+
+ /** Private DNS (DNS over TLS) probd. */
+ // TODO: link to INetworkMonitor.NETWORK_VALIDATION_PROBE_PRIVDNS
+ public static final int NETWORK_PROBE_PRIVATE_DNS = 0x40;
+
+ /** @hide */
+ @IntDef(
+ prefix = {"NETWORK_PROBE_"},
+ value = {
+ NETWORK_PROBE_DNS,
+ NETWORK_PROBE_HTTP,
+ NETWORK_PROBE_HTTPS,
+ NETWORK_PROBE_FALLBACK,
+ NETWORK_PROBE_PRIVATE_DNS
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface NetworkProbe {}
+
+ /**
+ * A bitmask of network validation probes that succeeded.
+ *
+ * <p>The possible bits values reported by this key are:
+ * {@link #NETWORK_PROBE_DNS},
+ * {@link #NETWORK_PROBE_HTTP},
+ * {@link #NETWORK_PROBE_HTTPS},
+ * {@link #NETWORK_PROBE_FALLBACK},
+ * {@link #NETWORK_PROBE_PRIVATE_DNS}.
+ */
+ @NetworkProbe
+ public static final String KEY_NETWORK_PROBES_SUCCEEDED_BITMASK =
+ "networkProbesSucceeded";
+
+ /**
+ * A bitmask of network validation probes that were attempted.
+ *
+ * <p>These probes may have failed or may be incomplete at the time of this report.
+ *
+ * <p>The possible bits values reported by this key are:
+ * {@link #NETWORK_PROBE_DNS},
+ * {@link #NETWORK_PROBE_HTTP},
+ * {@link #NETWORK_PROBE_HTTPS},
+ * {@link #NETWORK_PROBE_FALLBACK},
+ * {@link #NETWORK_PROBE_PRIVATE_DNS}.
+ */
+ @NetworkProbe
+ public static final String KEY_NETWORK_PROBES_ATTEMPTED_BITMASK =
+ "networkProbesAttemped";
+
+ /** @hide */
+ @StringDef(prefix = {"KEY_"}, value = {
+ KEY_NETWORK_VALIDATION_RESULT, KEY_NETWORK_PROBES_SUCCEEDED_BITMASK,
+ KEY_NETWORK_PROBES_ATTEMPTED_BITMASK})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ConnectivityReportBundleKeys {}
+
/** The Network for which this ConnectivityReport applied */
@NonNull private final Network mNetwork;
@@ -218,7 +342,7 @@ public class ConnectivityDiagnosticsManager {
/** Implement the Parcelable interface */
public static final @NonNull Creator<ConnectivityReport> CREATOR =
- new Creator<>() {
+ new Creator<ConnectivityReport>() {
public ConnectivityReport createFromParcel(Parcel in) {
return new ConnectivityReport(
in.readParcelable(null),
@@ -246,6 +370,49 @@ public class ConnectivityDiagnosticsManager {
value = {DETECTION_METHOD_DNS_EVENTS, DETECTION_METHOD_TCP_METRICS})
public @interface DetectionMethod {}
+ /**
+ * This key represents the period in milliseconds over which other included TCP metrics
+ * were measured.
+ *
+ * <p>This key will be included if the data stall detection method is
+ * {@link #DETECTION_METHOD_TCP_METRICS}.
+ *
+ * <p>This value is an int.
+ */
+ public static final String KEY_TCP_METRICS_COLLECTION_PERIOD_MILLIS =
+ "tcpMetricsCollectionPeriodMillis";
+
+ /**
+ * This key represents the fail rate of TCP packets when the suspected data stall was
+ * detected.
+ *
+ * <p>This key will be included if the data stall detection method is
+ * {@link #DETECTION_METHOD_TCP_METRICS}.
+ *
+ * <p>This value is an int percentage between 0 and 100.
+ */
+ public static final String KEY_TCP_PACKET_FAIL_RATE = "tcpPacketFailRate";
+
+ /**
+ * This key represents the consecutive number of DNS timeouts that have occurred.
+ *
+ * <p>The consecutive count will be reset any time a DNS response is received.
+ *
+ * <p>This key will be included if the data stall detection method is
+ * {@link #DETECTION_METHOD_DNS_EVENTS}.
+ *
+ * <p>This value is an int.
+ */
+ public static final String KEY_DNS_CONSECUTIVE_TIMEOUTS = "dnsConsecutiveTimeouts";
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @StringDef(prefix = {"KEY_"}, value = {
+ KEY_TCP_PACKET_FAIL_RATE,
+ KEY_DNS_CONSECUTIVE_TIMEOUTS
+ })
+ public @interface DataStallReportBundleKeys {}
+
/** The Network for which this DataStallReport applied */
@NonNull private final Network mNetwork;
@@ -315,6 +482,9 @@ public class ConnectivityDiagnosticsManager {
/**
* Returns a PersistableBundle with additional info for this report.
*
+ * <p>Gets a bundle with details about the suspected data stall including information
+ * specific to the monitoring method that detected the data stall.
+ *
* @return PersistableBundle that may contain additional information on the suspected data
* stall
*/
@@ -375,6 +545,53 @@ public class ConnectivityDiagnosticsManager {
};
}
+ /** @hide */
+ @VisibleForTesting
+ public static class ConnectivityDiagnosticsBinder
+ extends IConnectivityDiagnosticsCallback.Stub {
+ @NonNull private final ConnectivityDiagnosticsCallback mCb;
+ @NonNull private final Executor mExecutor;
+
+ /** @hide */
+ @VisibleForTesting
+ public ConnectivityDiagnosticsBinder(
+ @NonNull ConnectivityDiagnosticsCallback cb, @NonNull Executor executor) {
+ this.mCb = cb;
+ this.mExecutor = executor;
+ }
+
+ /** @hide */
+ @VisibleForTesting
+ public void onConnectivityReport(@NonNull ConnectivityReport report) {
+ Binder.withCleanCallingIdentity(() -> {
+ mExecutor.execute(() -> {
+ mCb.onConnectivityReport(report);
+ });
+ });
+ }
+
+ /** @hide */
+ @VisibleForTesting
+ public void onDataStallSuspected(@NonNull DataStallReport report) {
+ Binder.withCleanCallingIdentity(() -> {
+ mExecutor.execute(() -> {
+ mCb.onDataStallSuspected(report);
+ });
+ });
+ }
+
+ /** @hide */
+ @VisibleForTesting
+ public void onNetworkConnectivityReported(
+ @NonNull Network network, boolean hasConnectivity) {
+ Binder.withCleanCallingIdentity(() -> {
+ mExecutor.execute(() -> {
+ mCb.onNetworkConnectivityReported(network, hasConnectivity);
+ });
+ });
+ }
+ }
+
/**
* Abstract base class for Connectivity Diagnostics callbacks. Used for notifications about
* network connectivity events. Must be extended by applications wanting notifications.
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 753e754602d1..ce9693d88a97 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -33,7 +33,9 @@ import android.content.Context;
import android.content.Intent;
import android.net.IpSecManager.UdpEncapsulationSocket;
import android.net.SocketKeepalive.Callback;
+import android.net.TetheringManager.StartTetheringCallback;
import android.net.TetheringManager.TetheringEventCallback;
+import android.net.TetheringManager.TetheringRequest;
import android.os.Binder;
import android.os.Build;
import android.os.Build.VERSION_CODES;
@@ -2452,10 +2454,12 @@ public class ConnectivityManager {
*
* @param iface the interface name to tether.
* @return error a {@code TETHER_ERROR} value indicating success or failure type
+ * @deprecated Use {@link TetheringManager#startTethering} instead
*
* {@hide}
*/
@UnsupportedAppUsage
+ @Deprecated
public int tether(String iface) {
return getTetheringManager().tether(iface);
}
@@ -2512,9 +2516,12 @@ public class ConnectivityManager {
/**
* Callback for use with {@link #startTethering} to find out whether tethering succeeded.
+ *
+ * @deprecated Use {@link TetheringManager.StartTetheringCallback} instead.
* @hide
*/
@SystemApi
+ @Deprecated
public static abstract class OnStartTetheringCallback {
/**
* Called when tethering has been successfully started.
@@ -2531,9 +2538,12 @@ public class ConnectivityManager {
* Convenient overload for
* {@link #startTethering(int, boolean, OnStartTetheringCallback, Handler)} which passes a null
* handler to run on the current thread's {@link Looper}.
+ *
+ * @deprecated Use {@link TetheringManager#startTethering} instead.
* @hide
*/
@SystemApi
+ @Deprecated
@RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED)
public void startTethering(int type, boolean showProvisioningUi,
final OnStartTetheringCallback callback) {
@@ -2557,26 +2567,44 @@ public class ConnectivityManager {
* @param callback an {@link OnStartTetheringCallback} which will be called to notify the caller
* of the result of trying to tether.
* @param handler {@link Handler} to specify the thread upon which the callback will be invoked.
+ *
+ * @deprecated Use {@link TetheringManager#startTethering} instead.
* @hide
*/
@SystemApi
+ @Deprecated
@RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED)
public void startTethering(int type, boolean showProvisioningUi,
final OnStartTetheringCallback callback, Handler handler) {
Preconditions.checkNotNull(callback, "OnStartTetheringCallback cannot be null.");
- ResultReceiver wrappedCallback = new ResultReceiver(handler) {
+ final Executor executor = new Executor() {
@Override
- protected void onReceiveResult(int resultCode, Bundle resultData) {
- if (resultCode == TETHER_ERROR_NO_ERROR) {
- callback.onTetheringStarted();
+ public void execute(Runnable command) {
+ if (handler == null) {
+ command.run();
} else {
- callback.onTetheringFailed();
+ handler.post(command);
}
}
};
- getTetheringManager().startTethering(type, wrappedCallback, showProvisioningUi);
+ final StartTetheringCallback tetheringCallback = new StartTetheringCallback() {
+ @Override
+ public void onTetheringStarted() {
+ callback.onTetheringStarted();
+ }
+
+ @Override
+ public void onTetheringFailed(final int resultCode) {
+ callback.onTetheringFailed();
+ }
+ };
+
+ final TetheringRequest request = new TetheringRequest.Builder(type)
+ .setSilentProvisioning(!showProvisioningUi).build();
+
+ getTetheringManager().startTethering(request, executor, tetheringCallback);
}
/**
@@ -2602,7 +2630,7 @@ public class ConnectivityManager {
* Callback for use with {@link registerTetheringEventCallback} to find out tethering
* upstream status.
*
- * @deprecated Use {@line TetheringManager#OnTetheringEventCallback} instead.
+ * @deprecated Use {@link TetheringManager#OnTetheringEventCallback} instead.
* @hide
*/
@SystemApi
@@ -2632,7 +2660,7 @@ public class ConnectivityManager {
* @param executor the executor on which callback will be invoked.
* @param callback the callback to be called when tethering has change events.
*
- * @deprecated Use {@line TetheringManager#registerTetheringEventCallback} instead.
+ * @deprecated Use {@link TetheringManager#registerTetheringEventCallback} instead.
* @hide
*/
@SystemApi
@@ -2749,10 +2777,12 @@ public class ConnectivityManager {
*
* @param enable a boolean - {@code true} to enable tethering
* @return error a {@code TETHER_ERROR} value indicating success or failure type
+ * @deprecated Use {@link TetheringManager#startTethering} instead
*
* {@hide}
*/
@UnsupportedAppUsage
+ @Deprecated
public int setUsbTethering(boolean enable) {
return getTetheringManager().setUsbTethering(enable);
}
diff --git a/core/java/com/android/internal/car/ICarStatsService.aidl b/core/java/android/net/IConnectivityDiagnosticsCallback.aidl
index 170b448ba33f..3a161bfabfd2 100644
--- a/core/java/com/android/internal/car/ICarStatsService.aidl
+++ b/core/java/android/net/IConnectivityDiagnosticsCallback.aidl
@@ -1,11 +1,12 @@
-/*
+/**
+ *
* 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
+ * 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,
@@ -14,18 +15,14 @@
* limitations under the License.
*/
-package com.android.internal.car;
+package android.net;
-import android.os.StatsLogEventWrapper;
+import android.net.ConnectivityDiagnosticsManager;
+import android.net.Network;
-/**
- * Interface for pulling statsd atoms from automotive devices.
- *
- * @hide
- */
-interface ICarStatsService {
- /**
- * Pull the specified atom. Results will be sent to statsd when complete.
- */
- StatsLogEventWrapper[] pullData(int atomId);
-}
+/** @hide */
+oneway interface IConnectivityDiagnosticsCallback {
+ void onConnectivityReport(in ConnectivityDiagnosticsManager.ConnectivityReport report);
+ void onDataStallSuspected(in ConnectivityDiagnosticsManager.DataStallReport report);
+ void onNetworkConnectivityReported(in Network n, boolean hasConnectivity);
+} \ No newline at end of file
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index 186196bd31c7..3e9e7faccb02 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -18,6 +18,7 @@ package android.net;
import android.app.PendingIntent;
import android.net.ConnectionInfo;
+import android.net.IConnectivityDiagnosticsCallback;
import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkAgentConfig;
@@ -211,5 +212,9 @@ interface IConnectivityManager
boolean isCallerCurrentAlwaysOnVpnApp();
boolean isCallerCurrentAlwaysOnVpnLockdownApp();
+ void registerConnectivityDiagnosticsCallback(in IConnectivityDiagnosticsCallback callback,
+ in NetworkRequest request);
+ void unregisterConnectivityDiagnosticsCallback(in IConnectivityDiagnosticsCallback callback);
+
IBinder startOrGetTestNetworkService();
}
diff --git a/core/java/android/net/Ikev2VpnProfile.java b/core/java/android/net/Ikev2VpnProfile.java
new file mode 100644
index 000000000000..42b4da14d879
--- /dev/null
+++ b/core/java/android/net/Ikev2VpnProfile.java
@@ -0,0 +1,728 @@
+/*
+ * 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 static android.net.PlatformVpnProfile.TYPE_IKEV2_IPSEC_PSK;
+import static android.net.PlatformVpnProfile.TYPE_IKEV2_IPSEC_RSA;
+import static android.net.PlatformVpnProfile.TYPE_IKEV2_IPSEC_USER_PASS;
+
+import static com.android.internal.annotations.VisibleForTesting.Visibility;
+import static com.android.internal.util.Preconditions.checkStringNotEmpty;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.security.Credentials;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.net.VpnProfile;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.security.GeneralSecurityException;
+import java.security.KeyFactory;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Base64;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * The Ikev2VpnProfile is a configuration for the platform setup of IKEv2/IPsec VPNs.
+ *
+ * <p>Together with VpnManager, this allows apps to provision IKEv2/IPsec VPNs that do not require
+ * the VPN app to constantly run in the background.
+ *
+ * @see VpnManager
+ * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.3.2">RFC 7296 - Internet Key
+ * Exchange, Version 2 (IKEv2)</a>
+ */
+public final class Ikev2VpnProfile extends PlatformVpnProfile {
+ private static final String MISSING_PARAM_MSG_TMPL = "Required parameter was not provided: %s";
+ private static final String EMPTY_CERT = "";
+
+ @NonNull private final String mServerAddr;
+ @NonNull private final String mUserIdentity;
+
+ // PSK authentication
+ @Nullable private final byte[] mPresharedKey;
+
+ // Username/Password, RSA authentication
+ @Nullable private final X509Certificate mServerRootCaCert;
+
+ // Username/Password authentication
+ @Nullable private final String mUsername;
+ @Nullable private final String mPassword;
+
+ // RSA Certificate authentication
+ @Nullable private final PrivateKey mRsaPrivateKey;
+ @Nullable private final X509Certificate mUserCert;
+
+ @Nullable private final ProxyInfo mProxyInfo;
+ @NonNull private final List<String> mAllowedAlgorithms;
+ private final boolean mIsBypassable; // Defaults in builder
+ private final boolean mIsMetered; // Defaults in builder
+ private final int mMaxMtu; // Defaults in builder
+
+ private Ikev2VpnProfile(
+ int type,
+ @NonNull String serverAddr,
+ @NonNull String userIdentity,
+ @Nullable byte[] presharedKey,
+ @Nullable X509Certificate serverRootCaCert,
+ @Nullable String username,
+ @Nullable String password,
+ @Nullable PrivateKey rsaPrivateKey,
+ @Nullable X509Certificate userCert,
+ @Nullable ProxyInfo proxyInfo,
+ @NonNull List<String> allowedAlgorithms,
+ boolean isBypassable,
+ boolean isMetered,
+ int maxMtu) {
+ super(type);
+
+ checkNotNull(serverAddr, MISSING_PARAM_MSG_TMPL, "Server address");
+ checkNotNull(userIdentity, MISSING_PARAM_MSG_TMPL, "User Identity");
+ checkNotNull(allowedAlgorithms, MISSING_PARAM_MSG_TMPL, "Allowed Algorithms");
+
+ mServerAddr = serverAddr;
+ mUserIdentity = userIdentity;
+ mPresharedKey =
+ presharedKey == null ? null : Arrays.copyOf(presharedKey, presharedKey.length);
+ mServerRootCaCert = serverRootCaCert;
+ mUsername = username;
+ mPassword = password;
+ mRsaPrivateKey = rsaPrivateKey;
+ mUserCert = userCert;
+ mProxyInfo = new ProxyInfo(proxyInfo);
+
+ // UnmodifiableList doesn't make a defensive copy by default.
+ mAllowedAlgorithms = Collections.unmodifiableList(new ArrayList<>(allowedAlgorithms));
+
+ mIsBypassable = isBypassable;
+ mIsMetered = isMetered;
+ mMaxMtu = maxMtu;
+
+ validate();
+ }
+
+ private void validate() {
+ // Server Address not validated except to check an address was provided. This allows for
+ // dual-stack servers and hostname based addresses.
+ checkStringNotEmpty(mServerAddr, MISSING_PARAM_MSG_TMPL, "Server Address");
+ checkStringNotEmpty(mUserIdentity, MISSING_PARAM_MSG_TMPL, "User Identity");
+
+ // IPv6 MTU is greater; since profiles may be started by the system on IPv4 and IPv6
+ // networks, the VPN must provide a link fulfilling the stricter of the two conditions
+ // (at least that of the IPv6 MTU).
+ if (mMaxMtu < LinkProperties.MIN_MTU_V6) {
+ throw new IllegalArgumentException(
+ "Max MTU must be at least" + LinkProperties.MIN_MTU_V6);
+ }
+
+ switch (mType) {
+ case TYPE_IKEV2_IPSEC_USER_PASS:
+ checkNotNull(mUsername, MISSING_PARAM_MSG_TMPL, "Username");
+ checkNotNull(mPassword, MISSING_PARAM_MSG_TMPL, "Password");
+
+ if (mServerRootCaCert != null) checkCert(mServerRootCaCert);
+
+ break;
+ case TYPE_IKEV2_IPSEC_PSK:
+ checkNotNull(mPresharedKey, MISSING_PARAM_MSG_TMPL, "Preshared Key");
+ break;
+ case TYPE_IKEV2_IPSEC_RSA:
+ checkNotNull(mUserCert, MISSING_PARAM_MSG_TMPL, "User cert");
+ checkNotNull(mRsaPrivateKey, MISSING_PARAM_MSG_TMPL, "RSA Private key");
+
+ checkCert(mUserCert);
+ if (mServerRootCaCert != null) checkCert(mServerRootCaCert);
+
+ break;
+ default:
+ throw new IllegalArgumentException("Invalid auth method set");
+ }
+
+ VpnProfile.validateAllowedAlgorithms(mAllowedAlgorithms);
+ }
+
+ /** Retrieves the server address string. */
+ @NonNull
+ public String getServerAddr() {
+ return mServerAddr;
+ }
+
+ /** Retrieves the user identity. */
+ @NonNull
+ public String getUserIdentity() {
+ return mUserIdentity;
+ }
+
+ /**
+ * Retrieves the pre-shared key.
+ *
+ * <p>May be null if the profile is not using Pre-shared key authentication.
+ */
+ @Nullable
+ public byte[] getPresharedKey() {
+ return mPresharedKey == null ? null : Arrays.copyOf(mPresharedKey, mPresharedKey.length);
+ }
+
+ /**
+ * Retrieves the certificate for the server's root CA.
+ *
+ * <p>May be null if the profile is not using RSA Digital Signature Authentication or
+ * Username/Password authentication
+ */
+ @Nullable
+ public X509Certificate getServerRootCaCert() {
+ return mServerRootCaCert;
+ }
+
+ /**
+ * Retrieves the username.
+ *
+ * <p>May be null if the profile is not using Username/Password authentication
+ */
+ @Nullable
+ public String getUsername() {
+ return mUsername;
+ }
+
+ /**
+ * Retrieves the password.
+ *
+ * <p>May be null if the profile is not using Username/Password authentication
+ */
+ @Nullable
+ public String getPassword() {
+ return mPassword;
+ }
+
+ /**
+ * Retrieves the RSA private key.
+ *
+ * <p>May be null if the profile is not using RSA Digital Signature authentication
+ */
+ @Nullable
+ public PrivateKey getRsaPrivateKey() {
+ return mRsaPrivateKey;
+ }
+
+ /** Retrieves the user certificate, if any was set. */
+ @Nullable
+ public X509Certificate getUserCert() {
+ return mUserCert;
+ }
+
+ /** Retrieves the proxy information if any was set */
+ @Nullable
+ public ProxyInfo getProxyInfo() {
+ return mProxyInfo;
+ }
+
+ /** Returns all the algorithms allowed by this VPN profile. */
+ @NonNull
+ public List<String> getAllowedAlgorithms() {
+ return mAllowedAlgorithms;
+ }
+
+ /** Returns whether or not the VPN profile should be bypassable. */
+ public boolean isBypassable() {
+ return mIsBypassable;
+ }
+
+ /** Returns whether or not the VPN profile should be always considered metered. */
+ public boolean isMetered() {
+ return mIsMetered;
+ }
+
+ /** Retrieves the maximum MTU set for this VPN profile. */
+ public int getMaxMtu() {
+ return mMaxMtu;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(
+ mType,
+ mServerAddr,
+ mUserIdentity,
+ Arrays.hashCode(mPresharedKey),
+ mServerRootCaCert,
+ mUsername,
+ mPassword,
+ mRsaPrivateKey,
+ mUserCert,
+ mProxyInfo,
+ mAllowedAlgorithms,
+ mIsBypassable,
+ mIsMetered,
+ mMaxMtu);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof Ikev2VpnProfile)) {
+ return false;
+ }
+
+ final Ikev2VpnProfile other = (Ikev2VpnProfile) obj;
+ return mType == other.mType
+ && Objects.equals(mServerAddr, other.mServerAddr)
+ && Objects.equals(mUserIdentity, other.mUserIdentity)
+ && Arrays.equals(mPresharedKey, other.mPresharedKey)
+ && Objects.equals(mServerRootCaCert, other.mServerRootCaCert)
+ && Objects.equals(mUsername, other.mUsername)
+ && Objects.equals(mPassword, other.mPassword)
+ && Objects.equals(mRsaPrivateKey, other.mRsaPrivateKey)
+ && Objects.equals(mUserCert, other.mUserCert)
+ && Objects.equals(mProxyInfo, other.mProxyInfo)
+ && Objects.equals(mAllowedAlgorithms, other.mAllowedAlgorithms)
+ && mIsBypassable == other.mIsBypassable
+ && mIsMetered == other.mIsMetered
+ && mMaxMtu == other.mMaxMtu;
+ }
+
+ /**
+ * Builds a VpnProfile instance for internal use, based on the stored IKEv2/IPsec parameters.
+ *
+ * <p>Redundant authentication information (from previous calls to other setAuth* methods) will
+ * be discarded.
+ *
+ * @hide
+ */
+ @NonNull
+ public VpnProfile toVpnProfile() throws IOException, GeneralSecurityException {
+ final VpnProfile profile = new VpnProfile("" /* Key; value unused by IKEv2VpnProfile(s) */);
+ profile.type = mType;
+ profile.server = mServerAddr;
+ profile.ipsecIdentifier = mUserIdentity;
+ profile.proxy = mProxyInfo;
+ profile.setAllowedAlgorithms(mAllowedAlgorithms);
+ profile.isBypassable = mIsBypassable;
+ profile.isMetered = mIsMetered;
+ profile.maxMtu = mMaxMtu;
+ profile.areAuthParamsInline = true;
+ profile.saveLogin = true;
+
+ switch (mType) {
+ case TYPE_IKEV2_IPSEC_USER_PASS:
+ profile.username = mUsername;
+ profile.password = mPassword;
+ profile.ipsecCaCert =
+ mServerRootCaCert == null ? "" : certificateToPemString(mServerRootCaCert);
+ break;
+ case TYPE_IKEV2_IPSEC_PSK:
+ profile.ipsecSecret = encodeForIpsecSecret(mPresharedKey);
+ break;
+ case TYPE_IKEV2_IPSEC_RSA:
+ profile.ipsecUserCert = certificateToPemString(mUserCert);
+ profile.ipsecSecret = encodeForIpsecSecret(mRsaPrivateKey.getEncoded());
+ profile.ipsecCaCert =
+ mServerRootCaCert == null ? "" : certificateToPemString(mServerRootCaCert);
+ break;
+ default:
+ throw new IllegalArgumentException("Invalid auth method set");
+ }
+
+ return profile;
+ }
+
+ /**
+ * Constructs a Ikev2VpnProfile from an internal-use VpnProfile instance.
+ *
+ * <p>Redundant authentication information (not related to profile type) will be discarded.
+ *
+ * @hide
+ */
+ @NonNull
+ public static Ikev2VpnProfile fromVpnProfile(@NonNull VpnProfile profile)
+ throws IOException, GeneralSecurityException {
+ final Builder builder = new Builder(profile.server, profile.ipsecIdentifier);
+ builder.setProxy(profile.proxy);
+ builder.setAllowedAlgorithms(profile.getAllowedAlgorithms());
+ builder.setBypassable(profile.isBypassable);
+ builder.setMetered(profile.isMetered);
+ builder.setMaxMtu(profile.maxMtu);
+
+ switch (profile.type) {
+ case TYPE_IKEV2_IPSEC_USER_PASS:
+ builder.setAuthUsernamePassword(
+ profile.username,
+ profile.password,
+ certificateFromPemString(profile.ipsecCaCert));
+ break;
+ case TYPE_IKEV2_IPSEC_PSK:
+ builder.setAuthPsk(decodeFromIpsecSecret(profile.ipsecSecret));
+ break;
+ case TYPE_IKEV2_IPSEC_RSA:
+ final X509Certificate userCert = certificateFromPemString(profile.ipsecUserCert);
+ final PrivateKey key = getPrivateKey(profile.ipsecSecret);
+ final X509Certificate serverRootCa = certificateFromPemString(profile.ipsecCaCert);
+ builder.setAuthDigitalSignature(userCert, key, serverRootCa);
+ break;
+ default:
+ throw new IllegalArgumentException("Invalid auth method set");
+ }
+
+ return builder.build();
+ }
+
+ /**
+ * Converts a X509 Certificate to a PEM-formatted string.
+ *
+ * <p>Must be public due to runtime-package restrictions.
+ *
+ * @hide
+ */
+ @NonNull
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ public static String certificateToPemString(@Nullable X509Certificate cert)
+ throws IOException, CertificateEncodingException {
+ if (cert == null) {
+ return EMPTY_CERT;
+ }
+
+ // Credentials.convertToPem outputs ASCII bytes.
+ return new String(Credentials.convertToPem(cert), StandardCharsets.US_ASCII);
+ }
+
+ /**
+ * Decodes the provided Certificate(s).
+ *
+ * <p>Will use the first one if the certStr encodes more than one certificate.
+ */
+ @Nullable
+ private static X509Certificate certificateFromPemString(@Nullable String certStr)
+ throws CertificateException {
+ if (certStr == null || EMPTY_CERT.equals(certStr)) {
+ return null;
+ }
+
+ try {
+ final List<X509Certificate> certs =
+ Credentials.convertFromPem(certStr.getBytes(StandardCharsets.US_ASCII));
+ return certs.isEmpty() ? null : certs.get(0);
+ } catch (IOException e) {
+ throw new CertificateException(e);
+ }
+ }
+
+ /** @hide */
+ @NonNull
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ public static String encodeForIpsecSecret(@NonNull byte[] secret) {
+ checkNotNull(secret, MISSING_PARAM_MSG_TMPL, "secret");
+
+ return Base64.getEncoder().encodeToString(secret);
+ }
+
+ @NonNull
+ private static byte[] decodeFromIpsecSecret(@NonNull String encoded) {
+ checkNotNull(encoded, MISSING_PARAM_MSG_TMPL, "encoded");
+
+ return Base64.getDecoder().decode(encoded);
+ }
+
+ @NonNull
+ private static PrivateKey getPrivateKey(@NonNull String keyStr)
+ throws InvalidKeySpecException, NoSuchAlgorithmException {
+ final PKCS8EncodedKeySpec privateKeySpec =
+ new PKCS8EncodedKeySpec(decodeFromIpsecSecret(keyStr));
+ final KeyFactory keyFactory = KeyFactory.getInstance("RSA");
+ return keyFactory.generatePrivate(privateKeySpec);
+ }
+
+ private static void checkCert(@NonNull X509Certificate cert) {
+ try {
+ certificateToPemString(cert);
+ } catch (GeneralSecurityException | IOException e) {
+ throw new IllegalArgumentException("Certificate could not be encoded");
+ }
+ }
+
+ private static @NonNull <T> T checkNotNull(
+ final T reference, final String messageTemplate, final Object... messageArgs) {
+ return Objects.requireNonNull(reference, String.format(messageTemplate, messageArgs));
+ }
+
+ /** A incremental builder for IKEv2 VPN profiles */
+ public static final class Builder {
+ private int mType = -1;
+ @NonNull private final String mServerAddr;
+ @NonNull private final String mUserIdentity;
+
+ // PSK authentication
+ @Nullable private byte[] mPresharedKey;
+
+ // Username/Password, RSA authentication
+ @Nullable private X509Certificate mServerRootCaCert;
+
+ // Username/Password authentication
+ @Nullable private String mUsername;
+ @Nullable private String mPassword;
+
+ // RSA Certificate authentication
+ @Nullable private PrivateKey mRsaPrivateKey;
+ @Nullable private X509Certificate mUserCert;
+
+ @Nullable private ProxyInfo mProxyInfo;
+ @NonNull private List<String> mAllowedAlgorithms = new ArrayList<>();
+ private boolean mIsBypassable = false;
+ private boolean mIsMetered = true;
+ private int mMaxMtu = 1360;
+
+ /**
+ * Creates a new builder with the basic parameters of an IKEv2/IPsec VPN.
+ *
+ * @param serverAddr the server that the VPN should connect to
+ * @param identity the identity string to be used for IKEv2 authentication
+ */
+ public Builder(@NonNull String serverAddr, @NonNull String identity) {
+ checkNotNull(serverAddr, MISSING_PARAM_MSG_TMPL, "serverAddr");
+ checkNotNull(identity, MISSING_PARAM_MSG_TMPL, "identity");
+
+ mServerAddr = serverAddr;
+ mUserIdentity = identity;
+ }
+
+ private void resetAuthParams() {
+ mPresharedKey = null;
+ mServerRootCaCert = null;
+ mUsername = null;
+ mPassword = null;
+ mRsaPrivateKey = null;
+ mUserCert = null;
+ }
+
+ /**
+ * Set the IKEv2 authentication to use the provided username/password.
+ *
+ * <p>Setting this will configure IKEv2 authentication using EAP-MSCHAPv2. Only one
+ * authentication method may be set. This method will overwrite any previously set
+ * authentication method.
+ *
+ * @param user the username to be used for EAP-MSCHAPv2 authentication
+ * @param pass the password to be used for EAP-MSCHAPv2 authentication
+ * @param serverRootCa the root certificate to be used for verifying the identity of the
+ * server
+ * @return this {@link Builder} object to facilitate chaining of method calls
+ * @throws IllegalArgumentException if any of the certificates were invalid or of an
+ * unrecognized format
+ */
+ @NonNull
+ public Builder setAuthUsernamePassword(
+ @NonNull String user,
+ @NonNull String pass,
+ @Nullable X509Certificate serverRootCa) {
+ checkNotNull(user, MISSING_PARAM_MSG_TMPL, "user");
+ checkNotNull(pass, MISSING_PARAM_MSG_TMPL, "pass");
+
+ // Test to make sure all auth params can be encoded safely.
+ if (serverRootCa != null) checkCert(serverRootCa);
+
+ resetAuthParams();
+ mUsername = user;
+ mPassword = pass;
+ mServerRootCaCert = serverRootCa;
+ mType = VpnProfile.TYPE_IKEV2_IPSEC_USER_PASS;
+ return this;
+ }
+
+ /**
+ * Set the IKEv2 authentication to use Digital Signature Authentication with the given key.
+ *
+ * <p>Setting this will configure IKEv2 authentication using a Digital Signature scheme.
+ * Only one authentication method may be set. This method will overwrite any previously set
+ * authentication method.
+ *
+ * @param userCert the username to be used for RSA Digital signiture authentication
+ * @param key the PrivateKey instance associated with the user ceritificate, used for
+ * constructing the signature
+ * @param serverRootCa the root certificate to be used for verifying the identity of the
+ * server
+ * @return this {@link Builder} object to facilitate chaining of method calls
+ * @throws IllegalArgumentException if any of the certificates were invalid or of an
+ * unrecognized format
+ */
+ @NonNull
+ public Builder setAuthDigitalSignature(
+ @NonNull X509Certificate userCert,
+ @NonNull PrivateKey key,
+ @Nullable X509Certificate serverRootCa) {
+ checkNotNull(userCert, MISSING_PARAM_MSG_TMPL, "userCert");
+ checkNotNull(key, MISSING_PARAM_MSG_TMPL, "key");
+
+ // Test to make sure all auth params can be encoded safely.
+ checkCert(userCert);
+ if (serverRootCa != null) checkCert(serverRootCa);
+
+ resetAuthParams();
+ mUserCert = userCert;
+ mRsaPrivateKey = key;
+ mServerRootCaCert = serverRootCa;
+ mType = VpnProfile.TYPE_IKEV2_IPSEC_RSA;
+ return this;
+ }
+
+ /**
+ * Set the IKEv2 authentication to use Preshared keys.
+ *
+ * <p>Setting this will configure IKEv2 authentication using a Preshared Key. Only one
+ * authentication method may be set. This method will overwrite any previously set
+ * authentication method.
+ *
+ * @param psk the key to be used for Pre-Shared Key authentication
+ * @return this {@link Builder} object to facilitate chaining of method calls
+ */
+ @NonNull
+ public Builder setAuthPsk(@NonNull byte[] psk) {
+ checkNotNull(psk, MISSING_PARAM_MSG_TMPL, "psk");
+
+ resetAuthParams();
+ mPresharedKey = psk;
+ mType = VpnProfile.TYPE_IKEV2_IPSEC_PSK;
+ return this;
+ }
+
+ /**
+ * Sets whether apps can bypass this VPN connection.
+ *
+ * <p>By default, all traffic from apps are forwarded through the VPN interface and it is
+ * not possible for unprivileged apps to side-step the VPN. If a VPN is set to bypassable,
+ * apps may use methods such as {@link Network#getSocketFactory} or {@link
+ * Network#openConnection} to instead send/receive directly over the underlying network or
+ * any other network they have permissions for.
+ *
+ * @param isBypassable Whether or not the VPN should be considered bypassable. Defaults to
+ * {@code false}.
+ * @return this {@link Builder} object to facilitate chaining of method calls
+ */
+ @NonNull
+ public Builder setBypassable(boolean isBypassable) {
+ mIsBypassable = isBypassable;
+ return this;
+ }
+
+ /**
+ * Sets a proxy for the VPN network.
+ *
+ * <p>Note that this proxy is only a recommendation and it may be ignored by apps.
+ *
+ * @param proxy the ProxyInfo to be set for the VPN network
+ * @return this {@link Builder} object to facilitate chaining of method calls
+ */
+ @NonNull
+ public Builder setProxy(@Nullable ProxyInfo proxy) {
+ mProxyInfo = proxy;
+ return this;
+ }
+
+ /**
+ * Set the upper bound of the maximum transmission unit (MTU) of the VPN interface.
+ *
+ * <p>If it is not set, a safe value will be used. Additionally, the actual link MTU will be
+ * dynamically calculated/updated based on the underlying link's mtu.
+ *
+ * @param mtu the MTU (in bytes) of the VPN interface
+ * @return this {@link Builder} object to facilitate chaining of method calls
+ * @throws IllegalArgumentException if the value is not at least the minimum IPv6 MTU (1280)
+ */
+ @NonNull
+ public Builder setMaxMtu(int mtu) {
+ // IPv6 MTU is greater; since profiles may be started by the system on IPv4 and IPv6
+ // networks, the VPN must provide a link fulfilling the stricter of the two conditions
+ // (at least that of the IPv6 MTU).
+ if (mtu < LinkProperties.MIN_MTU_V6) {
+ throw new IllegalArgumentException(
+ "Max MTU must be at least " + LinkProperties.MIN_MTU_V6);
+ }
+ mMaxMtu = mtu;
+ return this;
+ }
+
+ /**
+ * Marks the VPN network as metered.
+ *
+ * <p>A VPN network is classified as metered when the user is sensitive to heavy data usage
+ * due to monetary costs and/or data limitations. In such cases, you should set this to
+ * {@code true} so that apps on the system can avoid doing large data transfers. Otherwise,
+ * set this to {@code false}. Doing so would cause VPN network to inherit its meteredness
+ * from the underlying network.
+ *
+ * @param isMetered {@code true} if the VPN network should be treated as metered regardless
+ * of underlying network meteredness. Defaults to {@code true}.
+ * @return this {@link Builder} object to facilitate chaining of method calls
+ * @see NetworkCapabilities.NET_CAPABILITY_NOT_METERED
+ */
+ @NonNull
+ public Builder setMetered(boolean isMetered) {
+ mIsMetered = isMetered;
+ return this;
+ }
+
+ /**
+ * Sets the allowable set of IPsec algorithms
+ *
+ * <p>A list of allowed IPsec algorithms as defined in {@link IpSecAlgorithm}
+ *
+ * @param algorithmNames the list of supported IPsec algorithms
+ * @return this {@link Builder} object to facilitate chaining of method calls
+ * @see IpSecAlgorithm
+ */
+ @NonNull
+ public Builder setAllowedAlgorithms(@NonNull List<String> algorithmNames) {
+ checkNotNull(algorithmNames, MISSING_PARAM_MSG_TMPL, "algorithmNames");
+ VpnProfile.validateAllowedAlgorithms(algorithmNames);
+
+ mAllowedAlgorithms = algorithmNames;
+ return this;
+ }
+
+ /**
+ * Validates, builds and provisions the VpnProfile.
+ *
+ * @throws IllegalArgumentException if any of the required keys or values were invalid
+ */
+ @NonNull
+ public Ikev2VpnProfile build() {
+ return new Ikev2VpnProfile(
+ mType,
+ mServerAddr,
+ mUserIdentity,
+ mPresharedKey,
+ mServerRootCaCert,
+ mUsername,
+ mPassword,
+ mRsaPrivateKey,
+ mUserCert,
+ mProxyInfo,
+ mAllowedAlgorithms,
+ mIsBypassable,
+ mIsMetered,
+ mMaxMtu);
+ }
+ }
+}
diff --git a/core/java/android/net/LinkAddress.java b/core/java/android/net/LinkAddress.java
index bf8b38fc7f84..8d9f0d068a57 100644
--- a/core/java/android/net/LinkAddress.java
+++ b/core/java/android/net/LinkAddress.java
@@ -19,6 +19,7 @@ package android.net;
import static android.system.OsConstants.IFA_F_DADFAILED;
import static android.system.OsConstants.IFA_F_DEPRECATED;
import static android.system.OsConstants.IFA_F_OPTIMISTIC;
+import static android.system.OsConstants.IFA_F_PERMANENT;
import static android.system.OsConstants.IFA_F_TENTATIVE;
import static android.system.OsConstants.RT_SCOPE_HOST;
import static android.system.OsConstants.RT_SCOPE_LINK;
@@ -34,6 +35,7 @@ import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
+import android.os.SystemClock;
import android.util.Pair;
import java.net.Inet4Address;
@@ -41,6 +43,7 @@ import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.InterfaceAddress;
import java.net.UnknownHostException;
+import java.util.Objects;
/**
* Identifies an IP address on a network link.
@@ -58,6 +61,21 @@ import java.net.UnknownHostException;
* </ul>
*/
public class LinkAddress implements Parcelable {
+
+ /**
+ * Indicates the deprecation or expiration time is unknown
+ * @hide
+ */
+ @SystemApi
+ public static final long LIFETIME_UNKNOWN = -1;
+
+ /**
+ * Indicates this address is permanent.
+ * @hide
+ */
+ @SystemApi
+ public static final long LIFETIME_PERMANENT = Long.MAX_VALUE;
+
/**
* IPv4 or IPv6 address.
*/
@@ -71,7 +89,9 @@ public class LinkAddress implements Parcelable {
private int prefixLength;
/**
- * Address flags. A bitmask of IFA_F_* values.
+ * Address flags. A bitmask of {@code IFA_F_*} values. Note that {@link #getFlags()} may not
+ * return these exact values. For example, it may set or clear the {@code IFA_F_DEPRECATED}
+ * flag depending on the current preferred lifetime.
*/
private int flags;
@@ -81,6 +101,23 @@ public class LinkAddress implements Parcelable {
private int scope;
/**
+ * The time, as reported by {@link SystemClock#elapsedRealtime}, when this LinkAddress will be
+ * or was deprecated. {@link #LIFETIME_UNKNOWN} indicates this information is not available. At
+ * the time existing connections can still use this address until it expires, but new
+ * connections should use the new address. {@link #LIFETIME_PERMANENT} indicates this
+ * {@link LinkAddress} will never be deprecated.
+ */
+ private long deprecationTime;
+
+ /**
+ * The time, as reported by {@link SystemClock#elapsedRealtime}, when this {@link LinkAddress}
+ * will expire and be removed from the interface. {@link #LIFETIME_UNKNOWN} indicates this
+ * information is not available. {@link #LIFETIME_PERMANENT} indicates this {@link LinkAddress}
+ * will never expire.
+ */
+ private long expirationTime;
+
+ /**
* Utility function to determines the scope of a unicast address. Per RFC 4291 section 2.5 and
* RFC 6724 section 3.2.
* @hide
@@ -152,7 +189,8 @@ public class LinkAddress implements Parcelable {
/**
* Utility function for the constructors.
*/
- private void init(InetAddress address, int prefixLength, int flags, int scope) {
+ private void init(InetAddress address, int prefixLength, int flags, int scope,
+ long deprecationTime, long expirationTime) {
if (address == null ||
address.isMulticastAddress() ||
prefixLength < 0 ||
@@ -161,15 +199,42 @@ public class LinkAddress implements Parcelable {
throw new IllegalArgumentException("Bad LinkAddress params " + address +
"/" + prefixLength);
}
+
+ // deprecation time and expiration time must be both provided, or neither.
+ if ((deprecationTime == LIFETIME_UNKNOWN) != (expirationTime == LIFETIME_UNKNOWN)) {
+ throw new IllegalArgumentException(
+ "Must not specify only one of deprecation time and expiration time");
+ }
+
+ // deprecation time needs to be a positive value.
+ if (deprecationTime != LIFETIME_UNKNOWN && deprecationTime < 0) {
+ throw new IllegalArgumentException("invalid deprecation time " + deprecationTime);
+ }
+
+ // expiration time needs to be a positive value.
+ if (expirationTime != LIFETIME_UNKNOWN && expirationTime < 0) {
+ throw new IllegalArgumentException("invalid expiration time " + expirationTime);
+ }
+
+ // expiration time can't be earlier than deprecation time
+ if (deprecationTime != LIFETIME_UNKNOWN && expirationTime != LIFETIME_UNKNOWN
+ && expirationTime < deprecationTime) {
+ throw new IllegalArgumentException("expiration earlier than deprecation ("
+ + deprecationTime + ", " + expirationTime + ")");
+ }
+
this.address = address;
this.prefixLength = prefixLength;
this.flags = flags;
this.scope = scope;
+ this.deprecationTime = deprecationTime;
+ this.expirationTime = expirationTime;
}
/**
* Constructs a new {@code LinkAddress} from an {@code InetAddress} and prefix length, with
* the specified flags and scope. Flags and scope are not checked for validity.
+ *
* @param address The IP address.
* @param prefixLength The prefix length. Must be &gt;= 0 and &lt;= (32 or 128) (IPv4 or IPv6).
* @param flags A bitmask of {@code IFA_F_*} values representing properties of the address.
@@ -181,7 +246,39 @@ public class LinkAddress implements Parcelable {
@TestApi
public LinkAddress(@NonNull InetAddress address, @IntRange(from = 0, to = 128) int prefixLength,
int flags, int scope) {
- init(address, prefixLength, flags, scope);
+ init(address, prefixLength, flags, scope, LIFETIME_UNKNOWN, LIFETIME_UNKNOWN);
+ }
+
+ /**
+ * Constructs a new {@code LinkAddress} from an {@code InetAddress}, prefix length, with
+ * the specified flags, scope, deprecation time, and expiration time. Flags and scope are not
+ * checked for validity. The value of the {@code IFA_F_DEPRECATED} and {@code IFA_F_PERMANENT}
+ * flag will be adjusted based on the passed-in lifetimes.
+ *
+ * @param address The IP address.
+ * @param prefixLength The prefix length. Must be &gt;= 0 and &lt;= (32 or 128) (IPv4 or IPv6).
+ * @param flags A bitmask of {@code IFA_F_*} values representing properties of the address.
+ * @param scope An integer defining the scope in which the address is unique (e.g.,
+ * {@link OsConstants#RT_SCOPE_LINK} or {@link OsConstants#RT_SCOPE_SITE}).
+ * @param deprecationTime The time, as reported by {@link SystemClock#elapsedRealtime}, when
+ * this {@link LinkAddress} will be or was deprecated.
+ * {@link #LIFETIME_UNKNOWN} indicates this information is not available.
+ * At the time existing connections can still use this address until it
+ * expires, but new connections should use the new address.
+ * {@link #LIFETIME_PERMANENT} indicates this {@link LinkAddress} will
+ * never be deprecated.
+ * @param expirationTime The time, as reported by {@link SystemClock#elapsedRealtime}, when this
+ * {@link LinkAddress} will expire and be removed from the interface.
+ * {@link #LIFETIME_UNKNOWN} indicates this information is not available.
+ * {@link #LIFETIME_PERMANENT} indicates this {@link LinkAddress} will
+ * never expire.
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ public LinkAddress(@NonNull InetAddress address, @IntRange(from = 0, to = 128) int prefixLength,
+ int flags, int scope, long deprecationTime, long expirationTime) {
+ init(address, prefixLength, flags, scope, deprecationTime, expirationTime);
}
/**
@@ -237,7 +334,7 @@ public class LinkAddress implements Parcelable {
// This may throw an IllegalArgumentException; catching it is the caller's responsibility.
// TODO: consider rejecting mapped IPv4 addresses such as "::ffff:192.0.2.5/24".
Pair<InetAddress, Integer> ipAndMask = NetworkUtils.parseIpAndMask(address);
- init(ipAndMask.first, ipAndMask.second, flags, scope);
+ init(ipAndMask.first, ipAndMask.second, flags, scope, LIFETIME_UNKNOWN, LIFETIME_UNKNOWN);
}
/**
@@ -265,10 +362,12 @@ public class LinkAddress implements Parcelable {
return false;
}
LinkAddress linkAddress = (LinkAddress) obj;
- return this.address.equals(linkAddress.address) &&
- this.prefixLength == linkAddress.prefixLength &&
- this.flags == linkAddress.flags &&
- this.scope == linkAddress.scope;
+ return this.address.equals(linkAddress.address)
+ && this.prefixLength == linkAddress.prefixLength
+ && this.flags == linkAddress.flags
+ && this.scope == linkAddress.scope
+ && this.deprecationTime == linkAddress.deprecationTime
+ && this.expirationTime == linkAddress.expirationTime;
}
/**
@@ -276,7 +375,7 @@ public class LinkAddress implements Parcelable {
*/
@Override
public int hashCode() {
- return address.hashCode() + 11 * prefixLength + 19 * flags + 43 * scope;
+ return Objects.hash(address, prefixLength, flags, scope, deprecationTime, expirationTime);
}
/**
@@ -329,6 +428,25 @@ public class LinkAddress implements Parcelable {
* Returns the flags of this {@code LinkAddress}.
*/
public int getFlags() {
+ int flags = this.flags;
+ if (deprecationTime != LIFETIME_UNKNOWN) {
+ if (SystemClock.elapsedRealtime() >= deprecationTime) {
+ flags |= IFA_F_DEPRECATED;
+ } else {
+ // If deprecation time is in the future, or permanent.
+ flags &= ~IFA_F_DEPRECATED;
+ }
+ }
+
+ if (expirationTime == LIFETIME_PERMANENT) {
+ flags |= IFA_F_PERMANENT;
+ } else if (expirationTime != LIFETIME_UNKNOWN) {
+ // If we know this address expired or will expire in the future or, then this address
+ // should not be permanent.
+ flags &= ~IFA_F_PERMANENT;
+ }
+
+ // Do no touch the original flags. Return the adjusted flags here.
return flags;
}
@@ -340,7 +458,35 @@ public class LinkAddress implements Parcelable {
}
/**
- * Returns true if this {@code LinkAddress} is global scope and preferred.
+ * @return The time that this address will be deprecated. At the time the existing connection
+ * can still use this address until it expires, but the new connection should use the new
+ * address. This is the EPOCH time in milliseconds. 0 indicates this information is not
+ * available.
+ *
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ public long getDeprecationTime() {
+ return deprecationTime;
+ }
+
+ /**
+ * @return The time that this address will expire and will be no longer valid. This is the EPOCH
+ * time in milliseconds. 0 indicates this information is not available.
+ *
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ public long getExpirationTime() {
+ return expirationTime;
+ }
+
+ /**
+ * Returns true if this {@code LinkAddress} is global scope and preferred (i.e., not currently
+ * deprecated).
+ *
* @hide
*/
@TestApi
@@ -352,6 +498,7 @@ public class LinkAddress implements Parcelable {
* state has cleared either DAD has succeeded or failed, and both
* flags are cleared regardless).
*/
+ int flags = getFlags();
return (scope == RT_SCOPE_UNIVERSE
&& !isIpv6ULA()
&& (flags & (IFA_F_DADFAILED | IFA_F_DEPRECATED)) == 0L
@@ -373,6 +520,8 @@ public class LinkAddress implements Parcelable {
dest.writeInt(prefixLength);
dest.writeInt(this.flags);
dest.writeInt(scope);
+ dest.writeLong(deprecationTime);
+ dest.writeLong(expirationTime);
}
/**
@@ -392,7 +541,10 @@ public class LinkAddress implements Parcelable {
int prefixLength = in.readInt();
int flags = in.readInt();
int scope = in.readInt();
- return new LinkAddress(address, prefixLength, flags, scope);
+ long deprecationTime = in.readLong();
+ long expirationTime = in.readLong();
+ return new LinkAddress(address, prefixLength, flags, scope, deprecationTime,
+ expirationTime);
}
public LinkAddress[] newArray(int size) {
diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java
index e83f5e4e810d..732ceb560cab 100644
--- a/core/java/android/net/LinkProperties.java
+++ b/core/java/android/net/LinkProperties.java
@@ -82,7 +82,8 @@ public final class LinkProperties implements Parcelable {
private final transient boolean mParcelSensitiveFields;
private static final int MIN_MTU = 68;
- private static final int MIN_MTU_V6 = 1280;
+ /* package-visibility - Used in other files (such as Ikev2VpnProfile) as minimum iface MTU. */
+ static final int MIN_MTU_V6 = 1280;
private static final int MAX_MTU = 10000;
private static final int INET6_ADDR_LENGTH = 16;
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index 738070b09264..4f4e27b446ef 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -26,6 +26,7 @@ import android.net.ConnectivityManager.NetworkCallback;
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
+import android.os.Process;
import android.util.ArraySet;
import android.util.proto.ProtoOutputStream;
@@ -35,6 +36,9 @@ import com.android.internal.util.Preconditions;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.StringJoiner;
@@ -55,7 +59,6 @@ import java.util.StringJoiner;
*/
public final class NetworkCapabilities implements Parcelable {
private static final String TAG = "NetworkCapabilities";
- private static final int INVALID_UID = -1;
// Set to true when private DNS is broken.
private boolean mPrivateDnsBroken;
@@ -82,7 +85,8 @@ public final class NetworkCapabilities implements Parcelable {
mTransportInfo = null;
mSignalStrength = SIGNAL_STRENGTH_UNSPECIFIED;
mUids = null;
- mEstablishingVpnAppUid = INVALID_UID;
+ mAdministratorUids.clear();
+ mOwnerUid = Process.INVALID_UID;
mSSID = null;
mPrivateDnsBroken = false;
}
@@ -100,7 +104,8 @@ public final class NetworkCapabilities implements Parcelable {
mTransportInfo = nc.mTransportInfo;
mSignalStrength = nc.mSignalStrength;
setUids(nc.mUids); // Will make the defensive copy
- mEstablishingVpnAppUid = nc.mEstablishingVpnAppUid;
+ setAdministratorUids(nc.mAdministratorUids);
+ mOwnerUid = nc.mOwnerUid;
mUnwantedNetworkCapabilities = nc.mUnwantedNetworkCapabilities;
mSSID = nc.mSSID;
mPrivateDnsBroken = nc.mPrivateDnsBroken;
@@ -805,31 +810,76 @@ public final class NetworkCapabilities implements Parcelable {
}
/**
- * UID of the app that manages this network, or INVALID_UID if none/unknown.
+ * UID of the app that owns this network, or INVALID_UID if none/unknown.
*
- * This field keeps track of the UID of the app that created this network and is in charge
- * of managing it. In the practice, it is used to store the UID of VPN apps so it is named
- * accordingly, but it may be renamed if other mechanisms are offered for third party apps
- * to create networks.
+ * <p>This field keeps track of the UID of the app that created this network and is in charge of
+ * its lifecycle. This could be the UID of apps such as the Wifi network suggestor, the running
+ * VPN, or Carrier Service app managing a cellular data connection.
+ */
+ private int mOwnerUid = Process.INVALID_UID;
+
+ /**
+ * Set the UID of the owner app.
+ */
+ public void setOwnerUid(final int uid) {
+ mOwnerUid = uid;
+ }
+
+ /**
+ * Retrieves the UID of the owner app.
+ */
+ public int getOwnerUid() {
+ return mOwnerUid;
+ }
+
+ /**
+ * UIDs of packages that are administrators of this network, or empty if none.
*
- * Because this field is only used in the services side (and to avoid apps being able to
- * set this to whatever they want), this field is not parcelled and will not be conserved
- * across the IPC boundary.
- * @hide
+ * <p>This field tracks the UIDs of packages that have permission to manage this network.
+ *
+ * <p>Network owners will also be listed as administrators.
+ *
+ * <p>For NetworkCapability instances being sent from the System Server, this value MUST be
+ * empty unless the destination is 1) the System Server, or 2) Telephony. In either case, the
+ * receiving entity must have the ACCESS_FINE_LOCATION permission and target R+.
*/
- private int mEstablishingVpnAppUid = INVALID_UID;
+ private final List<Integer> mAdministratorUids = new ArrayList<>();
/**
- * Set the UID of the managing app.
+ * Sets the list of UIDs that are administrators of this network.
+ *
+ * <p>UIDs included in administratorUids gain administrator privileges over this Network.
+ * Examples of UIDs that should be included in administratorUids are:
+ * <ul>
+ * <li>Carrier apps with privileges for the relevant subscription
+ * <li>Active VPN apps
+ * <li>Other application groups with a particular Network-related role
+ * </ul>
+ *
+ * <p>In general, user-supplied networks (such as WiFi networks) do not have an administrator.
+ *
+ * <p>An app is granted owner privileges over Networks that it supplies. Owner privileges
+ * implicitly include administrator privileges.
+ *
+ * @param administratorUids the UIDs to be set as administrators of this Network.
* @hide
*/
- public void setEstablishingVpnAppUid(final int uid) {
- mEstablishingVpnAppUid = uid;
+ @SystemApi
+ public void setAdministratorUids(@NonNull final List<Integer> administratorUids) {
+ mAdministratorUids.clear();
+ mAdministratorUids.addAll(administratorUids);
}
- /** @hide */
- public int getEstablishingVpnAppUid() {
- return mEstablishingVpnAppUid;
+ /**
+ * Retrieves the list of UIDs that are administrators of this Network.
+ *
+ * @return the List of UIDs that are administrators of this Network
+ * @hide
+ */
+ @NonNull
+ @SystemApi
+ public List<Integer> getAdministratorUids() {
+ return Collections.unmodifiableList(mAdministratorUids);
}
/**
@@ -1102,7 +1152,7 @@ public final class NetworkCapabilities implements Parcelable {
* member is null, then the network is not restricted by app UID. If it's an empty list, then
* it means nobody can use it.
* As a special exception, the app managing this network (as identified by its UID stored in
- * mEstablishingVpnAppUid) can always see this network. This is embodied by a special check in
+ * mOwnerUid) can always see this network. This is embodied by a special check in
* satisfiedByUids. That still does not mean the network necessarily <strong>applies</strong>
* to the app that manages it as determined by #appliesToUid.
* <p>
@@ -1209,7 +1259,7 @@ public final class NetworkCapabilities implements Parcelable {
* in the passed nc (representing the UIDs that this network is available to).
* <p>
* As a special exception, the UID that created the passed network (as represented by its
- * mEstablishingVpnAppUid field) always satisfies a NetworkRequest requiring it (of LISTEN
+ * mOwnerUid field) always satisfies a NetworkRequest requiring it (of LISTEN
* or REQUEST types alike), even if the network does not apply to it. That is so a VPN app
* can see its own network when it listens for it.
* <p>
@@ -1220,7 +1270,7 @@ public final class NetworkCapabilities implements Parcelable {
public boolean satisfiedByUids(@NonNull NetworkCapabilities nc) {
if (null == nc.mUids || null == mUids) return true; // The network satisfies everything.
for (UidRange requiredRange : mUids) {
- if (requiredRange.contains(nc.mEstablishingVpnAppUid)) return true;
+ if (requiredRange.contains(nc.mOwnerUid)) return true;
if (!nc.appliesToUidRange(requiredRange)) {
return false;
}
@@ -1471,6 +1521,7 @@ public final class NetworkCapabilities implements Parcelable {
public int describeContents() {
return 0;
}
+
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeLong(mNetworkCapabilities);
@@ -1484,6 +1535,8 @@ public final class NetworkCapabilities implements Parcelable {
dest.writeArraySet(mUids);
dest.writeString(mSSID);
dest.writeBoolean(mPrivateDnsBroken);
+ dest.writeList(mAdministratorUids);
+ dest.writeInt(mOwnerUid);
}
public static final @android.annotation.NonNull Creator<NetworkCapabilities> CREATOR =
@@ -1504,6 +1557,8 @@ public final class NetworkCapabilities implements Parcelable {
null /* ClassLoader, null for default */);
netCap.mSSID = in.readString();
netCap.mPrivateDnsBroken = in.readBoolean();
+ netCap.setAdministratorUids(in.readArrayList(null));
+ netCap.mOwnerUid = in.readInt();
return netCap;
}
@Override
@@ -1553,8 +1608,12 @@ public final class NetworkCapabilities implements Parcelable {
sb.append(" Uids: <").append(mUids).append(">");
}
}
- if (mEstablishingVpnAppUid != INVALID_UID) {
- sb.append(" EstablishingAppUid: ").append(mEstablishingVpnAppUid);
+ if (mOwnerUid != Process.INVALID_UID) {
+ sb.append(" OwnerUid: ").append(mOwnerUid);
+ }
+
+ if (!mAdministratorUids.isEmpty()) {
+ sb.append(" AdministratorUids: ").append(mAdministratorUids);
}
if (null != mSSID) {
diff --git a/core/java/android/net/PlatformVpnProfile.java b/core/java/android/net/PlatformVpnProfile.java
new file mode 100644
index 000000000000..fbae63707be2
--- /dev/null
+++ b/core/java/android/net/PlatformVpnProfile.java
@@ -0,0 +1,107 @@
+/*
+ * 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 static android.net.PlatformVpnProfile.TYPE_IKEV2_IPSEC_PSK;
+import static android.net.PlatformVpnProfile.TYPE_IKEV2_IPSEC_RSA;
+import static android.net.PlatformVpnProfile.TYPE_IKEV2_IPSEC_USER_PASS;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+
+import com.android.internal.net.VpnProfile;
+
+import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.security.GeneralSecurityException;
+
+/**
+ * PlatformVpnProfile represents a configuration for a platform-based VPN implementation.
+ *
+ * <p>Platform-based VPNs allow VPN applications to provide configuration and authentication options
+ * to leverage the Android OS' implementations of well-defined control plane (authentication, key
+ * negotiation) and data plane (per-packet encryption) protocols to simplify the creation of VPN
+ * tunnels. In contrast, {@link VpnService} based VPNs must implement both the control and data
+ * planes on a per-app basis.
+ *
+ * @see Ikev2VpnProfile
+ */
+public abstract class PlatformVpnProfile {
+ /**
+ * Alias to platform VPN related types from VpnProfile, for API use.
+ *
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({
+ TYPE_IKEV2_IPSEC_USER_PASS,
+ TYPE_IKEV2_IPSEC_PSK,
+ TYPE_IKEV2_IPSEC_RSA,
+ })
+ public static @interface PlatformVpnType {}
+
+ public static final int TYPE_IKEV2_IPSEC_USER_PASS = VpnProfile.TYPE_IKEV2_IPSEC_USER_PASS;
+ public static final int TYPE_IKEV2_IPSEC_PSK = VpnProfile.TYPE_IKEV2_IPSEC_PSK;
+ public static final int TYPE_IKEV2_IPSEC_RSA = VpnProfile.TYPE_IKEV2_IPSEC_RSA;
+
+ /** @hide */
+ @PlatformVpnType protected final int mType;
+
+ /** @hide */
+ PlatformVpnProfile(@PlatformVpnType int type) {
+ mType = type;
+ }
+ /** Returns the profile integer type. */
+ @PlatformVpnType
+ public final int getType() {
+ return mType;
+ }
+
+ /** Returns a type string describing the VPN profile type */
+ @NonNull
+ public final String getTypeString() {
+ switch (mType) {
+ case TYPE_IKEV2_IPSEC_USER_PASS:
+ return "IKEv2/IPsec Username/Password";
+ case TYPE_IKEV2_IPSEC_PSK:
+ return "IKEv2/IPsec Preshared key";
+ case TYPE_IKEV2_IPSEC_RSA:
+ return "IKEv2/IPsec RSA Digital Signature";
+ default:
+ return "Unknown VPN profile type";
+ }
+ }
+
+ /** @hide */
+ @NonNull
+ public abstract VpnProfile toVpnProfile() throws IOException, GeneralSecurityException;
+
+ /** @hide */
+ @NonNull
+ public static PlatformVpnProfile fromVpnProfile(@NonNull VpnProfile profile)
+ throws IOException, GeneralSecurityException {
+ switch (profile.type) {
+ case TYPE_IKEV2_IPSEC_USER_PASS: // fallthrough
+ case TYPE_IKEV2_IPSEC_PSK: // fallthrough
+ case TYPE_IKEV2_IPSEC_RSA:
+ return Ikev2VpnProfile.fromVpnProfile(profile);
+ default:
+ throw new IllegalArgumentException("Unknown VPN Profile type");
+ }
+ }
+}
diff --git a/core/java/android/net/VpnManager.java b/core/java/android/net/VpnManager.java
new file mode 100644
index 000000000000..f95807a14f00
--- /dev/null
+++ b/core/java/android/net/VpnManager.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.net;
+
+import static com.android.internal.util.Preconditions.checkNotNull;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.Intent;
+
+/**
+ * This class provides an interface for apps to manage platform VPN profiles
+ *
+ * <p>Apps can use this API to provide profiles with which the platform can set up a VPN without
+ * further app intermediation. When a VPN profile is present and the app is selected as an always-on
+ * VPN, the platform will directly trigger the negotiation of the VPN without starting or waking the
+ * app (unlike VpnService).
+ *
+ * <p>VPN apps using supported protocols should preferentially use this API over the {@link
+ * VpnService} API for ease-of-development and reduced maintainance burden. This also give the user
+ * the guarantee that VPN network traffic is not subjected to on-device packet interception.
+ *
+ * @see Ikev2VpnProfile
+ */
+public class VpnManager {
+ @NonNull private final Context mContext;
+ @NonNull private final IConnectivityManager mService;
+
+ /**
+ * Create an instance of the VpnManger with the given context.
+ *
+ * <p>Internal only. Applications are expected to obtain an instance of the VpnManager via the
+ * {@link Context.getSystemService()} method call.
+ *
+ * @hide
+ */
+ public VpnManager(@NonNull Context ctx, @NonNull IConnectivityManager service) {
+ mContext = checkNotNull(ctx, "missing Context");
+ mService = checkNotNull(service, "missing IConnectivityManager");
+ }
+
+ /**
+ * Install a VpnProfile configuration keyed on the calling app's package name.
+ *
+ * @param profile the PlatformVpnProfile provided by this package. Will override any previous
+ * PlatformVpnProfile stored for this package.
+ * @return an intent to request user consent if needed (null otherwise).
+ */
+ @Nullable
+ public Intent provisionVpnProfile(@NonNull PlatformVpnProfile profile) {
+ throw new UnsupportedOperationException("Not yet implemented");
+ }
+
+ /** Delete the VPN profile configuration that was provisioned by the calling app */
+ public void deleteProvisionedVpnProfile() {
+ throw new UnsupportedOperationException("Not yet implemented");
+ }
+
+ /**
+ * Request the startup of a previously provisioned VPN.
+ *
+ * @throws SecurityException exception if user or device settings prevent this VPN from being
+ * setup, or if user consent has not been granted
+ */
+ public void startProvisionedVpnProfile() {
+ throw new UnsupportedOperationException("Not yet implemented");
+ }
+
+ /** Tear down the VPN provided by the calling app (if any) */
+ public void stopProvisionedVpnProfile() {
+ throw new UnsupportedOperationException("Not yet implemented");
+ }
+}
diff --git a/core/java/android/os/BatteryStatsManager.java b/core/java/android/os/BatteryStatsManager.java
index 0545666ca743..152141edb52d 100644
--- a/core/java/android/os/BatteryStatsManager.java
+++ b/core/java/android/os/BatteryStatsManager.java
@@ -166,7 +166,7 @@ public class BatteryStatsManager {
* @param newRssi The new RSSI value.
*/
@RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
- public void noteWifiRssiChanged(@IntRange(from = -127, to = 0) int newRssi) {
+ public void reportWifiRssiChanged(@IntRange(from = -127, to = 0) int newRssi) {
try {
mBatteryStats.noteWifiRssiChanged(newRssi);
} catch (RemoteException e) {
@@ -178,7 +178,7 @@ public class BatteryStatsManager {
* Indicates that wifi was toggled on.
*/
@RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
- public void noteWifiOn() {
+ public void reportWifiOn() {
try {
mBatteryStats.noteWifiOn();
} catch (RemoteException e) {
@@ -190,7 +190,7 @@ public class BatteryStatsManager {
* Indicates that wifi was toggled off.
*/
@RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
- public void noteWifiOff() {
+ public void reportWifiOff() {
try {
mBatteryStats.noteWifiOff();
} catch (RemoteException e) {
@@ -205,7 +205,7 @@ public class BatteryStatsManager {
* @param accessPoint SSID of the network if wifi is connected to STA, else null.
*/
@RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
- public void noteWifiState(@WifiState int newWifiState,
+ public void reportWifiState(@WifiState int newWifiState,
@Nullable String accessPoint) {
try {
mBatteryStats.noteWifiState(newWifiState, accessPoint);
@@ -220,7 +220,7 @@ public class BatteryStatsManager {
* @param ws Worksource (to be used for battery blaming).
*/
@RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
- public void noteWifiScanStartedFromSource(@NonNull WorkSource ws) {
+ public void reportWifiScanStartedFromSource(@NonNull WorkSource ws) {
try {
mBatteryStats.noteWifiScanStartedFromSource(ws);
} catch (RemoteException e) {
@@ -234,7 +234,7 @@ public class BatteryStatsManager {
* @param ws Worksource (to be used for battery blaming).
*/
@RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
- public void noteWifiScanStoppedFromSource(@NonNull WorkSource ws) {
+ public void reportWifiScanStoppedFromSource(@NonNull WorkSource ws) {
try {
mBatteryStats.noteWifiScanStoppedFromSource(ws);
} catch (RemoteException e) {
@@ -249,7 +249,7 @@ public class BatteryStatsManager {
* @param csph Channels scanned per hour.
*/
@RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
- public void noteWifiBatchedScanStartedFromSource(@NonNull WorkSource ws,
+ public void reportWifiBatchedScanStartedFromSource(@NonNull WorkSource ws,
@IntRange(from = 0) int csph) {
try {
mBatteryStats.noteWifiBatchedScanStartedFromSource(ws, csph);
@@ -264,7 +264,7 @@ public class BatteryStatsManager {
* @param ws Worksource (to be used for battery blaming).
*/
@RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
- public void noteWifiBatchedScanStoppedFromSource(@NonNull WorkSource ws) {
+ public void reportWifiBatchedScanStoppedFromSource(@NonNull WorkSource ws) {
try {
mBatteryStats.noteWifiBatchedScanStoppedFromSource(ws);
} catch (RemoteException e) {
@@ -308,7 +308,7 @@ public class BatteryStatsManager {
* @param ws Worksource (to be used for battery blaming).
*/
@RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
- public void noteFullWifiLockAcquiredFromSource(@NonNull WorkSource ws) {
+ public void reportFullWifiLockAcquiredFromSource(@NonNull WorkSource ws) {
try {
mBatteryStats.noteFullWifiLockAcquiredFromSource(ws);
} catch (RemoteException e) {
@@ -322,7 +322,7 @@ public class BatteryStatsManager {
* @param ws Worksource (to be used for battery blaming).
*/
@RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
- public void noteFullWifiLockReleasedFromSource(@NonNull WorkSource ws) {
+ public void reportFullWifiLockReleasedFromSource(@NonNull WorkSource ws) {
try {
mBatteryStats.noteFullWifiLockReleasedFromSource(ws);
} catch (RemoteException e) {
@@ -338,7 +338,7 @@ public class BatteryStatsManager {
* authentication failure.
*/
@RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
- public void noteWifiSupplicantStateChanged(@WifiSupplState int newSupplState,
+ public void reportWifiSupplicantStateChanged(@WifiSupplState int newSupplState,
boolean failedAuth) {
try {
mBatteryStats.noteWifiSupplicantStateChanged(newSupplState, failedAuth);
@@ -353,7 +353,7 @@ public class BatteryStatsManager {
* @param uid UID of the app that acquired the wifi lock (to be used for battery blaming).
*/
@RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
- public void noteWifiMulticastEnabled(int uid) {
+ public void reportWifiMulticastEnabled(int uid) {
try {
mBatteryStats.noteWifiMulticastEnabled(uid);
} catch (RemoteException e) {
@@ -367,7 +367,7 @@ public class BatteryStatsManager {
* @param uid UID of the app that released the wifi lock (to be used for battery blaming).
*/
@RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
- public void noteWifiMulticastDisabled(int uid) {
+ public void reportWifiMulticastDisabled(int uid) {
try {
mBatteryStats.noteWifiMulticastDisabled(uid);
} catch (RemoteException e) {
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index c7a8474c8038..23ca2f03f044 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -179,6 +179,12 @@ public class Process {
public static final int OTA_UPDATE_UID = 1061;
/**
+ * Defines the UID used for statsd
+ * @hide
+ */
+ public static final int STATSD_UID = 1066;
+
+ /**
* Defines the UID used for incidentd.
* @hide
*/
diff --git a/core/java/android/os/StatsLogEventWrapper.java b/core/java/android/os/StatsLogEventWrapper.java
deleted file mode 100644
index 320fc137b8fa..000000000000
--- a/core/java/android/os/StatsLogEventWrapper.java
+++ /dev/null
@@ -1,267 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.os;
-
-import android.util.Slog;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Wrapper class for sending data from Android OS to StatsD.
- *
- * @hide
- */
-public final class StatsLogEventWrapper implements Parcelable {
- static final boolean DEBUG = false;
- static final String TAG = "StatsLogEventWrapper";
-
- // Keep in sync with FieldValue.h enums
- private static final int EVENT_TYPE_UNKNOWN = 0;
- private static final int EVENT_TYPE_INT = 1; /* int32_t */
- private static final int EVENT_TYPE_LONG = 2; /* int64_t */
- private static final int EVENT_TYPE_FLOAT = 3;
- private static final int EVENT_TYPE_DOUBLE = 4;
- private static final int EVENT_TYPE_STRING = 5;
- private static final int EVENT_TYPE_STORAGE = 6;
-
- List<Integer> mTypes = new ArrayList<>();
- List<Object> mValues = new ArrayList<>();
- int mTag;
- long mElapsedTimeNs;
- long mWallClockTimeNs;
- WorkSource mWorkSource = null;
-
- public StatsLogEventWrapper(int tag, long elapsedTimeNs, long wallClockTimeNs) {
- this.mTag = tag;
- this.mElapsedTimeNs = elapsedTimeNs;
- this.mWallClockTimeNs = wallClockTimeNs;
- }
-
- /**
- * Boilerplate for Parcel.
- */
- public static final @android.annotation.NonNull Parcelable.Creator<StatsLogEventWrapper> CREATOR = new
- Parcelable.Creator<StatsLogEventWrapper>() {
- public StatsLogEventWrapper createFromParcel(Parcel in) {
- return new StatsLogEventWrapper(in);
- }
-
- public StatsLogEventWrapper[] newArray(int size) {
- return new StatsLogEventWrapper[size];
- }
- };
-
- private StatsLogEventWrapper(Parcel in) {
- readFromParcel(in);
- }
-
- /**
- * Set work source if any.
- */
- public void setWorkSource(WorkSource ws) {
- if (ws.getWorkChains() == null || ws.getWorkChains().size() == 0) {
- Slog.w(TAG, "Empty worksource!");
- return;
- }
- mWorkSource = ws;
- }
-
- /**
- * Write a int value.
- */
- public void writeInt(int val) {
- mTypes.add(EVENT_TYPE_INT);
- mValues.add(val);
- }
-
- /**
- * Write a long value.
- */
- public void writeLong(long val) {
- mTypes.add(EVENT_TYPE_LONG);
- mValues.add(val);
- }
-
- /**
- * Write a string value.
- */
- public void writeString(String val) {
- mTypes.add(EVENT_TYPE_STRING);
- // use empty string for null
- mValues.add(val == null ? "" : val);
- }
-
- /**
- * Write a float value.
- */
- public void writeFloat(float val) {
- mTypes.add(EVENT_TYPE_FLOAT);
- mValues.add(val);
- }
-
- /**
- * Write a storage value.
- */
- public void writeStorage(byte[] val) {
- mTypes.add(EVENT_TYPE_STORAGE);
- mValues.add(val);
- }
-
- /**
- * Write a boolean value.
- */
- public void writeBoolean(boolean val) {
- mTypes.add(EVENT_TYPE_INT);
- mValues.add(val ? 1 : 0);
- }
-
- public void writeToParcel(Parcel out, int flags) {
- if (DEBUG) {
- Slog.d(TAG,
- "Writing " + mTag + " " + mElapsedTimeNs + " " + mWallClockTimeNs + " and "
- + mTypes.size() + " elements.");
- }
- out.writeInt(mTag);
- out.writeLong(mElapsedTimeNs);
- out.writeLong(mWallClockTimeNs);
- if (mWorkSource != null) {
- List<WorkSource.WorkChain> workChains = mWorkSource.getWorkChains();
- // number of chains
- out.writeInt(workChains.size());
- for (int i = 0; i < workChains.size(); i++) {
- android.os.WorkSource.WorkChain wc = workChains.get(i);
- if (wc.getSize() == 0) {
- Slog.w(TAG, "Empty work chain.");
- out.writeInt(0);
- continue;
- }
- if (wc.getUids().length != wc.getTags().length
- || wc.getUids().length != wc.getSize()) {
- Slog.w(TAG, "Malformated work chain.");
- out.writeInt(0);
- continue;
- }
- // number of nodes
- out.writeInt(wc.getSize());
- for (int j = 0; j < wc.getSize(); j++) {
- out.writeInt(wc.getUids()[j]);
- out.writeString(wc.getTags()[j] == null ? "" : wc.getTags()[j]);
- }
- }
- } else {
- // no chains
- out.writeInt(0);
- }
- out.writeInt(mTypes.size());
- for (int i = 0; i < mTypes.size(); i++) {
- out.writeInt(mTypes.get(i));
- switch (mTypes.get(i)) {
- case EVENT_TYPE_INT:
- out.writeInt((int) mValues.get(i));
- break;
- case EVENT_TYPE_LONG:
- out.writeLong((long) mValues.get(i));
- break;
- case EVENT_TYPE_FLOAT:
- out.writeFloat((float) mValues.get(i));
- break;
- case EVENT_TYPE_DOUBLE:
- out.writeDouble((double) mValues.get(i));
- break;
- case EVENT_TYPE_STRING:
- out.writeString((String) mValues.get(i));
- break;
- case EVENT_TYPE_STORAGE:
- out.writeByteArray((byte[]) mValues.get(i));
- break;
- default:
- break;
- }
- }
- }
-
- /**
- * Reads from parcel and appropriately fills member fields.
- */
- public void readFromParcel(Parcel in) {
- mTypes = new ArrayList<>();
- mValues = new ArrayList<>();
- mWorkSource = null;
-
- mTag = in.readInt();
- mElapsedTimeNs = in.readLong();
- mWallClockTimeNs = in.readLong();
-
- // Clear any data.
- if (DEBUG) {
- Slog.d(TAG, "Reading " + mTag + " " + mElapsedTimeNs + " " + mWallClockTimeNs);
- }
- // Set up worksource if present.
- int numWorkChains = in.readInt();
- if (numWorkChains > 0) {
- mWorkSource = new WorkSource();
- for (int i = 0; i < numWorkChains; i++) {
- android.os.WorkSource.WorkChain workChain = mWorkSource.createWorkChain();
- int workChainSize = in.readInt();
- for (int j = 0; j < workChainSize; j++) {
- int uid = in.readInt();
- String tag = in.readString();
- workChain.addNode(uid, tag);
- }
- }
- }
-
- // Do the rest of the types.
- int numTypes = in.readInt();
- if (DEBUG) {
- Slog.d(TAG, "Reading " + numTypes + " elements");
- }
- for (int i = 0; i < numTypes; i++) {
- int type = in.readInt();
- mTypes.add(type);
- switch (type) {
- case EVENT_TYPE_INT:
- mValues.add(in.readInt());
- break;
- case EVENT_TYPE_LONG:
- mValues.add(in.readLong());
- break;
- case EVENT_TYPE_FLOAT:
- mValues.add(in.readFloat());
- break;
- case EVENT_TYPE_DOUBLE:
- mValues.add(in.readDouble());
- break;
- case EVENT_TYPE_STRING:
- mValues.add(in.readString());
- break;
- case EVENT_TYPE_STORAGE:
- mValues.add(in.createByteArray());
- break;
- default:
- break;
- }
- }
- }
-
- /**
- * Boilerplate for Parcel.
- */
- public int describeContents() {
- return 0;
- }
-}
diff --git a/core/java/android/os/StatsServiceManager.java b/core/java/android/os/StatsServiceManager.java
new file mode 100644
index 000000000000..d032e98da00c
--- /dev/null
+++ b/core/java/android/os/StatsServiceManager.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.os;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+
+/**
+ * Provides a way to register and obtain the system service binder objects managed by the stats
+ * service.
+ *
+ * <p> Only the statsd mainline module will be able to access an instance of this class.
+ *
+ * TODO(b/148225705) Change to @SystemApi(client=MODULE_LIBRARIES) when the build system is ready.
+ * @hide
+ */
+@SystemApi
+public class StatsServiceManager {
+ /**
+ * @hide
+ */
+ public StatsServiceManager() {}
+
+ /**
+ * A class that exposes the methods to register and obtain each system service.
+ */
+ public static final class ServiceRegisterer {
+ private final String mServiceName;
+
+ /**
+ * @hide
+ */
+ public ServiceRegisterer(String serviceName) {
+ mServiceName = serviceName;
+ }
+
+ /**
+ * Get the system server binding object for StatsManagerService.
+ *
+ * <p> This blocks until the service instance is ready.
+ * or a timeout happens, in which case it returns null.
+ */
+ @Nullable
+ public IBinder get() {
+ return ServiceManager.getService(mServiceName);
+ }
+
+ /**
+ * Get the system server binding object for a service.
+ *
+ * <p>This blocks until the service instance is ready,
+ * or a timeout happens, in which case it throws {@link ServiceNotFoundException}.
+ */
+ @Nullable
+ public IBinder getOrThrow() throws ServiceNotFoundException {
+ try {
+ return ServiceManager.getServiceOrThrow(mServiceName);
+ } catch (ServiceManager.ServiceNotFoundException e) {
+ throw new ServiceNotFoundException(mServiceName);
+ }
+ }
+
+ /**
+ * Get the system server binding object for a service. If the specified service is
+ * not available, it returns null.
+ */
+ @Nullable
+ private IBinder tryGet() {
+ return ServiceManager.checkService(mServiceName);
+ }
+ }
+
+ /**
+ * See {@link ServiceRegisterer#getOrThrow()}
+ */
+ public static class ServiceNotFoundException extends ServiceManager.ServiceNotFoundException {
+ /**
+ * Constructor
+ *
+ * @param name the name of the binder service that cannot be found.
+ */
+ public ServiceNotFoundException(@NonNull String name) {
+ super(name);
+ }
+ }
+
+ /**
+ * Returns {@link ServiceRegisterer} for the "statscompanion" service.
+ */
+ @NonNull
+ public ServiceRegisterer getStatsCompanionServiceRegisterer() {
+ return new ServiceRegisterer("statscompanion");
+ }
+
+ /**
+ * Returns {@link ServiceRegisterer} for the "statsmanager" service.
+ */
+ @NonNull
+ public ServiceRegisterer getStatsManagerServiceRegisterer() {
+ return new ServiceRegisterer("statsmanager");
+ }
+
+ /**
+ * Returns {@link ServiceRegisterer} for the "statsd" service.
+ */
+ @NonNull
+ public ServiceRegisterer getStatsdServiceRegisterer() {
+ return new ServiceRegisterer("stats");
+ }
+}
diff --git a/core/java/android/os/TelephonyServiceManager.java b/core/java/android/os/TelephonyServiceManager.java
index 4f5f3d69b6f3..c67dedb4ac17 100644
--- a/core/java/android/os/TelephonyServiceManager.java
+++ b/core/java/android/os/TelephonyServiceManager.java
@@ -119,14 +119,6 @@ public class TelephonyServiceManager {
}
/**
- * Returns {@link ServiceRegisterer} for the telephony registry service.
- */
- @NonNull
- public ServiceRegisterer getTelephonyRegistryServiceRegisterer() {
- return new ServiceRegisterer("telephony.registry");
- }
-
- /**
* Returns {@link ServiceRegisterer} for the telephony IMS service.
*/
@NonNull
@@ -151,14 +143,6 @@ public class TelephonyServiceManager {
}
/**
- * Returns {@link ServiceRegisterer} for the network policy service.
- */
- @NonNull
- public ServiceRegisterer getNetworkPolicyServiceRegisterer() {
- return new ServiceRegisterer(Context.NETWORK_POLICY_SERVICE);
- }
-
- /**
* Returns {@link ServiceRegisterer} for the phone sub service.
*/
@NonNull
@@ -198,25 +182,12 @@ public class TelephonyServiceManager {
return new ServiceRegisterer("econtroller");
}
- @NonNull
- public ServiceRegisterer getEuiccCardControllerServiceRegisterer() {
- return new ServiceRegisterer("euicc_card_controller");
- }
-
/**
- * Returns {@link ServiceRegisterer} for the package manager service.
+ * Returns {@link ServiceRegisterer} for the eUICC card controller service.
*/
@NonNull
- public ServiceRegisterer getPackageManagerServiceRegisterer() {
- return new ServiceRegisterer("package");
- }
-
- /**
- * Returns {@link ServiceRegisterer} for the permission manager service.
- */
- @NonNull
- public ServiceRegisterer getPermissionManagerServiceRegisterer() {
- return new ServiceRegisterer("permissionmgr");
+ public ServiceRegisterer getEuiccCardControllerServiceRegisterer() {
+ return new ServiceRegisterer("euicc_card_controller");
}
/**
diff --git a/core/java/android/os/connectivity/WifiBatteryStats.java b/core/java/android/os/connectivity/WifiBatteryStats.java
index 895d837a359e..3c30f6343405 100644
--- a/core/java/android/os/connectivity/WifiBatteryStats.java
+++ b/core/java/android/os/connectivity/WifiBatteryStats.java
@@ -15,11 +15,13 @@
*/
package android.os.connectivity;
+import static android.os.BatteryStats.NUM_WIFI_SIGNAL_STRENGTH_BINS;
+import static android.os.BatteryStatsManager.NUM_WIFI_STATES;
+import static android.os.BatteryStatsManager.NUM_WIFI_SUPPL_STATES;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
-import android.os.BatteryStats;
-import android.os.BatteryStatsManager;
import android.os.Parcel;
import android.os.Parcelable;
@@ -33,31 +35,50 @@ import java.util.Objects;
*/
@SystemApi
public final class WifiBatteryStats implements Parcelable {
- private long mLoggingDurationMillis = 0;
- private long mKernelActiveTimeMillis = 0;
- private long mNumPacketsTx = 0;
- private long mNumBytesTx = 0;
- private long mNumPacketsRx = 0;
- private long mNumBytesRx = 0;
- private long mSleepTimeMillis = 0;
- private long mScanTimeMillis = 0;
- private long mIdleTimeMillis = 0;
- private long mRxTimeMillis = 0;
- private long mTxTimeMillis = 0;
- private long mEnergyConsumedMaMillis = 0;
- private long mNumAppScanRequest = 0;
- private long[] mTimeInStateMillis =
- new long[BatteryStatsManager.NUM_WIFI_STATES];
- private long[] mTimeInSupplicantStateMillis =
- new long[BatteryStatsManager.NUM_WIFI_SUPPL_STATES];
- private long[] mTimeInRxSignalStrengthLevelMillis =
- new long[BatteryStats.NUM_WIFI_SIGNAL_STRENGTH_BINS];
- private long mMonitoredRailChargeConsumedMaMillis = 0;
+ private final long mLoggingDurationMillis;
+ private final long mKernelActiveTimeMillis;
+ private final long mNumPacketsTx;
+ private final long mNumBytesTx;
+ private final long mNumPacketsRx;
+ private final long mNumBytesRx;
+ private final long mSleepTimeMillis;
+ private final long mScanTimeMillis;
+ private final long mIdleTimeMillis;
+ private final long mRxTimeMillis;
+ private final long mTxTimeMillis;
+ private final long mEnergyConsumedMaMillis;
+ private final long mAppScanRequestCount;
+ private final long[] mTimeInStateMillis;
+ private final long[] mTimeInSupplicantStateMillis;
+ private final long[] mTimeInRxSignalStrengthLevelMillis;
+ private final long mMonitoredRailChargeConsumedMaMillis;
public static final @NonNull Parcelable.Creator<WifiBatteryStats> CREATOR =
new Parcelable.Creator<WifiBatteryStats>() {
public WifiBatteryStats createFromParcel(Parcel in) {
- return new WifiBatteryStats(in);
+ long loggingDurationMillis = in.readLong();
+ long kernelActiveTimeMillis = in.readLong();
+ long numPacketsTx = in.readLong();
+ long numBytesTx = in.readLong();
+ long numPacketsRx = in.readLong();
+ long numBytesRx = in.readLong();
+ long sleepTimeMillis = in.readLong();
+ long scanTimeMillis = in.readLong();
+ long idleTimeMillis = in.readLong();
+ long rxTimeMillis = in.readLong();
+ long txTimeMillis = in.readLong();
+ long energyConsumedMaMillis = in.readLong();
+ long appScanRequestCount = in.readLong();
+ long[] timeInStateMillis = in.createLongArray();
+ long[] timeInRxSignalStrengthLevelMillis = in.createLongArray();
+ long[] timeInSupplicantStateMillis = in.createLongArray();
+ long monitoredRailChargeConsumedMaMillis = in.readLong();
+ return new WifiBatteryStats(loggingDurationMillis, kernelActiveTimeMillis,
+ numPacketsTx, numBytesTx, numPacketsRx, numBytesRx, sleepTimeMillis,
+ scanTimeMillis, idleTimeMillis, rxTimeMillis, txTimeMillis,
+ energyConsumedMaMillis, appScanRequestCount, timeInStateMillis,
+ timeInRxSignalStrengthLevelMillis, timeInSupplicantStateMillis,
+ monitoredRailChargeConsumedMaMillis);
}
public WifiBatteryStats[] newArray(int size) {
@@ -84,7 +105,7 @@ public final class WifiBatteryStats implements Parcelable {
out.writeLong(mRxTimeMillis);
out.writeLong(mTxTimeMillis);
out.writeLong(mEnergyConsumedMaMillis);
- out.writeLong(mNumAppScanRequest);
+ out.writeLong(mAppScanRequestCount);
out.writeLongArray(mTimeInStateMillis);
out.writeLongArray(mTimeInRxSignalStrengthLevelMillis);
out.writeLongArray(mTimeInSupplicantStateMillis);
@@ -108,7 +129,7 @@ public final class WifiBatteryStats implements Parcelable {
&& this.mRxTimeMillis == otherStats.mRxTimeMillis
&& this.mTxTimeMillis == otherStats.mTxTimeMillis
&& this.mEnergyConsumedMaMillis == otherStats.mEnergyConsumedMaMillis
- && this.mNumAppScanRequest == otherStats.mNumAppScanRequest
+ && this.mAppScanRequestCount == otherStats.mAppScanRequestCount
&& Arrays.equals(this.mTimeInStateMillis, otherStats.mTimeInStateMillis)
&& Arrays.equals(this.mTimeInSupplicantStateMillis,
otherStats.mTimeInSupplicantStateMillis)
@@ -123,33 +144,42 @@ public final class WifiBatteryStats implements Parcelable {
return Objects.hash(mLoggingDurationMillis, mKernelActiveTimeMillis, mNumPacketsTx,
mNumBytesTx, mNumPacketsRx, mNumBytesRx, mSleepTimeMillis, mScanTimeMillis,
mIdleTimeMillis, mRxTimeMillis, mTxTimeMillis, mEnergyConsumedMaMillis,
- mNumAppScanRequest, Arrays.hashCode(mTimeInStateMillis),
+ mAppScanRequestCount, Arrays.hashCode(mTimeInStateMillis),
Arrays.hashCode(mTimeInSupplicantStateMillis),
Arrays.hashCode(mTimeInRxSignalStrengthLevelMillis),
mMonitoredRailChargeConsumedMaMillis);
}
/** @hide **/
- public WifiBatteryStats() {}
-
- private void readFromParcel(Parcel in) {
- mLoggingDurationMillis = in.readLong();
- mKernelActiveTimeMillis = in.readLong();
- mNumPacketsTx = in.readLong();
- mNumBytesTx = in.readLong();
- mNumPacketsRx = in.readLong();
- mNumBytesRx = in.readLong();
- mSleepTimeMillis = in.readLong();
- mScanTimeMillis = in.readLong();
- mIdleTimeMillis = in.readLong();
- mRxTimeMillis = in.readLong();
- mTxTimeMillis = in.readLong();
- mEnergyConsumedMaMillis = in.readLong();
- mNumAppScanRequest = in.readLong();
- in.readLongArray(mTimeInStateMillis);
- in.readLongArray(mTimeInRxSignalStrengthLevelMillis);
- in.readLongArray(mTimeInSupplicantStateMillis);
- mMonitoredRailChargeConsumedMaMillis = in.readLong();
+ public WifiBatteryStats(long loggingDurationMillis, long kernelActiveTimeMillis,
+ long numPacketsTx, long numBytesTx, long numPacketsRx, long numBytesRx,
+ long sleepTimeMillis, long scanTimeMillis, long idleTimeMillis, long rxTimeMillis,
+ long txTimeMillis, long energyConsumedMaMillis, long appScanRequestCount,
+ @NonNull long[] timeInStateMillis, @NonNull long [] timeInRxSignalStrengthLevelMillis,
+ @NonNull long[] timeInSupplicantStateMillis, long monitoredRailChargeConsumedMaMillis) {
+ mLoggingDurationMillis = loggingDurationMillis;
+ mKernelActiveTimeMillis = kernelActiveTimeMillis;
+ mNumPacketsTx = numPacketsTx;
+ mNumBytesTx = numBytesTx;
+ mNumPacketsRx = numPacketsRx;
+ mNumBytesRx = numBytesRx;
+ mSleepTimeMillis = sleepTimeMillis;
+ mScanTimeMillis = scanTimeMillis;
+ mIdleTimeMillis = idleTimeMillis;
+ mRxTimeMillis = rxTimeMillis;
+ mTxTimeMillis = txTimeMillis;
+ mEnergyConsumedMaMillis = energyConsumedMaMillis;
+ mAppScanRequestCount = appScanRequestCount;
+ mTimeInStateMillis = Arrays.copyOfRange(
+ timeInStateMillis, 0,
+ Math.min(timeInStateMillis.length, NUM_WIFI_STATES));
+ mTimeInRxSignalStrengthLevelMillis = Arrays.copyOfRange(
+ timeInRxSignalStrengthLevelMillis, 0,
+ Math.min(timeInRxSignalStrengthLevelMillis.length, NUM_WIFI_SIGNAL_STRENGTH_BINS));
+ mTimeInSupplicantStateMillis = Arrays.copyOfRange(
+ timeInSupplicantStateMillis, 0,
+ Math.min(timeInSupplicantStateMillis.length, NUM_WIFI_SUPPL_STATES));
+ mMonitoredRailChargeConsumedMaMillis = monitoredRailChargeConsumedMaMillis;
}
/**
@@ -182,7 +212,7 @@ public final class WifiBatteryStats implements Parcelable {
}
/**
- * Returns the number of packets received over wifi within
+ * Returns the number of bytes transmitted over wifi within
* {@link #getLoggingDurationMillis()}.
*
* @return Number of packets received.
@@ -192,7 +222,7 @@ public final class WifiBatteryStats implements Parcelable {
}
/**
- * Returns the number of bytes transmitted over wifi within
+ * Returns the number of packets received over wifi within
* {@link #getLoggingDurationMillis()}.
*
* @return Number of bytes transmitted.
@@ -262,7 +292,7 @@ public final class WifiBatteryStats implements Parcelable {
}
/**
- * Returns an estimation of energy consumed by wifi chip within
+ * Returns an estimation of energy consumed in millis by wifi chip within
* {@link #getLoggingDurationMillis()}.
*
* @return Energy consumed in millis.
@@ -276,8 +306,8 @@ public final class WifiBatteryStats implements Parcelable {
*
* @return Number of app scans.
*/
- public long getNumAppScanRequest() {
- return mNumAppScanRequest;
+ public long getAppScanRequestCount() {
+ return mAppScanRequestCount;
}
/**
@@ -288,113 +318,4 @@ public final class WifiBatteryStats implements Parcelable {
public long getMonitoredRailChargeConsumedMaMillis() {
return mMonitoredRailChargeConsumedMaMillis;
}
-
- /** @hide */
- public void setLoggingDurationMillis(long t) {
- mLoggingDurationMillis = t;
- return;
- }
-
- /** @hide */
- public void setKernelActiveTimeMillis(long t) {
- mKernelActiveTimeMillis = t;
- return;
- }
-
- /** @hide */
- public void setNumPacketsTx(long n) {
- mNumPacketsTx = n;
- return;
- }
-
- /** @hide */
- public void setNumBytesTx(long b) {
- mNumBytesTx = b;
- return;
- }
-
- /** @hide */
- public void setNumPacketsRx(long n) {
- mNumPacketsRx = n;
- return;
- }
-
- /** @hide */
- public void setNumBytesRx(long b) {
- mNumBytesRx = b;
- return;
- }
-
- /** @hide */
- public void setSleepTimeMillis(long t) {
- mSleepTimeMillis = t;
- return;
- }
-
- /** @hide */
- public void setScanTimeMillis(long t) {
- mScanTimeMillis = t;
- return;
- }
-
- /** @hide */
- public void setIdleTimeMillis(long t) {
- mIdleTimeMillis = t;
- return;
- }
-
- /** @hide */
- public void setRxTimeMillis(long t) {
- mRxTimeMillis = t;
- return;
- }
-
- /** @hide */
- public void setTxTimeMillis(long t) {
- mTxTimeMillis = t;
- return;
- }
-
- /** @hide */
- public void setEnergyConsumedMaMillis(long e) {
- mEnergyConsumedMaMillis = e;
- return;
- }
-
- /** @hide */
- public void setNumAppScanRequest(long n) {
- mNumAppScanRequest = n;
- return;
- }
-
- /** @hide */
- public void setTimeInStateMillis(long[] t) {
- mTimeInStateMillis = Arrays.copyOfRange(t, 0,
- Math.min(t.length, BatteryStatsManager.NUM_WIFI_STATES));
- return;
- }
-
- /** @hide */
- public void setTimeInRxSignalStrengthLevelMillis(long[] t) {
- mTimeInRxSignalStrengthLevelMillis = Arrays.copyOfRange(t, 0,
- Math.min(t.length, BatteryStats.NUM_WIFI_SIGNAL_STRENGTH_BINS));
- return;
- }
-
- /** @hide */
- public void setTimeInSupplicantStateMillis(long[] t) {
- mTimeInSupplicantStateMillis = Arrays.copyOfRange(
- t, 0, Math.min(t.length, BatteryStatsManager.NUM_WIFI_SUPPL_STATES));
- return;
- }
-
- /** @hide */
- public void setMonitoredRailChargeConsumedMaMillis(long monitoredRailEnergyConsumedMaMillis) {
- mMonitoredRailChargeConsumedMaMillis = monitoredRailEnergyConsumedMaMillis;
- return;
- }
-
- private WifiBatteryStats(Parcel in) {
- readFromParcel(in);
- }
}
diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java
index 5a1ba7fe534c..4812ea98b569 100644
--- a/core/java/android/permission/PermissionManager.java
+++ b/core/java/android/permission/PermissionManager.java
@@ -266,6 +266,28 @@ public final class PermissionManager {
}
}
+ /**
+ * Grant default permissions to currently enabled carrier apps
+ * @param packageNames Package names of the apps to be granted permissions
+ * @param user The user handle
+ * @param executor The executor for the callback
+ * @param callback The callback provided by caller to be notified when grant completes
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.GRANT_RUNTIME_PERMISSIONS_TO_TELEPHONY_DEFAULTS)
+ public void grantDefaultPermissionsToEnabledCarrierApps(@NonNull String[] packageNames,
+ @NonNull UserHandle user, @NonNull @CallbackExecutor Executor executor,
+ @NonNull Consumer<Boolean> callback) {
+ try {
+ mPermissionManager.grantDefaultPermissionsToEnabledCarrierApps(packageNames,
+ user.getIdentifier());
+ executor.execute(() -> callback.accept(true));
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
private List<SplitPermissionInfo> splitPermissionInfoListToNonParcelableList(
List<SplitPermissionInfoParcelable> parcelableList) {
final int size = parcelableList.size();
@@ -416,4 +438,4 @@ public final class PermissionManager {
e.rethrowFromSystemServer();
}
}
-}
+} \ No newline at end of file
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 00b2feba8bcd..c16e77ee117f 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -8402,6 +8402,7 @@ public final class Settings {
*
* @hide
*/
+ @SystemApi
public static final String CARRIER_APPS_HANDLED = "carrier_apps_handled";
/**
@@ -9361,6 +9362,16 @@ public final class Settings {
public static final String DEVELOPMENT_ENABLE_SIZECOMPAT_FREEFORM =
"enable_sizecompat_freeform";
+ /**
+ * If true, shadows drawn around the window will be rendered by the system compositor. If
+ * false, shadows will be drawn by the client by setting an elevation on the root view and
+ * the contents will be inset by the surface insets.
+ * (0 = false, 1 = true)
+ * @hide
+ */
+ public static final String DEVELOPMENT_RENDER_SHADOWS_IN_COMPOSITOR =
+ "render_shadows_in_compositor";
+
/**
* Whether user has enabled development settings.
*/
@@ -9736,6 +9747,15 @@ public final class Settings {
*/
public static final String PACKAGE_VERIFIER_INCLUDE_ADB = "verifier_verify_adb_installs";
+ /**
+ * Run integrity checks for integrity rule providers.
+ * 0 = bypass integrity verification on installs from rule providers (default)
+ * 1 = perform integrity verification on installs from rule providers
+ * @hide
+ */
+ public static final String INTEGRITY_CHECK_INCLUDES_RULE_PROVIDER =
+ "verify_integrity_for_rule_provider";
+
/**
* Time since last fstrim (milliseconds) after which we force one to happen
* during device startup. If unset, the default is 3 days.
diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java
index ee9d8f55f40e..f25cdf1b8c98 100644
--- a/core/java/android/provider/Telephony.java
+++ b/core/java/android/provider/Telephony.java
@@ -5111,6 +5111,12 @@ public final class Telephony {
public static final String WFC_IMS_ROAMING_ENABLED = "wfc_ims_roaming_enabled";
/**
+ * Determines if the user has enabled IMS RCS User Capability Exchange (UCE) for this
+ * subscription.
+ */
+ public static final String IMS_RCS_UCE_ENABLED = "ims_rcs_uce_enabled";
+
+ /**
* TelephonyProvider column name for whether a subscription is opportunistic, that is,
* whether the network it connects to is limited in functionality or coverage.
* For example, CBRS.
diff --git a/core/java/android/se/omapi/ISecureElementReader.aidl b/core/java/android/se/omapi/ISecureElementReader.aidl
index a312c445395a..41244ab058e0 100644
--- a/core/java/android/se/omapi/ISecureElementReader.aidl
+++ b/core/java/android/se/omapi/ISecureElementReader.aidl
@@ -48,4 +48,10 @@ interface ISecureElementReader {
*/
void closeSessions();
+ /**
+ * Closes all the sessions opened on this reader and resets the reader.
+ * All the channels opened by all these sessions will be closed.
+ * @return true if the reset is successful, false otherwise.
+ */
+ boolean reset();
}
diff --git a/core/java/android/se/omapi/Reader.java b/core/java/android/se/omapi/Reader.java
index 80262f7533c8..7f68d9188650 100644
--- a/core/java/android/se/omapi/Reader.java
+++ b/core/java/android/se/omapi/Reader.java
@@ -23,6 +23,8 @@
package android.se.omapi;
import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
import android.util.Log;
@@ -150,4 +152,25 @@ public final class Reader {
} catch (RemoteException ignore) { }
}
}
+
+ /**
+ * Close all the sessions opened on this reader and reset the reader.
+ * All the channels opened by all these sessions will be closed.
+ * @return <code>true</code> if reset success, <code>false</code> otherwise.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.SECURE_ELEMENT_PRIVILEGED)
+ public boolean reset() {
+ if (!mService.isConnected()) {
+ Log.e(TAG, "service is not connected");
+ return false;
+ }
+ synchronized (mLock) {
+ try {
+ closeSessions();
+ return mReader.reset();
+ } catch (RemoteException ignore) {return false;}
+ }
+ }
}
diff --git a/core/java/android/service/autofill/FillResponse.java b/core/java/android/service/autofill/FillResponse.java
index dc0f5623e5e3..9333dbd1e1d5 100644
--- a/core/java/android/service/autofill/FillResponse.java
+++ b/core/java/android/service/autofill/FillResponse.java
@@ -87,6 +87,7 @@ public final class FillResponse implements Parcelable {
private final @Nullable UserData mUserData;
private final @Nullable int[] mCancelIds;
private final boolean mSupportsInlineSuggestions;
+ private final @Nullable ParceledListSlice<InlinePresentation> mInlineActions;
private FillResponse(@NonNull Builder builder) {
mDatasets = (builder.mDatasets != null) ? new ParceledListSlice<>(builder.mDatasets) : null;
@@ -105,6 +106,8 @@ public final class FillResponse implements Parcelable {
mUserData = builder.mUserData;
mCancelIds = builder.mCancelIds;
mSupportsInlineSuggestions = builder.mSupportsInlineSuggestions;
+ mInlineActions = (builder.mInlineActions != null) ? new ParceledListSlice<>(
+ builder.mInlineActions) : null;
}
/** @hide */
@@ -202,6 +205,11 @@ public final class FillResponse implements Parcelable {
return mSupportsInlineSuggestions;
}
+ /** @hide */
+ public @Nullable List<InlinePresentation> getInlineActions() {
+ return (mInlineActions != null) ? mInlineActions.getList() : null;
+ }
+
/**
* Builder for {@link FillResponse} objects. You must to provide at least
* one dataset or set an authentication intent with a presentation view.
@@ -223,6 +231,7 @@ public final class FillResponse implements Parcelable {
private UserData mUserData;
private int[] mCancelIds;
private boolean mSupportsInlineSuggestions;
+ private ArrayList<InlinePresentation> mInlineActions;
/**
* Triggers a custom UI before before autofilling the screen with any data set in this
@@ -578,6 +587,25 @@ public final class FillResponse implements Parcelable {
}
/**
+ * Adds a new {@link InlinePresentation} to this response representing an action UI.
+ *
+ * <p> For example, the UI can be associated with an intent which can open an activity for
+ * the user to manage the Autofill provider settings.
+ *
+ * @return This builder.
+ */
+ @NonNull
+ public Builder addInlineAction(@NonNull InlinePresentation inlineAction) {
+ throwIfDestroyed();
+ throwIfAuthenticationCalled();
+ if (mInlineActions == null) {
+ mInlineActions = new ArrayList<>();
+ }
+ mInlineActions.add(inlineAction);
+ return this;
+ }
+
+ /**
* Builds a new {@link FillResponse} instance.
*
* @throws IllegalStateException if any of the following conditions occur:
@@ -688,6 +716,10 @@ public final class FillResponse implements Parcelable {
if (mCancelIds != null) {
builder.append(", mCancelIds=").append(mCancelIds.length);
}
+ builder.append(", mSupportInlinePresentations=").append(mSupportsInlineSuggestions);
+ if (mInlineActions != null) {
+ builder.append(", mInlineActions=" + mInlineActions.getList());
+ }
return builder.append("]").toString();
}
@@ -717,6 +749,7 @@ public final class FillResponse implements Parcelable {
parcel.writeInt(mFlags);
parcel.writeIntArray(mCancelIds);
parcel.writeInt(mRequestId);
+ parcel.writeParcelable(mInlineActions, flags);
}
public static final @android.annotation.NonNull Parcelable.Creator<FillResponse> CREATOR =
@@ -771,6 +804,15 @@ public final class FillResponse implements Parcelable {
final int[] cancelIds = parcel.createIntArray();
builder.setPresentationCancelIds(cancelIds);
+ final ParceledListSlice<InlinePresentation> inlineActionsSlice = parcel.readParcelable(
+ null);
+ final List<InlinePresentation> inlineActions =
+ (inlineActionsSlice != null) ? inlineActionsSlice.getList() : null;
+ final int inlineActionsCount = (inlineActions != null) ? inlineActions.size() : 0;
+ for (int i = 0; i < inlineActionsCount; i++) {
+ builder.addInlineAction(inlineActions.get(i));
+ }
+
final FillResponse response = builder.build();
response.setRequestId(parcel.readInt());
diff --git a/core/java/android/service/autofill/InlinePresentation.java b/core/java/android/service/autofill/InlinePresentation.java
index 1568fb3af4c0..fb8406e99fa0 100644
--- a/core/java/android/service/autofill/InlinePresentation.java
+++ b/core/java/android/service/autofill/InlinePresentation.java
@@ -34,10 +34,22 @@ import com.android.internal.util.DataClass;
genEqualsHashCode = true)
public final class InlinePresentation implements Parcelable {
+
+ /**
+ * Represents the UI content and the action for the inline suggestion.
+ */
private final @NonNull Slice mSlice;
+ /**
+ * Specifies the UI specification for the inline suggestion.
+ */
private final @NonNull InlinePresentationSpec mInlinePresentationSpec;
+ /**
+ * Indicates whether the UI should be pinned, hence non-scrollable, in the host.
+ */
+ private final boolean mPinned;
+
// Code below generated by codegen v1.0.14.
@@ -53,30 +65,56 @@ public final class InlinePresentation implements Parcelable {
//@formatter:off
+ /**
+ * Creates a new InlinePresentation.
+ *
+ * @param slice
+ * Represents the UI content and the action for the inline suggestion.
+ * @param inlinePresentationSpec
+ * Specifies the UI specification for the inline suggestion.
+ * @param pinned
+ * Indicates whether the UI should be pinned, hence non-scrollable, in the host.
+ */
@DataClass.Generated.Member
public InlinePresentation(
@NonNull Slice slice,
- @NonNull InlinePresentationSpec inlinePresentationSpec) {
+ @NonNull InlinePresentationSpec inlinePresentationSpec,
+ boolean pinned) {
this.mSlice = slice;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mSlice);
this.mInlinePresentationSpec = inlinePresentationSpec;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mInlinePresentationSpec);
+ this.mPinned = pinned;
// onConstructed(); // You can define this method to get a callback
}
+ /**
+ * Represents the UI content and the action for the inline suggestion.
+ */
@DataClass.Generated.Member
public @NonNull Slice getSlice() {
return mSlice;
}
+ /**
+ * Specifies the UI specification for the inline suggestion.
+ */
@DataClass.Generated.Member
public @NonNull InlinePresentationSpec getInlinePresentationSpec() {
return mInlinePresentationSpec;
}
+ /**
+ * Indicates whether the UI should be pinned, hence non-scrollable, in the host.
+ */
+ @DataClass.Generated.Member
+ public boolean isPinned() {
+ return mPinned;
+ }
+
@Override
@DataClass.Generated.Member
public String toString() {
@@ -85,7 +123,8 @@ public final class InlinePresentation implements Parcelable {
return "InlinePresentation { " +
"slice = " + mSlice + ", " +
- "inlinePresentationSpec = " + mInlinePresentationSpec +
+ "inlinePresentationSpec = " + mInlinePresentationSpec + ", " +
+ "pinned = " + mPinned +
" }";
}
@@ -103,7 +142,8 @@ public final class InlinePresentation implements Parcelable {
//noinspection PointlessBooleanExpression
return true
&& java.util.Objects.equals(mSlice, that.mSlice)
- && java.util.Objects.equals(mInlinePresentationSpec, that.mInlinePresentationSpec);
+ && java.util.Objects.equals(mInlinePresentationSpec, that.mInlinePresentationSpec)
+ && mPinned == that.mPinned;
}
@Override
@@ -115,6 +155,7 @@ public final class InlinePresentation implements Parcelable {
int _hash = 1;
_hash = 31 * _hash + java.util.Objects.hashCode(mSlice);
_hash = 31 * _hash + java.util.Objects.hashCode(mInlinePresentationSpec);
+ _hash = 31 * _hash + Boolean.hashCode(mPinned);
return _hash;
}
@@ -124,6 +165,9 @@ public final class InlinePresentation implements Parcelable {
// You can override field parcelling by defining methods like:
// void parcelFieldName(Parcel dest, int flags) { ... }
+ byte flg = 0;
+ if (mPinned) flg |= 0x4;
+ dest.writeByte(flg);
dest.writeTypedObject(mSlice, flags);
dest.writeTypedObject(mInlinePresentationSpec, flags);
}
@@ -139,6 +183,8 @@ public final class InlinePresentation implements Parcelable {
// You can override field unparcelling by defining methods like:
// static FieldType unparcelFieldName(Parcel in) { ... }
+ byte flg = in.readByte();
+ boolean pinned = (flg & 0x4) != 0;
Slice slice = (Slice) in.readTypedObject(Slice.CREATOR);
InlinePresentationSpec inlinePresentationSpec = (InlinePresentationSpec) in.readTypedObject(InlinePresentationSpec.CREATOR);
@@ -148,6 +194,7 @@ public final class InlinePresentation implements Parcelable {
this.mInlinePresentationSpec = inlinePresentationSpec;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mInlinePresentationSpec);
+ this.mPinned = pinned;
// onConstructed(); // You can define this method to get a callback
}
@@ -167,10 +214,10 @@ public final class InlinePresentation implements Parcelable {
};
@DataClass.Generated(
- time = 1578081082387L,
+ time = 1579726472535L,
codegenVersion = "1.0.14",
sourceFile = "frameworks/base/core/java/android/service/autofill/InlinePresentation.java",
- inputSignatures = "private final @android.annotation.NonNull android.app.slice.Slice mSlice\nprivate final @android.annotation.NonNull android.view.inline.InlinePresentationSpec mInlinePresentationSpec\nclass InlinePresentation extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genHiddenConstDefs=true, genEqualsHashCode=true)")
+ inputSignatures = "private final @android.annotation.NonNull android.app.slice.Slice mSlice\nprivate final @android.annotation.NonNull android.view.inline.InlinePresentationSpec mInlinePresentationSpec\nprivate final boolean mPinned\nclass InlinePresentation extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genHiddenConstDefs=true, genEqualsHashCode=true)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/service/contentcapture/ContentCaptureService.java b/core/java/android/service/contentcapture/ContentCaptureService.java
index 3d82946876d1..36e2d1f6b251 100644
--- a/core/java/android/service/contentcapture/ContentCaptureService.java
+++ b/core/java/android/service/contentcapture/ContentCaptureService.java
@@ -34,9 +34,12 @@ import android.content.Intent;
import android.content.pm.ParceledListSlice;
import android.os.Binder;
import android.os.Bundle;
+import android.os.CancellationSignal;
import android.os.Handler;
import android.os.IBinder;
+import android.os.ICancellationSignal;
import android.os.Looper;
+import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.util.Log;
import android.util.Slog;
@@ -49,15 +52,20 @@ import android.view.contentcapture.ContentCaptureManager;
import android.view.contentcapture.ContentCaptureSession;
import android.view.contentcapture.ContentCaptureSessionId;
import android.view.contentcapture.DataRemovalRequest;
+import android.view.contentcapture.DataShareRequest;
import android.view.contentcapture.IContentCaptureDirectManager;
import android.view.contentcapture.MainContentCaptureSession;
import com.android.internal.os.IResultReceiver;
+import com.android.internal.util.Preconditions;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.lang.ref.WeakReference;
import java.util.List;
import java.util.Set;
+import java.util.concurrent.Executor;
+import java.util.function.Consumer;
/**
* A service used to capture the content of the screen to provide contextual data in other areas of
@@ -166,6 +174,12 @@ public abstract class ContentCaptureService extends Service {
}
@Override
+ public void onDataShared(DataShareRequest request, IDataShareCallback callback) {
+ mHandler.sendMessage(obtainMessage(ContentCaptureService::handleOnDataShared,
+ ContentCaptureService.this, request, callback));
+ }
+
+ @Override
public void onActivityEvent(ActivityEvent event) {
mHandler.sendMessage(obtainMessage(ContentCaptureService::handleOnActivityEvent,
ContentCaptureService.this, event));
@@ -318,6 +332,21 @@ public abstract class ContentCaptureService extends Service {
}
/**
+ * Notifies the service that data has been shared via a readable file.
+ *
+ * @param request request object containing information about data being shared
+ * @param callback callback to be fired with response on whether the request is "needed" and can
+ * be handled by the Content Capture service.
+ *
+ * @hide
+ */
+ @SystemApi
+ public void onDataShareRequest(@NonNull DataShareRequest request,
+ @NonNull DataShareCallback callback) {
+ if (sVerbose) Log.v(TAG, "onDataShareRequest()");
+ }
+
+ /**
* Notifies the service of {@link SnapshotData snapshot data} associated with a session.
*
* @param sessionId the session's Id
@@ -505,6 +534,37 @@ public abstract class ContentCaptureService extends Service {
onDataRemovalRequest(request);
}
+ private void handleOnDataShared(@NonNull DataShareRequest request,
+ IDataShareCallback callback) {
+ onDataShareRequest(request, new DataShareCallback() {
+
+ @Override
+ public void onAccept(@NonNull Executor executor,
+ @NonNull DataShareReadAdapter adapter) {
+ Preconditions.checkNotNull(adapter);
+ Preconditions.checkNotNull(executor);
+
+ DataShareReadAdapterDelegate delegate =
+ new DataShareReadAdapterDelegate(executor, adapter);
+
+ try {
+ callback.accept(delegate);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to accept data sharing", e);
+ }
+ }
+
+ @Override
+ public void onReject() {
+ try {
+ callback.reject();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to reject data sharing", e);
+ }
+ }
+ });
+ }
+
private void handleOnActivityEvent(@NonNull ActivityEvent event) {
onActivityEvent(event);
}
@@ -589,4 +649,57 @@ public abstract class ContentCaptureService extends Service {
Log.e(TAG, "failed to write flush metrics: " + e);
}
}
+
+ private static class DataShareReadAdapterDelegate extends IDataShareReadAdapter.Stub {
+
+ private final Object mLock = new Object();
+ private final WeakReference<DataShareReadAdapter> mAdapterReference;
+ private final WeakReference<Executor> mExecutorReference;
+
+ DataShareReadAdapterDelegate(Executor executor, DataShareReadAdapter adapter) {
+ Preconditions.checkNotNull(executor);
+ Preconditions.checkNotNull(adapter);
+
+ mExecutorReference = new WeakReference<>(executor);
+ mAdapterReference = new WeakReference<>(adapter);
+ }
+
+ @Override
+ public void start(ParcelFileDescriptor fd, ICancellationSignal remoteCancellationSignal)
+ throws RemoteException {
+ synchronized (mLock) {
+ CancellationSignal cancellationSignal = new CancellationSignal();
+ cancellationSignal.setRemote(remoteCancellationSignal);
+
+ executeAdapterMethodLocked(
+ adapter -> adapter.onStart(fd, cancellationSignal), "onStart");
+ }
+ }
+
+ @Override
+ public void error(int errorCode) throws RemoteException {
+ synchronized (mLock) {
+ executeAdapterMethodLocked(
+ adapter -> adapter.onError(errorCode), "onError");
+ }
+ }
+
+ private void executeAdapterMethodLocked(Consumer<DataShareReadAdapter> adapterFn,
+ String methodName) {
+ DataShareReadAdapter adapter = mAdapterReference.get();
+ Executor executor = mExecutorReference.get();
+
+ if (adapter == null || executor == null) {
+ Slog.w(TAG, "Can't execute " + methodName + "(), references have been GC'ed");
+ return;
+ }
+
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ executor.execute(() -> adapterFn.accept(adapter));
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+ }
}
diff --git a/core/java/android/service/contentcapture/DataShareCallback.java b/core/java/android/service/contentcapture/DataShareCallback.java
new file mode 100644
index 000000000000..e3c7bb3cd24f
--- /dev/null
+++ b/core/java/android/service/contentcapture/DataShareCallback.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.contentcapture;
+
+import android.annotation.CallbackExecutor;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+
+import java.util.concurrent.Executor;
+
+/**
+ * Callback for the Content Capture Service to accept or reject the data share request from a client
+ * app.
+ *
+ * If the request is rejected, client app would receive a signal and the data share session wouldn't
+ * be started.
+ *
+ * @hide
+ **/
+@SystemApi
+public interface DataShareCallback {
+
+ /** Accept the data share.
+ *
+ * @param executor executor to be used for running the adapter in.
+ * @param adapter adapter to be used for the share operation
+ */
+ void onAccept(@NonNull @CallbackExecutor Executor executor,
+ @NonNull DataShareReadAdapter adapter);
+
+ /** Reject the data share. */
+ void onReject();
+}
diff --git a/core/java/android/service/contentcapture/DataShareReadAdapter.java b/core/java/android/service/contentcapture/DataShareReadAdapter.java
new file mode 100644
index 000000000000..d9350ba5d774
--- /dev/null
+++ b/core/java/android/service/contentcapture/DataShareReadAdapter.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.contentcapture;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.CancellationSignal;
+import android.os.ParcelFileDescriptor;
+
+/**
+ * Adapter class to be used for the Content Capture Service app to propagate the status of the
+ * session
+ *
+ * @hide
+ **/
+@SystemApi
+public interface DataShareReadAdapter {
+
+ /**
+ * Signals the start of the data sharing session.
+ *
+ * @param fd file descriptor to use for reading data, that's being shared
+ * @param cancellationSignal cancellation signal to use if data is no longer needed and the
+ * session needs to be terminated.
+ **/
+ void onStart(@NonNull ParcelFileDescriptor fd, @NonNull CancellationSignal cancellationSignal);
+
+ /**
+ * Signals that the session failed to start or terminated unsuccessfully (e.g. due to a
+ * timeout).
+ **/
+ void onError(int errorCode);
+}
diff --git a/core/java/android/service/contentcapture/IContentCaptureService.aidl b/core/java/android/service/contentcapture/IContentCaptureService.aidl
index a7578af94004..277d82b5d909 100644
--- a/core/java/android/service/contentcapture/IContentCaptureService.aidl
+++ b/core/java/android/service/contentcapture/IContentCaptureService.aidl
@@ -20,8 +20,10 @@ import android.content.ComponentName;
import android.os.IBinder;
import android.service.contentcapture.ActivityEvent;
import android.service.contentcapture.SnapshotData;
+import android.service.contentcapture.IDataShareCallback;
import android.view.contentcapture.ContentCaptureContext;
import android.view.contentcapture.DataRemovalRequest;
+import android.view.contentcapture.DataShareRequest;
import com.android.internal.os.IResultReceiver;
@@ -40,5 +42,6 @@ oneway interface IContentCaptureService {
void onSessionFinished(int sessionId);
void onActivitySnapshot(int sessionId, in SnapshotData snapshotData);
void onDataRemovalRequest(in DataRemovalRequest request);
+ void onDataShared(in DataShareRequest request, in IDataShareCallback callback);
void onActivityEvent(in ActivityEvent event);
}
diff --git a/core/java/android/os/StatsLogEventWrapper.aidl b/core/java/android/service/contentcapture/IDataShareCallback.aidl
index 766343e38f3f..c1aa1bb7dcb5 100644
--- a/core/java/android/os/StatsLogEventWrapper.aidl
+++ b/core/java/android/service/contentcapture/IDataShareCallback.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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.
@@ -14,7 +14,12 @@
* limitations under the License.
*/
-package android.os;
+package android.service.contentcapture;
+
+import android.service.contentcapture.IDataShareReadAdapter;
/** @hide */
-parcelable StatsLogEventWrapper cpp_header "android/os/StatsLogEventWrapper.h"; \ No newline at end of file
+oneway interface IDataShareCallback {
+ void accept(in IDataShareReadAdapter adapter);
+ void reject();
+}
diff --git a/core/java/android/service/controls/actions/BooleanAction.aidl b/core/java/android/service/contentcapture/IDataShareReadAdapter.aidl
index d1e7e02730cc..73da5d515edc 100644
--- a/core/java/android/service/controls/actions/BooleanAction.aidl
+++ b/core/java/android/service/contentcapture/IDataShareReadAdapter.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * 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.
@@ -14,6 +14,12 @@
* limitations under the License.
*/
-package android.service.controls.actions;
+package android.service.contentcapture;
-parcelable BooleanAction; \ No newline at end of file
+import android.os.ICancellationSignal;
+
+/** @hide */
+oneway interface IDataShareReadAdapter {
+ void start(in ParcelFileDescriptor fd, in ICancellationSignal cancellationSignal);
+ void error(int errorCode);
+}
diff --git a/core/java/android/service/controls/Control.java b/core/java/android/service/controls/Control.java
index 43a308cf82cb..2d1d0ede62ca 100644
--- a/core/java/android/service/controls/Control.java
+++ b/core/java/android/service/controls/Control.java
@@ -19,12 +19,16 @@ package android.service.controls;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.app.PendingIntent;
import android.content.Intent;
+import android.content.res.ColorStateList;
+import android.graphics.drawable.Icon;
import android.os.Parcel;
import android.os.Parcelable;
import android.service.controls.actions.ControlAction;
import android.service.controls.templates.ControlTemplate;
+import android.service.controls.templates.ControlTemplateWrapper;
import android.util.Log;
import com.android.internal.util.Preconditions;
@@ -51,9 +55,8 @@ import java.lang.annotation.RetentionPolicy;
* <p>
* An {@link Intent} linking to the provider Activity that expands on this {@link Control} and
* allows for further actions should be provided.
- * @hide
*/
-public class Control implements Parcelable {
+public final class Control implements Parcelable {
private static final String TAG = "Control";
private static final int NUM_STATUS = 5;
@@ -99,6 +102,10 @@ public class Control implements Parcelable {
private final @Nullable CharSequence mStructure;
private final @Nullable CharSequence mZone;
private final @NonNull PendingIntent mAppIntent;
+
+ private final @Nullable Icon mCustomIcon;
+ private final @Nullable ColorStateList mCustomColor;
+
private final @Status int mStatus;
private final @NonNull ControlTemplate mControlTemplate;
private final @NonNull CharSequence mStatusText;
@@ -113,14 +120,21 @@ public class Control implements Parcelable {
* @param zone
* @param appIntent a {@link PendingIntent} linking to a page to interact with the
* corresponding device.
+ * @param customIcon
+ * @param customColor
+ * @param status
+ * @param controlTemplate
+ * @param statusText
*/
- public Control(@NonNull String controlId,
+ Control(@NonNull String controlId,
@DeviceTypes.DeviceType int deviceType,
@NonNull CharSequence title,
@NonNull CharSequence subtitle,
@Nullable CharSequence structure,
@Nullable CharSequence zone,
@NonNull PendingIntent appIntent,
+ @Nullable Icon customIcon,
+ @Nullable ColorStateList customColor,
@Status int status,
@NonNull ControlTemplate controlTemplate,
@NonNull CharSequence statusText) {
@@ -142,6 +156,10 @@ public class Control implements Parcelable {
mStructure = structure;
mZone = zone;
mAppIntent = appIntent;
+
+ mCustomColor = customColor;
+ mCustomIcon = customIcon;
+
if (status < 0 || status >= NUM_STATUS) {
mStatus = STATUS_UNKNOWN;
Log.e(TAG, "Status unknown:" + status);
@@ -152,7 +170,11 @@ public class Control implements Parcelable {
mStatusText = statusText;
}
- public Control(Parcel in) {
+ /**
+ * @param in
+ * @hide
+ */
+ Control(Parcel in) {
mControlId = in.readString();
mDeviceType = in.readInt();
mTitle = in.readCharSequence();
@@ -168,8 +190,22 @@ public class Control implements Parcelable {
mZone = null;
}
mAppIntent = PendingIntent.CREATOR.createFromParcel(in);
+
+ if (in.readByte() == (byte) 1) {
+ mCustomIcon = Icon.CREATOR.createFromParcel(in);
+ } else {
+ mCustomIcon = null;
+ }
+
+ if (in.readByte() == (byte) 1) {
+ mCustomColor = ColorStateList.CREATOR.createFromParcel(in);
+ } else {
+ mCustomColor = null;
+ }
+
mStatus = in.readInt();
- mControlTemplate = ControlTemplate.CREATOR.createFromParcel(in);
+ ControlTemplateWrapper wrapper = ControlTemplateWrapper.CREATOR.createFromParcel(in);
+ mControlTemplate = wrapper.getWrappedTemplate();
mStatusText = in.readCharSequence();
}
@@ -208,6 +244,16 @@ public class Control implements Parcelable {
return mAppIntent;
}
+ @Nullable
+ public Icon getCustomIcon() {
+ return mCustomIcon;
+ }
+
+ @Nullable
+ public ColorStateList getCustomColor() {
+ return mCustomColor;
+ }
+
@Status
public int getStatus() {
return mStatus;
@@ -229,7 +275,7 @@ public class Control implements Parcelable {
}
@Override
- public void writeToParcel(Parcel dest, int flags) {
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeString(mControlId);
dest.writeInt(mDeviceType);
dest.writeCharSequence(mTitle);
@@ -247,14 +293,27 @@ public class Control implements Parcelable {
dest.writeByte((byte) 0);
}
mAppIntent.writeToParcel(dest, flags);
+ if (mCustomIcon != null) {
+ dest.writeByte((byte) 1);
+ mCustomIcon.writeToParcel(dest, flags);
+ } else {
+ dest.writeByte((byte) 0);
+ }
+ if (mCustomColor != null) {
+ dest.writeByte((byte) 1);
+ mCustomColor.writeToParcel(dest, flags);
+ } else {
+ dest.writeByte((byte) 0);
+ }
+
dest.writeInt(mStatus);
- mControlTemplate.writeToParcel(dest, flags);
+ new ControlTemplateWrapper(mControlTemplate).writeToParcel(dest, flags);
dest.writeCharSequence(mStatusText);
}
- public static final Creator<Control> CREATOR = new Creator<Control>() {
+ public static final @NonNull Creator<Control> CREATOR = new Creator<Control>() {
@Override
- public Control createFromParcel(Parcel source) {
+ public Control createFromParcel(@NonNull Parcel source) {
return new Control(source);
}
@@ -275,25 +334,25 @@ public class Control implements Parcelable {
* <li> Subtitle: {@code ""}
* </ul>
* This fixes the values relating to state of the {@link Control} as required by
- * {@link ControlsProviderService#onLoad}:
+ * {@link ControlsProviderService#loadAvailableControls}:
* <ul>
* <li> Status: {@link Status#STATUS_UNKNOWN}
* <li> Control template: {@link ControlTemplate#NO_TEMPLATE}
* <li> Status text: {@code ""}
* </ul>
*/
- public static class StatelessBuilder {
+ @SuppressLint("MutableBareField")
+ public static final class StatelessBuilder {
private static final String TAG = "StatelessBuilder";
- protected @NonNull String mControlId;
- protected @DeviceTypes.DeviceType int mDeviceType = DeviceTypes.TYPE_UNKNOWN;
- protected @NonNull CharSequence mTitle = "";
- protected @NonNull CharSequence mSubtitle = "";
- protected @Nullable CharSequence mStructure;
- protected @Nullable CharSequence mZone;
- protected @NonNull PendingIntent mAppIntent;
- protected @Status int mStatus = STATUS_UNKNOWN;
- protected @NonNull ControlTemplate mControlTemplate = ControlTemplate.NO_TEMPLATE;
- protected @NonNull CharSequence mStatusText = "";
+ private @NonNull String mControlId;
+ private @DeviceTypes.DeviceType int mDeviceType = DeviceTypes.TYPE_UNKNOWN;
+ private @NonNull CharSequence mTitle = "";
+ private @NonNull CharSequence mSubtitle = "";
+ private @Nullable CharSequence mStructure;
+ private @Nullable CharSequence mZone;
+ private @NonNull PendingIntent mAppIntent;
+ private @Nullable Icon mCustomIcon;
+ private @Nullable ColorStateList mCustomColor;
/**
* @param controlId the identifier for the {@link Control}.
@@ -320,6 +379,8 @@ public class Control implements Parcelable {
mStructure = control.mStructure;
mZone = control.mZone;
mAppIntent = control.mAppIntent;
+ mCustomIcon = control.mCustomIcon;
+ mCustomColor = control.mCustomColor;
}
/**
@@ -385,6 +446,18 @@ public class Control implements Parcelable {
return this;
}
+ @NonNull
+ public StatelessBuilder setCustomIcon(@Nullable Icon customIcon) {
+ mCustomIcon = customIcon;
+ return this;
+ }
+
+ @NonNull
+ public StatelessBuilder setCustomColor(@Nullable ColorStateList customColor) {
+ mCustomColor = customColor;
+ return this;
+ }
+
/**
* Build a {@link Control}
* @return a valid {@link Control}
@@ -398,14 +471,42 @@ public class Control implements Parcelable {
mStructure,
mZone,
mAppIntent,
- mStatus,
- mControlTemplate,
- mStatusText);
+ mCustomIcon,
+ mCustomColor,
+ STATUS_UNKNOWN,
+ ControlTemplate.NO_TEMPLATE,
+ "");
}
}
- public static class StatefulBuilder extends StatelessBuilder {
+ /**
+ * Builder class for {@link Control}.
+ *
+ * This class facilitates the creation of {@link Control}.
+ * It provides the following defaults for non-optional parameters:
+ * <ul>
+ * <li> Device type: {@link DeviceTypes#TYPE_UNKNOWN}
+ * <li> Title: {@code ""}
+ * <li> Subtitle: {@code ""}
+ * <li> Status: {@link Status#STATUS_UNKNOWN}
+ * <li> Control template: {@link ControlTemplate#NO_TEMPLATE}
+ * <li> Status text: {@code ""}
+ * </ul>
+ */
+ public static final class StatefulBuilder {
private static final String TAG = "StatefulBuilder";
+ private @NonNull String mControlId;
+ private @DeviceTypes.DeviceType int mDeviceType = DeviceTypes.TYPE_UNKNOWN;
+ private @NonNull CharSequence mTitle = "";
+ private @NonNull CharSequence mSubtitle = "";
+ private @Nullable CharSequence mStructure;
+ private @Nullable CharSequence mZone;
+ private @NonNull PendingIntent mAppIntent;
+ private @Nullable Icon mCustomIcon;
+ private @Nullable ColorStateList mCustomColor;
+ private @Status int mStatus = STATUS_UNKNOWN;
+ private @NonNull ControlTemplate mControlTemplate = ControlTemplate.NO_TEMPLATE;
+ private @NonNull CharSequence mStatusText = "";
/**
* @param controlId the identifier for the {@link Control}.
@@ -413,11 +514,27 @@ public class Control implements Parcelable {
*/
public StatefulBuilder(@NonNull String controlId,
@NonNull PendingIntent appIntent) {
- super(controlId, appIntent);
+ Preconditions.checkNotNull(controlId);
+ Preconditions.checkNotNull(appIntent);
+ mControlId = controlId;
+ mAppIntent = appIntent;
}
+ /**
+ * Creates a {@link StatelessBuilder} using an existing {@link Control} as a base.
+ * @param control base for the builder.
+ */
public StatefulBuilder(@NonNull Control control) {
- super(control);
+ Preconditions.checkNotNull(control);
+ mControlId = control.mControlId;
+ mDeviceType = control.mDeviceType;
+ mTitle = control.mTitle;
+ mSubtitle = control.mSubtitle;
+ mStructure = control.mStructure;
+ mZone = control.mZone;
+ mAppIntent = control.mAppIntent;
+ mCustomIcon = control.mCustomIcon;
+ mCustomColor = control.mCustomColor;
mStatus = control.mStatus;
mControlTemplate = control.mControlTemplate;
mStatusText = control.mStatusText;
@@ -429,13 +546,19 @@ public class Control implements Parcelable {
*/
@NonNull
public StatefulBuilder setControlId(@NonNull String controlId) {
- super.setControlId(controlId);
+ Preconditions.checkNotNull(controlId);
+ mControlId = controlId;
return this;
}
@NonNull
public StatefulBuilder setDeviceType(@DeviceTypes.DeviceType int deviceType) {
- super.setDeviceType(deviceType);
+ if (!DeviceTypes.validDeviceType(deviceType)) {
+ Log.e(TAG, "Invalid device type:" + deviceType);
+ mDeviceType = DeviceTypes.TYPE_UNKNOWN;
+ } else {
+ mDeviceType = deviceType;
+ }
return this;
}
@@ -445,25 +568,27 @@ public class Control implements Parcelable {
*/
@NonNull
public StatefulBuilder setTitle(@NonNull CharSequence title) {
- super.setTitle(title);
+ Preconditions.checkNotNull(title);
+ mTitle = title;
return this;
}
@NonNull
public StatefulBuilder setSubtitle(@NonNull CharSequence subtitle) {
- super.setSubtitle(subtitle);
+ Preconditions.checkNotNull(subtitle);
+ mSubtitle = subtitle;
return this;
}
@NonNull
public StatefulBuilder setStructure(@Nullable CharSequence structure) {
- super.setStructure(structure);
+ mStructure = structure;
return this;
}
@NonNull
public StatefulBuilder setZone(@Nullable CharSequence zone) {
- super.setZone(zone);
+ mZone = zone;
return this;
}
@@ -473,7 +598,20 @@ public class Control implements Parcelable {
*/
@NonNull
public StatefulBuilder setAppIntent(@NonNull PendingIntent appIntent) {
- super.setAppIntent(appIntent);
+ Preconditions.checkNotNull(appIntent);
+ mAppIntent = appIntent;
+ return this;
+ }
+
+ @NonNull
+ public StatefulBuilder setCustomIcon(@Nullable Icon customIcon) {
+ mCustomIcon = customIcon;
+ return this;
+ }
+
+ @NonNull
+ public StatefulBuilder setCustomColor(@Nullable ColorStateList customColor) {
+ mCustomColor = customColor;
return this;
}
@@ -501,5 +639,21 @@ public class Control implements Parcelable {
mStatusText = statusText;
return this;
}
+
+ @NonNull
+ public Control build() {
+ return new Control(mControlId,
+ mDeviceType,
+ mTitle,
+ mSubtitle,
+ mStructure,
+ mZone,
+ mAppIntent,
+ mCustomIcon,
+ mCustomColor,
+ mStatus,
+ mControlTemplate,
+ mStatusText);
+ }
}
}
diff --git a/core/java/android/service/controls/ControlsProviderService.java b/core/java/android/service/controls/ControlsProviderService.java
index eca8541c9d33..bc6581887048 100644
--- a/core/java/android/service/controls/ControlsProviderService.java
+++ b/core/java/android/service/controls/ControlsProviderService.java
@@ -27,6 +27,7 @@ import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
import android.service.controls.actions.ControlAction;
+import android.service.controls.actions.ControlActionWrapper;
import android.service.controls.templates.ControlTemplate;
import android.text.TextUtils;
import android.util.Log;
@@ -35,150 +36,90 @@ import com.android.internal.util.Preconditions;
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.Flow.Publisher;
+import java.util.concurrent.Flow.Subscriber;
+import java.util.concurrent.Flow.Subscription;
+import java.util.function.Consumer;
/**
* Service implementation allowing applications to contribute controls to the
* System UI.
- * @hide
*/
public abstract class ControlsProviderService extends Service {
@SdkConstant(SdkConstantType.SERVICE_ACTION)
- public static final String CONTROLS_ACTION = "android.service.controls.ControlsProviderService";
- public static final String CALLBACK_BUNDLE = "CALLBACK_BUNDLE";
- public static final String CALLBACK_BINDER = "CALLBACK_BINDER";
- public static final String CALLBACK_TOKEN = "CALLBACK_TOKEN";
-
- public final String TAG = getClass().getSimpleName();
-
- private IControlsProviderCallback mCallback;
- private IBinder mToken;
- private RequestHandler mHandler;
-
+ public static final String SERVICE_CONTROLS =
+ "android.service.controls.ControlsProviderService";
/**
- * Signal to retrieve all Controls. When complete, call
- * {@link IControlsProviderCallback#onLoad} to inform the caller.
+ * @hide
*/
- public abstract void load();
+ public static final String CALLBACK_BUNDLE = "CALLBACK_BUNDLE";
/**
- * Informs the service that the caller is listening for updates to the given controlIds.
- * {@link IControlsProviderCallback#onRefreshState} should be called any time
- * there are Control updates to render.
+ * @hide
*/
- public abstract void subscribe(@NonNull List<String> controlIds);
+ public static final String CALLBACK_TOKEN = "CALLBACK_TOKEN";
- /**
- * Informs the service that the caller is done listening for updates,
- * and any calls to {@link IControlsProviderCallback#onRefreshState} will be ignored.
- */
- public abstract void unsubscribe();
+ public static final @NonNull String TAG = "ControlsProviderService";
- /**
- * The user has interacted with a Control. The action is dictated by the type of
- * {@link ControlAction} that was sent.
- */
- public abstract void onAction(@NonNull String controlId, @NonNull ControlAction action);
+ private IBinder mToken;
+ private RequestHandler mHandler;
/**
- * Sends a list of the controls available from this service.
- *
- * The items in the list must not have state information (as created by
- * {@link Control.StatelessBuilder}).
- * @param controls
+ * Retrieve all available controls, using the stateless builder
+ * {@link Control.StatelessBuilder} to build each Control, then use the
+ * provided consumer to callback to the call originator.
*/
- public final void onLoad(@NonNull List<Control> controls) {
- Preconditions.checkNotNull(controls);
- List<Control> list = new ArrayList<>();
- for (Control control: controls) {
- if (control == null) {
- Log.e(TAG, "onLoad: null control.");
- }
- if (isStateless(control)) {
- list.add(control);
- } else {
- Log.w(TAG, "onLoad: control is not stateless.");
- list.add(new Control.StatelessBuilder(control).build());
- }
- }
- try {
- mCallback.onLoad(mToken, list);
- } catch (RemoteException ex) {
- ex.rethrowAsRuntimeException();
- }
- }
+ public abstract void loadAvailableControls(@NonNull Consumer<List<Control>> consumer);
/**
- * Sends a list of the controls requested by {@link ControlsProviderService#subscribe} with
- * their state.
- * @param statefulControls
+ * Return a valid Publisher for the given controlIds. This publisher will be asked
+ * to provide updates for the given list of controlIds as long as the Subscription
+ * is valid.
*/
- public final void onRefreshState(@NonNull List<Control> statefulControls) {
- Preconditions.checkNotNull(statefulControls);
- try {
- mCallback.onRefreshState(mToken, statefulControls);
- } catch (RemoteException ex) {
- ex.rethrowAsRuntimeException();
- }
- }
+ @NonNull
+ public abstract Publisher<Control> publisherFor(@NonNull List<String> controlIds);
/**
- * Sends the response of a command in the specified {@link Control}.
- * @param controlId
- * @param response
+ * The user has interacted with a Control. The action is dictated by the type of
+ * {@link ControlAction} that was sent. A response can be sent via
+ * {@link Consumer#accept}, with the Integer argument being one of the provided
+ * {@link ControlAction.ResponseResult}. The Integer should indicate whether the action
+ * was received successfully, or if additional prompts should be presented to
+ * the user. Any visual control updates should be sent via the Publisher.
*/
- public final void onControlActionResponse(
- @NonNull String controlId, @ControlAction.ResponseResult int response) {
- Preconditions.checkNotNull(controlId);
- if (!ControlAction.isValidResponse(response)) {
- Log.e(TAG, "Not valid response result: " + response);
- response = ControlAction.RESPONSE_UNKNOWN;
- }
- try {
- mCallback.onControlActionResponse(mToken, controlId, response);
- } catch (RemoteException ex) {
- ex.rethrowAsRuntimeException();
- }
- }
-
- private boolean isStateless(Control control) {
- return (control.getStatus() == Control.STATUS_UNKNOWN
- && control.getControlTemplate().getTemplateType() == ControlTemplate.TYPE_NONE
- && TextUtils.isEmpty(control.getStatusText()));
- }
+ public abstract void performControlAction(@NonNull String controlId,
+ @NonNull ControlAction action, @NonNull Consumer<Integer> consumer);
@Override
- public IBinder onBind(Intent intent) {
+ @NonNull
+ public final IBinder onBind(@NonNull Intent intent) {
mHandler = new RequestHandler(Looper.getMainLooper());
Bundle bundle = intent.getBundleExtra(CALLBACK_BUNDLE);
- IBinder callbackBinder = bundle.getBinder(CALLBACK_BINDER);
mToken = bundle.getBinder(CALLBACK_TOKEN);
- mCallback = IControlsProviderCallback.Stub.asInterface(callbackBinder);
return new IControlsProvider.Stub() {
- public void load() {
- mHandler.sendEmptyMessage(RequestHandler.MSG_LOAD);
+ public void load(IControlsLoadCallback cb) {
+ mHandler.obtainMessage(RequestHandler.MSG_LOAD, cb).sendToTarget();
}
- public void subscribe(List<String> ids) {
- mHandler.obtainMessage(RequestHandler.MSG_SUBSCRIBE, ids).sendToTarget();
+ public void subscribe(List<String> controlIds,
+ IControlsSubscriber subscriber) {
+ SubscribeMessage msg = new SubscribeMessage(controlIds, subscriber);
+ mHandler.obtainMessage(RequestHandler.MSG_SUBSCRIBE, msg).sendToTarget();
}
- public void unsubscribe() {
- mHandler.sendEmptyMessage(RequestHandler.MSG_UNSUBSCRIBE);
- }
-
- public void onAction(String id, ControlAction action) {
- ActionMessage msg = new ActionMessage(id, action);
- mHandler.obtainMessage(RequestHandler.MSG_ON_ACTION, msg).sendToTarget();
+ public void action(String controlId, ControlActionWrapper action,
+ IControlsActionCallback cb) {
+ ActionMessage msg = new ActionMessage(controlId, action.getWrappedAction(), cb);
+ mHandler.obtainMessage(RequestHandler.MSG_ACTION, msg).sendToTarget();
}
};
}
@Override
- public boolean onUnbind(Intent intent) {
- mCallback = null;
+ public boolean onUnbind(@NonNull Intent intent) {
mHandler = null;
return true;
}
@@ -186,8 +127,7 @@ public abstract class ControlsProviderService extends Service {
private class RequestHandler extends Handler {
private static final int MSG_LOAD = 1;
private static final int MSG_SUBSCRIBE = 2;
- private static final int MSG_UNSUBSCRIBE = 3;
- private static final int MSG_ON_ACTION = 4;
+ private static final int MSG_ACTION = 3;
RequestHandler(Looper looper) {
super(looper);
@@ -196,30 +136,136 @@ public abstract class ControlsProviderService extends Service {
public void handleMessage(Message msg) {
switch(msg.what) {
case MSG_LOAD:
- ControlsProviderService.this.load();
+ final IControlsLoadCallback cb = (IControlsLoadCallback) msg.obj;
+ ControlsProviderService.this.loadAvailableControls(consumerFor(cb));
break;
+
case MSG_SUBSCRIBE:
- List<String> ids = (List<String>) msg.obj;
- ControlsProviderService.this.subscribe(ids);
- break;
- case MSG_UNSUBSCRIBE:
- ControlsProviderService.this.unsubscribe();
+ final SubscribeMessage sMsg = (SubscribeMessage) msg.obj;
+ final IControlsSubscriber cs = sMsg.mSubscriber;
+ Subscriber<Control> s = new Subscriber<Control>() {
+ public void onSubscribe(Subscription subscription) {
+ try {
+ cs.onSubscribe(mToken, new SubscriptionAdapter(subscription));
+ } catch (RemoteException ex) {
+ ex.rethrowAsRuntimeException();
+ }
+ }
+ public void onNext(@NonNull Control statefulControl) {
+ Preconditions.checkNotNull(statefulControl);
+ try {
+ cs.onNext(mToken, statefulControl);
+ } catch (RemoteException ex) {
+ ex.rethrowAsRuntimeException();
+ }
+ }
+ public void onError(Throwable t) {
+ try {
+ cs.onError(mToken, t.toString());
+ } catch (RemoteException ex) {
+ ex.rethrowAsRuntimeException();
+ }
+ }
+ public void onComplete() {
+ try {
+ cs.onComplete(mToken);
+ } catch (RemoteException ex) {
+ ex.rethrowAsRuntimeException();
+ }
+ }
+ };
+ ControlsProviderService.this.publisherFor(sMsg.mControlIds).subscribe(s);
break;
- case MSG_ON_ACTION:
- ActionMessage aMsg = (ActionMessage) msg.obj;
- ControlsProviderService.this.onAction(aMsg.mId, aMsg.mAction);
+
+ case MSG_ACTION:
+ final ActionMessage aMsg = (ActionMessage) msg.obj;
+ ControlsProviderService.this.performControlAction(aMsg.mControlId,
+ aMsg.mAction, consumerFor(aMsg.mControlId, aMsg.mCb));
break;
}
}
+
+ private Consumer<Integer> consumerFor(final String controlId,
+ final IControlsActionCallback cb) {
+ return (@NonNull Integer response) -> {
+ Preconditions.checkNotNull(response);
+ if (!ControlAction.isValidResponse(response)) {
+ Log.e(TAG, "Not valid response result: " + response);
+ response = ControlAction.RESPONSE_UNKNOWN;
+ }
+ try {
+ cb.accept(mToken, controlId, response);
+ } catch (RemoteException ex) {
+ ex.rethrowAsRuntimeException();
+ }
+ };
+ }
+
+ private Consumer<List<Control>> consumerFor(IControlsLoadCallback cb) {
+ return (@NonNull List<Control> controls) -> {
+ Preconditions.checkNotNull(controls);
+ List<Control> list = new ArrayList<>();
+ for (Control control: controls) {
+ if (control == null) {
+ Log.e(TAG, "onLoad: null control.");
+ }
+ if (isStatelessControl(control)) {
+ list.add(control);
+ } else {
+ Log.w(TAG, "onLoad: control is not stateless.");
+ list.add(new Control.StatelessBuilder(control).build());
+ }
+ }
+ try {
+ cb.accept(mToken, list);
+ } catch (RemoteException ex) {
+ ex.rethrowAsRuntimeException();
+ }
+ };
+ }
+
+ private boolean isStatelessControl(Control control) {
+ return (control.getStatus() == Control.STATUS_UNKNOWN
+ && control.getControlTemplate().getTemplateType() == ControlTemplate.TYPE_NONE
+ && TextUtils.isEmpty(control.getStatusText()));
+ }
+ }
+
+ private static class SubscriptionAdapter extends IControlsSubscription.Stub {
+ final Subscription mSubscription;
+
+ SubscriptionAdapter(Subscription s) {
+ this.mSubscription = s;
+ }
+
+ public void request(long n) {
+ mSubscription.request(n);
+ }
+
+ public void cancel() {
+ mSubscription.cancel();
+ }
}
- private class ActionMessage {
- final String mId;
+ private static class ActionMessage {
+ final String mControlId;
final ControlAction mAction;
+ final IControlsActionCallback mCb;
- ActionMessage(String id, ControlAction action) {
- this.mId = id;
+ ActionMessage(String controlId, ControlAction action, IControlsActionCallback cb) {
+ this.mControlId = controlId;
this.mAction = action;
+ this.mCb = cb;
+ }
+ }
+
+ private static class SubscribeMessage {
+ final List<String> mControlIds;
+ final IControlsSubscriber mSubscriber;
+
+ SubscribeMessage(List<String> controlIds, IControlsSubscriber subscriber) {
+ this.mControlIds = controlIds;
+ this.mSubscriber = subscriber;
}
}
}
diff --git a/core/java/android/service/controls/DeviceTypes.java b/core/java/android/service/controls/DeviceTypes.java
index b2d1c08d65ba..8dbb9cf8f9f7 100644
--- a/core/java/android/service/controls/DeviceTypes.java
+++ b/core/java/android/service/controls/DeviceTypes.java
@@ -21,9 +21,6 @@ import android.annotation.IntDef;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-/**
- * @hide
- */
public class DeviceTypes {
// Update this when adding new concrete types. Does not count TYPE_UNKNOWN
diff --git a/core/java/android/service/controls/templates/ToggleTemplate.aidl b/core/java/android/service/controls/IControlsActionCallback.aidl
index 98a9e49b7664..eab4c89cb8e6 100644
--- a/core/java/android/service/controls/templates/ToggleTemplate.aidl
+++ b/core/java/android/service/controls/IControlsActionCallback.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2019, The Android Open Source Project
+ * Copyright (c) 2020, The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,6 +14,11 @@
* limitations under the License.
*/
-package android.service.controls.templates;
+package android.service.controls;
-parcelable ToggleTemplate; \ No newline at end of file
+/**
+ * @hide
+ */
+oneway interface IControlsActionCallback {
+ void accept(in IBinder token, in String controlId, int response);
+} \ No newline at end of file
diff --git a/core/java/android/service/controls/IControlsLoadCallback.aidl b/core/java/android/service/controls/IControlsLoadCallback.aidl
new file mode 100644
index 000000000000..bfc61cdb54db
--- /dev/null
+++ b/core/java/android/service/controls/IControlsLoadCallback.aidl
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.controls;
+
+import android.service.controls.Control;
+
+/**
+ * @hide
+ */
+oneway interface IControlsLoadCallback {
+ void accept(in IBinder token, in List<Control> controls);
+} \ No newline at end of file
diff --git a/core/java/android/service/controls/IControlsProvider.aidl b/core/java/android/service/controls/IControlsProvider.aidl
index 6c105bb20c80..4ce658ed6990 100644
--- a/core/java/android/service/controls/IControlsProvider.aidl
+++ b/core/java/android/service/controls/IControlsProvider.aidl
@@ -16,15 +16,20 @@
package android.service.controls;
-import android.service.controls.actions.ControlAction;
+import android.service.controls.IControlsActionCallback;
+import android.service.controls.IControlsLoadCallback;
+import android.service.controls.IControlsSubscriber;
+import android.service.controls.actions.ControlActionWrapper;
-/** @hide */
+/**
+ * @hide
+ */
oneway interface IControlsProvider {
- void load();
-
- void subscribe(in List<String> controlIds);
+ void load(IControlsLoadCallback cb);
- void unsubscribe();
+ void subscribe(in List<String> controlIds,
+ IControlsSubscriber subscriber);
- void onAction(in String controlId, in ControlAction action);
+ void action(in String controlId, in ControlActionWrapper action,
+ IControlsActionCallback cb);
} \ No newline at end of file
diff --git a/core/java/android/service/controls/IControlsProviderCallback.aidl b/core/java/android/service/controls/IControlsSubscriber.aidl
index 91f6a7980ce6..75ce584c5321 100644
--- a/core/java/android/service/controls/IControlsProviderCallback.aidl
+++ b/core/java/android/service/controls/IControlsSubscriber.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2019, The Android Open Source Project
+ * Copyright (c) 2020, The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,12 +17,14 @@
package android.service.controls;
import android.service.controls.Control;
+import android.service.controls.IControlsSubscription;
-/** @hide */
-oneway interface IControlsProviderCallback {
- void onLoad(in IBinder token, in List<Control> controls);
-
- void onRefreshState(in IBinder token, in List<Control> statefulControls);
-
- void onControlActionResponse(in IBinder token, in String controlId, int response);
+/**
+ * @hide
+ */
+oneway interface IControlsSubscriber {
+ void onSubscribe(in IBinder token, in IControlsSubscription cs);
+ void onNext(in IBinder token, in Control c);
+ void onError(in IBinder token, in String s);
+ void onComplete(in IBinder token);
} \ No newline at end of file
diff --git a/core/java/android/service/controls/templates/ThumbnailTemplate.aidl b/core/java/android/service/controls/IControlsSubscription.aidl
index 81c879b227bb..0af575e07b5e 100644
--- a/core/java/android/service/controls/templates/ThumbnailTemplate.aidl
+++ b/core/java/android/service/controls/IControlsSubscription.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2019, The Android Open Source Project
+ * Copyright (c) 2020, The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,6 +14,12 @@
* limitations under the License.
*/
-package android.service.controls.templates;
+package android.service.controls;
-parcelable ThumbnailTemplate; \ No newline at end of file
+/**
+ * @hide
+ */
+oneway interface IControlsSubscription {
+ void request(long n);
+ void cancel();
+} \ No newline at end of file
diff --git a/core/java/android/service/controls/actions/BooleanAction.java b/core/java/android/service/controls/actions/BooleanAction.java
index fb2c5ad99659..02593353bfc8 100644
--- a/core/java/android/service/controls/actions/BooleanAction.java
+++ b/core/java/android/service/controls/actions/BooleanAction.java
@@ -19,12 +19,10 @@ package android.service.controls.actions;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Bundle;
-import android.os.Parcel;
import android.service.controls.templates.ToggleTemplate;
/**
* Action sent by a {@link ToggleTemplate}
- * @hide
*/
public final class BooleanAction extends ControlAction {
@@ -54,6 +52,10 @@ public final class BooleanAction extends ControlAction {
mNewState = newState;
}
+ /**
+ * @param b
+ * @hide
+ */
BooleanAction(Bundle b) {
super(b);
mNewState = b.getBoolean(KEY_NEW_STATE);
@@ -77,24 +79,15 @@ public final class BooleanAction extends ControlAction {
return TYPE;
}
+ /**
+ * @return
+ * @hide
+ */
@Override
- protected Bundle getDataBundle() {
+ @NonNull
+ Bundle getDataBundle() {
Bundle b = super.getDataBundle();
b.putBoolean(KEY_NEW_STATE, mNewState);
return b;
}
-
- public static final @NonNull Creator<BooleanAction> CREATOR = new Creator<BooleanAction>() {
- @Override
- public BooleanAction createFromParcel(Parcel source) {
- int type = source.readInt();
- verifyType(type, TYPE);
- return new BooleanAction(source.readBundle());
- }
-
- @Override
- public BooleanAction[] newArray(int size) {
- return new BooleanAction[size];
- }
- };
}
diff --git a/core/java/android/service/controls/actions/CommandAction.aidl b/core/java/android/service/controls/actions/CommandAction.aidl
deleted file mode 100644
index 7c1ee41fb8c3..000000000000
--- a/core/java/android/service/controls/actions/CommandAction.aidl
+++ /dev/null
@@ -1,18 +0,0 @@
-/*
- * 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.service.controls.actions;
-
-parcelable CommandAction; \ No newline at end of file
diff --git a/core/java/android/service/controls/actions/CommandAction.java b/core/java/android/service/controls/actions/CommandAction.java
index c69c53976349..84d60805c3e9 100644
--- a/core/java/android/service/controls/actions/CommandAction.java
+++ b/core/java/android/service/controls/actions/CommandAction.java
@@ -19,11 +19,7 @@ package android.service.controls.actions;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Bundle;
-import android.os.Parcel;
-/**
- * @hide
- */
public final class CommandAction extends ControlAction {
private static final @ActionType int TYPE = TYPE_COMMAND;
@@ -36,7 +32,11 @@ public final class CommandAction extends ControlAction {
this(templateId, null);
}
- public CommandAction(Bundle b) {
+ /**
+ * @param b
+ * @hide
+ */
+ CommandAction(Bundle b) {
super(b);
}
@@ -44,18 +44,4 @@ public final class CommandAction extends ControlAction {
public int getActionType() {
return TYPE;
}
-
- public static final Creator<CommandAction> CREATOR = new Creator<CommandAction>() {
- @Override
- public CommandAction createFromParcel(Parcel source) {
- int type = source.readInt();
- verifyType(type, TYPE);
- return new CommandAction(source.readBundle());
- }
-
- @Override
- public CommandAction[] newArray(int size) {
- return new CommandAction[size];
- }
- };
}
diff --git a/core/java/android/service/controls/actions/ControlAction.java b/core/java/android/service/controls/actions/ControlAction.java
index 83d1cf824f1f..4141da805b5a 100644
--- a/core/java/android/service/controls/actions/ControlAction.java
+++ b/core/java/android/service/controls/actions/ControlAction.java
@@ -21,10 +21,9 @@ import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Bundle;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.service.controls.IControlsProviderCallback;
+import android.service.controls.IControlsActionCallback;
import android.service.controls.templates.ControlTemplate;
+import android.util.Log;
import com.android.internal.util.Preconditions;
@@ -36,20 +35,21 @@ import java.lang.annotation.RetentionPolicy;
*
* The action may have a value to authenticate the input, when the provider has requested it to
* complete the action.
- * @hide
*/
-public abstract class ControlAction implements Parcelable {
+public abstract class ControlAction {
+ private static final String TAG = "ControlAction";
+
+ private static final String KEY_ACTION_TYPE = "key_action_type";
private static final String KEY_TEMPLATE_ID = "key_template_id";
private static final String KEY_CHALLENGE_VALUE = "key_challenge_value";
-
/**
* @hide
*/
@Retention(RetentionPolicy.SOURCE)
@IntDef({
- TYPE_UNKNOWN,
+ TYPE_ERROR,
TYPE_BOOLEAN,
TYPE_FLOAT,
TYPE_MULTI_FLOAT,
@@ -57,15 +57,16 @@ public abstract class ControlAction implements Parcelable {
TYPE_COMMAND
})
public @interface ActionType {};
- public static final ControlAction UNKNOWN_ACTION = new ControlAction() {
+ public static final @NonNull ControlAction ERROR_ACTION = new ControlAction() {
@Override
public int getActionType() {
- return TYPE_UNKNOWN;
+ return TYPE_ERROR;
}
};
- public static final @ActionType int TYPE_UNKNOWN = 0;
+ public static final @ActionType int TYPE_ERROR = -1;
+
/**
* The identifier of {@link BooleanAction}.
*/
@@ -104,27 +105,27 @@ public abstract class ControlAction implements Parcelable {
public static final @ResponseResult int RESPONSE_UNKNOWN = 0;
/**
- * Response code for {@link IControlsProviderCallback#onControlActionResponse} indicating that
+ * Response code for {@link IControlsActionCallback#accept} indicating that
* the action has been performed. The action may still fail later and the state may not change.
*/
public static final @ResponseResult int RESPONSE_OK = 1;
/**
- * Response code for {@link IControlsProviderCallback#onControlActionResponse} indicating that
+ * Response code for {@link IControlsActionCallback#accept} indicating that
* the action has failed.
*/
public static final @ResponseResult int RESPONSE_FAIL = 2;
/**
- * Response code for {@link IControlsProviderCallback#onControlActionResponse} indicating that
+ * Response code for {@link IControlsActionCallback#accept} indicating that
* in order for the action to be performed, acknowledgment from the user is required.
*/
public static final @ResponseResult int RESPONSE_CHALLENGE_ACK = 3;
/**
- * Response code for {@link IControlsProviderCallback#onControlActionResponse} indicating that
+ * Response code for {@link IControlsActionCallback#accept} indicating that
* in order for the action to be performed, a PIN is required.
*/
public static final @ResponseResult int RESPONSE_CHALLENGE_PIN = 4;
/**
- * Response code for {@link IControlsProviderCallback#onControlActionResponse} indicating that
+ * Response code for {@link IControlsActionCallback#accept} indicating that
* in order for the action to be performed, an alphanumeric passphrase is required.
*/
public static final @ResponseResult int RESPONSE_CHALLENGE_PASSPHRASE = 5;
@@ -175,68 +176,55 @@ public abstract class ControlAction implements Parcelable {
return mChallengeValue;
}
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public final void writeToParcel(Parcel dest, int flags) {
- dest.writeInt(getActionType());
- dest.writeBundle(getDataBundle());
- }
-
/**
* Obtain a {@link Bundle} describing this object populated with data.
*
* Implementations in subclasses should populate the {@link Bundle} returned by
* {@link ControlAction}.
* @return a {@link Bundle} containing the data that represents this object.
+ * @hide
*/
@CallSuper
- protected Bundle getDataBundle() {
+ @NonNull
+ Bundle getDataBundle() {
Bundle b = new Bundle();
+ b.putInt(KEY_ACTION_TYPE, getActionType());
b.putString(KEY_TEMPLATE_ID, mTemplateId);
b.putString(KEY_CHALLENGE_VALUE, mChallengeValue);
return b;
}
- public static final @NonNull Creator<ControlAction> CREATOR = new Creator<ControlAction>() {
- @Override
- public ControlAction createFromParcel(Parcel source) {
- int type = source.readInt();
- return createActionFromType(type, source);
- }
-
- @Override
- public ControlAction[] newArray(int size) {
- return new ControlAction[size];
- }
- };
-
-
- private static ControlAction createActionFromType(@ActionType int type, Parcel source) {
- switch(type) {
- case TYPE_BOOLEAN:
- return new BooleanAction(source.readBundle());
- case TYPE_FLOAT:
- return new FloatAction(source.readBundle());
- case TYPE_MULTI_FLOAT:
- return new MultiFloatAction(source.readBundle());
- case TYPE_MODE:
- return new ModeAction(source.readBundle());
- case TYPE_COMMAND:
- return new CommandAction(source.readBundle());
- default:
- source.readBundle();
- return UNKNOWN_ACTION;
+ /**
+ * @param bundle
+ * @return
+ * @hide
+ */
+ @NonNull
+ static ControlAction createActionFromBundle(@NonNull Bundle bundle) {
+ if (bundle == null) {
+ Log.e(TAG, "Null bundle");
+ return ERROR_ACTION;
}
- }
-
- protected static void verifyType(@ActionType int type, @ActionType int thisType) {
- if (type != thisType) {
- throw new IllegalStateException("The type " + type + "does not match " + thisType);
+ int type = bundle.getInt(KEY_ACTION_TYPE, TYPE_ERROR);
+ try {
+ switch (type) {
+ case TYPE_BOOLEAN:
+ return new BooleanAction(bundle);
+ case TYPE_FLOAT:
+ return new FloatAction(bundle);
+ case TYPE_MULTI_FLOAT:
+ return new MultiFloatAction(bundle);
+ case TYPE_MODE:
+ return new ModeAction(bundle);
+ case TYPE_COMMAND:
+ return new CommandAction(bundle);
+ case TYPE_ERROR:
+ default:
+ return ERROR_ACTION;
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Error creating action", e);
+ return ERROR_ACTION;
}
}
-
}
diff --git a/core/java/android/service/controls/actions/ModeAction.aidl b/core/java/android/service/controls/actions/ControlActionWrapper.aidl
index 3ef89e0bfce9..5ba962d822ba 100644
--- a/core/java/android/service/controls/actions/ModeAction.aidl
+++ b/core/java/android/service/controls/actions/ControlActionWrapper.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,4 +16,4 @@
package android.service.controls.actions;
-parcelable ModeAction; \ No newline at end of file
+parcelable ControlActionWrapper; \ No newline at end of file
diff --git a/core/java/android/service/controls/actions/ControlActionWrapper.java b/core/java/android/service/controls/actions/ControlActionWrapper.java
new file mode 100644
index 000000000000..6a3ec86835ed
--- /dev/null
+++ b/core/java/android/service/controls/actions/ControlActionWrapper.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.controls.actions;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * Wrapper for parceling/unparceling {@link ControlAction}.
+ * @hide
+ */
+public final class ControlActionWrapper implements Parcelable {
+
+ private final @NonNull ControlAction mControlAction;
+
+ public ControlActionWrapper(@NonNull ControlAction controlAction) {
+ Preconditions.checkNotNull(controlAction);
+
+ mControlAction = controlAction;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeBundle(mControlAction.getDataBundle());
+ }
+
+ @NonNull
+ public ControlAction getWrappedAction() {
+ return mControlAction;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final @NonNull Creator<ControlActionWrapper> CREATOR =
+ new Creator<ControlActionWrapper>() {
+ @Override
+ public ControlActionWrapper createFromParcel(@NonNull Parcel in) {
+ return new ControlActionWrapper(
+ ControlAction.createActionFromBundle(in.readBundle()));
+ }
+
+ @Override
+ public ControlActionWrapper[] newArray(int size) {
+ return new ControlActionWrapper[size];
+ }
+ };
+}
diff --git a/core/java/android/service/controls/actions/FloatAction.java b/core/java/android/service/controls/actions/FloatAction.java
index 1c3fb4d39744..5b271ce5c577 100644
--- a/core/java/android/service/controls/actions/FloatAction.java
+++ b/core/java/android/service/controls/actions/FloatAction.java
@@ -19,13 +19,11 @@ package android.service.controls.actions;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Bundle;
-import android.os.Parcel;
import android.service.controls.templates.RangeTemplate;
import android.service.controls.templates.ToggleRangeTemplate;
/**
* Action sent by a {@link RangeTemplate}, {@link ToggleRangeTemplate}.
- * @hide
*/
public final class FloatAction extends ControlAction {
@@ -56,7 +54,11 @@ public final class FloatAction extends ControlAction {
mNewValue = newValue;
}
- public FloatAction(Bundle b) {
+ /**
+ * @param b
+ * @hide
+ */
+ FloatAction(Bundle b) {
super(b);
mNewValue = b.getFloat(KEY_NEW_VALUE);
}
@@ -76,24 +78,15 @@ public final class FloatAction extends ControlAction {
return TYPE;
}
+ /**
+ * @return
+ * @hide
+ */
@Override
- protected Bundle getDataBundle() {
+ @NonNull
+ Bundle getDataBundle() {
Bundle b = super.getDataBundle();
b.putFloat(KEY_NEW_VALUE, mNewValue);
return b;
}
-
- public static final @NonNull Creator<FloatAction> CREATOR = new Creator<FloatAction>() {
- @Override
- public FloatAction createFromParcel(Parcel source) {
- int type = source.readInt();
- verifyType(type, TYPE);
- return new FloatAction(source.readBundle());
- }
-
- @Override
- public FloatAction[] newArray(int size) {
- return new FloatAction[size];
- }
- };
}
diff --git a/core/java/android/service/controls/actions/ModeAction.java b/core/java/android/service/controls/actions/ModeAction.java
index 0bd1d24e873f..ca40974d929b 100644
--- a/core/java/android/service/controls/actions/ModeAction.java
+++ b/core/java/android/service/controls/actions/ModeAction.java
@@ -19,11 +19,7 @@ package android.service.controls.actions;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Bundle;
-import android.os.Parcel;
-/**
- * @hide
- */
public final class ModeAction extends ControlAction {
private static final @ActionType int TYPE = TYPE_MODE;
@@ -45,13 +41,22 @@ public final class ModeAction extends ControlAction {
this(templateId, newMode, null);
}
+ /**
+ * @param b
+ * @hide
+ */
ModeAction(Bundle b) {
super(b);
mNewMode = b.getInt(KEY_MODE);
}
+ /**
+ * @return
+ * @hide
+ */
@Override
- protected Bundle getDataBundle() {
+ @NonNull
+ Bundle getDataBundle() {
Bundle b = super.getDataBundle();
b.putInt(KEY_MODE, mNewMode);
return b;
@@ -60,18 +65,4 @@ public final class ModeAction extends ControlAction {
public int getNewMode() {
return mNewMode;
}
-
- public static final Creator<ModeAction> CREATOR = new Creator<ModeAction>() {
- @Override
- public ModeAction createFromParcel(Parcel source) {
- int type = source.readInt();
- verifyType(type, TYPE);
- return new ModeAction(source.readBundle());
- }
-
- @Override
- public ModeAction[] newArray(int size) {
- return new ModeAction[size];
- }
- };
}
diff --git a/core/java/android/service/controls/actions/MultiFloatAction.aidl b/core/java/android/service/controls/actions/MultiFloatAction.aidl
deleted file mode 100644
index bcba75810b0c..000000000000
--- a/core/java/android/service/controls/actions/MultiFloatAction.aidl
+++ /dev/null
@@ -1,18 +0,0 @@
-/*
- * 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.service.controls.actions;
-
-parcelable MultiFloatAction; \ No newline at end of file
diff --git a/core/java/android/service/controls/actions/MultiFloatAction.java b/core/java/android/service/controls/actions/MultiFloatAction.java
index aef8a785a665..e5740795ab90 100644
--- a/core/java/android/service/controls/actions/MultiFloatAction.java
+++ b/core/java/android/service/controls/actions/MultiFloatAction.java
@@ -19,14 +19,10 @@ package android.service.controls.actions;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Bundle;
-import android.os.Parcel;
import android.util.Log;
import com.android.internal.util.Preconditions;
-/**
- * @hide
- */
public final class MultiFloatAction extends ControlAction {
private static final String TAG = "MultiFloatAction";
@@ -58,6 +54,10 @@ public final class MultiFloatAction extends ControlAction {
this(templateId, newValues, null);
}
+ /**
+ * @param b
+ * @hide
+ */
MultiFloatAction(Bundle b) {
super(b);
mNewValues = b.getFloatArray(KEY_VALUES);
@@ -68,24 +68,15 @@ public final class MultiFloatAction extends ControlAction {
return mNewValues.clone();
}
+ /**
+ * @return
+ * @hide
+ */
@Override
- protected Bundle getDataBundle() {
+ @NonNull
+ Bundle getDataBundle() {
Bundle b = super.getDataBundle();
b.putFloatArray(KEY_VALUES, mNewValues);
return b;
}
-
- public static final Creator<MultiFloatAction> CREATOR = new Creator<MultiFloatAction>() {
- @Override
- public MultiFloatAction createFromParcel(Parcel source) {
- int type = source.readInt();
- verifyType(type, TYPE);
- return new MultiFloatAction(source.readBundle());
- }
-
- @Override
- public MultiFloatAction[] newArray(int size) {
- return new MultiFloatAction[size];
- }
- };
}
diff --git a/core/java/android/service/controls/templates/ControlButton.java b/core/java/android/service/controls/templates/ControlButton.java
index e03ac6f8bf51..157e231b1da6 100644
--- a/core/java/android/service/controls/templates/ControlButton.java
+++ b/core/java/android/service/controls/templates/ControlButton.java
@@ -24,7 +24,6 @@ import com.android.internal.util.Preconditions;
/**
* Button element for {@link ControlTemplate}.
- * @hide
*/
public final class ControlButton implements Parcelable {
@@ -64,7 +63,8 @@ public final class ControlButton implements Parcelable {
}
@Override
- public void writeToParcel(Parcel dest, int flags) {
+ @NonNull
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeByte(mChecked ? (byte) 1 : (byte) 0);
dest.writeCharSequence(mActionDescription);
}
@@ -74,7 +74,7 @@ public final class ControlButton implements Parcelable {
mActionDescription = in.readCharSequence();
}
- public static final Creator<ControlButton> CREATOR = new Creator<ControlButton>() {
+ public static final @NonNull Creator<ControlButton> CREATOR = new Creator<ControlButton>() {
@Override
public ControlButton createFromParcel(Parcel source) {
return new ControlButton(source);
diff --git a/core/java/android/service/controls/templates/ControlTemplate.java b/core/java/android/service/controls/templates/ControlTemplate.java
index bf194f8efcda..d2c0f76907e6 100644
--- a/core/java/android/service/controls/templates/ControlTemplate.java
+++ b/core/java/android/service/controls/templates/ControlTemplate.java
@@ -19,11 +19,11 @@ package android.service.controls.templates;
import android.annotation.CallSuper;
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.os.Bundle;
-import android.os.Parcel;
-import android.os.Parcelable;
import android.service.controls.Control;
import android.service.controls.actions.ControlAction;
+import android.util.Log;
import com.android.internal.util.Preconditions;
@@ -39,27 +39,37 @@ import java.lang.annotation.RetentionPolicy;
* associated state. The actions available to a given {@link Control} are determined by its
* {@link ControlTemplate}.
* @see ControlAction
- * @hide
*/
-public abstract class ControlTemplate implements Parcelable {
+public abstract class ControlTemplate {
+
+ private static final String TAG = "ControlTemplate";
private static final String KEY_TEMPLATE_ID = "key_template_id";
+ private static final String KEY_TEMPLATE_TYPE = "key_template_type";
/**
* Singleton representing a {@link Control} with no input.
*/
- public static final ControlTemplate NO_TEMPLATE = new ControlTemplate("") {
+ public static final @NonNull ControlTemplate NO_TEMPLATE = new ControlTemplate("") {
@Override
public int getTemplateType() {
return TYPE_NONE;
}
};
+ public static final @NonNull ControlTemplate ERROR_TEMPLATE = new ControlTemplate("") {
+ @Override
+ public int getTemplateType() {
+ return TYPE_ERROR;
+ }
+ };
+
/**
* @hide
*/
@Retention(RetentionPolicy.SOURCE)
@IntDef({
+ TYPE_ERROR,
TYPE_NONE,
TYPE_TOGGLE,
TYPE_RANGE,
@@ -72,47 +82,50 @@ public abstract class ControlTemplate implements Parcelable {
})
public @interface TemplateType {}
+ public static final @TemplateType int TYPE_ERROR = -1;
+
/**
* Type identifier of {@link ControlTemplate#NO_TEMPLATE}.
*/
- public static final int TYPE_NONE = 0;
+ public static final @TemplateType int TYPE_NONE = 0;
/**
* Type identifier of {@link ToggleTemplate}.
*/
- public static final int TYPE_TOGGLE = 1;
+ public static final @TemplateType int TYPE_TOGGLE = 1;
/**
* Type identifier of {@link RangeTemplate}.
*/
- public static final int TYPE_RANGE = 2;
+ public static final @TemplateType int TYPE_RANGE = 2;
/**
* Type identifier of {@link ThumbnailTemplate}.
*/
- public static final int TYPE_THUMBNAIL = 3;
+ public static final @TemplateType int TYPE_THUMBNAIL = 3;
/**
* Type identifier of {@link DiscreteToggleTemplate}.
*/
- public static final int TYPE_DISCRETE_TOGGLE = 4;
+ public static final @TemplateType int TYPE_DISCRETE_TOGGLE = 4;
/**
* @hide
*/
- public static final int TYPE_COORD_RANGE = 5;
+ public static final @TemplateType int TYPE_COORD_RANGE = 5;
- public static final int TYPE_TOGGLE_RANGE = 6;
+ public static final @TemplateType int TYPE_TOGGLE_RANGE = 6;
- public static final int TYPE_TEMPERATURE = 7;
+ public static final @TemplateType int TYPE_TEMPERATURE = 7;
- public static final int TYPE_STATELESS = 8;
+ public static final @TemplateType int TYPE_STATELESS = 8;
private @NonNull final String mTemplateId;
/**
* @return the identifier for this object.
*/
+ @NonNull
public String getTemplateId() {
return mTemplateId;
}
@@ -122,24 +135,16 @@ public abstract class ControlTemplate implements Parcelable {
*/
public abstract @TemplateType int getTemplateType();
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public final void writeToParcel(Parcel dest, int flags) {
- dest.writeInt(getTemplateType());
- dest.writeBundle(getDataBundle());
- }
-
/**
* Obtain a {@link Bundle} describing this object populated with data.
* @return a {@link Bundle} containing the data that represents this object.
+ * @hide
*/
@CallSuper
- protected Bundle getDataBundle() {
+ @NonNull
+ Bundle getDataBundle() {
Bundle b = new Bundle();
+ b.putInt(KEY_TEMPLATE_TYPE, getTemplateType());
b.putString(KEY_TEMPLATE_ID, mTemplateId);
return b;
}
@@ -148,6 +153,10 @@ public abstract class ControlTemplate implements Parcelable {
mTemplateId = "";
}
+ /**
+ * @param b
+ * @hide
+ */
ControlTemplate(@NonNull Bundle b) {
mTemplateId = b.getString(KEY_TEMPLATE_ID);
}
@@ -160,48 +169,46 @@ public abstract class ControlTemplate implements Parcelable {
mTemplateId = templateId;
}
- public static final Creator<ControlTemplate> CREATOR = new Creator<ControlTemplate>() {
- @Override
- public ControlTemplate createFromParcel(Parcel source) {
- int type = source.readInt();
- return createTemplateFromType(type, source);
- }
-
- @Override
- public ControlTemplate[] newArray(int size) {
- return new ControlTemplate[size];
- }
- };
-
-
- private static ControlTemplate createTemplateFromType(@TemplateType int type, Parcel source) {
- switch(type) {
- case TYPE_TOGGLE:
- return new ToggleTemplate(source.readBundle());
- case TYPE_RANGE:
- return new RangeTemplate(source.readBundle());
- case TYPE_THUMBNAIL:
- return new ThumbnailTemplate(source.readBundle());
- case TYPE_DISCRETE_TOGGLE:
- return new DiscreteToggleTemplate(source.readBundle());
- case TYPE_COORD_RANGE:
- return new CoordinatedRangeTemplate(source.readBundle());
- case TYPE_TOGGLE_RANGE:
- return new ToggleRangeTemplate(source.readBundle());
- case TYPE_TEMPERATURE:
- return new TemperatureControlTemplate(source.readBundle());
- case TYPE_STATELESS:
- return new StatelessTemplate(source.readBundle());
- case TYPE_NONE:
- default:
- source.readBundle();
- return NO_TEMPLATE;
+ /**
+ *
+ * @param bundle
+ * @return
+ * @hide
+ */
+ @NonNull
+ static ControlTemplate createTemplateFromBundle(@Nullable Bundle bundle) {
+ if (bundle == null) {
+ Log.e(TAG, "Null bundle");
+ return ERROR_TEMPLATE;
}
- }
-
- protected static void verifyType(@TemplateType int type, @TemplateType int thisType) {
- if (type != thisType) {
- throw new IllegalStateException("The type " + type + "does not match " + thisType);
+ int type = bundle.getInt(KEY_TEMPLATE_TYPE, TYPE_ERROR);
+ try {
+ switch (type) {
+ case TYPE_TOGGLE:
+ return new ToggleTemplate(bundle);
+ case TYPE_RANGE:
+ return new RangeTemplate(bundle);
+ case TYPE_THUMBNAIL:
+ return new ThumbnailTemplate(bundle);
+ case TYPE_DISCRETE_TOGGLE:
+ return new DiscreteToggleTemplate(bundle);
+ case TYPE_COORD_RANGE:
+ return new CoordinatedRangeTemplate(bundle);
+ case TYPE_TOGGLE_RANGE:
+ return new ToggleRangeTemplate(bundle);
+ case TYPE_TEMPERATURE:
+ return new TemperatureControlTemplate(bundle);
+ case TYPE_STATELESS:
+ return new StatelessTemplate(bundle);
+ case TYPE_NONE:
+ return NO_TEMPLATE;
+ case TYPE_ERROR:
+ default:
+ return ERROR_TEMPLATE;
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Error creating template", e);
+ return ERROR_TEMPLATE;
}
}
}
diff --git a/core/java/android/service/controls/templates/StatelessTemplate.aidl b/core/java/android/service/controls/templates/ControlTemplateWrapper.aidl
index 02e18d9ac83d..208ca4e1c5a1 100644
--- a/core/java/android/service/controls/templates/StatelessTemplate.aidl
+++ b/core/java/android/service/controls/templates/ControlTemplateWrapper.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,4 +16,4 @@
package android.service.controls.templates;
-parcelable StatelessTemplate; \ No newline at end of file
+parcelable ControlTemplateWrapper; \ No newline at end of file
diff --git a/core/java/android/service/controls/templates/ControlTemplateWrapper.java b/core/java/android/service/controls/templates/ControlTemplateWrapper.java
new file mode 100644
index 000000000000..7957260475ee
--- /dev/null
+++ b/core/java/android/service/controls/templates/ControlTemplateWrapper.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.controls.templates;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * Wrapper for parceling/unparceling {@link ControlTemplate}.
+ * @hide
+ */
+public final class ControlTemplateWrapper implements Parcelable {
+
+ private final @NonNull ControlTemplate mControlTemplate;
+
+ public ControlTemplateWrapper(@NonNull ControlTemplate template) {
+ Preconditions.checkNotNull(template);
+ mControlTemplate = template;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @NonNull
+ public ControlTemplate getWrappedTemplate() {
+ return mControlTemplate;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeBundle(mControlTemplate.getDataBundle());
+ }
+
+ public static final @NonNull Creator<ControlTemplateWrapper> CREATOR =
+ new Creator<ControlTemplateWrapper>() {
+ @Override
+ public ControlTemplateWrapper createFromParcel(@NonNull Parcel source) {
+ return new ControlTemplateWrapper(
+ ControlTemplate.createTemplateFromBundle(source.readBundle()));
+ }
+
+ @Override
+ public ControlTemplateWrapper[] newArray(int size) {
+ return new ControlTemplateWrapper[size];
+ }
+ };
+}
diff --git a/core/java/android/service/controls/templates/CoordinatedRangeTemplate.aidl b/core/java/android/service/controls/templates/CoordinatedRangeTemplate.aidl
deleted file mode 100644
index 972142c5aa03..000000000000
--- a/core/java/android/service/controls/templates/CoordinatedRangeTemplate.aidl
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * 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.service.controls.templates;
-
-parcelable CoordinatedRangeTemplate; \ No newline at end of file
diff --git a/core/java/android/service/controls/templates/CoordinatedRangeTemplate.java b/core/java/android/service/controls/templates/CoordinatedRangeTemplate.java
index 3d820c49eeab..6aa5480f0611 100644
--- a/core/java/android/service/controls/templates/CoordinatedRangeTemplate.java
+++ b/core/java/android/service/controls/templates/CoordinatedRangeTemplate.java
@@ -19,12 +19,8 @@ package android.service.controls.templates;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Bundle;
-import android.os.Parcel;
import android.util.Log;
-/**
- * @hide
- */
public final class CoordinatedRangeTemplate extends ControlTemplate {
private static final String TAG = "CoordinatedRangeTemplate";
@@ -74,10 +70,14 @@ public final class CoordinatedRangeTemplate extends ControlTemplate {
minValueHigh, maxValueHigh, currentValueHigh, stepValue, formatString));
}
+ /**
+ * @param b
+ * @hide
+ */
CoordinatedRangeTemplate(Bundle b) {
super(b);
- mRangeLow = b.getParcelable(KEY_RANGE_LOW);
- mRangeHigh = b.getParcelable(KEY_RANGE_HIGH);
+ mRangeLow = new RangeTemplate(b.getBundle(KEY_RANGE_LOW));
+ mRangeHigh = new RangeTemplate(b.getBundle(KEY_RANGE_HIGH));
mMinGap = b.getFloat(KEY_MIN_GAP);
validateRanges();
}
@@ -134,11 +134,16 @@ public final class CoordinatedRangeTemplate extends ControlTemplate {
return TYPE;
}
+ /**
+ * @return
+ * @hide
+ */
@Override
- protected Bundle getDataBundle() {
+ @NonNull
+ Bundle getDataBundle() {
Bundle b = super.getDataBundle();
- b.putParcelable(KEY_RANGE_LOW, mRangeLow);
- b.putParcelable(KEY_RANGE_HIGH, mRangeHigh);
+ b.putBundle(KEY_RANGE_LOW, mRangeLow.getDataBundle());
+ b.putBundle(KEY_RANGE_HIGH, mRangeHigh.getDataBundle());
return b;
}
@@ -160,18 +165,4 @@ public final class CoordinatedRangeTemplate extends ControlTemplate {
}
}
- public static final Creator<CoordinatedRangeTemplate> CREATOR =
- new Creator<CoordinatedRangeTemplate>() {
- @Override
- public CoordinatedRangeTemplate createFromParcel(Parcel source) {
- int type = source.readInt();
- verifyType(type, TYPE);
- return new CoordinatedRangeTemplate(source.readBundle());
- }
-
- @Override
- public CoordinatedRangeTemplate[] newArray(int size) {
- return new CoordinatedRangeTemplate[size];
- }
- };
}
diff --git a/core/java/android/service/controls/templates/DiscreteToggleTemplate.aidl b/core/java/android/service/controls/templates/DiscreteToggleTemplate.aidl
deleted file mode 100644
index d22e37501605..000000000000
--- a/core/java/android/service/controls/templates/DiscreteToggleTemplate.aidl
+++ /dev/null
@@ -1,18 +0,0 @@
-/*
- * 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.service.controls.templates;
-
-parcelable DiscreteToggleTemplate; \ No newline at end of file
diff --git a/core/java/android/service/controls/templates/DiscreteToggleTemplate.java b/core/java/android/service/controls/templates/DiscreteToggleTemplate.java
index a8c193c5b1dc..7a1331a35ed4 100644
--- a/core/java/android/service/controls/templates/DiscreteToggleTemplate.java
+++ b/core/java/android/service/controls/templates/DiscreteToggleTemplate.java
@@ -18,7 +18,6 @@ package android.service.controls.templates;
import android.annotation.NonNull;
import android.os.Bundle;
-import android.os.Parcel;
import android.service.controls.Control;
import android.service.controls.actions.BooleanAction;
@@ -33,9 +32,8 @@ import com.android.internal.util.Preconditions;
* {@link BooleanAction#getNewState} will be {@code false} if the button was
* {@link DiscreteToggleTemplate#getNegativeButton} and {@code true} if the button was
* {@link DiscreteToggleTemplate#getPositiveButton}.
- * @hide
*/
-public class DiscreteToggleTemplate extends ControlTemplate {
+public final class DiscreteToggleTemplate extends ControlTemplate {
private static final @TemplateType int TYPE = TYPE_DISCRETE_TOGGLE;
private static final String KEY_NEGATIVE_BUTTON = "key_negative_button";
@@ -46,8 +44,8 @@ public class DiscreteToggleTemplate extends ControlTemplate {
/**
* @param templateId the identifier for this template object
- * @param negativeButton a {@ControlButton} for the <i>Negative</i> input
- * @param positiveButton a {@ControlButton} for the <i>Positive</i> input
+ * @param negativeButton a {@link ControlButton} for the <i>Negative</i> input
+ * @param positiveButton a {@link ControlButton} for the <i>Positive</i> input
*/
public DiscreteToggleTemplate(@NonNull String templateId,
@NonNull ControlButton negativeButton,
@@ -59,6 +57,10 @@ public class DiscreteToggleTemplate extends ControlTemplate {
mPositiveButton = positiveButton;
}
+ /**
+ * @param b
+ * @hide
+ */
DiscreteToggleTemplate(Bundle b) {
super(b);
mNegativeButton = b.getParcelable(KEY_NEGATIVE_BUTTON);
@@ -89,32 +91,17 @@ public class DiscreteToggleTemplate extends ControlTemplate {
return TYPE;
}
-
- @Override
- public int describeContents() {
- return 0;
- }
-
+ /**
+ * @return
+ * @hide
+ */
@Override
- protected Bundle getDataBundle() {
+ @NonNull
+ Bundle getDataBundle() {
Bundle b = super.getDataBundle();
b.putParcelable(KEY_NEGATIVE_BUTTON, mNegativeButton);
b.putParcelable(KEY_POSITIVE_BUTTON, mPositiveButton);
return b;
}
- public static final Creator<DiscreteToggleTemplate> CREATOR =
- new Creator<DiscreteToggleTemplate>() {
- @Override
- public DiscreteToggleTemplate createFromParcel(Parcel source) {
- int type = source.readInt();
- verifyType(type, TYPE);
- return new DiscreteToggleTemplate(source.readBundle());
- }
-
- @Override
- public DiscreteToggleTemplate[] newArray(int size) {
- return new DiscreteToggleTemplate[size];
- }
- };
}
diff --git a/core/java/android/service/controls/templates/RangeTemplate.java b/core/java/android/service/controls/templates/RangeTemplate.java
index bb79d83b1825..fe0d16707c9d 100644
--- a/core/java/android/service/controls/templates/RangeTemplate.java
+++ b/core/java/android/service/controls/templates/RangeTemplate.java
@@ -27,7 +27,6 @@ import android.service.controls.actions.FloatAction;
* A template for a {@link Control} with inputs in a "continuous" range of values.
*
* @see FloatAction
- * @hide
*/
public final class RangeTemplate extends ControlTemplate {
@@ -148,8 +147,13 @@ public final class RangeTemplate extends ControlTemplate {
return TYPE;
}
+ /**
+ * @return
+ * @hide
+ */
@Override
- protected Bundle getDataBundle() {
+ @NonNull
+ Bundle getDataBundle() {
Bundle b = super.getDataBundle();
b.putFloat(KEY_MIN_VALUE, mMinValue);
b.putFloat(KEY_MAX_VALUE, mMaxValue);
@@ -181,18 +185,4 @@ public final class RangeTemplate extends ControlTemplate {
throw new IllegalArgumentException(String.format("stepValue=%f <= 0", mStepValue));
}
}
-
- public static final Creator<RangeTemplate> CREATOR = new Creator<RangeTemplate>() {
- @Override
- public RangeTemplate createFromParcel(Parcel source) {
- int type = source.readInt();
- verifyType(type, TYPE);
- return new RangeTemplate(source.readBundle());
- }
-
- @Override
- public RangeTemplate[] newArray(int size) {
- return new RangeTemplate[size];
- }
- };
}
diff --git a/core/java/android/service/controls/templates/StatelessTemplate.java b/core/java/android/service/controls/templates/StatelessTemplate.java
index 12ab9bc567ac..3f98beac9cf8 100644
--- a/core/java/android/service/controls/templates/StatelessTemplate.java
+++ b/core/java/android/service/controls/templates/StatelessTemplate.java
@@ -18,11 +18,7 @@ package android.service.controls.templates;
import android.annotation.NonNull;
import android.os.Bundle;
-import android.os.Parcel;
-/**
- * @hide
- */
public final class StatelessTemplate extends ControlTemplate {
@Override
@@ -30,23 +26,15 @@ public final class StatelessTemplate extends ControlTemplate {
return TYPE_STATELESS;
}
- public StatelessTemplate(@NonNull Bundle b) {
+ /**
+ * @param b
+ * @hide
+ */
+ StatelessTemplate(@NonNull Bundle b) {
super(b);
}
public StatelessTemplate(@NonNull String templateId) {
super(templateId);
}
-
- public static final Creator<StatelessTemplate> CREATOR = new Creator<StatelessTemplate>() {
- @Override
- public StatelessTemplate createFromParcel(Parcel source) {
- return new StatelessTemplate(source.readBundle());
- }
-
- @Override
- public StatelessTemplate[] newArray(int size) {
- return new StatelessTemplate[size];
- }
- };
}
diff --git a/core/java/android/service/controls/templates/TemperatureControlTemplate.aidl b/core/java/android/service/controls/templates/TemperatureControlTemplate.aidl
deleted file mode 100644
index 7994d2651610..000000000000
--- a/core/java/android/service/controls/templates/TemperatureControlTemplate.aidl
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * 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.service.controls.templates;
-
-parcelable TemperatureControlTemplate; \ No newline at end of file
diff --git a/core/java/android/service/controls/templates/TemperatureControlTemplate.java b/core/java/android/service/controls/templates/TemperatureControlTemplate.java
index 987621e011c2..9d8dca6278c5 100644
--- a/core/java/android/service/controls/templates/TemperatureControlTemplate.java
+++ b/core/java/android/service/controls/templates/TemperatureControlTemplate.java
@@ -19,7 +19,6 @@ package android.service.controls.templates;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.os.Bundle;
-import android.os.Parcel;
import android.util.Log;
import com.android.internal.util.Preconditions;
@@ -27,9 +26,6 @@ import com.android.internal.util.Preconditions;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-/**
- * @hide
- */
public final class TemperatureControlTemplate extends ControlTemplate {
private static final String TAG = "ThermostatTemplate";
@@ -67,6 +63,9 @@ public final class TemperatureControlTemplate extends ControlTemplate {
public static final @Mode int MODE_ECO = 5;
+ /**
+ * @hide
+ */
@Retention(RetentionPolicy.SOURCE)
@IntDef(flag = true, value = {
FLAG_MODE_OFF,
@@ -136,18 +135,27 @@ public final class TemperatureControlTemplate extends ControlTemplate {
}
}
+ /**
+ * @param b
+ * @hide
+ */
TemperatureControlTemplate(@NonNull Bundle b) {
super(b);
- mTemplate = b.getParcelable(KEY_TEMPLATE);
+ mTemplate = ControlTemplate.createTemplateFromBundle(b.getBundle(KEY_TEMPLATE));
mCurrentMode = b.getInt(KEY_CURRENT_MODE);
mCurrentActiveMode = b.getInt(KEY_CURRENT_ACTIVE_MODE);
mModes = b.getInt(KEY_MODES);
}
+ /**
+ * @return
+ * @hide
+ */
@Override
- protected Bundle getDataBundle() {
+ @NonNull
+ Bundle getDataBundle() {
Bundle b = super.getDataBundle();
- b.putParcelable(KEY_TEMPLATE, mTemplate);
+ b.putBundle(KEY_TEMPLATE, mTemplate.getDataBundle());
b.putInt(KEY_CURRENT_MODE, mCurrentMode);
b.putInt(KEY_CURRENT_ACTIVE_MODE, mCurrentActiveMode);
b.putInt(KEY_MODES, mModes);
@@ -175,18 +183,4 @@ public final class TemperatureControlTemplate extends ControlTemplate {
public int getTemplateType() {
return TYPE;
}
-
- public static final Creator<TemperatureControlTemplate> CREATOR = new Creator<TemperatureControlTemplate>() {
- @Override
- public TemperatureControlTemplate createFromParcel(Parcel source) {
- int type = source.readInt();
- verifyType(type, TYPE);
- return new TemperatureControlTemplate(source.readBundle());
- }
-
- @Override
- public TemperatureControlTemplate[] newArray(int size) {
- return new TemperatureControlTemplate[size];
- }
- };
}
diff --git a/core/java/android/service/controls/templates/ThumbnailTemplate.java b/core/java/android/service/controls/templates/ThumbnailTemplate.java
index 111d60dc80c9..72179f4edc5e 100644
--- a/core/java/android/service/controls/templates/ThumbnailTemplate.java
+++ b/core/java/android/service/controls/templates/ThumbnailTemplate.java
@@ -19,15 +19,12 @@ package android.service.controls.templates;
import android.annotation.NonNull;
import android.graphics.drawable.Icon;
import android.os.Bundle;
-import android.os.Parcel;
import android.service.controls.Control;
import com.android.internal.util.Preconditions;
/**
* A template for a {@link Control} that displays an image.
- *
- * @hide
*/
public final class ThumbnailTemplate extends ControlTemplate {
@@ -52,6 +49,10 @@ public final class ThumbnailTemplate extends ControlTemplate {
mContentDescription = contentDescription;
}
+ /**
+ * @param b
+ * @hide
+ */
ThumbnailTemplate(Bundle b) {
super(b);
mThumbnail = b.getParcelable(KEY_ICON);
@@ -82,25 +83,16 @@ public final class ThumbnailTemplate extends ControlTemplate {
return TYPE;
}
+ /**
+ * @return
+ * @hide
+ */
@Override
- protected Bundle getDataBundle() {
+ @NonNull
+ Bundle getDataBundle() {
Bundle b = super.getDataBundle();
b.putObject(KEY_ICON, mThumbnail);
b.putObject(KEY_CONTENT_DESCRIPTION, mContentDescription);
return b;
}
-
- public static final Creator<ThumbnailTemplate> CREATOR = new Creator<ThumbnailTemplate>() {
- @Override
- public ThumbnailTemplate createFromParcel(Parcel source) {
- int type = source.readInt();
- verifyType(type, TYPE);
- return new ThumbnailTemplate(source.readBundle());
- }
-
- @Override
- public ThumbnailTemplate[] newArray(int size) {
- return new ThumbnailTemplate[size];
- }
- };
}
diff --git a/core/java/android/service/controls/templates/ToggleRangeTemplate.aidl b/core/java/android/service/controls/templates/ToggleRangeTemplate.aidl
deleted file mode 100644
index 261128488365..000000000000
--- a/core/java/android/service/controls/templates/ToggleRangeTemplate.aidl
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * 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.service.controls.templates;
-
-parcelable ToggleRangeTemplate; \ No newline at end of file
diff --git a/core/java/android/service/controls/templates/ToggleRangeTemplate.java b/core/java/android/service/controls/templates/ToggleRangeTemplate.java
index aa6f6fbad97b..af43b94699d2 100644
--- a/core/java/android/service/controls/templates/ToggleRangeTemplate.java
+++ b/core/java/android/service/controls/templates/ToggleRangeTemplate.java
@@ -18,13 +18,9 @@ package android.service.controls.templates;
import android.annotation.NonNull;
import android.os.Bundle;
-import android.os.Parcel;
import com.android.internal.util.Preconditions;
-/**
- * @hide
- */
public final class ToggleRangeTemplate extends ControlTemplate {
private static final @TemplateType int TYPE = TYPE_TOGGLE_RANGE;
@@ -34,11 +30,14 @@ public final class ToggleRangeTemplate extends ControlTemplate {
private @NonNull final ControlButton mControlButton;
private @NonNull final RangeTemplate mRangeTemplate;
-
+ /**
+ * @param b
+ * @hide
+ */
ToggleRangeTemplate(@NonNull Bundle b) {
super(b);
mControlButton = b.getParcelable(KEY_BUTTON);
- mRangeTemplate = b.getParcelable(KEY_RANGE);
+ mRangeTemplate = new RangeTemplate(b.getBundle(KEY_RANGE));
}
public ToggleRangeTemplate(@NonNull String templateId,
@@ -60,11 +59,16 @@ public final class ToggleRangeTemplate extends ControlTemplate {
range);
}
+ /**
+ * @return
+ * @hide
+ */
@Override
- protected Bundle getDataBundle() {
+ @NonNull
+ Bundle getDataBundle() {
Bundle b = super.getDataBundle();
b.putParcelable(KEY_BUTTON, mControlButton);
- b.putParcelable(KEY_RANGE, mRangeTemplate);
+ b.putBundle(KEY_RANGE, mRangeTemplate.getDataBundle());
return b;
}
@@ -86,19 +90,4 @@ public final class ToggleRangeTemplate extends ControlTemplate {
public int getTemplateType() {
return TYPE;
}
-
- public static final Creator<ToggleRangeTemplate> CREATOR = new Creator<ToggleRangeTemplate>() {
-
- @Override
- public ToggleRangeTemplate createFromParcel(Parcel source) {
- int type = source.readInt();
- verifyType(type, TYPE);
- return new ToggleRangeTemplate(source.readBundle());
- }
-
- @Override
- public ToggleRangeTemplate[] newArray(int size) {
- return new ToggleRangeTemplate[size];
- }
- };
}
diff --git a/core/java/android/service/controls/templates/ToggleTemplate.java b/core/java/android/service/controls/templates/ToggleTemplate.java
index 0e5fd336e2a7..e4aa6b0d6cec 100644
--- a/core/java/android/service/controls/templates/ToggleTemplate.java
+++ b/core/java/android/service/controls/templates/ToggleTemplate.java
@@ -18,7 +18,6 @@ package android.service.controls.templates;
import android.annotation.NonNull;
import android.os.Bundle;
-import android.os.Parcel;
import android.service.controls.Control;
import android.service.controls.actions.BooleanAction;
@@ -31,7 +30,6 @@ import com.android.internal.util.Preconditions;
* An action on this template will originate a {@link BooleanAction} to change that state.
*
* @see BooleanAction
- * @hide
*/
public final class ToggleTemplate extends ControlTemplate {
@@ -41,7 +39,7 @@ public final class ToggleTemplate extends ControlTemplate {
/**
* @param templateId the identifier for this template object
- * @param button a {@ControlButton} that can show the current state and toggle it
+ * @param button a {@link ControlButton} that can show the current state and toggle it
*/
public ToggleTemplate(@NonNull String templateId, @NonNull ControlButton button) {
super(templateId);
@@ -49,6 +47,10 @@ public final class ToggleTemplate extends ControlTemplate {
mButton = button;
}
+ /**
+ * @param b
+ * @hide
+ */
ToggleTemplate(Bundle b) {
super(b);
mButton = b.getParcelable(KEY_BUTTON);
@@ -58,6 +60,7 @@ public final class ToggleTemplate extends ControlTemplate {
return mButton.isChecked();
}
+ @NonNull
public CharSequence getContentDescription() {
return mButton.getActionDescription();
}
@@ -70,25 +73,15 @@ public final class ToggleTemplate extends ControlTemplate {
return TYPE;
}
+ /**
+ * @return
+ * @hide
+ */
@Override
- protected Bundle getDataBundle() {
+ @NonNull
+ Bundle getDataBundle() {
Bundle b = super.getDataBundle();
b.putParcelable(KEY_BUTTON, mButton);
return b;
}
-
- public static final Creator<ToggleTemplate> CREATOR = new Creator<ToggleTemplate>() {
- @Override
- public ToggleTemplate createFromParcel(Parcel source) {
- int type = source.readInt();
- verifyType(type, TYPE);
- return new ToggleTemplate(source.readBundle());
- }
-
- @Override
- public ToggleTemplate[] newArray(int size) {
- return new ToggleTemplate[size];
- }
- };
-
}
diff --git a/core/java/android/service/notification/StatusBarNotification.java b/core/java/android/service/notification/StatusBarNotification.java
index 4f400a8e9cb2..c5d97b718ff0 100644
--- a/core/java/android/service/notification/StatusBarNotification.java
+++ b/core/java/android/service/notification/StatusBarNotification.java
@@ -17,7 +17,8 @@
package android.service.notification;
import static android.app.NotificationChannel.PLACEHOLDER_CONVERSATION_ID;
-import static android.util.FeatureFlagUtils.*;
+import static android.util.FeatureFlagUtils.NOTIF_CONVO_BYPASS_SHORTCUT_REQ;
+import static android.util.FeatureFlagUtils.isEnabled;
import android.annotation.NonNull;
import android.app.Notification;
@@ -33,7 +34,6 @@ import android.os.Parcel;
import android.os.Parcelable;
import android.os.UserHandle;
import android.text.TextUtils;
-import android.util.FeatureFlagUtils;
import com.android.internal.logging.nano.MetricsProto;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
@@ -454,11 +454,23 @@ public class StatusBarNotification implements Parcelable {
return conversationId;
}
- private String getGroupLogTag() {
+ /**
+ * Returns a probably-unique string based on the notification's group name,
+ * with no more than MAX_LOG_TAG_LENGTH characters.
+ * @return String based on group name of notification.
+ * @hide
+ */
+ public String getGroupLogTag() {
return shortenTag(getGroup());
}
- private String getChannelIdLogTag() {
+ /**
+ * Returns a probably-unique string based on the notification's channel ID,
+ * with no more than MAX_LOG_TAG_LENGTH characters.
+ * @return String based on channel ID of notification.
+ * @hide
+ */
+ public String getChannelIdLogTag() {
if (notification.getChannelId() == null) {
return null;
}
diff --git a/core/java/android/telephony/TelephonyRegistryManager.java b/core/java/android/telephony/TelephonyRegistryManager.java
index 57375919e6cd..6787c46c046e 100644
--- a/core/java/android/telephony/TelephonyRegistryManager.java
+++ b/core/java/android/telephony/TelephonyRegistryManager.java
@@ -36,6 +36,7 @@ import android.telephony.Annotation.RadioPowerState;
import android.telephony.Annotation.SimActivationState;
import android.telephony.Annotation.SrvccState;
import android.telephony.data.ApnSetting;
+import android.telephony.emergency.EmergencyNumber;
import android.telephony.ims.ImsReasonInfo;
import android.util.Log;
@@ -198,6 +199,25 @@ public class TelephonyRegistryManager {
}
/**
+ * Listen for incoming subscriptions
+ * @param subId Subscription ID
+ * @param pkg Package name
+ * @param featureId Feature ID
+ * @param listener Listener providing callback
+ * @param events Events
+ * @param notifyNow Whether to notify instantly
+ */
+ public void listenForSubscriber(int subId, @NonNull String pkg, @NonNull String featureId,
+ @NonNull PhoneStateListener listener, int events, boolean notifyNow) {
+ try {
+ sRegistry.listenForSubscriber(
+ subId, pkg, featureId, listener.callback, events, notifyNow);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Informs the system of an intentional upcoming carrier network change by a carrier app.
* This call only used to allow the system to provide alternative UI while telephony is
* performing an action that may result in intentional, temporary network lack of connectivity.
@@ -258,6 +278,32 @@ public class TelephonyRegistryManager {
}
/**
+ * Notify {@link SubscriptionInfo} change.
+ * @hide
+ */
+ @SystemApi
+ public void notifySubscriptionInfoChanged() {
+ try {
+ sRegistry.notifySubscriptionInfoChanged();
+ } catch (RemoteException ex) {
+ // system server crash
+ }
+ }
+
+ /**
+ * Notify opportunistic {@link SubscriptionInfo} change.
+ * @hide
+ */
+ @SystemApi
+ public void notifyOpportunisticSubscriptionInfoChanged() {
+ try {
+ sRegistry.notifyOpportunisticSubscriptionInfoChanged();
+ } catch (RemoteException ex) {
+ // system server crash
+ }
+ }
+
+ /**
* Notify {@link ServiceState} update on certain subscription.
*
* @param subId for which the service state changed.
@@ -394,6 +440,36 @@ public class TelephonyRegistryManager {
}
/**
+ * Notify outgoing emergency call.
+ * @param phoneId Sender phone ID.
+ * @param subId Sender subscription ID.
+ * @param emergencyNumber Emergency number.
+ */
+ public void notifyOutgoingEmergencyCall(int phoneId, int subId,
+ @NonNull EmergencyNumber emergencyNumber) {
+ try {
+ sRegistry.notifyOutgoingEmergencyCall(phoneId, subId, emergencyNumber);
+ } catch (RemoteException ex) {
+ // system process is dead
+ }
+ }
+
+ /**
+ * Notify outgoing emergency SMS.
+ * @param phoneId Sender phone ID.
+ * @param subId Sender subscription ID.
+ * @param emergencyNumber Emergency number.
+ */
+ public void notifyOutgoingEmergencySms(int phoneId, int subId,
+ @NonNull EmergencyNumber emergencyNumber) {
+ try {
+ sRegistry.notifyOutgoingEmergencySms(phoneId, subId, emergencyNumber);
+ } catch (RemoteException ex) {
+ // system process is dead
+ }
+ }
+
+ /**
* Notify radio power state changed on certain subscription.
*
* @param subId for which radio power state changed.
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index d9c502e14e68..14390f1c209b 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -748,4 +748,9 @@ interface IWindowManager
void getWindowInsets(in WindowManager.LayoutParams attrs, int displayId,
out Rect outContentInsets, out Rect outStableInsets,
out DisplayCutout.ParcelableWrapper displayCutout);
+
+ /**
+ * Called to show global actions.
+ */
+ void showGlobalActions();
}
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 6487ed8ca00c..bee05a9a57d0 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -2551,6 +2551,7 @@ public final class SurfaceControl implements Parcelable {
/**
* @hide
*/
+ @Deprecated
@UnsupportedAppUsage
public Transaction deferTransactionUntilSurface(SurfaceControl sc, Surface barrierSurface,
long frameNumber) {
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 1981bdd93eeb..75d5538faedd 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -414,7 +414,8 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
}
t.setAlpha(mSurfaceControl, alpha);
if (!useBLAST) {
- t.deferTransactionUntilSurface(mSurfaceControl, parent, frame);
+ t.deferTransactionUntil(mSurfaceControl,
+ viewRoot.getRenderSurfaceControl(), frame);
}
}
// It's possible that mSurfaceControl is released in the UI thread before
@@ -1122,7 +1123,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
if (frameNumber > 0 && !WindowManagerGlobal.USE_BLAST_ADAPTER) {
final ViewRootImpl viewRoot = getViewRootImpl();
- t.deferTransactionUntilSurface(surface, viewRoot.mSurface,
+ t.deferTransactionUntil(surface, viewRoot.getRenderSurfaceControl(),
frameNumber);
}
@@ -1217,8 +1218,8 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
if (frameNumber > 0 && viewRoot != null && !useBLAST) {
if (viewRoot.mSurface.isValid()) {
- mRtTransaction.deferTransactionUntilSurface(mSurfaceControl, viewRoot.mSurface,
- frameNumber);
+ mRtTransaction.deferTransactionUntil(mSurfaceControl,
+ viewRoot.getRenderSurfaceControl(), frameNumber);
}
}
t.hide(mSurfaceControl);
diff --git a/core/java/android/view/SyncRtSurfaceTransactionApplier.java b/core/java/android/view/SyncRtSurfaceTransactionApplier.java
index a6536f5d83b7..abe44f45aba5 100644
--- a/core/java/android/view/SyncRtSurfaceTransactionApplier.java
+++ b/core/java/android/view/SyncRtSurfaceTransactionApplier.java
@@ -38,7 +38,7 @@ public class SyncRtSurfaceTransactionApplier {
public static final int FLAG_CORNER_RADIUS = 1 << 4;
public static final int FLAG_VISIBILITY = 1 << 5;
- private final Surface mTargetSurface;
+ private SurfaceControl mTargetSc;
private final ViewRootImpl mTargetViewRootImpl;
private final float[] mTmpFloat9 = new float[9];
@@ -47,7 +47,6 @@ public class SyncRtSurfaceTransactionApplier {
*/
public SyncRtSurfaceTransactionApplier(View targetView) {
mTargetViewRootImpl = targetView != null ? targetView.getViewRootImpl() : null;
- mTargetSurface = mTargetViewRootImpl != null ? mTargetViewRootImpl.mSurface : null;
}
/**
@@ -60,15 +59,16 @@ public class SyncRtSurfaceTransactionApplier {
if (mTargetViewRootImpl == null) {
return;
}
+ mTargetSc = mTargetViewRootImpl.getRenderSurfaceControl();
mTargetViewRootImpl.registerRtFrameCallback(frame -> {
- if (mTargetSurface == null || !mTargetSurface.isValid()) {
+ if (mTargetSc == null || !mTargetSc.isValid()) {
return;
}
Transaction t = new Transaction();
for (int i = params.length - 1; i >= 0; i--) {
SurfaceParams surfaceParams = params[i];
SurfaceControl surface = surfaceParams.surface;
- t.deferTransactionUntilSurface(surface, mTargetSurface, frame);
+ t.deferTransactionUntil(surface, mTargetSc, frame);
applyParams(t, surfaceParams, mTmpFloat9);
}
t.setEarlyWakeup();
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 0f2d2c21d28d..c5f4faf2f462 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -3659,8 +3659,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
*
* @deprecated For floating windows, use {@link LayoutParams#setFitInsetsTypes(int)} with
* {@link Type#navigationBars()}. For non-floating windows that fill the screen, call
- * {@link Window#setOnContentApplyWindowInsets} with {@code null} or a listener that doesn't
- * fit the navigation bar on the window content level.
+ * {@link Window#setOnContentApplyWindowInsetsListener} with {@code null} or a listener that
+ * doesn't fit the navigation bar on the window content level.
*/
public static final int SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION = 0x00000200;
@@ -3688,8 +3688,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
*
* @deprecated For floating windows, use {@link LayoutParams#setFitInsetsTypes(int)} with
* {@link Type#statusBars()} ()}. For non-floating windows that fill the screen, call
- * {@link Window#setOnContentApplyWindowInsets} with {@code null} or a listener that doesn't
- * fit the status bar on the window content level.
+ * {@link Window#setOnContentApplyWindowInsetsListener} with {@code null} or a listener that
+ * doesn't fit the status bar on the window content level.
*/
@Deprecated
public static final int SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN = 0x00000400;
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index f5cfbec924ac..7a93dcc9e4ec 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -1733,8 +1733,8 @@ public final class ViewRootImpl implements ViewParent,
private void updateBoundsLayer() {
if (mBoundsLayer != null) {
setBoundsLayerCrop();
- mTransaction.deferTransactionUntilSurface(mBoundsLayer,
- mSurface, mSurface.getNextFrameNumber())
+ mTransaction.deferTransactionUntil(mBoundsLayer,
+ getRenderSurfaceControl(), mSurface.getNextFrameNumber())
.apply();
}
}
@@ -9539,4 +9539,12 @@ public final class ViewRootImpl implements ViewParent,
SurfaceControl.Transaction getBLASTSyncTransaction() {
return mRtBLASTSyncTransaction;
}
+
+ SurfaceControl getRenderSurfaceControl() {
+ if (WindowManagerGlobal.USE_BLAST_ADAPTER) {
+ return mBlastSurfaceControl;
+ } else {
+ return mSurfaceControl;
+ }
+ }
}
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index c8dea43e8f4c..55c298e2a92b 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -497,13 +497,48 @@ public interface WindowManager extends ViewManager {
* Message for taking fullscreen screenshot
* @hide
*/
- final int TAKE_SCREENSHOT_FULLSCREEN = 1;
+ int TAKE_SCREENSHOT_FULLSCREEN = 1;
/**
* Message for taking screenshot of selected region.
* @hide
*/
- final int TAKE_SCREENSHOT_SELECTED_REGION = 2;
+ int TAKE_SCREENSHOT_SELECTED_REGION = 2;
+
+ /**
+ * Message for handling a screenshot flow with an image provided by the caller.
+ * @hide
+ */
+ int TAKE_SCREENSHOT_PROVIDED_IMAGE = 3;
+
+ /**
+ * Parcel key for the screen shot bitmap sent with messages of type
+ * {@link #TAKE_SCREENSHOT_PROVIDED_IMAGE}, type {@link android.graphics.Bitmap}
+ * @hide
+ */
+ String PARCEL_KEY_SCREENSHOT_BITMAP = "screenshot_screen_bitmap";
+
+ /**
+ * Parcel key for the screen bounds of the image sent with messages of type
+ * [@link {@link #TAKE_SCREENSHOT_PROVIDED_IMAGE}], type {@link Rect} in screen coordinates.
+ * @hide
+ */
+ String PARCEL_KEY_SCREENSHOT_BOUNDS = "screenshot_screen_bounds";
+
+ /**
+ * Parcel key for the task id of the task that the screen shot was taken of, sent with messages
+ * of type [@link {@link #TAKE_SCREENSHOT_PROVIDED_IMAGE}], type int.
+ * @hide
+ */
+ String PARCEL_KEY_SCREENSHOT_TASK_ID = "screenshot_task_id";
+
+ /**
+ * Parcel key for the visible insets of the image sent with messages of type
+ * [@link {@link #TAKE_SCREENSHOT_PROVIDED_IMAGE}], type {@link android.graphics.Insets} in
+ * screen coordinates.
+ * @hide
+ */
+ String PARCEL_KEY_SCREENSHOT_INSETS = "screenshot_insets";
/**
* @hide
diff --git a/core/java/android/view/contentcapture/ContentCaptureManager.java b/core/java/android/view/contentcapture/ContentCaptureManager.java
index 6040abd4f5f6..81c83834098c 100644
--- a/core/java/android/view/contentcapture/ContentCaptureManager.java
+++ b/core/java/android/view/contentcapture/ContentCaptureManager.java
@@ -19,6 +19,7 @@ import static android.view.contentcapture.ContentCaptureHelper.sDebug;
import static android.view.contentcapture.ContentCaptureHelper.sVerbose;
import static android.view.contentcapture.ContentCaptureHelper.toSet;
+import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -30,12 +31,16 @@ import android.content.ComponentName;
import android.content.ContentCaptureOptions;
import android.content.Context;
import android.graphics.Canvas;
+import android.os.Binder;
+import android.os.CancellationSignal;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
+import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.Log;
+import android.util.Slog;
import android.view.View;
import android.view.ViewStructure;
import android.view.WindowManager;
@@ -48,8 +53,11 @@ import com.android.internal.util.SyncResultReceiver;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Set;
+import java.util.concurrent.Executor;
+import java.util.function.Consumer;
/**
* <p>The {@link ContentCaptureManager} provides additional ways for for apps to
@@ -629,6 +637,33 @@ public final class ContentCaptureManager {
}
/**
+ * Called by the app to request data sharing via writing to a file.
+ *
+ * <p>The ContentCaptureService app will receive a read-only file descriptor pointing to the
+ * same file and will be able to read data being shared from it.
+ *
+ * <p>Note: using this API doesn't guarantee the app staying alive and is "best-effort".
+ * Starting a foreground service would minimize the chances of the app getting killed during the
+ * file sharing session.
+ *
+ * @param request object specifying details of the data being shared.
+ */
+ public void shareData(@NonNull DataShareRequest request,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull DataShareWriteAdapter dataShareWriteAdapter) {
+ Preconditions.checkNotNull(request);
+ Preconditions.checkNotNull(dataShareWriteAdapter);
+ Preconditions.checkNotNull(executor);
+
+ try {
+ mService.shareData(request,
+ new DataShareAdapterDelegate(executor, dataShareWriteAdapter));
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Runs a sync method in the service, properly handling exceptions.
*
* @throws SecurityException if caller is not allowed to execute the method.
@@ -675,4 +710,55 @@ public final class ContentCaptureManager {
private interface MyRunnable {
void run(@NonNull SyncResultReceiver receiver) throws RemoteException;
}
+
+ private static class DataShareAdapterDelegate extends IDataShareWriteAdapter.Stub {
+
+ private final WeakReference<DataShareWriteAdapter> mAdapterReference;
+ private final WeakReference<Executor> mExecutorReference;
+
+ private DataShareAdapterDelegate(Executor executor, DataShareWriteAdapter adapter) {
+ Preconditions.checkNotNull(executor);
+ Preconditions.checkNotNull(adapter);
+
+ mExecutorReference = new WeakReference<>(executor);
+ mAdapterReference = new WeakReference<>(adapter);
+ }
+
+ @Override
+ public void write(ParcelFileDescriptor destination)
+ throws RemoteException {
+ // TODO(b/148264965): implement this.
+ CancellationSignal cancellationSignal = new CancellationSignal();
+ executeAdapterMethodLocked(adapter -> adapter.onWrite(destination, cancellationSignal),
+ "onWrite");
+ }
+
+ @Override
+ public void error(int errorCode) throws RemoteException {
+ executeAdapterMethodLocked(adapter -> adapter.onError(errorCode), "onError");
+ }
+
+ @Override
+ public void rejected() throws RemoteException {
+ executeAdapterMethodLocked(DataShareWriteAdapter::onRejected, "onRejected");
+ }
+
+ private void executeAdapterMethodLocked(Consumer<DataShareWriteAdapter> adapterFn,
+ String methodName) {
+ DataShareWriteAdapter adapter = mAdapterReference.get();
+ Executor executor = mExecutorReference.get();
+
+ if (adapter == null || executor == null) {
+ Slog.w(TAG, "Can't execute " + methodName + "(), references have been GC'ed");
+ return;
+ }
+
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ executor.execute(() -> adapterFn.accept(adapter));
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+ }
}
diff --git a/core/java/android/service/controls/actions/FloatAction.aidl b/core/java/android/view/contentcapture/DataShareRequest.aidl
index 2c1e76d68803..75073e411ec8 100644
--- a/core/java/android/service/controls/actions/FloatAction.aidl
+++ b/core/java/android/view/contentcapture/DataShareRequest.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,7 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package android.view.contentcapture;
-package android.service.controls.actions;
-
-parcelable FloatAction; \ No newline at end of file
+parcelable DataShareRequest;
diff --git a/core/java/android/view/contentcapture/DataShareRequest.java b/core/java/android/view/contentcapture/DataShareRequest.java
new file mode 100644
index 000000000000..78c0ef9568ba
--- /dev/null
+++ b/core/java/android/view/contentcapture/DataShareRequest.java
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.contentcapture;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.ActivityThread;
+import android.content.LocusId;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.DataClass;
+import com.android.internal.util.Preconditions;
+
+/** Container class representing a request to share data with Content Capture service. */
+@DataClass(
+ genConstructor = false,
+ genEqualsHashCode = true,
+ genHiddenConstDefs = true,
+ genParcelable = true,
+ genToString = true
+)
+public final class DataShareRequest implements Parcelable {
+
+ /** Name of the package making the request. */
+ @NonNull private final String mPackageName;
+
+ /** Locus id helping to identify what data is being shared. */
+ @Nullable private final LocusId mLocusId;
+
+ /** MIME type of the data being shared. */
+ @NonNull private final String mMimeType;
+
+ /** Constructs a request to share data with the Content Capture Service. */
+ public DataShareRequest(@Nullable LocusId locusId, @NonNull String mimeType) {
+ Preconditions.checkNotNull(mimeType);
+
+ mPackageName = ActivityThread.currentActivityThread().getApplication().getPackageName();
+ mLocusId = locusId;
+ mMimeType = mimeType;
+ }
+
+
+
+ // Code below generated by codegen v1.0.14.
+ //
+ // DO NOT MODIFY!
+ // CHECKSTYLE:OFF Generated code
+ //
+ // To regenerate run:
+ // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/view/contentcapture/DataShareRequest.java
+ //
+ // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+ // Settings > Editor > Code Style > Formatter Control
+ //@formatter:off
+
+
+ /**
+ * Name of the package making the request.
+ */
+ @DataClass.Generated.Member
+ public @NonNull String getPackageName() {
+ return mPackageName;
+ }
+
+ /**
+ * Locus id helping to identify what data is being shared.
+ */
+ @DataClass.Generated.Member
+ public @Nullable LocusId getLocusId() {
+ return mLocusId;
+ }
+
+ /**
+ * MIME type of the data being shared.
+ */
+ @DataClass.Generated.Member
+ public @NonNull String getMimeType() {
+ return mMimeType;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public String toString() {
+ // You can override field toString logic by defining methods like:
+ // String fieldNameToString() { ... }
+
+ return "DataShareRequest { " +
+ "packageName = " + mPackageName + ", " +
+ "locusId = " + mLocusId + ", " +
+ "mimeType = " + mMimeType +
+ " }";
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public boolean equals(@Nullable Object o) {
+ // You can override field equality logic by defining either of the methods like:
+ // boolean fieldNameEquals(DataShareRequest other) { ... }
+ // boolean fieldNameEquals(FieldType otherValue) { ... }
+
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ @SuppressWarnings("unchecked")
+ DataShareRequest that = (DataShareRequest) o;
+ //noinspection PointlessBooleanExpression
+ return true
+ && java.util.Objects.equals(mPackageName, that.mPackageName)
+ && java.util.Objects.equals(mLocusId, that.mLocusId)
+ && java.util.Objects.equals(mMimeType, that.mMimeType);
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int hashCode() {
+ // You can override field hashCode logic by defining methods like:
+ // int fieldNameHashCode() { ... }
+
+ int _hash = 1;
+ _hash = 31 * _hash + java.util.Objects.hashCode(mPackageName);
+ _hash = 31 * _hash + java.util.Objects.hashCode(mLocusId);
+ _hash = 31 * _hash + java.util.Objects.hashCode(mMimeType);
+ return _hash;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ // You can override field parcelling by defining methods like:
+ // void parcelFieldName(Parcel dest, int flags) { ... }
+
+ byte flg = 0;
+ if (mLocusId != null) flg |= 0x2;
+ dest.writeByte(flg);
+ dest.writeString(mPackageName);
+ if (mLocusId != null) dest.writeTypedObject(mLocusId, flags);
+ dest.writeString(mMimeType);
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int describeContents() { return 0; }
+
+ /** @hide */
+ @SuppressWarnings({"unchecked", "RedundantCast"})
+ @DataClass.Generated.Member
+ /* package-private */ DataShareRequest(@NonNull Parcel in) {
+ // You can override field unparcelling by defining methods like:
+ // static FieldType unparcelFieldName(Parcel in) { ... }
+
+ byte flg = in.readByte();
+ String packageName = in.readString();
+ LocusId locusId = (flg & 0x2) == 0 ? null : (LocusId) in.readTypedObject(LocusId.CREATOR);
+ String mimeType = in.readString();
+
+ this.mPackageName = packageName;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mPackageName);
+ this.mLocusId = locusId;
+ this.mMimeType = mimeType;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mMimeType);
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ @DataClass.Generated.Member
+ public static final @NonNull Parcelable.Creator<DataShareRequest> CREATOR
+ = new Parcelable.Creator<DataShareRequest>() {
+ @Override
+ public DataShareRequest[] newArray(int size) {
+ return new DataShareRequest[size];
+ }
+
+ @Override
+ public DataShareRequest createFromParcel(@NonNull Parcel in) {
+ return new DataShareRequest(in);
+ }
+ };
+
+ @DataClass.Generated(
+ time = 1579870254459L,
+ codegenVersion = "1.0.14",
+ sourceFile = "frameworks/base/core/java/android/view/contentcapture/DataShareRequest.java",
+ inputSignatures = "private final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.Nullable android.content.LocusId mLocusId\nprivate final @android.annotation.NonNull java.lang.String mMimeType\nclass DataShareRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false, genEqualsHashCode=true, genHiddenConstDefs=true, genParcelable=true, genToString=true)")
+ @Deprecated
+ private void __metadata() {}
+
+
+ //@formatter:on
+ // End of generated code
+
+}
diff --git a/core/java/android/view/contentcapture/DataShareWriteAdapter.java b/core/java/android/view/contentcapture/DataShareWriteAdapter.java
new file mode 100644
index 000000000000..f791fea7ee8d
--- /dev/null
+++ b/core/java/android/view/contentcapture/DataShareWriteAdapter.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.contentcapture;
+
+import android.annotation.NonNull;
+import android.os.CancellationSignal;
+import android.os.ParcelFileDescriptor;
+
+/** Adapter class used by apps to share data with the Content Capture service. */
+public interface DataShareWriteAdapter {
+
+ /** Request has been rejected, because a concurrent data share sessions is in progress. */
+ int ERROR_CONCURRENT_REQUEST = 1;
+
+ /** Data share session timed out. */
+ int ERROR_UNKNOWN = 2;
+
+ /**
+ * Method invoked when the data share session has been started and the app needs to start
+ * writing into the file used for sharing.
+ *
+ * <p>App needs to handle explicitly cases when the file descriptor is closed and handle
+ * gracefully if IOExceptions happen.
+ *
+ * @param destination file descriptor used to write data into
+ * @param cancellationSignal cancellation signal that the app can use to subscribe to cancel
+ * operations.
+ */
+ void onWrite(@NonNull ParcelFileDescriptor destination,
+ @NonNull CancellationSignal cancellationSignal);
+
+ /** Data share sessions has been rejected by the Content Capture service. */
+ void onRejected();
+
+ /**
+ * Method invoked when an error occurred, for example sessions has not been started or
+ * terminated unsuccessfully.
+ *
+ * @param errorCode the error code corresponding to an ERROR_* value.
+ */
+ default void onError(int errorCode) {
+ /* do nothing - stub */
+ }
+}
diff --git a/core/java/android/view/contentcapture/IContentCaptureManager.aidl b/core/java/android/view/contentcapture/IContentCaptureManager.aidl
index 7850b67b8404..e8d85ac69907 100644
--- a/core/java/android/view/contentcapture/IContentCaptureManager.aidl
+++ b/core/java/android/view/contentcapture/IContentCaptureManager.aidl
@@ -20,6 +20,8 @@ import android.content.ComponentName;
import android.view.contentcapture.ContentCaptureContext;
import android.view.contentcapture.ContentCaptureEvent;
import android.view.contentcapture.DataRemovalRequest;
+import android.view.contentcapture.DataShareRequest;
+import android.view.contentcapture.IDataShareWriteAdapter;
import android.os.IBinder;
import com.android.internal.os.IResultReceiver;
@@ -64,6 +66,11 @@ oneway interface IContentCaptureManager {
void removeData(in DataRemovalRequest request);
/**
+ * Requests sharing of a binary data with the content capture service.
+ */
+ void shareData(in DataShareRequest request, in IDataShareWriteAdapter adapter);
+
+ /**
* Returns whether the content capture feature is enabled for the calling user.
*/
void isContentCaptureFeatureEnabled(in IResultReceiver result);
diff --git a/core/java/android/service/controls/actions/ControlAction.aidl b/core/java/android/view/contentcapture/IDataShareWriteAdapter.aidl
index b012521c7255..80924ef78f85 100644
--- a/core/java/android/service/controls/actions/ControlAction.aidl
+++ b/core/java/android/view/contentcapture/IDataShareWriteAdapter.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,6 +14,15 @@
* limitations under the License.
*/
-package android.service.controls.actions;
+package android.view.contentcapture;
-parcelable ControlAction; \ No newline at end of file
+import android.os.ICancellationSignal;
+
+/**
+ * @hide
+ */
+oneway interface IDataShareWriteAdapter {
+ void write(in ParcelFileDescriptor destination);
+ void error(int errorCode);
+ void rejected();
+}
diff --git a/core/java/android/view/inputmethod/InlineSuggestionInfo.java b/core/java/android/view/inputmethod/InlineSuggestionInfo.java
index 195b63a8fc8e..703b64f8224f 100644
--- a/core/java/android/view/inputmethod/InlineSuggestionInfo.java
+++ b/core/java/android/view/inputmethod/InlineSuggestionInfo.java
@@ -18,6 +18,7 @@ package android.view.inputmethod;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.annotation.TestApi;
import android.os.Parcelable;
import android.view.inline.InlinePresentationSpec;
@@ -45,6 +46,17 @@ public final class InlineSuggestionInfo implements Parcelable {
*/
public static final @Source String SOURCE_PLATFORM = "android:platform";
+ /**
+ * UI type: the UI contains an Autofill suggestion that will autofill the fields when tapped.
+ */
+ public static final @Type String TYPE_SUGGESTION = "android:autofill:suggestion";
+
+ /**
+ * UI type: the UI contains an widget that will launch an intent when tapped.
+ */
+ @SuppressLint({"IntentName"})
+ public static final @Type String TYPE_ACTION = "android:autofill:action";
+
/** The presentation spec to which the inflated suggestion view abides. */
private final @NonNull InlinePresentationSpec mPresentationSpec;
@@ -54,6 +66,9 @@ public final class InlineSuggestionInfo implements Parcelable {
/** Hints for the type of data being suggested. */
private final @Nullable String[] mAutofillHints;
+ /** The type of the UI. */
+ private final @NonNull @Type String mType;
+
/**
* Creates a new {@link InlineSuggestionInfo}, for testing purpose.
*
@@ -65,7 +80,8 @@ public final class InlineSuggestionInfo implements Parcelable {
@NonNull InlinePresentationSpec presentationSpec,
@NonNull @Source String source,
@Nullable String[] autofillHints) {
- return new InlineSuggestionInfo(presentationSpec, source, autofillHints);
+ // TODO(b/147394280): Add CTS test for the type field.
+ return new InlineSuggestionInfo(presentationSpec, source, autofillHints, TYPE_SUGGESTION);
}
@@ -92,6 +108,15 @@ public final class InlineSuggestionInfo implements Parcelable {
@DataClass.Generated.Member
public @interface Source {}
+ /** @hide */
+ @android.annotation.StringDef(prefix = "TYPE_", value = {
+ TYPE_SUGGESTION,
+ TYPE_ACTION
+ })
+ @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE)
+ @DataClass.Generated.Member
+ public @interface Type {}
+
/**
* Creates a new InlineSuggestionInfo.
*
@@ -101,13 +126,16 @@ public final class InlineSuggestionInfo implements Parcelable {
* The source from which the suggestion is provided.
* @param autofillHints
* Hints for the type of data being suggested.
+ * @param type
+ * The type of the UI.
* @hide
*/
@DataClass.Generated.Member
public InlineSuggestionInfo(
@NonNull InlinePresentationSpec presentationSpec,
@NonNull @Source String source,
- @Nullable String[] autofillHints) {
+ @Nullable String[] autofillHints,
+ @NonNull @Type String type) {
this.mPresentationSpec = presentationSpec;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mPresentationSpec);
@@ -124,6 +152,18 @@ public final class InlineSuggestionInfo implements Parcelable {
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mSource);
this.mAutofillHints = autofillHints;
+ this.mType = type;
+
+ if (!(java.util.Objects.equals(mType, TYPE_SUGGESTION))
+ && !(java.util.Objects.equals(mType, TYPE_ACTION))) {
+ throw new java.lang.IllegalArgumentException(
+ "type was " + mType + " but must be one of: "
+ + "TYPE_SUGGESTION(" + TYPE_SUGGESTION + "), "
+ + "TYPE_ACTION(" + TYPE_ACTION + ")");
+ }
+
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mType);
// onConstructed(); // You can define this method to get a callback
}
@@ -152,6 +192,14 @@ public final class InlineSuggestionInfo implements Parcelable {
return mAutofillHints;
}
+ /**
+ * The type of the UI.
+ */
+ @DataClass.Generated.Member
+ public @NonNull @Type String getType() {
+ return mType;
+ }
+
@Override
@DataClass.Generated.Member
public String toString() {
@@ -161,7 +209,8 @@ public final class InlineSuggestionInfo implements Parcelable {
return "InlineSuggestionInfo { " +
"presentationSpec = " + mPresentationSpec + ", " +
"source = " + mSource + ", " +
- "autofillHints = " + java.util.Arrays.toString(mAutofillHints) +
+ "autofillHints = " + java.util.Arrays.toString(mAutofillHints) + ", " +
+ "type = " + mType +
" }";
}
@@ -180,7 +229,8 @@ public final class InlineSuggestionInfo implements Parcelable {
return true
&& java.util.Objects.equals(mPresentationSpec, that.mPresentationSpec)
&& java.util.Objects.equals(mSource, that.mSource)
- && java.util.Arrays.equals(mAutofillHints, that.mAutofillHints);
+ && java.util.Arrays.equals(mAutofillHints, that.mAutofillHints)
+ && java.util.Objects.equals(mType, that.mType);
}
@Override
@@ -193,6 +243,7 @@ public final class InlineSuggestionInfo implements Parcelable {
_hash = 31 * _hash + java.util.Objects.hashCode(mPresentationSpec);
_hash = 31 * _hash + java.util.Objects.hashCode(mSource);
_hash = 31 * _hash + java.util.Arrays.hashCode(mAutofillHints);
+ _hash = 31 * _hash + java.util.Objects.hashCode(mType);
return _hash;
}
@@ -208,6 +259,7 @@ public final class InlineSuggestionInfo implements Parcelable {
dest.writeTypedObject(mPresentationSpec, flags);
dest.writeString(mSource);
if (mAutofillHints != null) dest.writeStringArray(mAutofillHints);
+ dest.writeString(mType);
}
@Override
@@ -225,6 +277,7 @@ public final class InlineSuggestionInfo implements Parcelable {
InlinePresentationSpec presentationSpec = (InlinePresentationSpec) in.readTypedObject(InlinePresentationSpec.CREATOR);
String source = in.readString();
String[] autofillHints = (flg & 0x4) == 0 ? null : in.createStringArray();
+ String type = in.readString();
this.mPresentationSpec = presentationSpec;
com.android.internal.util.AnnotationValidations.validate(
@@ -242,6 +295,18 @@ public final class InlineSuggestionInfo implements Parcelable {
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mSource);
this.mAutofillHints = autofillHints;
+ this.mType = type;
+
+ if (!(java.util.Objects.equals(mType, TYPE_SUGGESTION))
+ && !(java.util.Objects.equals(mType, TYPE_ACTION))) {
+ throw new java.lang.IllegalArgumentException(
+ "type was " + mType + " but must be one of: "
+ + "TYPE_SUGGESTION(" + TYPE_SUGGESTION + "), "
+ + "TYPE_ACTION(" + TYPE_ACTION + ")");
+ }
+
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mType);
// onConstructed(); // You can define this method to get a callback
}
@@ -261,10 +326,10 @@ public final class InlineSuggestionInfo implements Parcelable {
};
@DataClass.Generated(
- time = 1578972121865L,
+ time = 1579806757327L,
codegenVersion = "1.0.14",
sourceFile = "frameworks/base/core/java/android/view/inputmethod/InlineSuggestionInfo.java",
- inputSignatures = "public static final @android.view.inputmethod.InlineSuggestionInfo.Source java.lang.String SOURCE_AUTOFILL\npublic static final @android.view.inputmethod.InlineSuggestionInfo.Source java.lang.String SOURCE_PLATFORM\nprivate final @android.annotation.NonNull android.view.inline.InlinePresentationSpec mPresentationSpec\nprivate final @android.annotation.NonNull @android.view.inputmethod.InlineSuggestionInfo.Source java.lang.String mSource\nprivate final @android.annotation.Nullable java.lang.String[] mAutofillHints\npublic static @android.annotation.TestApi @android.annotation.NonNull android.view.inputmethod.InlineSuggestionInfo newInlineSuggestionInfo(android.view.inline.InlinePresentationSpec,java.lang.String,java.lang.String[])\nclass InlineSuggestionInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genHiddenConstDefs=true, genHiddenConstructor=true)")
+ inputSignatures = "public static final @android.view.inputmethod.InlineSuggestionInfo.Source java.lang.String SOURCE_AUTOFILL\npublic static final @android.view.inputmethod.InlineSuggestionInfo.Source java.lang.String SOURCE_PLATFORM\npublic static final @android.view.inputmethod.InlineSuggestionInfo.Type java.lang.String TYPE_SUGGESTION\npublic static final @android.annotation.SuppressLint({\"IntentName\"}) @android.view.inputmethod.InlineSuggestionInfo.Type java.lang.String TYPE_ACTION\nprivate final @android.annotation.NonNull android.view.inline.InlinePresentationSpec mPresentationSpec\nprivate final @android.annotation.NonNull @android.view.inputmethod.InlineSuggestionInfo.Source java.lang.String mSource\nprivate final @android.annotation.Nullable java.lang.String[] mAutofillHints\nprivate final @android.annotation.NonNull @android.view.inputmethod.InlineSuggestionInfo.Type java.lang.String mType\npublic static @android.annotation.TestApi @android.annotation.NonNull android.view.inputmethod.InlineSuggestionInfo newInlineSuggestionInfo(android.view.inline.InlinePresentationSpec,java.lang.String,java.lang.String[])\nclass InlineSuggestionInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genHiddenConstDefs=true, genHiddenConstructor=true)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index cdf8c686ef00..7fd41508dfc3 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -5107,7 +5107,7 @@ public class Editor {
int lineLeft = (int) layout.getLineLeft(line);
lineLeft += mTextView.getTotalPaddingLeft() - mTextView.getScrollX();
int lineRight = (int) layout.getLineRight(line);
- lineRight -= mTextView.getTotalPaddingRight() + mTextView.getScrollX();
+ lineRight += mTextView.getTotalPaddingLeft() - mTextView.getScrollX();
mMagnifierAnimator.mMagnifier.setSourceHorizontalBounds(lineLeft, lineRight);
}
mMagnifierAnimator.show(showPosInView.x, showPosInView.y);
diff --git a/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java b/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java
index b2aa0431251d..77d8e0290f20 100644
--- a/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java
+++ b/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java
@@ -156,14 +156,32 @@ public abstract class AbstractMultiProfilePagerAdapter extends PagerAdapter {
@VisibleForTesting
public abstract Object getAdapterForIndex(int pageIndex);
+ /**
+ * Returns the {@link ResolverListAdapter} instance of the profile that represents
+ * <code>userHandle</code>. If there is no such adapter for the specified
+ * <code>userHandle</code>, returns {@code null}.
+ * <p>For example, if there is a work profile on the device with user id 10, calling this method
+ * with <code>UserHandle.of(10)</code> returns the work profile {@link ResolverListAdapter}.
+ */
+ @Nullable
+ abstract ResolverListAdapter getListAdapterForUserHandle(UserHandle userHandle);
+
+ /**
+ * Returns the {@link ResolverListAdapter} instance of the profile that is currently visible
+ * to the user.
+ * <p>For example, if the user is viewing the work tab in the share sheet, this method returns
+ * the work profile {@link ResolverListAdapter}.
+ * @see #getInactiveListAdapter()
+ */
@VisibleForTesting
public abstract ResolverListAdapter getActiveListAdapter();
/**
* If this is a device with a work profile, returns the {@link ResolverListAdapter} instance
- * of the profile that is not the active one. Otherwise returns {@code null}. For example,
- * if the share sheet is launched in the work profile, this method returns the personal
- * profile {@link ResolverListAdapter}.
+ * of the profile that is <b><i>not</i></b> currently visible to the user. Otherwise returns
+ * {@code null}.
+ * <p>For example, if the user is viewing the work tab in the share sheet, this method returns
+ * the personal profile {@link ResolverListAdapter}.
* @see #getActiveListAdapter()
*/
@VisibleForTesting
diff --git a/core/java/com/android/internal/app/BlockedAppActivity.java b/core/java/com/android/internal/app/BlockedAppActivity.java
deleted file mode 100644
index fbdbbfb06b78..000000000000
--- a/core/java/com/android/internal/app/BlockedAppActivity.java
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.app;
-
-import android.content.Intent;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.os.Bundle;
-import android.text.TextUtils;
-import android.util.Slog;
-
-import com.android.internal.R;
-
-/**
- * A dialog shown to the user when they try to launch an app that is not allowed in lock task
- * mode. The intent to start this activity must be created with the static factory method provided
- * below.
- */
-public class BlockedAppActivity extends AlertActivity {
-
- private static final String TAG = "BlockedAppActivity";
- private static final String PACKAGE_NAME = "com.android.internal.app";
- private static final String EXTRA_BLOCKED_PACKAGE = PACKAGE_NAME + ".extra.BLOCKED_PACKAGE";
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- Intent intent = getIntent();
- int userId = intent.getIntExtra(Intent.EXTRA_USER_ID, /* defaultValue= */ -1);
- if (userId < 0) {
- Slog.wtf(TAG, "Invalid user: " + userId);
- finish();
- return;
- }
-
- String packageName = intent.getStringExtra(EXTRA_BLOCKED_PACKAGE);
- if (TextUtils.isEmpty(packageName)) {
- Slog.wtf(TAG, "Invalid package: " + packageName);
- finish();
- return;
- }
-
- CharSequence appLabel = getAppLabel(userId, packageName);
-
- mAlertParams.mTitle = getString(R.string.app_blocked_title);
- mAlertParams.mMessage = getString(R.string.app_blocked_message, appLabel);
- mAlertParams.mPositiveButtonText = getString(android.R.string.ok);
- setupAlert();
- }
-
- private CharSequence getAppLabel(int userId, String packageName) {
- PackageManager pm = getPackageManager();
- try {
- ApplicationInfo aInfo =
- pm.getApplicationInfoAsUser(packageName, /* flags= */ 0, userId);
- return aInfo.loadLabel(pm);
- } catch (PackageManager.NameNotFoundException ne) {
- Slog.e(TAG, "Package " + packageName + " not found", ne);
- }
- return packageName;
- }
-
-
- /** Creates an intent that launches {@link BlockedAppActivity}. */
- public static Intent createIntent(int userId, String packageName) {
- return new Intent()
- .setClassName("android", BlockedAppActivity.class.getName())
- .putExtra(Intent.EXTRA_USER_ID, userId)
- .putExtra(EXTRA_BLOCKED_PACKAGE, packageName);
- }
-}
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 8bbc343fa4ca..65128e4a2eda 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -72,7 +72,6 @@ import android.os.IBinder;
import android.os.Message;
import android.os.Parcelable;
import android.os.PatternMatcher;
-import android.os.Process;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.UserHandle;
@@ -90,6 +89,7 @@ import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.HashedStringCache;
import android.util.Log;
+import android.util.Pair;
import android.util.Size;
import android.util.Slog;
import android.view.LayoutInflater;
@@ -150,6 +150,8 @@ public class ChooserActivity extends ResolverActivity implements
ChooserListAdapter.ChooserListCommunicator,
SelectableTargetInfoCommunicator {
private static final String TAG = "ChooserActivity";
+ private AppPredictor mPersonalAppPredictor;
+ private AppPredictor mWorkAppPredictor;
@UnsupportedAppUsage
public ChooserActivity() {
@@ -164,7 +166,7 @@ public class ChooserActivity extends ResolverActivity implements
private static final String PREF_NUM_SHEET_EXPANSIONS = "pref_num_sheet_expansions";
- private static final boolean DEBUG = false;
+ private static final boolean DEBUG = true;
private static final boolean USE_PREDICTION_MANAGER_FOR_SHARE_ACTIVITIES = true;
// TODO(b/123088566) Share these in a better way.
@@ -177,9 +179,8 @@ public class ChooserActivity extends ResolverActivity implements
public static final int LIST_VIEW_UPDATE_INTERVAL_IN_MILLIS = 250;
private boolean mIsAppPredictorComponentAvailable;
- private AppPredictor mAppPredictor;
- private AppPredictor.Callback mAppPredictorCallback;
private Map<ChooserTarget, AppTarget> mDirectShareAppTargetCache;
+ private Map<ChooserTarget, ShortcutInfo> mDirectShareShortcutInfoCache;
public static final int TARGET_TYPE_DEFAULT = 0;
public static final int TARGET_TYPE_CHOOSER_TARGET = 1;
@@ -239,7 +240,7 @@ public class ChooserActivity extends ResolverActivity implements
private static final int MAX_RANKED_TARGETS = 4;
private final List<ChooserTargetServiceConnection> mServiceConnections = new ArrayList<>();
- private final Set<ComponentName> mServicesRequested = new HashSet<>();
+ private final Set<Pair<ComponentName, UserHandle>> mServicesRequested = new HashSet<>();
private static final int MAX_LOG_RANK_POSITION = 12;
@@ -422,6 +423,7 @@ public class ChooserActivity extends ResolverActivity implements
WATCHDOG_TIMEOUT_MIN_MILLIS);
sendEmptyMessageDelayed(CHOOSER_TARGET_SERVICE_WATCHDOG_MAX_TIMEOUT,
WATCHDOG_TIMEOUT_MAX_MILLIS);
+
}
private void maybeStopServiceRequestTimer() {
@@ -454,10 +456,14 @@ public class ChooserActivity extends ResolverActivity implements
break;
}
if (sri.resultTargets != null) {
- // TODO(arangelov): Instead of using getCurrentListAdapter(), pass the
- // profileId as part of the message.
- mChooserMultiProfilePagerAdapter.getActiveListAdapter().addServiceResults(
- sri.originalTarget, sri.resultTargets, TARGET_TYPE_CHOOSER_TARGET);
+ ChooserListAdapter adapterForUserHandle =
+ mChooserMultiProfilePagerAdapter.getListAdapterForUserHandle(
+ sri.userHandle);
+ if (adapterForUserHandle != null) {
+ adapterForUserHandle.addServiceResults(sri.originalTarget,
+ sri.resultTargets, TARGET_TYPE_CHOOSER_TARGET,
+ /* directShareShortcutInfoCache */ null);
+ }
}
unbindService(sri.connection);
sri.connection.destroy();
@@ -480,15 +486,23 @@ public class ChooserActivity extends ResolverActivity implements
Log.d(TAG, "LIST_VIEW_UPDATE_MESSAGE; ");
}
- mChooserMultiProfilePagerAdapter.getActiveListAdapter().refreshListView();
+ UserHandle userHandle = (UserHandle) msg.obj;
+ mChooserMultiProfilePagerAdapter.getListAdapterForUserHandle(userHandle)
+ .refreshListView();
break;
case SHORTCUT_MANAGER_SHARE_TARGET_RESULT:
if (DEBUG) Log.d(TAG, "SHORTCUT_MANAGER_SHARE_TARGET_RESULT");
final ServiceResultInfo resultInfo = (ServiceResultInfo) msg.obj;
if (resultInfo.resultTargets != null) {
- mChooserMultiProfilePagerAdapter.getActiveListAdapter().addServiceResults(
- resultInfo.originalTarget, resultInfo.resultTargets, msg.arg1);
+ ChooserListAdapter adapterForUserHandle =
+ mChooserMultiProfilePagerAdapter.getListAdapterForUserHandle(
+ resultInfo.userHandle);
+ if (adapterForUserHandle != null) {
+ adapterForUserHandle.addServiceResults(
+ resultInfo.originalTarget, resultInfo.resultTargets, msg.arg1,
+ mDirectShareShortcutInfoCache);
+ }
}
break;
@@ -638,45 +652,6 @@ public class ChooserActivity extends ResolverActivity implements
.addTaggedData(MetricsEvent.FIELD_SHARESHEET_MIMETYPE, target.getType())
.addTaggedData(MetricsEvent.FIELD_TIME_TO_APP_TARGETS, systemCost));
- AppPredictor appPredictor = getAppPredictorForDirectShareIfEnabled();
- if (appPredictor != null) {
- mDirectShareAppTargetCache = new HashMap<>();
- mAppPredictorCallback = resultList -> {
- //TODO(arangelov) Take care of edge case when callback called after swiping tabs
- if (isFinishing() || isDestroyed()) {
- return;
- }
- if (mChooserMultiProfilePagerAdapter.getActiveListAdapter().getCount() == 0) {
- return;
- }
- if (resultList.isEmpty()) {
- // APS may be disabled, so try querying targets ourselves.
- //TODO(arangelov) queryDirectShareTargets indirectly uses mIntents.
- // Investigate implications for work tab.
- queryDirectShareTargets(
- mChooserMultiProfilePagerAdapter.getActiveListAdapter(), true);
- return;
- }
- final List<DisplayResolveInfo> driList =
- getDisplayResolveInfos(
- mChooserMultiProfilePagerAdapter.getActiveListAdapter());
- final List<ShortcutManager.ShareShortcutInfo> shareShortcutInfos =
- new ArrayList<>();
- for (AppTarget appTarget : resultList) {
- if (appTarget.getShortcutInfo() == null) {
- continue;
- }
- shareShortcutInfos.add(new ShortcutManager.ShareShortcutInfo(
- appTarget.getShortcutInfo(),
- new ComponentName(
- appTarget.getPackageName(), appTarget.getClassName())));
- }
- sendShareShortcutInfoList(shareShortcutInfos, driList, resultList);
- };
- appPredictor
- .registerPredictionUpdates(this.getMainExecutor(), mAppPredictorCallback);
- }
-
mChooserRowServiceSpacing = getResources()
.getDimensionPixelSize(R.dimen.chooser_service_spacing);
@@ -707,6 +682,54 @@ public class ChooserActivity extends ResolverActivity implements
if (DEBUG) {
Log.d(TAG, "System Time Cost is " + systemCost);
}
+
+ mDirectShareShortcutInfoCache = new HashMap<>();
+ }
+
+ private AppPredictor setupAppPredictorForUser(UserHandle userHandle,
+ AppPredictor.Callback appPredictorCallback) {
+ AppPredictor appPredictor = getAppPredictorForDirectShareIfEnabled(userHandle);
+ if (appPredictor == null) {
+ return null;
+ }
+ mDirectShareAppTargetCache = new HashMap<>();
+ appPredictor.registerPredictionUpdates(this.getMainExecutor(), appPredictorCallback);
+ return appPredictor;
+ }
+
+ private AppPredictor.Callback createAppPredictorCallback(
+ ChooserListAdapter chooserListAdapter) {
+ return resultList -> {
+ //TODO(arangelov) Take care of edge case when callback called after swiping tabs
+ if (isFinishing() || isDestroyed()) {
+ return;
+ }
+ if (chooserListAdapter.getCount() == 0) {
+ return;
+ }
+ if (resultList.isEmpty()) {
+ // APS may be disabled, so try querying targets ourselves.
+ //TODO(arangelov) queryDirectShareTargets indirectly uses mIntents.
+ // Investigate implications for work tab.
+ queryDirectShareTargets(chooserListAdapter, true);
+ return;
+ }
+ final List<DisplayResolveInfo> driList =
+ getDisplayResolveInfos(chooserListAdapter);
+ final List<ShortcutManager.ShareShortcutInfo> shareShortcutInfos =
+ new ArrayList<>();
+ for (AppTarget appTarget : resultList) {
+ if (appTarget.getShortcutInfo() == null) {
+ continue;
+ }
+ shareShortcutInfos.add(new ShortcutManager.ShareShortcutInfo(
+ appTarget.getShortcutInfo(),
+ new ComponentName(
+ appTarget.getPackageName(), appTarget.getClassName())));
+ }
+ sendShareShortcutInfoList(shareShortcutInfos, driList, resultList,
+ chooserListAdapter.getUserHandle());
+ };
}
static SharedPreferences getPinnedSharedPrefs(Context context) {
@@ -1260,9 +1283,9 @@ public class ChooserActivity extends ResolverActivity implements
if (mPreviewCoord != null) mPreviewCoord.cancelLoads();
- if (mAppPredictor != null) {
- mAppPredictor.unregisterPredictionUpdates(mAppPredictorCallback);
- mAppPredictor.destroy();
+ mChooserMultiProfilePagerAdapter.getActiveListAdapter().destroyAppPredictor();
+ if (mChooserMultiProfilePagerAdapter.getInactiveListAdapter() != null) {
+ mChooserMultiProfilePagerAdapter.getInactiveListAdapter().destroyAppPredictor();
}
}
@@ -1312,7 +1335,8 @@ public class ChooserActivity extends ResolverActivity implements
mChooserMultiProfilePagerAdapter.getActiveListAdapter().addServiceResults(
/* origTarget */ null,
Lists.newArrayList(mCallerChooserTargets),
- TARGET_TYPE_DEFAULT);
+ TARGET_TYPE_DEFAULT,
+ /* directShareShortcutInfoCache */ null);
}
}
@@ -1541,8 +1565,10 @@ public class ChooserActivity extends ResolverActivity implements
void queryTargetServices(ChooserListAdapter adapter) {
mQueriedTargetServicesTimeMs = System.currentTimeMillis();
- final PackageManager pm = getPackageManager();
- ShortcutManager sm = (ShortcutManager) getSystemService(ShortcutManager.class);
+ Context selectedProfileContext = createContextAsUser(
+ adapter.getUserHandle(), 0 /* flags */);
+ final PackageManager pm = selectedProfileContext.getPackageManager();
+ ShortcutManager sm = selectedProfileContext.getSystemService(ShortcutManager.class);
int targetsToQuery = 0;
for (int i = 0, N = adapter.getDisplayResolveInfoCount(); i < N; i++) {
@@ -1565,10 +1591,13 @@ public class ChooserActivity extends ResolverActivity implements
final ComponentName serviceComponent = new ComponentName(
ai.packageName, serviceName);
- if (mServicesRequested.contains(serviceComponent)) {
+ UserHandle userHandle = adapter.getUserHandle();
+ Pair<ComponentName, UserHandle> requestedItem =
+ new Pair<>(serviceComponent, userHandle);
+ if (mServicesRequested.contains(requestedItem)) {
continue;
}
- mServicesRequested.add(serviceComponent);
+ mServicesRequested.add(requestedItem);
final Intent serviceIntent = new Intent(ChooserTargetService.SERVICE_INTERFACE)
.setComponent(serviceComponent);
@@ -1596,13 +1625,14 @@ public class ChooserActivity extends ResolverActivity implements
}
final ChooserTargetServiceConnection conn =
- new ChooserTargetServiceConnection(this, dri);
+ new ChooserTargetServiceConnection(this, dri,
+ adapter.getUserHandle());
- // Explicitly specify Process.myUserHandle instead of calling bindService
+ // Explicitly specify the user handle instead of calling bindService
// to avoid the warning from calling from the system process without an explicit
// user handle
if (bindServiceAsUser(serviceIntent, conn, BIND_AUTO_CREATE | BIND_NOT_FOREGROUND,
- Process.myUserHandle())) {
+ adapter.getUserHandle())) {
if (DEBUG) {
Log.d(TAG, "Binding service connection for target " + dri
+ " intent " + serviceIntent);
@@ -1684,8 +1714,9 @@ public class ChooserActivity extends ResolverActivity implements
private void queryDirectShareTargets(
ChooserListAdapter adapter, boolean skipAppPredictionService) {
mQueriedSharingShortcutsTimeMs = System.currentTimeMillis();
+ UserHandle userHandle = adapter.getUserHandle();
if (!skipAppPredictionService) {
- AppPredictor appPredictor = getAppPredictorForDirectShareIfEnabled();
+ AppPredictor appPredictor = getAppPredictorForDirectShareIfEnabled(userHandle);
if (appPredictor != null) {
appPredictor.requestPredictionUpdate();
return;
@@ -1700,17 +1731,18 @@ public class ChooserActivity extends ResolverActivity implements
final List<DisplayResolveInfo> driList = getDisplayResolveInfos(adapter);
AsyncTask.execute(() -> {
- //TODO(arangelov) use the selected probile tab's ShortcutManager
- ShortcutManager sm = (ShortcutManager) getSystemService(Context.SHORTCUT_SERVICE);
+ Context selectedProfileContext = createContextAsUser(userHandle, 0 /* flags */);
+ ShortcutManager sm = (ShortcutManager) selectedProfileContext
+ .getSystemService(Context.SHORTCUT_SERVICE);
List<ShortcutManager.ShareShortcutInfo> resultList = sm.getShareTargets(filter);
- sendShareShortcutInfoList(resultList, driList, null);
+ sendShareShortcutInfoList(resultList, driList, null, userHandle);
});
}
private void sendShareShortcutInfoList(
List<ShortcutManager.ShareShortcutInfo> resultList,
List<DisplayResolveInfo> driList,
- @Nullable List<AppTarget> appTargets) {
+ @Nullable List<AppTarget> appTargets, UserHandle userHandle) {
if (appTargets != null && appTargets.size() != resultList.size()) {
throw new RuntimeException("resultList and appTargets must have the same size."
+ " resultList.size()=" + resultList.size()
@@ -1749,9 +1781,11 @@ public class ChooserActivity extends ResolverActivity implements
List<ChooserTarget> chooserTargets = convertToChooserTarget(
matchingShortcuts, resultList, appTargets, shortcutType);
+
+
final Message msg = Message.obtain();
msg.what = ChooserHandler.SHORTCUT_MANAGER_SHARE_TARGET_RESULT;
- msg.obj = new ServiceResultInfo(driList.get(i), chooserTargets, null);
+ msg.obj = new ServiceResultInfo(driList.get(i), chooserTargets, null, userHandle);
msg.arg1 = shortcutType;
mChooserHandler.sendMessage(msg);
resultMessageSent = true;
@@ -1842,6 +1876,9 @@ public class ChooserActivity extends ResolverActivity implements
mDirectShareAppTargetCache.put(chooserTarget,
allAppTargets.get(indexInAllShortcuts));
}
+ if (mDirectShareShortcutInfoCache != null) {
+ mDirectShareShortcutInfoCache.put(chooserTarget, shortcutInfo);
+ }
}
// Sort ChooserTargets by score in descending order
Comparator<ChooserTarget> byScore =
@@ -1915,7 +1952,8 @@ public class ChooserActivity extends ResolverActivity implements
}
private void sendClickToAppPredictor(TargetInfo targetInfo) {
- AppPredictor directShareAppPredictor = getAppPredictorForDirectShareIfEnabled();
+ AppPredictor directShareAppPredictor = getAppPredictorForDirectShareIfEnabled(
+ mChooserMultiProfilePagerAdapter.getCurrentUserHandle());
if (directShareAppPredictor == null) {
return;
}
@@ -1937,34 +1975,54 @@ public class ChooserActivity extends ResolverActivity implements
}
@Nullable
- private AppPredictor getAppPredictor() {
+ private AppPredictor createAppPredictor(UserHandle userHandle) {
if (!mIsAppPredictorComponentAvailable) {
return null;
}
- if (mAppPredictor == null) {
- final IntentFilter filter = getTargetIntentFilter();
- Bundle extras = new Bundle();
- extras.putParcelable(APP_PREDICTION_INTENT_FILTER_KEY, filter);
- AppPredictionContext appPredictionContext = new AppPredictionContext.Builder(this)
- .setUiSurface(APP_PREDICTION_SHARE_UI_SURFACE)
- .setPredictedTargetCount(APP_PREDICTION_SHARE_TARGET_QUERY_PACKAGE_LIMIT)
- .setExtras(extras)
- .build();
- AppPredictionManager appPredictionManager
- = getSystemService(AppPredictionManager.class);
- mAppPredictor = appPredictionManager.createAppPredictionSession(appPredictionContext);
+
+ if (getPersonalProfileUserHandle() == userHandle) {
+ if (mPersonalAppPredictor != null) {
+ return mPersonalAppPredictor;
+ }
+ } else {
+ if (mWorkAppPredictor != null) {
+ return mWorkAppPredictor;
+ }
+ }
+
+ // TODO(b/148230574): Currently AppPredictor fetches only the same-profile app targets.
+ // Make AppPredictor work cross-profile.
+ Context contextAsUser = createContextAsUser(userHandle, 0 /* flags */);
+ final IntentFilter filter = getTargetIntentFilter();
+ Bundle extras = new Bundle();
+ extras.putParcelable(APP_PREDICTION_INTENT_FILTER_KEY, filter);
+ AppPredictionContext appPredictionContext = new AppPredictionContext.Builder(contextAsUser)
+ .setUiSurface(APP_PREDICTION_SHARE_UI_SURFACE)
+ .setPredictedTargetCount(APP_PREDICTION_SHARE_TARGET_QUERY_PACKAGE_LIMIT)
+ .setExtras(extras)
+ .build();
+ AppPredictionManager appPredictionManager =
+ contextAsUser
+ .getSystemService(AppPredictionManager.class);
+ AppPredictor appPredictionSession = appPredictionManager.createAppPredictionSession(
+ appPredictionContext);
+ if (getPersonalProfileUserHandle() == userHandle) {
+ mPersonalAppPredictor = appPredictionSession;
+ } else {
+ mWorkAppPredictor = appPredictionSession;
}
- return mAppPredictor;
+ return appPredictionSession;
}
/**
* This will return an app predictor if it is enabled for direct share sorting
* and if one exists. Otherwise, it returns null.
+ * @param userHandle
*/
@Nullable
- private AppPredictor getAppPredictorForDirectShareIfEnabled() {
+ private AppPredictor getAppPredictorForDirectShareIfEnabled(UserHandle userHandle) {
return ChooserFlags.USE_PREDICTION_MANAGER_FOR_DIRECT_TARGETS
- && !ActivityManager.isLowRamDeviceStatic() ? getAppPredictor() : null;
+ && !ActivityManager.isLowRamDeviceStatic() ? createAppPredictor(userHandle) : null;
}
/**
@@ -1972,8 +2030,8 @@ public class ChooserActivity extends ResolverActivity implements
* and if one exists. Otherwise, it returns null.
*/
@Nullable
- private AppPredictor getAppPredictorForShareActivitesIfEnabled() {
- return USE_PREDICTION_MANAGER_FOR_SHARE_ACTIVITIES ? getAppPredictor() : null;
+ private AppPredictor getAppPredictorForShareActivitiesIfEnabled(UserHandle userHandle) {
+ return USE_PREDICTION_MANAGER_FOR_SHARE_ACTIVITIES ? createAppPredictor(userHandle) : null;
}
void onRefinementResult(TargetInfo selectedTarget, Intent matchingIntent) {
@@ -2016,12 +2074,13 @@ public class ChooserActivity extends ResolverActivity implements
return false;
}
- void filterServiceTargets(String packageName, List<ChooserTarget> targets) {
+ void filterServiceTargets(Context contextAsUser, String packageName,
+ List<ChooserTarget> targets) {
if (targets == null) {
return;
}
- final PackageManager pm = getPackageManager();
+ final PackageManager pm = contextAsUser.getPackageManager();
for (int i = targets.size() - 1; i >= 0; i--) {
final ChooserTarget target = targets.get(i);
final ComponentName targetName = target.getComponentName();
@@ -2104,19 +2163,24 @@ public class ChooserActivity extends ResolverActivity implements
public ChooserGridAdapter createChooserGridAdapter(Context context,
List<Intent> payloadIntents, Intent[] initialIntents, List<ResolveInfo> rList,
boolean filterLastUsed, boolean useLayoutForBrowsables, UserHandle userHandle) {
- return new ChooserGridAdapter(
- new ChooserListAdapter(context, payloadIntents, initialIntents, rList,
- filterLastUsed, createListController(userHandle), useLayoutForBrowsables,
- this, this));
+ ChooserListAdapter chooserListAdapter = new ChooserListAdapter(context, payloadIntents,
+ initialIntents, rList,
+ filterLastUsed, createListController(userHandle), useLayoutForBrowsables,
+ this, this);
+ AppPredictor.Callback appPredictorCallback = createAppPredictorCallback(chooserListAdapter);
+ AppPredictor appPredictor = setupAppPredictorForUser(userHandle, appPredictorCallback);
+ chooserListAdapter.setAppPredictor(appPredictor);
+ chooserListAdapter.setAppPredictorCallback(appPredictorCallback);
+ return new ChooserGridAdapter(chooserListAdapter);
}
@VisibleForTesting
protected ResolverListController createListController(UserHandle userHandle) {
- AppPredictor appPredictor = getAppPredictorForShareActivitesIfEnabled();
+ AppPredictor appPredictor = getAppPredictorForShareActivitiesIfEnabled(userHandle);
AbstractResolverComparator resolverComparator;
if (appPredictor != null) {
resolverComparator = new AppPredictionServiceResolverComparator(this, getTargetIntent(),
- getReferrerPackageName(), appPredictor, getUser());
+ getReferrerPackageName(), appPredictor, userHandle);
} else {
resolverComparator =
new ResolverRankerServiceResolverComparator(this, getTargetIntent(),
@@ -2297,9 +2361,11 @@ public class ChooserActivity extends ResolverActivity implements
}
@Override // ChooserListCommunicator
- public void sendListViewUpdateMessage() {
- mChooserHandler.sendEmptyMessageDelayed(ChooserHandler.LIST_VIEW_UPDATE_MESSAGE,
- LIST_VIEW_UPDATE_INTERVAL_IN_MILLIS);
+ public void sendListViewUpdateMessage(UserHandle userHandle) {
+ Message msg = Message.obtain();
+ msg.what = ChooserHandler.LIST_VIEW_UPDATE_MESSAGE;
+ msg.obj = userHandle;
+ mChooserHandler.sendMessageDelayed(msg, LIST_VIEW_UPDATE_INTERVAL_IN_MILLIS);
}
@Override
@@ -3158,6 +3224,7 @@ public class ChooserActivity extends ResolverActivity implements
private DisplayResolveInfo mOriginalTarget;
private ComponentName mConnectedComponent;
private ChooserActivity mChooserActivity;
+ private final UserHandle mUserHandle;
private final Object mLock = new Object();
private final IChooserTargetResult mChooserTargetResult = new IChooserTargetResult.Stub() {
@@ -3169,21 +3236,24 @@ public class ChooserActivity extends ResolverActivity implements
+ mConnectedComponent + "; ignoring...");
return;
}
- mChooserActivity.filterServiceTargets(
+ Context contextAsUser =
+ mChooserActivity.createContextAsUser(mUserHandle, 0 /* flags */);
+ mChooserActivity.filterServiceTargets(contextAsUser,
mOriginalTarget.getResolveInfo().activityInfo.packageName, targets);
final Message msg = Message.obtain();
msg.what = ChooserHandler.CHOOSER_TARGET_SERVICE_RESULT;
msg.obj = new ServiceResultInfo(mOriginalTarget, targets,
- ChooserTargetServiceConnection.this);
+ ChooserTargetServiceConnection.this, mUserHandle);
mChooserActivity.mChooserHandler.sendMessage(msg);
}
}
};
public ChooserTargetServiceConnection(ChooserActivity chooserActivity,
- DisplayResolveInfo dri) {
+ DisplayResolveInfo dri, UserHandle userHandle) {
mChooserActivity = chooserActivity;
mOriginalTarget = dri;
+ mUserHandle = userHandle;
}
@Override
@@ -3249,12 +3319,14 @@ public class ChooserActivity extends ResolverActivity implements
public final DisplayResolveInfo originalTarget;
public final List<ChooserTarget> resultTargets;
public final ChooserTargetServiceConnection connection;
+ public final UserHandle userHandle;
public ServiceResultInfo(DisplayResolveInfo ot, List<ChooserTarget> rt,
- ChooserTargetServiceConnection c) {
+ ChooserTargetServiceConnection c, UserHandle userHandle) {
originalTarget = ot;
resultTargets = rt;
connection = c;
+ this.userHandle = userHandle;
}
}
diff --git a/core/java/com/android/internal/app/ChooserListAdapter.java b/core/java/com/android/internal/app/ChooserListAdapter.java
index 6ff844a2eaae..ca3b7e7a7837 100644
--- a/core/java/com/android/internal/app/ChooserListAdapter.java
+++ b/core/java/com/android/internal/app/ChooserListAdapter.java
@@ -19,7 +19,9 @@ package com.android.internal.app;
import static com.android.internal.app.ChooserActivity.TARGET_TYPE_SHORTCUTS_FROM_PREDICTION_SERVICE;
import static com.android.internal.app.ChooserActivity.TARGET_TYPE_SHORTCUTS_FROM_SHORTCUT_MANAGER;
+import android.annotation.Nullable;
import android.app.ActivityManager;
+import android.app.prediction.AppPredictor;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -27,7 +29,9 @@ import android.content.pm.ActivityInfo;
import android.content.pm.LabeledIntent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
+import android.content.pm.ShortcutInfo;
import android.os.AsyncTask;
+import android.os.UserHandle;
import android.os.UserManager;
import android.service.chooser.ChooserTarget;
import android.util.Log;
@@ -90,6 +94,8 @@ public class ChooserListAdapter extends ResolverListAdapter {
// Sorted list of DisplayResolveInfos for the alphabetical app section.
private List<DisplayResolveInfo> mSortedList = new ArrayList<>();
+ private AppPredictor mAppPredictor;
+ private AppPredictor.Callback mAppPredictorCallback;
public ChooserListAdapter(Context context, List<Intent> payloadIntents,
Intent[] initialIntents, List<ResolveInfo> rList,
@@ -160,6 +166,10 @@ public class ChooserListAdapter extends ResolverListAdapter {
}
}
+ AppPredictor getAppPredictor() {
+ return mAppPredictor;
+ }
+
@Override
public void handlePackagesChanged() {
if (DEBUG) {
@@ -173,7 +183,7 @@ public class ChooserListAdapter extends ResolverListAdapter {
@Override
public void notifyDataSetChanged() {
if (!mListViewDataChanged) {
- mChooserListCommunicator.sendListViewUpdateMessage();
+ mChooserListCommunicator.sendListViewUpdateMessage(getUserHandle());
mListViewDataChanged = true;
}
}
@@ -383,7 +393,8 @@ public class ChooserListAdapter extends ResolverListAdapter {
* if score is too low.
*/
public void addServiceResults(DisplayResolveInfo origTarget, List<ChooserTarget> targets,
- @ChooserActivity.ShareTargetType int targetType) {
+ @ChooserActivity.ShareTargetType int targetType,
+ Map<ChooserTarget, ShortcutInfo> directShareToShortcutInfos) {
if (DEBUG) {
Log.d(TAG, "addServiceResults " + origTarget + ", " + targets.size()
+ " targets");
@@ -412,8 +423,11 @@ public class ChooserListAdapter extends ResolverListAdapter {
// This incents ChooserTargetServices to define what's truly better.
targetScore = lastScore * 0.95f;
}
- boolean isInserted = insertServiceTarget(new SelectableTargetInfo(
- mContext, origTarget, target, targetScore, mSelectableTargetInfoComunicator));
+ UserHandle userHandle = getUserHandle();
+ Context contextAsUser = mContext.createContextAsUser(userHandle, 0 /* flags */);
+ boolean isInserted = insertServiceTarget(new SelectableTargetInfo(contextAsUser,
+ origTarget, target, targetScore, mSelectableTargetInfoComunicator,
+ (isShortcutResult ? directShareToShortcutInfos.get(target) : null)));
if (isInserted && isShortcutResult) {
mNumShortcutResults++;
@@ -547,6 +561,21 @@ public class ChooserListAdapter extends ResolverListAdapter {
};
}
+ public void setAppPredictor(AppPredictor appPredictor) {
+ mAppPredictor = appPredictor;
+ }
+
+ public void setAppPredictorCallback(AppPredictor.Callback appPredictorCallback) {
+ mAppPredictorCallback = appPredictorCallback;
+ }
+
+ public void destroyAppPredictor() {
+ if (getAppPredictor() != null) {
+ getAppPredictor().unregisterPredictionUpdates(mAppPredictorCallback);
+ getAppPredictor().destroy();
+ }
+ }
+
/**
* Necessary methods to communicate between {@link ChooserListAdapter}
* and {@link ChooserActivity}.
@@ -555,7 +584,7 @@ public class ChooserListAdapter extends ResolverListAdapter {
int getMaxRankedTargets();
- void sendListViewUpdateMessage();
+ void sendListViewUpdateMessage(UserHandle userHandle);
boolean isSendAction(Intent targetIntent);
}
diff --git a/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java b/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java
index 1c52d0e8f9f1..663e0255feb9 100644
--- a/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java
+++ b/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java
@@ -18,6 +18,7 @@ package com.android.internal.app;
import android.annotation.Nullable;
import android.content.Context;
+import android.os.UserHandle;
import android.view.LayoutInflater;
import android.view.ViewGroup;
@@ -84,6 +85,18 @@ public class ChooserMultiProfilePagerAdapter extends AbstractMultiProfilePagerAd
}
@Override
+ @Nullable
+ ChooserListAdapter getListAdapterForUserHandle(UserHandle userHandle) {
+ if (getActiveListAdapter().getUserHandle() == userHandle) {
+ return getActiveListAdapter();
+ } else if (getInactiveListAdapter() != null
+ && getInactiveListAdapter().getUserHandle() == userHandle) {
+ return getInactiveListAdapter();
+ }
+ return null;
+ }
+
+ @Override
void setupListAdapter(int pageIndex) {
final RecyclerView recyclerView = getItem(pageIndex).recyclerView;
ChooserActivity.ChooserGridAdapter chooserGridAdapter =
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 68d6e03f654c..30a41d338806 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -543,6 +543,9 @@ public class ResolverActivity extends Activity implements
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
mMultiProfilePagerAdapter.getActiveListAdapter().handlePackagesChanged();
+ if (mMultiProfilePagerAdapter.getInactiveListAdapter() != null) {
+ mMultiProfilePagerAdapter.getInactiveListAdapter().handlePackagesChanged();
+ }
if (mSystemWindowInsets != null) {
mResolverDrawerLayout.setPadding(mSystemWindowInsets.left, mSystemWindowInsets.top,
@@ -1101,6 +1104,9 @@ public class ResolverActivity extends Activity implements
}
if (target != null) {
+ if (intent != null) {
+ intent.fixUris(UserHandle.myUserId());
+ }
safelyStartActivity(target);
// Rely on the ActivityManager to pop up a dialog regarding app suspension
diff --git a/core/java/com/android/internal/app/ResolverListAdapter.java b/core/java/com/android/internal/app/ResolverListAdapter.java
index d227ec5c1b38..405112d99fe7 100644
--- a/core/java/com/android/internal/app/ResolverListAdapter.java
+++ b/core/java/com/android/internal/app/ResolverListAdapter.java
@@ -407,6 +407,7 @@ public class ResolverListAdapter extends BaseAdapter {
// We assume that at this point we've already filtered out the only intent for a different
// targetUserId which we're going to use.
private void addResolveInfo(DisplayResolveInfo dri) {
+ // TODO(arangelov): Is that UserHandle.USER_CURRENT check okay?
if (dri != null && dri.getResolveInfo() != null
&& dri.getResolveInfo().targetUserId == UserHandle.USER_CURRENT) {
// Checks if this info is already listed in display.
@@ -575,7 +576,7 @@ public class ResolverListAdapter extends BaseAdapter {
Drawable loadIconForResolveInfo(ResolveInfo ri) {
// Load icons based on the current process. If in work profile icons should be badged.
- return makePresentationGetter(ri).getIcon(mResolverListController.getUserHandle());
+ return makePresentationGetter(ri).getIcon(getUserHandle());
}
void loadFilteredItemIconTaskAsync(@NonNull ImageView iconView) {
@@ -585,6 +586,10 @@ public class ResolverListAdapter extends BaseAdapter {
}
}
+ UserHandle getUserHandle() {
+ return mResolverListController.getUserHandle();
+ }
+
/**
* Necessary methods to communicate between {@link ResolverListAdapter}
* and {@link ResolverActivity}.
diff --git a/core/java/com/android/internal/app/ResolverMultiProfilePagerAdapter.java b/core/java/com/android/internal/app/ResolverMultiProfilePagerAdapter.java
index 567ed74670bf..9d3c6c9ad8b1 100644
--- a/core/java/com/android/internal/app/ResolverMultiProfilePagerAdapter.java
+++ b/core/java/com/android/internal/app/ResolverMultiProfilePagerAdapter.java
@@ -18,6 +18,7 @@ package com.android.internal.app;
import android.annotation.Nullable;
import android.content.Context;
+import android.os.UserHandle;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import android.widget.ListView;
@@ -88,6 +89,18 @@ public class ResolverMultiProfilePagerAdapter extends AbstractMultiProfilePagerA
}
@Override
+ @Nullable
+ ResolverListAdapter getListAdapterForUserHandle(UserHandle userHandle) {
+ if (getActiveListAdapter().getUserHandle() == userHandle) {
+ return getActiveListAdapter();
+ } else if (getInactiveListAdapter() != null
+ && getInactiveListAdapter().getUserHandle() == userHandle) {
+ return getInactiveListAdapter();
+ }
+ return null;
+ }
+
+ @Override
@VisibleForTesting
public ResolverListAdapter getActiveListAdapter() {
return getAdapterForIndex(getCurrentPage());
diff --git a/core/java/com/android/internal/app/SuspendedAppActivity.java b/core/java/com/android/internal/app/SuspendedAppActivity.java
index c610ac4503c9..0589baa76b8a 100644
--- a/core/java/com/android/internal/app/SuspendedAppActivity.java
+++ b/core/java/com/android/internal/app/SuspendedAppActivity.java
@@ -18,23 +18,31 @@ package com.android.internal.app;
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
+import static android.content.pm.SuspendDialogInfo.BUTTON_ACTION_MORE_DETAILS;
+import static android.content.pm.SuspendDialogInfo.BUTTON_ACTION_UNSUSPEND;
import static android.content.res.Resources.ID_NULL;
import android.Manifest;
+import android.annotation.Nullable;
import android.app.AlertDialog;
+import android.app.AppGlobals;
import android.content.DialogInterface;
import android.content.Intent;
+import android.content.IntentSender;
+import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.SuspendDialogInfo;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
+import android.os.RemoteException;
import android.os.UserHandle;
import android.util.Slog;
import android.view.WindowManager;
import com.android.internal.R;
+import com.android.internal.util.ArrayUtils;
public class SuspendedAppActivity extends AlertActivity
implements DialogInterface.OnClickListener {
@@ -46,8 +54,13 @@ public class SuspendedAppActivity extends AlertActivity
PACKAGE_NAME + ".extra.SUSPENDING_PACKAGE";
public static final String EXTRA_DIALOG_INFO = PACKAGE_NAME + ".extra.DIALOG_INFO";
public static final String EXTRA_ACTIVITY_OPTIONS = PACKAGE_NAME + ".extra.ACTIVITY_OPTIONS";
+ public static final String EXTRA_UNSUSPEND_INTENT = PACKAGE_NAME + ".extra.UNSUSPEND_INTENT";
private Intent mMoreDetailsIntent;
+ private IntentSender mOnUnsuspend;
+ private String mSuspendedPackage;
+ private String mSuspendingPackage;
+ private int mNeutralButtonAction;
private int mUserId;
private PackageManager mPm;
private Resources mSuspendingAppResources;
@@ -63,16 +76,15 @@ public class SuspendedAppActivity extends AlertActivity
return packageName;
}
- private Intent getMoreDetailsActivity(String suspendingPackage, String suspendedPackage,
- int userId) {
+ private Intent getMoreDetailsActivity() {
final Intent moreDetailsIntent = new Intent(Intent.ACTION_SHOW_SUSPENDED_APP_DETAILS)
- .setPackage(suspendingPackage);
+ .setPackage(mSuspendingPackage);
final String requiredPermission = Manifest.permission.SEND_SHOW_SUSPENDED_APP_DETAILS;
final ResolveInfo resolvedInfo = mPm.resolveActivityAsUser(moreDetailsIntent,
- MATCH_DIRECT_BOOT_UNAWARE | MATCH_DIRECT_BOOT_AWARE, userId);
+ MATCH_DIRECT_BOOT_UNAWARE | MATCH_DIRECT_BOOT_AWARE, mUserId);
if (resolvedInfo != null && resolvedInfo.activityInfo != null
&& requiredPermission.equals(resolvedInfo.activityInfo.permission)) {
- moreDetailsIntent.putExtra(Intent.EXTRA_PACKAGE_NAME, suspendedPackage)
+ moreDetailsIntent.putExtra(Intent.EXTRA_PACKAGE_NAME, mSuspendedPackage)
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
return moreDetailsIntent;
}
@@ -105,8 +117,8 @@ public class SuspendedAppActivity extends AlertActivity
return getString(R.string.app_suspended_title);
}
- private String resolveDialogMessage(String suspendingPkg, String suspendedPkg) {
- final CharSequence suspendedAppLabel = getAppLabel(suspendedPkg);
+ private String resolveDialogMessage() {
+ final CharSequence suspendedAppLabel = getAppLabel(mSuspendedPackage);
if (mSuppliedDialogInfo != null) {
final int messageId = mSuppliedDialogInfo.getDialogMessageResId();
final String message = mSuppliedDialogInfo.getDialogMessage();
@@ -122,10 +134,30 @@ public class SuspendedAppActivity extends AlertActivity
}
}
return getString(R.string.app_suspended_default_message, suspendedAppLabel,
- getAppLabel(suspendingPkg));
+ getAppLabel(mSuspendingPackage));
}
+ /**
+ * Returns a text to be displayed on the neutral button or {@code null} if the button should
+ * not be shown.
+ */
+ @Nullable
private String resolveNeutralButtonText() {
+ final int defaultButtonTextId;
+ switch (mNeutralButtonAction) {
+ case BUTTON_ACTION_MORE_DETAILS:
+ if (mMoreDetailsIntent == null) {
+ return null;
+ }
+ defaultButtonTextId = R.string.app_suspended_more_details;
+ break;
+ case BUTTON_ACTION_UNSUSPEND:
+ defaultButtonTextId = R.string.app_suspended_unsuspend_message;
+ break;
+ default:
+ Slog.w(TAG, "Unknown neutral button action: " + mNeutralButtonAction);
+ return null;
+ }
final int buttonTextId = (mSuppliedDialogInfo != null)
? mSuppliedDialogInfo.getNeutralButtonTextResId() : ID_NULL;
if (buttonTextId != ID_NULL && mSuspendingAppResources != null) {
@@ -135,7 +167,7 @@ public class SuspendedAppActivity extends AlertActivity
Slog.e(TAG, "Could not resolve string resource id " + buttonTextId);
}
}
- return getString(R.string.app_suspended_more_details);
+ return getString(defaultButtonTextId);
}
@Override
@@ -152,27 +184,29 @@ public class SuspendedAppActivity extends AlertActivity
finish();
return;
}
- final String suspendedPackage = intent.getStringExtra(EXTRA_SUSPENDED_PACKAGE);
- final String suspendingPackage = intent.getStringExtra(EXTRA_SUSPENDING_PACKAGE);
+ mSuspendedPackage = intent.getStringExtra(EXTRA_SUSPENDED_PACKAGE);
+ mSuspendingPackage = intent.getStringExtra(EXTRA_SUSPENDING_PACKAGE);
mSuppliedDialogInfo = intent.getParcelableExtra(EXTRA_DIALOG_INFO);
+ mOnUnsuspend = intent.getParcelableExtra(EXTRA_UNSUSPEND_INTENT);
if (mSuppliedDialogInfo != null) {
try {
- mSuspendingAppResources = mPm.getResourcesForApplicationAsUser(suspendingPackage,
+ mSuspendingAppResources = mPm.getResourcesForApplicationAsUser(mSuspendingPackage,
mUserId);
} catch (PackageManager.NameNotFoundException ne) {
- Slog.e(TAG, "Could not find resources for " + suspendingPackage, ne);
+ Slog.e(TAG, "Could not find resources for " + mSuspendingPackage, ne);
}
}
+ mNeutralButtonAction = (mSuppliedDialogInfo != null)
+ ? mSuppliedDialogInfo.getNeutralButtonAction() : BUTTON_ACTION_MORE_DETAILS;
+ mMoreDetailsIntent = (mNeutralButtonAction == BUTTON_ACTION_MORE_DETAILS)
+ ? getMoreDetailsActivity() : null;
final AlertController.AlertParams ap = mAlertParams;
ap.mIcon = resolveIcon();
ap.mTitle = resolveTitle();
- ap.mMessage = resolveDialogMessage(suspendingPackage, suspendedPackage);
+ ap.mMessage = resolveDialogMessage();
ap.mPositiveButtonText = getString(android.R.string.ok);
- mMoreDetailsIntent = getMoreDetailsActivity(suspendingPackage, suspendedPackage, mUserId);
- if (mMoreDetailsIntent != null) {
- ap.mNeutralButtonText = resolveNeutralButtonText();
- }
+ ap.mNeutralButtonText = resolveNeutralButtonText();
ap.mPositiveButtonListener = ap.mNeutralButtonListener = this;
setupAlert();
}
@@ -181,21 +215,62 @@ public class SuspendedAppActivity extends AlertActivity
public void onClick(DialogInterface dialog, int which) {
switch (which) {
case AlertDialog.BUTTON_NEUTRAL:
- startActivityAsUser(mMoreDetailsIntent, mOptions, UserHandle.of(mUserId));
- Slog.i(TAG, "Started activity: " + mMoreDetailsIntent.getAction()
- + " in user " + mUserId);
+ switch (mNeutralButtonAction) {
+ case BUTTON_ACTION_MORE_DETAILS:
+ if (mMoreDetailsIntent != null) {
+ startActivityAsUser(mMoreDetailsIntent, mOptions,
+ UserHandle.of(mUserId));
+ } else {
+ Slog.wtf(TAG, "Neutral button should not have existed!");
+ }
+ break;
+ case BUTTON_ACTION_UNSUSPEND:
+ final IPackageManager ipm = AppGlobals.getPackageManager();
+ try {
+ final String[] errored = ipm.setPackagesSuspendedAsUser(
+ new String[]{mSuspendedPackage}, false, null, null, null,
+ mSuspendingPackage, mUserId);
+ if (ArrayUtils.contains(errored, mSuspendedPackage)) {
+ Slog.e(TAG, "Could not unsuspend " + mSuspendedPackage);
+ break;
+ }
+ } catch (RemoteException re) {
+ Slog.e(TAG, "Can't talk to system process", re);
+ break;
+ }
+ final Intent reportUnsuspend = new Intent()
+ .setAction(Intent.ACTION_PACKAGE_UNSUSPENDED_MANUALLY)
+ .putExtra(Intent.EXTRA_PACKAGE_NAME, mSuspendedPackage)
+ .setPackage(mSuspendingPackage)
+ .addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
+ sendBroadcastAsUser(reportUnsuspend, UserHandle.of(mUserId));
+
+ if (mOnUnsuspend != null) {
+ try {
+ mOnUnsuspend.sendIntent(this, 0, null, null, null);
+ } catch (IntentSender.SendIntentException e) {
+ Slog.e(TAG, "Error while starting intent " + mOnUnsuspend, e);
+ }
+ }
+ break;
+ default:
+ Slog.e(TAG, "Unexpected action on neutral button: " + mNeutralButtonAction);
+ break;
+ }
break;
}
finish();
}
public static Intent createSuspendedAppInterceptIntent(String suspendedPackage,
- String suspendingPackage, SuspendDialogInfo dialogInfo, Bundle options, int userId) {
+ String suspendingPackage, SuspendDialogInfo dialogInfo, Bundle options,
+ IntentSender onUnsuspend, int userId) {
return new Intent()
.setClassName("android", SuspendedAppActivity.class.getName())
.putExtra(EXTRA_SUSPENDED_PACKAGE, suspendedPackage)
.putExtra(EXTRA_DIALOG_INFO, dialogInfo)
.putExtra(EXTRA_SUSPENDING_PACKAGE, suspendingPackage)
+ .putExtra(EXTRA_UNSUSPEND_INTENT, onUnsuspend)
.putExtra(EXTRA_ACTIVITY_OPTIONS, options)
.putExtra(Intent.EXTRA_USER_ID, userId)
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
diff --git a/core/java/com/android/internal/app/chooser/SelectableTargetInfo.java b/core/java/com/android/internal/app/chooser/SelectableTargetInfo.java
index 86a0d10cf67e..246a07d3d0fe 100644
--- a/core/java/com/android/internal/app/chooser/SelectableTargetInfo.java
+++ b/core/java/com/android/internal/app/chooser/SelectableTargetInfo.java
@@ -16,6 +16,7 @@
package com.android.internal.app.chooser;
+import android.annotation.Nullable;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
@@ -70,7 +71,8 @@ public final class SelectableTargetInfo implements ChooserTargetInfo {
public SelectableTargetInfo(Context context, DisplayResolveInfo sourceInfo,
ChooserTarget chooserTarget,
- float modifiedScore, SelectableTargetInfoCommunicator selectableTargetInfoComunicator) {
+ float modifiedScore, SelectableTargetInfoCommunicator selectableTargetInfoComunicator,
+ @Nullable ShortcutInfo shortcutInfo) {
mContext = context;
mSourceInfo = sourceInfo;
mChooserTarget = chooserTarget;
@@ -91,7 +93,7 @@ public final class SelectableTargetInfo implements ChooserTargetInfo {
}
}
// TODO(b/121287224): do this in the background thread, and only for selected targets
- mDisplayIcon = getChooserTargetIconDrawable(chooserTarget);
+ mDisplayIcon = getChooserTargetIconDrawable(chooserTarget, shortcutInfo);
if (sourceInfo != null) {
mBackupResolveInfo = null;
@@ -134,34 +136,18 @@ public final class SelectableTargetInfo implements ChooserTargetInfo {
return mIsSuspended;
}
- /**
- * Since ShortcutInfos are returned by ShortcutManager, we can cache the shortcuts and skip
- * the call to LauncherApps#getShortcuts(ShortcutQuery).
- */
- // TODO(121287224): Refactor code to apply the suggestion above
- private Drawable getChooserTargetIconDrawable(ChooserTarget target) {
+ private Drawable getChooserTargetIconDrawable(ChooserTarget target,
+ @Nullable ShortcutInfo shortcutInfo) {
Drawable directShareIcon = null;
// First get the target drawable and associated activity info
final Icon icon = target.getIcon();
if (icon != null) {
directShareIcon = icon.loadDrawable(mContext);
- } else if (ChooserFlags.USE_SHORTCUT_MANAGER_FOR_DIRECT_TARGETS) {
- Bundle extras = target.getIntentExtras();
- if (extras != null && extras.containsKey(Intent.EXTRA_SHORTCUT_ID)) {
- CharSequence shortcutId = extras.getCharSequence(Intent.EXTRA_SHORTCUT_ID);
- LauncherApps launcherApps = (LauncherApps) mContext.getSystemService(
- Context.LAUNCHER_APPS_SERVICE);
- final LauncherApps.ShortcutQuery q = new LauncherApps.ShortcutQuery();
- q.setPackage(target.getComponentName().getPackageName());
- q.setShortcutIds(Arrays.asList(shortcutId.toString()));
- q.setQueryFlags(LauncherApps.ShortcutQuery.FLAG_MATCH_DYNAMIC);
- final List<ShortcutInfo> shortcuts =
- launcherApps.getShortcuts(q, mContext.getUser());
- if (shortcuts != null && shortcuts.size() > 0) {
- directShareIcon = launcherApps.getShortcutIconDrawable(shortcuts.get(0), 0);
- }
- }
+ } else if (ChooserFlags.USE_SHORTCUT_MANAGER_FOR_DIRECT_TARGETS && shortcutInfo != null) {
+ LauncherApps launcherApps = (LauncherApps) mContext.getSystemService(
+ Context.LAUNCHER_APPS_SERVICE);
+ directShareIcon = launcherApps.getShortcutIconDrawable(shortcutInfo, 0);
}
if (directShareIcon == null) return null;
diff --git a/core/java/com/android/internal/net/VpnProfile.java b/core/java/com/android/internal/net/VpnProfile.java
index 4bb012aac769..bbae0273ef4e 100644
--- a/core/java/com/android/internal/net/VpnProfile.java
+++ b/core/java/com/android/internal/net/VpnProfile.java
@@ -16,6 +16,7 @@
package com.android.internal.net;
+import android.annotation.NonNull;
import android.compat.annotation.UnsupportedAppUsage;
import android.net.ProxyInfo;
import android.os.Build;
@@ -23,21 +24,34 @@ import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
+import com.android.internal.annotations.VisibleForTesting;
+
import java.net.InetAddress;
import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
/**
- * Parcel-like entity class for VPN profiles. To keep things simple, all
- * fields are package private. Methods are provided for serialization, so
- * storage can be implemented easily. Two rules are set for this class.
- * First, all fields must be kept non-null. Second, always make a copy
- * using clone() before modifying.
+ * Profile storage class for a platform VPN.
+ *
+ * <p>This class supports both the Legacy VPN, as well as application-configurable platform VPNs
+ * (such as IKEv2/IPsec).
+ *
+ * <p>This class is serialized and deserialized via the {@link #encode()} and {@link #decode()}
+ * functions for persistent storage in the Android Keystore. The encoding is entirely custom, but
+ * must be kept for backward compatibility for devices upgrading between Android versions.
*
* @hide
*/
-public class VpnProfile implements Cloneable, Parcelable {
+public final class VpnProfile implements Cloneable, Parcelable {
private static final String TAG = "VpnProfile";
+ @VisibleForTesting static final String VALUE_DELIMITER = "\0";
+ @VisibleForTesting static final String LIST_DELIMITER = ",";
+
// Match these constants with R.array.vpn_types.
public static final int TYPE_PPTP = 0;
public static final int TYPE_L2TP_IPSEC_PSK = 1;
@@ -45,39 +59,85 @@ public class VpnProfile implements Cloneable, Parcelable {
public static final int TYPE_IPSEC_XAUTH_PSK = 3;
public static final int TYPE_IPSEC_XAUTH_RSA = 4;
public static final int TYPE_IPSEC_HYBRID_RSA = 5;
- public static final int TYPE_MAX = 5;
+ public static final int TYPE_IKEV2_IPSEC_USER_PASS = 6;
+ public static final int TYPE_IKEV2_IPSEC_PSK = 7;
+ public static final int TYPE_IKEV2_IPSEC_RSA = 8;
+ public static final int TYPE_MAX = 8;
// Match these constants with R.array.vpn_proxy_settings.
public static final int PROXY_NONE = 0;
public static final int PROXY_MANUAL = 1;
+ private static final String ENCODED_NULL_PROXY_INFO = "\0\0\0\0";
+
// Entity fields.
@UnsupportedAppUsage
- public final String key; // -1
+ public final String key; // -1
+
@UnsupportedAppUsage
- public String name = ""; // 0
+ public String name = ""; // 0
+
@UnsupportedAppUsage
- public int type = TYPE_PPTP; // 1
+ public int type = TYPE_PPTP; // 1
+
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
- public String server = ""; // 2
+ public String server = ""; // 2
+
@UnsupportedAppUsage
- public String username = ""; // 3
- public String password = ""; // 4
- public String dnsServers = ""; // 5
- public String searchDomains = ""; // 6
- public String routes = ""; // 7
- public boolean mppe = true; // 8
- public String l2tpSecret = ""; // 9
- public String ipsecIdentifier = "";// 10
- public String ipsecSecret = ""; // 11
- public String ipsecUserCert = ""; // 12
- public String ipsecCaCert = ""; // 13
- public String ipsecServerCert = "";// 14
- public ProxyInfo proxy = null; // 15~18
+ public String username = ""; // 3
+ public String password = ""; // 4
+ public String dnsServers = ""; // 5
+ public String searchDomains = ""; // 6
+ public String routes = ""; // 7
+ public boolean mppe = true; // 8
+ public String l2tpSecret = ""; // 9
+ public String ipsecIdentifier = ""; // 10
+
+ /**
+ * The RSA private key or pre-shared key used for authentication.
+ *
+ * <p>If areAuthParamsInline is {@code true}, this String will be either:
+ *
+ * <ul>
+ * <li>If this is an IKEv2 RSA profile: a PKCS#8 encoded {@link java.security.PrivateKey}
+ * <li>If this is an IKEv2 PSK profile: a string value representing the PSK.
+ * </ul>
+ */
+ public String ipsecSecret = ""; // 11
+
+ /**
+ * The RSA certificate to be used for digital signature authentication.
+ *
+ * <p>If areAuthParamsInline is {@code true}, this String will be a pem-encoded {@link
+ * java.security.X509Certificate}
+ */
+ public String ipsecUserCert = ""; // 12
+
+ /**
+ * The RSA certificate that should be used to verify the server's end/target certificate.
+ *
+ * <p>If areAuthParamsInline is {@code true}, this String will be a pem-encoded {@link
+ * java.security.X509Certificate}
+ */
+ public String ipsecCaCert = ""; // 13
+ public String ipsecServerCert = ""; // 14
+ public ProxyInfo proxy = null; // 15~18
+
+ /**
+ * The list of allowable algorithms.
+ *
+ * <p>This list is validated in the setter to ensure that encoding characters (list, value
+ * delimiters) are not present in the algorithm names. See {@link #validateAllowedAlgorithms()}
+ */
+ private List<String> mAllowedAlgorithms = new ArrayList<>(); // 19
+ public boolean isBypassable = false; // 20
+ public boolean isMetered = false; // 21
+ public int maxMtu = 1400; // 22
+ public boolean areAuthParamsInline = false; // 23
// Helper fields.
@UnsupportedAppUsage
- public boolean saveLogin = false;
+ public transient boolean saveLogin = false;
public VpnProfile(String key) {
this.key = key;
@@ -103,6 +163,34 @@ public class VpnProfile implements Cloneable, Parcelable {
ipsecServerCert = in.readString();
saveLogin = in.readInt() != 0;
proxy = in.readParcelable(null);
+ mAllowedAlgorithms = new ArrayList<>();
+ in.readList(mAllowedAlgorithms, null);
+ isBypassable = in.readBoolean();
+ isMetered = in.readBoolean();
+ maxMtu = in.readInt();
+ areAuthParamsInline = in.readBoolean();
+ }
+
+ /**
+ * Retrieves the list of allowed algorithms.
+ *
+ * <p>The contained elements are as listed in {@link IpSecAlgorithm}
+ */
+ public List<String> getAllowedAlgorithms() {
+ return Collections.unmodifiableList(mAllowedAlgorithms);
+ }
+
+ /**
+ * Validates and sets the list of algorithms that can be used for the IPsec transforms.
+ *
+ * @param allowedAlgorithms the list of allowable algorithms, as listed in {@link
+ * IpSecAlgorithm}.
+ * @throws IllegalArgumentException if any delimiters are used in algorithm names. See {@link
+ * #VALUE_DELIMITER} and {@link LIST_DELIMITER}.
+ */
+ public void setAllowedAlgorithms(List<String> allowedAlgorithms) {
+ validateAllowedAlgorithms(allowedAlgorithms);
+ mAllowedAlgorithms = allowedAlgorithms;
}
@Override
@@ -125,8 +213,18 @@ public class VpnProfile implements Cloneable, Parcelable {
out.writeString(ipsecServerCert);
out.writeInt(saveLogin ? 1 : 0);
out.writeParcelable(proxy, flags);
+ out.writeList(mAllowedAlgorithms);
+ out.writeBoolean(isBypassable);
+ out.writeBoolean(isMetered);
+ out.writeInt(maxMtu);
+ out.writeBoolean(areAuthParamsInline);
}
+ /**
+ * Decodes a VpnProfile instance from the encoded byte array.
+ *
+ * <p>See {@link #encode()}
+ */
@UnsupportedAppUsage
public static VpnProfile decode(String key, byte[] value) {
try {
@@ -134,9 +232,11 @@ public class VpnProfile implements Cloneable, Parcelable {
return null;
}
- String[] values = new String(value, StandardCharsets.UTF_8).split("\0", -1);
- // There can be 14 - 19 Bytes in values.length.
- if (values.length < 14 || values.length > 19) {
+ String[] values = new String(value, StandardCharsets.UTF_8).split(VALUE_DELIMITER, -1);
+ // Acceptable numbers of values are:
+ // 14-19: Standard profile, with option for serverCert, proxy
+ // 24: Standard profile with serverCert, proxy and platform-VPN parameters.
+ if ((values.length < 14 || values.length > 19) && values.length != 24) {
return null;
}
@@ -164,13 +264,23 @@ public class VpnProfile implements Cloneable, Parcelable {
String port = (values.length > 16) ? values[16] : "";
String exclList = (values.length > 17) ? values[17] : "";
String pacFileUrl = (values.length > 18) ? values[18] : "";
- if (pacFileUrl.isEmpty()) {
+ if (!host.isEmpty() || !port.isEmpty() || !exclList.isEmpty()) {
profile.proxy = new ProxyInfo(host, port.isEmpty() ?
0 : Integer.parseInt(port), exclList);
- } else {
+ } else if (!pacFileUrl.isEmpty()) {
profile.proxy = new ProxyInfo(pacFileUrl);
}
- } // else profle.proxy = null
+ } // else profile.proxy = null
+
+ // Either all must be present, or none must be.
+ if (values.length >= 24) {
+ profile.mAllowedAlgorithms = Arrays.asList(values[19].split(LIST_DELIMITER));
+ profile.isBypassable = Boolean.parseBoolean(values[20]);
+ profile.isMetered = Boolean.parseBoolean(values[21]);
+ profile.maxMtu = Integer.parseInt(values[22]);
+ profile.areAuthParamsInline = Boolean.parseBoolean(values[23]);
+ }
+
profile.saveLogin = !profile.username.isEmpty() || !profile.password.isEmpty();
return profile;
} catch (Exception e) {
@@ -179,36 +289,52 @@ public class VpnProfile implements Cloneable, Parcelable {
return null;
}
+ /**
+ * Encodes a VpnProfile instance to a byte array for storage.
+ *
+ * <p>See {@link #decode(String, byte[])}
+ */
public byte[] encode() {
StringBuilder builder = new StringBuilder(name);
- builder.append('\0').append(type);
- builder.append('\0').append(server);
- builder.append('\0').append(saveLogin ? username : "");
- builder.append('\0').append(saveLogin ? password : "");
- builder.append('\0').append(dnsServers);
- builder.append('\0').append(searchDomains);
- builder.append('\0').append(routes);
- builder.append('\0').append(mppe);
- builder.append('\0').append(l2tpSecret);
- builder.append('\0').append(ipsecIdentifier);
- builder.append('\0').append(ipsecSecret);
- builder.append('\0').append(ipsecUserCert);
- builder.append('\0').append(ipsecCaCert);
- builder.append('\0').append(ipsecServerCert);
+ builder.append(VALUE_DELIMITER).append(type);
+ builder.append(VALUE_DELIMITER).append(server);
+ builder.append(VALUE_DELIMITER).append(saveLogin ? username : "");
+ builder.append(VALUE_DELIMITER).append(saveLogin ? password : "");
+ builder.append(VALUE_DELIMITER).append(dnsServers);
+ builder.append(VALUE_DELIMITER).append(searchDomains);
+ builder.append(VALUE_DELIMITER).append(routes);
+ builder.append(VALUE_DELIMITER).append(mppe);
+ builder.append(VALUE_DELIMITER).append(l2tpSecret);
+ builder.append(VALUE_DELIMITER).append(ipsecIdentifier);
+ builder.append(VALUE_DELIMITER).append(ipsecSecret);
+ builder.append(VALUE_DELIMITER).append(ipsecUserCert);
+ builder.append(VALUE_DELIMITER).append(ipsecCaCert);
+ builder.append(VALUE_DELIMITER).append(ipsecServerCert);
if (proxy != null) {
- builder.append('\0').append(proxy.getHost() != null ? proxy.getHost() : "");
- builder.append('\0').append(proxy.getPort());
- builder.append('\0').append(proxy.getExclusionListAsString() != null ?
- proxy.getExclusionListAsString() : "");
- builder.append('\0').append(proxy.getPacFileUrl().toString());
+ builder.append(VALUE_DELIMITER).append(proxy.getHost() != null ? proxy.getHost() : "");
+ builder.append(VALUE_DELIMITER).append(proxy.getPort());
+ builder.append(VALUE_DELIMITER)
+ .append(
+ proxy.getExclusionListAsString() != null
+ ? proxy.getExclusionListAsString()
+ : "");
+ builder.append(VALUE_DELIMITER).append(proxy.getPacFileUrl().toString());
+ } else {
+ builder.append(ENCODED_NULL_PROXY_INFO);
}
+
+ builder.append(VALUE_DELIMITER).append(String.join(LIST_DELIMITER, mAllowedAlgorithms));
+ builder.append(VALUE_DELIMITER).append(isBypassable);
+ builder.append(VALUE_DELIMITER).append(isMetered);
+ builder.append(VALUE_DELIMITER).append(maxMtu);
+ builder.append(VALUE_DELIMITER).append(areAuthParamsInline);
+
return builder.toString().getBytes(StandardCharsets.UTF_8);
}
/**
- * Tests if profile is valid for lockdown, which requires IPv4 address for
- * both server and DNS. Server hostnames would require using DNS before
- * connection.
+ * Tests if profile is valid for lockdown, which requires IPv4 address for both server and DNS.
+ * Server hostnames would require using DNS before connection.
*/
public boolean isValidLockdownProfile() {
return isTypeValidForLockdown()
@@ -238,10 +364,7 @@ public class VpnProfile implements Cloneable, Parcelable {
return !TextUtils.isEmpty(dnsServers);
}
- /**
- * Returns {@code true} if all DNS servers have numeric addresses,
- * e.g. 8.8.8.8
- */
+ /** Returns {@code true} if all DNS servers have numeric addresses, e.g. 8.8.8.8 */
public boolean areDnsAddressesNumeric() {
try {
for (String dnsServer : dnsServers.split(" +")) {
@@ -253,6 +376,62 @@ public class VpnProfile implements Cloneable, Parcelable {
return true;
}
+ /**
+ * Validates that the provided list of algorithms does not contain illegal characters.
+ *
+ * @param allowedAlgorithms The list to be validated
+ */
+ public static void validateAllowedAlgorithms(List<String> allowedAlgorithms) {
+ for (final String alg : allowedAlgorithms) {
+ if (alg.contains(VALUE_DELIMITER) || alg.contains(LIST_DELIMITER)) {
+ throw new IllegalArgumentException(
+ "Algorithm contained illegal ('\0' or ',') character");
+ }
+ }
+ }
+
+ /** Generates a hashcode over the VpnProfile. */
+ @Override
+ public int hashCode() {
+ return Objects.hash(
+ key, type, server, username, password, dnsServers, searchDomains, routes, mppe,
+ l2tpSecret, ipsecIdentifier, ipsecSecret, ipsecUserCert, ipsecCaCert, ipsecServerCert,
+ proxy, mAllowedAlgorithms, isBypassable, isMetered, maxMtu, areAuthParamsInline);
+ }
+
+ /** Checks VPN profiles for interior equality. */
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof VpnProfile)) {
+ return false;
+ }
+
+ final VpnProfile other = (VpnProfile) obj;
+ return Objects.equals(key, other.key)
+ && Objects.equals(name, other.name)
+ && type == other.type
+ && Objects.equals(server, other.server)
+ && Objects.equals(username, other.username)
+ && Objects.equals(password, other.password)
+ && Objects.equals(dnsServers, other.dnsServers)
+ && Objects.equals(searchDomains, other.searchDomains)
+ && Objects.equals(routes, other.routes)
+ && mppe == other.mppe
+ && Objects.equals(l2tpSecret, other.l2tpSecret)
+ && Objects.equals(ipsecIdentifier, other.ipsecIdentifier)
+ && Objects.equals(ipsecSecret, other.ipsecSecret)
+ && Objects.equals(ipsecUserCert, other.ipsecUserCert)
+ && Objects.equals(ipsecCaCert, other.ipsecCaCert)
+ && Objects.equals(ipsecServerCert, other.ipsecServerCert)
+ && Objects.equals(proxy, other.proxy)
+ && Objects.equals(mAllowedAlgorithms, other.mAllowedAlgorithms)
+ && isBypassable == other.isBypassable
+ && isMetered == other.isMetered
+ && maxMtu == other.maxMtu
+ && areAuthParamsInline == other.areAuthParamsInline;
+ }
+
+ @NonNull
public static final Creator<VpnProfile> CREATOR = new Creator<VpnProfile>() {
@Override
public VpnProfile createFromParcel(Parcel in) {
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index b3548b8c82f8..580c1f00d788 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -12644,7 +12644,6 @@ public class BatteryStatsImpl extends BatteryStats {
/*@hide */
public WifiBatteryStats getWifiBatteryStats() {
- WifiBatteryStats s = new WifiBatteryStats();
final int which = STATS_SINCE_CHARGED;
final long rawRealTime = SystemClock.elapsedRealtime() * 1000;
final ControllerActivityCounter counter = getWifiControllerActivity();
@@ -12675,24 +12674,16 @@ public class BatteryStatsImpl extends BatteryStats {
for (int i=0; i<NUM_WIFI_SIGNAL_STRENGTH_BINS; i++) {
timeSignalStrengthTimeMs[i] = getWifiSignalStrengthTime(i, rawRealTime, which) / 1000;
}
- s.setLoggingDurationMillis(computeBatteryRealtime(rawRealTime, which) / 1000);
- s.setKernelActiveTimeMillis(getWifiActiveTime(rawRealTime, which) / 1000);
- s.setNumPacketsTx(getNetworkActivityPackets(NETWORK_WIFI_TX_DATA, which));
- s.setNumBytesTx(getNetworkActivityBytes(NETWORK_WIFI_TX_DATA, which));
- s.setNumPacketsRx(getNetworkActivityPackets(NETWORK_WIFI_RX_DATA, which));
- s.setNumBytesRx(getNetworkActivityBytes(NETWORK_WIFI_RX_DATA, which));
- s.setSleepTimeMillis(sleepTimeMs);
- s.setIdleTimeMillis(idleTimeMs);
- s.setRxTimeMillis(rxTimeMs);
- s.setTxTimeMillis(txTimeMs);
- s.setScanTimeMillis(scanTimeMs);
- s.setEnergyConsumedMaMillis(energyConsumedMaMs);
- s.setNumAppScanRequest(numAppScanRequest);
- s.setTimeInStateMillis(timeInStateMs);
- s.setTimeInSupplicantStateMillis(timeInSupplStateMs);
- s.setTimeInRxSignalStrengthLevelMillis(timeSignalStrengthTimeMs);
- s.setMonitoredRailChargeConsumedMaMillis(monitoredRailChargeConsumedMaMs);
- return s;
+ return new WifiBatteryStats(
+ computeBatteryRealtime(rawRealTime, which) / 1000,
+ getWifiActiveTime(rawRealTime, which) / 1000,
+ getNetworkActivityPackets(NETWORK_WIFI_TX_DATA, which),
+ getNetworkActivityBytes(NETWORK_WIFI_TX_DATA, which),
+ getNetworkActivityPackets(NETWORK_WIFI_RX_DATA, which),
+ getNetworkActivityBytes(NETWORK_WIFI_RX_DATA, which),
+ sleepTimeMs, scanTimeMs, idleTimeMs, rxTimeMs, txTimeMs, energyConsumedMaMs,
+ numAppScanRequest, timeInStateMs, timeSignalStrengthTimeMs, timeInSupplStateMs,
+ monitoredRailChargeConsumedMaMs);
}
/*@hide */
diff --git a/core/java/com/android/internal/os/RuntimeInit.java b/core/java/com/android/internal/os/RuntimeInit.java
index 13d0c5c831b6..7adb27cd9e36 100644
--- a/core/java/com/android/internal/os/RuntimeInit.java
+++ b/core/java/com/android/internal/os/RuntimeInit.java
@@ -19,6 +19,8 @@ package com.android.internal.os;
import android.app.ActivityManager;
import android.app.ActivityThread;
import android.app.ApplicationErrorReport;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledAfter;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.type.DefaultMimeMapFactory;
import android.os.Build;
@@ -34,6 +36,7 @@ import android.util.Slog;
import com.android.internal.logging.AndroidConfig;
import com.android.server.NetworkManagementSocketTagger;
+import dalvik.annotation.compat.VersionCodes;
import dalvik.system.RuntimeHooks;
import dalvik.system.ThreadPrioritySetter;
import dalvik.system.VMRuntime;
@@ -64,8 +67,17 @@ public class RuntimeInit {
private static volatile boolean mCrashing = false;
+ /*
+ * Native heap allocations will now have a non-zero tag in the most significant byte.
+ * See {@linktourl https://source.android.com/devices/tech/debug/tagged-pointers}.
+ */
+ @ChangeId
+ @EnabledAfter(targetSdkVersion = VersionCodes.Q)
+ private static final long NATIVE_HEAP_POINTER_TAGGING = 135754954; // This is a bug id.
+
private static final native void nativeFinishInit();
private static final native void nativeSetExitWithoutCleanup(boolean exitWithoutCleanup);
+ private static native void nativeDisableHeapPointerTagging();
private static int Clog_e(String tag, String msg, Throwable tr) {
return Log.printlns(Log.LOG_ID_CRASH, Log.ERROR, tag, msg, tr);
@@ -398,6 +410,20 @@ public class RuntimeInit {
if (DEBUG) Slog.d(TAG, "Leaving RuntimeInit!");
}
+ private static void maybeDisableHeapPointerTagging(long[] disabledCompatChanges) {
+ // Heap tagging needs to be disabled before any additional threads are created, but the
+ // AppCompat framework is not initialized enough at this point.
+ // Check if the change is enabled manually.
+ if (disabledCompatChanges != null) {
+ for (int i = 0; i < disabledCompatChanges.length; i++) {
+ if (disabledCompatChanges[i] == NATIVE_HEAP_POINTER_TAGGING) {
+ nativeDisableHeapPointerTagging();
+ break;
+ }
+ }
+ }
+ }
+
protected static Runnable applicationInit(int targetSdkVersion, long[] disabledCompatChanges,
String[] argv, ClassLoader classLoader) {
// If the application calls System.exit(), terminate the process
@@ -410,6 +436,8 @@ public class RuntimeInit {
VMRuntime.getRuntime().setTargetSdkVersion(targetSdkVersion);
VMRuntime.getRuntime().setDisabledCompatChanges(disabledCompatChanges);
+ maybeDisableHeapPointerTagging(disabledCompatChanges);
+
final Arguments args = new Arguments(argv);
// The end of of the RuntimeInit event (see #zygoteInit).
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index 8412b846b2a6..adb403681b36 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -95,8 +95,6 @@ import android.view.ViewTreeObserver;
import android.view.Window;
import android.view.WindowCallbacks;
import android.view.WindowInsets;
-import android.view.WindowInsets.Side;
-import android.view.WindowInsets.Type;
import android.view.WindowInsetsController;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityEvent;
@@ -131,9 +129,9 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
private static final boolean SWEEP_OPEN_MENU = false;
// The height of a window which has focus in DIP.
- private final static int DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP = 20;
+ public static final int DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP = 20;
// The height of a window which has not in DIP.
- private final static int DECOR_SHADOW_UNFOCUSED_HEIGHT_IN_DIP = 5;
+ public static final int DECOR_SHADOW_UNFOCUSED_HEIGHT_IN_DIP = 5;
private static final int SCRIM_LIGHT = 0xe6ffffff; // 90% white
@@ -1629,7 +1627,9 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
int opacity = PixelFormat.OPAQUE;
final WindowConfiguration winConfig = getResources().getConfiguration().windowConfiguration;
- if (winConfig.hasWindowShadow()) {
+ // If we draw shadows in the compositor we don't need to force the surface to be
+ // translucent.
+ if (winConfig.hasWindowShadow() && !mWindow.mRenderShadowsInCompositor) {
// If the window has a shadow, it must be translucent.
opacity = PixelFormat.TRANSLUCENT;
} else{
@@ -2414,6 +2414,10 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
}
private void updateElevation() {
+ // If rendering shadows in the compositor, don't set an elevation on the view
+ if (mWindow.mRenderShadowsInCompositor) {
+ return;
+ }
float elevation = 0;
final boolean wasAdjustedForStack = mElevationAdjustedForStack;
// Do not use a shadow when we are in resizing mode (mBackdropFrameRenderer not null)
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index 95558c31e671..f13a638f7844 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -17,6 +17,7 @@
package com.android.internal.policy;
import static android.provider.Settings.Global.DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES;
+import static android.provider.Settings.Global.DEVELOPMENT_RENDER_SHADOWS_IN_COMPOSITOR;
import static android.view.View.SYSTEM_UI_LAYOUT_FLAGS;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
@@ -134,6 +135,11 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
private final static String TAG = "PhoneWindow";
+ /* If true, shadows drawn around the window will be rendered by the system compositor. If
+ * false, shadows will be drawn by the client by setting an elevation on the root view and
+ * the contents will be inset by the shadow radius. */
+ public final boolean mRenderShadowsInCompositor;
+
private static final boolean DEBUG = false;
private final static int DEFAULT_BACKGROUND_FADE_DURATION_MS = 300;
@@ -327,6 +333,8 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
public PhoneWindow(Context context) {
super(context);
mLayoutInflater = LayoutInflater.from(context);
+ mRenderShadowsInCompositor = Settings.Global.getInt(context.getContentResolver(),
+ DEVELOPMENT_RENDER_SHADOWS_IN_COMPOSITOR, 0) != 0;
}
/**
diff --git a/core/java/com/android/internal/util/ScreenshotHelper.java b/core/java/com/android/internal/util/ScreenshotHelper.java
index 8cad5a0d1d09..7cff90bbf437 100644
--- a/core/java/com/android/internal/util/ScreenshotHelper.java
+++ b/core/java/com/android/internal/util/ScreenshotHelper.java
@@ -6,7 +6,11 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
+import android.graphics.Bitmap;
+import android.graphics.Insets;
+import android.graphics.Rect;
import android.net.Uri;
+import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
@@ -14,6 +18,7 @@ import android.os.Messenger;
import android.os.RemoteException;
import android.os.UserHandle;
import android.util.Log;
+import android.view.WindowManager;
import java.util.function.Consumer;
@@ -38,9 +43,9 @@ public class ScreenshotHelper {
* is recommended for general use.
*
* @param screenshotType The type of screenshot, for example either
- * {@link android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN}
+ * {@link android.view.WindowManager#TAKE_SCREENSHOT_FULLSCREEN}
* or
- * {@link android.view.WindowManager.TAKE_SCREENSHOT_SELECTED_REGION}
+ * {@link android.view.WindowManager#TAKE_SCREENSHOT_SELECTED_REGION}
* @param hasStatus {@code true} if the status bar is currently showing. {@code false}
* if
* not.
@@ -65,9 +70,9 @@ public class ScreenshotHelper {
* is recommended for general use.
*
* @param screenshotType The type of screenshot, for example either
- * {@link android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN}
+ * {@link android.view.WindowManager#TAKE_SCREENSHOT_FULLSCREEN}
* or
- * {@link android.view.WindowManager.TAKE_SCREENSHOT_SELECTED_REGION}
+ * {@link android.view.WindowManager#TAKE_SCREENSHOT_SELECTED_REGION}
* @param hasStatus {@code true} if the status bar is currently showing. {@code false}
* if
* not.
@@ -84,6 +89,40 @@ public class ScreenshotHelper {
public void takeScreenshot(final int screenshotType, final boolean hasStatus,
final boolean hasNav, long timeoutMs, @NonNull Handler handler,
@Nullable Consumer<Uri> completionConsumer) {
+ takeScreenshot(screenshotType, hasStatus, hasNav, timeoutMs, handler, null,
+ completionConsumer
+ );
+ }
+
+ /**
+ * Request that provided image be handled as if it was a screenshot.
+ *
+ * @param screenshot The bitmap to treat as the screen shot.
+ * @param boundsInScreen The bounds in screen coordinates that the bitmap orginated from.
+ * @param insets The insets that the image was shown with, inside the screenbounds.
+ * @param taskId The taskId of the task that the screen shot was taken of.
+ * @param handler A handler used in case the screenshot times out
+ * @param completionConsumer Consumes `false` if a screenshot was not taken, and `true` if the
+ * screenshot was taken.
+ */
+ public void provideScreenshot(@NonNull Bitmap screenshot, @NonNull Rect boundsInScreen,
+ @NonNull Insets insets, int taskId, @NonNull Handler handler,
+ @Nullable Consumer<Uri> completionConsumer) {
+ Bundle imageBundle = new Bundle();
+ imageBundle.putParcelable(WindowManager.PARCEL_KEY_SCREENSHOT_BITMAP, screenshot);
+ imageBundle.putParcelable(WindowManager.PARCEL_KEY_SCREENSHOT_BOUNDS, boundsInScreen);
+ imageBundle.putParcelable(WindowManager.PARCEL_KEY_SCREENSHOT_INSETS, insets);
+ imageBundle.putInt(WindowManager.PARCEL_KEY_SCREENSHOT_TASK_ID, taskId);
+
+ takeScreenshot(
+ WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE,
+ false, false, // ignored when image bundle is set
+ SCREENSHOT_TIMEOUT_MS, handler, imageBundle, completionConsumer);
+ }
+
+ private void takeScreenshot(final int screenshotType, final boolean hasStatus,
+ final boolean hasNav, long timeoutMs, @NonNull Handler handler,
+ @Nullable Bundle providedImage, @Nullable Consumer<Uri> completionConsumer) {
synchronized (mScreenshotLock) {
if (mScreenshotConnection != null) {
return;
@@ -139,6 +178,10 @@ public class ScreenshotHelper {
msg.arg1 = hasStatus ? 1 : 0;
msg.arg2 = hasNav ? 1 : 0;
+ if (screenshotType == WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE) {
+ msg.setData(providedImage);
+ }
+
try {
messenger.send(msg);
} catch (RemoteException e) {
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index c41b19ef0692..9783b655e057 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -46,6 +46,7 @@
#include <signal.h>
#include <dirent.h>
#include <assert.h>
+#include <bionic/malloc.h>
#include <string>
#include <vector>
@@ -237,6 +238,14 @@ static void com_android_internal_os_RuntimeInit_nativeSetExitWithoutCleanup(JNIE
gCurRuntime->setExitWithoutCleanup(exitWithoutCleanup);
}
+static void com_android_internal_os_RuntimeInit_nativeDisableHeapPointerTagging(
+ JNIEnv* env, jobject clazz) {
+ HeapTaggingLevel tag_level = M_HEAP_TAGGING_LEVEL_NONE;
+ if (!android_mallopt(M_SET_HEAP_TAGGING_LEVEL, &tag_level, sizeof(tag_level))) {
+ ALOGE("ERROR: could not disable heap pointer tagging\n");
+ }
+}
+
/*
* JNI registration.
*/
@@ -244,10 +253,12 @@ static void com_android_internal_os_RuntimeInit_nativeSetExitWithoutCleanup(JNIE
int register_com_android_internal_os_RuntimeInit(JNIEnv* env)
{
const JNINativeMethod methods[] = {
- { "nativeFinishInit", "()V",
- (void*) com_android_internal_os_RuntimeInit_nativeFinishInit },
- { "nativeSetExitWithoutCleanup", "(Z)V",
- (void*) com_android_internal_os_RuntimeInit_nativeSetExitWithoutCleanup },
+ {"nativeFinishInit", "()V",
+ (void*)com_android_internal_os_RuntimeInit_nativeFinishInit},
+ {"nativeSetExitWithoutCleanup", "(Z)V",
+ (void*)com_android_internal_os_RuntimeInit_nativeSetExitWithoutCleanup},
+ {"nativeDisableHeapPointerTagging", "()V",
+ (void*)com_android_internal_os_RuntimeInit_nativeDisableHeapPointerTagging},
};
return jniRegisterNativeMethods(env, "com/android/internal/os/RuntimeInit",
methods, NELEM(methods));
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index 32a7cf32bc0a..eb7d432b559b 100755
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -475,8 +475,7 @@ static jboolean Bitmap_compress(JNIEnv* env, jobject clazz, jlong bitmapHandle,
}
auto fm = static_cast<Bitmap::JavaCompressFormat>(format);
- auto result = bitmap->bitmap().compress(fm, quality, strm.get());
- return result == Bitmap::CompressResult::Success ? JNI_TRUE : JNI_FALSE;
+ return bitmap->bitmap().compress(fm, quality, strm.get()) ? JNI_TRUE : JNI_FALSE;
}
static inline void bitmapErase(SkBitmap bitmap, const SkColor4f& color,
diff --git a/core/jni/android/graphics/apex/android_bitmap.cpp b/core/jni/android/graphics/apex/android_bitmap.cpp
index 66285292743b..decd19071944 100644
--- a/core/jni/android/graphics/apex/android_bitmap.cpp
+++ b/core/jni/android/graphics/apex/android_bitmap.cpp
@@ -290,14 +290,8 @@ int ABitmap_compress(const AndroidBitmapInfo* info, ADataSpace dataSpace, const
}
CompressWriter stream(userContext, fn);
- switch (Bitmap::compress(bitmap, format, quality, &stream)) {
- case Bitmap::CompressResult::Success:
- return ANDROID_BITMAP_RESULT_SUCCESS;
- case Bitmap::CompressResult::AllocationFailed:
- return ANDROID_BITMAP_RESULT_ALLOCATION_FAILED;
- case Bitmap::CompressResult::Error:
- return ANDROID_BITMAP_RESULT_JNI_EXCEPTION;
- }
+ return Bitmap::compress(bitmap, format, quality, &stream) ? ANDROID_BITMAP_RESULT_SUCCESS
+ : ANDROID_BITMAP_RESULT_JNI_EXCEPTION;
}
AHardwareBuffer* ABitmap_getHardwareBuffer(ABitmap* bitmapHandle) {
diff --git a/core/jni/android_media_AudioEffectDescriptor.cpp b/core/jni/android_media_AudioEffectDescriptor.cpp
index 5175a05c4c3b..37d8114052b8 100644
--- a/core/jni/android_media_AudioEffectDescriptor.cpp
+++ b/core/jni/android_media_AudioEffectDescriptor.cpp
@@ -39,17 +39,21 @@ jint convertAudioEffectDescriptorFromNative(JNIEnv* env, jobject* jDescriptor,
jstring jImplementor;
char str[EFFECT_STRING_LEN_MAX];
- if ((nDescriptor->flags & EFFECT_FLAG_TYPE_MASK)
- == EFFECT_FLAG_TYPE_AUXILIARY) {
- jConnect = env->NewStringUTF("Auxiliary");
- } else if ((nDescriptor->flags & EFFECT_FLAG_TYPE_MASK)
- == EFFECT_FLAG_TYPE_INSERT) {
- jConnect = env->NewStringUTF("Insert");
- } else if ((nDescriptor->flags & EFFECT_FLAG_TYPE_MASK)
- == EFFECT_FLAG_TYPE_PRE_PROC) {
- jConnect = env->NewStringUTF("Pre Processing");
- } else {
- return (jint) AUDIO_JAVA_BAD_VALUE;
+ switch (nDescriptor->flags & EFFECT_FLAG_TYPE_MASK) {
+ case EFFECT_FLAG_TYPE_AUXILIARY:
+ jConnect = env->NewStringUTF("Auxiliary");
+ break;
+ case EFFECT_FLAG_TYPE_INSERT:
+ jConnect = env->NewStringUTF("Insert");
+ break;
+ case EFFECT_FLAG_TYPE_PRE_PROC:
+ jConnect = env->NewStringUTF("Pre Processing");
+ break;
+ case EFFECT_FLAG_TYPE_POST_PROC:
+ jConnect = env->NewStringUTF("Post Processing");
+ break;
+ default:
+ return (jint)AUDIO_JAVA_BAD_VALUE;
}
AudioEffect::guidToString(&nDescriptor->type, str, EFFECT_STRING_LEN_MAX);
diff --git a/core/proto/OWNERS b/core/proto/OWNERS
index 74ced8921799..4892faaceafe 100644
--- a/core/proto/OWNERS
+++ b/core/proto/OWNERS
@@ -14,6 +14,7 @@ per-file settings_enums.proto=tmfang@google.com
# Frameworks
ogunwale@google.com
jjaggi@google.com
+per-file usagestatsservice.proto, usagestatsservice_v2.proto = mwachens@google.com
# Launcher
hyunyoungs@google.com
diff --git a/core/proto/android/server/usagestatsservice_v2.proto b/core/proto/android/server/usagestatsservice_v2.proto
index a28fcf3589f1..24b0728c29ec 100644
--- a/core/proto/android/server/usagestatsservice_v2.proto
+++ b/core/proto/android/server/usagestatsservice_v2.proto
@@ -99,6 +99,7 @@ message EventObfuscatedProto {
optional int32 instance_id = 10;
optional int32 task_root_package_token = 11;
optional int32 task_root_class_token = 12;
+ optional int32 locus_id_token = 13;
}
/**
@@ -117,6 +118,7 @@ message PendingEventProto {
optional int32 instance_id = 10;
optional string task_root_package = 11;
optional string task_root_class = 12;
+ optional string locus_id = 13;
}
/**
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 4595bab82465..b8e2cc2b9d4d 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -53,6 +53,7 @@
<protected-broadcast android:name="android.intent.action.PACKAGE_VERIFIED" />
<protected-broadcast android:name="android.intent.action.PACKAGES_SUSPENDED" />
<protected-broadcast android:name="android.intent.action.PACKAGES_UNSUSPENDED" />
+ <protected-broadcast android:name="android.intent.action.PACKAGE_UNSUSPENDED_MANUALLY" />
<protected-broadcast android:name="android.intent.action.DISTRACTING_PACKAGES_CHANGED" />
<protected-broadcast android:name="android.intent.action.ACTION_PREFERRED_ACTIVITY_CHANGED" />
<protected-broadcast android:name="android.intent.action.UID_REMOVED" />
@@ -1528,6 +1529,14 @@
android:protectionLevel="signature|privileged" />
<uses-permission android:name="android.permission.LOCATION_HARDWARE"/>
+ <!-- @SystemApi Allows an application to use the Context Hub.
+ <p>Not for use by third-party applications.
+ @hide
+ -->
+ <permission android:name="android.permission.ACCESS_CONTEXT_HUB"
+ android:protectionLevel="signature" />
+ <uses-permission android:name="android.permission.ACCESS_CONTEXT_HUB"/>
+
<!-- @SystemApi Allows an application to create mock location providers for testing.
<p>Protection level: signature
@hide
@@ -5046,12 +5055,6 @@
android:process=":ui">
</activity>
- <activity android:name="com.android.internal.app.BlockedAppActivity"
- android:theme="@style/Theme.Dialog.Confirmation"
- android:excludeFromRecents="true"
- android:process=":ui">
- </activity>
-
<activity android:name="com.android.settings.notification.NotificationAccessConfirmationActivity"
android:theme="@style/Theme.Dialog.Confirmation"
android:excludeFromRecents="true">
diff --git a/core/res/res/layout/autofill_inline_suggestion.xml b/core/res/res/layout/autofill_inline_suggestion.xml
index f7ac1642f6b7..27faea4b00de 100644
--- a/core/res/res/layout/autofill_inline_suggestion.xml
+++ b/core/res/res/layout/autofill_inline_suggestion.xml
@@ -16,19 +16,20 @@
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
+ style="?android:attr/autofillInlineSuggestionChip"
android:layout_width="wrap_content"
- android:layout_height="56dp"
- android:background="@color/white"
- android:orientation="horizontal"
- android:paddingStart="12dp"
- android:paddingEnd="12dp">
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:paddingTop="4dp"
+ android:paddingBottom="4dp"
+ android:orientation="horizontal">
<ImageView
android:id="@+id/autofill_inline_suggestion_start_icon"
- android:layout_width="24dp"
- android:layout_height="24dp"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
android:layout_gravity="center"
+ android:scaleType="fitCenter"
android:contentDescription="autofill_inline_suggestion_start_icon" />
<LinearLayout
@@ -36,32 +37,33 @@
android:layout_height="match_parent"
android:layout_gravity="center"
android:layout_weight="1"
- android:layout_marginStart="12dp"
- android:layout_marginEnd="12dp"
+ android:paddingStart="4dp"
+ android:paddingEnd="4dp"
android:orientation="vertical"
- android:gravity="center_vertical">
+ android:gravity="center">
<TextView
+ style="?android:attr/autofillInlineSuggestionTitle"
android:id="@+id/autofill_inline_suggestion_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="end"
- android:maxLines="1"
- tools:text="username1"/>
+ android:maxLines="1"/>
<TextView
+ style="?android:attr/autofillInlineSuggestionSubtitle"
android:id="@+id/autofill_inline_suggestion_subtitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="end"
- android:maxLines="1"
- tools:text="inline fill service"/>
+ android:maxLines="1"/>
</LinearLayout>
<ImageView
android:id="@+id/autofill_inline_suggestion_end_icon"
- android:layout_width="24dp"
- android:layout_height="24dp"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
android:layout_gravity="center"
+ android:scaleType="fitCenter"
android:contentDescription="autofill_inline_suggestion_end_icon" />
</LinearLayout>
diff --git a/core/res/res/values/arrays.xml b/core/res/res/values/arrays.xml
index 8f2d6c3e02f4..deb6afcfa3b4 100644
--- a/core/res/res/values/arrays.xml
+++ b/core/res/res/values/arrays.xml
@@ -165,7 +165,7 @@
<item>中文 (繁體)</item>
</string-array>
- <array name="sim_colors">
+ <array name="simColors">
<item>@color/Teal_700</item>
<item>@color/Blue_700</item>
<item>@color/Indigo_700</item>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 940e9f1ef88b..c9c47b92f782 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -9197,4 +9197,12 @@
</declare-styleable>
<attr name="autoSizePresetSizes" />
+
+ <declare-styleable name="AutofillInlineSuggestion">
+ <!-- @hide @SystemApi -->
+ <attr name="isAutofillInlineSuggestionTheme" format="boolean" />
+ <attr name="autofillInlineSuggestionChip" format="reference" />
+ <attr name="autofillInlineSuggestionTitle" format="reference" />
+ <attr name="autofillInlineSuggestionSubtitle" format="reference" />
+ </declare-styleable>
</resources>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 42127e77317f..dadb92415839 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2685,6 +2685,11 @@
<!-- The amount to scale fullscreen snapshots for Overview and snapshot starting windows. -->
<item name="config_fullTaskSnapshotScale" format="float" type="dimen">1.0</item>
+ <!-- The amount to scale reduced scale snapshots for Overview and snapshot starting windows.
+ Reduced scale snapshots are loaded before full screen snapshots to improve load times and
+ minimize the chance the user will see an empty task card. -->
+ <item name="config_reducedTaskSnapshotScale" format="float" type="dimen">0.5</item>
+
<!-- Feature flag to store TaskSnapshot in 16 bit pixel format to save memory. -->
<bool name="config_use16BitTaskSnapshotPixelFormat">false</bool>
@@ -4173,7 +4178,7 @@
where: IDs are unique per device, Modality as defined in BiometricAuthenticator.java,
and Strength as defined in Authenticators.java -->
<string-array name="config_biometric_sensors" translatable="false" >
- <item>0:2:15</item> <!-- ID0:Fingerprint:Strong -->
+ <!-- <item>0:2:15</item> ID0:Fingerprint:Strong -->
</string-array>
<!-- Messages that should not be shown to the user during face auth enrollment. This should be
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 36dbcbd53977..4f61730ad2e2 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3012,12 +3012,18 @@
<public name="sdkVersion" />
<!-- @hide @SystemApi -->
<public name="minExtensionVersion" />
+ <public name="autofillInlineSuggestionChip" />
+ <public name="autofillInlineSuggestionTitle" />
+ <public name="autofillInlineSuggestionSubtitle" />
+ <!-- @hide @SystemApi -->
+ <public name="isAutofillInlineSuggestionTheme" />
</public-group>
<public-group type="drawable" first-id="0x010800b5">
</public-group>
<public-group type="style" first-id="0x010302e5">
+ <public name="Theme.AutofillInlineSuggestion" />
</public-group>
<public-group type="id" first-id="0x0102004a">
@@ -3037,6 +3043,8 @@
<public name="config_defaultCallScreening" />
<!-- @hide @SystemApi @TestApi -->
<public name="config_systemGallery" />
+ <!-- @hide @SystemApi -->
+ <public name="low_memory" />
</public-group>
<public-group type="bool" first-id="0x01110005">
@@ -3059,6 +3067,18 @@
<public name="accessibilitySystemActionLockScreen" />
<public name="accessibilitySystemActionTakeScreenshot" />
</public-group>
+
+ <public-group type="array" first-id="0x01070006">
+ <!-- @hide @SystemApi -->
+ <public name="simColors" />
+ <!-- @hide @SystemApi -->
+ <public name="config_restrictedPreinstalledCarrierApps" />
+ <!-- @hide @SystemApi -->
+ <public name="config_sms_enabled_single_shift_tables" />
+ <!-- @hide @SystemApi -->
+ <public name="config_sms_enabled_locking_shift_tables" />
+ </public-group>
+
<!-- ===============================================================
DO NOT ADD UN-GROUPED ITEMS HERE
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index be2b678565d3..83ef456cbbcf 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -4923,6 +4923,8 @@
</string>
<!-- Title of the button to show users more details about why the app has been suspended [CHAR LIMIT=50]-->
<string name="app_suspended_more_details">Learn more</string>
+ <!-- Title of the button to unsuspend a suspended app immediately [CHAR LIMIT=50]-->
+ <string name="app_suspended_unsuspend_message">Unpause app</string>
<!-- Title of a dialog. The string is asking if the user wants to turn on their work profile, which contains work apps that are managed by their employer. "Work" is an adjective. [CHAR LIMIT=30] -->
<string name="work_mode_off_title">Turn on work profile?</string>
@@ -4931,13 +4933,6 @@
<!-- Title for button to turn on work profile. [CHAR LIMIT=NONE] -->
<string name="work_mode_turn_on">Turn on</string>
- <!-- Title of the dialog that is shown when the user tries to launch a suspended application [CHAR LIMIT=50] -->
- <string name="app_blocked_title">App is not available</string>
- <!-- Default message shown in the dialog that is shown when the user tries to launch a suspended application [CHAR LIMIT=NONE] -->
- <string name="app_blocked_message">
- <xliff:g id="app_name" example="Gmail">%1$s</xliff:g> is not available right now.
- </string>
-
<!-- Message displayed in dialog when app is too old to run on this verison of android. [CHAR LIMIT=NONE] -->
<string name="deprecated_target_sdk_message">This app was built for an older version of Android and may not work properly. Try checking for updates, or contact the developer.</string>
<!-- Title for button to see application detail in app store which it came from - it may allow user to update to newer version. [CHAR LIMIT=50] -->
@@ -5318,7 +5313,8 @@
<string name="accessibility_system_action_lock_screen_label">Lock Screen</string>
<!-- Label for taking screenshot action [CHAR LIMIT=NONE] -->
<string name="accessibility_system_action_screenshot_label">Screenshot</string>
-
+ <!-- Label for showing accessibility menu action [CHAR LIMIT=NONE] -->
+ <string name="accessibility_system_action_accessibility_menu_label">Accessibility Menu</string>
<!-- Accessibility description of caption view -->
<string name="accessibility_freeform_caption">Caption bar of <xliff:g id="app_name">%1$s</xliff:g>.</string>
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index bcce1f05f0dd..751eca036ba6 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -1482,6 +1482,22 @@ please see styles_device_defaults.xml.
<item name="android:windowExitAnimation">@anim/slide_out_down</item>
</style>
+ <!-- The style for the Autofill inline suggestion chip. -->
+ <!-- @hide -->
+ <style name="AutofillInlineSuggestionChip">
+ <item name="background">@drawable/autofill_dataset_picker_background</item>
+ </style>
+
+ <!-- @hide -->
+ <style name="AutofillInlineSuggestionTitle">
+ <item name="android:textAppearance">@style/TextAppearance</item>
+ </style>
+
+ <!-- @hide -->
+ <style name="AutofillInlineSuggestionSubtitle">
+ <item name="android:textAppearance">@style/TextAppearance.Small</item>
+ </style>
+
<!-- The style for the container of media actions in a notification. -->
<!-- @hide -->
<style name="NotificationMediaActionContainer">
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index ea8ca9aa1c49..9414cdba92f5 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -360,6 +360,7 @@
<java-symbol type="bool" name="config_enableNewAutoSelectNetworkUI"/>
<java-symbol type="bool" name="config_disableUsbPermissionDialogs"/>
<java-symbol type="dimen" name="config_fullTaskSnapshotScale" />
+ <java-symbol type="dimen" name="config_reducedTaskSnapshotScale" />
<java-symbol type="bool" name="config_use16BitTaskSnapshotPixelFormat" />
<java-symbol type="bool" name="config_hasRecents" />
<java-symbol type="string" name="config_recentsComponentName" />
@@ -1240,7 +1241,7 @@
<java-symbol type="array" name="preloaded_color_state_lists" />
<java-symbol type="array" name="preloaded_drawables" />
<java-symbol type="array" name="preloaded_freeform_multi_window_drawables" />
- <java-symbol type="array" name="sim_colors" />
+ <java-symbol type="array" name="simColors" />
<java-symbol type="array" name="special_locale_codes" />
<java-symbol type="array" name="special_locale_names" />
<java-symbol type="array" name="supported_locales" />
@@ -3038,11 +3039,9 @@
<java-symbol type="string" name="app_suspended_title" />
<java-symbol type="string" name="app_suspended_more_details" />
+ <java-symbol type="string" name="app_suspended_unsuspend_message" />
<java-symbol type="string" name="app_suspended_default_message" />
- <java-symbol type="string" name="app_blocked_title" />
- <java-symbol type="string" name="app_blocked_message" />
-
<!-- Used internally for assistant to launch activity transitions -->
<java-symbol type="id" name="cross_task_transition" />
@@ -3803,6 +3802,7 @@
<java-symbol type="string" name="accessibility_system_action_recents_label" />
<java-symbol type="string" name="accessibility_system_action_screenshot_label" />
<java-symbol type="string" name="accessibility_system_action_toggle_split_screen_label" />
+ <java-symbol type="string" name="accessibility_system_action_accessibility_menu_label" />
<java-symbol type="string" name="accessibility_freeform_caption" />
diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml
index ad38f3d57c23..5e6dd8294776 100644
--- a/core/res/res/values/themes.xml
+++ b/core/res/res/values/themes.xml
@@ -893,4 +893,11 @@ please see themes_device_defaults.xml.
<item name="windowActivityTransitions">false</item>
</style>
+ <!-- Theme for the Autofill inline suggestion on IME -->
+ <style name="Theme.AutofillInlineSuggestion" parent="Theme.DeviceDefault">
+ <item name="isAutofillInlineSuggestionTheme">true</item>
+ <item name="autofillInlineSuggestionChip">@style/AutofillInlineSuggestionChip</item>
+ <item name="autofillInlineSuggestionTitle">@style/AutofillInlineSuggestionTitle</item>
+ <item name="autofillInlineSuggestionSubtitle">@style/AutofillInlineSuggestionSubtitle</item>
+ </style>
</resources>
diff --git a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
index d8b527c8a11a..c986db8b2a83 100644
--- a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
+++ b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
@@ -96,16 +96,6 @@ public class ActivityThreadTest {
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
}
- @Test
- public void testSleepAndStop() throws Exception {
- final Activity activity = mActivityTestRule.launchActivity(new Intent());
- final IApplicationThread appThread = activity.getActivityThread().getApplicationThread();
-
- appThread.scheduleSleeping(activity.getActivityToken(), true /* sleeping */);
- appThread.scheduleTransaction(newStopTransaction(activity));
- InstrumentationRegistry.getInstrumentation().waitForIdleSync();
- }
-
/** Verify that repeated resume requests to activity will be ignored. */
@Test
public void testRepeatedResume() throws Exception {
diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
index ecea9011e704..372b8c294702 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
@@ -448,10 +448,6 @@ public class TransactionParcelTests {
}
@Override
- public void scheduleSleeping(IBinder iBinder, boolean b) throws RemoteException {
- }
-
- @Override
public void profilerControl(boolean b, ProfilerInfo profilerInfo, int i)
throws RemoteException {
}
diff --git a/core/tests/coretests/src/android/content/integrity/AtomicFormulaTest.java b/core/tests/coretests/src/android/content/integrity/AtomicFormulaTest.java
index e48f1c3a89c7..c0d9be5dde59 100644
--- a/core/tests/coretests/src/android/content/integrity/AtomicFormulaTest.java
+++ b/core/tests/coretests/src/android/content/integrity/AtomicFormulaTest.java
@@ -18,12 +18,9 @@ package android.content.integrity;
import static android.content.integrity.TestUtils.assertExpectException;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
+import static com.google.common.truth.Truth.assertThat;
import android.content.integrity.AtomicFormula.BooleanAtomicFormula;
-import android.content.integrity.AtomicFormula.IntAtomicFormula;
import android.content.integrity.AtomicFormula.StringAtomicFormula;
import android.os.Parcel;
@@ -36,19 +33,93 @@ public class AtomicFormulaTest {
@Test
public void testValidAtomicFormula_stringValue() {
+ String packageName = "com.test.app";
StringAtomicFormula stringAtomicFormula =
new StringAtomicFormula(
- AtomicFormula.PACKAGE_NAME, "com.test.app", /* isHashedValue= */ false);
+ AtomicFormula.PACKAGE_NAME, packageName, /* isHashedValue= */false);
- assertEquals(AtomicFormula.PACKAGE_NAME, stringAtomicFormula.getKey());
+ assertThat(stringAtomicFormula.getKey()).isEqualTo(AtomicFormula.PACKAGE_NAME);
+ assertThat(stringAtomicFormula.getValue()).isEqualTo(packageName);
+ assertThat(stringAtomicFormula.getIsHashedValue()).isFalse();
}
@Test
- public void testValidAtomicFormula_intValue() {
- IntAtomicFormula intAtomicFormula =
- new IntAtomicFormula(AtomicFormula.VERSION_CODE, AtomicFormula.LE, 1);
+ public void testValidAtomicFormula_stringValue_autoHash_packageNameLessThanLimit() {
+ String packageName = "com.test.app";
+ StringAtomicFormula stringAtomicFormula =
+ new StringAtomicFormula(AtomicFormula.PACKAGE_NAME, packageName);
+
+ assertThat(stringAtomicFormula.getKey()).isEqualTo(AtomicFormula.PACKAGE_NAME);
+ assertThat(stringAtomicFormula.getValue()).isEqualTo(packageName);
+ assertThat(stringAtomicFormula.getIsHashedValue()).isFalse();
+ }
+
+ @Test
+ public void testValidAtomicFormula_stringValue_autoHash_longPackageName() {
+ String packageName = "com.test.app.test.app.test.app.test.app.test.app";
+ StringAtomicFormula stringAtomicFormula =
+ new StringAtomicFormula(AtomicFormula.PACKAGE_NAME, packageName);
+
+ assertThat(stringAtomicFormula.getKey()).isEqualTo(AtomicFormula.PACKAGE_NAME);
+ assertThat(stringAtomicFormula.getValue()).doesNotMatch(packageName);
+ assertThat(stringAtomicFormula.getIsHashedValue()).isTrue();
+ }
+
+ @Test
+ public void testValidAtomicFormula_stringValue_autoHash_installerNameLessThanLimit() {
+ String installerName = "com.test.app";
+ StringAtomicFormula stringAtomicFormula =
+ new StringAtomicFormula(AtomicFormula.INSTALLER_NAME, installerName);
+
+
+ assertThat(stringAtomicFormula.getKey()).isEqualTo(AtomicFormula.INSTALLER_NAME);
+ assertThat(stringAtomicFormula.getValue()).isEqualTo(installerName);
+ assertThat(stringAtomicFormula.getIsHashedValue()).isFalse();
+ }
+
+ @Test
+ public void testValidAtomicFormula_stringValue_autoHash_longInstallerName() {
+ String installerName = "com.test.app.test.app.test.app.test.app.test.app";
+ StringAtomicFormula stringAtomicFormula =
+ new StringAtomicFormula(AtomicFormula.INSTALLER_NAME, installerName);
- assertEquals(AtomicFormula.VERSION_CODE, intAtomicFormula.getKey());
+ assertThat(stringAtomicFormula.getKey()).isEqualTo(AtomicFormula.INSTALLER_NAME);
+ assertThat(stringAtomicFormula.getValue()).doesNotMatch(installerName);
+ assertThat(stringAtomicFormula.getIsHashedValue()).isTrue();
+ }
+
+ @Test
+ public void testValidAtomicFormula_stringValue_appCertificateAutoHashed() {
+ String appCert = "cert";
+ StringAtomicFormula stringAtomicFormula =
+ new StringAtomicFormula(AtomicFormula.APP_CERTIFICATE, appCert);
+
+ assertThat(stringAtomicFormula.getKey()).isEqualTo(AtomicFormula.APP_CERTIFICATE);
+ assertThat(stringAtomicFormula.getValue()).doesNotMatch(appCert);
+ assertThat(stringAtomicFormula.getIsHashedValue()).isTrue();
+ }
+
+ @Test
+ public void testValidAtomicFormula_stringValue_installerCertificateAutoHashed() {
+ String installerCert = "cert";
+ StringAtomicFormula stringAtomicFormula =
+ new StringAtomicFormula(AtomicFormula.INSTALLER_CERTIFICATE,
+ installerCert);
+
+ assertThat(stringAtomicFormula.getKey()).isEqualTo(
+ AtomicFormula.INSTALLER_CERTIFICATE);
+ assertThat(stringAtomicFormula.getValue()).doesNotMatch(installerCert);
+ assertThat(stringAtomicFormula.getIsHashedValue()).isTrue();
+ }
+
+ @Test
+ public void testValidAtomicFormula_longValue() {
+ AtomicFormula.LongAtomicFormula longAtomicFormula =
+ new AtomicFormula.LongAtomicFormula(
+ AtomicFormula.VERSION_CODE, AtomicFormula.GTE, 1);
+
+ assertThat(longAtomicFormula.getKey()).isEqualTo(AtomicFormula.VERSION_CODE);
+ assertThat(longAtomicFormula.getValue()).isEqualTo(1);
}
@Test
@@ -56,7 +127,8 @@ public class AtomicFormulaTest {
BooleanAtomicFormula atomicFormula =
new BooleanAtomicFormula(AtomicFormula.PRE_INSTALLED, true);
- assertEquals(AtomicFormula.PRE_INSTALLED, atomicFormula.getKey());
+ assertThat(atomicFormula.getKey()).isEqualTo(AtomicFormula.PRE_INSTALLED);
+ assertThat(atomicFormula.getValue()).isTrue();
}
@Test
@@ -73,12 +145,14 @@ public class AtomicFormulaTest {
}
@Test
- public void testInvalidAtomicFormula_intValue() {
+ public void testInvalidAtomicFormula_longValue() {
assertExpectException(
IllegalArgumentException.class,
/* expectedExceptionMessageRegex */
- String.format("Key PACKAGE_NAME cannot be used with IntAtomicFormula"),
- () -> new IntAtomicFormula(AtomicFormula.PACKAGE_NAME, AtomicFormula.EQ, 1));
+ String.format("Key PACKAGE_NAME cannot be used with LongAtomicFormula"),
+ () ->
+ new AtomicFormula.LongAtomicFormula(
+ AtomicFormula.PACKAGE_NAME, AtomicFormula.EQ, 1));
}
@Test
@@ -91,197 +165,174 @@ public class AtomicFormulaTest {
}
@Test
- public void testIsSatisfiable_string_true() {
- StringAtomicFormula stringAtomicFormula =
+ public void testParcelUnparcel_string() {
+ StringAtomicFormula formula =
new StringAtomicFormula(
- AtomicFormula.PACKAGE_NAME, "com.test.app", /* isHashedValue= */ false);
- AppInstallMetadata appInstallMetadata =
- getAppInstallMetadataBuilder().setPackageName("com.test.app").build();
+ AtomicFormula.PACKAGE_NAME, "abc", /* isHashedValue= */ false);
+ Parcel p = Parcel.obtain();
+ formula.writeToParcel(p, 0);
+ p.setDataPosition(0);
+ StringAtomicFormula newFormula = StringAtomicFormula.CREATOR.createFromParcel(p);
- assertTrue(stringAtomicFormula.isSatisfied(appInstallMetadata));
+ assertThat(newFormula).isEqualTo(formula);
}
@Test
- public void testIsSatisfiable_string_false() {
- StringAtomicFormula stringAtomicFormula =
- new StringAtomicFormula(
- AtomicFormula.PACKAGE_NAME, "com.test.app", /* isHashedValue= */ false);
- AppInstallMetadata appInstallMetadata =
- getAppInstallMetadataBuilder().setPackageName("com.foo.bar").build();
+ public void testParcelUnparcel_int() {
+ AtomicFormula.LongAtomicFormula formula =
+ new AtomicFormula.LongAtomicFormula(
+ AtomicFormula.VERSION_CODE, AtomicFormula.GT, 1);
+ Parcel p = Parcel.obtain();
+ formula.writeToParcel(p, 0);
+ p.setDataPosition(0);
+ AtomicFormula.LongAtomicFormula newFormula =
+ AtomicFormula.LongAtomicFormula.CREATOR.createFromParcel(p);
- assertFalse(stringAtomicFormula.isSatisfied(appInstallMetadata));
+ assertThat(newFormula).isEqualTo(formula);
}
@Test
- public void testIsSatisfiable_int_eq_true() {
- IntAtomicFormula intAtomicFormula =
- new IntAtomicFormula(AtomicFormula.VERSION_CODE, AtomicFormula.EQ, 0);
- AppInstallMetadata appInstallMetadata =
- getAppInstallMetadataBuilder().setVersionCode(0).build();
+ public void testParcelUnparcel_bool() {
+ BooleanAtomicFormula formula = new BooleanAtomicFormula(
+ AtomicFormula.PRE_INSTALLED, true);
+ Parcel p = Parcel.obtain();
+ formula.writeToParcel(p, 0);
+ p.setDataPosition(0);
+ BooleanAtomicFormula newFormula = BooleanAtomicFormula.CREATOR.createFromParcel(p);
- assertTrue(intAtomicFormula.isSatisfied(appInstallMetadata));
+ assertThat(newFormula).isEqualTo(formula);
}
@Test
- public void testIsSatisfiable_int_eq_false() {
- IntAtomicFormula intAtomicFormula =
- new IntAtomicFormula(AtomicFormula.VERSION_CODE, AtomicFormula.EQ, 0);
- AppInstallMetadata appInstallMetadata =
- getAppInstallMetadataBuilder().setVersionCode(1).build();
-
- assertFalse(intAtomicFormula.isSatisfied(appInstallMetadata));
+ public void testInvalidAtomicFormula_invalidKey() {
+ assertExpectException(
+ IllegalArgumentException.class,
+ /* expectedExceptionMessageRegex */ "Unknown key: -1",
+ () -> new AtomicFormula.LongAtomicFormula(/* key= */ -1, AtomicFormula.EQ, 0));
}
@Test
- public void testIsSatisfiable_int_gt_true() {
- IntAtomicFormula intAtomicFormula =
- new IntAtomicFormula(AtomicFormula.VERSION_CODE, AtomicFormula.GT, 0);
- AppInstallMetadata appInstallMetadata =
- getAppInstallMetadataBuilder().setVersionCode(1).build();
-
- assertTrue(intAtomicFormula.isSatisfied(appInstallMetadata));
+ public void testInvalidAtomicFormula_invalidOperator() {
+ assertExpectException(
+ IllegalArgumentException.class,
+ /* expectedExceptionMessageRegex */ "Unknown operator: -1",
+ () ->
+ new AtomicFormula.LongAtomicFormula(
+ AtomicFormula.VERSION_CODE, /* operator= */ -1, 0));
}
@Test
- public void testIsSatisfiable_int_gt_false() {
- IntAtomicFormula intAtomicFormula =
- new IntAtomicFormula(AtomicFormula.VERSION_CODE, AtomicFormula.GT, 1);
+ public void testFormulaMatches_string_true() {
+ StringAtomicFormula stringAtomicFormula =
+ new StringAtomicFormula(
+ AtomicFormula.PACKAGE_NAME, "com.test.app", /* isHashedValue= */
+ false);
AppInstallMetadata appInstallMetadata =
- getAppInstallMetadataBuilder().setVersionCode(0).build();
+ getAppInstallMetadataBuilder().setPackageName("com.test.app").build();
- assertFalse(intAtomicFormula.isSatisfied(appInstallMetadata));
+ assertThat(stringAtomicFormula.matches(appInstallMetadata)).isTrue();
}
@Test
- public void testIsSatisfiable_int_ge_true() {
- IntAtomicFormula intAtomicFormula =
- new IntAtomicFormula(AtomicFormula.VERSION_CODE, AtomicFormula.GE, 0);
+ public void testFormulaMatches_string_false() {
+ StringAtomicFormula stringAtomicFormula =
+ new StringAtomicFormula(
+ AtomicFormula.PACKAGE_NAME, "com.test.app", /* isHashedValue= */
+ false);
AppInstallMetadata appInstallMetadata =
- getAppInstallMetadataBuilder().setVersionCode(1).build();
+ getAppInstallMetadataBuilder().setPackageName("com.foo.bar").build();
- assertTrue(intAtomicFormula.isSatisfied(appInstallMetadata));
+ assertThat(stringAtomicFormula.matches(appInstallMetadata)).isFalse();
}
@Test
- public void testIsSatisfiable_int_ge_false() {
- IntAtomicFormula intAtomicFormula =
- new IntAtomicFormula(AtomicFormula.VERSION_CODE, AtomicFormula.GE, 1);
+ public void testFormulaMatches_long_eq_true() {
+ AtomicFormula.LongAtomicFormula longAtomicFormula =
+ new AtomicFormula.LongAtomicFormula(
+ AtomicFormula.VERSION_CODE, AtomicFormula.EQ, 0);
AppInstallMetadata appInstallMetadata =
getAppInstallMetadataBuilder().setVersionCode(0).build();
- assertFalse(intAtomicFormula.isSatisfied(appInstallMetadata));
+ assertThat(longAtomicFormula.matches(appInstallMetadata)).isTrue();
}
@Test
- public void testIsSatisfiable_int_lt_true() {
- IntAtomicFormula intAtomicFormula =
- new IntAtomicFormula(AtomicFormula.VERSION_CODE, AtomicFormula.LT, 1);
+ public void testFormulaMatches_long_eq_false() {
+ AtomicFormula.LongAtomicFormula longAtomicFormula =
+ new AtomicFormula.LongAtomicFormula(
+ AtomicFormula.VERSION_CODE, AtomicFormula.EQ, 0);
AppInstallMetadata appInstallMetadata =
- getAppInstallMetadataBuilder().setVersionCode(0).build();
+ getAppInstallMetadataBuilder().setVersionCode(1).build();
- assertTrue(intAtomicFormula.isSatisfied(appInstallMetadata));
+ assertThat(longAtomicFormula.matches(appInstallMetadata)).isFalse();
}
@Test
- public void testIsSatisfiable_int_lt_false() {
- IntAtomicFormula intAtomicFormula =
- new IntAtomicFormula(AtomicFormula.VERSION_CODE, AtomicFormula.LT, 1);
+ public void testFormulaMatches_long_gt_true() {
+ AtomicFormula.LongAtomicFormula longAtomicFormula =
+ new AtomicFormula.LongAtomicFormula(AtomicFormula.VERSION_CODE, AtomicFormula.GT,
+ 0);
AppInstallMetadata appInstallMetadata =
- getAppInstallMetadataBuilder().setVersionCode(2).build();
+ getAppInstallMetadataBuilder().setVersionCode(1).build();
- assertFalse(intAtomicFormula.isSatisfied(appInstallMetadata));
+ assertThat(longAtomicFormula.matches(appInstallMetadata)).isTrue();
}
@Test
- public void testIsSatisfiable_int_le_true() {
- IntAtomicFormula intAtomicFormula =
- new IntAtomicFormula(AtomicFormula.VERSION_CODE, AtomicFormula.LE, 1);
+ public void testFormulaMatches_long_gt_false() {
+ AtomicFormula.LongAtomicFormula longAtomicFormula =
+ new AtomicFormula.LongAtomicFormula(AtomicFormula.VERSION_CODE, AtomicFormula.GT,
+ 1);
AppInstallMetadata appInstallMetadata =
getAppInstallMetadataBuilder().setVersionCode(0).build();
- assertTrue(intAtomicFormula.isSatisfied(appInstallMetadata));
+ assertThat(longAtomicFormula.matches(appInstallMetadata)).isFalse();
}
@Test
- public void testIsSatisfiable_int_le_false() {
- IntAtomicFormula intAtomicFormula =
- new IntAtomicFormula(AtomicFormula.VERSION_CODE, AtomicFormula.LE, 1);
- AppInstallMetadata appInstallMetadata =
+ public void testFormulaMatches_long_gte_true() {
+ AtomicFormula.LongAtomicFormula longAtomicFormula =
+ new AtomicFormula.LongAtomicFormula(
+ AtomicFormula.VERSION_CODE, AtomicFormula.GTE, 1);
+
+ AppInstallMetadata appInstallMetadata1 =
+ getAppInstallMetadataBuilder().setVersionCode(1).build();
+ assertThat(longAtomicFormula.matches(appInstallMetadata1)).isTrue();
+
+ AppInstallMetadata appInstallMetadata2 =
getAppInstallMetadataBuilder().setVersionCode(2).build();
+ assertThat(longAtomicFormula.matches(appInstallMetadata2)).isTrue();
+ }
- assertFalse(intAtomicFormula.isSatisfied(appInstallMetadata));
+ @Test
+ public void testFormulaMatches_long_gte_false() {
+ AtomicFormula.LongAtomicFormula longAtomicFormula =
+ new AtomicFormula.LongAtomicFormula(
+ AtomicFormula.VERSION_CODE, AtomicFormula.GTE, 1);
+ AppInstallMetadata appInstallMetadata =
+ getAppInstallMetadataBuilder().setVersionCode(0).build();
+
+ assertThat(longAtomicFormula.matches(appInstallMetadata)).isFalse();
}
@Test
- public void testIsSatisfiable_bool_true() {
+ public void testFormulaMatches_bool_true() {
BooleanAtomicFormula boolFormula =
new BooleanAtomicFormula(AtomicFormula.PRE_INSTALLED, true);
AppInstallMetadata appInstallMetadata =
getAppInstallMetadataBuilder().setIsPreInstalled(true).build();
- assertTrue(boolFormula.isSatisfied(appInstallMetadata));
+ assertThat(boolFormula.matches(appInstallMetadata)).isTrue();
}
@Test
- public void testIsSatisfiable_bool_false() {
+ public void testFormulaMatches_bool_false() {
BooleanAtomicFormula boolFormula =
new BooleanAtomicFormula(AtomicFormula.PRE_INSTALLED, true);
AppInstallMetadata appInstallMetadata =
getAppInstallMetadataBuilder().setIsPreInstalled(false).build();
- assertFalse(boolFormula.isSatisfied(appInstallMetadata));
- }
-
- @Test
- public void testParcelUnparcel_string() {
- StringAtomicFormula formula =
- new StringAtomicFormula(
- AtomicFormula.PACKAGE_NAME, "abc", /* isHashedValue= */ false);
- Parcel p = Parcel.obtain();
- formula.writeToParcel(p, 0);
- p.setDataPosition(0);
- StringAtomicFormula newFormula = StringAtomicFormula.CREATOR.createFromParcel(p);
-
- assertEquals(formula, newFormula);
- }
-
- @Test
- public void testParcelUnparcel_int() {
- IntAtomicFormula formula =
- new IntAtomicFormula(AtomicFormula.VERSION_CODE, AtomicFormula.LT, 1);
- Parcel p = Parcel.obtain();
- formula.writeToParcel(p, 0);
- p.setDataPosition(0);
- IntAtomicFormula newFormula = IntAtomicFormula.CREATOR.createFromParcel(p);
-
- assertEquals(formula, newFormula);
- }
-
- @Test
- public void testParcelUnparcel_bool() {
- BooleanAtomicFormula formula = new BooleanAtomicFormula(AtomicFormula.PRE_INSTALLED, true);
- Parcel p = Parcel.obtain();
- formula.writeToParcel(p, 0);
- p.setDataPosition(0);
- BooleanAtomicFormula newFormula = BooleanAtomicFormula.CREATOR.createFromParcel(p);
-
- assertEquals(formula, newFormula);
- }
-
- @Test
- public void testInvalidAtomicFormula_invalidKey() {
- assertExpectException(
- IllegalArgumentException.class,
- /* expectedExceptionMessageRegex */ "Unknown key: -1",
- () -> new IntAtomicFormula(/* key= */ -1, AtomicFormula.EQ, 0));
- }
-
- @Test
- public void testInvalidAtomicFormula_invalidOperator() {
- assertExpectException(
- IllegalArgumentException.class,
- /* expectedExceptionMessageRegex */ "Unknown operator: -1",
- () -> new IntAtomicFormula(AtomicFormula.VERSION_CODE, /* operator= */ -1, 0));
+ assertThat(boolFormula.matches(appInstallMetadata)).isFalse();
}
/** Returns a builder with all fields filled with some dummy data. */
diff --git a/core/tests/coretests/src/android/content/integrity/CompoundFormulaTest.java b/core/tests/coretests/src/android/content/integrity/CompoundFormulaTest.java
index 927e4dbb5200..fa3d671c09e5 100644
--- a/core/tests/coretests/src/android/content/integrity/CompoundFormulaTest.java
+++ b/core/tests/coretests/src/android/content/integrity/CompoundFormulaTest.java
@@ -18,9 +18,9 @@ package android.content.integrity;
import static android.content.integrity.TestUtils.assertExpectException;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
import android.os.Parcel;
@@ -38,7 +38,7 @@ public class CompoundFormulaTest {
new AtomicFormula.StringAtomicFormula(
AtomicFormula.PACKAGE_NAME, "test1", /* isHashedValue= */ false);
private static final AtomicFormula ATOMIC_FORMULA_2 =
- new AtomicFormula.IntAtomicFormula(AtomicFormula.VERSION_CODE, AtomicFormula.EQ, 1);
+ new AtomicFormula.LongAtomicFormula(AtomicFormula.VERSION_CODE, AtomicFormula.EQ, 1);
@Test
public void testValidCompoundFormula() {
@@ -75,163 +75,156 @@ public class CompoundFormulaTest {
}
@Test
- public void testIsSatisfiable_notFalse_true() {
- CompoundFormula compoundFormula =
- new CompoundFormula(CompoundFormula.NOT, Arrays.asList(ATOMIC_FORMULA_1));
- AppInstallMetadata appInstallMetadata =
- getAppInstallMetadataBuilder().setPackageName("test2").build();
- // validate assumptions about the metadata
- assertFalse(ATOMIC_FORMULA_1.isSatisfied(appInstallMetadata));
+ public void testParcelUnparcel() {
+ CompoundFormula formula =
+ new CompoundFormula(
+ CompoundFormula.AND, Arrays.asList(ATOMIC_FORMULA_2, ATOMIC_FORMULA_1));
+ Parcel p = Parcel.obtain();
+ formula.writeToParcel(p, 0);
+ p.setDataPosition(0);
+ CompoundFormula newFormula = CompoundFormula.CREATOR.createFromParcel(p);
+
+ assertEquals(formula, newFormula);
+ }
- assertTrue(compoundFormula.isSatisfied(appInstallMetadata));
+ @Test
+ public void testInvalidCompoundFormula_invalidConnector() {
+ assertExpectException(
+ IllegalArgumentException.class,
+ /* expectedExceptionMessageRegex */ "Unknown connector: -1",
+ () ->
+ new CompoundFormula(
+ /* connector= */ -1,
+ Arrays.asList(ATOMIC_FORMULA_1, ATOMIC_FORMULA_2)));
}
@Test
- public void testIsSatisfiable_notTrue_false() {
+ public void testFormulaMatches_notFalse_true() {
+ AppInstallMetadata appInstallMetadata =
+ getAppInstallMetadataBuilder().setPackageName("test2").build();
+
+ assertThat(ATOMIC_FORMULA_1.matches(appInstallMetadata)).isFalse();
+
CompoundFormula compoundFormula =
new CompoundFormula(CompoundFormula.NOT, Arrays.asList(ATOMIC_FORMULA_1));
+ assertThat(compoundFormula.matches(appInstallMetadata)).isTrue();
+ }
+
+ @Test
+ public void testFormulaMatches_notTrue_false() {
AppInstallMetadata appInstallMetadata =
getAppInstallMetadataBuilder().setPackageName("test1").build();
- // validate assumptions about the metadata
- assertTrue(ATOMIC_FORMULA_1.isSatisfied(appInstallMetadata));
- assertFalse(compoundFormula.isSatisfied(appInstallMetadata));
+ assertThat(ATOMIC_FORMULA_1.matches(appInstallMetadata)).isTrue();
+
+ CompoundFormula compoundFormula =
+ new CompoundFormula(CompoundFormula.NOT, Arrays.asList(ATOMIC_FORMULA_1));
+ assertThat(compoundFormula.matches(appInstallMetadata)).isFalse();
}
@Test
- public void testIsSatisfiable_trueAndTrue_true() {
+ public void testFormulaMatches_trueAndTrue_true() {
CompoundFormula compoundFormula =
new CompoundFormula(
CompoundFormula.AND, Arrays.asList(ATOMIC_FORMULA_1, ATOMIC_FORMULA_2));
AppInstallMetadata appInstallMetadata =
getAppInstallMetadataBuilder().setPackageName("test1").setVersionCode(1).build();
// validate assumptions about the metadata
- assertTrue(ATOMIC_FORMULA_1.isSatisfied(appInstallMetadata));
- assertTrue(ATOMIC_FORMULA_2.isSatisfied(appInstallMetadata));
+ assertThat(ATOMIC_FORMULA_1.matches(appInstallMetadata)).isTrue();
+ assertThat(ATOMIC_FORMULA_1.matches(appInstallMetadata)).isTrue();
- assertTrue(compoundFormula.isSatisfied(appInstallMetadata));
+ assertThat(compoundFormula.matches(appInstallMetadata)).isTrue();
}
@Test
- public void testIsSatisfiable_trueAndFalse_false() {
+ public void testFormulaMatches_trueAndFalse_false() {
CompoundFormula compoundFormula =
new CompoundFormula(
CompoundFormula.AND, Arrays.asList(ATOMIC_FORMULA_1, ATOMIC_FORMULA_2));
AppInstallMetadata appInstallMetadata =
getAppInstallMetadataBuilder().setPackageName("test1").setVersionCode(2).build();
- // validate assumptions about the metadata
- assertTrue(ATOMIC_FORMULA_1.isSatisfied(appInstallMetadata));
- assertFalse(ATOMIC_FORMULA_2.isSatisfied(appInstallMetadata));
- assertFalse(compoundFormula.isSatisfied(appInstallMetadata));
+ assertThat(ATOMIC_FORMULA_1.matches(appInstallMetadata)).isTrue();
+ assertThat(ATOMIC_FORMULA_2.matches(appInstallMetadata)).isFalse();
+ assertThat(compoundFormula.matches(appInstallMetadata)).isFalse();
}
@Test
- public void testIsSatisfiable_falseAndTrue_false() {
+ public void testFormulaMatches_falseAndTrue_false() {
CompoundFormula compoundFormula =
new CompoundFormula(
CompoundFormula.AND, Arrays.asList(ATOMIC_FORMULA_1, ATOMIC_FORMULA_2));
AppInstallMetadata appInstallMetadata =
getAppInstallMetadataBuilder().setPackageName("test2").setVersionCode(1).build();
- // validate assumptions about the metadata
- assertFalse(ATOMIC_FORMULA_1.isSatisfied(appInstallMetadata));
- assertTrue(ATOMIC_FORMULA_2.isSatisfied(appInstallMetadata));
- assertFalse(compoundFormula.isSatisfied(appInstallMetadata));
+ assertThat(ATOMIC_FORMULA_1.matches(appInstallMetadata)).isFalse();
+ assertThat(ATOMIC_FORMULA_2.matches(appInstallMetadata)).isTrue();
+ assertThat(compoundFormula.matches(appInstallMetadata)).isFalse();
}
@Test
- public void testIsSatisfiable_falseAndFalse_false() {
+ public void testFormulaMatches_falseAndFalse_false() {
CompoundFormula compoundFormula =
new CompoundFormula(
CompoundFormula.AND, Arrays.asList(ATOMIC_FORMULA_1, ATOMIC_FORMULA_2));
AppInstallMetadata appInstallMetadata =
getAppInstallMetadataBuilder().setPackageName("test2").setVersionCode(2).build();
- // validate assumptions about the metadata
- assertFalse(ATOMIC_FORMULA_1.isSatisfied(appInstallMetadata));
- assertFalse(ATOMIC_FORMULA_2.isSatisfied(appInstallMetadata));
- assertFalse(compoundFormula.isSatisfied(appInstallMetadata));
+ assertThat(ATOMIC_FORMULA_1.matches(appInstallMetadata)).isFalse();
+ assertThat(ATOMIC_FORMULA_2.matches(appInstallMetadata)).isFalse();
+ assertThat(compoundFormula.matches(appInstallMetadata)).isFalse();
}
@Test
- public void testIsSatisfiable_trueOrTrue_true() {
+ public void testFormulaMatches_trueOrTrue_true() {
CompoundFormula compoundFormula =
new CompoundFormula(
CompoundFormula.OR, Arrays.asList(ATOMIC_FORMULA_1, ATOMIC_FORMULA_2));
AppInstallMetadata appInstallMetadata =
getAppInstallMetadataBuilder().setPackageName("test1").setVersionCode(1).build();
- // validate assumptions about the metadata
- assertTrue(ATOMIC_FORMULA_1.isSatisfied(appInstallMetadata));
- assertTrue(ATOMIC_FORMULA_2.isSatisfied(appInstallMetadata));
- assertTrue(compoundFormula.isSatisfied(appInstallMetadata));
+ assertThat(ATOMIC_FORMULA_1.matches(appInstallMetadata)).isTrue();
+ assertThat(ATOMIC_FORMULA_2.matches(appInstallMetadata)).isTrue();
+ assertThat(compoundFormula.matches(appInstallMetadata)).isTrue();
}
@Test
- public void testIsSatisfiable_trueOrFalse_true() {
+ public void testFormulaMatches_trueOrFalse_true() {
CompoundFormula compoundFormula =
new CompoundFormula(
CompoundFormula.OR, Arrays.asList(ATOMIC_FORMULA_1, ATOMIC_FORMULA_2));
AppInstallMetadata appInstallMetadata =
getAppInstallMetadataBuilder().setPackageName("test1").setVersionCode(2).build();
- // validate assumptions about the metadata
- assertTrue(ATOMIC_FORMULA_1.isSatisfied(appInstallMetadata));
- assertFalse(ATOMIC_FORMULA_2.isSatisfied(appInstallMetadata));
- assertTrue(compoundFormula.isSatisfied(appInstallMetadata));
+ assertThat(ATOMIC_FORMULA_1.matches(appInstallMetadata)).isTrue();
+ assertThat(ATOMIC_FORMULA_2.matches(appInstallMetadata)).isFalse();
+ assertThat(compoundFormula.matches(appInstallMetadata)).isTrue();
}
@Test
- public void testIsSatisfiable_falseOrTrue_true() {
+ public void testFormulaMatches_falseOrTrue_true() {
CompoundFormula compoundFormula =
new CompoundFormula(
CompoundFormula.OR, Arrays.asList(ATOMIC_FORMULA_1, ATOMIC_FORMULA_2));
AppInstallMetadata appInstallMetadata =
getAppInstallMetadataBuilder().setPackageName("test2").setVersionCode(1).build();
- // validate assumptions about the metadata
- assertFalse(ATOMIC_FORMULA_1.isSatisfied(appInstallMetadata));
- assertTrue(ATOMIC_FORMULA_2.isSatisfied(appInstallMetadata));
- assertTrue(compoundFormula.isSatisfied(appInstallMetadata));
+ assertThat(ATOMIC_FORMULA_1.matches(appInstallMetadata)).isFalse();
+ assertThat(ATOMIC_FORMULA_2.matches(appInstallMetadata)).isTrue();
+ assertThat(compoundFormula.matches(appInstallMetadata)).isTrue();
}
@Test
- public void testIsSatisfiable_falseOrFalse_false() {
+ public void testFormulaMatches_falseOrFalse_false() {
CompoundFormula compoundFormula =
new CompoundFormula(
CompoundFormula.OR, Arrays.asList(ATOMIC_FORMULA_1, ATOMIC_FORMULA_2));
AppInstallMetadata appInstallMetadata =
getAppInstallMetadataBuilder().setPackageName("test2").setVersionCode(2).build();
- // validate assumptions about the metadata
- assertFalse(ATOMIC_FORMULA_1.isSatisfied(appInstallMetadata));
- assertFalse(ATOMIC_FORMULA_2.isSatisfied(appInstallMetadata));
-
- assertFalse(compoundFormula.isSatisfied(appInstallMetadata));
- }
-
- @Test
- public void testParcelUnparcel() {
- CompoundFormula formula =
- new CompoundFormula(
- CompoundFormula.AND, Arrays.asList(ATOMIC_FORMULA_2, ATOMIC_FORMULA_1));
- Parcel p = Parcel.obtain();
- formula.writeToParcel(p, 0);
- p.setDataPosition(0);
- CompoundFormula newFormula = CompoundFormula.CREATOR.createFromParcel(p);
- assertEquals(formula, newFormula);
- }
-
- @Test
- public void testInvalidCompoundFormula_invalidConnector() {
- assertExpectException(
- IllegalArgumentException.class,
- /* expectedExceptionMessageRegex */ "Unknown connector: -1",
- () ->
- new CompoundFormula(
- /* connector= */ -1,
- Arrays.asList(ATOMIC_FORMULA_1, ATOMIC_FORMULA_2)));
+ assertThat(ATOMIC_FORMULA_1.matches(appInstallMetadata)).isFalse();
+ assertThat(ATOMIC_FORMULA_2.matches(appInstallMetadata)).isFalse();
+ assertThat(compoundFormula.matches(appInstallMetadata)).isFalse();
}
/** Returns a builder with all fields filled with some dummy data. */
diff --git a/core/tests/coretests/src/android/content/integrity/IntegrityFormulaTest.java b/core/tests/coretests/src/android/content/integrity/IntegrityFormulaTest.java
new file mode 100644
index 000000000000..c1806028f75b
--- /dev/null
+++ b/core/tests/coretests/src/android/content/integrity/IntegrityFormulaTest.java
@@ -0,0 +1,214 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.integrity;
+
+import static android.content.integrity.IntegrityFormula.COMPOUND_FORMULA_TAG;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.testng.Assert.assertThrows;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class IntegrityFormulaTest {
+
+ @Test
+ public void createEqualsFormula_packageName() {
+ String packageName = "com.test.app";
+ IntegrityFormula formula =
+ IntegrityFormula.PACKAGE_NAME.equalTo(packageName);
+
+ AtomicFormula.StringAtomicFormula stringAtomicFormula =
+ (AtomicFormula.StringAtomicFormula) formula;
+
+ assertThat(stringAtomicFormula.getKey()).isEqualTo(AtomicFormula.PACKAGE_NAME);
+ assertThat(stringAtomicFormula.getValue()).isEqualTo(packageName);
+ assertThat(stringAtomicFormula.getIsHashedValue()).isEqualTo(false);
+ }
+
+ @Test
+ public void createEqualsFormula_appCertificate() {
+ String appCertificate = "com.test.app";
+ IntegrityFormula formula =
+ IntegrityFormula.APP_CERTIFICATE.equalTo(appCertificate);
+
+ AtomicFormula.StringAtomicFormula stringAtomicFormula =
+ (AtomicFormula.StringAtomicFormula) formula;
+
+ assertThat(stringAtomicFormula.getKey()).isEqualTo(AtomicFormula.APP_CERTIFICATE);
+ assertThat(stringAtomicFormula.getValue()).doesNotMatch(appCertificate);
+ assertThat(stringAtomicFormula.getIsHashedValue()).isEqualTo(true);
+ }
+
+ @Test
+ public void createEqualsFormula_installerName() {
+ String installerName = "com.test.app";
+ IntegrityFormula formula =
+ IntegrityFormula.INSTALLER_NAME.equalTo(installerName);
+
+ AtomicFormula.StringAtomicFormula stringAtomicFormula =
+ (AtomicFormula.StringAtomicFormula) formula;
+
+ assertThat(stringAtomicFormula.getKey()).isEqualTo(AtomicFormula.INSTALLER_NAME);
+ assertThat(stringAtomicFormula.getValue()).isEqualTo(installerName);
+ assertThat(stringAtomicFormula.getIsHashedValue()).isEqualTo(false);
+ }
+
+ @Test
+ public void createEqualsFormula_installerCertificate() {
+ String installerCertificate = "com.test.app";
+ IntegrityFormula formula =
+ IntegrityFormula.INSTALLER_CERTIFICATE.equalTo(installerCertificate);
+
+ AtomicFormula.StringAtomicFormula stringAtomicFormula =
+ (AtomicFormula.StringAtomicFormula) formula;
+
+ assertThat(stringAtomicFormula.getKey()).isEqualTo(AtomicFormula.INSTALLER_CERTIFICATE);
+ assertThat(stringAtomicFormula.getValue()).doesNotMatch(installerCertificate);
+ assertThat(stringAtomicFormula.getIsHashedValue()).isEqualTo(true);
+ }
+
+ @Test
+ public void createEqualsFormula_versionCode() {
+ int versionCode = 12;
+ IntegrityFormula formula =
+ IntegrityFormula.VERSION_CODE.equalTo(versionCode);
+
+ AtomicFormula.LongAtomicFormula stringAtomicFormula =
+ (AtomicFormula.LongAtomicFormula) formula;
+
+ assertThat(stringAtomicFormula.getKey()).isEqualTo(AtomicFormula.VERSION_CODE);
+ assertThat(stringAtomicFormula.getValue()).isEqualTo(versionCode);
+ assertThat(stringAtomicFormula.getOperator()).isEqualTo(AtomicFormula.EQ);
+ }
+
+ @Test
+ public void createEqualsFormula_invalidKeyTypeForStringParameter() {
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> IntegrityFormula.PRE_INSTALLED.equalTo("wrongString"));
+ }
+
+ @Test
+ public void createEqualsFormula_invalidKeyTypeForLongParameter() {
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> IntegrityFormula.PACKAGE_NAME.equalTo(12));
+ }
+
+ @Test
+ public void createGreaterThanFormula_versionCode() {
+ int versionCode = 12;
+ IntegrityFormula formula =
+ IntegrityFormula.VERSION_CODE.greaterThan(versionCode);
+
+ AtomicFormula.LongAtomicFormula stringAtomicFormula =
+ (AtomicFormula.LongAtomicFormula) formula;
+
+ assertThat(stringAtomicFormula.getKey()).isEqualTo(AtomicFormula.VERSION_CODE);
+ assertThat(stringAtomicFormula.getValue()).isEqualTo(versionCode);
+ assertThat(stringAtomicFormula.getOperator()).isEqualTo(AtomicFormula.GT);
+ }
+
+ @Test
+ public void createGreaterThanFormula_invalidKeyTypeForLongParameter() {
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> IntegrityFormula.PACKAGE_NAME.greaterThan(12));
+ }
+
+ @Test
+ public void createGreaterThanOrEqualsToFormula_versionCode() {
+ int versionCode = 12;
+ IntegrityFormula formula =
+ IntegrityFormula.VERSION_CODE.greaterThanOrEquals(versionCode);
+
+ AtomicFormula.LongAtomicFormula stringAtomicFormula =
+ (AtomicFormula.LongAtomicFormula) formula;
+
+ assertThat(stringAtomicFormula.getKey()).isEqualTo(AtomicFormula.VERSION_CODE);
+ assertThat(stringAtomicFormula.getValue()).isEqualTo(versionCode);
+ assertThat(stringAtomicFormula.getOperator()).isEqualTo(AtomicFormula.GTE);
+ }
+
+ @Test
+ public void createGreaterThanOrEqualsToFormula_invalidKeyTypeForLongParameter() {
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> IntegrityFormula.PACKAGE_NAME.greaterThanOrEquals(12));
+ }
+
+ @Test
+ public void createIsTrueFormula_preInstalled() {
+ IntegrityFormula formula = IntegrityFormula.PRE_INSTALLED.equalTo(true);
+
+ AtomicFormula.BooleanAtomicFormula stringAtomicFormula =
+ (AtomicFormula.BooleanAtomicFormula) formula;
+
+ assertThat(stringAtomicFormula.getKey()).isEqualTo(AtomicFormula.PRE_INSTALLED);
+ assertThat(stringAtomicFormula.getValue()).isTrue();
+ }
+
+ @Test
+ public void createIsTrueFormula_invalidKeyTypeForBoolParameter() {
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> IntegrityFormula.PACKAGE_NAME.equalTo(true));
+ }
+
+ @Test
+ public void createAllFormula() {
+ String packageName = "com.test.package";
+ String certificateName = "certificate";
+ IntegrityFormula formula1 =
+ IntegrityFormula.PACKAGE_NAME.equalTo(packageName);
+ IntegrityFormula formula2 =
+ IntegrityFormula.APP_CERTIFICATE.equalTo(certificateName);
+
+ IntegrityFormula compoundFormula = IntegrityFormula.all(formula1, formula2);
+
+ assertThat(compoundFormula.getTag()).isEqualTo(COMPOUND_FORMULA_TAG);
+ }
+
+ @Test
+ public void createAnyFormula() {
+ String packageName = "com.test.package";
+ String certificateName = "certificate";
+ IntegrityFormula formula1 =
+ IntegrityFormula.PACKAGE_NAME.equalTo(packageName);
+ IntegrityFormula formula2 =
+ IntegrityFormula.APP_CERTIFICATE.equalTo(certificateName);
+
+ IntegrityFormula compoundFormula = IntegrityFormula.any(formula1, formula2);
+
+ assertThat(compoundFormula.getTag()).isEqualTo(COMPOUND_FORMULA_TAG);
+ }
+
+ @Test
+ public void createNotFormula() {
+ String packageName = "com.test.package";
+
+ IntegrityFormula compoundFormula =
+ IntegrityFormula.not(
+ IntegrityFormula.PACKAGE_NAME.equalTo(packageName));
+
+ assertThat(compoundFormula.getTag()).isEqualTo(COMPOUND_FORMULA_TAG);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/integrity/IntegrityUtilsTest.java b/core/tests/coretests/src/android/content/integrity/IntegrityUtilsTest.java
index ac7f8f98ab9b..639adf6a05d7 100644
--- a/services/tests/servicestests/src/com/android/server/integrity/IntegrityUtilsTest.java
+++ b/core/tests/coretests/src/android/content/integrity/IntegrityUtilsTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,48 +14,44 @@
* limitations under the License.
*/
-package com.android.server.integrity;
+package android.content.integrity;
-import static com.android.server.integrity.IntegrityUtils.getBytesFromHexDigest;
-import static com.android.server.integrity.IntegrityUtils.getHexDigest;
-import static com.android.server.testutils.TestUtils.assertExpectException;
+import static com.google.common.truth.Truth.assertThat;
-import static org.junit.Assert.assertArrayEquals;
-import static org.junit.Assert.assertEquals;
-
-import androidx.test.runner.AndroidJUnit4;
+import static org.testng.internal.junit.ArrayAsserts.assertArrayEquals;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
-/** Unit test for {@link com.android.server.integrity.IntegrityUtils} */
-@RunWith(AndroidJUnit4.class)
+/** Unit test for {@link IntegrityUtils} */
+@RunWith(JUnit4.class)
public class IntegrityUtilsTest {
private static final String HEX_DIGEST = "1234567890ABCDEF";
private static final byte[] BYTES =
- new byte[] {0x12, 0x34, 0x56, 0x78, (byte) 0x90, (byte) 0xAB, (byte) 0xCD, (byte) 0xEF};
+ new byte[]{0x12, 0x34, 0x56, 0x78, (byte) 0x90, (byte) 0xAB, (byte) 0xCD, (byte) 0xEF};
@Test
public void testGetBytesFromHexDigest() {
- assertArrayEquals(BYTES, getBytesFromHexDigest(HEX_DIGEST));
+ assertArrayEquals(BYTES, IntegrityUtils.getBytesFromHexDigest(HEX_DIGEST));
}
@Test
public void testGetHexDigest() {
- assertEquals(HEX_DIGEST, getHexDigest(BYTES));
+ assertThat(IntegrityUtils.getHexDigest(BYTES)).isEqualTo(HEX_DIGEST);
}
@Test
public void testInvalidHexDigest() {
- assertExpectException(
+ TestUtils.assertExpectException(
IllegalArgumentException.class,
"must have even length",
- () -> getBytesFromHexDigest("ABC"));
+ () -> IntegrityUtils.getBytesFromHexDigest("ABC"));
- assertExpectException(
+ TestUtils.assertExpectException(
IllegalArgumentException.class,
"Invalid hex char",
- () -> getBytesFromHexDigest("GH"));
+ () -> IntegrityUtils.getBytesFromHexDigest("GH"));
}
}
diff --git a/core/tests/coretests/src/android/content/integrity/RuleTest.java b/core/tests/coretests/src/android/content/integrity/RuleTest.java
index 19e74e6b93cc..8c4cfca39e90 100644
--- a/core/tests/coretests/src/android/content/integrity/RuleTest.java
+++ b/core/tests/coretests/src/android/content/integrity/RuleTest.java
@@ -35,11 +35,15 @@ public class RuleTest {
private static final @Rule.Effect int DENY_EFFECT = Rule.DENY;
private static final String PACKAGE_NAME = "com.test.app";
private static final String APP_CERTIFICATE = "test_cert";
- private static final Formula PACKAGE_NAME_ATOMIC_FORMULA =
- new AtomicFormula.StringAtomicFormula(AtomicFormula.PACKAGE_NAME, PACKAGE_NAME,
+ private static final IntegrityFormula PACKAGE_NAME_ATOMIC_FORMULA =
+ new AtomicFormula.StringAtomicFormula(
+ AtomicFormula.PACKAGE_NAME,
+ PACKAGE_NAME,
/* isHashedValue= */ false);
- private static final Formula APP_CERTIFICATE_ATOMIC_FORMULA =
- new AtomicFormula.StringAtomicFormula(AtomicFormula.APP_CERTIFICATE, APP_CERTIFICATE,
+ private static final IntegrityFormula APP_CERTIFICATE_ATOMIC_FORMULA =
+ new AtomicFormula.StringAtomicFormula(
+ AtomicFormula.APP_CERTIFICATE,
+ APP_CERTIFICATE,
/* isHashedValue= */ false);
@Test
diff --git a/core/tests/coretests/src/android/content/pm/PackageParserTest.java b/core/tests/coretests/src/android/content/pm/PackageParserTest.java
index 47c4286b2afb..dfd762b17307 100644
--- a/core/tests/coretests/src/android/content/pm/PackageParserTest.java
+++ b/core/tests/coretests/src/android/content/pm/PackageParserTest.java
@@ -470,7 +470,7 @@ public class PackageParserTest {
PackageParser.collectCertificates(p, false);
PackageInfo pi = PackageParser.generatePackageInfo(p, apexInfo, flags);
- assertEquals("com.google.android.tzdata2", pi.applicationInfo.packageName);
+ assertEquals("com.google.android.tzdata", pi.applicationInfo.packageName);
assertTrue(pi.applicationInfo.enabled);
assertEquals(28, pi.applicationInfo.targetSdkVersion);
assertEquals(191000070, pi.applicationInfo.longVersionCode);
@@ -479,7 +479,7 @@ public class PackageParserTest {
assertEquals("Bundle[{com.android.vending.derived.apk.id=1}]",
pi.applicationInfo.metaData.toString());
- assertEquals("com.google.android.tzdata2", pi.packageName);
+ assertEquals("com.google.android.tzdata", pi.packageName);
assertEquals(191000070, pi.getLongVersionCode());
assertNotNull(pi.signingInfo);
assertTrue(pi.signingInfo.getApkContentsSigners().length > 0);
diff --git a/core/tests/coretests/src/android/service/controls/ControlProviderServiceTest.java b/core/tests/coretests/src/android/service/controls/ControlProviderServiceTest.java
new file mode 100644
index 000000000000..2648a0644dc1
--- /dev/null
+++ b/core/tests/coretests/src/android/service/controls/ControlProviderServiceTest.java
@@ -0,0 +1,242 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.controls;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.PendingIntent;
+import android.content.IIntentSender;
+import android.content.Intent;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.service.controls.actions.CommandAction;
+import android.service.controls.actions.ControlAction;
+import android.service.controls.actions.ControlActionWrapper;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.Flow.Publisher;
+import java.util.concurrent.Flow.Subscriber;
+import java.util.concurrent.Flow.Subscription;
+import java.util.function.Consumer;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class ControlProviderServiceTest {
+
+ private IBinder mToken = new Binder();
+ @Mock
+ private IControlsActionCallback.Stub mActionCallback;
+ @Mock
+ private IControlsLoadCallback.Stub mLoadCallback;
+ @Mock
+ private IControlsSubscriber.Stub mSubscriber;
+ @Mock
+ private IIntentSender mIIntentSender;
+
+ private PendingIntent mPendingIntent;
+ private FakeControlsProviderService mControlsProviderService;
+
+ private IControlsProvider mControlsProvider;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ when(mActionCallback.asBinder()).thenCallRealMethod();
+ when(mActionCallback.queryLocalInterface(any())).thenReturn(mActionCallback);
+ when(mLoadCallback.asBinder()).thenCallRealMethod();
+ when(mLoadCallback.queryLocalInterface(any())).thenReturn(mLoadCallback);
+ when(mSubscriber.asBinder()).thenCallRealMethod();
+ when(mSubscriber.queryLocalInterface(any())).thenReturn(mSubscriber);
+
+ Bundle b = new Bundle();
+ b.putBinder(ControlsProviderService.CALLBACK_TOKEN, mToken);
+ Intent intent = new Intent();
+ intent.putExtra(ControlsProviderService.CALLBACK_BUNDLE, b);
+
+ mPendingIntent = new PendingIntent(mIIntentSender);
+
+ mControlsProviderService = new FakeControlsProviderService();
+ mControlsProvider = IControlsProvider.Stub.asInterface(
+ mControlsProviderService.onBind(intent));
+ }
+
+ @Test
+ public void testOnLoad_allStateless() throws RemoteException {
+ Control control1 = new Control.StatelessBuilder("TEST_ID", mPendingIntent).build();
+ Control control2 = new Control.StatelessBuilder("TEST_ID_2", mPendingIntent)
+ .setDeviceType(DeviceTypes.TYPE_AIR_FRESHENER).build();
+
+ @SuppressWarnings("unchecked")
+ ArgumentCaptor<List<Control>> captor = ArgumentCaptor.forClass(List.class);
+
+ ArrayList<Control> list = new ArrayList<>();
+ list.add(control1);
+ list.add(control2);
+
+ mControlsProviderService.setControls(list);
+ mControlsProvider.load(mLoadCallback);
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+
+ verify(mLoadCallback).accept(eq(mToken), captor.capture());
+ List<Control> l = captor.getValue();
+ assertEquals(2, l.size());
+ assertTrue(equals(control1, l.get(0)));
+ assertTrue(equals(control2, l.get(1)));
+ }
+
+ @Test
+ public void testOnLoad_statefulConvertedToStateless() throws RemoteException {
+ Control control = new Control.StatefulBuilder("TEST_ID", mPendingIntent)
+ .setTitle("TEST_TITLE")
+ .setStatus(Control.STATUS_OK)
+ .build();
+ Control statelessControl = new Control.StatelessBuilder(control).build();
+
+ @SuppressWarnings("unchecked")
+ ArgumentCaptor<List<Control>> captor = ArgumentCaptor.forClass(List.class);
+
+ ArrayList<Control> list = new ArrayList<>();
+ list.add(control);
+
+ mControlsProviderService.setControls(list);
+ mControlsProvider.load(mLoadCallback);
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+
+ verify(mLoadCallback).accept(eq(mToken), captor.capture());
+ List<Control> l = captor.getValue();
+ assertEquals(1, l.size());
+ assertFalse(equals(control, l.get(0)));
+ assertTrue(equals(statelessControl, l.get(0)));
+ assertEquals(Control.STATUS_UNKNOWN, l.get(0).getStatus());
+ }
+
+ @Test
+ public void testSubscribe() throws RemoteException {
+ Control control = new Control.StatefulBuilder("TEST_ID", mPendingIntent)
+ .setTitle("TEST_TITLE")
+ .setStatus(Control.STATUS_OK)
+ .build();
+
+ @SuppressWarnings("unchecked")
+ ArgumentCaptor<Control> controlCaptor =
+ ArgumentCaptor.forClass(Control.class);
+ ArgumentCaptor<IControlsSubscription.Stub> subscriptionCaptor =
+ ArgumentCaptor.forClass(IControlsSubscription.Stub.class);
+
+ ArrayList<Control> list = new ArrayList<>();
+ list.add(control);
+
+ mControlsProviderService.setControls(list);
+
+ mControlsProvider.subscribe(new ArrayList<String>(), mSubscriber);
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+
+ verify(mSubscriber).onSubscribe(eq(mToken), subscriptionCaptor.capture());
+ subscriptionCaptor.getValue().request(1);
+
+ verify(mSubscriber).onNext(eq(mToken), controlCaptor.capture());
+ Control c = controlCaptor.getValue();
+ assertTrue(equals(c, list.get(0)));
+ }
+
+ @Test
+ public void testOnAction() throws RemoteException {
+ mControlsProvider.action("TEST_ID", new ControlActionWrapper(
+ new CommandAction("", null)), mActionCallback);
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+
+ verify(mActionCallback).accept(mToken, "TEST_ID",
+ ControlAction.RESPONSE_OK);
+ }
+
+ private static boolean equals(Control c1, Control c2) {
+ if (c1 == c2) return true;
+ if (c1 == null || c2 == null) return false;
+ return Objects.equals(c1.getControlId(), c2.getControlId())
+ && c1.getDeviceType() == c2.getDeviceType()
+ && Objects.equals(c1.getTitle(), c2.getTitle())
+ && Objects.equals(c1.getSubtitle(), c2.getSubtitle())
+ && Objects.equals(c1.getStructure(), c2.getStructure())
+ && Objects.equals(c1.getZone(), c2.getZone())
+ && Objects.equals(c1.getAppIntent(), c2.getAppIntent())
+ && Objects.equals(c1.getCustomIcon(), c2.getCustomIcon())
+ && Objects.equals(c1.getCustomColor(), c2.getCustomColor())
+ && c1.getStatus() == c2.getStatus()
+ && Objects.equals(c1.getControlTemplate(), c2.getControlTemplate())
+ && Objects.equals(c1.getStatusText(), c2.getStatusText());
+ }
+
+ static class FakeControlsProviderService extends ControlsProviderService {
+
+ private List<Control> mControls;
+
+ public void setControls(List<Control> controls) {
+ mControls = controls;
+ }
+
+ @Override
+ public void loadAvailableControls(Consumer<List<Control>> cb) {
+ cb.accept(mControls);
+ }
+
+ @Override
+ public Publisher<Control> publisherFor(List<String> ids) {
+ return new Publisher<Control>() {
+ public void subscribe(final Subscriber s) {
+ s.onSubscribe(new Subscription() {
+ public void request(long n) {
+ for (Control c : mControls) {
+ s.onNext(c);
+ }
+ }
+ public void cancel() {}
+ });
+ }
+ };
+ }
+
+ @Override
+ public void performControlAction(String controlId, ControlAction action,
+ Consumer<Integer> cb) {
+ cb.accept(ControlAction.RESPONSE_OK);
+ }
+ }
+}
+
+
diff --git a/core/tests/coretests/src/android/service/controls/ControlActionTest.java b/core/tests/coretests/src/android/service/controls/actions/ControlActionTest.java
index d0264da206d9..10a7b76390ec 100644
--- a/core/tests/coretests/src/android/service/controls/ControlActionTest.java
+++ b/core/tests/coretests/src/android/service/controls/actions/ControlActionTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.service.controls;
+package android.service.controls.actions;
import static junit.framework.Assert.assertTrue;
@@ -22,12 +22,6 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import android.os.Parcel;
-import android.service.controls.actions.BooleanAction;
-import android.service.controls.actions.CommandAction;
-import android.service.controls.actions.ControlAction;
-import android.service.controls.actions.FloatAction;
-import android.service.controls.actions.ModeAction;
-import android.service.controls.actions.MultiFloatAction;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -97,9 +91,9 @@ public class ControlActionTest {
assertNotNull(parcel);
parcel.setDataPosition(0);
- toParcel.writeToParcel(parcel, 0);
+ new ControlActionWrapper(toParcel).writeToParcel(parcel, 0);
parcel.setDataPosition(0);
- return ControlAction.CREATOR.createFromParcel(parcel);
+ return ControlActionWrapper.CREATOR.createFromParcel(parcel).getWrappedAction();
}
}
diff --git a/core/tests/coretests/src/android/service/controls/ControlTemplateTest.java b/core/tests/coretests/src/android/service/controls/templates/ControlTemplateTest.java
index 2756891c7c36..c9b5eecdcd21 100644
--- a/core/tests/coretests/src/android/service/controls/ControlTemplateTest.java
+++ b/core/tests/coretests/src/android/service/controls/templates/ControlTemplateTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.service.controls;
+package android.service.controls.templates;
import static junit.framework.Assert.assertTrue;
@@ -24,16 +24,6 @@ import static org.junit.Assert.assertNotNull;
import android.annotation.DrawableRes;
import android.graphics.drawable.Icon;
import android.os.Parcel;
-import android.service.controls.templates.ControlButton;
-import android.service.controls.templates.ControlTemplate;
-import android.service.controls.templates.CoordinatedRangeTemplate;
-import android.service.controls.templates.DiscreteToggleTemplate;
-import android.service.controls.templates.RangeTemplate;
-import android.service.controls.templates.StatelessTemplate;
-import android.service.controls.templates.TemperatureControlTemplate;
-import android.service.controls.templates.ThumbnailTemplate;
-import android.service.controls.templates.ToggleRangeTemplate;
-import android.service.controls.templates.ToggleTemplate;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -65,8 +55,7 @@ public class ControlTemplateTest {
@Test
public void testUnparcelingCorrectClass_none() {
- ControlTemplate
- toParcel = ControlTemplate.NO_TEMPLATE;
+ ControlTemplate toParcel = ControlTemplate.NO_TEMPLATE;
ControlTemplate fromParcel = parcelAndUnparcel(toParcel);
@@ -75,8 +64,7 @@ public class ControlTemplateTest {
@Test
public void testUnparcelingCorrectClass_toggle() {
- ControlTemplate
- toParcel = new android.service.controls.templates.ToggleTemplate(TEST_ID, mControlButton);
+ ControlTemplate toParcel = new ToggleTemplate(TEST_ID, mControlButton);
ControlTemplate fromParcel = parcelAndUnparcel(toParcel);
@@ -86,8 +74,7 @@ public class ControlTemplateTest {
@Test
public void testUnparcelingCorrectClass_range() {
- ControlTemplate
- toParcel = new RangeTemplate(TEST_ID, 0, 2, 1, 1, "%f");
+ ControlTemplate toParcel = new RangeTemplate(TEST_ID, 0, 2, 1, 1, "%f");
ControlTemplate fromParcel = parcelAndUnparcel(toParcel);
@@ -117,8 +104,7 @@ public class ControlTemplateTest {
@Test
public void testUnparcelingCorrectClass_thumbnail() {
- ControlTemplate
- toParcel = new ThumbnailTemplate(TEST_ID, mIcon, TEST_ACTION_DESCRIPTION);
+ ControlTemplate toParcel = new ThumbnailTemplate(TEST_ID, mIcon, TEST_ACTION_DESCRIPTION);
ControlTemplate fromParcel = parcelAndUnparcel(toParcel);
@@ -140,7 +126,7 @@ public class ControlTemplateTest {
@Test
public void testUnparcelingCorrectClass_coordRange() {
ControlTemplate toParcel =
- new CoordinatedRangeTemplate(TEST_ID,0.1f, 0, 1, 0.5f, 1, 2, 1.5f, 0.1f, "%f");
+ new CoordinatedRangeTemplate(TEST_ID, 0.1f, 0, 1, 0.5f, 1, 2, 1.5f, 0.1f, "%f");
ControlTemplate fromParcel = parcelAndUnparcel(toParcel);
assertEquals(ControlTemplate.TYPE_COORD_RANGE, fromParcel.getTemplateType());
assertTrue(fromParcel instanceof CoordinatedRangeTemplate);
@@ -149,7 +135,7 @@ public class ControlTemplateTest {
@Test
public void testCoordRangeParameters_negativeMinGap() {
CoordinatedRangeTemplate template =
- new CoordinatedRangeTemplate(TEST_ID,-0.1f, 0, 1, 0.5f, 1, 2, 1.5f, 0.1f, "%f");
+ new CoordinatedRangeTemplate(TEST_ID, -0.1f, 0, 1, 0.5f, 1, 2, 1.5f, 0.1f, "%f");
assertEquals(0, template.getMinGap(), 0);
}
@@ -176,9 +162,8 @@ public class ControlTemplateTest {
@Test
public void testUnparcelingCorrectClass_toggleRange() {
- ControlTemplate toParcel =
- new ToggleRangeTemplate(TEST_ID, mControlButton,
- new RangeTemplate(TEST_ID, 0, 2, 1, 1, "%f"));
+ ControlTemplate toParcel = new ToggleRangeTemplate(TEST_ID, mControlButton,
+ new RangeTemplate(TEST_ID, 0, 2, 1, 1, "%f"));
ControlTemplate fromParcel = parcelAndUnparcel(toParcel);
@@ -198,11 +183,12 @@ public class ControlTemplateTest {
@Test
public void testUnparcelingCorrectClass_thermostat() {
- ControlTemplate toParcel = new TemperatureControlTemplate(TEST_ID,
- new ToggleTemplate("", mControlButton),
- TemperatureControlTemplate.MODE_OFF,
- TemperatureControlTemplate.MODE_OFF,
- TemperatureControlTemplate.FLAG_MODE_OFF);
+ ControlTemplate toParcel = new TemperatureControlTemplate(
+ TEST_ID,
+ new ToggleTemplate("", mControlButton),
+ TemperatureControlTemplate.MODE_OFF,
+ TemperatureControlTemplate.MODE_OFF,
+ TemperatureControlTemplate.FLAG_MODE_OFF);
ControlTemplate fromParcel = parcelAndUnparcel(toParcel);
@@ -212,48 +198,70 @@ public class ControlTemplateTest {
@Test
public void testThermostatParams_wrongMode() {
- TemperatureControlTemplate thermostat = new TemperatureControlTemplate(TEST_ID, ControlTemplate.NO_TEMPLATE, -1,
- TemperatureControlTemplate.MODE_OFF, TemperatureControlTemplate.FLAG_MODE_OFF);
+ TemperatureControlTemplate thermostat = new TemperatureControlTemplate(
+ TEST_ID,
+ ControlTemplate.NO_TEMPLATE,
+ -1,
+ TemperatureControlTemplate.MODE_OFF,
+ TemperatureControlTemplate.FLAG_MODE_OFF);
assertEquals(TemperatureControlTemplate.MODE_UNKNOWN, thermostat.getCurrentMode());
- thermostat = new TemperatureControlTemplate(TEST_ID, ControlTemplate.NO_TEMPLATE, 100,
- TemperatureControlTemplate.MODE_OFF, TemperatureControlTemplate.FLAG_MODE_OFF);
+ thermostat = new TemperatureControlTemplate(
+ TEST_ID,
+ ControlTemplate.NO_TEMPLATE,
+ 100,
+ TemperatureControlTemplate.MODE_OFF,
+ TemperatureControlTemplate.FLAG_MODE_OFF);
assertEquals(TemperatureControlTemplate.MODE_UNKNOWN, thermostat.getCurrentMode());
}
@Test
public void testThermostatParams_wrongActiveMode() {
- TemperatureControlTemplate thermostat = new TemperatureControlTemplate(TEST_ID, ControlTemplate.NO_TEMPLATE,
- TemperatureControlTemplate.MODE_OFF,-1, TemperatureControlTemplate.FLAG_MODE_OFF);
+ TemperatureControlTemplate thermostat = new TemperatureControlTemplate(
+ TEST_ID,
+ ControlTemplate.NO_TEMPLATE,
+ TemperatureControlTemplate.MODE_OFF,
+ -1,
+ TemperatureControlTemplate.FLAG_MODE_OFF);
assertEquals(TemperatureControlTemplate.MODE_UNKNOWN, thermostat.getCurrentActiveMode());
- thermostat = new TemperatureControlTemplate(TEST_ID, ControlTemplate.NO_TEMPLATE,
- TemperatureControlTemplate.MODE_OFF,100, TemperatureControlTemplate.FLAG_MODE_OFF);
+ thermostat = new TemperatureControlTemplate(
+ TEST_ID,
+ ControlTemplate.NO_TEMPLATE,
+ TemperatureControlTemplate.MODE_OFF,
+ 100,
+ TemperatureControlTemplate.FLAG_MODE_OFF);
assertEquals(TemperatureControlTemplate.MODE_UNKNOWN, thermostat.getCurrentActiveMode());
}
@Test(expected = IllegalArgumentException.class)
public void testThermostatParams_wrongFlags_currentMode() {
- new TemperatureControlTemplate(TEST_ID, ControlTemplate.NO_TEMPLATE, TemperatureControlTemplate.MODE_HEAT,
- TemperatureControlTemplate.MODE_OFF, TemperatureControlTemplate.FLAG_MODE_OFF);
+ new TemperatureControlTemplate(
+ TEST_ID,
+ ControlTemplate.NO_TEMPLATE,
+ TemperatureControlTemplate.MODE_HEAT,
+ TemperatureControlTemplate.MODE_OFF,
+ TemperatureControlTemplate.FLAG_MODE_OFF);
}
@Test(expected = IllegalArgumentException.class)
public void testThermostatParams_wrongFlags_currentActiveMode() {
- new TemperatureControlTemplate(TEST_ID, ControlTemplate.NO_TEMPLATE, TemperatureControlTemplate.MODE_HEAT,
- TemperatureControlTemplate.MODE_OFF, TemperatureControlTemplate.FLAG_MODE_HEAT);
+ new TemperatureControlTemplate(TEST_ID,
+ ControlTemplate.NO_TEMPLATE,
+ TemperatureControlTemplate.MODE_HEAT,
+ TemperatureControlTemplate.MODE_OFF,
+ TemperatureControlTemplate.FLAG_MODE_HEAT);
}
- private ControlTemplate parcelAndUnparcel(
- ControlTemplate toParcel) {
+ private ControlTemplate parcelAndUnparcel(ControlTemplate toParcel) {
Parcel parcel = Parcel.obtain();
assertNotNull(parcel);
parcel.setDataPosition(0);
- toParcel.writeToParcel(parcel, 0);
+ new ControlTemplateWrapper(toParcel).writeToParcel(parcel, 0);
parcel.setDataPosition(0);
- return ControlTemplate.CREATOR.createFromParcel(parcel);
+ return ControlTemplateWrapper.CREATOR.createFromParcel(parcel).getWrappedTemplate();
}
}
diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
index 411868d8befe..d4c362143bf0 100644
--- a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
@@ -92,7 +92,9 @@ import org.mockito.Mockito;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.function.Function;
/**
@@ -976,6 +978,8 @@ public class ChooserActivityTest {
.launchActivity(Intent.createChooser(sendIntent, null));
// Insert the direct share target
+ Map<ChooserTarget, ShortcutInfo> directShareToShortcutInfos = new HashMap<>();
+ directShareToShortcutInfos.put(serviceTargets.get(0), null);
InstrumentationRegistry.getInstrumentation().runOnMainSync(
() -> activity.getAdapter().addServiceResults(
activity.createTestDisplayResolveInfo(sendIntent,
@@ -985,7 +989,8 @@ public class ChooserActivityTest {
sendIntent,
/* resolveInfoPresentationGetter */ null),
serviceTargets,
- TARGET_TYPE_CHOOSER_TARGET)
+ TARGET_TYPE_CHOOSER_TARGET,
+ directShareToShortcutInfos)
);
// Thread.sleep shouldn't be a thing in an integration test but it's
@@ -1044,6 +1049,8 @@ public class ChooserActivityTest {
.launchActivity(Intent.createChooser(sendIntent, null));
// Insert the direct share target
+ Map<ChooserTarget, ShortcutInfo> directShareToShortcutInfos = new HashMap<>();
+ directShareToShortcutInfos.put(serviceTargets.get(0), null);
InstrumentationRegistry.getInstrumentation().runOnMainSync(
() -> activity.getAdapter().addServiceResults(
activity.createTestDisplayResolveInfo(sendIntent,
@@ -1053,7 +1060,8 @@ public class ChooserActivityTest {
sendIntent,
/* resolveInfoPresentationGetter */ null),
serviceTargets,
- TARGET_TYPE_CHOOSER_TARGET)
+ TARGET_TYPE_CHOOSER_TARGET,
+ directShareToShortcutInfos)
);
// Thread.sleep shouldn't be a thing in an integration test but it's
// necessary here because of the way the code is structured
@@ -1128,6 +1136,8 @@ public class ChooserActivityTest {
final ChooserWrapperActivity activity = mActivityRule
.launchActivity(Intent.createChooser(sendIntent, null));
// Insert the direct share target
+ Map<ChooserTarget, ShortcutInfo> directShareToShortcutInfos = new HashMap<>();
+ directShareToShortcutInfos.put(serviceTargets.get(0), null);
InstrumentationRegistry.getInstrumentation().runOnMainSync(
() -> activity.getAdapter().addServiceResults(
activity.createTestDisplayResolveInfo(sendIntent,
@@ -1137,7 +1147,8 @@ public class ChooserActivityTest {
sendIntent,
/* resolveInfoPresentationGetter */ null),
serviceTargets,
- TARGET_TYPE_CHOOSER_TARGET)
+ TARGET_TYPE_CHOOSER_TARGET,
+ directShareToShortcutInfos)
);
// Thread.sleep shouldn't be a thing in an integration test but it's
// necessary here because of the way the code is structured
diff --git a/core/tests/screenshothelpertests/src/com/android/internal/util/ScreenshotHelperTest.java b/core/tests/screenshothelpertests/src/com/android/internal/util/ScreenshotHelperTest.java
index 1472b9034249..cd6b3af5fa6d 100644
--- a/core/tests/screenshothelpertests/src/com/android/internal/util/ScreenshotHelperTest.java
+++ b/core/tests/screenshothelpertests/src/com/android/internal/util/ScreenshotHelperTest.java
@@ -31,6 +31,9 @@ import static org.mockito.Mockito.mock;
import android.content.Context;
import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.Insets;
+import android.graphics.Rect;
import android.os.Handler;
import android.os.Looper;
@@ -85,6 +88,13 @@ public final class ScreenshotHelperTest {
}
@Test
+ public void testProvidedImageScreenshot() {
+ mScreenshotHelper.provideScreenshot(
+ Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888), new Rect(),
+ Insets.of(0, 0, 0, 0), 1, mHandler, null);
+ }
+
+ @Test
public void testScreenshotTimesOut() {
long timeoutMs = 10;
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 3677b8f9e66e..133b1aed68f1 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -200,6 +200,7 @@ applications that come with the platform
<permission name="android.permission.CLEAR_APP_CACHE"/>
<permission name="android.permission.CONNECTIVITY_INTERNAL"/>
<permission name="android.permission.START_ACTIVITIES_FROM_BACKGROUND"/>
+ <permission name="android.permission.WRITE_MEDIA_STORAGE"/>
<permission name="android.permission.UPDATE_APP_OPS_STATS"/>
<permission name="android.permission.UPDATE_DEVICE_STATS"/>
</privapp-permissions>
@@ -357,6 +358,11 @@ applications that come with the platform
<permission name="android.permission.CONTROL_INCALL_EXPERIENCE"/>
<!-- Permission required for Tethering CTS tests. -->
<permission name="android.permission.TETHER_PRIVILEGED"/>
+ <!-- Permissions required to test ambient display. -->
+ <permission name="android.permission.READ_DREAM_STATE" />
+ <permission name="android.permission.WRITE_DREAM_STATE" />
+ <!-- Permission required to test lights control APIs. -->
+ <permission name="android.permission.CONTROL_DEVICE_LIGHTS" />
</privapp-permissions>
<privapp-permissions package="com.android.statementservice">
diff --git a/docs/html/reference/images/camera2/metadata/android.scaler.rotateAndCrop/crop-region-rotate-90-43-ratio.png b/docs/html/reference/images/camera2/metadata/android.scaler.rotateAndCrop/crop-region-rotate-90-43-ratio.png
new file mode 100644
index 000000000000..2633996521b5
--- /dev/null
+++ b/docs/html/reference/images/camera2/metadata/android.scaler.rotateAndCrop/crop-region-rotate-90-43-ratio.png
Binary files differ
diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp
index 3c402e996218..7c0efe16838e 100644
--- a/libs/hwui/hwui/Bitmap.cpp
+++ b/libs/hwui/hwui/Bitmap.cpp
@@ -471,36 +471,14 @@ BitmapPalette Bitmap::computePalette(const SkImageInfo& info, const void* addr,
return BitmapPalette::Unknown;
}
-Bitmap::CompressResult Bitmap::compress(JavaCompressFormat format, int32_t quality,
- SkWStream* stream) {
+bool Bitmap::compress(JavaCompressFormat format, int32_t quality, SkWStream* stream) {
SkBitmap skbitmap;
getSkBitmap(&skbitmap);
return compress(skbitmap, format, quality, stream);
}
-Bitmap::CompressResult Bitmap::compress(const SkBitmap& bitmap, JavaCompressFormat format,
- int32_t quality, SkWStream* stream) {
- SkBitmap skbitmap = bitmap;
- if (skbitmap.colorType() == kRGBA_F16_SkColorType) {
- // Convert to P3 before encoding. This matches
- // SkAndroidCodec::computeOutputColorSpace for wide gamuts. Now that F16
- // could already be P3, we still want to convert to 8888.
- auto cs = SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, SkNamedGamut::kDCIP3);
- auto info = skbitmap.info().makeColorType(kRGBA_8888_SkColorType)
- .makeColorSpace(std::move(cs));
- SkBitmap p3;
- if (!p3.tryAllocPixels(info)) {
- return CompressResult::AllocationFailed;
- }
-
- SkPixmap pm;
- SkAssertResult(p3.peekPixels(&pm)); // should always work if tryAllocPixels() did.
- if (!skbitmap.readPixels(pm)) {
- return CompressResult::Error;
- }
- skbitmap = p3;
- }
-
+bool Bitmap::compress(const SkBitmap& bitmap, JavaCompressFormat format,
+ int32_t quality, SkWStream* stream) {
SkEncodedImageFormat fm;
switch (format) {
case JavaCompressFormat::Jpeg:
@@ -518,12 +496,10 @@ Bitmap::CompressResult Bitmap::compress(const SkBitmap& bitmap, JavaCompressForm
options.fQuality = quality;
options.fCompression = format == JavaCompressFormat::WebpLossy ?
SkWebpEncoder::Compression::kLossy : SkWebpEncoder::Compression::kLossless;
- return SkWebpEncoder::Encode(stream, skbitmap.pixmap(), options)
- ? CompressResult::Success : CompressResult::Error;
+ return SkWebpEncoder::Encode(stream, bitmap.pixmap(), options);
}
}
- return SkEncodeImage(stream, skbitmap, fm, quality)
- ? CompressResult::Success : CompressResult::Error;
+ return SkEncodeImage(stream, bitmap, fm, quality);
}
} // namespace android
diff --git a/libs/hwui/hwui/Bitmap.h b/libs/hwui/hwui/Bitmap.h
index ee365af2f7be..3bfb7800f735 100644
--- a/libs/hwui/hwui/Bitmap.h
+++ b/libs/hwui/hwui/Bitmap.h
@@ -154,16 +154,10 @@ public:
WebpLossless = 4,
};
- enum class CompressResult {
- Success,
- AllocationFailed,
- Error,
- };
-
- CompressResult compress(JavaCompressFormat format, int32_t quality, SkWStream* stream);
+ bool compress(JavaCompressFormat format, int32_t quality, SkWStream* stream);
- static CompressResult compress(const SkBitmap& bitmap, JavaCompressFormat format,
- int32_t quality, SkWStream* stream);
+ static bool compress(const SkBitmap& bitmap, JavaCompressFormat format,
+ int32_t quality, SkWStream* stream);
private:
static sk_sp<Bitmap> allocateAshmemBitmap(size_t size, const SkImageInfo& i, size_t rowBytes);
static sk_sp<Bitmap> allocateHeapBitmap(size_t size, const SkImageInfo& i, size_t rowBytes);
diff --git a/libs/input/PointerController.h b/libs/input/PointerController.h
index 52305b8244a6..ebc622bae302 100644
--- a/libs/input/PointerController.h
+++ b/libs/input/PointerController.h
@@ -100,6 +100,7 @@ public:
virtual int32_t getDisplayId() const;
virtual void fade(Transition transition);
virtual void unfade(Transition transition);
+ virtual void setDisplayViewport(const DisplayViewport& viewport);
virtual void setPresentation(Presentation presentation);
virtual void setSpots(const PointerCoords* spotCoords,
@@ -108,7 +109,6 @@ public:
void updatePointerIcon(int32_t iconId);
void setCustomPointerIcon(const SpriteIcon& icon);
- void setDisplayViewport(const DisplayViewport& viewport);
void setInactivityTimeout(InactivityTimeout inactivityTimeout);
void reloadPointerResources();
diff --git a/libs/services/Android.bp b/libs/services/Android.bp
index 901ffaa59cd1..9b047ca22d19 100644
--- a/libs/services/Android.bp
+++ b/libs/services/Android.bp
@@ -21,7 +21,6 @@ cc_library_shared {
"src/content/ComponentName.cpp",
"src/os/DropBoxManager.cpp",
"src/os/StatsDimensionsValue.cpp",
- "src/os/StatsLogEventWrapper.cpp",
],
shared_libs: [
diff --git a/libs/services/include/android/os/StatsLogEventWrapper.h b/libs/services/include/android/os/StatsLogEventWrapper.h
deleted file mode 100644
index 8de2ab49f42b..000000000000
--- a/libs/services/include/android/os/StatsLogEventWrapper.h
+++ /dev/null
@@ -1,127 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#ifndef STATS_LOG_EVENT_WRAPPER_H
-#define STATS_LOG_EVENT_WRAPPER_H
-
-#include <binder/Parcel.h>
-#include <binder/Parcelable.h>
-#include <binder/Status.h>
-#include <utils/RefBase.h>
-#include <vector>
-
-namespace android {
-namespace os {
-
-/**
- * A wrapper for a union type to contain multiple types of values.
- *
- */
-struct StatsLogValue {
- // Keep in sync with FieldValue.h
- enum STATS_LOG_VALUE_TYPE {
- UNKNOWN = 0,
- INT = 1,
- LONG = 2,
- FLOAT = 3,
- DOUBLE = 4,
- STRING = 5,
- STORAGE = 6
- };
-
- StatsLogValue() : type(UNKNOWN) {}
-
- StatsLogValue(int32_t v) {
- int_value = v;
- type = INT;
- }
-
- StatsLogValue(int64_t v) {
- long_value = v;
- type = LONG;
- }
-
- StatsLogValue(float v) {
- float_value = v;
- type = FLOAT;
- }
-
- StatsLogValue(double v) {
- double_value = v;
- type = DOUBLE;
- }
-
- StatsLogValue(const std::string& v) {
- str_value = v;
- type = STRING;
- }
-
- void setType(STATS_LOG_VALUE_TYPE t) { type = t; }
-
- union {
- int32_t int_value;
- int64_t long_value;
- float float_value;
- double double_value;
- };
- std::string str_value;
- std::vector<uint8_t> storage_value;
-
- STATS_LOG_VALUE_TYPE type;
-};
-
-struct WorkChain {
- std::vector<int32_t> uids;
- std::vector<std::string> tags;
-};
-
-// Represents a parcelable object. Only used to send data from Android OS to statsd.
-class StatsLogEventWrapper : public android::Parcelable {
- public:
- StatsLogEventWrapper();
-
- StatsLogEventWrapper(StatsLogEventWrapper&& in) = default;
-
- android::status_t writeToParcel(android::Parcel* out) const;
-
- android::status_t readFromParcel(const android::Parcel* in);
-
- int getTagId() const { return mTagId; }
-
- int64_t getElapsedRealTimeNs() const { return mElapsedRealTimeNs; }
-
- int64_t getWallClockTimeNs() const { return mWallClockTimeNs; }
-
- const std::vector<StatsLogValue>& getElements() const { return mElements; }
-
- const std::vector<WorkChain>& getWorkChains() const { return mWorkChains; }
-
- private:
- int mTagId;
-
- int64_t mElapsedRealTimeNs;
-
- int64_t mWallClockTimeNs;
-
- std::vector<StatsLogValue> mElements;
-
- std::vector<WorkChain> mWorkChains;
-};
-} // Namespace os
-} // Namespace android
-
-
-#endif // STATS_LOG_EVENT_WRAPPER_H
-
diff --git a/libs/services/src/os/StatsLogEventWrapper.cpp b/libs/services/src/os/StatsLogEventWrapper.cpp
deleted file mode 100644
index f6dfdef16e19..000000000000
--- a/libs/services/src/os/StatsLogEventWrapper.cpp
+++ /dev/null
@@ -1,127 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#include <android/os/StatsLogEventWrapper.h>
-
-#include <binder/Parcel.h>
-#include <binder/Parcelable.h>
-#include <binder/Status.h>
-#include <utils/RefBase.h>
-#include <vector>
-
-using android::Parcel;
-using android::Parcelable;
-using android::status_t;
-using std::vector;
-
-namespace android {
-namespace os {
-
-StatsLogEventWrapper::StatsLogEventWrapper(){};
-
-status_t StatsLogEventWrapper::writeToParcel(Parcel* out) const {
- // Implement me if desired. We don't currently use this.
- ALOGE(
- "Cannot do c++ StatsLogEventWrapper.writeToParcel(); it is not "
- "implemented.");
- (void)out; // To prevent compile error of unused parameter 'in'
- return UNKNOWN_ERROR;
-};
-
-status_t StatsLogEventWrapper::readFromParcel(const Parcel* in) {
- status_t res = OK;
- if (in == NULL) {
- ALOGE("statsd received parcel argument was NULL.");
- return BAD_VALUE;
- }
- if ((res = in->readInt32(&mTagId)) != OK) {
- ALOGE("statsd could not read tagId from parcel");
- return res;
- }
- if ((res = in->readInt64(&mElapsedRealTimeNs)) != OK) {
- ALOGE("statsd could not read elapsed real time from parcel");
- return res;
- }
- if ((res = in->readInt64(&mWallClockTimeNs)) != OK) {
- ALOGE("statsd could not read wall clock time from parcel");
- return res;
- }
- int numWorkChain = 0;
- if ((res = in->readInt32(&numWorkChain)) != OK) {
- ALOGE("statsd could not read number of work chains from parcel");
- return res;
- }
- if (numWorkChain > 0) {
- for (int i = 0; i < numWorkChain; i++) {
- int numNodes = 0;
- if ((res = in->readInt32(&numNodes)) != OK) {
- ALOGE(
- "statsd could not read number of nodes in work chain from parcel");
- return res;
- }
- if (numNodes == 0) {
- ALOGE("empty work chain");
- return BAD_VALUE;
- }
- WorkChain wc;
- for (int j = 0; j < numNodes; j++) {
- wc.uids.push_back(in->readInt32());
- wc.tags.push_back(std::string(String8(in->readString16()).string()));
- }
- mWorkChains.push_back(wc);
- }
- }
- int dataSize = 0;
- if ((res = in->readInt32(&dataSize)) != OK) {
- ALOGE("statsd could not read data size from parcel");
- return res;
- }
- if (mTagId <= 0 || mElapsedRealTimeNs <= 0 || mWallClockTimeNs <= 0 ||
- dataSize <= 0) {
- ALOGE("statsd received invalid parcel");
- return BAD_VALUE;
- }
-
- for (int i = 0; i < dataSize; i++) {
- int type = in->readInt32();
- switch (type) {
- case StatsLogValue::INT:
- mElements.push_back(StatsLogValue(in->readInt32()));
- break;
- case StatsLogValue::LONG:
- mElements.push_back(StatsLogValue(in->readInt64()));
- break;
- case StatsLogValue::STRING:
- mElements.push_back(
- StatsLogValue(std::string(String8(in->readString16()).string())));
- break;
- case StatsLogValue::FLOAT:
- mElements.push_back(StatsLogValue(in->readFloat()));
- break;
- case StatsLogValue::STORAGE:
- mElements.push_back(StatsLogValue());
- mElements.back().setType(StatsLogValue::STORAGE);
- in->readByteVector(&(mElements.back().storage_value));
- break;
- default:
- ALOGE("unrecognized data type: %d", type);
- return BAD_TYPE;
- }
- }
- return NO_ERROR;
-};
-
-} // Namespace os
-} // Namespace android
diff --git a/location/java/android/location/GnssClock.java b/location/java/android/location/GnssClock.java
index 8a7878b46e89..ed4bf1b2d53e 100644
--- a/location/java/android/location/GnssClock.java
+++ b/location/java/android/location/GnssClock.java
@@ -17,6 +17,7 @@
package android.location;
import android.annotation.FloatRange;
+import android.annotation.NonNull;
import android.annotation.TestApi;
import android.os.Parcel;
import android.os.Parcelable;
@@ -39,6 +40,9 @@ public final class GnssClock implements Parcelable {
private static final int HAS_DRIFT_UNCERTAINTY = (1<<6);
private static final int HAS_ELAPSED_REALTIME_NANOS = (1 << 7);
private static final int HAS_ELAPSED_REALTIME_UNCERTAINTY_NANOS = (1 << 8);
+ private static final int HAS_REFERENCE_CONSTELLATION_TYPE_FOR_ISB = (1 << 9);
+ private static final int HAS_REFERENCE_CARRIER_FREQUENCY_FOR_ISB = (1 << 10);
+ private static final int HAS_REFERENCE_CODE_TYPE_FOR_ISB = (1 << 11);
// End enumerations in sync with gps.h
@@ -54,6 +58,9 @@ public final class GnssClock implements Parcelable {
private int mHardwareClockDiscontinuityCount;
private long mElapsedRealtimeNanos;
private double mElapsedRealtimeUncertaintyNanos;
+ private int mReferenceConstellationTypeForIsb;
+ private double mReferenceCarrierFrequencyHzForIsb;
+ private String mReferenceCodeTypeForIsb;
/**
* @hide
@@ -81,6 +88,9 @@ public final class GnssClock implements Parcelable {
mHardwareClockDiscontinuityCount = clock.mHardwareClockDiscontinuityCount;
mElapsedRealtimeNanos = clock.mElapsedRealtimeNanos;
mElapsedRealtimeUncertaintyNanos = clock.mElapsedRealtimeUncertaintyNanos;
+ mReferenceConstellationTypeForIsb = clock.mReferenceConstellationTypeForIsb;
+ mReferenceCarrierFrequencyHzForIsb = clock.mReferenceCarrierFrequencyHzForIsb;
+ mReferenceCodeTypeForIsb = clock.mReferenceCodeTypeForIsb;
}
/**
@@ -196,7 +206,6 @@ public final class GnssClock implements Parcelable {
@TestApi
public void resetTimeUncertaintyNanos() {
resetFlag(HAS_TIME_UNCERTAINTY);
- mTimeUncertaintyNanos = Double.NaN;
}
/**
@@ -286,7 +295,6 @@ public final class GnssClock implements Parcelable {
@TestApi
public void resetBiasNanos() {
resetFlag(HAS_BIAS);
- mBiasNanos = Double.NaN;
}
/**
@@ -327,7 +335,6 @@ public final class GnssClock implements Parcelable {
@TestApi
public void resetBiasUncertaintyNanos() {
resetFlag(HAS_BIAS_UNCERTAINTY);
- mBiasUncertaintyNanos = Double.NaN;
}
/**
@@ -371,7 +378,6 @@ public final class GnssClock implements Parcelable {
@TestApi
public void resetDriftNanosPerSecond() {
resetFlag(HAS_DRIFT);
- mDriftNanosPerSecond = Double.NaN;
}
/**
@@ -411,7 +417,6 @@ public final class GnssClock implements Parcelable {
@TestApi
public void resetDriftUncertaintyNanosPerSecond() {
resetFlag(HAS_DRIFT_UNCERTAINTY);
- mDriftUncertaintyNanosPerSecond = Double.NaN;
}
/**
@@ -495,7 +500,128 @@ public final class GnssClock implements Parcelable {
@TestApi
public void resetElapsedRealtimeUncertaintyNanos() {
resetFlag(HAS_ELAPSED_REALTIME_UNCERTAINTY_NANOS);
- mElapsedRealtimeUncertaintyNanos = Double.NaN;
+ }
+
+ /**
+ * Returns {@code true} if {@link #getReferenceConstellationTypeForIsb()} is available,
+ * {@code false} otherwise.
+ */
+ public boolean hasReferenceConstellationTypeForIsb() {
+ return isFlagSet(HAS_REFERENCE_CONSTELLATION_TYPE_FOR_ISB);
+ }
+
+ /**
+ * Returns the reference constellation type for inter-signal bias.
+ *
+ * <p>The value is only available if {@link #hasReferenceConstellationTypeForIsb()} is
+ * {@code true}.
+ *
+ * <p>The return value is one of those constants with {@code CONSTELLATION_} prefix in
+ * {@link GnssStatus}.
+ */
+ @GnssStatus.ConstellationType
+ public int getReferenceConstellationTypeForIsb() {
+ return mReferenceConstellationTypeForIsb;
+ }
+
+ /**
+ * Sets the reference constellation type for inter-signal bias.
+ * @hide
+ */
+ @TestApi
+ public void setReferenceConstellationTypeForIsb(@GnssStatus.ConstellationType int value) {
+ setFlag(HAS_REFERENCE_CONSTELLATION_TYPE_FOR_ISB);
+ mReferenceConstellationTypeForIsb = value;
+ }
+
+ /**
+ * Resets the reference constellation type for inter-signal bias.
+ * @hide
+ */
+ @TestApi
+ public void resetReferenceConstellationTypeForIsb() {
+ resetFlag(HAS_REFERENCE_CONSTELLATION_TYPE_FOR_ISB);
+ mReferenceConstellationTypeForIsb = GnssStatus.CONSTELLATION_UNKNOWN;
+ }
+
+ /**
+ * Returns {@code true} if {@link #getReferenceCarrierFrequencyHzForIsb()} is available, {@code
+ * false} otherwise.
+ */
+ public boolean hasReferenceCarrierFrequencyHzForIsb() {
+ return isFlagSet(HAS_REFERENCE_CARRIER_FREQUENCY_FOR_ISB);
+ }
+
+ /**
+ * Returns the reference carrier frequency in Hz for inter-signal bias.
+ *
+ * <p>The value is only available if {@link #hasReferenceCarrierFrequencyHzForIsb()} is
+ * {@code true}.
+ */
+ @FloatRange(from = 0.0)
+ public double getReferenceCarrierFrequencyHzForIsb() {
+ return mReferenceCarrierFrequencyHzForIsb;
+ }
+
+ /**
+ * Sets the reference carrier frequency in Hz for inter-signal bias.
+ * @hide
+ */
+ @TestApi
+ public void setReferenceCarrierFrequencyHzForIsb(@FloatRange(from = 0.0) double value) {
+ setFlag(HAS_REFERENCE_CARRIER_FREQUENCY_FOR_ISB);
+ mReferenceCarrierFrequencyHzForIsb = value;
+ }
+
+ /**
+ * Resets the reference carrier frequency in Hz for inter-signal bias.
+ * @hide
+ */
+ @TestApi
+ public void resetReferenceCarrierFrequencyHzForIsb() {
+ resetFlag(HAS_REFERENCE_CARRIER_FREQUENCY_FOR_ISB);
+ }
+
+ /**
+ * Returns {@code true} if {@link #getReferenceCodeTypeForIsb()} is available, {@code
+ * false} otherwise.
+ */
+ public boolean hasReferenceCodeTypeForIsb() {
+ return isFlagSet(HAS_REFERENCE_CODE_TYPE_FOR_ISB);
+ }
+
+ /**
+ * Returns the reference code type for inter-signal bias.
+ *
+ * <p>The value is only available if {@link #hasReferenceCodeTypeForIsb()} is
+ * {@code true}.
+ *
+ * <p>The return value is one of those constants defined in
+ * {@link GnssMeasurement#getCodeType()}.
+ */
+ @NonNull
+ public String getReferenceCodeTypeForIsb() {
+ return mReferenceCodeTypeForIsb;
+ }
+
+ /**
+ * Sets the reference code type for inter-signal bias.
+ * @hide
+ */
+ @TestApi
+ public void setReferenceCodeTypeForIsb(@NonNull String codeType) {
+ setFlag(HAS_REFERENCE_CODE_TYPE_FOR_ISB);
+ mReferenceCodeTypeForIsb = codeType;
+ }
+
+ /**
+ * Resets the reference code type for inter-signal bias.
+ * @hide
+ */
+ @TestApi
+ public void resetReferenceCodeTypeForIsb() {
+ resetFlag(HAS_REFERENCE_CODE_TYPE_FOR_ISB);
+ mReferenceCodeTypeForIsb = "UNKNOWN";
}
/**
@@ -543,6 +669,9 @@ public final class GnssClock implements Parcelable {
gpsClock.mHardwareClockDiscontinuityCount = parcel.readInt();
gpsClock.mElapsedRealtimeNanos = parcel.readLong();
gpsClock.mElapsedRealtimeUncertaintyNanos = parcel.readDouble();
+ gpsClock.mReferenceConstellationTypeForIsb = parcel.readInt();
+ gpsClock.mReferenceCarrierFrequencyHzForIsb = parcel.readDouble();
+ gpsClock.mReferenceCodeTypeForIsb = parcel.readString();
return gpsClock;
}
@@ -567,6 +696,9 @@ public final class GnssClock implements Parcelable {
parcel.writeInt(mHardwareClockDiscontinuityCount);
parcel.writeLong(mElapsedRealtimeNanos);
parcel.writeDouble(mElapsedRealtimeUncertaintyNanos);
+ parcel.writeInt(mReferenceConstellationTypeForIsb);
+ parcel.writeDouble(mReferenceCarrierFrequencyHzForIsb);
+ parcel.writeString(mReferenceCodeTypeForIsb);
}
@Override
@@ -580,7 +712,9 @@ public final class GnssClock implements Parcelable {
final String formatWithUncertainty = " %-15s = %-25s %-26s = %s\n";
StringBuilder builder = new StringBuilder("GnssClock:\n");
- builder.append(String.format(format, "LeapSecond", hasLeapSecond() ? mLeapSecond : null));
+ if (hasLeapSecond()) {
+ builder.append(String.format(format, "LeapSecond", mLeapSecond));
+ }
builder.append(String.format(
formatWithUncertainty,
@@ -589,39 +723,57 @@ public final class GnssClock implements Parcelable {
"TimeUncertaintyNanos",
hasTimeUncertaintyNanos() ? mTimeUncertaintyNanos : null));
- builder.append(String.format(
- format,
- "FullBiasNanos",
- hasFullBiasNanos() ? mFullBiasNanos : null));
+ if (hasFullBiasNanos()) {
+ builder.append(String.format(format, "FullBiasNanos", mFullBiasNanos));
+ }
- builder.append(String.format(
- formatWithUncertainty,
- "BiasNanos",
- hasBiasNanos() ? mBiasNanos : null,
- "BiasUncertaintyNanos",
- hasBiasUncertaintyNanos() ? mBiasUncertaintyNanos : null));
+ if (hasBiasNanos() || hasBiasUncertaintyNanos()) {
+ builder.append(String.format(
+ formatWithUncertainty,
+ "BiasNanos",
+ hasBiasNanos() ? mBiasNanos : null,
+ "BiasUncertaintyNanos",
+ hasBiasUncertaintyNanos() ? mBiasUncertaintyNanos : null));
+ }
- builder.append(String.format(
- formatWithUncertainty,
- "DriftNanosPerSecond",
- hasDriftNanosPerSecond() ? mDriftNanosPerSecond : null,
- "DriftUncertaintyNanosPerSecond",
- hasDriftUncertaintyNanosPerSecond() ? mDriftUncertaintyNanosPerSecond : null));
+ if (hasDriftNanosPerSecond() || hasDriftUncertaintyNanosPerSecond()) {
+ builder.append(String.format(
+ formatWithUncertainty,
+ "DriftNanosPerSecond",
+ hasDriftNanosPerSecond() ? mDriftNanosPerSecond : null,
+ "DriftUncertaintyNanosPerSecond",
+ hasDriftUncertaintyNanosPerSecond() ? mDriftUncertaintyNanosPerSecond : null));
+ }
builder.append(String.format(
format,
"HardwareClockDiscontinuityCount",
mHardwareClockDiscontinuityCount));
- builder.append(String.format(
- format,
- "ElapsedRealtimeNanos",
- hasElapsedRealtimeNanos() ? mElapsedRealtimeNanos : null));
+ if (hasElapsedRealtimeNanos() || hasElapsedRealtimeUncertaintyNanos()) {
+ builder.append(String.format(
+ formatWithUncertainty,
+ "ElapsedRealtimeNanos",
+ hasElapsedRealtimeNanos() ? mElapsedRealtimeNanos : null,
+ "ElapsedRealtimeUncertaintyNanos",
+ hasElapsedRealtimeUncertaintyNanos() ? mElapsedRealtimeUncertaintyNanos
+ : null));
+ }
- builder.append(String.format(
- format,
- "ElapsedRealtimeUncertaintyNanos",
- hasElapsedRealtimeUncertaintyNanos() ? mElapsedRealtimeUncertaintyNanos : null));
+ if (hasReferenceConstellationTypeForIsb()) {
+ builder.append(String.format(format, "ReferenceConstellationTypeForIsb",
+ mReferenceConstellationTypeForIsb));
+ }
+
+ if (hasReferenceCarrierFrequencyHzForIsb()) {
+ builder.append(String.format(format, "ReferenceCarrierFrequencyHzForIsb",
+ mReferenceCarrierFrequencyHzForIsb));
+ }
+
+ if (hasReferenceCodeTypeForIsb()) {
+ builder.append(
+ String.format(format, "ReferenceCodeTypeForIsb", mReferenceCodeTypeForIsb));
+ }
return builder.toString();
}
@@ -639,6 +791,9 @@ public final class GnssClock implements Parcelable {
setHardwareClockDiscontinuityCount(Integer.MIN_VALUE);
resetElapsedRealtimeNanos();
resetElapsedRealtimeUncertaintyNanos();
+ resetReferenceConstellationTypeForIsb();
+ resetReferenceCarrierFrequencyHzForIsb();
+ resetReferenceCodeTypeForIsb();
}
private void setFlag(int flag) {
diff --git a/location/java/android/location/GnssMeasurement.java b/location/java/android/location/GnssMeasurement.java
index 3eeb3a2051e9..83a8995bee13 100644
--- a/location/java/android/location/GnssMeasurement.java
+++ b/location/java/android/location/GnssMeasurement.java
@@ -16,11 +16,21 @@
package android.location;
+import static android.hardware.gnss.V2_1.IGnssMeasurementCallback.GnssMeasurementFlags.HAS_AUTOMATIC_GAIN_CONTROL;
+import static android.hardware.gnss.V2_1.IGnssMeasurementCallback.GnssMeasurementFlags.HAS_CARRIER_CYCLES;
+import static android.hardware.gnss.V2_1.IGnssMeasurementCallback.GnssMeasurementFlags.HAS_CARRIER_FREQUENCY;
+import static android.hardware.gnss.V2_1.IGnssMeasurementCallback.GnssMeasurementFlags.HAS_CARRIER_PHASE;
+import static android.hardware.gnss.V2_1.IGnssMeasurementCallback.GnssMeasurementFlags.HAS_CARRIER_PHASE_UNCERTAINTY;
+import static android.hardware.gnss.V2_1.IGnssMeasurementCallback.GnssMeasurementFlags.HAS_RECEIVER_ISB;
+import static android.hardware.gnss.V2_1.IGnssMeasurementCallback.GnssMeasurementFlags.HAS_RECEIVER_ISB_UNCERTAINTY;
+import static android.hardware.gnss.V2_1.IGnssMeasurementCallback.GnssMeasurementFlags.HAS_SATELLITE_ISB;
+import static android.hardware.gnss.V2_1.IGnssMeasurementCallback.GnssMeasurementFlags.HAS_SATELLITE_ISB_UNCERTAINTY;
+import static android.hardware.gnss.V2_1.IGnssMeasurementCallback.GnssMeasurementFlags.HAS_SNR;
+
import android.annotation.FloatRange;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.TestApi;
-import android.hardware.gnss.V1_0.IGnssMeasurementCallback.GnssMeasurementFlags;
import android.os.Parcel;
import android.os.Parcelable;
@@ -53,19 +63,14 @@ public final class GnssMeasurement implements Parcelable {
private double mSnrInDb;
private double mAutomaticGainControlLevelInDb;
@NonNull private String mCodeType;
+ private double mReceiverInterSignalBiasNanos;
+ private double mReceiverInterSignalBiasUncertaintyNanos;
+ private double mSatelliteInterSignalBiasNanos;
+ private double mSatelliteInterSignalBiasUncertaintyNanos;
// The following enumerations must be in sync with the values declared in GNSS HAL.
private static final int HAS_NO_FLAGS = 0;
- private static final int HAS_SNR = GnssMeasurementFlags.HAS_SNR;
- private static final int HAS_CARRIER_FREQUENCY = GnssMeasurementFlags.HAS_CARRIER_FREQUENCY;
- private static final int HAS_CARRIER_CYCLES = GnssMeasurementFlags.HAS_CARRIER_CYCLES;
- private static final int HAS_CARRIER_PHASE = GnssMeasurementFlags.HAS_CARRIER_PHASE;
- private static final int HAS_CARRIER_PHASE_UNCERTAINTY =
- GnssMeasurementFlags.HAS_CARRIER_PHASE_UNCERTAINTY;
- private static final int HAS_AUTOMATIC_GAIN_CONTROL =
- GnssMeasurementFlags.HAS_AUTOMATIC_GAIN_CONTROL;
-
private static final int HAS_CODE_TYPE = (1 << 14);
private static final int HAS_BASEBAND_CN0 = (1 << 15);
@@ -263,6 +268,12 @@ public final class GnssMeasurement implements Parcelable {
mSnrInDb = measurement.mSnrInDb;
mAutomaticGainControlLevelInDb = measurement.mAutomaticGainControlLevelInDb;
mCodeType = measurement.mCodeType;
+ mReceiverInterSignalBiasNanos = measurement.mReceiverInterSignalBiasNanos;
+ mReceiverInterSignalBiasUncertaintyNanos =
+ measurement.mReceiverInterSignalBiasUncertaintyNanos;
+ mSatelliteInterSignalBiasNanos = measurement.mSatelliteInterSignalBiasNanos;
+ mSatelliteInterSignalBiasUncertaintyNanos =
+ measurement.mSatelliteInterSignalBiasUncertaintyNanos;
}
/**
@@ -838,7 +849,6 @@ public final class GnssMeasurement implements Parcelable {
@TestApi
public void resetBasebandCn0DbHz() {
resetFlag(HAS_BASEBAND_CN0);
- mBasebandCn0DbHz = Double.NaN;
}
/**
@@ -1169,7 +1179,6 @@ public final class GnssMeasurement implements Parcelable {
@Deprecated
public void resetCarrierPhase() {
resetFlag(HAS_CARRIER_PHASE);
- mCarrierPhase = Double.NaN;
}
/**
@@ -1224,7 +1233,6 @@ public final class GnssMeasurement implements Parcelable {
@Deprecated
public void resetCarrierPhaseUncertainty() {
resetFlag(HAS_CARRIER_PHASE_UNCERTAINTY);
- mCarrierPhaseUncertainty = Double.NaN;
}
/**
@@ -1295,7 +1303,6 @@ public final class GnssMeasurement implements Parcelable {
@TestApi
public void resetSnrInDb() {
resetFlag(HAS_SNR);
- mSnrInDb = Double.NaN;
}
/**
@@ -1343,7 +1350,6 @@ public final class GnssMeasurement implements Parcelable {
@TestApi
public void resetAutomaticGainControlLevel() {
resetFlag(HAS_AUTOMATIC_GAIN_CONTROL);
- mAutomaticGainControlLevelInDb = Double.NaN;
}
/**
@@ -1428,7 +1434,200 @@ public final class GnssMeasurement implements Parcelable {
mCodeType = "UNKNOWN";
}
- public static final @android.annotation.NonNull Creator<GnssMeasurement> CREATOR = new Creator<GnssMeasurement>() {
+ /**
+ * Returns {@code true} if {@link #getReceiverInterSignalBiasNanos()} is available,
+ * {@code false} otherwise.
+ */
+ public boolean hasReceiverInterSignalBiasNanos() {
+ return isFlagSet(HAS_RECEIVER_ISB);
+ }
+
+ /**
+ * Gets the GNSS measurement's receiver inter-signal bias in nanoseconds with sub-nanosecond
+ * accuracy.
+ *
+ * <p>This value is the estimated receiver-side inter-system (different from the
+ * constellation in {@link GnssClock#getReferenceConstellationTypeForIsb()} bias and
+ * inter-frequency (different from the carrier frequency in
+ * {@link GnssClock#getReferenceCarrierFrequencyHzForIsb()}) bias. The reported receiver
+ * inter-signal bias must include signal delays caused by:
+ *
+ * <ul>
+ * <li>Receiver inter-constellation bias</li>
+ * <li>Receiver inter-frequency bias</li>
+ * <li>Receiver inter-code bias</li>
+ * </ul>
+ *
+ * <p>The value does not include the inter-frequency Ionospheric bias.
+ *
+ * <p>The value is only available if {@link #hasReceiverInterSignalBiasNanos()} is {@code true}.
+ */
+ public double getReceiverInterSignalBiasNanos() {
+ return mReceiverInterSignalBiasNanos;
+ }
+
+ /**
+ * Sets the GNSS measurement's receiver inter-signal bias in nanoseconds.
+ *
+ * @hide
+ */
+ @TestApi
+ public void setReceiverInterSignalBiasNanos(double receiverInterSignalBiasNanos) {
+ setFlag(HAS_RECEIVER_ISB);
+ mReceiverInterSignalBiasNanos = receiverInterSignalBiasNanos;
+ }
+
+ /**
+ * Resets the GNSS measurement's receiver inter-signal bias in nanoseconds.
+ *
+ * @hide
+ */
+ @TestApi
+ public void resetReceiverInterSignalBiasNanos() {
+ resetFlag(HAS_RECEIVER_ISB);
+ }
+
+ /**
+ * Returns {@code true} if {@link #getReceiverInterSignalBiasUncertaintyNanos()} is available,
+ * {@code false} otherwise.
+ */
+ public boolean hasReceiverInterSignalBiasUncertaintyNanos() {
+ return isFlagSet(HAS_RECEIVER_ISB_UNCERTAINTY);
+ }
+
+ /**
+ * Gets the GNSS measurement's receiver inter-signal bias uncertainty (1 sigma) in
+ * nanoseconds with sub-nanosecond accuracy.
+ *
+ * <p>The value is only available if {@link #hasReceiverInterSignalBiasUncertaintyNanos()} is
+ * {@code true}.
+ */
+ @FloatRange(from = 0.0)
+ public double getReceiverInterSignalBiasUncertaintyNanos() {
+ return mReceiverInterSignalBiasUncertaintyNanos;
+ }
+
+ /**
+ * Sets the GNSS measurement's receiver inter-signal bias uncertainty (1 sigma) in nanoseconds.
+ *
+ * @hide
+ */
+ @TestApi
+ public void setReceiverInterSignalBiasUncertaintyNanos(@FloatRange(from = 0.0)
+ double receiverInterSignalBiasUncertaintyNanos) {
+ setFlag(HAS_RECEIVER_ISB_UNCERTAINTY);
+ mReceiverInterSignalBiasUncertaintyNanos = receiverInterSignalBiasUncertaintyNanos;
+ }
+
+ /**
+ * Resets the GNSS measurement's receiver inter-signal bias uncertainty (1 sigma) in
+ * nanoseconds.
+ *
+ * @hide
+ */
+ @TestApi
+ public void resetReceiverInterSignalBiasUncertaintyNanos() {
+ resetFlag(HAS_RECEIVER_ISB_UNCERTAINTY);
+ }
+
+ /**
+ * Returns {@code true} if {@link #getSatelliteInterSignalBiasNanos()} is available,
+ * {@code false} otherwise.
+ */
+ public boolean hasSatelliteInterSignalBiasNanos() {
+ return isFlagSet(HAS_SATELLITE_ISB);
+ }
+
+ /**
+ * Gets the GNSS measurement's satellite inter-signal bias in nanoseconds with sub-nanosecond
+ * accuracy.
+ *
+ * <p>This value is the satellite-and-control-segment-side inter-system (different from the
+ * constellation in {@link GnssClock#getReferenceConstellationTypeForIsb()}) bias and
+ * inter-frequency (different from the carrier frequency in
+ * {@link GnssClock#getReferenceCarrierFrequencyHzForIsb()}) bias, including:
+ *
+ * <ul>
+ * <li>Master clock bias (e.g., GPS-GAL Time Offset (GGTO), GPT-UTC Time Offset (TauGps),
+ * BDS-GLO Time Offset (BGTO))</li>
+ * <li>Group delay (e.g., Total Group Delay (TGD))</li>
+ * <li>Satellite inter-signal bias, which includes satellite inter-frequency bias (GLO only),
+ * and satellite inter-code bias (e.g., Differential Code Bias (DCB)).</li>
+ * </ul>
+ *
+ * <p>The value is only available if {@link #hasSatelliteInterSignalBiasNanos()} is {@code
+ * true}.
+ */
+ public double getSatelliteInterSignalBiasNanos() {
+ return mSatelliteInterSignalBiasNanos;
+ }
+
+ /**
+ * Sets the GNSS measurement's satellite inter-signal bias in nanoseconds.
+ *
+ * @hide
+ */
+ @TestApi
+ public void setSatelliteInterSignalBiasNanos(double satelliteInterSignalBiasNanos) {
+ setFlag(HAS_SATELLITE_ISB);
+ mSatelliteInterSignalBiasNanos = satelliteInterSignalBiasNanos;
+ }
+
+ /**
+ * Resets the GNSS measurement's satellite inter-signal bias in nanoseconds.
+ *
+ * @hide
+ */
+ @TestApi
+ public void resetSatelliteInterSignalBiasNanos() {
+ resetFlag(HAS_SATELLITE_ISB);
+ }
+
+ /**
+ * Returns {@code true} if {@link #getSatelliteInterSignalBiasUncertaintyNanos()} is available,
+ * {@code false} otherwise.
+ */
+ public boolean hasSatelliteInterSignalBiasUncertaintyNanos() {
+ return isFlagSet(HAS_SATELLITE_ISB_UNCERTAINTY);
+ }
+
+ /**
+ * Gets the GNSS measurement's satellite inter-signal bias uncertainty (1 sigma) in
+ * nanoseconds with sub-nanosecond accuracy.
+ *
+ * <p>The value is only available if {@link #hasSatelliteInterSignalBiasUncertaintyNanos()} is
+ * {@code true}.
+ */
+ @FloatRange(from = 0.0)
+ public double getSatelliteInterSignalBiasUncertaintyNanos() {
+ return mSatelliteInterSignalBiasUncertaintyNanos;
+ }
+
+ /**
+ * Sets the GNSS measurement's satellite inter-signal bias uncertainty (1 sigma) in nanoseconds.
+ *
+ * @hide
+ */
+ @TestApi
+ public void setSatelliteInterSignalBiasUncertaintyNanos(@FloatRange(from = 0.0)
+ double satelliteInterSignalBiasUncertaintyNanos) {
+ setFlag(HAS_SATELLITE_ISB_UNCERTAINTY);
+ mSatelliteInterSignalBiasUncertaintyNanos = satelliteInterSignalBiasUncertaintyNanos;
+ }
+
+ /**
+ * Resets the GNSS measurement's satellite inter-signal bias uncertainty (1 sigma) in
+ * nanoseconds.
+ *
+ * @hide
+ */
+ @TestApi
+ public void resetSatelliteInterSignalBiasUncertaintyNanos() {
+ resetFlag(HAS_SATELLITE_ISB_UNCERTAINTY);
+ }
+
+
+ public static final @NonNull Creator<GnssMeasurement> CREATOR = new Creator<GnssMeasurement>() {
@Override
public GnssMeasurement createFromParcel(Parcel parcel) {
GnssMeasurement gnssMeasurement = new GnssMeasurement();
@@ -1455,6 +1654,10 @@ public final class GnssMeasurement implements Parcelable {
gnssMeasurement.mAutomaticGainControlLevelInDb = parcel.readDouble();
gnssMeasurement.mCodeType = parcel.readString();
gnssMeasurement.mBasebandCn0DbHz = parcel.readDouble();
+ gnssMeasurement.mReceiverInterSignalBiasNanos = parcel.readDouble();
+ gnssMeasurement.mReceiverInterSignalBiasUncertaintyNanos = parcel.readDouble();
+ gnssMeasurement.mSatelliteInterSignalBiasNanos = parcel.readDouble();
+ gnssMeasurement.mSatelliteInterSignalBiasUncertaintyNanos = parcel.readDouble();
return gnssMeasurement;
}
@@ -1489,6 +1692,10 @@ public final class GnssMeasurement implements Parcelable {
parcel.writeDouble(mAutomaticGainControlLevelInDb);
parcel.writeString(mCodeType);
parcel.writeDouble(mBasebandCn0DbHz);
+ parcel.writeDouble(mReceiverInterSignalBiasNanos);
+ parcel.writeDouble(mReceiverInterSignalBiasUncertaintyNanos);
+ parcel.writeDouble(mSatelliteInterSignalBiasNanos);
+ parcel.writeDouble(mSatelliteInterSignalBiasUncertaintyNanos);
}
@Override
@@ -1517,8 +1724,9 @@ public final class GnssMeasurement implements Parcelable {
builder.append(String.format(format, "Cn0DbHz", mCn0DbHz));
- builder.append(String.format(format, "BasebandCn0DbHz",
- hasBasebandCn0DbHz() ? mBasebandCn0DbHz : null));
+ if (hasBasebandCn0DbHz()) {
+ builder.append(String.format(format, "BasebandCn0DbHz", mBasebandCn0DbHz));
+ }
builder.append(String.format(
formatWithUncertainty,
@@ -1539,37 +1747,57 @@ public final class GnssMeasurement implements Parcelable {
"AccumulatedDeltaRangeUncertaintyMeters",
mAccumulatedDeltaRangeUncertaintyMeters));
- builder.append(String.format(
- format,
- "CarrierFrequencyHz",
- hasCarrierFrequencyHz() ? mCarrierFrequencyHz : null));
+ if (hasCarrierFrequencyHz()) {
+ builder.append(String.format(format, "CarrierFrequencyHz", mCarrierFrequencyHz));
+ }
- builder.append(String.format(
- format,
- "CarrierCycles",
- hasCarrierCycles() ? mCarrierCycles : null));
+ if (hasCarrierCycles()) {
+ builder.append(String.format(format, "CarrierCycles", mCarrierCycles));
+ }
- builder.append(String.format(
- formatWithUncertainty,
- "CarrierPhase",
- hasCarrierPhase() ? mCarrierPhase : null,
- "CarrierPhaseUncertainty",
- hasCarrierPhaseUncertainty() ? mCarrierPhaseUncertainty : null));
+ if (hasCarrierPhase() || hasCarrierPhaseUncertainty()) {
+ builder.append(String.format(
+ formatWithUncertainty,
+ "CarrierPhase",
+ hasCarrierPhase() ? mCarrierPhase : null,
+ "CarrierPhaseUncertainty",
+ hasCarrierPhaseUncertainty() ? mCarrierPhaseUncertainty : null));
+ }
builder.append(String.format(format, "MultipathIndicator", getMultipathIndicatorString()));
- builder.append(String.format(
- format,
- "SnrInDb",
- hasSnrInDb() ? mSnrInDb : null));
- builder.append(String.format(
- format,
- "AgcLevelDb",
- hasAutomaticGainControlLevelDb() ? mAutomaticGainControlLevelInDb : null));
- builder.append(String.format(
- format,
- "CodeType",
- hasCodeType() ? mCodeType : null));
+ if (hasSnrInDb()) {
+ builder.append(String.format(format, "SnrInDb", mSnrInDb));
+ }
+
+ if (hasAutomaticGainControlLevelDb()) {
+ builder.append(String.format(format, "AgcLevelDb", mAutomaticGainControlLevelInDb));
+ }
+
+ if (hasCodeType()) {
+ builder.append(String.format(format, "CodeType", mCodeType));
+ }
+
+ if (hasReceiverInterSignalBiasNanos() || hasReceiverInterSignalBiasUncertaintyNanos()) {
+ builder.append(String.format(
+ formatWithUncertainty,
+ "ReceiverInterSignalBiasNs",
+ hasReceiverInterSignalBiasNanos() ? mReceiverInterSignalBiasNanos : null,
+ "ReceiverInterSignalBiasUncertaintyNs",
+ hasReceiverInterSignalBiasUncertaintyNanos()
+ ? mReceiverInterSignalBiasUncertaintyNanos : null));
+ }
+
+ if (hasSatelliteInterSignalBiasNanos() || hasSatelliteInterSignalBiasUncertaintyNanos()) {
+ builder.append(String.format(
+ formatWithUncertainty,
+ "SatelliteInterSignalBiasNs",
+ hasSatelliteInterSignalBiasNanos() ? mSatelliteInterSignalBiasNanos : null,
+ "SatelliteInterSignalBiasUncertaintyNs",
+ hasSatelliteInterSignalBiasUncertaintyNanos()
+ ? mSatelliteInterSignalBiasUncertaintyNanos
+ : null));
+ }
return builder.toString();
}
@@ -1596,6 +1824,10 @@ public final class GnssMeasurement implements Parcelable {
resetAutomaticGainControlLevel();
resetCodeType();
resetBasebandCn0DbHz();
+ resetReceiverInterSignalBiasNanos();
+ resetReceiverInterSignalBiasUncertaintyNanos();
+ resetSatelliteInterSignalBiasNanos();
+ resetSatelliteInterSignalBiasUncertaintyNanos();
}
private void setFlag(int flag) {
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index 687535c3304b..0c5fe787bbbc 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -74,7 +74,7 @@ import java.util.function.Consumer;
* still return location results, but the exact location will be obfuscated to a coarse level of
* accuracy.
*/
-@SuppressWarnings({"deprecation", "DeprecatedIsStillUsed"})
+@SuppressWarnings({"deprecation"})
@SystemService(Context.LOCATION_SERVICE)
@RequiresFeature(PackageManager.FEATURE_LOCATION)
public class LocationManager {
diff --git a/location/java/android/location/LocationManagerInternal.java b/location/java/android/location/LocationManagerInternal.java
new file mode 100644
index 000000000000..44d9d2372665
--- /dev/null
+++ b/location/java/android/location/LocationManagerInternal.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.location;
+
+
+import android.annotation.NonNull;
+
+/**
+ * Location manager local system service interface.
+ *
+ * @hide Only for use within the system server.
+ */
+public abstract class LocationManagerInternal {
+
+ /**
+ * Requests that a provider change its allowed state. A provider may or may not honor this
+ * request, and if the provider does change its state as a result, that may happen
+ * asynchronously after some delay.
+ *
+ * <p>Setting a provider's state to allowed implies that any consents or terms and conditions
+ * that may be necessary to allow the provider are agreed to. Setting a providers state to
+ * disallowed implies that any consents or terms and conditions have their agreement revoked.
+ *
+ * @param provider A location provider as listed by {@link LocationManager#getAllProviders()}
+ * @param allowed Whether the location provider is being requested to allow or disallow
+ * itself
+ * @throws IllegalArgumentException if provider is null
+ */
+ public abstract void requestSetProviderAllowed(@NonNull String provider, boolean allowed);
+}
diff --git a/location/java/com/android/internal/location/ILocationProvider.aidl b/location/java/com/android/internal/location/ILocationProvider.aidl
index 4246c6cd1004..b7817ff1e1fc 100644
--- a/location/java/com/android/internal/location/ILocationProvider.aidl
+++ b/location/java/com/android/internal/location/ILocationProvider.aidl
@@ -37,4 +37,6 @@ interface ILocationProvider {
@UnsupportedAppUsage
oneway void sendExtraCommand(String command, in Bundle extras);
+
+ oneway void requestSetAllowed(boolean allowed);
}
diff --git a/location/java/com/android/internal/location/ILocationProviderManager.aidl b/location/java/com/android/internal/location/ILocationProviderManager.aidl
index 85e18ba5ec4b..439039148773 100644
--- a/location/java/com/android/internal/location/ILocationProviderManager.aidl
+++ b/location/java/com/android/internal/location/ILocationProviderManager.aidl
@@ -29,7 +29,7 @@ interface ILocationProviderManager {
void onSetAdditionalProviderPackages(in List<String> packageNames);
@UnsupportedAppUsage
- void onSetEnabled(boolean enabled);
+ void onSetAllowed(boolean allowed);
@UnsupportedAppUsage
void onSetProperties(in ProviderProperties properties);
diff --git a/location/lib/api/current.txt b/location/lib/api/current.txt
index 5471bea549f4..49fcaabe981a 100644
--- a/location/lib/api/current.txt
+++ b/location/lib/api/current.txt
@@ -9,18 +9,21 @@ package com.android.location.provider {
public abstract class LocationProviderBase {
ctor public LocationProviderBase(String, com.android.location.provider.ProviderPropertiesUnbundled);
method public android.os.IBinder getBinder();
- method @RequiresApi(android.os.Build.VERSION_CODES.Q) public boolean isEnabled();
+ method @RequiresApi(android.os.Build.VERSION_CODES.R) public boolean isAllowed();
+ method @Deprecated @RequiresApi(android.os.Build.VERSION_CODES.Q) public boolean isEnabled();
method @Deprecated protected void onDisable();
method @Deprecated protected void onDump(java.io.FileDescriptor, java.io.PrintWriter, String[]);
method @Deprecated protected void onEnable();
method @Deprecated protected int onGetStatus(android.os.Bundle);
method @Deprecated protected long onGetStatusUpdateTime();
method protected void onInit();
+ method protected void onRequestSetAllowed(boolean);
method protected boolean onSendExtraCommand(@Nullable String, @Nullable android.os.Bundle);
method protected abstract void onSetRequest(com.android.location.provider.ProviderRequestUnbundled, android.os.WorkSource);
method public void reportLocation(android.location.Location);
method @RequiresApi(android.os.Build.VERSION_CODES.Q) public void setAdditionalProviderPackages(java.util.List<java.lang.String>);
- method @RequiresApi(android.os.Build.VERSION_CODES.Q) public void setEnabled(boolean);
+ method @RequiresApi(android.os.Build.VERSION_CODES.R) public void setAllowed(boolean);
+ method @Deprecated @RequiresApi(android.os.Build.VERSION_CODES.Q) public void setEnabled(boolean);
method @RequiresApi(android.os.Build.VERSION_CODES.Q) public void setProperties(com.android.location.provider.ProviderPropertiesUnbundled);
field public static final String EXTRA_NO_GPS_LOCATION = "noGPSLocation";
field public static final String FUSED_PROVIDER = "fused";
diff --git a/location/lib/java/com/android/location/provider/LocationProviderBase.java b/location/lib/java/com/android/location/provider/LocationProviderBase.java
index fc7bff3c6dab..f67d08e045e2 100644
--- a/location/lib/java/com/android/location/provider/LocationProviderBase.java
+++ b/location/lib/java/com/android/location/provider/LocationProviderBase.java
@@ -92,7 +92,7 @@ public abstract class LocationProviderBase {
// write locked on mBinder, read lock is optional depending on atomicity requirements
@Nullable private volatile ILocationProviderManager mManager;
private volatile ProviderProperties mProperties;
- private volatile boolean mEnabled;
+ private volatile boolean mAllowed;
private final ArrayList<String> mAdditionalProviderPackages;
public LocationProviderBase(String tag, ProviderPropertiesUnbundled properties) {
@@ -104,7 +104,7 @@ public abstract class LocationProviderBase {
mManager = null;
mProperties = properties.getProviderProperties();
- mEnabled = true;
+ mAllowed = true;
mAdditionalProviderPackages = new ArrayList<>(0);
}
@@ -113,35 +113,44 @@ public abstract class LocationProviderBase {
}
/**
- * Sets whether this provider is currently enabled or not. Note that this is specific to the
+ * @deprecated Use {@link #setAllowed(boolean)} instead.
+ */
+ @Deprecated
+ @RequiresApi(VERSION_CODES.Q)
+ public void setEnabled(boolean enabled) {
+ setAllowed(enabled);
+ }
+
+ /**
+ * Sets whether this provider is currently allowed or not. Note that this is specific to the
* provider only, and is not related to global location settings. This is a hint to the Location
* Manager that this provider will generally be unable to fulfill incoming requests. This
- * provider may still receive callbacks to onSetRequest while not enabled, and must decide
+ * provider may still receive callbacks to onSetRequest while not allowed, and must decide
* whether to attempt to satisfy those requests or not.
*
- * Some guidelines: providers should set their own enabled/disabled status based only on state
- * "owned" by that provider. For instance, providers should not take into account the state of
- * the location master setting when setting themselves enabled or disabled, as this state is not
- * owned by a particular provider. If a provider requires some additional user consent that is
- * particular to the provider, this should be use to set the enabled/disabled state. If the
- * provider proxies to another provider, the child provider's enabled/disabled state should be
- * taken into account in the parent's enabled/disabled state. For most providers, it is expected
- * that they will be always enabled.
+ * <p>Some guidelines: providers should set their own allowed/disallowed status based only on
+ * state "owned" by that provider. For instance, providers should not take into account the
+ * state of the location master setting when setting themselves allowed or disallowed, as this
+ * state is not owned by a particular provider. If a provider requires some additional user
+ * consent that is particular to the provider, this should be use to set the allowed/disallowed
+ * state. If the provider proxies to another provider, the child provider's allowed/disallowed
+ * state should be taken into account in the parent's allowed state. For most providers, it is
+ * expected that they will be always allowed.
*/
- @RequiresApi(VERSION_CODES.Q)
- public void setEnabled(boolean enabled) {
+ @RequiresApi(VERSION_CODES.R)
+ public void setAllowed(boolean allowed) {
synchronized (mBinder) {
- if (mEnabled == enabled) {
+ if (mAllowed == allowed) {
return;
}
- mEnabled = enabled;
+ mAllowed = allowed;
}
ILocationProviderManager manager = mManager;
if (manager != null) {
try {
- manager.onSetEnabled(mEnabled);
+ manager.onSetAllowed(mAllowed);
} catch (RemoteException | RuntimeException e) {
Log.w(mTag, e);
}
@@ -193,12 +202,20 @@ public abstract class LocationProviderBase {
}
/**
- * Returns true if this provider has been set as enabled. This will be true unless explicitly
- * set otherwise.
+ * @deprecated Use {@link #isAllowed()} instead.
*/
+ @Deprecated
@RequiresApi(VERSION_CODES.Q)
public boolean isEnabled() {
- return mEnabled;
+ return isAllowed();
+ }
+
+ /**
+ * Returns true if this provider is allowed. Providers start as allowed on construction.
+ */
+ @RequiresApi(VERSION_CODES.R)
+ public boolean isAllowed() {
+ return mAllowed;
}
/**
@@ -285,6 +302,17 @@ public abstract class LocationProviderBase {
return false;
}
+ /**
+ * Invoked when the system wishes to request that the provider sets its allowed state as
+ * desired. This implies that the caller is providing/retracting consent for any terms and
+ * conditions or consents associated with the provider.
+ *
+ * <p>It is generally only necessary to override this function if the provider has some barriers
+ * or gates for enabling/disabling itself, in which case this function should handle those
+ * appropriately. A provider that is always allowed has no need to override this function.
+ */
+ protected void onRequestSetAllowed(boolean allowed) {}
+
private final class Service extends ILocationProvider.Stub {
@Override
@@ -295,7 +323,7 @@ public abstract class LocationProviderBase {
manager.onSetAdditionalProviderPackages(mAdditionalProviderPackages);
}
manager.onSetProperties(mProperties);
- manager.onSetEnabled(mEnabled);
+ manager.onSetAllowed(mAllowed);
} catch (RemoteException e) {
Log.w(mTag, e);
}
@@ -315,5 +343,10 @@ public abstract class LocationProviderBase {
public void sendExtraCommand(String command, Bundle extras) {
onSendExtraCommand(command, extras);
}
+
+ @Override
+ public void requestSetAllowed(boolean allowed) {
+ onRequestSetAllowed(allowed);
+ }
}
}
diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java
index cbc9683b9cc1..197786f42490 100644
--- a/media/java/android/media/MediaCodecInfo.java
+++ b/media/java/android/media/MediaCodecInfo.java
@@ -763,7 +763,13 @@ public final class MediaCodecInfo {
int maxLevel = 0;
for (CodecProfileLevel pl : profileLevels) {
if (pl.profile == profile && pl.level > maxLevel) {
- maxLevel = pl.level;
+ // H.263 levels are not completely ordered:
+ // Level45 support only implies Level10 support
+ if (!mMime.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_H263)
+ || pl.level != CodecProfileLevel.H263Level45
+ || maxLevel == CodecProfileLevel.H263Level10) {
+ maxLevel = pl.level;
+ }
}
}
levelCaps = createFromProfileLevel(mMime, profile, maxLevel);
diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java
index f408ac344d7c..f61d55ef2a30 100644
--- a/media/java/android/media/MediaFormat.java
+++ b/media/java/android/media/MediaFormat.java
@@ -20,6 +20,8 @@ import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
+import android.media.MediaCodec;
+import android.media.MediaParser;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -45,6 +47,7 @@ import java.util.stream.Collectors;
* <table>
* <tr><th>Name</th><th>Value Type</th><th>Description</th></tr>
* <tr><td>{@link #KEY_MIME}</td><td>String</td><td>The type of the format.</td></tr>
+ * <tr><td>{@link #KEY_CODECS_STRING}</td><td>String</td><td>optional, the RFC 6381 codecs string of the MediaFormat</td></tr>
* <tr><td>{@link #KEY_MAX_INPUT_SIZE}</td><td>Integer</td><td>optional, maximum size of a buffer of input data</td></tr>
* <tr><td>{@link #KEY_PIXEL_ASPECT_RATIO_WIDTH}</td><td>Integer</td><td>optional, the pixel aspect ratio width</td></tr>
* <tr><td>{@link #KEY_PIXEL_ASPECT_RATIO_HEIGHT}</td><td>Integer</td><td>optional, the pixel aspect ratio height</td></tr>
@@ -217,6 +220,15 @@ public final class MediaFormat {
public static final String KEY_MIME = "mime";
/**
+ * A key describing the codecs string of the MediaFormat. See RFC 6381 section 3.2 for the
+ * syntax of the value. The value does not hold {@link MediaCodec}-exposed codec names.
+ * The associated value is a string.
+ *
+ * @see MediaParser.TrackData#mediaFormat
+ */
+ public static final String KEY_CODECS_STRING = "codecs-string";
+
+ /**
* An optional key describing the low latency decoding mode. This is an optional parameter
* that applies only to decoders. If enabled, the decoder doesn't hold input and output
* data more than required by the codec standards.
diff --git a/media/java/android/media/MediaMetadataRetriever.java b/media/java/android/media/MediaMetadataRetriever.java
index 4cd581b6628c..1617b875c6f7 100644
--- a/media/java/android/media/MediaMetadataRetriever.java
+++ b/media/java/android/media/MediaMetadataRetriever.java
@@ -28,6 +28,7 @@ import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Build;
import android.os.IBinder;
+import android.text.TextUtils;
import java.io.FileDescriptor;
import java.io.FileInputStream;
@@ -44,6 +45,162 @@ import java.util.Map;
* frame and meta data from an input media file.
*/
public class MediaMetadataRetriever implements AutoCloseable {
+
+ // borrowed from ExoPlayer
+ private static final String[] STANDARD_GENRES = new String[] {
+ // These are the official ID3v1 genres.
+ "Blues",
+ "Classic Rock",
+ "Country",
+ "Dance",
+ "Disco",
+ "Funk",
+ "Grunge",
+ "Hip-Hop",
+ "Jazz",
+ "Metal",
+ "New Age",
+ "Oldies",
+ "Other",
+ "Pop",
+ "R&B",
+ "Rap",
+ "Reggae",
+ "Rock",
+ "Techno",
+ "Industrial",
+ "Alternative",
+ "Ska",
+ "Death Metal",
+ "Pranks",
+ "Soundtrack",
+ "Euro-Techno",
+ "Ambient",
+ "Trip-Hop",
+ "Vocal",
+ "Jazz+Funk",
+ "Fusion",
+ "Trance",
+ "Classical",
+ "Instrumental",
+ "Acid",
+ "House",
+ "Game",
+ "Sound Clip",
+ "Gospel",
+ "Noise",
+ "AlternRock",
+ "Bass",
+ "Soul",
+ "Punk",
+ "Space",
+ "Meditative",
+ "Instrumental Pop",
+ "Instrumental Rock",
+ "Ethnic",
+ "Gothic",
+ "Darkwave",
+ "Techno-Industrial",
+ "Electronic",
+ "Pop-Folk",
+ "Eurodance",
+ "Dream",
+ "Southern Rock",
+ "Comedy",
+ "Cult",
+ "Gangsta",
+ "Top 40",
+ "Christian Rap",
+ "Pop/Funk",
+ "Jungle",
+ "Native American",
+ "Cabaret",
+ "New Wave",
+ "Psychadelic",
+ "Rave",
+ "Showtunes",
+ "Trailer",
+ "Lo-Fi",
+ "Tribal",
+ "Acid Punk",
+ "Acid Jazz",
+ "Polka",
+ "Retro",
+ "Musical",
+ "Rock & Roll",
+ "Hard Rock",
+ // These were made up by the authors of Winamp and later added to the ID3 spec.
+ "Folk",
+ "Folk-Rock",
+ "National Folk",
+ "Swing",
+ "Fast Fusion",
+ "Bebob",
+ "Latin",
+ "Revival",
+ "Celtic",
+ "Bluegrass",
+ "Avantgarde",
+ "Gothic Rock",
+ "Progressive Rock",
+ "Psychedelic Rock",
+ "Symphonic Rock",
+ "Slow Rock",
+ "Big Band",
+ "Chorus",
+ "Easy Listening",
+ "Acoustic",
+ "Humour",
+ "Speech",
+ "Chanson",
+ "Opera",
+ "Chamber Music",
+ "Sonata",
+ "Symphony",
+ "Booty Bass",
+ "Primus",
+ "Porn Groove",
+ "Satire",
+ "Slow Jam",
+ "Club",
+ "Tango",
+ "Samba",
+ "Folklore",
+ "Ballad",
+ "Power Ballad",
+ "Rhythmic Soul",
+ "Freestyle",
+ "Duet",
+ "Punk Rock",
+ "Drum Solo",
+ "A capella",
+ "Euro-House",
+ "Dance Hall",
+ // These were made up by the authors of Winamp but have not been added to the ID3 spec.
+ "Goa",
+ "Drum & Bass",
+ "Club-House",
+ "Hardcore",
+ "Terror",
+ "Indie",
+ "BritPop",
+ "Afro-Punk",
+ "Polsk Punk",
+ "Beat",
+ "Christian Gangsta Rap",
+ "Heavy Metal",
+ "Black Metal",
+ "Crossover",
+ "Contemporary Christian",
+ "Christian Rock",
+ "Merengue",
+ "Salsa",
+ "Thrash Metal",
+ "Anime",
+ "Jpop",
+ "Synthpop"
+ };
+
static {
System.loadLibrary("media_jni");
native_init();
@@ -225,6 +382,8 @@ public class MediaMetadataRetriever implements AutoCloseable {
private native void _setDataSource(MediaDataSource dataSource)
throws IllegalArgumentException;
+ private native @Nullable String nativeExtractMetadata(int keyCode);
+
/**
* Call this method after setDataSource(). This method retrieves the
* meta data value associated with the keyCode.
@@ -236,7 +395,118 @@ public class MediaMetadataRetriever implements AutoCloseable {
* @return The meta data value associate with the given keyCode on success;
* null on failure.
*/
- public native @Nullable String extractMetadata(int keyCode);
+ public @Nullable String extractMetadata(int keyCode) {
+ String meta = nativeExtractMetadata(keyCode);
+ if (keyCode == METADATA_KEY_GENRE) {
+ // translate numeric genre code(s) to human readable
+ meta = convertGenreTag(meta);
+ }
+ return meta;
+ }
+
+ /*
+ * The id3v2 spec doesn't specify the syntax of the genre tag very precisely, so
+ * some assumptions are made. Using one possible interpretation of the id3v2
+ * spec, this method converts an id3 genre tag string to a human readable string,
+ * as follows:
+ * - if the first character of the tag is a digit, the entire tag is assumed to
+ * be an id3v1 numeric genre code. If the tag does not parse to a number, or
+ * the number is outside the range of defined standard genres, it is ignored.
+ * - if the tag does not start with a digit, it is assumed to be an id3v2 style
+ * tag consisting of one or more genres, with each genre being either a parenthesized
+ * integer referring to an id3v1 numeric genre code, the special indicators "(CR)" or
+ * "(RX)" (for "Cover" or "Remix", respectively), or a custom genre string. When
+ * a custom genre string is encountered, it is assumed to continue until the end
+ * of the tag, unless it starts with "((" in which case it is assumed to continue
+ * until the next close-parenthesis or the end of the tag. Any parse error in the tag
+ * causes it to be ignored.
+ * The human-readable genre string is not localized, and uses the English genre names
+ * from the spec.
+ */
+ private String convertGenreTag(String meta) {
+ if (TextUtils.isEmpty(meta)) {
+ return null;
+ }
+
+ if (Character.isDigit(meta.charAt(0))) {
+ // assume a single id3v1-style bare number without any extra characters
+ try {
+ int genreIndex = Integer.parseInt(meta);
+ if (genreIndex >= 0 && genreIndex < STANDARD_GENRES.length) {
+ return STANDARD_GENRES[genreIndex];
+ }
+ } catch (NumberFormatException e) {
+ // ignore and fall through
+ }
+ return null;
+ } else {
+ // assume id3v2-style genre tag, with parenthesized numeric genres
+ // and/or literal genre strings, possibly more than one per tag.
+ StringBuilder genres = null;
+ String nextGenre = null;
+ while (true) {
+ if (!TextUtils.isEmpty(nextGenre)) {
+ if (genres == null) {
+ genres = new StringBuilder();
+ }
+ if (genres.length() != 0) {
+ genres.append(", ");
+ }
+ genres.append(nextGenre);
+ nextGenre = null;
+ }
+ if (TextUtils.isEmpty(meta)) {
+ // entire tag has been processed.
+ break;
+ }
+ if (meta.startsWith("(RX)")) {
+ nextGenre = "Remix";
+ meta = meta.substring(4);
+ } else if (meta.startsWith("(CR)")) {
+ nextGenre = "Cover";
+ meta = meta.substring(4);
+ } else if (meta.startsWith("((")) {
+ // the id3v2 spec says that custom genres that start with a parenthesis
+ // should be "escaped" with another parenthesis, however the spec doesn't
+ // specify escaping parentheses inside the custom string. We'll parse any
+ // such strings until a closing parenthesis is found, or the end of
+ // the tag is reached.
+ int closeParenOffset = meta.indexOf(')');
+ if (closeParenOffset == -1) {
+ // string continues to end of tag
+ nextGenre = meta.substring(1);
+ meta = "";
+ } else {
+ nextGenre = meta.substring(1, closeParenOffset + 1);
+ meta = meta.substring(closeParenOffset + 1);
+ }
+ } else if (meta.startsWith("(")) {
+ // should be a parenthesized numeric genre
+ int closeParenOffset = meta.indexOf(')');
+ if (closeParenOffset == -1) {
+ return null;
+ }
+ String genreNumString = meta.substring(1, closeParenOffset);
+ try {
+ int genreIndex = Integer.parseInt(genreNumString.toString());
+ if (genreIndex >= 0 && genreIndex < STANDARD_GENRES.length) {
+ nextGenre = STANDARD_GENRES[genreIndex];
+ } else {
+ return null;
+ }
+ } catch (NumberFormatException e) {
+ return null;
+ }
+ meta = meta.substring(closeParenOffset + 1);
+ } else {
+ // custom genre
+ nextGenre = meta;
+ meta = "";
+ }
+ }
+ return genres == null || genres.length() == 0 ? null : genres.toString();
+ }
+ }
/**
* This method is similar to {@link #getFrameAtTime(long, int, BitmapParams)}
diff --git a/media/java/android/media/audiofx/AudioEffect.java b/media/java/android/media/audiofx/AudioEffect.java
index cad5aa6aaa3c..c25a5333017b 100644
--- a/media/java/android/media/audiofx/AudioEffect.java
+++ b/media/java/android/media/audiofx/AudioEffect.java
@@ -356,10 +356,14 @@ public class AudioEffect {
public static final String EFFECT_AUXILIARY = "Auxiliary";
/**
* Effect connection mode is pre processing.
- * The audio pre processing effects are attached to an audio input (AudioRecord).
- * @hide
+ * The audio pre processing effects are attached to an audio input stream or device
*/
public static final String EFFECT_PRE_PROCESSING = "Pre Processing";
+ /**
+ * Effect connection mode is post processing.
+ * The audio post processing effects are attached to an audio output stream or device
+ */
+ public static final String EFFECT_POST_PROCESSING = "Post Processing";
// --------------------------------------------------------------------------
// Member variables
diff --git a/media/java/android/media/tv/DvbDeviceInfo.java b/media/java/android/media/tv/DvbDeviceInfo.java
index 96c852812a74..54fc39e9db9e 100644
--- a/media/java/android/media/tv/DvbDeviceInfo.java
+++ b/media/java/android/media/tv/DvbDeviceInfo.java
@@ -16,6 +16,7 @@
package android.media.tv;
+import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.os.Parcel;
@@ -23,9 +24,13 @@ import android.os.Parcelable;
import android.util.Log;
/**
- * Simple container for information about DVB device.
- * Not for third-party developers.
+ * A digital video broadcasting (DVB) device.
*
+ * <p> Simple wrapper around a <a href="https://www.linuxtv.org/docs/dvbapi/dvbapi.html">Linux DVB
+ * v3</a> device.
+ *
+ * @see TvInputManager#getDvbDeviceList()
+ * @see TvInputManager#openDvbDevice(DvbDeviceInfo, int)
* @hide
*/
@SystemApi
@@ -67,17 +72,19 @@ public final class DvbDeviceInfo implements Parcelable {
}
/**
- * Returns the adapter ID of DVB device, in terms of enumerating the DVB device adapters
- * installed in the system. The adapter ID counts from zero.
+ * Returns the adapter ID.
+ *
+ * <p>DVB Adapters contain one or more devices.
*/
+ @IntRange(from = 0)
public int getAdapterId() {
return mAdapterId;
}
/**
- * Returns the device ID of DVB device, in terms of enumerating the DVB devices attached to
- * the same device adapter. The device ID counts from zero.
+ * Returns the device ID.
*/
+ @IntRange(from = 0)
public int getDeviceId() {
return mDeviceId;
}
diff --git a/media/java/android/media/tv/TvContentRating.java b/media/java/android/media/tv/TvContentRating.java
index 9e671b1177cd..5babb1698c85 100644
--- a/media/java/android/media/tv/TvContentRating.java
+++ b/media/java/android/media/tv/TvContentRating.java
@@ -17,7 +17,6 @@
package android.media.tv;
import android.annotation.NonNull;
-import android.annotation.SystemApi;
import android.text.TextUtils;
import com.android.internal.util.Preconditions;
@@ -725,16 +724,16 @@ import java.util.Objects;
* <td>NZ_TV_G</td>
* <td>Programmes which exclude material likely to be unsuitable for children. Programmes
* may not necessarily be designed for child viewers but should not contain material likely
- * to alarm or distress them.</td>
+ * to alarm or distress them</td>
* </tr>
* <tr>
* <td>NZ_TV_PGR</td>
* <td>Programmes containing material more suited for mature audiences but not necessarily
- * unsuitable for child viewers when subject to the guidance of a parent or an adult.</td>
+ * unsuitable for child viewers when subject to the guidance of a parent or an adult</td>
* </tr>
* <tr>
* <td>NZ_TV_AO</td>
- * <td>Programmes containing adult themes and directed primarily at mature audiences.</td>
+ * <td>Programmes containing adult themes and directed primarily at mature audiences</td>
* </tr>
* <tr>
* <td valign="top" rowspan="6">SG_TV</td>
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index 9cdfa2aa0807..bb867630ca01 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -1833,7 +1833,7 @@ public final class TvInputManager {
}
/**
- * Returns the list of currently available DVB devices on the system.
+ * Returns the list of currently available DVB frontend devices on the system.
*
* @return the list of {@link DvbDeviceInfo} objects representing available DVB devices.
* @hide
@@ -1850,16 +1850,17 @@ public final class TvInputManager {
}
/**
- * Returns a {@link ParcelFileDescriptor} of a specified DVB device for a given
- * {@link DvbDeviceInfo}
+ * Returns a {@link ParcelFileDescriptor} of a specified DVB device of a given type for a given
+ * {@link DvbDeviceInfo}.
*
* @param info A {@link DvbDeviceInfo} to open a DVB device.
- * @param deviceType A DVB device type. The type can be {@link #DVB_DEVICE_DEMUX},
- * {@link #DVB_DEVICE_DVR} or {@link #DVB_DEVICE_FRONTEND}.
+ * @param deviceType A DVB device type.
* @return a {@link ParcelFileDescriptor} of a specified DVB device for a given
- * {@link DvbDeviceInfo}, or {@code null} if the given {@link DvbDeviceInfo}
- * failed to open.
+ * {@link DvbDeviceInfo}, or {@code null} if the given {@link DvbDeviceInfo}
+ * failed to open.
* @throws IllegalArgumentException if {@code deviceType} is invalid or the device is not found.
+
+ * @see <a href="https://www.linuxtv.org/docs/dvbapi/dvbapi.html">Linux DVB API v3</a>
* @hide
*/
@SystemApi
diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp
index ab6966d5d1c3..9b37f955fa17 100644
--- a/media/jni/android_media_MediaCodec.cpp
+++ b/media/jni/android_media_MediaCodec.cpp
@@ -1750,22 +1750,150 @@ static void android_media_MediaCodec_queueSecureInputBuffer(
return;
}
- NativeCryptoInfo cryptoInfo{env, cryptoInfoObj};
+ jint numSubSamples =
+ env->GetIntField(cryptoInfoObj, gFields.cryptoInfoNumSubSamplesID);
+
+ jintArray numBytesOfClearDataObj =
+ (jintArray)env->GetObjectField(
+ cryptoInfoObj, gFields.cryptoInfoNumBytesOfClearDataID);
+
+ jintArray numBytesOfEncryptedDataObj =
+ (jintArray)env->GetObjectField(
+ cryptoInfoObj, gFields.cryptoInfoNumBytesOfEncryptedDataID);
+
+ jbyteArray keyObj =
+ (jbyteArray)env->GetObjectField(cryptoInfoObj, gFields.cryptoInfoKeyID);
+
+ jbyteArray ivObj =
+ (jbyteArray)env->GetObjectField(cryptoInfoObj, gFields.cryptoInfoIVID);
+
+ jint jmode = env->GetIntField(cryptoInfoObj, gFields.cryptoInfoModeID);
+ enum CryptoPlugin::Mode mode;
+ if (jmode == gCryptoModes.Unencrypted) {
+ mode = CryptoPlugin::kMode_Unencrypted;
+ } else if (jmode == gCryptoModes.AesCtr) {
+ mode = CryptoPlugin::kMode_AES_CTR;
+ } else if (jmode == gCryptoModes.AesCbc) {
+ mode = CryptoPlugin::kMode_AES_CBC;
+ } else {
+ throwExceptionAsNecessary(env, INVALID_OPERATION);
+ return;
+ }
+
+ jobject patternObj = env->GetObjectField(cryptoInfoObj, gFields.cryptoInfoPatternID);
+
+ CryptoPlugin::Pattern pattern;
+ if (patternObj == NULL) {
+ pattern.mEncryptBlocks = 0;
+ pattern.mSkipBlocks = 0;
+ } else {
+ pattern.mEncryptBlocks = env->GetIntField(patternObj, gFields.patternEncryptBlocksID);
+ pattern.mSkipBlocks = env->GetIntField(patternObj, gFields.patternSkipBlocksID);
+ }
+
+ status_t err = OK;
+
+ CryptoPlugin::SubSample *subSamples = NULL;
+ jbyte *key = NULL;
+ jbyte *iv = NULL;
+
+ if (numSubSamples <= 0) {
+ err = -EINVAL;
+ } else if (numBytesOfClearDataObj == NULL
+ && numBytesOfEncryptedDataObj == NULL) {
+ err = -EINVAL;
+ } else if (numBytesOfEncryptedDataObj != NULL
+ && env->GetArrayLength(numBytesOfEncryptedDataObj) < numSubSamples) {
+ err = -ERANGE;
+ } else if (numBytesOfClearDataObj != NULL
+ && env->GetArrayLength(numBytesOfClearDataObj) < numSubSamples) {
+ err = -ERANGE;
+ // subSamples array may silently overflow if number of samples are too large. Use
+ // INT32_MAX as maximum allocation size may be less than SIZE_MAX on some platforms
+ } else if ( CC_UNLIKELY(numSubSamples >= (signed)(INT32_MAX / sizeof(*subSamples))) ) {
+ err = -EINVAL;
+ } else {
+ jboolean isCopy;
+
+ jint *numBytesOfClearData =
+ (numBytesOfClearDataObj == NULL)
+ ? NULL
+ : env->GetIntArrayElements(numBytesOfClearDataObj, &isCopy);
+
+ jint *numBytesOfEncryptedData =
+ (numBytesOfEncryptedDataObj == NULL)
+ ? NULL
+ : env->GetIntArrayElements(numBytesOfEncryptedDataObj, &isCopy);
+
+ subSamples = new CryptoPlugin::SubSample[numSubSamples];
+
+ for (jint i = 0; i < numSubSamples; ++i) {
+ subSamples[i].mNumBytesOfClearData =
+ (numBytesOfClearData == NULL) ? 0 : numBytesOfClearData[i];
+
+ subSamples[i].mNumBytesOfEncryptedData =
+ (numBytesOfEncryptedData == NULL)
+ ? 0 : numBytesOfEncryptedData[i];
+ }
+
+ if (numBytesOfEncryptedData != NULL) {
+ env->ReleaseIntArrayElements(
+ numBytesOfEncryptedDataObj, numBytesOfEncryptedData, 0);
+ numBytesOfEncryptedData = NULL;
+ }
+
+ if (numBytesOfClearData != NULL) {
+ env->ReleaseIntArrayElements(
+ numBytesOfClearDataObj, numBytesOfClearData, 0);
+ numBytesOfClearData = NULL;
+ }
+ }
+
+ if (err == OK && keyObj != NULL) {
+ if (env->GetArrayLength(keyObj) != 16) {
+ err = -EINVAL;
+ } else {
+ jboolean isCopy;
+ key = env->GetByteArrayElements(keyObj, &isCopy);
+ }
+ }
+
+ if (err == OK && ivObj != NULL) {
+ if (env->GetArrayLength(ivObj) != 16) {
+ err = -EINVAL;
+ } else {
+ jboolean isCopy;
+ iv = env->GetByteArrayElements(ivObj, &isCopy);
+ }
+ }
+
AString errorDetailMsg;
- status_t err = cryptoInfo.mErr;
if (err == OK) {
err = codec->queueSecureInputBuffer(
index, offset,
- cryptoInfo.mSubSamples, cryptoInfo.mNumSubSamples,
- (const uint8_t *)cryptoInfo.mKey, (const uint8_t *)cryptoInfo.mIv,
- cryptoInfo.mMode,
- cryptoInfo.mPattern,
+ subSamples, numSubSamples,
+ (const uint8_t *)key, (const uint8_t *)iv,
+ mode,
+ pattern,
timestampUs,
flags,
&errorDetailMsg);
}
+ if (iv != NULL) {
+ env->ReleaseByteArrayElements(ivObj, iv, 0);
+ iv = NULL;
+ }
+
+ if (key != NULL) {
+ env->ReleaseByteArrayElements(keyObj, key, 0);
+ key = NULL;
+ }
+
+ delete[] subSamples;
+ subSamples = NULL;
+
throwExceptionAsNecessary(
env, err, ACTION_CODE_FATAL, errorDetailMsg.empty() ? NULL : errorDetailMsg.c_str());
}
diff --git a/media/jni/android_media_MediaMetadataRetriever.cpp b/media/jni/android_media_MediaMetadataRetriever.cpp
index a5c62cb720cf..1c9b349a7eed 100644
--- a/media/jni/android_media_MediaMetadataRetriever.cpp
+++ b/media/jni/android_media_MediaMetadataRetriever.cpp
@@ -728,7 +728,7 @@ static const JNINativeMethod nativeMethods[] = {
(void *)android_media_MediaMetadataRetriever_getFrameAtIndex
},
- {"extractMetadata", "(I)Ljava/lang/String;",
+ {"nativeExtractMetadata", "(I)Ljava/lang/String;",
(void *)android_media_MediaMetadataRetriever_extractMetadata},
{"getEmbeddedPicture", "(I)[B",
(void *)android_media_MediaMetadataRetriever_getEmbeddedPicture},
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java
index f979fddd7bda..c529952843a1 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java
@@ -311,6 +311,12 @@ public class CameraBinderTest extends AndroidTestCase {
cameraId, status));
}
@Override
+ public void onPhysicalCameraStatusChanged(int status, String cameraId,
+ String physicalCameraId) throws RemoteException {
+ Log.v(TAG, String.format("Camera %s : %s has status changed to 0x%x",
+ cameraId, physicalCameraId, status));
+ }
+ @Override
public void onCameraAccessPrioritiesChanged() {
Log.v(TAG, "Camera access permission change");
}
diff --git a/packages/CarSystemUI/res/layout/super_notification_shade.xml b/packages/CarSystemUI/res/layout/super_notification_shade.xml
index 3fe1ea331a07..cb65045533f8 100644
--- a/packages/CarSystemUI/res/layout/super_notification_shade.xml
+++ b/packages/CarSystemUI/res/layout/super_notification_shade.xml
@@ -59,24 +59,6 @@
sysui:ignoreRightInset="true"
/>
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="@dimen/status_bar_height"
- android:orientation="vertical"
- >
- <FrameLayout
- android:id="@+id/status_bar_container"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:visibility="gone"
- />
-
- <FrameLayout
- android:id="@+id/car_top_navigation_bar_container"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"/>
- </LinearLayout>
-
<include layout="@layout/brightness_mirror"/>
<ViewStub android:id="@+id/fullscreen_user_switcher_stub"
diff --git a/packages/CarSystemUI/res/layout/super_status_bar.xml b/packages/CarSystemUI/res/layout/super_status_bar.xml
index c7b22f823ba4..d93f62f8809d 100644
--- a/packages/CarSystemUI/res/layout/super_status_bar.xml
+++ b/packages/CarSystemUI/res/layout/super_status_bar.xml
@@ -25,9 +25,22 @@
android:layout_height="match_parent"
android:fitsSystemWindows="true">
- <FrameLayout
- android:id="@+id/status_bar_container"
+ <LinearLayout
android:layout_width="match_parent"
- android:layout_height="wrap_content" />
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ >
+ <FrameLayout
+ android:id="@+id/status_bar_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:visibility="gone"
+ />
+
+ <FrameLayout
+ android:id="@+id/car_top_navigation_bar_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"/>
+ </LinearLayout>
</com.android.systemui.statusbar.phone.StatusBarWindowView>
diff --git a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBar.java b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBar.java
index dc84935034bc..3a5201517af2 100644
--- a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBar.java
+++ b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBar.java
@@ -235,7 +235,7 @@ public class CarNavigationBar extends SystemUI implements CommandQueue.Callbacks
private void buildNavBarWindows() {
mTopNavigationBarWindow = mSuperStatusBarViewFactory
- .getNotificationShadeWindowView()
+ .getStatusBarWindowView()
.findViewById(R.id.car_top_navigation_bar_container);
mBottomNavigationBarWindow = mCarNavigationBarController.getBottomWindow();
mLeftNavigationBarWindow = mCarNavigationBarController.getLeftWindow();
diff --git a/packages/FusedLocation/test/src/com/android/location/fused/tests/FusedLocationServiceTest.java b/packages/FusedLocation/test/src/com/android/location/fused/tests/FusedLocationServiceTest.java
index 33126510bc53..d1a379afa25e 100644
--- a/packages/FusedLocation/test/src/com/android/location/fused/tests/FusedLocationServiceTest.java
+++ b/packages/FusedLocation/test/src/com/android/location/fused/tests/FusedLocationServiceTest.java
@@ -205,7 +205,7 @@ public class FusedLocationServiceTest {
}
@Override
- public void onSetEnabled(boolean enabled) {
+ public void onSetAllowed(boolean allowed) {
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 486386f3d284..107207638205 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -4488,7 +4488,7 @@ public class SettingsProvider extends ContentProvider {
try {
overlayManager.setEnabledExclusiveInCategory(
NAV_BAR_MODE_2BUTTON_OVERLAY, UserHandle.USER_CURRENT);
- } catch (RemoteException e) {
+ } catch (SecurityException | IllegalStateException | RemoteException e) {
throw new IllegalStateException(
"Failed to set nav bar interaction mode overlay");
}
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index b896a2a8d9a4..5721055fa506 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -224,6 +224,7 @@ public class SettingsBackupTest {
Settings.Global.DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES,
Settings.Global.DEVELOPMENT_FORCE_RTL,
Settings.Global.DEVELOPMENT_ENABLE_SIZECOMPAT_FREEFORM,
+ Settings.Global.DEVELOPMENT_RENDER_SHADOWS_IN_COMPOSITOR,
Settings.Global.DEVICE_DEMO_MODE,
Settings.Global.DEVICE_IDLE_CONSTANTS,
Settings.Global.BATTERY_SAVER_ADAPTIVE_CONSTANTS,
@@ -574,7 +575,8 @@ public class SettingsBackupTest {
Settings.Global.ENABLED_SUBSCRIPTION_FOR_SLOT,
Settings.Global.MODEM_STACK_ENABLED_FOR_SLOT,
Settings.Global.POWER_BUTTON_LONG_PRESS,
- Settings.Global.POWER_BUTTON_VERY_LONG_PRESS);
+ Settings.Global.POWER_BUTTON_VERY_LONG_PRESS,
+ Settings.Global.INTEGRITY_CHECK_INCLUDES_RULE_PROVIDER);
private static final Set<String> BACKUP_BLACKLISTED_SECURE_SETTINGS =
newHashSet(
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index fb9bc522f527..d40e7d4addab 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -239,6 +239,13 @@
<!-- Allows setting brightness from the shell -->
<uses-permission android:name="android.permission.CONTROL_DISPLAY_BRIGHTNESS"/>
+ <!-- Permissions required to test ambient display. -->
+ <uses-permission android:name="android.permission.READ_DREAM_STATE"/>
+ <uses-permission android:name="android.permission.WRITE_DREAM_STATE"/>
+
+ <!-- Permission required for CTS test - CtsLightsManagerTest -->
+ <uses-permission android:name="android.permission.CONTROL_DEVICE_LIGHTS" />
+
<application android:label="@string/app_label"
android:theme="@android:style/Theme.DeviceDefault.DayNight"
android:defaultToDeviceProtectedStorage="true"
diff --git a/packages/SystemUI/res/drawable/ic_bubble_overflow_button.xml b/packages/SystemUI/res/drawable/ic_bubble_overflow_button.xml
new file mode 100644
index 000000000000..64b57c5aac2b
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_bubble_overflow_button.xml
@@ -0,0 +1,23 @@
+<!--
+Copyright (C) 2020 The Android Open Source Project
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:viewportHeight="24"
+ android:viewportWidth="24"
+ android:height="52dp"
+ android:width="52dp">
+ <path android:fillColor="#1A73E8"
+ android:pathData="M6,10c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zM18,10c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zM12,10c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2z"/>
+</vector> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/bubble_overflow_activity.xml b/packages/SystemUI/res/layout/bubble_overflow_activity.xml
index 4cee74615bd2..95f205a1be34 100644
--- a/packages/SystemUI/res/layout/bubble_overflow_activity.xml
+++ b/packages/SystemUI/res/layout/bubble_overflow_activity.xml
@@ -13,8 +13,9 @@
~ See the License for the specific language governing permissions and
~ limitations under the License
-->
+
<androidx.recyclerview.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/bubble_overflow_recycler"
- android:scrollbars="vertical"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"/>
+ android:layout_gravity="center_horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"/>
diff --git a/packages/SystemUI/res/layout/bubble_overflow_button.xml b/packages/SystemUI/res/layout/bubble_overflow_button.xml
new file mode 100644
index 000000000000..eb5dc9b0051a
--- /dev/null
+++ b/packages/SystemUI/res/layout/bubble_overflow_button.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<ImageView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/bubble_overflow_button"
+ android:layout_width="@dimen/individual_bubble_size"
+ android:layout_height="@dimen/individual_bubble_size"
+ android:src="@drawable/ic_bubble_overflow_button"
+ android:scaleType="center"
+ android:layout_gravity="end"/>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index e52010600ab3..edcd8012c82c 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -296,6 +296,7 @@
<item>com.android.systemui.statusbar.notification.InstantAppNotifier</item>
<item>com.android.systemui.theme.ThemeOverlayController</item>
<item>com.android.systemui.accessibility.WindowMagnification</item>
+ <item>com.android.systemui.accessibility.SystemActions</item>
</string-array>
<!-- SystemUI vender service, used in config_systemUIServiceComponents. -->
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 65ca9f2132cd..b40c5c0b0264 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -96,6 +96,9 @@
<!-- Spacing before the airplane mode icon if there are any icons preceding it. -->
<dimen name="status_bar_airplane_spacer_width">4dp</dimen>
+ <!-- Spacing between system icons. -->
+ <dimen name="status_bar_system_icon_spacing">0dp</dimen>
+
<!-- The amount to scale each of the status bar icons by. A value of 1 means no scaling. -->
<item name="status_bar_icon_scale_factor" format="float" type="dimen">1.0</item>
diff --git a/packages/SystemUI/res/values/integers.xml b/packages/SystemUI/res/values/integers.xml
index c1cf7b420078..4171cd974132 100644
--- a/packages/SystemUI/res/values/integers.xml
+++ b/packages/SystemUI/res/values/integers.xml
@@ -27,6 +27,12 @@
performance issues arise. -->
<integer name="bubbles_max_rendered">5</integer>
+ <!-- Number of columns in bubble overflow. -->
+ <integer name="bubbles_overflow_columns">4</integer>
+
+ <!-- Maximum number of bubbles we allow in overflow before we dismiss the oldest one. -->
+ <integer name="bubbles_max_overflow">16</integer>
+
<!-- Ratio of "left" end of status bar that will swipe to QQS. -->
<integer name="qqs_split_fraction">3</integer>
<!-- Ratio of "right" end of status bar that will swipe to QS. -->
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 3ff824374aac..10f59a423805 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -43,7 +43,7 @@
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowNoTitle">true</item>
<item name="android:windowContentOverlay">@null</item>
- <item name="android:windowBackground">@null</item>
+ <item name="android:windowBackground">@android:color/transparent</item>
<item name="android:colorBackgroundCacheHint">@null</item>
<item name="android:statusBarColor">@*android:color/transparent</item>
<item name="android:windowAnimationStyle">@style/Animation.PipPhoneOverlayControl</item>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
index 1cabee1ae679..2d288ff40b2c 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
@@ -16,12 +16,15 @@
package com.android.systemui.shared.recents;
+import android.graphics.Bitmap;
+import android.graphics.Insets;
import android.graphics.Rect;
import android.os.Bundle;
import android.view.MotionEvent;
/**
* Temporary callbacks into SystemUI.
+ * Next id = 22
*/
interface ISystemUiProxy {
@@ -114,4 +117,10 @@ interface ISystemUiProxy {
* Sets the shelf height and visibility.
*/
void setShelfHeight(boolean visible, int shelfHeight) = 20;
+
+ /**
+ * Handle the provided image as if it was a screenshot.
+ */
+ void handleImageAsScreenshot(in Bitmap screenImage, in Rect locationInScreen,
+ in Insets visibleInsets, int taskId) = 21;
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/ThumbnailData.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/ThumbnailData.java
index 98b7e2484478..44a8d3c53db7 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/ThumbnailData.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/ThumbnailData.java
@@ -16,6 +16,7 @@
package com.android.systemui.shared.recents.model;
+import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
import static android.content.res.Configuration.ORIENTATION_UNDEFINED;
import static com.android.systemui.shared.system.WindowManagerWrapper.WINDOWING_MODE_UNDEFINED;
@@ -31,6 +32,7 @@ public class ThumbnailData {
public final Bitmap thumbnail;
public int orientation;
+ public int rotation;
public Rect insets;
public boolean reducedResolution;
public boolean isRealSnapshot;
@@ -43,6 +45,7 @@ public class ThumbnailData {
public ThumbnailData() {
thumbnail = null;
orientation = ORIENTATION_UNDEFINED;
+ rotation = ROTATION_UNDEFINED;
insets = new Rect();
reducedResolution = false;
scale = 1f;
@@ -57,6 +60,7 @@ public class ThumbnailData {
thumbnail = Bitmap.wrapHardwareBuffer(snapshot.getSnapshot(), snapshot.getColorSpace());
insets = new Rect(snapshot.getContentInsets());
orientation = snapshot.getOrientation();
+ rotation = snapshot.getRotation();
reducedResolution = snapshot.isReducedResolution();
scale = snapshot.getScale();
isRealSnapshot = snapshot.isRealSnapshot();
diff --git a/packages/SystemUI/src/com/android/systemui/DumpController.kt b/packages/SystemUI/src/com/android/systemui/DumpController.kt
index f14c4cd8e6c6..7a83a8948bb0 100644
--- a/packages/SystemUI/src/com/android/systemui/DumpController.kt
+++ b/packages/SystemUI/src/com/android/systemui/DumpController.kt
@@ -19,10 +19,10 @@ package com.android.systemui
import android.util.ArraySet
import android.util.Log
import androidx.annotation.GuardedBy
-import com.android.internal.util.Preconditions
import java.io.FileDescriptor
import java.io.PrintWriter
import java.lang.ref.WeakReference
+import java.util.Objects.requireNonNull
import javax.inject.Inject
import javax.inject.Singleton
@@ -58,7 +58,7 @@ class DumpController @Inject constructor() : Dumpable {
* @param dumpable the [Dumpable] to be added
*/
fun registerDumpable(dumpable: Dumpable) {
- Preconditions.checkNotNull(dumpable, "The dumpable to be added cannot be null")
+ requireNonNull(dumpable, "The dumpable to be added cannot be null")
registerDumpable(dumpable.javaClass.simpleName, dumpable)
}
@@ -71,7 +71,7 @@ class DumpController @Inject constructor() : Dumpable {
* @param dumpable the [Dumpable] to be added
*/
fun registerDumpable(tag: String, dumpable: Dumpable) {
- Preconditions.checkNotNull(dumpable, "The dumpable to be added cannot be null")
+ requireNonNull(dumpable, "The dumpable to be added cannot be null")
if (DEBUG) Log.v(TAG, "*** register callback for $dumpable")
synchronized<Unit>(listeners) {
if (listeners.any { it.tag == tag }) {
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java b/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java
new file mode 100644
index 000000000000..7262f8caac89
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java
@@ -0,0 +1,393 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.accessibility;
+
+import android.accessibilityservice.AccessibilityService;
+import android.app.PendingIntent;
+import android.app.RemoteAction;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.graphics.drawable.Icon;
+import android.hardware.input.InputManager;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.PowerManager;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.util.Log;
+import android.view.Display;
+import android.view.IWindowManager;
+import android.view.InputDevice;
+import android.view.KeyCharacterMap;
+import android.view.KeyEvent;
+import android.view.WindowManager;
+import android.view.WindowManagerGlobal;
+import android.view.accessibility.AccessibilityManager;
+
+import com.android.internal.R;
+import com.android.internal.util.ScreenshotHelper;
+import com.android.systemui.Dependency;
+import com.android.systemui.SystemUI;
+import com.android.systemui.recents.Recents;
+import com.android.systemui.statusbar.phone.StatusBar;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ * Class to register system actions with accessibility framework.
+ */
+@Singleton
+public class SystemActions extends SystemUI {
+ private static final String TAG = "SystemActions";
+ // TODO(b/147916452): add implementation on launcher side to register this action.
+
+ /**
+ * Action ID to go back.
+ */
+ private static final int SYSTEM_ACTION_ID_BACK = AccessibilityService.GLOBAL_ACTION_BACK; // = 1
+
+ /**
+ * Action ID to go home.
+ */
+ private static final int SYSTEM_ACTION_ID_HOME = AccessibilityService.GLOBAL_ACTION_HOME; // = 2
+
+ /**
+ * Action ID to toggle showing the overview of recent apps. Will fail on platforms that don't
+ * show recent apps.
+ */
+ private static final int SYSTEM_ACTION_ID_RECENTS =
+ AccessibilityService.GLOBAL_ACTION_RECENTS; // = 3
+
+ /**
+ * Action ID to open the notifications.
+ */
+ private static final int SYSTEM_ACTION_ID_NOTIFICATIONS =
+ AccessibilityService.GLOBAL_ACTION_NOTIFICATIONS; // = 4
+
+ /**
+ * Action ID to open the quick settings.
+ */
+ private static final int SYSTEM_ACTION_ID_QUICK_SETTINGS =
+ AccessibilityService.GLOBAL_ACTION_QUICK_SETTINGS; // = 5
+
+ /**
+ * Action ID to open the power long-press dialog.
+ */
+ private static final int SYSTEM_ACTION_ID_POWER_DIALOG =
+ AccessibilityService.GLOBAL_ACTION_POWER_DIALOG; // = 6
+
+ /**
+ * Action ID to toggle docking the current app's window
+ */
+ private static final int SYSTEM_ACTION_ID_TOGGLE_SPLIT_SCREEN =
+ AccessibilityService.GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN; // = 7
+
+ /**
+ * Action ID to lock the screen
+ */
+ private static final int SYSTEM_ACTION_ID_LOCK_SCREEN =
+ AccessibilityService.GLOBAL_ACTION_LOCK_SCREEN; // = 8
+
+ /**
+ * Action ID to take a screenshot
+ */
+ private static final int SYSTEM_ACTION_ID_TAKE_SCREENSHOT =
+ AccessibilityService.GLOBAL_ACTION_TAKE_SCREENSHOT; // = 9
+
+ /**
+ * Action ID to show accessibility menu
+ */
+ private static final int SYSTEM_ACTION_ID_ACCESSIBILITY_MENU = 10;
+
+ private Recents mRecents;
+ private StatusBar mStatusBar;
+ private SystemActionsBroadcastReceiver mReceiver;
+
+ @Inject
+ public SystemActions(Context context) {
+ super(context);
+ mRecents = Dependency.get(Recents.class);
+ mStatusBar = Dependency.get(StatusBar.class);
+ mReceiver = new SystemActionsBroadcastReceiver();
+ }
+
+ @Override
+ public void start() {
+ mContext.registerReceiverForAllUsers(mReceiver, mReceiver.createIntentFilter(), null, null);
+
+ // TODO(b/148087487): update the icon used below to a valid one
+ RemoteAction actionBack = new RemoteAction(
+ Icon.createWithResource(mContext, R.drawable.ic_info),
+ mContext.getString(R.string.accessibility_system_action_back_label),
+ mContext.getString(R.string.accessibility_system_action_back_label),
+ mReceiver.createPendingIntent(
+ mContext, SystemActionsBroadcastReceiver.INTENT_ACTION_BACK));
+ RemoteAction actionHome = new RemoteAction(
+ Icon.createWithResource(mContext, R.drawable.ic_info),
+ mContext.getString(R.string.accessibility_system_action_home_label),
+ mContext.getString(R.string.accessibility_system_action_home_label),
+ mReceiver.createPendingIntent(
+ mContext, SystemActionsBroadcastReceiver.INTENT_ACTION_HOME));
+
+ RemoteAction actionRecents = new RemoteAction(
+ Icon.createWithResource(mContext, R.drawable.ic_info),
+ mContext.getString(R.string.accessibility_system_action_recents_label),
+ mContext.getString(R.string.accessibility_system_action_recents_label),
+ mReceiver.createPendingIntent(
+ mContext, SystemActionsBroadcastReceiver.INTENT_ACTION_RECENTS));
+
+ RemoteAction actionNotifications = new RemoteAction(
+ Icon.createWithResource(mContext, R.drawable.ic_info),
+ mContext.getString(R.string.accessibility_system_action_notifications_label),
+ mContext.getString(R.string.accessibility_system_action_notifications_label),
+ mReceiver.createPendingIntent(
+ mContext, SystemActionsBroadcastReceiver.INTENT_ACTION_NOTIFICATIONS));
+
+ RemoteAction actionQuickSettings = new RemoteAction(
+ Icon.createWithResource(mContext, R.drawable.ic_info),
+ mContext.getString(R.string.accessibility_system_action_quick_settings_label),
+ mContext.getString(R.string.accessibility_system_action_quick_settings_label),
+ mReceiver.createPendingIntent(
+ mContext, SystemActionsBroadcastReceiver.INTENT_ACTION_QUICK_SETTINGS));
+
+ RemoteAction actionPowerDialog = new RemoteAction(
+ Icon.createWithResource(mContext, R.drawable.ic_info),
+ mContext.getString(R.string.accessibility_system_action_power_dialog_label),
+ mContext.getString(R.string.accessibility_system_action_power_dialog_label),
+ mReceiver.createPendingIntent(
+ mContext, SystemActionsBroadcastReceiver.INTENT_ACTION_POWER_DIALOG));
+
+ RemoteAction actionToggleSplitScreen = new RemoteAction(
+ Icon.createWithResource(mContext, R.drawable.ic_info),
+ mContext.getString(R.string.accessibility_system_action_toggle_split_screen_label),
+ mContext.getString(R.string.accessibility_system_action_toggle_split_screen_label),
+ mReceiver.createPendingIntent(
+ mContext,
+ SystemActionsBroadcastReceiver.INTENT_ACTION_TOGGLE_SPLIT_SCREEN));
+
+ RemoteAction actionLockScreen = new RemoteAction(
+ Icon.createWithResource(mContext, R.drawable.ic_info),
+ mContext.getString(R.string.accessibility_system_action_lock_screen_label),
+ mContext.getString(R.string.accessibility_system_action_lock_screen_label),
+ mReceiver.createPendingIntent(
+ mContext, SystemActionsBroadcastReceiver.INTENT_ACTION_LOCK_SCREEN));
+
+ RemoteAction actionTakeScreenshot = new RemoteAction(
+ Icon.createWithResource(mContext, R.drawable.ic_info),
+ mContext.getString(R.string.accessibility_system_action_screenshot_label),
+ mContext.getString(R.string.accessibility_system_action_screenshot_label),
+ mReceiver.createPendingIntent(
+ mContext, SystemActionsBroadcastReceiver.INTENT_ACTION_TAKE_SCREENSHOT));
+
+ RemoteAction actionAccessibilityMenu = new RemoteAction(
+ Icon.createWithResource(mContext, R.drawable.ic_info),
+ mContext.getString(R.string.accessibility_system_action_accessibility_menu_label),
+ mContext.getString(R.string.accessibility_system_action_accessibility_menu_label),
+ mReceiver.createPendingIntent(
+ mContext, SystemActionsBroadcastReceiver.INTENT_ACTION_ACCESSIBILITY_MENU));
+
+ AccessibilityManager am = (AccessibilityManager) mContext.getSystemService(
+ Context.ACCESSIBILITY_SERVICE);
+
+ am.registerSystemAction(actionBack, SYSTEM_ACTION_ID_BACK);
+ am.registerSystemAction(actionHome, SYSTEM_ACTION_ID_HOME);
+ am.registerSystemAction(actionRecents, SYSTEM_ACTION_ID_RECENTS);
+ am.registerSystemAction(actionNotifications, SYSTEM_ACTION_ID_NOTIFICATIONS);
+ am.registerSystemAction(actionQuickSettings, SYSTEM_ACTION_ID_QUICK_SETTINGS);
+ am.registerSystemAction(actionPowerDialog, SYSTEM_ACTION_ID_POWER_DIALOG);
+ am.registerSystemAction(actionToggleSplitScreen, SYSTEM_ACTION_ID_TOGGLE_SPLIT_SCREEN);
+ am.registerSystemAction(actionLockScreen, SYSTEM_ACTION_ID_LOCK_SCREEN);
+ am.registerSystemAction(actionTakeScreenshot, SYSTEM_ACTION_ID_TAKE_SCREENSHOT);
+ am.registerSystemAction(actionAccessibilityMenu, SYSTEM_ACTION_ID_ACCESSIBILITY_MENU);
+ }
+
+ private void handleBack() {
+ sendDownAndUpKeyEvents(KeyEvent.KEYCODE_BACK);
+ }
+
+ private void handleHome() {
+ sendDownAndUpKeyEvents(KeyEvent.KEYCODE_HOME);
+ }
+
+ private void sendDownAndUpKeyEvents(int keyCode) {
+ final long downTime = SystemClock.uptimeMillis();
+ sendKeyEventIdentityCleared(keyCode, KeyEvent.ACTION_DOWN, downTime, downTime);
+ sendKeyEventIdentityCleared(
+ keyCode, KeyEvent.ACTION_UP, downTime, SystemClock.uptimeMillis());
+ }
+
+ private void sendKeyEventIdentityCleared(int keyCode, int action, long downTime, long time) {
+ KeyEvent event = KeyEvent.obtain(downTime, time, action, keyCode, 0, 0,
+ KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FROM_SYSTEM,
+ InputDevice.SOURCE_KEYBOARD, null);
+ InputManager.getInstance()
+ .injectInputEvent(event, InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
+ event.recycle();
+ }
+
+ private void handleRecents() {
+ mRecents.toggleRecentApps();
+ }
+
+ private void handleNotifications() {
+ mStatusBar.animateExpandNotificationsPanel();
+ }
+
+ private void handleQuickSettings() {
+ mStatusBar.animateExpandSettingsPanel(null);
+ }
+
+ private void handlePowerDialog() {
+ IWindowManager windowManager = WindowManagerGlobal.getWindowManagerService();
+
+ try {
+ windowManager.showGlobalActions();
+ } catch (RemoteException e) {
+ Log.e(TAG, "failed to display power dialog.");
+ }
+ }
+
+ private void handleToggleSplitScreen() {
+ mStatusBar.toggleSplitScreen();
+ }
+
+ private void handleLockScreen() {
+ IWindowManager windowManager = WindowManagerGlobal.getWindowManagerService();
+
+ mContext.getSystemService(PowerManager.class).goToSleep(SystemClock.uptimeMillis(),
+ PowerManager.GO_TO_SLEEP_REASON_ACCESSIBILITY, 0);
+ try {
+ windowManager.lockNow(null);
+ } catch (RemoteException e) {
+ Log.e(TAG, "failed to lock screen.");
+ }
+ }
+
+ private void handleTakeScreenshot() {
+ ScreenshotHelper screenshotHelper = new ScreenshotHelper(mContext);
+ screenshotHelper.takeScreenshot(WindowManager.TAKE_SCREENSHOT_FULLSCREEN,
+ true, true, new Handler(Looper.getMainLooper()), null);
+ }
+
+ private void handleAccessibilityMenu() {
+ AccessibilityManager.getInstance(mContext).notifyAccessibilityButtonClicked(
+ Display.DEFAULT_DISPLAY);
+ }
+
+ private class SystemActionsBroadcastReceiver extends BroadcastReceiver {
+ private static final String INTENT_ACTION_BACK = "SYSTEM_ACTION_BACK";
+ private static final String INTENT_ACTION_HOME = "SYSTEM_ACTION_HOME";
+ private static final String INTENT_ACTION_RECENTS = "SYSTEM_ACTION_RECENTS";
+ private static final String INTENT_ACTION_NOTIFICATIONS = "SYSTEM_ACTION_NOTIFICATIONS";
+ private static final String INTENT_ACTION_QUICK_SETTINGS = "SYSTEM_ACTION_QUICK_SETTINGS";
+ private static final String INTENT_ACTION_POWER_DIALOG = "SYSTEM_ACTION_POWER_DIALOG";
+ private static final String INTENT_ACTION_TOGGLE_SPLIT_SCREEN =
+ "SYSTEM_ACTION_TOGGLE_SPLIT_SCREEN";
+ private static final String INTENT_ACTION_LOCK_SCREEN = "SYSTEM_ACTION_LOCK_SCREEN";
+ private static final String INTENT_ACTION_TAKE_SCREENSHOT = "SYSTEM_ACTION_TAKE_SCREENSHOT";
+ private static final String INTENT_ACTION_ACCESSIBILITY_MENU =
+ "SYSTEM_ACTION_ACCESSIBILITY_MENU";
+
+ private PendingIntent createPendingIntent(Context context, String intentAction) {
+ switch (intentAction) {
+ case INTENT_ACTION_BACK:
+ case INTENT_ACTION_HOME:
+ case INTENT_ACTION_RECENTS:
+ case INTENT_ACTION_NOTIFICATIONS:
+ case INTENT_ACTION_QUICK_SETTINGS:
+ case INTENT_ACTION_POWER_DIALOG:
+ case INTENT_ACTION_TOGGLE_SPLIT_SCREEN:
+ case INTENT_ACTION_LOCK_SCREEN:
+ case INTENT_ACTION_TAKE_SCREENSHOT:
+ case INTENT_ACTION_ACCESSIBILITY_MENU: {
+ Intent intent = new Intent(intentAction);
+ return PendingIntent.getBroadcast(context, 0, intent, 0);
+ }
+ default:
+ break;
+ }
+ return null;
+ }
+
+ private IntentFilter createIntentFilter() {
+ IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction(INTENT_ACTION_BACK);
+ intentFilter.addAction(INTENT_ACTION_HOME);
+ intentFilter.addAction(INTENT_ACTION_RECENTS);
+ intentFilter.addAction(INTENT_ACTION_NOTIFICATIONS);
+ intentFilter.addAction(INTENT_ACTION_QUICK_SETTINGS);
+ intentFilter.addAction(INTENT_ACTION_POWER_DIALOG);
+ intentFilter.addAction(INTENT_ACTION_TOGGLE_SPLIT_SCREEN);
+ intentFilter.addAction(INTENT_ACTION_LOCK_SCREEN);
+ intentFilter.addAction(INTENT_ACTION_TAKE_SCREENSHOT);
+ intentFilter.addAction(INTENT_ACTION_ACCESSIBILITY_MENU);
+ return intentFilter;
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String intentAction = intent.getAction();
+ switch (intentAction) {
+ case INTENT_ACTION_BACK: {
+ handleBack();
+ break;
+ }
+ case INTENT_ACTION_HOME: {
+ handleHome();
+ break;
+ }
+ case INTENT_ACTION_RECENTS: {
+ handleRecents();
+ break;
+ }
+ case INTENT_ACTION_NOTIFICATIONS: {
+ handleNotifications();
+ break;
+ }
+ case INTENT_ACTION_QUICK_SETTINGS: {
+ handleQuickSettings();
+ break;
+ }
+ case INTENT_ACTION_POWER_DIALOG: {
+ handlePowerDialog();
+ break;
+ }
+ case INTENT_ACTION_TOGGLE_SPLIT_SCREEN: {
+ handleToggleSplitScreen();
+ break;
+ }
+ case INTENT_ACTION_LOCK_SCREEN: {
+ handleLockScreen();
+ break;
+ }
+ case INTENT_ACTION_TAKE_SCREENSHOT: {
+ handleTakeScreenshot();
+ break;
+ }
+ case INTENT_ACTION_ACCESSIBILITY_MENU: {
+ handleAccessibilityMenu();
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java
index ccfd3a57811c..82c8a469a057 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java
@@ -72,10 +72,10 @@ public class AuthCredentialPasswordView extends AuthCredentialView
}
// Wait a bit to focus the field so the focusable flag on the window is already set then.
- post(() -> {
+ postDelayed(() -> {
mPasswordField.requestFocus();
mImm.showSoftInput(mPasswordField, InputMethodManager.SHOW_IMPLICIT);
- });
+ }, 100);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BadgedImageView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BadgedImageView.java
index dc2499602125..601bae286451 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BadgedImageView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BadgedImageView.java
@@ -89,12 +89,12 @@ public class BadgedImageView extends ImageView {
/**
* Updates the view with provided info.
*/
- public void update(Bubble bubble, Bitmap bubbleImage, int dotColor, Path dotPath) {
+ public void update(Bubble bubble) {
mBubble = bubble;
- setImageBitmap(bubbleImage);
+ setImageBitmap(bubble.getBadgedImage());
setDotState(DOT_STATE_SUPPRESSED_FOR_FLYOUT);
- mDotColor = dotColor;
- drawDot(dotPath);
+ mDotColor = bubble.getDotColor();
+ drawDot(bubble.getDotPath());
animateDot();
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
index 2d9775deb4b5..ccce85ca74fb 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
@@ -31,6 +31,8 @@ import android.content.pm.LauncherApps;
import android.content.pm.PackageManager;
import android.content.pm.ShortcutInfo;
import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
@@ -93,6 +95,9 @@ class Bubble {
}
private FlyoutMessage mFlyoutMessage;
+ private Bitmap mBadgedImage;
+ private int mDotColor;
+ private Path mDotPath;
public static String groupId(NotificationEntry entry) {
UserHandle user = entry.getSbn().getUser();
@@ -124,6 +129,18 @@ class Bubble {
return mEntry.getSbn().getPackageName();
}
+ public Bitmap getBadgedImage() {
+ return mBadgedImage;
+ }
+
+ public int getDotColor() {
+ return mDotColor;
+ }
+
+ public Path getDotPath() {
+ return mDotPath;
+ }
+
@Nullable
public String getAppName() {
return mAppName;
@@ -205,8 +222,12 @@ class Bubble {
mAppName = info.appName;
mFlyoutMessage = info.flyoutMessage;
+ mBadgedImage = info.badgedBubbleImage;
+ mDotColor = info.dotColor;
+ mDotPath = info.dotPath;
+
mExpandedView.update(this);
- mIconView.update(this, info.badgedBubbleImage, info.dotColor, info.dotPath);
+ mIconView.update(this);
}
/**
@@ -262,6 +283,13 @@ class Bubble {
}
/**
+ * Should be invoked whenever a Bubble is promoted from overflow.
+ */
+ void markUpdatedAt(long lastAccessedMillis) {
+ mLastUpdated = lastAccessedMillis;
+ }
+
+ /**
* Whether this notification should be shown in the shade when it is also displayed as a
* bubble.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index e642d4e16802..8c9946fcfc7b 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -103,6 +103,7 @@ import java.lang.annotation.Target;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.List;
import java.util.function.Consumer;
import javax.inject.Inject;
@@ -151,6 +152,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
private BubbleData mBubbleData;
@Nullable private BubbleStackView mStackView;
private BubbleIconFactory mBubbleIconFactory;
+ private int mMaxBubbles;
// Tracks the id of the current (foreground) user.
private int mCurrentUserId;
@@ -171,6 +173,8 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
private StatusBarStateListener mStatusBarStateListener;
private final ScreenshotHelper mScreenshotHelper;
+ // Callback that updates BubbleOverflowActivity on data change.
+ @Nullable private Runnable mOverflowCallback = null;
private final NotificationInterruptionStateProvider mNotificationInterruptionStateProvider;
private IStatusBarService mBarService;
@@ -301,6 +305,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
mBubbleData = data;
mBubbleData.setListener(mBubbleDataListener);
+ mMaxBubbles = mContext.getResources().getInteger(R.integer.bubbles_max_rendered);
mNotificationEntryManager = entryManager;
mNotificationEntryManager.addNotificationEntryListener(mEntryListener);
@@ -370,6 +375,18 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
mInflateSynchronously = inflateSynchronously;
}
+ void setOverflowCallback(Runnable updateOverflow) {
+ mOverflowCallback = updateOverflow;
+ }
+
+ /**
+ * @return Bubbles for updating overflow.
+ */
+ List<Bubble> getOverflowBubbles() {
+ return mBubbleData.getOverflowBubbles();
+ }
+
+
/**
* BubbleStackView is lazily created by this method the first time a Bubble is added. This
* method initializes the stack view and adds it to the StatusBar just above the scrim.
@@ -537,6 +554,10 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
mBubbleData.setSelectedBubble(bubble);
}
+ void promoteBubbleFromOverflow(Bubble bubble) {
+ mBubbleData.promoteBubbleFromOverflow(bubble);
+ }
+
/**
* Request the stack expand if needed, then select the specified Bubble as current.
*
@@ -817,6 +838,11 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
@Override
public void applyUpdate(BubbleData.Update update) {
+ // Update bubbles in overflow.
+ if (mOverflowCallback != null) {
+ mOverflowCallback.run();
+ }
+
if (update.addedBubble != null) {
mStackView.addBubble(update.addedBubble);
}
@@ -890,6 +916,8 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
mStackView.updateBubble(update.updatedBubble);
}
+ // At this point, the correct bubbles are inflated in the stack.
+ // Make sure the order in bubble data is reflected in bubble row.
if (update.orderChanged) {
mStackView.updateBubbleOrder(update.bubbles);
}
@@ -912,15 +940,18 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
updateStack();
if (DEBUG_BUBBLE_CONTROLLER) {
- Log.d(TAG, "[BubbleData]");
+ Log.d(TAG, "\n[BubbleData] bubbles:");
Log.d(TAG, BubbleDebugConfig.formatBubblesString(mBubbleData.getBubbles(),
mBubbleData.getSelectedBubble()));
if (mStackView != null) {
- Log.d(TAG, "[BubbleStackView]");
+ Log.d(TAG, "\n[BubbleStackView]");
Log.d(TAG, BubbleDebugConfig.formatBubblesString(mStackView.getBubblesOnScreen(),
mStackView.getExpandedBubble()));
}
+ Log.d(TAG, "\n[BubbleData] overflow:");
+ Log.d(TAG, BubbleDebugConfig.formatBubblesString(mBubbleData.getOverflowBubbles(),
+ null));
}
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
index cc0824ecc45c..8b687e7114db 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
@@ -78,9 +78,11 @@ public class BubbleData {
// A read-only view of the bubbles list, changes there will be reflected here.
final List<Bubble> bubbles;
+ final List<Bubble> overflowBubbles;
- private Update(List<Bubble> bubbleOrder) {
- bubbles = Collections.unmodifiableList(bubbleOrder);
+ private Update(List<Bubble> row, List<Bubble> overflow) {
+ bubbles = Collections.unmodifiableList(row);
+ overflowBubbles = Collections.unmodifiableList(overflow);
}
boolean anythingChanged() {
@@ -113,11 +115,14 @@ public class BubbleData {
private final Context mContext;
/** Bubbles that are actively in the stack. */
private final List<Bubble> mBubbles;
+ /** Bubbles that aged out to overflow. */
+ private final List<Bubble> mOverflowBubbles;
/** Bubbles that are being loaded but haven't been added to the stack just yet. */
private final List<Bubble> mPendingBubbles;
private Bubble mSelectedBubble;
private boolean mExpanded;
private final int mMaxBubbles;
+ private final int mMaxOverflowBubbles;
// State tracked during an operation -- keeps track of what listener events to dispatch.
private Update mStateChange;
@@ -146,9 +151,11 @@ public class BubbleData {
public BubbleData(Context context) {
mContext = context;
mBubbles = new ArrayList<>();
+ mOverflowBubbles = new ArrayList<>();
mPendingBubbles = new ArrayList<>();
- mStateChange = new Update(mBubbles);
+ mStateChange = new Update(mBubbles, mOverflowBubbles);
mMaxBubbles = mContext.getResources().getInteger(R.integer.bubbles_max_rendered);
+ mMaxOverflowBubbles = mContext.getResources().getInteger(R.integer.bubbles_max_overflow);
}
public boolean hasBubbles() {
@@ -184,6 +191,19 @@ public class BubbleData {
dispatchPendingChanges();
}
+ public void promoteBubbleFromOverflow(Bubble bubble) {
+ if (DEBUG_BUBBLE_DATA) {
+ Log.d(TAG, "promoteBubbleFromOverflow: " + bubble);
+ }
+ mOverflowBubbles.remove(bubble);
+ doAdd(bubble);
+ setSelectedBubbleInternal(bubble);
+ // Preserve new order for next repack, which sorts by last updated time.
+ bubble.markUpdatedAt(mTimeSource.currentTimeMillis());
+ trim();
+ dispatchPendingChanges();
+ }
+
/**
* Constructs a new bubble or returns an existing one. Does not add new bubbles to
* bubble data, must go through {@link #notificationEntryUpdated(Bubble, boolean, boolean)}
@@ -343,6 +363,7 @@ public class BubbleData {
mStateChange.orderChanged = true;
}
mStateChange.addedBubble = bubble;
+
if (!isExpanded()) {
mStateChange.orderChanged |= packGroup(findFirstIndexForGroup(bubble.getGroupId()));
// Top bubble becomes selected.
@@ -407,6 +428,17 @@ public class BubbleData {
mStateChange.orderChanged |= repackAll();
}
+ if (reason == BubbleController.DISMISS_AGED) {
+ if (DEBUG_BUBBLE_DATA) {
+ Log.d(TAG, "overflowing bubble: " + bubbleToRemove);
+ }
+ mOverflowBubbles.add(0, bubbleToRemove);
+ if (mOverflowBubbles.size() == mMaxOverflowBubbles + 1) {
+ // Remove oldest bubble.
+ mOverflowBubbles.remove(mOverflowBubbles.size() - 1);
+ }
+ }
+
// Note: If mBubbles.isEmpty(), then mSelectedBubble is now null.
if (Objects.equals(mSelectedBubble, bubbleToRemove)) {
// Move selection to the new bubble at the same position.
@@ -454,7 +486,7 @@ public class BubbleData {
if (mListener != null && mStateChange.anythingChanged()) {
mListener.applyUpdate(mStateChange);
}
- mStateChange = new Update(mBubbles);
+ mStateChange = new Update(mBubbles, mOverflowBubbles);
}
/**
@@ -689,12 +721,19 @@ public class BubbleData {
}
/**
- * The set of bubbles.
+ * The set of bubbles in row.
*/
@VisibleForTesting(visibility = PRIVATE)
public List<Bubble> getBubbles() {
return Collections.unmodifiableList(mBubbles);
}
+ /**
+ * The set of bubbles in overflow.
+ */
+ @VisibleForTesting(visibility = PRIVATE)
+ public List<Bubble> getOverflowBubbles() {
+ return Collections.unmodifiableList(mOverflowBubbles);
+ }
@VisibleForTesting(visibility = PRIVATE)
Bubble getBubbleWithKey(String key) {
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
index 48ce4e9b0097..cf8b2be1becb 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
@@ -24,6 +24,7 @@ import static com.android.systemui.bubbles.BubbleDebugConfig.DEBUG_BUBBLE_EXPAND
import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_BUBBLES;
import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME;
+import android.annotation.Nullable;
import android.app.ActivityOptions;
import android.app.ActivityTaskManager;
import android.app.ActivityView;
@@ -83,7 +84,7 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList
private ActivityViewStatus mActivityViewStatus = ActivityViewStatus.INITIALIZING;
private int mTaskId = -1;
- private PendingIntent mBubbleIntent;
+ private PendingIntent mPendingIntent;
private boolean mKeyboardVisible;
private boolean mNeedsNewHeight;
@@ -98,7 +99,9 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList
private int[] mTempLoc = new int[2];
private int mExpandedViewTouchSlop;
- private Bubble mBubble;
+ @Nullable private Bubble mBubble;
+
+ private boolean mIsOverflow;
private BubbleController mBubbleController = Dependency.get(BubbleController.class);
private WindowManager mWindowManager;
@@ -125,7 +128,7 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList
+ "bubble=" + getBubbleKey());
}
try {
- if (mBubble.usingShortcutInfo()) {
+ if (!mIsOverflow && mBubble.usingShortcutInfo()) {
mActivityView.startShortcutActivity(mBubble.getShortcutInfo(),
options, null /* sourceBounds */);
} else {
@@ -133,7 +136,7 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList
// Apply flags to make behaviour match documentLaunchMode=always.
fillInIntent.addFlags(FLAG_ACTIVITY_NEW_DOCUMENT);
fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
- mActivityView.startActivity(mBubbleIntent, fillInIntent, options);
+ mActivityView.startActivity(mPendingIntent, fillInIntent, options);
}
} catch (RuntimeException e) {
// If there's a runtime exception here then there's something
@@ -141,7 +144,7 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList
// the bubble again so we'll just remove it.
Log.w(TAG, "Exception while displaying bubble: " + getBubbleKey()
+ ", " + e.getMessage() + "; removing bubble");
- mBubbleController.removeBubble(mBubble.getKey(),
+ mBubbleController.removeBubble(getBubbleKey(),
BubbleController.DISMISS_INVALID_INTENT);
}
});
@@ -241,6 +244,7 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList
mActivityView = new ActivityView(mContext, null /* attrs */, 0 /* defStyle */,
true /* singleTaskInstance */);
+
// Set ActivityView's alpha value as zero, since there is no view content to be shown.
setContentVisibility(false);
addView(mActivityView);
@@ -342,6 +346,15 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList
mStackView = stackView;
}
+ public void setOverflow(boolean overflow) {
+ mIsOverflow = overflow;
+
+ Intent target = new Intent(mContext, BubbleOverflowActivity.class);
+ mPendingIntent = PendingIntent.getActivity(mContext, /* requestCode */ 0,
+ target, PendingIntent.FLAG_UPDATE_CURRENT);
+ mSettingsIcon.setVisibility(GONE);
+ }
+
/**
* Sets the bubble used to populate this view.
*/
@@ -350,14 +363,14 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList
Log.d(TAG, "update: bubble=" + (bubble != null ? bubble.getKey() : "null"));
}
boolean isNew = mBubble == null;
- if (isNew || bubble.getKey().equals(mBubble.getKey())) {
+ if (isNew || bubble != null && bubble.getKey().equals(mBubble.getKey())) {
mBubble = bubble;
mSettingsIcon.setContentDescription(getResources().getString(
R.string.bubbles_settings_button_description, bubble.getAppName()));
if (isNew) {
- mBubbleIntent = mBubble.getBubbleIntent();
- if (mBubbleIntent != null || mBubble.getShortcutInfo() != null) {
+ mPendingIntent = mBubble.getBubbleIntent();
+ if (mPendingIntent != null || mBubble.getShortcutInfo() != null) {
setContentVisibility(false);
mActivityView.setVisibility(VISIBLE);
}
@@ -393,12 +406,16 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList
return true;
}
+ // TODO(138116789) Fix overflow height.
void updateHeight() {
if (DEBUG_BUBBLE_EXPANDED_VIEW) {
Log.d(TAG, "updateHeight: bubble=" + getBubbleKey());
}
if (usingActivityView()) {
- float desiredHeight = Math.max(mBubble.getDesiredHeight(mContext), mMinHeight);
+ float desiredHeight = mMinHeight;
+ if (!mIsOverflow) {
+ desiredHeight = Math.max(mBubble.getDesiredHeight(mContext), mMinHeight);
+ }
float height = Math.min(desiredHeight, getMaxExpandedHeight());
height = Math.max(height, mMinHeight);
LayoutParams lp = (LayoutParams) mActivityView.getLayoutParams();
@@ -423,8 +440,12 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList
int bottomInset = getRootWindowInsets() != null
? getRootWindowInsets().getStableInsetBottom()
: 0;
- return mDisplaySize.y - windowLocation[1] - mSettingsIconHeight - mPointerHeight
+ int mh = mDisplaySize.y - windowLocation[1] - mSettingsIconHeight - mPointerHeight
- mPointerMargin - bottomInset;
+ Log.i(TAG, "max exp height: " + mh);
+// return mDisplaySize.y - windowLocation[1] - mSettingsIconHeight - mPointerHeight
+// - mPointerMargin - bottomInset;
+ return mh;
}
/**
@@ -543,7 +564,7 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList
}
private boolean usingActivityView() {
- return (mBubbleIntent != null || mBubble.getShortcutInfo() != null)
+ return (mPendingIntent != null || mBubble.getShortcutInfo() != null)
&& mActivityView != null;
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExperimentConfig.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExperimentConfig.java
index 4252f72b808e..006de8406ce2 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExperimentConfig.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExperimentConfig.java
@@ -76,6 +76,9 @@ public class BubbleExperimentConfig {
private static final String ALLOW_BUBBLE_MENU = "allow_bubble_screenshot_menu";
private static final boolean ALLOW_BUBBLE_MENU_DEFAULT = false;
+ private static final String ALLOW_BUBBLE_OVERFLOW = "allow_bubble_overflow";
+ private static final boolean ALLOW_BUBBLE_OVERFLOW_DEFAULT = false;
+
/**
* When true, if a notification has the information necessary to bubble (i.e. valid
* contentIntent and an icon or image), then a {@link android.app.Notification.BubbleMetadata}
@@ -141,6 +144,16 @@ public class BubbleExperimentConfig {
}
/**
+ * When true, show a menu when a bubble is long-pressed, which will allow the user to take
+ * actions on that bubble.
+ */
+ static boolean allowBubbleOverflow(Context context) {
+ return Settings.Secure.getInt(context.getContentResolver(),
+ ALLOW_BUBBLE_OVERFLOW,
+ ALLOW_BUBBLE_OVERFLOW_DEFAULT ? 1 : 0) != 0;
+ }
+
+ /**
* If {@link #allowAnyNotifToBubble(Context)} is true, this method creates and adds
* {@link android.app.Notification.BubbleMetadata} to the notification entry as long as
* the notification has necessary info for BubbleMetadata.
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java
index d99607fd6236..bea55c820b40 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java
@@ -1,15 +1,42 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package com.android.systemui.bubbles;
+import static com.android.systemui.bubbles.BubbleDebugConfig.DEBUG_OVERFLOW;
+import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_BUBBLES;
+import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME;
+
import android.app.Activity;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.os.Bundle;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.ViewGroup;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.android.systemui.R;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Consumer;
+
import javax.inject.Inject;
/**
@@ -17,9 +44,13 @@ import javax.inject.Inject;
* Must be public to be accessible to androidx...AppComponentFactory
*/
public class BubbleOverflowActivity extends Activity {
+ private static final String TAG = TAG_WITH_CLASS_NAME ? "BubbleOverflowActivity" : TAG_BUBBLES;
+
+ private BubbleController mBubbleController;
+ private BubbleOverflowAdapter mAdapter;
private RecyclerView mRecyclerView;
+ private List<Bubble> mOverflowBubbles = new ArrayList<>();
private int mMaxBubbles;
- private BubbleController mBubbleController;
@Inject
public BubbleOverflowActivity(BubbleController controller) {
@@ -35,17 +66,42 @@ public class BubbleOverflowActivity extends Activity {
mMaxBubbles = getResources().getInteger(R.integer.bubbles_max_rendered);
mRecyclerView = findViewById(R.id.bubble_overflow_recycler);
mRecyclerView.setLayoutManager(
- new GridLayoutManager(getApplicationContext(), /* numberOfColumns */ mMaxBubbles));
+ new GridLayoutManager(getApplicationContext(),
+ getResources().getInteger(R.integer.bubbles_overflow_columns)));
+
+ mAdapter = new BubbleOverflowAdapter(mOverflowBubbles,
+ mBubbleController::promoteBubbleFromOverflow);
+ mRecyclerView.setAdapter(mAdapter);
+
+ updateData(mBubbleController.getOverflowBubbles());
+ mBubbleController.setOverflowCallback(() -> {
+ updateData(mBubbleController.getOverflowBubbles());
+ });
}
void setBackgroundColor() {
final TypedArray ta = getApplicationContext().obtainStyledAttributes(
- new int[] {android.R.attr.colorBackgroundFloating});
+ new int[]{android.R.attr.colorBackgroundFloating});
int bgColor = ta.getColor(0, Color.WHITE);
ta.recycle();
findViewById(android.R.id.content).setBackgroundColor(bgColor);
}
+ void updateData(List<Bubble> bubbles) {
+ mOverflowBubbles.clear();
+ if (bubbles.size() > mMaxBubbles) {
+ mOverflowBubbles.addAll(bubbles.subList(mMaxBubbles, bubbles.size()));
+ } else {
+ mOverflowBubbles.addAll(bubbles);
+ }
+ mAdapter.notifyDataSetChanged();
+
+ if (DEBUG_OVERFLOW) {
+ Log.d(TAG, "Updated overflow bubbles:\n" + BubbleDebugConfig.formatBubblesString(
+ mOverflowBubbles, /*selected*/ null));
+ }
+ }
+
@Override
public void onStart() {
super.onStart();
@@ -75,3 +131,48 @@ public class BubbleOverflowActivity extends Activity {
super.onDestroy();
}
}
+
+class BubbleOverflowAdapter extends RecyclerView.Adapter<BubbleOverflowAdapter.ViewHolder> {
+ private Consumer<Bubble> mPromoteBubbleFromOverflow;
+ private List<Bubble> mBubbles;
+
+ public BubbleOverflowAdapter(List<Bubble> list, Consumer<Bubble> promoteBubble) {
+ mBubbles = list;
+ mPromoteBubbleFromOverflow = promoteBubble;
+ }
+
+ @Override
+ public BubbleOverflowAdapter.ViewHolder onCreateViewHolder(ViewGroup parent,
+ int viewType) {
+ BadgedImageView view = (BadgedImageView) LayoutInflater.from(parent.getContext())
+ .inflate(R.layout.bubble_view, parent, false);
+ view.setPadding(15, 15, 15, 15);
+ return new ViewHolder(view);
+ }
+
+ @Override
+ public void onBindViewHolder(ViewHolder vh, int index) {
+ Bubble bubble = mBubbles.get(index);
+
+ vh.mBadgedImageView.update(bubble);
+ vh.mBadgedImageView.setOnClickListener(view -> {
+ mBubbles.remove(bubble);
+ notifyDataSetChanged();
+ mPromoteBubbleFromOverflow.accept(bubble);
+ });
+ }
+
+ @Override
+ public int getItemCount() {
+ return mBubbles.size();
+ }
+
+ public static class ViewHolder extends RecyclerView.ViewHolder {
+ public BadgedImageView mBadgedImageView;
+
+ public ViewHolder(BadgedImageView v) {
+ super(v);
+ mBadgedImageView = v;
+ }
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index 54a42a6ec212..fe4c91509057 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -33,6 +33,8 @@ import android.app.Notification;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.Color;
import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
import android.graphics.Paint;
@@ -40,6 +42,9 @@ import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.RectF;
+import android.graphics.drawable.AdaptiveIconDrawable;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.InsetDrawable;
import android.os.Bundle;
import android.os.VibrationEffect;
import android.os.Vibrator;
@@ -58,6 +63,7 @@ import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.widget.FrameLayout;
+import android.widget.ImageView;
import androidx.annotation.MainThread;
import androidx.annotation.Nullable;
@@ -195,8 +201,6 @@ public class BubbleStackView extends FrameLayout {
private int mPointerHeight;
private int mStatusBarHeight;
private int mImeOffset;
- private int mBubbleMenuOffset = 252;
- private BubbleIconFactory mBubbleIconFactory;
private Bubble mExpandedBubble;
private boolean mIsExpanded;
@@ -320,6 +324,8 @@ public class BubbleStackView extends FrameLayout {
private Runnable mAfterMagnet;
private int mOrientation = Configuration.ORIENTATION_UNDEFINED;
+ private BubbleExpandedView mOverflowExpandedView;
+ private ImageView mOverflowBtn;
public BubbleStackView(Context context, BubbleData data,
@Nullable SurfaceSynchronizer synchronizer) {
@@ -369,8 +375,6 @@ public class BubbleStackView extends FrameLayout {
mBubbleContainer.setClipChildren(false);
addView(mBubbleContainer, new FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT));
- mBubbleIconFactory = new BubbleIconFactory(context);
-
mExpandedViewContainer = new FrameLayout(context);
mExpandedViewContainer.setElevation(elevation);
mExpandedViewContainer.setPadding(mExpandedViewPadding, mExpandedViewPadding,
@@ -414,6 +418,10 @@ public class BubbleStackView extends FrameLayout {
setFocusable(true);
mBubbleContainer.bringToFront();
+ if (BubbleExperimentConfig.allowBubbleOverflow(mContext)) {
+ setUpOverflow();
+ }
+
setOnApplyWindowInsetsListener((View view, WindowInsets insets) -> {
if (!mIsExpanded || mIsExpansionAnimating) {
return view.onApplyWindowInsets(insets);
@@ -421,7 +429,13 @@ public class BubbleStackView extends FrameLayout {
mExpandedAnimationController.updateYPosition(
// Update the insets after we're done translating otherwise position
// calculation for them won't be correct.
- () -> mExpandedBubble.getExpandedView().updateInsets(insets));
+ () -> {
+ if (mExpandedBubble == null) {
+ mOverflowExpandedView.updateInsets(insets);
+ } else {
+ mExpandedBubble.getExpandedView().updateInsets(insets);
+ }
+ });
return view.onApplyWindowInsets(insets);
});
@@ -433,7 +447,11 @@ public class BubbleStackView extends FrameLayout {
// Reposition & adjust the height for new orientation
if (mIsExpanded) {
mExpandedViewContainer.setTranslationY(getExpandedViewY());
- mExpandedBubble.getExpandedView().updateView();
+ if (mExpandedBubble == null) {
+ mOverflowExpandedView.updateView();
+ } else {
+ mExpandedBubble.getExpandedView().updateView();
+ }
}
// Need to update the padding around the view
@@ -499,6 +517,51 @@ public class BubbleStackView extends FrameLayout {
mBubbleMenuView = findViewById(R.id.bubble_menu_container);
}
+ private void setUpOverflow() {
+ mOverflowExpandedView = (BubbleExpandedView) mInflater.inflate(
+ R.layout.bubble_expanded_view, this /* root */, false /* attachToRoot */);
+ mOverflowExpandedView.setOverflow(true);
+
+ mInflater.inflate(R.layout.bubble_overflow_button, this);
+ mOverflowBtn = findViewById(R.id.bubble_overflow_button);
+ mOverflowBtn.setOnClickListener(v -> {
+ showOverflow();
+ });
+
+ TypedArray ta = mContext.obtainStyledAttributes(
+ new int[]{android.R.attr.colorBackgroundFloating});
+ int bgColor = ta.getColor(0, Color.WHITE /* default */);
+ ta.recycle();
+
+ InsetDrawable fg = new InsetDrawable(mOverflowBtn.getDrawable(), 28);
+ ColorDrawable bg = new ColorDrawable(bgColor);
+ AdaptiveIconDrawable adaptiveIcon = new AdaptiveIconDrawable(bg, fg);
+ mOverflowBtn.setImageDrawable(adaptiveIcon);
+ mOverflowBtn.setVisibility(GONE);
+ }
+
+ void showOverflow() {
+ if (DEBUG_BUBBLE_STACK_VIEW) {
+ Log.d(TAG, "Show overflow.");
+ }
+ mExpandedViewContainer.setAlpha(0.0f);
+ mSurfaceSynchronizer.syncSurfaceAndRun(() -> {
+ if (mExpandedBubble != null) {
+ mExpandedBubble.setContentVisibility(false);
+ mExpandedBubble = null;
+ }
+ mExpandedViewContainer.removeAllViews();
+ if (mIsExpanded) {
+ mExpandedViewContainer.addView(mOverflowExpandedView);
+ mOverflowExpandedView.populateExpandedView();
+ mExpandedViewContainer.setVisibility(VISIBLE);
+ mExpandedViewContainer.setAlpha(1.0f);
+ mOverflowExpandedView.setContentVisibility(true);
+ }
+ requestUpdate();
+ });
+ }
+
private void setUpFlyout() {
if (mFlyout != null) {
removeView(mFlyout);
@@ -734,9 +797,10 @@ public class BubbleStackView extends FrameLayout {
new FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
ViewClippingUtil.setClippingDeactivated(bubble.getIconView(), true, mClippingParameters);
animateInFlyoutForBubble(bubble);
+ updatePointerPosition();
+ updateOverflowBtnVisibility( /*apply */ true);
requestUpdate();
logBubbleEvent(bubble, SysUiStatsLog.BUBBLE_UICHANGED__ACTION__POSTED);
- updatePointerPosition();
}
// via BubbleData.Listener
@@ -753,7 +817,32 @@ public class BubbleStackView extends FrameLayout {
} else {
Log.d(TAG, "was asked to remove Bubble, but didn't find the view! " + bubble);
}
- updatePointerPosition();
+ updateOverflowBtnVisibility(/* apply */ true);
+ }
+
+ private void updateOverflowBtnVisibility(boolean apply) {
+ if (!BubbleExperimentConfig.allowBubbleOverflow(mContext)) {
+ return;
+ }
+ if (mIsExpanded) {
+ if (DEBUG_BUBBLE_STACK_VIEW) {
+ Log.d(TAG, "Expanded && overflow > 0. Show overflow button at");
+ Log.d(TAG, "x: " + mExpandedAnimationController.getOverflowBtnLeft());
+ Log.d(TAG, "y: " + mExpandedAnimationController.getExpandedY());
+ }
+ mOverflowBtn.setX(mExpandedAnimationController.getOverflowBtnLeft());
+ mOverflowBtn.setY(mExpandedAnimationController.getExpandedY());
+ mOverflowBtn.setVisibility(VISIBLE);
+ mExpandedAnimationController.setShowOverflowBtn(true);
+ if (apply) {
+ mExpandedAnimationController.expandFromStack(null /* after */);
+ }
+ } else {
+ if (DEBUG_BUBBLE_STACK_VIEW) {
+ Log.d(TAG, "Collapsed. Hide overflow button.");
+ }
+ mOverflowBtn.setVisibility(GONE);
+ }
}
// via BubbleData.Listener
@@ -953,6 +1042,12 @@ public class BubbleStackView extends FrameLayout {
final Bubble previouslySelected = mExpandedBubble;
beforeExpandedViewAnimation();
+ if (DEBUG_BUBBLE_STACK_VIEW) {
+ Log.d(TAG, "animateCollapse");
+ Log.d(TAG, BubbleDebugConfig.formatBubblesString(this.getBubblesOnScreen(),
+ this.getExpandedBubble()));
+ }
+ updateOverflowBtnVisibility(/* apply */ false);
mBubbleContainer.cancelAllAnimations();
mExpandedAnimationController.collapseBackToStack(
mStackAnimationController.getStackPositionAlongNearestHorizontalEdge()
@@ -960,7 +1055,9 @@ public class BubbleStackView extends FrameLayout {
() -> {
mBubbleContainer.setActiveController(mStackAnimationController);
afterExpandedViewAnimation();
- previouslySelected.setContentVisibility(false);
+ if (previouslySelected != null) {
+ previouslySelected.setContentVisibility(false);
+ }
});
mExpandedViewXAnim.animateToFinalPosition(getCollapsedX());
@@ -975,12 +1072,12 @@ public class BubbleStackView extends FrameLayout {
beforeExpandedViewAnimation();
mBubbleContainer.setActiveController(mExpandedAnimationController);
+ updateOverflowBtnVisibility(/* apply */ false);
mExpandedAnimationController.expandFromStack(() -> {
updatePointerPosition();
afterExpandedViewAnimation();
} /* after */);
-
mExpandedViewContainer.setTranslationX(getCollapsedX());
mExpandedViewContainer.setTranslationY(getCollapsedY());
mExpandedViewContainer.setAlpha(0f);
@@ -1063,9 +1160,11 @@ public class BubbleStackView extends FrameLayout {
Log.d(TAG, "onDragStart()");
}
if (mIsExpanded || mIsExpansionAnimating) {
+ if (DEBUG_BUBBLE_STACK_VIEW) {
+ Log.d(TAG, "mIsExpanded or mIsExpansionAnimating");
+ }
return;
}
-
hideBubbleMenu();
mStackAnimationController.cancelStackPositionAnimations();
mBubbleContainer.setActiveController(mStackAnimationController);
@@ -1545,7 +1644,11 @@ public class BubbleStackView extends FrameLayout {
if (!mExpandedViewYAnim.isRunning()) {
// We're not animating so set the value
mExpandedViewContainer.setTranslationY(y);
- mExpandedBubble.getExpandedView().updateView();
+ if (mExpandedBubble == null) {
+ mOverflowExpandedView.updateView();
+ } else {
+ mExpandedBubble.getExpandedView().updateView();
+ }
} else {
// We are animating so update the value; there is an end listener on the animator
// that will ensure expandedeView.updateView gets called.
@@ -1571,23 +1674,28 @@ public class BubbleStackView extends FrameLayout {
}
private void updatePointerPosition() {
- if (DEBUG_BUBBLE_STACK_VIEW) {
- Log.d(TAG, "updatePointerPosition()");
- }
-
Bubble expandedBubble = getExpandedBubble();
if (expandedBubble == null) {
return;
}
int index = getBubbleIndex(expandedBubble);
+ if (index >= mMaxBubbles) {
+ // In between state, where extra bubble will be overflowed, and new bubble added
+ index = 0;
+ }
float bubbleLeftFromScreenLeft = mExpandedAnimationController.getBubbleLeft(index);
float halfBubble = mBubbleSize / 2f;
float bubbleCenter = bubbleLeftFromScreenLeft + halfBubble;
// Padding might be adjusted for insets, so get it directly from the view
bubbleCenter -= mExpandedViewContainer.getPaddingLeft();
- expandedBubble.getExpandedView().setPointerPosition(bubbleCenter);
+ if (index >= mMaxBubbles) {
+ Bubble first = mBubbleData.getBubbles().get(0);
+ first.getExpandedView().setPointerPosition(bubbleCenter);
+ } else {
+ expandedBubble.getExpandedView().setPointerPosition(bubbleCenter);
+ }
}
/**
@@ -1680,7 +1788,11 @@ public class BubbleStackView extends FrameLayout {
if (!isExpanded()) {
return false;
}
- return mExpandedBubble.getExpandedView().performBackPressIfNeeded();
+ if (mExpandedBubble == null) {
+ return mOverflowExpandedView.performBackPressIfNeeded();
+ } else {
+ return mExpandedBubble.getExpandedView().performBackPressIfNeeded();
+ }
}
/** For debugging only */
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java
index e7055847f034..6e23777babd9 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java
@@ -157,6 +157,7 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask
PackageManager pm = c.getPackageManager();
ApplicationInfo appInfo;
Drawable badgedIcon;
+ Drawable appIcon;
try {
appInfo = pm.getApplicationInfo(
packageName,
@@ -167,7 +168,7 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask
if (appInfo != null) {
info.appName = String.valueOf(pm.getApplicationLabel(appInfo));
}
- Drawable appIcon = pm.getApplicationIcon(packageName);
+ appIcon = pm.getApplicationIcon(packageName);
badgedIcon = pm.getUserBadgedIcon(appIcon, sbn.getUser());
} catch (PackageManager.NameNotFoundException exception) {
// If we can't find package... don't think we should show the bubble.
@@ -178,6 +179,11 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask
// Badged bubble image
Drawable bubbleDrawable = iconFactory.getBubbleDrawable(c, info.shortcutInfo,
b.getEntry().getBubbleMetadata());
+ if (bubbleDrawable == null) {
+ // Default to app icon
+ bubbleDrawable = appIcon;
+ }
+
BitmapInfo badgeBitmapInfo = iconFactory.getBadgeBitmap(badgedIcon);
info.badgedBubbleImage = iconFactory.getBubbleBitmap(bubbleDrawable,
badgeBitmapInfo).icon;
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java b/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java
index 6528f3762473..6d6969da8c8a 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java
@@ -67,6 +67,8 @@ public class ExpandedAnimationController
private float mBubblePaddingTop;
/** Size of each bubble. */
private float mBubbleSizePx;
+ /** Width of the overflow button. */
+ private float mOverflowBtnWidth;
/** Height of the status bar. */
private float mStatusBarHeight;
/** Size of display. */
@@ -81,7 +83,7 @@ public class ExpandedAnimationController
private boolean mAnimatingExpand = false;
private boolean mAnimatingCollapse = false;
- private Runnable mAfterExpand;
+ private @Nullable Runnable mAfterExpand;
private Runnable mAfterCollapse;
private PointF mCollapsePoint;
@@ -97,6 +99,7 @@ public class ExpandedAnimationController
private boolean mSpringingBubbleToTouch = false;
private int mExpandedViewPadding;
+ private boolean mShowOverflowBtn;
public ExpandedAnimationController(Point displaySize, int expandedViewPadding,
int orientation) {
@@ -116,7 +119,7 @@ public class ExpandedAnimationController
/**
* Animates expanding the bubbles into a row along the top of the screen.
*/
- public void expandFromStack(Runnable after) {
+ public void expandFromStack(@Nullable Runnable after) {
mAnimatingCollapse = false;
mAnimatingExpand = true;
mAfterExpand = after;
@@ -150,6 +153,14 @@ public class ExpandedAnimationController
}
}
+ public void setShowOverflowBtn(boolean showBtn) {
+ mShowOverflowBtn = showBtn;
+ }
+
+ public boolean getShowOverflowBtn() {
+ return mShowOverflowBtn;
+ }
+
/**
* Animates the bubbles along a curved path, either to expand them along the top or collapse
* them back into a stack.
@@ -380,6 +391,7 @@ public class ExpandedAnimationController
mStackOffsetPx = res.getDimensionPixelSize(R.dimen.bubble_stack_offset);
mBubblePaddingTop = res.getDimensionPixelSize(R.dimen.bubble_padding_top);
mBubbleSizePx = res.getDimensionPixelSize(R.dimen.individual_bubble_size);
+ mOverflowBtnWidth = mBubbleSizePx;
mStatusBarHeight =
res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
mBubblesMaxRendered = res.getInteger(R.integer.bubbles_max_rendered);
@@ -498,6 +510,14 @@ public class ExpandedAnimationController
return getRowLeft() + bubbleFromRowLeft;
}
+ public float getOverflowBtnLeft() {
+ if (mLayout == null || mLayout.getChildCount() == 0) {
+ return 0;
+ }
+ return getBubbleLeft(mLayout.getChildCount() - 1) + mBubbleSizePx
+ + getSpaceBetweenBubbles();
+ }
+
/**
* When expanded, the bubbles are centered in the screen. In portrait, all available space is
* used. In landscape we have too much space so the value is restricted. This method accounts
@@ -505,7 +525,7 @@ public class ExpandedAnimationController
*
* @return the desired width to display the expanded bubbles in.
*/
- private float getWidthForDisplayingBubbles() {
+ public float getWidthForDisplayingBubbles() {
final float availableWidth = getAvailableScreenWidth(true /* includeStableInsets */);
if (mScreenOrientation == Configuration.ORIENTATION_LANDSCAPE) {
// display size y in landscape will be the smaller dimension of the screen
@@ -551,7 +571,11 @@ public class ExpandedAnimationController
final float totalBubbleWidth = bubbleCount * mBubbleSizePx;
final float totalGapWidth = (bubbleCount - 1) * getSpaceBetweenBubbles();
- final float rowWidth = totalGapWidth + totalBubbleWidth;
+ float rowWidth = totalGapWidth + totalBubbleWidth;
+ if (mShowOverflowBtn) {
+ rowWidth += getSpaceBetweenBubbles();
+ rowWidth += mOverflowBtnWidth;
+ }
// This display size we're using includes the size of the insets, we want the true
// center of the display minus the notch here, which means we should include the
@@ -559,7 +583,6 @@ public class ExpandedAnimationController
final float trueCenter = getAvailableScreenWidth(false /* subtractStableInsets */) / 2f;
final float halfRow = rowWidth / 2f;
final float rowLeft = trueCenter - halfRow;
-
return rowLeft;
}
@@ -567,12 +590,12 @@ public class ExpandedAnimationController
* @return Space between bubbles in row above expanded view.
*/
private float getSpaceBetweenBubbles() {
- final float rowMargins = mExpandedViewPadding * 2;
- final float maxRowWidth = getWidthForDisplayingBubbles() - rowMargins;
-
final float totalBubbleWidth = mBubblesMaxRendered * mBubbleSizePx;
- final float totalGapWidth = maxRowWidth - totalBubbleWidth;
-
+ final float rowMargins = mExpandedViewPadding * 2;
+ float totalGapWidth = getWidthForDisplayingBubbles() - rowMargins - totalBubbleWidth;
+ if (mShowOverflowBtn) {
+ totalGapWidth -= mBubbleSizePx;
+ }
final int gapCount = mBubblesMaxRendered - 1;
final float gapWidth = totalGapWidth / gapCount;
return gapWidth;
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt
index 80e48b925fc9..2db2cf1af191 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt
@@ -20,7 +20,10 @@ import android.content.ComponentName
import android.content.Context
import android.os.IBinder
import android.service.controls.Control
-import android.service.controls.IControlsProviderCallback
+import android.service.controls.IControlsActionCallback
+import android.service.controls.IControlsLoadCallback
+import android.service.controls.IControlsSubscriber
+import android.service.controls.IControlsSubscription
import android.service.controls.actions.ControlAction
import android.util.ArrayMap
import android.util.Log
@@ -54,25 +57,40 @@ open class ControlsBindingControllerImpl @Inject constructor(
private val componentMap: MutableMap<ComponentName, ControlsProviderLifecycleManager> =
ArrayMap<ComponentName, ControlsProviderLifecycleManager>()
- private val serviceCallback = object : IControlsProviderCallback.Stub() {
- override fun onLoad(token: IBinder, controls: MutableList<Control>) {
+ private val loadCallbackService = object : IControlsLoadCallback.Stub() {
+ override fun accept(token: IBinder, controls: MutableList<Control>) {
backgroundExecutor.execute(OnLoadRunnable(token, controls))
}
+ }
+
+ private val actionCallbackService = object : IControlsActionCallback.Stub() {
+ override fun accept(
+ token: IBinder,
+ controlId: String,
+ @ControlAction.ResponseResult response: Int
+ ) {
+ backgroundExecutor.execute(OnActionResponseRunnable(token, controlId, response))
+ }
+ }
+
+ private val subscriberService = object : IControlsSubscriber.Stub() {
+ override fun onSubscribe(token: IBinder, subs: IControlsSubscription) {
+ backgroundExecutor.execute(OnSubscribeRunnable(token, subs))
+ }
- override fun onRefreshState(token: IBinder, controlStates: List<Control>) {
+ override fun onNext(token: IBinder, c: Control) {
if (!refreshing.get()) {
Log.d(TAG, "Refresh outside of window for token:$token")
} else {
- backgroundExecutor.execute(OnRefreshStateRunnable(token, controlStates))
+ backgroundExecutor.execute(OnNextRunnable(token, c))
}
}
+ override fun onError(token: IBinder, s: String) {
+ backgroundExecutor.execute(OnErrorRunnable(token, s))
+ }
- override fun onControlActionResponse(
- token: IBinder,
- controlId: String,
- @ControlAction.ResponseResult response: Int
- ) {
- backgroundExecutor.execute(OnActionResponseRunnable(token, controlId, response))
+ override fun onComplete(token: IBinder) {
+ backgroundExecutor.execute(OnCompleteRunnable(token))
}
}
@@ -82,7 +100,9 @@ open class ControlsBindingControllerImpl @Inject constructor(
return ControlsProviderLifecycleManager(
context,
backgroundExecutor,
- serviceCallback,
+ loadCallbackService,
+ actionCallbackService,
+ subscriberService,
component
)
}
@@ -176,16 +196,51 @@ open class ControlsBindingControllerImpl @Inject constructor(
}
}
- private inner class OnRefreshStateRunnable(
+ private inner class OnNextRunnable(
token: IBinder,
- val list: List<Control>
+ val control: Control
) : CallbackRunnable(token) {
override fun run() {
if (!refreshing.get()) {
Log.d(TAG, "onRefresh outside of window from:${provider?.componentName}")
}
provider?.let {
- lazyController.get().refreshStatus(it.componentName, list)
+ lazyController.get().refreshStatus(it.componentName, control)
+ }
+ }
+ }
+
+ private inner class OnSubscribeRunnable(
+ token: IBinder,
+ val subscription: IControlsSubscription
+ ) : CallbackRunnable(token) {
+ override fun run() {
+ if (!refreshing.get()) {
+ Log.d(TAG, "onRefresh outside of window from '${provider?.componentName}'")
+ }
+ provider?.let {
+ it.startSubscription(subscription)
+ }
+ }
+ }
+
+ private inner class OnCompleteRunnable(
+ token: IBinder
+ ) : CallbackRunnable(token) {
+ override fun run() {
+ provider?.let {
+ Log.i(TAG, "onComplete receive from '${provider?.componentName}'")
+ }
+ }
+ }
+
+ private inner class OnErrorRunnable(
+ token: IBinder,
+ val error: String
+ ) : CallbackRunnable(token) {
+ override fun run() {
+ provider?.let {
+ Log.e(TAG, "onError receive from '${provider?.componentName}': $error")
}
}
}
@@ -201,4 +256,4 @@ open class ControlsBindingControllerImpl @Inject constructor(
}
}
}
-} \ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt
index 4d958224e917..e098faa00d03 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt
@@ -30,11 +30,11 @@ interface ControlsController {
fun changeFavoriteStatus(controlInfo: ControlInfo, state: Boolean)
fun unsubscribe()
fun action(controlInfo: ControlInfo, action: ControlAction)
- fun refreshStatus(componentName: ComponentName, controls: List<Control>)
+ fun refreshStatus(componentName: ComponentName, control: Control)
fun onActionResponse(
componentName: ComponentName,
controlId: String,
@ControlAction.ResponseResult response: Int
)
fun clearFavorites()
-} \ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
index 7e328e467129..d5b5b5f0442e 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
@@ -210,20 +210,20 @@ class ControlsControllerImpl @Inject constructor (
}
}
- override fun refreshStatus(componentName: ComponentName, controls: List<Control>) {
+ override fun refreshStatus(componentName: ComponentName, control: Control) {
if (!available) {
Log.d(TAG, "Controls not available")
return
}
executor.execute {
synchronized(currentFavorites) {
- val changed = updateFavoritesLocked(componentName, controls)
+ val changed = updateFavoritesLocked(componentName, listOf(control))
if (changed) {
persistenceWrapper.storeFavorites(favoritesAsListLocked())
}
}
}
- uiController.onRefreshState(componentName, controls)
+ uiController.onRefreshState(componentName, listOf(control))
}
override fun onActionResponse(componentName: ComponentName, controlId: String, response: Int) {
@@ -270,4 +270,4 @@ class ControlsControllerImpl @Inject constructor (
}
}
}
-} \ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt
index 79057ad25b20..99aa3601ba30 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt
@@ -25,11 +25,13 @@ import android.os.Bundle
import android.os.IBinder
import android.os.RemoteException
import android.service.controls.Control
-import android.service.controls.ControlsProviderService.CALLBACK_BINDER
import android.service.controls.ControlsProviderService.CALLBACK_BUNDLE
import android.service.controls.ControlsProviderService.CALLBACK_TOKEN
+import android.service.controls.IControlsActionCallback
+import android.service.controls.IControlsLoadCallback
import android.service.controls.IControlsProvider
-import android.service.controls.IControlsProviderCallback
+import android.service.controls.IControlsSubscriber
+import android.service.controls.IControlsSubscription
import android.service.controls.actions.ControlAction
import android.util.ArraySet
import android.util.Log
@@ -41,19 +43,23 @@ typealias LoadCallback = (List<Control>) -> Unit
class ControlsProviderLifecycleManager(
private val context: Context,
private val executor: DelayableExecutor,
- private val serviceCallback: IControlsProviderCallback.Stub,
+ private val loadCallbackService: IControlsLoadCallback.Stub,
+ private val actionCallbackService: IControlsActionCallback.Stub,
+ private val subscriberService: IControlsSubscriber.Stub,
val componentName: ComponentName
) : IBinder.DeathRecipient {
var lastLoadCallback: LoadCallback? = null
private set
val token: IBinder = Binder()
+ @GuardedBy("subscriptions")
+ private val subscriptions = mutableListOf<IControlsSubscription>()
private var unbindImmediate = false
private var requiresBound = false
private var isBound = false
@GuardedBy("queuedMessages")
private val queuedMessages: MutableSet<Message> = ArraySet()
- private var wrapper: ControlsProviderServiceWrapper? = null
+ private var wrapper: ServiceWrapper? = null
private var bindTryCount = 0
private val TAG = javaClass.simpleName
private var onLoadCanceller: Runnable? = null
@@ -61,12 +67,12 @@ class ControlsProviderLifecycleManager(
companion object {
private const val MSG_LOAD = 0
private const val MSG_SUBSCRIBE = 1
- private const val MSG_UNSUBSCRIBE = 2
- private const val MSG_ON_ACTION = 3
- private const val MSG_UNBIND = 4
+ private const val MSG_ACTION = 2
+ private const val MSG_UNBIND = 3
private const val BIND_RETRY_DELAY = 1000L // ms
private const val LOAD_TIMEOUT = 5000L // ms
private const val MAX_BIND_RETRIES = 5
+ private const val MAX_CONTROLS_REQUEST = 100000L
private const val DEBUG = true
private val BIND_FLAGS = Context.BIND_AUTO_CREATE or Context.BIND_FOREGROUND_SERVICE or
Context.BIND_WAIVE_PRIORITY
@@ -75,7 +81,6 @@ class ControlsProviderLifecycleManager(
private val intent = Intent().apply {
component = componentName
putExtra(CALLBACK_BUNDLE, Bundle().apply {
- putBinder(CALLBACK_BINDER, serviceCallback)
putBinder(CALLBACK_TOKEN, token)
})
}
@@ -119,7 +124,7 @@ class ControlsProviderLifecycleManager(
override fun onServiceConnected(name: ComponentName, service: IBinder) {
if (DEBUG) Log.d(TAG, "onServiceConnected $name")
bindTryCount = 0
- wrapper = ControlsProviderServiceWrapper(IControlsProvider.Stub.asInterface(service))
+ wrapper = ServiceWrapper(IControlsProvider.Stub.asInterface(service))
try {
service.linkToDeath(this@ControlsProviderLifecycleManager, 0)
} catch (_: RemoteException) {}
@@ -151,7 +156,7 @@ class ControlsProviderLifecycleManager(
}
queue.filter { it is Message.Action }.forEach {
val msg = it as Message.Action
- onAction(msg.id, msg.action)
+ action(msg.id, msg.action)
}
}
@@ -182,7 +187,7 @@ class ControlsProviderLifecycleManager(
if (DEBUG) {
Log.d(TAG, "load $componentName")
}
- if (!(wrapper?.load() ?: false)) {
+ if (!(wrapper?.load(loadCallbackService) ?: false)) {
queueMessage(Message.Load)
binderDied()
}
@@ -194,7 +199,7 @@ class ControlsProviderLifecycleManager(
onLoadCanceller = executor.executeDelayed({
// Didn't receive a response in time, log and send back empty list
Log.d(TAG, "Timeout waiting onLoad for $componentName")
- serviceCallback.onLoad(token, emptyList())
+ loadCallbackService.accept(token, emptyList())
}, LOAD_TIMEOUT, TimeUnit.MILLISECONDS)
if (isBound) {
load()
@@ -218,7 +223,7 @@ class ControlsProviderLifecycleManager(
if (DEBUG) {
Log.d(TAG, "subscribe $componentName - $controlIds")
}
- if (!(wrapper?.subscribe(controlIds) ?: false)) {
+ if (!(wrapper?.subscribe(controlIds, subscriberService) ?: false)) {
queueMessage(Message.Subscribe(controlIds))
binderDied()
}
@@ -226,29 +231,45 @@ class ControlsProviderLifecycleManager(
fun maybeBindAndSendAction(controlId: String, action: ControlAction) {
if (isBound) {
- onAction(controlId, action)
+ action(controlId, action)
} else {
queueMessage(Message.Action(controlId, action))
bindService(true)
}
}
- private fun onAction(controlId: String, action: ControlAction) {
+ private fun action(controlId: String, action: ControlAction) {
if (DEBUG) {
Log.d(TAG, "onAction $componentName - $controlId")
}
- if (!(wrapper?.onAction(controlId, action) ?: false)) {
+ if (!(wrapper?.action(controlId, action, actionCallbackService) ?: false)) {
queueMessage(Message.Action(controlId, action))
binderDied()
}
}
+ fun startSubscription(subscription: IControlsSubscription) {
+ synchronized(subscriptions) {
+ subscriptions.add(subscription)
+ }
+ wrapper?.request(subscription, MAX_CONTROLS_REQUEST)
+ }
+
fun unsubscribe() {
if (DEBUG) {
Log.d(TAG, "unsubscribe $componentName")
}
unqueueMessage(Message.Subscribe(emptyList())) // Removes all subscribe messages
- wrapper?.unsubscribe()
+
+ val subs = synchronized(subscriptions) {
+ ArrayList(subscriptions).also {
+ subscriptions.clear()
+ }
+ }
+
+ subs.forEach {
+ wrapper?.cancel(it)
+ }
}
fun maybeUnbindAndRemoveCallback() {
@@ -277,7 +298,7 @@ class ControlsProviderLifecycleManager(
override val type = MSG_SUBSCRIBE
}
class Action(val id: String, val action: ControlAction) : Message() {
- override val type = MSG_ON_ACTION
+ override val type = MSG_ACTION
}
}
-} \ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderServiceWrapper.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ServiceWrapper.kt
index 882a10d54431..5c812b1347e5 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderServiceWrapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ServiceWrapper.kt
@@ -17,12 +17,17 @@
package com.android.systemui.controls.controller
import android.service.controls.actions.ControlAction
+import android.service.controls.IControlsActionCallback
+import android.service.controls.IControlsLoadCallback
import android.service.controls.IControlsProvider
+import android.service.controls.IControlsSubscriber
+import android.service.controls.IControlsSubscription
+import android.service.controls.actions.ControlActionWrapper
import android.util.Log
-class ControlsProviderServiceWrapper(val service: IControlsProvider) {
+class ServiceWrapper(val service: IControlsProvider) {
companion object {
- private const val TAG = "ControlsProviderServiceWrapper"
+ private const val TAG = "ServiceWrapper"
}
private fun callThroughService(block: () -> Unit): Boolean {
@@ -30,32 +35,42 @@ class ControlsProviderServiceWrapper(val service: IControlsProvider) {
block()
return true
} catch (ex: Exception) {
- Log.d(TAG, "Caught exception from ControlsProviderService", ex)
+ Log.e(TAG, "Caught exception from ControlsProviderService", ex)
return false
}
}
- fun load(): Boolean {
+ fun load(cb: IControlsLoadCallback): Boolean {
return callThroughService {
- service.load()
+ service.load(cb)
}
}
- fun subscribe(controlIds: List<String>): Boolean {
+ fun subscribe(controlIds: List<String>, subscriber: IControlsSubscriber): Boolean {
return callThroughService {
- service.subscribe(controlIds)
+ service.subscribe(controlIds, subscriber)
}
}
- fun unsubscribe(): Boolean {
+ fun request(subscription: IControlsSubscription, num: Long): Boolean {
return callThroughService {
- service.unsubscribe()
+ subscription.request(num)
}
}
- fun onAction(controlId: String, action: ControlAction): Boolean {
+ fun cancel(subscription: IControlsSubscription): Boolean {
return callThroughService {
- service.onAction(controlId, action)
+ subscription.cancel()
}
}
-} \ No newline at end of file
+
+ fun action(
+ controlId: String,
+ action: ControlAction,
+ cb: IControlsActionCallback
+ ): Boolean {
+ return callThroughService {
+ service.action(controlId, ControlActionWrapper(action), cb)
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingControllerImpl.kt
index 937216230123..3949c5929a85 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingControllerImpl.kt
@@ -51,7 +51,7 @@ class ControlsListingControllerImpl @VisibleForTesting constructor(
context,
executor,
ServiceListing.Builder(context)
- .setIntentAction(ControlsProviderService.CONTROLS_ACTION)
+ .setIntentAction(ControlsProviderService.SERVICE_CONTROLS)
.setPermission("android.permission.BIND_CONTROLS")
.setNoun("Controls Provider")
.setSetting("controls_providers")
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
index 99dd5e2356d6..d4e47f699345 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
@@ -21,6 +21,7 @@ import com.android.systemui.ScreenDecorations;
import com.android.systemui.SizeCompatModeActivityController;
import com.android.systemui.SliceBroadcastRelayHandler;
import com.android.systemui.SystemUI;
+import com.android.systemui.accessibility.SystemActions;
import com.android.systemui.accessibility.WindowMagnification;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.globalactions.GlobalActionsComponent;
@@ -140,6 +141,12 @@ public abstract class SystemUIBinder {
@ClassKey(StatusBar.class)
public abstract SystemUI bindsStatusBar(StatusBar sysui);
+ /** Inject into SystemActions. */
+ @Binds
+ @IntoMap
+ @ClassKey(SystemActions.class)
+ public abstract SystemUI bindSystemActions(SystemActions sysui);
+
/** Inject into ThemeOverlayController. */
@Binds
@IntoMap
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index 569f660d1797..79a33c926993 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -38,6 +38,8 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
+import android.graphics.Bitmap;
+import android.graphics.Insets;
import android.graphics.Rect;
import android.graphics.Region;
import android.hardware.input.InputManager;
@@ -55,6 +57,7 @@ import android.view.MotionEvent;
import android.view.accessibility.AccessibilityManager;
import com.android.internal.policy.ScreenDecorationsUtils;
+import com.android.internal.util.ScreenshotHelper;
import com.android.systemui.Dumpable;
import com.android.systemui.model.SysUiState;
import com.android.systemui.pip.PipUI;
@@ -115,6 +118,7 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
private final DeviceProvisionedController mDeviceProvisionedController;
private final List<OverviewProxyListener> mConnectionCallbacks = new ArrayList<>();
private final Intent mQuickStepIntent;
+ private final ScreenshotHelper mScreenshotHelper;
private Region mActiveNavBarRegion;
@@ -365,6 +369,13 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
}
}
+ @Override
+ public void handleImageAsScreenshot(Bitmap screenImage, Rect locationInScreen,
+ Insets visibleInsets, int taskId) {
+ mScreenshotHelper.provideScreenshot(screenImage, locationInScreen, visibleInsets,
+ taskId, mHandler, null);
+ }
+
private boolean verifyCaller(String reason) {
final int callerId = Binder.getCallingUserHandle().getIdentifier();
if (callerId != mCurrentBoundedUserId) {
@@ -518,6 +529,7 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
// Listen for status bar state changes
statusBarWinController.registerCallback(mStatusBarWindowCallback);
+ mScreenshotHelper = new ScreenshotHelper(context);
}
public void notifyBackAction(boolean completed, int downX, int downY, boolean isButton,
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
index 99a9dfeae1d6..880b8f8776e8 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
@@ -29,6 +29,7 @@ import android.animation.AnimatorSet;
import android.animation.ValueAnimator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.app.ActivityManager;
import android.app.ActivityOptions;
import android.app.Notification;
@@ -38,6 +39,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.graphics.Bitmap;
+import android.graphics.Insets;
import android.graphics.Outline;
import android.graphics.PixelFormat;
import android.graphics.PointF;
@@ -300,8 +302,11 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
int width = crop.width();
int height = crop.height();
- // Take the screenshot
- mScreenBitmap = SurfaceControl.screenshot(crop, width, height, rot);
+ takeScreenshot(SurfaceControl.screenshot(crop, width, height, rot), finisher, null);
+ }
+
+ private void takeScreenshot(Bitmap screenshot, Consumer<Uri> finisher, Rect screenRect) {
+ mScreenBitmap = screenshot;
if (mScreenBitmap == null) {
mNotificationsController.notifyScreenshotError(
R.string.screenshot_failed_to_capture_text);
@@ -317,7 +322,8 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
mScreenshotLayout.getViewTreeObserver().addOnComputeInternalInsetsListener(this);
// Start the post-screenshot animation
- startAnimation(finisher, mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels);
+ startAnimation(finisher, mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels,
+ screenRect);
}
void takeScreenshot(Consumer<Uri> finisher) {
@@ -327,9 +333,16 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
new Rect(0, 0, mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels));
}
+ void handleImageAsScreenshot(Bitmap screenshot, Rect screenshotScreenBounds,
+ Insets visibleInsets, int taskId, Consumer<Uri> finisher) {
+ // TODO use taskId and visibleInsets
+ takeScreenshot(screenshot, finisher, screenshotScreenBounds);
+ }
+
/**
* Displays a screenshot selector
*/
+ @SuppressLint("ClickableViewAccessibility")
void takeScreenshotPartial(final Consumer<Uri> finisher) {
mWindowManager.addView(mScreenshotLayout, mWindowLayoutParams);
mScreenshotSelectorView.setOnTouchListener(new View.OnTouchListener() {
@@ -402,7 +415,8 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
/**
* Starts the animation after taking the screenshot
*/
- private void startAnimation(final Consumer<Uri> finisher, int w, int h) {
+ private void startAnimation(final Consumer<Uri> finisher, int w, int h,
+ @Nullable Rect screenRect) {
// If power save is on, show a toast so there is some visual indication that a screenshot
// has been taken.
PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
@@ -422,7 +436,8 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
mScreenshotAnimation.removeAllListeners();
}
- ValueAnimator screenshotDropInAnim = createScreenshotDropInAnimation();
+ ValueAnimator screenshotDropInAnim = screenRect != null ? createRectAnimation(screenRect)
+ : createScreenshotDropInAnimation();
ValueAnimator screenshotFadeOutAnim = createScreenshotToCornerAnimation(w, h);
mScreenshotAnimation = new AnimatorSet();
mScreenshotAnimation.playSequentially(screenshotDropInAnim, screenshotFadeOutAnim);
@@ -460,6 +475,46 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
});
}
+ private ValueAnimator createRectAnimation(Rect rect) {
+ mScreenshotView.setAdjustViewBounds(true);
+ mScreenshotView.setMaxHeight(rect.height());
+ mScreenshotView.setMaxWidth(rect.width());
+
+ final float flashPeakDurationPct = ((float) (SCREENSHOT_FLASH_TO_PEAK_DURATION)
+ / SCREENSHOT_DROP_IN_DURATION);
+ final float flashDurationPct = 2f * flashPeakDurationPct;
+ final Interpolator scaleInterpolator = x -> {
+ // We start scaling when the flash is at it's peak
+ if (x < flashPeakDurationPct) {
+ return 0;
+ }
+ return (x - flashDurationPct) / (1f - flashDurationPct);
+ };
+
+ ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
+ anim.setDuration(SCREENSHOT_DROP_IN_DURATION);
+ anim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ mBackgroundView.setAlpha(0f);
+ mBackgroundView.setVisibility(View.VISIBLE);
+ mScreenshotView.setAlpha(0f);
+ mScreenshotView.setElevation(0f);
+ mScreenshotView.setTranslationX(0f);
+ mScreenshotView.setTranslationY(0f);
+ mScreenshotView.setScaleX(1f);
+ mScreenshotView.setScaleY(1f);
+ mScreenshotView.setVisibility(View.VISIBLE);
+ }
+ });
+ anim.addUpdateListener(animation -> {
+ float t = (Float) animation.getAnimatedValue();
+ mBackgroundView.setAlpha(scaleInterpolator.getInterpolation(t) * BACKGROUND_ALPHA);
+ mScreenshotView.setAlpha(t);
+ });
+ return anim;
+ }
+
private ValueAnimator createScreenshotDropInAnimation() {
final float flashPeakDurationPct = ((float) (SCREENSHOT_FLASH_TO_PEAK_DURATION)
/ SCREENSHOT_DROP_IN_DURATION);
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshotLegacy.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshotLegacy.java
index a5baa7a49bd0..f3614ffbdb1b 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshotLegacy.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshotLegacy.java
@@ -30,6 +30,7 @@ import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Color;
+import android.graphics.Insets;
import android.graphics.PixelFormat;
import android.graphics.PointF;
import android.graphics.Rect;
@@ -205,8 +206,13 @@ public class GlobalScreenshotLegacy {
int width = crop.width();
int height = crop.height();
- // Take the screenshot
- mScreenBitmap = SurfaceControl.screenshot(crop, width, height, rot);
+ takeScreenshot(SurfaceControl.screenshot(crop, width, height, rot), finisher,
+ statusBarVisible, navBarVisible, null);
+ }
+
+ private void takeScreenshot(Bitmap screenshot, Consumer<Uri> finisher, boolean statusBarVisible,
+ boolean navBarVisible, Rect screenboundsOfBitmap) {
+ mScreenBitmap = screenshot;
if (mScreenBitmap == null) {
mNotificationsController.notifyScreenshotError(
R.string.screenshot_failed_to_capture_text);
@@ -220,7 +226,7 @@ public class GlobalScreenshotLegacy {
// Start the post-screenshot animation
startAnimation(finisher, mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels,
- statusBarVisible, navBarVisible);
+ statusBarVisible, navBarVisible, screenboundsOfBitmap);
}
void takeScreenshot(Consumer<Uri> finisher, boolean statusBarVisible, boolean navBarVisible) {
@@ -229,6 +235,12 @@ public class GlobalScreenshotLegacy {
new Rect(0, 0, mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels));
}
+ void handleImageAsScreenshot(Bitmap screenshot, Rect screenshotScreenBounds,
+ Insets visibleInsets, int taskId, Consumer<Uri> finisher) {
+ // TODO use taskId and visibleInsets
+ takeScreenshot(screenshot, finisher, false, false, screenshotScreenBounds);
+ }
+
/**
* Displays a screenshot selector
*/
@@ -302,7 +314,7 @@ public class GlobalScreenshotLegacy {
* Starts the animation after taking the screenshot
*/
private void startAnimation(final Consumer<Uri> finisher, int w, int h,
- boolean statusBarVisible, boolean navBarVisible) {
+ boolean statusBarVisible, boolean navBarVisible, @Nullable Rect screenBoundsOfBitmap) {
// If power save is on, show a toast so there is some visual indication that a screenshot
// has been taken.
PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
@@ -323,7 +335,8 @@ public class GlobalScreenshotLegacy {
}
mWindowManager.addView(mScreenshotLayout, mWindowLayoutParams);
- ValueAnimator screenshotDropInAnim = createScreenshotDropInAnimation();
+ ValueAnimator screenshotDropInAnim = screenBoundsOfBitmap != null
+ ? createRectAnimation(screenBoundsOfBitmap) : createScreenshotDropInAnimation();
ValueAnimator screenshotFadeOutAnim =
createScreenshotDropOutAnimation(w, h, statusBarVisible, navBarVisible);
mScreenshotAnimation = new AnimatorSet();
@@ -430,6 +443,53 @@ public class GlobalScreenshotLegacy {
return anim;
}
+ /**
+ * If a bitmap was supplied to be used as the screenshot, animated from where that bitmap was
+ * on screen, rather than using the whole screen.
+ */
+ private ValueAnimator createRectAnimation(Rect rect) {
+ mScreenshotView.setAdjustViewBounds(true);
+ mScreenshotView.setMaxHeight(rect.height());
+ mScreenshotView.setMaxWidth(rect.width());
+
+ final float flashPeakDurationPct = ((float) (SCREENSHOT_FLASH_TO_PEAK_DURATION)
+ / SCREENSHOT_DROP_IN_DURATION);
+ final float flashDurationPct = 2f * flashPeakDurationPct;
+ final Interpolator scaleInterpolator = x -> {
+ // We start scaling when the flash is at it's peak
+ if (x < flashPeakDurationPct) {
+ return 0;
+ }
+ return (x - flashDurationPct) / (1f - flashDurationPct);
+ };
+
+ ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
+ anim.setDuration(SCREENSHOT_DROP_IN_DURATION);
+ anim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ mBackgroundView.setAlpha(0f);
+ mBackgroundView.setVisibility(View.VISIBLE);
+ mScreenshotView.setAlpha(0f);
+ mScreenshotView.setElevation(0f);
+ mScreenshotView.setTranslationX(0f);
+ mScreenshotView.setTranslationY(0f);
+ mScreenshotView.setScaleX(SCREENSHOT_SCALE + mBgPaddingScale);
+ mScreenshotView.setScaleY(SCREENSHOT_SCALE + mBgPaddingScale);
+ mScreenshotView.setVisibility(View.VISIBLE);
+ }
+ });
+ anim.addUpdateListener(animation -> {
+ float t = (Float) animation.getAnimatedValue();
+ float scaleT = (SCREENSHOT_SCALE + mBgPaddingScale)
+ - scaleInterpolator.getInterpolation(t)
+ * (SCREENSHOT_SCALE - SCREENSHOT_DROP_IN_MIN_SCALE);
+ mBackgroundView.setAlpha(scaleInterpolator.getInterpolation(t) * BACKGROUND_ALPHA);
+ mScreenshotView.setAlpha(t);
+ });
+ return anim;
+ }
+
private ValueAnimator createScreenshotDropOutAnimation(int w, int h, boolean statusBarVisible,
boolean navBarVisible) {
ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
index 9570b5a3b57c..4ac59df07eb9 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
@@ -22,6 +22,9 @@ import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.SCREEN
import android.app.Service;
import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.Insets;
+import android.graphics.Rect;
import android.net.Uri;
import android.os.Handler;
import android.os.IBinder;
@@ -85,6 +88,22 @@ public class TakeScreenshotService extends Service {
finisher, msg.arg1 > 0, msg.arg2 > 0);
}
break;
+ case WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE:
+ Bitmap screenshot = msg.getData().getParcelable(
+ WindowManager.PARCEL_KEY_SCREENSHOT_BITMAP);
+ Rect screenBounds = msg.getData().getParcelable(
+ WindowManager.PARCEL_KEY_SCREENSHOT_BOUNDS);
+ Insets insets = msg.getData().getParcelable(
+ WindowManager.PARCEL_KEY_SCREENSHOT_INSETS);
+ int taskId = msg.getData().getInt(WindowManager.PARCEL_KEY_SCREENSHOT_TASK_ID);
+ if (useCornerFlow) {
+ mScreenshot.handleImageAsScreenshot(
+ screenshot, screenBounds, insets, taskId, finisher);
+ } else {
+ mScreenshotLegacy.handleImageAsScreenshot(
+ screenshot, screenBounds, insets, taskId, finisher);
+ }
+ break;
default:
Log.d(TAG, "Invalid screenshot option: " + msg.what);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java
index ac05c53c38dd..6839921e90a7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java
@@ -56,7 +56,7 @@ public class FeatureFlags {
}
public boolean isNewNotifPipelineEnabled() {
- return getDeviceConfigFlag("notification.newpipeline.enabled", false);
+ return getDeviceConfigFlag("notification.newpipeline.enabled", true);
}
public boolean isNewNotifPipelineRenderingEnabled() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationModeController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationModeController.java
index d6336ed3e18a..826af669cb39 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationModeController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationModeController.java
@@ -387,7 +387,7 @@ public class NavigationModeController implements Dumpable {
Log.d(TAG, "setModeOverlay: overlayPackage=" + overlayPkg
+ " userId=" + userId);
}
- } catch (RemoteException e) {
+ } catch (SecurityException | IllegalStateException | RemoteException e) {
Log.e(TAG, "Failed to enable overlay " + overlayPkg + " for user " + userId);
}
});
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java
index d06164963f7b..5a7c5c9b5ebc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java
@@ -56,6 +56,7 @@ public class StatusIconContainer extends AlphaOptimizedLinearLayout {
private static final int MAX_DOTS = 1;
private int mDotPadding;
+ private int mIconSpacing;
private int mStaticDotDiameter;
private int mUnderflowWidth;
private int mUnderflowStart = 0;
@@ -99,6 +100,7 @@ public class StatusIconContainer extends AlphaOptimizedLinearLayout {
mIconDotFrameWidth = getResources().getDimensionPixelSize(
com.android.internal.R.dimen.status_bar_icon_size);
mDotPadding = getResources().getDimensionPixelSize(R.dimen.overflow_icon_dot_padding);
+ mIconSpacing = getResources().getDimensionPixelSize(R.dimen.status_bar_system_icon_spacing);
int radius = getResources().getDimensionPixelSize(R.dimen.overflow_dot_radius);
mStaticDotDiameter = 2 * radius;
mUnderflowWidth = mIconDotFrameWidth + (MAX_DOTS - 1) * (mStaticDotDiameter + mDotPadding);
@@ -163,20 +165,21 @@ public class StatusIconContainer extends AlphaOptimizedLinearLayout {
// Measure all children so that they report the correct width
int childWidthSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.UNSPECIFIED);
mNeedsUnderflow = mShouldRestrictIcons && visibleCount > MAX_ICONS;
- for (int i = 0; i < mMeasureViews.size(); i++) {
+ for (int i = 0; i < visibleCount; i++) {
// Walking backwards
View child = mMeasureViews.get(visibleCount - i - 1);
measureChild(child, childWidthSpec, heightMeasureSpec);
+ int spacing = i == visibleCount - 1 ? 0 : mIconSpacing;
if (mShouldRestrictIcons) {
if (i < maxVisible && trackWidth) {
- totalWidth += getViewTotalMeasuredWidth(child);
+ totalWidth += getViewTotalMeasuredWidth(child) + spacing;
} else if (trackWidth) {
// We've hit the icon limit; add space for dots
totalWidth += mUnderflowWidth;
trackWidth = false;
}
} else {
- totalWidth += getViewTotalMeasuredWidth(child);
+ totalWidth += getViewTotalMeasuredWidth(child) + spacing;
}
}
@@ -284,11 +287,15 @@ public class StatusIconContainer extends AlphaOptimizedLinearLayout {
continue;
}
+ // Move translationX to the spot within StatusIconContainer's layout to add the view
+ // without cutting off the child view.
+ translationX -= getViewTotalWidth(child);
childState.visibleState = STATE_ICON;
- childState.xTranslation = translationX - getViewTotalWidth(child);
+ childState.xTranslation = translationX;
mLayoutStates.add(0, childState);
- translationX -= getViewTotalWidth(child);
+ // Shift translationX over by mIconSpacing for the next view.
+ translationX -= mIconSpacing;
}
// Show either 1-MAX_ICONS icons, or (MAX_ICONS - 1) icons + overflow
@@ -306,7 +313,8 @@ public class StatusIconContainer extends AlphaOptimizedLinearLayout {
firstUnderflowIndex = i;
break;
}
- mUnderflowStart = (int) Math.max(contentStart, state.xTranslation - mUnderflowWidth);
+ mUnderflowStart = (int) Math.max(
+ contentStart, state.xTranslation - mUnderflowWidth - mIconSpacing);
visible++;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
index 718522c9908a..6baf36c81d30 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
@@ -54,7 +54,8 @@ public class WifiSignalController extends
mWifiTracker.setListening(true);
mHasMobileData = hasMobileData;
if (wifiManager != null) {
- wifiManager.registerTrafficStateCallback(new WifiTrafficStateCallback());
+ wifiManager.registerTrafficStateCallback(context.getMainExecutor(),
+ new WifiTrafficStateCallback());
}
// WiFi only has one state.
mCurrentState.iconGroup = mLastState.iconGroup = new IconGroup(
diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayManager.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayManager.java
index 41e026af7c72..665cb6307b6a 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayManager.java
+++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayManager.java
@@ -178,7 +178,7 @@ class ThemeOverlayManager {
} else {
mOverlayManager.setEnabled(pkg, false, userHandle);
}
- } catch (IllegalStateException e) {
+ } catch (SecurityException | IllegalStateException e) {
Log.e(TAG,
String.format("setEnabled failed: %s %s %b", pkg, userHandle, enabled), e);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java
index b09603d78ecb..1a2e23796c78 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java
@@ -257,7 +257,7 @@ public class BubbleDataTest extends SysuiTestCase {
* enforced by expiring the bubble which was least recently updated (lowest timestamp).
*/
@Test
- public void test_collapsed_addBubble_atMaxBubbles_expiresOldest() {
+ public void test_collapsed_addBubble_atMaxBubbles_overflowsOldest() {
// Setup
sendUpdatedEntryAtTime(mEntryA1, 1000);
sendUpdatedEntryAtTime(mEntryA2, 2000);
@@ -269,7 +269,10 @@ public class BubbleDataTest extends SysuiTestCase {
// Test
sendUpdatedEntryAtTime(mEntryC1, 6000);
verifyUpdateReceived();
+
+ // Verify
assertBubbleRemoved(mBubbleA1, BubbleController.DISMISS_AGED);
+ assertThat(mBubbleData.getOverflowBubbles()).isEqualTo(ImmutableList.of(mBubbleA1));
}
/**
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt
index a19c299940cd..be86a9c15e5f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt
@@ -224,8 +224,9 @@ class ControlsControllerImplTest : SysuiTestCase() {
@Test
fun testRefreshStatus() {
- val list = listOf(Control.StatefulBuilder(TEST_CONTROL_ID, pendingIntent).build())
- controller.refreshStatus(TEST_COMPONENT, list)
+ val control = Control.StatefulBuilder(TEST_CONTROL_ID, pendingIntent).build()
+ val list = listOf(control)
+ controller.refreshStatus(TEST_COMPONENT, control)
verify(uiController).onRefreshState(TEST_COMPONENT, list)
}
@@ -340,7 +341,7 @@ class ControlsControllerImplTest : SysuiTestCase() {
val newControlInfo = TEST_CONTROL_INFO.copy(controlTitle = TEST_CONTROL_TITLE_2)
val control = builderFromInfo(newControlInfo).build()
- controller.refreshStatus(TEST_COMPONENT, listOf(control))
+ controller.refreshStatus(TEST_COMPONENT, control)
delayableExecutor.runAllReady()
@@ -357,4 +358,4 @@ class ControlsControllerImplTest : SysuiTestCase() {
controller.clearFavorites()
assertTrue(controller.getFavoriteControls().isEmpty())
}
-} \ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManagerTest.kt
index 556bb4092d07..4fc1cca76be6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManagerTest.kt
@@ -18,9 +18,12 @@ package com.android.systemui.controls.controller
import android.content.ComponentName
import android.service.controls.Control
+import android.service.controls.IControlsActionCallback
+import android.service.controls.IControlsLoadCallback
import android.service.controls.IControlsProvider
-import android.service.controls.IControlsProviderCallback
+import android.service.controls.IControlsSubscriber
import android.service.controls.actions.ControlAction
+import android.service.controls.actions.ControlActionWrapper
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
@@ -35,7 +38,10 @@ import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
import org.mockito.ArgumentMatchers
+import org.mockito.ArgumentMatchers.eq
+import org.mockito.Captor
import org.mockito.Mock
import org.mockito.Mockito.`when`
import org.mockito.Mockito.verify
@@ -46,14 +52,25 @@ import org.mockito.MockitoAnnotations
class ControlsProviderLifecycleManagerTest : SysuiTestCase() {
@Mock
- private lateinit var serviceCallback: IControlsProviderCallback.Stub
+ private lateinit var actionCallback: IControlsActionCallback.Stub
+ @Mock
+ private lateinit var loadCallback: IControlsLoadCallback.Stub
+ @Mock
+ private lateinit var subscriber: IControlsSubscriber.Stub
@Mock
private lateinit var service: IControlsProvider.Stub
+ @Captor
+ private lateinit var wrapperCaptor: ArgumentCaptor<ControlActionWrapper>
+
private val componentName = ComponentName("test.pkg", "test.cls")
private lateinit var manager: ControlsProviderLifecycleManager
private lateinit var executor: DelayableExecutor
+ companion object {
+ fun <T> capture(argumentCaptor: ArgumentCaptor<T>): T = argumentCaptor.capture()
+ }
+
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
@@ -66,7 +83,9 @@ class ControlsProviderLifecycleManagerTest : SysuiTestCase() {
manager = ControlsProviderLifecycleManager(
context,
executor,
- serviceCallback,
+ loadCallback,
+ actionCallback,
+ subscriber,
componentName
)
}
@@ -94,7 +113,7 @@ class ControlsProviderLifecycleManagerTest : SysuiTestCase() {
val callback: (List<Control>) -> Unit = {}
manager.maybeBindAndLoad(callback)
- verify(service).load()
+ verify(service).load(loadCallback)
assertTrue(mContext.isBound(componentName))
assertEquals(callback, manager.lastLoadCallback)
@@ -110,29 +129,23 @@ class ControlsProviderLifecycleManagerTest : SysuiTestCase() {
}
@Test
- fun testUnsubscribe() {
- manager.bindPermanently()
- manager.unsubscribe()
-
- verify(service).unsubscribe()
- }
-
- @Test
fun testMaybeBindAndSubscribe() {
val list = listOf("TEST_ID")
manager.maybeBindAndSubscribe(list)
assertTrue(mContext.isBound(componentName))
- verify(service).subscribe(list)
+ verify(service).subscribe(list, subscriber)
}
@Test
fun testMaybeBindAndAction() {
val controlId = "TEST_ID"
- val action = ControlAction.UNKNOWN_ACTION
+ val action = ControlAction.ERROR_ACTION
manager.maybeBindAndSendAction(controlId, action)
assertTrue(mContext.isBound(componentName))
- verify(service).onAction(controlId, action)
+ verify(service).action(eq(controlId), capture(wrapperCaptor),
+ eq(actionCallback))
+ assertEquals(action, wrapperCaptor.getValue().getWrappedAction())
}
-} \ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsProviderServiceWrapperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsProviderServiceWrapperTest.kt
deleted file mode 100644
index d6993c06a3c3..000000000000
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsProviderServiceWrapperTest.kt
+++ /dev/null
@@ -1,127 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.controls.controller
-
-import android.os.RemoteException
-import android.service.controls.IControlsProvider
-import android.service.controls.actions.ControlAction
-import android.testing.AndroidTestingRunner
-import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-import org.junit.Assert.assertFalse
-import org.junit.Assert.assertTrue
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.Mock
-import org.mockito.Mockito.`when`
-import org.mockito.Mockito.any
-import org.mockito.Mockito.verify
-import org.mockito.MockitoAnnotations
-
-@SmallTest
-@RunWith(AndroidTestingRunner::class)
-class ControlsProviderServiceWrapperTest : SysuiTestCase() {
-
- @Mock
- private lateinit var service: IControlsProvider
-
- private val exception = RemoteException()
-
- private lateinit var wrapper: ControlsProviderServiceWrapper
-
- @Before
- fun setUp() {
- MockitoAnnotations.initMocks(this)
-
- wrapper = ControlsProviderServiceWrapper(service)
- }
-
- @Test
- fun testLoad_happyPath() {
- val result = wrapper.load()
-
- assertTrue(result)
- verify(service).load()
- }
-
- @Test
- fun testLoad_error() {
- `when`(service.load()).thenThrow(exception)
- val result = wrapper.load()
-
- assertFalse(result)
- }
-
- @Test
- fun testSubscribe_happyPath() {
- val list = listOf("TEST_ID")
- val result = wrapper.subscribe(list)
-
- assertTrue(result)
- verify(service).subscribe(list)
- }
-
- @Test
- fun testSubscribe_error() {
- `when`(service.subscribe(any())).thenThrow(exception)
-
- val list = listOf("TEST_ID")
- val result = wrapper.subscribe(list)
-
- assertFalse(result)
- }
-
- @Test
- fun testUnsubscribe_happyPath() {
- val result = wrapper.unsubscribe()
-
- assertTrue(result)
- verify(service).unsubscribe()
- }
-
- @Test
- fun testUnsubscribe_error() {
- `when`(service.unsubscribe()).thenThrow(exception)
- val result = wrapper.unsubscribe()
-
- assertFalse(result)
- }
-
- @Test
- fun testOnAction_happyPath() {
- val id = "TEST_ID"
- val action = ControlAction.UNKNOWN_ACTION
-
- val result = wrapper.onAction(id, action)
-
- assertTrue(result)
- verify(service).onAction(id, action)
- }
-
- @Test
- fun testOnAction_error() {
- `when`(service.onAction(any(), any())).thenThrow(exception)
-
- val id = "TEST_ID"
- val action = ControlAction.UNKNOWN_ACTION
-
- val result = wrapper.onAction(id, action)
-
- assertFalse(result)
- }
-} \ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ServiceWrapperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ServiceWrapperTest.kt
new file mode 100644
index 000000000000..9e7ce06bb74f
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ServiceWrapperTest.kt
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.controls.controller
+
+import android.os.RemoteException
+import android.service.controls.IControlsActionCallback
+import android.service.controls.IControlsLoadCallback
+import android.service.controls.IControlsProvider
+import android.service.controls.IControlsSubscriber
+import android.service.controls.IControlsSubscription
+import android.service.controls.actions.ControlAction
+import android.service.controls.actions.ControlActionWrapper
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertTrue
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.ArgumentMatchers.eq
+import org.mockito.Captor
+import org.mockito.Mock
+import org.mockito.Mockito.any
+import org.mockito.Mockito.`when`
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class ServiceWrapperTest : SysuiTestCase() {
+
+ @Mock
+ private lateinit var service: IControlsProvider
+
+ @Mock
+ private lateinit var subscription: IControlsSubscription
+
+ @Mock
+ private lateinit var subscriber: IControlsSubscriber
+
+ @Mock
+ private lateinit var loadCallback: IControlsLoadCallback
+
+ @Mock
+ private lateinit var actionCallback: IControlsActionCallback
+
+ @Captor
+ private lateinit var wrapperCaptor: ArgumentCaptor<ControlActionWrapper>
+
+ private val exception = RemoteException()
+
+ private lateinit var wrapper: ServiceWrapper
+
+ companion object {
+ fun <T> capture(argumentCaptor: ArgumentCaptor<T>): T = argumentCaptor.capture()
+ }
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ wrapper = ServiceWrapper(service)
+ }
+
+ @Test
+ fun testLoad_happyPath() {
+ val result = wrapper.load(loadCallback)
+
+ assertTrue(result)
+ verify(service).load(loadCallback)
+ }
+
+ @Test
+ fun testLoad_error() {
+ `when`(service.load(any())).thenThrow(exception)
+ val result = wrapper.load(loadCallback)
+
+ assertFalse(result)
+ }
+
+ @Test
+ fun testSubscribe_happyPath() {
+ val list = listOf("TEST_ID")
+ val result = wrapper.subscribe(list, subscriber)
+
+ assertTrue(result)
+ verify(service).subscribe(list, subscriber)
+ }
+
+ @Test
+ fun testSubscribe_error() {
+ `when`(service.subscribe(any(), any())).thenThrow(exception)
+
+ val list = listOf("TEST_ID")
+ val result = wrapper.subscribe(list, subscriber)
+
+ assertFalse(result)
+ }
+
+ @Test
+ fun testCancel_happyPath() {
+ val result = wrapper.cancel(subscription)
+
+ assertTrue(result)
+ verify(subscription).cancel()
+ }
+
+ @Test
+ fun testCancel_error() {
+ `when`(subscription.cancel()).thenThrow(exception)
+ val result = wrapper.cancel(subscription)
+
+ assertFalse(result)
+ }
+
+ @Test
+ fun testOnAction_happyPath() {
+ val id = "TEST_ID"
+ val action = ControlAction.ERROR_ACTION
+
+ val result = wrapper.action(id, action, actionCallback)
+
+ assertTrue(result)
+ verify(service).action(eq(id), capture(wrapperCaptor),
+ eq(actionCallback))
+ assertEquals(action, wrapperCaptor.getValue().getWrappedAction())
+ }
+
+ @Test
+ fun testOnAction_error() {
+ `when`(service.action(any(), any(), any())).thenThrow(exception)
+
+ val id = "TEST_ID"
+ val action = ControlAction.ERROR_ACTION
+
+ val result = wrapper.action(id, action, actionCallback)
+
+ assertFalse(result)
+ }
+}
diff --git a/packages/Tethering/apex/AndroidManifest.xml b/packages/Tethering/apex/AndroidManifest.xml
index 5c35c51dc77f..4aae3cc3000d 100644
--- a/packages/Tethering/apex/AndroidManifest.xml
+++ b/packages/Tethering/apex/AndroidManifest.xml
@@ -20,8 +20,10 @@
<application android:hasCode="false" />
<!-- b/145383354: Current minSdk is locked to Q for development cycle, lock it to next version
before ship. -->
- <uses-sdk
+ <!-- TODO: Uncomment this when the R API level is fixed. b/148281152 -->
+ <!--uses-sdk
android:minSdkVersion="29"
android:targetSdkVersion="29"
/>
+ -->
</manifest>
diff --git a/packages/Tethering/common/TetheringLib/Android.bp b/packages/Tethering/common/TetheringLib/Android.bp
index a8d1239a3c30..e0adb34dad6c 100644
--- a/packages/Tethering/common/TetheringLib/Android.bp
+++ b/packages/Tethering/common/TetheringLib/Android.bp
@@ -19,7 +19,15 @@ aidl_interface {
local_include_dir: "src",
include_dirs: ["frameworks/base/core/java"], // For framework parcelables.
srcs: [
- "src/android/net/*.aidl",
+ // @JavaOnlyStableParcelable aidl declarations must not be listed here, as this would cause
+ // compilation to fail (b/148001843).
+ "src/android/net/IIntResultListener.aidl",
+ "src/android/net/ITetheringConnector.aidl",
+ "src/android/net/ITetheringEventCallback.aidl",
+ "src/android/net/TetheringCallbackStartedParcel.aidl",
+ "src/android/net/TetheringConfigurationParcel.aidl",
+ "src/android/net/TetheringRequestParcel.aidl",
+ "src/android/net/TetherStatesParcel.aidl",
],
backend: {
ndk: {
@@ -35,6 +43,7 @@ java_library {
name: "framework-tethering",
sdk_version: "system_current",
srcs: [
+ "src/android/net/TetheredClient.java",
"src/android/net/TetheringManager.java",
"src/android/net/TetheringConstants.java",
":framework-tethering-annotations",
@@ -63,6 +72,8 @@ java_library {
filegroup {
name: "framework-tethering-srcs",
srcs: [
+ "src/android/net/TetheredClient.aidl",
+ "src/android/net/TetheredClient.java",
"src/android/net/TetheringManager.java",
"src/android/net/TetheringConstants.java",
"src/android/net/IIntResultListener.aidl",
@@ -70,6 +81,7 @@ filegroup {
"src/android/net/ITetheringConnector.aidl",
"src/android/net/TetheringCallbackStartedParcel.aidl",
"src/android/net/TetheringConfigurationParcel.aidl",
+ "src/android/net/TetheringRequestParcel.aidl",
"src/android/net/TetherStatesParcel.aidl",
],
path: "src"
diff --git a/packages/Tethering/common/TetheringLib/src/android/net/ITetheringConnector.aidl b/packages/Tethering/common/TetheringLib/src/android/net/ITetheringConnector.aidl
index d30c39986984..5febe73288bf 100644
--- a/packages/Tethering/common/TetheringLib/src/android/net/ITetheringConnector.aidl
+++ b/packages/Tethering/common/TetheringLib/src/android/net/ITetheringConnector.aidl
@@ -17,6 +17,7 @@ package android.net;
import android.net.IIntResultListener;
import android.net.ITetheringEventCallback;
+import android.net.TetheringRequestParcel;
import android.os.ResultReceiver;
/** @hide */
@@ -27,8 +28,8 @@ oneway interface ITetheringConnector {
void setUsbTethering(boolean enable, String callerPkg, IIntResultListener receiver);
- void startTethering(int type, in ResultReceiver receiver, boolean showProvisioningUi,
- String callerPkg);
+ void startTethering(in TetheringRequestParcel request, String callerPkg,
+ IIntResultListener receiver);
void stopTethering(int type, String callerPkg, IIntResultListener receiver);
diff --git a/core/java/android/service/controls/templates/ControlTemplate.aidl b/packages/Tethering/common/TetheringLib/src/android/net/TetheredClient.aidl
index b6ab28026a8d..0b279b882367 100644
--- a/core/java/android/service/controls/templates/ControlTemplate.aidl
+++ b/packages/Tethering/common/TetheringLib/src/android/net/TetheredClient.aidl
@@ -1,5 +1,5 @@
-/*
- * Copyright (c) 2019, The Android Open Source Project
+/**
+ * Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,7 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package android.net;
-package android.service.controls.templates;
-
-parcelable ControlTemplate; \ No newline at end of file
+@JavaOnlyStableParcelable parcelable TetheredClient; \ No newline at end of file
diff --git a/packages/Tethering/common/TetheringLib/src/android/net/TetheredClient.java b/packages/Tethering/common/TetheringLib/src/android/net/TetheredClient.java
new file mode 100644
index 000000000000..651468846ca8
--- /dev/null
+++ b/packages/Tethering/common/TetheringLib/src/android/net/TetheredClient.java
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+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 java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Information on a tethered downstream client.
+ * @hide
+ */
+@SystemApi
+@TestApi
+public final class TetheredClient implements Parcelable {
+ @NonNull
+ private final MacAddress mMacAddress;
+ @NonNull
+ private final List<AddressInfo> mAddresses;
+ // TODO: use an @IntDef here
+ private final int mTetheringType;
+
+ public TetheredClient(@NonNull MacAddress macAddress,
+ @NonNull Collection<AddressInfo> addresses, int tetheringType) {
+ mMacAddress = macAddress;
+ mAddresses = new ArrayList<>(addresses);
+ mTetheringType = tetheringType;
+ }
+
+ private TetheredClient(@NonNull Parcel in) {
+ this(in.readParcelable(null), in.createTypedArrayList(AddressInfo.CREATOR), in.readInt());
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeParcelable(mMacAddress, flags);
+ dest.writeTypedList(mAddresses);
+ dest.writeInt(mTetheringType);
+ }
+
+ @NonNull
+ public MacAddress getMacAddress() {
+ return mMacAddress;
+ }
+
+ @NonNull
+ public List<AddressInfo> getAddresses() {
+ return new ArrayList<>(mAddresses);
+ }
+
+ public int getTetheringType() {
+ return mTetheringType;
+ }
+
+ /**
+ * Return a new {@link TetheredClient} that has all the attributes of this instance, plus the
+ * {@link AddressInfo} of the provided {@link TetheredClient}.
+ *
+ * <p>Duplicate addresses are removed.
+ * @hide
+ */
+ public TetheredClient addAddresses(@NonNull TetheredClient other) {
+ final HashSet<AddressInfo> newAddresses = new HashSet<>(
+ mAddresses.size() + other.mAddresses.size());
+ newAddresses.addAll(mAddresses);
+ newAddresses.addAll(other.mAddresses);
+ return new TetheredClient(mMacAddress, newAddresses, mTetheringType);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mMacAddress, mAddresses, mTetheringType);
+ }
+
+ @Override
+ public boolean equals(@Nullable Object obj) {
+ if (!(obj instanceof TetheredClient)) return false;
+ final TetheredClient other = (TetheredClient) obj;
+ return mMacAddress.equals(other.mMacAddress)
+ && mAddresses.equals(other.mAddresses)
+ && mTetheringType == other.mTetheringType;
+ }
+
+ /**
+ * Information on an lease assigned to a tethered client.
+ */
+ public static final class AddressInfo implements Parcelable {
+ @NonNull
+ private final LinkAddress mAddress;
+ @Nullable
+ private final String mHostname;
+ // TODO: use LinkAddress expiration time once it is supported
+ private final long mExpirationTime;
+
+ /** @hide */
+ public AddressInfo(@NonNull LinkAddress address, @Nullable String hostname) {
+ this(address, hostname, 0);
+ }
+
+ /** @hide */
+ public AddressInfo(@NonNull LinkAddress address, String hostname, long expirationTime) {
+ this.mAddress = address;
+ this.mHostname = hostname;
+ this.mExpirationTime = expirationTime;
+ }
+
+ private AddressInfo(Parcel in) {
+ this(in.readParcelable(null), in.readString(), in.readLong());
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeParcelable(mAddress, flags);
+ dest.writeString(mHostname);
+ dest.writeLong(mExpirationTime);
+ }
+
+ @NonNull
+ public LinkAddress getAddress() {
+ return mAddress;
+ }
+
+ @Nullable
+ public String getHostname() {
+ return mHostname;
+ }
+
+ /** @hide TODO: use expiration time in LinkAddress */
+ public long getExpirationTime() {
+ return mExpirationTime;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mAddress, mHostname, mExpirationTime);
+ }
+
+ @Override
+ public boolean equals(@Nullable Object obj) {
+ if (!(obj instanceof AddressInfo)) return false;
+ final AddressInfo other = (AddressInfo) obj;
+ // Use .equals() for addresses as all changes, including address expiry changes,
+ // should be included.
+ return other.mAddress.equals(mAddress)
+ && Objects.equals(mHostname, other.mHostname)
+ && mExpirationTime == other.mExpirationTime;
+ }
+
+ @NonNull
+ public static final Creator<AddressInfo> CREATOR = new Creator<AddressInfo>() {
+ @NonNull
+ @Override
+ public AddressInfo createFromParcel(@NonNull Parcel in) {
+ return new AddressInfo(in);
+ }
+
+ @NonNull
+ @Override
+ public AddressInfo[] newArray(int size) {
+ return new AddressInfo[size];
+ }
+ };
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @NonNull
+ public static final Creator<TetheredClient> CREATOR = new Creator<TetheredClient>() {
+ @NonNull
+ @Override
+ public TetheredClient createFromParcel(@NonNull Parcel in) {
+ return new TetheredClient(in);
+ }
+
+ @NonNull
+ @Override
+ public TetheredClient[] newArray(int size) {
+ return new TetheredClient[size];
+ }
+ };
+}
diff --git a/packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java b/packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java
index e1b9c16b8185..79c693040416 100644
--- a/packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java
+++ b/packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java
@@ -15,6 +15,7 @@
*/
package android.net;
+import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
@@ -30,6 +31,7 @@ import android.util.ArrayMap;
import android.util.Log;
import java.util.Arrays;
+import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
@@ -128,6 +130,12 @@ public class TetheringManager {
*/
public static final int TETHERING_WIFI_P2P = 3;
+ /**
+ * Ncm local tethering type.
+ * @see #startTethering(TetheringRequest, Executor, StartTetheringCallback)
+ */
+ public static final int TETHERING_NCM = 4;
+
public static final int TETHER_ERROR_NO_ERROR = 0;
public static final int TETHER_ERROR_UNKNOWN_IFACE = 1;
public static final int TETHER_ERROR_SERVICE_UNAVAIL = 2;
@@ -326,27 +334,171 @@ public class TetheringManager {
}
/**
+ * Use with {@link #startTethering} to specify additional parameters when starting tethering.
+ */
+ public static class TetheringRequest {
+ /** A configuration set for TetheringRequest. */
+ private final TetheringRequestParcel mRequestParcel;
+
+ private TetheringRequest(final TetheringRequestParcel request) {
+ mRequestParcel = request;
+ }
+
+ /** Builder used to create TetheringRequest. */
+ public static class Builder {
+ private final TetheringRequestParcel mBuilderParcel;
+
+ /** Default constructor of Builder. */
+ public Builder(final int type) {
+ mBuilderParcel = new TetheringRequestParcel();
+ mBuilderParcel.tetheringType = type;
+ mBuilderParcel.localIPv4Address = null;
+ mBuilderParcel.exemptFromEntitlementCheck = false;
+ mBuilderParcel.showProvisioningUi = true;
+ }
+
+ /**
+ * Configure tethering with static IPv4 assignment (with DHCP disabled).
+ *
+ * @param localIPv4Address The preferred local IPv4 address to use.
+ */
+ @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED)
+ @NonNull
+ public Builder useStaticIpv4Addresses(@NonNull final LinkAddress localIPv4Address) {
+ mBuilderParcel.localIPv4Address = localIPv4Address;
+ return this;
+ }
+
+ /** Start tethering without entitlement checks. */
+ @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED)
+ @NonNull
+ public Builder setExemptFromEntitlementCheck(boolean exempt) {
+ mBuilderParcel.exemptFromEntitlementCheck = exempt;
+ return this;
+ }
+
+ /** Start tethering without showing the provisioning UI. */
+ @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED)
+ @NonNull
+ public Builder setSilentProvisioning(boolean silent) {
+ mBuilderParcel.showProvisioningUi = silent;
+ return this;
+ }
+
+ /** Build {@link TetheringRequest] with the currently set configuration. */
+ @NonNull
+ public TetheringRequest build() {
+ return new TetheringRequest(mBuilderParcel);
+ }
+ }
+
+ /**
+ * Get a TetheringRequestParcel from the configuration
+ * @hide
+ */
+ public TetheringRequestParcel getParcel() {
+ return mRequestParcel;
+ }
+
+ /** String of TetheringRequest detail. */
+ public String toString() {
+ return "TetheringRequest [ type= " + mRequestParcel.tetheringType
+ + ", localIPv4Address= " + mRequestParcel.localIPv4Address
+ + ", exemptFromEntitlementCheck= "
+ + mRequestParcel.exemptFromEntitlementCheck + ", showProvisioningUi= "
+ + mRequestParcel.showProvisioningUi + " ]";
+ }
+ }
+
+ /**
+ * Callback for use with {@link #startTethering} to find out whether tethering succeeded.
+ */
+ public abstract static class StartTetheringCallback {
+ /**
+ * Called when tethering has been successfully started.
+ */
+ public void onTetheringStarted() {}
+
+ /**
+ * Called when starting tethering failed.
+ *
+ * @param resultCode One of the {@code TETHER_ERROR_*} constants.
+ */
+ public void onTetheringFailed(final int resultCode) {}
+ }
+
+ /**
* Starts tethering and runs tether provisioning for the given type if needed. If provisioning
* fails, stopTethering will be called automatically.
- * @hide
+ *
+ * <p>Without {@link android.Manifest.permission.TETHER_PRIVILEGED} permission, the call will
+ * fail if a tethering entitlement check is required.
+ *
+ * @param request a {@link TetheringRequest} which can specify the preferred configuration.
+ * @param executor {@link Executor} to specify the thread upon which the callback of
+ * TetheringRequest will be invoked.
+ * @param callback A callback that will be called to indicate the success status of the
+ * tethering start request.
*/
- // TODO: improve the usage of ResultReceiver, b/145096122
- public void startTethering(final int type, @NonNull final ResultReceiver receiver,
- final boolean showProvisioningUi) {
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.TETHER_PRIVILEGED,
+ android.Manifest.permission.WRITE_SETTINGS
+ })
+ public void startTethering(@NonNull final TetheringRequest request,
+ @NonNull final Executor executor, @NonNull final StartTetheringCallback callback) {
final String callerPkg = mContext.getOpPackageName();
Log.i(TAG, "startTethering caller:" + callerPkg);
+ final IIntResultListener listener = new IIntResultListener.Stub() {
+ @Override
+ public void onResult(final int resultCode) {
+ executor.execute(() -> {
+ if (resultCode == TETHER_ERROR_NO_ERROR) {
+ callback.onTetheringStarted();
+ } else {
+ callback.onTetheringFailed(resultCode);
+ }
+ });
+ }
+ };
try {
- mConnector.startTethering(type, receiver, showProvisioningUi, callerPkg);
+ mConnector.startTethering(request.getParcel(), callerPkg, listener);
} catch (RemoteException e) {
throw new IllegalStateException(e);
}
}
/**
+ * Starts tethering and runs tether provisioning for the given type if needed. If provisioning
+ * fails, stopTethering will be called automatically.
+ *
+ * <p>Without {@link android.Manifest.permission.TETHER_PRIVILEGED} permission, the call will
+ * fail if a tethering entitlement check is required.
+ *
+ * @param type The tethering type, on of the {@code TetheringManager#TETHERING_*} constants.
+ * @param executor {@link Executor} to specify the thread upon which the callback of
+ * TetheringRequest will be invoked.
+ */
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.TETHER_PRIVILEGED,
+ android.Manifest.permission.WRITE_SETTINGS
+ })
+ public void startTethering(int type, @NonNull final Executor executor,
+ @NonNull final StartTetheringCallback callback) {
+ startTethering(new TetheringRequest.Builder(type).build(), executor, callback);
+ }
+
+ /**
* Stops tethering for the given type. Also cancels any provisioning rechecks for that type if
* applicable.
+ *
+ * <p>Without {@link android.Manifest.permission.TETHER_PRIVILEGED} permission, the call will
+ * fail if a tethering entitlement check is required.
*/
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.TETHER_PRIVILEGED,
+ android.Manifest.permission.WRITE_SETTINGS
+ })
public void stopTethering(final int type) {
final String callerPkg = mContext.getOpPackageName();
Log.i(TAG, "stopTethering caller:" + callerPkg);
@@ -386,6 +538,9 @@ public class TetheringManager {
* {@link #TETHER_ERROR_ENTITLEMENT_UNKNOWN} will be returned. If {@code showEntitlementUi} is
* true, entitlement will be run.
*
+ * <p>Without {@link android.Manifest.permission.TETHER_PRIVILEGED} permission, the call will
+ * fail if a tethering entitlement check is required.
+ *
* @param type the downstream type of tethering. Must be one of {@code #TETHERING_*} constants.
* @param showEntitlementUi a boolean indicating whether to run UI-based entitlement check.
* @param executor the executor on which callback will be invoked.
@@ -393,7 +548,10 @@ public class TetheringManager {
* notify the caller of the result of entitlement check. The listener may be called zero
* or one time.
*/
- @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED)
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.TETHER_PRIVILEGED,
+ android.Manifest.permission.WRITE_SETTINGS
+ })
public void requestLatestTetheringEntitlementResult(int type, boolean showEntitlementUi,
@NonNull Executor executor,
@NonNull final OnTetheringEntitlementResultListener listener) {
@@ -502,6 +660,19 @@ public class TetheringManager {
* @param error One of {@code TetheringManager#TETHER_ERROR_*}.
*/
public void onError(@NonNull String ifName, int error) {}
+
+ /**
+ * Called when the list of tethered clients changes.
+ *
+ * <p>This callback provides best-effort information on connected clients based on state
+ * known to the system, however the list cannot be completely accurate (and should not be
+ * used for security purposes). For example, clients behind a bridge and using static IP
+ * assignments are not visible to the tethering device; or even when using DHCP, such
+ * clients may still be reported by this callback after disconnection as the system cannot
+ * determine if they are still connected.
+ * @param clients The new set of tethered clients; the collection is not ordered.
+ */
+ public void onClientsChanged(@NonNull Collection<TetheredClient> clients) {}
}
/**
@@ -562,6 +733,7 @@ public class TetheringManager {
* @param executor the executor on which callback will be invoked.
* @param callback the callback to be called when tethering has change events.
*/
+ @RequiresPermission(Manifest.permission.ACCESS_NETWORK_STATE)
public void registerTetheringEventCallback(@NonNull Executor executor,
@NonNull TetheringEventCallback callback) {
final String callerPkg = mContext.getOpPackageName();
@@ -669,6 +841,10 @@ public class TetheringManager {
*
* @param callback previously registered callback.
*/
+ @RequiresPermission(anyOf = {
+ Manifest.permission.TETHER_PRIVILEGED,
+ Manifest.permission.ACCESS_NETWORK_STATE
+ })
public void unregisterTetheringEventCallback(@NonNull final TetheringEventCallback callback) {
final String callerPkg = mContext.getOpPackageName();
Log.i(TAG, "unregisterTetheringEventCallback caller:" + callerPkg);
@@ -833,7 +1009,14 @@ public class TetheringManager {
/**
* Stop all active tethering.
+ *
+ * <p>Without {@link android.Manifest.permission.TETHER_PRIVILEGED} permission, the call will
+ * fail if a tethering entitlement check is required.
*/
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.TETHER_PRIVILEGED,
+ android.Manifest.permission.WRITE_SETTINGS
+ })
public void stopAllTethering() {
final String callerPkg = mContext.getOpPackageName();
Log.i(TAG, "stopAllTethering caller:" + callerPkg);
diff --git a/packages/Tethering/common/TetheringLib/src/android/net/TetheringRequestParcel.aidl b/packages/Tethering/common/TetheringLib/src/android/net/TetheringRequestParcel.aidl
new file mode 100644
index 000000000000..bf19d85f6a83
--- /dev/null
+++ b/packages/Tethering/common/TetheringLib/src/android/net/TetheringRequestParcel.aidl
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import android.net.LinkAddress;
+
+/**
+ * Configuration details for requesting tethering.
+ * @hide
+ */
+parcelable TetheringRequestParcel {
+ int tetheringType;
+ LinkAddress localIPv4Address;
+ boolean exemptFromEntitlementCheck;
+ boolean showProvisioningUi;
+}
diff --git a/packages/Tethering/res/values/config.xml b/packages/Tethering/res/values/config.xml
index 19e8d696c39a..c489cbcd5a1c 100644
--- a/packages/Tethering/res/values/config.xml
+++ b/packages/Tethering/res/values/config.xml
@@ -29,6 +29,12 @@
</string-array>
<!-- List of regexpressions describing the interface (if any) that represent tetherable
+ NCM interfaces. If the device doesn't want to support tethering over NCM this should
+ be empty. -->
+ <string-array translatable="false" name="config_tether_ncm_regexs">
+ </string-array>
+
+ <!-- List of regexpressions describing the interface (if any) that represent tetherable
Wifi interfaces. If the device doesn't want to support tethering over Wifi this
should be empty. An example would be "softap.*" -->
<string-array translatable="false" name="config_tether_wifi_regexs">
diff --git a/packages/Tethering/res/values/overlayable.xml b/packages/Tethering/res/values/overlayable.xml
index e089d9d19950..fe025c7ac993 100644
--- a/packages/Tethering/res/values/overlayable.xml
+++ b/packages/Tethering/res/values/overlayable.xml
@@ -17,6 +17,7 @@
<overlayable name="TetheringConfig">
<policy type="product|system|vendor">
<item type="array" name="config_tether_usb_regexs"/>
+ <item type="array" name="config_tether_ncm_regexs" />
<item type="array" name="config_tether_wifi_regexs"/>
<item type="array" name="config_tether_wifi_p2p_regexs"/>
<item type="array" name="config_tether_bluetooth_regexs"/>
diff --git a/packages/Tethering/src/android/net/ip/IpServer.java b/packages/Tethering/src/android/net/ip/IpServer.java
index 0491ad7c3413..57cc4dd554f1 100644
--- a/packages/Tethering/src/android/net/ip/IpServer.java
+++ b/packages/Tethering/src/android/net/ip/IpServer.java
@@ -416,7 +416,8 @@ public class IpServer extends StateMachine {
final Inet4Address srvAddr;
int prefixLen = 0;
try {
- if (mInterfaceType == TetheringManager.TETHERING_USB) {
+ if (mInterfaceType == TetheringManager.TETHERING_USB
+ || mInterfaceType == TetheringManager.TETHERING_NCM) {
srvAddr = (Inet4Address) parseNumericAddress(USB_NEAR_IFACE_ADDR);
prefixLen = USB_PREFIX_LENGTH;
} else if (mInterfaceType == TetheringManager.TETHERING_WIFI) {
diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java b/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java
index 5370145f1992..02ba17e4b48b 100644
--- a/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java
+++ b/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java
@@ -19,6 +19,7 @@ package com.android.server.connectivity.tethering;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.hardware.usb.UsbManager.USB_CONFIGURED;
import static android.hardware.usb.UsbManager.USB_CONNECTED;
+import static android.hardware.usb.UsbManager.USB_FUNCTION_NCM;
import static android.hardware.usb.UsbManager.USB_FUNCTION_RNDIS;
import static android.net.ConnectivityManager.ACTION_RESTRICT_BACKGROUND_CHANGED;
import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
@@ -30,6 +31,7 @@ import static android.net.TetheringManager.EXTRA_AVAILABLE_TETHER;
import static android.net.TetheringManager.EXTRA_ERRORED_TETHER;
import static android.net.TetheringManager.TETHERING_BLUETOOTH;
import static android.net.TetheringManager.TETHERING_INVALID;
+import static android.net.TetheringManager.TETHERING_NCM;
import static android.net.TetheringManager.TETHERING_USB;
import static android.net.TetheringManager.TETHERING_WIFI;
import static android.net.TetheringManager.TETHERING_WIFI_P2P;
@@ -66,6 +68,7 @@ import android.content.IntentFilter;
import android.content.res.Resources;
import android.hardware.usb.UsbManager;
import android.net.ConnectivityManager;
+import android.net.IIntResultListener;
import android.net.INetd;
import android.net.ITetheringEventCallback;
import android.net.IpPrefix;
@@ -76,6 +79,7 @@ import android.net.NetworkInfo;
import android.net.TetherStatesParcel;
import android.net.TetheringCallbackStartedParcel;
import android.net.TetheringConfigurationParcel;
+import android.net.TetheringRequestParcel;
import android.net.ip.IpServer;
import android.net.shared.NetdUtils;
import android.net.util.BaseNetdUnsolicitedEventListener;
@@ -406,6 +410,8 @@ public class Tethering {
return TETHERING_USB;
} else if (cfg.isBluetooth(iface)) {
return TETHERING_BLUETOOTH;
+ } else if (cfg.isNcm(iface)) {
+ return TETHERING_NCM;
}
return TETHERING_INVALID;
}
@@ -424,9 +430,10 @@ public class Tethering {
}
}
- void startTethering(int type, ResultReceiver receiver, boolean showProvisioningUi) {
- mEntitlementMgr.startProvisioningIfNeeded(type, showProvisioningUi);
- enableTetheringInternal(type, true /* enabled */, receiver);
+ void startTethering(final TetheringRequestParcel request, final IIntResultListener listener) {
+ mEntitlementMgr.startProvisioningIfNeeded(request.tetheringType,
+ request.showProvisioningUi);
+ enableTetheringInternal(request.tetheringType, true /* enabled */, listener);
}
void stopTethering(int type) {
@@ -438,29 +445,36 @@ public class Tethering {
* Enables or disables tethering for the given type. If provisioning is required, it will
* schedule provisioning rechecks for the specified interface.
*/
- private void enableTetheringInternal(int type, boolean enable, ResultReceiver receiver) {
+ private void enableTetheringInternal(int type, boolean enable,
+ final IIntResultListener listener) {
int result;
switch (type) {
case TETHERING_WIFI:
result = setWifiTethering(enable);
- sendTetherResult(receiver, result);
+ sendTetherResult(listener, result);
break;
case TETHERING_USB:
result = setUsbTethering(enable);
- sendTetherResult(receiver, result);
+ sendTetherResult(listener, result);
break;
case TETHERING_BLUETOOTH:
- setBluetoothTethering(enable, receiver);
+ setBluetoothTethering(enable, listener);
+ break;
+ case TETHERING_NCM:
+ result = setNcmTethering(enable);
+ sendTetherResult(listener, result);
break;
default:
Log.w(TAG, "Invalid tether type.");
- sendTetherResult(receiver, TETHER_ERROR_UNKNOWN_IFACE);
+ sendTetherResult(listener, TETHER_ERROR_UNKNOWN_IFACE);
}
}
- private void sendTetherResult(ResultReceiver receiver, int result) {
- if (receiver != null) {
- receiver.send(result, null);
+ private void sendTetherResult(final IIntResultListener listener, int result) {
+ if (listener != null) {
+ try {
+ listener.onResult(result);
+ } catch (RemoteException e) { }
}
}
@@ -486,12 +500,12 @@ public class Tethering {
return TETHER_ERROR_MASTER_ERROR;
}
- private void setBluetoothTethering(final boolean enable, final ResultReceiver receiver) {
+ private void setBluetoothTethering(final boolean enable, final IIntResultListener listener) {
final BluetoothAdapter adapter = mDeps.getBluetoothAdapter();
if (adapter == null || !adapter.isEnabled()) {
Log.w(TAG, "Tried to enable bluetooth tethering with null or disabled adapter. null: "
+ (adapter == null));
- sendTetherResult(receiver, TETHER_ERROR_SERVICE_UNAVAIL);
+ sendTetherResult(listener, TETHER_ERROR_SERVICE_UNAVAIL);
return;
}
@@ -520,7 +534,7 @@ public class Tethering {
final int result = (((BluetoothPan) proxy).isTetheringOn() == enable)
? TETHER_ERROR_NO_ERROR
: TETHER_ERROR_MASTER_ERROR;
- sendTetherResult(receiver, result);
+ sendTetherResult(listener, result);
adapter.closeProfileProxy(BluetoothProfile.PAN, proxy);
}
}, BluetoothProfile.PAN);
@@ -799,6 +813,7 @@ public class Tethering {
final boolean usbConnected = intent.getBooleanExtra(USB_CONNECTED, false);
final boolean usbConfigured = intent.getBooleanExtra(USB_CONFIGURED, false);
final boolean rndisEnabled = intent.getBooleanExtra(USB_FUNCTION_RNDIS, false);
+ final boolean ncmEnabled = intent.getBooleanExtra(USB_FUNCTION_NCM, false);
mLog.log(String.format("USB bcast connected:%s configured:%s rndis:%s",
usbConnected, usbConfigured, rndisEnabled));
@@ -826,6 +841,8 @@ public class Tethering {
} else if (usbConfigured && rndisEnabled) {
// Tether if rndis is enabled and usb is configured.
tetherMatchingInterfaces(IpServer.STATE_TETHERED, TETHERING_USB);
+ } else if (usbConnected && ncmEnabled) {
+ tetherMatchingInterfaces(IpServer.STATE_LOCAL_ONLY, TETHERING_NCM);
}
mRndisEnabled = usbConfigured && rndisEnabled;
}
@@ -1127,6 +1144,16 @@ public class Tethering {
return TETHER_ERROR_NO_ERROR;
}
+ private int setNcmTethering(boolean enable) {
+ if (VDBG) Log.d(TAG, "setNcmTethering(" + enable + ")");
+ UsbManager usbManager = (UsbManager) mContext.getSystemService(Context.USB_SERVICE);
+ synchronized (mPublicSync) {
+ usbManager.setCurrentFunctions(enable ? UsbManager.FUNCTION_NCM
+ : UsbManager.FUNCTION_NONE);
+ }
+ return TETHER_ERROR_NO_ERROR;
+ }
+
// TODO review API - figure out how to delete these entirely.
String[] getTetheredIfaces() {
ArrayList<String> list = new ArrayList<String>();
diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringConfiguration.java b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringConfiguration.java
index 068c346fbfc1..7e9e26f5af40 100644
--- a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringConfiguration.java
+++ b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringConfiguration.java
@@ -83,6 +83,7 @@ public class TetheringConfiguration {
public final String[] tetherableWifiRegexs;
public final String[] tetherableWifiP2pRegexs;
public final String[] tetherableBluetoothRegexs;
+ public final String[] tetherableNcmRegexs;
public final boolean isDunRequired;
public final boolean chooseUpstreamAutomatically;
public final Collection<Integer> preferredUpstreamIfaceTypes;
@@ -103,6 +104,7 @@ public class TetheringConfiguration {
Resources res = getResources(ctx, activeDataSubId);
tetherableUsbRegexs = getResourceStringArray(res, R.array.config_tether_usb_regexs);
+ tetherableNcmRegexs = getResourceStringArray(res, R.array.config_tether_ncm_regexs);
// TODO: Evaluate deleting this altogether now that Wi-Fi always passes
// us an interface name. Careful consideration needs to be given to
// implications for Settings and for provisioning checks.
@@ -156,6 +158,11 @@ public class TetheringConfiguration {
return matchesDownstreamRegexs(iface, tetherableBluetoothRegexs);
}
+ /** Check if interface is ncm */
+ public boolean isNcm(String iface) {
+ return matchesDownstreamRegexs(iface, tetherableNcmRegexs);
+ }
+
/** Check whether no ui entitlement application is available.*/
public boolean hasMobileHotspotProvisionApp() {
return !TextUtils.isEmpty(provisioningAppNoUi);
@@ -170,6 +177,7 @@ public class TetheringConfiguration {
dumpStringArray(pw, "tetherableWifiRegexs", tetherableWifiRegexs);
dumpStringArray(pw, "tetherableWifiP2pRegexs", tetherableWifiP2pRegexs);
dumpStringArray(pw, "tetherableBluetoothRegexs", tetherableBluetoothRegexs);
+ dumpStringArray(pw, "tetherableNcmRegexs", tetherableNcmRegexs);
pw.print("isDunRequired: ");
pw.println(isDunRequired);
diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java
index cb7d3920e693..7dc5c5f2db8a 100644
--- a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java
+++ b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java
@@ -33,6 +33,7 @@ import android.net.ITetheringConnector;
import android.net.ITetheringEventCallback;
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
+import android.net.TetheringRequestParcel;
import android.net.dhcp.DhcpServerCallbacks;
import android.net.dhcp.DhcpServingParamsParcel;
import android.net.ip.IpServer;
@@ -143,11 +144,11 @@ public class TetheringService extends Service {
}
@Override
- public void startTethering(int type, ResultReceiver receiver, boolean showProvisioningUi,
- String callerPkg) {
- if (checkAndNotifyCommonError(callerPkg, receiver)) return;
+ public void startTethering(TetheringRequestParcel request, String callerPkg,
+ IIntResultListener listener) {
+ if (checkAndNotifyCommonError(callerPkg, listener)) return;
- mTethering.startTethering(type, receiver, showProvisioningUi);
+ mTethering.startTethering(request, listener);
}
@Override
diff --git a/packages/Tethering/tests/unit/src/android/net/TetheredClientTest.kt b/packages/Tethering/tests/unit/src/android/net/TetheredClientTest.kt
new file mode 100644
index 000000000000..83c19ec14d56
--- /dev/null
+++ b/packages/Tethering/tests/unit/src/android/net/TetheredClientTest.kt
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net
+
+import android.net.InetAddresses.parseNumericAddress
+import android.net.TetheredClient.AddressInfo
+import android.net.TetheringManager.TETHERING_BLUETOOTH
+import android.net.TetheringManager.TETHERING_USB
+import androidx.test.filters.SmallTest
+import androidx.test.runner.AndroidJUnit4
+import com.android.testutils.assertParcelSane
+import org.junit.Test
+import org.junit.runner.RunWith
+import kotlin.test.assertEquals
+import kotlin.test.assertNotEquals
+
+private val TEST_MACADDR = MacAddress.fromBytes(byteArrayOf(12, 23, 34, 45, 56, 67))
+private val TEST_OTHER_MACADDR = MacAddress.fromBytes(byteArrayOf(23, 34, 45, 56, 67, 78))
+private val TEST_ADDR1 = LinkAddress(parseNumericAddress("192.168.113.3"), 24)
+private val TEST_ADDR2 = LinkAddress(parseNumericAddress("fe80::1:2:3"), 64)
+private val TEST_ADDRINFO1 = AddressInfo(TEST_ADDR1, "test_hostname")
+private val TEST_ADDRINFO2 = AddressInfo(TEST_ADDR2, null)
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class TetheredClientTest {
+ @Test
+ fun testParceling() {
+ assertParcelSane(makeTestClient(), fieldCount = 3)
+ }
+
+ @Test
+ fun testEquals() {
+ assertEquals(makeTestClient(), makeTestClient())
+
+ // Different mac address
+ assertNotEquals(makeTestClient(), TetheredClient(
+ TEST_OTHER_MACADDR,
+ listOf(TEST_ADDRINFO1, TEST_ADDRINFO2),
+ TETHERING_BLUETOOTH))
+
+ // Different hostname
+ assertNotEquals(makeTestClient(), TetheredClient(
+ TEST_MACADDR,
+ listOf(AddressInfo(TEST_ADDR1, "test_other_hostname"), TEST_ADDRINFO2),
+ TETHERING_BLUETOOTH))
+
+ // Null hostname
+ assertNotEquals(makeTestClient(), TetheredClient(
+ TEST_MACADDR,
+ listOf(AddressInfo(TEST_ADDR1, null), TEST_ADDRINFO2),
+ TETHERING_BLUETOOTH))
+
+ // Missing address
+ assertNotEquals(makeTestClient(), TetheredClient(
+ TEST_MACADDR,
+ listOf(TEST_ADDRINFO2),
+ TETHERING_BLUETOOTH))
+
+ // Different type
+ assertNotEquals(makeTestClient(), TetheredClient(
+ TEST_MACADDR,
+ listOf(TEST_ADDRINFO1, TEST_ADDRINFO2),
+ TETHERING_BLUETOOTH))
+ }
+
+ @Test
+ fun testAddAddresses() {
+ val client1 = TetheredClient(TEST_MACADDR, listOf(TEST_ADDRINFO1), TETHERING_USB)
+ val client2 = TetheredClient(TEST_OTHER_MACADDR, listOf(TEST_ADDRINFO2), TETHERING_USB)
+ assertEquals(TetheredClient(
+ TEST_MACADDR,
+ listOf(TEST_ADDRINFO1, TEST_ADDRINFO2),
+ TETHERING_USB), client1.addAddresses(client2))
+ }
+
+ private fun makeTestClient() = TetheredClient(
+ TEST_MACADDR,
+ listOf(TEST_ADDRINFO1, TEST_ADDRINFO2),
+ TETHERING_BLUETOOTH)
+} \ No newline at end of file
diff --git a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java
index 9f0d8769b1f9..4710287f33f3 100644
--- a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java
+++ b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java
@@ -18,6 +18,7 @@ package com.android.server.connectivity.tethering;
import static android.hardware.usb.UsbManager.USB_CONFIGURED;
import static android.hardware.usb.UsbManager.USB_CONNECTED;
+import static android.hardware.usb.UsbManager.USB_FUNCTION_NCM;
import static android.hardware.usb.UsbManager.USB_FUNCTION_RNDIS;
import static android.net.ConnectivityManager.ACTION_RESTRICT_BACKGROUND_CHANGED;
import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_DISABLED;
@@ -27,6 +28,7 @@ import static android.net.TetheringManager.ACTION_TETHER_STATE_CHANGED;
import static android.net.TetheringManager.EXTRA_ACTIVE_LOCAL_ONLY;
import static android.net.TetheringManager.EXTRA_ACTIVE_TETHER;
import static android.net.TetheringManager.EXTRA_AVAILABLE_TETHER;
+import static android.net.TetheringManager.TETHERING_NCM;
import static android.net.TetheringManager.TETHERING_USB;
import static android.net.TetheringManager.TETHERING_WIFI;
import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR;
@@ -88,6 +90,7 @@ import android.net.RouteInfo;
import android.net.TetherStatesParcel;
import android.net.TetheringCallbackStartedParcel;
import android.net.TetheringConfigurationParcel;
+import android.net.TetheringRequestParcel;
import android.net.dhcp.DhcpServerCallbacks;
import android.net.dhcp.DhcpServingParamsParcel;
import android.net.dhcp.IDhcpServer;
@@ -150,6 +153,7 @@ public class TetheringTest {
private static final String TEST_USB_IFNAME = "test_rndis0";
private static final String TEST_WLAN_IFNAME = "test_wlan0";
private static final String TEST_P2P_IFNAME = "test_p2p-p2p0-0";
+ private static final String TEST_NCM_IFNAME = "test_ncm0";
private static final String TETHERING_NAME = "Tethering";
private static final int DHCPSERVER_START_TIMEOUT_MS = 1000;
@@ -251,9 +255,11 @@ public class TetheringTest {
ifName.equals(TEST_USB_IFNAME)
|| ifName.equals(TEST_WLAN_IFNAME)
|| ifName.equals(TEST_MOBILE_IFNAME)
- || ifName.equals(TEST_P2P_IFNAME));
+ || ifName.equals(TEST_P2P_IFNAME)
+ || ifName.equals(TEST_NCM_IFNAME));
final String[] ifaces = new String[] {
- TEST_USB_IFNAME, TEST_WLAN_IFNAME, TEST_MOBILE_IFNAME, TEST_P2P_IFNAME};
+ TEST_USB_IFNAME, TEST_WLAN_IFNAME, TEST_MOBILE_IFNAME, TEST_P2P_IFNAME,
+ TEST_NCM_IFNAME};
return new InterfaceParams(ifName, ArrayUtils.indexOf(ifaces, ifName) + IFINDEX_OFFSET,
MacAddress.ALL_ZEROS_ADDRESS);
}
@@ -427,13 +433,16 @@ public class TetheringTest {
.thenReturn(new String[]{ "test_p2p-p2p\\d-.*" });
when(mResources.getStringArray(R.array.config_tether_bluetooth_regexs))
.thenReturn(new String[0]);
+ when(mResources.getStringArray(R.array.config_tether_ncm_regexs))
+ .thenReturn(new String[] { "test_ncm\\d" });
when(mResources.getIntArray(R.array.config_tether_upstream_types)).thenReturn(new int[0]);
when(mResources.getBoolean(R.bool.config_tether_upstream_automatic)).thenReturn(false);
when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn(
false);
when(mNetd.interfaceGetList())
.thenReturn(new String[] {
- TEST_MOBILE_IFNAME, TEST_WLAN_IFNAME, TEST_USB_IFNAME, TEST_P2P_IFNAME});
+ TEST_MOBILE_IFNAME, TEST_WLAN_IFNAME, TEST_USB_IFNAME, TEST_P2P_IFNAME,
+ TEST_NCM_IFNAME});
when(mResources.getString(R.string.config_wifi_tether_enable)).thenReturn("");
mInterfaceConfiguration = new InterfaceConfigurationParcel();
mInterfaceConfiguration.flags = new String[0];
@@ -468,6 +477,16 @@ public class TetheringTest {
return new Tethering(mTetheringDependencies);
}
+ private TetheringRequestParcel createTetheringRquestParcel(final int type) {
+ final TetheringRequestParcel request = new TetheringRequestParcel();
+ request.tetheringType = type;
+ request.localIPv4Address = null;
+ request.exemptFromEntitlementCheck = false;
+ request.showProvisioningUi = false;
+
+ return request;
+ }
+
@After
public void tearDown() {
mServiceContext.unregisterReceiver(mBroadcastReceiver);
@@ -513,11 +532,16 @@ public class TetheringTest {
P2P_RECEIVER_PERMISSIONS_FOR_BROADCAST);
}
- private void sendUsbBroadcast(boolean connected, boolean configured, boolean rndisFunction) {
+ private void sendUsbBroadcast(boolean connected, boolean configured, boolean function,
+ int type) {
final Intent intent = new Intent(UsbManager.ACTION_USB_STATE);
intent.putExtra(USB_CONNECTED, connected);
intent.putExtra(USB_CONFIGURED, configured);
- intent.putExtra(USB_FUNCTION_RNDIS, rndisFunction);
+ if (type == TETHERING_USB) {
+ intent.putExtra(USB_FUNCTION_RNDIS, function);
+ } else {
+ intent.putExtra(USB_FUNCTION_NCM, function);
+ }
mServiceContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
}
@@ -567,13 +591,22 @@ public class TetheringTest {
verifyNoMoreInteractions(mWifiManager);
}
+ private void prepareNcmTethering() {
+ // Emulate startTethering(TETHERING_NCM) called
+ mTethering.startTethering(createTetheringRquestParcel(TETHERING_NCM), null);
+ mLooper.dispatchAll();
+ verify(mUsbManager, times(1)).setCurrentFunctions(UsbManager.FUNCTION_NCM);
+
+ mTethering.interfaceStatusChanged(TEST_NCM_IFNAME, true);
+ }
+
private void prepareUsbTethering(UpstreamNetworkState upstreamState) {
when(mUpstreamNetworkMonitor.getCurrentPreferredUpstream()).thenReturn(upstreamState);
when(mUpstreamNetworkMonitor.selectPreferredUpstreamType(any()))
.thenReturn(upstreamState);
// Emulate pressing the USB tethering button in Settings UI.
- mTethering.startTethering(TETHERING_USB, null, false);
+ mTethering.startTethering(createTetheringRquestParcel(TETHERING_USB), null);
mLooper.dispatchAll();
verify(mUsbManager, times(1)).setCurrentFunctions(UsbManager.FUNCTION_RNDIS);
@@ -589,7 +622,7 @@ public class TetheringTest {
verifyNoMoreInteractions(mNetd);
// Pretend we then receive USB configured broadcast.
- sendUsbBroadcast(true, true, true);
+ sendUsbBroadcast(true, true, true, TETHERING_USB);
mLooper.dispatchAll();
// Now we should see the start of tethering mechanics (in this case:
// tetherMatchingInterfaces() which starts by fetching all interfaces).
@@ -680,7 +713,7 @@ public class TetheringTest {
private void runUsbTethering(UpstreamNetworkState upstreamState) {
prepareUsbTethering(upstreamState);
- sendUsbBroadcast(true, true, true);
+ sendUsbBroadcast(true, true, true, TETHERING_USB);
mLooper.dispatchAll();
}
@@ -803,6 +836,29 @@ public class TetheringTest {
verify(mUpstreamNetworkMonitor, times(1)).setCurrentUpstream(upstreamState.network);
}
+ private void runNcmTethering() {
+ prepareNcmTethering();
+ sendUsbBroadcast(true, true, true, TETHERING_NCM);
+ mLooper.dispatchAll();
+ }
+
+ @Test
+ public void workingNcmTethering() throws Exception {
+ runNcmTethering();
+
+ verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).start(any());
+ }
+
+ @Test
+ public void workingNcmTethering_LegacyDhcp() {
+ when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn(
+ true);
+ sendConfigurationChanged();
+ runNcmTethering();
+
+ verify(mIpServerDependencies, never()).makeDhcpServer(any(), any(), any());
+ }
+
@Test
public void workingLocalOnlyHotspotEnrichedApBroadcastWithIfaceChanged() throws Exception {
workingLocalOnlyHotspotEnrichedApBroadcast(true);
@@ -819,7 +875,7 @@ public class TetheringTest {
when(mWifiManager.startTetheredHotspot(any(SoftApConfiguration.class))).thenReturn(true);
// Emulate pressing the WiFi tethering button.
- mTethering.startTethering(TETHERING_WIFI, null, false);
+ mTethering.startTethering(createTetheringRquestParcel(TETHERING_WIFI), null);
mLooper.dispatchAll();
verify(mWifiManager, times(1)).startTetheredHotspot(null);
verifyNoMoreInteractions(mWifiManager);
@@ -846,7 +902,7 @@ public class TetheringTest {
when(mWifiManager.startTetheredHotspot(any(SoftApConfiguration.class))).thenReturn(true);
// Emulate pressing the WiFi tethering button.
- mTethering.startTethering(TETHERING_WIFI, null, false);
+ mTethering.startTethering(createTetheringRquestParcel(TETHERING_WIFI), null);
mLooper.dispatchAll();
verify(mWifiManager, times(1)).startTetheredHotspot(null);
verifyNoMoreInteractions(mWifiManager);
@@ -923,7 +979,7 @@ public class TetheringTest {
doThrow(new RemoteException()).when(mNetd).ipfwdEnableForwarding(TETHERING_NAME);
// Emulate pressing the WiFi tethering button.
- mTethering.startTethering(TETHERING_WIFI, null, false);
+ mTethering.startTethering(createTetheringRquestParcel(TETHERING_WIFI), null);
mLooper.dispatchAll();
verify(mWifiManager, times(1)).startTetheredHotspot(null);
verifyNoMoreInteractions(mWifiManager);
@@ -1188,7 +1244,7 @@ public class TetheringTest {
tetherState = callback.pollTetherStatesChanged();
assertArrayEquals(tetherState.availableList, new String[] {TEST_WLAN_IFNAME});
- mTethering.startTethering(TETHERING_WIFI, null, false);
+ mTethering.startTethering(createTetheringRquestParcel(TETHERING_WIFI), null);
sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, TEST_WLAN_IFNAME, IFACE_IP_MODE_TETHERED);
mLooper.dispatchAll();
tetherState = callback.pollTetherStatesChanged();
diff --git a/proto/src/task_snapshot.proto b/proto/src/task_snapshot.proto
index 821db860dd96..789019ce8b75 100644
--- a/proto/src/task_snapshot.proto
+++ b/proto/src/task_snapshot.proto
@@ -34,4 +34,5 @@
string top_activity_component = 10;
float scale = 11;
int64 id = 12;
+ int32 rotation = 13;
}
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index 26245b15f92b..28298cb51f6e 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -634,8 +634,11 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
final SuspendDialogInfo dialogInfo =
mPackageManagerInternal.getSuspendedDialogInfo(providerPackage,
suspendingPackage, providerUserId);
+ // TODO(b/148035643): Send the original widget intent or ACTION_MAIN as an
+ // IntentSender to SuspendedAppActivity.
onClickIntent = SuspendedAppActivity.createSuspendedAppInterceptIntent(
- providerPackage, suspendingPackage, dialogInfo, null, providerUserId);
+ providerPackage, suspendingPackage, dialogInfo, null, null,
+ providerUserId);
}
} else if (provider.maskedByQuietProfile) {
showBadge = true;
diff --git a/services/autofill/java/com/android/server/autofill/InlineSuggestionFactory.java b/services/autofill/java/com/android/server/autofill/InlineSuggestionFactory.java
index 5e6f97e4f282..c7be80cd538b 100644
--- a/services/autofill/java/com/android/server/autofill/InlineSuggestionFactory.java
+++ b/services/autofill/java/com/android/server/autofill/InlineSuggestionFactory.java
@@ -20,7 +20,6 @@ import static com.android.server.autofill.Helper.sDebug;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.app.slice.Slice;
import android.content.Context;
import android.os.RemoteException;
import android.service.autofill.Dataset;
@@ -123,7 +122,8 @@ public final class InlineSuggestionFactory {
// TODO(b/146453195): fill in the autofill hint properly.
final InlineSuggestionInfo inlineSuggestionInfo = new InlineSuggestionInfo(
inlinePresentation.getInlinePresentationSpec(),
- InlineSuggestionInfo.SOURCE_PLATFORM, new String[]{""});
+ InlineSuggestionInfo.SOURCE_PLATFORM, new String[]{""},
+ InlineSuggestionInfo.TYPE_SUGGESTION);
final View.OnClickListener onClickListener = v -> {
try {
client.autofill(sessionId, dataset.getFieldIds(), dataset.getFieldValues());
@@ -132,7 +132,7 @@ public final class InlineSuggestionFactory {
}
};
final InlineSuggestion inlineSuggestion = new InlineSuggestion(inlineSuggestionInfo,
- createInlineContentProvider(inlinePresentation.getSlice(), inlineSuggestionUi,
+ createInlineContentProvider(inlinePresentation, inlineSuggestionUi,
onClickListener));
return inlineSuggestion;
}
@@ -146,25 +146,28 @@ public final class InlineSuggestionFactory {
// TODO(b/146453195): fill in the autofill hint properly.
final InlineSuggestionInfo inlineSuggestionInfo = new InlineSuggestionInfo(
inlinePresentation.getInlinePresentationSpec(),
- InlineSuggestionInfo.SOURCE_AUTOFILL, new String[]{""});
+ InlineSuggestionInfo.SOURCE_AUTOFILL, new String[]{""},
+ InlineSuggestionInfo.TYPE_SUGGESTION);
final View.OnClickListener onClickListener = v -> {
client.fill(requestId, fieldIndex, dataset);
};
final InlineSuggestion inlineSuggestion = new InlineSuggestion(inlineSuggestionInfo,
- createInlineContentProvider(inlinePresentation.getSlice(), inlineSuggestionUi,
+ createInlineContentProvider(inlinePresentation, inlineSuggestionUi,
onClickListener));
return inlineSuggestion;
}
private static IInlineContentProvider.Stub createInlineContentProvider(
- @NonNull Slice slice, @NonNull InlineSuggestionUi inlineSuggestionUi,
+ @NonNull InlinePresentation inlinePresentation,
+ @NonNull InlineSuggestionUi inlineSuggestionUi,
@Nullable View.OnClickListener onClickListener) {
return new IInlineContentProvider.Stub() {
@Override
public void provideContent(int width, int height,
IInlineContentCallback callback) {
UiThread.getHandler().post(() -> {
- SurfaceControl sc = inlineSuggestionUi.inflate(slice, width, height,
+ SurfaceControl sc = inlineSuggestionUi.inflate(inlinePresentation, width,
+ height,
onClickListener);
try {
callback.onContent(sc);
diff --git a/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionUi.java b/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionUi.java
index 2460732d17dc..2adefeabfcc8 100644
--- a/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionUi.java
@@ -25,10 +25,17 @@ import android.annotation.Nullable;
import android.app.slice.Slice;
import android.app.slice.SliceItem;
import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
import android.graphics.PixelFormat;
import android.graphics.drawable.Icon;
+import android.graphics.fonts.SystemFonts;
import android.os.IBinder;
+import android.service.autofill.InlinePresentation;
+import android.text.TextUtils;
import android.util.Log;
+import android.view.ContextThemeWrapper;
import android.view.LayoutInflater;
import android.view.SurfaceControl;
import android.view.SurfaceControlViewHost;
@@ -41,6 +48,8 @@ import android.widget.TextView;
import com.android.internal.R;
import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
/**
* This is a temporary inline suggestion UI inflater which will be replaced by the ExtServices
@@ -54,6 +63,10 @@ public class InlineSuggestionUi {
private static final String TAG = "InlineSuggestionUi";
+ // The pattern to match the value can be obtained by calling {@code Resources#getResourceName
+ // (int)}. This name is a single string of the form "package:type/entry".
+ private static final Pattern RESOURCE_NAME_PATTERN = Pattern.compile("([^:]+):([^/]+)/(\\S+)");
+
private final Context mContext;
public InlineSuggestionUi(Context context) {
@@ -65,28 +78,36 @@ public class InlineSuggestionUi {
*/
@MainThread
@Nullable
- public SurfaceControl inflate(@NonNull Slice slice, int width, int height,
- @Nullable View.OnClickListener onClickListener) {
+ public SurfaceControl inflate(@NonNull InlinePresentation inlinePresentation, int width,
+ int height, @Nullable View.OnClickListener onClickListener) {
Log.d(TAG, "Inflating the inline suggestion UI");
//TODO(b/137800469): Pass in inputToken from IME.
final SurfaceControlViewHost wvr = new SurfaceControlViewHost(mContext,
mContext.getDisplay(), (IBinder) null);
final SurfaceControl sc = wvr.getSurfacePackage().getSurfaceControl();
- final ViewGroup suggestionView = (ViewGroup) renderSlice(slice);
+
+ Context contextThemeWrapper = getContextThemeWrapper(mContext,
+ inlinePresentation.getInlinePresentationSpec().getStyle());
+ if (contextThemeWrapper == null) {
+ contextThemeWrapper = getDefaultContextThemeWrapper(mContext);
+ }
+ final View suggestionView = renderSlice(inlinePresentation.getSlice(),
+ contextThemeWrapper);
if (onClickListener != null) {
suggestionView.setOnClickListener(onClickListener);
}
WindowManager.LayoutParams lp =
new WindowManager.LayoutParams(width, height,
- WindowManager.LayoutParams.TYPE_APPLICATION, 0, PixelFormat.TRANSPARENT);
+ WindowManager.LayoutParams.TYPE_APPLICATION, 0,
+ PixelFormat.TRANSPARENT);
wvr.addView(suggestionView, lp);
return sc;
}
- private View renderSlice(Slice slice) {
- final LayoutInflater inflater = LayoutInflater.from(mContext);
+ private static View renderSlice(Slice slice, Context context) {
+ final LayoutInflater inflater = LayoutInflater.from(context);
final ViewGroup suggestionView =
(ViewGroup) inflater.inflate(R.layout.autofill_inline_suggestion, null);
@@ -137,4 +158,96 @@ public class InlineSuggestionUi {
return suggestionView;
}
+
+ private Context getDefaultContextThemeWrapper(@NonNull Context context) {
+ Resources.Theme theme = context.getResources().newTheme();
+ theme.applyStyle(android.R.style.Theme_AutofillInlineSuggestion, true);
+ return new ContextThemeWrapper(context, theme);
+ }
+
+ /**
+ * Returns a context wrapping the theme in the provided {@code style}, or null if {@code
+ * style} doesn't pass validation.
+ */
+ @Nullable
+ private static Context getContextThemeWrapper(@NonNull Context context,
+ @Nullable String style) {
+ if (style == null) {
+ return null;
+ }
+ Matcher matcher = RESOURCE_NAME_PATTERN.matcher(style);
+ if (!matcher.matches()) {
+ Log.d(TAG, "Can not parse the style=" + style);
+ return null;
+ }
+ String packageName = matcher.group(1);
+ String type = matcher.group(2);
+ String entry = matcher.group(3);
+ if (TextUtils.isEmpty(packageName) || TextUtils.isEmpty(type) || TextUtils.isEmpty(entry)) {
+ Log.d(TAG, "Can not proceed with empty field values in the style=" + style);
+ return null;
+ }
+ Resources resources = null;
+ try {
+ resources = context.getPackageManager().getResourcesForApplication(
+ packageName);
+ } catch (PackageManager.NameNotFoundException e) {
+ return null;
+ }
+ int resId = resources.getIdentifier(entry, type, packageName);
+ if (resId == Resources.ID_NULL) {
+ return null;
+ }
+ Resources.Theme theme = resources.newTheme();
+ theme.applyStyle(resId, true);
+ if (!validateBaseTheme(theme, resId)) {
+ Log.d(TAG, "Provided theme is not a child of Theme.InlineSuggestion, ignoring it.");
+ return null;
+ }
+ if (!validateFontFamilyForTextViewStyles(theme)) {
+ Log.d(TAG,
+ "Provided theme specifies a font family that is not system font, ignoring it.");
+ return null;
+ }
+ return new ContextThemeWrapper(context, theme);
+ }
+
+ private static boolean validateFontFamilyForTextViewStyles(Resources.Theme theme) {
+ return validateFontFamily(theme, android.R.attr.autofillInlineSuggestionTitle)
+ && validateFontFamily(theme, android.R.attr.autofillInlineSuggestionSubtitle);
+ }
+
+ private static boolean validateFontFamily(Resources.Theme theme, int styleAttr) {
+ TypedArray ta = null;
+ try {
+ ta = theme.obtainStyledAttributes(null, new int[]{android.R.attr.fontFamily},
+ styleAttr,
+ 0);
+ if (ta.getIndexCount() == 0) {
+ return true;
+ }
+ String fontFamily = ta.getString(ta.getIndex(0));
+ return SystemFonts.getRawSystemFallbackMap().containsKey(fontFamily);
+ } finally {
+ if (ta != null) {
+ ta.recycle();
+ }
+ }
+ }
+
+ private static boolean validateBaseTheme(Resources.Theme theme, int styleAttr) {
+ TypedArray ta = null;
+ try {
+ ta = theme.obtainStyledAttributes(null,
+ new int[]{android.R.attr.isAutofillInlineSuggestionTheme}, styleAttr, 0);
+ if (ta.getIndexCount() == 0) {
+ return false;
+ }
+ return ta.getBoolean(ta.getIndex(0), false);
+ } finally {
+ if (ta != null) {
+ ta.recycle();
+ }
+ }
+ }
}
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
index 8eca62a4932b..9245a1da43b2 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
@@ -45,7 +45,12 @@ import android.database.ContentObserver;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
+import android.os.CancellationSignal;
+import android.os.Handler;
import android.os.IBinder;
+import android.os.ICancellationSignal;
+import android.os.Looper;
+import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ShellCallback;
@@ -55,8 +60,11 @@ import android.provider.DeviceConfig;
import android.provider.DeviceConfig.Properties;
import android.provider.Settings;
import android.service.contentcapture.ActivityEvent.ActivityEventType;
+import android.service.contentcapture.IDataShareCallback;
+import android.service.contentcapture.IDataShareReadAdapter;
import android.util.ArraySet;
import android.util.LocalLog;
+import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
@@ -64,7 +72,10 @@ import android.view.contentcapture.ContentCaptureCondition;
import android.view.contentcapture.ContentCaptureHelper;
import android.view.contentcapture.ContentCaptureManager;
import android.view.contentcapture.DataRemovalRequest;
+import android.view.contentcapture.DataShareRequest;
+import android.view.contentcapture.DataShareWriteAdapter;
import android.view.contentcapture.IContentCaptureManager;
+import android.view.contentcapture.IDataShareWriteAdapter;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.infra.AbstractRemoteService;
@@ -77,9 +88,16 @@ import com.android.server.infra.AbstractMasterSystemService;
import com.android.server.infra.FrameworkResourcesServiceNameResolver;
import java.io.FileDescriptor;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
/**
* A service used to observe the contents of the screen.
@@ -94,6 +112,9 @@ public final class ContentCaptureManagerService extends
static final String RECEIVER_BUNDLE_EXTRA_SESSIONS = "sessions";
private static final int MAX_TEMP_SERVICE_DURATION_MS = 1_000 * 60 * 2; // 2 minutes
+ private static final int MAX_DATA_SHARE_FILE_DESCRIPTORS_TTL_MS = 1_000 * 60 * 5; // 5 minutes
+ private static final int MAX_CONCURRENT_FILE_SHARING_REQUESTS = 10;
+ private static final int DATA_SHARE_BYTE_BUFFER_LENGTH = 1_024;
private final LocalService mLocalService = new LocalService();
@@ -126,6 +147,12 @@ public final class ContentCaptureManagerService extends
@GuardedBy("mLock") int mDevCfgLogHistorySize;
@GuardedBy("mLock") int mDevCfgIdleUnbindTimeoutMs;
+ private final Executor mDataShareExecutor = Executors.newCachedThreadPool();
+ private final Handler mHandler = new Handler(Looper.getMainLooper());
+
+ @GuardedBy("mLock")
+ private final Set<String> mPackagesWithShareRequests = new HashSet<>();
+
final GlobalContentCaptureOptions mGlobalContentCaptureOptions =
new GlobalContentCaptureOptions();
@@ -618,6 +645,33 @@ public final class ContentCaptureManagerService extends
}
@Override
+ public void shareData(@NonNull DataShareRequest request,
+ @NonNull IDataShareWriteAdapter clientAdapter) {
+ Preconditions.checkNotNull(request);
+ Preconditions.checkNotNull(clientAdapter);
+
+ assertCalledByPackageOwner(request.getPackageName());
+
+ final int userId = UserHandle.getCallingUserId();
+ synchronized (mLock) {
+ final ContentCapturePerUserService service = getServiceForUserLocked(userId);
+
+ if (mPackagesWithShareRequests.size() >= MAX_CONCURRENT_FILE_SHARING_REQUESTS
+ || mPackagesWithShareRequests.contains(request.getPackageName())) {
+ try {
+ clientAdapter.error(DataShareWriteAdapter.ERROR_CONCURRENT_REQUEST);
+ } catch (RemoteException e) {
+ Slog.e(mTag, "Failed to send error message to client");
+ }
+ return;
+ }
+
+ service.onDataSharedLocked(request,
+ new DataShareCallbackDelegate(request, clientAdapter));
+ }
+ }
+
+ @Override
public void isContentCaptureFeatureEnabled(@NonNull IResultReceiver result) {
boolean enabled;
synchronized (mLock) {
@@ -860,4 +914,179 @@ public final class ContentCaptureManagerService extends
}
}
}
+
+ // TODO(b/148265162): DataShareCallbackDelegate should be a static class keeping week references
+ // to the needed info
+ private class DataShareCallbackDelegate extends IDataShareCallback.Stub {
+
+ @NonNull private final DataShareRequest mDataShareRequest;
+ @NonNull private final IDataShareWriteAdapter mClientAdapter;
+
+ DataShareCallbackDelegate(@NonNull DataShareRequest dataShareRequest,
+ @NonNull IDataShareWriteAdapter clientAdapter) {
+ mDataShareRequest = dataShareRequest;
+ mClientAdapter = clientAdapter;
+ }
+
+ @Override
+ public void accept(IDataShareReadAdapter serviceAdapter)
+ throws RemoteException {
+ Slog.i(mTag, "Data share request accepted by Content Capture service");
+
+ Pair<ParcelFileDescriptor, ParcelFileDescriptor> clientPipe = createPipe();
+ if (clientPipe == null) {
+ mClientAdapter.error(DataShareWriteAdapter.ERROR_UNKNOWN);
+ serviceAdapter.error(DataShareWriteAdapter.ERROR_UNKNOWN);
+ return;
+ }
+
+ ParcelFileDescriptor source_in = clientPipe.second;
+ ParcelFileDescriptor sink_in = clientPipe.first;
+
+ Pair<ParcelFileDescriptor, ParcelFileDescriptor> servicePipe = createPipe();
+ if (servicePipe == null) {
+ bestEffortCloseFileDescriptors(source_in, sink_in);
+
+ mClientAdapter.error(DataShareWriteAdapter.ERROR_UNKNOWN);
+ serviceAdapter.error(DataShareWriteAdapter.ERROR_UNKNOWN);
+ return;
+ }
+
+ ParcelFileDescriptor source_out = servicePipe.second;
+ ParcelFileDescriptor sink_out = servicePipe.first;
+
+ ICancellationSignal cancellationSignalTransport =
+ CancellationSignal.createTransport();
+ mPackagesWithShareRequests.add(mDataShareRequest.getPackageName());
+
+ mClientAdapter.write(source_in);
+ serviceAdapter.start(sink_out, cancellationSignalTransport);
+
+ // TODO(b/148264965): use cancellation signals for timeouts and cancelling
+ CancellationSignal cancellationSignal =
+ CancellationSignal.fromTransport(cancellationSignalTransport);
+
+ cancellationSignal.setOnCancelListener(() -> {
+ try {
+ // TODO(b/148264965): this should propagate with the cancellation signal to the
+ // client
+ mClientAdapter.error(DataShareWriteAdapter.ERROR_UNKNOWN);
+ } catch (RemoteException e) {
+ Slog.e(mTag, "Failed to propagate cancel operation to the caller", e);
+ }
+ });
+
+ // File descriptor received by the client app will be a copy of the current one. Close
+ // the one that belongs to the system server, so there's only 1 open left for the
+ // current pipe.
+ bestEffortCloseFileDescriptor(source_in);
+
+ mDataShareExecutor.execute(() -> {
+ try (InputStream fis =
+ new ParcelFileDescriptor.AutoCloseInputStream(sink_in);
+ OutputStream fos =
+ new ParcelFileDescriptor.AutoCloseOutputStream(source_out)) {
+
+ byte[] byteBuffer = new byte[DATA_SHARE_BYTE_BUFFER_LENGTH];
+ while (true) {
+ int readBytes = fis.read(byteBuffer);
+
+ if (readBytes == -1) {
+ break;
+ }
+
+ fos.write(byteBuffer, 0 /* offset */, readBytes);
+ }
+ } catch (IOException e) {
+ Slog.e(mTag, "Failed to pipe client and service streams", e);
+ }
+ });
+
+ mHandler.postDelayed(() -> {
+ synchronized (mLock) {
+ mPackagesWithShareRequests.remove(mDataShareRequest.getPackageName());
+
+ // Interaction finished successfully <=> all data has been written to Content
+ // Capture Service. If it hasn't been read successfully, service would be able
+ // to signal through the cancellation signal.
+ boolean finishedSuccessfully = !sink_in.getFileDescriptor().valid()
+ && !source_out.getFileDescriptor().valid();
+
+ if (finishedSuccessfully) {
+ Slog.i(mTag, "Content capture data sharing session terminated "
+ + "successfully for package '"
+ + mDataShareRequest.getPackageName()
+ + "'");
+ } else {
+ Slog.i(mTag, "Reached the timeout of Content Capture data sharing session "
+ + "for package '"
+ + mDataShareRequest.getPackageName()
+ + "', terminating the pipe.");
+ }
+
+ // Ensure all the descriptors are closed after the session.
+ bestEffortCloseFileDescriptors(source_in, sink_in, source_out, sink_out);
+
+ if (!finishedSuccessfully) {
+ try {
+ mClientAdapter.error(DataShareWriteAdapter.ERROR_UNKNOWN);
+ } catch (RemoteException e) {
+ Slog.e(mTag, "Failed to call error() to client", e);
+ }
+ try {
+ serviceAdapter.error(DataShareWriteAdapter.ERROR_UNKNOWN);
+ } catch (RemoteException e) {
+ Slog.e(mTag, "Failed to call error() to service", e);
+ }
+ }
+ }
+ }, MAX_DATA_SHARE_FILE_DESCRIPTORS_TTL_MS);
+ }
+
+ @Override
+ public void reject() throws RemoteException {
+ Slog.i(mTag, "Data share request rejected by Content Capture service");
+
+ mClientAdapter.rejected();
+ }
+
+ private Pair<ParcelFileDescriptor, ParcelFileDescriptor> createPipe() {
+ ParcelFileDescriptor[] fileDescriptors;
+ try {
+ fileDescriptors = ParcelFileDescriptor.createPipe();
+ } catch (IOException e) {
+ Slog.e(mTag, "Failed to create a content capture data-sharing pipe", e);
+ return null;
+ }
+
+ if (fileDescriptors.length != 2) {
+ Slog.e(mTag, "Failed to create a content capture data-sharing pipe, "
+ + "unexpected number of file descriptors");
+ return null;
+ }
+
+ if (!fileDescriptors[0].getFileDescriptor().valid()
+ || !fileDescriptors[1].getFileDescriptor().valid()) {
+ Slog.e(mTag, "Failed to create a content capture data-sharing pipe, didn't "
+ + "receive a pair of valid file descriptors.");
+ return null;
+ }
+
+ return Pair.create(fileDescriptors[0], fileDescriptors[1]);
+ }
+
+ private void bestEffortCloseFileDescriptor(ParcelFileDescriptor fd) {
+ try {
+ fd.close();
+ } catch (IOException e) {
+ Slog.e(mTag, "Failed to close a file descriptor", e);
+ }
+ }
+
+ private void bestEffortCloseFileDescriptors(ParcelFileDescriptor... fds) {
+ for (ParcelFileDescriptor fd : fds) {
+ bestEffortCloseFileDescriptor(fd);
+ }
+ }
+ }
}
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
index a186d4e7f467..0f1122e3886a 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
@@ -54,6 +54,7 @@ import android.service.contentcapture.ContentCaptureService;
import android.service.contentcapture.ContentCaptureServiceInfo;
import android.service.contentcapture.FlushMetrics;
import android.service.contentcapture.IContentCaptureServiceCallback;
+import android.service.contentcapture.IDataShareCallback;
import android.service.contentcapture.SnapshotData;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -63,6 +64,7 @@ import android.util.SparseBooleanArray;
import android.util.StatsLog;
import android.view.contentcapture.ContentCaptureCondition;
import android.view.contentcapture.DataRemovalRequest;
+import android.view.contentcapture.DataShareRequest;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.os.IResultReceiver;
@@ -375,6 +377,16 @@ final class ContentCapturePerUserService
}
@GuardedBy("mLock")
+ public void onDataSharedLocked(@NonNull DataShareRequest request,
+ IDataShareCallback.Stub dataShareCallback) {
+ if (!isEnabledLocked()) {
+ return;
+ }
+ assertCallerLocked(request.getPackageName());
+ mRemoteService.onDataShareRequest(request, dataShareCallback);
+ }
+
+ @GuardedBy("mLock")
@Nullable
public ComponentName getServiceSettingsActivityLocked() {
if (mInfo == null) return null;
diff --git a/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java b/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java
index 01d33b0e5445..c16df0f19943 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java
@@ -29,11 +29,13 @@ import android.os.IBinder;
import android.service.contentcapture.ActivityEvent;
import android.service.contentcapture.IContentCaptureService;
import android.service.contentcapture.IContentCaptureServiceCallback;
+import android.service.contentcapture.IDataShareCallback;
import android.service.contentcapture.SnapshotData;
import android.util.Slog;
import android.util.StatsLog;
import android.view.contentcapture.ContentCaptureContext;
import android.view.contentcapture.DataRemovalRequest;
+import android.view.contentcapture.DataShareRequest;
import com.android.internal.infra.AbstractMultiplePendingRequestsRemoteService;
import com.android.internal.os.IResultReceiver;
@@ -145,6 +147,11 @@ final class RemoteContentCaptureService
mComponentName);
}
+ public void onDataShareRequest(@NonNull DataShareRequest request,
+ @NonNull IDataShareCallback.Stub dataShareCallback) {
+ scheduleAsyncRequest((s) -> s.onDataShared(request, dataShareCallback));
+ }
+
/**
* Called by {@link ContentCaptureServerSession} to notify a high-level activity event.
*/
diff --git a/services/core/java/android/app/usage/UsageStatsManagerInternal.java b/services/core/java/android/app/usage/UsageStatsManagerInternal.java
index f3647602e69b..a8be66990fff 100644
--- a/services/core/java/android/app/usage/UsageStatsManagerInternal.java
+++ b/services/core/java/android/app/usage/UsageStatsManagerInternal.java
@@ -16,10 +16,14 @@
package android.app.usage;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.usage.UsageStatsManager.StandbyBuckets;
import android.content.ComponentName;
+import android.content.LocusId;
import android.content.res.Configuration;
+import android.os.IBinder;
import android.os.UserHandle;
import android.os.UserManager;
@@ -111,6 +115,20 @@ public abstract class UsageStatsManagerInternal {
public abstract void reportContentProviderUsage(String name, String pkgName,
@UserIdInt int userId);
+
+ /**
+ * Reports locusId update for a given activity.
+ *
+ * @param activity The component name of the app.
+ * @param userId The user id of who uses the app.
+ * @param locusId The locusId a unique, stable id that identifies this activity.
+ * @param appToken ActivityRecord's appToken.
+ * {@link UsageEvents}
+ * @hide
+ */
+ public abstract void reportLocusUpdate(@NonNull ComponentName activity, @UserIdInt int userId,
+ @Nullable LocusId locusId, @NonNull IBinder appToken);
+
/**
* Prepares the UsageStatsService for shutdown.
*/
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index 994c3147d70c..27b6bfb8f5fd 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -252,11 +252,23 @@ public abstract class PackageManagerInternal {
String packageName, int userId);
/**
+ * Do a straight uid lookup for the given package/application in the given user. This enforces
+ * app visibility rules and permissions. Call {@link #getPackageUidInternal} for the internal
+ * implementation.
+ * @deprecated Use {@link PackageManager#getPackageUid(String, int)}
+ * @return The app's uid, or < 0 if the package was not found in that user
+ */
+ @Deprecated
+ public abstract int getPackageUid(String packageName,
+ @PackageInfoFlags int flags, int userId);
+
+ /**
* Do a straight uid lookup for the given package/application in the given user.
* @see PackageManager#getPackageUidAsUser(String, int, int)
* @return The app's uid, or < 0 if the package was not found in that user
+ * TODO(b/148235092): rename this to getPackageUid
*/
- public abstract int getPackageUid(String packageName,
+ public abstract int getPackageUidInternal(String packageName,
@PackageInfoFlags int flags, int userId);
/**
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 1c9f5dc9c2f1..dd33566322e2 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -64,6 +64,7 @@ import android.net.CaptivePortal;
import android.net.ConnectionInfo;
import android.net.ConnectivityManager;
import android.net.ICaptivePortal;
+import android.net.IConnectivityDiagnosticsCallback;
import android.net.IConnectivityManager;
import android.net.IDnsResolver;
import android.net.IIpConnectivityMetrics;
@@ -210,6 +211,7 @@ import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
+import java.util.Collections;
import java.util.Comparator;
import java.util.ConcurrentModificationException;
import java.util.HashMap;
@@ -1622,7 +1624,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
return getNetworkCapabilitiesInternal(getNetworkAgentInfoForNetwork(network));
}
- private NetworkCapabilities networkCapabilitiesRestrictedForCallerPermissions(
+ @VisibleForTesting
+ NetworkCapabilities networkCapabilitiesRestrictedForCallerPermissions(
NetworkCapabilities nc, int callerPid, int callerUid) {
final NetworkCapabilities newNc = new NetworkCapabilities(nc);
if (!checkSettingsPermission(callerPid, callerUid)) {
@@ -1632,9 +1635,24 @@ public class ConnectivityService extends IConnectivityManager.Stub
if (newNc.getNetworkSpecifier() != null) {
newNc.setNetworkSpecifier(newNc.getNetworkSpecifier().redact());
}
+ newNc.setAdministratorUids(Collections.EMPTY_LIST);
+
+ maybeSanitizeLocationInfoForCaller(newNc, callerUid);
+
return newNc;
}
+ private void maybeSanitizeLocationInfoForCaller(
+ NetworkCapabilities nc, int callerUid) {
+ // TODO(b/142072839): Conditionally reset the owner UID if the following
+ // conditions are not met:
+ // 1. The destination app is the network owner
+ // 2. The destination app has the ACCESS_COARSE_LOCATION permission granted
+ // if target SDK<29 or otherwise has the ACCESS_FINE_LOCATION permission granted
+ // 3. The user's location toggle is on
+ nc.setOwnerUid(INVALID_UID);
+ }
+
private LinkProperties linkPropertiesRestrictedForCallerPermissions(
LinkProperties lp, int callerPid, int callerUid) {
if (lp == null) return new LinkProperties();
@@ -1662,6 +1680,10 @@ public class ConnectivityService extends IConnectivityManager.Stub
if (!checkSettingsPermission()) {
nc.setSingleUid(Binder.getCallingUid());
}
+ nc.setAdministratorUids(Collections.EMPTY_LIST);
+
+ // Clear owner UID; this can never come from an app.
+ nc.setOwnerUid(INVALID_UID);
}
private void restrictBackgroundRequestForCaller(NetworkCapabilities nc) {
@@ -5798,7 +5820,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
final Set<UidRange> ranges = nai.networkCapabilities.getUids();
- final int vpnAppUid = nai.networkCapabilities.getEstablishingVpnAppUid();
+ final int vpnAppUid = nai.networkCapabilities.getOwnerUid();
// TODO: this create a window of opportunity for apps to receive traffic between the time
// when the old rules are removed and the time when new rules are added. To fix this,
// make eBPF support two whitelisted interfaces so here new rules can be added before the
@@ -5997,7 +6019,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
if (nc == null || lp == null) return false;
return nai.isVPN()
&& !nai.networkAgentConfig.allowBypass
- && nc.getEstablishingVpnAppUid() != Process.SYSTEM_UID
+ && nc.getOwnerUid() != Process.SYSTEM_UID
&& lp.getInterfaceName() != null
&& (lp.hasIPv4DefaultRoute() || lp.hasIPv6DefaultRoute());
}
@@ -6045,12 +6067,10 @@ public class ConnectivityService extends IConnectivityManager.Stub
// TODO Fix this window by computing an accurate diff on Set<UidRange>, so the old range
// to be removed will never overlap with the new range to be added.
if (wasFiltering && !prevRanges.isEmpty()) {
- mPermissionMonitor.onVpnUidRangesRemoved(iface, prevRanges,
- prevNc.getEstablishingVpnAppUid());
+ mPermissionMonitor.onVpnUidRangesRemoved(iface, prevRanges, prevNc.getOwnerUid());
}
if (shouldFilter && !newRanges.isEmpty()) {
- mPermissionMonitor.onVpnUidRangesAdded(iface, newRanges,
- newNc.getEstablishingVpnAppUid());
+ mPermissionMonitor.onVpnUidRangesAdded(iface, newRanges, newNc.getOwnerUid());
}
} catch (Exception e) {
// Never crash!
@@ -7311,4 +7331,20 @@ public class ConnectivityService extends IConnectivityManager.Stub
return mTNS;
}
}
+
+ @Override
+ public void registerConnectivityDiagnosticsCallback(
+ @NonNull IConnectivityDiagnosticsCallback callback, @NonNull NetworkRequest request) {
+ // TODO(b/146444622): implement register IConnectivityDiagnosticsCallback functionality
+ throw new UnsupportedOperationException(
+ "registerConnectivityDiagnosticsCallback not yet implemented");
+ }
+
+ @Override
+ public void unregisterConnectivityDiagnosticsCallback(
+ @NonNull IConnectivityDiagnosticsCallback callback) {
+ // TODO(b/146444622): implement register IConnectivityDiagnosticsCallback functionality
+ throw new UnsupportedOperationException(
+ "unregisterConnectivityDiagnosticsCallback not yet implemented");
+ }
}
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index de0b6fcbd4ae..3b6ff26d5f03 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -52,6 +52,7 @@ import android.location.ILocationListener;
import android.location.ILocationManager;
import android.location.Location;
import android.location.LocationManager;
+import android.location.LocationManagerInternal;
import android.location.LocationRequest;
import android.location.LocationTime;
import android.os.Binder;
@@ -133,11 +134,12 @@ public class LocationManagerService extends ILocationManager.Stub {
*/
public static class Lifecycle extends SystemService {
- private LocationManagerService mService;
+ private final LocationManagerService mService;
public Lifecycle(Context context) {
super(context);
mService = new LocationManagerService(context);
+ LocalServices.addService(LocationManagerInternal.class, mService.new LocalService());
}
@Override
@@ -465,7 +467,7 @@ public class LocationManagerService extends ILocationManager.Stub {
mContext.sendBroadcastAsUser(intent, UserHandle.of(userId));
for (LocationProviderManager manager : mProviderManagers) {
- manager.onUseableChangedLocked(userId);
+ manager.onEnabledChangedLocked(userId);
}
}
@@ -633,10 +635,10 @@ public class LocationManagerService extends ILocationManager.Stub {
for (LocationProviderManager manager : mProviderManagers) {
// update LOCATION_PROVIDERS_ALLOWED for best effort backwards compatibility
mSettingsStore.setLocationProviderAllowed(manager.getName(),
- manager.isUseable(newUserId), newUserId);
+ manager.isEnabled(newUserId), newUserId);
- manager.onUseableChangedLocked(oldUserId);
- manager.onUseableChangedLocked(newUserId);
+ manager.onEnabledChangedLocked(oldUserId);
+ manager.onEnabledChangedLocked(newUserId);
}
}
@@ -650,22 +652,22 @@ public class LocationManagerService extends ILocationManager.Stub {
// acquiring mLock makes operations on mProvider atomic, but is otherwise unnecessary
protected final MockableLocationProvider mProvider;
- // useable state for parent user ids, no entry implies false. location state is only kept
+ // enabled state for parent user ids, no entry implies false. location state is only kept
// for parent user ids, the location state for a profile user id is assumed to be the same
// as for the parent. if querying this structure, ensure that the user id being used is a
// parent id or the results may be incorrect.
@GuardedBy("mLock")
- private final SparseArray<Boolean> mUseable;
+ private final SparseArray<Boolean> mEnabled;
private LocationProviderManager(String name) {
mName = name;
- mUseable = new SparseArray<>(1);
+ mEnabled = new SparseArray<>(1);
// initialize last since this lets our reference escape
mProvider = new MockableLocationProvider(mContext, mLock, this);
- // we can assume all users start with unuseable location state since the initial state
- // of all providers is disabled. no need to initialize mUseable further.
+ // we can assume all users start with disabled location state since the initial state
+ // of all providers is disabled. no need to initialize mEnabled further.
}
public String getName() {
@@ -693,13 +695,13 @@ public class LocationManagerService extends ILocationManager.Stub {
return mProvider.getState().properties;
}
- public void setMockProviderEnabled(boolean enabled) {
+ public void setMockProviderAllowed(boolean enabled) {
synchronized (mLock) {
if (!mProvider.isMock()) {
throw new IllegalArgumentException(mName + " provider is not a test provider");
}
- mProvider.setMockProviderEnabled(enabled);
+ mProvider.setMockProviderAllowed(enabled);
}
}
@@ -760,7 +762,7 @@ public class LocationManagerService extends ILocationManager.Stub {
return;
}
- if (!GPS_PROVIDER.equals(mName) || !isUseable()) {
+ if (!GPS_PROVIDER.equals(mName) || !isEnabled()) {
Slog.w(TAG, "reportLocationBatch() called without user permission");
return;
}
@@ -771,30 +773,33 @@ public class LocationManagerService extends ILocationManager.Stub {
@GuardedBy("mLock")
@Override
public void onStateChanged(State oldState, State newState) {
- if (oldState.enabled != newState.enabled) {
+ if (oldState.allowed != newState.allowed) {
// it would be more correct to call this for all users, but we know this can
// only affect the current user since providers are disabled for non-current
// users
- onUseableChangedLocked(mUserInfoStore.getCurrentUserId());
+ onEnabledChangedLocked(mUserInfoStore.getCurrentUserId());
}
}
- public boolean isUseable() {
- return isUseable(mUserInfoStore.getCurrentUserId());
+ public void requestSetAllowed(boolean allowed) {
+ mProvider.requestSetAllowed(allowed);
}
- public boolean isUseable(int userId) {
+ public boolean isEnabled() {
+ return isEnabled(mUserInfoStore.getCurrentUserId());
+ }
+
+ public boolean isEnabled(int userId) {
synchronized (mLock) {
// normalize user id to always refer to parent since profile state is always the
// same as parent state
userId = mUserInfoStore.getParentUserId(userId);
-
- return mUseable.get(userId, Boolean.FALSE);
+ return mEnabled.get(userId, Boolean.FALSE);
}
}
@GuardedBy("mLock")
- public void onUseableChangedLocked(int userId) {
+ public void onEnabledChangedLocked(int userId) {
if (userId == UserHandle.USER_NULL) {
// only used during initialization - we don't care about the null user
return;
@@ -804,36 +809,36 @@ public class LocationManagerService extends ILocationManager.Stub {
// as parent state
userId = mUserInfoStore.getParentUserId(userId);
- // if any property that contributes to "useability" here changes state, it MUST result
- // in a direct or indrect call to onUseableChangedLocked. this allows the provider to
+ // if any property that contributes to "enabled" here changes state, it MUST result
+ // in a direct or indrect call to onEnabledChangedLocked. this allows the provider to
// guarantee that it will always eventually reach the correct state.
- boolean useable = (userId == mUserInfoStore.getCurrentUserId())
- && mSettingsStore.isLocationEnabled(userId) && mProvider.getState().enabled;
+ boolean enabled = (userId == mUserInfoStore.getCurrentUserId())
+ && mSettingsStore.isLocationEnabled(userId) && mProvider.getState().allowed;
- if (useable == isUseable(userId)) {
+ if (enabled == isEnabled(userId)) {
return;
}
- mUseable.put(userId, useable);
+ mEnabled.put(userId, enabled);
if (D) {
- Log.d(TAG, "[u" + userId + "] " + mName + " provider useable = " + useable);
+ Log.d(TAG, "[u" + userId + "] " + mName + " provider enabled = " + enabled);
}
// fused and passive provider never get public updates for legacy reasons
if (!FUSED_PROVIDER.equals(mName) && !PASSIVE_PROVIDER.equals(mName)) {
// update LOCATION_PROVIDERS_ALLOWED for best effort backwards compatibility
- mSettingsStore.setLocationProviderAllowed(mName, useable, userId);
+ mSettingsStore.setLocationProviderAllowed(mName, enabled, userId);
Intent intent = new Intent(LocationManager.PROVIDERS_CHANGED_ACTION)
.putExtra(LocationManager.EXTRA_PROVIDER_NAME, mName)
- .putExtra(LocationManager.EXTRA_PROVIDER_ENABLED, useable)
+ .putExtra(LocationManager.EXTRA_PROVIDER_ENABLED, enabled)
.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY)
.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
mContext.sendBroadcastAsUser(intent, UserHandle.of(userId));
}
- if (!useable) {
+ if (!enabled) {
// If any provider has been disabled, clear all last locations for all
// providers. This is to be on the safe side in case a provider has location
// derived from this disabled provider.
@@ -841,7 +846,7 @@ public class LocationManagerService extends ILocationManager.Stub {
mLastLocationCoarseInterval.clear();
}
- updateProviderUseableLocked(this);
+ updateProviderEnabledLocked(this);
}
public void dump(FileDescriptor fd, IndentingPrintWriter pw, String[] args) {
@@ -854,10 +859,10 @@ public class LocationManagerService extends ILocationManager.Stub {
pw.increaseIndent();
- boolean useable = isUseable();
- pw.println("useable=" + useable);
- if (!useable) {
- pw.println("enabled=" + mProvider.getState().enabled);
+ boolean enabled = isEnabled();
+ pw.println("enabled=" + enabled);
+ if (!enabled) {
+ pw.println("allowed=" + mProvider.getState().allowed);
}
pw.println("properties=" + mProvider.getState().properties);
@@ -1009,7 +1014,7 @@ public class LocationManagerService extends ILocationManager.Stub {
if (manager == null) {
continue;
}
- if (!manager.isUseable() && !isSettingsExemptLocked(updateRecord)) {
+ if (!manager.isEnabled() && !isSettingsExemptLocked(updateRecord)) {
continue;
}
@@ -1425,7 +1430,7 @@ public class LocationManagerService extends ILocationManager.Stub {
if (FUSED_PROVIDER.equals(name)) {
continue;
}
- if (enabledOnly && !manager.isUseable()) {
+ if (enabledOnly && !manager.isEnabled()) {
continue;
}
if (criteria != null
@@ -1467,8 +1472,8 @@ public class LocationManagerService extends ILocationManager.Stub {
}
@GuardedBy("mLock")
- private void updateProviderUseableLocked(LocationProviderManager manager) {
- boolean useable = manager.isUseable();
+ private void updateProviderEnabledLocked(LocationProviderManager manager) {
+ boolean enabled = manager.isEnabled();
ArrayList<Receiver> deadReceivers = null;
@@ -1486,7 +1491,7 @@ public class LocationManagerService extends ILocationManager.Stub {
}
// Sends a notification message to the receiver
- if (!record.mReceiver.callProviderEnabledLocked(manager.getName(), useable)) {
+ if (!record.mReceiver.callProviderEnabledLocked(manager.getName(), enabled)) {
if (deadReceivers == null) {
deadReceivers = new ArrayList<>();
}
@@ -1553,7 +1558,7 @@ public class LocationManagerService extends ILocationManager.Stub {
}
final boolean isBatterySaverDisablingLocation = shouldThrottleRequests
|| (isForegroundOnlyMode && !record.mIsForegroundUid);
- if (!manager.isUseable() || isBatterySaverDisablingLocation) {
+ if (!manager.isEnabled() || isBatterySaverDisablingLocation) {
if (isSettingsExemptLocked(record)) {
providerRequest.setLocationSettingsIgnored(true);
providerRequest.setLowPowerMode(false);
@@ -1970,7 +1975,7 @@ public class LocationManagerService extends ILocationManager.Stub {
oldRecord.disposeLocked(false);
}
- if (!manager.isUseable() && !isSettingsExemptLocked(record)) {
+ if (!manager.isEnabled() && !isSettingsExemptLocked(record)) {
// Notify the listener that updates are currently disabled - but only if the request
// does not ignore location settings
receiver.callProviderEnabledLocked(name, false);
@@ -2082,7 +2087,7 @@ public class LocationManagerService extends ILocationManager.Stub {
return null;
}
- if (!manager.isUseable()) {
+ if (!manager.isEnabled()) {
return null;
}
@@ -2204,7 +2209,7 @@ public class LocationManagerService extends ILocationManager.Stub {
synchronized (mLock) {
LocationProviderManager manager = getLocationProviderManager(location.getProvider());
- if (manager == null || !manager.isUseable()) {
+ if (manager == null || !manager.isEnabled()) {
return false;
}
@@ -2491,7 +2496,7 @@ public class LocationManagerService extends ILocationManager.Stub {
synchronized (mLock) {
LocationProviderManager manager = getLocationProviderManager(providerName);
- return manager != null && manager.isUseable(userId);
+ return manager != null && manager.isEnabled(userId);
}
}
@@ -2549,8 +2554,8 @@ public class LocationManagerService extends ILocationManager.Stub {
long now = SystemClock.elapsedRealtime();
- // only update last location for locations that come from useable providers
- if (manager.isUseable()) {
+ // only update last location for locations that come from enabled providers
+ if (manager.isEnabled()) {
updateLastLocationLocked(location, manager.getName());
}
@@ -2560,7 +2565,7 @@ public class LocationManagerService extends ILocationManager.Stub {
if (lastLocationCoarseInterval == null) {
lastLocationCoarseInterval = new Location(location);
- if (manager.isUseable()) {
+ if (manager.isEnabled()) {
mLastLocationCoarseInterval.put(manager.getName(), lastLocationCoarseInterval);
}
}
@@ -2593,7 +2598,7 @@ public class LocationManagerService extends ILocationManager.Stub {
Receiver receiver = r.mReceiver;
boolean receiverDead = false;
- if (!manager.isUseable() && !isSettingsExemptLocked(r)) {
+ if (!manager.isEnabled() && !isSettingsExemptLocked(r)) {
continue;
}
@@ -2812,7 +2817,7 @@ public class LocationManagerService extends ILocationManager.Stub {
throw new IllegalArgumentException("provider doesn't exist: " + provider);
}
- manager.setMockProviderEnabled(enabled);
+ manager.setMockProviderAllowed(enabled);
}
@Override
@@ -2946,4 +2951,19 @@ public class LocationManagerService extends ILocationManager.Stub {
}
}
}
+
+ private class LocalService extends LocationManagerInternal {
+
+ @Override
+ public void requestSetProviderAllowed(String provider, boolean allowed) {
+ Preconditions.checkArgument(provider != null, "invalid null provider");
+
+ synchronized (mLock) {
+ LocationProviderManager manager = getLocationProviderManager(provider);
+ if (manager != null) {
+ manager.requestSetAllowed(allowed);
+ }
+ }
+ }
+ }
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 5596b2fcb762..89f33b9e599a 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -199,6 +199,7 @@ import android.content.IIntentReceiver;
import android.content.IIntentSender;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.LocusId;
import android.content.pm.ActivityInfo;
import android.content.pm.ActivityPresentationInfo;
import android.content.pm.ApplicationInfo;
@@ -19088,6 +19089,20 @@ public class ActivityManagerService extends IActivityManager.Stub
public void unregisterProcessObserver(IProcessObserver processObserver) {
ActivityManagerService.this.unregisterProcessObserver(processObserver);
}
+
+ @Override
+ public boolean isUidCurrentlyInstrumented(int uid) {
+ synchronized (ActivityManagerService.this) {
+ for (int i = mActiveInstrumentation.size() - 1; i >= 0; i--) {
+ ActiveInstrumentation activeInst = mActiveInstrumentation.get(i);
+ if (!activeInst.mFinished && activeInst.mTargetInfo != null
+ && activeInst.mTargetInfo.uid == uid) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
}
long inputDispatchingTimedOut(int pid, final boolean aboveSystem, String reason) {
@@ -19642,4 +19657,19 @@ public class ActivityManagerService extends IActivityManager.Stub
}
}
}
+
+ @Override
+ public void setActivityLocusContext(ComponentName activity, LocusId locusId, IBinder appToken) {
+ final int callingUid = Binder.getCallingUid();
+ final int userId = UserHandle.getCallingUserId();
+ if (getPackageManagerInternalLocked().getPackageUid(activity.getPackageName(),
+ /*flags=*/ 0, userId) != callingUid) {
+ throw new SecurityException("Calling uid " + callingUid + " cannot set locusId"
+ + "for package " + activity.getPackageName());
+ }
+
+ if (mUsageStatsService != null) {
+ mUsageStatsService.reportLocusUpdate(activity, userId, locusId, appToken);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index afc3d918073e..c5d6bba27234 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -1689,6 +1689,9 @@ class UserController implements Handler.Callback {
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
| Intent.FLAG_RECEIVER_FOREGROUND);
intent.putExtra(Intent.EXTRA_USER_HANDLE, profileUserId);
+ // Also, add the UserHandle for mainline modules which can't use the @hide
+ // EXTRA_USER_HANDLE.
+ intent.putExtra(Intent.EXTRA_USER, UserHandle.of(profileUserId));
mInjector.broadcastIntent(intent,
null, null, 0, null, null, null, AppOpsManager.OP_NONE,
null, false, false, MY_PID, SYSTEM_UID, callingUid, callingPid,
@@ -1705,6 +1708,9 @@ class UserController implements Handler.Callback {
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
| Intent.FLAG_RECEIVER_FOREGROUND);
intent.putExtra(Intent.EXTRA_USER_HANDLE, profileUserId);
+ // Also, add the UserHandle for mainline modules which can't use the @hide
+ // EXTRA_USER_HANDLE.
+ intent.putExtra(Intent.EXTRA_USER, UserHandle.of(profileUserId));
mInjector.broadcastIntent(intent,
null, null, 0, null, null, null, AppOpsManager.OP_NONE,
null, false, false, MY_PID, SYSTEM_UID, callingUid, callingPid,
@@ -1714,6 +1720,9 @@ class UserController implements Handler.Callback {
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
| Intent.FLAG_RECEIVER_FOREGROUND);
intent.putExtra(Intent.EXTRA_USER_HANDLE, newUserId);
+ // Also, add the UserHandle for mainline modules which can't use the @hide
+ // EXTRA_USER_HANDLE.
+ intent.putExtra(Intent.EXTRA_USER, UserHandle.of(newUserId));
mInjector.broadcastIntent(intent,
null, null, 0, null, null,
new String[] {android.Manifest.permission.MANAGE_USERS},
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 08136a3cb9e5..581fb4e75d0e 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -50,12 +50,14 @@ import static android.app.AppOpsManager.resolveFirstUnrestrictedUidState;
import static android.content.Intent.ACTION_PACKAGE_REMOVED;
import static android.content.Intent.EXTRA_REPLACING;
import static android.content.pm.PermissionInfo.PROTECTION_DANGEROUS;
+import static android.os.Process.STATSD_UID;
import android.Manifest;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
+import android.app.ActivityManagerInternal;
import android.app.ActivityThread;
import android.app.AppGlobals;
import android.app.AppOpsManager;
@@ -1760,6 +1762,15 @@ public class AppOpsService extends IAppOpsService.Stub {
beginTimeMillis, endTimeMillis, flags);
Objects.requireNonNull(callback, "callback cannot be null");
+ ActivityManagerInternal ami = LocalServices.getService(ActivityManagerInternal.class);
+ boolean isCallerInstrumented = ami.isUidCurrentlyInstrumented(Binder.getCallingUid());
+ boolean isCallerStatsCollector = Binder.getCallingUid() == STATSD_UID;
+
+ if (!isCallerStatsCollector && !isCallerInstrumented) {
+ mHandler.post(() -> callback.sendResult(new Bundle()));
+ return;
+ }
+
mContext.enforcePermission(android.Manifest.permission.GET_APP_OPS_STATS,
Binder.getCallingPid(), Binder.getCallingUid(), "getHistoricalOps");
@@ -4475,8 +4486,6 @@ public class AppOpsService extends IAppOpsService.Stub {
pw.println(" Limit output to data associated with the given feature id.");
pw.println(" --watchers");
pw.println(" Only output the watcher sections.");
- pw.println(" --history");
- pw.println(" Output the historical data.");
}
private void dumpStatesLocked(@NonNull PrintWriter pw, @Nullable String filterFeatureId,
@@ -4609,6 +4618,7 @@ public class AppOpsService extends IAppOpsService.Stub {
int dumpUid = Process.INVALID_UID;
int dumpMode = -1;
boolean dumpWatchers = false;
+ // TODO ntmyren: Remove the dumpHistory and dumpFilter
boolean dumpHistory = false;
@HistoricalOpsRequestFilter int dumpFilter = 0;
@@ -4671,8 +4681,6 @@ public class AppOpsService extends IAppOpsService.Stub {
}
} else if ("--watchers".equals(arg)) {
dumpWatchers = true;
- } else if ("--history".equals(arg)) {
- dumpHistory = true;
} else if (arg.length() > 0 && arg.charAt(0) == '-'){
pw.println("Unknown option: " + arg);
return;
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 0c9abae2e0fc..423e0212dfd6 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -954,7 +954,7 @@ public class Vpn {
NetworkAgentConfig networkAgentConfig = new NetworkAgentConfig();
networkAgentConfig.allowBypass = mConfig.allowBypass && !mLockdown;
- mNetworkCapabilities.setEstablishingVpnAppUid(Binder.getCallingUid());
+ mNetworkCapabilities.setOwnerUid(Binder.getCallingUid());
mNetworkCapabilities.setUids(createUserAndRestrictedProfilesRanges(mUserHandle,
mConfig.allowedApplications, mConfig.disallowedApplications));
long token = Binder.clearCallingIdentity();
diff --git a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
index 2926ec94417f..214dcc0220ff 100644
--- a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
+++ b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
@@ -17,16 +17,15 @@
package com.android.server.integrity;
import static android.content.Intent.ACTION_PACKAGE_NEEDS_INTEGRITY_VERIFICATION;
+import static android.content.Intent.EXTRA_LONG_VERSION_CODE;
import static android.content.Intent.EXTRA_ORIGINATING_UID;
import static android.content.Intent.EXTRA_PACKAGE_NAME;
-import static android.content.Intent.EXTRA_VERSION_CODE;
import static android.content.integrity.AppIntegrityManager.EXTRA_STATUS;
import static android.content.integrity.AppIntegrityManager.STATUS_FAILURE;
import static android.content.integrity.AppIntegrityManager.STATUS_SUCCESS;
+import static android.content.integrity.IntegrityUtils.getHexDigest;
import static android.content.pm.PackageManager.EXTRA_VERIFICATION_ID;
-import static com.android.server.integrity.IntegrityUtils.getHexDigest;
-
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.BroadcastReceiver;
@@ -49,6 +48,7 @@ import android.os.Handler;
import android.os.HandlerThread;
import android.os.RemoteException;
import android.util.Slog;
+import android.util.StatsLog;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
@@ -74,18 +74,29 @@ import java.util.Map;
/** Implementation of {@link AppIntegrityManagerService}. */
public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub {
+ /**
+ * This string will be used as the "installer" for formula evaluation when the app's installer
+ * cannot be determined.
+ *
+ * <p>This may happen for various reasons. e.g., the installing app's package name may not match
+ * its UID.
+ */
+ private static final String UNKNOWN_INSTALLER = "";
+ /**
+ * This string will be used as the "installer" for formula evaluation when the app is being
+ * installed via ADB.
+ */
+ private static final String ADB_INSTALLER = "adb";
+
private static final String TAG = "AppIntegrityManagerServiceImpl";
private static final String PACKAGE_MIME_TYPE = "application/vnd.android.package-archive";
- private static final char[] HEX_CHARS = "0123456789ABCDEF".toCharArray();
private static final String PACKAGE_INSTALLER = "com.google.android.packageinstaller";
private static final String BASE_APK_FILE = "base.apk";
private static final String ALLOWED_INSTALLERS_METADATA_NAME = "allowed-installers";
private static final String ALLOWED_INSTALLER_DELIMITER = ",";
private static final String INSTALLER_PACKAGE_CERT_DELIMITER = "\\|";
- private static final String ADB_INSTALLER = "adb";
- private static final String UNKNOWN_INSTALLER = "";
private static final String INSTALLER_CERT_NOT_APPLICABLE = "";
// Access to files inside mRulesDir is protected by mRulesLock;
@@ -161,6 +172,8 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub {
success = false;
}
+ StatsLog.write(StatsLog.INTEGRITY_RULES_PUSHED, success, ruleProvider, version);
+
Intent intent = new Intent();
intent.putExtra(EXTRA_STATUS, success ? STATUS_SUCCESS : STATUS_FAILURE);
try {
@@ -238,7 +251,7 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub {
builder.setPackageName(getPackageNameNormalized(packageName));
builder.setAppCertificate(appCert == null ? "" : appCert);
- builder.setVersionCode(intent.getIntExtra(EXTRA_VERSION_CODE, -1));
+ builder.setVersionCode(intent.getLongExtra(EXTRA_LONG_VERSION_CODE, -1));
builder.setInstallerName(getPackageNameNormalized(installerPackageName));
builder.setInstallerCertificate(
getInstallerCertificateFingerprint(installerPackageName));
@@ -258,6 +271,16 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub {
+ result.getEffect()
+ " due to "
+ result.getRule());
+
+ StatsLog.write(
+ StatsLog.INTEGRITY_CHECK_RESULT_REPORTED,
+ packageName,
+ appCert,
+ appInstallMetadata.getVersionCode(),
+ installerPackageName,
+ getLoggingResponse(result),
+ isCausedByAppCertRule(result),
+ isCausedByInstallerRule(result));
mPackageManagerInternal.setIntegrityVerificationResult(
verificationId,
result.getEffect() == IntegrityCheckResult.Effect.ALLOW
@@ -570,6 +593,26 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub {
}
}
+ private static int getLoggingResponse(IntegrityCheckResult result) {
+ if (result.getEffect() == IntegrityCheckResult.Effect.DENY) {
+ return StatsLog.INTEGRITY_CHECK_RESULT_REPORTED__RESPONSE__REJECTED;
+ } else if (result.getRule() != null) {
+ return StatsLog.INTEGRITY_CHECK_RESULT_REPORTED__RESPONSE__FORCE_ALLOWED;
+ } else {
+ return StatsLog.INTEGRITY_CHECK_RESULT_REPORTED__RESPONSE__ALLOWED;
+ }
+ }
+
+ private static boolean isCausedByAppCertRule(IntegrityCheckResult result) {
+ // TODO(b/147095027): implement this.
+ return true;
+ }
+
+ private static boolean isCausedByInstallerRule(IntegrityCheckResult result) {
+ // TODO(b/147095027): implement this.
+ return true;
+ }
+
private List<String> getAllowedRuleProviders() {
return Arrays.asList(mContext.getResources().getStringArray(
R.array.config_integrityRuleProviderPackages));
diff --git a/services/core/java/com/android/server/integrity/engine/RuleEvaluationEngine.java b/services/core/java/com/android/server/integrity/engine/RuleEvaluationEngine.java
index 07eacbfd87dd..1a6b3d8b8411 100644
--- a/services/core/java/com/android/server/integrity/engine/RuleEvaluationEngine.java
+++ b/services/core/java/com/android/server/integrity/engine/RuleEvaluationEngine.java
@@ -19,7 +19,7 @@ package com.android.server.integrity.engine;
import android.content.integrity.AppInstallMetadata;
import android.content.integrity.AtomicFormula;
import android.content.integrity.CompoundFormula;
-import android.content.integrity.Formula;
+import android.content.integrity.IntegrityFormula;
import android.content.integrity.Rule;
import android.util.Slog;
@@ -65,7 +65,7 @@ public class RuleEvaluationEngine {
* Load, and match the list of rules against an app install metadata.
*
* @param appInstallMetadata Metadata of the app to be installed, and to evaluate the rules
- * against.
+ * against.
* @return result of the integrity check
*/
public IntegrityCheckResult evaluate(
@@ -89,14 +89,14 @@ public class RuleEvaluationEngine {
return Optional.empty();
}
- List<Formula> formulas = new ArrayList<>(allowedInstallers.size());
+ List<IntegrityFormula> formulas = new ArrayList<>(allowedInstallers.size());
allowedInstallers.forEach(
(installer, cert) -> {
formulas.add(allowedInstallerFormula(installer, cert));
});
// We need this special case since OR-formulas require at least two operands.
- Formula allInstallersFormula =
+ IntegrityFormula allInstallersFormula =
formulas.size() == 1
? formulas.get(0)
: new CompoundFormula(CompoundFormula.OR, formulas);
@@ -108,7 +108,7 @@ public class RuleEvaluationEngine {
Rule.DENY));
}
- private static Formula allowedInstallerFormula(String installer, String cert) {
+ private static IntegrityFormula allowedInstallerFormula(String installer, String cert) {
return new CompoundFormula(
CompoundFormula.AND,
Arrays.asList(
@@ -117,8 +117,7 @@ public class RuleEvaluationEngine {
installer,
/* isHashedValue= */ false),
new AtomicFormula.StringAtomicFormula(
- AtomicFormula.INSTALLER_CERTIFICATE,
- cert,
- /* isHashedValue= */ false)));
+ AtomicFormula.INSTALLER_CERTIFICATE, cert, /* isHashedValue= */
+ false)));
}
}
diff --git a/services/core/java/com/android/server/integrity/engine/RuleEvaluator.java b/services/core/java/com/android/server/integrity/engine/RuleEvaluator.java
index b1c20d27c792..66537ff6105e 100644
--- a/services/core/java/com/android/server/integrity/engine/RuleEvaluator.java
+++ b/services/core/java/com/android/server/integrity/engine/RuleEvaluator.java
@@ -22,7 +22,6 @@ import static android.content.integrity.Rule.FORCE_ALLOW;
import android.annotation.NonNull;
import android.content.integrity.AppInstallMetadata;
import android.content.integrity.Rule;
-import android.util.Slog;
import com.android.server.integrity.model.IntegrityCheckResult;
@@ -35,8 +34,6 @@ import java.util.List;
*/
final class RuleEvaluator {
- private static final String TAG = "RuleEvaluator";
-
/**
* Match the list of rules against an app install metadata.
*
@@ -53,7 +50,7 @@ final class RuleEvaluator {
List<Rule> rules, AppInstallMetadata appInstallMetadata) {
List<Rule> matchedRules = new ArrayList<>();
for (Rule rule : rules) {
- if (rule.getFormula().isSatisfied(appInstallMetadata)) {
+ if (rule.getFormula().matches(appInstallMetadata)) {
matchedRules.add(rule);
}
}
@@ -71,8 +68,7 @@ final class RuleEvaluator {
case FORCE_ALLOW:
return IntegrityCheckResult.allow(rule);
default:
- Slog.e(TAG, "Matched an unknown effect rule: " + rule);
- return IntegrityCheckResult.allow();
+ throw new IllegalArgumentException("Matched an unknown effect rule: " + rule);
}
}
return denied ? IntegrityCheckResult.deny(denyRule) : IntegrityCheckResult.allow();
diff --git a/services/core/java/com/android/server/integrity/model/BitOutputStream.java b/services/core/java/com/android/server/integrity/model/BitOutputStream.java
index 7d1bb3fb8203..14b35fd016eb 100644
--- a/services/core/java/com/android/server/integrity/model/BitOutputStream.java
+++ b/services/core/java/com/android/server/integrity/model/BitOutputStream.java
@@ -61,7 +61,7 @@ public class BitOutputStream {
/**
* Set the next bit in the stream to value.
*
- * @param value The value to set the bit to.
+ * @param value The value to set the bit to
*/
public void setNext(boolean value) throws IOException {
int byteToWrite = mNextBitIndex / BYTE_BITS;
diff --git a/services/core/java/com/android/server/integrity/parser/BinaryFileOperations.java b/services/core/java/com/android/server/integrity/parser/BinaryFileOperations.java
index 2c5b7d3c122c..f09e035ecc78 100644
--- a/services/core/java/com/android/server/integrity/parser/BinaryFileOperations.java
+++ b/services/core/java/com/android/server/integrity/parser/BinaryFileOperations.java
@@ -19,7 +19,8 @@ package com.android.server.integrity.parser;
import static com.android.server.integrity.model.ComponentBitSize.IS_HASHED_BITS;
import static com.android.server.integrity.model.ComponentBitSize.VALUE_SIZE_BITS;
-import com.android.server.integrity.IntegrityUtils;
+import android.content.integrity.IntegrityUtils;
+
import com.android.server.integrity.model.BitInputStream;
import java.io.IOException;
diff --git a/services/core/java/com/android/server/integrity/parser/RuleBinaryParser.java b/services/core/java/com/android/server/integrity/parser/RuleBinaryParser.java
index 90954ff21d57..4b8efafcb6b0 100644
--- a/services/core/java/com/android/server/integrity/parser/RuleBinaryParser.java
+++ b/services/core/java/com/android/server/integrity/parser/RuleBinaryParser.java
@@ -35,7 +35,7 @@ import static com.android.server.integrity.parser.BinaryFileOperations.getString
import android.content.integrity.AtomicFormula;
import android.content.integrity.CompoundFormula;
-import android.content.integrity.Formula;
+import android.content.integrity.IntegrityFormula;
import android.content.integrity.Rule;
import com.android.server.integrity.model.BitInputStream;
@@ -121,7 +121,7 @@ public class RuleBinaryParser implements RuleParser {
}
private Rule parseRule(BitInputStream bitInputStream) throws IOException {
- Formula formula = parseFormula(bitInputStream);
+ IntegrityFormula formula = parseFormula(bitInputStream);
int effect = bitInputStream.getNext(EFFECT_BITS);
if (bitInputStream.getNext(SIGNAL_BIT) != 1) {
@@ -131,7 +131,7 @@ public class RuleBinaryParser implements RuleParser {
return new Rule(formula, effect);
}
- private Formula parseFormula(BitInputStream bitInputStream) throws IOException {
+ private IntegrityFormula parseFormula(BitInputStream bitInputStream) throws IOException {
int separator = bitInputStream.getNext(SEPARATOR_BITS);
switch (separator) {
case ATOMIC_FORMULA_START:
@@ -148,9 +148,9 @@ public class RuleBinaryParser implements RuleParser {
private CompoundFormula parseCompoundFormula(BitInputStream bitInputStream) throws IOException {
int connector = bitInputStream.getNext(CONNECTOR_BITS);
- List<Formula> formulas = new ArrayList<>();
+ List<IntegrityFormula> formulas = new ArrayList<>();
- Formula parsedFormula = parseFormula(bitInputStream);
+ IntegrityFormula parsedFormula = parseFormula(bitInputStream);
while (parsedFormula != null) {
formulas.add(parsedFormula);
parsedFormula = parseFormula(bitInputStream);
@@ -173,8 +173,11 @@ public class RuleBinaryParser implements RuleParser {
String stringValue = getStringValue(bitInputStream, valueSize, isHashedValue);
return new AtomicFormula.StringAtomicFormula(key, stringValue, isHashedValue);
case AtomicFormula.VERSION_CODE:
- int intValue = getIntValue(bitInputStream);
- return new AtomicFormula.IntAtomicFormula(key, operator, intValue);
+ // TODO(b/147880712): temporary hack until our input handles long
+ long upper = getIntValue(bitInputStream);
+ long lower = getIntValue(bitInputStream);
+ long longValue = (upper << 32) | lower;
+ return new AtomicFormula.LongAtomicFormula(key, operator, longValue);
case AtomicFormula.PRE_INSTALLED:
boolean booleanValue = getBooleanValue(bitInputStream);
return new AtomicFormula.BooleanAtomicFormula(key, booleanValue);
diff --git a/services/core/java/com/android/server/integrity/parser/RuleXmlParser.java b/services/core/java/com/android/server/integrity/parser/RuleXmlParser.java
index 53b0c2e59453..f37ca1efce13 100644
--- a/services/core/java/com/android/server/integrity/parser/RuleXmlParser.java
+++ b/services/core/java/com/android/server/integrity/parser/RuleXmlParser.java
@@ -18,7 +18,7 @@ package com.android.server.integrity.parser;
import android.content.integrity.AtomicFormula;
import android.content.integrity.CompoundFormula;
-import android.content.integrity.Formula;
+import android.content.integrity.IntegrityFormula;
import android.content.integrity.Rule;
import android.util.Xml;
@@ -107,7 +107,7 @@ public final class RuleXmlParser implements RuleParser {
}
private static Rule parseRule(XmlPullParser parser) throws IOException, XmlPullParserException {
- Formula formula = null;
+ IntegrityFormula formula = null;
int effect = Integer.parseInt(extractAttributeValue(parser, EFFECT_ATTRIBUTE).orElse("-1"));
int eventType;
@@ -139,11 +139,11 @@ public final class RuleXmlParser implements RuleParser {
return new Rule(formula, effect);
}
- private static Formula parseCompoundFormula(XmlPullParser parser)
+ private static IntegrityFormula parseCompoundFormula(XmlPullParser parser)
throws IOException, XmlPullParserException {
int connector =
Integer.parseInt(extractAttributeValue(parser, CONNECTOR_ATTRIBUTE).orElse("-1"));
- List<Formula> formulas = new ArrayList<>();
+ List<IntegrityFormula> formulas = new ArrayList<>();
int eventType;
while ((eventType = parser.next()) != XmlPullParser.END_DOCUMENT) {
@@ -175,7 +175,7 @@ public final class RuleXmlParser implements RuleParser {
return new CompoundFormula(connector, formulas);
}
- private static Formula parseAtomicFormula(XmlPullParser parser)
+ private static IntegrityFormula parseAtomicFormula(XmlPullParser parser)
throws IOException, XmlPullParserException {
int key = Integer.parseInt(extractAttributeValue(parser, KEY_ATTRIBUTE).orElse("-1"));
int operator =
@@ -193,7 +193,7 @@ public final class RuleXmlParser implements RuleParser {
return constructAtomicFormulaBasedOnKey(key, operator, value, isHashedValue);
}
- private static Formula constructAtomicFormulaBasedOnKey(
+ private static IntegrityFormula constructAtomicFormulaBasedOnKey(
@AtomicFormula.Key int key,
@AtomicFormula.Operator int operator,
String value,
@@ -208,7 +208,7 @@ public final class RuleXmlParser implements RuleParser {
case AtomicFormula.PRE_INSTALLED:
return new AtomicFormula.BooleanAtomicFormula(key, Boolean.parseBoolean(value));
case AtomicFormula.VERSION_CODE:
- return new AtomicFormula.IntAtomicFormula(key, operator, Integer.parseInt(value));
+ return new AtomicFormula.LongAtomicFormula(key, operator, Integer.parseInt(value));
default:
throw new RuntimeException(String.format("Found unexpected key: %d", key));
}
diff --git a/services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java b/services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java
index f5ed975bf772..d01499668e8c 100644
--- a/services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java
+++ b/services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java
@@ -36,11 +36,11 @@ import static com.android.server.integrity.serializer.RuleIndexingDetails.PACKAG
import android.content.integrity.AtomicFormula;
import android.content.integrity.CompoundFormula;
-import android.content.integrity.Formula;
+import android.content.integrity.IntegrityFormula;
+import android.content.integrity.IntegrityUtils;
import android.content.integrity.Rule;
import com.android.internal.util.Preconditions;
-import com.android.server.integrity.IntegrityUtils;
import com.android.server.integrity.model.BitOutputStream;
import com.android.server.integrity.model.ByteTrackedOutputStream;
@@ -192,7 +192,7 @@ public class RuleBinarySerializer implements RuleSerializer {
bitOutputStream.setNext();
}
- private void serializeFormula(Formula formula, BitOutputStream bitOutputStream)
+ private void serializeFormula(IntegrityFormula formula, BitOutputStream bitOutputStream)
throws IOException {
if (formula instanceof AtomicFormula) {
serializeAtomicFormula((AtomicFormula) formula, bitOutputStream);
@@ -212,7 +212,7 @@ public class RuleBinarySerializer implements RuleSerializer {
bitOutputStream.setNext(SEPARATOR_BITS, COMPOUND_FORMULA_START);
bitOutputStream.setNext(CONNECTOR_BITS, compoundFormula.getConnector());
- for (Formula formula : compoundFormula.getFormulas()) {
+ for (IntegrityFormula formula : compoundFormula.getFormulas()) {
serializeFormula(formula, bitOutputStream);
}
bitOutputStream.setNext(SEPARATOR_BITS, COMPOUND_FORMULA_END);
@@ -234,11 +234,14 @@ public class RuleBinarySerializer implements RuleSerializer {
stringAtomicFormula.getValue(),
stringAtomicFormula.getIsHashedValue(),
bitOutputStream);
- } else if (atomicFormula.getTag() == AtomicFormula.INT_ATOMIC_FORMULA_TAG) {
- AtomicFormula.IntAtomicFormula intAtomicFormula =
- (AtomicFormula.IntAtomicFormula) atomicFormula;
- bitOutputStream.setNext(OPERATOR_BITS, intAtomicFormula.getOperator());
- serializeIntValue(intAtomicFormula.getValue(), bitOutputStream);
+ } else if (atomicFormula.getTag() == AtomicFormula.LONG_ATOMIC_FORMULA_TAG) {
+ AtomicFormula.LongAtomicFormula longAtomicFormula =
+ (AtomicFormula.LongAtomicFormula) atomicFormula;
+ bitOutputStream.setNext(OPERATOR_BITS, longAtomicFormula.getOperator());
+ // TODO(b/147880712): Temporary hack until we support long values in bitOutputStream
+ long value = longAtomicFormula.getValue();
+ serializeIntValue((int) (value >>> 32), bitOutputStream);
+ serializeIntValue((int) value, bitOutputStream);
} else if (atomicFormula.getTag() == AtomicFormula.BOOLEAN_ATOMIC_FORMULA_TAG) {
AtomicFormula.BooleanAtomicFormula booleanAtomicFormula =
(AtomicFormula.BooleanAtomicFormula) atomicFormula;
diff --git a/services/core/java/com/android/server/integrity/serializer/RuleIndexingDetailsIdentifier.java b/services/core/java/com/android/server/integrity/serializer/RuleIndexingDetailsIdentifier.java
index 7d9a90188983..6f7d172aabcc 100644
--- a/services/core/java/com/android/server/integrity/serializer/RuleIndexingDetailsIdentifier.java
+++ b/services/core/java/com/android/server/integrity/serializer/RuleIndexingDetailsIdentifier.java
@@ -22,7 +22,7 @@ import static com.android.server.integrity.serializer.RuleIndexingDetails.PACKAG
import android.content.integrity.AtomicFormula;
import android.content.integrity.CompoundFormula;
-import android.content.integrity.Formula;
+import android.content.integrity.IntegrityFormula;
import android.content.integrity.Rule;
import java.util.ArrayList;
@@ -76,15 +76,15 @@ class RuleIndexingDetailsIdentifier {
return typeOrganizedRuleMap;
}
- private static RuleIndexingDetails getIndexingDetails(Formula formula) {
+ private static RuleIndexingDetails getIndexingDetails(IntegrityFormula formula) {
switch (formula.getTag()) {
- case Formula.COMPOUND_FORMULA_TAG:
+ case IntegrityFormula.COMPOUND_FORMULA_TAG:
return getIndexingDetailsForCompoundFormula((CompoundFormula) formula);
- case Formula.STRING_ATOMIC_FORMULA_TAG:
+ case IntegrityFormula.STRING_ATOMIC_FORMULA_TAG:
return getIndexingDetailsForStringAtomicFormula(
(AtomicFormula.StringAtomicFormula) formula);
- case Formula.INT_ATOMIC_FORMULA_TAG:
- case Formula.BOOLEAN_ATOMIC_FORMULA_TAG:
+ case IntegrityFormula.LONG_ATOMIC_FORMULA_TAG:
+ case IntegrityFormula.BOOLEAN_ATOMIC_FORMULA_TAG:
// Package name and app certificate related formulas are string atomic formulas.
return new RuleIndexingDetails(NOT_INDEXED);
default:
@@ -96,7 +96,7 @@ class RuleIndexingDetailsIdentifier {
private static RuleIndexingDetails getIndexingDetailsForCompoundFormula(
CompoundFormula compoundFormula) {
int connector = compoundFormula.getConnector();
- List<Formula> formulas = compoundFormula.getFormulas();
+ List<IntegrityFormula> formulas = compoundFormula.getFormulas();
switch (connector) {
case CompoundFormula.AND:
diff --git a/services/core/java/com/android/server/integrity/serializer/RuleXmlSerializer.java b/services/core/java/com/android/server/integrity/serializer/RuleXmlSerializer.java
index 8f164e645434..6e1218064096 100644
--- a/services/core/java/com/android/server/integrity/serializer/RuleXmlSerializer.java
+++ b/services/core/java/com/android/server/integrity/serializer/RuleXmlSerializer.java
@@ -22,7 +22,7 @@ import static com.android.server.integrity.serializer.RuleIndexingDetails.PACKAG
import android.content.integrity.AtomicFormula;
import android.content.integrity.CompoundFormula;
-import android.content.integrity.Formula;
+import android.content.integrity.IntegrityFormula;
import android.content.integrity.Rule;
import android.util.Xml;
@@ -128,7 +128,8 @@ public class RuleXmlSerializer implements RuleSerializer {
xmlSerializer.endTag(NAMESPACE, RULE_TAG);
}
- private void serializeFormula(Formula formula, XmlSerializer xmlSerializer) throws IOException {
+ private void serializeFormula(IntegrityFormula formula, XmlSerializer xmlSerializer)
+ throws IOException {
if (formula instanceof AtomicFormula) {
serializeAtomicFormula((AtomicFormula) formula, xmlSerializer);
} else if (formula instanceof CompoundFormula) {
@@ -147,7 +148,7 @@ public class RuleXmlSerializer implements RuleSerializer {
xmlSerializer.startTag(NAMESPACE, COMPOUND_FORMULA_TAG);
serializeAttributeValue(
CONNECTOR_ATTRIBUTE, String.valueOf(compoundFormula.getConnector()), xmlSerializer);
- for (Formula formula : compoundFormula.getFormulas()) {
+ for (IntegrityFormula formula : compoundFormula.getFormulas()) {
serializeFormula(formula, xmlSerializer);
}
xmlSerializer.endTag(NAMESPACE, COMPOUND_FORMULA_TAG);
@@ -171,14 +172,14 @@ public class RuleXmlSerializer implements RuleSerializer {
String.valueOf(
((AtomicFormula.StringAtomicFormula) atomicFormula).getIsHashedValue()),
xmlSerializer);
- } else if (atomicFormula.getTag() == AtomicFormula.INT_ATOMIC_FORMULA_TAG) {
+ } else if (atomicFormula.getTag() == AtomicFormula.LONG_ATOMIC_FORMULA_TAG) {
serializeAttributeValue(
OPERATOR_ATTRIBUTE,
- String.valueOf(((AtomicFormula.IntAtomicFormula) atomicFormula).getOperator()),
+ String.valueOf(((AtomicFormula.LongAtomicFormula) atomicFormula).getOperator()),
xmlSerializer);
serializeAttributeValue(
VALUE_ATTRIBUTE,
- String.valueOf(((AtomicFormula.IntAtomicFormula) atomicFormula).getValue()),
+ String.valueOf(((AtomicFormula.LongAtomicFormula) atomicFormula).getValue()),
xmlSerializer);
} else if (atomicFormula.getTag() == AtomicFormula.BOOLEAN_ATOMIC_FORMULA_TAG) {
serializeAttributeValue(
diff --git a/services/core/java/com/android/server/location/AbstractLocationProvider.java b/services/core/java/com/android/server/location/AbstractLocationProvider.java
index ed6a759409d4..5afa48a2b34d 100644
--- a/services/core/java/com/android/server/location/AbstractLocationProvider.java
+++ b/services/core/java/com/android/server/location/AbstractLocationProvider.java
@@ -79,9 +79,9 @@ public abstract class AbstractLocationProvider {
Collections.emptySet());
/**
- * The provider's enabled state.
+ * The provider's allowed state.
*/
- public final boolean enabled;
+ public final boolean allowed;
/**
* The provider's properties.
@@ -93,18 +93,18 @@ public abstract class AbstractLocationProvider {
*/
public final Set<String> providerPackageNames;
- private State(boolean enabled, ProviderProperties properties,
+ private State(boolean allowed, ProviderProperties properties,
Set<String> providerPackageNames) {
- this.enabled = enabled;
+ this.allowed = allowed;
this.properties = properties;
this.providerPackageNames = Objects.requireNonNull(providerPackageNames);
}
- private State withEnabled(boolean enabled) {
- if (enabled == this.enabled) {
+ private State withAllowed(boolean allowed) {
+ if (allowed == this.allowed) {
return this;
} else {
- return new State(enabled, properties, providerPackageNames);
+ return new State(allowed, properties, providerPackageNames);
}
}
@@ -112,7 +112,7 @@ public abstract class AbstractLocationProvider {
if (properties.equals(this.properties)) {
return this;
} else {
- return new State(enabled, properties, providerPackageNames);
+ return new State(allowed, properties, providerPackageNames);
}
}
@@ -120,7 +120,7 @@ public abstract class AbstractLocationProvider {
if (providerPackageNames.equals(this.providerPackageNames)) {
return this;
} else {
- return new State(enabled, properties, providerPackageNames);
+ return new State(allowed, properties, providerPackageNames);
}
}
@@ -133,13 +133,13 @@ public abstract class AbstractLocationProvider {
return false;
}
State state = (State) o;
- return enabled == state.enabled && properties == state.properties
+ return allowed == state.allowed && properties == state.properties
&& providerPackageNames.equals(state.providerPackageNames);
}
@Override
public int hashCode() {
- return Objects.hash(enabled, properties, providerPackageNames);
+ return Objects.hash(allowed, properties, providerPackageNames);
}
}
@@ -259,10 +259,10 @@ public abstract class AbstractLocationProvider {
}
/**
- * The current enabled state of this provider.
+ * The current allowed state of this provider.
*/
- protected boolean isEnabled() {
- return mInternalState.get().state.enabled;
+ protected boolean isAllowed() {
+ return mInternalState.get().state.allowed;
}
/**
@@ -281,10 +281,10 @@ public abstract class AbstractLocationProvider {
}
/**
- * Call this method to report a change in provider enabled/disabled status.
+ * Call this method to report a change in provider allowed status.
*/
- protected void setEnabled(boolean enabled) {
- setState(state -> state.withEnabled(enabled));
+ protected void setAllowed(boolean allowed) {
+ setState(state -> state.withAllowed(allowed));
}
/**
@@ -358,6 +358,19 @@ public abstract class AbstractLocationProvider {
protected void onExtraCommand(int uid, int pid, String command, Bundle extras) {}
/**
+ * Requests a provider to enable itself for the given user id.
+ */
+ public final void requestSetAllowed(boolean allowed) {
+ // all calls into the provider must be moved onto the provider thread to prevent deadlock
+ mExecutor.execute(() -> onRequestSetAllowed(allowed));
+ }
+
+ /**
+ * Always invoked on the provider executor.
+ */
+ protected void onRequestSetAllowed(boolean allowed) {}
+
+ /**
* Dumps debug or log information. May be invoked from any thread.
*/
public abstract void dump(FileDescriptor fd, PrintWriter pw, String[] args);
diff --git a/services/core/java/com/android/server/location/ContextHubClientBroker.java b/services/core/java/com/android/server/location/ContextHubClientBroker.java
index bb96e985cf53..e27eb65eccfc 100644
--- a/services/core/java/com/android/server/location/ContextHubClientBroker.java
+++ b/services/core/java/com/android/server/location/ContextHubClientBroker.java
@@ -16,6 +16,8 @@
package com.android.server.location;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+
import android.Manifest;
import android.app.PendingIntent;
import android.content.Context;
@@ -110,6 +112,11 @@ public class ContextHubClientBroker extends IContextHubClient.Stub
private AtomicBoolean mIsPendingIntentCancelled = new AtomicBoolean(false);
/*
+ * True if the application creating the client has the ACCESS_CONTEXT_HUB permission.
+ */
+ private final boolean mHasAccessContextHubPermission;
+
+ /*
* Helper class to manage registered PendingIntent requests from the client.
*/
private class PendingIntentRequest {
@@ -166,6 +173,9 @@ public class ContextHubClientBroker extends IContextHubClient.Stub
mCallbackInterface = callback;
mPendingIntentRequest = new PendingIntentRequest();
mPackage = mContext.getPackageManager().getNameForUid(Binder.getCallingUid());
+
+ mHasAccessContextHubPermission = context.checkCallingPermission(
+ Manifest.permission.ACCESS_CONTEXT_HUB) == PERMISSION_GRANTED;
}
/* package */ ContextHubClientBroker(
@@ -179,6 +189,9 @@ public class ContextHubClientBroker extends IContextHubClient.Stub
mHostEndPointId = hostEndPointId;
mPendingIntentRequest = new PendingIntentRequest(pendingIntent, nanoAppId);
mPackage = pendingIntent.getCreatorPackage();
+
+ mHasAccessContextHubPermission = context.checkCallingPermission(
+ Manifest.permission.ACCESS_CONTEXT_HUB) == PERMISSION_GRANTED;
}
/**
@@ -416,10 +429,12 @@ public class ContextHubClientBroker extends IContextHubClient.Stub
*/
private void doSendPendingIntent(PendingIntent pendingIntent, Intent intent) {
try {
+ String requiredPermission = mHasAccessContextHubPermission
+ ? Manifest.permission.ACCESS_CONTEXT_HUB
+ : Manifest.permission.LOCATION_HARDWARE;
pendingIntent.send(
mContext, 0 /* code */, intent, null /* onFinished */, null /* Handler */,
- Manifest.permission.LOCATION_HARDWARE /* requiredPermission */,
- null /* options */);
+ requiredPermission, null /* options */);
} catch (PendingIntent.CanceledException e) {
mIsPendingIntentCancelled.set(true);
// The PendingIntent is no longer valid
diff --git a/services/core/java/com/android/server/location/ContextHubServiceUtil.java b/services/core/java/com/android/server/location/ContextHubServiceUtil.java
index 033437a53891..76cd9ceaf7c4 100644
--- a/services/core/java/com/android/server/location/ContextHubServiceUtil.java
+++ b/services/core/java/com/android/server/location/ContextHubServiceUtil.java
@@ -16,6 +16,8 @@
package com.android.server.location;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+
import android.Manifest;
import android.content.Context;
import android.hardware.contexthub.V1_0.ContextHub;
@@ -30,11 +32,10 @@ import android.hardware.location.NanoAppMessage;
import android.hardware.location.NanoAppState;
import android.util.Log;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
-import java.util.Iterator;
import java.util.List;
-import java.util.ArrayList;
/**
* A class encapsulating helper functions used by the ContextHubService class
@@ -42,8 +43,7 @@ import java.util.ArrayList;
/* package */ class ContextHubServiceUtil {
private static final String TAG = "ContextHubServiceUtil";
private static final String HARDWARE_PERMISSION = Manifest.permission.LOCATION_HARDWARE;
- private static final String ENFORCE_HW_PERMISSION_MESSAGE = "Permission '"
- + HARDWARE_PERMISSION + "' not granted to access ContextHub Hardware";
+ private static final String CONTEXT_HUB_PERMISSION = Manifest.permission.ACCESS_CONTEXT_HUB;
/**
* Creates a ConcurrentHashMap of the Context Hub ID to the ContextHubInfo object given an
@@ -200,7 +200,11 @@ import java.util.ArrayList;
*/
/* package */
static void checkPermissions(Context context) {
- context.enforceCallingPermission(HARDWARE_PERMISSION, ENFORCE_HW_PERMISSION_MESSAGE);
+ if (context.checkCallingPermission(HARDWARE_PERMISSION) != PERMISSION_GRANTED
+ && context.checkCallingPermission(CONTEXT_HUB_PERMISSION) != PERMISSION_GRANTED) {
+ throw new SecurityException(
+ "LOCATION_HARDWARE or ACCESS_CONTEXT_HUB permission required to use Context Hub");
+ }
}
/**
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index 15cf190952d1..306e1e3afcd7 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -729,7 +729,7 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
}, UserHandle.USER_ALL);
setProperties(PROPERTIES);
- setEnabled(true);
+ setAllowed(true);
}
/**
diff --git a/services/core/java/com/android/server/location/LocationProviderProxy.java b/services/core/java/com/android/server/location/LocationProviderProxy.java
index 805b018a8f45..cf299fe9bb2a 100644
--- a/services/core/java/com/android/server/location/LocationProviderProxy.java
+++ b/services/core/java/com/android/server/location/LocationProviderProxy.java
@@ -97,8 +97,8 @@ public class LocationProviderProxy extends AbstractLocationProvider {
// executed on binder thread
@Override
- public void onSetEnabled(boolean enabled) {
- setEnabled(enabled);
+ public void onSetAllowed(boolean allowed) {
+ setAllowed(allowed);
}
// executed on binder thread
@@ -169,6 +169,14 @@ public class LocationProviderProxy extends AbstractLocationProvider {
}
@Override
+ public void onRequestSetAllowed(boolean allowed) {
+ mServiceWatcher.runOnBinder(binder -> {
+ ILocationProvider service = ILocationProvider.Stub.asInterface(binder);
+ service.requestSetAllowed(allowed);
+ });
+ }
+
+ @Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println("service=" + mServiceWatcher);
}
diff --git a/services/core/java/com/android/server/location/MockProvider.java b/services/core/java/com/android/server/location/MockProvider.java
index 60c9fc12c201..bcec8b12b371 100644
--- a/services/core/java/com/android/server/location/MockProvider.java
+++ b/services/core/java/com/android/server/location/MockProvider.java
@@ -42,9 +42,9 @@ public class MockProvider extends AbstractLocationProvider {
setProperties(properties);
}
- /** Sets the enabled state of this mock provider. */
- public void setProviderEnabled(boolean enabled) {
- setEnabled(enabled);
+ /** Sets the allowed state of this mock provider. */
+ public void setProviderAllowed(boolean allowed) {
+ setAllowed(allowed);
}
/** Sets the location to report for this mock provider. */
@@ -56,10 +56,15 @@ public class MockProvider extends AbstractLocationProvider {
}
@Override
- public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- pw.println("last mock location=" + mLocation);
+ public void onSetRequest(ProviderRequest request) {}
+
+ @Override
+ protected void onRequestSetAllowed(boolean allowed) {
+ setAllowed(allowed);
}
@Override
- public void onSetRequest(ProviderRequest request) {}
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println("last mock location=" + mLocation);
+ }
}
diff --git a/services/core/java/com/android/server/location/MockableLocationProvider.java b/services/core/java/com/android/server/location/MockableLocationProvider.java
index f50dfe7edbb7..18615f87609f 100644
--- a/services/core/java/com/android/server/location/MockableLocationProvider.java
+++ b/services/core/java/com/android/server/location/MockableLocationProvider.java
@@ -170,13 +170,13 @@ public class MockableLocationProvider extends AbstractLocationProvider {
}
/**
- * Sets the mock provider implementation's enabled state. Will throw an exception if the mock
+ * Sets the mock provider implementation's allowed state. Will throw an exception if the mock
* provider is not currently the active implementation.
*/
- public void setMockProviderEnabled(boolean enabled) {
+ public void setMockProviderAllowed(boolean allowed) {
synchronized (mOwnerLock) {
Preconditions.checkState(isMock());
- mMockProvider.setProviderEnabled(enabled);
+ mMockProvider.setProviderAllowed(allowed);
}
}
/**
diff --git a/services/core/java/com/android/server/location/PassiveProvider.java b/services/core/java/com/android/server/location/PassiveProvider.java
index b33877069d70..ef157a39fa28 100644
--- a/services/core/java/com/android/server/location/PassiveProvider.java
+++ b/services/core/java/com/android/server/location/PassiveProvider.java
@@ -56,7 +56,7 @@ public class PassiveProvider extends AbstractLocationProvider {
mReportLocation = false;
setProperties(PROPERTIES);
- setEnabled(true);
+ setAllowed(true);
}
@Override
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 7cc67324032a..e92f3ec5a836 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -513,6 +513,7 @@ public class NotificationManagerService extends SystemService {
private TriPredicate<String, Integer, String> mAllowedManagedServicePackages;
private final SavePolicyFileRunnable mSavePolicyFile = new SavePolicyFileRunnable();
+ private NotificationRecordLogger mNotificationRecordLogger;
private static class Archive {
final int mBufferSize;
@@ -1727,7 +1728,14 @@ public class NotificationManagerService extends SystemService {
}
public NotificationManagerService(Context context) {
+ this(context, new NotificationRecordLoggerImpl());
+ }
+
+ @VisibleForTesting
+ public NotificationManagerService(Context context,
+ NotificationRecordLogger notificationRecordLogger) {
super(context);
+ mNotificationRecordLogger = notificationRecordLogger;
Notification.processWhitelistToken = WHITELIST_TOKEN;
}
@@ -6304,9 +6312,11 @@ public class NotificationManagerService extends SystemService {
mRankingHelper.extractSignals(r);
mRankingHelper.sort(mNotificationList);
+ final int position = mRankingHelper.indexOf(mNotificationList, r);
+ int buzzBeepBlinkLoggingCode = 0;
if (!r.isHidden()) {
- buzzBeepBlinkLocked(r);
+ buzzBeepBlinkLoggingCode = buzzBeepBlinkLocked(r);
}
if (notification.getSmallIcon() != null) {
@@ -6346,6 +6356,10 @@ public class NotificationManagerService extends SystemService {
}
maybeRecordInterruptionLocked(r);
+
+ // Log event to statsd
+ mNotificationRecordLogger.logNotificationReported(r, old, position,
+ buzzBeepBlinkLoggingCode);
} finally {
int N = mEnqueuedNotifications.size();
for (int i = 0; i < N; i++) {
@@ -6574,9 +6588,13 @@ public class NotificationManagerService extends SystemService {
@VisibleForTesting
@GuardedBy("mNotificationLock")
- void buzzBeepBlinkLocked(NotificationRecord record) {
+ /**
+ * Determine whether this notification should attempt to make noise, vibrate, or flash the LED
+ * @return buzzBeepBlink - bitfield (buzz ? 1 : 0) | (beep ? 2 : 0) | (blink ? 4 : 0)
+ */
+ int buzzBeepBlinkLocked(NotificationRecord record) {
if (mIsAutomotive && !mNotificationEffectsEnabledForAutomotive) {
- return;
+ return 0;
}
boolean buzz = false;
boolean beep = false;
@@ -6674,7 +6692,8 @@ public class NotificationManagerService extends SystemService {
} else if (wasShowLights) {
updateLightsLocked();
}
- if (buzz || beep || blink) {
+ final int buzzBeepBlink = (buzz ? 1 : 0) | (beep ? 2 : 0) | (blink ? 4 : 0);
+ if (buzzBeepBlink > 0) {
// Ignore summary updates because we don't display most of the information.
if (record.sbn.isGroup() && record.sbn.getNotification().isGroupSummary()) {
if (DEBUG_INTERRUPTIVENESS) {
@@ -6696,10 +6715,11 @@ public class NotificationManagerService extends SystemService {
MetricsLogger.action(record.getLogMaker()
.setCategory(MetricsEvent.NOTIFICATION_ALERT)
.setType(MetricsEvent.TYPE_OPEN)
- .setSubtype((buzz ? 1 : 0) | (beep ? 2 : 0) | (blink ? 4 : 0)));
+ .setSubtype(buzzBeepBlink));
EventLogTags.writeNotificationAlert(key, buzz ? 1 : 0, beep ? 1 : 0, blink ? 1 : 0);
}
record.setAudiblyAlerted(buzz || beep);
+ return buzzBeepBlink;
}
@GuardedBy("mNotificationLock")
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index 2bea21891f8f..660d574fe64e 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -675,6 +675,10 @@ public final class NotificationRecord {
}
}
+ String getAdjustmentIssuer() {
+ return mAdjustmentIssuer;
+ }
+
public void setIsAppImportanceLocked(boolean isAppImportanceLocked) {
mIsAppImportanceLocked = isAppImportanceLocked;
calculateUserSentiment();
@@ -783,10 +787,22 @@ public final class NotificationRecord {
return mImportance;
}
+ int getInitialImportance() {
+ return stats.naturalImportance;
+ }
+
public float getRankingScore() {
return mRankingScore;
}
+ int getImportanceExplanationCode() {
+ return mImportanceExplanationCode;
+ }
+
+ int getInitialImportanceExplanationCode() {
+ return mInitialImportanceExplanationCode;
+ }
+
public CharSequence getImportanceExplanation() {
switch (mImportanceExplanationCode) {
case MetricsEvent.IMPORTANCE_EXPLANATION_UNKNOWN:
diff --git a/services/core/java/com/android/server/notification/NotificationRecordLogger.java b/services/core/java/com/android/server/notification/NotificationRecordLogger.java
new file mode 100644
index 000000000000..03929e883852
--- /dev/null
+++ b/services/core/java/com/android/server/notification/NotificationRecordLogger.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.notification;
+
+import android.annotation.Nullable;
+import android.app.Notification;
+import android.app.Person;
+import android.os.Bundle;
+
+import com.android.internal.logging.UiEvent;
+import com.android.internal.logging.UiEventLogger;
+
+import java.util.ArrayList;
+import java.util.Objects;
+
+/**
+ * Interface for writing NotificationReported atoms to statsd log.
+ * @hide
+ */
+public interface NotificationRecordLogger {
+
+ /**
+ * Logs a NotificationReported atom reflecting the posting or update of a notification.
+ * @param r The new NotificationRecord. If null, no action is taken.
+ * @param old The previous NotificationRecord. Null if there was no previous record.
+ * @param position The position at which this notification is ranked.
+ * @param buzzBeepBlink Logging code reflecting whether this notification alerted the user.
+ */
+ void logNotificationReported(@Nullable NotificationRecord r, @Nullable NotificationRecord old,
+ int position, int buzzBeepBlink);
+
+ /**
+ * The UiEvent enums that this class can log.
+ */
+ enum NotificationReportedEvents implements UiEventLogger.UiEventEnum {
+ INVALID(0),
+ @UiEvent(doc = "New notification enqueued to post")
+ NOTIFICATION_POSTED(162),
+ @UiEvent(doc = "Notification substantially updated")
+ NOTIFICATION_UPDATED(163);
+
+ private final int mId;
+ NotificationReportedEvents(int id) {
+ mId = id;
+ }
+ @Override public int getId() {
+ return mId;
+ }
+ }
+
+ /**
+ * A helper for extracting logging information from one or two NotificationRecords.
+ */
+ class NotificationRecordPair {
+ public final NotificationRecord r, old;
+ /**
+ * Construct from one or two NotificationRecords.
+ * @param r The new NotificationRecord. If null, only shouldLog() method is usable.
+ * @param old The previous NotificationRecord. Null if there was no previous record.
+ */
+ NotificationRecordPair(@Nullable NotificationRecord r, @Nullable NotificationRecord old) {
+ this.r = r;
+ this.old = old;
+ }
+
+ /**
+ * @return True if old is null, alerted, or important logged fields have changed.
+ */
+ boolean shouldLog(int buzzBeepBlink) {
+ if (r == null) {
+ return false;
+ }
+ if ((old == null) || (buzzBeepBlink > 0)) {
+ return true;
+ }
+
+ return !(Objects.equals(r.sbn.getChannelIdLogTag(), old.sbn.getChannelIdLogTag())
+ && Objects.equals(r.sbn.getGroupLogTag(), old.sbn.getGroupLogTag())
+ && (r.sbn.getNotification().isGroupSummary()
+ == old.sbn.getNotification().isGroupSummary())
+ && Objects.equals(r.sbn.getNotification().category,
+ old.sbn.getNotification().category)
+ && (r.getImportance() == old.getImportance()));
+ }
+
+ NotificationReportedEvents getUiEvent() {
+ return (old != null) ? NotificationReportedEvents.NOTIFICATION_UPDATED :
+ NotificationReportedEvents.NOTIFICATION_POSTED;
+ }
+
+ /**
+ * @return hash code for the notification style class, or 0 if none exists.
+ */
+ public int getStyle() {
+ return getStyle(r.sbn.getNotification().extras);
+ }
+
+ private int getStyle(@Nullable Bundle extras) {
+ if (extras != null) {
+ String template = extras.getString(Notification.EXTRA_TEMPLATE);
+ if (template != null && !template.isEmpty()) {
+ return template.hashCode();
+ }
+ }
+ return 0;
+ }
+
+ int getNumPeople() {
+ return getNumPeople(r.sbn.getNotification().extras);
+ }
+
+ private int getNumPeople(@Nullable Bundle extras) {
+ if (extras != null) {
+ ArrayList<Person> people = extras.getParcelableArrayList(
+ Notification.EXTRA_PEOPLE_LIST);
+ if (people != null && !people.isEmpty()) {
+ return people.size();
+ }
+ }
+ return 0;
+ }
+
+ int getAssistantHash() {
+ String assistant = r.getAdjustmentIssuer();
+ return (assistant == null) ? 0 : assistant.hashCode();
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java b/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java
new file mode 100644
index 000000000000..d637ad5e368b
--- /dev/null
+++ b/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.notification;
+
+import android.util.StatsLog;
+
+/**
+ * Standard implementation of NotificationRecordLogger interface.
+ * @hide
+ */
+public class NotificationRecordLoggerImpl implements NotificationRecordLogger {
+
+ @Override
+ public void logNotificationReported(NotificationRecord r, NotificationRecord old,
+ int position, int buzzBeepBlink) {
+ NotificationRecordPair p = new NotificationRecordPair(r, old);
+ if (!p.shouldLog(buzzBeepBlink)) {
+ return;
+ }
+ StatsLog.write(StatsLog.NOTIFICATION_REPORTED,
+ /* int32 event_id = 1 */ p.getUiEvent().getId(),
+ /* int32 uid = 2 */ r.getUid(),
+ /* string package_name = 3 */ r.sbn.getPackageName(),
+ /* int32 instance_id = 4 */ 0, // TODO generate and fill instance ids
+ /* int32 notification_id = 5 */ r.sbn.getId(),
+ /* string notification_tag = 6 */ r.sbn.getTag(),
+ /* string channel_id = 7 */ r.sbn.getChannelIdLogTag(),
+ /* string group_id = 8 */ r.sbn.getGroupLogTag(),
+ /* int32 group_instance_id = 9 */ 0, // TODO generate and fill instance ids
+ /* bool is_group_summary = 10 */ r.sbn.getNotification().isGroupSummary(),
+ /* string category = 11 */ r.sbn.getNotification().category,
+ /* int32 style = 12 */ p.getStyle(),
+ /* int32 num_people = 13 */ p.getNumPeople(),
+ /* int32 position = 14 */ position,
+ /* android.stats.sysui.NotificationImportance importance = 15 */ r.getImportance(),
+ /* int32 alerting = 16 */ buzzBeepBlink,
+ /* NotificationImportanceExplanation importance_source = 17 */
+ r.getImportanceExplanationCode(),
+ /* android.stats.sysui.NotificationImportance importance_initial = 18 */
+ r.getInitialImportance(),
+ /* NotificationImportanceExplanation importance_initial_source = 19 */
+ r.getInitialImportanceExplanationCode(),
+ /* android.stats.sysui.NotificationImportance importance_asst = 20 */
+ r.getAssistantImportance(),
+ /* int32 assistant_hash = 21 */ p.getAssistantHash(),
+ /* float assistant_ranking_score = 22 */ 0 // TODO connect up ranking score
+ );
+ }
+
+
+
+
+
+
+}
diff --git a/services/core/java/com/android/server/om/OverlayActorEnforcer.java b/services/core/java/com/android/server/om/OverlayActorEnforcer.java
index ac3bf9ad5d8a..0a9f923ac817 100644
--- a/services/core/java/com/android/server/om/OverlayActorEnforcer.java
+++ b/services/core/java/com/android/server/om/OverlayActorEnforcer.java
@@ -43,6 +43,9 @@ import java.util.Map;
*/
public class OverlayActorEnforcer {
+ // By default, the reason is not logged to prevent leaks of why it failed
+ private static final boolean DEBUG_REASON = false;
+
private final VerifyCallback mVerifyCallback;
/**
@@ -92,7 +95,7 @@ public class OverlayActorEnforcer {
throw new SecurityException("UID" + callingUid + " is not allowed to call "
+ methodName + " for "
+ (TextUtils.isEmpty(targetOverlayableName) ? "" : (targetOverlayableName + " in "))
- + overlayInfo.targetPackageName + " because " + actorState
+ + overlayInfo.targetPackageName + (DEBUG_REASON ? (" because " + actorState) : "")
);
}
diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java
index 9dff7754c4f3..6e7e5d884a4a 100644
--- a/services/core/java/com/android/server/pm/AppsFilter.java
+++ b/services/core/java/com/android/server/pm/AppsFilter.java
@@ -662,7 +662,7 @@ public class AppsFilter {
String description, Throwable throwable) {
Slog.wtf(TAG,
"interaction: " + callingPkgSetting
- + " -> " + targetPkgSetting.name + " "
+ + " -> " + targetPkgSetting + " "
+ description, throwable);
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index ca4ae0277aaf..de6b7d5af397 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -27,6 +27,7 @@ import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE;
import static android.content.Intent.ACTION_MAIN;
import static android.content.Intent.CATEGORY_DEFAULT;
import static android.content.Intent.CATEGORY_HOME;
+import static android.content.Intent.EXTRA_LONG_VERSION_CODE;
import static android.content.Intent.EXTRA_PACKAGE_NAME;
import static android.content.Intent.EXTRA_VERSION_CODE;
import static android.content.pm.PackageManager.CERT_INPUT_RAW_X509;
@@ -124,8 +125,8 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
-import android.app.ApplicationPackageManager;
import android.app.AppOpsManager;
+import android.app.ApplicationPackageManager;
import android.app.BroadcastOptions;
import android.app.IActivityManager;
import android.app.ResourcesManager;
@@ -386,6 +387,7 @@ import java.util.Objects;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -2867,23 +2869,34 @@ public class PackageManagerService extends IPackageManager.Stub
scanFlags = scanFlags | SCAN_FIRST_BOOT_OR_UPGRADE;
}
+ final int systemParseFlags = mDefParseFlags | PackageParser.PARSE_IS_SYSTEM_DIR;
+ final int systemScanFlags = scanFlags | SCAN_AS_SYSTEM;
+
+ PackageParser packageParser = new PackageParser();
+ packageParser.setSeparateProcesses(mSeparateProcesses);
+ packageParser.setOnlyCoreApps(mOnlyCore);
+ packageParser.setDisplayMetrics(mMetrics);
+ packageParser.setCacheDir(mCacheDir);
+ packageParser.setCallback(mPackageParserCallback);
+
+ ExecutorService executorService = ParallelPackageParser.makeExecutorService();
// Collect vendor/product/system_ext overlay packages. (Do this before scanning
// any apps.)
// For security and version matching reason, only consider overlay packages if they
// reside in the right directory.
- final int systemParseFlags = mDefParseFlags | PackageParser.PARSE_IS_SYSTEM_DIR;
- final int systemScanFlags = scanFlags | SCAN_AS_SYSTEM;
for (int i = mDirsToScanAsSystem.size() - 1; i >= 0; i--) {
final SystemPartition partition = mDirsToScanAsSystem.get(i);
if (partition.overlayFolder == null) {
continue;
}
scanDirTracedLI(partition.overlayFolder, systemParseFlags,
- systemScanFlags | partition.scanFlag, 0);
+ systemScanFlags | partition.scanFlag, 0,
+ packageParser, executorService);
}
scanDirTracedLI(frameworkDir, systemParseFlags,
- systemScanFlags | SCAN_NO_DEX | SCAN_AS_PRIVILEGED, 0);
+ systemScanFlags | SCAN_NO_DEX | SCAN_AS_PRIVILEGED, 0,
+ packageParser, executorService);
if (!mPackages.containsKey("android")) {
throw new IllegalStateException(
"Failed to load frameworks package; check log for warnings");
@@ -2892,10 +2905,12 @@ public class PackageManagerService extends IPackageManager.Stub
final SystemPartition partition = mDirsToScanAsSystem.get(i);
if (partition.privAppFolder != null) {
scanDirTracedLI(partition.privAppFolder, systemParseFlags,
- systemScanFlags | SCAN_AS_PRIVILEGED | partition.scanFlag, 0);
+ systemScanFlags | SCAN_AS_PRIVILEGED | partition.scanFlag, 0,
+ packageParser, executorService);
}
scanDirTracedLI(partition.appFolder, systemParseFlags,
- systemScanFlags | partition.scanFlag, 0);
+ systemScanFlags | partition.scanFlag, 0,
+ packageParser, executorService);
}
@@ -2999,8 +3014,18 @@ public class PackageManagerService extends IPackageManager.Stub
if (!mOnlyCore) {
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_DATA_SCAN_START,
SystemClock.uptimeMillis());
- scanDirTracedLI(sAppInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0);
+ scanDirTracedLI(sAppInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0,
+ packageParser, executorService);
+
+ }
+
+ List<Runnable> unfinishedTasks = executorService.shutdownNow();
+ if (!unfinishedTasks.isEmpty()) {
+ throw new IllegalStateException("Not all tasks finished before calling close: "
+ + unfinishedTasks);
+ }
+ if (!mOnlyCore) {
// Remove disable package settings for updated system apps that were
// removed via an OTA. If the update is no longer present, remove the
// app completely. Otherwise, revoke their system privileges.
@@ -4554,7 +4579,10 @@ public class PackageManagerService extends IPackageManager.Stub
flags = updateFlagsForPackage(flags, userId);
mPermissionManager.enforceCrossUserPermission(callingUid, userId,
false /*requireFullPermission*/, false /*checkShell*/, "getPackageUid");
+ return getPackageUidInternal(packageName, flags, userId, callingUid);
+ }
+ private int getPackageUidInternal(String packageName, int flags, int userId, int callingUid) {
// reader
synchronized (mLock) {
final AndroidPackage p = mPackages.get(packageName);
@@ -8574,16 +8602,18 @@ public class PackageManagerService extends IPackageManager.Stub
return finalList;
}
- private void scanDirTracedLI(File scanDir, final int parseFlags, int scanFlags, long currentTime) {
+ private void scanDirTracedLI(File scanDir, final int parseFlags, int scanFlags,
+ long currentTime, PackageParser packageParser, ExecutorService executorService) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanDir [" + scanDir.getAbsolutePath() + "]");
try {
- scanDirLI(scanDir, parseFlags, scanFlags, currentTime);
+ scanDirLI(scanDir, parseFlags, scanFlags, currentTime, packageParser, executorService);
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
}
- private void scanDirLI(File scanDir, int parseFlags, int scanFlags, long currentTime) {
+ private void scanDirLI(File scanDir, int parseFlags, int scanFlags, long currentTime,
+ PackageParser packageParser, ExecutorService executorService) {
final File[] files = scanDir.listFiles();
if (ArrayUtils.isEmpty(files)) {
Log.d(TAG, "No files in app dir " + scanDir);
@@ -8594,58 +8624,58 @@ public class PackageManagerService extends IPackageManager.Stub
Log.d(TAG, "Scanning app dir " + scanDir + " scanFlags=" + scanFlags
+ " flags=0x" + Integer.toHexString(parseFlags));
}
- try (ParallelPackageParser parallelPackageParser = new ParallelPackageParser(
- mSeparateProcesses, mOnlyCore, mMetrics, mCacheDir,
- mPackageParserCallback)) {
- // Submit files for parsing in parallel
- int fileCount = 0;
- for (File file : files) {
- final boolean isPackage = (isApkFile(file) || file.isDirectory())
- && !PackageInstallerService.isStageName(file.getName());
- if (!isPackage) {
- // Ignore entries which are not packages
- continue;
- }
- parallelPackageParser.submit(file, parseFlags);
- fileCount++;
+
+ ParallelPackageParser parallelPackageParser =
+ new ParallelPackageParser(packageParser, executorService);
+
+ // Submit files for parsing in parallel
+ int fileCount = 0;
+ for (File file : files) {
+ final boolean isPackage = (isApkFile(file) || file.isDirectory())
+ && !PackageInstallerService.isStageName(file.getName());
+ if (!isPackage) {
+ // Ignore entries which are not packages
+ continue;
}
+ parallelPackageParser.submit(file, parseFlags);
+ fileCount++;
+ }
- // Process results one by one
- for (; fileCount > 0; fileCount--) {
- ParallelPackageParser.ParseResult parseResult = parallelPackageParser.take();
- Throwable throwable = parseResult.throwable;
- int errorCode = PackageManager.INSTALL_SUCCEEDED;
+ // Process results one by one
+ for (; fileCount > 0; fileCount--) {
+ ParallelPackageParser.ParseResult parseResult = parallelPackageParser.take();
+ Throwable throwable = parseResult.throwable;
+ int errorCode = PackageManager.INSTALL_SUCCEEDED;
- if (throwable == null) {
- // TODO(toddke): move lower in the scan chain
- // Static shared libraries have synthetic package names
- if (parseResult.parsedPackage.isStaticSharedLibrary()) {
- renameStaticSharedLibraryPackage(parseResult.parsedPackage);
- }
- try {
- addForInitLI(parseResult.parsedPackage, parseFlags, scanFlags,
- currentTime, null);
- } catch (PackageManagerException e) {
- errorCode = e.error;
- Slog.w(TAG, "Failed to scan " + parseResult.scanFile + ": " + e.getMessage());
- }
- } else if (throwable instanceof PackageParserException) {
- PackageParserException e = (PackageParserException)
- throwable;
+ if (throwable == null) {
+ // TODO(toddke): move lower in the scan chain
+ // Static shared libraries have synthetic package names
+ if (parseResult.parsedPackage.isStaticSharedLibrary()) {
+ renameStaticSharedLibraryPackage(parseResult.parsedPackage);
+ }
+ try {
+ addForInitLI(parseResult.parsedPackage, parseFlags, scanFlags,
+ currentTime, null);
+ } catch (PackageManagerException e) {
errorCode = e.error;
- Slog.w(TAG, "Failed to parse " + parseResult.scanFile + ": " + e.getMessage());
- } else {
- throw new IllegalStateException("Unexpected exception occurred while parsing "
- + parseResult.scanFile, throwable);
+ Slog.w(TAG, "Failed to scan " + parseResult.scanFile + ": " + e.getMessage());
}
+ } else if (throwable instanceof PackageParserException) {
+ PackageParserException e = (PackageParserException)
+ throwable;
+ errorCode = e.error;
+ Slog.w(TAG, "Failed to parse " + parseResult.scanFile + ": " + e.getMessage());
+ } else {
+ throw new IllegalStateException("Unexpected exception occurred while parsing "
+ + parseResult.scanFile, throwable);
+ }
- // Delete invalid userdata apps
- if ((scanFlags & SCAN_AS_SYSTEM) == 0 &&
- errorCode != PackageManager.INSTALL_SUCCEEDED) {
- logCriticalInfo(Log.WARN,
- "Deleting invalid package at " + parseResult.scanFile);
- removeCodePathLI(parseResult.scanFile);
- }
+ // Delete invalid userdata apps
+ if ((scanFlags & SCAN_AS_SYSTEM) == 0
+ && errorCode != PackageManager.INSTALL_SUCCEEDED) {
+ logCriticalInfo(Log.WARN,
+ "Deleting invalid package at " + parseResult.scanFile);
+ removeCodePathLI(parseResult.scanFile);
}
}
}
@@ -14375,6 +14405,7 @@ public class PackageManagerService extends IPackageManager.Stub
integrityVerification.putExtra(EXTRA_VERIFICATION_ID, verificationId);
integrityVerification.putExtra(EXTRA_PACKAGE_NAME, pkgLite.packageName);
integrityVerification.putExtra(EXTRA_VERSION_CODE, pkgLite.versionCode);
+ integrityVerification.putExtra(EXTRA_LONG_VERSION_CODE, pkgLite.getLongVersionCode());
populateInstallerExtras(integrityVerification);
// send to integrity component only.
@@ -20211,8 +20242,8 @@ public class PackageManagerService extends IPackageManager.Stub
// Disable any carrier apps. We do this very early in boot to prevent the apps from being
// disabled after already being started.
- CarrierAppUtils.disableCarrierAppsUntilPrivileged(mContext.getOpPackageName(), this,
- mPermissionManagerService, UserHandle.USER_SYSTEM, mContext);
+ CarrierAppUtils.disableCarrierAppsUntilPrivileged(
+ mContext.getOpPackageName(), UserHandle.USER_SYSTEM, mContext);
disableSkuSpecificApps();
@@ -23098,6 +23129,12 @@ public class PackageManagerService extends IPackageManager.Stub
}
@Override
+ public int getPackageUidInternal(String packageName, int flags, int userId) {
+ return PackageManagerService.this
+ .getPackageUidInternal(packageName, flags, userId, Process.SYSTEM_UID);
+ }
+
+ @Override
public ApplicationInfo getApplicationInfo(
String packageName, int flags, int filterCallingUid, int userId) {
return PackageManagerService.this
diff --git a/services/core/java/com/android/server/pm/ParallelPackageParser.java b/services/core/java/com/android/server/pm/ParallelPackageParser.java
index a5065145cafd..448dad026e16 100644
--- a/services/core/java/com/android/server/pm/ParallelPackageParser.java
+++ b/services/core/java/com/android/server/pm/ParallelPackageParser.java
@@ -22,13 +22,11 @@ import android.content.pm.PackageParser;
import android.content.pm.parsing.ParsedPackage;
import android.os.Process;
import android.os.Trace;
-import android.util.DisplayMetrics;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ConcurrentUtils;
import java.io.File;
-import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
@@ -38,30 +36,27 @@ import java.util.concurrent.ExecutorService;
* <p>Parsing requests are processed by a thread-pool of {@link #MAX_THREADS}.
* At any time, at most {@link #QUEUE_CAPACITY} results are kept in RAM</p>
*/
-class ParallelPackageParser implements AutoCloseable {
+class ParallelPackageParser {
- private static final int QUEUE_CAPACITY = 10;
+ private static final int QUEUE_CAPACITY = 30;
private static final int MAX_THREADS = 4;
- private final String[] mSeparateProcesses;
- private final boolean mOnlyCore;
- private final DisplayMetrics mMetrics;
- private final File mCacheDir;
- private final PackageParser.Callback mPackageParserCallback;
private volatile String mInterruptedInThread;
private final BlockingQueue<ParseResult> mQueue = new ArrayBlockingQueue<>(QUEUE_CAPACITY);
- private final ExecutorService mService = ConcurrentUtils.newFixedThreadPool(MAX_THREADS,
- "package-parsing-thread", Process.THREAD_PRIORITY_FOREGROUND);
+ static ExecutorService makeExecutorService() {
+ return ConcurrentUtils.newFixedThreadPool(MAX_THREADS, "package-parsing-thread",
+ Process.THREAD_PRIORITY_FOREGROUND);
+ }
+
+ private final PackageParser mPackageParser;
+
+ private final ExecutorService mExecutorService;
- ParallelPackageParser(String[] separateProcesses, boolean onlyCoreApps,
- DisplayMetrics metrics, File cacheDir, PackageParser.Callback callback) {
- mSeparateProcesses = separateProcesses;
- mOnlyCore = onlyCoreApps;
- mMetrics = metrics;
- mCacheDir = cacheDir;
- mPackageParserCallback = callback;
+ ParallelPackageParser(PackageParser packageParser, ExecutorService executorService) {
+ mPackageParser = packageParser;
+ mExecutorService = executorService;
}
static class ParseResult {
@@ -104,18 +99,12 @@ class ParallelPackageParser implements AutoCloseable {
* @param parseFlags parse flags
*/
public void submit(File scanFile, int parseFlags) {
- mService.submit(() -> {
+ mExecutorService.submit(() -> {
ParseResult pr = new ParseResult();
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parallel parsePackage [" + scanFile + "]");
try {
- PackageParser pp = new PackageParser();
- pp.setSeparateProcesses(mSeparateProcesses);
- pp.setOnlyCoreApps(mOnlyCore);
- pp.setDisplayMetrics(mMetrics);
- pp.setCacheDir(mCacheDir);
- pp.setCallback(mPackageParserCallback);
pr.scanFile = scanFile;
- pr.parsedPackage = parsePackage(pp, scanFile, parseFlags);
+ pr.parsedPackage = parsePackage(scanFile, parseFlags);
} catch (Throwable e) {
pr.throwable = e;
} finally {
@@ -134,17 +123,8 @@ class ParallelPackageParser implements AutoCloseable {
}
@VisibleForTesting
- protected ParsedPackage parsePackage(PackageParser packageParser, File scanFile,
- int parseFlags) throws PackageParser.PackageParserException {
- return packageParser.parseParsedPackage(scanFile, parseFlags, true);
- }
-
- @Override
- public void close() {
- List<Runnable> unfinishedTasks = mService.shutdownNow();
- if (!unfinishedTasks.isEmpty()) {
- throw new IllegalStateException("Not all tasks finished before calling close: "
- + unfinishedTasks);
- }
+ protected ParsedPackage parsePackage(File scanFile, int parseFlags)
+ throws PackageParser.PackageParserException {
+ return mPackageParser.parseParsedPackage(scanFile, parseFlags, true);
}
}
diff --git a/services/core/java/com/android/server/rollback/Rollback.java b/services/core/java/com/android/server/rollback/Rollback.java
index 5c0dd9a44dc4..cb583cd42412 100644
--- a/services/core/java/com/android/server/rollback/Rollback.java
+++ b/services/core/java/com/android/server/rollback/Rollback.java
@@ -179,6 +179,13 @@ class Rollback {
private final IntArray mTokens = new IntArray();
/**
+ * Session ids for all packages in the install. For multi-package sessions, this is the list
+ * of child session ids. For normal sessions, this list is a single element with the normal
+ * session id.
+ */
+ private final int[] mPackageSessionIds;
+
+ /**
* Constructs a new, empty Rollback instance.
*
* @param rollbackId the id of the rollback.
@@ -186,9 +193,10 @@ class Rollback {
* @param stagedSessionId the session id if this is a staged rollback, -1 otherwise.
* @param userId the user that performed the install with rollback enabled.
* @param installerPackageName the installer package name from the original install session.
+ * @param packageSessionIds the session ids for all packages in the install.
*/
Rollback(int rollbackId, File backupDir, int stagedSessionId, int userId,
- String installerPackageName) {
+ String installerPackageName, int[] packageSessionIds) {
this.info = new RollbackInfo(rollbackId,
/* packages */ new ArrayList<>(),
/* isStaged */ stagedSessionId != -1,
@@ -200,6 +208,12 @@ class Rollback {
mStagedSessionId = stagedSessionId;
mState = ROLLBACK_STATE_ENABLING;
mTimestamp = Instant.now();
+ mPackageSessionIds = packageSessionIds != null ? packageSessionIds : new int[0];
+ }
+
+ Rollback(int rollbackId, File backupDir, int stagedSessionId, int userId,
+ String installerPackageName) {
+ this(rollbackId, backupDir, stagedSessionId, userId, installerPackageName, null);
}
/**
@@ -217,6 +231,11 @@ class Rollback {
mState = state;
mApkSessionId = apkSessionId;
mRestoreUserDataInProgress = restoreUserDataInProgress;
+ // TODO(b/120200473): Include this field during persistence. This field will be used to
+ // decide which rollback to expire when ACTION_PACKAGE_REPLACED is received. Note persisting
+ // this field is not backward compatible. We won't fix b/120200473 until S to minimize the
+ // impact.
+ mPackageSessionIds = new int[0];
}
/**
@@ -793,6 +812,25 @@ class Rollback {
}
}
+ /**
+ * Returns true if this rollback contains the provided {@code packageSessionId}.
+ */
+ boolean containsSessionId(int packageSessionId) {
+ for (int id : mPackageSessionIds) {
+ if (id == packageSessionId) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns the number of package session ids in this rollback.
+ */
+ int getPackageSessionIdCount() {
+ return mPackageSessionIds.length;
+ }
+
static String rollbackStateToString(@RollbackState int state) {
switch (state) {
case Rollback.ROLLBACK_STATE_ENABLING: return "enabling";
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
index de48939825e4..4bab22478e6a 100644
--- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
+++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
@@ -1238,7 +1238,8 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
// equal to the number of sessions we are installing, to ensure we didn't skip enabling
// of any sessions. If we successfully enable an apex, then we can assume we enabled
// rollback for the embedded apk-in-apex, if any.
- if (rollback.getPackageCount(0 /*flags*/) != newRollback.getPackageSessionIdCount()) {
+ // TODO: add a helper instead of exposing 2 methods from Rollback
+ if (rollback.getPackageCount(0 /*flags*/) != rollback.getPackageSessionIdCount()) {
Slog.e(TAG, "Failed to enable rollback for all packages in session.");
rollback.delete(mAppDataRollbackHelper);
return null;
@@ -1343,13 +1344,6 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
public final Rollback rollback;
/**
- * Session ids for all packages in the install. For multi-package sessions, this is the list
- * of child session ids. For normal sessions, this list is a single element with the normal
- * session id.
- */
- private final int[] mPackageSessionIds;
-
- /**
* The number of sessions in the install which are notified with success by
* {@link PackageInstaller.SessionCallback#onFinished(int, boolean)}.
* This NewRollback will be enabled only after all child sessions finished with success.
@@ -1359,28 +1353,8 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
private final Object mNewRollbackLock = new Object();
- NewRollback(Rollback rollback, int[] packageSessionIds) {
+ NewRollback(Rollback rollback) {
this.rollback = rollback;
- this.mPackageSessionIds = packageSessionIds;
- }
-
- /**
- * Returns true if this NewRollback contains the provided {@code packageSessionId}.
- */
- boolean containsSessionId(int packageSessionId) {
- for (int id : mPackageSessionIds) {
- if (id == packageSessionId) {
- return true;
- }
- }
- return false;
- }
-
- /**
- * Returns the number of package session ids in this NewRollback.
- */
- int getPackageSessionIdCount() {
- return mPackageSessionIds.length;
}
/**
@@ -1390,7 +1364,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
*/
boolean notifySessionWithSuccess() {
synchronized (mNewRollbackLock) {
- return ++mNumPackageSessionsWithSuccess == mPackageSessionIds.length;
+ return ++mNumPackageSessionsWithSuccess == rollback.getPackageSessionIdCount();
}
}
}
@@ -1414,14 +1388,6 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
+ " user=" + userId + " installer=" + installerPackageName);
}
- if (parentSession.isStaged()) {
- rollback = mRollbackStore.createStagedRollback(rollbackId, parentSessionId, userId,
- installerPackageName);
- } else {
- rollback = mRollbackStore.createNonStagedRollback(rollbackId, userId,
- installerPackageName);
- }
-
int[] packageSessionIds;
if (parentSession.isMultiPackage()) {
packageSessionIds = parentSession.getChildSessionIds();
@@ -1429,7 +1395,15 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
packageSessionIds = new int[]{parentSessionId};
}
- return new NewRollback(rollback, packageSessionIds);
+ if (parentSession.isStaged()) {
+ rollback = mRollbackStore.createStagedRollback(rollbackId, parentSessionId, userId,
+ installerPackageName, packageSessionIds);
+ } else {
+ rollback = mRollbackStore.createNonStagedRollback(rollbackId, userId,
+ installerPackageName, packageSessionIds);
+ }
+
+ return new NewRollback(rollback);
}
/**
@@ -1443,7 +1417,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
// We expect mNewRollbacks to be a very small list; linear search
// should be plenty fast.
for (NewRollback newRollback: mNewRollbacks) {
- if (newRollback.containsSessionId(packageSessionId)) {
+ if (newRollback.rollback.containsSessionId(packageSessionId)) {
return newRollback;
}
}
diff --git a/services/core/java/com/android/server/rollback/RollbackStore.java b/services/core/java/com/android/server/rollback/RollbackStore.java
index bbcd0def05a8..4f894821db27 100644
--- a/services/core/java/com/android/server/rollback/RollbackStore.java
+++ b/services/core/java/com/android/server/rollback/RollbackStore.java
@@ -204,6 +204,13 @@ class RollbackStore {
return new Rollback(rollbackId, backupDir, -1, userId, installerPackageName);
}
+ Rollback createNonStagedRollback(int rollbackId, int userId, String installerPackageName,
+ int[] packageSessionIds) {
+ File backupDir = new File(mRollbackDataDir, Integer.toString(rollbackId));
+ return new Rollback(rollbackId, backupDir, -1, userId, installerPackageName,
+ packageSessionIds);
+ }
+
/**
* Creates a new Rollback instance for a staged rollback with
* backupDir assigned.
@@ -215,6 +222,17 @@ class RollbackStore {
}
/**
+ * TODO: Now we have 4 factory methods for creating Rollback objects which is verbose and
+ * cumbersome. Need to merge them for simplicity.
+ */
+ Rollback createStagedRollback(int rollbackId, int stagedSessionId, int userId,
+ String installerPackageName, int[] packageSessionIds) {
+ File backupDir = new File(mRollbackDataDir, Integer.toString(rollbackId));
+ return new Rollback(rollbackId, backupDir, stagedSessionId, userId, installerPackageName,
+ packageSessionIds);
+ }
+
+ /**
* Creates a backup copy of an apk or apex for a package.
* For packages containing splits, this method should be called for each
* of the package's split apks in addition to the base apk.
diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
index a0e6be4d9a6b..39d1a51c01de 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -20,7 +20,6 @@ import static android.app.AppOpsManager.OP_FLAGS_ALL_TRUSTED;
import static android.content.pm.PackageInfo.REQUESTED_PERMISSION_GRANTED;
import static android.content.pm.PermissionInfo.PROTECTION_DANGEROUS;
import static android.os.Debug.getIonHeapsSizeKb;
-import static android.os.Process.THREAD_PRIORITY_BACKGROUND;
import static android.os.Process.getUidForPid;
import static android.os.storage.VolumeInfo.TYPE_PRIVATE;
import static android.os.storage.VolumeInfo.TYPE_PUBLIC;
@@ -32,11 +31,8 @@ import static com.android.server.stats.pull.ProcfsMemoryUtil.forEachPid;
import static com.android.server.stats.pull.ProcfsMemoryUtil.readCmdlineFromProcfs;
import static com.android.server.stats.pull.ProcfsMemoryUtil.readMemorySnapshotFromProcfs;
-import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManagerInternal;
-import android.app.AlarmManager;
-import android.app.AlarmManager.OnAlarmListener;
import android.app.AppOpsManager;
import android.app.AppOpsManager.HistoricalOps;
import android.app.AppOpsManager.HistoricalOpsRequest;
@@ -49,10 +45,7 @@ import android.app.StatsManager.PullAtomMetadata;
import android.bluetooth.BluetoothActivityEnergyInfo;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.UidTraffic;
-import android.content.BroadcastReceiver;
import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
@@ -74,23 +67,14 @@ import android.os.Build;
import android.os.Bundle;
import android.os.CoolingDevice;
import android.os.Environment;
-import android.os.FileUtils;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.IBinder;
-import android.os.IPullAtomCallback;
-import android.os.IStatsCompanionService;
-import android.os.IStatsd;
import android.os.IStoraged;
import android.os.IThermalEventListener;
import android.os.IThermalService;
-import android.os.Looper;
import android.os.ParcelFileDescriptor;
import android.os.Parcelable;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.StatFs;
-import android.os.StatsLogEventWrapper;
import android.os.SynchronousResultReceiver;
import android.os.SystemClock;
import android.os.SystemProperties;
@@ -134,7 +118,6 @@ import com.android.internal.os.LooperStats;
import com.android.internal.os.PowerProfile;
import com.android.internal.os.ProcessCpuTracker;
import com.android.internal.os.StoragedUidIoStatsReader;
-import com.android.internal.util.DumpUtils;
import com.android.server.BinderCallsStatsService;
import com.android.server.LocalServices;
import com.android.server.SystemService;
@@ -156,20 +139,15 @@ import org.json.JSONException;
import org.json.JSONObject;
import java.io.File;
-import java.io.FileDescriptor;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
-import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
-import java.util.Map.Entry;
import java.util.MissingResourceException;
-import java.util.Objects;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
@@ -229,6 +207,25 @@ public class StatsPullAtomService extends SystemService {
mTelephony = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
mStorageManager = (StorageManager) mContext.getSystemService(StorageManager.class);
+ final ConnectivityManager connectivityManager =
+ (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
+ // Default NetworkRequest should cover all transport types.
+ final NetworkRequest request = new NetworkRequest.Builder().build();
+ connectivityManager.registerNetworkCallback(request, new ConnectivityStatsCallback());
+
+ // Enable push notifications of throttling from vendor thermal
+ // management subsystem via thermalservice.
+ IThermalService thermalService = getIThermalService();
+ if (thermalService != null) {
+ try {
+ thermalService.registerThermalEventListener(
+ new ThermalEventListener());
+ Slog.i(TAG, "register thermal listener successfully");
+ } catch (RemoteException e) {
+ Slog.i(TAG, "failed to register thermal listener");
+ }
+ }
+
// Initialize state for CPU_TIME_PER_FREQ atom
PowerProfile powerProfile = new PowerProfile(mContext);
final int numClusters = powerProfile.getNumCpuClusters();
@@ -2896,4 +2893,29 @@ public class StatsPullAtomService extends SystemService {
BackgroundThread.getExecutor()
);
}
+
+
+ // Thermal event received from vendor thermal management subsystem
+ private static final class ThermalEventListener extends IThermalEventListener.Stub {
+ @Override
+ public void notifyThrottling(Temperature temp) {
+ StatsLog.write(StatsLog.THERMAL_THROTTLING_SEVERITY_STATE_CHANGED, temp.getType(),
+ temp.getName(), (int) (temp.getValue() * 10), temp.getStatus());
+ }
+ }
+
+ private static final class ConnectivityStatsCallback extends
+ ConnectivityManager.NetworkCallback {
+ @Override
+ public void onAvailable(Network network) {
+ StatsLog.write(StatsLog.CONNECTIVITY_STATE_CHANGED, network.netId,
+ StatsLog.CONNECTIVITY_STATE_CHANGED__STATE__CONNECTED);
+ }
+
+ @Override
+ public void onLost(Network network) {
+ StatsLog.write(StatsLog.CONNECTIVITY_STATE_CHANGED, network.netId,
+ StatsLog.CONNECTIVITY_STATE_CHANGED__STATE__DISCONNECTED);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 36e97755ab8a..2115f7ccfe98 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -3433,6 +3433,9 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
+ pw.print("mDefaultWallpaperComponent="); pw.println(mDefaultWallpaperComponent);
+ pw.print("mImageWallpaper="); pw.println(mImageWallpaper);
+
synchronized (mLock) {
pw.println("System wallpaper state:");
for (int i = 0; i < mWallpaperMap.size(); i++) {
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateService.java b/services/core/java/com/android/server/webkit/WebViewUpdateService.java
index 0abe68f270f3..8130546e2699 100644
--- a/services/core/java/com/android/server/webkit/WebViewUpdateService.java
+++ b/services/core/java/com/android/server/webkit/WebViewUpdateService.java
@@ -22,6 +22,7 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
import android.os.Binder;
import android.os.PatternMatcher;
import android.os.Process;
@@ -34,6 +35,7 @@ import android.webkit.WebViewProviderInfo;
import android.webkit.WebViewProviderResponse;
import com.android.internal.util.DumpUtils;
+import com.android.server.LocalServices;
import com.android.server.SystemService;
import java.io.FileDescriptor;
@@ -191,7 +193,26 @@ public class WebViewUpdateService extends SystemService {
throw new IllegalStateException("Cannot create a WebView from the SystemServer");
}
- return WebViewUpdateService.this.mImpl.waitForAndGetProvider();
+ final WebViewProviderResponse webViewProviderResponse =
+ WebViewUpdateService.this.mImpl.waitForAndGetProvider();
+ if (webViewProviderResponse.packageInfo != null) {
+ grantVisibilityToCaller(
+ webViewProviderResponse.packageInfo.packageName, Binder.getCallingUid());
+ }
+ return webViewProviderResponse;
+ }
+
+ /**
+ * Grants app visibility of the webViewPackageName to the currently bound caller.
+ * @param webViewPackageName
+ */
+ private void grantVisibilityToCaller(String webViewPackageName, int callingUid) {
+ final PackageManagerInternal pmInternal = LocalServices.getService(
+ PackageManagerInternal.class);
+ final int webviewUid = pmInternal.getPackageUidInternal(
+ webViewPackageName, 0, UserHandle.getUserId(callingUid));
+ pmInternal.grantImplicitAccess(UserHandle.getUserId(callingUid), null, webviewUid,
+ UserHandle.getAppId(callingUid));
}
/**
@@ -231,13 +252,18 @@ public class WebViewUpdateService extends SystemService {
@Override // Binder call
public String getCurrentWebViewPackageName() {
- PackageInfo pi = WebViewUpdateService.this.mImpl.getCurrentWebViewPackage();
+ PackageInfo pi = getCurrentWebViewPackage();
return pi == null ? null : pi.packageName;
}
@Override // Binder call
public PackageInfo getCurrentWebViewPackage() {
- return WebViewUpdateService.this.mImpl.getCurrentWebViewPackage();
+ final PackageInfo currentWebViewPackage =
+ WebViewUpdateService.this.mImpl.getCurrentWebViewPackage();
+ if (currentWebViewPackage != null) {
+ grantVisibilityToCaller(currentWebViewPackage.packageName, Binder.getCallingUid());
+ }
+ return currentWebViewPackage;
}
@Override // Binder call
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index d1c8448f2a4a..33e18c1263ab 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -94,7 +94,6 @@ import static android.os.Build.VERSION_CODES.O;
import static android.os.Process.SYSTEM_UID;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.view.Display.COLOR_MODE_DEFAULT;
-import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
import static android.view.Surface.ROTATION_270;
import static android.view.Surface.ROTATION_90;
@@ -495,7 +494,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// process that it is hidden.
private boolean mLastDeferHidingClient; // If true we will defer setting mClientVisible to false
// and reporting to the client that it is hidden.
- boolean sleeping; // have we told the activity to sleep?
+ private boolean mSetToSleep; // have we told the activity to sleep?
boolean nowVisible; // is this activity's window visible?
boolean mDrawn; // is this activity's window drawn?
boolean mClientVisibilityDeferred;// was the visibility change message to client deferred?
@@ -613,6 +612,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
private boolean mLastContainsShowWhenLockedWindow;
private boolean mLastContainsDismissKeyguardWindow;
+ private boolean mLastContainsTurnScreenOnWindow;
/**
* A flag to determine if this AR is in the process of closing or entering PIP. This is needed
@@ -895,7 +895,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
pw.print(" finishing="); pw.println(finishing);
pw.print(prefix); pw.print("keysPaused="); pw.print(keysPaused);
pw.print(" inHistory="); pw.print(inHistory);
- pw.print(" sleeping="); pw.print(sleeping);
+ pw.print(" setToSleep="); pw.print(mSetToSleep);
pw.print(" idle="); pw.print(idle);
pw.print(" mStartingWindowState=");
pw.println(startingWindowStateToString(mStartingWindowState));
@@ -1627,7 +1627,12 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
requestedVrComponent = (aInfo.requestedVrComponent == null) ?
null : ComponentName.unflattenFromString(aInfo.requestedVrComponent);
- lockTaskLaunchMode = getLockTaskLaunchMode(aInfo, options);
+ lockTaskLaunchMode = aInfo.lockTaskLaunchMode;
+ if (info.applicationInfo.isPrivilegedApp()
+ && (lockTaskLaunchMode == LOCK_TASK_LAUNCH_MODE_ALWAYS
+ || lockTaskLaunchMode == LOCK_TASK_LAUNCH_MODE_NEVER)) {
+ lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_DEFAULT;
+ }
if (options != null) {
pendingOptions = options;
@@ -1635,25 +1640,13 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
if (usageReport != null) {
appTimeTracker = new AppTimeTracker(usageReport);
}
- // Gets launch display id from options. It returns INVALID_DISPLAY if not set.
- mHandoverLaunchDisplayId = options.getLaunchDisplayId();
- }
- }
-
- static int getLockTaskLaunchMode(ActivityInfo aInfo, @Nullable ActivityOptions options) {
- int lockTaskLaunchMode = aInfo.lockTaskLaunchMode;
- if (aInfo.applicationInfo.isPrivilegedApp()
- && (lockTaskLaunchMode == LOCK_TASK_LAUNCH_MODE_ALWAYS
- || lockTaskLaunchMode == LOCK_TASK_LAUNCH_MODE_NEVER)) {
- lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_DEFAULT;
- }
- if (options != null) {
- final boolean useLockTask = options.getLockTaskMode();
+ final boolean useLockTask = pendingOptions.getLockTaskMode();
if (useLockTask && lockTaskLaunchMode == LOCK_TASK_LAUNCH_MODE_DEFAULT) {
lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED;
}
+ // Gets launch display id from options. It returns INVALID_DISPLAY if not set.
+ mHandoverLaunchDisplayId = options.getLaunchDisplayId();
}
- return lockTaskLaunchMode;
}
@Override
@@ -2661,7 +2654,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// Make sure the record is cleaned out of other places.
mStackSupervisor.mStoppingActivities.remove(this);
- mStackSupervisor.mGoingToSleepActivities.remove(this);
final ActivityStack stack = getRootTask();
final DisplayContent display = getDisplay();
@@ -3364,6 +3356,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
mLastContainsDismissKeyguardWindow = containsDismissKeyguard;
mLastContainsShowWhenLockedWindow = containsShowWhenLocked;
+ mLastContainsTurnScreenOnWindow = containsTurnScreenOnWindow();
}
boolean containsDismissKeyguardWindow() {
@@ -4362,6 +4355,15 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
|| state5 == mState;
}
+ /**
+ * Returns {@code true} if the Activity is in one of the specified states.
+ */
+ boolean isState(ActivityState state1, ActivityState state2, ActivityState state3,
+ ActivityState state4, ActivityState state5, ActivityState state6) {
+ return state1 == mState || state2 == mState || state3 == mState || state4 == mState
+ || state5 == mState || state6 == mState;
+ }
+
void destroySurfaces() {
destroySurfaces(false /*cleanupOnResume*/);
}
@@ -4451,20 +4453,26 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
return false;
}
- // Whether the activity is on the sleeping display.
- // TODO(b/129750406): This should be applied for the default display, too.
- final boolean isDisplaySleeping = getDisplay().isSleeping()
- && getDisplayId() != DEFAULT_DISPLAY;
- // Whether this activity is the top activity of this stack.
+ // Check if the activity is on a sleeping display, and if it can turn it ON.
+ if (getDisplay().isSleeping()) {
+ final boolean canTurnScreenOn = !mSetToSleep || canTurnScreenOn()
+ || canShowWhenLocked() || containsDismissKeyguardWindow();
+ if (!canTurnScreenOn) {
+ return false;
+ }
+ }
+
+ // Now check whether it's really visible depending on Keyguard state, and update
+ // {@link ActivityStack} internal states.
+ // Inform the method if this activity is the top activity of this stack, but exclude the
+ // case where this is the top activity in a pinned stack.
final boolean isTop = this == stack.getTopNonFinishingActivity();
- // Exclude the case where this is the top activity in a pinned stack.
final boolean isTopNotPinnedStack = stack.isAttached()
&& stack.getDisplay().isTopNotPinnedStack(stack);
- // Now check whether it's really visible depending on Keyguard state, and update
- // {@link ActivityStack} internal states.
final boolean visibleIgnoringDisplayStatus = stack.checkKeyguardVisibility(this,
visibleIgnoringKeyguard, isTop && isTopNotPinnedStack);
- return visibleIgnoringDisplayStatus && !isDisplaySleeping;
+
+ return visibleIgnoringDisplayStatus;
}
boolean shouldBeVisible() {
@@ -4496,7 +4504,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
stack.mUndrawnActivitiesBelowTopTranslucent.add(this);
}
setVisibility(true);
- sleeping = false;
+ mSetToSleep = false;
app.postPendingUiCleanMsg(true);
if (reportToClient) {
mClientVisibilityDeferred = false;
@@ -4506,7 +4514,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
// The activity may be waiting for stop, but that is no longer appropriate for it.
mStackSupervisor.mStoppingActivities.remove(this);
- mStackSupervisor.mGoingToSleepActivities.remove(this);
} catch (Exception e) {
// Just skip on any failure; we'll make it visible when it next restarts.
Slog.w(TAG, "Exception thrown making visible: " + intent.getComponent(), e);
@@ -5452,25 +5459,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
return mVisibleRequested || nowVisible || mState == PAUSING || mState == RESUMED;
}
- void setSleeping(boolean _sleeping) {
- setSleeping(_sleeping, false);
- }
-
- void setSleeping(boolean _sleeping, boolean force) {
- if (!force && sleeping == _sleeping) {
- return;
- }
- if (attachedToProcess()) {
- try {
- app.getThread().scheduleSleeping(appToken, _sleeping);
- if (_sleeping && !mStackSupervisor.mGoingToSleepActivities.contains(this)) {
- mStackSupervisor.mGoingToSleepActivities.add(this);
- }
- sleeping = _sleeping;
- } catch (RemoteException e) {
- Slog.w(TAG, "Exception thrown when sleeping: " + intent.getComponent(), e);
- }
- }
+ void setSleeping(boolean sleeping) {
+ mSetToSleep = sleeping;
}
static int getTaskForActivityLocked(IBinder token, boolean onlyRoot) {
@@ -7413,7 +7403,22 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
boolean getTurnScreenOnFlag() {
- return mTurnScreenOn;
+ return mTurnScreenOn || containsTurnScreenOnWindow();
+ }
+
+ private boolean containsTurnScreenOnWindow() {
+ // When we are relaunching, it is possible for us to be unfrozen before our previous
+ // windows have been added back. Using the cached value ensures that our previous
+ // showWhenLocked preference is honored until relaunching is complete.
+ if (isRelaunching()) {
+ return mLastContainsTurnScreenOnWindow;
+ }
+ for (int i = mChildren.size() - 1; i >= 0; i--) {
+ if ((mChildren.get(i).mAttrs.flags & LayoutParams.FLAG_TURN_SCREEN_ON) != 0) {
+ return true;
+ }
+ }
+ return false;
}
/**
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index 942be8409db2..663423f8728b 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -1263,6 +1263,8 @@ class ActivityStack extends Task implements BoundsAnimationTarget {
void awakeFromSleepingLocked() {
// Ensure activities are no longer sleeping.
forAllActivities((Consumer<ActivityRecord>) (r) -> r.setSleeping(false));
+ ensureActivitiesVisible(null /* starting */, 0 /* configChanges */,
+ false /* preserveWindows */);
if (mPausingActivity != null) {
Slog.d(TAG, "awakeFromSleepingLocked: previously pausing activity didn't pause");
mPausingActivity.activityPaused(true);
@@ -1312,13 +1314,6 @@ class ActivityStack extends Task implements BoundsAnimationTarget {
mStackSupervisor.scheduleIdle();
shouldSleep = false;
}
-
- if (containsActivityFromStack(mStackSupervisor.mGoingToSleepActivities)) {
- // Still need to tell some activities to sleep; can't sleep yet.
- if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Sleep still need to sleep "
- + mStackSupervisor.mGoingToSleepActivities.size() + " activities");
- shouldSleep = false;
- }
}
if (shouldSleep) {
@@ -1329,16 +1324,18 @@ class ActivityStack extends Task implements BoundsAnimationTarget {
}
void goToSleep() {
- // Ensure visibility without updating configuration, as activities are about to sleep.
- ensureActivitiesVisible(null /* starting */, 0 /* configChanges */, !PRESERVE_WINDOWS);
-
- // Make sure any paused or stopped but visible activities are now sleeping.
- // This ensures that the activity's onStop() is called.
+ // Make sure all visible activities are now sleeping. This will update the activity's
+ // visibility and onStop() will be called.
forAllActivities((r) -> {
- if (r.isState(STARTED, STOPPING, STOPPED, PAUSED, PAUSING)) {
+ if (r.isState(STARTED, RESUMED, PAUSING, PAUSED, STOPPING, STOPPED)) {
r.setSleeping(true);
}
});
+
+ // Ensure visibility after updating sleep states without updating configuration,
+ // as activities are about to be sent to sleep.
+ ensureActivitiesVisible(null /* starting */, 0 /* configChanges */,
+ !PRESERVE_WINDOWS);
}
private boolean containsActivityFromStack(List<ActivityRecord> rs) {
@@ -2040,8 +2037,17 @@ class ActivityStack extends Task implements BoundsAnimationTarget {
return false;
}
- // If we are sleeping, and there is no resumed activity, and the top
- // activity is paused, well that is the state we want.
+ // If we are currently pausing an activity, then don't do anything until that is done.
+ final boolean allPausedComplete = mRootWindowContainer.allPausedActivitiesComplete();
+ if (!allPausedComplete) {
+ if (DEBUG_SWITCH || DEBUG_PAUSE || DEBUG_STATES) {
+ Slog.v(TAG_PAUSE, "resumeTopActivityLocked: Skip resume: some activity pausing.");
+ }
+ return false;
+ }
+
+ // If we are sleeping, and there is no resumed activity, and the top activity is paused,
+ // well that is the state we want.
if (shouldSleepOrShutDownActivities()
&& mLastPausedActivity == next
&& mRootWindowContainer.allPausedActivitiesComplete()) {
@@ -2082,8 +2088,7 @@ class ActivityStack extends Task implements BoundsAnimationTarget {
// The activity may be waiting for stop, but that is no longer
// appropriate for it.
mStackSupervisor.mStoppingActivities.remove(next);
- mStackSupervisor.mGoingToSleepActivities.remove(next);
- next.sleeping = false;
+ next.setSleeping(false);
if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Resuming " + next);
@@ -2352,7 +2357,7 @@ class ActivityStack extends Task implements BoundsAnimationTarget {
EventLogTags.writeWmResumeActivity(next.mUserId, System.identityHashCode(next),
next.getTask().mTaskId, next.shortComponentName);
- next.sleeping = false;
+ next.setSleeping(false);
mAtmService.getAppWarningsLocked().onResumeActivity(next);
next.app.setPendingUiCleanAndForceProcessStateUpTo(mAtmService.mTopProcessState);
next.clearOptionsLocked();
@@ -3985,6 +3990,10 @@ class ActivityStack extends Task implements BoundsAnimationTarget {
* Used to make room for shadows in the pinned windowing mode.
*/
int getStackOutset() {
+ // If we are drawing shadows on the task then don't outset the stack.
+ if (mWmService.mRenderShadowsInCompositor) {
+ return 0;
+ }
DisplayContent displayContent = getDisplayContent();
if (inPinnedWindowingMode() && displayContent != null) {
final DisplayMetrics displayMetrics = displayContent.getDisplayMetrics();
@@ -4034,7 +4043,9 @@ class ActivityStack extends Task implements BoundsAnimationTarget {
@Override
void onDisplayChanged(DisplayContent dc) {
super.onDisplayChanged(dc);
- updateSurfaceBounds();
+ if (isRootTask()) {
+ updateSurfaceBounds();
+ }
}
/**
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index 2c0f3e65f198..362e781f7fb9 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -285,9 +285,6 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
* settle down before doing so. It contains ActivityRecord objects. */
final ArrayList<ActivityRecord> mFinishingActivities = new ArrayList<>();
- /** List of activities that are in the process of going to sleep. */
- final ArrayList<ActivityRecord> mGoingToSleepActivities = new ArrayList<>();
-
/** List of activities whose multi-window mode changed that we need to report to the
* application */
private final ArrayList<ActivityRecord> mMultiWindowModeChangedActivities = new ArrayList<>();
@@ -881,7 +878,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
}
mService.getPackageManagerInternalLocked().notifyPackageUse(
r.intent.getComponent().getPackageName(), NOTIFY_PACKAGE_USE_ACTIVITY);
- r.sleeping = false;
+ r.setSleeping(false);
r.forceNewConfig = false;
mService.getAppWarningsLocked().onStartActivity(r);
r.compat = mService.compatibilityInfoForPackageLocked(r.info.applicationInfo);
@@ -1239,7 +1236,8 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
final PackageInfo packageInfo;
try {
packageInfo = mService.mContext.getPackageManager()
- .getPackageInfo(callingPackage, PackageManager.GET_PERMISSIONS);
+ .getPackageInfoAsUser(callingPackage, PackageManager.GET_PERMISSIONS,
+ UserHandle.getUserId(callingUid));
} catch (PackageManager.NameNotFoundException e) {
Slog.i(TAG, "Cannot find package info for " + callingPackage);
return ACTIVITY_RESTRICTION_NONE;
@@ -1991,16 +1989,6 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
}
}
- void activitySleptLocked(ActivityRecord r) {
- mGoingToSleepActivities.remove(r);
- final ActivityStack s = r.getRootTask();
- if (s != null) {
- s.checkReadyForSleep();
- } else {
- checkReadyForSleepLocked(true);
- }
- }
-
void checkReadyForSleepLocked(boolean allowDelay) {
if (!mService.isSleepingOrShuttingDownLocked()) {
// Do not care.
@@ -2137,7 +2125,6 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
void removeHistoryRecords(WindowProcessController app) {
removeHistoryRecords(mStoppingActivities, app, "mStoppingActivities");
- removeHistoryRecords(mGoingToSleepActivities, app, "mGoingToSleepActivities");
removeHistoryRecords(mFinishingActivities, app, "mFinishingActivities");
}
@@ -2474,7 +2461,10 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
if (r != null) {
r.finishRelaunching();
if (r.getRootTask().shouldSleepOrShutDownActivities()) {
- r.setSleeping(true, true);
+ // Activity is always relaunched to either resumed or paused state. If it was
+ // relaunched while hidden (by keyguard or smth else), it should be stopped.
+ r.getStack().ensureActivitiesVisible(null /* starting */, 0 /* configChanges */,
+ false /* preserveWindows */);
}
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
index d61d29d1084e..2fb0ac5fbeaa 100644
--- a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
+++ b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
@@ -52,7 +52,6 @@ import android.os.UserHandle;
import android.os.UserManager;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.app.BlockedAppActivity;
import com.android.internal.app.HarmfulAppWarningActivity;
import com.android.internal.app.SuspendedAppActivity;
import com.android.internal.app.UnlaunchableAppActivity;
@@ -167,9 +166,6 @@ class ActivityStartInterceptor {
// no user action can undo this.
return true;
}
- if (interceptLockTaskModeViolationPackageIfNeeded()) {
- return true;
- }
if (interceptHarmfulAppIfNeeded()) {
// If the app has a "harmful app" warning associated with it, we should ask to uninstall
// before issuing the work challenge.
@@ -178,6 +174,11 @@ class ActivityStartInterceptor {
return interceptWorkProfileChallengeIfNeeded();
}
+ private boolean hasCrossProfileAnimation() {
+ return mActivityOptions != null
+ && mActivityOptions.getAnimationType() == ANIM_OPEN_CROSS_PROFILE_APPS;
+ }
+
/**
* If the activity option is the {@link ActivityOptions#ANIM_OPEN_CROSS_PROFILE_APPS} one,
* defer the animation until the original intent is started.
@@ -185,8 +186,7 @@ class ActivityStartInterceptor {
* @return the activity option used to start the original intent.
*/
private Bundle deferCrossProfileAppsAnimationIfNecessary() {
- if (mActivityOptions != null
- && mActivityOptions.getAnimationType() == ANIM_OPEN_CROSS_PROFILE_APPS) {
+ if (hasCrossProfileAnimation()) {
mActivityOptions = null;
return ActivityOptions.makeOpenCrossProfileAppsAnimation().toBundle();
}
@@ -255,28 +255,13 @@ class ActivityStartInterceptor {
}
final SuspendDialogInfo dialogInfo = pmi.getSuspendedDialogInfo(suspendedPackage,
suspendingPackage, mUserId);
+ final Bundle crossProfileOptions = hasCrossProfileAnimation()
+ ? ActivityOptions.makeOpenCrossProfileAppsAnimation().toBundle()
+ : null;
+ final IntentSender target = createIntentSenderForOriginalIntent(mCallingUid,
+ FLAG_IMMUTABLE);
mIntent = SuspendedAppActivity.createSuspendedAppInterceptIntent(suspendedPackage,
- suspendingPackage, dialogInfo, deferCrossProfileAppsAnimationIfNecessary(),
- mUserId);
- mCallingPid = mRealCallingPid;
- mCallingUid = mRealCallingUid;
- mResolvedType = null;
- mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, mUserId, 0, mRealCallingUid);
- mAInfo = mSupervisor.resolveActivity(mIntent, mRInfo, mStartFlags, null /*profilerInfo*/);
- return true;
- }
-
- private boolean interceptLockTaskModeViolationPackageIfNeeded() {
- if (mAInfo == null || mAInfo.applicationInfo == null) {
- return false;
- }
- LockTaskController controller = mService.getLockTaskController();
- String packageName = mAInfo.applicationInfo.packageName;
- int lockTaskLaunchMode = ActivityRecord.getLockTaskLaunchMode(mAInfo, mActivityOptions);
- if (controller.isActivityAllowed(mUserId, packageName, lockTaskLaunchMode)) {
- return false;
- }
- mIntent = BlockedAppActivity.createIntent(mUserId, mAInfo.applicationInfo.packageName);
+ suspendingPackage, dialogInfo, crossProfileOptions, target, mUserId);
mCallingPid = mRealCallingPid;
mCallingUid = mRealCallingUid;
mResolvedType = null;
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 31b7c688d685..976fbdb36bba 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -1043,6 +1043,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
public final int startActivities(IApplicationThread caller, String callingPackage,
Intent[] intents, String[] resolvedTypes, IBinder resultTo, Bundle bOptions,
int userId) {
+ assertPackageMatchesCallingUid(callingPackage);
final String reason = "startActivities";
enforceNotIsolatedCaller(reason);
userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), userId, reason);
@@ -1062,10 +1063,11 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
true /*validateIncomingUser*/);
}
- int startActivityAsUser(IApplicationThread caller, String callingPackage,
+ private int startActivityAsUser(IApplicationThread caller, String callingPackage,
Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId,
boolean validateIncomingUser) {
+ assertPackageMatchesCallingUid(callingPackage);
enforceNotIsolatedCaller("startActivityAsUser");
userId = getActivityStartController().checkTargetUser(userId, validateIncomingUser,
@@ -1238,6 +1240,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
public final WaitResult startActivityAndWait(IApplicationThread caller, String callingPackage,
Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId) {
+ assertPackageMatchesCallingUid(callingPackage);
final WaitResult res = new WaitResult();
enforceNotIsolatedCaller("startActivityAndWait");
userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
@@ -1263,6 +1266,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
public final int startActivityWithConfig(IApplicationThread caller, String callingPackage,
Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
int startFlags, Configuration config, Bundle bOptions, int userId) {
+ assertPackageMatchesCallingUid(callingPackage);
enforceNotIsolatedCaller("startActivityWithConfig");
userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), userId,
"startActivityWithConfig");
@@ -1447,6 +1451,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
Intent intent, String resolvedType, IVoiceInteractionSession session,
IVoiceInteractor interactor, int startFlags, ProfilerInfo profilerInfo,
Bundle bOptions, int userId) {
+ assertPackageMatchesCallingUid(callingPackage);
mAmInternal.enforceCallingPermission(BIND_VOICE_INTERACTION, "startVoiceActivity()");
if (session == null || interactor == null) {
throw new NullPointerException("null session or interactor");
@@ -1470,6 +1475,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
@Override
public int startAssistantActivity(String callingPackage, int callingPid, int callingUid,
Intent intent, String resolvedType, Bundle bOptions, int userId) {
+ assertPackageMatchesCallingUid(callingPackage);
mAmInternal.enforceCallingPermission(BIND_VOICE_INTERACTION, "startAssistantActivity()");
userId = handleIncomingUser(callingPid, callingUid, userId, "startAssistantActivity");
@@ -1825,21 +1831,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
Binder.restoreCallingIdentity(origId);
}
- public final void activitySlept(IBinder token) {
- if (DEBUG_ALL) Slog.v(TAG, "Activity slept: token=" + token);
-
- final long origId = Binder.clearCallingIdentity();
-
- synchronized (mGlobalLock) {
- final ActivityRecord r = ActivityRecord.isInStackLocked(token);
- if (r != null) {
- mStackSupervisor.activitySleptLocked(r);
- }
- }
-
- Binder.restoreCallingIdentity(origId);
- }
-
@Override
public void setRequestedOrientation(IBinder token, int requestedOrientation) {
synchronized (mGlobalLock) {
@@ -2391,15 +2382,9 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
void moveTaskToFrontLocked(@Nullable IApplicationThread appThread,
@Nullable String callingPackage, int taskId, int flags, SafeActivityOptions options,
boolean fromRecents) {
-
final int callingPid = Binder.getCallingPid();
final int callingUid = Binder.getCallingUid();
- if (!isSameApp(callingUid, callingPackage)) {
- String msg = "Permission Denial: moveTaskToFrontLocked() from pid="
- + Binder.getCallingPid() + " as package " + callingPackage;
- Slog.w(TAG, msg);
- throw new SecurityException(msg);
- }
+ assertPackageMatchesCallingUid(callingPackage);
if (!checkAppSwitchAllowedLocked(callingPid, callingUid, -1, -1, "Task to front")) {
SafeActivityOptions.abort(options);
return;
@@ -2451,7 +2436,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
/**
* Return true if callingUid is system, or packageName belongs to that callingUid.
*/
- boolean isSameApp(int callingUid, @Nullable String packageName) {
+ private boolean isSameApp(int callingUid, @Nullable String packageName) {
try {
if (callingUid != 0 && callingUid != SYSTEM_UID) {
if (packageName == null) {
@@ -2468,6 +2453,21 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
return true;
}
+ /**
+ * Checks that the provided package name matches the current calling UID, throws a security
+ * exception if it doesn't.
+ */
+ void assertPackageMatchesCallingUid(@Nullable String packageName) {
+ final int callingUid = Binder.getCallingUid();
+ if (isSameApp(callingUid, packageName)) {
+ return;
+ }
+ final String msg = "Permission Denial: package=" + packageName
+ + " does not belong to uid=" + callingUid;
+ Slog.w(TAG, msg);
+ throw new SecurityException(msg);
+ }
+
boolean checkAppSwitchAllowedLocked(int sourcePid, int sourceUid,
int callingPid, int callingUid, String name) {
if (mAppSwitchesAllowedTime < SystemClock.uptimeMillis()) {
@@ -3033,6 +3033,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
@Override
public List<IBinder> getAppTasks(String callingPackage) {
int callingUid = Binder.getCallingUid();
+ assertPackageMatchesCallingUid(callingPackage);
long ident = Binder.clearCallingIdentity();
try {
synchronized (mGlobalLock) {
@@ -3303,7 +3304,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
}
- private int sanitizeAndApplyChange(ConfigurationContainer container,
+ private int sanitizeAndApplyChange(WindowContainer container,
WindowContainerTransaction.Change change) {
if (!(container instanceof Task || container instanceof ActivityStack)) {
throw new RuntimeException("Invalid token in task transaction");
@@ -3347,13 +3348,13 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
}
- private int applyWindowContainerChange(ConfigurationContainer cc,
+ private int applyWindowContainerChange(WindowContainer wc,
WindowContainerTransaction.Change c) {
- int effects = sanitizeAndApplyChange(cc, c);
+ int effects = sanitizeAndApplyChange(wc, c);
Rect enterPipBounds = c.getEnterPipBounds();
if (enterPipBounds != null) {
- Task tr = (Task) cc;
+ Task tr = (Task) wc;
mStackSupervisor.updatePictureInPictureMode(tr,
enterPipBounds, true);
}
@@ -3378,17 +3379,14 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
while (entries.hasNext()) {
final Map.Entry<IBinder, WindowContainerTransaction.Change> entry =
entries.next();
- final ConfigurationContainer cc =
- ConfigurationContainer.RemoteToken.fromBinder(
- entry.getKey()).getContainer();
- int containerEffect = applyWindowContainerChange(cc, entry.getValue());
+ final WindowContainer wc = WindowContainer.RemoteToken.fromBinder(
+ entry.getKey()).getContainer();
+ int containerEffect = applyWindowContainerChange(wc, entry.getValue());
effects |= containerEffect;
// Lifecycle changes will trigger ensureConfig for everything.
if ((effects & TRANSACT_EFFECTS_LIFECYCLE) == 0
&& (containerEffect & TRANSACT_EFFECTS_CLIENT_CONFIG) != 0) {
- if (cc instanceof WindowContainer) {
- haveConfigChanges.add((WindowContainer) cc);
- }
+ haveConfigChanges.add(wc);
}
}
if ((effects & TRANSACT_EFFECTS_LIFECYCLE) != 0) {
@@ -6332,6 +6330,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
SafeActivityOptions options, int userId, boolean validateIncomingUser,
PendingIntentRecord originatingPendingIntent,
boolean allowBackgroundActivityStart) {
+ assertPackageMatchesCallingUid(callingPackage);
synchronized (mGlobalLock) {
return getActivityStartController().startActivitiesInPackage(uid, realCallingPid,
realCallingUid, callingPackage, intents, resolvedTypes, resultTo, options,
@@ -6347,6 +6346,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
int userId, Task inTask, String reason, boolean validateIncomingUser,
PendingIntentRecord originatingPendingIntent,
boolean allowBackgroundActivityStart) {
+ assertPackageMatchesCallingUid(callingPackage);
synchronized (mGlobalLock) {
return getActivityStartController().startActivityInPackage(uid, realCallingPid,
realCallingUid, callingPackage, intent, resolvedType, resultTo, resultWho,
diff --git a/services/core/java/com/android/server/wm/AppTaskImpl.java b/services/core/java/com/android/server/wm/AppTaskImpl.java
index 357f9e5bec6c..16a75645f9ae 100644
--- a/services/core/java/com/android/server/wm/AppTaskImpl.java
+++ b/services/core/java/com/android/server/wm/AppTaskImpl.java
@@ -27,7 +27,6 @@ import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
import android.os.UserHandle;
-import android.util.Slog;
/**
* An implementation of IAppTask, that allows an app to manage its own tasks via
@@ -97,12 +96,7 @@ class AppTaskImpl extends IAppTask.Stub {
// Will bring task to front if it already has a root activity.
final int callingPid = Binder.getCallingPid();
final int callingUid = Binder.getCallingUid();
- if (!mService.isSameApp(callingUid, callingPackage)) {
- String msg = "Permission Denial: moveToFront() from pid="
- + Binder.getCallingPid() + " as package " + callingPackage;
- Slog.w(TAG, msg);
- throw new SecurityException(msg);
- }
+ mService.assertPackageMatchesCallingUid(callingPackage);
final long origId = Binder.clearCallingIdentity();
try {
synchronized (mService.mGlobalLock) {
@@ -134,6 +128,7 @@ class AppTaskImpl extends IAppTask.Stub {
public int startActivity(IBinder whoThread, String callingPackage,
Intent intent, String resolvedType, Bundle bOptions) {
checkCaller();
+ mService.assertPackageMatchesCallingUid(callingPackage);
int callingUser = UserHandle.getCallingUserId();
Task task;
diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java
index 7b23e2d383b6..9bd380a95ad0 100644
--- a/services/core/java/com/android/server/wm/ConfigurationContainer.java
+++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java
@@ -39,15 +39,11 @@ import android.app.WindowConfiguration;
import android.content.res.Configuration;
import android.graphics.Point;
import android.graphics.Rect;
-import android.os.IBinder;
import android.util.proto.ProtoOutputStream;
-import android.view.IWindowContainer;
-import android.view.SurfaceControl;
import com.android.internal.annotations.VisibleForTesting;
import java.io.PrintWriter;
-import java.lang.ref.WeakReference;
import java.util.ArrayList;
/**
@@ -104,12 +100,6 @@ public abstract class ConfigurationContainer<E extends ConfigurationContainer> {
static final int BOUNDS_CHANGE_SIZE = 1 << 1;
/**
- * Used as a unique, cross-process identifier for this Container. It also serves a minimal
- * interface to other processes.
- */
- RemoteToken mRemoteToken = null;
-
- /**
* Returns full configuration applied to this configuration container.
* This method should be used for getting settings applied in each particular level of the
* hierarchy.
@@ -629,21 +619,6 @@ public abstract class ConfigurationContainer<E extends ConfigurationContainer> {
return mFullConfiguration.windowConfiguration.isAlwaysOnTop();
}
- /**
- * Returns {@code true} if this container is focusable. Generally, if a parent is not focusable,
- * this will not be focusable either.
- */
- boolean isFocusable() {
- // TODO(split): Move this to WindowContainer once Split-screen is based on a WindowContainer
- // like DisplayArea vs. TaskTiles.
- ConfigurationContainer parent = getParent();
- return parent == null || parent.isFocusable();
- }
-
- boolean setFocusable(boolean focusable) {
- return false;
- }
-
boolean hasChild() {
return getChildCount() > 0;
}
@@ -654,40 +629,4 @@ public abstract class ConfigurationContainer<E extends ConfigurationContainer> {
abstract protected ConfigurationContainer getParent();
- // TODO: Consider moving to WindowContainer once hierarchies and Task/Stack are merged.
- static class RemoteToken extends IWindowContainer.Stub {
- final WeakReference<ConfigurationContainer> mWeakRef;
-
- RemoteToken(ConfigurationContainer container) {
- mWeakRef = new WeakReference<>(container);
- }
-
- ConfigurationContainer getContainer() {
- return mWeakRef.get();
- }
-
- static RemoteToken fromBinder(IBinder binder) {
- return (RemoteToken) binder;
- }
-
- @Override
- public SurfaceControl getLeash() {
- throw new RuntimeException("Not implemented");
- }
-
- @Override
- public String toString() {
- StringBuilder sb = new StringBuilder(128);
- sb.append("RemoteToken{");
- sb.append(Integer.toHexString(System.identityHashCode(this)));
- sb.append(' ');
- sb.append(mWeakRef.get());
- sb.append('}');
- return sb.toString();
- }
- }
-
- RemoteToken getRemoteToken() {
- return mRemoteToken;
- }
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 825f93cde52b..36bee87a4ed3 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -3116,6 +3116,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
}
}
+ onWindowFocusChanged(oldFocus, newFocus);
+
int focusChanged = getDisplayPolicy().focusChangedLw(oldFocus, newFocus);
if (imWindowChanged && oldFocus != mInputMethodWindow) {
@@ -3158,6 +3160,20 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
return true;
}
+ private static void onWindowFocusChanged(WindowState oldFocus, WindowState newFocus) {
+ final Task focusedTask = newFocus != null ? newFocus.getTask() : null;
+ final Task unfocusedTask = oldFocus != null ? oldFocus.getTask() : null;
+ if (focusedTask == unfocusedTask) {
+ return;
+ }
+ if (focusedTask != null) {
+ focusedTask.onWindowFocusChanged(true /* hasFocus */);
+ }
+ if (unfocusedTask != null) {
+ unfocusedTask.onWindowFocusChanged(false /* hasFocus */);
+ }
+ }
+
/**
* Set the new focused app to this display.
*
diff --git a/services/core/java/com/android/server/wm/LockTaskController.java b/services/core/java/com/android/server/wm/LockTaskController.java
index 33b0453a25ee..02413bb48518 100644
--- a/services/core/java/com/android/server/wm/LockTaskController.java
+++ b/services/core/java/com/android/server/wm/LockTaskController.java
@@ -23,8 +23,6 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.content.Context.DEVICE_POLICY_SERVICE;
import static android.content.Context.STATUS_BAR_SERVICE;
import static android.content.Intent.ACTION_CALL_EMERGENCY;
-import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_ALWAYS;
-import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_NEVER;
import static android.os.UserHandle.USER_ALL;
import static android.os.UserHandle.USER_CURRENT;
import static android.telecom.TelecomManager.EMERGENCY_DIALER_COMPONENT;
@@ -341,20 +339,6 @@ public class LockTaskController {
& DevicePolicyManager.LOCK_TASK_FEATURE_KEYGUARD) != 0;
}
- boolean isActivityAllowed(int userId, String packageName, int lockTaskLaunchMode) {
- if (mLockTaskModeState != LOCK_TASK_MODE_LOCKED) {
- return true;
- }
- switch (lockTaskLaunchMode) {
- case LOCK_TASK_LAUNCH_MODE_ALWAYS:
- return true;
- case LOCK_TASK_LAUNCH_MODE_NEVER:
- return false;
- default:
- }
- return isPackageWhitelisted(userId, packageName);
- }
-
private boolean isEmergencyCallTask(Task task) {
final Intent intent = task.intent;
if (intent == null) {
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 0b54245cd424..e6fd512df72c 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -164,7 +164,6 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.HashMap;
-import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.Set;
@@ -2348,18 +2347,6 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
}
}
}
-
- if (displayShouldSleep || mStackSupervisor.mGoingToSleepActivities.isEmpty()) {
- continue;
- }
- // The display is awake now, so clean up the going to sleep list.
- for (Iterator<ActivityRecord> it =
- mStackSupervisor.mGoingToSleepActivities.iterator(); it.hasNext(); ) {
- final ActivityRecord r = it.next();
- if (r.getDisplayId() == display.mDisplayId) {
- it.remove();
- }
- }
}
}
@@ -3578,9 +3565,6 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
printed |= dumpHistoryList(fd, pw, mStackSupervisor.mStoppingActivities, " ",
"Stop", false, !dumpAll,
false, dumpPackage, true, " Activities waiting to stop:", null);
- printed |= dumpHistoryList(fd, pw, mStackSupervisor.mGoingToSleepActivities,
- " ", "Sleep", false, !dumpAll,
- false, dumpPackage, true, " Activities waiting to sleep:", null);
return printed;
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 917b437c8244..19461c52bd9e 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -16,13 +16,13 @@
package com.android.server.wm;
-import static android.app.ActivityTaskManager.INVALID_STACK_ID;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.app.ActivityTaskManager.RESIZE_MODE_FORCED;
import static android.app.ActivityTaskManager.RESIZE_MODE_SYSTEM;
import static android.app.ActivityTaskManager.RESIZE_MODE_SYSTEM_SCREEN_ROTATION;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.app.WindowConfiguration.PINNED_WINDOWING_MODE_ELEVATION_IN_DIP;
import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
@@ -59,6 +59,8 @@ import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
import static android.view.SurfaceControl.METADATA_TASK_ID;
+import static com.android.internal.policy.DecorView.DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP;
+import static com.android.internal.policy.DecorView.DECOR_SHADOW_UNFOCUSED_HEIGHT_IN_DIP;
import static com.android.server.am.TaskRecordProto.ACTIVITIES;
import static com.android.server.am.TaskRecordProto.ACTIVITY_TYPE;
import static com.android.server.am.TaskRecordProto.FULLSCREEN;
@@ -99,6 +101,7 @@ import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STACK;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_MOVEMENT;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+import static com.android.server.wm.WindowManagerService.dipToPixel;
import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_BEFORE_ANIM;
import static java.lang.Integer.MAX_VALUE;
@@ -210,6 +213,8 @@ class Task extends WindowContainer<WindowContainer> {
static final int PERSIST_TASK_VERSION = 1;
static final int INVALID_MIN_SIZE = -1;
+ private float mShadowRadius = 0;
+ private final Rect mLastSurfaceCrop = new Rect();
/**
* The modes to control how the stack is moved to the front when calling {@link Task#reparent}.
@@ -496,7 +501,7 @@ class Task extends WindowContainer<WindowContainer> {
}
class TaskToken extends RemoteToken {
- TaskToken(ConfigurationContainer container) {
+ TaskToken(WindowContainer container) {
super(container);
}
@@ -1869,6 +1874,7 @@ class Task extends WindowContainer<WindowContainer> {
super.onConfigurationChanged(newParentConfig);
if (wasInMultiWindowMode != inMultiWindowMode()) {
mStackSupervisor.scheduleUpdateMultiWindowMode(this);
+ updateShadowsRadius(isFocused(), getPendingTransaction());
}
// If the configuration supports persistent bounds (eg. Freeform), keep track of the
@@ -2527,18 +2533,36 @@ class Task extends WindowContainer<WindowContainer> {
? getStack().getDisplayContent() : null;
if (displayContent != null) {
rotation = displayContent.getDisplayInfo().rotation;
- } else if (bounds == null) {
- return super.setBounds(bounds);
}
final int boundsChange = super.setBounds(bounds);
-
mRotation = rotation;
-
updateSurfacePosition();
return boundsChange;
}
+ private void updateSurfaceCrop() {
+ // Only update the crop if we are drawing shadows on the task.
+ if (mSurfaceControl == null || !mWmService.mRenderShadowsInCompositor) {
+ return;
+ }
+
+ if (inSplitScreenWindowingMode()) {
+ // inherit crop from parent
+ mTmpRect.setEmpty();
+ } else {
+ getBounds(mTmpRect);
+ }
+
+ mTmpRect.offsetTo(0, 0);
+ if (mLastSurfaceCrop.equals(mTmpRect)) {
+ return;
+ }
+
+ getPendingTransaction().setWindowCrop(mSurfaceControl, mTmpRect);
+ mLastSurfaceCrop.set(mTmpRect);
+ }
+
@Override
public boolean onDescendantOrientationChanged(IBinder freezeDisplayToken,
ConfigurationContainer requestingContainer) {
@@ -2724,6 +2748,7 @@ class Task extends WindowContainer<WindowContainer> {
boolean[] foundTop = { false };
final PooledConsumer c = PooledLambda.obtainConsumer(Task::getMaxVisibleBounds,
PooledLambda.__(ActivityRecord.class), out, foundTop);
+ forAllActivities(c);
c.recycle();
if (foundTop[0]) {
return;
@@ -3126,6 +3151,9 @@ class Task extends WindowContainer<WindowContainer> {
} else {
mTmpDimBoundsRect.offsetTo(0, 0);
}
+
+ updateSurfaceCrop();
+
if (mDimmer.updateDims(getPendingTransaction(), mTmpDimBoundsRect)) {
scheduleAnimation();
}
@@ -3925,4 +3953,58 @@ class Task extends WindowContainer<WindowContainer> {
setTaskOrganizer(org);
}
}
+
+ /**
+ * @return true if the task is currently focused.
+ */
+ private boolean isFocused() {
+ if (mDisplayContent == null || mDisplayContent.mCurrentFocus == null) {
+ return false;
+ }
+ return mDisplayContent.mCurrentFocus.getTask() == this;
+ }
+
+ /**
+ * @return the desired shadow radius in pixels for the current task.
+ */
+ private float getShadowRadius(boolean taskIsFocused) {
+ if (mDisplayContent == null) {
+ return 0;
+ }
+
+ if (inPinnedWindowingMode()) {
+ return dipToPixel(PINNED_WINDOWING_MODE_ELEVATION_IN_DIP,
+ mDisplayContent.getDisplayMetrics());
+ }
+ if (inFreeformWindowingMode()) {
+ final int elevation = taskIsFocused
+ ? DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP : DECOR_SHADOW_UNFOCUSED_HEIGHT_IN_DIP;
+ return dipToPixel(elevation, mDisplayContent.getDisplayMetrics());
+ }
+
+ // For all other windowing modes, do not draw a shadow.
+ return 0;
+ }
+
+ /**
+ * Update the length of the shadow if needed based on windowing mode and task focus state.
+ */
+ private void updateShadowsRadius(boolean taskIsFocused,
+ SurfaceControl.Transaction pendingTransaction) {
+ if (!mWmService.mRenderShadowsInCompositor) return;
+
+ final float newShadowRadius = getShadowRadius(taskIsFocused);
+ if (mShadowRadius != newShadowRadius) {
+ mShadowRadius = newShadowRadius;
+ pendingTransaction.setShadowRadius(getSurfaceControl(), mShadowRadius);
+ }
+ }
+
+ /**
+ * Called on the task of a window which gained or lost focus.
+ * @param hasFocus
+ */
+ void onWindowFocusChanged(boolean hasFocus) {
+ updateShadowsRadius(hasFocus, getPendingTransaction());
+ }
}
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java
index 4cb5de44bef0..10d6823c850f 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotController.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java
@@ -343,6 +343,7 @@ class TaskSnapshotController {
builder.setPixelFormat(pixelFormat);
builder.setIsTranslucent(isTranslucent);
builder.setOrientation(activity.getTask().getConfiguration().orientation);
+ builder.setRotation(activity.getTask().getDisplayContent().getRotation());
builder.setWindowingMode(task.getWindowingMode());
builder.setSystemUiVisibility(getSystemUiVisibility(task));
return true;
@@ -492,7 +493,8 @@ class TaskSnapshotController {
return new TaskSnapshot(
System.currentTimeMillis() /* id */,
topChild.mActivityComponent, hwBitmap.createGraphicBufferHandle(),
- hwBitmap.getColorSpace(), topChild.getTask().getConfiguration().orientation,
+ hwBitmap.getColorSpace(), mainWindow.getConfiguration().orientation,
+ mainWindow.getWindowConfiguration().getRotation(),
getInsets(mainWindow), ActivityManager.isLowRamDeviceStatic() /* reduced */,
mFullSnapshotScale, false /* isRealSnapshot */, task.getWindowingMode(),
getSystemUiVisibility(task), false);
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotLoader.java b/services/core/java/com/android/server/wm/TaskSnapshotLoader.java
index 22c1ea59d176..6e9986ffd411 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotLoader.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotLoader.java
@@ -102,8 +102,8 @@ class TaskSnapshotLoader {
// For legacy snapshots, restore the scale based on the reduced resolution state
final float legacyScale = reducedResolution ? mPersister.getReducedScale() : 1f;
final float scale = Float.compare(proto.scale, 0f) != 0 ? proto.scale : legacyScale;
- return new TaskSnapshot(proto.id, topActivityComponent, buffer,
- hwBitmap.getColorSpace(), proto.orientation,
+ return new TaskSnapshot(proto.id, topActivityComponent, buffer, hwBitmap.getColorSpace(),
+ proto.orientation, proto.rotation,
new Rect(proto.insetLeft, proto.insetTop, proto.insetRight, proto.insetBottom),
reducedResolution, scale, proto.isRealSnapshot, proto.windowingMode,
proto.systemUiVisibility, proto.isTranslucent);
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotPersister.java b/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
index 38a7000803bd..ee5098b6c699 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
@@ -52,7 +52,6 @@ class TaskSnapshotPersister {
private static final String TAG = TAG_WITH_CLASS_NAME ? "TaskSnapshotPersister" : TAG_WM;
private static final String SNAPSHOTS_DIRNAME = "snapshots";
private static final String REDUCED_POSTFIX = "_reduced";
- private static final float REDUCED_SCALE = .5f;
private static final float LOW_RAM_REDUCED_SCALE = .8f;
static final boolean DISABLE_FULL_SIZED_BITMAPS = ActivityManager.isLowRamDeviceStatic();
private static final long DELAY_MS = 100;
@@ -84,8 +83,13 @@ class TaskSnapshotPersister {
TaskSnapshotPersister(WindowManagerService service, DirectoryResolver resolver) {
mDirectoryResolver = resolver;
- mReducedScale = ActivityManager.isLowRamDeviceStatic()
- ? LOW_RAM_REDUCED_SCALE : REDUCED_SCALE;
+
+ if (ActivityManager.isLowRamDeviceStatic()) {
+ mReducedScale = LOW_RAM_REDUCED_SCALE;
+ } else {
+ mReducedScale = service.mContext.getResources().getFloat(
+ com.android.internal.R.dimen.config_reducedTaskSnapshotScale);
+ }
mUse16BitFormat = service.mContext.getResources().getBoolean(
com.android.internal.R.bool.config_use16BitTaskSnapshotPixelFormat);
}
@@ -338,6 +342,7 @@ class TaskSnapshotPersister {
boolean writeProto() {
final TaskSnapshotProto proto = new TaskSnapshotProto();
proto.orientation = mSnapshot.getOrientation();
+ proto.rotation = mSnapshot.getRotation();
proto.insetLeft = mSnapshot.getContentInsets().left;
proto.insetTop = mSnapshot.getContentInsets().top;
proto.insetRight = mSnapshot.getContentInsets().right;
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 1c876d9cc02f..0ab5f91f0ce9 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -61,6 +61,7 @@ import android.util.Pools;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
import android.view.DisplayInfo;
+import android.view.IWindowContainer;
import android.view.MagnificationSpec;
import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
@@ -75,6 +76,7 @@ import com.android.server.protolog.common.ProtoLog;
import com.android.server.wm.SurfaceAnimator.Animatable;
import java.io.PrintWriter;
+import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.LinkedList;
@@ -249,6 +251,12 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
private boolean mIsFocusable = true;
+ /**
+ * Used as a unique, cross-process identifier for this Container. It also serves a minimal
+ * interface to other processes.
+ */
+ RemoteToken mRemoteToken = null;
+
WindowContainer(WindowManagerService wms) {
mWmService = wms;
mPendingTransaction = wms.mTransactionFactory.get();
@@ -860,13 +868,16 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
return false;
}
- @Override
+ /**
+ * Returns {@code true} if this container is focusable. Generally, if a parent is not focusable,
+ * this will not be focusable either.
+ */
boolean isFocusable() {
- return super.isFocusable() && mIsFocusable;
+ final WindowContainer parent = getParent();
+ return (parent == null || parent.isFocusable()) && mIsFocusable;
}
/** Set whether this container or its children can be focusable */
- @Override
boolean setFocusable(boolean focusable) {
if (mIsFocusable == focusable) {
return false;
@@ -2259,4 +2270,40 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
ActivityRecord asActivityRecord() {
return null;
}
+
+ RemoteToken getRemoteToken() {
+ return mRemoteToken;
+ }
+
+ static class RemoteToken extends IWindowContainer.Stub {
+ final WeakReference<WindowContainer> mWeakRef;
+
+ RemoteToken(WindowContainer container) {
+ mWeakRef = new WeakReference<>(container);
+ }
+
+ WindowContainer getContainer() {
+ return mWeakRef.get();
+ }
+
+ static RemoteToken fromBinder(IBinder binder) {
+ return (RemoteToken) binder;
+ }
+
+ @Override
+ public SurfaceControl getLeash() {
+ throw new RuntimeException("Not implemented");
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder(128);
+ sb.append("RemoteToken{");
+ sb.append(Integer.toHexString(System.identityHashCode(this)));
+ sb.append(' ');
+ sb.append(mWeakRef.get());
+ sb.append('}');
+ return sb.toString();
+ }
+ }
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 8d4ad28972e9..1360711ec97e 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -39,6 +39,7 @@ import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDO
import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_SIZECOMPAT_FREEFORM;
import static android.provider.Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS;
import static android.provider.Settings.Global.DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES;
+import static android.provider.Settings.Global.DEVELOPMENT_RENDER_SHADOWS_IN_COMPOSITOR;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
import static android.view.WindowManager.DOCKED_INVALID;
@@ -122,6 +123,7 @@ import android.animation.ValueAnimator;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
import android.app.ActivityManager;
import android.app.ActivityManager.TaskSnapshot;
import android.app.ActivityManagerInternal;
@@ -420,6 +422,11 @@ public class WindowManagerService extends IWindowManager.Stub
int mVr2dDisplayId = INVALID_DISPLAY;
boolean mVrModeEnabled = false;
+ /* If true, shadows drawn around the window will be rendered by the system compositor. If
+ * false, shadows will be drawn by the client by setting an elevation on the root view and
+ * the contents will be inset by the shadow radius. */
+ boolean mRenderShadowsInCompositor = false;
+
/**
* Tracks a map of input tokens to info that is used to decide whether to intercept
* a key event.
@@ -721,6 +728,8 @@ public class WindowManagerService extends IWindowManager.Stub
DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES);
private final Uri mSizeCompatFreeformUri = Settings.Global.getUriFor(
DEVELOPMENT_ENABLE_SIZECOMPAT_FREEFORM);
+ private final Uri mRenderShadowsInCompositorUri = Settings.Global.getUriFor(
+ DEVELOPMENT_RENDER_SHADOWS_IN_COMPOSITOR);
public SettingsObserver() {
super(new Handler());
@@ -743,6 +752,8 @@ public class WindowManagerService extends IWindowManager.Stub
resolver.registerContentObserver(mForceResizableUri, false, this, UserHandle.USER_ALL);
resolver.registerContentObserver(mSizeCompatFreeformUri, false, this,
UserHandle.USER_ALL);
+ resolver.registerContentObserver(mRenderShadowsInCompositorUri, false, this,
+ UserHandle.USER_ALL);
}
@Override
@@ -781,6 +792,11 @@ public class WindowManagerService extends IWindowManager.Stub
return;
}
+ if (mRenderShadowsInCompositorUri.equals(uri)) {
+ setShadowRenderer();
+ return;
+ }
+
@UpdateAnimationScaleMode
final int mode;
if (mWindowAnimationScaleUri.equals(uri)) {
@@ -862,6 +878,11 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
+ private void setShadowRenderer() {
+ mRenderShadowsInCompositor = Settings.Global.getInt(mContext.getContentResolver(),
+ DEVELOPMENT_RENDER_SHADOWS_IN_COMPOSITOR, 0) != 0;
+ }
+
PowerManager mPowerManager;
PowerManagerInternal mPowerManagerInternal;
@@ -1248,6 +1269,7 @@ public class WindowManagerService extends IWindowManager.Stub
float[] spotColor = {0.f, 0.f, 0.f, spotShadowAlpha};
SurfaceControl.setGlobalShadowSettings(ambientColor, spotColor, lightY, lightZ,
lightRadius);
+ setShadowRenderer();
}
/**
@@ -2996,7 +3018,13 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
- void showGlobalActions() {
+ @RequiresPermission(Manifest.permission.INTERNAL_SYSTEM_WINDOW)
+ @Override
+ public void showGlobalActions() {
+ if (!checkCallingPermission(Manifest.permission.INTERNAL_SYSTEM_WINDOW,
+ "showGlobalActions()")) {
+ throw new SecurityException("Requires INTERNAL_SYSTEM_WINDOW permission");
+ }
mPolicy.showGlobalActions();
}
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index c0891d739788..212a3a638634 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -312,7 +312,6 @@ private:
void updateInactivityTimeoutLocked();
void handleInterceptActions(jint wmActions, nsecs_t when, uint32_t& policyFlags);
void ensureSpriteControllerLocked();
- const DisplayViewport* findDisplayViewportLocked(int32_t displayId);
int32_t getPointerDisplayId();
void updatePointerDisplayLocked();
static bool checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName);
@@ -390,16 +389,6 @@ bool NativeInputManager::checkAndClearExceptionFromCallback(JNIEnv* env, const c
return false;
}
-const DisplayViewport* NativeInputManager::findDisplayViewportLocked(int32_t displayId)
- REQUIRES(mLock) {
- for (const DisplayViewport& v : mLocked.viewports) {
- if (v.displayId == displayId) {
- return &v;
- }
- }
- return nullptr;
-}
-
void NativeInputManager::setDisplayViewports(JNIEnv* env, jobjectArray viewportObjArray) {
std::vector<DisplayViewport> viewports;
@@ -547,6 +536,8 @@ void NativeInputManager::getReaderConfiguration(InputReaderConfiguration* outCon
outConfig->setDisplayViewports(mLocked.viewports);
+ outConfig->defaultPointerDisplayId = mLocked.pointerDisplayId;
+
outConfig->disabledDevices = mLocked.disabledInputDevices;
} // release lock
}
@@ -564,8 +555,6 @@ sp<PointerControllerInterface> NativeInputManager::obtainPointerController(int32
updateInactivityTimeoutLocked();
}
- updatePointerDisplayLocked();
-
return controller;
}
@@ -580,23 +569,6 @@ int32_t NativeInputManager::getPointerDisplayId() {
return pointerDisplayId;
}
-void NativeInputManager::updatePointerDisplayLocked() REQUIRES(mLock) {
- ATRACE_CALL();
-
- sp<PointerController> controller = mLocked.pointerController.promote();
- if (controller != nullptr) {
- const DisplayViewport* viewport = findDisplayViewportLocked(mLocked.pointerDisplayId);
- if (viewport == nullptr) {
- ALOGW("Can't find pointer display viewport, fallback to default display.");
- viewport = findDisplayViewportLocked(ADISPLAY_ID_DEFAULT);
- }
-
- if (viewport != nullptr) {
- controller->setDisplayViewport(*viewport);
- }
- }
-}
-
void NativeInputManager::ensureSpriteControllerLocked() REQUIRES(mLock) {
if (mLocked.spriteController == nullptr) {
JNIEnv* env = jniEnv();
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index 00436bb8ca70..08a759227526 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -1208,6 +1208,22 @@ void GnssMeasurementCallback::translateSingleGnssMeasurement
translateSingleGnssMeasurement(&(measurement_V2_1->v2_0), object);
SET(BasebandCn0DbHz, measurement_V2_1->basebandCN0DbHz);
+
+ if (measurement_V2_1->flags & GnssMeasurementFlags::HAS_RECEIVER_ISB) {
+ SET(ReceiverInterSignalBiasNs, measurement_V2_1->receiverInterSignalBiasNs);
+ }
+
+ if (measurement_V2_1->flags & GnssMeasurementFlags::HAS_RECEIVER_ISB_UNCERTAINTY) {
+ SET(ReceiverInterSignalBiasUncertaintyNs, measurement_V2_1->receiverInterSignalBiasUncertaintyNs);
+ }
+
+ if (measurement_V2_1->flags & GnssMeasurementFlags::HAS_SATELLITE_ISB) {
+ SET(SatelliteInterSignalBiasNs, measurement_V2_1->satelliteInterSignalBiasNs);
+ }
+
+ if (measurement_V2_1->flags & GnssMeasurementFlags::HAS_SATELLITE_ISB_UNCERTAINTY) {
+ SET(SatelliteInterSignalBiasUncertaintyNs, measurement_V2_1->satelliteInterSignalBiasUncertaintyNs);
+ }
}
template<class T>
@@ -1253,6 +1269,19 @@ void GnssMeasurementCallback::translateGnssClock(
template<>
void GnssMeasurementCallback::translateGnssClock(
+ JavaObject& object, const IGnssMeasurementCallback_V2_1::GnssClock& clock) {
+ JNIEnv* env = getJniEnv();
+ SET(ReferenceConstellationTypeForIsb,
+ static_cast<int32_t>(clock.referenceSignalTypeForIsb.constellation));
+ SET(ReferenceCarrierFrequencyHzForIsb, clock.referenceSignalTypeForIsb.carrierFrequencyHz);
+ SET(ReferenceCodeTypeForIsb,
+ env->NewStringUTF(clock.referenceSignalTypeForIsb.codeType.c_str()));
+
+ translateGnssClock(object, clock.v1_0);
+}
+
+template<>
+void GnssMeasurementCallback::translateGnssClock(
JavaObject& object, const IGnssMeasurementCallback_V2_0::GnssData& data) {
auto elapsedRealtime = data.elapsedRealtime;
uint16_t flags = static_cast<uint16_t>(elapsedRealtime.flags);
@@ -1265,6 +1294,20 @@ void GnssMeasurementCallback::translateGnssClock(
translateGnssClock(object, data.clock);
}
+template<>
+void GnssMeasurementCallback::translateGnssClock(
+ JavaObject& object, const IGnssMeasurementCallback_V2_1::GnssData& data) {
+ auto elapsedRealtime = data.elapsedRealtime;
+ uint16_t flags = static_cast<uint16_t>(elapsedRealtime.flags);
+ if (flags & ElapsedRealtimeFlags::HAS_TIMESTAMP_NS) {
+ SET(ElapsedRealtimeNanos, static_cast<uint64_t>(elapsedRealtime.timestampNs));
+ }
+ if (flags & ElapsedRealtimeFlags::HAS_TIME_UNCERTAINTY_NS) {
+ SET(ElapsedRealtimeUncertaintyNanos, static_cast<double>(elapsedRealtime.timeUncertaintyNs));
+ }
+ translateGnssClock(object, data.clock);
+}
+
template<class T>
jobjectArray GnssMeasurementCallback::translateAllGnssMeasurements(JNIEnv* env,
const T* measurements,
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
index 43ee97dfa17b..9b85a7b55c94 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
@@ -75,4 +75,11 @@ abstract class BaseIDevicePolicyManager extends IDevicePolicyManager.Stub {
public void setPersonalAppsSuspended(ComponentName admin, boolean suspended) {
}
+
+ public void setManagedProfileMaximumTimeOff(ComponentName admin, long timeoutMs) {
+ }
+
+ public long getManagedProfileMaximumTimeOff(ComponentName admin) {
+ return 0;
+ }
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index da107d04b551..d7ea2f53c286 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -67,6 +67,9 @@ import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX;
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
+import static android.app.admin.DevicePolicyManager.PERSONAL_APPS_NOT_SUSPENDED;
+import static android.app.admin.DevicePolicyManager.PERSONAL_APPS_SUSPENDED_EXPLICITLY;
+import static android.app.admin.DevicePolicyManager.PERSONAL_APPS_SUSPENDED_PROFILE_TIMEOUT;
import static android.app.admin.DevicePolicyManager.PRIVATE_DNS_MODE_OFF;
import static android.app.admin.DevicePolicyManager.PRIVATE_DNS_MODE_OPPORTUNISTIC;
import static android.app.admin.DevicePolicyManager.PRIVATE_DNS_MODE_PROVIDER_HOSTNAME;
@@ -177,6 +180,7 @@ import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.location.LocationManager;
+import android.location.LocationManagerInternal;
import android.media.AudioManager;
import android.media.IAudioService;
import android.net.ConnectivityManager;
@@ -315,7 +319,6 @@ import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Function;
-import java.util.stream.Collectors;
/**
* Implementation of the device policy APIs.
@@ -378,16 +381,21 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
private static final String TAG_SECONDARY_LOCK_SCREEN = "secondary-lock-screen";
- private static final String TAG_PERSONAL_APPS_SUSPENDED = "personal-apps-suspended";
+ private static final String TAG_APPS_SUSPENDED = "apps-suspended";
private static final int REQUEST_EXPIRE_PASSWORD = 5571;
+ private static final int REQUEST_PROFILE_OFF_DEADLINE = 5572;
+
private static final long MS_PER_DAY = TimeUnit.DAYS.toMillis(1);
private static final long EXPIRATION_GRACE_PERIOD_MS = 5 * MS_PER_DAY; // 5 days, in ms
- private static final String ACTION_EXPIRED_PASSWORD_NOTIFICATION
- = "com.android.server.ACTION_EXPIRED_PASSWORD_NOTIFICATION";
+ private static final String ACTION_EXPIRED_PASSWORD_NOTIFICATION =
+ "com.android.server.ACTION_EXPIRED_PASSWORD_NOTIFICATION";
+
+ private static final String ACTION_PROFILE_OFF_DEADLINE =
+ "com.android.server.ACTION_PROFILE_OFF_DEADLINE";
private static final String ATTR_PERMISSION_PROVIDER = "permission-provider";
private static final String ATTR_SETUP_COMPLETE = "setup-complete";
@@ -799,9 +807,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
long mPasswordTokenHandle = 0;
- // Flag reflecting the current state of the personal apps suspension. This flag should
- // only be written AFTER all the needed apps were suspended or unsuspended.
- boolean mPersonalAppsSuspended = false;
+ // Whether user's apps are suspended. This flag should only be written AFTER all the needed
+ // apps were suspended or unsuspended.
+ boolean mAppsSuspended = false;
public DevicePolicyData(int userHandle) {
mUserHandle = userHandle;
@@ -848,7 +856,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
RemoteBugreportUtils.NOTIFICATION_ID,
RemoteBugreportUtils.buildNotification(mContext,
DevicePolicyManager.NOTIFICATION_BUGREPORT_FINISHED_NOT_ACCEPTED),
- UserHandle.ALL);
+ UserHandle.ALL);
}
if (Intent.ACTION_BOOT_COMPLETED.equals(action)
|| ACTION_EXPIRED_PASSWORD_NOTIFICATION.equals(action)) {
@@ -895,12 +903,20 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
handlePackagesChanged(null /* check all admins */, userHandle);
} else if (Intent.ACTION_USER_STOPPED.equals(action)) {
sendDeviceOwnerUserCommand(DeviceAdminReceiver.ACTION_USER_STOPPED, userHandle);
+ if (isManagedProfile(userHandle)) {
+ Slog.d(LOG_TAG, "Managed profile was stopped");
+ updatePersonalAppSuspension(userHandle, false /* profileIsOn */);
+ }
} else if (Intent.ACTION_USER_SWITCHED.equals(action)) {
sendDeviceOwnerUserCommand(DeviceAdminReceiver.ACTION_USER_SWITCHED, userHandle);
} else if (Intent.ACTION_USER_UNLOCKED.equals(action)) {
synchronized (getLockObject()) {
maybeSendAdminEnabledBroadcastLocked(userHandle);
}
+ if (isManagedProfile(userHandle)) {
+ Slog.d(LOG_TAG, "Managed profile became unlocked");
+ updatePersonalAppSuspension(userHandle, true /* profileIsOn */);
+ }
} else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
handlePackagesChanged(null /* check all admins */, userHandle);
} else if (Intent.ACTION_PACKAGE_CHANGED.equals(action)
@@ -918,8 +934,15 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
// (ACTION_DATE_CHANGED), or when manual clock adjustment is made
// (ACTION_TIME_CHANGED)
updateSystemUpdateFreezePeriodsRecord(/* saveIfChanged */ true);
+ } else if (ACTION_PROFILE_OFF_DEADLINE.equals(action)) {
+ Slog.i(LOG_TAG, "Profile off deadline alarm was triggered");
+ final int userId = getManagedUserId(UserHandle.USER_SYSTEM);
+ if (userId >= 0) {
+ updatePersonalAppSuspension(userId, mUserManager.isUserUnlocked(userId));
+ } else {
+ Slog.wtf(LOG_TAG, "Got deadline alarm for nonexistent profile");
+ }
}
-
}
private void sendDeviceOwnerUserCommand(String action, int userHandle) {
@@ -1033,6 +1056,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
private static final String TAG_FACTORY_RESET_PROTECTION_POLICY =
"factory_reset_protection_policy";
private static final String TAG_SUSPEND_PERSONAL_APPS = "suspend-personal-apps";
+ private static final String TAG_PROFILE_MAXIMUM_TIME_OFF = "profile-max-time-off";
+ private static final String TAG_PROFILE_OFF_DEADLINE = "profile-off-deadline";
DeviceAdminInfo info;
@@ -1157,6 +1182,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
// Whether the admin explicitly requires personal apps to be suspended
boolean mSuspendPersonalApps = false;
+ // Maximum time the profile owned by this admin can be off.
+ long mProfileMaximumTimeOff = 0;
+ // Time by which the profile should be turned on according to System.currentTimeMillis().
+ long mProfileOffDeadline = 0;
+
ActiveAdmin(DeviceAdminInfo _info, boolean parent) {
info = _info;
@@ -1391,6 +1421,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (mSuspendPersonalApps) {
writeAttributeValueToXml(out, TAG_SUSPEND_PERSONAL_APPS, mSuspendPersonalApps);
}
+ if (mProfileMaximumTimeOff != 0) {
+ writeAttributeValueToXml(out, TAG_PROFILE_MAXIMUM_TIME_OFF, mProfileMaximumTimeOff);
+ }
+ if (mProfileMaximumTimeOff != 0) {
+ writeAttributeValueToXml(out, TAG_PROFILE_OFF_DEADLINE, mProfileOffDeadline);
+ }
}
void writeTextToXml(XmlSerializer out, String tag, String text) throws IOException {
@@ -1630,6 +1666,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
} else if (TAG_SUSPEND_PERSONAL_APPS.equals(tag)) {
mSuspendPersonalApps = Boolean.parseBoolean(
parser.getAttributeValue(null, ATTR_VALUE));
+ } else if (TAG_PROFILE_MAXIMUM_TIME_OFF.equals(tag)) {
+ mProfileMaximumTimeOff =
+ Long.parseLong(parser.getAttributeValue(null, ATTR_VALUE));
+ } else if (TAG_PROFILE_OFF_DEADLINE.equals(tag)) {
+ mProfileOffDeadline =
+ Long.parseLong(parser.getAttributeValue(null, ATTR_VALUE));
} else {
Slog.w(LOG_TAG, "Unknown admin tag: " + tag);
XmlUtils.skipCurrentTag(parser);
@@ -1855,7 +1897,13 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
pw.println(mCrossProfileCalendarPackages);
}
pw.print("mCrossProfilePackages=");
- pw.println(mCrossProfilePackages);
+ pw.println(mCrossProfilePackages);
+ pw.print("mSuspendPersonalApps=");
+ pw.println(mSuspendPersonalApps);
+ pw.print("mProfileMaximumTimeOff=");
+ pw.println(mProfileMaximumTimeOff);
+ pw.print("mProfileOffDeadline=");
+ pw.println(mProfileOffDeadline);
}
}
@@ -2049,6 +2097,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return mContext.getSystemService(LocationManager.class);
}
+ LocationManagerInternal getLocationManagerInternal() {
+ return LocalServices.getService(LocationManagerInternal.class);
+ }
+
IWindowManager getIWindowManager() {
return IWindowManager.Stub
.asInterface(ServiceManager.getService(Context.WINDOW_SERVICE));
@@ -2373,12 +2425,14 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_BOOT_COMPLETED);
filter.addAction(ACTION_EXPIRED_PASSWORD_NOTIFICATION);
+ filter.addAction(ACTION_PROFILE_OFF_DEADLINE);
filter.addAction(Intent.ACTION_USER_ADDED);
filter.addAction(Intent.ACTION_USER_REMOVED);
filter.addAction(Intent.ACTION_USER_STARTED);
filter.addAction(Intent.ACTION_USER_STOPPED);
filter.addAction(Intent.ACTION_USER_SWITCHED);
filter.addAction(Intent.ACTION_USER_UNLOCKED);
+ filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
mContext.registerReceiverAsUser(mReceiver, UserHandle.ALL, filter, null, mHandler);
filter = new IntentFilter();
@@ -3436,11 +3490,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
out.endTag(null, TAG_PROTECTED_PACKAGES);
}
- if (policy.mPersonalAppsSuspended) {
- out.startTag(null, TAG_PERSONAL_APPS_SUSPENDED);
- out.attribute(null, ATTR_VALUE,
- Boolean.toString(policy.mPersonalAppsSuspended));
- out.endTag(null, TAG_PERSONAL_APPS_SUSPENDED);
+ if (policy.mAppsSuspended) {
+ out.startTag(null, TAG_APPS_SUSPENDED);
+ out.attribute(null, ATTR_VALUE, Boolean.toString(policy.mAppsSuspended));
+ out.endTag(null, TAG_APPS_SUSPENDED);
}
out.endTag(null, "policies");
@@ -3659,8 +3712,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
policy.mOwnerInstalledCaCerts.add(parser.getAttributeValue(null, ATTR_ALIAS));
} else if (TAG_PROTECTED_PACKAGES.equals(tag)) {
policy.mProtectedPackages.add(parser.getAttributeValue(null, ATTR_NAME));
- } else if (TAG_PERSONAL_APPS_SUSPENDED.equals(tag)) {
- policy.mPersonalAppsSuspended =
+ } else if (TAG_APPS_SUSPENDED.equals(tag)) {
+ policy.mAppsSuspended =
Boolean.parseBoolean(parser.getAttributeValue(null, ATTR_VALUE));
} else {
Slog.w(LOG_TAG, "Unknown tag: " + tag);
@@ -3802,7 +3855,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
synchronized (getLockObject()) {
maybeMigrateToProfileOnOrganizationOwnedDeviceLocked();
}
- checkPackageSuspensionOnBoot();
+ final int userId = getManagedUserId(UserHandle.USER_SYSTEM);
+ if (userId >= 0) {
+ updatePersonalAppSuspension(userId, false /* running */);
+ }
break;
case SystemService.PHASE_BOOT_COMPLETED:
ensureDeviceOwnerUserStarted(); // TODO Consider better place to do this.
@@ -3810,34 +3866,6 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
}
- private void checkPackageSuspensionOnBoot() {
- int profileUserId = UserHandle.USER_NULL;
- final boolean shouldSuspend;
- synchronized (getLockObject()) {
- for (final int userId : mOwners.getProfileOwnerKeys()) {
- if (mOwners.isProfileOwnerOfOrganizationOwnedDevice(userId)) {
- profileUserId = userId;
- break;
- }
- }
-
- if (profileUserId == UserHandle.USER_NULL) {
- shouldSuspend = false;
- } else {
- shouldSuspend = getProfileOwnerAdminLocked(profileUserId).mSuspendPersonalApps;
- }
- }
-
- final boolean suspended = getUserData(UserHandle.USER_SYSTEM).mPersonalAppsSuspended;
- if (suspended != shouldSuspend) {
- suspendPersonalAppsInternal(shouldSuspend, UserHandle.USER_SYSTEM);
- }
-
- if (shouldSuspend) {
- sendPersonalAppsSuspendedNotification(profileUserId);
- }
- }
-
private void onLockSettingsReady() {
getUserData(UserHandle.USER_SYSTEM);
loadOwners();
@@ -3950,13 +3978,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@Override
void handleUnlockUser(int userId) {
startOwnerService(userId, "unlock-user");
- maybeUpdatePersonalAppsSuspendedNotification(userId);
}
@Override
void handleStopUser(int userId) {
stopOwnerService(userId, "stop-user");
- maybeUpdatePersonalAppsSuspendedNotification(userId);
}
private void startOwnerService(int userId, String actionForLog) {
@@ -9427,10 +9453,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
pw.println();
pw.increaseIndent();
pw.print("mPasswordOwner="); pw.println(policy.mPasswordOwner);
- pw.decreaseIndent();
- pw.println();
- pw.increaseIndent();
pw.print("mProtectedPackages="); pw.println(policy.mProtectedPackages);
+ pw.print("mAppsSuspended="); pw.println(policy.mAppsSuspended);
pw.decreaseIndent();
}
}
@@ -11534,6 +11558,17 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
@Override
+ public void requestSetLocationProviderAllowed(ComponentName who, String provider,
+ boolean providerAllowed) {
+ Objects.requireNonNull(who, "ComponentName is null");
+ enforceDeviceOwner(who);
+
+ mInjector.binderWithCleanCallingIdentity(
+ () -> mInjector.getLocationManagerInternal().requestSetProviderAllowed(provider,
+ providerAllowed));
+ }
+
+ @Override
public boolean setTime(ComponentName who, long millis) {
Objects.requireNonNull(who, "ComponentName is null in setTime");
enforceDeviceOwnerOrProfileOwnerOnOrganizationOwnedDevice(who);
@@ -12211,7 +12246,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
LocalDate.now());
}
synchronized (getLockObject()) {
- getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
+ getActiveAdminForCallerLocked(who,
+ DeviceAdminInfo.USES_POLICY_ORGANIZATION_OWNED_PROFILE_OWNER);
if (policy == null) {
mOwners.clearSystemUpdatePolicy();
} else {
@@ -12220,9 +12256,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
mOwners.writeDeviceOwner();
}
- mContext.sendBroadcastAsUser(
+ mInjector.binderWithCleanCallingIdentity(() -> mContext.sendBroadcastAsUser(
new Intent(DevicePolicyManager.ACTION_SYSTEM_UPDATE_POLICY_CHANGED),
- UserHandle.SYSTEM);
+ UserHandle.SYSTEM));
DevicePolicyEventLogger
.createEvent(DevicePolicyEnums.SET_SYSTEM_UPDATE_POLICY)
.setAdmin(who)
@@ -14884,7 +14920,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
.setAdmin(admin)
.setBoolean(isDeviceAB())
.write();
- enforceDeviceOwner(admin);
+ enforceDeviceOwnerOrProfileOwnerOnOrganizationOwnedDevice(admin);
mInjector.binderWithCleanCallingIdentity(() -> {
UpdateInstaller updateInstaller;
if (isDeviceAB()) {
@@ -15271,10 +15307,20 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
false /* parent */);
// DO shouldn't be able to use this method.
enforceProfileOwnerOfOrganizationOwnedDevice(admin);
- if (admin.mSuspendPersonalApps) {
- return DevicePolicyManager.PERSONAL_APPS_SUSPENDED_EXPLICITLY;
+ final DevicePolicyData userData =
+ getUserData(getProfileParentId(mInjector.userHandleGetCallingUserId()));
+ if (!userData.mAppsSuspended) {
+ return PERSONAL_APPS_NOT_SUSPENDED;
} else {
- return DevicePolicyManager.PERSONAL_APPS_NOT_SUSPENDED;
+ int reasons = PERSONAL_APPS_NOT_SUSPENDED;
+ if (admin.mSuspendPersonalApps) {
+ reasons |= PERSONAL_APPS_SUSPENDED_EXPLICITLY;
+ }
+ final long deadline = admin.mProfileOffDeadline;
+ if (deadline != 0 && System.currentTimeMillis() > deadline) {
+ reasons |= PERSONAL_APPS_SUSPENDED_PROFILE_TIMEOUT;
+ }
+ return reasons;
}
}
}
@@ -15288,26 +15334,112 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
false /* parent */);
// DO shouldn't be able to use this method.
enforceProfileOwnerOfOrganizationOwnedDevice(admin);
+ enforceHandlesCheckPolicyComplianceIntent(callingUserId, admin.info.getPackageName());
+ boolean shouldSaveSettings = false;
if (admin.mSuspendPersonalApps != suspended) {
admin.mSuspendPersonalApps = suspended;
+ shouldSaveSettings = true;
+ }
+ if (admin.mProfileOffDeadline != 0) {
+ admin.mProfileOffDeadline = 0;
+ shouldSaveSettings = true;
+ }
+ if (shouldSaveSettings) {
saveSettingsLocked(callingUserId);
}
}
- if (getUserData(UserHandle.USER_SYSTEM).mPersonalAppsSuspended == suspended) {
- // Admin request matches current state, nothing to do.
- return;
- }
-
- suspendPersonalAppsInternal(suspended, UserHandle.USER_SYSTEM);
+ mInjector.binderWithCleanCallingIdentity(
+ () -> applyPersonalAppsSuspension(callingUserId, suspended));
+ }
- mInjector.binderWithCleanCallingIdentity(() -> {
- if (suspended) {
- sendPersonalAppsSuspendedNotification(callingUserId);
+ /**
+ * Checks whether there is a policy that requires personal apps to be suspended and if so,
+ * applies it.
+ * @param running whether the profile is currently considered running.
+ */
+ private void updatePersonalAppSuspension(int profileUserId, boolean running) {
+ final boolean shouldSuspend;
+ synchronized (getLockObject()) {
+ final ActiveAdmin profileOwner = getProfileOwnerAdminLocked(profileUserId);
+ if (profileOwner != null) {
+ final boolean deadlineReached =
+ updateProfileOffDeadlineLocked(profileUserId, profileOwner, running);
+ shouldSuspend = deadlineReached || profileOwner.mSuspendPersonalApps;
+ Slog.d(LOG_TAG, String.format(
+ "Should personal use be suspended: %b; explicit: %b; timeout: %b",
+ shouldSuspend, profileOwner.mSuspendPersonalApps, deadlineReached));
} else {
- clearPersonalAppsSuspendedNotification(callingUserId);
+ shouldSuspend = false;
}
- });
+ }
+
+ applyPersonalAppsSuspension(profileUserId, shouldSuspend);
+ }
+
+ /**
+ * Checks work profile time off policy, scheduling personal apps suspension via alarm if
+ * necessary.
+ * @return whether the apps should be suspended based on maximum time off policy.
+ */
+ private boolean updateProfileOffDeadlineLocked(
+ int profileUserId, ActiveAdmin profileOwner, boolean unlocked) {
+ final long now = System.currentTimeMillis();
+ if (profileOwner.mProfileOffDeadline != 0 && now > profileOwner.mProfileOffDeadline) {
+ // Profile off deadline is already reached.
+ Slog.i(LOG_TAG, "Profile off deadline has been reached.");
+ return true;
+ }
+ boolean shouldSaveSettings = false;
+ if (profileOwner.mProfileOffDeadline != 0
+ && (profileOwner.mProfileMaximumTimeOff == 0 || unlocked)) {
+ // There is a deadline but either there is no policy or the profile is unlocked -> clear
+ // the deadline.
+ Slog.i(LOG_TAG, "Profile off deadline is reset to zero");
+ profileOwner.mProfileOffDeadline = 0;
+ shouldSaveSettings = true;
+ } else if (profileOwner.mProfileOffDeadline == 0
+ && (profileOwner.mProfileMaximumTimeOff != 0 && !unlocked)) {
+ // There profile is locked and there is a policy, but the deadline is not set -> set the
+ // deadline.
+ Slog.i(LOG_TAG, "Profile off deadline is set.");
+ profileOwner.mProfileOffDeadline = now + profileOwner.mProfileMaximumTimeOff;
+ shouldSaveSettings = true;
+ }
+
+ updateProfileOffAlarm(profileOwner.mProfileOffDeadline);
+
+ if (shouldSaveSettings) {
+ saveSettingsLocked(profileUserId);
+ }
+ return false;
+ }
+
+ private void updateProfileOffAlarm(long profileOffDeadline) {
+ final AlarmManager am = mInjector.getAlarmManager();
+ final PendingIntent pi = PendingIntent.getBroadcast(mContext, REQUEST_PROFILE_OFF_DEADLINE,
+ new Intent(ACTION_PROFILE_OFF_DEADLINE),
+ PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_UPDATE_CURRENT);
+ am.cancel(pi);
+ if (profileOffDeadline != 0) {
+ Slog.i(LOG_TAG, "Profile off deadline alarm is set.");
+ am.set(AlarmManager.RTC, profileOffDeadline, pi);
+ } else {
+ Slog.i(LOG_TAG, "Profile off deadline alarm is removed.");
+ }
+ }
+
+ private void applyPersonalAppsSuspension(int profileUserId, boolean shouldSuspend) {
+ final boolean suspended = getUserData(UserHandle.USER_SYSTEM).mAppsSuspended;
+ if (suspended != shouldSuspend) {
+ suspendPersonalAppsInternal(shouldSuspend, UserHandle.USER_SYSTEM);
+ }
+
+ if (shouldSuspend) {
+ sendPersonalAppsSuspendedNotification(profileUserId);
+ } else {
+ clearPersonalAppsSuspendedNotification();
+ }
}
private void suspendPersonalAppsInternal(boolean suspended, int userId) {
@@ -15331,21 +15463,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
});
synchronized (getLockObject()) {
- getUserData(userId).mPersonalAppsSuspended = suspended;
+ getUserData(userId).mAppsSuspended = suspended;
saveSettingsLocked(userId);
}
}
- private void maybeUpdatePersonalAppsSuspendedNotification(int profileUserId) {
- // TODO(b/147414651): Unless updated, the notification stops working after turning the
- // profile off and back on, so it has to be updated more often than necessary.
- if (getUserData(UserHandle.USER_SYSTEM).mPersonalAppsSuspended
- && getProfileParentId(profileUserId) == UserHandle.USER_SYSTEM) {
- sendPersonalAppsSuspendedNotification(profileUserId);
- }
- }
-
- private void clearPersonalAppsSuspendedNotification(int userId) {
+ private void clearPersonalAppsSuspendedNotification() {
mInjector.binderWithCleanCallingIdentity(() ->
mInjector.getNotificationManager().cancel(
SystemMessage.NOTE_PERSONAL_APPS_SUSPENDED));
@@ -15379,4 +15502,48 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
mInjector.getNotificationManager().notify(
SystemMessage.NOTE_PERSONAL_APPS_SUSPENDED, notification);
}
+
+ @Override
+ public void setManagedProfileMaximumTimeOff(ComponentName who, long timeoutMs) {
+ final int userId = mInjector.userHandleGetCallingUserId();
+ synchronized (getLockObject()) {
+ final ActiveAdmin admin = getActiveAdminForCallerLocked(who,
+ DeviceAdminInfo.USES_POLICY_ORGANIZATION_OWNED_PROFILE_OWNER,
+ false /* parent */);
+ // DO shouldn't be able to use this method.
+ enforceProfileOwnerOfOrganizationOwnedDevice(admin);
+ enforceHandlesCheckPolicyComplianceIntent(userId, admin.info.getPackageName());
+ if (admin.mProfileMaximumTimeOff == timeoutMs) {
+ return;
+ }
+ admin.mProfileMaximumTimeOff = timeoutMs;
+ saveSettingsLocked(userId);
+ }
+
+ mInjector.binderWithCleanCallingIdentity(
+ () -> updatePersonalAppSuspension(userId, mUserManager.isUserUnlocked()));
+ }
+
+ void enforceHandlesCheckPolicyComplianceIntent(@UserIdInt int userId, String packageName) {
+ mInjector.binderWithCleanCallingIdentity(() -> {
+ final Intent intent = new Intent(DevicePolicyManager.ACTION_CHECK_POLICY_COMPLIANCE);
+ intent.setPackage(packageName);
+ final List<ResolveInfo> handlers = mInjector.getPackageManager()
+ .queryIntentActivitiesAsUser(intent, /* flags= */ 0, userId);
+ Preconditions.checkState(!handlers.isEmpty(),
+ "Admin doesn't handle " + DevicePolicyManager.ACTION_CHECK_POLICY_COMPLIANCE);
+ });
+ }
+
+ @Override
+ public long getManagedProfileMaximumTimeOff(ComponentName who) {
+ synchronized (getLockObject()) {
+ final ActiveAdmin admin = getActiveAdminForCallerLocked(who,
+ DeviceAdminInfo.USES_POLICY_ORGANIZATION_OWNED_PROFILE_OWNER,
+ false /* parent */);
+ // DO shouldn't be able to use this method.
+ enforceProfileOwnerOfOrganizationOwnedDevice(admin);
+ return admin.mProfileMaximumTimeOff;
+ }
+ }
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsServiceTest.java
index 067f23a5bb23..155de3bc0266 100644
--- a/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsServiceTest.java
@@ -283,6 +283,8 @@ public class AppOpsServiceTest {
}
+ /*
+ TODO ntmyren: re enable when we have time to rewrite test.
@Test
public void testPackageRemovedHistoricalOps() throws InterruptedException {
mAppOpsService.setMode(OP_READ_SMS, mMyUid, sMyPackageName, MODE_ALLOWED);
@@ -321,6 +323,7 @@ public class AppOpsServiceTest {
assertThat(latchRef.get().getCount()).isEqualTo(0);
assertThat(resultOpsRef.get().isEmpty()).isTrue();
}
+ */
@Test
public void testUidRemoved() {
diff --git a/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java b/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java
index a6af9a99788f..9a633931017e 100644
--- a/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java
@@ -57,7 +57,6 @@ import android.os.Handler;
import android.os.Message;
import androidx.test.InstrumentationRegistry;
-import androidx.test.runner.AndroidJUnit4;
import com.android.internal.R;
import com.android.server.LocalServices;
@@ -68,6 +67,7 @@ import com.android.server.testutils.TestUtils;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
@@ -81,7 +81,7 @@ import java.util.List;
import java.util.Map;
/** Unit test for {@link com.android.server.integrity.AppIntegrityManagerServiceImpl} */
-@RunWith(AndroidJUnit4.class)
+@RunWith(JUnit4.class)
public class AppIntegrityManagerServiceImplTest {
private static final String TEST_APP_PATH =
"/data/local/tmp/AppIntegrityManagerServiceTestApp.apk";
@@ -91,8 +91,10 @@ public class AppIntegrityManagerServiceImplTest {
private static final String TEST_FRAMEWORK_PACKAGE = "com.android.frameworks.servicestests";
private static final String PACKAGE_NAME = "com.test.app";
- private static final int VERSION_CODE = 100;
+
+ private static final long VERSION_CODE = 100;
private static final String INSTALLER = "com.long.random.test.installer.name";
+
// These are obtained by running the test and checking logcat.
private static final String APP_CERT =
"301AA3CB081134501C45F1422ABC66C24224FD5DED5FDC8F17E697176FD866AA";
@@ -108,7 +110,8 @@ public class AppIntegrityManagerServiceImplTest {
"play_store_cert";
private static final String ADB_CERT = "";
- @org.junit.Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();
+ @org.junit.Rule
+ public MockitoRule mMockitoRule = MockitoJUnit.rule();
@Mock PackageManagerInternal mPackageManagerInternal;
@Mock Context mMockContext;
@@ -173,7 +176,8 @@ public class AppIntegrityManagerServiceImplTest {
makeUsSystemApp();
Rule rule =
new Rule(
- new AtomicFormula.BooleanAtomicFormula(AtomicFormula.PRE_INSTALLED, true),
+ new AtomicFormula.BooleanAtomicFormula(
+ AtomicFormula.PRE_INSTALLED, true),
Rule.DENY);
TestUtils.assertExpectException(
SecurityException.class,
@@ -191,7 +195,8 @@ public class AppIntegrityManagerServiceImplTest {
whitelistUsAsRuleProvider();
Rule rule =
new Rule(
- new AtomicFormula.BooleanAtomicFormula(AtomicFormula.PRE_INSTALLED, true),
+ new AtomicFormula.BooleanAtomicFormula(
+ AtomicFormula.PRE_INSTALLED, true),
Rule.DENY);
TestUtils.assertExpectException(
SecurityException.class,
@@ -210,7 +215,8 @@ public class AppIntegrityManagerServiceImplTest {
makeUsSystemApp();
Rule rule =
new Rule(
- new AtomicFormula.BooleanAtomicFormula(AtomicFormula.PRE_INSTALLED, true),
+ new AtomicFormula.BooleanAtomicFormula(
+ AtomicFormula.PRE_INSTALLED, true),
Rule.DENY);
// no SecurityException
@@ -447,7 +453,7 @@ public class AppIntegrityManagerServiceImplTest {
intent.putExtra(
EXTRA_VERIFICATION_INSTALLER_UID,
mMockContext.getPackageManager().getPackageUid(installer, /* flags= */ 0));
- intent.putExtra(Intent.EXTRA_VERSION_CODE, VERSION_CODE);
+ intent.putExtra(Intent.EXTRA_LONG_VERSION_CODE, VERSION_CODE);
return intent;
}
}
diff --git a/services/tests/servicestests/src/com/android/server/integrity/IntegrityFileManagerTest.java b/services/tests/servicestests/src/com/android/server/integrity/IntegrityFileManagerTest.java
index a1810b971b09..86daf69fb2d9 100644
--- a/services/tests/servicestests/src/com/android/server/integrity/IntegrityFileManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/integrity/IntegrityFileManagerTest.java
@@ -22,7 +22,7 @@ import static com.google.common.truth.Truth.assertThat;
import android.content.integrity.AppInstallMetadata;
import android.content.integrity.AtomicFormula;
-import android.content.integrity.AtomicFormula.IntAtomicFormula;
+import android.content.integrity.AtomicFormula.LongAtomicFormula;
import android.content.integrity.AtomicFormula.StringAtomicFormula;
import android.content.integrity.CompoundFormula;
import android.content.integrity.Rule;
@@ -116,7 +116,8 @@ public class IntegrityFileManagerTest {
Rule packageCertRule = getAppCertificateIndexedRule(packageCert);
Rule versionCodeRule =
new Rule(
- new IntAtomicFormula(AtomicFormula.VERSION_CODE, AtomicFormula.LE, version),
+ new LongAtomicFormula(
+ AtomicFormula.VERSION_CODE, AtomicFormula.EQ, version),
Rule.DENY);
Rule randomRule =
new Rule(
@@ -127,9 +128,9 @@ public class IntegrityFileManagerTest {
AtomicFormula.PACKAGE_NAME,
"abc",
/* isHashedValue= */ false),
- new IntAtomicFormula(
+ new LongAtomicFormula(
AtomicFormula.VERSION_CODE,
- AtomicFormula.LE,
+ AtomicFormula.EQ,
version))),
Rule.DENY);
@@ -201,21 +202,22 @@ public class IntegrityFileManagerTest {
private Rule getPackageNameIndexedRule(String packageName) {
return new Rule(
new StringAtomicFormula(
- AtomicFormula.PACKAGE_NAME, packageName, /* isHashedValue= */ false),
+ AtomicFormula.PACKAGE_NAME, packageName, /* isHashedValue= */false),
Rule.DENY);
}
private Rule getAppCertificateIndexedRule(String appCertificate) {
return new Rule(
new StringAtomicFormula(
- AtomicFormula.APP_CERTIFICATE, appCertificate, /* isHashedValue= */ false),
+ AtomicFormula.APP_CERTIFICATE,
+ appCertificate, /* isHashedValue= */ false),
Rule.DENY);
}
private Rule getInstallerCertificateRule(String installerCert) {
return new Rule(
new StringAtomicFormula(
- AtomicFormula.INSTALLER_NAME, installerCert, /* isHashedValue= */ false),
+ AtomicFormula.INSTALLER_NAME, installerCert, /* isHashedValue= */false),
Rule.DENY);
}
diff --git a/services/tests/servicestests/src/com/android/server/integrity/engine/RuleEvaluationEngineTest.java b/services/tests/servicestests/src/com/android/server/integrity/engine/RuleEvaluationEngineTest.java
index d3864878d4b2..99157024bb66 100644
--- a/services/tests/servicestests/src/com/android/server/integrity/engine/RuleEvaluationEngineTest.java
+++ b/services/tests/servicestests/src/com/android/server/integrity/engine/RuleEvaluationEngineTest.java
@@ -50,7 +50,8 @@ public class RuleEvaluationEngineTest {
private static final String RANDOM_INSTALLER = "random";
private static final String RANDOM_INSTALLER_CERT = "random_cert";
- @Mock private IntegrityFileManager mIntegrityFileManager;
+ @Mock
+ private IntegrityFileManager mIntegrityFileManager;
private RuleEvaluationEngine mEngine;
@@ -70,29 +71,29 @@ public class RuleEvaluationEngineTest {
assertEquals(
IntegrityCheckResult.Effect.ALLOW,
mEngine.evaluate(
- getAppInstallMetadataBuilder()
- .setInstallerName(INSTALLER_1)
- .setInstallerCertificate(INSTALLER_1_CERT)
- .build(),
- allowedInstallers)
+ getAppInstallMetadataBuilder()
+ .setInstallerName(INSTALLER_1)
+ .setInstallerCertificate(INSTALLER_1_CERT)
+ .build(),
+ allowedInstallers)
.getEffect());
assertEquals(
IntegrityCheckResult.Effect.ALLOW,
mEngine.evaluate(
- getAppInstallMetadataBuilder()
- .setInstallerName(INSTALLER_2)
- .setInstallerCertificate(INSTALLER_2_CERT)
- .build(),
- allowedInstallers)
+ getAppInstallMetadataBuilder()
+ .setInstallerName(INSTALLER_2)
+ .setInstallerCertificate(INSTALLER_2_CERT)
+ .build(),
+ allowedInstallers)
.getEffect());
assertEquals(
IntegrityCheckResult.Effect.ALLOW,
mEngine.evaluate(
- getAppInstallMetadataBuilder()
- .setInstallerName(RANDOM_INSTALLER)
- .setInstallerCertificate(RANDOM_INSTALLER_CERT)
- .build(),
- allowedInstallers)
+ getAppInstallMetadataBuilder()
+ .setInstallerName(RANDOM_INSTALLER)
+ .setInstallerCertificate(RANDOM_INSTALLER_CERT)
+ .build(),
+ allowedInstallers)
.getEffect());
}
@@ -104,38 +105,38 @@ public class RuleEvaluationEngineTest {
assertEquals(
IntegrityCheckResult.Effect.ALLOW,
mEngine.evaluate(
- getAppInstallMetadataBuilder()
- .setInstallerName(INSTALLER_1)
- .setInstallerCertificate(INSTALLER_1_CERT)
- .build(),
- allowedInstallers)
+ getAppInstallMetadataBuilder()
+ .setInstallerName(INSTALLER_1)
+ .setInstallerCertificate(INSTALLER_1_CERT)
+ .build(),
+ allowedInstallers)
.getEffect());
assertEquals(
IntegrityCheckResult.Effect.DENY,
mEngine.evaluate(
- getAppInstallMetadataBuilder()
- .setInstallerName(RANDOM_INSTALLER)
- .setInstallerCertificate(INSTALLER_1_CERT)
- .build(),
- allowedInstallers)
+ getAppInstallMetadataBuilder()
+ .setInstallerName(RANDOM_INSTALLER)
+ .setInstallerCertificate(INSTALLER_1_CERT)
+ .build(),
+ allowedInstallers)
.getEffect());
assertEquals(
IntegrityCheckResult.Effect.DENY,
mEngine.evaluate(
- getAppInstallMetadataBuilder()
- .setInstallerName(INSTALLER_1)
- .setInstallerCertificate(RANDOM_INSTALLER_CERT)
- .build(),
- allowedInstallers)
+ getAppInstallMetadataBuilder()
+ .setInstallerName(INSTALLER_1)
+ .setInstallerCertificate(RANDOM_INSTALLER_CERT)
+ .build(),
+ allowedInstallers)
.getEffect());
assertEquals(
IntegrityCheckResult.Effect.DENY,
mEngine.evaluate(
- getAppInstallMetadataBuilder()
- .setInstallerName(RANDOM_INSTALLER)
- .setInstallerCertificate(RANDOM_INSTALLER_CERT)
- .build(),
- allowedInstallers)
+ getAppInstallMetadataBuilder()
+ .setInstallerName(RANDOM_INSTALLER)
+ .setInstallerCertificate(RANDOM_INSTALLER_CERT)
+ .build(),
+ allowedInstallers)
.getEffect());
}
@@ -149,38 +150,38 @@ public class RuleEvaluationEngineTest {
assertEquals(
IntegrityCheckResult.Effect.ALLOW,
mEngine.evaluate(
- getAppInstallMetadataBuilder()
- .setInstallerName(INSTALLER_1)
- .setInstallerCertificate(INSTALLER_1_CERT)
- .build(),
- allowedInstallers)
+ getAppInstallMetadataBuilder()
+ .setInstallerName(INSTALLER_1)
+ .setInstallerCertificate(INSTALLER_1_CERT)
+ .build(),
+ allowedInstallers)
.getEffect());
assertEquals(
IntegrityCheckResult.Effect.ALLOW,
mEngine.evaluate(
- getAppInstallMetadataBuilder()
- .setInstallerName(INSTALLER_2)
- .setInstallerCertificate(INSTALLER_2_CERT)
- .build(),
- allowedInstallers)
+ getAppInstallMetadataBuilder()
+ .setInstallerName(INSTALLER_2)
+ .setInstallerCertificate(INSTALLER_2_CERT)
+ .build(),
+ allowedInstallers)
.getEffect());
assertEquals(
IntegrityCheckResult.Effect.DENY,
mEngine.evaluate(
- getAppInstallMetadataBuilder()
- .setInstallerName(INSTALLER_1)
- .setInstallerCertificate(INSTALLER_2_CERT)
- .build(),
- allowedInstallers)
+ getAppInstallMetadataBuilder()
+ .setInstallerName(INSTALLER_1)
+ .setInstallerCertificate(INSTALLER_2_CERT)
+ .build(),
+ allowedInstallers)
.getEffect());
assertEquals(
IntegrityCheckResult.Effect.DENY,
mEngine.evaluate(
- getAppInstallMetadataBuilder()
- .setInstallerName(INSTALLER_2)
- .setInstallerCertificate(INSTALLER_1_CERT)
- .build(),
- allowedInstallers)
+ getAppInstallMetadataBuilder()
+ .setInstallerName(INSTALLER_2)
+ .setInstallerCertificate(INSTALLER_1_CERT)
+ .build(),
+ allowedInstallers)
.getEffect());
}
diff --git a/services/tests/servicestests/src/com/android/server/integrity/engine/RuleEvaluatorTest.java b/services/tests/servicestests/src/com/android/server/integrity/engine/RuleEvaluatorTest.java
index eda2b701fd8d..629fd14befcc 100644
--- a/services/tests/servicestests/src/com/android/server/integrity/engine/RuleEvaluatorTest.java
+++ b/services/tests/servicestests/src/com/android/server/integrity/engine/RuleEvaluatorTest.java
@@ -19,10 +19,11 @@ package com.android.server.integrity.engine;
import static com.android.server.integrity.model.IntegrityCheckResult.Effect.ALLOW;
import static com.android.server.integrity.model.IntegrityCheckResult.Effect.DENY;
-import static org.junit.Assert.assertEquals;
+import static com.google.common.truth.Truth.assertThat;
import android.content.integrity.AppInstallMetadata;
import android.content.integrity.AtomicFormula;
+import android.content.integrity.AtomicFormula.LongAtomicFormula;
import android.content.integrity.AtomicFormula.StringAtomicFormula;
import android.content.integrity.CompoundFormula;
import android.content.integrity.Rule;
@@ -57,7 +58,7 @@ public class RuleEvaluatorTest {
IntegrityCheckResult result = RuleEvaluator.evaluateRules(rules, APP_INSTALL_METADATA);
- assertEquals(ALLOW, result.getEffect());
+ assertThat(result.getEffect()).isEqualTo(ALLOW);
}
@Test
@@ -73,7 +74,7 @@ public class RuleEvaluatorTest {
IntegrityCheckResult result =
RuleEvaluator.evaluateRules(Collections.singletonList(rule1), APP_INSTALL_METADATA);
- assertEquals(ALLOW, result.getEffect());
+ assertThat(result.getEffect()).isEqualTo(ALLOW);
}
@Test
@@ -96,8 +97,8 @@ public class RuleEvaluatorTest {
IntegrityCheckResult result =
RuleEvaluator.evaluateRules(Arrays.asList(rule1, rule2), APP_INSTALL_METADATA);
- assertEquals(DENY, result.getEffect());
- assertEquals(rule1, result.getRule());
+ assertThat(result.getEffect()).isEqualTo(DENY);
+ assertThat(result.getRule()).isEqualTo(rule1);
}
@Test
@@ -126,8 +127,8 @@ public class RuleEvaluatorTest {
IntegrityCheckResult result =
RuleEvaluator.evaluateRules(Arrays.asList(rule1, rule2), APP_INSTALL_METADATA);
- assertEquals(DENY, result.getEffect());
- assertEquals(rule1, result.getRule());
+ assertThat(result.getEffect()).isEqualTo(DENY);
+ assertThat(result.getRule()).isEqualTo(rule1);
}
@Test
@@ -145,23 +146,23 @@ public class RuleEvaluatorTest {
IntegrityCheckResult result =
RuleEvaluator.evaluateRules(Collections.singletonList(rule), APP_INSTALL_METADATA);
- assertEquals(DENY, result.getEffect());
- assertEquals(rule, result.getRule());
+ assertThat(result.getEffect()).isEqualTo(DENY);
+ assertThat(result.getRule()).isEqualTo(rule);
}
@Test
public void testEvaluateRules_ruleWithIntegerOperators_deny() {
Rule rule =
new Rule(
- new AtomicFormula.IntAtomicFormula(
- AtomicFormula.VERSION_CODE, AtomicFormula.GT, 1),
+ new LongAtomicFormula(AtomicFormula.VERSION_CODE,
+ AtomicFormula.GT, 1),
Rule.DENY);
IntegrityCheckResult result =
RuleEvaluator.evaluateRules(Collections.singletonList(rule), APP_INSTALL_METADATA);
- assertEquals(DENY, result.getEffect());
- assertEquals(rule, result.getRule());
+ assertThat(result.getEffect()).isEqualTo(DENY);
+ assertThat(result.getRule()).isEqualTo(rule);
}
@Test
@@ -183,8 +184,8 @@ public class RuleEvaluatorTest {
IntegrityCheckResult result =
RuleEvaluator.evaluateRules(Collections.singletonList(rule), APP_INSTALL_METADATA);
- assertEquals(DENY, result.getEffect());
- assertEquals(rule, result.getRule());
+ assertThat(result.getEffect()).isEqualTo(DENY);
+ assertThat(result.getRule()).isEqualTo(rule);
}
@Test
@@ -206,7 +207,7 @@ public class RuleEvaluatorTest {
IntegrityCheckResult result =
RuleEvaluator.evaluateRules(Collections.singletonList(rule), APP_INSTALL_METADATA);
- assertEquals(DENY, result.getEffect());
+ assertThat(result.getEffect()).isEqualTo(DENY);
}
@Test
@@ -230,7 +231,7 @@ public class RuleEvaluatorTest {
IntegrityCheckResult result =
RuleEvaluator.evaluateRules(Collections.singletonList(rule), APP_INSTALL_METADATA);
- assertEquals(DENY, result.getEffect());
+ assertThat(result.getEffect()).isEqualTo(DENY);
}
@Test
@@ -259,7 +260,7 @@ public class RuleEvaluatorTest {
IntegrityCheckResult result =
RuleEvaluator.evaluateRules(Arrays.asList(rule1, rule2), APP_INSTALL_METADATA);
- assertEquals(ALLOW, result.getEffect());
- assertEquals(rule1, result.getRule());
+ assertThat(result.getEffect()).isEqualTo(ALLOW);
+ assertThat(result.getRule()).isEqualTo(rule1);
}
-}
+} \ No newline at end of file
diff --git a/services/tests/servicestests/src/com/android/server/integrity/parser/BinaryFileOperationsTest.java b/services/tests/servicestests/src/com/android/server/integrity/parser/BinaryFileOperationsTest.java
index cfa1de371e8c..723b6c5af451 100644
--- a/services/tests/servicestests/src/com/android/server/integrity/parser/BinaryFileOperationsTest.java
+++ b/services/tests/servicestests/src/com/android/server/integrity/parser/BinaryFileOperationsTest.java
@@ -26,7 +26,8 @@ import static com.android.server.integrity.utils.TestUtils.getValueBits;
import static com.google.common.truth.Truth.assertThat;
-import com.android.server.integrity.IntegrityUtils;
+import android.content.integrity.IntegrityUtils;
+
import com.android.server.integrity.model.BitInputStream;
import org.junit.Test;
diff --git a/services/tests/servicestests/src/com/android/server/integrity/parser/RuleBinaryParserTest.java b/services/tests/servicestests/src/com/android/server/integrity/parser/RuleBinaryParserTest.java
index e0b2e2257ee4..38cf562f8c5b 100644
--- a/services/tests/servicestests/src/com/android/server/integrity/parser/RuleBinaryParserTest.java
+++ b/services/tests/servicestests/src/com/android/server/integrity/parser/RuleBinaryParserTest.java
@@ -36,10 +36,9 @@ import static com.google.common.truth.Truth.assertThat;
import android.content.integrity.AtomicFormula;
import android.content.integrity.CompoundFormula;
+import android.content.integrity.IntegrityUtils;
import android.content.integrity.Rule;
-import com.android.server.integrity.IntegrityUtils;
-
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@@ -345,7 +344,7 @@ public class RuleBinaryParserTest {
+ ATOMIC_FORMULA_START_BITS
+ VERSION_CODE
+ EQ
- + getBits(versionCode, /* numOfBits= */ 32)
+ + getBits(versionCode, /* numOfBits= */ 64)
+ DENY
+ END_BIT;
byte[] ruleBytes = getBytes(ruleBits);
@@ -356,7 +355,7 @@ public class RuleBinaryParserTest {
RuleParser binaryParser = new RuleBinaryParser();
Rule expectedRule =
new Rule(
- new AtomicFormula.IntAtomicFormula(
+ new AtomicFormula.LongAtomicFormula(
AtomicFormula.VERSION_CODE, AtomicFormula.EQ, 1),
Rule.DENY);
@@ -384,7 +383,8 @@ public class RuleBinaryParserTest {
RuleParser binaryParser = new RuleBinaryParser();
Rule expectedRule =
new Rule(
- new AtomicFormula.BooleanAtomicFormula(AtomicFormula.PRE_INSTALLED, true),
+ new AtomicFormula.BooleanAtomicFormula(
+ AtomicFormula.PRE_INSTALLED, true),
Rule.DENY);
List<Rule> rules = binaryParser.parse(rule.array());
@@ -400,7 +400,7 @@ public class RuleBinaryParserTest {
+ ATOMIC_FORMULA_START_BITS
+ VERSION_CODE
+ EQ
- + getBits(versionCode, /* numOfBits= */ 32)
+ + getBits(versionCode, /* numOfBits= */ 64)
+ DENY;
byte[] ruleBytes = getBytes(ruleBits);
ByteBuffer rule =
@@ -488,7 +488,7 @@ public class RuleBinaryParserTest {
+ ATOMIC_FORMULA_START_BITS
+ VERSION_CODE
+ INVALID_OPERATOR
- + getBits(versionCode, /* numOfBits= */ 32)
+ + getBits(versionCode, /* numOfBits= */ 64)
+ COMPOUND_FORMULA_END_BITS
+ DENY
+ END_BIT;
diff --git a/services/tests/servicestests/src/com/android/server/integrity/parser/RuleXmlParserTest.java b/services/tests/servicestests/src/com/android/server/integrity/parser/RuleXmlParserTest.java
index 0f0dee924e29..c57136c1acab 100644
--- a/services/tests/servicestests/src/com/android/server/integrity/parser/RuleXmlParserTest.java
+++ b/services/tests/servicestests/src/com/android/server/integrity/parser/RuleXmlParserTest.java
@@ -46,15 +46,15 @@ public class RuleXmlParserTest {
String ruleXmlCompoundFormula =
"<RL>"
+ generateTagWithAttribute(
- /* tag= */ "R",
- Collections.singletonMap("E", String.valueOf(Rule.DENY)),
- /* closed= */ false)
+ /* tag= */ "R",
+ Collections.singletonMap("E", String.valueOf(Rule.DENY)),
+ /* closed= */ false)
+ generateTagWithAttribute(
- /* tag= */ "OF",
- Collections.singletonMap("C", String.valueOf(CompoundFormula.NOT)),
- /* closed= */ false)
+ /* tag= */ "OF",
+ Collections.singletonMap("C", String.valueOf(CompoundFormula.NOT)),
+ /* closed= */ false)
+ generateTagWithAttribute(
- /* tag= */ "AF", atomicFormulaAttrs, /* closed= */ true)
+ /* tag= */ "AF", atomicFormulaAttrs, /* closed= */ true)
+ "</OF>"
+ "</R>"
+ "</RL>";
@@ -83,15 +83,15 @@ public class RuleXmlParserTest {
String ruleXmlCompoundFormula =
"<RL>"
+ generateTagWithAttribute(
- /* tag= */ "R",
- Collections.singletonMap("E", String.valueOf(Rule.DENY)),
- /* closed= */ false)
+ /* tag= */ "R",
+ Collections.singletonMap("E", String.valueOf(Rule.DENY)),
+ /* closed= */ false)
+ generateTagWithAttribute(
- /* tag= */ "OF",
- Collections.singletonMap("C", String.valueOf(CompoundFormula.NOT)),
- /* closed= */ false)
+ /* tag= */ "OF",
+ Collections.singletonMap("C", String.valueOf(CompoundFormula.NOT)),
+ /* closed= */ false)
+ generateTagWithAttribute(
- /* tag= */ "AF", packageNameAttrs, /* closed= */ true)
+ /* tag= */ "AF", packageNameAttrs, /* closed= */ true)
+ "</OF>"
+ "</R>"
+ "</RL>";
@@ -123,17 +123,17 @@ public class RuleXmlParserTest {
String ruleXmlCompoundFormula =
"<RL>"
+ generateTagWithAttribute(
- /* tag= */ "R",
- Collections.singletonMap("E", String.valueOf(Rule.DENY)),
- /* closed= */ false)
+ /* tag= */ "R",
+ Collections.singletonMap("E", String.valueOf(Rule.DENY)),
+ /* closed= */ false)
+ generateTagWithAttribute(
- /* tag= */ "OF",
- Collections.singletonMap("C", String.valueOf(CompoundFormula.AND)),
- /* closed= */ false)
+ /* tag= */ "OF",
+ Collections.singletonMap("C", String.valueOf(CompoundFormula.AND)),
+ /* closed= */ false)
+ generateTagWithAttribute(
- /* tag= */ "AF", packageNameAttrs, /* closed= */ true)
+ /* tag= */ "AF", packageNameAttrs, /* closed= */ true)
+ generateTagWithAttribute(
- /* tag= */ "AF", appCertificateAttrs, /* closed= */ true)
+ /* tag= */ "AF", appCertificateAttrs, /* closed= */ true)
+ "</OF>"
+ "</R>"
+ "</RL>";
@@ -168,17 +168,17 @@ public class RuleXmlParserTest {
String ruleXmlCompoundFormula =
"<RL>"
+ generateTagWithAttribute(
- /* tag= */ "R",
- Collections.singletonMap("E", String.valueOf(Rule.DENY)),
- /* closed= */ false)
+ /* tag= */ "R",
+ Collections.singletonMap("E", String.valueOf(Rule.DENY)),
+ /* closed= */ false)
+ generateTagWithAttribute(
- /* tag= */ "OF",
- Collections.singletonMap("C", String.valueOf(CompoundFormula.OR)),
- /* closed= */ false)
+ /* tag= */ "OF",
+ Collections.singletonMap("C", String.valueOf(CompoundFormula.OR)),
+ /* closed= */ false)
+ generateTagWithAttribute(
- /* tag= */ "AF", packageNameAttrs, /* closed= */ true)
+ /* tag= */ "AF", packageNameAttrs, /* closed= */ true)
+ generateTagWithAttribute(
- /* tag= */ "AF", appCertificateAttrs, /* closed= */ true)
+ /* tag= */ "AF", appCertificateAttrs, /* closed= */ true)
+ "</OF>"
+ "</R>"
+ "</RL>";
@@ -211,15 +211,15 @@ public class RuleXmlParserTest {
String ruleXmlCompoundFormula =
"<RL>"
+ generateTagWithAttribute(
- /* tag= */ "R",
- Collections.singletonMap("E", String.valueOf(Rule.DENY)),
- /* closed= */ false)
+ /* tag= */ "R",
+ Collections.singletonMap("E", String.valueOf(Rule.DENY)),
+ /* closed= */ false)
+ generateTagWithAttribute(
- /* tag= */ "OF",
- Collections.singletonMap("C", String.valueOf(CompoundFormula.NOT)),
- /* closed= */ false)
+ /* tag= */ "OF",
+ Collections.singletonMap("C", String.valueOf(CompoundFormula.NOT)),
+ /* closed= */ false)
+ generateTagWithAttribute(
- /* tag= */ "AF", packageNameAttrs, /* closed= */ true)
+ /* tag= */ "AF", packageNameAttrs, /* closed= */ true)
+ "</OF>"
+ "</R>"
+ "</RL>";
@@ -252,17 +252,17 @@ public class RuleXmlParserTest {
String ruleXmlCompoundFormula =
"<RL>"
+ generateTagWithAttribute(
- /* tag= */ "R",
- Collections.singletonMap("E", String.valueOf(Rule.DENY)),
- /* closed= */ false)
+ /* tag= */ "R",
+ Collections.singletonMap("E", String.valueOf(Rule.DENY)),
+ /* closed= */ false)
+ generateTagWithAttribute(
- /* tag= */ "OF",
- Collections.singletonMap("C", String.valueOf(CompoundFormula.NOT)),
- /* closed= */ false)
+ /* tag= */ "OF",
+ Collections.singletonMap("C", String.valueOf(CompoundFormula.NOT)),
+ /* closed= */ false)
+ generateTagWithAttribute(
- /* tag= */ "AF", packageNameAttrs, /* closed= */ true)
+ /* tag= */ "AF", packageNameAttrs, /* closed= */ true)
+ generateTagWithAttribute(
- /* tag= */ "AF", versionCodeAttrs, /* closed= */ true)
+ /* tag= */ "AF", versionCodeAttrs, /* closed= */ true)
+ "</OF>"
+ "</R>"
+ "</RL>";
@@ -283,15 +283,15 @@ public class RuleXmlParserTest {
String ruleXmlCompoundFormula =
"<RL>"
+ generateTagWithAttribute(
- /* tag= */ "R",
- Collections.singletonMap("E", String.valueOf(Rule.DENY)),
- /* closed= */ false)
+ /* tag= */ "R",
+ Collections.singletonMap("E", String.valueOf(Rule.DENY)),
+ /* closed= */ false)
+ generateTagWithAttribute(
- /* tag= */ "OF",
- Collections.singletonMap("C", String.valueOf(CompoundFormula.NOT)),
- /* closed= */ false)
+ /* tag= */ "OF",
+ Collections.singletonMap("C", String.valueOf(CompoundFormula.NOT)),
+ /* closed= */ false)
+ generateTagWithAttribute(
- /* tag= */ "AF", packageNameAttrs, /* closed= */ true)
+ /* tag= */ "AF", packageNameAttrs, /* closed= */ true)
+ "</OF>"
+ "</R>"
+ "</RL>";
@@ -311,15 +311,15 @@ public class RuleXmlParserTest {
String ruleXmlCompoundFormula =
"<RL>"
+ generateTagWithAttribute(
- /* tag= */ "R",
- Collections.singletonMap("E", "INVALID_EFFECT"),
- /* closed= */ false)
+ /* tag= */ "R",
+ Collections.singletonMap("E", "INVALID_EFFECT"),
+ /* closed= */ false)
+ generateTagWithAttribute(
- /* tag= */ "OF",
- Collections.singletonMap("C", String.valueOf(CompoundFormula.NOT)),
- /* closed= */ false)
+ /* tag= */ "OF",
+ Collections.singletonMap("C", String.valueOf(CompoundFormula.NOT)),
+ /* closed= */ false)
+ generateTagWithAttribute(
- /* tag= */ "AF", packageNameAttrs, /* closed= */ true)
+ /* tag= */ "AF", packageNameAttrs, /* closed= */ true)
+ "</OF>"
+ "</R>"
+ "</RL>";
@@ -339,17 +339,17 @@ public class RuleXmlParserTest {
String ruleXmlCompoundFormula =
"<RL>"
+ generateTagWithAttribute(
- /* tag= */ "R",
- Collections.singletonMap("E", String.valueOf(Rule.DENY)),
- /* closed= */ false)
+ /* tag= */ "R",
+ Collections.singletonMap("E", String.valueOf(Rule.DENY)),
+ /* closed= */ false)
+ generateTagWithAttribute(
- /* tag= */ "OF",
- Collections.singletonMap("C", String.valueOf(CompoundFormula.NOT)),
- /* closed= */ false)
+ /* tag= */ "OF",
+ Collections.singletonMap("C", String.valueOf(CompoundFormula.NOT)),
+ /* closed= */ false)
+ generateTagWithAttribute(
- /* tag= */ "InvalidAtomicFormula",
- packageNameAttrs,
- /* closed= */ true)
+ /* tag= */ "InvalidAtomicFormula",
+ packageNameAttrs,
+ /* closed= */ true)
+ "</OF>"
+ "</R>"
+ "</RL>";
@@ -369,11 +369,11 @@ public class RuleXmlParserTest {
String ruleXmlAtomicFormula =
"<RL>"
+ generateTagWithAttribute(
- /* tag= */ "R",
- Collections.singletonMap("E", String.valueOf(Rule.DENY)),
- /* closed= */ false)
+ /* tag= */ "R",
+ Collections.singletonMap("E", String.valueOf(Rule.DENY)),
+ /* closed= */ false)
+ generateTagWithAttribute(
- /* tag= */ "AF", packageNameAttrs, /* closed= */ true)
+ /* tag= */ "AF", packageNameAttrs, /* closed= */ true)
+ "</R>"
+ "</RL>";
RuleParser xmlParser = new RuleXmlParser();
@@ -399,17 +399,17 @@ public class RuleXmlParserTest {
String ruleXmlAtomicFormula =
"<RL>"
+ generateTagWithAttribute(
- /* tag= */ "R",
- Collections.singletonMap("E", String.valueOf(Rule.DENY)),
- /* closed= */ false)
+ /* tag= */ "R",
+ Collections.singletonMap("E", String.valueOf(Rule.DENY)),
+ /* closed= */ false)
+ generateTagWithAttribute(
- /* tag= */ "AF", versionCodeAttrs, /* closed= */ true)
+ /* tag= */ "AF", versionCodeAttrs, /* closed= */ true)
+ "</R>"
+ "</RL>";
RuleParser xmlParser = new RuleXmlParser();
Rule expectedRule =
new Rule(
- new AtomicFormula.IntAtomicFormula(
+ new AtomicFormula.LongAtomicFormula(
AtomicFormula.VERSION_CODE, AtomicFormula.EQ, 1),
Rule.DENY);
@@ -426,17 +426,18 @@ public class RuleXmlParserTest {
String ruleXmlAtomicFormula =
"<RL>"
+ generateTagWithAttribute(
- /* tag= */ "R",
- Collections.singletonMap("E", String.valueOf(Rule.DENY)),
- /* closed= */ false)
+ /* tag= */ "R",
+ Collections.singletonMap("E", String.valueOf(Rule.DENY)),
+ /* closed= */ false)
+ generateTagWithAttribute(
- /* tag= */ "AF", preInstalledAttrs, /* closed= */ true)
+ /* tag= */ "AF", preInstalledAttrs, /* closed= */ true)
+ "</R>"
+ "</RL>";
RuleParser xmlParser = new RuleXmlParser();
Rule expectedRule =
new Rule(
- new AtomicFormula.BooleanAtomicFormula(AtomicFormula.PRE_INSTALLED, true),
+ new AtomicFormula.BooleanAtomicFormula(
+ AtomicFormula.PRE_INSTALLED, true),
Rule.DENY);
List<Rule> rules = xmlParser.parse(ruleXmlAtomicFormula.getBytes(StandardCharsets.UTF_8));
@@ -452,11 +453,11 @@ public class RuleXmlParserTest {
String ruleXmlAtomicFormula =
"<RL>"
+ generateTagWithAttribute(
- /* tag= */ "R",
- Collections.singletonMap("E", String.valueOf(Rule.DENY)),
- /* closed= */ false)
+ /* tag= */ "R",
+ Collections.singletonMap("E", String.valueOf(Rule.DENY)),
+ /* closed= */ false)
+ generateTagWithAttribute(
- /* tag= */ "AF", packageNameAttrs, /* closed= */ true)
+ /* tag= */ "AF", packageNameAttrs, /* closed= */ true)
+ "</R>"
+ "</RL>";
RuleParser xmlParser = new RuleXmlParser();
@@ -481,11 +482,11 @@ public class RuleXmlParserTest {
String ruleXmlAtomicFormula =
"<RL>"
+ generateTagWithAttribute(
- /* tag= */ "R",
- Collections.singletonMap("E", String.valueOf(Rule.DENY)),
- /* closed= */ false)
+ /* tag= */ "R",
+ Collections.singletonMap("E", String.valueOf(Rule.DENY)),
+ /* closed= */ false)
+ generateTagWithAttribute(
- /* tag= */ "AF", packageNameAttrs, /* closed= */ true)
+ /* tag= */ "AF", packageNameAttrs, /* closed= */ true)
+ "</R>"
+ "</RL>";
RuleParser xmlParser = new RuleXmlParser();
@@ -504,11 +505,11 @@ public class RuleXmlParserTest {
String ruleXmlAtomicFormula =
"<RL>"
+ generateTagWithAttribute(
- /* tag= */ "R",
- Collections.singletonMap("BadEffect", String.valueOf(Rule.DENY)),
- /* closed= */ false)
+ /* tag= */ "R",
+ Collections.singletonMap("BadEffect", String.valueOf(Rule.DENY)),
+ /* closed= */ false)
+ generateTagWithAttribute(
- /* tag= */ "AF", packageNameAttrs, /* closed= */ true)
+ /* tag= */ "AF", packageNameAttrs, /* closed= */ true)
+ "</R>"
+ "</RL>";
RuleParser xmlParser = new RuleXmlParser();
@@ -526,16 +527,16 @@ public class RuleXmlParserTest {
String ruleXmlCompoundFormula =
"<RL>"
+ generateTagWithAttribute(
- /* tag= */ "R",
- Collections.singletonMap("E", String.valueOf(Rule.DENY)),
- /* closed= */ false)
+ /* tag= */ "R",
+ Collections.singletonMap("E", String.valueOf(Rule.DENY)),
+ /* closed= */ false)
+ generateTagWithAttribute(
- /* tag= */ "OF",
- Collections.singletonMap(
- "BadConnector", String.valueOf(CompoundFormula.NOT)),
- /* closed= */ false)
+ /* tag= */ "OF",
+ Collections.singletonMap(
+ "BadConnector", String.valueOf(CompoundFormula.NOT)),
+ /* closed= */ false)
+ generateTagWithAttribute(
- /* tag= */ "AF", packageNameAttrs, /* closed= */ true)
+ /* tag= */ "AF", packageNameAttrs, /* closed= */ true)
+ "</OF>"
+ "</R>"
+ "</RL>";
@@ -555,11 +556,11 @@ public class RuleXmlParserTest {
String ruleXmlAtomicFormula =
"<RL>"
+ generateTagWithAttribute(
- /* tag= */ "R",
- Collections.singletonMap("E", String.valueOf(Rule.DENY)),
- /* closed= */ false)
+ /* tag= */ "R",
+ Collections.singletonMap("E", String.valueOf(Rule.DENY)),
+ /* closed= */ false)
+ generateTagWithAttribute(
- /* tag= */ "AF", packageNameAttrs, /* closed= */ true)
+ /* tag= */ "AF", packageNameAttrs, /* closed= */ true)
+ "</R>"
+ "</RL>";
RuleParser xmlParser = new RuleXmlParser();
@@ -577,15 +578,15 @@ public class RuleXmlParserTest {
atomicFormulaAttrs.put("V", "com.app.test");
String ruleXmlWithNoRuleList =
generateTagWithAttribute(
- /* tag= */ "R",
- Collections.singletonMap("E", String.valueOf(Rule.DENY)),
- /* closed= */ false)
+ /* tag= */ "R",
+ Collections.singletonMap("E", String.valueOf(Rule.DENY)),
+ /* closed= */ false)
+ generateTagWithAttribute(
- /* tag= */ "OF",
- Collections.singletonMap("C", String.valueOf(CompoundFormula.NOT)),
- /* closed= */ false)
+ /* tag= */ "OF",
+ Collections.singletonMap("C", String.valueOf(CompoundFormula.NOT)),
+ /* closed= */ false)
+ generateTagWithAttribute(
- /* tag= */ "AF", atomicFormulaAttrs, /* closed= */ true)
+ /* tag= */ "AF", atomicFormulaAttrs, /* closed= */ true)
+ "</OF>"
+ "</R>";
RuleParser xmlParser = new RuleXmlParser();
@@ -603,15 +604,15 @@ public class RuleXmlParserTest {
atomicFormulaAttrs.put("V", "com.app.test");
String ruleXmlWithNoRuleList =
generateTagWithAttribute(
- /* tag= */ "R",
- Collections.singletonMap("E", String.valueOf(Rule.DENY)),
- /* closed= */ false)
+ /* tag= */ "R",
+ Collections.singletonMap("E", String.valueOf(Rule.DENY)),
+ /* closed= */ false)
+ generateTagWithAttribute(
- /* tag= */ "OF",
- Collections.singletonMap("C", String.valueOf(CompoundFormula.NOT)),
- /* closed= */ false)
+ /* tag= */ "OF",
+ Collections.singletonMap("C", String.valueOf(CompoundFormula.NOT)),
+ /* closed= */ false)
+ generateTagWithAttribute(
- /* tag= */ "AF", atomicFormulaAttrs, /* closed= */ true)
+ /* tag= */ "AF", atomicFormulaAttrs, /* closed= */ true)
+ "</OF>"
+ "</R>";
RuleParser xmlParser = new RuleXmlParser();
diff --git a/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleBinarySerializerTest.java b/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleBinarySerializerTest.java
index e5cbeee2860d..f3da286585fd 100644
--- a/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleBinarySerializerTest.java
+++ b/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleBinarySerializerTest.java
@@ -42,13 +42,12 @@ import static com.google.common.truth.Truth.assertThat;
import android.content.integrity.AppInstallMetadata;
import android.content.integrity.AtomicFormula;
import android.content.integrity.CompoundFormula;
-import android.content.integrity.Formula;
+import android.content.integrity.IntegrityFormula;
+import android.content.integrity.IntegrityUtils;
import android.content.integrity.Rule;
import androidx.annotation.NonNull;
-import com.android.server.integrity.IntegrityUtils;
-
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@@ -424,11 +423,12 @@ public class RuleBinarySerializerTest {
@Test
public void testBinaryString_serializeValidAtomicFormula_integerValue() throws Exception {
- int versionCode = 1;
+ long versionCode = 1;
Rule rule =
new Rule(
- new AtomicFormula.IntAtomicFormula(
- AtomicFormula.VERSION_CODE, AtomicFormula.EQ, versionCode),
+ new AtomicFormula.LongAtomicFormula(
+ AtomicFormula.VERSION_CODE, AtomicFormula.EQ,
+ versionCode),
Rule.DENY);
RuleSerializer binarySerializer = new RuleBinarySerializer();
String expectedBits =
@@ -436,7 +436,7 @@ public class RuleBinarySerializerTest {
+ ATOMIC_FORMULA_START_BITS
+ VERSION_CODE
+ EQ
- + getBits(versionCode, /* numOfBits= */ 32)
+ + getBits(versionCode, /* numOfBits= */ 64)
+ DENY
+ END_BIT;
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
@@ -456,7 +456,8 @@ public class RuleBinarySerializerTest {
String preInstalled = "1";
Rule rule =
new Rule(
- new AtomicFormula.BooleanAtomicFormula(AtomicFormula.PRE_INSTALLED, true),
+ new AtomicFormula.BooleanAtomicFormula(
+ AtomicFormula.PRE_INSTALLED, true),
Rule.DENY);
RuleSerializer binarySerializer = new RuleBinarySerializer();
String expectedBits =
@@ -481,7 +482,7 @@ public class RuleBinarySerializerTest {
@Test
public void testBinaryString_serializeInvalidFormulaType() throws Exception {
- Formula invalidFormula = getInvalidFormula();
+ IntegrityFormula invalidFormula = getInvalidFormula();
Rule rule = new Rule(invalidFormula, Rule.DENY);
RuleSerializer binarySerializer = new RuleBinarySerializer();
@@ -858,16 +859,16 @@ public class RuleBinarySerializerTest {
+ END_BIT;
}
- private static Formula getInvalidFormula() {
- return new Formula() {
+ private static IntegrityFormula getInvalidFormula() {
+ return new AtomicFormula(0) {
@Override
- public boolean isSatisfied(AppInstallMetadata appInstallMetadata) {
- return false;
+ public int getTag() {
+ return 0;
}
@Override
- public int getTag() {
- return 0;
+ public boolean matches(AppInstallMetadata appInstallMetadata) {
+ return false;
}
@Override
diff --git a/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleIndexingDetailsIdentifierTest.java b/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleIndexingDetailsIdentifierTest.java
index 1674422f3af9..038ab7ff0c35 100644
--- a/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleIndexingDetailsIdentifierTest.java
+++ b/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleIndexingDetailsIdentifierTest.java
@@ -27,7 +27,7 @@ import static com.google.common.truth.Truth.assertThat;
import android.content.integrity.AppInstallMetadata;
import android.content.integrity.AtomicFormula;
import android.content.integrity.CompoundFormula;
-import android.content.integrity.Formula;
+import android.content.integrity.IntegrityFormula;
import android.content.integrity.Rule;
import androidx.annotation.NonNull;
@@ -71,9 +71,11 @@ public class RuleIndexingDetailsIdentifierTest {
SAMPLE_INSTALLER_CERTIFICATE,
/* isHashedValue= */ false);
private static final AtomicFormula ATOMIC_FORMULA_WITH_VERSION_CODE =
- new AtomicFormula.IntAtomicFormula(AtomicFormula.VERSION_CODE, AtomicFormula.EQ, 12);
+ new AtomicFormula.LongAtomicFormula(AtomicFormula.VERSION_CODE,
+ AtomicFormula.EQ, 12);
private static final AtomicFormula ATOMIC_FORMULA_WITH_ISPREINSTALLED =
- new AtomicFormula.BooleanAtomicFormula(AtomicFormula.PRE_INSTALLED, /* booleanValue= */
+ new AtomicFormula.BooleanAtomicFormula(
+ AtomicFormula.PRE_INSTALLED, /* booleanValue= */
true);
@@ -284,16 +286,16 @@ public class RuleIndexingDetailsIdentifierTest {
Rule.DENY);
}
- private Formula getInvalidFormula() {
- return new Formula() {
+ private IntegrityFormula getInvalidFormula() {
+ return new AtomicFormula(0) {
@Override
- public boolean isSatisfied(AppInstallMetadata appInstallMetadata) {
- return false;
+ public int getTag() {
+ return 4;
}
@Override
- public int getTag() {
- return 4;
+ public boolean matches(AppInstallMetadata appInstallMetadata) {
+ return false;
}
@Override
diff --git a/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleXmlSerializerTest.java b/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleXmlSerializerTest.java
index ff7722c07d29..6558cd538638 100644
--- a/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleXmlSerializerTest.java
+++ b/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleXmlSerializerTest.java
@@ -23,7 +23,7 @@ import static org.junit.Assert.assertEquals;
import android.content.integrity.AppInstallMetadata;
import android.content.integrity.AtomicFormula;
import android.content.integrity.CompoundFormula;
-import android.content.integrity.Formula;
+import android.content.integrity.IntegrityFormula;
import android.content.integrity.Rule;
import androidx.annotation.NonNull;
@@ -328,7 +328,7 @@ public class RuleXmlSerializerTest {
public void testXmlString_serializeValidAtomicFormula_integerValue() throws Exception {
Rule rule =
new Rule(
- new AtomicFormula.IntAtomicFormula(
+ new AtomicFormula.LongAtomicFormula(
AtomicFormula.VERSION_CODE, AtomicFormula.EQ, 1),
Rule.DENY);
RuleSerializer xmlSerializer = new RuleXmlSerializer();
@@ -384,7 +384,7 @@ public class RuleXmlSerializerTest {
@Test
public void testXmlString_serializeInvalidFormulaType() throws Exception {
- Formula invalidFormula = getInvalidFormula();
+ IntegrityFormula invalidFormula = getInvalidFormula();
Rule rule = new Rule(invalidFormula, Rule.DENY);
RuleSerializer xmlSerializer = new RuleXmlSerializer();
@@ -530,16 +530,16 @@ public class RuleXmlSerializerTest {
+ "</R>";
}
- private Formula getInvalidFormula() {
- return new Formula() {
+ private IntegrityFormula getInvalidFormula() {
+ return new AtomicFormula(0) {
@Override
- public boolean isSatisfied(AppInstallMetadata appInstallMetadata) {
- return false;
+ public int getTag() {
+ return 0;
}
@Override
- public int getTag() {
- return 0;
+ public boolean matches(AppInstallMetadata appInstallMetadata) {
+ return false;
}
@Override
diff --git a/services/tests/servicestests/src/com/android/server/integrity/utils/TestUtils.java b/services/tests/servicestests/src/com/android/server/integrity/utils/TestUtils.java
index e54410b04b79..55abc7c1050d 100644
--- a/services/tests/servicestests/src/com/android/server/integrity/utils/TestUtils.java
+++ b/services/tests/servicestests/src/com/android/server/integrity/utils/TestUtils.java
@@ -18,8 +18,8 @@ package com.android.server.integrity.utils;
public class TestUtils {
- public static String getBits(int component, int numOfBits) {
- return String.format("%" + numOfBits + "s", Integer.toBinaryString(component))
+ public static String getBits(long component, int numOfBits) {
+ return String.format("%" + numOfBits + "s", Long.toBinaryString(component))
.replace(' ', '0');
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
index 8329227360e7..cf51fa31fad3 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
@@ -20,6 +20,8 @@ import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
+import static android.content.pm.SuspendDialogInfo.BUTTON_ACTION_MORE_DETAILS;
+import static android.content.pm.SuspendDialogInfo.BUTTON_ACTION_UNSUSPEND;
import static android.content.res.Resources.ID_NULL;
import static org.hamcrest.CoreMatchers.is;
@@ -217,6 +219,7 @@ public class PackageManagerSettingsTests {
assertThat(params.dialogInfo.getTitleResId(), is(ID_NULL));
assertThat(params.dialogInfo.getIconResId(), is(TEST_RESOURCE_ID));
assertThat(params.dialogInfo.getNeutralButtonTextResId(), is(ID_NULL));
+ assertThat(params.dialogInfo.getNeutralButtonAction(), is(BUTTON_ACTION_MORE_DETAILS));
assertThat(params.dialogInfo.getDialogMessageResId(), is(ID_NULL));
}
@@ -243,12 +246,14 @@ public class PackageManagerSettingsTests {
.setTitle(0x11220002)
.setMessage("1st message")
.setNeutralButtonText(0x11220003)
+ .setNeutralButtonAction(BUTTON_ACTION_MORE_DETAILS)
.build();
final SuspendDialogInfo dialogInfo2 = new SuspendDialogInfo.Builder()
.setIcon(0x22220001)
.setTitle(0x22220002)
.setMessage("2nd message")
.setNeutralButtonText(0x22220003)
+ .setNeutralButtonAction(BUTTON_ACTION_UNSUSPEND)
.build();
ps1.addOrUpdateSuspension("suspendingPackage1", dialogInfo1, appExtras1, launcherExtras1,
diff --git a/services/tests/servicestests/src/com/android/server/pm/ParallelPackageParserTest.java b/services/tests/servicestests/src/com/android/server/pm/ParallelPackageParserTest.java
index a0efc8a03719..da5986f71f11 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ParallelPackageParserTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ParallelPackageParserTest.java
@@ -31,6 +31,7 @@ import org.junit.runner.RunWith;
import java.io.File;
import java.util.HashSet;
import java.util.Set;
+import java.util.concurrent.ExecutorService;
/**
* Tests for {@link ParallelPackageParser}
@@ -43,7 +44,8 @@ public class ParallelPackageParserTest {
@Before
public void setUp() {
- mParser = new TestParallelPackageParser();
+ mParser = new TestParallelPackageParser(new PackageParser(),
+ ParallelPackageParser.makeExecutorService());
}
@Test(timeout = 1000)
@@ -68,15 +70,14 @@ public class ParallelPackageParserTest {
}
}
- class TestParallelPackageParser extends ParallelPackageParser {
+ private class TestParallelPackageParser extends ParallelPackageParser {
- TestParallelPackageParser() {
- super(null, false, null, null, null);
+ TestParallelPackageParser(PackageParser packageParser, ExecutorService executorService) {
+ super(packageParser, executorService);
}
@Override
- protected ParsedPackage parsePackage(PackageParser packageParser, File scanFile,
- int parseFlags) throws PackageParser.PackageParserException {
+ protected ParsedPackage parsePackage(File scanFile, int parseFlags) {
// Do not actually parse the package for testing
return null;
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/SuspendDialogInfoTest.java b/services/tests/servicestests/src/com/android/server/pm/SuspendDialogInfoTest.java
index 7eccd6728533..322e448d983f 100644
--- a/services/tests/servicestests/src/com/android/server/pm/SuspendDialogInfoTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/SuspendDialogInfoTest.java
@@ -16,6 +16,9 @@
package com.android.server.pm;
+import static android.content.pm.SuspendDialogInfo.BUTTON_ACTION_MORE_DETAILS;
+import static android.content.pm.SuspendDialogInfo.BUTTON_ACTION_UNSUSPEND;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNull;
@@ -39,7 +42,8 @@ public class SuspendDialogInfoTest {
.setIcon(VALID_TEST_RES_ID_1)
.setTitle(VALID_TEST_RES_ID_1)
.setMessage(VALID_TEST_RES_ID_1)
- .setNeutralButtonText(VALID_TEST_RES_ID_1);
+ .setNeutralButtonText(VALID_TEST_RES_ID_1)
+ .setNeutralButtonAction(BUTTON_ACTION_MORE_DETAILS);
}
@Test
@@ -73,6 +77,25 @@ public class SuspendDialogInfoTest {
}
@Test
+ public void equalsComparesButtonAction() {
+ final SuspendDialogInfo.Builder dialogBuilder1 = createDefaultDialogBuilder();
+ final SuspendDialogInfo.Builder dialogBuilder2 = createDefaultDialogBuilder();
+ assertEquals(dialogBuilder1.build(), dialogBuilder2.build());
+ // Only button action is different
+ dialogBuilder2.setNeutralButtonAction(BUTTON_ACTION_UNSUSPEND);
+ assertNotEquals(dialogBuilder1.build(), dialogBuilder2.build());
+ }
+
+ @Test
+ public void defaultButtonAction() {
+ final SuspendDialogInfo.Builder dialogBuilder = new SuspendDialogInfo.Builder()
+ .setIcon(VALID_TEST_RES_ID_1)
+ .setTitle(VALID_TEST_RES_ID_1)
+ .setMessage(VALID_TEST_RES_ID_1);
+ assertEquals(BUTTON_ACTION_MORE_DETAILS, dialogBuilder.build().getNeutralButtonAction());
+ }
+
+ @Test
public void equalsComparesMessageIds() {
final SuspendDialogInfo.Builder dialogBuilder1 = createDefaultDialogBuilder();
final SuspendDialogInfo.Builder dialogBuilder2 = createDefaultDialogBuilder();
diff --git a/services/tests/servicestests/src/com/android/server/usage/IntervalStatsTests.java b/services/tests/servicestests/src/com/android/server/usage/IntervalStatsTests.java
index f1b2ef811885..5d849c114e69 100644
--- a/services/tests/servicestests/src/com/android/server/usage/IntervalStatsTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/IntervalStatsTests.java
@@ -92,6 +92,9 @@ public class IntervalStatsTests {
case UsageEvents.Event.NOTIFICATION_INTERRUPTION:
event.mNotificationChannelId = "channel" + (i % 5); //"random" channel
break;
+ case UsageEvents.Event.LOCUS_ID_SET:
+ event.mLocusId = "locus" + (i % 7); //"random" locus
+ break;
}
intervalStats.addEvent(event);
diff --git a/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java b/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java
index e6bb244ef05b..f1c39067994c 100644
--- a/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java
+++ b/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java
@@ -52,6 +52,7 @@ import java.util.Set;
public class UsageStatsDatabaseTest {
private static final int MAX_TESTED_VERSION = 5;
+ private static final int OLDER_VERSION_MAX_EVENT_TYPE = 29;
protected Context mContext;
private UsageStatsDatabase mUsageStatsDatabase;
private File mTestDir;
@@ -79,7 +80,7 @@ public class UsageStatsDatabaseTest {
mUsageStatsDatabase = new UsageStatsDatabase(mTestDir);
mUsageStatsDatabase.readMappingsLocked();
mUsageStatsDatabase.init(1);
- populateIntervalStats();
+ populateIntervalStats(MAX_TESTED_VERSION);
clearUsageStatsFiles();
}
@@ -117,7 +118,7 @@ public class UsageStatsDatabaseTest {
return sb.toString();
}
- private void populateIntervalStats() {
+ private void populateIntervalStats(int minVersion) {
final int numberOfEvents = 3000;
final int timeProgression = 23;
long time = System.currentTimeMillis() - (numberOfEvents*timeProgression);
@@ -147,9 +148,12 @@ public class UsageStatsDatabaseTest {
final int instanceId = i % 11;
event.mClass = ".fake.class.name" + instanceId;
event.mTimeStamp = time;
- event.mEventType = i % (MAX_EVENT_TYPE + 1); //"random" event type
event.mInstanceId = instanceId;
+ int maxEventType = (minVersion < 5) ? OLDER_VERSION_MAX_EVENT_TYPE : MAX_EVENT_TYPE;
+ event.mEventType = i % (maxEventType + 1); //"random" event type
+
+
final int rootPackageInt = (i % 5); // 5 "apps" start each task
event.mTaskRootPackage = "fake.package.name" + rootPackageInt;
@@ -174,6 +178,9 @@ public class UsageStatsDatabaseTest {
//"random" channel
event.mNotificationChannelId = "channel" + (i % 5);
break;
+ case Event.LOCUS_ID_SET:
+ event.mLocusId = "locus" + (i % 7); //"random" locus
+ break;
}
mIntervalStats.addEvent(event);
@@ -281,6 +288,10 @@ public class UsageStatsDatabaseTest {
assertEquals(e1.mNotificationChannelIdToken, e2.mNotificationChannelIdToken,
"Usage event " + debugId);
break;
+ case Event.LOCUS_ID_SET:
+ assertEquals(e1.mLocusIdToken, e2.mLocusIdToken,
+ "Usage event " + debugId);
+ break;
}
// fallthrough
case 4: // test fields added in version 4
@@ -392,6 +403,7 @@ public class UsageStatsDatabaseTest {
* version and read the automatically upgraded files on disk in the new file format.
*/
void runVersionChangeTest(int oldVersion, int newVersion, int interval) throws IOException {
+ populateIntervalStats(oldVersion);
// Write IntervalStats to disk in old version format
UsageStatsDatabase prevDB = new UsageStatsDatabase(mTestDir, oldVersion);
prevDB.readMappingsLocked();
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
index 587cfbf062fb..651ad40a4781 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
@@ -98,6 +98,7 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase {
NotificationUsageStats mUsageStats;
@Mock
IAccessibilityManager mAccessibilityService;
+ NotificationRecordLoggerFake mNotificationRecordLogger = new NotificationRecordLoggerFake();
private NotificationManagerService mService;
private String mPkg = "com.android.server.notification";
@@ -148,7 +149,7 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase {
verify(mAccessibilityService).addClient(any(IAccessibilityManagerClient.class), anyInt());
assertTrue(accessibilityManager.isEnabled());
- mService = spy(new NotificationManagerService(getContext()));
+ mService = spy(new NotificationManagerService(getContext(), mNotificationRecordLogger));
mService.setAudioManager(mAudioManager);
mService.setVibrator(mVibrator);
mService.setSystemReady(true);
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 93e09dfb3f57..1b92abef7c94 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -256,6 +256,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
UserManager mUm;
@Mock
NotificationHistoryManager mHistoryManager;
+ NotificationRecordLoggerFake mNotificationRecordLogger = new NotificationRecordLoggerFake();
+
// Use a Testable subclass so we can simulate calls from the system without failing.
private static class TestableNotificationManagerService extends NotificationManagerService {
@@ -265,8 +267,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
@Nullable
NotificationAssistantAccessGrantedCallback mNotificationAssistantAccessGrantedCallback;
- TestableNotificationManagerService(Context context) {
- super(context);
+ TestableNotificationManagerService(Context context, NotificationRecordLogger logger) {
+ super(context, logger);
}
@Override
@@ -353,7 +355,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
doNothing().when(mContext).sendBroadcastAsUser(any(), any(), any());
- mService = new TestableNotificationManagerService(mContext);
+ mService = new TestableNotificationManagerService(mContext, mNotificationRecordLogger);
// Use this testable looper.
mTestableLooper = TestableLooper.get(this);
@@ -1118,6 +1120,61 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
@Test
+ public void testEnqueueNotificationWithTag_WritesExpectedLog() throws Exception {
+ final String tag = "testEnqueueNotificationWithTag_WritesExpectedLog";
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, tag, 0,
+ generateNotificationRecord(null).getNotification(), 0);
+ waitForIdle();
+ assertEquals(1, mNotificationRecordLogger.getCalls().size());
+ NotificationRecordLoggerFake.CallRecord call = mNotificationRecordLogger.get(0);
+ assertTrue(call.shouldLog());
+ assertNotNull(call.r);
+ assertNull(call.old);
+ assertEquals(0, call.position);
+ assertEquals(0, call.buzzBeepBlink);
+ assertEquals(PKG, call.r.sbn.getPackageName());
+ assertEquals(0, call.r.sbn.getId());
+ assertEquals(tag, call.r.sbn.getTag());
+ }
+
+ @Test
+ public void testEnqueueNotificationWithTag_LogsOnMajorUpdates() throws Exception {
+ final String tag = "testEnqueueNotificationWithTag_LogsOnMajorUpdates";
+ Notification original = new Notification.Builder(mContext,
+ mTestNotificationChannel.getId())
+ .setSmallIcon(android.R.drawable.sym_def_app_icon).build();
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, tag, 0, original, 0);
+ Notification update = new Notification.Builder(mContext,
+ mTestNotificationChannel.getId())
+ .setSmallIcon(android.R.drawable.sym_def_app_icon)
+ .setCategory(Notification.CATEGORY_ALARM).build();
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, tag, 0, update, 0);
+ waitForIdle();
+ assertEquals(2, mNotificationRecordLogger.getCalls().size());
+ assertTrue(mNotificationRecordLogger.get(0).shouldLog());
+ assertEquals(
+ NotificationRecordLogger.NotificationReportedEvents.NOTIFICATION_POSTED,
+ mNotificationRecordLogger.get(0).getUiEvent());
+ assertEquals(
+ NotificationRecordLogger.NotificationReportedEvents.NOTIFICATION_UPDATED,
+ mNotificationRecordLogger.get(1).getUiEvent());
+ assertTrue(mNotificationRecordLogger.get(1).shouldLog());
+ }
+
+ @Test
+ public void testEnqueueNotificationWithTag_DoesNotLogOnMinorUpdates() throws Exception {
+ final String tag = "testEnqueueNotificationWithTag_DoesNotLogOnMinorUpdates";
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, tag, 0,
+ generateNotificationRecord(null).getNotification(), 0);
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, tag, 0,
+ generateNotificationRecord(null).getNotification(), 0);
+ waitForIdle();
+ assertEquals(2, mNotificationRecordLogger.getCalls().size());
+ assertTrue(mNotificationRecordLogger.get(0).shouldLog());
+ assertFalse(mNotificationRecordLogger.get(1).shouldLog());
+ }
+
+ @Test
public void testCancelNotificationImmediatelyAfterEnqueue() throws Exception {
mBinderService.enqueueNotificationWithTag(PKG, PKG,
"testCancelNotificationImmediatelyAfterEnqueue", 0,
@@ -2252,7 +2309,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
@Test
public void testHasCompanionDevice_noService() {
- mService = new TestableNotificationManagerService(mContext);
+ mService = new TestableNotificationManagerService(mContext, mNotificationRecordLogger);
assertFalse(mService.hasCompanionDevice(mListener));
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerFake.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerFake.java
new file mode 100644
index 000000000000..11972fd6a8dc
--- /dev/null
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerFake.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.notification;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Fake implementation of NotificationRecordLogger, for testing.
+ */
+class NotificationRecordLoggerFake implements NotificationRecordLogger {
+ class CallRecord extends NotificationRecordPair {
+ public int position, buzzBeepBlink;
+ CallRecord(NotificationRecord r, NotificationRecord old, int position,
+ int buzzBeepBlink) {
+ super(r, old);
+ this.position = position;
+ this.buzzBeepBlink = buzzBeepBlink;
+ }
+ boolean shouldLog() {
+ return shouldLog(buzzBeepBlink);
+ }
+ }
+ List<CallRecord> mCalls = new ArrayList<CallRecord>();
+
+ List<CallRecord> getCalls() {
+ return mCalls;
+ }
+
+ CallRecord get(int index) {
+ return mCalls.get(index);
+ }
+
+ @Override
+ public void logNotificationReported(NotificationRecord r, NotificationRecord old,
+ int position, int buzzBeepBlink) {
+ mCalls.add(new CallRecord(r, old, position, buzzBeepBlink));
+ }
+}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java b/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java
index c828f02901b3..e18c8919b645 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java
@@ -18,7 +18,6 @@ package com.android.server.notification;
import static android.app.role.RoleManager.ROLE_DIALER;
import static android.app.role.RoleManager.ROLE_EMERGENCY;
-import static android.app.role.RoleManager.ROLE_SMS;
import static android.content.pm.PackageManager.MATCH_ALL;
import static junit.framework.Assert.assertFalse;
@@ -92,24 +91,20 @@ public class RoleObserverTest extends UiServiceTestCase {
private Executor mExecutor;
@Mock
private RoleManager mRoleManager;
+ NotificationRecordLoggerFake mNotificationRecordLogger = new NotificationRecordLoggerFake();
private List<UserInfo> mUsers;
private static class TestableNotificationManagerService extends NotificationManagerService {
-
- TestableNotificationManagerService(Context context) {
- super(context);
+ TestableNotificationManagerService(Context context, NotificationRecordLogger logger) {
+ super(context, logger);
}
@Override
- protected void handleSavePolicyFile() {
- return;
- }
+ protected void handleSavePolicyFile() { }
@Override
- protected void loadPolicyFile() {
- return;
- }
+ protected void loadPolicyFile() { }
}
@Before
@@ -125,7 +120,7 @@ public class RoleObserverTest extends UiServiceTestCase {
mUsers.add(new UserInfo(10, "second", 0));
when(mUm.getUsers()).thenReturn(mUsers);
- mService = new TestableNotificationManagerService(mContext);
+ mService = new TestableNotificationManagerService(mContext, mNotificationRecordLogger);
mRoleObserver = mService.new RoleObserver(mRoleManager, mPm, mExecutor);
try {
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
index 135d00586329..399cf49ac06f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
@@ -16,7 +16,6 @@
package com.android.server.wm;
-import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_DEFAULT;
import static android.content.pm.ApplicationInfo.FLAG_SUSPENDED;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
@@ -45,7 +44,6 @@ import android.testing.DexmakerShareClassLoaderRule;
import androidx.test.filters.SmallTest;
-import com.android.internal.app.BlockedAppActivity;
import com.android.internal.app.HarmfulAppWarningActivity;
import com.android.internal.app.SuspendedAppActivity;
import com.android.internal.app.UnlaunchableAppActivity;
@@ -107,8 +105,6 @@ public class ActivityStartInterceptorTest {
private PackageManagerService mPackageManager;
@Mock
private ActivityManagerInternal mAmInternal;
- @Mock
- private LockTaskController mLockTaskController;
private ActivityStartInterceptor mInterceptor;
private ActivityInfo mAInfo = new ActivityInfo();
@@ -149,13 +145,6 @@ public class ActivityStartInterceptorTest {
when(mPackageManager.getHarmfulAppWarning(TEST_PACKAGE_NAME, TEST_USER_ID))
.thenReturn(null);
- // Mock LockTaskController
- mAInfo.lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_DEFAULT;
- when(mService.getLockTaskController()).thenReturn(mLockTaskController);
- when(mLockTaskController.isActivityAllowed(
- TEST_USER_ID, TEST_PACKAGE_NAME, LOCK_TASK_LAUNCH_MODE_DEFAULT))
- .thenReturn(true);
-
// Initialise activity info
mAInfo.applicationInfo = new ApplicationInfo();
mAInfo.packageName = mAInfo.applicationInfo.packageName = TEST_PACKAGE_NAME;
@@ -207,18 +196,6 @@ public class ActivityStartInterceptorTest {
}
@Test
- public void testInterceptLockTaskModeViolationPackage() {
- when(mLockTaskController.isActivityAllowed(
- TEST_USER_ID, TEST_PACKAGE_NAME, LOCK_TASK_LAUNCH_MODE_DEFAULT))
- .thenReturn(false);
-
- assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null, 0, 0, null));
-
- assertTrue(BlockedAppActivity.createIntent(TEST_USER_ID, TEST_PACKAGE_NAME)
- .filterEquals(mInterceptor.mIntent));
- }
-
- @Test
public void testInterceptQuietProfile() {
// GIVEN that the user the activity is starting as is currently in quiet mode
when(mUserManager.isQuietModeEnabled(eq(UserHandle.of(TEST_USER_ID)))).thenReturn(true);
diff --git a/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java
index 75ec53d69a71..039ff604f3f1 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java
@@ -29,9 +29,6 @@ import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_HOME;
import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_KEYGUARD;
import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_NONE;
import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_NOTIFICATIONS;
-import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_ALWAYS;
-import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_DEFAULT;
-import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_NEVER;
import static android.os.Process.SYSTEM_UID;
import static android.telecom.TelecomManager.EMERGENCY_DIALER_COMPONENT;
@@ -696,38 +693,6 @@ public class LockTaskControllerTest {
assertTrue((StatusBarManager.DISABLE2_QUICK_SETTINGS & flags.second) != 0);
}
- @Test
- public void testIsActivityAllowed() {
- // WHEN lock task mode is not enabled
- assertTrue(mLockTaskController.isActivityAllowed(
- TEST_USER_ID, TEST_PACKAGE_NAME, LOCK_TASK_LAUNCH_MODE_DEFAULT));
-
- // WHEN lock task mode is enabled
- Task tr = getTask(Task.LOCK_TASK_AUTH_WHITELISTED);
- mLockTaskController.startLockTaskMode(tr, false, TEST_UID);
-
-
- // package with LOCK_TASK_LAUNCH_MODE_ALWAYS should always be allowed
- assertTrue(mLockTaskController.isActivityAllowed(
- TEST_USER_ID, TEST_PACKAGE_NAME, LOCK_TASK_LAUNCH_MODE_ALWAYS));
-
- // unwhitelisted package should not be allowed
- assertFalse(mLockTaskController.isActivityAllowed(
- TEST_USER_ID, TEST_PACKAGE_NAME, LOCK_TASK_LAUNCH_MODE_DEFAULT));
-
- // update the whitelist
- String[] whitelist = new String[] { TEST_PACKAGE_NAME };
- mLockTaskController.updateLockTaskPackages(TEST_USER_ID, whitelist);
-
- // whitelisted package should be allowed
- assertTrue(mLockTaskController.isActivityAllowed(
- TEST_USER_ID, TEST_PACKAGE_NAME, LOCK_TASK_LAUNCH_MODE_DEFAULT));
-
- // package with LOCK_TASK_LAUNCH_MODE_NEVER should never be allowed
- assertFalse(mLockTaskController.isActivityAllowed(
- TEST_USER_ID, TEST_PACKAGE_NAME, LOCK_TASK_LAUNCH_MODE_NEVER));
- }
-
private Task getTask(int lockTaskAuth) {
return getTask(TEST_PACKAGE_NAME, lockTaskAuth);
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java
index eb8eb98fd484..d73830468359 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java
@@ -31,6 +31,7 @@ import android.graphics.Rect;
import android.os.SystemClock;
import android.platform.test.annotations.Presubmit;
import android.util.ArraySet;
+import android.view.Surface;
import android.view.View;
import androidx.test.filters.MediumTest;
@@ -47,7 +48,7 @@ import java.util.function.Predicate;
* Test class for {@link TaskSnapshotPersister} and {@link TaskSnapshotLoader}
*
* Build/Install/Run:
- * atest WmTests:TaskSnapshotPersisterLoaderTest
+ * atest TaskSnapshotPersisterLoaderTest
*/
@MediumTest
@Presubmit
@@ -316,4 +317,18 @@ public class TaskSnapshotPersisterLoaderTest extends TaskSnapshotPersisterTestBa
new File(FILES_DIR.getPath() + "/snapshots/2_reduced.jpg")};
assertTrueForFiles(existsFiles, File::exists, " must exist");
}
+
+ @Test
+ public void testRotationPersistAndLoadSnapshot() {
+ TaskSnapshot a = new TaskSnapshotBuilder()
+ .setRotation(Surface.ROTATION_270)
+ .build();
+ mPersister.persistSnapshot(1 , mTestUserId, createSnapshot());
+ mPersister.persistSnapshot(2 , mTestUserId, a);
+ mPersister.waitForQueueEmpty();
+ final TaskSnapshot snapshotA = mLoader.loadTask(1, mTestUserId, false /* reduced */);
+ final TaskSnapshot snapshotB = mLoader.loadTask(2, mTestUserId, false /* reduced */);
+ assertEquals(Surface.ROTATION_0, snapshotA.getRotation());
+ assertEquals(Surface.ROTATION_270, snapshotB.getRotation());
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java
index f7496229f66b..f86c9e63dc48 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java
@@ -32,6 +32,7 @@ import android.graphics.GraphicBuffer;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.os.UserManager;
+import android.view.Surface;
import org.junit.After;
import org.junit.Before;
@@ -93,6 +94,7 @@ class TaskSnapshotPersisterTestBase extends WindowTestsBase {
private boolean mIsTranslucent = false;
private int mWindowingMode = WINDOWING_MODE_FULLSCREEN;
private int mSystemUiVisibility = 0;
+ private int mRotation = Surface.ROTATION_0;
TaskSnapshotBuilder setScale(float scale) {
mScale = scale;
@@ -124,6 +126,11 @@ class TaskSnapshotPersisterTestBase extends WindowTestsBase {
return this;
}
+ TaskSnapshotBuilder setRotation(int rotation) {
+ mRotation = rotation;
+ return this;
+ }
+
TaskSnapshot build() {
final GraphicBuffer buffer = GraphicBuffer.create(100, 100, PixelFormat.RGBA_8888,
USAGE_HW_TEXTURE | USAGE_SW_READ_RARELY | USAGE_SW_READ_RARELY);
@@ -131,7 +138,8 @@ class TaskSnapshotPersisterTestBase extends WindowTestsBase {
c.drawColor(Color.RED);
buffer.unlockCanvasAndPost(c);
return new TaskSnapshot(MOCK_SNAPSHOT_ID, new ComponentName("", ""), buffer,
- ColorSpace.get(ColorSpace.Named.SRGB), ORIENTATION_PORTRAIT, TEST_INSETS,
+ ColorSpace.get(ColorSpace.Named.SRGB), ORIENTATION_PORTRAIT,
+ mRotation, TEST_INSETS,
mReducedResolution, mScale, mIsRealSnapshot,
mWindowingMode, mSystemUiVisibility, mIsTranslucent);
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
index ed87f3a0c604..bb0e5aec8e2e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
@@ -40,6 +40,7 @@ import android.graphics.GraphicBuffer;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
+import android.view.Surface;
import android.view.SurfaceControl;
import androidx.test.filters.SmallTest;
@@ -69,7 +70,8 @@ public class TaskSnapshotSurfaceTest extends WindowTestsBase {
final TaskSnapshot snapshot = new TaskSnapshot(
System.currentTimeMillis(),
new ComponentName("", ""), buffer,
- ColorSpace.get(ColorSpace.Named.SRGB), ORIENTATION_PORTRAIT, contentInsets, false,
+ ColorSpace.get(ColorSpace.Named.SRGB), ORIENTATION_PORTRAIT,
+ Surface.ROTATION_0, contentInsets, false,
1.0f, true /* isRealSnapshot */, WINDOWING_MODE_FULLSCREEN,
0 /* systemUiVisibility */, false /* isTranslucent */);
mSurface = new TaskSnapshotSurface(mWm, new Window(), new SurfaceControl(), snapshot, "Test",
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
index 5cf9c44ee9e4..186ff6b1515b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -323,11 +323,18 @@ public class WindowStateTests extends WindowTestsBase {
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
final WindowState first = createWindow(null, TYPE_APPLICATION, activity, "first");
final WindowState second = createWindow(null, TYPE_APPLICATION, activity, "second");
- second.mAttrs.flags |= WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON;
testPrepareWindowToDisplayDuringRelayout(first, false /* expectedWakeupCalled */,
true /* expectedCurrentLaunchCanTurnScreenOn */);
- testPrepareWindowToDisplayDuringRelayout(second, true /* expectedWakeupCalled */,
+ testPrepareWindowToDisplayDuringRelayout(second, false /* expectedWakeupCalled */,
+ true /* expectedCurrentLaunchCanTurnScreenOn */);
+
+ // Call prepareWindowToDisplayDuringRelayout for two windows from the same activity, one of
+ // which has FLAG_TURN_SCREEN_ON. The first processed one should trigger the wakeup.
+ second.mAttrs.flags |= WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON;
+ testPrepareWindowToDisplayDuringRelayout(first, true /* expectedWakeupCalled */,
+ false /* expectedCurrentLaunchCanTurnScreenOn */);
+ testPrepareWindowToDisplayDuringRelayout(second, false /* expectedWakeupCalled */,
false /* expectedCurrentLaunchCanTurnScreenOn */);
// Call prepareWindowToDisplayDuringRelayout for two window that have FLAG_TURN_SCREEN_ON
diff --git a/services/usage/java/com/android/server/usage/IntervalStats.java b/services/usage/java/com/android/server/usage/IntervalStats.java
index 8fb283adc740..8fadf5eb9333 100644
--- a/services/usage/java/com/android/server/usage/IntervalStats.java
+++ b/services/usage/java/com/android/server/usage/IntervalStats.java
@@ -28,6 +28,7 @@ import static android.app.usage.UsageEvents.Event.FOREGROUND_SERVICE_START;
import static android.app.usage.UsageEvents.Event.FOREGROUND_SERVICE_STOP;
import static android.app.usage.UsageEvents.Event.KEYGUARD_HIDDEN;
import static android.app.usage.UsageEvents.Event.KEYGUARD_SHOWN;
+import static android.app.usage.UsageEvents.Event.LOCUS_ID_SET;
import static android.app.usage.UsageEvents.Event.NOTIFICATION_INTERRUPTION;
import static android.app.usage.UsageEvents.Event.ROLLOVER_FOREGROUND_SERVICE;
import static android.app.usage.UsageEvents.Event.SCREEN_INTERACTIVE;
@@ -568,6 +569,16 @@ public class IntervalStats {
continue;
}
break;
+ case LOCUS_ID_SET:
+ event.mLocusId = packagesTokenData.getString(packageToken, event.mLocusIdToken);
+ if (event.mLocusId == null) {
+ Slog.e(TAG, "Unable to parse locus " + event.mLocusIdToken
+ + " for package " + packageToken);
+ this.events.remove(i);
+ dataOmitted = true;
+ continue;
+ }
+ break;
}
}
return dataOmitted;
@@ -675,6 +686,12 @@ public class IntervalStats {
packageToken, event.mPackage, event.mNotificationChannelId);
}
break;
+ case LOCUS_ID_SET:
+ if (!TextUtils.isEmpty(event.mLocusId)) {
+ event.mLocusIdToken = packagesTokenData.getTokenOrAdd(packageToken,
+ event.mPackage, event.mLocusId);
+ }
+ break;
}
}
}
diff --git a/services/usage/java/com/android/server/usage/UsageStatsProtoV2.java b/services/usage/java/com/android/server/usage/UsageStatsProtoV2.java
index fe5da923597e..e4aa9fe0dc1e 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsProtoV2.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsProtoV2.java
@@ -277,6 +277,10 @@ final class UsageStatsProtoV2 {
event.mTaskRootClassToken = proto.readInt(
EventObfuscatedProto.TASK_ROOT_CLASS_TOKEN) - 1;
break;
+ case (int) EventObfuscatedProto.LOCUS_ID_TOKEN:
+ event.mLocusIdToken = proto.readInt(
+ EventObfuscatedProto.LOCUS_ID_TOKEN) - 1;
+ break;
case ProtoInputStream.NO_MORE_FIELDS:
// timeStamp was not read, assume default value 0 plus beginTime
if (event.mTimeStamp == 0) {
@@ -398,6 +402,11 @@ final class UsageStatsProtoV2 {
proto.write(EventObfuscatedProto.SHORTCUT_ID_TOKEN, event.mShortcutIdToken + 1);
}
break;
+ case UsageEvents.Event.LOCUS_ID_SET:
+ if (event.mLocusIdToken != PackagesTokenData.UNASSIGNED_TOKEN) {
+ proto.write(EventObfuscatedProto.LOCUS_ID_TOKEN, event.mLocusIdToken + 1);
+ }
+ break;
case UsageEvents.Event.NOTIFICATION_INTERRUPTION:
if (event.mNotificationChannelIdToken != PackagesTokenData.UNASSIGNED_TOKEN) {
proto.write(EventObfuscatedProto.NOTIFICATION_CHANNEL_ID_TOKEN,
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 5119e5824f7f..9a18f8cd3a46 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -21,6 +21,7 @@ import static android.app.usage.UsageEvents.Event.CONFIGURATION_CHANGE;
import static android.app.usage.UsageEvents.Event.DEVICE_EVENT_PACKAGE_NAME;
import static android.app.usage.UsageEvents.Event.DEVICE_SHUTDOWN;
import static android.app.usage.UsageEvents.Event.FLUSH_TO_DISK;
+import static android.app.usage.UsageEvents.Event.LOCUS_ID_SET;
import static android.app.usage.UsageEvents.Event.NOTIFICATION_INTERRUPTION;
import static android.app.usage.UsageEvents.Event.SHORTCUT_INVOCATION;
import static android.app.usage.UsageEvents.Event.USER_STOPPED;
@@ -30,6 +31,8 @@ import static android.app.usage.UsageStatsManager.USAGE_SOURCE_TASK_ROOT_ACTIVIT
import android.Manifest;
import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.AppOpsManager;
import android.app.IUidObserver;
@@ -52,6 +55,7 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.LocusId;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
@@ -1987,6 +1991,17 @@ public class UsageStatsService extends SystemService implements
}
@Override
+ public void reportLocusUpdate(@NonNull ComponentName activity, @UserIdInt int userId,
+ @Nullable LocusId locusId, @NonNull IBinder appToken) {
+ Event event = new Event(LOCUS_ID_SET, SystemClock.elapsedRealtime());
+ event.mLocusId = locusId.getId();
+ event.mPackage = activity.getPackageName();
+ event.mClass = activity.getClassName();
+ event.mInstanceId = appToken.hashCode();
+ reportEventOrAddToQueue(userId, event);
+ }
+
+ @Override
public void reportContentProviderUsage(String name, String packageName, int userId) {
mAppStandby.postReportContentProviderUsage(name, packageName, userId);
}
diff --git a/telecomm/java/android/telecom/DisconnectCause.java b/telecomm/java/android/telecom/DisconnectCause.java
index bebbbd01fd88..0093843c4a27 100644
--- a/telecomm/java/android/telecom/DisconnectCause.java
+++ b/telecomm/java/android/telecom/DisconnectCause.java
@@ -80,20 +80,17 @@ public final class DisconnectCause implements Parcelable {
* Reason code (returned via {@link #getReason()}) which indicates that a call could not be
* completed because the cellular radio is off or out of service, the device is connected to
* a wifi network, but the user has not enabled wifi calling.
- * @hide
*/
public static final String REASON_WIFI_ON_BUT_WFC_OFF = "REASON_WIFI_ON_BUT_WFC_OFF";
/**
* Reason code (returned via {@link #getReason()}), which indicates that the video telephony
* call was disconnected because IMS access is blocked.
- * @hide
*/
public static final String REASON_IMS_ACCESS_BLOCKED = "REASON_IMS_ACCESS_BLOCKED";
/**
* Reason code, which indicates that the conference call is simulating single party conference.
- * @hide
*/
public static final String REASON_EMULATING_SINGLE_CALL = "EMULATING_SINGLE_CALL";
diff --git a/telecomm/java/android/telecom/ParcelableCallAnalytics.java b/telecomm/java/android/telecom/ParcelableCallAnalytics.java
index 2b9213ba9dbc..b8ad9e2fbe6c 100644
--- a/telecomm/java/android/telecom/ParcelableCallAnalytics.java
+++ b/telecomm/java/android/telecom/ParcelableCallAnalytics.java
@@ -258,27 +258,6 @@ public class ParcelableCallAnalytics implements Parcelable {
public static final int SIP_PHONE = 0x8;
public static final int THIRD_PARTY_PHONE = 0x10;
- /**
- * Indicating the call source is not specified.
- *
- * @hide
- */
- public static final int CALL_SOURCE_UNSPECIFIED = 0;
-
- /**
- * Indicating the call is initiated via emergency dialer's dialpad.
- *
- * @hide
- */
- public static final int CALL_SOURCE_EMERGENCY_DIALPAD = 1;
-
- /**
- * Indicating the call is initiated via emergency dialer's shortcut button.
- *
- * @hide
- */
- public static final int CALL_SOURCE_EMERGENCY_SHORTCUT = 2;
-
public static final long MILLIS_IN_5_MINUTES = 1000 * 60 * 5;
public static final long MILLIS_IN_1_SECOND = 1000;
@@ -343,7 +322,7 @@ public class ParcelableCallAnalytics implements Parcelable {
private List<VideoEvent> videoEvents;
// The source where user initiated this call. ONE OF the CALL_SOURCE_* constants.
- private int callSource = CALL_SOURCE_UNSPECIFIED;
+ private int callSource = TelecomManager.CALL_SOURCE_UNSPECIFIED;
public ParcelableCallAnalytics(long startTimeMillis, long callDurationMillis, int callType,
boolean isAdditionalCall, boolean isInterrupted, int callTechnologies,
diff --git a/telecomm/java/android/telecom/PhoneAccount.java b/telecomm/java/android/telecom/PhoneAccount.java
index abb210f13376..f00432b5fad3 100644
--- a/telecomm/java/android/telecom/PhoneAccount.java
+++ b/telecomm/java/android/telecom/PhoneAccount.java
@@ -50,6 +50,7 @@ public final class PhoneAccount implements Parcelable {
* {@link android.telecom.ConnectionService}.
* @hide
*/
+ @SystemApi
public static final String EXTRA_SORT_ORDER =
"android.telecom.extra.SORT_ORDER";
@@ -79,10 +80,13 @@ public final class PhoneAccount implements Parcelable {
public static final String EXTRA_CALL_SUBJECT_CHARACTER_ENCODING =
"android.telecom.extra.CALL_SUBJECT_CHARACTER_ENCODING";
- /**
- * Indicating flag for phone account whether to use voip audio mode for voip calls
+ /**
+ * Boolean {@link PhoneAccount} extras key (see {@link PhoneAccount#getExtras()}) which
+ * indicates that all calls from this {@link PhoneAccount} should be treated as VoIP calls
+ * rather than cellular calls.
* @hide
*/
+ @SystemApi
public static final String EXTRA_ALWAYS_USE_VOIP_AUDIO_MODE =
"android.telecom.extra.ALWAYS_USE_VOIP_AUDIO_MODE";
@@ -107,6 +111,7 @@ public final class PhoneAccount implements Parcelable {
*
* @hide
*/
+ @SystemApi
public static final String EXTRA_SUPPORTS_VIDEO_CALLING_FALLBACK =
"android.telecom.extra.SUPPORTS_VIDEO_CALLING_FALLBACK";
@@ -155,6 +160,7 @@ public final class PhoneAccount implements Parcelable {
* in progress.
* @hide
*/
+ @SystemApi
public static final String EXTRA_PLAY_CALL_RECORDING_TONE =
"android.telecom.extra.PLAY_CALL_RECORDING_TONE";
@@ -249,6 +255,7 @@ public final class PhoneAccount implements Parcelable {
* See {@link #getCapabilities}
* @hide
*/
+ @SystemApi
public static final int CAPABILITY_EMERGENCY_CALLS_ONLY = 0x80;
/**
@@ -272,6 +279,7 @@ public final class PhoneAccount implements Parcelable {
* convert all outgoing video calls to emergency numbers to audio-only.
* @hide
*/
+ @SystemApi
public static final int CAPABILITY_EMERGENCY_VIDEO_CALLING = 0x200;
/**
@@ -329,6 +337,7 @@ public final class PhoneAccount implements Parcelable {
*
* @hide
*/
+ @SystemApi
public static final int CAPABILITY_EMERGENCY_PREFERRED = 0x2000;
/**
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 49b74c6061f4..bf4dee2c1f04 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -313,15 +313,18 @@ public class TelecomManager {
"android.telecom.extra.IS_USER_INTENT_EMERGENCY_CALL";
/**
+ * A mandatory extra containing a {@link Uri} to be passed in when calling
+ * {@link #addNewUnknownCall(PhoneAccountHandle, Bundle)}. The {@link Uri} value indicates
+ * the remote handle of the new call.
* @hide
*/
+ @SystemApi
public static final String EXTRA_UNKNOWN_CALL_HANDLE =
"android.telecom.extra.UNKNOWN_CALL_HANDLE";
/**
* Optional extra for incoming and outgoing calls containing a long which specifies the time the
* call was created. This value is in milliseconds since boot.
- * @hide
*/
public static final String EXTRA_CALL_CREATED_TIME_MILLIS =
"android.telecom.extra.CALL_CREATED_TIME_MILLIS";
@@ -366,10 +369,18 @@ public class TelecomManager {
"android.telecom.extra.CONNECTION_SERVICE";
/**
- * Optional extra for communicating the call technology used by a
- * {@link com.android.internal.telephony.Connection} to Telecom
+ * Optional extra for communicating the call technology used by a {@link ConnectionService}
+ * to Telecom. Valid values are:
+ * <ul>
+ * <li>{@link TelephonyManager#PHONE_TYPE_CDMA}</li>
+ * <li>{@link TelephonyManager#PHONE_TYPE_GSM}</li>
+ * <li>{@link TelephonyManager#PHONE_TYPE_IMS}</li>
+ * <li>{@link TelephonyManager#PHONE_TYPE_THIRD_PARTY}</li>
+ * <li>{@link TelephonyManager#PHONE_TYPE_SIP}</li>
+ * </ul>
* @hide
*/
+ @SystemApi
public static final String EXTRA_CALL_TECHNOLOGY_TYPE =
"android.telecom.extra.CALL_TECHNOLOGY_TYPE";
@@ -712,21 +723,24 @@ public class TelecomManager {
* @see #EXTRA_CURRENT_TTY_MODE
* @hide
*/
+ @SystemApi
public static final String ACTION_CURRENT_TTY_MODE_CHANGED =
"android.telecom.action.CURRENT_TTY_MODE_CHANGED";
/**
* The lookup key for an int that indicates the current TTY mode.
* Valid modes are:
- * - {@link #TTY_MODE_OFF}
- * - {@link #TTY_MODE_FULL}
- * - {@link #TTY_MODE_HCO}
- * - {@link #TTY_MODE_VCO}
- *
+ * <ul>
+ * <li>{@link #TTY_MODE_OFF}</li>
+ * <li>{@link #TTY_MODE_FULL}</li>
+ * <li>{@link #TTY_MODE_HCO}</li>
+ * <li>{@link #TTY_MODE_VCO}</li>
+ * </ul>
* @hide
*/
+ @SystemApi
public static final String EXTRA_CURRENT_TTY_MODE =
- "android.telecom.intent.extra.CURRENT_TTY_MODE";
+ "android.telecom.extra.CURRENT_TTY_MODE";
/**
* Broadcast intent action indicating that the TTY preferred operating mode has changed. An
@@ -735,6 +749,7 @@ public class TelecomManager {
* @see #EXTRA_TTY_PREFERRED_MODE
* @hide
*/
+ @SystemApi
public static final String ACTION_TTY_PREFERRED_MODE_CHANGED =
"android.telecom.action.TTY_PREFERRED_MODE_CHANGED";
@@ -745,8 +760,9 @@ public class TelecomManager {
*
* @hide
*/
+ @SystemApi
public static final String EXTRA_TTY_PREFERRED_MODE =
- "android.telecom.intent.extra.TTY_PREFERRED";
+ "android.telecom.extra.TTY_PREFERRED_MODE";
/**
* Broadcast intent action for letting custom component know to show the missed call
@@ -815,16 +831,41 @@ public class TelecomManager {
/**
* Optional extra for {@link #placeCall(Uri, Bundle)} containing an integer that specifies
* the source where user initiated this call. This data is used in metrics.
- * Valid source are:
- * {@link ParcelableCallAnalytics#CALL_SOURCE_UNSPECIFIED},
- * {@link ParcelableCallAnalytics#CALL_SOURCE_EMERGENCY_DIALPAD},
- * {@link ParcelableCallAnalytics#CALL_SOURCE_EMERGENCY_SHORTCUT}.
+ * Valid sources are:
+ * {@link TelecomManager#CALL_SOURCE_UNSPECIFIED},
+ * {@link TelecomManager#CALL_SOURCE_EMERGENCY_DIALPAD},
+ * {@link TelecomManager#CALL_SOURCE_EMERGENCY_SHORTCUT}.
*
* @hide
*/
+ @SystemApi
public static final String EXTRA_CALL_SOURCE = "android.telecom.extra.CALL_SOURCE";
/**
+ * Indicating the call is initiated via emergency dialer's shortcut button.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int CALL_SOURCE_EMERGENCY_SHORTCUT = 2;
+
+ /**
+ * Indicating the call is initiated via emergency dialer's dialpad.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int CALL_SOURCE_EMERGENCY_DIALPAD = 1;
+
+ /**
+ * Indicating the call source is not specified.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int CALL_SOURCE_UNSPECIFIED = 0;
+
+ /**
* The following 4 constants define how properties such as phone numbers and names are
* displayed to the user.
*/
diff --git a/telephony/OWNERS b/telephony/OWNERS
index 58a7ea08da3f..628c48070314 100644
--- a/telephony/OWNERS
+++ b/telephony/OWNERS
@@ -1,18 +1,16 @@
set noparent
-tgunn@google.com
-breadley@google.com
-hallliu@google.com
-rgreenwalt@google.com
-mpq@google.com
amitmahajan@google.com
+breadley@google.com
fionaxu@google.com
jackyu@google.com
+hallliu@google.com
+rgreenwalt@google.com
+tgunn@google.com
jminjie@google.com
-satk@google.com
shuoq@google.com
refuhoo@google.com
-paulye@google.com
nazaninb@google.com
sarahchin@google.com
dbright@google.com
+xiaotonj@google.com
diff --git a/telephony/common/com/android/internal/telephony/CarrierAppUtils.java b/telephony/common/com/android/internal/telephony/CarrierAppUtils.java
index 9bc534c2877a..553bcff931d2 100644
--- a/telephony/common/com/android/internal/telephony/CarrierAppUtils.java
+++ b/telephony/common/com/android/internal/telephony/CarrierAppUtils.java
@@ -20,19 +20,16 @@ import android.annotation.Nullable;
import android.content.ContentResolver;
import android.content.Context;
import android.content.pm.ApplicationInfo;
-import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.content.res.Resources;
-import android.os.RemoteException;
import android.os.UserHandle;
-import android.permission.IPermissionManager;
+import android.permission.PermissionManager;
import android.provider.Settings;
import android.telephony.TelephonyManager;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
-import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.util.ArrayUtils;
import com.android.server.SystemConfig;
@@ -77,7 +74,6 @@ public final class CarrierAppUtils {
* privileged apps may have changed.
*/
public static synchronized void disableCarrierAppsUntilPrivileged(String callingPackage,
- IPackageManager packageManager, IPermissionManager permissionManager,
TelephonyManager telephonyManager, int userId, Context context) {
if (DEBUG) {
Log.d(TAG, "disableCarrierAppsUntilPrivileged");
@@ -88,14 +84,14 @@ public final class CarrierAppUtils {
ArrayMap<String, List<String>> systemCarrierAssociatedAppsDisabledUntilUsed =
config.getDisabledUntilUsedPreinstalledCarrierAssociatedApps();
ContentResolver contentResolver = getContentResolverForUser(context, userId);
- disableCarrierAppsUntilPrivileged(callingPackage, packageManager, permissionManager,
- telephonyManager, contentResolver, userId, systemCarrierAppsDisabledUntilUsed,
- systemCarrierAssociatedAppsDisabledUntilUsed);
+ disableCarrierAppsUntilPrivileged(callingPackage, telephonyManager, contentResolver,
+ userId, systemCarrierAppsDisabledUntilUsed,
+ systemCarrierAssociatedAppsDisabledUntilUsed, context);
}
/**
- * Like {@link #disableCarrierAppsUntilPrivileged(String, IPackageManager, TelephonyManager,
- * ContentResolver, int)}, but assumes that no carrier apps have carrier privileges.
+ * Like {@link #disableCarrierAppsUntilPrivileged(String, TelephonyManager, int, Context)},
+ * but assumes that no carrier apps have carrier privileges.
*
* This prevents a potential race condition on first boot - since the app's default state is
* enabled, we will initially disable it when the telephony stack is first initialized as it has
@@ -105,8 +101,7 @@ public final class CarrierAppUtils {
* Manager can kill it, and this can lead to crashes as the app is in an unexpected state.
*/
public static synchronized void disableCarrierAppsUntilPrivileged(String callingPackage,
- IPackageManager packageManager, IPermissionManager permissionManager, int userId,
- Context context) {
+ int userId, Context context) {
if (DEBUG) {
Log.d(TAG, "disableCarrierAppsUntilPrivileged");
}
@@ -114,13 +109,12 @@ public final class CarrierAppUtils {
ArraySet<String> systemCarrierAppsDisabledUntilUsed =
config.getDisabledUntilUsedPreinstalledCarrierApps();
-
ArrayMap<String, List<String>> systemCarrierAssociatedAppsDisabledUntilUsed =
config.getDisabledUntilUsedPreinstalledCarrierAssociatedApps();
ContentResolver contentResolver = getContentResolverForUser(context, userId);
- disableCarrierAppsUntilPrivileged(callingPackage, packageManager, permissionManager,
- null /* telephonyManager */, contentResolver, userId,
- systemCarrierAppsDisabledUntilUsed, systemCarrierAssociatedAppsDisabledUntilUsed);
+ disableCarrierAppsUntilPrivileged(callingPackage, null /* telephonyManager */,
+ contentResolver, userId, systemCarrierAppsDisabledUntilUsed,
+ systemCarrierAssociatedAppsDisabledUntilUsed, context);
}
private static ContentResolver getContentResolverForUser(Context context, int userId) {
@@ -136,21 +130,21 @@ public final class CarrierAppUtils {
// Must be public b/c framework unit tests can't access package-private methods.
@VisibleForTesting
public static void disableCarrierAppsUntilPrivileged(String callingPackage,
- IPackageManager packageManager, IPermissionManager permissionManager,
- @Nullable TelephonyManager telephonyManager,
- ContentResolver contentResolver, int userId,
- ArraySet<String> systemCarrierAppsDisabledUntilUsed,
- ArrayMap<String, List<String>> systemCarrierAssociatedAppsDisabledUntilUsed) {
+ @Nullable TelephonyManager telephonyManager, ContentResolver contentResolver,
+ int userId, ArraySet<String> systemCarrierAppsDisabledUntilUsed,
+ ArrayMap<String, List<String>> systemCarrierAssociatedAppsDisabledUntilUsed,
+ Context context) {
+ PackageManager packageManager = context.getPackageManager();
+ PermissionManager permissionManager =
+ (PermissionManager) context.getSystemService(Context.PERMISSION_SERVICE);
List<ApplicationInfo> candidates = getDefaultNotUpdatedCarrierAppCandidatesHelper(
- packageManager, userId, systemCarrierAppsDisabledUntilUsed);
+ userId, systemCarrierAppsDisabledUntilUsed, context);
if (candidates == null || candidates.isEmpty()) {
return;
}
Map<String, List<ApplicationInfo>> associatedApps = getDefaultCarrierAssociatedAppsHelper(
- packageManager,
- userId,
- systemCarrierAssociatedAppsDisabledUntilUsed);
+ userId, systemCarrierAssociatedAppsDisabledUntilUsed, context);
List<String> enabledCarrierPackages = new ArrayList<>();
boolean hasRunOnce = Settings.Secure.getInt(contentResolver,
@@ -160,26 +154,25 @@ public final class CarrierAppUtils {
for (ApplicationInfo ai : candidates) {
String packageName = ai.packageName;
String[] restrictedCarrierApps = Resources.getSystem().getStringArray(
- R.array.config_restrictedPreinstalledCarrierApps);
+ android.R.array.config_restrictedPreinstalledCarrierApps);
boolean hasPrivileges = telephonyManager != null
&& telephonyManager.checkCarrierPrivilegesForPackageAnyPhone(packageName)
== TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS
&& !ArrayUtils.contains(restrictedCarrierApps, packageName);
// add hiddenUntilInstalled flag for carrier apps and associated apps
- packageManager.setSystemAppHiddenUntilInstalled(packageName, true);
+ packageManager.setSystemAppState(
+ packageName, PackageManager.SYSTEM_APP_STATE_HIDDEN_UNTIL_INSTALLED_HIDDEN);
List<ApplicationInfo> associatedAppList = associatedApps.get(packageName);
if (associatedAppList != null) {
for (ApplicationInfo associatedApp : associatedAppList) {
- packageManager.setSystemAppHiddenUntilInstalled(
- associatedApp.packageName,
- true
- );
+ packageManager.setSystemAppState(associatedApp.packageName,
+ PackageManager.SYSTEM_APP_STATE_HIDDEN_UNTIL_INSTALLED_HIDDEN);
}
}
- int enabledSetting = packageManager.getApplicationEnabledSetting(packageName,
- userId);
+ int enabledSetting = context.createContextAsUser(UserHandle.of(userId), 0)
+ .getPackageManager().getApplicationEnabledSetting(packageName);
if (hasPrivileges) {
// Only update enabled state for the app on /system. Once it has been
// updated we shouldn't touch it.
@@ -190,24 +183,25 @@ public final class CarrierAppUtils {
|| (ai.flags & ApplicationInfo.FLAG_INSTALLED) == 0) {
Log.i(TAG, "Update state(" + packageName + "): ENABLED for user "
+ userId);
- packageManager.setSystemAppInstallState(
- packageName,
- true /*installed*/,
- userId);
- packageManager.setApplicationEnabledSetting(
- packageName,
- PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
- PackageManager.DONT_KILL_APP,
- userId,
- callingPackage);
+ context.createContextAsUser(UserHandle.of(userId), 0)
+ .getPackageManager()
+ .setSystemAppState(
+ packageName, PackageManager.SYSTEM_APP_STATE_INSTALLED);
+ context.createPackageContextAsUser(callingPackage, 0, UserHandle.of(userId))
+ .getPackageManager()
+ .setApplicationEnabledSetting(
+ packageName,
+ PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
+ PackageManager.DONT_KILL_APP);
}
// Also enable any associated apps for this carrier app.
if (associatedAppList != null) {
for (ApplicationInfo associatedApp : associatedAppList) {
- int associatedAppEnabledSetting =
- packageManager.getApplicationEnabledSetting(
- associatedApp.packageName, userId);
+ int associatedAppEnabledSetting = context
+ .createContextAsUser(UserHandle.of(userId), 0)
+ .getPackageManager()
+ .getApplicationEnabledSetting(associatedApp.packageName);
if (associatedAppEnabledSetting
== PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
|| associatedAppEnabledSetting
@@ -216,16 +210,17 @@ public final class CarrierAppUtils {
& ApplicationInfo.FLAG_INSTALLED) == 0) {
Log.i(TAG, "Update associated state(" + associatedApp.packageName
+ "): ENABLED for user " + userId);
- packageManager.setSystemAppInstallState(
- associatedApp.packageName,
- true /*installed*/,
- userId);
- packageManager.setApplicationEnabledSetting(
- associatedApp.packageName,
- PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
- PackageManager.DONT_KILL_APP,
- userId,
- callingPackage);
+ context.createContextAsUser(UserHandle.of(userId), 0)
+ .getPackageManager()
+ .setSystemAppState(associatedApp.packageName,
+ PackageManager.SYSTEM_APP_STATE_INSTALLED);
+ context.createPackageContextAsUser(
+ callingPackage, 0, UserHandle.of(userId))
+ .getPackageManager()
+ .setApplicationEnabledSetting(
+ associatedApp.packageName,
+ PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
+ PackageManager.DONT_KILL_APP);
}
}
}
@@ -240,10 +235,10 @@ public final class CarrierAppUtils {
&& (ai.flags & ApplicationInfo.FLAG_INSTALLED) != 0) {
Log.i(TAG, "Update state(" + packageName
+ "): DISABLED_UNTIL_USED for user " + userId);
- packageManager.setSystemAppInstallState(
- packageName,
- false /*installed*/,
- userId);
+ context.createContextAsUser(UserHandle.of(userId), 0)
+ .getPackageManager()
+ .setSystemAppState(
+ packageName, PackageManager.SYSTEM_APP_STATE_UNINSTALLED);
}
// Also disable any associated apps for this carrier app if this is the first
@@ -252,9 +247,10 @@ public final class CarrierAppUtils {
if (!hasRunOnce) {
if (associatedAppList != null) {
for (ApplicationInfo associatedApp : associatedAppList) {
- int associatedAppEnabledSetting =
- packageManager.getApplicationEnabledSetting(
- associatedApp.packageName, userId);
+ int associatedAppEnabledSetting = context
+ .createContextAsUser(UserHandle.of(userId), 0)
+ .getPackageManager()
+ .getApplicationEnabledSetting(associatedApp.packageName);
if (associatedAppEnabledSetting
== PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
&& (associatedApp.flags
@@ -262,10 +258,10 @@ public final class CarrierAppUtils {
Log.i(TAG,
"Update associated state(" + associatedApp.packageName
+ "): DISABLED_UNTIL_USED for user " + userId);
- packageManager.setSystemAppInstallState(
- associatedApp.packageName,
- false /*installed*/,
- userId);
+ context.createContextAsUser(UserHandle.of(userId), 0)
+ .getPackageManager()
+ .setSystemAppState(associatedApp.packageName,
+ PackageManager.SYSTEM_APP_STATE_UNINSTALLED);
}
}
}
@@ -283,9 +279,10 @@ public final class CarrierAppUtils {
// apps.
String[] packageNames = new String[enabledCarrierPackages.size()];
enabledCarrierPackages.toArray(packageNames);
- permissionManager.grantDefaultPermissionsToEnabledCarrierApps(packageNames, userId);
+ permissionManager.grantDefaultPermissionsToEnabledCarrierApps(packageNames,
+ UserHandle.of(userId), Runnable::run, isSuccess -> { });
}
- } catch (RemoteException e) {
+ } catch (PackageManager.NameNotFoundException e) {
Log.w(TAG, "Could not reach PackageManager", e);
}
}
@@ -294,13 +291,13 @@ public final class CarrierAppUtils {
* Returns the list of "default" carrier apps.
*
* This is the subset of apps returned by
- * {@link #getDefaultCarrierAppCandidates(IPackageManager, int)} which currently have carrier
+ * {@link #getDefaultCarrierAppCandidates(int, Context)} which currently have carrier
* privileges per the SIM(s) inserted in the device.
*/
- public static List<ApplicationInfo> getDefaultCarrierApps(IPackageManager packageManager,
- TelephonyManager telephonyManager, int userId) {
+ public static List<ApplicationInfo> getDefaultCarrierApps(
+ TelephonyManager telephonyManager, int userId, Context context) {
// Get all system apps from the default list.
- List<ApplicationInfo> candidates = getDefaultCarrierAppCandidates(packageManager, userId);
+ List<ApplicationInfo> candidates = getDefaultCarrierAppCandidates(userId, context);
if (candidates == null || candidates.isEmpty()) {
return null;
}
@@ -326,25 +323,23 @@ public final class CarrierAppUtils {
* Returns the list of "default" carrier app candidates.
*
* These are the apps subject to the hiding/showing logic in
- * {@link CarrierAppUtils#disableCarrierAppsUntilPrivileged(String, IPackageManager,
- * TelephonyManager, ContentResolver, int)}, as well as the apps which should have default
+ * {@link CarrierAppUtils#disableCarrierAppsUntilPrivileged(String, TelephonyManager, int,
+ * Context)}, as well as the apps which should have default
* permissions granted, when a matching SIM is inserted.
*
* Whether or not the app is actually considered a default app depends on whether the app has
* carrier privileges as determined by the SIMs in the device.
*/
public static List<ApplicationInfo> getDefaultCarrierAppCandidates(
- IPackageManager packageManager, int userId) {
+ int userId, Context context) {
ArraySet<String> systemCarrierAppsDisabledUntilUsed =
SystemConfig.getInstance().getDisabledUntilUsedPreinstalledCarrierApps();
- return getDefaultCarrierAppCandidatesHelper(packageManager, userId,
- systemCarrierAppsDisabledUntilUsed);
+ return getDefaultCarrierAppCandidatesHelper(userId, systemCarrierAppsDisabledUntilUsed,
+ context);
}
private static List<ApplicationInfo> getDefaultCarrierAppCandidatesHelper(
- IPackageManager packageManager,
- int userId,
- ArraySet<String> systemCarrierAppsDisabledUntilUsed) {
+ int userId, ArraySet<String> systemCarrierAppsDisabledUntilUsed, Context context) {
if (systemCarrierAppsDisabledUntilUsed == null) {
return null;
}
@@ -358,7 +353,7 @@ public final class CarrierAppUtils {
for (int i = 0; i < size; i++) {
String packageName = systemCarrierAppsDisabledUntilUsed.valueAt(i);
ApplicationInfo ai =
- getApplicationInfoIfSystemApp(packageManager, userId, packageName);
+ getApplicationInfoIfSystemApp(userId, packageName, context);
if (ai != null) {
apps.add(ai);
}
@@ -367,9 +362,7 @@ public final class CarrierAppUtils {
}
private static List<ApplicationInfo> getDefaultNotUpdatedCarrierAppCandidatesHelper(
- IPackageManager packageManager,
- int userId,
- ArraySet<String> systemCarrierAppsDisabledUntilUsed) {
+ int userId, ArraySet<String> systemCarrierAppsDisabledUntilUsed, Context context) {
if (systemCarrierAppsDisabledUntilUsed == null) {
return null;
}
@@ -383,7 +376,7 @@ public final class CarrierAppUtils {
for (int i = 0; i < size; i++) {
String packageName = systemCarrierAppsDisabledUntilUsed.valueAt(i);
ApplicationInfo ai =
- getApplicationInfoIfNotUpdatedSystemApp(packageManager, userId, packageName);
+ getApplicationInfoIfNotUpdatedSystemApp(userId, packageName, context);
if (ai != null) {
apps.add(ai);
}
@@ -392,9 +385,8 @@ public final class CarrierAppUtils {
}
private static Map<String, List<ApplicationInfo>> getDefaultCarrierAssociatedAppsHelper(
- IPackageManager packageManager,
- int userId,
- ArrayMap<String, List<String>> systemCarrierAssociatedAppsDisabledUntilUsed) {
+ int userId, ArrayMap<String, List<String>> systemCarrierAssociatedAppsDisabledUntilUsed,
+ Context context) {
int size = systemCarrierAssociatedAppsDisabledUntilUsed.size();
Map<String, List<ApplicationInfo>> associatedApps = new ArrayMap<>(size);
for (int i = 0; i < size; i++) {
@@ -404,7 +396,7 @@ public final class CarrierAppUtils {
for (int j = 0; j < associatedAppPackages.size(); j++) {
ApplicationInfo ai =
getApplicationInfoIfNotUpdatedSystemApp(
- packageManager, userId, associatedAppPackages.get(j));
+ userId, associatedAppPackages.get(j), context);
// Only update enabled state for the app on /system. Once it has been updated we
// shouldn't touch it.
if (ai != null) {
@@ -422,19 +414,19 @@ public final class CarrierAppUtils {
@Nullable
private static ApplicationInfo getApplicationInfoIfNotUpdatedSystemApp(
- IPackageManager packageManager,
- int userId,
- String packageName) {
+ int userId, String packageName, Context context) {
try {
- ApplicationInfo ai = packageManager.getApplicationInfo(packageName,
- PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS
- | PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS
- | PackageManager.MATCH_SYSTEM_ONLY
- | PackageManager.MATCH_FACTORY_ONLY, userId);
+ ApplicationInfo ai = context.createContextAsUser(UserHandle.of(userId), 0)
+ .getPackageManager()
+ .getApplicationInfo(packageName,
+ PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS
+ | PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS
+ | PackageManager.MATCH_SYSTEM_ONLY
+ | PackageManager.MATCH_FACTORY_ONLY);
if (ai != null) {
return ai;
}
- } catch (RemoteException e) {
+ } catch (PackageManager.NameNotFoundException e) {
Log.w(TAG, "Could not reach PackageManager", e);
}
return null;
@@ -442,18 +434,18 @@ public final class CarrierAppUtils {
@Nullable
private static ApplicationInfo getApplicationInfoIfSystemApp(
- IPackageManager packageManager,
- int userId,
- String packageName) {
+ int userId, String packageName, Context context) {
try {
- ApplicationInfo ai = packageManager.getApplicationInfo(packageName,
- PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS
- | PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS
- | PackageManager.MATCH_SYSTEM_ONLY, userId);
+ ApplicationInfo ai = context.createContextAsUser(UserHandle.of(userId), 0)
+ .getPackageManager()
+ .getApplicationInfo(packageName,
+ PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS
+ | PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS
+ | PackageManager.MATCH_SYSTEM_ONLY);
if (ai != null) {
return ai;
}
- } catch (RemoteException e) {
+ } catch (PackageManager.NameNotFoundException e) {
Log.w(TAG, "Could not reach PackageManager", e);
}
return null;
diff --git a/telephony/common/com/android/internal/telephony/GsmAlphabet.java b/telephony/common/com/android/internal/telephony/GsmAlphabet.java
index 5c53f7e5a4d0..c62cec270440 100644
--- a/telephony/common/com/android/internal/telephony/GsmAlphabet.java
+++ b/telephony/common/com/android/internal/telephony/GsmAlphabet.java
@@ -19,12 +19,10 @@ package com.android.internal.telephony;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.res.Resources;
import android.os.Build;
-import android.util.Log;
import android.text.TextUtils;
+import android.util.Log;
import android.util.SparseIntArray;
-import com.android.internal.R;
-
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.util.ArrayList;
@@ -1089,8 +1087,10 @@ public class GsmAlphabet {
private static void enableCountrySpecificEncodings() {
Resources r = Resources.getSystem();
// See comments in frameworks/base/core/res/res/values/config.xml for allowed values
- sEnabledSingleShiftTables = r.getIntArray(R.array.config_sms_enabled_single_shift_tables);
- sEnabledLockingShiftTables = r.getIntArray(R.array.config_sms_enabled_locking_shift_tables);
+ sEnabledSingleShiftTables = r.getIntArray(
+ android.R.array.config_sms_enabled_single_shift_tables);
+ sEnabledLockingShiftTables = r.getIntArray(
+ android.R.array.config_sms_enabled_locking_shift_tables);
if (sEnabledSingleShiftTables.length > 0) {
sHighestEnabledSingleShiftCode =
diff --git a/telephony/common/com/google/android/mms/util/SqliteWrapper.java b/telephony/common/com/google/android/mms/util/SqliteWrapper.java
index 31fe4d7683d6..4871434ebc31 100644
--- a/telephony/common/com/google/android/mms/util/SqliteWrapper.java
+++ b/telephony/common/com/google/android/mms/util/SqliteWrapper.java
@@ -60,8 +60,7 @@ public final class SqliteWrapper {
@UnsupportedAppUsage
public static void checkSQLiteException(Context context, SQLiteException e) {
if (isLowMemory(e)) {
- Toast.makeText(context, com.android.internal.R.string.low_memory,
- Toast.LENGTH_SHORT).show();
+ Toast.makeText(context, android.R.string.low_memory, Toast.LENGTH_SHORT).show();
} else {
throw e;
}
diff --git a/core/java/android/service/euicc/IEuiccServiceDumpResultCallback.aidl b/telephony/java/android/service/euicc/IEuiccServiceDumpResultCallback.aidl
index ea55ebbadd88..ea55ebbadd88 100644
--- a/core/java/android/service/euicc/IEuiccServiceDumpResultCallback.aidl
+++ b/telephony/java/android/service/euicc/IEuiccServiceDumpResultCallback.aidl
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index b30f5868cc63..945c8888f402 100755
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -4230,26 +4230,26 @@ public class CarrierConfigManager {
sDefaults.putIntArray(KEY_5G_NR_SSRSRP_THRESHOLDS_INT_ARRAY,
// Boundaries: [-140 dB, -44 dB]
new int[] {
- -125, /* SIGNAL_STRENGTH_POOR */
- -115, /* SIGNAL_STRENGTH_MODERATE */
- -105, /* SIGNAL_STRENGTH_GOOD */
- -95, /* SIGNAL_STRENGTH_GREAT */
+ -110, /* SIGNAL_STRENGTH_POOR */
+ -90, /* SIGNAL_STRENGTH_MODERATE */
+ -80, /* SIGNAL_STRENGTH_GOOD */
+ -65, /* SIGNAL_STRENGTH_GREAT */
});
sDefaults.putIntArray(KEY_5G_NR_SSRSRQ_THRESHOLDS_INT_ARRAY,
// Boundaries: [-20 dB, -3 dB]
new int[] {
- -14, /* SIGNAL_STRENGTH_POOR */
- -12, /* SIGNAL_STRENGTH_MODERATE */
- -10, /* SIGNAL_STRENGTH_GOOD */
- -8 /* SIGNAL_STRENGTH_GREAT */
+ -16, /* SIGNAL_STRENGTH_POOR */
+ -11, /* SIGNAL_STRENGTH_MODERATE */
+ -9, /* SIGNAL_STRENGTH_GOOD */
+ -7 /* SIGNAL_STRENGTH_GREAT */
});
sDefaults.putIntArray(KEY_5G_NR_SSSINR_THRESHOLDS_INT_ARRAY,
// Boundaries: [-23 dB, 40 dB]
new int[] {
- -8, /* SIGNAL_STRENGTH_POOR */
- 0, /* SIGNAL_STRENGTH_MODERATE */
- 8, /* SIGNAL_STRENGTH_GOOD */
- 16 /* SIGNAL_STRENGTH_GREAT */
+ -5, /* SIGNAL_STRENGTH_POOR */
+ 5, /* SIGNAL_STRENGTH_MODERATE */
+ 15, /* SIGNAL_STRENGTH_GOOD */
+ 30 /* SIGNAL_STRENGTH_GREAT */
});
sDefaults.putInt(KEY_PARAMETERS_USE_FOR_5G_NR_SIGNAL_BAR_INT,
CellSignalStrengthNr.USE_SSRSRP);
diff --git a/telephony/java/android/telephony/CellSignalStrengthNr.java b/telephony/java/android/telephony/CellSignalStrengthNr.java
index 4d67bcf536cf..f4c13fff9943 100644
--- a/telephony/java/android/telephony/CellSignalStrengthNr.java
+++ b/telephony/java/android/telephony/CellSignalStrengthNr.java
@@ -45,28 +45,28 @@ public final class CellSignalStrengthNr extends CellSignalStrength implements Pa
// Lifted from Default carrier configs and max range of SSRSRP
// Boundaries: [-140 dB, -44 dB]
private int[] mSsRsrpThresholds = new int[] {
- -125, /* SIGNAL_STRENGTH_POOR */
- -115, /* SIGNAL_STRENGTH_MODERATE */
- -105, /* SIGNAL_STRENGTH_GOOD */
- -95, /* SIGNAL_STRENGTH_GREAT */
+ -110, /* SIGNAL_STRENGTH_POOR */
+ -90, /* SIGNAL_STRENGTH_MODERATE */
+ -80, /* SIGNAL_STRENGTH_GOOD */
+ -65, /* SIGNAL_STRENGTH_GREAT */
};
// Lifted from Default carrier configs and max range of SSRSRQ
// Boundaries: [-20 dB, -3 dB]
private int[] mSsRsrqThresholds = new int[] {
- -14, /* SIGNAL_STRENGTH_POOR */
- -12, /* SIGNAL_STRENGTH_MODERATE */
- -10, /* SIGNAL_STRENGTH_GOOD */
- -8 /* SIGNAL_STRENGTH_GREAT */
+ -16, /* SIGNAL_STRENGTH_POOR */
+ -11, /* SIGNAL_STRENGTH_MODERATE */
+ -9, /* SIGNAL_STRENGTH_GOOD */
+ -7 /* SIGNAL_STRENGTH_GREAT */
};
// Lifted from Default carrier configs and max range of SSSINR
// Boundaries: [-23 dB, 40 dB]
private int[] mSsSinrThresholds = new int[] {
- -8, /* SIGNAL_STRENGTH_POOR */
- 0, /* SIGNAL_STRENGTH_MODERATE */
- 8, /* SIGNAL_STRENGTH_GOOD */
- 16 /* SIGNAL_STRENGTH_GREAT */
+ -5, /* SIGNAL_STRENGTH_POOR */
+ 5, /* SIGNAL_STRENGTH_MODERATE */
+ 15, /* SIGNAL_STRENGTH_GOOD */
+ 30 /* SIGNAL_STRENGTH_GREAT */
};
/**
diff --git a/telephony/java/android/telephony/ModemActivityInfo.java b/telephony/java/android/telephony/ModemActivityInfo.java
index aebe78031ba2..1ba21f29df11 100644
--- a/telephony/java/android/telephony/ModemActivityInfo.java
+++ b/telephony/java/android/telephony/ModemActivityInfo.java
@@ -17,7 +17,6 @@
package android.telephony;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
@@ -60,7 +59,7 @@ public final class ModemActivityInfo implements Parcelable {
private int mRxTimeMs;
public ModemActivityInfo(long timestamp, int sleepTimeMs, int idleTimeMs,
- @Nullable int[] txTimeMs, int rxTimeMs) {
+ @NonNull int[] txTimeMs, int rxTimeMs) {
mTimestamp = timestamp;
mSleepTimeMs = sleepTimeMs;
mIdleTimeMs = idleTimeMs;
@@ -69,13 +68,10 @@ public final class ModemActivityInfo implements Parcelable {
}
/** helper API to populate tx power range for each bucket **/
- private void populateTransmitPowerRange(@Nullable int[] transmitPowerMs) {
+ private void populateTransmitPowerRange(@NonNull int[] transmitPowerMs) {
int i = 0;
- if (transmitPowerMs != null) {
- for ( ; i < Math.min(transmitPowerMs.length, TX_POWER_LEVELS); i++) {
- mTransmitPowerInfo.add(i,
- new TransmitPower(TX_POWER_RANGES[i], transmitPowerMs[i]));
- }
+ for ( ; i < Math.min(transmitPowerMs.length, TX_POWER_LEVELS); i++) {
+ mTransmitPowerInfo.add(i, new TransmitPower(TX_POWER_RANGES[i], transmitPowerMs[i]));
}
// Make sure that mTransmitPowerInfo is fully initialized.
for ( ; i < TX_POWER_LEVELS; i++) {
@@ -98,7 +94,7 @@ public final class ModemActivityInfo implements Parcelable {
return 0;
}
- public static final @NonNull Parcelable.Creator<ModemActivityInfo> CREATOR =
+ public static final @android.annotation.NonNull Parcelable.Creator<ModemActivityInfo> CREATOR =
new Parcelable.Creator<ModemActivityInfo>() {
public ModemActivityInfo createFromParcel(Parcel in) {
long timestamp = in.readLong();
@@ -153,7 +149,7 @@ public final class ModemActivityInfo implements Parcelable {
}
/** @hide */
- public void setTransmitTimeMillis(@Nullable int[] txTimeMs) {
+ public void setTransmitTimeMillis(int[] txTimeMs) {
populateTransmitPowerRange(txTimeMs);
}
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index 1e64a8182e3b..5b09cd9e6caa 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -317,6 +317,14 @@ public class ServiceState implements Parcelable {
*/
public static final int UNKNOWN_ID = -1;
+ /**
+ * A parcelable extra used with {@link Intent#ACTION_SERVICE_STATE} representing the service
+ * state.
+ * @hide
+ */
+ private static final String EXTRA_SERVICE_STATE = "android.intent.extra.SERVICE_STATE";
+
+
private String mOperatorAlphaLong;
private String mOperatorAlphaShort;
private String mOperatorNumeric;
@@ -1292,7 +1300,7 @@ public class ServiceState implements Parcelable {
*/
@UnsupportedAppUsage
private void setFromNotifierBundle(Bundle m) {
- ServiceState ssFromBundle = m.getParcelable(Intent.EXTRA_SERVICE_STATE);
+ ServiceState ssFromBundle = m.getParcelable(EXTRA_SERVICE_STATE);
if (ssFromBundle != null) {
copyFrom(ssFromBundle);
}
@@ -1310,7 +1318,7 @@ public class ServiceState implements Parcelable {
*/
@SystemApi
public void fillInNotifierBundle(@NonNull Bundle m) {
- m.putParcelable(Intent.EXTRA_SERVICE_STATE, this);
+ m.putParcelable(EXTRA_SERVICE_STATE, this);
// serviceState already consists of below entries.
// for backward compatibility, we continue fill in below entries.
m.putInt("voiceRegState", mVoiceRegState);
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 63a85fa2845c..4da3a54306ee 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -54,6 +54,7 @@ import android.os.Looper;
import android.os.ParcelUuid;
import android.os.Process;
import android.os.RemoteException;
+import android.os.ServiceManager;
import android.provider.Telephony.SimInfo;
import android.telephony.euicc.EuiccManager;
import android.telephony.ims.ImsMmTelManager;
@@ -515,13 +516,6 @@ public class SubscriptionManager {
public static final String ISO_COUNTRY_CODE = SimInfo.ISO_COUNTRY_CODE;
/**
- * TelephonyProvider column name for the sim provisioning status associated with a SIM.
- * <P>Type: INTEGER (int)</P>
- * @hide
- */
- public static final String SIM_PROVISIONING_STATUS = SimInfo.SIM_PROVISIONING_STATUS;
-
- /**
* TelephonyProvider column name for whether a subscription is embedded (that is, present on an
* eSIM).
* <p>Type: INTEGER (int), 1 for embedded or 0 for non-embedded.
@@ -677,6 +671,13 @@ public class SubscriptionManager {
public static final String WFC_IMS_ROAMING_ENABLED = SimInfo.WFC_IMS_ROAMING_ENABLED;
/**
+ * Determines if the user has enabled IMS RCS User Capability Exchange (UCE) for this
+ * subscription.
+ * @hide
+ */
+ public static final String IMS_RCS_UCE_ENABLED = SimInfo.IMS_RCS_UCE_ENABLED;
+
+ /**
* TelephonyProvider column name for whether a subscription is opportunistic, that is,
* whether the network it connects to is limited in functionality or coverage.
* For example, CBRS.
@@ -976,10 +977,7 @@ public class SubscriptionManager {
private INetworkPolicyManager getINetworkPolicyManager() {
if (mNetworkPolicy == null) {
mNetworkPolicy = INetworkPolicyManager.Stub.asInterface(
- TelephonyFrameworkInitializer
- .getTelephonyServiceManager()
- .getNetworkPolicyServiceRegisterer()
- .get());
+ ServiceManager.getService(Context.NETWORK_POLICY_SERVICE));
}
return mNetworkPolicy;
}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 903be6f68282..86913a636913 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -99,7 +99,6 @@ import com.android.internal.telephony.IOns;
import com.android.internal.telephony.IPhoneSubInfo;
import com.android.internal.telephony.ISetOpportunisticDataCallback;
import com.android.internal.telephony.ITelephony;
-import com.android.internal.telephony.ITelephonyRegistry;
import com.android.internal.telephony.IUpdateAvailableNetworksCallback;
import com.android.internal.telephony.OperatorInfo;
import com.android.internal.telephony.PhoneConstants;
@@ -5624,14 +5623,6 @@ public class TelephonyManager {
.getTelephonyServiceManager().getTelephonyServiceRegisterer().get());
}
- private ITelephonyRegistry getTelephonyRegistry() {
- return ITelephonyRegistry.Stub.asInterface(
- TelephonyFrameworkInitializer
- .getTelephonyServiceManager()
- .getTelephonyRegistryServiceRegisterer()
- .get());
- }
-
private IOns getIOns() {
return IOns.Stub.asInterface(
TelephonyFrameworkInitializer
@@ -5685,29 +5676,27 @@ public class TelephonyManager {
*/
public void listen(PhoneStateListener listener, int events) {
if (mContext == null) return;
- try {
- boolean notifyNow = (getITelephony() != null);
- ITelephonyRegistry registry = getTelephonyRegistry();
- if (registry != null) {
- // subId from PhoneStateListener is deprecated Q on forward, use the subId from
- // TelephonyManager instance. keep using subId from PhoneStateListener for pre-Q.
- int subId = mSubId;
- if (Compatibility.isChangeEnabled(LISTEN_CODE_CHANGE)) {
- // since mSubId in PhoneStateListener is deprecated from Q on forward, this is
- // the only place to set mSubId and its for "informational" only.
- // TODO: remove this once we completely get rid of mSubId in PhoneStateListener
- listener.mSubId = (events == PhoneStateListener.LISTEN_NONE)
- ? SubscriptionManager.INVALID_SUBSCRIPTION_ID : subId;
- } else if (listener.mSubId != null) {
- subId = listener.mSubId;
- }
- registry.listenForSubscriber(subId, getOpPackageName(), getFeatureId(),
- listener.callback, events, notifyNow);
- } else {
- Rlog.w(TAG, "telephony registry not ready.");
- }
- } catch (RemoteException ex) {
- // system process dead
+ boolean notifyNow = (getITelephony() != null);
+ TelephonyRegistryManager telephonyRegistry =
+ (TelephonyRegistryManager)
+ mContext.getSystemService(Context.TELEPHONY_REGISTRY_SERVICE);
+ if (telephonyRegistry != null) {
+ // subId from PhoneStateListener is deprecated Q on forward, use the subId from
+ // TelephonyManager instance. keep using subId from PhoneStateListener for pre-Q.
+ int subId = mSubId;
+ if (Compatibility.isChangeEnabled(LISTEN_CODE_CHANGE)) {
+ // since mSubId in PhoneStateListener is deprecated from Q on forward, this is
+ // the only place to set mSubId and its for "informational" only.
+ // TODO: remove this once we completely get rid of mSubId in PhoneStateListener
+ listener.mSubId = (events == PhoneStateListener.LISTEN_NONE)
+ ? SubscriptionManager.INVALID_SUBSCRIPTION_ID : subId;
+ } else if (listener.mSubId != null) {
+ subId = listener.mSubId;
+ }
+ telephonyRegistry.listenForSubscriber(subId, getOpPackageName(), getFeatureId(),
+ listener, events, notifyNow);
+ } else {
+ Rlog.w(TAG, "telephony registry not ready.");
}
}
@@ -7359,6 +7348,30 @@ public class TelephonyManager {
}
}
+
+ /**
+ * Resets the {@link android.telephony.ims.ImsService} associated with the specified sim slot.
+ * Used by diagnostic apps to force the IMS stack to be disabled and re-enabled in an effort to
+ * recover from scenarios where the {@link android.telephony.ims.ImsService} gets in to a bad
+ * state.
+ *
+ * @param slotIndex the sim slot to reset the IMS stack on.
+ * @hide */
+ @SystemApi
+ @WorkerThread
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ public void resetIms(int slotIndex) {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ telephony.resetIms(slotIndex);
+ }
+ } catch (RemoteException e) {
+ Rlog.e(TAG, "toggleImsOnOff, RemoteException: "
+ + e.getMessage());
+ }
+ }
+
/**
* Enables IMS for the framework. This will trigger IMS registration and ImsFeature capability
* status updates, if not already enabled.
diff --git a/telephony/java/android/telephony/euicc/EuiccManager.java b/telephony/java/android/telephony/euicc/EuiccManager.java
index 27a70228a433..7488a1aec0e5 100644
--- a/telephony/java/android/telephony/euicc/EuiccManager.java
+++ b/telephony/java/android/telephony/euicc/EuiccManager.java
@@ -38,6 +38,9 @@ import com.android.internal.telephony.euicc.IEuiccController;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
/**
* EuiccManager is the application interface to eUICCs, or eSIMs/embedded SIMs.
@@ -254,27 +257,38 @@ public class EuiccManager {
* the error is related to download.Since the OperationCode only uses at most one byte, the
* maximum allowed quantity is 255(0xFF).
*
- * ErrorCode is the remaing three bytes of the result code, and it denotes what happened.
+ * ErrorCode is the remaining three bytes of the result code, and it denotes what happened.
* e.g a combination of {@link #OPERATION_DOWNLOAD} and {@link #ERROR_TIME_OUT} will suggest the
* download operation has timed out. The only exception here is
* {@link #OPERATION_SMDX_SUBJECT_REASON_CODE}, where instead of ErrorCode, SubjectCode[5.2.6.1
* from GSMA (SGP.22 v2.2) and ReasonCode[5.2.6.2] from GSMA (SGP.22 v2.2) are encoded. @see
* {@link #EXTRA_EMBEDDED_SUBSCRIPTION_SMDX_SUBJECT_CODE} and
* {@link #EXTRA_EMBEDDED_SUBSCRIPTION_SMDX_REASON_CODE}
+ *
+ * In the case where ErrorCode contains a value of 0, it means it's an unknown error. E.g Intent
+ * only contains {@link #OPERATION_DOWNLOAD} and ErrorCode is 0 implies this is an unknown
+ * Download error.
+ *
+ * @see {@link #EXTRA_EMBEDDED_SUBSCRIPTION_OPERATION_CODE}
+ * @see {@link #EXTRA_EMBEDDED_SUBSCRIPTION_ERROR_CODE}
+ * @see {@link #EXTRA_EMBEDDED_SUBSCRIPTION_SMDX_SUBJECT_CODE}
+ * @see {@link #EXTRA_EMBEDDED_SUBSCRIPTION_SMDX_REASON_CODE}
*/
public static final String EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE =
"android.telephony.euicc.extra.EMBEDDED_SUBSCRIPTION_DETAILED_CODE";
/**
* Key for an extra set on {@link PendingIntent} result callbacks providing a
- * OperationCode of {@link #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE}.
+ * OperationCode of {@link #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE},
+ * value will be an int.
*/
public static final String EXTRA_EMBEDDED_SUBSCRIPTION_OPERATION_CODE =
"android.telephony.euicc.extra.EMBEDDED_SUBSCRIPTION_OPERATION_CODE";
/**
* Key for an extra set on {@link PendingIntent} result callbacks providing a
- * ErrorCode of {@link #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE}.
+ * ErrorCode of {@link #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE},
+ * value will be an int.
*/
public static final String EXTRA_EMBEDDED_SUBSCRIPTION_ERROR_CODE =
"android.telephony.euicc.extra.EMBEDDED_SUBSCRIPTION_ERROR_CODE";
@@ -283,6 +297,7 @@ public class EuiccManager {
* Key for an extra set on {@link PendingIntent} result callbacks providing a
* SubjectCode[5.2.6.1] from GSMA (SGP.22 v2.2) decoded from
* {@link #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE}.
+ * The value of this extra will be a String.
*/
public static final String EXTRA_EMBEDDED_SUBSCRIPTION_SMDX_SUBJECT_CODE =
"android.telephony.euicc.extra.EMBEDDED_SUBSCRIPTION_SMDX_SUBJECT_CODE";
@@ -291,6 +306,7 @@ public class EuiccManager {
* Key for an extra set on {@link PendingIntent} result callbacks providing a
* ReasonCode[5.2.6.2] from GSMA (SGP.22 v2.2) decoded from
* {@link #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE}.
+ * The value of this extra will be a String.
*/
public static final String EXTRA_EMBEDDED_SUBSCRIPTION_SMDX_REASON_CODE =
"android.telephony.euicc.extra.EMBEDDED_SUBSCRIPTION_SMDX_REASON_CODE";
@@ -665,7 +681,7 @@ public class EuiccManager {
ERROR_EUICC_MISSING,
ERROR_UNSUPPORTED_VERSION,
ERROR_SIM_MISSING,
- ERROR_EUICC_GSMA_INSTALL_ERROR,
+ ERROR_INSTALL_PROFILE,
ERROR_DISALLOWED_BY_PPR,
ERROR_ADDRESS_MISSING,
ERROR_CERTIFICATE_ERROR,
@@ -733,14 +749,14 @@ public class EuiccManager {
public static final int ERROR_SIM_MISSING = 10008;
/**
- * Failure to load the profile onto the eUICC card. i.e
+ * Failure to load the profile onto the eUICC card. e.g
* 1. iccid of the profile already exists on the eUICC.
* 2. GSMA(.22 v2.2) Profile Install Result - installFailedDueToDataMismatch
* 3. operation was interrupted
* 4. SIMalliance error in PEStatus(SGP.22 v2.2 section 2.5.6.1)
* @see {@link #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE} for details
*/
- public static final int ERROR_EUICC_GSMA_INSTALL_ERROR = 10009;
+ public static final int ERROR_INSTALL_PROFILE = 10009;
/**
* Failed to load profile onto eUICC due to Profile Poicly Rules.
@@ -1235,6 +1251,138 @@ public class EuiccManager {
}
/**
+ * Sets the supported countries for eUICC.
+ *
+ * <p>Requires that the calling app has the
+ * {@code android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission.
+ *
+ * <p>The supported country list will be replaced by {@code supportedCountries}. For how we
+ * determine whether a country is supported please check {@link #isSupportedCountry}.
+ *
+ * @param supportedCountries is a list of strings contains country ISO codes in uppercase.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
+ public void setSupportedCountries(@NonNull List<String> supportedCountries) {
+ if (!isEnabled()) {
+ return;
+ }
+ try {
+ getIEuiccController().setSupportedCountries(
+ true /* isSupported */,
+ supportedCountries.stream()
+ .map(String::toUpperCase).collect(Collectors.toList()));
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Sets the unsupported countries for eUICC.
+ *
+ * <p>Requires that the calling app has the
+ * {@code android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission.
+ *
+ * <p>The unsupported country list will be replaced by {@code unsupportedCountries}. For how we
+ * determine whether a country is supported please check {@link #isSupportedCountry}.
+ *
+ * @param unsupportedCountries is a list of strings contains country ISO codes in uppercase.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
+ public void setUnsupportedCountries(@NonNull List<String> unsupportedCountries) {
+ if (!isEnabled()) {
+ return;
+ }
+ try {
+ getIEuiccController().setSupportedCountries(
+ false /* isSupported */,
+ unsupportedCountries.stream()
+ .map(String::toUpperCase).collect(Collectors.toList()));
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Gets the supported countries for eUICC.
+ *
+ * <p>Requires that the calling app has the
+ * {@code android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission.
+ *
+ * @return list of strings contains country ISO codes in uppercase.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
+ @NonNull
+ public List<String> getSupportedCountries() {
+ if (!isEnabled()) {
+ return Collections.emptyList();
+ }
+ try {
+ return getIEuiccController().getSupportedCountries(true /* isSupported */);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Gets the unsupported countries for eUICC.
+ *
+ * <p>Requires that the calling app has the
+ * {@code android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission.
+ *
+ * @return list of strings contains country ISO codes in uppercase.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
+ @NonNull
+ public List<String> getUnsupportedCountries() {
+ if (!isEnabled()) {
+ return Collections.emptyList();
+ }
+ try {
+ return getIEuiccController().getSupportedCountries(false /* isSupported */);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns whether the given country supports eUICC.
+ *
+ * <p>Supported country list has a higher prority than unsupported country list. If the
+ * supported country list is not empty, {@code countryIso} will be considered as supported when
+ * it exists in the supported country list. Otherwise {@code countryIso} is not supported. If
+ * the supported country list is empty, {@code countryIso} will be considered as supported if it
+ * does not exist in the unsupported country list. Otherwise {@code countryIso} is not
+ * supported. If both supported and unsupported country lists are empty, then all countries are
+ * consider be supported. For how to set supported and unsupported country list, please check
+ * {@link #setSupportedCountries} and {@link #setUnsupportedCountries}.
+ *
+ * @param countryIso should be the ISO-3166 country code is provided in uppercase 2 character
+ * format.
+ * @return whether the given country supports eUICC or not.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
+ public boolean isSupportedCountry(@NonNull String countryIso) {
+ if (!isEnabled()) {
+ return false;
+ }
+ try {
+ return getIEuiccController().isSupportedCountry(countryIso.toUpperCase());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Refreshes the cardId if its uninitialized, and returns whether we should continue the
* operation.
* <p>
diff --git a/telephony/java/android/telephony/ims/RcsUceAdapter.java b/telephony/java/android/telephony/ims/RcsUceAdapter.java
index 2e3f59a13670..72167761c88d 100644
--- a/telephony/java/android/telephony/ims/RcsUceAdapter.java
+++ b/telephony/java/android/telephony/ims/RcsUceAdapter.java
@@ -21,6 +21,8 @@ import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.net.Uri;
import android.os.Binder;
import android.os.IBinder;
@@ -28,6 +30,7 @@ import android.os.RemoteException;
import android.telephony.TelephonyFrameworkInitializer;
import android.telephony.ims.aidl.IImsRcsController;
import android.telephony.ims.aidl.IRcsUceControllerCallback;
+import android.telephony.ims.feature.RcsFeature;
import android.util.Log;
import java.lang.annotation.Retention;
@@ -41,6 +44,8 @@ import java.util.concurrent.Executor;
* @see ImsRcsManager#getUceAdapter() for information on creating an instance of this class.
* @hide
*/
+@SystemApi
+@TestApi
public class RcsUceAdapter {
private static final String TAG = "RcsUceAdapter";
@@ -169,6 +174,7 @@ public class RcsUceAdapter {
* Provides a one-time callback for the response to a UCE request. After this callback is called
* by the framework, the reference to this callback will be discarded on the service side.
* @see #requestCapabilities(Executor, List, CapabilitiesCallback)
+ * @hide
*/
public static class CapabilitiesCallback {
@@ -196,6 +202,7 @@ public class RcsUceAdapter {
/**
* Not to be instantiated directly, use
* {@link ImsRcsManager#getUceAdapter()} to instantiate this manager class.
+ * @hide
*/
RcsUceAdapter(int subId) {
mSubId = subId;
@@ -219,6 +226,7 @@ public class RcsUceAdapter {
* {@link RcsUceAdapter} is valid, but the ImsService associated with the subscription is not
* available. This can happen if the ImsService has crashed, for example, or if the subscription
* becomes inactive. See {@link ImsException#getCode()} for more information on the error codes.
+ * @hide
*/
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public void requestCapabilities(@CallbackExecutor Executor executor,
@@ -281,6 +289,7 @@ public class RcsUceAdapter {
* {@link RcsUceAdapter} is valid, but the ImsService associated with the subscription is not
* available. This can happen if the ImsService has crashed, for example, or if the subscription
* becomes inactive. See {@link ImsException#getCode()} for more information on the error codes.
+ * @hide
*/
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public @PublishState int getUcePublishState() throws ImsException {
@@ -305,7 +314,7 @@ public class RcsUceAdapter {
* for the associated subscription.
*
* @return true if the user’s setting for UCE is enabled, false otherwise. If false,
- * {@link ImsRcsManager#isCapable(int)} will return false for
+ * {@link ImsRcsManager#isCapable(int, int)} will return false for
* {@link RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_OPTIONS_UCE} and
* {@link RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_PRESENCE_UCE}
* @see #setUceSettingEnabled(boolean)
diff --git a/telephony/java/android/telephony/ims/feature/MmTelFeature.java b/telephony/java/android/telephony/ims/feature/MmTelFeature.java
index 0d5a979e5894..f3aea4978971 100644
--- a/telephony/java/android/telephony/ims/feature/MmTelFeature.java
+++ b/telephony/java/android/telephony/ims/feature/MmTelFeature.java
@@ -325,7 +325,6 @@ public class MmTelFeature extends ImsFeature {
*/
@NonNull
@Override
- @SystemApi @TestApi
public String toString() {
StringBuilder builder = new StringBuilder("MmTel Capabilities - [");
builder.append("Voice: ");
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 6aa5a52d55d5..3f573c92d022 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -831,6 +831,11 @@ interface ITelephony {
void disableIms(int slotId);
/**
+ * Toggle framework IMS disables and enables.
+ */
+ void resetIms(int slotIndex);
+
+ /**
* Get IImsMmTelFeature binder from ImsResolver that corresponds to the subId and MMTel feature
* as well as registering the MmTelFeature for callbacks using the IImsServiceFeatureCallback
* interface.
diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java
index 0db86d6054b3..9ac8cb136c6b 100644
--- a/telephony/java/com/android/internal/telephony/RILConstants.java
+++ b/telephony/java/com/android/internal/telephony/RILConstants.java
@@ -480,6 +480,7 @@ public interface RILConstants {
int RIL_REQUEST_STOP_KEEPALIVE = 145;
int RIL_REQUEST_ENABLE_MODEM = 146;
int RIL_REQUEST_GET_MODEM_STATUS = 147;
+ int RIL_REQUEST_CDMA_SEND_SMS_EXPECT_MORE = 148;
/* The following requests are not defined in RIL.h */
int RIL_REQUEST_HAL_NON_RIL_BASE = 200;
diff --git a/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl b/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl
index 7422863d862c..35e8a12e898a 100644
--- a/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl
+++ b/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl
@@ -21,6 +21,7 @@ import android.content.Intent;
import android.os.Bundle;
import android.telephony.euicc.DownloadableSubscription;
import android.telephony.euicc.EuiccInfo;
+import java.util.List;
/** @hide */
interface IEuiccController {
@@ -47,4 +48,7 @@ interface IEuiccController {
oneway void eraseSubscriptionsWithOptions(
int cardId, int options, in PendingIntent callbackIntent);
oneway void retainSubscriptionsForFactoryReset(int cardId, in PendingIntent callbackIntent);
+ void setSupportedCountries(boolean isSupported, in List<String> countriesList);
+ List<String> getSupportedCountries(boolean isSupported);
+ boolean isSupportedCountry(String countryIso);
}
diff --git a/tests/net/common/java/android/net/LinkAddressTest.java b/tests/net/common/java/android/net/LinkAddressTest.java
index b2e573b6c74b..e566e44c811a 100644
--- a/tests/net/common/java/android/net/LinkAddressTest.java
+++ b/tests/net/common/java/android/net/LinkAddressTest.java
@@ -38,6 +38,8 @@ import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import android.os.SystemClock;
+
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -316,11 +318,84 @@ public class LinkAddressTest {
l = new LinkAddress(V6_ADDRESS, 64, 123, 456);
assertParcelingIsLossless(l);
+ l = new LinkAddress(V6_ADDRESS, 64, 123, 456,
+ 1L, 3600000L);
+ assertParcelingIsLossless(l);
l = new LinkAddress(V4 + "/28", IFA_F_PERMANENT, RT_SCOPE_LINK);
- assertParcelSane(l, 4);
+ assertParcelSane(l, 6);
+ }
+
+ /*
+ @Test
+ public void testDeprecationTime() {
+ try {
+ new LinkAddress(V6_ADDRESS, 64, 0, 456,
+ LinkAddress.LIFETIME_UNKNOWN,
+ SystemClock.elapsedRealtime() + 200000);
+ fail("Only one time provided should cause exception");
+ } catch (IllegalArgumentException expected) { }
+
+ try {
+ new LinkAddress(V6_ADDRESS, 64, 0, 456,
+ SystemClock.elapsedRealtime() - 100000,
+ SystemClock.elapsedRealtime() - 200000);
+ fail("deprecation time later than expiration time should cause exception");
+ } catch (IllegalArgumentException expected) { }
+
+ try {
+ new LinkAddress(V6_ADDRESS, 64, 0, 456,
+ -2, SystemClock.elapsedRealtime());
+ fail("negative deprecation time should cause exception");
+ } catch (IllegalArgumentException expected) { }
}
+ @Test
+ public void testExpirationTime() {
+ try {
+ new LinkAddress(V6_ADDRESS, 64, 0, 456,
+ SystemClock.elapsedRealtime() + 200000,
+ LinkAddress.LIFETIME_UNKNOWN);
+ fail("Only one time provided should cause exception");
+ } catch (IllegalArgumentException expected) { }
+
+ try {
+ new LinkAddress(V6_ADDRESS, 64, 0, 456,
+ SystemClock.elapsedRealtime() - 10000, -2);
+ fail("negative expiration time should cause exception");
+ } catch (IllegalArgumentException expected) { }
+ }
+
+ @Test
+ public void testGetFlags() {
+ LinkAddress l = new LinkAddress(V6_ADDRESS, 64, 123, RT_SCOPE_HOST);
+ assertEquals(123, l.getFlags());
+
+ // Test if deprecated bit was added/remove automatically based on the provided deprecation
+ // time
+ l = new LinkAddress(V6_ADDRESS, 64, 0, RT_SCOPE_HOST,
+ SystemClock.elapsedRealtime() - 100000, LinkAddress.LIFETIME_PERMANENT);
+ // Check if the flag is added automatically.
+ assertTrue((l.getFlags() & IFA_F_DEPRECATED) != 0);
+
+ l = new LinkAddress(V6_ADDRESS, 64, IFA_F_DEPRECATED, RT_SCOPE_HOST,
+ SystemClock.elapsedRealtime() + 100000, LinkAddress.LIFETIME_PERMANENT);
+ // Check if the flag is removed automatically.
+ assertTrue((l.getFlags() & IFA_F_DEPRECATED) == 0);
+
+ l = new LinkAddress(V6_ADDRESS, 64, IFA_F_DEPRECATED, RT_SCOPE_HOST,
+ LinkAddress.LIFETIME_PERMANENT, LinkAddress.LIFETIME_PERMANENT);
+ // Check if the permanent flag is added.
+ assertTrue((l.getFlags() & IFA_F_PERMANENT) != 0);
+
+ l = new LinkAddress(V6_ADDRESS, 64, IFA_F_PERMANENT, RT_SCOPE_HOST,
+ SystemClock.elapsedRealtime() - 100000,
+ SystemClock.elapsedRealtime() + 100000);
+ // Check if the permanent flag is removed
+ assertTrue((l.getFlags() & IFA_F_PERMANENT) == 0);
+ }*/
+
+
private void assertGlobalPreferred(LinkAddress l, String msg) {
assertTrue(msg, l.isGlobalPreferred());
}
@@ -389,5 +464,12 @@ public class LinkAddressTest {
(IFA_F_TEMPORARY|IFA_F_TENTATIVE|IFA_F_OPTIMISTIC),
RT_SCOPE_UNIVERSE);
assertGlobalPreferred(l, "v6,global,tempaddr+optimistic");
+
+ l = new LinkAddress(V6_ADDRESS, 64, IFA_F_DEPRECATED,
+ RT_SCOPE_UNIVERSE, SystemClock.elapsedRealtime() + 100000,
+ SystemClock.elapsedRealtime() + 200000);
+ // Although the deprecated bit is set, but the deprecation time is in the future, test
+ // if the flag is removed automatically.
+ assertGlobalPreferred(l, "v6,global,tempaddr+deprecated in the future");
}
}
diff --git a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java b/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
index 15691127cab7..3e4f3d818840 100644
--- a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
+++ b/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
@@ -269,9 +269,10 @@ public class NetworkCapabilitiesTest {
.setUids(uids)
.addCapability(NET_CAPABILITY_EIMS)
.addCapability(NET_CAPABILITY_NOT_METERED);
+ netCap.setOwnerUid(123);
assertParcelingIsLossless(netCap);
netCap.setSSID(TEST_SSID);
- assertParcelSane(netCap, 12);
+ assertParcelSane(netCap, 13);
}
@Test
diff --git a/tests/net/java/android/net/ConnectivityDiagnosticsManagerTest.java b/tests/net/java/android/net/ConnectivityDiagnosticsManagerTest.java
index 065add4fc253..7ab4b56fae80 100644
--- a/tests/net/java/android/net/ConnectivityDiagnosticsManagerTest.java
+++ b/tests/net/java/android/net/ConnectivityDiagnosticsManagerTest.java
@@ -16,6 +16,8 @@
package android.net;
+import static android.net.ConnectivityDiagnosticsManager.ConnectivityDiagnosticsBinder;
+import static android.net.ConnectivityDiagnosticsManager.ConnectivityDiagnosticsCallback;
import static android.net.ConnectivityDiagnosticsManager.ConnectivityReport;
import static android.net.ConnectivityDiagnosticsManager.DataStallReport;
@@ -25,12 +27,19 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
import android.os.PersistableBundle;
+import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
+import org.mockito.Mock;
+
+import java.util.concurrent.Executor;
@RunWith(JUnit4.class)
public class ConnectivityDiagnosticsManagerTest {
@@ -41,6 +50,19 @@ public class ConnectivityDiagnosticsManagerTest {
private static final String BUNDLE_KEY = "key";
private static final String BUNDLE_VALUE = "value";
+ private static final Executor INLINE_EXECUTOR = x -> x.run();
+
+ @Mock private ConnectivityDiagnosticsCallback mCb;
+
+ private ConnectivityDiagnosticsBinder mBinder;
+
+ @Before
+ public void setUp() {
+ mCb = mock(ConnectivityDiagnosticsCallback.class);
+
+ mBinder = new ConnectivityDiagnosticsBinder(mCb, INLINE_EXECUTOR);
+ }
+
private ConnectivityReport createSampleConnectivityReport() {
final LinkProperties linkProperties = new LinkProperties();
linkProperties.setInterfaceName(INTERFACE_NAME);
@@ -193,4 +215,34 @@ public class ConnectivityDiagnosticsManagerTest {
public void testDataStallReportParcelUnparcel() {
assertParcelSane(createSampleDataStallReport(), 4);
}
+
+ @Test
+ public void testConnectivityDiagnosticsCallbackOnConnectivityReport() {
+ mBinder.onConnectivityReport(createSampleConnectivityReport());
+
+ // The callback will be invoked synchronously by inline executor. Immediately check the
+ // latch without waiting.
+ verify(mCb).onConnectivityReport(eq(createSampleConnectivityReport()));
+ }
+
+ @Test
+ public void testConnectivityDiagnosticsCallbackOnDataStallSuspected() {
+ mBinder.onDataStallSuspected(createSampleDataStallReport());
+
+ // The callback will be invoked synchronously by inline executor. Immediately check the
+ // latch without waiting.
+ verify(mCb).onDataStallSuspected(eq(createSampleDataStallReport()));
+ }
+
+ @Test
+ public void testConnectivityDiagnosticsCallbackOnNetworkConnectivityReported() {
+ final Network n = new Network(NET_ID);
+ final boolean connectivity = true;
+
+ mBinder.onNetworkConnectivityReported(n, connectivity);
+
+ // The callback will be invoked synchronously by inline executor. Immediately check the
+ // latch without waiting.
+ verify(mCb).onNetworkConnectivityReported(eq(n), eq(connectivity));
+ }
}
diff --git a/tests/net/java/android/net/Ikev2VpnProfileTest.java b/tests/net/java/android/net/Ikev2VpnProfileTest.java
new file mode 100644
index 000000000000..d6a2176d7e81
--- /dev/null
+++ b/tests/net/java/android/net/Ikev2VpnProfileTest.java
@@ -0,0 +1,360 @@
+/*
+ * 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 static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.mock;
+
+import android.test.mock.MockContext;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.net.VpnProfile;
+import com.android.org.bouncycastle.x509.X509V1CertificateGenerator;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.math.BigInteger;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.PrivateKey;
+import java.security.cert.X509Certificate;
+import java.util.Date;
+import java.util.concurrent.TimeUnit;
+
+import javax.security.auth.x500.X500Principal;
+
+/** Unit tests for {@link Ikev2VpnProfile.Builder}. */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class Ikev2VpnProfileTest {
+ private static final String SERVER_ADDR_STRING = "1.2.3.4";
+ private static final String IDENTITY_STRING = "Identity";
+ private static final String USERNAME_STRING = "username";
+ private static final String PASSWORD_STRING = "pa55w0rd";
+ private static final String EXCL_LIST = "exclList";
+ private static final byte[] PSK_BYTES = "preSharedKey".getBytes();
+ private static final int TEST_MTU = 1300;
+
+ private final MockContext mMockContext =
+ new MockContext() {
+ @Override
+ public String getOpPackageName() {
+ return "fooPackage";
+ }
+ };
+ private final ProxyInfo mProxy = new ProxyInfo(SERVER_ADDR_STRING, -1, EXCL_LIST);
+
+ private X509Certificate mUserCert;
+ private X509Certificate mServerRootCa;
+ private PrivateKey mPrivateKey;
+
+ @Before
+ public void setUp() throws Exception {
+ mServerRootCa = generateRandomCertAndKeyPair().cert;
+
+ final CertificateAndKey userCertKey = generateRandomCertAndKeyPair();
+ mUserCert = userCertKey.cert;
+ mPrivateKey = userCertKey.key;
+ }
+
+ private Ikev2VpnProfile.Builder getBuilderWithDefaultOptions() {
+ final Ikev2VpnProfile.Builder builder =
+ new Ikev2VpnProfile.Builder(SERVER_ADDR_STRING, IDENTITY_STRING);
+
+ builder.setBypassable(true);
+ builder.setProxy(mProxy);
+ builder.setMaxMtu(TEST_MTU);
+ builder.setMetered(true);
+
+ return builder;
+ }
+
+ @Test
+ public void testBuildValidProfileWithOptions() throws Exception {
+ final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+
+ builder.setAuthUsernamePassword(USERNAME_STRING, PASSWORD_STRING, mServerRootCa);
+ final Ikev2VpnProfile profile = builder.build();
+ assertNotNull(profile);
+
+ // Check non-auth parameters correctly stored
+ assertEquals(SERVER_ADDR_STRING, profile.getServerAddr());
+ assertEquals(IDENTITY_STRING, profile.getUserIdentity());
+ assertEquals(mProxy, profile.getProxyInfo());
+ assertTrue(profile.isBypassable());
+ assertTrue(profile.isMetered());
+ assertEquals(TEST_MTU, profile.getMaxMtu());
+ }
+
+ @Test
+ public void testBuildUsernamePasswordProfile() throws Exception {
+ final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+
+ builder.setAuthUsernamePassword(USERNAME_STRING, PASSWORD_STRING, mServerRootCa);
+ final Ikev2VpnProfile profile = builder.build();
+ assertNotNull(profile);
+
+ assertEquals(USERNAME_STRING, profile.getUsername());
+ assertEquals(PASSWORD_STRING, profile.getPassword());
+ assertEquals(mServerRootCa, profile.getServerRootCaCert());
+
+ assertNull(profile.getPresharedKey());
+ assertNull(profile.getRsaPrivateKey());
+ assertNull(profile.getUserCert());
+ }
+
+ @Test
+ public void testBuildDigitalSignatureProfile() throws Exception {
+ final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+
+ builder.setAuthDigitalSignature(mUserCert, mPrivateKey, mServerRootCa);
+ final Ikev2VpnProfile profile = builder.build();
+ assertNotNull(profile);
+
+ assertEquals(profile.getUserCert(), mUserCert);
+ assertEquals(mPrivateKey, profile.getRsaPrivateKey());
+ assertEquals(profile.getServerRootCaCert(), mServerRootCa);
+
+ assertNull(profile.getPresharedKey());
+ assertNull(profile.getUsername());
+ assertNull(profile.getPassword());
+ }
+
+ @Test
+ public void testBuildPresharedKeyProfile() throws Exception {
+ final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+
+ builder.setAuthPsk(PSK_BYTES);
+ final Ikev2VpnProfile profile = builder.build();
+ assertNotNull(profile);
+
+ assertArrayEquals(PSK_BYTES, profile.getPresharedKey());
+
+ assertNull(profile.getServerRootCaCert());
+ assertNull(profile.getUsername());
+ assertNull(profile.getPassword());
+ assertNull(profile.getRsaPrivateKey());
+ assertNull(profile.getUserCert());
+ }
+
+ @Test
+ public void testBuildNoAuthMethodSet() throws Exception {
+ final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+
+ try {
+ builder.build();
+ fail("Expected exception due to lack of auth method");
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+ @Test
+ public void testBuildInvalidMtu() throws Exception {
+ final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+
+ try {
+ builder.setMaxMtu(500);
+ fail("Expected exception due to too-small MTU");
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+ private void verifyVpnProfileCommon(VpnProfile profile) {
+ assertEquals(SERVER_ADDR_STRING, profile.server);
+ assertEquals(IDENTITY_STRING, profile.ipsecIdentifier);
+ assertEquals(mProxy, profile.proxy);
+ assertTrue(profile.isBypassable);
+ assertTrue(profile.isMetered);
+ assertEquals(TEST_MTU, profile.maxMtu);
+ }
+
+ @Test
+ public void testPskConvertToVpnProfile() throws Exception {
+ final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+
+ builder.setAuthPsk(PSK_BYTES);
+ final VpnProfile profile = builder.build().toVpnProfile();
+
+ verifyVpnProfileCommon(profile);
+ assertEquals(Ikev2VpnProfile.encodeForIpsecSecret(PSK_BYTES), profile.ipsecSecret);
+
+ // Check nothing else is set
+ assertEquals("", profile.username);
+ assertEquals("", profile.password);
+ assertEquals("", profile.ipsecUserCert);
+ assertEquals("", profile.ipsecCaCert);
+ }
+
+ @Test
+ public void testUsernamePasswordConvertToVpnProfile() throws Exception {
+ final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+
+ builder.setAuthUsernamePassword(USERNAME_STRING, PASSWORD_STRING, mServerRootCa);
+ final VpnProfile profile = builder.build().toVpnProfile();
+
+ verifyVpnProfileCommon(profile);
+ assertEquals(USERNAME_STRING, profile.username);
+ assertEquals(PASSWORD_STRING, profile.password);
+ assertEquals(Ikev2VpnProfile.certificateToPemString(mServerRootCa), profile.ipsecCaCert);
+
+ // Check nothing else is set
+ assertEquals("", profile.ipsecUserCert);
+ assertEquals("", profile.ipsecSecret);
+ }
+
+ @Test
+ public void testRsaConvertToVpnProfile() throws Exception {
+ final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+
+ builder.setAuthDigitalSignature(mUserCert, mPrivateKey, mServerRootCa);
+ final VpnProfile profile = builder.build().toVpnProfile();
+
+ verifyVpnProfileCommon(profile);
+ assertEquals(Ikev2VpnProfile.certificateToPemString(mUserCert), profile.ipsecUserCert);
+ assertEquals(
+ Ikev2VpnProfile.encodeForIpsecSecret(mPrivateKey.getEncoded()),
+ profile.ipsecSecret);
+ assertEquals(Ikev2VpnProfile.certificateToPemString(mServerRootCa), profile.ipsecCaCert);
+
+ // Check nothing else is set
+ assertEquals("", profile.username);
+ assertEquals("", profile.password);
+ }
+
+ @Test
+ public void testPskFromVpnProfileDiscardsIrrelevantValues() throws Exception {
+ final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+
+ builder.setAuthPsk(PSK_BYTES);
+ final VpnProfile profile = builder.build().toVpnProfile();
+ profile.username = USERNAME_STRING;
+ profile.password = PASSWORD_STRING;
+ profile.ipsecCaCert = Ikev2VpnProfile.certificateToPemString(mServerRootCa);
+ profile.ipsecUserCert = Ikev2VpnProfile.certificateToPemString(mUserCert);
+
+ final Ikev2VpnProfile result = Ikev2VpnProfile.fromVpnProfile(profile);
+ assertNull(result.getUsername());
+ assertNull(result.getPassword());
+ assertNull(result.getUserCert());
+ assertNull(result.getRsaPrivateKey());
+ assertNull(result.getServerRootCaCert());
+ }
+
+ @Test
+ public void testUsernamePasswordFromVpnProfileDiscardsIrrelevantValues() throws Exception {
+ final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+
+ builder.setAuthUsernamePassword(USERNAME_STRING, PASSWORD_STRING, mServerRootCa);
+ final VpnProfile profile = builder.build().toVpnProfile();
+ profile.ipsecSecret = new String(PSK_BYTES);
+ profile.ipsecUserCert = Ikev2VpnProfile.certificateToPemString(mUserCert);
+
+ final Ikev2VpnProfile result = Ikev2VpnProfile.fromVpnProfile(profile);
+ assertNull(result.getPresharedKey());
+ assertNull(result.getUserCert());
+ assertNull(result.getRsaPrivateKey());
+ }
+
+ @Test
+ public void testRsaFromVpnProfileDiscardsIrrelevantValues() throws Exception {
+ final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+
+ builder.setAuthDigitalSignature(mUserCert, mPrivateKey, mServerRootCa);
+ final VpnProfile profile = builder.build().toVpnProfile();
+ profile.username = USERNAME_STRING;
+ profile.password = PASSWORD_STRING;
+
+ final Ikev2VpnProfile result = Ikev2VpnProfile.fromVpnProfile(profile);
+ assertNull(result.getUsername());
+ assertNull(result.getPassword());
+ assertNull(result.getPresharedKey());
+ }
+
+ @Test
+ public void testPskConversionIsLossless() throws Exception {
+ final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+
+ builder.setAuthPsk(PSK_BYTES);
+ final Ikev2VpnProfile ikeProfile = builder.build();
+
+ assertEquals(ikeProfile, Ikev2VpnProfile.fromVpnProfile(ikeProfile.toVpnProfile()));
+ }
+
+ @Test
+ public void testUsernamePasswordConversionIsLossless() throws Exception {
+ final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+
+ builder.setAuthUsernamePassword(USERNAME_STRING, PASSWORD_STRING, mServerRootCa);
+ final Ikev2VpnProfile ikeProfile = builder.build();
+
+ assertEquals(ikeProfile, Ikev2VpnProfile.fromVpnProfile(ikeProfile.toVpnProfile()));
+ }
+
+ @Test
+ public void testRsaConversionIsLossless() throws Exception {
+ final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+
+ builder.setAuthDigitalSignature(mUserCert, mPrivateKey, mServerRootCa);
+ final Ikev2VpnProfile ikeProfile = builder.build();
+
+ assertEquals(ikeProfile, Ikev2VpnProfile.fromVpnProfile(ikeProfile.toVpnProfile()));
+ }
+
+ private static class CertificateAndKey {
+ public final X509Certificate cert;
+ public final PrivateKey key;
+
+ CertificateAndKey(X509Certificate cert, PrivateKey key) {
+ this.cert = cert;
+ this.key = key;
+ }
+ }
+
+ private static CertificateAndKey generateRandomCertAndKeyPair() throws Exception {
+ final Date validityBeginDate =
+ new Date(System.currentTimeMillis() - TimeUnit.DAYS.toMillis(1L));
+ final Date validityEndDate =
+ new Date(System.currentTimeMillis() + TimeUnit.DAYS.toMillis(1L));
+
+ // Generate a keypair
+ final KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
+ keyPairGenerator.initialize(512);
+ final KeyPair keyPair = keyPairGenerator.generateKeyPair();
+
+ final X500Principal dnName = new X500Principal("CN=test.android.com");
+ final X509V1CertificateGenerator certGen = new X509V1CertificateGenerator();
+ certGen.setSerialNumber(BigInteger.valueOf(System.currentTimeMillis()));
+ certGen.setSubjectDN(dnName);
+ certGen.setIssuerDN(dnName);
+ certGen.setNotBefore(validityBeginDate);
+ certGen.setNotAfter(validityEndDate);
+ certGen.setPublicKey(keyPair.getPublic());
+ certGen.setSignatureAlgorithm("SHA256WithRSAEncryption");
+
+ final X509Certificate cert = certGen.generate(keyPair.getPrivate(), "AndroidOpenSSL");
+ return new CertificateAndKey(cert, keyPair.getPrivate());
+ }
+}
diff --git a/tests/net/java/android/net/VpnManagerTest.java b/tests/net/java/android/net/VpnManagerTest.java
new file mode 100644
index 000000000000..655c4d118592
--- /dev/null
+++ b/tests/net/java/android/net/VpnManagerTest.java
@@ -0,0 +1,83 @@
+/*
+ * 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 static org.mockito.Mockito.mock;
+
+import android.test.mock.MockContext;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/** Unit tests for {@link VpnManager}. */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class VpnManagerTest {
+ private static final String VPN_PROFILE_KEY = "KEY";
+
+ private IConnectivityManager mMockCs;
+ private VpnManager mVpnManager;
+ private final MockContext mMockContext =
+ new MockContext() {
+ @Override
+ public String getOpPackageName() {
+ return "fooPackage";
+ }
+ };
+
+ @Before
+ public void setUp() throws Exception {
+ mMockCs = mock(IConnectivityManager.class);
+ mVpnManager = new VpnManager(mMockContext, mMockCs);
+ }
+
+ @Test
+ public void testProvisionVpnProfile() throws Exception {
+ try {
+ mVpnManager.provisionVpnProfile(mock(PlatformVpnProfile.class));
+ } catch (UnsupportedOperationException expected) {
+ }
+ }
+
+ @Test
+ public void testDeleteProvisionedVpnProfile() throws Exception {
+ try {
+ mVpnManager.deleteProvisionedVpnProfile();
+ } catch (UnsupportedOperationException expected) {
+ }
+ }
+
+ @Test
+ public void testStartProvisionedVpnProfile() throws Exception {
+ try {
+ mVpnManager.startProvisionedVpnProfile();
+ } catch (UnsupportedOperationException expected) {
+ }
+ }
+
+ @Test
+ public void testStopProvisionedVpnProfile() throws Exception {
+ try {
+ mVpnManager.stopProvisionedVpnProfile();
+ } catch (UnsupportedOperationException expected) {
+ }
+ }
+}
diff --git a/tests/net/java/com/android/internal/net/VpnProfileTest.java b/tests/net/java/com/android/internal/net/VpnProfileTest.java
new file mode 100644
index 000000000000..8a4b53343c26
--- /dev/null
+++ b/tests/net/java/com/android/internal/net/VpnProfileTest.java
@@ -0,0 +1,185 @@
+/*
+ * 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.net;
+
+import static com.android.testutils.ParcelUtilsKt.assertParcelSane;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.net.IpSecAlgorithm;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.Arrays;
+
+/** Unit tests for {@link VpnProfile}. */
+@SmallTest
+@RunWith(JUnit4.class)
+public class VpnProfileTest {
+ private static final String DUMMY_PROFILE_KEY = "Test";
+
+ @Test
+ public void testDefaults() throws Exception {
+ final VpnProfile p = new VpnProfile(DUMMY_PROFILE_KEY);
+
+ assertEquals(DUMMY_PROFILE_KEY, p.key);
+ assertEquals("", p.name);
+ assertEquals(VpnProfile.TYPE_PPTP, p.type);
+ assertEquals("", p.server);
+ assertEquals("", p.username);
+ assertEquals("", p.password);
+ assertEquals("", p.dnsServers);
+ assertEquals("", p.searchDomains);
+ assertEquals("", p.routes);
+ assertTrue(p.mppe);
+ assertEquals("", p.l2tpSecret);
+ assertEquals("", p.ipsecIdentifier);
+ assertEquals("", p.ipsecSecret);
+ assertEquals("", p.ipsecUserCert);
+ assertEquals("", p.ipsecCaCert);
+ assertEquals("", p.ipsecServerCert);
+ assertEquals(null, p.proxy);
+ assertTrue(p.getAllowedAlgorithms() != null && p.getAllowedAlgorithms().isEmpty());
+ assertFalse(p.isBypassable);
+ assertFalse(p.isMetered);
+ assertEquals(1400, p.maxMtu);
+ assertFalse(p.areAuthParamsInline);
+ }
+
+ private VpnProfile getSampleIkev2Profile(String key) {
+ final VpnProfile p = new VpnProfile(key);
+
+ p.name = "foo";
+ p.type = VpnProfile.TYPE_IKEV2_IPSEC_USER_PASS;
+ p.server = "bar";
+ p.username = "baz";
+ p.password = "qux";
+ p.dnsServers = "8.8.8.8";
+ p.searchDomains = "";
+ p.routes = "0.0.0.0/0";
+ p.mppe = false;
+ p.l2tpSecret = "";
+ p.ipsecIdentifier = "quux";
+ p.ipsecSecret = "quuz";
+ p.ipsecUserCert = "corge";
+ p.ipsecCaCert = "grault";
+ p.ipsecServerCert = "garply";
+ p.proxy = null;
+ p.setAllowedAlgorithms(
+ Arrays.asList(
+ IpSecAlgorithm.AUTH_CRYPT_AES_GCM,
+ IpSecAlgorithm.AUTH_HMAC_SHA512,
+ IpSecAlgorithm.CRYPT_AES_CBC));
+ p.isBypassable = true;
+ p.isMetered = true;
+ p.maxMtu = 1350;
+ p.areAuthParamsInline = true;
+
+ // Not saved, but also not compared.
+ p.saveLogin = true;
+
+ return p;
+ }
+
+ @Test
+ public void testEquals() {
+ assertEquals(
+ getSampleIkev2Profile(DUMMY_PROFILE_KEY), getSampleIkev2Profile(DUMMY_PROFILE_KEY));
+
+ final VpnProfile modified = getSampleIkev2Profile(DUMMY_PROFILE_KEY);
+ modified.maxMtu--;
+ assertNotEquals(getSampleIkev2Profile(DUMMY_PROFILE_KEY), modified);
+ }
+
+ @Test
+ public void testParcelUnparcel() {
+ assertParcelSane(getSampleIkev2Profile(DUMMY_PROFILE_KEY), 22);
+ }
+
+ @Test
+ public void testSetInvalidAlgorithmValueDelimiter() {
+ final VpnProfile profile = getSampleIkev2Profile(DUMMY_PROFILE_KEY);
+
+ try {
+ profile.setAllowedAlgorithms(
+ Arrays.asList("test" + VpnProfile.VALUE_DELIMITER + "test"));
+ fail("Expected failure due to value separator in algorithm name");
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+ @Test
+ public void testSetInvalidAlgorithmListDelimiter() {
+ final VpnProfile profile = getSampleIkev2Profile(DUMMY_PROFILE_KEY);
+
+ try {
+ profile.setAllowedAlgorithms(
+ Arrays.asList("test" + VpnProfile.LIST_DELIMITER + "test"));
+ fail("Expected failure due to value separator in algorithm name");
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+ @Test
+ public void testEncodeDecode() {
+ final VpnProfile profile = getSampleIkev2Profile(DUMMY_PROFILE_KEY);
+ final VpnProfile decoded = VpnProfile.decode(DUMMY_PROFILE_KEY, profile.encode());
+ assertEquals(profile, decoded);
+ }
+
+ @Test
+ public void testEncodeDecodeTooManyValues() {
+ final VpnProfile profile = getSampleIkev2Profile(DUMMY_PROFILE_KEY);
+ final byte[] tooManyValues =
+ (new String(profile.encode()) + VpnProfile.VALUE_DELIMITER + "invalid").getBytes();
+
+ assertNull(VpnProfile.decode(DUMMY_PROFILE_KEY, tooManyValues));
+ }
+
+ @Test
+ public void testEncodeDecodeInvalidNumberOfValues() {
+ final VpnProfile profile = getSampleIkev2Profile(DUMMY_PROFILE_KEY);
+ final String encoded = new String(profile.encode());
+ final byte[] tooFewValues =
+ encoded.substring(0, encoded.lastIndexOf(VpnProfile.VALUE_DELIMITER)).getBytes();
+
+ assertNull(VpnProfile.decode(DUMMY_PROFILE_KEY, tooFewValues));
+ }
+
+ @Test
+ public void testEncodeDecodeLoginsNotSaved() {
+ final VpnProfile profile = getSampleIkev2Profile(DUMMY_PROFILE_KEY);
+ profile.saveLogin = false;
+
+ final VpnProfile decoded = VpnProfile.decode(DUMMY_PROFILE_KEY, profile.encode());
+ assertNotEquals(profile, decoded);
+
+ // Add the username/password back, everything else must be equal.
+ decoded.username = profile.username;
+ decoded.password = profile.password;
+ assertEquals(profile, decoded);
+ }
+}
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 09cc69e83f41..a0a1352a6330 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -6313,12 +6313,24 @@ public class ConnectivityServiceTest {
assertEquals(wifiLp, mService.getActiveLinkProperties());
}
+ @Test
+ public void testNetworkCapabilitiesRestrictedForCallerPermissions() {
+ int callerUid = Process.myUid();
+ final NetworkCapabilities originalNc = new NetworkCapabilities();
+ originalNc.setOwnerUid(callerUid);
+
+ final NetworkCapabilities newNc =
+ mService.networkCapabilitiesRestrictedForCallerPermissions(
+ originalNc, Process.myPid(), callerUid);
+
+ assertEquals(Process.INVALID_UID, newNc.getOwnerUid());
+ }
- private TestNetworkAgentWrapper establishVpn(LinkProperties lp, int establishingUid,
- Set<UidRange> vpnRange) throws Exception {
+ private TestNetworkAgentWrapper establishVpn(
+ LinkProperties lp, int ownerUid, Set<UidRange> vpnRange) throws Exception {
final TestNetworkAgentWrapper
vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN, lp);
- vpnNetworkAgent.getNetworkCapabilities().setEstablishingVpnAppUid(establishingUid);
+ vpnNetworkAgent.getNetworkCapabilities().setOwnerUid(ownerUid);
mMockVpn.setNetworkAgent(vpnNetworkAgent);
mMockVpn.connect();
mMockVpn.setUids(vpnRange);
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index 558de7c82d92..31b3a5064932 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -97,6 +97,8 @@ interface IWifiManager
void setMacRandomizationSettingPasspointEnabled(String fqdn, boolean enable);
+ void setMeteredOverridePasspoint(String fqdn, int meteredOverride);
+
boolean startScan(String packageName, String featureId);
List<ScanResult> getScanResults(String callingPackage, String callingFeatureId);
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index 114e0fa21296..0ef224a5f8ee 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -481,12 +481,14 @@ public class WifiConfiguration implements Parcelable {
allowedKeyManagement.set(WifiConfiguration.KeyMgmt.IEEE8021X);
break;
case SECURITY_TYPE_SAE:
+ allowedProtocols.set(WifiConfiguration.Protocol.RSN);
allowedKeyManagement.set(WifiConfiguration.KeyMgmt.SAE);
allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP);
allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP);
requirePMF = true;
break;
case SECURITY_TYPE_EAP_SUITE_B:
+ allowedProtocols.set(WifiConfiguration.Protocol.RSN);
allowedKeyManagement.set(WifiConfiguration.KeyMgmt.SUITE_B_192);
allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.GCMP_256);
allowedGroupCiphers.set(WifiConfiguration.GroupCipher.GCMP_256);
@@ -496,6 +498,7 @@ public class WifiConfiguration implements Parcelable {
requirePMF = true;
break;
case SECURITY_TYPE_OWE:
+ allowedProtocols.set(WifiConfiguration.Protocol.RSN);
allowedKeyManagement.set(WifiConfiguration.KeyMgmt.OWE);
allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP);
allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP);
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 0e8c6ed8774e..fb3e794d92d1 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -1793,18 +1793,6 @@ public class WifiManager {
}
/**
- * Same as {@link #registerNetworkRequestMatchCallback(Executor, NetworkRequestMatchCallback)},
- * except that the callback will be executed on the application's main thread.
- * @param callback Callback for network match events to register.
- * @hide
- */
- @SystemApi
- @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
- public void registerNetworkRequestMatchCallback(@NonNull NetworkRequestMatchCallback callback) {
- registerNetworkRequestMatchCallback(mContext.getMainExecutor(), callback);
- }
-
- /**
* Registers a callback for NetworkRequest matches. See {@link NetworkRequestMatchCallback}.
* Caller can unregister a previously registered callback using
* {@link #unregisterNetworkRequestMatchCallback(NetworkRequestMatchCallback)}
@@ -3719,18 +3707,6 @@ public class WifiManager {
}
/**
- * Same as {@link #registerSoftApCallback(Executor, SoftApCallback)},
- * except that the callback will be executed on the application's main thread.
- * @param callback Callback for soft AP events
- * @hide
- */
- @SystemApi
- @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
- public void registerSoftApCallback(@NonNull SoftApCallback callback) {
- registerSoftApCallback(mContext.getMainExecutor(), callback);
- }
-
- /**
* Registers a callback for Soft AP. See {@link SoftApCallback}. Caller will receive the current
* soft AP state and number of connected devices immediately after a successful call to this API
* via callback. Note that receiving an immediate WIFI_AP_STATE_FAILED value for soft AP state
@@ -4373,6 +4349,25 @@ public class WifiManager {
}
/**
+ * Sets the user's choice of metered override for a Passpoint profile.
+ *
+ * @param fqdn the FQDN (fully qualified domain name) of the passpoint profile.
+ * @param meteredOverride One of three values: {@link WifiConfiguration#METERED_OVERRIDE_NONE},
+ * {@link WifiConfiguration#METERED_OVERRIDE_METERED},
+ * {@link WifiConfiguration#METERED_OVERRIDE_NOT_METERED}
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
+ public void setMeteredOverridePasspoint(@NonNull String fqdn, int meteredOverride) {
+ try {
+ mService.setMeteredOverridePasspoint(fqdn, meteredOverride);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Disable an ephemeral network.
*
* @param ssid in the format of WifiConfiguration's SSID.
@@ -5152,18 +5147,6 @@ public class WifiManager {
}
/**
- * Same as {@link #registerTrafficStateCallback(Executor, TrafficStateCallback)},
- * except that the callback will be executed on the application's main thread.
- * @param callback Callback for traffic state events
- * @hide
- */
- @SystemApi
- @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
- public void registerTrafficStateCallback(@NonNull TrafficStateCallback callback) {
- registerTrafficStateCallback(mContext.getMainExecutor(), callback);
- }
-
- /**
* Registers a callback for monitoring traffic state. See {@link TrafficStateCallback}. These
* callbacks will be invoked periodically by platform to inform clients about the current
* traffic state. Caller can unregister a previously registered callback using
diff --git a/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java b/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java
index 7c335fc323f5..3a0d080594c8 100644
--- a/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java
+++ b/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java
@@ -16,6 +16,9 @@
package android.net.wifi.hotspot2;
+import static android.net.wifi.WifiConfiguration.METERED_OVERRIDE_NONE;
+import static android.net.wifi.WifiConfiguration.MeteredOverride;
+
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.net.wifi.hotspot2.pps.Credential;
@@ -438,6 +441,18 @@ public final class PasspointConfiguration implements Parcelable {
private boolean mIsMacRandomizationEnabled = true;
/**
+ * Indicates if the end user has expressed an explicit opinion about the
+ * meteredness of this network, such as through the Settings app.
+ * This value is one of {@link #METERED_OVERRIDE_NONE}, {@link #METERED_OVERRIDE_METERED},
+ * or {@link #METERED_OVERRIDE_NOT_METERED}.
+ * <p>
+ * This should always override any values from {@link WifiInfo#getMeteredHint()}.
+ *
+ * By default this field is set to {@link #METERED_OVERRIDE_NONE}.
+ */
+ private int mMeteredOverride = METERED_OVERRIDE_NONE;
+
+ /**
* Configures the auto-association status of this Passpoint configuration. A value of true
* indicates that the configuration will be considered for auto-connection, a value of false
* indicates that only manual connection will work - the framework will not auto-associate to
@@ -463,6 +478,16 @@ public final class PasspointConfiguration implements Parcelable {
}
/**
+ * Sets the metered override setting for this Passpoint configuration.
+ *
+ * @param meteredOverride One of the values in {@link MeteredOverride}
+ * @hide
+ */
+ public void setMeteredOverride(@MeteredOverride int meteredOverride) {
+ mMeteredOverride = meteredOverride;
+ }
+
+ /**
* Indicates whether the Passpoint configuration may be auto-connected to by the framework. A
* value of true indicates that auto-connection can happen, a value of false indicates that it
* cannot. However, even when auto-connection is not possible manual connection by the user is
@@ -478,6 +503,18 @@ public final class PasspointConfiguration implements Parcelable {
}
/**
+ * Indicates whether the user chose this configuration to be treated as metered or not.
+ *
+ * @return One of the values in {@link MeteredOverride}
+ * @hide
+ */
+ @SystemApi
+ @MeteredOverride
+ public int getMeteredOverride() {
+ return mMeteredOverride;
+ }
+
+ /**
* Indicates whether a randomized MAC address or device MAC address will be used for
* connections to this Passpoint network. If true, a randomized MAC address will be used.
* Otherwise, the device MAC address will be used.
@@ -534,6 +571,7 @@ public final class PasspointConfiguration implements Parcelable {
mCarrierId = source.mCarrierId;
mIsAutoJoinEnabled = source.mIsAutoJoinEnabled;
mIsMacRandomizationEnabled = source.mIsMacRandomizationEnabled;
+ mMeteredOverride = source.mMeteredOverride;
}
@Override
@@ -565,6 +603,7 @@ public final class PasspointConfiguration implements Parcelable {
dest.writeInt(mCarrierId);
dest.writeBoolean(mIsAutoJoinEnabled);
dest.writeBoolean(mIsMacRandomizationEnabled);
+ dest.writeInt(mMeteredOverride);
}
@Override
@@ -597,6 +636,7 @@ public final class PasspointConfiguration implements Parcelable {
&& mCarrierId == that.mCarrierId
&& mIsAutoJoinEnabled == that.mIsAutoJoinEnabled
&& mIsMacRandomizationEnabled == that.mIsMacRandomizationEnabled
+ && mMeteredOverride == that.mMeteredOverride
&& (mServiceFriendlyNames == null ? that.mServiceFriendlyNames == null
: mServiceFriendlyNames.equals(that.mServiceFriendlyNames));
}
@@ -607,7 +647,8 @@ public final class PasspointConfiguration implements Parcelable {
mUpdateIdentifier, mCredentialPriority, mSubscriptionCreationTimeInMillis,
mSubscriptionExpirationTimeInMillis, mUsageLimitUsageTimePeriodInMinutes,
mUsageLimitStartTimeInMillis, mUsageLimitDataLimit, mUsageLimitTimeLimitInMinutes,
- mServiceFriendlyNames, mCarrierId, mIsAutoJoinEnabled, mIsMacRandomizationEnabled);
+ mServiceFriendlyNames, mCarrierId, mIsAutoJoinEnabled, mIsMacRandomizationEnabled,
+ mMeteredOverride);
}
@Override
@@ -663,6 +704,7 @@ public final class PasspointConfiguration implements Parcelable {
builder.append("CarrierId:" + mCarrierId);
builder.append("IsAutoJoinEnabled:" + mIsAutoJoinEnabled);
builder.append("mIsMacRandomizationEnabled:" + mIsMacRandomizationEnabled);
+ builder.append("mMeteredOverride:" + mMeteredOverride);
return builder.toString();
}
@@ -770,6 +812,7 @@ public final class PasspointConfiguration implements Parcelable {
config.mCarrierId = in.readInt();
config.mIsAutoJoinEnabled = in.readBoolean();
config.mIsMacRandomizationEnabled = in.readBoolean();
+ config.mMeteredOverride = in.readInt();
return config;
}
diff --git a/wifi/java/com/android/server/wifi/BaseWifiService.java b/wifi/java/com/android/server/wifi/BaseWifiService.java
index 080c6c772f63..b46755349ad7 100644
--- a/wifi/java/com/android/server/wifi/BaseWifiService.java
+++ b/wifi/java/com/android/server/wifi/BaseWifiService.java
@@ -198,6 +198,11 @@ public class BaseWifiService extends IWifiManager.Stub {
}
@Override
+ public void setMeteredOverridePasspoint(String fqdn, int meteredOverride) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
public boolean startScan(String packageName, String featureId) {
throw new UnsupportedOperationException();
}
diff --git a/wifi/tests/src/android/net/wifi/WifiManagerTest.java b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
index f369203e05ab..0c2876e340c1 100644
--- a/wifi/tests/src/android/net/wifi/WifiManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
@@ -16,6 +16,7 @@
package android.net.wifi;
+import static android.net.wifi.WifiConfiguration.METERED_OVERRIDE_METERED;
import static android.net.wifi.WifiManager.ActionListener;
import static android.net.wifi.WifiManager.BUSY;
import static android.net.wifi.WifiManager.ERROR;
@@ -881,17 +882,6 @@ public class WifiManagerTest {
}
/**
- * Verify main looper is used when handler is not provided.
- */
- @Test
- public void registerSoftApCallbackUsesMainExecutorOnNoExecutorProvided() {
- when(mContext.getMainExecutor()).thenReturn(
- new HandlerExecutor(new Handler(mLooper.getLooper())));
- mWifiManager.registerSoftApCallback(mSoftApCallback);
- verify(mContext).getMainExecutor();
- }
-
- /**
* Verify the call to registerSoftApCallback goes to WifiServiceImpl.
*/
@Test
@@ -1389,11 +1379,10 @@ public class WifiManagerTest {
@Test
public void registerTrafficStateCallbackUsesMainLooperOnNullArgumentForHandler()
throws Exception {
- when(mContext.getMainExecutor()).thenReturn(
- new HandlerExecutor(new Handler(mLooper.getLooper())));
ArgumentCaptor<ITrafficStateCallback.Stub> callbackCaptor =
ArgumentCaptor.forClass(ITrafficStateCallback.Stub.class);
- mWifiManager.registerTrafficStateCallback(mTrafficStateCallback);
+ mWifiManager.registerTrafficStateCallback(
+ new HandlerExecutor(new Handler(mLooper.getLooper())), mTrafficStateCallback);
verify(mWifiService).registerTrafficStateCallback(
any(IBinder.class), callbackCaptor.capture(), anyInt());
@@ -1474,11 +1463,11 @@ public class WifiManagerTest {
@Test
public void registerNetworkRequestMatchCallbackCallGoesToWifiServiceImpl()
throws Exception {
- when(mContext.getMainExecutor()).thenReturn(
- new HandlerExecutor(new Handler(mLooper.getLooper())));
ArgumentCaptor<INetworkRequestMatchCallback.Stub> callbackCaptor =
ArgumentCaptor.forClass(INetworkRequestMatchCallback.Stub.class);
- mWifiManager.registerNetworkRequestMatchCallback(mNetworkRequestMatchCallback);
+ mWifiManager.registerNetworkRequestMatchCallback(
+ new HandlerExecutor(new Handler(mLooper.getLooper())),
+ mNetworkRequestMatchCallback);
verify(mWifiService).registerNetworkRequestMatchCallback(
any(IBinder.class), callbackCaptor.capture(), anyInt());
@@ -1530,11 +1519,11 @@ public class WifiManagerTest {
@Test
public void networkRequestUserSelectionCallbackCallGoesToWifiServiceImpl()
throws Exception {
- when(mContext.getMainExecutor()).thenReturn(
- new HandlerExecutor(new Handler(mLooper.getLooper())));
ArgumentCaptor<INetworkRequestMatchCallback.Stub> callbackCaptor =
ArgumentCaptor.forClass(INetworkRequestMatchCallback.Stub.class);
- mWifiManager.registerNetworkRequestMatchCallback(mNetworkRequestMatchCallback);
+ mWifiManager.registerNetworkRequestMatchCallback(
+ new HandlerExecutor(new Handler(mLooper.getLooper())),
+ mNetworkRequestMatchCallback);
verify(mWifiService).registerNetworkRequestMatchCallback(
any(IBinder.class), callbackCaptor.capture(), anyInt());
@@ -1808,6 +1797,16 @@ public class WifiManagerTest {
verify(mWifiService).setMacRandomizationSettingPasspointEnabled(fqdn, true);
}
+ /**
+ * Test behavior of
+ * {@link WifiManager#setMacRandomizationSettingPasspointEnabled(String, boolean)}
+ */
+ @Test
+ public void testSetMeteredOverridePasspoint() throws Exception {
+ final String fqdn = "FullyQualifiedDomainName";
+ mWifiManager.setMeteredOverridePasspoint(fqdn, METERED_OVERRIDE_METERED);
+ verify(mWifiService).setMeteredOverridePasspoint(fqdn, METERED_OVERRIDE_METERED);
+ }
/**
* Test behavior of {@link WifiManager#disconnect()}
diff --git a/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java b/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java
index 603e78b90ff2..654154d77b0d 100644
--- a/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java
+++ b/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java
@@ -16,6 +16,8 @@
package android.net.wifi.hotspot2;
+import static android.net.wifi.WifiConfiguration.METERED_OVERRIDE_NONE;
+
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -173,6 +175,7 @@ public class PasspointConfigurationTest {
assertFalse(config.validateForR2());
assertTrue(config.isAutoJoinEnabled());
assertTrue(config.isMacRandomizationEnabled());
+ assertTrue(config.getMeteredOverride() == METERED_OVERRIDE_NONE);
}
/**